mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
fix(reference): preserve historical backslash import bases
This commit is contained in:
@@ -505,6 +505,13 @@ by file order. Omit `--evidence-bundle` when the request supplies inline Evidenc
|
||||
|
||||
## Import historical source
|
||||
|
||||
Historical source inventories preserve exact filenames, including literal backslashes
|
||||
on POSIX hosts. Relative import lookup uses the recorded source file's owning
|
||||
directory and decodes URL percent escapes once; encoded slash, NUL and malformed
|
||||
escapes remain unresolved. A decoded backslash remains part of a filename rather
|
||||
than a directory separator. This inventory lookup does not claim that Node can
|
||||
load that filename or apply Node's runtime file-URL admission policy.
|
||||
|
||||
Import an older source tree to compare with the current artifact:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -3,10 +3,10 @@ import { createHash } from "node:crypto";
|
||||
import { posix } from "node:path";
|
||||
|
||||
import type { JavaScriptPackageType } from "../domain/javascript/javascriptSourceParser.js";
|
||||
import { decodeJavaScriptModuleUrlPath } from "../domain/javascript/javascriptModuleSpecifier.js";
|
||||
import { logicalPathEscapesRoot } from "../domain/artifactIdentity.js";
|
||||
import {
|
||||
createReferenceSourcePathLookup,
|
||||
decodeReferenceSourceModuleUrlPath,
|
||||
isPortableAbsoluteReferenceTarget,
|
||||
type ReferenceSourcePathLookup,
|
||||
} from "../domain/referenceSourcePathIdentity.js";
|
||||
@@ -322,7 +322,7 @@ const importLookupPath = (
|
||||
);
|
||||
if (!target.pathname.startsWith("/rea-reference/"))
|
||||
return err("The module URL escapes the selected source inventory.");
|
||||
const decoded = decodeJavaScriptModuleUrlPath(
|
||||
const decoded = decodeReferenceSourceModuleUrlPath(
|
||||
target.pathname.slice("/rea-reference/".length),
|
||||
);
|
||||
return decoded.ok
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import { createReferenceSourcePathLookup } from "./referenceSourcePathIdentity.js";
|
||||
import {
|
||||
createReferenceSourcePathLookup,
|
||||
decodeReferenceSourceModuleUrlPath,
|
||||
} from "./referenceSourcePathIdentity.js";
|
||||
|
||||
it("reports ambiguous canonical identities while preserving exact paths", () => {
|
||||
const first = "package/a\u0301\u0323/package.json";
|
||||
@@ -13,3 +16,21 @@ it("reports ambiguous canonical identities while preserving exact paths", () =>
|
||||
expect(lookup.resolve(first)).toBe(first);
|
||||
expect(lookup.resolve(second)).toBe(second);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["literal%5Cdir/dep.js", "literal\\dir/dep.js"],
|
||||
["literal%255Cdir/dep.js", "literal%5Cdir/dep.js"],
|
||||
["literal%252Fdir/dep.js", "literal%2Fdir/dep.js"],
|
||||
])("decodes historical inventory bytes once: %s", (encoded, path) => {
|
||||
expect(decodeReferenceSourceModuleUrlPath(encoded)).toEqual({
|
||||
ok: true,
|
||||
value: path,
|
||||
});
|
||||
});
|
||||
|
||||
it.each(["dir%2Fdep.js", "dir%2fdep.js", "dir/%00dep.js", "dir/%invalid.js"])(
|
||||
"refuses malformed historical module paths: %s",
|
||||
(encoded) => {
|
||||
expect(decodeReferenceSourceModuleUrlPath(encoded).ok).toBe(false);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { posix } from "node:path";
|
||||
import { err, ok, type Result } from "./result.js";
|
||||
|
||||
export interface ReferenceSourcePathLookup {
|
||||
/** Whether an exact or canonical identity exists, including ambiguous ones. */
|
||||
@@ -53,3 +54,24 @@ export const isPortableAbsoluteReferenceTarget = (target: string): boolean =>
|
||||
target.startsWith("/") ||
|
||||
/^[A-Za-z]:[\\/]/u.test(target) ||
|
||||
/^\\\\[^\\/]+[\\/][^\\/]+/u.test(target);
|
||||
|
||||
/** Decode one historical inventory URL path; backslash remains a filename byte. */
|
||||
export const decodeReferenceSourceModuleUrlPath = (
|
||||
value: string,
|
||||
): Result<string, string> => {
|
||||
if (/%2f/iu.test(value))
|
||||
return err(
|
||||
"Historical module URL paths cannot contain percent-encoded slash bytes.",
|
||||
);
|
||||
try {
|
||||
const decoded = decodeURIComponent(value);
|
||||
return decoded.includes("\0")
|
||||
? err("The decoded historical module URL path contains NUL.")
|
||||
: ok(decoded);
|
||||
} catch (cause: unknown) {
|
||||
if (!(cause instanceof URIError)) throw cause;
|
||||
return err(
|
||||
"The historical module URL path contains malformed percent encoding.",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user