mirror of
https://github.com/morluto/rea.git
synced 2026-10-27 01:45:32 +00:00
refactor(javascript): group application workflows and verification (#896)
This commit is contained in:
1 parent
faaee693d5
commit
0804722cdf
129 files changed
+402
-367
No files matched your search
@@ -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,
|
||||
]),
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user