fix(source-map): clarify policy and evidence provenance (#7828)
This commit is contained in:
1 parent
aba158506b
commit
aaf4d21068
6 files changed
+77
-12
No files matched your search
@@ -101,8 +101,11 @@ run token while process release is unconfirmed, and the directory until removal
|
||||
succeeds. A later successful retry settles every error bound to that command;
|
||||
historical failure diagnostics remain available.
|
||||
|
||||
Complete evidence is bounded by a 4 MiB map, 32 MiB codec reply, 262144 decoded
|
||||
rows/segments and 64 nested section levels. Those structural budgets and the
|
||||
Complete evidence is bounded by a 4 MiB map, 32 MiB codec reply and a shared
|
||||
262144-record decoder budget. Generated rows, mapping segments, leaf maps,
|
||||
source and name declarations, `sourcesContent` entries and indexed sections all
|
||||
consume this budget. Generated rows and mapping segments also each have a
|
||||
262144 limit; indexed maps allow at most 64 nested section levels. Those budgets and the
|
||||
192 MiB codec heap have no runtime override. Exceeding a budget returns an explicit
|
||||
failure, without partial results. The existing 32 MiB manifest and 16 MiB selected
|
||||
script boundaries also apply. No global toolchain or browser configuration changes
|
||||
|
||||
@@ -60,6 +60,23 @@ it("passes an explicit decode deadline and omits the default from Evidence", asy
|
||||
expect(seen).toEqual([250, undefined]);
|
||||
});
|
||||
|
||||
it("reports shared decoder record costs in result and Evidence limitations", async () => {
|
||||
const response = await service().trace(args);
|
||||
if (!response.ok) throw response.error;
|
||||
const result = webSourceLocationResultSchema.parse(
|
||||
response.value.normalized_result,
|
||||
);
|
||||
for (const limitations of [response.value.limitations, result.limitations]) {
|
||||
expect(limitations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.stringContaining(
|
||||
"shared decoder budget for generated rows, mapping segments, leaf maps, source and name declarations, sourcesContent entries, and indexed sections",
|
||||
),
|
||||
]),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ ...args, manifest_path: "relative.json" },
|
||||
{ ...args, script_index: -1 },
|
||||
|
||||
@@ -50,13 +50,13 @@ const artifactsSchema = selectedWebScriptArtifactsSchema.extend({
|
||||
const LIMITATIONS = [
|
||||
"The source/map pairing and source-map URL context are caller-selected. Deployment authenticity and execution remain unknown.",
|
||||
"Capture metadata and its Evidence reference are historical context; this trace independently verifies only the selected manifest, script and map bytes.",
|
||||
"Source names and sourceRoot are reported declarations. Resolved URLs are derived by the pinned upstream codec; no original source is fetched or executed.",
|
||||
"Source names and sourceRoot are reported declarations. REA derives resolved URLs using WHATWG URL resolution; the pinned upstream codec decodes mappings. No original source is fetched or executed.",
|
||||
"Embedded source hashes identify the UTF-8 encoding of decoded sourcesContent text, not an independently retained original file. Missing or out-of-range original content positions remain explicit.",
|
||||
"Lookup returns all segments at the greatest generated position not greater than the selected point, including earlier lines and generated-only segments. A mapped point does not establish an executed range or UI causality.",
|
||||
"Regular maps and inline indexed sections are supported. External section URLs are unsupported; unknown extensions are retained in raw map text and are not interpreted.",
|
||||
];
|
||||
const codecBudget = (timeoutMs: number): string =>
|
||||
`Complete evidence has a 4 MiB map, 32 MiB reply, 262144 decoded rows/segments and 64-level section budget. The owned codec has a ${String(timeoutMs)} ms decode_timeout_ms deadline`;
|
||||
`Complete evidence has a 4 MiB map, 32 MiB reply and a ${String(WEB_SOURCE_MAP_LIMITS.decodedRecords)}-record shared decoder budget for generated rows, mapping segments, leaf maps, source and name declarations, sourcesContent entries, and indexed sections. Generated rows and mapping segments have separate ${String(WEB_SOURCE_MAP_LIMITS.decodedRows)} and ${String(WEB_SOURCE_MAP_LIMITS.decodedSegments)} limits, and indexed maps have a ${String(WEB_SOURCE_MAP_LIMITS.sectionDepth)}-level section budget. The owned codec has a ${String(timeoutMs)} ms decode_timeout_ms deadline`;
|
||||
/** Bun accepts but ignores the codec's V8 heap flags; Windows enforces a commit limit instead. */
|
||||
const codecBudgetLimitation = (
|
||||
runtime: WebSourceMapReport["runtime"],
|
||||
|
||||
@@ -331,7 +331,7 @@ const fetchOne = async (
|
||||
return emptySourceMapItem(
|
||||
request,
|
||||
"policy_filtered",
|
||||
"Declared source-map URL is outside the approved exact origins.",
|
||||
"Declared source-map URL must be a credential-free HTTP(S) URL on an approved exact origin.",
|
||||
);
|
||||
let effectiveUrl: string | null = null;
|
||||
let completedResponseIdentity: ResponseIdentity | undefined;
|
||||
@@ -349,7 +349,7 @@ const fetchOne = async (
|
||||
return emptySourceMapItem(
|
||||
request,
|
||||
"policy_filtered",
|
||||
"A source-map redirect left the approved exact origins.",
|
||||
"Each source-map redirect must be a credential-free HTTP(S) URL on an approved exact origin.",
|
||||
effectiveUrl,
|
||||
);
|
||||
const { response, fetchedUrl } = fetched;
|
||||
|
||||
@@ -18,7 +18,15 @@ cliTest(
|
||||
"accepts numeric UTF-16 coordinates and runs the actual owned source-map codec without a browser",
|
||||
async ({ cli }) => {
|
||||
const root = await createTestTempDirectory("rea-source-map-cli-");
|
||||
const fixture = webSourceLocationFixture();
|
||||
const fixture = webSourceLocationFixture(
|
||||
JSON.stringify({
|
||||
version: 3,
|
||||
sources: ["dir/original.ts", "dir//original.ts"],
|
||||
sourcesContent: ["original", "second"],
|
||||
names: [],
|
||||
mappings: "AAAA,ACAA",
|
||||
}),
|
||||
);
|
||||
await mkdir(join(root, "files", "modules"), { recursive: true });
|
||||
await writeFile(join(root, "files", "modules", "main.js"), fixture.source);
|
||||
let manifest = join(root, "manifest.json").replaceAll("\\", "/");
|
||||
@@ -63,12 +71,29 @@ cliTest(
|
||||
source_map: { association: "caller-selected" },
|
||||
generated_offset: 0,
|
||||
execution: "unknown",
|
||||
matches: [{ content: { text: "original" } }],
|
||||
matches: [
|
||||
{
|
||||
source_index: 0,
|
||||
resolved_url: "https://app.test/dir/original.ts",
|
||||
content: { text: "original" },
|
||||
},
|
||||
{
|
||||
source_index: 1,
|
||||
resolved_url: "https://app.test/dir//original.ts",
|
||||
content: { text: "second" },
|
||||
},
|
||||
],
|
||||
runtime: {
|
||||
id: process.versions.bun === undefined ? "node-v8" : "bun",
|
||||
},
|
||||
},
|
||||
});
|
||||
const limitation =
|
||||
"Source names and sourceRoot are reported declarations. REA derives resolved URLs using WHATWG URL resolution; the pinned upstream codec decodes mappings. No original source is fetched or executed.";
|
||||
expect(response.json).toMatchObject({
|
||||
limitations: expect.arrayContaining([limitation]),
|
||||
normalized_result: { limitations: expect.arrayContaining([limitation]) },
|
||||
});
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [
|
||||
|
||||
@@ -551,7 +551,10 @@ describe("web source-map fetching and validation: fetching maps and following re
|
||||
expectInvalidIncludedSourceMaps(result);
|
||||
});
|
||||
|
||||
it("reauthorizes every redirect and never contacts a disallowed origin", async () => {
|
||||
it.each([
|
||||
["another origin", "https://private.example.test/map"],
|
||||
["same-origin credentials", "https://user:[email protected]/map"],
|
||||
])("reauthorizes a redirect with %s", async (_case, location) => {
|
||||
const calls: string[] = [];
|
||||
const result = await fetchWebSourceMapsOk([request], input(), undefined, {
|
||||
fetch: (url) => {
|
||||
@@ -559,23 +562,33 @@ describe("web source-map fetching and validation: fetching maps and following re
|
||||
return Promise.resolve(
|
||||
new Response(null, {
|
||||
status: 302,
|
||||
headers: { location: "https://private.example.test/map" },
|
||||
headers: { location },
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
expect(calls).toEqual([request.fetchUrl]);
|
||||
expect(JSON.stringify(result)).not.toContain("user:secret");
|
||||
expect(result).toMatchObject({
|
||||
status: "unavailable",
|
||||
items: [{ status: "policy_filtered", effective_url: request.fetchUrl }],
|
||||
items: [
|
||||
{
|
||||
status: "policy_filtered",
|
||||
effective_url: request.fetchUrl,
|
||||
limitation:
|
||||
"Each source-map redirect must be a credential-free HTTP(S) URL on an approved exact origin.",
|
||||
},
|
||||
],
|
||||
});
|
||||
expectInvalidSourceMaps({
|
||||
...result,
|
||||
items: [{ ...result.items[0], limitation: null }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("source-map declared URL policy", () => {
|
||||
it.each([
|
||||
["embedded credentials", "https://user:[email protected]/a.map"],
|
||||
["a different scheme on the same host", "http://app.example.test/a.map"],
|
||||
@@ -602,7 +615,14 @@ describe("web source-map fetching and validation: fetching maps and following re
|
||||
expect(calls).toEqual([]);
|
||||
expect(result).toMatchObject({
|
||||
status: "unavailable",
|
||||
items: [{ status: "policy_filtered", effective_url: null }],
|
||||
items: [
|
||||
{
|
||||
status: "policy_filtered",
|
||||
effective_url: null,
|
||||
limitation:
|
||||
"Declared source-map URL must be a credential-free HTTP(S) URL on an approved exact origin.",
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user