fix(audit): keep coverage unknown for nested ASARs in package hosts

A member of a PyInstaller or .NET host reaches the binary probe as a
plain file. An ASAR among them has a NUL-bearing header, so the probe
dropped it with no traversal and no omission, and source map checks could
read not_observed with complete coverage.

Ask the ASAR owner's signature probe, and record a match as a nested
package that was not expanded, as inventory-classified ones already are.
This commit is contained in:
Matt Van Horn committed 2026-11-02 00:48:49 -08:00
1 parent 23fff0a227
commit c50d0ed1a0
2 files changed
+48 -3

No files matched your search

@@ -1260,6 +1260,34 @@ describe("ReleaseExposureReader member allowances", () => {
});
});
describe("ReleaseExposureReader nested packages in a host", () => {
it("leaves coverage unknown for an ASAR a PyInstaller host carries", async () => {
const root = await createTestTempDirectory("rea-release-exposure-nest-");
await writeFile(
join(root, "app.exe"),
cArchiveExecutable({
carrier: "pe",
layout: "2.1",
pythonVersion: 310,
members: [
{
name: "resources",
typeCode: "x",
compress: true,
payload: asarArchive([
{ name: "app.js.map", bytes: Buffer.from("{}") },
]),
},
],
}),
);
const scanned = await inspect(root);
expect(scanned.coverage).toBe("unknown");
expect(scanned.extractable.formats).toContain("asar");
expect(scanned.limitations.join("\n")).toContain("was not expanded");
});
});
describe("ReleaseExposureReader source-map count", () => {
it.each([
[
+20 -3
View File
@@ -27,6 +27,7 @@ import {
} from "./ArtifactFileOperation.js";
import { decodeBinaryPlistText } from "./apple/BinaryPlistMaterializer.js";
import { ArtifactEmbeddedFormatProbes } from "./ArtifactEmbeddedFormatProbes.js";
import { createAsarHeaderSignatureProbe } from "./AsarHeader.js";
import { ArtifactTraversalObservation } from "./ArtifactTraversalObservation.js";
import { classifyEmbeddedExecutablePackage } from "./inventory/classify.js";
import { inventoryArtifact } from "./inventory/ArtifactInventory.js";
@@ -546,13 +547,11 @@ const scanOccurrences = async (
packageKind !== null &&
!parentsWithChildren.has(occurrence.occurrence_id)
) {
state.formats.add(packageKind);
noteUnexaminedPackage(state);
noteNestedPackage(state, packageKind, location);
if (!occurrence.encrypted) {
state.readable += 1;
pushSample(state.memberSamples, location);
}
pushLimitation(state, `Nested package ${location} was not expanded.`);
continue;
}
await examineMember(state, {
@@ -1491,6 +1490,10 @@ const examinePackagedBinary = async (
notePeNotExamined(state, member.location);
}
}
if (native === null && header !== null && holdsAsarHeader(header)) {
noteNestedPackage(state, "asar", member.location);
return true;
}
if (native !== null) state.packagedNative += 1;
else await examineProbedText(state, member, probe);
if (native !== "pe") return true;
@@ -1841,6 +1844,20 @@ const noteUnexaminedPackage = (state: ScanState): void => {
state.coverageUnknown = true;
};
const noteNestedPackage = (
state: ScanState,
format: PackageFormat,
location: string,
): void => {
state.formats.add(format);
noteUnexaminedPackage(state);
pushLimitation(state, `Nested package ${location} was not expanded.`);
};
/** The ASAR owner's signature test. A match does not establish a valid archive. */
const holdsAsarHeader = (bytes: Buffer): boolean =>
createAsarHeaderSignatureProbe().write(bytes).state === "matched";
const readListedEntry = async (
container: OpenContainer | undefined,
entry: ArtifactEntry,