@gate-forge/core 0.7.0 → 0.8.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 +173 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +296 -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 +22 -1
- package/dist/mapping/resolve.d.ts.map +1 -1
- package/dist/mapping/resolve.js +71 -25
- 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 +296 -22
- 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 +38 -4
- 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 +54 -0
- package/dist/verdict/evaluate.d.ts.map +1 -1
- package/dist/verdict/evaluate.js +278 -12
- 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
|
@@ -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;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/receipt/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,eAAe,EAAkB,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAoB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/receipt/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { CauseCode } from '../schemas/verdict.js';
|
|
2
|
+
/** Minimal report data required to build a consistent human-readable message. */
|
|
3
|
+
export interface HumanMessageEntry {
|
|
4
|
+
/** The stable cause code, when the entry has one. */
|
|
5
|
+
cause?: CauseCode | null;
|
|
6
|
+
/** Plain-language detail or verdict reason. */
|
|
7
|
+
detail?: string;
|
|
8
|
+
/** Resource or obligation identity used to choose a useful command. */
|
|
9
|
+
id?: string;
|
|
10
|
+
/** Suggested action associated with the cause. */
|
|
11
|
+
nextAction?: string | null;
|
|
12
|
+
/** Verdict reason, when the entry represents an obligation. */
|
|
13
|
+
reason?: string | null;
|
|
14
|
+
/** Blocking kind or verdict name used when no cause is available. */
|
|
15
|
+
type?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Prefix of the refusal a receipt sealed by an older engine produces. The
|
|
19
|
+
* receipt records the version that sealed it, so a version disagreement is
|
|
20
|
+
* reportable fact, and it is the one cause a user acts on by re-sealing.
|
|
21
|
+
*/
|
|
22
|
+
export declare const ENGINE_UPGRADE_REFUSAL_PREFIX = "require-e2e: this receipt was sealed by Gateforge ";
|
|
23
|
+
/**
|
|
24
|
+
* Builds the common sentence, copyable command, and final cause marker for a report entry.
|
|
25
|
+
*
|
|
26
|
+
* Args:
|
|
27
|
+
* entry: report detail, identity, cause, and suggested action.
|
|
28
|
+
*
|
|
29
|
+
* Returns:
|
|
30
|
+
* string: one deterministic sentence followed by a runnable command and `[CODE]`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function humanMessage(entry: HumanMessageEntry): string;
|
|
33
|
+
//# sourceMappingURL=human-message.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"human-message.d.ts","sourceRoot":"","sources":["../../src/report/human-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,iFAAiF;AACjF,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,uDACY,CAAC;AAEvD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAiB7D"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prefix of the refusal a receipt sealed by an older engine produces. The
|
|
3
|
+
* receipt records the version that sealed it, so a version disagreement is
|
|
4
|
+
* reportable fact, and it is the one cause a user acts on by re-sealing.
|
|
5
|
+
*/
|
|
6
|
+
export const ENGINE_UPGRADE_REFUSAL_PREFIX = 'require-e2e: this receipt was sealed by Gateforge ';
|
|
7
|
+
/**
|
|
8
|
+
* Builds the common sentence, copyable command, and final cause marker for a report entry.
|
|
9
|
+
*
|
|
10
|
+
* Args:
|
|
11
|
+
* entry: report detail, identity, cause, and suggested action.
|
|
12
|
+
*
|
|
13
|
+
* Returns:
|
|
14
|
+
* string: one deterministic sentence followed by a runnable command and `[CODE]`.
|
|
15
|
+
*/
|
|
16
|
+
export function humanMessage(entry) {
|
|
17
|
+
// The sentence gets its own period below; a detail that already ends in
|
|
18
|
+
// one would print `.. Run`.
|
|
19
|
+
const sentence = (entry.reason ?? entry.detail ?? 'Gateforge found an unresolved item').trim().replace(/\.$/, '');
|
|
20
|
+
const command = entry.nextAction?.startsWith('gateforge ')
|
|
21
|
+
? entry.nextAction
|
|
22
|
+
: entry.cause === 'TEST_MAPPING_MISSING' ||
|
|
23
|
+
entry.cause === 'TEST_MAPPING_AMBIGUOUS' ||
|
|
24
|
+
entry.cause === 'TEST_MAPPING_STALE' ||
|
|
25
|
+
entry.cause === 'TEST_KIND_UNKNOWN'
|
|
26
|
+
? 'gateforge tests suggest'
|
|
27
|
+
: entry.id !== undefined
|
|
28
|
+
? `gateforge explain ${entry.id}`
|
|
29
|
+
: 'gateforge discover --json';
|
|
30
|
+
const code = entry.cause ?? entry.type?.toUpperCase().replaceAll('-', '_') ?? 'BLOCKING_FINDING';
|
|
31
|
+
return `${sentence}. Run \`${command}\`. [${code}]`;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=human-message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"human-message.js","sourceRoot":"","sources":["../../src/report/human-message.ts"],"names":[],"mappings":"AAkBA;;;;GAIG;AACH,MAAM,CAAC,MAAM,6BAA6B,GACxC,oDAAoD,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,KAAwB;IACnD,wEAAwE;IACxE,4BAA4B;IAC5B,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,oCAAoC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAClH,MAAM,OAAO,GACX,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,YAAY,CAAC;QACxC,CAAC,CAAC,KAAK,CAAC,UAAU;QAClB,CAAC,CAAC,KAAK,CAAC,KAAK,KAAK,sBAAsB;YACpC,KAAK,CAAC,KAAK,KAAK,wBAAwB;YACxC,KAAK,CAAC,KAAK,KAAK,oBAAoB;YACpC,KAAK,CAAC,KAAK,KAAK,mBAAmB;YACrC,CAAC,CAAC,yBAAyB;YAC3B,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,SAAS;gBACtB,CAAC,CAAC,qBAAqB,KAAK,CAAC,EAAE,EAAE;gBACjC,CAAC,CAAC,2BAA2B,CAAC;IACtC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,kBAAkB,CAAC;IACjG,OAAO,GAAG,QAAQ,WAAW,OAAO,QAAQ,IAAI,GAAG,CAAC;AACtD,CAAC"}
|