mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 17:59:08 +00:00
fix(audit): bound the paths kept for local source map targets
noteMapTarget kept every declared target resolved against its member's directory, so a short URL under a long directory could retain far more than the script's own size, with no cumulative limit. Add the allowance noteScriptTarget already has. A target past it stays unassessed and counts as a skipped map, and is never read.
This commit is contained in:
3 files changed
+51
-2
No files matched your search
@@ -34,6 +34,7 @@ import {
|
||||
MAX_RELEASE_EXPOSURE_INVENTORY_ENTRIES,
|
||||
MAX_RELEASE_EXPOSURE_MEMBER_PROBE_BYTES,
|
||||
MAX_RELEASE_EXPOSURE_PACKAGED_PE_BYTES,
|
||||
MAX_RELEASE_EXPOSURE_MAP_TARGET_BYTES,
|
||||
MAX_RELEASE_EXPOSURE_SCRIPT_TARGET_BYTES,
|
||||
MAX_RELEASE_EXPOSURE_SOURCE_MAP_BYTES,
|
||||
MAX_RELEASE_EXPOSURE_SOURCE_MAPS,
|
||||
@@ -2733,6 +2734,37 @@ describe("ReleaseExposureReader inline map reads", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("ReleaseExposureReader map target allowance", () => {
|
||||
it("keeps a map past the target allowance unassessed", async () => {
|
||||
const root = await createTestTempDirectory("rea-release-exposure-maps-");
|
||||
const folder = join(
|
||||
root,
|
||||
...Array.from({ length: 4 }, () => "d".repeat(200)),
|
||||
);
|
||||
await mkdir(folder, { recursive: true });
|
||||
const declarations = Array.from(
|
||||
{ length: Math.ceil(MAX_RELEASE_EXPOSURE_MAP_TARGET_BYTES / 800) + 1 },
|
||||
(_, index) => `//# sourceMappingURL=m${String(index)}`,
|
||||
);
|
||||
await writeFile(
|
||||
join(folder, "a.js"),
|
||||
`${declarations.join("\n")}\n//# sourceMappingURL=z\n`,
|
||||
);
|
||||
await writeFile(
|
||||
join(folder, "z"),
|
||||
JSON.stringify({
|
||||
version: 3,
|
||||
sources: ["s.ts"],
|
||||
sourcesContent: ["x"],
|
||||
mappings: "",
|
||||
}),
|
||||
);
|
||||
const scanned = await inspect(root);
|
||||
expect(scanned.source_maps.embedded).toBe(0);
|
||||
expect(sourceMapSourcesStatus(scanned)).toBe("unknown");
|
||||
});
|
||||
});
|
||||
|
||||
describe("ReleaseExposureReader HTML script references", () => {
|
||||
const inlineMap = `//# sourceMappingURL=data:application/json;base64,${Buffer.from(
|
||||
JSON.stringify({
|
||||
|
||||
@@ -78,6 +78,7 @@ import {
|
||||
MAX_RELEASE_EXPOSURE_PACKAGED_PE_BYTES,
|
||||
MAX_RELEASE_EXPOSURE_PACKAGED_PE_READS,
|
||||
MAX_RELEASE_EXPOSURE_SAMPLES,
|
||||
MAX_RELEASE_EXPOSURE_MAP_TARGET_BYTES,
|
||||
MAX_RELEASE_EXPOSURE_SCRIPT_TARGET_BYTES,
|
||||
MAX_RELEASE_EXPOSURE_SOURCE_MAP_BYTES,
|
||||
MAX_RELEASE_EXPOSURE_SOURCE_MAPS,
|
||||
@@ -753,6 +754,7 @@ interface ScanState {
|
||||
scriptTargets: Set<string>;
|
||||
/** Bytes of resolved path offered to `scriptTargets`, kept or refused. */
|
||||
scriptTargetBytes: number;
|
||||
mapTargetBytes: number;
|
||||
/** Members already examined as a script. */
|
||||
examinedScripts: Set<string>;
|
||||
visitText: ReleaseExposureTextVisitor;
|
||||
@@ -795,6 +797,7 @@ const createState = (visitText: ReleaseExposureTextVisitor): ScanState => ({
|
||||
examinedMaps: new Set(),
|
||||
scriptTargets: new Set(),
|
||||
scriptTargetBytes: 0,
|
||||
mapTargetBytes: 0,
|
||||
examinedScripts: new Set(),
|
||||
visitText,
|
||||
});
|
||||
@@ -1362,10 +1365,21 @@ const noteLocalMapReference = (
|
||||
noteMapTarget(state, resolve(dirname(member.location), decoded));
|
||||
};
|
||||
|
||||
/**
|
||||
* A short URL is expanded by the declaring member's directory, so declared
|
||||
* paths can outgrow their text many times over. A target past the allowance
|
||||
* stays unassessed.
|
||||
*/
|
||||
const noteMapTarget = (state: ScanState, location: string): void => {
|
||||
const target = memberLocationKey(location);
|
||||
if (state.examinedMaps.has(target)) return;
|
||||
if (state.examinedLocations.has(target)) state.skippedMaps += 1;
|
||||
if (state.examinedMaps.has(target) || state.mapTargets.has(target)) return;
|
||||
if (state.examinedLocations.has(target)) {
|
||||
state.skippedMaps += 1;
|
||||
return;
|
||||
}
|
||||
state.mapTargetBytes += Buffer.byteLength(target);
|
||||
if (state.mapTargetBytes > MAX_RELEASE_EXPOSURE_MAP_TARGET_BYTES)
|
||||
state.skippedMaps += 1;
|
||||
else state.mapTargets.add(target);
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ export const MAX_RELEASE_EXPOSURE_TEXT_MEMBER_BYTES = 256 * 1024;
|
||||
export const MAX_RELEASE_EXPOSURE_TEXT_BYTES = 4 * 1024 * 1024;
|
||||
/** Resolved paths retained for HTML script targets across one audit. */
|
||||
export const MAX_RELEASE_EXPOSURE_SCRIPT_TARGET_BYTES = 1024 * 1024;
|
||||
/** Resolved paths retained for local source map targets across one audit. */
|
||||
export const MAX_RELEASE_EXPOSURE_MAP_TARGET_BYTES = 1024 * 1024;
|
||||
/** Source maps read from one artifact. */
|
||||
export const MAX_RELEASE_EXPOSURE_SOURCE_MAPS = 32;
|
||||
/** Bytes read from one source map. */
|
||||
@@ -43,6 +45,7 @@ export const RELEASE_EXPOSURE_ALLOWANCES = [
|
||||
`Selected file reads stop at ${String(MAX_RELEASE_EXPOSURE_FILE_BYTES)} bytes.`,
|
||||
`Text member scans read at most ${String(MAX_RELEASE_EXPOSURE_TEXT_MEMBER_BYTES)} bytes each and ${String(MAX_RELEASE_EXPOSURE_TEXT_BYTES)} bytes in total.`,
|
||||
`HTML script targets keep at most ${String(MAX_RELEASE_EXPOSURE_SCRIPT_TARGET_BYTES)} bytes of resolved paths. Later targets stay unassessed.`,
|
||||
`Local source map targets keep at most ${String(MAX_RELEASE_EXPOSURE_MAP_TARGET_BYTES)} bytes of resolved paths. Later targets stay unassessed.`,
|
||||
`Source map scans read at most ${String(MAX_RELEASE_EXPOSURE_SOURCE_MAPS)} maps and ${String(MAX_RELEASE_EXPOSURE_SOURCE_MAP_BYTES)} bytes each. sourcesContent text is counted and then dropped.`,
|
||||
`Other files and package members are identified from their first ${String(MAX_RELEASE_EXPOSURE_MEMBER_PROBE_BYTES)} bytes, and those without a NUL byte in that prefix are scanned as text. Up to ${String(MAX_RELEASE_EXPOSURE_PACKAGED_PE_READS)} packaged PE images are read for debug records, ${String(MAX_RELEASE_EXPOSURE_PACKAGED_PE_BYTES)} bytes each.`,
|
||||
`Go, Rust, and Electron fuse readers run on at most ${String(MAX_RELEASE_EXPOSURE_NATIVE_IMAGES)} native images.`,
|
||||
|
||||
Reference in new issue
Block a user