Skip to content

Route path generation: use string concatenation instead of template literals to eliminate $-escaping heuristics #3703

Description

@coderabbitai

Summary

During review of #3692 (security fix for unescaped OpenAPI strings in generated template literals), a related edge case was identified in packages/core/src/getters/route.ts's getRoutePath function: a literal $ immediately preceding a {param} in an OpenAPI path (e.g. /foo${petId}) is dropped in the generated template literal, producing /foo${petId} instead of /foo$${petId}.

Rationale (root cause, not just this instance)

The underlying problem is not this single edge case — it's the approach. Orval currently builds generated route/URL strings as raw JS template literals, and relies on ad-hoc heuristics (inspecting preceding characters such as \ vs $) to disambiguate a literal $ in the OpenAPI spec from the ${...} interpolation syntax it emits for path parameters. As more jsesc-based escaping of static segments is layered on top, these heuristics keep multiplying and are fragile by construction — every new escaping rule risks colliding with legitimate spec content in a new way.

Patching the heuristic for this one case (literal $ before {param}) does not address the underlying issue: any future OpenAPI path containing characters adjacent to ${/} sequences from jsesc escaping is a candidate for a similar bug.

Proposed fix

Move getRoutePath, getRoute, and getRouteAsArray (and any other generators building route/URL strings from spec content) away from constructing raw template-literal source text via character-adjacency heuristics. Instead, build the generated string using explicit string concatenation of separately-escaped literal segments and parameter placeholders (or an equivalent AST/token-based builder). This removes the need to guess intent from adjacent characters, since literal text and parameter interpolation are never emitted as raw overlapping template-literal syntax that has to be disambiguated after the fact.

Affected areas

  • packages/core/src/getters/route.ts (getRoutePath, getRoute, getRouteAsArray)
  • Possibly other generators that build template-literal strings from OpenAPI spec content

Acceptance criteria

  • Generated route/URL strings are built via string concatenation (or equivalent non-heuristic construction) instead of raw template-literal text with character-adjacency escaping heuristics.
  • Literal $ (and other characters that could collide with template-literal syntax) immediately preceding a {param} in an OpenAPI path is preserved correctly regardless of adjacency (e.g. /foo${petId} emits the correct literal $ before the parameter).
  • No regression in generated output for existing test fixtures.
  • Scoped for the v9 release per the maintainer's guidance.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions