fix(browser): preserve unknown script ownership through reconciliation

This commit is contained in:
morluto committed 2026-10-24 04:23:56 +08:00
1 parent d397ac7427
commit d9a855f5cb
5 files changed
+80 -4

No files matched your search

@@ -144,6 +144,10 @@ Script inventory completeness remains a separate reported fact. Unavailable,
truncated, or attach-limited worker inventory prevents negative code-presence
claims even when the selected page's script inventory is complete. Accepted
positive script correspondences remain loaded.
An Electron script's explicit unknown `frame_scope` preserves its reported
frame ID and sourceURL provenance as node properties, but does not link that
script to the current frame inventory. Attach-limited script coverage cannot
establish negative code-presence claims.
None of these states proves feature execution, initialization order,
reachability, or causality. In particular, `not-observed-in-capture` never means
+3 -3
View File
@@ -4,9 +4,9 @@ import { AnalysisError } from "../../domain/analysisErrorBase.js";
import { AnalysisOutputError } from "../../domain/analysisErrorCore.js";
import { sanitizeBrowserUrl } from "../../domain/browserObservation.js";
import { jsonObjectSchema } from "../../domain/jsonValue.js";
import {
type WebRuntimeSource,
type webRuntimeLocationSchema,
import type {
WebRuntimeSource,
webRuntimeLocationSchema,
} from "../../domain/webRuntime.js";
import type { CdpEvent } from "../CdpConnection.js";
import type { CdpRuntimeSession } from "./CdpRuntimeSession.js";
@@ -506,10 +506,12 @@ const assertRuntimeParameters = (
};
const scriptsComplete = (completeness: {
readonly attach_limited_sections: readonly string[];
readonly truncated_sections: readonly string[];
readonly unavailable_sections: readonly string[];
readonly dropped_events: { readonly scripts: number };
}): boolean =>
!completeness.attach_limited_sections.includes("scripts") &&
!completeness.truncated_sections.includes("scripts") &&
!completeness.unavailable_sections.includes("scripts") &&
completeness.dropped_events.scripts === 0;
@@ -157,7 +157,10 @@ const scriptEntities = (
nodeKind: "runtime-script-instance",
runtimeKey: script.script_key,
targetKey: capture.inspection.target.target_id,
frameKey: script.frame_id,
frameKey:
"frame_scope" in script && script.frame_scope === "unknown"
? null
: script.frame_id,
scriptKey: script.script_key,
location,
staticKind: "javascript",
@@ -176,6 +179,15 @@ const scriptEntities = (
...("reported_url" in script && script.reported_url !== undefined
? { reported_url: script.reported_url }
: {}),
...("frame_scope" in script && script.frame_scope !== undefined
? {
frame_scope: script.frame_scope,
reported_frame_id: script.frame_id,
}
: {}),
...("has_source_url" in script && script.has_source_url !== undefined
? { has_source_url: script.has_source_url }
: {}),
cdp_hash: script.cdp_hash,
length: script.length,
is_module: script.is_module,
@@ -11,6 +11,7 @@ import { reconcileJavaScriptRuntimeEvidence } from "../../../../src/application/
import { createEvidence } from "../../../../src/domain/evidence.js";
import { mappedRuntimePaths } from "../../../../src/domain/javascript/javascriptRuntimeStaticCandidates.js";
import { projectRuntimeCaptures } from "../../../../src/domain/javascript/javascriptRuntimeReconciliationRuntime.js";
import { parseRuntimeCaptures } from "../../../../src/domain/javascript/javascriptRuntimeReconciliationCaptureParsing.js";
import { electronPageInspectionSchema } from "../../../../src/domain/javascript/electronObservation.js";
import {
readRuntimeReconciliationInput,
@@ -263,6 +264,63 @@ it("preserves positive script matches without inventing worker absence when work
);
});
it("preserves unknown Electron frame provenance without linking it to an authorized frame", async () => {
const root = await applicationFixture();
const recorded = electronRuntimeEvidence(root, SOURCE);
const inspection = electronPageInspectionSchema.parse(
recorded.normalized_result,
);
const evidence = createEvidence(undefined, recorded.provider, {
predicateType: recorded.predicate_type,
operation: recorded.operation,
parameters: recorded.parameters,
confidence: recorded.confidence,
authority: recorded.authority,
result: {
...inspection,
completeness: {
...inspection.completeness,
status: "attach_limited",
conditions: ["attach_limited"],
attach_limited_sections: ["scripts"],
},
scripts: {
...inspection.scripts,
items: inspection.scripts.items.map((script) => ({
...script,
frame_scope: "unknown",
has_source_url: true,
reported_url: "file:///reported-label.js",
})),
},
},
});
const captures = parseRuntimeCaptures([evidence]);
expect(captures[0]?.scriptsCompleteWithinScope).toBe(false);
const projected = projectRuntimeCaptures(captures);
const script = projected.entities.find(({ kind }) => kind === "script");
expect(script?.frameKey).toBeNull();
expect(script?.node.properties).toMatchObject({
frame_scope: "unknown",
reported_frame_id: inspection.scripts.items[0]?.frame_id,
has_source_url: true,
reported_url: "file:///reported-label.js",
});
const frameIds = new Set(
projected.entities
.filter(({ kind }) => kind === "frame")
.map(({ node }) => node.node_id),
);
expect(
projected.edges.some(
(edge) =>
edge.relation === "contains" &&
frameIds.has(edge.source_node_id) &&
edge.target_node_id === script?.node.node_id,
),
).toBe(false);
});
it("reconciles active Electron as a partial target-only runtime capture", async () => {
const fixture = await applicationFixture();
const staticEvidence = await analyzeFixture(fixture);