Path Utils

Error Classes

Error types and error handling for path-utils.

The @ucdjs/path-utils package provides detailed error classes to help identify and handle different types of path-related security and validation issues.

Error Hierarchy

All errors extend from the base PathUtilsBaseError class, which extends the native Error class:

PathUtilsBaseError
├── MaximumDecodingIterationsExceededError
├── PathTraversalError
├── WindowsDriveMismatchError
├── FailedToDecodePathError
├── IllegalCharacterInPathError
├── WindowsPathBehaviorNotImplementedError
└── UNCPathNotSupportedError

Error Classes

PathUtilsBaseError

The abstract base class for all path-utils errors.

Signature

abstract class PathUtilsBaseError extends Error {
  constructor(message: string, options?: ErrorOptions)
}

Usage

import { PathUtilsBaseError } from '@ucdjs/path-utils';

try {
  // Path operations
} catch (error) {
  if (error instanceof PathUtilsBaseError) {
    // Handle any path-utils error
    console.error('Path utility error:', error.message);
  }
}

PathTraversalError

Thrown when a path traversal attempt is detected (trying to escape the base directory).

Properties

  • message (string) - Descriptive error message
  • basePath (string) - The allowed base path boundary
  • accessedPath (string) - The path that was attempted to be accessed

When Thrown

  • When resolveSafePath() resolves to a path outside the base directory
  • When encoded traversal attempts are detected (%2e%2e%2f)
  • When relative paths with ../ escape the boundary

Example

import { resolveSafePath, PathTraversalError } from '@ucdjs/path-utils';

try {
  resolveSafePath('/var/www/html', '../../etc/passwd');
} catch (error) {
  if (error instanceof PathTraversalError) {
    console.error(`Traversal attempt detected!`);
    console.error(`Base: ${error.basePath}`);
    console.error(`Attempted: ${error.accessedPath}`);
    // Base: /var/www/html
    // Attempted: /etc/passwd
  }
}

WindowsDriveMismatchError

Thrown when attempting to access a path on a different Windows drive letter than the base path.

Properties

  • message (string) - Descriptive error message
  • baseDrive (string) - The drive letter of the base path (e.g., "C")
  • accessedDrive (string) - The drive letter that was attempted (e.g., "D")

When Thrown

  • When resolveSafePath() receives paths with different drive letters on Windows
  • When the input path specifies a different drive than the base path

Example

import { resolveSafePath, WindowsDriveMismatchError } from '@ucdjs/path-utils';

try {
  resolveSafePath('C:\\Users\\John', 'D:\\External\\file.txt');
} catch (error) {
  if (error instanceof WindowsDriveMismatchError) {
    console.error(`Drive mismatch!`);
    console.error(`Base drive: ${error.baseDrive}`);
    console.error(`Attempted drive: ${error.accessedDrive}`);
    // Base drive: C
    // Attempted drive: D
  }
}

UNCPathNotSupportedError

Thrown when a UNC (Universal Naming Convention) network path is provided.

Properties

  • message (string) - Descriptive error message
  • path (string) - The UNC path that was rejected

When Thrown

  • When any function receives a UNC path (\\server\share)
  • UNC paths are rejected for security reasons across all functions

Example

import { resolveSafePath, UNCPathNotSupportedError } from '@ucdjs/path-utils';

try {
  resolveSafePath('C:\\base', '\\\\server\\share\\file.txt');
} catch (error) {
  if (error instanceof UNCPathNotSupportedError) {
    console.error(`UNC path rejected: ${error.path}`);
    // UNC path rejected: \\server\share\file.txt
  }
}
Security Note: UNC paths are rejected because they reference network locations, which could bypass filesystem boundaries and access remote systems.

IllegalCharacterInPathError

Thrown when a path contains illegal characters such as null bytes or control characters.

Properties

  • message (string) - Descriptive error message including the illegal character

When Thrown

  • When resolveSafePath() detects null bytes (\0) in the path
  • When control characters (Unicode category Cc) are found in the path

Example

import { resolveSafePath, IllegalCharacterInPathError } from '@ucdjs/path-utils';

try {
  // Null byte injection attempt
  resolveSafePath('/base', 'safe.txt\0../../../etc/passwd');
} catch (error) {
  if (error instanceof IllegalCharacterInPathError) {
    console.error('Illegal character detected:', error.message);
    // Illegal character detected: Illegal character detected in path: '\0'
  }
}

