@gate-forge/core 0.7.1 → 0.9.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/dist/classifier/bind.d.ts +2 -0
- package/dist/classifier/bind.d.ts.map +1 -1
- package/dist/classifier/bind.js +8 -0
- package/dist/classifier/bind.js.map +1 -1
- package/dist/classifier/classify.d.ts +2 -0
- package/dist/classifier/classify.d.ts.map +1 -1
- package/dist/classifier/classify.js +127 -2
- package/dist/classifier/classify.js.map +1 -1
- package/dist/classifier/index.d.ts +2 -2
- package/dist/classifier/index.d.ts.map +1 -1
- package/dist/classifier/index.js +1 -1
- package/dist/classifier/index.js.map +1 -1
- package/dist/classifier/schema.d.ts +54 -0
- package/dist/classifier/schema.d.ts.map +1 -1
- package/dist/classifier/schema.js +17 -0
- package/dist/classifier/schema.js.map +1 -1
- package/dist/config/index.d.ts +186 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +327 -1
- package/dist/config/index.js.map +1 -1
- package/dist/graph/schema.d.ts +34 -0
- package/dist/graph/schema.d.ts.map +1 -1
- package/dist/index.d.ts +80 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +58 -6
- package/dist/index.js.map +1 -1
- package/dist/mapping/resolve.d.ts +32 -1
- package/dist/mapping/resolve.d.ts.map +1 -1
- package/dist/mapping/resolve.js +297 -65
- package/dist/mapping/resolve.js.map +1 -1
- package/dist/policy/evaluate.d.ts +30 -0
- package/dist/policy/evaluate.d.ts.map +1 -1
- package/dist/policy/evaluate.js +22 -12
- package/dist/policy/evaluate.js.map +1 -1
- package/dist/quarantine/index.d.ts +82 -0
- package/dist/quarantine/index.d.ts.map +1 -0
- package/dist/quarantine/index.js +194 -0
- package/dist/quarantine/index.js.map +1 -0
- package/dist/receipt/index.d.ts +65 -1
- package/dist/receipt/index.d.ts.map +1 -1
- package/dist/receipt/index.js +115 -1
- package/dist/receipt/index.js.map +1 -1
- package/dist/report/human-message.d.ts +33 -0
- package/dist/report/human-message.d.ts.map +1 -0
- package/dist/report/human-message.js +33 -0
- package/dist/report/human-message.js.map +1 -0
- package/dist/report/index.d.ts +183 -9
- package/dist/report/index.d.ts.map +1 -1
- package/dist/report/index.js +317 -23
- package/dist/report/index.js.map +1 -1
- package/dist/schemas/adoption.d.ts +2 -0
- package/dist/schemas/adoption.d.ts.map +1 -1
- package/dist/schemas/adoption.js +6 -0
- package/dist/schemas/adoption.js.map +1 -1
- package/dist/schemas/behavior-catalog.d.ts +12 -0
- package/dist/schemas/behavior-catalog.d.ts.map +1 -1
- package/dist/schemas/behavior-evidence.d.ts +40 -0
- package/dist/schemas/behavior-evidence.d.ts.map +1 -1
- package/dist/schemas/behavior-evidence.js +9 -0
- package/dist/schemas/behavior-evidence.js.map +1 -1
- package/dist/schemas/behavior-policy.d.ts +26 -0
- package/dist/schemas/behavior-policy.d.ts.map +1 -1
- package/dist/schemas/behavior-policy.js +37 -1
- package/dist/schemas/behavior-policy.js.map +1 -1
- package/dist/schemas/execution-result.d.ts +17 -5
- package/dist/schemas/execution-result.d.ts.map +1 -1
- package/dist/schemas/execution-result.js +57 -2
- package/dist/schemas/execution-result.js.map +1 -1
- package/dist/schemas/gate-receipt.d.ts +25 -2
- package/dist/schemas/gate-receipt.d.ts.map +1 -1
- package/dist/schemas/gate-receipt.js +181 -2
- package/dist/schemas/gate-receipt.js.map +1 -1
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/index.js.map +1 -1
- package/dist/schemas/policy.d.ts +19 -0
- package/dist/schemas/policy.d.ts.map +1 -1
- package/dist/schemas/policy.js +36 -0
- package/dist/schemas/policy.js.map +1 -1
- package/dist/schemas/quarantine.d.ts +25 -0
- package/dist/schemas/quarantine.d.ts.map +1 -0
- package/dist/schemas/quarantine.js +34 -0
- package/dist/schemas/quarantine.js.map +1 -0
- package/dist/schemas/queue-observer.d.ts +195 -0
- package/dist/schemas/queue-observer.d.ts.map +1 -0
- package/dist/schemas/queue-observer.js +190 -0
- package/dist/schemas/queue-observer.js.map +1 -0
- package/dist/schemas/run-record.d.ts +57 -0
- package/dist/schemas/run-record.d.ts.map +1 -0
- package/dist/schemas/run-record.js +108 -0
- package/dist/schemas/run-record.js.map +1 -0
- package/dist/schemas/runtime-config.d.ts +67 -0
- package/dist/schemas/runtime-config.d.ts.map +1 -1
- package/dist/schemas/runtime-config.js +75 -1
- package/dist/schemas/runtime-config.js.map +1 -1
- package/dist/schemas/signature-profile.d.ts +99 -0
- package/dist/schemas/signature-profile.d.ts.map +1 -0
- package/dist/schemas/signature-profile.js +197 -0
- package/dist/schemas/signature-profile.js.map +1 -0
- package/dist/schemas/test-map.d.ts +4 -0
- package/dist/schemas/test-map.d.ts.map +1 -1
- package/dist/schemas/test-map.js +12 -0
- package/dist/schemas/test-map.js.map +1 -1
- package/dist/schemas/verdict.d.ts +19 -4
- package/dist/schemas/verdict.d.ts.map +1 -1
- package/dist/schemas/verdict.js +42 -5
- package/dist/schemas/verdict.js.map +1 -1
- package/dist/strictness.d.ts +102 -0
- package/dist/strictness.d.ts.map +1 -0
- package/dist/strictness.js +92 -0
- package/dist/strictness.js.map +1 -0
- package/dist/supervision/index.js +6 -6
- package/dist/supervision/index.js.map +1 -1
- package/dist/twin-paths.d.ts +125 -0
- package/dist/twin-paths.d.ts.map +1 -0
- package/dist/twin-paths.js +233 -0
- package/dist/twin-paths.js.map +1 -0
- package/dist/verdict/behavior.d.ts.map +1 -1
- package/dist/verdict/behavior.js +284 -10
- package/dist/verdict/behavior.js.map +1 -1
- package/dist/verdict/cause.d.ts.map +1 -1
- package/dist/verdict/cause.js +18 -0
- package/dist/verdict/cause.js.map +1 -1
- package/dist/verdict/evaluate.d.ts +62 -0
- package/dist/verdict/evaluate.d.ts.map +1 -1
- package/dist/verdict/evaluate.js +300 -13
- package/dist/verdict/evaluate.js.map +1 -1
- package/dist/verdict/index.d.ts +3 -3
- package/dist/verdict/index.d.ts.map +1 -1
- package/dist/verdict/index.js +3 -3
- package/dist/verdict/index.js.map +1 -1
- package/dist/verdict/pack-verifiers.d.ts +17 -3
- package/dist/verdict/pack-verifiers.d.ts.map +1 -1
- package/dist/verdict/pack-verifiers.js +47 -8
- package/dist/verdict/pack-verifiers.js.map +1 -1
- package/dist/verdict/registry.d.ts +19 -0
- package/dist/verdict/registry.d.ts.map +1 -1
- package/dist/verdict/registry.js +21 -0
- package/dist/verdict/registry.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/policy/evaluate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAsC,KAAK,YAAY,EAAiB,MAAM,sBAAsB,CAAC;AAG5G,OAAO,EAAiC,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAmD,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGzG,4DAA4D;AAC5D,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAE5C;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,iBAAiB,CAAC;AAK1D;;;;GAIG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,yDAAyD;AACzD,eAAO,MAAM,mBAAmB
|
|
1
|
+
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/policy/evaluate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAsC,KAAK,YAAY,EAAiB,MAAM,sBAAsB,CAAC;AAG5G,OAAO,EAAiC,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAmD,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGzG,4DAA4D;AAC5D,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAE5C;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,iBAAiB,CAAC;AAK1D;;;;GAIG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,yDAAyD;AACzD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAsC5B,CAAC;AAEL,qCAAqC;AACrC,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAKjF;AAED,yEAAyE;AACzE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;kBAM9B,CAAC;AAEL,uCAAuC;AACvC,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,sEAAsE;AACtE,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAcrC,CAAC;AAEL,+CAA+C;AAC/C,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE;IACT,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CAC3D,GACA,OAAO,CAaT;AAED,MAAM,WAAW,qBAAqB;IACpC,6CAA6C;IAC7C,KAAK,EAAE,aAAa,CAAC;IACrB,iEAAiE;IACjE,QAAQ,EAAE,UAAU,CAAC;IACrB,qEAAqE;IACrE,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;IACjB;;;OAGG;IACH,aAAa,CAAC,EAAE,aAAa,EAAE,CAAC;CACjC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAwErF"}
|
package/dist/policy/evaluate.js
CHANGED
|
@@ -209,14 +209,13 @@ export function evaluatePolicies(input) {
|
|
|
209
209
|
resourceId: resource.id,
|
|
210
210
|
name: resource.name,
|
|
211
211
|
detail: `resource '${resource.id ?? resource.name}' has no effective classification ` +
|
|
212
|
-
'(the classifier blocked it definitionally)
|
|
213
|
-
`'gateforge explain ${resource.id ?? resource.name}' for the typed ` +
|
|
214
|
-
'reason and its in-code resolution',
|
|
212
|
+
'(the classifier blocked it definitionally)',
|
|
215
213
|
location: resource.location,
|
|
214
|
+
nextAction: 'gateforge classify --json',
|
|
216
215
|
});
|
|
217
216
|
continue;
|
|
218
217
|
}
|
|
219
|
-
generateObligations(resource, policyFile.policies, obligations, obligationIds);
|
|
218
|
+
generateObligations(resource, policyFile.policies, obligations, obligationIds, policyFile.options?.['http.endpoint.requireObservation'] === 'all');
|
|
220
219
|
}
|
|
221
220
|
for (const entry of input.graph.unresolved) {
|
|
222
221
|
blocking.push({
|
|
@@ -264,13 +263,13 @@ export function evaluatePolicies(input) {
|
|
|
264
263
|
* policy's required contract that the lifecycle allows, deduplicated
|
|
265
264
|
* by obligation id (first matching policy owns the id).
|
|
266
265
|
*/
|
|
267
|
-
function generateObligations(resource, policies, obligations, obligationIds) {
|
|
266
|
+
function generateObligations(resource, policies, obligations, obligationIds, observationScopeAll) {
|
|
268
267
|
const classification = resource.classification;
|
|
269
268
|
if (classification === null || resource.id === null)
|
|
270
269
|
return;
|
|
271
270
|
const exposure = classification.exposure;
|
|
272
271
|
for (const policy of policies) {
|
|
273
|
-
if (!policyMatches(policy, resource, exposure))
|
|
272
|
+
if (!policyMatches(policy, resource, exposure, observationScopeAll))
|
|
274
273
|
continue;
|
|
275
274
|
for (const contract of policy.require) {
|
|
276
275
|
if (contract.startsWith(CRUD_CONTRACT_PREFIX) ||
|
|
@@ -301,7 +300,7 @@ function generateObligations(resource, policies, obligations, obligationIds) {
|
|
|
301
300
|
}
|
|
302
301
|
}
|
|
303
302
|
}
|
|
304
|
-
function policyMatches(policy, resource, exposure) {
|
|
303
|
+
function policyMatches(policy, resource, exposure, observationScopeAll) {
|
|
305
304
|
const when = policy.when;
|
|
306
305
|
if (when.kind !== undefined && when.kind !== resource.kind)
|
|
307
306
|
return false;
|
|
@@ -321,14 +320,25 @@ function policyMatches(policy, resource, exposure) {
|
|
|
321
320
|
}
|
|
322
321
|
}
|
|
323
322
|
if (when.consumed !== undefined) {
|
|
323
|
+
// Plan Phase 4c (E60): the pinned
|
|
324
|
+
// `http.endpoint.requireObservation: all` option widens ONLY the
|
|
325
|
+
// `consumed: true` matcher of an endpoint-scoped policy to the
|
|
326
|
+
// whole discovered route inventory, so a new route no UI calls
|
|
327
|
+
// owes the observation contracts too (blocking
|
|
328
|
+
// TEST_MAPPING_MISSING until mapped or baselined). A `consumed:
|
|
329
|
+
// false` policy and every non-endpoint resource are untouched —
|
|
330
|
+
// the option narrows nothing and adds no contract.
|
|
331
|
+
const observationScopeWidened = observationScopeAll && when.kind === HTTP_ENDPOINT_RESOURCE_KIND && when.consumed === true;
|
|
324
332
|
// Endpoint consumption match: `true` requires the attribute to be
|
|
325
333
|
// EXACTLY true; `false` matches everything else (including absent —
|
|
326
334
|
// non-endpoint resources are "not consumed", never match-excluded).
|
|
327
|
-
if (
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
335
|
+
if (!observationScopeWidened) {
|
|
336
|
+
if (when.consumed === true && resource.attributes['frontendConsumed'] !== true) {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
if (when.consumed === false && resource.attributes['frontendConsumed'] === true) {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
332
342
|
}
|
|
333
343
|
}
|
|
334
344
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../../src/policy/evaluate.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../../src/policy/evaluate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type Quarantine } from '../schemas/quarantine.js';
|
|
2
|
+
/** Maximum quarantine lifetime in days: a quarantine always expires. */
|
|
3
|
+
export declare const QUARANTINE_MAX_DAYS = 14;
|
|
4
|
+
/** The documented ceiling in milliseconds. */
|
|
5
|
+
export declare const QUARANTINE_MAX_DURATION_MS: number;
|
|
6
|
+
/** Repo-relative quarantine directory. */
|
|
7
|
+
export declare const QUARANTINE_DIR = ".gateforge/quarantine";
|
|
8
|
+
/** One fail-closed quarantine-loading problem (file-scoped, actionable). */
|
|
9
|
+
export interface QuarantineProblem {
|
|
10
|
+
/** Quarantine file the problem was found in (basename). */
|
|
11
|
+
file: string;
|
|
12
|
+
/** Single-cause human explanation. */
|
|
13
|
+
detail: string;
|
|
14
|
+
}
|
|
15
|
+
/** Error raised for any fail-closed quarantine configuration problem. */
|
|
16
|
+
export declare class GateforgeQuarantineError extends Error {
|
|
17
|
+
/** Every problem found, file-scoped, in deterministic order. */
|
|
18
|
+
readonly problems: readonly QuarantineProblem[];
|
|
19
|
+
constructor(problems: readonly QuarantineProblem[]);
|
|
20
|
+
}
|
|
21
|
+
/** One loaded quarantine with its file and expiry state. */
|
|
22
|
+
export interface LoadedQuarantine {
|
|
23
|
+
/** Basename of the file the record came from. */
|
|
24
|
+
file: string;
|
|
25
|
+
/** The schema-valid quarantine document. */
|
|
26
|
+
quarantine: Quarantine;
|
|
27
|
+
}
|
|
28
|
+
/** Result of loading a quarantine directory against the injected clock. */
|
|
29
|
+
export interface QuarantineLoadResult {
|
|
30
|
+
/** Unexpired quarantines — the required set minus these tests. */
|
|
31
|
+
active: LoadedQuarantine[];
|
|
32
|
+
/** Quarantines expired at the injected `now` — each one blocks. */
|
|
33
|
+
expired: LoadedQuarantine[];
|
|
34
|
+
}
|
|
35
|
+
/** Options for {@link loadQuarantines}. */
|
|
36
|
+
export interface QuarantineLoadOptions {
|
|
37
|
+
/** Injected clock instant (Date or ISO-8601) expiry is judged against. */
|
|
38
|
+
now: Date | string;
|
|
39
|
+
/** Maximum allowed lifetime; defaults to the documented 14 days. */
|
|
40
|
+
maxDurationMs?: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Loads and validates every `*.yml` quarantine file in `dir` (sorted by
|
|
44
|
+
* filename for determinism). A missing directory yields an empty result
|
|
45
|
+
* — most repositories have no quarantines. Any problem (unparsable
|
|
46
|
+
* YAML, missing mandatory field, duplicate test key, over-long duration)
|
|
47
|
+
* fails closed with {@link GateforgeQuarantineError}.
|
|
48
|
+
*
|
|
49
|
+
* Args:
|
|
50
|
+
* dir: the quarantine directory (`.gateforge/quarantine`).
|
|
51
|
+
* options: injected clock and optional maximum duration.
|
|
52
|
+
*
|
|
53
|
+
* Returns:
|
|
54
|
+
* QuarantineLoadResult: active and expired partitions, both sorted by
|
|
55
|
+
* test key.
|
|
56
|
+
*
|
|
57
|
+
* @throws GateforgeQuarantineError when any file fails closed.
|
|
58
|
+
*/
|
|
59
|
+
export declare function loadQuarantines(dir: string, options: QuarantineLoadOptions): QuarantineLoadResult;
|
|
60
|
+
/**
|
|
61
|
+
* Serializes one quarantine document in the stable on-disk form
|
|
62
|
+
* (block YAML, trailing newline).
|
|
63
|
+
*
|
|
64
|
+
* Args:
|
|
65
|
+
* quarantine: the schema-valid document.
|
|
66
|
+
*
|
|
67
|
+
* Returns:
|
|
68
|
+
* string: the file content.
|
|
69
|
+
*/
|
|
70
|
+
export declare function serializeQuarantine(quarantine: Quarantine): string;
|
|
71
|
+
/**
|
|
72
|
+
* Writes one quarantine document, creating parent directories as needed.
|
|
73
|
+
* Fail-closed: a write failure surfaces as a {@link GateforgeQuarantineError}.
|
|
74
|
+
*
|
|
75
|
+
* Args:
|
|
76
|
+
* path: destination file path.
|
|
77
|
+
* quarantine: the schema-valid document to write.
|
|
78
|
+
*
|
|
79
|
+
* @throws GateforgeQuarantineError when the file cannot be written.
|
|
80
|
+
*/
|
|
81
|
+
export declare function writeQuarantine(path: string, quarantine: Quarantine): void;
|
|
82
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/quarantine/index.ts"],"names":[],"mappings":"AA0BA,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAG7E,wEAAwE;AACxE,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,8CAA8C;AAC9C,eAAO,MAAM,0BAA0B,QAAmC,CAAC;AAE3E,0CAA0C;AAC1C,eAAO,MAAM,cAAc,0BAA0B,CAAC;AAEtD,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,yEAAyE;AACzE,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;gBAEpC,QAAQ,EAAE,SAAS,iBAAiB,EAAE;CAQnD;AAED,4DAA4D;AAC5D,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB;IACnC,kEAAkE;IAClE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,mEAAmE;IACnE,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B;AAED,2CAA2C;AAC3C,MAAM,WAAW,qBAAqB;IACpC,0EAA0E;IAC1E,GAAG,EAAE,IAAI,GAAG,MAAM,CAAC;IACnB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAwBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,qBAAqB,GAC7B,oBAAoB,CAoEtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAUlE;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,IAAI,CAY1E"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flaky-test quarantine loading.
|
|
3
|
+
*
|
|
4
|
+
* Owner-approved, always-expiring files in `.gateforge/quarantine/*.yml`
|
|
5
|
+
* that remove ONE test from the required set. The same trust pattern as
|
|
6
|
+
* waivers, with one difference in force: a quarantined test never
|
|
7
|
+
* FORGIVES anything — it never blocks and its evidence is never used,
|
|
8
|
+
* so an obligation only it covered stays `missing`.
|
|
9
|
+
*
|
|
10
|
+
* Fail-closed rules:
|
|
11
|
+
* - ALL FOUR attribution fields (testKey, owner, approver, reason) plus
|
|
12
|
+
* the expiry are mandatory; a file missing any of them is a
|
|
13
|
+
* configuration error, never a partially-applied exception.
|
|
14
|
+
* - Expiry is judged against the INJECTED clock, never the wall clock
|
|
15
|
+
* (invariant 7).
|
|
16
|
+
* - A maximum duration is enforced: a quarantine may never outlive the
|
|
17
|
+
* documented ceiling, so a "temporary" escape hatch cannot become
|
|
18
|
+
* permanent by hand-editing the expiry.
|
|
19
|
+
* - Two quarantines on the same test key are ambiguous → error.
|
|
20
|
+
*
|
|
21
|
+
* Loading is synchronous, matching the waivers loader.
|
|
22
|
+
*/
|
|
23
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
24
|
+
import { basename, dirname, join } from 'node:path';
|
|
25
|
+
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
26
|
+
import { compareStrings } from '../graph/util.js';
|
|
27
|
+
import { QuarantineSchema } from '../schemas/quarantine.js';
|
|
28
|
+
import { parseInstant } from '../verdict/index.js';
|
|
29
|
+
/** Maximum quarantine lifetime in days: a quarantine always expires. */
|
|
30
|
+
export const QUARANTINE_MAX_DAYS = 14;
|
|
31
|
+
/** The documented ceiling in milliseconds. */
|
|
32
|
+
export const QUARANTINE_MAX_DURATION_MS = QUARANTINE_MAX_DAYS * 86_400_000;
|
|
33
|
+
/** Repo-relative quarantine directory. */
|
|
34
|
+
export const QUARANTINE_DIR = '.gateforge/quarantine';
|
|
35
|
+
/** Error raised for any fail-closed quarantine configuration problem. */
|
|
36
|
+
export class GateforgeQuarantineError extends Error {
|
|
37
|
+
/** Every problem found, file-scoped, in deterministic order. */
|
|
38
|
+
problems;
|
|
39
|
+
constructor(problems) {
|
|
40
|
+
const rendered = problems.map((problem) => ` ${problem.file}: ${problem.detail}`).join('\n');
|
|
41
|
+
super(`quarantine configuration failed closed with ${problems.length} problem(s):\n${rendered}`);
|
|
42
|
+
this.name = 'GateforgeQuarantineError';
|
|
43
|
+
this.problems = problems;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Parses one quarantine document (fail-closed: an empty or non-mapping
|
|
48
|
+
* document is a problem, never an empty quarantine).
|
|
49
|
+
*
|
|
50
|
+
* Args:
|
|
51
|
+
* text: the raw file content.
|
|
52
|
+
*
|
|
53
|
+
* Returns:
|
|
54
|
+
* unknown: the parsed YAML mapping.
|
|
55
|
+
*
|
|
56
|
+
* @throws Error when the bytes are unparsable or not a mapping.
|
|
57
|
+
*/
|
|
58
|
+
function parseYamlDocument(text) {
|
|
59
|
+
const parsed = parseYaml(text);
|
|
60
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
61
|
+
throw new Error('quarantine document must be a mapping with schemaVersion, testKey, owner, approver, reason, expiresAt');
|
|
62
|
+
}
|
|
63
|
+
return parsed;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Loads and validates every `*.yml` quarantine file in `dir` (sorted by
|
|
67
|
+
* filename for determinism). A missing directory yields an empty result
|
|
68
|
+
* — most repositories have no quarantines. Any problem (unparsable
|
|
69
|
+
* YAML, missing mandatory field, duplicate test key, over-long duration)
|
|
70
|
+
* fails closed with {@link GateforgeQuarantineError}.
|
|
71
|
+
*
|
|
72
|
+
* Args:
|
|
73
|
+
* dir: the quarantine directory (`.gateforge/quarantine`).
|
|
74
|
+
* options: injected clock and optional maximum duration.
|
|
75
|
+
*
|
|
76
|
+
* Returns:
|
|
77
|
+
* QuarantineLoadResult: active and expired partitions, both sorted by
|
|
78
|
+
* test key.
|
|
79
|
+
*
|
|
80
|
+
* @throws GateforgeQuarantineError when any file fails closed.
|
|
81
|
+
*/
|
|
82
|
+
export function loadQuarantines(dir, options) {
|
|
83
|
+
const now = parseInstant(options.now);
|
|
84
|
+
const maxDurationMs = options.maxDurationMs ?? QUARANTINE_MAX_DURATION_MS;
|
|
85
|
+
if (!existsSync(dir))
|
|
86
|
+
return { active: [], expired: [] };
|
|
87
|
+
const files = readdirSync(dir, { withFileTypes: true })
|
|
88
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith('.yml'))
|
|
89
|
+
.map((entry) => entry.name)
|
|
90
|
+
.sort(compareStrings);
|
|
91
|
+
const problems = [];
|
|
92
|
+
const parsed = [];
|
|
93
|
+
for (const file of files) {
|
|
94
|
+
let document;
|
|
95
|
+
try {
|
|
96
|
+
document = parseYamlDocument(readFileSync(join(dir, file), 'utf8'));
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
problems.push({
|
|
100
|
+
file,
|
|
101
|
+
detail: `not parsable YAML: ${error instanceof Error ? error.message : String(error)}`,
|
|
102
|
+
});
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const result = QuarantineSchema.safeParse(document);
|
|
106
|
+
if (!result.success) {
|
|
107
|
+
for (const issue of result.error.issues) {
|
|
108
|
+
problems.push({ file, detail: `${issue.path.join('.') || '<quarantine>'}: ${issue.message}` });
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const duration = Date.parse(result.data.expiresAt) - now.getTime();
|
|
113
|
+
if (duration > maxDurationMs) {
|
|
114
|
+
problems.push({
|
|
115
|
+
file,
|
|
116
|
+
detail: `expiresAt '${result.data.expiresAt}' exceeds the maximum quarantine duration ` +
|
|
117
|
+
`(${String(Math.round(maxDurationMs / 86_400_000))} days from now)`,
|
|
118
|
+
});
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
parsed.push({ file, quarantine: result.data });
|
|
122
|
+
}
|
|
123
|
+
const byTestKey = new Map();
|
|
124
|
+
for (const entry of parsed) {
|
|
125
|
+
const firstFile = byTestKey.get(entry.quarantine.testKey);
|
|
126
|
+
if (firstFile !== undefined) {
|
|
127
|
+
problems.push({
|
|
128
|
+
file: entry.file,
|
|
129
|
+
detail: `test key '${entry.quarantine.testKey}' is already quarantined by '${firstFile}' — ` +
|
|
130
|
+
'one quarantine per test, never two overlapping exceptions',
|
|
131
|
+
});
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
byTestKey.set(entry.quarantine.testKey, entry.file);
|
|
135
|
+
}
|
|
136
|
+
if (problems.length > 0)
|
|
137
|
+
throw new GateforgeQuarantineError(problems);
|
|
138
|
+
const active = [];
|
|
139
|
+
const expired = [];
|
|
140
|
+
for (const entry of parsed) {
|
|
141
|
+
if (now.getTime() >= Date.parse(entry.quarantine.expiresAt))
|
|
142
|
+
expired.push(entry);
|
|
143
|
+
else
|
|
144
|
+
active.push(entry);
|
|
145
|
+
}
|
|
146
|
+
const byTest = (a, b) => compareStrings(a.quarantine.testKey, b.quarantine.testKey);
|
|
147
|
+
return { active: [...active].sort(byTest), expired: [...expired].sort(byTest) };
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Serializes one quarantine document in the stable on-disk form
|
|
151
|
+
* (block YAML, trailing newline).
|
|
152
|
+
*
|
|
153
|
+
* Args:
|
|
154
|
+
* quarantine: the schema-valid document.
|
|
155
|
+
*
|
|
156
|
+
* Returns:
|
|
157
|
+
* string: the file content.
|
|
158
|
+
*/
|
|
159
|
+
export function serializeQuarantine(quarantine) {
|
|
160
|
+
const body = stringifyYaml({
|
|
161
|
+
schemaVersion: quarantine.schemaVersion,
|
|
162
|
+
testKey: quarantine.testKey,
|
|
163
|
+
owner: quarantine.owner,
|
|
164
|
+
approver: quarantine.approver,
|
|
165
|
+
reason: quarantine.reason,
|
|
166
|
+
expiresAt: quarantine.expiresAt,
|
|
167
|
+
});
|
|
168
|
+
return `${body.endsWith('\n') ? body : `${body}\n`}`;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Writes one quarantine document, creating parent directories as needed.
|
|
172
|
+
* Fail-closed: a write failure surfaces as a {@link GateforgeQuarantineError}.
|
|
173
|
+
*
|
|
174
|
+
* Args:
|
|
175
|
+
* path: destination file path.
|
|
176
|
+
* quarantine: the schema-valid document to write.
|
|
177
|
+
*
|
|
178
|
+
* @throws GateforgeQuarantineError when the file cannot be written.
|
|
179
|
+
*/
|
|
180
|
+
export function writeQuarantine(path, quarantine) {
|
|
181
|
+
try {
|
|
182
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
183
|
+
writeFileSync(path, serializeQuarantine(quarantine), 'utf8');
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
throw new GateforgeQuarantineError([
|
|
187
|
+
{
|
|
188
|
+
file: basename(path),
|
|
189
|
+
detail: `could not be written: ${error instanceof Error ? error.message : String(error)}`,
|
|
190
|
+
},
|
|
191
|
+
]);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/quarantine/index.ts"],"names":[],"mappings":"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"}
|
package/dist/receipt/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type JsonValue } from '../canonical-json.js';
|
|
2
|
-
import { type GateReceipt } from '../schemas/gate-receipt.js';
|
|
3
2
|
import { EMPTY_BEHAVIOR_CATALOG_DIGEST } from '../policy/behavior.js';
|
|
3
|
+
import { type GateReceipt } from '../schemas/gate-receipt.js';
|
|
4
|
+
import type { ExecutedOutcome } from '../schemas/execution-result.js';
|
|
5
|
+
import { type RunRecord, type RunRecordBody } from '../schemas/run-record.js';
|
|
4
6
|
/** Domain tag binding receipt MACs to the gate-receipt envelope format. */
|
|
5
7
|
export declare const RECEIPT_DOMAIN = "gateforge.receipt.v2";
|
|
6
8
|
/** The only receipt envelope version this code produces or honors. */
|
|
@@ -63,6 +65,68 @@ export { EMPTY_BEHAVIOR_CATALOG_DIGEST };
|
|
|
63
65
|
* TypeError: when the verifier key is empty.
|
|
64
66
|
*/
|
|
65
67
|
export declare function gateReceiptMac(verifierKey: string, body: GateReceiptBody): string;
|
|
68
|
+
/**
|
|
69
|
+
* Digest over a run's per-test outcomes, in the sealed order. A run
|
|
70
|
+
* record binds it so the record can never claim a cleaner run than the
|
|
71
|
+
* execution result it names, and a consumer recomputes it from that
|
|
72
|
+
* execution result when it recomputes the re-seal.
|
|
73
|
+
*
|
|
74
|
+
* Args:
|
|
75
|
+
* outcomes: the sealed execution result's per-test outcomes.
|
|
76
|
+
*
|
|
77
|
+
* Returns:
|
|
78
|
+
* string: 64-char lowercase hex digest.
|
|
79
|
+
*/
|
|
80
|
+
export declare function testOutcomesDigestOf(outcomes: readonly ExecutedOutcome[]): string;
|
|
81
|
+
/** Domain tag binding run-record MACs to the run-record envelope format. */
|
|
82
|
+
export declare const RUN_RECORD_DOMAIN = "gateforge.run-record.v1";
|
|
83
|
+
/** The only run-record envelope version this code produces or honors. */
|
|
84
|
+
export declare const RUN_RECORD_VERSION = 1;
|
|
85
|
+
/**
|
|
86
|
+
* Computes the run-record MAC: HMAC-SHA256 over the GF-canonical JSON
|
|
87
|
+
* of `{domain: 'gateforge.run-record.v1', ...body}` keyed by the same
|
|
88
|
+
* witness verifier key a receipt uses. The distinct domain tag makes
|
|
89
|
+
* cross-format acceptance impossible in both directions: a run record
|
|
90
|
+
* can never verify as a gate receipt, and a receipt MAC can never
|
|
91
|
+
* verify as a run record.
|
|
92
|
+
*
|
|
93
|
+
* Args:
|
|
94
|
+
* verifierKey: the witness verifier secret (non-empty).
|
|
95
|
+
* body: the unsigned run-record body.
|
|
96
|
+
*
|
|
97
|
+
* Returns:
|
|
98
|
+
* string: 64-char lowercase hex HMAC.
|
|
99
|
+
*
|
|
100
|
+
* Throws:
|
|
101
|
+
* TypeError: when the verifier key is empty.
|
|
102
|
+
*/
|
|
103
|
+
export declare function runRecordMac(verifierKey: string, body: RunRecordBody): string;
|
|
104
|
+
/** Typed reasons a run-record candidate failed verification (fail closed). */
|
|
105
|
+
export type RunRecordRejection = 'missing' | 'malformed' | 'mac-fail' | 'not-whole-suite';
|
|
106
|
+
/** The verification outcome: a validated run record or a typed rejection. */
|
|
107
|
+
export type RunRecordVerification = {
|
|
108
|
+
ok: true;
|
|
109
|
+
record: RunRecord;
|
|
110
|
+
} | {
|
|
111
|
+
ok: false;
|
|
112
|
+
rejection: RunRecordRejection;
|
|
113
|
+
detail: string;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Verifies a run-record candidate end to end (fail closed): the strict
|
|
117
|
+
* schema first, then the MAC under the given verifier key. A run record
|
|
118
|
+
* authenticates EXACTLY this much and no more — it carries no gate
|
|
119
|
+
* verdict, so there is nothing here to grade, compare against a policy
|
|
120
|
+
* pin, or accept as proof for a blocked gate.
|
|
121
|
+
*
|
|
122
|
+
* Args:
|
|
123
|
+
* verifierKey: the witness verifier secret the record must carry a MAC for.
|
|
124
|
+
* candidate: the parsed run-state document.
|
|
125
|
+
*
|
|
126
|
+
* Returns:
|
|
127
|
+
* RunRecordVerification: the validated record or the typed rejection.
|
|
128
|
+
*/
|
|
129
|
+
export declare function verifyRunRecord(verifierKey: string, candidate: unknown): RunRecordVerification;
|
|
66
130
|
/** Typed reasons a receipt candidate failed verification (fail closed). */
|
|
67
131
|
export type ReceiptRejection = 'missing' | 'malformed' | 'mac-fail' | 'input-digest-mismatch' | 'policy-digest-mismatch' | 'selection-digest-mismatch' | 'catalog-digest-mismatch' | 'execution-digest-mismatch' | 'attestation-digest-mismatch' | 'tree-mismatch' | 'behavior-digest-mismatch' | 'case-set-mismatch' | 'case-execution-mismatch' | 'engine-bundle-mismatch' | 'boundary-mismatch' | 'artifact-mismatch' | 'not-clean';
|
|
68
132
|
/** The verification outcome: a validated receipt or a typed rejection. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/receipt/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAkC,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/receipt/index.ts"],"names":[],"mappings":"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"}
|
package/dist/receipt/index.js
CHANGED
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
17
17
|
import { canonicalJson, sha256Canonical } from '../canonical-json.js';
|
|
18
|
-
import { GateReceiptSchema } from '../schemas/gate-receipt.js';
|
|
19
18
|
import { EMPTY_BEHAVIOR_CATALOG_DIGEST } from '../policy/behavior.js';
|
|
19
|
+
import { GateReceiptSchema } from '../schemas/gate-receipt.js';
|
|
20
|
+
import { RunRecordSchema } from '../schemas/run-record.js';
|
|
20
21
|
/** Domain tag binding receipt MACs to the gate-receipt envelope format. */
|
|
21
22
|
export const RECEIPT_DOMAIN = 'gateforge.receipt.v2';
|
|
22
23
|
/** The only receipt envelope version this code produces or honors. */
|
|
@@ -127,6 +128,119 @@ export function gateReceiptMac(verifierKey, body) {
|
|
|
127
128
|
}))
|
|
128
129
|
.digest('hex');
|
|
129
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Digest over a run's per-test outcomes, in the sealed order. A run
|
|
133
|
+
* record binds it so the record can never claim a cleaner run than the
|
|
134
|
+
* execution result it names, and a consumer recomputes it from that
|
|
135
|
+
* execution result when it recomputes the re-seal.
|
|
136
|
+
*
|
|
137
|
+
* Args:
|
|
138
|
+
* outcomes: the sealed execution result's per-test outcomes.
|
|
139
|
+
*
|
|
140
|
+
* Returns:
|
|
141
|
+
* string: 64-char lowercase hex digest.
|
|
142
|
+
*/
|
|
143
|
+
export function testOutcomesDigestOf(outcomes) {
|
|
144
|
+
return sha256Canonical(outcomes);
|
|
145
|
+
}
|
|
146
|
+
/** Domain tag binding run-record MACs to the run-record envelope format. */
|
|
147
|
+
export const RUN_RECORD_DOMAIN = 'gateforge.run-record.v1';
|
|
148
|
+
/** The only run-record envelope version this code produces or honors. */
|
|
149
|
+
export const RUN_RECORD_VERSION = 1;
|
|
150
|
+
/**
|
|
151
|
+
* Computes the run-record MAC: HMAC-SHA256 over the GF-canonical JSON
|
|
152
|
+
* of `{domain: 'gateforge.run-record.v1', ...body}` keyed by the same
|
|
153
|
+
* witness verifier key a receipt uses. The distinct domain tag makes
|
|
154
|
+
* cross-format acceptance impossible in both directions: a run record
|
|
155
|
+
* can never verify as a gate receipt, and a receipt MAC can never
|
|
156
|
+
* verify as a run record.
|
|
157
|
+
*
|
|
158
|
+
* Args:
|
|
159
|
+
* verifierKey: the witness verifier secret (non-empty).
|
|
160
|
+
* body: the unsigned run-record body.
|
|
161
|
+
*
|
|
162
|
+
* Returns:
|
|
163
|
+
* string: 64-char lowercase hex HMAC.
|
|
164
|
+
*
|
|
165
|
+
* Throws:
|
|
166
|
+
* TypeError: when the verifier key is empty.
|
|
167
|
+
*/
|
|
168
|
+
export function runRecordMac(verifierKey, body) {
|
|
169
|
+
if (typeof verifierKey !== 'string' || verifierKey.length === 0) {
|
|
170
|
+
throw new TypeError('runRecordMac: verifier key must be a non-empty string');
|
|
171
|
+
}
|
|
172
|
+
return createHmac('sha256', verifierKey)
|
|
173
|
+
.update(canonicalJson({
|
|
174
|
+
domain: RUN_RECORD_DOMAIN,
|
|
175
|
+
...body,
|
|
176
|
+
}))
|
|
177
|
+
.digest('hex');
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Verifies a run-record candidate end to end (fail closed): the strict
|
|
181
|
+
* schema first, then the MAC under the given verifier key. A run record
|
|
182
|
+
* authenticates EXACTLY this much and no more — it carries no gate
|
|
183
|
+
* verdict, so there is nothing here to grade, compare against a policy
|
|
184
|
+
* pin, or accept as proof for a blocked gate.
|
|
185
|
+
*
|
|
186
|
+
* Args:
|
|
187
|
+
* verifierKey: the witness verifier secret the record must carry a MAC for.
|
|
188
|
+
* candidate: the parsed run-state document.
|
|
189
|
+
*
|
|
190
|
+
* Returns:
|
|
191
|
+
* RunRecordVerification: the validated record or the typed rejection.
|
|
192
|
+
*/
|
|
193
|
+
export function verifyRunRecord(verifierKey, candidate) {
|
|
194
|
+
if (candidate === undefined || candidate === null) {
|
|
195
|
+
return { ok: false, rejection: 'missing', detail: 'run record is missing' };
|
|
196
|
+
}
|
|
197
|
+
const parsed = RunRecordSchema.safeParse(candidate);
|
|
198
|
+
if (!parsed.success) {
|
|
199
|
+
const issue = parsed.error.issues[0];
|
|
200
|
+
const path = issue === undefined ? '' : ` at '${issue.path.map(String).join('.')}':`;
|
|
201
|
+
return {
|
|
202
|
+
ok: false,
|
|
203
|
+
rejection: 'malformed',
|
|
204
|
+
detail: `run record is malformed${path} ${issue?.message ?? 'unknown schema error'}`,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
const record = parsed.data;
|
|
208
|
+
const { mac, ...body } = record;
|
|
209
|
+
if (typeof verifierKey !== 'string' || verifierKey.length === 0) {
|
|
210
|
+
return {
|
|
211
|
+
ok: false,
|
|
212
|
+
rejection: 'mac-fail',
|
|
213
|
+
detail: 'run record cannot verify without a verifier key (fail closed)',
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (!MAC_PATTERN.test(mac)) {
|
|
217
|
+
return { ok: false, rejection: 'mac-fail', detail: 'run record mac is malformed' };
|
|
218
|
+
}
|
|
219
|
+
let expectedMac;
|
|
220
|
+
let claimedMac;
|
|
221
|
+
try {
|
|
222
|
+
expectedMac = Buffer.from(runRecordMac(verifierKey, body), 'hex');
|
|
223
|
+
claimedMac = Buffer.from(mac, 'hex');
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
return { ok: false, rejection: 'mac-fail', detail: 'run record mac could not be recomputed' };
|
|
227
|
+
}
|
|
228
|
+
if (expectedMac.length !== claimedMac.length || !timingSafeEqual(expectedMac, claimedMac)) {
|
|
229
|
+
return {
|
|
230
|
+
ok: false,
|
|
231
|
+
rejection: 'mac-fail',
|
|
232
|
+
detail: 'run record signature fails; the record was forged or tampered with (fail closed)',
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (record.plannedTests === 0) {
|
|
236
|
+
return {
|
|
237
|
+
ok: false,
|
|
238
|
+
rejection: 'not-whole-suite',
|
|
239
|
+
detail: 'run record holds no planned test, so it proves no whole-suite run',
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return { ok: true, record };
|
|
243
|
+
}
|
|
130
244
|
/**
|
|
131
245
|
* Verifies a receipt candidate end-to-end (fail closed):
|
|
132
246
|
* 1. structural — the value must parse against the strict v1 schema;
|