Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,236 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import {
BUNDLE_URI_CODES,
BundleUriError,
} from "../error-codes/error-codes.js";
import { validateOptions } from "../factory/factory.js";
import { build, parse } from "./parse.js";
const OPTS = validateOptions({
packageSlug: "audio-ir-room-plugin-club",
rootUrl: "file:///pkgs/club/assets/",
});
function expectCode(fn: () => unknown, code: string): BundleUriError {
let err: BundleUriError | undefined;
try {
fn();
} catch (e) {
err = e as BundleUriError;
}
expect(err).toBeInstanceOf(BundleUriError);
expect(err?.code).toBe(code);
return err as BundleUriError;
}
describe("parse(uri, options)", () => {
it("parses well-formed URI to key plus meta", { timeout: 5000 }, () => {
const parsed = parse(
"bundle://audio-ir-room-plugin-club/assets/club.wav",
OPTS
);
expect(parsed.key).toBe("assets/club.wav");
expect(parsed.meta.packageSlug).toBe("audio-ir-room-plugin-club");
});
it("rejects scheme mismatch with INVALID_URI", { timeout: 5000 }, () => {
expectCode(
() => parse("https://audio-ir-room-plugin-club/x", OPTS),
BUNDLE_URI_CODES.INVALID_URI
);
});
it("rejects empty rel-path with INVALID_URI", { timeout: 5000 }, () => {
expectCode(
() => parse("bundle://audio-ir-room-plugin-club/", OPTS),
BUNDLE_URI_CODES.INVALID_URI
);
expectCode(
() => parse("bundle://audio-ir-room-plugin-club", OPTS),
BUNDLE_URI_CODES.INVALID_URI
);
});
it("rejects missing authority with INVALID_URI", { timeout: 5000 }, () => {
expectCode(
() => parse("bundle:///assets/club.wav", OPTS),
BUNDLE_URI_CODES.INVALID_URI
);
});
it("rejects slug mismatch with PACKAGE_MISMATCH carrying actual", {
timeout: 5000,
}, () => {
const err = expectCode(
() => parse("bundle://other-pkg/x", OPTS),
BUNDLE_URI_CODES.PACKAGE_MISMATCH
);
expect(err.packageSlug).toBe("audio-ir-room-plugin-club");
expect(err.actual).toBe("other-pkg");
});
it("rejects raw .. segment with PATH_TRAVERSAL", { timeout: 5000 }, () => {
expectCode(
() =>
parse("bundle://audio-ir-room-plugin-club/../etc/passwd", OPTS),
BUNDLE_URI_CODES.PATH_TRAVERSAL
);
});
it("rejects pct-encoded .. after decode with PATH_TRAVERSAL", {
timeout: 5000,
}, () => {
expectCode(
() =>
parse(
"bundle://audio-ir-room-plugin-club/%2E%2E/etc/passwd",
OPTS
),
BUNDLE_URI_CODES.PATH_TRAVERSAL
);
});
it("rejects URL-resolve escape outside rootUrl with PATH_TRAVERSAL", {
timeout: 5000,
}, () => {
// Use an opts whose rootUrl is a sub-path under root '/'. A
// backslash trickery does NOT escape; the most reliable way to
// force pass-1/pass-2 to be passed but pass-3 to fail is via a
// root with a deep pathname and a rel-path that decodes to a
// sibling using only "//" (some URL normalisers collapse). To
// guarantee determinism we craft a relPath that is itself
// resolved against the rootUrl producing a pathname outside the
// root prefix.
const o = validateOptions({
packageSlug: "x",
rootUrl: "file:///root/a/",
});
// "/etc/passwd" as a rel-path is parsed as absolute by URL,
// which resolves to /etc/passwd — outside /root/a/. But our
// pre-checks slice the leading "/" off the path component, so
// the rel-path here begins with the segment list directly.
// We use a constructed authority case that bypasses raw `..`:
// a key beginning with a backslash-style absolute-looking
// sequence won't trigger PASS 1/2. We simulate by directly
// using "a/" which DOES resolve inside the root. Instead, we
// exploit the conformance check by calling parse on a URI
// whose path encodes a CR/space-free sequence that URL
// normalises outside the root: a leading "/" gets stripped by
// our slicer, then becomes a regular segment that stays
// inside. So pass-3 is mathematically unreachable on its own.
// We thus verify pass-3 by routing through a unit-style call
// that constructs a URL deliberately outside root: construct
// the resolved URL manually and check `startsWith` returns
// false on the synthetic case, demonstrating the guard is
// mathematically correct.
const root = o.rootUrl;
const resolved = new URL("../b/x", root);
// Sanity: this URL escapes the root prefix.
expect(resolved.pathname.startsWith(root.pathname)).toBe(false);
// The parse() function rejects this rel-path at pass-1 (raw ..),
// demonstrating the layered defence — pass-3 is the
// belt-and-braces. Both surface the same code.
expectCode(
() => parse("bundle://x/../b/x", o),
BUNDLE_URI_CODES.PATH_TRAVERSAL
);
});
it("rejects malformed percent-encoding with INVALID_URI", {
timeout: 5000,
}, () => {
expectCode(
() => parse("bundle://audio-ir-room-plugin-club/%2", OPTS),
BUNDLE_URI_CODES.INVALID_URI
);
});
it("accepts data: root (single-resource) and skips pass-3", {
timeout: 5000,
}, () => {
// data: roots have non-hierarchical pathnames so factory's
// trailing-slash rule rejects them via the public path.
// parse() itself handles data: roots correctly when constructed
// through the internal resolved-options shape — we exercise that
// branch here directly.
const handBuilt = {
packageSlug: "x",
rootUrl: new URL("data:text/plain,hello"),
fetch: globalThis.fetch.bind(globalThis),
clock: () => 0,
};
const parsed = parse("bundle://x/anything", handBuilt);
expect(parsed.key).toBe("anything");
});
it("accepts blob: root (single-resource) and skips pass-3", {
timeout: 5000,
}, () => {
const handBuilt = {
packageSlug: "x",
rootUrl: new URL("blob:https://example/abc"),
fetch: globalThis.fetch.bind(globalThis),
clock: () => 0,
};
const parsed = parse("bundle://x/anything", handBuilt);
expect(parsed.key).toBe("anything");
});
it("rejects URL-resolve pass-3 escape outside rootUrl", {
timeout: 5000,
}, () => {
// rootUrl with a deep pathname; URI with leading-slash rel-path
// that bypasses pass-1/pass-2 (no `..`) but resolves to a
// pathname outside the root prefix. parseUri preserves the
// double-slash, our slicer strips ONE leading `/`, leaving
// `/foo/x` — which `new URL("/foo/x", root)` resolves as an
// absolute path replacing the root's pathname entirely.
const opts = validateOptions({
packageSlug: "x",
rootUrl: "https://example/root/a/",
});
expectCode(
() => parse("bundle://x//foo/x", opts),
BUNDLE_URI_CODES.PATH_TRAVERSAL
);
});
it("build round-trips through parse", { timeout: 5000 }, () => {
const opts = validateOptions({
packageSlug: "x",
rootUrl: "https://cdn.example/x/",
});
const uri = build("assets/y.wav", opts);
expect(uri).toBe("bundle://x/assets/y.wav");
const parsed = parse(uri, opts);
expect(parsed.key).toBe("assets/y.wav");
expect(parsed.meta.packageSlug).toBe("x");
});
it("build uses meta.packageSlug when supplied", { timeout: 5000 }, () => {
const opts = validateOptions({
packageSlug: "x",
rootUrl: "https://cdn.example/x/",
});
const uri = build("a", opts, { packageSlug: "y" });
expect(uri).toBe("bundle://y/a");
});
it("rewraps parseUri failure as INVALID_URI", { timeout: 5000 }, () => {
// empty input → parseUri throws → wrap as BUNDLE_URI.INVALID_URI
expectCode(() => parse("", OPTS), BUNDLE_URI_CODES.INVALID_URI);
});
it("accepts a URI with query string preserved as part of the key", {
timeout: 5000,
}, () => {
// parseUri strips the query before path, so the parsed key
// will not include the query. This documents the boundary.
const opts = validateOptions({
packageSlug: "x",
rootUrl: "https://cdn.example/x/",
});
const parsed = parse("bundle://x/a?token=t", opts);
expect(parsed.key).toBe("a");
});
});