Files
reverse-engineering-anything/scripts/verify/javascript/runtime-observation.mjs
T
morluto 7bcefead69 test: strengthen and prune audited test coverage (#8573)
* test: strengthen and prune audited test coverage

* test: require delivered DeepSeek skill instructions

* test: keep schema fixture types private

* test: verify Gemini receives skill instructions

* test: bind Qwen skill evidence to its tool result

* test: report NativeAOT verifier checks precisely

* fix: verify Ghidra cleanup paths reliably

* fix: verify Command Code skill activation

* test: tighten cleanup and fixture checks

* test: verify packet capture evidence is recorded

* fix: report HTTP3 capture provenance accurately

* test: preserve resource coverage through MCP

* test: prune repeated assertions and retain cancellation identity

* fix: validate client verifier fixture paths

* test: skip Copilot verifier cases on Windows

* test: keep verifier setup target relative
2026-11-02 02:13:32 +08:00

180 lines
5.6 KiB
JavaScript

#!/usr/bin/env node
import { realpath } from "node:fs/promises";
import { parseArgs } from "node:util";
import { readStableArtifact } from "../../../dist/artifacts/readStableArtifact.js";
import { ArtifactReaderFailure } from "../../../dist/artifacts/ArtifactReader.js";
import { ArtifactResourceScope } from "../../../dist/artifacts/ArtifactResourceScope.js";
import {
ArtifactFileCleanupFailure,
artifactFileCompletion,
artifactFilePrimaryFacts,
} from "../../../dist/artifacts/ArtifactFileOperation.js";
import { reconcileJavaScriptRuntimeEvidence } from "../../../dist/application/javascript/JavaScriptRuntimeReconciliationService.js";
import { analyzeJavaScriptApplicationWithOwnedScope } from "../../lib/javascript-application-analysis.mjs";
import {
completeVerifierRun,
createVerifierRun,
} from "../../lib/verifier-run.mjs";
const verifierRun = createVerifierRun();
const options = parseArgs({
options: {
application: { type: "string" },
cache: { type: "string", multiple: true, default: [] },
assets: { type: "string", multiple: true, default: [] },
"runtime-evidence": { type: "string", multiple: true, default: [] },
mapping: { type: "string", multiple: true, default: [] },
},
strict: true,
allowPositionals: false,
}).values;
if (options.application === undefined)
throw new Error("--application must name an operator-provided app/ASAR path");
if (options["runtime-evidence"].length === 0)
throw new Error(
"At least one --runtime-evidence path from inspect_web_page or inspect_electron_page is required",
);
const requestedLayers = [
{ role: "application", path: options.application },
...options.cache.map((path) => ({ role: "cache", path })),
...options.assets.map((path) => ({ role: "assets", path })),
];
const staticLayers = [];
for (const layer of requestedLayers) {
const analyzed = await analyzeJavaScriptApplicationWithOwnedScope({
input_path: layer.path,
format: "auto",
});
staticLayers.push({
role: layer.role,
analysis: analyzed,
runtime_mappings: [],
});
}
for (const encoded of options.mapping) {
const mapping = parseMapping(encoded, staticLayers.length);
staticLayers[mapping.layer].runtime_mappings.push(mapping.value);
}
const runtimeObservations = [];
for (const path of options["runtime-evidence"]) {
const observations = await readEvidenceFile(path);
for (const observation of observations) runtimeObservations.push(observation);
}
const reconciled = reconcileJavaScriptRuntimeEvidence({
static_layers: staticLayers,
runtime_observations: runtimeObservations,
});
if (!reconciled.ok) throw reconciled.error;
const result = reconciled.value.evidence.normalized_result;
process.stdout.write(
`${JSON.stringify({
verifier_run: await completeVerifierRun(verifierRun),
reconciliation_id: result.reconciliation_id,
evidence_id: reconciled.value.evidence.evidence_id,
graph_id: result.graph.graph_id,
layers: result.static_layers.map((layer) => ({
role: layer.role,
evidence_id: layer.evidence_id,
graph_id: layer.graph_id,
root_artifact_sha256: layer.root_artifact_sha256,
input_path: layer.input_path,
})),
runtime_captures: result.runtime_captures.map((capture) => ({
evidence_id: capture.evidence_id,
capture_sha256: capture.capture_sha256,
target_location: capture.target_location,
})),
summary: result.summary,
coverage: result.coverage,
unmatched_reasons: counted(
result.reconciliations
.filter(({ status }) => status !== "matched")
.map(({ reason }) => reason),
),
static_load_states: counted(
result.static_load_states.map(({ status }) => status),
),
verified: true,
})}\n`,
);
async function readEvidenceFile(path) {
const scope = new ArtifactResourceScope();
let outcome;
try {
outcome = {
ok: true,
value: await readStableArtifact(
await realpath(path),
64 * 1024 * 1024,
undefined,
undefined,
scope,
),
};
} catch (cause) {
outcome = { ok: false, cause };
}
try {
await scope.close();
} catch (cleanupCause) {
if (!outcome.ok)
throw new AggregateError(
[outcome.cause, cleanupCause],
"Verifier cleanup failed",
{ cause: outcome.cause },
);
throw cleanupCause;
}
if (!outcome.ok && outcome.cause instanceof ArtifactFileCleanupFailure) {
const completion = artifactFileCompletion(outcome.cause);
outcome =
completion === undefined
? { ok: false, cause: artifactFilePrimaryFacts(outcome.cause).cause }
: { ok: true, value: completion.value };
}
if (!outcome.ok) {
const cause = outcome.cause;
if (cause instanceof ArtifactReaderFailure && cause.reason === "limit")
throw new Error(`Runtime Evidence exceeds 64 MiB: ${path}`, { cause });
throw cause;
}
const value = JSON.parse(outcome.value.bytes.toString("utf8"));
return Array.isArray(value) ? value : [value];
}
function parseMapping(encoded, layerCount) {
const parsed = JSON.parse(encoded);
if (
typeof parsed !== "object" ||
parsed === null ||
!Number.isInteger(parsed.layer) ||
parsed.layer < 0 ||
parsed.layer >= layerCount ||
(parsed.kind !== "file-root" && parsed.kind !== "url-prefix")
)
throw new Error(
"--mapping must be JSON with a valid layer index and file-root or url-prefix kind",
);
const { layer, ...value } = parsed;
return { layer, value };
}
function counted(values) {
return Object.fromEntries(
[...new Set(values)]
.sort()
.map((value) => [
value,
values.filter((candidate) => candidate === value).length,
]),
);
}