mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 17:59:08 +00:00
fix(audit): keep examining a page loaded as a script as HTML
A script element naming an .html member promoted that member to a script, and the text pass then chose one reading. The page's own script elements were never read, so an inline map in it was lost and a shipped sourcesContent went unreported. Run the script reading and the HTML reading independently, and list the script target allowance among the cases that leave source map coverage undecided.
This commit is contained in:
3 files changed
+18
-4
No files matched your search
@@ -46,7 +46,7 @@ The audit composes existing readers. It does not reread Electron fuse bytes itse
|
||||
|
||||
`native.hardening`, `macho.hardened_runtime`, and `android.manifest` stay `not_assessed`. Their next probes are `inspect_binary_hardening`, `inspect_signature`, and `inspect_android_package`.
|
||||
|
||||
Source map text is counted and then dropped. A selected file is read through the stable artifact reader. Text and source-map members stay inside the allowances listed under `coverage.allowances`, and each kind has its own allowance, so binary members cannot use up the read that a later source map needs. Native images on disk are registered from inventory classification and are not limited to those allowances. Other files and package members are identified from a bounded header. A header with no NUL byte, or one that starts with a UTF-16 byte order mark, is scanned as text, so names such as `.env.production`, `server.key`, and `credentials` do not depend on an extension list. UTF-16 without a byte order mark has NUL bytes and is not recognized this way. A member already known to be text, such as a script, is also scanned when it is UTF-8 holding a stray NUL. One whose header is at least an eighth NUL bytes is taken to be wide text. It is not decoded and is counted as skipped, as is UTF-32. A binary plist is decoded, so its strings, dictionary keys and data are scanned in whichever encoding each object uses. One that cannot be decoded still has its bytes scanned and is counted as skipped. A file whose header could not be read leaves `text.credential_markers` undecided. Among binary package members, only a PE is read further, for its debug records. A script that an HTML file loads is examined as a script whatever it is called. One read before that HTML file, one that is not found, or one at a location the audit cannot place leaves `javascript.source_map_sources` undecided. Skipped members are listed under `coverage.omitted`. When the selection is a file, the parent directory is listed up to a fixed entry allowance for sibling `.pdb`, `.dSYM`, `.debug`, and linker `.map` debug companions.
|
||||
Source map text is counted and then dropped. A selected file is read through the stable artifact reader. Text and source-map members stay inside the allowances listed under `coverage.allowances`, and each kind has its own allowance, so binary members cannot use up the read that a later source map needs. Native images on disk are registered from inventory classification and are not limited to those allowances. Other files and package members are identified from a bounded header. A header with no NUL byte, or one that starts with a UTF-16 byte order mark, is scanned as text, so names such as `.env.production`, `server.key`, and `credentials` do not depend on an extension list. UTF-16 without a byte order mark has NUL bytes and is not recognized this way. A member already known to be text, such as a script, is also scanned when it is UTF-8 holding a stray NUL. One whose header is at least an eighth NUL bytes is taken to be wide text. It is not decoded and is counted as skipped, as is UTF-32. A binary plist is decoded, so its strings, dictionary keys and data are scanned in whichever encoding each object uses. One that cannot be decoded still has its bytes scanned and is counted as skipped. A file whose header could not be read leaves `text.credential_markers` undecided. Among binary package members, only a PE is read further, for its debug records. A script that an HTML file loads is examined as a script whatever it is called, and an HTML file loaded this way is still examined as HTML. One read before that HTML file, one that is not found, one at a location the audit cannot place, or one past the 1 MiB of resolved script paths the audit keeps (`MAX_RELEASE_EXPOSURE_SCRIPT_TARGET_BYTES`) leaves `javascript.source_map_sources` undecided. Skipped members are listed under `coverage.omitted`. When the selection is a file, the parent directory is listed up to a fixed entry allowance for sibling `.pdb`, `.dSYM`, `.debug`, and linker `.map` debug companions.
|
||||
|
||||
Inventory stops at 65,536 entries or 2 GiB of hashed bytes, and member coverage then stays `unknown`. A declared integrity value that disagrees with the bytes does not stop the audit, because signing an unpacked native module after packaging is routine. The contradiction is recorded in the limitations and the audit continues.
|
||||
|
||||
|
||||
@@ -2661,6 +2661,17 @@ describe("ReleaseExposureReader HTML script references", () => {
|
||||
expect(scanned.source_maps).toMatchObject({ embedded: 1, skipped: 0 });
|
||||
});
|
||||
|
||||
it("still reads a page another page loads as a script as HTML", async () => {
|
||||
const scanned = await inspect(
|
||||
await directory({
|
||||
"a.html": '<script src="z.html"></script>',
|
||||
"z.html": `<!doctype html><script>\nrun();\n${inlineMap}</script>`,
|
||||
}),
|
||||
);
|
||||
expect(scanned.source_maps.embedded).toBe(1);
|
||||
expect(sourceMapSourcesStatus(scanned)).toBe("exposed");
|
||||
});
|
||||
|
||||
it("keeps a script past the target allowance unassessed", async () => {
|
||||
const baseBytes = 64 * 1024;
|
||||
const references = Array.from(
|
||||
|
||||
@@ -1114,9 +1114,12 @@ const examineTextMember = async (
|
||||
scanText(state, member, loaded.bytes.length, text);
|
||||
if (holdsScripts(member)) {
|
||||
if (text === null) state.skippedMaps += 1;
|
||||
else if (isJavaScriptMember(member))
|
||||
examineInlineMaps(state, member, text.text, member.logical);
|
||||
else examineHtmlScripts(state, member, text.text);
|
||||
else {
|
||||
if (isJavaScriptMember(member))
|
||||
examineInlineMaps(state, member, text.text, member.logical);
|
||||
// A page another page loads as a script still ships as a page.
|
||||
if (isHtmlMember(member)) examineHtmlScripts(state, member, text.text);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The allowance bounds the read, so a read that yielded no text still spends it.
|
||||
|
||||
Reference in new issue
Block a user