From 39179db68d002a2ba4d3f90321b9905d44eb0707 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Tue, 27 Oct 2026 09:05:18 +0800 Subject: [PATCH] refactor(artifacts): unify inventory integrity policy --- .../javascript/JavaScriptArtifactReconstruction.ts | 2 +- src/artifacts/ArtifactProvider.ts | 2 +- src/artifacts/extraction/ArtifactExtraction.ts | 2 +- src/artifacts/inventory/scanCanonical.ts | 8 ++------ src/artifacts/inventory/scanReader.ts | 11 ++++------- src/artifacts/inventory/types.ts | 12 ++---------- .../filesystem/asarInventoryContainment.test.ts | 2 +- .../filesystem/asarSelectedCompanion.test.ts | 2 +- 8 files changed, 13 insertions(+), 28 deletions(-) diff --git a/src/application/javascript/JavaScriptArtifactReconstruction.ts b/src/application/javascript/JavaScriptArtifactReconstruction.ts index 3d4e2fc4f1..b035acfea6 100644 --- a/src/application/javascript/JavaScriptArtifactReconstruction.ts +++ b/src/application/javascript/JavaScriptArtifactReconstruction.ts @@ -112,7 +112,7 @@ export const reconstructJavaScriptArtifact = async ( { resourceScope: scope, signal, - integrity: { mode: input.integrity_policy }, + integrity: input.integrity_policy, asarHeaderAdmission, }, undefined, diff --git a/src/artifacts/ArtifactProvider.ts b/src/artifacts/ArtifactProvider.ts index ca5ac153fd..0095d39e09 100644 --- a/src/artifacts/ArtifactProvider.ts +++ b/src/artifacts/ArtifactProvider.ts @@ -522,7 +522,7 @@ class ArtifactClient implements AnalysisClient { this.settings, ), ...(options?.signal === undefined ? {} : { signal: options.signal }), - integrity: { mode: parsed.integrity_policy }, + integrity: parsed.integrity_policy, }); } } diff --git a/src/artifacts/extraction/ArtifactExtraction.ts b/src/artifacts/extraction/ArtifactExtraction.ts index 4a68ff81c6..ab36f69d22 100644 --- a/src/artifacts/extraction/ArtifactExtraction.ts +++ b/src/artifacts/extraction/ArtifactExtraction.ts @@ -99,7 +99,7 @@ const extractArtifactInScope = async ( asarHeaderAdmission: input.asarHeaderAdmission ?? resolveAsarHeaderAdmission(input.environment), - integrity: { mode: input.integrityPolicy }, + integrity: input.integrityPolicy, }, admission.probeFacts, ); diff --git a/src/artifacts/inventory/scanCanonical.ts b/src/artifacts/inventory/scanCanonical.ts index 4f6cb5ec1b..e78979130c 100644 --- a/src/artifacts/inventory/scanCanonical.ts +++ b/src/artifacts/inventory/scanCanonical.ts @@ -47,10 +47,7 @@ import { scanReader, type PendingIntegrityContradiction, } from "./scanReader.js"; -import { - STRICT_INTEGRITY_POLICY, - type ArtifactInventoryOptions, -} from "./types.js"; +import type { ArtifactInventoryOptions } from "./types.js"; export interface TransferredArtifactInventorySource { readonly owner: ArtifactResourceOwner; @@ -104,7 +101,6 @@ const scanCanonical = async ( readonly snapshot: ArtifactInventorySnapshot; readonly transferredSource: TransferredArtifactInventorySource | undefined; }> => { - const integrity = options.integrity ?? STRICT_INTEGRITY_POLICY; const metadata = await lstat(path); const { format: rootFormat, @@ -149,7 +145,7 @@ const scanCanonical = async ( reader, ownedReaders, options.signal, - integrity, + options.integrity, options.asarHeaderAdmission, ); outcome = { diff --git a/src/artifacts/inventory/scanReader.ts b/src/artifacts/inventory/scanReader.ts index a28db8381f..7117430469 100644 --- a/src/artifacts/inventory/scanReader.ts +++ b/src/artifacts/inventory/scanReader.ts @@ -31,10 +31,7 @@ import { resolveOccurrenceParents, type MutableOccurrence, } from "./ArtifactGraphConstruction.js"; -import { - STRICT_INTEGRITY_POLICY, - type ArtifactIntegrityPolicy, -} from "./types.js"; +import type { ArtifactIntegrityPolicyName } from "../../domain/artifactIntegrityPolicy.js"; import { abortIfNeeded, hashReadable } from "../ArtifactHash.js"; export interface PendingIntegrityContradiction { @@ -56,7 +53,7 @@ const emptyScan = (): { interface ScanContext { readonly reader: ArtifactReader; readonly signal: AbortSignal | undefined; - readonly integrity: ArtifactIntegrityPolicy; + readonly integrity: ArtifactIntegrityPolicyName; readonly asarHeaderAdmission: AsarHeaderAdmission | undefined; readonly nodes: Map; readonly occurrences: MutableOccurrence[]; @@ -72,7 +69,7 @@ export const scanReader = async ( reader: ArtifactReader | undefined, ownedReaders: ArtifactReader[], signal?: AbortSignal, - integrity: ArtifactIntegrityPolicy = STRICT_INTEGRITY_POLICY, + integrity: ArtifactIntegrityPolicyName = "fail", asarHeaderAdmission?: AsarHeaderAdmission, ): Promise<{ readonly nodes: Map; @@ -327,7 +324,7 @@ const digestArtifactEntry = async ( ); const mismatched = claim !== null && claim.digest !== observed; if (mismatched && claim !== null && observed !== undefined) { - if (context.integrity.mode === "fail") + if (context.integrity === "fail") throw new ArtifactReaderFailure( "integrity", `Artifact integrity metadata disagrees with content: ${logicalPath}`, diff --git a/src/artifacts/inventory/types.ts b/src/artifacts/inventory/types.ts index 659cf43d74..b9fa6a2e11 100644 --- a/src/artifacts/inventory/types.ts +++ b/src/artifacts/inventory/types.ts @@ -1,15 +1,7 @@ +import type { ArtifactIntegrityPolicyName } from "../../domain/artifactIntegrityPolicy.js"; import type { AsarHeaderAdmission } from "../../config/asarHeaderAdmission.js"; import type { ArtifactResourceScope } from "../ArtifactResourceScope.js"; -/** Resolved integrity behavior admitted to the artifact scanner. */ -export type ArtifactIntegrityPolicy = - | { readonly mode: "fail" } - | { readonly mode: "record-and-continue" }; - -export const STRICT_INTEGRITY_POLICY: ArtifactIntegrityPolicy = { - mode: "fail", -}; - /** Options shared by artifact inventory scans. */ export interface ArtifactInventoryOptions { readonly resourceScope: ArtifactResourceScope; @@ -18,7 +10,7 @@ export interface ArtifactInventoryOptions { /** Independently selected root spelling used only for ASAR companion lookup. */ readonly selectedPath?: string | undefined; readonly signal?: AbortSignal | undefined; - readonly integrity?: ArtifactIntegrityPolicy | undefined; + readonly integrity?: ArtifactIntegrityPolicyName | undefined; readonly environment?: Readonly | undefined; /** ASAR header/index admission resolved from the owner's environment; default when absent. */ readonly asarHeaderAdmission?: AsarHeaderAdmission | undefined; diff --git a/tests/boundary/filesystem/asarInventoryContainment.test.ts b/tests/boundary/filesystem/asarInventoryContainment.test.ts index 5d6d323eb4..c720c03946 100644 --- a/tests/boundary/filesystem/asarInventoryContainment.test.ts +++ b/tests/boundary/filesystem/asarInventoryContainment.test.ts @@ -192,7 +192,7 @@ describe("expanded ASAR inventory containment", () => { reason: "integrity", }); const observed = await inventoryArtifact(application, { - integrity: { mode: "record-and-continue" }, + integrity: "record-and-continue", }); const container = artifactOccurrenceAt(observed, "resources/app.asar"); const child = artifactOccurrenceAt(observed, "resources/app.asar/main.js"); diff --git a/tests/boundary/filesystem/asarSelectedCompanion.test.ts b/tests/boundary/filesystem/asarSelectedCompanion.test.ts index cde62c9dd3..e1bbb31f08 100644 --- a/tests/boundary/filesystem/asarSelectedCompanion.test.ts +++ b/tests/boundary/filesystem/asarSelectedCompanion.test.ts @@ -98,7 +98,7 @@ it.skipIf(process.platform === "win32")( expect(extractFile(selected, "native.node").equals(bytes)).toBe(true); const selectedInventory = await inventoryArtifact(selected); const canonicalInventory = await inventoryArtifact(canonical, { - integrity: { mode: "record-and-continue" }, + integrity: "record-and-continue", }); expect(selectedInventory.manifest.root_sha256).toBe( canonicalInventory.manifest.root_sha256,