refactor(javascript): group application workflows and verification (#896)

This commit is contained in:
ysopwnable authored and GitHub committed 2026-10-07 16:03:37 +08:00
1 parent faaee693d5
commit 0804722cdf
129 files changed
+402 -367

No files matched your search

@@ -0,0 +1,207 @@
#!/usr/bin/env node
import { realpath } from "node:fs/promises";
import { parseArgs } from "node:util";
import { analyzeJavaScriptApplication } from "../../../dist/application/javascript/JavaScriptApplicationService.js";
import {
compareApplicationVersionsEvidence,
compareJavaScriptExportShapesEvidence,
traceApplicationFeatureEvidence,
} from "../../../dist/application/javascript/JavaScriptApplicationWorkflowService.js";
import { applicationVersionComparisonResultSchema } from "../../../dist/domain/javascriptApplicationVersionComparisonSchemas.js";
import { applicationFeatureTraceResultSchema } from "../../../dist/domain/javascriptFeatureTraceSchemas.js";
import { javaScriptExportShapeComparisonResultSchema } from "../../../dist/domain/javascriptExportShapeComparisonSchemas.js";
import {
completeVerifierRun,
createVerifierRun,
} from "../../lib/verifier-run.mjs";
const verifierRun = createVerifierRun();
const options = parseArgs({
options: {
left: { type: "string" },
right: { type: "string" },
"seed-kind": { type: "string", default: "module" },
"seed-value": { type: "string" },
"left-module-path": { type: "string" },
"left-export-name": { type: "string" },
"right-module-path": { type: "string" },
"right-export-name": { type: "string" },
},
strict: true,
allowPositionals: false,
}).values;
if (options.left === undefined || options.right === undefined)
throw new Error(
"--left and --right must name operator-provided app/ASAR paths",
);
const [leftPath, rightPath] = await Promise.all([
realpath(options.left),
realpath(options.right),
]);
if (leftPath === rightPath)
throw new Error("--left and --right must resolve to distinct paths");
const [left, right] = await Promise.all([
analyze(leftPath),
analyze(rightPath),
]);
const comparison = compareApplicationVersionsEvidence({ left, right });
if (!comparison.ok) throw comparison.error;
const comparisonResult = applicationVersionComparisonResultSchema.parse(
comparison.value.normalized_result,
);
const seedValue =
options["seed-value"] ??
left.normalized_result.graph.nodes.find(
({ kind }) => kind === "javascript-module",
)?.observations[0]?.label;
const trace =
seedValue === undefined
? null
: traceApplicationFeatureEvidence({
application: left,
seed: {
kind: options["seed-kind"],
value: seedValue,
match: "exact",
case_sensitive: false,
},
direction: "both",
});
if (trace !== null && !trace.ok) throw trace.error;
const traceResult =
trace === null
? null
: applicationFeatureTraceResultSchema.parse(trace.value.normalized_result);
const exportShapeSelector = resolveExportShapeSelector(left, right, options);
const exportShapeComparison =
exportShapeSelector === null
? null
: compareJavaScriptExportShapesEvidence({
left,
right,
...exportShapeSelector,
});
if (exportShapeComparison !== null && !exportShapeComparison.ok)
throw exportShapeComparison.error;
const exportShapeResult =
exportShapeComparison === null
? null
: javaScriptExportShapeComparisonResultSchema.parse(
exportShapeComparison.value.normalized_result,
);
process.stdout.write(
`${JSON.stringify({
verifier_run: await completeVerifierRun(verifierRun),
comparison_id: comparisonResult.comparison_id,
evidence_id: comparison.value.evidence_id,
left: comparisonResult.left,
right: comparisonResult.right,
summary: comparisonResult.summary,
matching: comparisonResult.matching,
coverage: comparisonResult.coverage,
export_shape_comparison:
exportShapeResult === null
? null
: {
comparison_id: exportShapeResult.comparison_id,
evidence_id: exportShapeComparison.value.evidence_id,
left: exportShapeResult.left,
right: exportShapeResult.right,
summary: exportShapeResult.summary,
changes: exportShapeResult.changes,
coverage: exportShapeResult.coverage,
runtime_validation: exportShapeResult.runtime_validation,
},
trace:
traceResult === null
? null
: {
trace_id: traceResult.trace_id,
evidence_id: trace.value.evidence_id,
seed: traceResult.seed,
summary: traceResult.summary,
coverage: traceResult.coverage,
native_handoffs: traceResult.native_handoffs.map((handoff) => ({
artifact_sha256: handoff.artifact_sha256,
status: handoff.status,
requested_exports: handoff.requested_exports,
evidence_ids: handoff.evidence_ids,
})),
},
verified: true,
})}\n`,
);
function resolveExportShapeSelector(left, right, values) {
const explicit = [
values["left-module-path"],
values["left-export-name"],
values["right-module-path"],
values["right-export-name"],
];
if (explicit.some((value) => value !== undefined)) {
if (explicit.some((value) => value === undefined))
throw new Error(
"Explicit export-shape verification requires all left/right module and export selectors",
);
return {
left_module_path: explicit[0],
left_export_name: explicit[1],
right_module_path: explicit[2],
right_export_name: explicit[3],
};
}
const leftSelectors = returnShapeSelectors(left.normalized_result.graph);
const rightSelectors = new Set(
returnShapeSelectors(right.normalized_result.graph).map(selectorKey),
);
const common = leftSelectors.find((selector) =>
rightSelectors.has(selectorKey(selector)),
);
return common === undefined
? null
: {
left_module_path: common.modulePath,
left_export_name: common.exportName,
right_module_path: common.modulePath,
right_export_name: common.exportName,
};
}
function returnShapeSelectors(graph) {
const selectors = [];
for (const node of graph.nodes) {
for (const observation of node.observations) {
const properties = observation.properties;
if (
properties.semantic_role === "export-return-shapes" &&
typeof properties.module_path === "string" &&
typeof properties.exported_name === "string"
)
selectors.push({
modulePath: properties.module_path,
exportName: properties.exported_name,
});
}
}
return selectors.sort((left, right) =>
selectorKey(left).localeCompare(selectorKey(right), "en"),
);
}
function selectorKey(selector) {
return `${selector.modulePath}\0${selector.exportName}`;
}
async function analyze(path) {
const result = await analyzeJavaScriptApplication({
input_path: path,
});
if (!result.ok) throw result.error;
return result.value;
}
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env node
import assert from "node:assert/strict";
import { constants } from "node:buffer";
import { createHash } from "node:crypto";
import { canonicalDigest } from "../../../dist/domain/comparisonSemantics.js";
import {
createEvidence,
parseEvidence,
} from "../../../dist/domain/evidence.js";
// This opt-in check crosses the actual engine string limit; keep it outside
// routine source tests. Build first, then run this script with the local Node.
const leaf = "x".repeat(1024 * 1024);
const encodedLeaf = JSON.stringify(leaf);
const count = Math.ceil(constants.MAX_STRING_LENGTH / encodedLeaf.length) + 1;
const value = { payload: Array.from({ length: count }, () => leaf) };
const expected = createHash("sha256");
expected.update('{"payload":[');
for (let index = 0; index < count; index += 1) {
if (index > 0) expected.update(",");
expected.update(encodedLeaf);
}
expected.update("]}");
assert.equal(canonicalDigest(value), expected.digest("hex"));
const evidence = createEvidence(
undefined,
{ id: "large-json-fixture", name: "Large JSON fixture", version: "1" },
{ operation: "inspect_fixture", parameters: {}, result: value },
);
assert.equal(parseEvidence(evidence).evidence_id, evidence.evidence_id);
process.stdout.write(
`${JSON.stringify({
verified: true,
serialized_value_bytes: count * encodedLeaf.length + count - 1 + 14,
engine_string_limit: constants.MAX_STRING_LENGTH,
evidence_id: evidence.evidence_id,
})}\n`,
);
+189
View File
@@ -0,0 +1,189 @@
#!/usr/bin/env node
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import { parseEvidence } from "../../../dist/domain/evidence.js";
import { javascriptRecoveryResultSchema } from "../../../dist/domain/javascriptRecovery.js";
import { buildRecoveryFixtures } from "../../fixtures/javascript-recovery/build.mjs";
import { mcpTextValue } from "../../lib/mcp-verifier-results.mjs";
const OFFICIAL_BINARY_SHA256 =
"e8c7ca052974604197389f6ace7464e4dd24dce64e2730971521bd6606ec9c9e";
if (!process.env.REA_WAKARU_COMMAND)
throw new Error(
"Provide REA_WAKARU_COMMAND for the official Wakaru 1.13.0 Linux x64 binary",
);
const entrypoint =
process.argv[2] ?? fileURLToPath(new URL("../../rea.mjs", import.meta.url));
const root = await mkdtemp(join(tmpdir(), "rea-real-recovery-"));
const environment = { ...process.env, REA_LOG_LEVEL: "silent" };
const transport = new StdioClientTransport({
command: process.execPath,
args: [entrypoint, "mcp"],
env: environment,
stderr: "pipe",
});
const client = new Client({ name: "real-recovery", version: "1" });
const run = promisify(execFile);
const cli = async (...args) => {
const { stdout } = await run(
process.execPath,
[entrypoint, ...args, "--json"],
{ env: environment, timeout: 150000, maxBuffer: 32 * 1024 * 1024 },
);
return parseEvidence(JSON.parse(stdout));
};
const call = async (name, args) => {
const result = await client.callTool({ name, arguments: args }, undefined, {
timeout: 150000,
});
assert.notEqual(result.isError, true, mcpTextValue(result));
return parseEvidence(JSON.parse(mcpTextValue(result)).evidence);
};
const fingerprint = (bytes) => createHash("sha256").update(bytes).digest("hex");
const counts = [];
try {
const fixtures = await buildRecoveryFixtures(
root,
process.env.REA_JAVASCRIPT_FIXTURE_TOOLS,
);
await client.connect(transport);
for (const fixture of fixtures.files) {
for (const surface of ["cli", "stdio_mcp"]) {
const output = join(root, `${fixture.name}-${surface}`);
const flags =
fixture.options === undefined
? []
: [
"--extraction-mode",
fixture.options.extraction_mode,
"--rewrite-level",
fixture.options.rewrite_level,
];
const evidence =
surface === "cli"
? await cli(
"recover-javascript-sources",
fixture.path,
output,
...flags,
)
: await call("recover_javascript_sources", {
path: fixture.path,
output_directory: output,
...fixture.options,
});
const result = javascriptRecoveryResultSchema.parse(
evidence.normalized_result,
);
await assertRecoveredArtifacts(evidence, result, fixture);
const analysis =
surface === "cli"
? await cli(
"analyze-javascript-application",
result.analysis_input.input_path,
)
: await call("analyze_javascript_application", result.analysis_input);
assert.equal(analysis.normalized_result.statistics.parse_failures, 0);
assert.equal(
analysis.normalized_result.statistics.parsed_javascript_files,
result.modules.length,
);
const recoveredEntry =
result.modules.find(
(module) => module.reported_filename === "entry.js",
) ?? result.modules[0];
assert.ok(recoveredEntry);
const oracle =
'await import((await import("node:url")).pathToFileURL(process.argv[1])); const f=globalThis.recoveryFixture; console.log(JSON.stringify([f.greet(null),f.greet("REA"),f.total([1,2,3])]));';
for (const selected of fixture.options?.extraction_mode === "inspection"
? []
: [fixture.path, recoveredEntry.artifact.path]) {
const execution = await run(
process.execPath,
["--input-type=module", "-e", oracle, selected],
{ env: environment, timeout: 10000 },
);
assert.deepEqual(JSON.parse(execution.stdout), [
"Hello world",
"Hello REA",
6,
]);
}
counts.push({
fixture: fixture.name,
surface,
modules: result.modules.length,
maps: result.modules.filter((module) => module.source_map !== null)
.length,
finite_fixture_oracle:
fixture.options?.extraction_mode !== "inspection",
});
}
}
console.log(
JSON.stringify({
engine: "wakaru 1.13.0",
toolchains: fixtures.toolchains,
results: counts,
verified: true,
}),
);
} finally {
await client.close();
await transport.close();
await rm(root, { recursive: true, force: true });
}
async function assertRecoveredArtifacts(evidence, result, fixture) {
assert.deepEqual(result.options, {
extraction_mode: "structural",
rewrite_level: "standard",
...fixture.options,
});
if (fixture.options?.extraction_mode === "inspection")
assert.equal(result.reported_safety, "inspection-only");
assert.equal(result.engine.executable.sha256, OFFICIAL_BINARY_SHA256);
assert.equal(result.engine.executed_source_revision, null);
assert.equal(evidence.confidence, "derived");
assert.equal(result.runtime_equivalence, "unknown");
assert.equal(result.status, "complete");
assert.equal(result.reported_format, fixture.expectedFormat);
assert.equal(result.modules.length, fixture.expectedModules);
assert.equal(result.source.sha256, fingerprint(await readFile(fixture.path)));
for (const file of [
result.source.published_copy,
result.report,
result.provenance,
result.manifest,
...result.modules.flatMap((module) => [
module.artifact,
...(module.source_map ? [module.source_map] : []),
]),
]) {
const bytes = await readFile(file.path);
assert.equal(bytes.length, file.bytes);
assert.equal(fingerprint(bytes), file.sha256);
}
for (const module of result.modules) {
assert.equal(module.provenance.original_sha256, result.source.sha256);
assert.equal(module.provenance.reported_input, result.source.snapshot_path);
for (const range of module.provenance.byte_ranges)
assert.ok(range.start <= range.end && range.end <= result.source.bytes);
if (module.source_map) {
const map = JSON.parse(await readFile(module.source_map.path, "utf8"));
for (const source of map.sources)
assert.equal(
resolve(dirname(module.source_map.path), source),
result.source.published_copy.path,
);
}
}
}
@@ -0,0 +1,138 @@
#!/usr/bin/env node
import { readFile, realpath } from "node:fs/promises";
import { parseArgs } from "node:util";
import { analyzeJavaScriptApplication } from "../../../dist/application/javascript/JavaScriptApplicationService.js";
import { reconcileJavaScriptRuntimeEvidence } from "../../../dist/application/javascript/JavaScriptRuntimeReconciliationService.js";
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 canonicalLayers = await Promise.all(
requestedLayers.map(async (layer) => ({
...layer,
path: await realpath(layer.path),
})),
);
const staticLayers = [];
for (const layer of canonicalLayers) {
const analyzed = await analyzeJavaScriptApplication({
input_path: layer.path,
format: "auto",
});
if (!analyzed.ok) throw analyzed.error;
staticLayers.push({
role: layer.role,
analysis: analyzed.value,
runtime_mappings: [],
});
}
for (const encoded of options.mapping) {
const mapping = parseMapping(encoded, staticLayers.length);
staticLayers[mapping.layer].runtime_mappings.push(mapping.value);
}
const runtimeObservations = (
await Promise.all(options["runtime-evidence"].map(readEvidenceFile))
).flat();
const reconciled = reconcileJavaScriptRuntimeEvidence({
static_layers: staticLayers,
runtime_observations: runtimeObservations,
});
if (!reconciled.ok) throw reconciled.error;
const result = reconciled.value.normalized_result;
process.stdout.write(
`${JSON.stringify({
verifier_run: await completeVerifierRun(verifierRun),
reconciliation_id: result.reconciliation_id,
evidence_id: reconciled.value.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 bytes = await readFile(await realpath(path));
if (bytes.byteLength > 64 * 1024 * 1024)
throw new Error(`Runtime Evidence exceeds 64 MiB: ${path}`);
const value = JSON.parse(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,
]),
);
}