Path Safety Resolver
Resolves a requested path inside an approved base directory and rejects traversal.
Function signature
ogResolveSafePath(base_path, requested_path, options = array())
Categories
- File and Upload Safety
Parameters
base_pathApproved root directory used to contain all local file operations.requested_pathCaller-supplied relative path to resolve safely under the base directory.optionsOptional documented policy controls for the helper.Return value
Public-safe status string returned by the function for controller branching or logging.
- success
- message
- data
Compatibility
Existing function name, slug, path, and call order preserved; advertised metadata corrected to the actual source behavior.
Minimum PHP version: 7.4
Security notes
Use caller-owned allowlists and context-specific escaping; validate file paths, routes, email tokens, cart totals, discount rules, and tax-region rules before production use.
Code
<?php
/*
* Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
* Free for personal and internal use. Paid project use requires visible credit
* to Jeffery L. Paris. Corporate use requires a paid license fee unless a
* separate written license states otherwise.
*/
/**
* Resolves a requested path inside an approved base directory and rejects traversal.
*
* Primary use case: File managers, downloads, imports.
* Typical inputs: base path, requested path.
* Typical output: safe absolute path or error.
*
* Implementation note: Use realpath carefully and handle non-existing target paths.
*
* @return array Structured result data with success, message, and data keys.
*/
function ogResolveSafePath($base_path, $requested_path, $options = array()) {
$result = array(
'success' => false,
'message' => '',
'data' => array()
);
$base_path = (string)$base_path;
$requested_path = trim((string)$requested_path);
if (!is_array($options)) {
$options = array();
}
$base_real = realpath($base_path);
if ($base_real === false || !is_dir($base_real)) {
$result['message'] = 'Approved base path is missing or is not a directory.';
return $result;
}
if (empty($requested_path) || strpos($requested_path, "\0") !== false) {
$result['message'] = 'Requested path is invalid.';
return $result;
}
$requested_path = str_replace('\\', '/', $requested_path);
if (preg_match('/(^|\/)\.\.(\/|$)/', $requested_path)) {
$result['message'] = 'Path traversal was rejected.';
return $result;
}
if (preg_match('/^[a-zA-Z]:\//', $requested_path) || substr($requested_path, 0, 1) == '/') {
$result['message'] = 'Absolute requested paths are not accepted.';
return $result;
}
$must_exist = true;
if (isset($options['must_exist']) && $options['must_exist'] === false) {
$must_exist = false;
}
$target_path = $base_real . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $requested_path);
$target_real = realpath($target_path);
if ($target_real === false) {
if ($must_exist) {
$result['message'] = 'Requested path does not exist.';
return $result;
}
$parent_real = realpath(dirname($target_path));
if ($parent_real === false || strpos($parent_real, $base_real) !== 0) {
$result['message'] = 'Target parent is outside the approved base path.';
return $result;
}
$target_real = $parent_real . DIRECTORY_SEPARATOR . basename($target_path);
} else {
if (strpos($target_real, $base_real) !== 0) {
$result['message'] = 'Resolved path is outside the approved base path.';
return $result;
}
if (is_link($target_real)) {
$result['message'] = 'Symbolic links are not accepted.';
return $result;
}
}
$result['success'] = true;
$result['message'] = 'Safe path resolved.';
$result['data'] = array(
'base_path' => $base_real,
'requested_path' => $requested_path,
'safe_path' => $target_real,
'exists' => file_exists($target_real)
);
return $result;
}