cli-validator 7.0.32 → 7.0.34

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.
@@ -0,0 +1,498 @@
1
+ import { publicBindSites } from "./validator-compliance.mjs";
2
+ import { createHash, createPublicKey, verify as verifySignature } from "node:crypto";
3
+
4
+ const REQ = "validator.skill.request/1.0";
5
+ const RES = "validator.skill.response/1.0";
6
+ const ERR = "validator.skill.error/1.0";
7
+ const NAME = "validator";
8
+ const COMPILER_VERSION = "v7.0.34";
9
+ const CATALOG_SCHEMA = "cli.tax.skill-catalog/1.0";
10
+ const RECEIPT_SCHEMA = "validator.execution-receipt/1.0";
11
+ const VALIDATION_SUBJECT_SCHEMA = "validator.validation-subject/1.0";
12
+ const GOLDEN_BASELINE_SCHEMA = "validator.golden-baseline/1.0";
13
+ const TEST_EVIDENCE_SCHEMA = "cli.tax.test-evidence/1.0";
14
+ const RECEIPT_PUBLIC_KEY_ENV = "CLITAX_VALIDATOR_RECEIPT_PUBLIC_KEY";
15
+ const MAX_RECEIPT_LIFETIME_MS = 10 * 60 * 1000;
16
+ const ID_PATTERN = "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$";
17
+ const MEMBER_PATTERN = "^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$";
18
+ const CHAIN_PATTERN = "^chn-[0-9a-f-]{36}$";
19
+ const SHA_PATTERN = "^[0-9a-f]{64}$";
20
+ const idRegex = new RegExp(ID_PATTERN);
21
+ const memberRegex = new RegExp(MEMBER_PATTERN);
22
+ const chainRegex = new RegExp(CHAIN_PATTERN);
23
+ const shaRegex = new RegExp(SHA_PATTERN);
24
+
25
+ const OPS = ["capabilities","help","intake","plan","validate-structure","security-scan","compliance-audit","functional-verify","sandbox-run","fuzz-input","perf-benchmark","intrusive-test","verdict"];
26
+ const PURE = new Set(["capabilities","help","intake","plan","validate-structure","security-scan","compliance-audit","functional-verify","verdict"]);
27
+ const LOCAL_ONLY = new Set(["sandbox-run","fuzz-input","perf-benchmark","intrusive-test"]);
28
+ const CATALOG = OPS.map((operation) => ({ operation, summary: operation }));
29
+
30
+ const stringSchema = (extra = {}) => ({ type: "string", ...extra });
31
+ const arraySchema = (items, extra = {}) => ({ type: "array", items, ...extra });
32
+ const objectSchema = (properties, required = [], extra = {}) => ({
33
+ type: "object", properties, required, additionalProperties: false, ...extra,
34
+ });
35
+ const anyObjectSchema = { type: "object" };
36
+ const findingSchema = objectSchema({
37
+ severity: { enum: ["P0", "P1", "P2"] }, ruleId: stringSchema({ minLength: 1 }),
38
+ entityRef: stringSchema({ minLength: 1 }), message: stringSchema({ minLength: 1 }),
39
+ evidence: anyObjectSchema,
40
+ }, ["severity", "ruleId", "entityRef", "message", "evidence"]);
41
+ const receiptSchema = objectSchema({
42
+ schemaVersion: { const: RECEIPT_SCHEMA }, keyId: stringSchema({ pattern: SHA_PATTERN }),
43
+ nonce: stringSchema({ pattern: ID_PATTERN, minLength: 16, maxLength: 128 }), subjectDigest: stringSchema({ pattern: SHA_PATTERN }),
44
+ issuedAt: stringSchema({ format: "date-time" }), expiresAt: stringSchema({ format: "date-time" }),
45
+ result: objectSchema({ runner: { const: "trusted-runner" }, passed: { type: "boolean" },
46
+ exitCode: { type: "integer" }, durationMs: { type: "number", minimum: 0 },
47
+ summary: stringSchema({ minLength: 1 }) }, ["runner", "passed", "exitCode", "durationMs", "summary"]),
48
+ signature: stringSchema({ minLength: 1 }),
49
+ }, ["schemaVersion", "keyId", "nonce", "subjectDigest", "issuedAt", "expiresAt", "result", "signature"]);
50
+ const baselineSchema = objectSchema({
51
+ schemaVersion: { const: GOLDEN_BASELINE_SCHEMA }, baselineId: stringSchema({ pattern: ID_PATTERN }),
52
+ source: objectSchema({ kind: { enum: ["repository-commit", "artifact", "approved-record"] },
53
+ locator: stringSchema({ minLength: 1 }), digestSha256: stringSchema({ pattern: SHA_PATTERN }) },
54
+ ["kind", "locator", "digestSha256"]),
55
+ version: stringSchema({ pattern: ID_PATTERN }), frozen: { const: true },
56
+ frozenAt: stringSchema({ format: "date-time" }), frozenBy: stringSchema({ pattern: ID_PATTERN }),
57
+ testsSha256: stringSchema({ pattern: SHA_PATTERN }),
58
+ }, ["schemaVersion", "baselineId", "source", "version", "frozen", "frozenAt", "frozenBy", "testsSha256"]);
59
+ const contractsSchema = objectSchema({
60
+ aimlock: objectSchema({ goalId: stringSchema({ pattern: ID_PATTERN }),
61
+ scopeContractSha256: stringSchema({ pattern: SHA_PATTERN }), snapshotSha256: stringSchema({ pattern: SHA_PATTERN }) },
62
+ ["goalId", "scopeContractSha256", "snapshotSha256"]),
63
+ blueprint: objectSchema({ blueprintId: stringSchema({ pattern: ID_PATTERN }),
64
+ acceptanceReportSha256: stringSchema({ pattern: SHA_PATTERN }) }, ["blueprintId", "acceptanceReportSha256"]),
65
+ archguard: objectSchema({ contractSha256: stringSchema({ pattern: SHA_PATTERN }),
66
+ ledgerSha256: stringSchema({ pattern: SHA_PATTERN }),
67
+ driftStatus: { enum: ["green", "yellow", "red"] } },
68
+ ["contractSha256", "ledgerSha256", "driftStatus"]),
69
+ });
70
+ const validationFileSchema = objectSchema({
71
+ path: stringSchema({ minLength: 1, maxLength: 500 }),
72
+ sha256: stringSchema({ pattern: SHA_PATTERN }),
73
+ }, ["path", "sha256"]);
74
+ const subjectSchema = objectSchema({
75
+ schemaVersion: { const: VALIDATION_SUBJECT_SCHEMA }, artifactSha256: stringSchema({ pattern: SHA_PATTERN }),
76
+ memberId: stringSchema({ pattern: MEMBER_PATTERN }), chainId: stringSchema({ pattern: CHAIN_PATTERN }),
77
+ executedAt: stringSchema({ format: "date-time" }), files: arraySchema(validationFileSchema, { minItems: 1 }),
78
+ validationRunId: stringSchema({ pattern: ID_PATTERN }), planId: stringSchema({ pattern: ID_PATTERN }),
79
+ tests: arraySchema(anyObjectSchema, { minItems: 1 }), policy: anyObjectSchema,
80
+ goldenBaseline: baselineSchema, contracts: contractsSchema,
81
+ }, ["schemaVersion", "artifactSha256", "validationRunId", "planId", "tests", "policy", "goldenBaseline"]);
82
+ const testEvidenceProperties = {
83
+ schemaVersion: { const: TEST_EVIDENCE_SCHEMA }, evidenceId: stringSchema({ pattern: ID_PATTERN }),
84
+ kind: { enum: ["test", "build", "lint", "security", "benchmark"] }, command: stringSchema({ minLength: 1 }),
85
+ exitCode: { type: "integer" }, durationMs: { type: "number", minimum: 0 },
86
+ summary: stringSchema({ minLength: 1 }), artifactSha256: stringSchema({ pattern: SHA_PATTERN }),
87
+ subject: subjectSchema, subjectDigest: stringSchema({ pattern: SHA_PATTERN }), receipt: receiptSchema,
88
+ };
89
+ const testEvidenceRequired = ["schemaVersion", "evidenceId", "kind", "runner", "command", "exitCode", "durationMs", "summary"];
90
+ const testEvidenceSchema = {
91
+ oneOf: [
92
+ objectSchema({ ...testEvidenceProperties, runner: { const: "local" } }, testEvidenceRequired),
93
+ objectSchema({ ...testEvidenceProperties, runner: { const: "trusted-runner" } },
94
+ [...testEvidenceRequired, "artifactSha256", "subject", "subjectDigest", "receipt"]),
95
+ ],
96
+ };
97
+ const riskEntrySchema = objectSchema({
98
+ riskId: stringSchema({ pattern: ID_PATTERN }), findingRuleId: stringSchema({ minLength: 1 }),
99
+ findingEntityRef: stringSchema({ minLength: 1 }), owner: stringSchema({ pattern: ID_PATTERN }),
100
+ mitigation: stringSchema({ minLength: 1 }), acceptedBy: stringSchema({ pattern: ID_PATTERN }),
101
+ acceptedAt: stringSchema({ format: "date-time" }),
102
+ }, ["riskId", "findingRuleId", "findingEntityRef", "owner", "mitigation", "acceptedBy", "acceptedAt"]);
103
+ const repairCategorySchema = {
104
+ enum: ["structure-schema", "formula-calculation", "scope-drift", "execution-dispatch", "validator-self"],
105
+ };
106
+ const fileSchema = objectSchema({ path: stringSchema({ minLength: 1 }), content: stringSchema(), schema: anyObjectSchema }, ["path", "content"]);
107
+ const nextSchema = objectSchema({ operation: { type: ["string", "null"] }, instruction: stringSchema() }, ["operation", "instruction"]);
108
+ const responseSchema = (properties, required) => objectSchema({
109
+ schemaVersion: { const: RES }, requestId: stringSchema({ minLength: 1 }), status: { enum: ["succeeded", "blocked", "failed"] }, ...properties,
110
+ }, ["schemaVersion", "requestId", "status", ...required]);
111
+ const operationSchema = (input, inputRequired, output, outputRequired) => ({
112
+ input: objectSchema(input, inputRequired), output: responseSchema(output, outputRequired),
113
+ });
114
+ const SCHEMAS = Object.freeze({
115
+ capabilities: operationSchema({}, [], { capabilities: anyObjectSchema, skill: anyObjectSchema, operationSchemas: anyObjectSchema, nextStep: nextSchema }, ["capabilities", "skill", "operationSchemas", "nextStep"]),
116
+ help: operationSchema({}, [], { help: anyObjectSchema, operationSchemas: anyObjectSchema, nextStep: nextSchema }, ["help", "operationSchemas", "nextStep"]),
117
+ intake: operationSchema({ goal: stringSchema({ minLength: 1 }), riskLevel: { enum: ["low", "medium", "high"] }, complianceReqs: arraySchema(stringSchema()), targetFiles: arraySchema(stringSchema()) }, ["goal", "riskLevel"], { intake: anyObjectSchema, nextStep: nextSchema }, ["intake", "nextStep"]),
118
+ plan: operationSchema({ findings: arraySchema(findingSchema), findingCategories: arraySchema(repairCategorySchema), intakeResult: anyObjectSchema, availableSkills: arraySchema(stringSchema()) }, ["intakeResult"], { plan: anyObjectSchema, nextStep: nextSchema }, ["plan", "nextStep"]),
119
+ "validate-structure": operationSchema({ files: arraySchema(fileSchema, { minItems: 1 }), rules: arraySchema(anyObjectSchema) }, ["files"], { findings: arraySchema(findingSchema), summary: anyObjectSchema, nextStep: nextSchema }, ["findings", "summary", "nextStep"]),
120
+ "security-scan": operationSchema({ files: arraySchema(fileSchema, { minItems: 1 }), rules: arraySchema(anyObjectSchema) }, ["files"], { findings: arraySchema(findingSchema), summary: anyObjectSchema, nextStep: nextSchema }, ["findings", "summary", "nextStep"]),
121
+ "compliance-audit": operationSchema({ files: arraySchema(fileSchema, { minItems: 1 }), template: stringSchema({ minLength: 1 }), requirements: arraySchema(stringSchema()) }, ["files", "template"], { findings: arraySchema(findingSchema), template: stringSchema(), nextStep: nextSchema }, ["findings", "template", "nextStep"]),
122
+ "functional-verify": operationSchema({ validationContext: subjectSchema, receipts: arraySchema(receiptSchema, { minItems: 1 }) }, ["validationContext", "receipts"], { subject: subjectSchema, subjectDigest: stringSchema({ pattern: SHA_PATTERN }), results: arraySchema(anyObjectSchema), summary: anyObjectSchema, findings: arraySchema(findingSchema), evidence: arraySchema(testEvidenceSchema), nextStep: nextSchema }, ["subject", "subjectDigest", "results", "summary", "findings", "evidence", "nextStep"]),
123
+ "sandbox-run": operationSchema({ command: stringSchema({ minLength: 1 }), files: arraySchema(fileSchema), timeout: { type: "number", minimum: 0 }, networkPolicy: { enum: ["block", "allow"] } }, ["command", "networkPolicy"], { sandbox: anyObjectSchema, runner: { const: "local-only" }, evidence: arraySchema(testEvidenceSchema), nextStep: nextSchema }, ["sandbox", "runner", "evidence", "nextStep"]),
124
+ "fuzz-input": operationSchema({ targetFile: stringSchema({ minLength: 1 }), cases: arraySchema(anyObjectSchema), maxCases: { type: "integer", minimum: 1 } }, ["targetFile", "maxCases"], { fuzz: anyObjectSchema, runner: { const: "local-only" }, evidence: arraySchema(testEvidenceSchema), nextStep: nextSchema }, ["fuzz", "runner", "evidence", "nextStep"]),
125
+ "perf-benchmark": operationSchema({ command: stringSchema({ minLength: 1 }), baseline: baselineSchema, threshold: { type: "number", minimum: 0 } }, ["command", "baseline", "threshold"], { benchmark: anyObjectSchema, runner: { const: "local-only" }, evidence: arraySchema(testEvidenceSchema), nextStep: nextSchema }, ["benchmark", "runner", "evidence", "nextStep"]),
126
+ "intrusive-test": operationSchema({ authorization: { type: "boolean" }, tests: arraySchema(anyObjectSchema, { minItems: 1 }), sandbox: { const: true } }, ["authorization", "tests", "sandbox"], { intrusive: anyObjectSchema, runner: { const: "local-only" }, evidence: arraySchema(testEvidenceSchema), nextStep: nextSchema }, ["intrusive", "runner", "evidence", "nextStep"]),
127
+ verdict: operationSchema({ expectedSubject: subjectSchema, validationContext: subjectSchema, findings: arraySchema(findingSchema), evidence: arraySchema(testEvidenceSchema), riskLedger: arraySchema(riskEntrySchema) }, [], { report: anyObjectSchema, findings: arraySchema(findingSchema), evidence: arraySchema(testEvidenceSchema), nextStep: nextSchema }, ["report", "findings", "evidence", "nextStep"]),
128
+ });
129
+
130
+ function text(value) { return String(value ?? ""); }
131
+ function isObj(value) { return value !== null && typeof value === "object" && !Array.isArray(value); }
132
+ function finding(severity, ruleId, entityRef, message, evidence) {
133
+ return { severity, ruleId, entityRef, message, evidence: evidence === undefined ? {} : { example: evidence } };
134
+ }
135
+ function ok(requestId, payload) { return { schemaVersion: RES, requestId, status: "succeeded", ...payload }; }
136
+ function blocked(requestId, findings) { return { schemaVersion: RES, requestId, status: "blocked", validation: { valid: false, guarantee: "blocked", findings } }; }
137
+ function failed(requestId, code, message) { return { schemaVersion: RES, requestId, status: "failed", errorSchema: ERR, error: { code, message } }; }
138
+ function requiredText(input, key) {
139
+ const value = text(input[key]).trim();
140
+ return value ? { value } : { error: finding("P0", "REQUIRED", `input.${key}`, `${key} is required`) };
141
+ }
142
+ function requiredArray(input, key, minimum = 0) {
143
+ if (!Array.isArray(input[key]) || input[key].length < minimum) return { error: finding("P0", "REQUIRED", `input.${key}`, `${key} must contain at least ${minimum} item(s)`) };
144
+ return { value: input[key] };
145
+ }
146
+ function canonicalJson(value) {
147
+ if (value === null || typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
148
+ if (typeof value === "number") {
149
+ if (!Number.isFinite(value)) throw new TypeError("Evidence must contain finite numbers");
150
+ return JSON.stringify(value);
151
+ }
152
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
153
+ if (!isObj(value)) throw new TypeError("Evidence must be JSON serializable");
154
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
155
+ }
156
+ export function validatorReceiptSubject(value) { return createHash("sha256").update(canonicalJson(value)).digest("hex"); }
157
+ export function validatorArtifactSubject(files) {
158
+ const manifest = files.map((file) => `${file.path}\0${file.sha256}`).join("\n");
159
+ return createHash("sha256").update(`validator.file-manifest/1.0\n${manifest}`).digest("hex");
160
+ }
161
+ export function validatorReceiptPayload(receipt) {
162
+ if (!isObj(receipt)) throw new TypeError("Execution receipt must be an object");
163
+ const { signature: _signature, ...payload } = receipt;
164
+ return canonicalJson(payload);
165
+ }
166
+
167
+ function validateGoldenBaseline(value, entityRef, tests) {
168
+ const findings = [];
169
+ if (!isObj(value)) return [finding("P0", "GOLDEN-BASELINE-REQUIRED", entityRef, "A frozen golden baseline is required")];
170
+ if (value.schemaVersion !== GOLDEN_BASELINE_SCHEMA) findings.push(finding("P0", "GOLDEN-BASELINE-SCHEMA", `${entityRef}.schemaVersion`, `Expected ${GOLDEN_BASELINE_SCHEMA}`));
171
+ if (!idRegex.test(text(value.baselineId)) || !idRegex.test(text(value.version)) || !idRegex.test(text(value.frozenBy))) findings.push(finding("P0", "GOLDEN-BASELINE-ID", entityRef, "baselineId, version, and frozenBy must be stable identifiers"));
172
+ if (value.frozen !== true || !Number.isFinite(Date.parse(text(value.frozenAt)))) findings.push(finding("P0", "GOLDEN-BASELINE-FROZEN", entityRef, "Baseline must be frozen with a valid timestamp"));
173
+ if (!isObj(value.source) || !["repository-commit", "artifact", "approved-record"].includes(value.source.kind)
174
+ || !text(value.source.locator).trim() || !shaRegex.test(text(value.source.digestSha256))) findings.push(finding("P0", "GOLDEN-BASELINE-SOURCE", `${entityRef}.source`, "Baseline requires traceable source, locator, and SHA-256"));
175
+ if (!shaRegex.test(text(value.testsSha256)) || value.testsSha256 !== validatorReceiptSubject(tests)) findings.push(finding("P0", "GOLDEN-BASELINE-TESTS", `${entityRef}.testsSha256`, "Frozen tests digest does not match subject tests"));
176
+ return findings;
177
+ }
178
+ function readValidationSubject(value, entityRef) {
179
+ if (!isObj(value)) return { findings: [finding("P0", "VALIDATION-SUBJECT-REQUIRED", entityRef, "A validation subject is required")] };
180
+ const findings = [];
181
+ const allowed = new Set(["schemaVersion", "memberId", "chainId", "executedAt", "files", "artifactSha256", "validationRunId", "planId", "tests", "policy", "goldenBaseline", "contracts"]);
182
+ const unknown = Object.keys(value).filter((key) => !allowed.has(key));
183
+ if (unknown.length) findings.push(finding("P0", "VALIDATION-SUBJECT-FIELDS", entityRef, `Unknown fields: ${unknown.join(", ")}`));
184
+ if (value.schemaVersion !== VALIDATION_SUBJECT_SCHEMA) findings.push(finding("P0", "VALIDATION-SUBJECT-SCHEMA", `${entityRef}.schemaVersion`, `Expected ${VALIDATION_SUBJECT_SCHEMA}`));
185
+ if (!shaRegex.test(text(value.artifactSha256))) findings.push(finding("P0", "VALIDATION-SUBJECT-ARTIFACT", `${entityRef}.artifactSha256`, "artifactSha256 must be lowercase SHA-256"));
186
+ for (const key of ["validationRunId", "planId"]) if (!idRegex.test(text(value[key]))) findings.push(finding("P0", "VALIDATION-SUBJECT-ID", `${entityRef}.${key}`, `${key} must be stable`));
187
+ if (!Array.isArray(value.tests) || value.tests.length === 0) findings.push(finding("P0", "VALIDATION-SUBJECT-TESTS", `${entityRef}.tests`, "tests must be non-empty"));
188
+ if (!isObj(value.policy) || !text(value.policy.command).trim() || !Number.isInteger(value.policy.requiredExitCode)) findings.push(finding("P0", "VALIDATION-SUBJECT-POLICY", `${entityRef}.policy`, "policy requires command and requiredExitCode"));
189
+ const hardenedFields = ["memberId", "chainId", "executedAt", "files"];
190
+ const hardened = hardenedFields.some((key) => value[key] !== undefined);
191
+ if (hardened) {
192
+ if (!memberRegex.test(text(value.memberId))) findings.push(finding("P0", "VALIDATION-SUBJECT-MEMBER", `${entityRef}.memberId`, "memberId must be a stable member identifier"));
193
+ if (!chainRegex.test(text(value.chainId))) findings.push(finding("P0", "VALIDATION-SUBJECT-CHAIN", `${entityRef}.chainId`, "chainId must be a valid member chain identifier"));
194
+ if (!Number.isFinite(Date.parse(text(value.executedAt)))) findings.push(finding("P0", "VALIDATION-SUBJECT-EXECUTED-AT", `${entityRef}.executedAt`, "executedAt must be a valid timestamp"));
195
+ if (!Array.isArray(value.files) || value.files.length === 0) findings.push(finding("P0", "VALIDATION-SUBJECT-FILES", `${entityRef}.files`, "files must be a non-empty normalized manifest"));
196
+ else {
197
+ for (const [index, file] of value.files.entries()) {
198
+ const path = text(file?.path);
199
+ const normalized = path === path.normalize("NFC") && !/[\u0000-\u001f\u007f]/.test(path)
200
+ && !path.startsWith("/") && !path.includes("\\")
201
+ && path.split("/").every((segment) => segment && segment !== "." && segment !== "..");
202
+ if (!isObj(file) || !normalized || !shaRegex.test(text(file.sha256))) findings.push(finding("P0", "VALIDATION-SUBJECT-FILE", `${entityRef}.files[${index}]`, "Each file needs a normalized relative path and SHA-256"));
203
+ if (index > 0 && text(value.files[index - 1]?.path) >= path) findings.push(finding("P0", "VALIDATION-SUBJECT-FILE-ORDER", `${entityRef}.files[${index}].path`, "File paths must be unique and sorted"));
204
+ }
205
+ if (!findings.some((item) => item.ruleId.startsWith("VALIDATION-SUBJECT-FILE"))
206
+ && value.artifactSha256 !== validatorArtifactSubject(value.files)) findings.push(finding("P0", "VALIDATION-SUBJECT-ARTIFACT-MANIFEST", `${entityRef}.artifactSha256`, "Artifact digest does not match the file manifest"));
207
+ }
208
+ }
209
+ if (hardened && (!isObj(value.contracts) || !isObj(value.contracts.aimlock))) findings.push(finding("P0", "VALIDATION-SUBJECT-SNAPSHOT", `${entityRef}.contracts.aimlock`, "Hardened validation requires an Aimlock snapshot binding"));
210
+ if (value.contracts !== undefined && (!isObj(value.contracts)
211
+ || (value.contracts.aimlock !== undefined && (!isObj(value.contracts.aimlock)
212
+ || !idRegex.test(text(value.contracts.aimlock.goalId))
213
+ || !shaRegex.test(text(value.contracts.aimlock.scopeContractSha256))
214
+ || !shaRegex.test(text(value.contracts.aimlock.snapshotSha256))))
215
+ || (value.contracts.blueprint !== undefined && (!isObj(value.contracts.blueprint)
216
+ || !idRegex.test(text(value.contracts.blueprint.blueprintId))
217
+ || !shaRegex.test(text(value.contracts.blueprint.acceptanceReportSha256))))
218
+ || (value.contracts.archguard !== undefined && (!isObj(value.contracts.archguard)
219
+ || !shaRegex.test(text(value.contracts.archguard.contractSha256))
220
+ || !shaRegex.test(text(value.contracts.archguard.ledgerSha256))
221
+ || !["green", "yellow", "red"].includes(value.contracts.archguard.driftStatus))))) findings.push(finding("P0", "VALIDATION-SUBJECT-CONTRACTS", `${entityRef}.contracts`, "Aimlock, Blueprint, and ArchGuard bridge contracts require stable ids, SHA-256 digests, and a valid drift status"));
222
+ if (Array.isArray(value.tests)) findings.push(...validateGoldenBaseline(value.goldenBaseline, `${entityRef}.goldenBaseline`, value.tests));
223
+ try { canonicalJson(value); } catch (error) { findings.push(finding("P0", "VALIDATION-SUBJECT-JSON", entityRef, error instanceof Error ? error.message : "Invalid JSON")); }
224
+ return findings.length ? { findings } : { value };
225
+ }
226
+ function expectedValidationSubject(input) {
227
+ if (input.expectedSubject === undefined && input.validationContext === undefined) return readValidationSubject(undefined, "input.expectedSubject");
228
+ const expected = input.expectedSubject === undefined ? null : readValidationSubject(input.expectedSubject, "input.expectedSubject");
229
+ const context = input.validationContext === undefined ? null : readValidationSubject(input.validationContext, "input.validationContext");
230
+ const findings = [...(expected?.findings ?? []), ...(context?.findings ?? [])];
231
+ if (findings.length) return { findings };
232
+ const value = expected?.value ?? context.value;
233
+ const digest = validatorReceiptSubject(value);
234
+ if (expected?.value && context?.value && digest !== validatorReceiptSubject(context.value)) return { findings: [finding("P0", "VALIDATION-SUBJECT-CONFLICT", "input.validationContext", "Targets differ")] };
235
+ return { value, digest };
236
+ }
237
+
238
+ function configuredReceiptKey() {
239
+ const encoded = text(process.env[RECEIPT_PUBLIC_KEY_ENV]).trim();
240
+ if (!/^[A-Za-z0-9+/]+={0,2}$/.test(encoded)) return null;
241
+ try {
242
+ const der = Buffer.from(encoded, "base64");
243
+ const key = createPublicKey({ key: der, format: "der", type: "spki" });
244
+ return key.asymmetricKeyType === "ed25519" ? { key, keyId: createHash("sha256").update(der).digest("hex") } : null;
245
+ } catch { return null; }
246
+ }
247
+ function verifiedReceipt(receipt, subjectDigest, subject) {
248
+ const configured = configuredReceiptKey();
249
+ if (!configured || !isObj(receipt) || receipt.schemaVersion !== RECEIPT_SCHEMA) return null;
250
+ const result = receipt.result;
251
+ const issuedAt = Date.parse(text(receipt.issuedAt));
252
+ const expiresAt = Date.parse(text(receipt.expiresAt));
253
+ const executedAt = subject.executedAt === undefined ? null : Date.parse(text(subject.executedAt));
254
+ const now = Date.now();
255
+ if (receipt.keyId !== configured.keyId || receipt.subjectDigest !== subjectDigest || !idRegex.test(text(receipt.nonce))
256
+ || !Number.isFinite(issuedAt) || !Number.isFinite(expiresAt) || issuedAt > now + 60_000 || expiresAt <= now
257
+ || (executedAt !== null && (!Number.isFinite(executedAt) || executedAt > issuedAt
258
+ || issuedAt - executedAt > MAX_RECEIPT_LIFETIME_MS))
259
+ || expiresAt <= issuedAt || expiresAt - issuedAt > MAX_RECEIPT_LIFETIME_MS || !isObj(result)
260
+ || result.runner !== "trusted-runner" || typeof result.passed !== "boolean" || !Number.isInteger(result.exitCode)
261
+ || typeof result.durationMs !== "number" || result.durationMs < 0 || !text(result.summary).trim()) return null;
262
+ try {
263
+ const signature = Buffer.from(text(receipt.signature), "base64url");
264
+ return verifySignature(null, Buffer.from(validatorReceiptPayload(receipt)), configured.key, signature) ? receipt : null;
265
+ } catch { return null; }
266
+ }
267
+ function createTestEvidence(receipt, subject, subjectDigest, index) {
268
+ return { schemaVersion: TEST_EVIDENCE_SCHEMA, evidenceId: `${subject.validationRunId}:${index}`, kind: "test",
269
+ runner: "trusted-runner", command: subject.policy.command, exitCode: receipt.result.exitCode,
270
+ durationMs: receipt.result.durationMs, summary: receipt.result.summary, artifactSha256: subject.artifactSha256,
271
+ subject, subjectDigest, receipt };
272
+ }
273
+ function evidenceState(evidence, subject, subjectDigest) {
274
+ if (!isObj(evidence) || evidence.schemaVersion !== TEST_EVIDENCE_SCHEMA || evidence.runner === "local") return "unverifiable";
275
+ let evidenceDigest;
276
+ try { evidenceDigest = validatorReceiptSubject(evidence.subject); } catch { return "unverifiable"; }
277
+ if (!idRegex.test(text(evidence.evidenceId)) || !["test", "build", "lint", "security", "benchmark"].includes(evidence.kind)
278
+ || evidence.runner !== "trusted-runner" || evidence.command !== subject.policy.command
279
+ || evidence.artifactSha256 !== subject.artifactSha256 || evidence.subjectDigest !== subjectDigest
280
+ || !isObj(evidence.subject) || evidenceDigest !== subjectDigest) return "unverifiable";
281
+ const receipt = verifiedReceipt(evidence.receipt, subjectDigest, subject);
282
+ if (!receipt || evidence.exitCode !== receipt.result.exitCode || evidence.durationMs !== receipt.result.durationMs
283
+ || evidence.summary !== receipt.result.summary) return "unverifiable";
284
+ return receipt.result.passed && receipt.result.exitCode === subject.policy.requiredExitCode ? "valid" : "failed";
285
+ }
286
+
287
+ const DEFAULT_SECURITY_RULES = [
288
+ { id: "SEC-EVAL", pattern: /\beval\s*\(|new\s+Function\b|\bFunction\s*\(/g, severity: "P0", fix: "Use controlled AST evaluation", executableOnly: true },
289
+ { id: "SEC-SECRETS", pattern: /(?:api[_-]?key|token|password|secret)\s*[:=]\s*['"][A-Za-z0-9_\-]{8,}/gi, severity: "P0", fix: "Remove hardcoded credentials" },
290
+ { id: "SEC-SQLI", pattern: /(?:SELECT|INSERT|UPDATE|DELETE)\s+.*\+\s*(?:req\.|input\.|params\.)/gi, severity: "P0", fix: "Use parameterized queries" },
291
+ { id: "SEC-XSS", pattern: /innerHTML\s*=\s*(?!\s*['"`]\s*['"`])/g, severity: "P1", fix: "Use sanitized output" },
292
+ ];
293
+ const DEFAULT_STRUCTURE_RULES = ["references-closed", "required-fields", "type-correct", "no-cycle"];
294
+ function executableJavaScript(path) { return /\.(?:[cm]?[jt]sx?)$/i.test(path); }
295
+ function stripJavaScriptInertText(source) {
296
+ let output = "";
297
+ let state = "code";
298
+ let quote = "";
299
+ for (let index = 0; index < source.length; index += 1) {
300
+ const character = source[index];
301
+ const next = source[index + 1];
302
+ if (state === "line") {
303
+ if (character === "\n") { state = "code"; output += "\n"; }
304
+ continue;
305
+ }
306
+ if (state === "block") {
307
+ if (character === "*" && next === "/") { state = "code"; index += 1; }
308
+ else if (character === "\n") output += "\n";
309
+ continue;
310
+ }
311
+ if (state === "string") {
312
+ if (character === "\\") { index += 1; continue; }
313
+ if (character === quote) { state = "code"; quote = ""; }
314
+ else if (character === "\n") output += "\n";
315
+ continue;
316
+ }
317
+ if (character === "/" && next === "/") { state = "line"; index += 1; continue; }
318
+ if (character === "/" && next === "*") { state = "block"; index += 1; continue; }
319
+ if (["'", '"', "`"].includes(character)) { state = "string"; quote = character; continue; }
320
+ output += character;
321
+ }
322
+ return output;
323
+ }
324
+ function runSecurityScan(files, rules = DEFAULT_SECURITY_RULES) {
325
+ const findings = [];
326
+ for (const file of files) for (const rule of rules) {
327
+ const path = text(file.path);
328
+ const content = rule.executableOnly
329
+ ? executableJavaScript(path) ? stripJavaScriptInertText(text(file.content)) : ""
330
+ : text(file.content);
331
+ const matches = content.match(rule.pattern instanceof RegExp ? rule.pattern : new RegExp(rule.pattern, "g"));
332
+ if (matches) findings.push(finding(rule.severity, rule.id, text(file.path), `${matches.length} match(es): ${rule.fix}`, { sample: matches[0] }));
333
+ }
334
+ return findings;
335
+ }
336
+ function runStructureValidation(files) {
337
+ const findings = [];
338
+ for (const file of files) {
339
+ const path = text(file.path);
340
+ if (path.endsWith(".json")) try { JSON.parse(text(file.content)); } catch (error) { findings.push(finding("P0", "STR-JSON", path, error.message)); }
341
+ if (file.schema && path.endsWith(".json")) try {
342
+ const data = JSON.parse(text(file.content));
343
+ for (const field of file.schema.required ?? []) if (data[field] === undefined) findings.push(finding("P0", "STR-REQ", `${path}.${field}`, "Required field is missing"));
344
+ } catch { /* invalid JSON is reported above */ }
345
+ }
346
+ return findings;
347
+ }
348
+ function validateReq(request) {
349
+ const findings = [];
350
+ if (!isObj(request)) return [finding("P0", "REQ_OBJECT", "request", "request must be an object")];
351
+ if (request.schemaVersion !== REQ) findings.push(finding("P0", "REQ_SCHEMA", "request.schemaVersion", `Expected ${REQ}`));
352
+ if (!text(request.requestId).trim()) findings.push(finding("P0", "REQ_FIELD", "request.requestId", "requestId is required"));
353
+ if (!OPS.includes(request.operation)) findings.push(finding("P0", "REQ_OPERATION", "request.operation", "operation is unsupported"));
354
+ if (!isObj(request.input)) findings.push(finding("P0", "REQ_INPUT", "request.input", "input must be an object"));
355
+ return findings;
356
+ }
357
+ function severitySummary(findings) {
358
+ return { total: findings.length, p0: findings.filter((item) => item.severity === "P0").length,
359
+ p1: findings.filter((item) => item.severity === "P1").length,
360
+ p2: findings.filter((item) => item.severity === "P2").length };
361
+ }
362
+ const REPAIR_ROUTES = Object.freeze([
363
+ { category: "structure-schema", skill: "blueprint", action: "recompile-contract", trigger: "structure or schema mismatch", requiresHumanConfirmation: false },
364
+ { category: "formula-calculation", skill: "calctool", action: "repair-formula-engine", trigger: "formula or calculation mismatch", requiresHumanConfirmation: false },
365
+ { category: "scope-drift", skill: "aimlock", action: "relock-scope", trigger: "scope contract violation", requiresHumanConfirmation: false },
366
+ { category: "execution-dispatch", skill: "swarm", action: "repair-dispatch", trigger: "execution or dispatch failure", requiresHumanConfirmation: false },
367
+ { category: "validator-self", skill: "validator", action: "propose-validator-patch", trigger: "Validator rule or engine defect", requiresHumanConfirmation: true },
368
+ ]);
369
+
370
+ function runMeta(requestId, operation) {
371
+ const operationStatus = { implementedPure: [...PURE], localRunnerRequired: [...LOCAL_ONLY], planned: ["mutation-testing"] };
372
+ const goldenPathExample = { operation: "functional-verify", input: { validationContext: "frozen ValidationSubject", receipts: "signed trusted-runner receipt[]" }, next: "verdict with cli.tax.test-evidence/1.0" };
373
+ if (operation === "capabilities") return ok(requestId, { capabilities: { pure: false, stateless: true, operationStatus,
374
+ verdictLevels: ["pass", "pass-with-risk", "blocked", "incomplete"], testEvidenceSchema: TEST_EVIDENCE_SCHEMA,
375
+ goldenBaselineSchema: GOLDEN_BASELINE_SCHEMA, catalogSchema: CATALOG_SCHEMA }, operationSchemas: SCHEMAS,
376
+ goldenPathExample, skill: { name: NAME, version: COMPILER_VERSION }, nextStep: { operation: "intake", instruction: "Collect validation requirements." } });
377
+ return ok(requestId, { help: { name: NAME, version: COMPILER_VERSION, operations: CATALOG, operationStatus, goldenPathExample },
378
+ operationSchemas: SCHEMAS, nextStep: { operation: "intake", instruction: "Collect validation requirements." } });
379
+ }
380
+ function runPlanning(requestId, operation, input) {
381
+ if (operation === "intake") {
382
+ const goal = requiredText(input, "goal");
383
+ const risk = requiredText(input, "riskLevel");
384
+ if (goal.error || risk.error) return blocked(requestId, [goal.error, risk.error].filter(Boolean));
385
+ if (!["low", "medium", "high"].includes(risk.value)) return blocked(requestId, [finding("P0", "RISK-LEVEL", "input.riskLevel", "riskLevel must be low, medium, or high")]);
386
+ return ok(requestId, { intake: { goal: goal.value, riskLevel: risk.value, complianceReqs: input.complianceReqs, targetFiles: input.targetFiles }, nextStep: { operation: "plan", instruction: "Generate validation plan." } });
387
+ }
388
+ const risk = text(input.intakeResult?.riskLevel);
389
+ if (!["low", "medium", "high"].includes(risk)) return blocked(requestId, [finding("P0", "INTAKE-RESULT", "input.intakeResult", "A valid intakeResult is required")]);
390
+ const modules = ["validate-structure", "security-scan", "functional-verify"];
391
+ if (risk === "high") modules.push("compliance-audit", "sandbox-run", "intrusive-test");
392
+ else modules.push("fuzz-input");
393
+ modules.push("perf-benchmark", "verdict");
394
+ const available = Array.isArray(input.availableSkills) ? input.availableSkills : [];
395
+ const requestedCategories = Array.isArray(input.findingCategories) ? input.findingCategories : [];
396
+ const invalidCategory = requestedCategories.find((category) => !REPAIR_ROUTES.some((route) => route.category === category));
397
+ if (invalidCategory) return blocked(requestId, [finding("P0", "REPAIR-CATEGORY", "input.findingCategories", `Unsupported repair category: ${invalidCategory}`)]);
398
+ const selectedRoutes = requestedCategories.length
399
+ ? REPAIR_ROUTES.filter((route) => requestedCategories.includes(route.category)) : REPAIR_ROUTES;
400
+ const routing = selectedRoutes.map((route) => ({
401
+ ...route,
402
+ available: route.skill === "validator" || available.includes(route.skill),
403
+ invoke: route.skill !== "validator" && available.includes(route.skill),
404
+ }));
405
+ return ok(requestId, { plan: { modules, routing, routingPolicy: "deterministic-category-map",
406
+ riskLevel: risk, totalSteps: modules.length }, nextStep: { operation: modules[0], instruction: `Execute ${modules[0]}.` } });
407
+ }
408
+ function runStatic(requestId, operation, input) {
409
+ const files = requiredArray(input, "files", 1);
410
+ if (files.error) return blocked(requestId, [files.error]);
411
+ if (operation === "validate-structure") {
412
+ const findings = runStructureValidation(files.value);
413
+ return ok(requestId, { findings, summary: severitySummary(findings), line: "static", nextStep: { operation: "security-scan", instruction: "Run security scan." } });
414
+ }
415
+ if (operation === "security-scan") {
416
+ const findings = runSecurityScan(files.value, input.rules);
417
+ return ok(requestId, { findings, summary: severitySummary(findings), line: "static", nextStep: { operation: "functional-verify", instruction: "Run frozen golden baseline." } });
418
+ }
419
+ const template = requiredText(input, "template");
420
+ if (template.error) return blocked(requestId, [template.error]);
421
+ const findings = files.value.flatMap((file) => publicBindSites(text(file.path), text(file.content)).map((site) => finding("P1", "COMP-PORT", text(file.path), "Public bind requires review", site)));
422
+ return ok(requestId, { findings, template: template.value, nextStep: { operation: "functional-verify", instruction: "Run frozen golden baseline." } });
423
+ }
424
+ function runFunctional(requestId, input) {
425
+ const subject = readValidationSubject(input.validationContext, "input.validationContext");
426
+ if (subject.findings) return blocked(requestId, subject.findings);
427
+ const receipts = requiredArray(input, "receipts", 1);
428
+ if (receipts.error) return blocked(requestId, [finding("P0", "TRUSTED-RECEIPT-REQUIRED", "input.receipts", "Signed trusted-runner receipts are required")]);
429
+ const subjectDigest = validatorReceiptSubject(subject.value);
430
+ const verified = receipts.value.map((receipt) => verifiedReceipt(receipt, subjectDigest, subject.value));
431
+ if (verified.some((receipt) => receipt === null)) return blocked(requestId, [finding("P0", "INVALID-EXECUTION-RECEIPT", "input.receipts", "Receipt signature, lifetime, result, or subject is invalid")]);
432
+ const results = verified.map((receipt) => receipt.result);
433
+ const findings = results.flatMap((result, index) => result.passed && result.exitCode === subject.value.policy.requiredExitCode ? [] : [finding("P1", "GOLDEN-FAIL", `receipt:${index}`, "Frozen golden baseline failed", result)]);
434
+ const evidence = verified.map((receipt, index) => createTestEvidence(receipt, subject.value, subjectDigest, index));
435
+ return ok(requestId, { subject: subject.value, subjectDigest, results,
436
+ summary: { total: results.length, passed: results.length - findings.length, failed: findings.length },
437
+ runner: "trusted-runner", findings, evidence, nextStep: { operation: "verdict", instruction: "Render verdict from TestEvidence." } });
438
+ }
439
+
440
+ function runLocalProtocol(requestId, operation, input) {
441
+ if (operation === "intrusive-test" && input.authorization !== true) return blocked(requestId, [finding("P0", "INTRUSIVE-NO-AUTH", "input.authorization", "Explicit authorization is required")]);
442
+ const descriptor = operation === "fuzz-input" ? requiredText(input, "targetFile") : requiredText(input, "command");
443
+ if (descriptor.error && operation !== "intrusive-test") return blocked(requestId, [descriptor.error]);
444
+ const pending = { operation, status: "pending-execution", requestedTarget: descriptor.value };
445
+ const key = operation === "sandbox-run" ? "sandbox" : operation === "fuzz-input" ? "fuzz"
446
+ : operation === "perf-benchmark" ? "benchmark" : "intrusive";
447
+ return ok(requestId, { [key]: pending, runner: "local-only", evidence: [],
448
+ pendingEvidenceRequirements: { schemaVersion: TEST_EVIDENCE_SCHEMA, trustedReceiptRequiredForFinalPass: true },
449
+ nextStep: { operation: "verdict", instruction: "Local runner must return signed TestEvidence; pending is incomplete." } });
450
+ }
451
+ function validRiskLedger(findings, ledger) {
452
+ if (!Array.isArray(ledger)) return false;
453
+ return findings.filter((item) => item.severity === "P1").every((item) => ledger.some((entry) => isObj(entry)
454
+ && idRegex.test(text(entry.riskId)) && entry.findingRuleId === item.ruleId
455
+ && entry.findingEntityRef === item.entityRef
456
+ && idRegex.test(text(entry.owner)) && text(entry.mitigation).trim()
457
+ && idRegex.test(text(entry.acceptedBy)) && Number.isFinite(Date.parse(text(entry.acceptedAt)))));
458
+ }
459
+ function runVerdict(requestId, input) {
460
+ const expected = expectedValidationSubject(input);
461
+ if (expected.findings) return blocked(requestId, expected.findings);
462
+ const findings = Array.isArray(input.findings) ? input.findings : [];
463
+ const evidence = Array.isArray(input.evidence) ? input.evidence : [];
464
+ const states = evidence.map((item) => evidenceState(item, expected.value, expected.digest));
465
+ const failedCount = states.filter((state) => state === "failed").length;
466
+ const p0 = findings.filter((item) => item.severity === "P0").length;
467
+ const p1 = findings.filter((item) => item.severity === "P1").length;
468
+ const allValid = states.length > 0 && states.every((state) => state === "valid");
469
+ const riskLedgerValid = p1 === 0 || validRiskLedger(findings, input.riskLedger);
470
+ const level = p0 > 0 || failedCount > 0 ? "blocked"
471
+ : !allValid || !riskLedgerValid ? "incomplete" : p1 > 0 ? "pass-with-risk" : "pass";
472
+ const report = { verdict: level, subjectDigest: expected.digest, findings: severitySummary(findings),
473
+ evidenceCount: evidence.length, evidenceValid: allValid,
474
+ evidenceSummary: { valid: states.filter((state) => state === "valid").length, failed: failedCount,
475
+ pending: 0, unverifiable: states.filter((state) => state === "unverifiable").length },
476
+ riskLedgerValid, riskLedger: input.riskLedger };
477
+ return ok(requestId, { report, findings, evidence,
478
+ nextStep: level === "blocked" ? { operation: "plan", instruction: "Repair blocking failures." }
479
+ : level === "incomplete" ? { operation: "verdict", instruction: "Provide trusted evidence and complete P1 risk ledger." }
480
+ : { operation: null, instruction: `Verdict: ${level}.` } });
481
+ }
482
+
483
+ export async function run(request) {
484
+ const validationFindings = validateReq(request);
485
+ if (validationFindings.length) return { ...blocked(request?.requestId ?? "unknown", validationFindings), errorSchema: ERR };
486
+ const { requestId, operation, input } = request;
487
+ if (operation === "capabilities" || operation === "help") return runMeta(requestId, operation);
488
+ if (operation === "intake" || operation === "plan") return runPlanning(requestId, operation, input);
489
+ if (["validate-structure", "security-scan", "compliance-audit"].includes(operation)) return runStatic(requestId, operation, input);
490
+ if (operation === "functional-verify") return runFunctional(requestId, input);
491
+ if (LOCAL_ONLY.has(operation)) return runLocalProtocol(requestId, operation, input);
492
+ if (operation === "verdict") return runVerdict(requestId, input);
493
+ return failed(requestId, "UNSUPPORTED_OPERATION", `Unsupported operation: ${operation}`);
494
+ }
495
+
496
+ export { COMPILER_VERSION, NAME, OPS, PURE, CATALOG, SCHEMAS, GOLDEN_BASELINE_SCHEMA,
497
+ TEST_EVIDENCE_SCHEMA, DEFAULT_SECURITY_RULES, DEFAULT_STRUCTURE_RULES,
498
+ runSecurityScan, runStructureValidation };