@stacksjs/router
Version:
The Stacks framework router.
65 lines (64 loc) • 2.29 kB
TypeScript
/**
* Validate and return a path parameter, throwing if it's unsafe to use
* in filesystem interpolation.
*
* The default contract is single-segment: no `/`, no `\`, no `..`, no
* absolute path, no null bytes, no control characters, length ≤ 255.
* Pass `allowSlashes: true` for a multi-segment path (still rejects
* the rest).
*
* @throws {PathParamError} when the value fails any check.
*/
export declare function sanitizePathParam(value: unknown, options?: SanitizePathParamOptions): string;
/**
* Non-throwing variant. Returns the sanitized value or `null` if any
* check failed. Use when you want a fast yes/no in a conditional
* without a try/catch around the throw site.
*/
export declare function safePathParam(value: unknown, options?: SanitizePathParamOptions): string | null;
export declare interface SanitizePathParamOptions {
context?: string
maxLength?: number
allowSlashes?: boolean
}
/**
* Path-parameter sanitization helpers.
*
* Route params arrive from the URL as untyped strings and are merged
* directly into `req.params`. Actions that interpolate those values
* into filesystem paths or shell commands without first scrubbing
* them are vulnerable to `..`-traversal, absolute-path takeovers, and
* null-byte truncation attacks.
*
* The router itself can't auto-sanitize every param (some are
* deliberately path-shaped — file servers, asset proxies, etc.). What
* we ship instead is a single canonical helper that callers reach for
* at the boundary where the param meets the filesystem.
*
* See stacksjs/stacks#1870 R-12.
*
* @example
* ```ts
* import { sanitizePathParam } from '@stacksjs/router'
*
* const filename = sanitizePathParam(req.params.filename, {
* context: 'avatar download',
* })
* return new Response(Bun.file(path.appPath(`avatars/${filename}`)))
* ```
*/
/**
* Reasons {@link sanitizePathParam} rejects a value. Surfaced via the
* thrown error so callers can log or branch.
*/
export type PathParamRejection = | 'empty'
| 'not-string'
| 'absolute-path'
| 'traversal'
| 'null-byte'
| 'control-char'
| 'too-long';
export declare class PathParamError extends Error {
readonly reason: PathParamRejection;
constructor(reason: PathParamRejection, value: unknown, context?: string);
}