adachsoft / normalized-safe-path
A library to normalize paths and prevent directory traversal.
Installs: 7
Dependents: 3
Suggesters: 0
Security: 0
Stars: 0
Forks: 0
pkg:composer/adachsoft/normalized-safe-path
Requires
- php: ^7.4 || ^8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.89
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.4
This package is not auto-updated.
Last update: 2025-12-02 18:09:33 UTC
README
A PHP library for normalizing file paths and preventing directory traversal attacks.
Installation
Install via Composer:
composer require adachsoft/normalized-safe-path
Requirements
- PHP 7.4 or higher
- Composer
Usage
Basic Usage
<?php
use AdachSoft\NormalizedSafePath\SafePath;
use AdachSoft\NormalizedSafePath\SafePathHelper;
// Using the main class
$safePath = new SafePath('/var/www/html');
$normalized = $safePath->normalize('uploads/../images/file.jpg');
// Returns: "images/file.jpg"
// Using the static helper
$normalized = SafePathHelper::normalize('/var/www/html', 'uploads/../images/file.jpg');
// Returns: "images/file.jpg"
Security Features
The library automatically prevents directory traversal attacks:
<?php
use AdachSoft\NormalizedSafePath\SafePath;
use AdachSoft\NormalizedSafePath\Exception\InvalidPathException;
$safePath = new SafePath('/var/www/html');
try {
$safePath->normalize('../../../etc/passwd');
// Throws InvalidPathException
} catch (InvalidPathException $e) {
echo "Path traversal detected!";
}
Path Normalization Features
- Cross-platform separators: Handles both
/and\as path separators - Dot resolution: Removes
.and resolves..appropriately - Double slash normalization: Normalizes multiple consecutive slashes
- Empty path handling: Returns empty string for empty or dot-only paths
<?php
$safePath = new SafePath('/base');
// Cross-platform separators
$safePath->normalize('folder\\subfolder/file.txt');
// Returns: "folder/subfolder/file.txt"
// Dot resolution
$safePath->normalize('./folder/../other/./file.txt');
// Returns: "other/file.txt"
// Double slash removal
$safePath->normalize('folder//subfolder///file.txt');
// Returns: "folder/subfolder/file.txt"
API Reference
SafePath Class
Constructor
new SafePath(string $basePath)
- $basePath: The base directory path for validation
Methods
normalize(string $path): string
- $path: The path to normalize and validate
- Returns: Normalized path relative to base directory
- Throws:
InvalidPathExceptionif path traversal is detected
SafePathHelper Class
Static Methods
SafePathHelper::normalize(string $basePath, string $path): string
- $basePath: The base directory path
- $path: The path to normalize and validate
- Returns: Normalized path
- Throws:
InvalidPathExceptionif path traversal is detected
Exception Handling
The library throws AdachSoft\NormalizedSafePath\Exception\InvalidPathException when:
- A path attempts to traverse outside the base directory using
.. - The resolved path would escape the base directory
Examples
File Upload Security
<?php
use AdachSoft\NormalizedSafePath\SafePath;
$uploadDir = '/var/www/uploads';
$safePath = new SafePath($uploadDir);
// User-provided filename
$userFilename = $_POST['filename'] ?? '';
try {
$safeFilename = $safePath->normalize($userFilename);
$fullPath = $uploadDir . DIRECTORY_SEPARATOR . $safeFilename;
// Now safe to use for file operations
move_uploaded_file($_FILES['file']['tmp_name'], $fullPath);
} catch (InvalidPathException $e) {
// Handle malicious path attempt
http_response_code(400);
echo "Invalid file path provided";
}
Configuration File Loading
<?php
use AdachSoft\NormalizedSafePath\SafePathHelper;
$configDir = '/app/config';
$configFile = SafePathHelper::normalize($configDir, $_GET['config'] ?? 'default.json');
$configPath = $configDir . DIRECTORY_SEPARATOR . $configFile;
if (file_exists($configPath)) {
$config = json_decode(file_get_contents($configPath), true);
}
Testing
Run the test suite:
composer test
Run with coverage:
composer test-coverage
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
License
MIT License - see LICENSE file for details.
Author
Arkadiusz Adach