try {
  // Control character
  resolveSafePath('/base', 'file\x01.txt');
} catch (error) {
  if (error instanceof IllegalCharacterInPathError) {
    console.error('Control character found');
  }
}

FailedToDecodePathError

Thrown when a path cannot be safely decoded.

When Thrown

  • When decodePathSafely() exceeds the maximum decoding iterations (10)
  • Indicates potential malicious input with excessive nested encoding

Example

import { resolveSafePath, FailedToDecodePathError } from '@ucdjs/path-utils';

// Maliciously nested encoding
const maliciousInput = '%25252525...'; // Many layers deep

try {
  resolveSafePath('/base', maliciousInput);
} catch (error) {
  if (error instanceof FailedToDecodePathError) {
    console.error('Path decoding failed - possible attack');
  }
}

MaximumDecodingIterationsExceededError

Thrown when path decoding exceeds the safety limit of 10 iterations.

When Thrown

  • When decodePathSafely() detects more than 10 layers of URL encoding
  • Protection against infinite loops from malicious inputs

Example

import { decodePathSafely, MaximumDecodingIterationsExceededError } from '@ucdjs/path-utils';

// Create deeply nested encoding
let malicious = 'attack';
for (let i = 0; i < 15; i++) {
  malicious = encodeURIComponent(malicious);
}

try {
  decodePathSafely(malicious);
} catch (error) {
  if (error instanceof MaximumDecodingIterationsExceededError) {
    console.error('Too many encoding layers - rejecting input');
  }
}

WindowsPathBehaviorNotImplementedError

Thrown when an unsupported Windows path behavior is encountered.

When Thrown

  • When resolveSafePath() encounters Windows path combinations that aren't implemented
  • Rare edge case for future Windows path handling features

Example

import { WindowsPathBehaviorNotImplementedError } from '@ucdjs/path-utils';

try {
  // Some unsupported Windows path scenario
} catch (error) {
  if (error instanceof WindowsPathBehaviorNotImplementedError) {
    console.error('Unsupported Windows path behavior');
  }
}

Error Handling Patterns

Specific Error Handling

import {
  resolveSafePath,
  PathTraversalError,
  WindowsDriveMismatchError,
  UNCPathNotSupportedError,
  IllegalCharacterInPathError,
} from '@ucdjs/path-utils';

function safelyHandlePath(basePath: string, userInput: string): string {
  try {
    return resolveSafePath(basePath, userInput);
  } catch (error) {
    if (error instanceof PathTraversalError) {
      throw new Error('Access denied: path outside allowed directory');
    }
    if (error instanceof WindowsDriveMismatchError) {
      throw new Error('Access denied: different drive letter');
    }
    if (error instanceof UNCPathNotSupportedError) {
      throw new Error('Network paths are not supported');
    }
    if (error instanceof IllegalCharacterInPathError) {
      throw new Error('Invalid characters in path');
    }
    throw error; // Re-throw unexpected errors
  }
}

Generic Error Handling

import { resolveSafePath, PathUtilsBaseError } from '@ucdjs/path-utils';

function handlePath(basePath: string, userInput: string): string | null {
  try {
    return resolveSafePath(basePath, userInput);
  } catch (error) {
    if (error instanceof PathUtilsBaseError) {
      // Log security event
      console.warn('Path security violation:', error.message);
      return null;
    }
    throw error; // Non path-utils errors
  }
}

Logging Security Events

import { resolveSafePath, PathTraversalError } from '@ucdjs/path-utils';

function logSecurityEvent(error: Error, userInput: string, userId?: string) {
  console.error({
    type: 'security_violation',
    error: error.name,
    message: error.message,
    input: userInput,
    userId,
    timestamp: new Date().toISOString(),
  });
}

try {
  const path = resolveSafePath('/base', userInput);
} catch (error) {
  if (error instanceof PathTraversalError) {
    logSecurityEvent(error, userInput, currentUserId);
    // Return 403 Forbidden to client
  }
}

Best Practices

Always Catch Errors

Always use try-catch when calling security functions with user input to prevent unhandled exceptions.

Log Security Events

Log PathTraversalError and IllegalCharacterInPathError as potential security incidents.

User-Friendly Messages

Don't expose detailed error messages to end users - provide generic "invalid path" messages.

Check Error Types

Use instanceof checks to handle different error types appropriately.
Security Warning: Never expose the full error details (including paths) to end users, as this could reveal information about your filesystem structure.