@clossys/launcher 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1373 -60
- package/contracts/conversation-contract.md +2 -2
- package/contracts/product-ci-workflow.yml +74 -0
- package/contracts/repository-inventory.json +53 -0
- package/dist/admission-fixture.d.ts +168 -0
- package/dist/admission-fixture.d.ts.map +1 -0
- package/dist/admission-fixture.js +467 -0
- package/dist/admission-fixture.js.map +1 -0
- package/dist/admission.d.ts +124 -0
- package/dist/admission.d.ts.map +1 -0
- package/dist/admission.js +804 -0
- package/dist/admission.js.map +1 -0
- package/dist/agents-guide.d.ts +9 -0
- package/dist/agents-guide.d.ts.map +1 -0
- package/dist/agents-guide.js +26 -0
- package/dist/agents-guide.js.map +1 -0
- package/dist/apply-command-options.check.d.ts +12 -0
- package/dist/apply-command-options.check.d.ts.map +1 -0
- package/dist/apply-command-options.check.js +20 -0
- package/dist/apply-command-options.check.js.map +1 -0
- package/dist/apply-plan-cli.d.ts +39 -1
- package/dist/apply-plan-cli.d.ts.map +1 -1
- package/dist/apply-plan-cli.js +432 -15
- package/dist/apply-plan-cli.js.map +1 -1
- package/dist/apply-plan.d.ts +46 -59
- package/dist/apply-plan.d.ts.map +1 -1
- package/dist/apply-plan.js +112 -97
- package/dist/apply-plan.js.map +1 -1
- package/dist/apply-step-fixture.d.ts +87 -0
- package/dist/apply-step-fixture.d.ts.map +1 -0
- package/dist/apply-step-fixture.js +199 -0
- package/dist/apply-step-fixture.js.map +1 -0
- package/dist/apply-store.d.ts +93 -0
- package/dist/apply-store.d.ts.map +1 -0
- package/dist/apply-store.js +625 -0
- package/dist/apply-store.js.map +1 -0
- package/dist/approval-sheet.d.ts +21 -0
- package/dist/approval-sheet.d.ts.map +1 -0
- package/dist/approval-sheet.js +163 -0
- package/dist/approval-sheet.js.map +1 -0
- package/dist/body-command.d.ts +42 -0
- package/dist/body-command.d.ts.map +1 -0
- package/dist/body-command.js +143 -0
- package/dist/body-command.js.map +1 -0
- package/dist/change-set-contract.d.ts +403 -0
- package/dist/change-set-contract.d.ts.map +1 -0
- package/dist/change-set-contract.js +781 -0
- package/dist/change-set-contract.js.map +1 -0
- package/dist/change-set-digest.d.ts +28 -0
- package/dist/change-set-digest.d.ts.map +1 -0
- package/dist/change-set-digest.js +65 -0
- package/dist/change-set-digest.js.map +1 -0
- package/dist/check-cli.d.ts.map +1 -1
- package/dist/check-cli.js +14 -3
- package/dist/check-cli.js.map +1 -1
- package/dist/cli.d.ts +17 -6
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +84 -23
- package/dist/cli.js.map +1 -1
- package/dist/core.d.ts +79 -22
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +843 -268
- package/dist/core.js.map +1 -1
- package/dist/dry-materialize.d.ts +63 -0
- package/dist/dry-materialize.d.ts.map +1 -0
- package/dist/dry-materialize.js +330 -0
- package/dist/dry-materialize.js.map +1 -0
- package/dist/existing-declaration-adoption.check.d.ts +2 -0
- package/dist/existing-declaration-adoption.check.d.ts.map +1 -0
- package/dist/existing-declaration-adoption.check.js +10 -0
- package/dist/existing-declaration-adoption.check.js.map +1 -0
- package/dist/generated/contract-schema.generated.d.ts +97 -0
- package/dist/generated/contract-schema.generated.d.ts.map +1 -0
- package/dist/generated/contract-schema.generated.js +496 -0
- package/dist/generated/contract-schema.generated.js.map +1 -0
- package/dist/generated/package-scope.generated.d.ts +6 -0
- package/dist/generated/package-scope.generated.d.ts.map +1 -0
- package/dist/generated/package-scope.generated.js +10 -0
- package/dist/generated/package-scope.generated.js.map +1 -0
- package/dist/generated/plan-contracts.generated.d.ts +3 -0
- package/dist/generated/plan-contracts.generated.d.ts.map +1 -0
- package/dist/generated/plan-contracts.generated.js +3101 -0
- package/dist/generated/plan-contracts.generated.js.map +1 -0
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +11 -0
- package/dist/host.js.map +1 -1
- package/dist/identity.d.ts +15 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +48 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +35 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/dist/inventory-adoption.d.ts +24 -5
- package/dist/inventory-adoption.d.ts.map +1 -1
- package/dist/inventory-adoption.js +70 -25
- package/dist/inventory-adoption.js.map +1 -1
- package/dist/inventory-choice.d.ts +40 -0
- package/dist/inventory-choice.d.ts.map +1 -0
- package/dist/inventory-choice.js +156 -0
- package/dist/inventory-choice.js.map +1 -0
- package/dist/inventory-contract.d.ts +89 -0
- package/dist/inventory-contract.d.ts.map +1 -0
- package/dist/inventory-contract.js +121 -0
- package/dist/inventory-contract.js.map +1 -0
- package/dist/key-editor.d.ts +30 -0
- package/dist/key-editor.d.ts.map +1 -0
- package/dist/key-editor.js +445 -0
- package/dist/key-editor.js.map +1 -0
- package/dist/ledger-contract.d.ts +190 -0
- package/dist/ledger-contract.d.ts.map +1 -0
- package/dist/ledger-contract.js +555 -0
- package/dist/ledger-contract.js.map +1 -0
- package/dist/ledger-trust.d.ts +90 -0
- package/dist/ledger-trust.d.ts.map +1 -0
- package/dist/ledger-trust.js +203 -0
- package/dist/ledger-trust.js.map +1 -0
- package/dist/lockfile-invariants.d.ts +48 -0
- package/dist/lockfile-invariants.d.ts.map +1 -0
- package/dist/lockfile-invariants.js +375 -0
- package/dist/lockfile-invariants.js.map +1 -0
- package/dist/lockfile-readers.d.ts +72 -0
- package/dist/lockfile-readers.d.ts.map +1 -0
- package/dist/lockfile-readers.js +713 -0
- package/dist/lockfile-readers.js.map +1 -0
- package/dist/lockfile-regen.d.ts +106 -0
- package/dist/lockfile-regen.d.ts.map +1 -0
- package/dist/lockfile-regen.js +760 -0
- package/dist/lockfile-regen.js.map +1 -0
- package/dist/lockfile-tool-env.d.ts +29 -0
- package/dist/lockfile-tool-env.d.ts.map +1 -0
- package/dist/lockfile-tool-env.js +111 -0
- package/dist/lockfile-tool-env.js.map +1 -0
- package/dist/materialize.d.ts +113 -0
- package/dist/materialize.d.ts.map +1 -0
- package/dist/materialize.js +881 -0
- package/dist/materialize.js.map +1 -0
- package/dist/observe-repository.d.ts +90 -0
- package/dist/observe-repository.d.ts.map +1 -0
- package/dist/observe-repository.js +1367 -0
- package/dist/observe-repository.js.map +1 -0
- package/dist/plan-bundle-setup-fixture.d.ts +68 -0
- package/dist/plan-bundle-setup-fixture.d.ts.map +1 -0
- package/dist/plan-bundle-setup-fixture.js +167 -0
- package/dist/plan-bundle-setup-fixture.js.map +1 -0
- package/dist/plan-bundle.d.ts +256 -0
- package/dist/plan-bundle.d.ts.map +1 -0
- package/dist/plan-bundle.js +882 -0
- package/dist/plan-bundle.js.map +1 -0
- package/dist/plan-command.d.ts +29 -0
- package/dist/plan-command.d.ts.map +1 -0
- package/dist/plan-command.js +523 -0
- package/dist/plan-command.js.map +1 -0
- package/dist/plan-contract.d.ts +153 -0
- package/dist/plan-contract.d.ts.map +1 -0
- package/dist/plan-contract.js +61 -0
- package/dist/plan-contract.js.map +1 -0
- package/dist/plan-digest.d.ts +25 -0
- package/dist/plan-digest.d.ts.map +1 -0
- package/dist/plan-digest.js +106 -0
- package/dist/plan-digest.js.map +1 -0
- package/dist/plan-rules.d.ts +23 -0
- package/dist/plan-rules.d.ts.map +1 -0
- package/dist/plan-rules.js +177 -0
- package/dist/plan-rules.js.map +1 -0
- package/dist/planned-bundle.d.ts +20 -0
- package/dist/planned-bundle.d.ts.map +1 -0
- package/dist/planned-bundle.js +191 -0
- package/dist/planned-bundle.js.map +1 -0
- package/dist/product-repository.d.ts +4 -0
- package/dist/product-repository.d.ts.map +1 -1
- package/dist/product-repository.js +9 -1
- package/dist/product-repository.js.map +1 -1
- package/dist/provenance-gate.d.ts +48 -0
- package/dist/provenance-gate.d.ts.map +1 -0
- package/dist/provenance-gate.js +324 -0
- package/dist/provenance-gate.js.map +1 -0
- package/dist/pull-request-body.d.ts +45 -0
- package/dist/pull-request-body.d.ts.map +1 -0
- package/dist/pull-request-body.js +232 -0
- package/dist/pull-request-body.js.map +1 -0
- package/dist/registry-snapshot.d.ts +141 -0
- package/dist/registry-snapshot.d.ts.map +1 -0
- package/dist/registry-snapshot.js +483 -0
- package/dist/registry-snapshot.js.map +1 -0
- package/dist/release-age-edit.d.ts +52 -0
- package/dist/release-age-edit.d.ts.map +1 -0
- package/dist/release-age-edit.js +413 -0
- package/dist/release-age-edit.js.map +1 -0
- package/dist/root-entries.d.ts +36 -0
- package/dist/root-entries.d.ts.map +1 -0
- package/dist/root-entries.js +80 -0
- package/dist/root-entries.js.map +1 -0
- package/dist/setup-template-scripts.d.ts +36 -0
- package/dist/setup-template-scripts.d.ts.map +1 -0
- package/dist/setup-template-scripts.js +568 -0
- package/dist/setup-template-scripts.js.map +1 -0
- package/dist/setup-templates.d.ts +55 -0
- package/dist/setup-templates.d.ts.map +1 -0
- package/dist/setup-templates.js +438 -0
- package/dist/setup-templates.js.map +1 -0
- package/dist/skills.d.ts +34 -1
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +129 -17
- package/dist/skills.js.map +1 -1
- package/dist/status.d.ts +63 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +539 -0
- package/dist/status.js.map +1 -0
- package/dist/types.d.ts +151 -13
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/skeleton/README.md +14 -9
- package/skeleton/package.json +2 -1
- package/skill/SKILL.md +23 -7
- package/skill-catalogue/advisor/SKILL.md +59 -6
- package/skill-catalogue/architect/SKILL.md +2 -2
- package/skill-catalogue/bouncer/SKILL.md +2 -2
- package/skill-catalogue/builder/SKILL.md +2 -2
- package/skill-catalogue/butler/SKILL.md +2 -2
- package/skill-catalogue/controller/SKILL.md +2 -2
- package/skill-catalogue/customer/SKILL.md +2 -2
- package/skill-catalogue/designer/SKILL.md +4 -2
- package/skill-catalogue/giver/SKILL.md +2 -2
- package/skill-catalogue/influencer/SKILL.md +2 -2
- package/skill-catalogue/inspector/SKILL.md +2 -2
- package/skill-catalogue/integrator/SKILL.md +2 -2
- package/skill-catalogue/keeper/SKILL.md +2 -2
- package/skill-catalogue/launcher/SKILL.md +23 -7
- package/skill-catalogue/locksmith/SKILL.md +2 -2
- package/skill-catalogue/messenger/SKILL.md +2 -2
- package/skill-catalogue/observer/SKILL.md +2 -2
- package/skill-catalogue/publisher/SKILL.md +2 -2
- package/skill-catalogue/starter/SKILL.md +3 -2
- package/skill-catalogue/strategist/SKILL.md +12 -4
- package/skill-catalogue/writer/SKILL.md +2 -2
- package/src/admission-fixture.ts +585 -0
- package/src/admission.ts +819 -0
- package/src/agents-guide.ts +29 -0
- package/src/apply-command-options.check.ts +27 -0
- package/src/apply-plan-cli.ts +454 -14
- package/src/apply-plan.ts +112 -124
- package/src/apply-step-fixture.ts +236 -0
- package/src/apply-store.ts +584 -0
- package/src/approval-sheet.ts +170 -0
- package/src/body-command.ts +162 -0
- package/src/change-set-contract.ts +987 -0
- package/src/change-set-digest.ts +70 -0
- package/src/check-cli.ts +14 -3
- package/src/cli.ts +90 -22
- package/src/core.ts +973 -275
- package/src/dry-materialize.ts +353 -0
- package/src/existing-declaration-adoption.check.ts +12 -0
- package/src/generated/contract-schema.generated.ts +520 -0
- package/src/generated/package-scope.generated.ts +10 -0
- package/src/generated/plan-contracts.generated.ts +3101 -0
- package/src/host.ts +10 -0
- package/src/identity.ts +51 -0
- package/src/index.ts +74 -3
- package/src/inventory-adoption.ts +107 -29
- package/src/inventory-choice.ts +172 -0
- package/src/inventory-contract.ts +166 -0
- package/src/key-editor.ts +446 -0
- package/src/ledger-contract.ts +660 -0
- package/src/ledger-trust.ts +272 -0
- package/src/lockfile-invariants.ts +421 -0
- package/src/lockfile-readers.ts +749 -0
- package/src/lockfile-regen.ts +851 -0
- package/src/lockfile-tool-env.ts +131 -0
- package/src/materialize.ts +915 -0
- package/src/observe-repository.ts +1365 -0
- package/src/plan-bundle-setup-fixture.ts +200 -0
- package/src/plan-bundle.ts +1014 -0
- package/src/plan-command.ts +532 -0
- package/src/plan-contract.ts +179 -0
- package/src/plan-digest.ts +102 -0
- package/src/plan-rules.ts +188 -0
- package/src/planned-bundle.ts +211 -0
- package/src/product-repository.ts +10 -1
- package/src/provenance-gate.ts +352 -0
- package/src/pull-request-body.ts +261 -0
- package/src/registry-snapshot.ts +534 -0
- package/src/release-age-edit.ts +430 -0
- package/src/root-entries.ts +81 -0
- package/src/setup-template-scripts.ts +580 -0
- package/src/setup-templates.ts +479 -0
- package/src/skills.ts +161 -18
- package/src/status.ts +557 -0
- package/src/types.ts +148 -13
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
// Provenance gate for a change set (RFC apply-approved-plan: V9, D20, D23, T12).
|
|
2
|
+
//
|
|
3
|
+
// Every package version a set installs or pins must have registry provenance
|
|
4
|
+
// that Integrator verifies. The hub pins `@clossys/integrator` (D23), so this
|
|
5
|
+
// runs the hub's own `integrator-provenance-check` bin against the
|
|
6
|
+
// materialized tree, reads its JSON report strictly, and turns it into one V9
|
|
7
|
+
// verdict for the set. The bin is found only inside the hub's own
|
|
8
|
+
// node_modules, never through npx or PATH, and runs under the same literal
|
|
9
|
+
// environment allow-list the lockfile step uses (lockfile-tool-env.ts): no
|
|
10
|
+
// parent credential, proxy, CA-trust or NODE_OPTIONS value reaches it.
|
|
11
|
+
//
|
|
12
|
+
// Every ambiguity refuses. A report that does not parse to exactly the shape
|
|
13
|
+
// the bin prints, or that disagrees with its own exit code, is indeterminate,
|
|
14
|
+
// never trusted as far as it goes. A package version the bin does not verify is
|
|
15
|
+
// never passed by any exception: it stays violated or indeterminate exactly as
|
|
16
|
+
// Integrator reports it.
|
|
17
|
+
//
|
|
18
|
+
// D20's first-identity-publication exception is deliberately not implemented.
|
|
19
|
+
// It needs evidence that a version is a package's first publication, and the
|
|
20
|
+
// registry snapshot cannot give it: a snapshot taken to resolve a plan records
|
|
21
|
+
// only the one version `latest` names, whatever else the package has published,
|
|
22
|
+
// and Integrator reports "attestation failed" (violated) the same way as "no
|
|
23
|
+
// attestation". Any condition built on the snapshot would therefore also pass a
|
|
24
|
+
// package with earlier releases whose latest release fails verification. Until
|
|
25
|
+
// the snapshot contract records evidence of a first publication, an unattested
|
|
26
|
+
// first publication blocks the apply: fail closed. Nothing here fetches,
|
|
27
|
+
// repairs or retries.
|
|
28
|
+
//
|
|
29
|
+
// The RFC and the snapshot contract are in the public repository, not shipped
|
|
30
|
+
// in this package: docs/rfcs/apply-approved-plan.md and
|
|
31
|
+
// docs/contracts/registry-snapshot.json.
|
|
32
|
+
import { lstat, realpath, stat } from "node:fs/promises";
|
|
33
|
+
import { delimiter, dirname, isAbsolute, join, relative, sep } from "node:path";
|
|
34
|
+
import { compareCodeUnits } from "./change-set-contract.js";
|
|
35
|
+
import { readContractDocument } from "./generated/contract-schema.generated.js";
|
|
36
|
+
import { PACKAGE_SCOPE } from "./generated/package-scope.generated.js";
|
|
37
|
+
import { spawnLockfileTool } from "./lockfile-regen.js";
|
|
38
|
+
import { lockfileToolEnv, prepareLockfileScratch } from "./lockfile-tool-env.js";
|
|
39
|
+
import { canonicalDigest } from "./plan-digest.js";
|
|
40
|
+
import { canonicalRegistrySnapshot, registrySnapshotViolations } from "./registry-snapshot.js";
|
|
41
|
+
/** The bin Integrator installs, found under the hub's node_modules/.bin. */
|
|
42
|
+
export const PROVENANCE_CHECK_BIN = "integrator-provenance-check";
|
|
43
|
+
/** How long one run of the bin may take. */
|
|
44
|
+
export const PROVENANCE_CHECK_TIMEOUT_MS = 120_000;
|
|
45
|
+
/** Bytes kept of the bin's stdout; a report at or past this is treated as truncated. */
|
|
46
|
+
export const PROVENANCE_CHECK_MAX_BUFFER = 1_000_000;
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// The snapshot digest
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
/**
|
|
51
|
+
* The snapshot's digest, as docs/contracts/registry-snapshot.json defines it
|
|
52
|
+
* (in the public repository, not shipped in this package): `sha256:` and the
|
|
53
|
+
* SHA-256 of the canonical JSON of `{registry, packages}`, packages sorted by
|
|
54
|
+
* name, each reduced to name, status, latest and versions, versions sorted by
|
|
55
|
+
* version and kept whole. fetchedAt, fetchedBy, responseSha256, schemaVersion
|
|
56
|
+
* and kind are left out. Throws TypeError for a snapshot that does not validate
|
|
57
|
+
* against the contract: an invalid snapshot has no digest. The gate itself
|
|
58
|
+
* does not read a snapshot; this is exported for the plan binding that a later
|
|
59
|
+
* change wires in.
|
|
60
|
+
*/
|
|
61
|
+
export function registrySnapshotDigest(snapshot) {
|
|
62
|
+
if (registrySnapshotViolations(snapshot).length > 0)
|
|
63
|
+
throw new TypeError("a snapshot that does not validate against the registry snapshot contract has no digest");
|
|
64
|
+
const canonical = canonicalRegistrySnapshot(snapshot);
|
|
65
|
+
return canonicalDigest({
|
|
66
|
+
registry: canonical.registry,
|
|
67
|
+
packages: canonical.packages.map((entry) => ({ name: entry.name, status: entry.status, latest: entry.latest, versions: entry.versions })),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Verdict plumbing
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
const indeterminate = (rule) => ({ check: "V9", verdict: "indeterminate", rule });
|
|
74
|
+
const violated = (rule) => ({ check: "V9", verdict: "violated", rule });
|
|
75
|
+
/** The rules behind a failed V9, one entry per distinct rule, in code-unit order. */
|
|
76
|
+
function distinctRules(verdict, rules) {
|
|
77
|
+
return [...rules].sort(compareCodeUnits).map((rule) => (verdict === "indeterminate" ? indeterminate(rule) : violated(rule)));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The packages this set installs or pins that its base does not already hold,
|
|
81
|
+
* by exact name, each once. Only a `satisfiedInBase` of exactly `true` skips an
|
|
82
|
+
* item: a value other than `true` (`false`, missing, `null`, a string or a
|
|
83
|
+
* number) is gated. Undefined when one name is gated at two versions: the set
|
|
84
|
+
* then asks for something no single report can answer.
|
|
85
|
+
*/
|
|
86
|
+
function gatedPackages(items) {
|
|
87
|
+
const versions = new Map();
|
|
88
|
+
for (const item of items) {
|
|
89
|
+
if ((item.act !== "install" && item.act !== "pin-starter") || item.satisfiedInBase === true)
|
|
90
|
+
continue;
|
|
91
|
+
const earlier = versions.get(item.package.name);
|
|
92
|
+
if (earlier !== undefined && earlier !== item.package.version)
|
|
93
|
+
return undefined;
|
|
94
|
+
versions.set(item.package.name, item.package.version);
|
|
95
|
+
}
|
|
96
|
+
return [...versions].map(([name, version]) => ({ name, version }));
|
|
97
|
+
}
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// Roots and the bin
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
/** An absolute path that is its own realpath and names a directory. */
|
|
102
|
+
async function isCanonicalDirectory(path) {
|
|
103
|
+
if (typeof path !== "string" || !isAbsolute(path))
|
|
104
|
+
return false;
|
|
105
|
+
try {
|
|
106
|
+
if ((await realpath(path)) !== path)
|
|
107
|
+
return false;
|
|
108
|
+
return (await stat(path)).isDirectory();
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** Whether `child` lies strictly inside `parent`; both are already real paths. */
|
|
115
|
+
function isStrictlyInside(parent, child) {
|
|
116
|
+
const path = relative(parent, child);
|
|
117
|
+
return path !== "" && path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute(path);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The verified real path of the hub's Integrator bin, or undefined. The
|
|
121
|
+
* `.bin` entry must resolve to a regular file strictly inside the real path of
|
|
122
|
+
* `node_modules/@clossys/integrator`, and that directory's real path must
|
|
123
|
+
* itself lie strictly inside `<hubRoot>/node_modules`, so a link that leaves
|
|
124
|
+
* the hub, leaves node_modules, or points at some other package's file is
|
|
125
|
+
* refused. `hubRoot` is already its own realpath.
|
|
126
|
+
*
|
|
127
|
+
* The caller spawns the returned real path, not the `.bin` entry: the launch
|
|
128
|
+
* then goes to the very file that was checked, and a link swapped in after the
|
|
129
|
+
* check cannot redirect it.
|
|
130
|
+
*/
|
|
131
|
+
async function verifiedBin(hubRoot) {
|
|
132
|
+
const nodeModules = join(hubRoot, "node_modules");
|
|
133
|
+
try {
|
|
134
|
+
const realBin = await realpath(join(nodeModules, ".bin", PROVENANCE_CHECK_BIN));
|
|
135
|
+
const realIntegrator = await realpath(join(nodeModules, "@clossys", "integrator"));
|
|
136
|
+
if (!(await lstat(realBin)).isFile() || !(await lstat(realIntegrator)).isDirectory())
|
|
137
|
+
return undefined;
|
|
138
|
+
if (!isStrictlyInside(nodeModules, realIntegrator) || !isStrictlyInside(realIntegrator, realBin))
|
|
139
|
+
return undefined;
|
|
140
|
+
return realBin;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const REPORT_KEYS = new Set(["state", "registryBaseUrl", "packages"]);
|
|
147
|
+
const PACKAGE_KEYS = new Set(["name", "installedVersion", "latestVersion", "currencyDistance", "state", "reasons"]);
|
|
148
|
+
const isPlainObject = (value) => {
|
|
149
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
150
|
+
return false;
|
|
151
|
+
const prototype = Object.getPrototypeOf(value);
|
|
152
|
+
return prototype === Object.prototype || prototype === null;
|
|
153
|
+
};
|
|
154
|
+
const isState = (value) => value === "verified" || value === "violated" || value === "indeterminate";
|
|
155
|
+
const isString = (value) => typeof value === "string";
|
|
156
|
+
/** Whether `value` is an own-keyed plain object whose keys all come from `allowed`. */
|
|
157
|
+
function hasOnlyKeys(value, allowed) {
|
|
158
|
+
return Object.keys(value).every((key) => allowed.has(key) && Object.hasOwn(value, key));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Reads the bin's stdout as the closed report it prints, or undefined. Output
|
|
162
|
+
* at or past the capture limit is refused as truncated. The text is read with
|
|
163
|
+
* the same strict reader the contracts use, so a repeated key or a byte order
|
|
164
|
+
* mark is refused as well as anything JSON.parse refuses; no key outside the
|
|
165
|
+
* closed sets is accepted, and each field must have the type the bin gives it.
|
|
166
|
+
* The report's reasons are checked for shape and never used.
|
|
167
|
+
*/
|
|
168
|
+
function readReport(stdout) {
|
|
169
|
+
if (Buffer.byteLength(stdout, "utf8") >= PROVENANCE_CHECK_MAX_BUFFER)
|
|
170
|
+
return undefined;
|
|
171
|
+
let value;
|
|
172
|
+
try {
|
|
173
|
+
value = readContractDocument(Buffer.from(stdout, "utf8"));
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
if (!isPlainObject(value) || !hasOnlyKeys(value, REPORT_KEYS) || Object.keys(value).length !== REPORT_KEYS.size)
|
|
179
|
+
return undefined;
|
|
180
|
+
const { state, registryBaseUrl, packages } = value;
|
|
181
|
+
if (!isState(state) || registryBaseUrl !== PACKAGE_SCOPE.registry || !Array.isArray(packages))
|
|
182
|
+
return undefined;
|
|
183
|
+
const reported = [];
|
|
184
|
+
for (const entry of packages) {
|
|
185
|
+
if (!isPlainObject(entry) || !hasOnlyKeys(entry, PACKAGE_KEYS))
|
|
186
|
+
return undefined;
|
|
187
|
+
const { name, installedVersion, latestVersion, currencyDistance, state: packageState, reasons } = entry;
|
|
188
|
+
if (!isString(name) || !isString(installedVersion) || !isState(packageState))
|
|
189
|
+
return undefined;
|
|
190
|
+
if (!Array.isArray(reasons) || !reasons.every(isString))
|
|
191
|
+
return undefined;
|
|
192
|
+
if (Object.hasOwn(entry, "latestVersion") && !isString(latestVersion))
|
|
193
|
+
return undefined;
|
|
194
|
+
if (Object.hasOwn(entry, "currencyDistance") && !isString(currencyDistance))
|
|
195
|
+
return undefined;
|
|
196
|
+
reported.push({ name, installedVersion, state: packageState });
|
|
197
|
+
}
|
|
198
|
+
return { state, packages: reported };
|
|
199
|
+
}
|
|
200
|
+
/** Whether the report says what its exit code says: exit 0 all verified, exit 1 a violation and nothing indeterminate. */
|
|
201
|
+
function agreesWithExit(status, report) {
|
|
202
|
+
if (status === 0)
|
|
203
|
+
return report.state === "verified" && report.packages.length > 0 && report.packages.every((entry) => entry.state === "verified");
|
|
204
|
+
return report.state === "violated" && report.packages.some((entry) => entry.state === "violated") && report.packages.every((entry) => entry.state !== "indeterminate");
|
|
205
|
+
}
|
|
206
|
+
/** The rule an unsuccessful launch maps to, or undefined when the bin ran to exit 0 or 1 with no failure flag. */
|
|
207
|
+
function launchFailure(result) {
|
|
208
|
+
if (result.failure === "not-found")
|
|
209
|
+
return "engine-missing-bin";
|
|
210
|
+
if (result.failure === "timeout")
|
|
211
|
+
return "engine-timeout";
|
|
212
|
+
if (result.failure !== undefined)
|
|
213
|
+
return "engine-failed";
|
|
214
|
+
if (result.status === 2)
|
|
215
|
+
return "engine-indeterminate";
|
|
216
|
+
if (result.status !== 0 && result.status !== 1)
|
|
217
|
+
return "engine-failed";
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
// The gate
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
/**
|
|
224
|
+
* V9 for one change set. Satisfied without touching anything when the set
|
|
225
|
+
* installs or pins nothing its base lacks. Otherwise runs the hub's
|
|
226
|
+
* `integrator-provenance-check` against `tree` and judges each gated package
|
|
227
|
+
* by exact name and exact version: satisfied only when every one is verified
|
|
228
|
+
* at that version, with no exception for an unverified one. Other `@clossys`
|
|
229
|
+
* packages in the report never gate: an unrelated violated legacy pin passes
|
|
230
|
+
* when every gated package is verified. Indeterminate wins over violated; the
|
|
231
|
+
* result lists each distinct rule once, in code-unit order, and never mixes a
|
|
232
|
+
* satisfied entry with a refusal. Never throws for a bad input; everything it
|
|
233
|
+
* cannot rely on is an indeterminate entry. The input carries no ledger or
|
|
234
|
+
* snapshot: nothing here reads one.
|
|
235
|
+
*/
|
|
236
|
+
export async function checkSetProvenance(input, ports = {}) {
|
|
237
|
+
// 1. The gated set.
|
|
238
|
+
const gated = gatedPackages(input.items);
|
|
239
|
+
if (gated === undefined)
|
|
240
|
+
return [indeterminate("gated-package-conflict")];
|
|
241
|
+
if (gated.length === 0)
|
|
242
|
+
return [{ check: "V9", verdict: "satisfied" }];
|
|
243
|
+
// 2 and 3. Roots, then the bin.
|
|
244
|
+
if (!(await isCanonicalDirectory(input.tree)) || !(await isCanonicalDirectory(input.hubRoot)))
|
|
245
|
+
return [indeterminate("root-not-canonical")];
|
|
246
|
+
const command = await verifiedBin(input.hubRoot);
|
|
247
|
+
if (command === undefined)
|
|
248
|
+
return [indeterminate("engine-missing-bin")];
|
|
249
|
+
// 4. Launch, in a scratch working directory, under the allow-list environment.
|
|
250
|
+
const run = ports.spawn ?? spawnLockfileTool;
|
|
251
|
+
let scratch;
|
|
252
|
+
try {
|
|
253
|
+
scratch = await prepareLockfileScratch();
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
return [indeterminate("engine-failed")];
|
|
257
|
+
}
|
|
258
|
+
let result;
|
|
259
|
+
try {
|
|
260
|
+
const env = lockfileToolEnv({ tool: "npm", scratch: scratch.path, registry: PACKAGE_SCOPE.registry, corepack: false, parent: process.env });
|
|
261
|
+
// The bin's `#!/usr/bin/env node` must find the node that is running this code.
|
|
262
|
+
env.PATH = `${dirname(process.execPath)}${delimiter}${env.PATH ?? ""}`;
|
|
263
|
+
result = await run({
|
|
264
|
+
command,
|
|
265
|
+
args: ["--cwd", input.tree, "--registry", PACKAGE_SCOPE.registry],
|
|
266
|
+
cwd: scratch.path,
|
|
267
|
+
env,
|
|
268
|
+
timeoutMs: PROVENANCE_CHECK_TIMEOUT_MS,
|
|
269
|
+
maxBuffer: PROVENANCE_CHECK_MAX_BUFFER,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
result = undefined;
|
|
274
|
+
}
|
|
275
|
+
finally {
|
|
276
|
+
try {
|
|
277
|
+
await scratch.remove();
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
// The verdict does not depend on the scratch's removal, and it is under the OS temp root.
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (result === undefined)
|
|
284
|
+
return [indeterminate("engine-failed")];
|
|
285
|
+
const failure = launchFailure(result);
|
|
286
|
+
if (failure !== undefined)
|
|
287
|
+
return [indeterminate(failure)];
|
|
288
|
+
// 5 and 6. The report, and whether it agrees with its exit code.
|
|
289
|
+
const report = typeof result.stdout === "string" ? readReport(result.stdout) : undefined;
|
|
290
|
+
if (report === undefined)
|
|
291
|
+
return [indeterminate("report-unreadable")];
|
|
292
|
+
if (!agreesWithExit(result.status, report))
|
|
293
|
+
return [indeterminate("report-contradicts-exit")];
|
|
294
|
+
// 7 and 8. Each gated package, by exact name and exact version.
|
|
295
|
+
const indeterminateRules = new Set();
|
|
296
|
+
const violatedRules = new Set();
|
|
297
|
+
for (const pkg of gated) {
|
|
298
|
+
const entries = report.packages.filter((entry) => entry.name === pkg.name);
|
|
299
|
+
if (entries.length === 0) {
|
|
300
|
+
indeterminateRules.add("package-not-in-report");
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (entries.some((entry) => entry.installedVersion !== pkg.version)) {
|
|
304
|
+
violatedRules.add("version-mismatch");
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
// The report already agrees with its exit code, so no entry is indeterminate; this is the same refusal kept local.
|
|
308
|
+
if (entries.some((entry) => entry.state === "indeterminate")) {
|
|
309
|
+
indeterminateRules.add("package-indeterminate");
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (entries.every((entry) => entry.state === "verified"))
|
|
313
|
+
continue;
|
|
314
|
+
// Unverified at the act's own version: violated, with no exception.
|
|
315
|
+
violatedRules.add("provenance-unverified");
|
|
316
|
+
}
|
|
317
|
+
// 9. Indeterminate wins over violated; a satisfied entry never sits beside a refusal.
|
|
318
|
+
if (indeterminateRules.size > 0)
|
|
319
|
+
return distinctRules("indeterminate", indeterminateRules);
|
|
320
|
+
if (violatedRules.size > 0)
|
|
321
|
+
return distinctRules("violated", violatedRules);
|
|
322
|
+
return [{ check: "V9", verdict: "satisfied" }];
|
|
323
|
+
}
|
|
324
|
+
//# sourceMappingURL=provenance-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenance-gate.js","sourceRoot":"","sources":["../src/provenance-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ApprovalBinding, RepositoryChangeSet } from "./change-set-contract.js";
|
|
2
|
+
export type PullRequestRefusalReason = "change-set-invalid" | "title-mismatch" | "binding-invalid" | "binding-phase-mismatch" | "task-record-invalid" | "supersedes-invalid" | "value-unsafe" | "body-invalid" | "render-failed";
|
|
3
|
+
/** A refusal: a fixed token, never a value. */
|
|
4
|
+
export interface PullRequestRefusal {
|
|
5
|
+
readonly state: "refused";
|
|
6
|
+
readonly reason: PullRequestRefusalReason;
|
|
7
|
+
}
|
|
8
|
+
export interface RenderPullRequestInput {
|
|
9
|
+
readonly set: RepositoryChangeSet;
|
|
10
|
+
/** What admission decided for this set (decideSetBinding). It is shown, not checked here beyond its shape. */
|
|
11
|
+
readonly binding: ApprovalBinding;
|
|
12
|
+
/** The number of the task-record issue in the target repository: a positive safe integer. */
|
|
13
|
+
readonly taskRecord: number;
|
|
14
|
+
/**
|
|
15
|
+
* The numbers of the pull requests of older change sets this one replaces (RFC section 6, D6): each a positive safe integer,
|
|
16
|
+
* none repeated and none the task record. Written ascending under `## Supersedes`. Absent or empty: no such section, and the
|
|
17
|
+
* body is the one it was without this option.
|
|
18
|
+
*/
|
|
19
|
+
readonly supersedes?: readonly number[];
|
|
20
|
+
}
|
|
21
|
+
export interface PullRequestText {
|
|
22
|
+
readonly state: "rendered";
|
|
23
|
+
readonly title: string;
|
|
24
|
+
readonly body: string;
|
|
25
|
+
/** `sha256:` and the hex SHA-256 of the body's UTF-8 bytes. */
|
|
26
|
+
readonly bodySha256: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The change-set digest a body carries, or null. Exactly one line must be
|
|
30
|
+
* exactly `<!-- clossys-change-set: sha256:<64 hex> -->`, and the marker's
|
|
31
|
+
* name must appear nowhere else in the body, so a second marker line, or a
|
|
32
|
+
* marker inside another line, reads as none.
|
|
33
|
+
*/
|
|
34
|
+
export declare function readChangeSetMarker(body: string): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* The pull request title and body for one stored change set. The title is
|
|
37
|
+
* exactly `set.pullRequest.title`, which must be `Clossys: apply plan ` and
|
|
38
|
+
* the first 12 hex digits of the set's own digest. Refuses a set that fails
|
|
39
|
+
* validateRepositoryChangeSet or whose digest does not recompute, a malformed
|
|
40
|
+
* binding, an `admitted` binding on a setup set, a task record that is not a
|
|
41
|
+
* positive safe integer, and any value it cannot prove safe to write. Never
|
|
42
|
+
* throws.
|
|
43
|
+
*/
|
|
44
|
+
export declare function renderPullRequest(input: RenderPullRequestInput): PullRequestText | PullRequestRefusal;
|
|
45
|
+
//# sourceMappingURL=pull-request-body.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull-request-body.d.ts","sourceRoot":"","sources":["../src/pull-request-body.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,eAAe,EAAiB,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAGpG,MAAM,MAAM,wBAAwB,GAChC,oBAAoB,GACpB,gBAAgB,GAChB,iBAAiB,GACjB,wBAAwB,GACxB,qBAAqB,GACrB,oBAAoB,GACpB,cAAc,GACd,cAAc,GACd,eAAe,CAAC;AAEpB,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC;CAC3C;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,GAAG,EAAE,mBAAmB,CAAC;IAClC,8GAA8G;IAC9G,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,6FAA6F;IAC7F,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAyCD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM/D;AA0BD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,eAAe,GAAG,kBAAkB,CAMrG"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// The title and body of one repository's pull request (issue #1178; the apply
|
|
2
|
+
// RFC's section 6 body marker, threat T6, decision D31 and section 12.7, in the
|
|
3
|
+
// public repository, not shipped in this package). A pure function of three
|
|
4
|
+
// inputs: a stored change set, the ApprovalBinding admission decided for it,
|
|
5
|
+
// and the number of the task-record issue. It reads nothing, writes nothing and
|
|
6
|
+
// opens nothing; a later step decides the binding, passes it in, records
|
|
7
|
+
// `bodySha256` and hands the text to the agent that opens the pull request.
|
|
8
|
+
// The caller may also pass the numbers of the older pull requests this one
|
|
9
|
+
// replaces; they are written ascending under `## Supersedes` and nothing else.
|
|
10
|
+
//
|
|
11
|
+
// This is a public-safety surface, so every ambiguity refuses:
|
|
12
|
+
//
|
|
13
|
+
// - The text carries ids, act names, versions and digests only: never a brief,
|
|
14
|
+
// plan prose, `texts`, a key value, a path or a file's contents. Every value
|
|
15
|
+
// is matched against its own strict pattern before it is written, and any
|
|
16
|
+
// that holds `<`, `>`, a backtick, `|`, a carriage return or a line feed is
|
|
17
|
+
// refused, so no value can start a marker, close a code span or add a line.
|
|
18
|
+
// - Each value is written inside a code span, so a package name such as
|
|
19
|
+
// `@scope/name` is never read by GitHub as a mention.
|
|
20
|
+
// - The body opens with the one marker line and holds no other, and it is read
|
|
21
|
+
// back with readChangeSetMarker before it is returned.
|
|
22
|
+
// - The renderer does not decide the binding: it shows what the caller passes.
|
|
23
|
+
// It does not check that the task-record issue exists, and it cannot stop a
|
|
24
|
+
// pull request's body being edited after it is opened; `bodySha256` and the
|
|
25
|
+
// marker are how a later `status` sees that.
|
|
26
|
+
//
|
|
27
|
+
// A refusal carries a fixed token, never an id, a digest or any input text.
|
|
28
|
+
import { createHash } from "node:crypto";
|
|
29
|
+
import { validateRepositoryChangeSet } from "./change-set-contract.js";
|
|
30
|
+
import { changeSetDigest } from "./change-set-digest.js";
|
|
31
|
+
const DIGEST = /^sha256:[0-9a-f]{64}$/u;
|
|
32
|
+
const TITLE = /^Clossys: apply plan [0-9a-f]{12}$/u;
|
|
33
|
+
const MARKER_LINE = /^<!-- clossys-change-set: (sha256:[0-9a-f]{64}) -->$/u;
|
|
34
|
+
const REPOSITORY_ID = /^(?:[A-Za-z0-9][A-Za-z0-9-]{0,38}\/)?[A-Za-z0-9._-]{1,100}$/u;
|
|
35
|
+
// 355 characters: a repository id of up to 140, a colon and a package name of up to 214 (the planItem the contract derives, C16).
|
|
36
|
+
const ITEM_ID = /^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,354}$/u;
|
|
37
|
+
const ID_TOKEN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
38
|
+
const PACKAGE_NAME = /^(?=.{1,214}$)@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$/u;
|
|
39
|
+
const EXACT_VERSION = /^(?:0|[1-9][0-9]{0,15})\.(?:0|[1-9][0-9]{0,15})\.(?:0|[1-9][0-9]{0,15})$/u;
|
|
40
|
+
const UNSAFE = /[<>`|\r\n]/u;
|
|
41
|
+
const PRINTABLE_LINES = /^[\x20-\x7e\n]*$/u;
|
|
42
|
+
const MARKER_WORD = /clossys-change-set/giu;
|
|
43
|
+
const PHASES = new Set(["setup", "apply"]);
|
|
44
|
+
const ACTS = new Set([
|
|
45
|
+
"write-record",
|
|
46
|
+
"compose-skills",
|
|
47
|
+
"install",
|
|
48
|
+
"pin-starter",
|
|
49
|
+
"exempt-release-age",
|
|
50
|
+
"declare-root-entry",
|
|
51
|
+
"write-ledger",
|
|
52
|
+
"add-caller-workflow",
|
|
53
|
+
"write-starter-request",
|
|
54
|
+
"add-ci-template",
|
|
55
|
+
"add-path-scope-job",
|
|
56
|
+
]);
|
|
57
|
+
const refuse = (reason) => ({ state: "refused", reason });
|
|
58
|
+
/** A value that may be interpolated: a string that matches its own pattern and holds nothing that could start a marker, close a code span or add a line. */
|
|
59
|
+
function safe(value, pattern) {
|
|
60
|
+
return typeof value === "string" && !UNSAFE.test(value) && pattern.test(value);
|
|
61
|
+
}
|
|
62
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
63
|
+
const sha256Of = (text) => `sha256:${createHash("sha256").update(Buffer.from(text, "utf8")).digest("hex")}`;
|
|
64
|
+
/**
|
|
65
|
+
* The change-set digest a body carries, or null. Exactly one line must be
|
|
66
|
+
* exactly `<!-- clossys-change-set: sha256:<64 hex> -->`, and the marker's
|
|
67
|
+
* name must appear nowhere else in the body, so a second marker line, or a
|
|
68
|
+
* marker inside another line, reads as none.
|
|
69
|
+
*/
|
|
70
|
+
export function readChangeSetMarker(body) {
|
|
71
|
+
if (typeof body !== "string")
|
|
72
|
+
return null;
|
|
73
|
+
const found = body.split("\n").flatMap((line) => MARKER_LINE.exec(line)?.[1] ?? []);
|
|
74
|
+
if (found.length !== 1)
|
|
75
|
+
return null;
|
|
76
|
+
if ((body.match(MARKER_WORD) ?? []).length !== 1)
|
|
77
|
+
return null;
|
|
78
|
+
return found[0];
|
|
79
|
+
}
|
|
80
|
+
function itemLine(item) {
|
|
81
|
+
if (!safe(item.id, ITEM_ID) || typeof item.act !== "string" || !ACTS.has(item.act))
|
|
82
|
+
return null;
|
|
83
|
+
if (item.act !== "install" && item.act !== "pin-starter")
|
|
84
|
+
return `- \`${item.id}\` \`${item.act}\``;
|
|
85
|
+
if (!safe(item.package.name, PACKAGE_NAME) || !safe(item.package.version, EXACT_VERSION))
|
|
86
|
+
return null;
|
|
87
|
+
return `- \`${item.id}\` \`${item.act}\` \`${item.package.name}@${item.package.version}\``;
|
|
88
|
+
}
|
|
89
|
+
function bindingLines(binding, set) {
|
|
90
|
+
if (!isRecord(binding))
|
|
91
|
+
return refuse("binding-invalid");
|
|
92
|
+
const keys = Object.keys(binding).sort().join(",");
|
|
93
|
+
if (binding.kind === "approved") {
|
|
94
|
+
if (keys !== "kind,subjectDigest" || !safe(binding.subjectDigest, DIGEST))
|
|
95
|
+
return refuse("binding-invalid");
|
|
96
|
+
return ["- Binding: `approved`", `- Approved subject: \`${binding.subjectDigest}\``];
|
|
97
|
+
}
|
|
98
|
+
if (binding.kind === "admitted") {
|
|
99
|
+
if (keys !== "kind,setupChangeSet,subjectDigest" || !safe(binding.subjectDigest, DIGEST) || !safe(binding.setupChangeSet, DIGEST))
|
|
100
|
+
return refuse("binding-invalid");
|
|
101
|
+
if (set.phase !== "apply")
|
|
102
|
+
return refuse("binding-phase-mismatch");
|
|
103
|
+
// Admission never binds a set to itself, or to the bundle that holds the set.
|
|
104
|
+
if (binding.setupChangeSet === set.changeSetDigest || binding.subjectDigest === set.bundle)
|
|
105
|
+
return refuse("binding-invalid");
|
|
106
|
+
return ["- Binding: `admitted`", `- Admitted subject: \`${binding.subjectDigest}\``, `- Setup change set: \`${binding.setupChangeSet}\``];
|
|
107
|
+
}
|
|
108
|
+
return refuse("binding-invalid");
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The pull request title and body for one stored change set. The title is
|
|
112
|
+
* exactly `set.pullRequest.title`, which must be `Clossys: apply plan ` and
|
|
113
|
+
* the first 12 hex digits of the set's own digest. Refuses a set that fails
|
|
114
|
+
* validateRepositoryChangeSet or whose digest does not recompute, a malformed
|
|
115
|
+
* binding, an `admitted` binding on a setup set, a task record that is not a
|
|
116
|
+
* positive safe integer, and any value it cannot prove safe to write. Never
|
|
117
|
+
* throws.
|
|
118
|
+
*/
|
|
119
|
+
export function renderPullRequest(input) {
|
|
120
|
+
try {
|
|
121
|
+
return render(input);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return refuse("render-failed");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function render(input) {
|
|
128
|
+
const { taskRecord } = input;
|
|
129
|
+
if (typeof taskRecord !== "number" || !Number.isSafeInteger(taskRecord) || taskRecord < 1)
|
|
130
|
+
return refuse("task-record-invalid");
|
|
131
|
+
// Read once, like the set: what is checked is what is written. Only a list of distinct positive safe integers other than the task record passes.
|
|
132
|
+
let supersedes = [];
|
|
133
|
+
if (input.supersedes !== undefined) {
|
|
134
|
+
let read;
|
|
135
|
+
try {
|
|
136
|
+
read = JSON.parse(JSON.stringify(input.supersedes));
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return refuse("supersedes-invalid");
|
|
140
|
+
}
|
|
141
|
+
if (!Array.isArray(read))
|
|
142
|
+
return refuse("supersedes-invalid");
|
|
143
|
+
for (const entry of read)
|
|
144
|
+
if (typeof entry !== "number" || !Number.isSafeInteger(entry) || entry < 1 || entry === taskRecord)
|
|
145
|
+
return refuse("supersedes-invalid");
|
|
146
|
+
supersedes = read.slice().sort((left, right) => left - right);
|
|
147
|
+
if (supersedes.some((entry, index) => index > 0 && entry === supersedes[index - 1]))
|
|
148
|
+
return refuse("supersedes-invalid");
|
|
149
|
+
}
|
|
150
|
+
// One snapshot, so a getter or a proxy cannot show the checks one value and the text another.
|
|
151
|
+
let set;
|
|
152
|
+
let binding;
|
|
153
|
+
try {
|
|
154
|
+
set = JSON.parse(JSON.stringify(input.set));
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return refuse("change-set-invalid");
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
binding = JSON.parse(JSON.stringify(input.binding));
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return refuse("binding-invalid");
|
|
164
|
+
}
|
|
165
|
+
if (!validateRepositoryChangeSet(set).valid || changeSetDigest(set) !== set.changeSetDigest)
|
|
166
|
+
return refuse("change-set-invalid");
|
|
167
|
+
// The contract's rule C5 already ties the title to the digest; this is the same rule, held here too.
|
|
168
|
+
const digest = set.changeSetDigest;
|
|
169
|
+
if (!safe(digest, DIGEST))
|
|
170
|
+
return refuse("change-set-invalid");
|
|
171
|
+
if (!safe(set.pullRequest.title, TITLE) || set.pullRequest.title !== `Clossys: apply plan ${digest.slice("sha256:".length, "sha256:".length + 12)}`)
|
|
172
|
+
return refuse("title-mismatch");
|
|
173
|
+
const bound = bindingLines(binding, set);
|
|
174
|
+
if (!Array.isArray(bound))
|
|
175
|
+
return bound;
|
|
176
|
+
const repositoryId = set.repository.id;
|
|
177
|
+
if (!safe(repositoryId, REPOSITORY_ID) || repositoryId === "." || repositoryId === ".." || repositoryId.endsWith("/.") || repositoryId.endsWith("/.."))
|
|
178
|
+
return refuse("value-unsafe");
|
|
179
|
+
if (typeof set.phase !== "string" || !PHASES.has(set.phase))
|
|
180
|
+
return refuse("value-unsafe");
|
|
181
|
+
if (!safe(set.planDigest, DIGEST) || !safe(set.bundle, DIGEST))
|
|
182
|
+
return refuse("value-unsafe");
|
|
183
|
+
const items = [];
|
|
184
|
+
for (const item of set.items) {
|
|
185
|
+
const line = itemLine(item);
|
|
186
|
+
if (line === null)
|
|
187
|
+
return refuse("value-unsafe");
|
|
188
|
+
items.push(line);
|
|
189
|
+
}
|
|
190
|
+
const notes = [];
|
|
191
|
+
for (const deferral of set.deferred) {
|
|
192
|
+
if (!safe(deferral.planItem, ITEM_ID) || !safe(deferral.reason, ID_TOKEN))
|
|
193
|
+
return refuse("value-unsafe");
|
|
194
|
+
notes.push(`- Deferred: \`${deferral.planItem}\` \`${deferral.reason}\``);
|
|
195
|
+
}
|
|
196
|
+
for (const refusal of set.refused) {
|
|
197
|
+
if (!safe(refusal.item, ITEM_ID) || !safe(refusal.reason, ID_TOKEN))
|
|
198
|
+
return refuse("value-unsafe");
|
|
199
|
+
notes.push(`- Refused: \`${refusal.item}\` \`${refusal.reason}\``);
|
|
200
|
+
}
|
|
201
|
+
const lines = [
|
|
202
|
+
`<!-- clossys-change-set: ${digest} -->`,
|
|
203
|
+
"",
|
|
204
|
+
"## Change set",
|
|
205
|
+
"",
|
|
206
|
+
`- Repository: \`${repositoryId}\``,
|
|
207
|
+
`- Phase: \`${set.phase}\``,
|
|
208
|
+
`- Change set: \`${digest}\``,
|
|
209
|
+
`- Plan: \`${set.planDigest}\``,
|
|
210
|
+
`- Bundle: \`${set.bundle}\``,
|
|
211
|
+
"",
|
|
212
|
+
"## Binding",
|
|
213
|
+
"",
|
|
214
|
+
...bound,
|
|
215
|
+
"",
|
|
216
|
+
"## Items",
|
|
217
|
+
"",
|
|
218
|
+
...items,
|
|
219
|
+
...(notes.length === 0 ? [] : ["", "## Not applied", "", ...notes]),
|
|
220
|
+
...(supersedes.length === 0 ? [] : ["", "## Supersedes", "", ...supersedes.map((number) => `- #${number}`)]),
|
|
221
|
+
"",
|
|
222
|
+
"## Task record",
|
|
223
|
+
"",
|
|
224
|
+
`- #${taskRecord}`,
|
|
225
|
+
];
|
|
226
|
+
const body = `${lines.join("\n")}\n`;
|
|
227
|
+
// The last word: printable ASCII and line feeds only, no line feed at the end but one, and the marker reads back as this set's digest.
|
|
228
|
+
if (!PRINTABLE_LINES.test(body) || body.endsWith("\n\n") || readChangeSetMarker(body) !== digest || !body.startsWith(`<!-- clossys-change-set: ${digest} -->\n`))
|
|
229
|
+
return refuse("body-invalid");
|
|
230
|
+
return { state: "rendered", title: set.pullRequest.title, body, bodySha256: sha256Of(body) };
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=pull-request-body.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull-request-body.js","sourceRoot":"","sources":["../src/pull-request-body.ts"],"names":[],"mappings":"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"}
|