Normalise the separators
The code below is the file exactly as it stood in honojs/hono. The defect was introduced on 2023-11-04, reviewed, merged, and fixed on 2026-05-24 in commit 82dad629 — after 932 days in the tree.
The commit was written by a human contributor to honojs/hono, not by an agent. The ticket and the description on the next two panels are ours — a reconstruction of the case the original change made, so you meet it the way its reviewer did. The code, the defect and the dates are untouched.
Static file paths break on Windows-style separators
Some clients send asset paths with backslashes rather than slashes. The resolver should normalise a request path to a single separator style before it touches the filesystem, so the same asset resolves the same way whichever style the client used.
Traversal must stay blocked, obviously.
The resolver already strips a leading separator, blocks any path containing a `..` segment, and converts backslashes to slashes so a Windows-style request resolves the same as a POSIX one. It also refuses absolute paths when no root is configured.
I reviewed the whole function against the traversal cases and it holds — `..` is rejected before any normalisation happens, so there is no ordering hole. Existing tests pass.
Our reconstruction of the argument the real change made, not a quotation of it. Fluent, specific, and not evidence of anything — which is the point.
type FilePathOptions = { filename: string root?: string defaultDocument?: string} export const getFilePath = (options: FilePathOptions): string | undefined => { let filename = options.filename const defaultDocument = options.defaultDocument || 'index.html' if (filename.endsWith('/')) { // /top/ => /top/index.html filename = filename.concat(defaultDocument) } else if (!filename.match(/\.[a-zA-Z0-9_-]+$/)) { // /top => /top/index.html filename = filename.concat('/' + defaultDocument) } const path = getFilePathWithoutDefaultDocument({ root: options.root, filename, }) return path} export const getFilePathWithoutDefaultDocument = ( options: Omit<FilePathOptions, 'defaultDocument'>): string | undefined => { let root = options.root || '' let filename = options.filename if (/(?:^|[\/\\])\.\.(?:$|[\/\\])/.test(filename)) { return } // /foo.html => foo.html filename = filename.replace(/^\.?[\/\\]/, '') // foo\bar.txt => foo/bar.txt filename = filename.replace(/\\/, '/') // assets/ => assets root = root.replace(/\/$/, '') // ./assets/foo.html => assets/foo.html let path = root ? root + '/' + filename : filename path = path.replace(/^\.?\//, '') if (root[0] !== '/' && path[0] === '/') { return } return path}