Examples
Comprehensive usage examples and real-world patterns
Examples
This page provides comprehensive examples of using @ucdjs/path-utils in real-world scenarios.
Basic Virtual Filesystem
Creating a sandboxed file access system with a defined boundary.
import { resolveSafePath } from '@ucdjs/path-utils';
// Define your application's data boundary
const appDataPath = '/home/user/myapp';
// Resolve user-provided paths safely
function getFilePath(userPath: string): string {
return resolveSafePath(appDataPath, userPath);
}
// Usage
getFilePath('config.json');
// → '/home/user/myapp/config.json'
getFilePath('/uploads/document.pdf');
// → '/home/user/myapp/uploads/document.pdf'
getFilePath('data/cache/temp.txt');
// → '/home/user/myapp/data/cache/temp.txt'
// Attempts to escape are blocked
getFilePath('../../etc/passwd');
// ❌ PathTraversalErrorWeb Server Static File Serving
Safely serve static files from a document root directory.
import { resolveSafePath, PathTraversalError } from '@ucdjs/path-utils';
import { readFile } from 'fs/promises';
const DOCUMENT_ROOT = '/var/www/html';
async function serveStaticFile(requestPath: string): Promise<Buffer> {
try {
// Safely resolve the file path
const filePath = resolveSafePath(DOCUMENT_ROOT, requestPath);
// Read and serve the file
return await readFile(filePath);
} catch (error) {
if (error instanceof PathTraversalError) {
throw new Error('403 Forbidden: Access denied');
}
throw error;
}
}
// Safe requests
await serveStaticFile('/assets/style.css');
await serveStaticFile('/images/logo.png');
await serveStaticFile('/js/app.js');
// Blocked requests
await serveStaticFile('../../etc/passwd');
// → 403 Forbidden
await serveStaticFile('%2e%2e%2f%2e%2e%2fetc%2fpasswd');
// → 403 Forbidden (encoded traversal)File Upload Handler
Handle user file uploads with path validation.
import { resolveSafePath, PathTraversalError, IllegalCharacterInPathError } from '@ucdjs/path-utils';
import { writeFile } from 'fs/promises';
import { mkdir } from 'fs/promises';
const UPLOAD_DIR = '/var/uploads';
interface UploadResult {
success: boolean;
path?: string;
error?: string;
}
async function handleFileUpload(
userId: string,
filename: string,
content: Buffer
): Promise<UploadResult> {
try {
// Create user-specific subdirectory path
const userDir = `users/${userId}`;
const filePath = `${userDir}/${filename}`;
// Resolve safely within upload boundary
const safePath = resolveSafePath(UPLOAD_DIR, filePath);
// Create directory if needed
const dirPath = resolveSafePath(UPLOAD_DIR, userDir);
await mkdir(dirPath, { recursive: true });
// Write file
await writeFile(safePath, content);
return {
success: true,
path: safePath,
};
} catch (error) {
if (error instanceof PathTraversalError) {
return {
success: false,
error: 'Invalid file path',
};
}
if (error instanceof IllegalCharacterInPathError) {
return {
success: false,
error: 'Invalid characters in filename',
};
}
throw error;
}
}
// Safe uploads
await handleFileUpload('user123', 'document.pdf', buffer);
// → /var/uploads/users/user123/document.pdf
await handleFileUpload('user456', 'photos/vacation.jpg', buffer);
// → /var/uploads/users/user456/photos/vacation.jpg
// Blocked uploads
await handleFileUpload('user789', '../../../etc/passwd', buffer);
// → { success: false, error: 'Invalid file path' }
await handleFileUpload('user789', 'file\0.txt', buffer);
// → { success: false, error: 'Invalid characters in filename' }Cross-Platform Configuration System
Handle configuration files across different operating systems.
import { resolveSafePath, toUnixFormat, osPlatform } from '@ucdjs/path-utils';
import { readFile, writeFile } from 'fs/promises';
class ConfigManager {
private configDir: string;
constructor(configDir: string) {
// Normalize the config directory path
this.configDir = osPlatform === 'win32'
? configDir
: toUnixFormat(configDir);
}
async getConfigPath(name: string): Promise<string> {
// Ensure config name is safe
return resolveSafePath(this.configDir, `${name}.json`);
}
async loadConfig<T = any>(name: string): Promise<T> {
const path = await this.getConfigPath(name);
const content = await readFile(path, 'utf-8');
return JSON.parse(content);
}
async saveConfig<T = any>(name: string, data: T): Promise<void> {
const path = await this.getConfigPath(name);
await writeFile(path, JSON.stringify(data, null, 2));
}
}
// Works on any platform
const config = new ConfigManager('C:\\Users\\John\\AppData\\myapp');
// or
const config = new ConfigManager('/home/user/.config/myapp');
await config.loadConfig('settings');
await config.saveConfig('preferences', { theme: 'dark' });
// Blocked
await config.loadConfig('../../passwords');
// ❌ PathTraversalErrorPlugin Sandbox System
Sandbox plugin file access to specific directories.
import { resolveSafePath, PathTraversalError } from '@ucdjs/path-utils';
import { readFile, writeFile, readdir } from 'fs/promises';
class PluginSandbox {
private pluginDataDir: string;
constructor(pluginId: string) {
this.pluginDataDir = `/var/plugins/${pluginId}/data`;
}
async readFile(path: string): Promise<string> {
const safePath = resolveSafePath(this.pluginDataDir, path);
return readFile(safePath, 'utf-8');
}
async writeFile(path: string, content: string): Promise<void> {
const safePath = resolveSafePath(this.pluginDataDir, path);
await writeFile(safePath, content);
}
async listFiles(dir: string = '/'): Promise<string[]> {
const safePath = resolveSafePath(this.pluginDataDir, dir);
return readdir(safePath);
}
}
// Plugin can only access its own data directory
const sandbox = new PluginSandbox('my-plugin');
await sandbox.writeFile('/config.json', '{}');
// → /var/plugins/my-plugin/data/config.json
await sandbox.readFile('/cache/data.txt');
// → /var/plugins/my-plugin/data/cache/data.txt
await sandbox.listFiles('/');
// → ['config.json', 'cache']
// Plugin cannot escape sandbox
await sandbox.readFile('../../other-plugin/secrets.txt');
// ❌ PathTraversalError
await sandbox.readFile('/etc/passwd');
// ❌ PathTraversalError (treated as /var/plugins/my-plugin/data/etc/passwd)Windows-Specific Scenarios
Handling Windows paths with drive letters and mixed separators.
import {
resolveSafePath,
isWindowsDrivePath,
getWindowsDriveLetter,
WindowsDriveMismatchError,
} from '@ucdjs/path-utils';
const projectRoot = 'C:\\Users\\John\\Projects\\MyApp';
// Mixed separators work
resolveSafePath(projectRoot, 'src/components\\Button.tsx');
// → 'C:/Users/John/Projects/MyApp/src/components/Button.tsx'
// Backslashes work
resolveSafePath(projectRoot, 'data\\users\\profiles.json');
// → 'C:/Users/John/Projects/MyApp/data/users/profiles.json'
// Absolute Windows paths within boundary
resolveSafePath(
projectRoot,
'C:\\Users\\John\\Projects\\MyApp\\config.json'
);
// → 'C:/Users/John/Projects/MyApp/config.json'
// Different drive letter blocked
try {
resolveSafePath(projectRoot, 'D:\\external\\file.txt');
} catch (error) {
if (error instanceof WindowsDriveMismatchError) {
console.error(
`Cannot access drive ${error.accessedDrive}, ` +
`base is on drive ${error.baseDrive}`
);
}
}
// Check if path is Windows drive path
if (isWindowsDrivePath('C:\\Users')) {
const drive = getWindowsDriveLetter('C:\\Users');
console.log(`Drive letter: ${drive}`); // 'C'
}Encoding Attack Prevention
Protect against various encoding-based attacks.
import {
resolveSafePath,
decodePathSafely,
PathTraversalError,
MaximumDecodingIterationsExceededError,
} from '@ucdjs/path-utils';
const boundary = '/var/www/uploads';
// Simple URL encoding (safe)
resolveSafePath(boundary, 'user%20files/document.pdf');
// → '/var/www/uploads/user files/document.pdf'
// Encoded path separators (safe)
resolveSafePath(boundary, 'folder%2Ffile.txt');
// → '/var/www/uploads/folder/file.txt'
// Encoded traversal attempts (blocked)
try {
resolveSafePath(boundary, '%2e%2e%2f%2e%2e%2fetc%2fpasswd');
} catch (error) {
console.error('Encoded traversal blocked');
// ❌ PathTraversalError
}
// Double encoding (blocked)
try {
resolveSafePath(boundary, '%252e%252e%252f%252e%252e');
} catch (error) {
console.error('Double-encoded traversal blocked');
// ❌ PathTraversalError
}
// Excessive encoding (blocked)
const malicious = '%25'.repeat(20) + '2e2e2f';
try {
decodePathSafely(malicious);
} catch (error) {
if (error instanceof MaximumDecodingIterationsExceededError) {
console.error('Too many encoding layers');
}
}API Request Handler
Complete example of handling API requests with path parameters.
import { resolveSafePath, PathUtilsBaseError } from '@ucdjs/path-utils';
import { readFile } from 'fs/promises';
const STORAGE_ROOT = '/var/app/storage';
interface ApiResponse {
status: number;
data?: any;
error?: string;
}
async function handleApiRequest(
userId: string,
resourcePath: string
): Promise<ApiResponse> {
try {
// Build user-specific path
const userPath = `users/${userId}`;
const fullPath = `${userPath}/${resourcePath}`;
// Resolve safely
const safePath = resolveSafePath(STORAGE_ROOT, fullPath);
// Read file (example)
const data = await readFile(safePath, 'utf-8');
return {
status: 200,
data: JSON.parse(data),
};
} catch (error) {
// Handle path-utils errors
if (error instanceof PathUtilsBaseError) {
return {
status: 403,
error: 'Forbidden: Invalid path',
};
}
// Handle file not found
if ((error as any).code === 'ENOENT') {
return {
status: 404,
error: 'Resource not found',
};
}
// Other errors
return {
status: 500,
error: 'Internal server error',
};
}
}
// Valid requests
await handleApiRequest('user123', 'documents/report.json');
// → { status: 200, data: {...} }
await handleApiRequest('user456', 'photos/vacation.jpg');
// → { status: 200, data: {...} }
// Invalid requests
await handleApiRequest('user789', '../../etc/passwd');
// → { status: 403, error: 'Forbidden: Invalid path' }
await handleApiRequest('user789', '%2e%2e%2fadmin%2fsecrets');
// → { status: 403, error: 'Forbidden: Invalid path' }Database File Storage
Manage database file storage with safe path resolution.
import { resolveSafePath } from '@ucdjs/path-utils';
import { writeFile, readFile } from 'fs/promises';
import { createHash } from 'crypto';
const BLOB_STORAGE = '/var/db/blobs';
class BlobStorage {
async store(userId: string, filename: string, data: Buffer): Promise<string> {
// Create hash-based subdirectories for better distribution
const hash = createHash('sha256').update(data).digest('hex');
const prefix = hash.substring(0, 2);
const subdir = hash.substring(2, 4);
// Safe path: /var/db/blobs/users/{userId}/{prefix}/{subdir}/{hash}
const path = resolveSafePath(
BLOB_STORAGE,
`users/${userId}/${prefix}/${subdir}/${hash}`
);
await writeFile(path, data);
// Return reference path
return `${prefix}/${subdir}/${hash}`;
}
async retrieve(userId: string, reference: string): Promise<Buffer> {
const path = resolveSafePath(
BLOB_STORAGE,
`users/${userId}/${reference}`
);
return readFile(path);
}
}
const storage = new BlobStorage();
// Store blob
const ref = await storage.store('user123', 'avatar.png', buffer);
// → '5f/a3/5fa3...'
// Retrieve blob
const data = await storage.retrieve('user123', ref);
// Cannot access other users' blobs
await storage.retrieve('user123', '../../user456/5f/a3/5fa3...');
// ❌ PathTraversalErrorBest Practices Summary
- Always Validate Input - Use
resolveSafePathfor all user-provided paths before file operations. - Define Clear Boundaries - Establish clear base directories for different types of data (uploads, configs, cache).
- Handle Errors Gracefully - Catch and handle path-utils errors appropriately, logging security events.
- Never Expose Details - Don't expose actual file paths or error details to end users.
- Use Type Guards - Use
instanceofchecks to handle different error types specifically. - Log Security Events - Log path traversal attempts and other security violations for monitoring.