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

v0.1.0 2025-11-17 20:51 UTC

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: InvalidPathException if 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: InvalidPathException if 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

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

License

MIT License - see LICENSE file for details.

Author

Arkadiusz Adach