A captured script whose bytes matched no static digest was reported as high-confidence unmatched even when no automatic or caller-declared mapping applied to its location in any static layer. The matcher now computes mapping presence once while locating candidates and shares one no-candidate classification between the captured-source and source-free branches, so an unmapped location stays unknown with no-authorized-location-mapping and is not counted as unmatched. Fixes #6061. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
433 lines
14 KiB
TypeScript
433 lines
14 KiB
TypeScript
import { writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { expect, it } from "vitest";
|
|
|
|
import {
|
|
parseRuntimeReconciliationInput,
|
|
reconcileJavaScriptRuntime,
|
|
} from "../../../../src/domain/javascript/javascriptRuntimeReconciliation.js";
|
|
import { reconcileRuntimeEntities } from "../../../../src/domain/javascript/javascriptRuntimeReconciliationMatching.js";
|
|
import { classifyStaticLoadStates } from "../../../../src/domain/javascript/javascriptRuntimeLoadState.js";
|
|
import { projectRuntimeCaptures } from "../../../../src/domain/javascript/javascriptRuntimeReconciliationRuntime.js";
|
|
import { reconcileJavaScriptRuntimeInputSchema } from "../../../../src/domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
|
import { collectStaticRuntimeCandidates } from "../../../../src/domain/javascript/javascriptRuntimeStaticCandidates.js";
|
|
import {
|
|
SOURCE,
|
|
analyzeFixture,
|
|
applicationFixture,
|
|
electronRuntimeEvidence,
|
|
} from "../../../fixtures/javascriptRuntimeReconciliation.js";
|
|
import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js";
|
|
|
|
const CAPTURED = "export const observed = 'different';\n";
|
|
|
|
const collisionInput = async (
|
|
alternatives: number,
|
|
explicitMapping: boolean,
|
|
) => {
|
|
const root = await applicationFixture();
|
|
if (alternatives > 0) await writeFile(join(root, "other.js"), CAPTURED);
|
|
const live = explicitMapping
|
|
? await createTestTempDirectory("rea-runtime-content-live-")
|
|
: root;
|
|
const extra = await createTestTempDirectory("rea-runtime-content-extra-");
|
|
await writeFile(join(extra, "app.js"), CAPTURED);
|
|
return parseRuntimeReconciliationInput(
|
|
reconcileJavaScriptRuntimeInputSchema.parse({
|
|
static_layers: [
|
|
{
|
|
role: "application",
|
|
analysis: await analyzeFixture(root),
|
|
runtime_mappings: explicitMapping
|
|
? [{ kind: "file-root", root: live, artifact_prefix: "" }]
|
|
: [],
|
|
},
|
|
...(alternatives > 1
|
|
? [{ role: "assets", analysis: await analyzeFixture(extra) }]
|
|
: []),
|
|
],
|
|
runtime_observations: [
|
|
electronRuntimeEvidence(live, CAPTURED, { includeWorker: false }),
|
|
],
|
|
}),
|
|
);
|
|
};
|
|
|
|
it.each([
|
|
[0, false],
|
|
[1, false],
|
|
[2, false],
|
|
[0, true],
|
|
[1, true],
|
|
[2, true],
|
|
] as const)(
|
|
"preserves mapped disagreement with %i other content matches (explicit mapping: %s)",
|
|
async (alternatives, explicitMapping) => {
|
|
const parsed = await collisionInput(alternatives, explicitMapping);
|
|
const result = reconcileJavaScriptRuntime(parsed);
|
|
const script = result.reconciliations.find(
|
|
({ entity_kind }) => entity_kind === "script",
|
|
);
|
|
expect(script).toMatchObject({
|
|
status: "unmatched",
|
|
basis: explicitMapping ? "operator-file-mapping" : "artifact-path",
|
|
confidence: "high",
|
|
reason: "captured-content-disagrees-with-static-location",
|
|
static_layer_id: null,
|
|
static_node_id: null,
|
|
candidate_static_count: 1,
|
|
});
|
|
const candidate = script?.candidate_static_nodes[0];
|
|
const selected = result.graph.nodes.find(
|
|
({ node_id }) => node_id === candidate?.static_node_id,
|
|
);
|
|
expect(selected?.observations).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
properties: expect.objectContaining({ path: "app.js" }),
|
|
}),
|
|
]),
|
|
);
|
|
expect(
|
|
result.graph.edges.some(
|
|
(edge) =>
|
|
edge.relation === "observed_as" &&
|
|
edge.target_node_id === script?.runtime_node_id,
|
|
),
|
|
).toBe(false);
|
|
},
|
|
);
|
|
|
|
it("restricts content-and-location ambiguity to mapped candidates", async () => {
|
|
const application = await applicationFixture();
|
|
const cache = await applicationFixture();
|
|
const other = await applicationFixture();
|
|
const live = await createTestTempDirectory("rea-runtime-content-mapped-");
|
|
const analysis = await analyzeFixture(application);
|
|
const mapping = [{ kind: "file-root", root: live, artifact_prefix: "" }];
|
|
const parsed = parseRuntimeReconciliationInput(
|
|
reconcileJavaScriptRuntimeInputSchema.parse({
|
|
static_layers: [
|
|
{ role: "application", analysis, runtime_mappings: mapping },
|
|
{
|
|
role: "cache",
|
|
analysis: await analyzeFixture(cache),
|
|
runtime_mappings: mapping,
|
|
},
|
|
{ role: "assets", analysis: await analyzeFixture(other) },
|
|
],
|
|
runtime_observations: [
|
|
electronRuntimeEvidence(live, SOURCE, { includeWorker: false }),
|
|
],
|
|
}),
|
|
);
|
|
const result = reconcileJavaScriptRuntime(parsed);
|
|
const script = result.reconciliations.find(
|
|
({ entity_kind }) => entity_kind === "script",
|
|
);
|
|
expect(script).toMatchObject({
|
|
status: "ambiguous",
|
|
reason: "ambiguous-static-candidates",
|
|
candidate_static_count: 2,
|
|
});
|
|
const unmapped = result.static_layers.find(({ role }) => role === "assets");
|
|
expect(
|
|
script?.candidate_static_nodes.map(
|
|
({ static_layer_id }) => static_layer_id,
|
|
),
|
|
).not.toContain(unmapped?.layer_id);
|
|
});
|
|
|
|
it.each([false, true])(
|
|
"retains digest-only matching without a mapped candidate (mapping: %s)",
|
|
async (mapped) => {
|
|
const root = await applicationFixture();
|
|
await writeFile(join(root, "other.js"), CAPTURED);
|
|
const live = await createTestTempDirectory(
|
|
"rea-runtime-content-unlocated-",
|
|
);
|
|
const result = reconcileJavaScriptRuntime(
|
|
parseRuntimeReconciliationInput(
|
|
reconcileJavaScriptRuntimeInputSchema.parse({
|
|
static_layers: [
|
|
{
|
|
role: "application",
|
|
analysis: await analyzeFixture(root),
|
|
runtime_mappings: mapped
|
|
? [{ kind: "file-root", root: live, artifact_prefix: "" }]
|
|
: [],
|
|
},
|
|
],
|
|
runtime_observations: [
|
|
electronRuntimeEvidence(live, CAPTURED, {
|
|
scriptFile: "absent.js",
|
|
includeWorker: false,
|
|
}),
|
|
],
|
|
}),
|
|
),
|
|
);
|
|
expect(
|
|
result.reconciliations.find(
|
|
({ entity_kind }) => entity_kind === "script",
|
|
),
|
|
).toMatchObject({
|
|
status: "matched",
|
|
basis: "content-sha256",
|
|
confidence: "high",
|
|
reason: "unique-content-match",
|
|
candidate_static_count: 1,
|
|
});
|
|
},
|
|
);
|
|
|
|
it("keeps unavailable mapped digests unknown instead of borrowing unrelated matches", async () => {
|
|
const parsed = await collisionInput(1, true);
|
|
const candidates = collectStaticRuntimeCandidates(parsed.layers).map(
|
|
(candidate) =>
|
|
candidate.paths.includes("app.js") && candidate.category === "javascript"
|
|
? { ...candidate, digests: [] }
|
|
: candidate,
|
|
);
|
|
// Model the matcher's supported missing-digest boundary; do not falsify Evidence.
|
|
const runtime = projectRuntimeCaptures(parsed.captures);
|
|
const result = reconcileRuntimeEntities({
|
|
entities: runtime.entities,
|
|
candidates,
|
|
layers: parsed.layers,
|
|
});
|
|
expect(
|
|
result.items.find(({ entity_kind }) => entity_kind === "script"),
|
|
).toMatchObject({
|
|
status: "unknown",
|
|
basis: "operator-file-mapping",
|
|
confidence: "unknown",
|
|
reason: "static-or-runtime-coverage-incomplete",
|
|
candidate_static_count: 1,
|
|
});
|
|
const script = result.items.find(
|
|
({ entity_kind }) => entity_kind === "script",
|
|
);
|
|
const candidate = script?.candidate_static_nodes[0];
|
|
const states = classifyStaticLoadStates(
|
|
parsed.layers,
|
|
runtime.entities,
|
|
result.items,
|
|
{
|
|
reconciliationComplete: true,
|
|
},
|
|
);
|
|
expect(
|
|
states.states.find(
|
|
(state) =>
|
|
state.static_layer_id === candidate?.static_layer_id &&
|
|
state.static_node_id === candidate?.static_node_id,
|
|
),
|
|
).toMatchObject({
|
|
status: "unknown",
|
|
reason: "static-or-runtime-coverage-incomplete",
|
|
});
|
|
});
|
|
|
|
const UNRELATED = "export const unrelated = 17;\n";
|
|
|
|
type LayerSetup = {
|
|
readonly root: string;
|
|
readonly mapping: "none" | "explicit";
|
|
};
|
|
|
|
/** Reconcile one captured script, located at absent.js under live, against layers. */
|
|
const reconcileCapturedScript = async (input: {
|
|
readonly layers: readonly LayerSetup[];
|
|
readonly live: string;
|
|
readonly source: string;
|
|
readonly includeWorker?: boolean;
|
|
}) => {
|
|
const result = reconcileJavaScriptRuntime(
|
|
parseRuntimeReconciliationInput(
|
|
reconcileJavaScriptRuntimeInputSchema.parse({
|
|
static_layers: await Promise.all(
|
|
input.layers.map(async ({ root, mapping }, index) => ({
|
|
role: (["application", "cache", "assets"] as const)[index],
|
|
analysis: await analyzeFixture(root),
|
|
runtime_mappings:
|
|
mapping === "explicit"
|
|
? [{ kind: "file-root", root: input.live, artifact_prefix: "" }]
|
|
: [],
|
|
})),
|
|
),
|
|
runtime_observations: [
|
|
electronRuntimeEvidence(input.live, input.source, {
|
|
scriptFile: "absent.js",
|
|
includeWorker: input.includeWorker ?? false,
|
|
}),
|
|
],
|
|
}),
|
|
),
|
|
);
|
|
const item = (kind: string) =>
|
|
result.reconciliations.find(({ entity_kind }) => entity_kind === kind);
|
|
return { result, script: item("script"), worker: item("worker") };
|
|
};
|
|
|
|
it("keeps an unmapped captured script without a content match unknown", async () => {
|
|
const root = await applicationFixture();
|
|
const live = await createTestTempDirectory("rea-runtime-unmapped-");
|
|
const { result, script, worker } = await reconcileCapturedScript({
|
|
layers: [{ root, mapping: "none" }],
|
|
live,
|
|
source: UNRELATED,
|
|
includeWorker: true,
|
|
});
|
|
const unknown = {
|
|
status: "unknown",
|
|
basis: "none",
|
|
confidence: "unknown",
|
|
reason: "no-authorized-location-mapping",
|
|
static_layer_id: null,
|
|
static_node_id: null,
|
|
candidate_static_count: 0,
|
|
};
|
|
expect(script).toMatchObject(unknown);
|
|
// A source-free worker at an unmapped location shares the same rule.
|
|
expect(worker).toMatchObject(unknown);
|
|
expect(result.summary).toMatchObject({ matched: 0, unmatched: 0 });
|
|
expect(
|
|
result.graph.edges.some(({ relation }) => relation === "observed_as"),
|
|
).toBe(false);
|
|
});
|
|
|
|
it.each(["explicit", "implicit-root"] as const)(
|
|
"reports a mapped captured script without a content match as unmatched (%s)",
|
|
async (mapping) => {
|
|
const root = await applicationFixture();
|
|
const live =
|
|
mapping === "explicit"
|
|
? await createTestTempDirectory("rea-runtime-mapped-absent-")
|
|
: root;
|
|
const { result, script } = await reconcileCapturedScript({
|
|
layers: [{ root, mapping: mapping === "explicit" ? "explicit" : "none" }],
|
|
live,
|
|
source: UNRELATED,
|
|
});
|
|
expect(script).toMatchObject({
|
|
status: "unmatched",
|
|
basis: "none",
|
|
confidence: "high",
|
|
reason: "no-static-content-match",
|
|
candidate_static_count: 0,
|
|
});
|
|
expect(result.summary.unmatched).toBe(1);
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
["only the second", ["none", "explicit"], "unmatched"],
|
|
["only the first", ["explicit", "none"], "unmatched"],
|
|
["neither", ["none", "none"], "unknown"],
|
|
] as const)(
|
|
"classifies an unmatched captured script when %s of two layers maps it",
|
|
async (_label, mappings, status) => {
|
|
const live = await createTestTempDirectory("rea-runtime-layer-mapping-");
|
|
const { script } = await reconcileCapturedScript({
|
|
layers: await Promise.all(
|
|
mappings.map(async (mapping) => ({
|
|
root: await applicationFixture(),
|
|
mapping,
|
|
})),
|
|
),
|
|
live,
|
|
source: UNRELATED,
|
|
});
|
|
expect(script).toMatchObject({
|
|
status,
|
|
reason:
|
|
status === "unknown"
|
|
? "no-authorized-location-mapping"
|
|
: "no-static-content-match",
|
|
});
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
[1, "matched", "unique-content-match"],
|
|
[2, "ambiguous", "ambiguous-static-candidates"],
|
|
] as const)(
|
|
"keeps unmapped content-only correspondence with %i digest candidates",
|
|
async (copies, status, reason) => {
|
|
const live = await createTestTempDirectory("rea-runtime-unmapped-content-");
|
|
const { result, script } = await reconcileCapturedScript({
|
|
layers: await Promise.all(
|
|
Array.from({ length: copies }, async () => {
|
|
const root = await applicationFixture();
|
|
await writeFile(join(root, "other.js"), CAPTURED);
|
|
return { root, mapping: "none" as const };
|
|
}),
|
|
),
|
|
live,
|
|
source: CAPTURED,
|
|
});
|
|
expect(script).toMatchObject({
|
|
status,
|
|
basis: copies === 1 ? "content-sha256" : "none",
|
|
reason,
|
|
candidate_static_count: copies,
|
|
});
|
|
expect(result.summary.unmatched).toBe(0);
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
["https://example.test/assets/absent.js", "unmatched"],
|
|
["https://other.test/assets/absent.js", "unknown"],
|
|
] as const)(
|
|
"applies URL-prefix mapping presence to captured script %s",
|
|
async (url, status) => {
|
|
const root = await applicationFixture();
|
|
const parsed = parseRuntimeReconciliationInput(
|
|
reconcileJavaScriptRuntimeInputSchema.parse({
|
|
static_layers: [
|
|
{
|
|
role: "application",
|
|
analysis: await analyzeFixture(root),
|
|
runtime_mappings: [
|
|
{
|
|
kind: "url-prefix",
|
|
prefix: "https://example.test/assets/",
|
|
artifact_prefix: "",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
runtime_observations: [
|
|
electronRuntimeEvidence(root, UNRELATED, { includeWorker: false }),
|
|
],
|
|
}),
|
|
);
|
|
// Model a URL-located capture at the matcher boundary; browser and
|
|
// Inspector projections supply URL locations to the same classifier.
|
|
const entities = projectRuntimeCaptures(parsed.captures).entities.map(
|
|
(entity) =>
|
|
entity.kind === "script"
|
|
? { ...entity, location: { kind: "url" as const, value: url } }
|
|
: entity,
|
|
);
|
|
const result = reconcileRuntimeEntities({
|
|
entities,
|
|
candidates: collectStaticRuntimeCandidates(parsed.layers),
|
|
layers: parsed.layers,
|
|
});
|
|
expect(
|
|
result.items.find(({ entity_kind }) => entity_kind === "script"),
|
|
).toMatchObject({
|
|
status,
|
|
confidence: status === "unknown" ? "unknown" : "high",
|
|
reason:
|
|
status === "unknown"
|
|
? "no-authorized-location-mapping"
|
|
: "no-static-content-match",
|
|
});
|
|
},
|
|
);
|