Skip to content

toFileSystemPath does not decode %3F back to "?", breaking round-trip for paths containing a literal question mark #427

Description

@patchwright

fromFileSystemPath/toFileSystemPath in lib/util/url.ts are meant to be inverses for local filesystem paths (that's the whole point of the manual encode/decode tables — encodeURI/decodeURI leave URL-reserved characters like # and ? alone, so this module encodes them going in and decodes them coming back out).

The encode table (urlEncodePatterns) handles both:

const urlEncodePatterns = [
  [/\?/g, "%3F"],
  [/#/g, "%23"],
] as [RegExp, string][];

but the decode table (urlDecodePatterns) only reverses #, $, &, ,, @%3F? is missing:

const urlDecodePatterns = [/%23/g, "#", /%24/g, "$", /%26/g, "&", /%2C/g, ",", /%40/g, "@"];

The comment right above toFileSystemPath's decode step even names the character:

// Step 2: Manually decode characters that are not decoded by `decodeURI`.
// This includes characters such as "#" and "?", which have special meaning in URLs,
// but are just normal characters in a filesystem path.

Reproduction

? is a legal character in POSIX filenames. Any $RefParser.parse()/.dereference()/.bundle() call on a local path containing ? fails, because lib/index.ts runs user-supplied paths through fromFileSystemPath (which encodes ?%3F) but resolution never decodes it back:

import $RefParser from "@apidevtools/json-schema-ref-parser";
// file exists at ./defs?1.json
await $RefParser.parse("./defs?1.json");
// ResolverError: Error opening file .../defs%3F1.json: ENOENT: no such file or directory

The literal ? in the path gets treated as if it were still URL query-string syntax and never makes it back to a real filesystem path.

Fix

Add the missing pair to urlDecodePatterns:

const urlDecodePatterns = [/%23/g, "#", /%24/g, "$", /%26/g, "&", /%2C/g, ",", /%3F/g, "?", /%40/g, "@"];

I have this fixed with a regression test and will open a PR referencing this issue.

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