Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion tests/helpers/update-capability-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const readsFor = (module: string): Set<string> | undefined => {
module = module.replace(/^node:/, "");
if (module === "fs" || module === "fs/promises") return fsReads;
if (module === "bun") return bunReads;
if (module === "crypto") return new Set(["randomBytes", "randomUUID", "createHash"]);
// createPublicKey and verify are pure: they build a key object from bytes and check a signature.
if (module === "crypto") return new Set(["randomBytes", "randomUUID", "createHash", "createPublicKey", "verify"]);
if (module === "os") return new Set(["homedir", "tmpdir", "platform", "arch", "type", "release"]);
if (module === "tty") return new Set(["isatty"]);
// process imports are disallowed: the global view below is deliberately narrow.
Expand Down
3 changes: 2 additions & 1 deletion tests/helpers/update-helper-control-contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
type Contract = readonly [file: string, context: string, call: string, count: number, contract: string];
import { orchestratedChildReview } from "./update-review-orchestrated-child";
import { orchestratedEntryReview } from "./update-review-orchestrated-entry";
import { signedSupplyReview } from "./update-review-signed-supply";

const baseHelperControlContracts: readonly Contract[] = [
["src/lib/desktop-cli-handoff.mjs","runDesktopCliHandoff","launch.onSpawn(child.pid)",1,"pre-mutation: publish the sync lease delegate for the launched child"],
Expand Down Expand Up @@ -123,4 +124,4 @@ const baseHelperControlContracts: readonly Contract[] = [
["src/update/mutation-guard.mjs","guardedRun","operation(...data)",1,"materialized data then authorizer then direct primitive dispatch"],
["src/update/mutation-guard.mjs","ownedArtifactRm","native(...data)",1,"bounded private transaction artifact cleanup"]
];
export const helperControlContracts: readonly Contract[] = [...baseHelperControlContracts, ...orchestratedEntryReview.contracts, ...orchestratedChildReview.contracts];
export const helperControlContracts: readonly Contract[] = [...baseHelperControlContracts, ...orchestratedEntryReview.contracts, ...orchestratedChildReview.contracts, ...signedSupplyReview.contracts];
5 changes: 3 additions & 2 deletions tests/helpers/update-import-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// security boundary or completeness proof. Runtime mutation guards enforce authority.
import { orchestratedChildReview } from "./update-review-orchestrated-child";
import { orchestratedEntryReview } from "./update-review-orchestrated-entry";
import { signedSupplyReview } from "./update-review-signed-supply";
import { mergeStaticImports } from "./update-review-table";

const baseStaticImports: Readonly<Record<string, readonly string[]>> = {
Expand Down Expand Up @@ -366,7 +367,7 @@ const baseStaticImports: Readonly<Record<string, readonly string[]>> = {
"import { dirname, isAbsolute } from \"node:path\";"
]
};
export const updateStaticImports = mergeStaticImports(baseStaticImports, orchestratedEntryReview.staticImports, orchestratedChildReview.staticImports);
export const updateStaticImports = mergeStaticImports(baseStaticImports, orchestratedEntryReview.staticImports, orchestratedChildReview.staticImports, signedSupplyReview.staticImports);
const baseDeferredImports: readonly string[] = [
// Read-only identity verification before the child enters mutation.
'src/update/index.ts:runUpdate:session = createOrchestratedChildSession({ home: getConfigDir(), resultPath: child.resultPath, resultCapability: child.resultCapability, expect, notAfter: child.notAfter, verifyIdentity: async () => (await import("./orchestrated-entry")).verifyOrchestratedChildIdentity(expect, child.version) })',
Expand All @@ -386,7 +387,7 @@ const baseDeferredImports: readonly string[] = [
"src/update/job-update-tray.ts:defaultWorkerTrayHandoff:{ getWindowsTrayStatus, guardedStartWindowsTray, guardedStopWindowsTray } = await import(\"./guarded-tray-ops\")",
"src/update/job.ts:restartAfterUpdate:{ serviceReinstallArgs } = await import(\"../service\")"
];
export const updateDeferredImports: readonly string[] = [...baseDeferredImports, ...orchestratedEntryReview.deferredImports, ...orchestratedChildReview.deferredImports];
export const updateDeferredImports: readonly string[] = [...baseDeferredImports, ...orchestratedEntryReview.deferredImports, ...orchestratedChildReview.deferredImports, ...signedSupplyReview.deferredImports];
// Existing trusted wrapper internals: each complete expression is reviewed.
// Import permission does not grant a wrapper arbitrary loader/global access.
export const updateBoundaryRootSites = new Map([
Expand Down
3 changes: 2 additions & 1 deletion tests/helpers/update-primitive-allowlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// Probe rows confer no update authority. Child rows retain only their own child.
import { orchestratedChildReview } from "./update-review-orchestrated-child";
import { orchestratedEntryReview } from "./update-review-orchestrated-entry";
import { signedSupplyReview } from "./update-review-signed-supply";

const basePrimitiveAllowlist: readonly (readonly [string, string])[] = [
["src/update/config-preflight.ts:<module>:import { closeSync, constants, fstatSync, lstatSync, openSync, readFileSync } from \"node:fs\";", "read-only config preflight: open with O_RDONLY | O_NONBLOCK, inspect and read the fd, then close it"],
Expand Down Expand Up @@ -216,4 +217,4 @@ const basePrimitiveAllowlist: readonly (readonly [string, string])[] = [
["src/update/npm-install.mjs:runNpmInstall:process.stderr.write(chunk)","bounded helper protocol output; no successful update publication"],
["src/update/refresh-scheduler.ts:createRefreshScheduler:deps.write(tag, latest, deps.now())","version badge cache only; no runtime or install authority"]
];
export const updatePrimitiveAllowlist: readonly (readonly [string, string])[] = [...basePrimitiveAllowlist, ...orchestratedEntryReview.primitive, ...orchestratedChildReview.primitive];
export const updatePrimitiveAllowlist: readonly (readonly [string, string])[] = [...basePrimitiveAllowlist, ...orchestratedEntryReview.primitive, ...orchestratedChildReview.primitive, ...signedSupplyReview.primitive];
3 changes: 2 additions & 1 deletion tests/helpers/update-process-allowlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Exact existing terminal/probe lifecycle sites. No arbitrary process API is admitted.
import { orchestratedChildReview } from "./update-review-orchestrated-child";
import { orchestratedEntryReview } from "./update-review-orchestrated-entry";
import { signedSupplyReview } from "./update-review-signed-supply";

const baseProcessAllowlist: readonly (readonly [string, string, number])[] = [
["bin/ocx.mjs:runPackageManagerSelfUpdate:process.on(signal, () => {})","pre-mutation: keep the update session owner alive during installer supervision",1],
Expand Down Expand Up @@ -35,4 +36,4 @@ const baseProcessAllowlist: readonly (readonly [string, string, number])[] = [
["src/update/notify.ts:maybeShowUpdatePrompt:process.exit(0)","ordinary launcher/help/probe or terminal prompt outcome; update success remains guarded",1],
["src/update/service-home-probe-entry.ts:<module>:process.exit(1)","terminal failure only; cannot publish successful update",1],
];
export const updateProcessAllowlist: readonly (readonly [string, string, number])[] = [...baseProcessAllowlist, ...orchestratedEntryReview.process, ...orchestratedChildReview.process];
export const updateProcessAllowlist: readonly (readonly [string, string, number])[] = [...baseProcessAllowlist, ...orchestratedEntryReview.process, ...orchestratedChildReview.process, ...signedSupplyReview.process];
57 changes: 57 additions & 0 deletions tests/helpers/update-review-signed-supply.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Best-effort regression lint data, not a security boundary or completeness proof.
// Review rows for the #8294 offline signed-update verification modules (minisign.mjs, signed-supply.mjs,
// update-bounded-json.mjs, update-checkpoint-codec.mjs, update-contracts.mjs,
// update-signature-threshold.mjs, update-signed-version.mjs), kept apart from the base tables so the
// group is reviewed on its own. Every module here is pure verification over caller-supplied bytes: no
// filesystem, process, network or runtime capability, and no update authority. A file key may appear in
// only one table.
import type { UpdateReviewTable } from "./update-review-table";

export const signedSupplyReview: UpdateReviewTable = {
staticImports: {
"src/update/minisign.mjs": [
"import { snapshotUpdateBytes } from \"./update-bytes.mjs\";", // pure: bounded copy of caller bytes
],
"src/update/signed-supply.mjs": [
"import { parseRootTransitionEnvelope, parseSignatureEnvelope, parseUpdateDocument } from \"./update-contracts.mjs\";", // pure: parse signed documents
"import { EMBEDDED_ROOT_SHA256 } from \"./update-trust-anchor.mjs\";", // pure: embedded trust-anchor constant
"import { snapshotUpdateBytes } from \"./update-bytes.mjs\";", // pure: bounded copy of caller bytes
"import { verifySignatureThreshold } from \"./update-signature-threshold.mjs\";", // pure: count valid signatures
"import { deriveCandidateId } from \"./update-candidate-id.mjs\";", // pure: digest-derived identifier
"import { decodePublicKey, verifyMinisignBytes } from \"./minisign.mjs\";", // pure: signature verification
"import { trustedCommentMatchesVersion } from \"./update-signed-version.mjs\";", // pure: version comparison
],
"src/update/update-bounded-json.mjs": [
"import { snapshotUpdateBytes } from \"./update-bytes.mjs\";", // pure: bounded copy of caller bytes
],
"src/update/update-checkpoint-codec.mjs": [
"import { snapshotUpdateBytes } from \"./update-bytes.mjs\";", // pure: bounded copy of caller bytes
"import { parseRootTransitionEnvelope, parseUpdateDocument } from \"./update-contracts.mjs\";", // pure: parse signed documents
"import { sameSignedVersion } from \"./update-signed-version.mjs\";", // pure: version comparison
],
"src/update/update-contracts.mjs": [
"import { parseBoundedJson } from \"./update-bounded-json.mjs\";", // pure: bounded JSON parse
"import { decodePublicKey } from \"./minisign.mjs\";", // pure: key decoding
"import { sameSignedVersion } from \"./update-signed-version.mjs\";", // pure: version comparison
],
"src/update/update-signature-threshold.mjs": [
"import { decodePublicKey, verifyMinisignBytes } from \"./minisign.mjs\";", // pure: signature verification
],
},
deferredImports: [],
primitive: [
["src/update/update-signed-version.mjs:validVersion:VERSION.exec", "pure: RegExp#exec on a version string, not a process exec"],
["src/update/update-bounded-json.mjs:parseBoundedJson:NUMBER.exec", "pure: RegExp#exec on JSON number text, not a process exec"],
// Keyed on the complete expression: any other Reflect member at these sites is still flagged.
["src/update/signed-supply.mjs:verifyExternalMetadataEvidence:Reflect.ownKeys(descriptors)", "pure: counts a descriptor snapshot's own keys; no capability acquisition"],
["src/update/update-checkpoint-codec.mjs:record:Reflect.ownKeys(value)", "pure: checks a decoded record's exact key set; no capability acquisition"],
["src/update/update-checkpoint-codec.mjs:list:Reflect.ownKeys(value)", "pure: checks a decoded list's exact index keys; no capability acquisition"],
],
process: [],
contracts: [
["src/update/signed-supply.mjs", "verifyExternalMetadataEvidence", "Reflect.ownKeys(descriptors)", 1, "read-only: exact own-key count of a caller descriptor snapshot before any field is trusted"],
["src/update/update-checkpoint-codec.mjs", "record", "Reflect.ownKeys(value)", 1, "read-only: exact key set of a decoded checkpoint record"],
["src/update/update-checkpoint-codec.mjs", "list", "Reflect.ownKeys(value)", 1, "read-only: exact index keys of a decoded checkpoint list"],
],
};

Loading