Skip to content

Bug: Trailing backslash in path injects literal "undefined" into token value #433

@travis-burmaster

Description

@travis-burmaster

Summary

When a path string ends with a backslash (\), the parse() function produces a token with the literal string "undefined" appended to its value. This causes pathToRegexp() to generate a regex that matches the wrong path entirely.

Environment

  • path-to-regexp version: 8.4.1 (latest)
  • Node.js: v20+

Reproduction

const { parse, pathToRegexp } = require("path-to-regexp");

// parse() produces "undefined" in token value
console.log(parse("/test\\").tokens);
// => [{ type: "text", value: "/testundefined" }]  ⚠️

// pathToRegexp() generates wrong regex
const { regexp } = pathToRegexp("/test\\");
console.log(regexp.toString());
// => /^(?:\/testundefined)(?:\/$)?$/i

// The route now matches the wrong path
console.log(regexp.test("/testundefined")); // true  ⚠️
console.log(regexp.test("/test"));          // false ⚠️

Root Cause

In parse(), the escape token handler does:

else if (value === "\\") {
    tokens.push({ type: "escape", index, value: chars[index++] });
}

When \ is the last character in the string, chars[index++] reads past the end of the array and returns undefined. JavaScript then coerces this to the string "undefined" when it is concatenated in consumeUntil() to build the text token value.

Impact

Any route defined with a trailing backslash (e.g. from user-supplied route definitions, config files, or programmatic path construction) will:

  1. Silently fail to match any legitimate requests to that path
  2. Instead match requests to the path with undefined literally appended

This can cause route confusion and potential security bypasses if middleware guards a route that is inadvertently broken by this bug.

Additionally, the same issue affects intermediate backslashes followed by end-of-string:

parse("/a\\b\\").tokens
// => [{ type: "text", value: "/abundefined" }]  ⚠️

Expected Behavior

parse() should throw a PathError when a backslash appears as the last character (unterminated escape), similar to how unterminated quotes are handled:

throw new PathError(`Unterminated escape at index ${index}`, str);

Suggested Fix

else if (value === "\\") {
    if (index >= chars.length) {
        throw new PathError(`Unterminated escape at index ${index - 1}`, str);
    }
    tokens.push({ type: "escape", index, value: chars[index++] });
}

Discovered via manual source code audit of v8.4.1. No exploit code released.

Reported by Travis Burmaster — travis@burmaster.com

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions