@peac/audit 0.10.9
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/LICENSE +190 -0
- package/README.md +133 -0
- package/dist/bundle.d.ts +97 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +209 -0
- package/dist/bundle.js.map +1 -0
- package/dist/dispute-bundle-types.d.ts +303 -0
- package/dist/dispute-bundle-types.d.ts.map +1 -0
- package/dist/dispute-bundle-types.js +31 -0
- package/dist/dispute-bundle-types.js.map +1 -0
- package/dist/dispute-bundle.d.ts +54 -0
- package/dist/dispute-bundle.d.ts.map +1 -0
- package/dist/dispute-bundle.js +838 -0
- package/dist/dispute-bundle.js.map +1 -0
- package/dist/entry.d.ts +76 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +237 -0
- package/dist/entry.js.map +1 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +101 -0
- package/dist/index.js.map +1 -0
- package/dist/jsonl.d.ts +111 -0
- package/dist/jsonl.d.ts.map +1 -0
- package/dist/jsonl.js +182 -0
- package/dist/jsonl.js.map +1 -0
- package/dist/types.d.ts +191 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/verification-report.d.ts +48 -0
- package/dist/verification-report.d.ts.map +1 -0
- package/dist/verification-report.js +556 -0
- package/dist/verification-report.js.map +1 -0
- package/dist/workflow-dag.d.ts +112 -0
- package/dist/workflow-dag.d.ts.map +1 -0
- package/dist/workflow-dag.js +409 -0
- package/dist/workflow-dag.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Verification Report (v0.9.30+)
|
|
4
|
+
*
|
|
5
|
+
* Deterministic verification of dispute bundles with JCS-canonicalized reports.
|
|
6
|
+
* The report_hash enables cross-language parity: TS and Go implementations
|
|
7
|
+
* must produce identical hashes for the same bundle verification.
|
|
8
|
+
*
|
|
9
|
+
* Key design principles:
|
|
10
|
+
* 1. Real Ed25519 signature verification using @peac/crypto
|
|
11
|
+
* 2. No timestamps in deterministic output
|
|
12
|
+
* 3. All arrays sorted by stable keys for reproducibility
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.verifyBundle = verifyBundle;
|
|
16
|
+
exports.serializeReport = serializeReport;
|
|
17
|
+
exports.formatReportText = formatReportText;
|
|
18
|
+
const node_crypto_1 = require("node:crypto");
|
|
19
|
+
const crypto_1 = require("@peac/crypto");
|
|
20
|
+
const dispute_bundle_js_1 = require("./dispute-bundle.js");
|
|
21
|
+
const dispute_bundle_js_2 = require("./dispute-bundle.js");
|
|
22
|
+
const dispute_bundle_types_js_1 = require("./dispute-bundle-types.js");
|
|
23
|
+
/**
|
|
24
|
+
* Compute SHA-256 hash of data with self-describing format.
|
|
25
|
+
* Returns `sha256:<64 lowercase hex chars>` format.
|
|
26
|
+
*/
|
|
27
|
+
function sha256Hex(data) {
|
|
28
|
+
const hash = (0, node_crypto_1.createHash)('sha256');
|
|
29
|
+
hash.update(data);
|
|
30
|
+
return `sha256:${hash.digest('hex')}`;
|
|
31
|
+
}
|
|
32
|
+
/** Decode base64url to Buffer */
|
|
33
|
+
function base64urlDecode(str) {
|
|
34
|
+
const padded = str + '='.repeat((4 - (str.length % 4)) % 4);
|
|
35
|
+
const base64 = padded.replace(/-/g, '+').replace(/_/g, '/');
|
|
36
|
+
return Buffer.from(base64, 'base64');
|
|
37
|
+
}
|
|
38
|
+
/** Parse JWS to extract header and payload */
|
|
39
|
+
function parseJws(jws) {
|
|
40
|
+
const parts = jws.split('.');
|
|
41
|
+
if (parts.length !== 3) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const headerJson = base64urlDecode(parts[0]).toString('utf8');
|
|
46
|
+
const payloadJson = base64urlDecode(parts[1]).toString('utf8');
|
|
47
|
+
return {
|
|
48
|
+
header: JSON.parse(headerJson),
|
|
49
|
+
payload: JSON.parse(payloadJson),
|
|
50
|
+
signature: parts[2],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Create a bundle error */
|
|
58
|
+
function bundleError(code, message, details) {
|
|
59
|
+
return { code, message, details };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Strip undefined values from an object recursively.
|
|
63
|
+
* JCS canonicalization cannot handle undefined values.
|
|
64
|
+
*/
|
|
65
|
+
function stripUndefined(obj) {
|
|
66
|
+
if (obj === null || obj === undefined) {
|
|
67
|
+
return obj;
|
|
68
|
+
}
|
|
69
|
+
if (Array.isArray(obj)) {
|
|
70
|
+
return obj.map(stripUndefined);
|
|
71
|
+
}
|
|
72
|
+
if (typeof obj === 'object') {
|
|
73
|
+
const result = {};
|
|
74
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
75
|
+
if (value !== undefined) {
|
|
76
|
+
result[key] = stripUndefined(value);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
return obj;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Verify receipt claims (basic validation without cryptographic signature check).
|
|
85
|
+
*
|
|
86
|
+
* This validates:
|
|
87
|
+
* - Required claims are present (jti, iss, iat)
|
|
88
|
+
* - Timestamps are valid (not expired, not in future)
|
|
89
|
+
*
|
|
90
|
+
* Note: Signature verification requires access to the signing key and is
|
|
91
|
+
* handled separately. This function focuses on claim validation.
|
|
92
|
+
*/
|
|
93
|
+
function verifyReceiptClaims(payload, now) {
|
|
94
|
+
const errors = [];
|
|
95
|
+
const nowSec = Math.floor(now.getTime() / 1000);
|
|
96
|
+
// Required claims
|
|
97
|
+
if (!payload.jti) {
|
|
98
|
+
errors.push('E_RECEIPT_MISSING_JTI');
|
|
99
|
+
}
|
|
100
|
+
if (!payload.iss) {
|
|
101
|
+
errors.push('E_RECEIPT_MISSING_ISS');
|
|
102
|
+
}
|
|
103
|
+
if (payload.iat === undefined) {
|
|
104
|
+
errors.push('E_RECEIPT_MISSING_IAT');
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
const iat = typeof payload.iat === 'number' ? payload.iat : NaN;
|
|
108
|
+
if (isNaN(iat)) {
|
|
109
|
+
errors.push('E_RECEIPT_INVALID_IAT');
|
|
110
|
+
}
|
|
111
|
+
else if (iat > nowSec + 300) {
|
|
112
|
+
// Allow 5 min clock skew
|
|
113
|
+
errors.push('E_RECEIPT_NOT_YET_VALID');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Check expiry if present
|
|
117
|
+
if (payload.exp !== undefined) {
|
|
118
|
+
const exp = typeof payload.exp === 'number' ? payload.exp : NaN;
|
|
119
|
+
if (isNaN(exp)) {
|
|
120
|
+
errors.push('E_RECEIPT_INVALID_EXP');
|
|
121
|
+
}
|
|
122
|
+
else if (exp < nowSec) {
|
|
123
|
+
errors.push('E_RECEIPT_EXPIRED');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
valid: errors.length === 0,
|
|
128
|
+
errors,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Find the key ID used to sign a receipt.
|
|
133
|
+
*/
|
|
134
|
+
function getReceiptKeyId(header) {
|
|
135
|
+
return typeof header.kid === 'string' ? header.kid : undefined;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Convert JWK to raw Ed25519 public key bytes (32 bytes).
|
|
139
|
+
* Returns null if the JWK is not a valid Ed25519 key.
|
|
140
|
+
*/
|
|
141
|
+
function jwkToPublicKeyBytes(jwk) {
|
|
142
|
+
if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519' || !jwk.x) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
// Decode base64url to bytes
|
|
146
|
+
const padded = jwk.x + '='.repeat((4 - (jwk.x.length % 4)) % 4);
|
|
147
|
+
const base64 = padded.replace(/-/g, '+').replace(/_/g, '/');
|
|
148
|
+
const bytes = Buffer.from(base64, 'base64');
|
|
149
|
+
if (bytes.length !== 32) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
return new Uint8Array(bytes);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Find a key by kid in the bundle's JWKS.
|
|
156
|
+
*/
|
|
157
|
+
function findKey(keys, kid) {
|
|
158
|
+
return keys.find((k) => k.kid === kid);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Verify a single receipt with real Ed25519 signature verification.
|
|
162
|
+
*
|
|
163
|
+
* In offline mode, we use only the keys bundled in the JWKS.
|
|
164
|
+
* All signature verification is cryptographic - not just key presence.
|
|
165
|
+
*/
|
|
166
|
+
async function verifyReceipt(receiptId, jws, bundleContents, options) {
|
|
167
|
+
const parsed = parseJws(jws);
|
|
168
|
+
if (!parsed) {
|
|
169
|
+
return {
|
|
170
|
+
receipt_id: receiptId,
|
|
171
|
+
signature_valid: false,
|
|
172
|
+
claims_valid: false,
|
|
173
|
+
errors: ['E_RECEIPT_INVALID_FORMAT'],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const { header, payload } = parsed;
|
|
177
|
+
const keyId = getReceiptKeyId(header);
|
|
178
|
+
const errors = [];
|
|
179
|
+
// Check key ID presence
|
|
180
|
+
if (!keyId) {
|
|
181
|
+
return {
|
|
182
|
+
receipt_id: receiptId,
|
|
183
|
+
signature_valid: false,
|
|
184
|
+
claims_valid: false,
|
|
185
|
+
errors: ['E_RECEIPT_MISSING_KID'],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
// Find the key in the bundle's JWKS
|
|
189
|
+
const jwk = findKey(bundleContents.keys.keys, keyId);
|
|
190
|
+
if (!jwk) {
|
|
191
|
+
// Key not found - in offline mode this is fatal
|
|
192
|
+
if (options.offline) {
|
|
193
|
+
return {
|
|
194
|
+
receipt_id: receiptId,
|
|
195
|
+
signature_valid: false,
|
|
196
|
+
claims_valid: false,
|
|
197
|
+
key_id: keyId,
|
|
198
|
+
errors: [dispute_bundle_js_2.BundleErrorCodes.KEY_MISSING],
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// In online mode, we could try to fetch the key, but for now fail
|
|
202
|
+
return {
|
|
203
|
+
receipt_id: receiptId,
|
|
204
|
+
signature_valid: false,
|
|
205
|
+
claims_valid: false,
|
|
206
|
+
key_id: keyId,
|
|
207
|
+
errors: [dispute_bundle_js_2.BundleErrorCodes.KEY_MISSING],
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
// Convert JWK to raw public key bytes
|
|
211
|
+
const publicKeyBytes = jwkToPublicKeyBytes(jwk);
|
|
212
|
+
if (!publicKeyBytes) {
|
|
213
|
+
return {
|
|
214
|
+
receipt_id: receiptId,
|
|
215
|
+
signature_valid: false,
|
|
216
|
+
claims_valid: false,
|
|
217
|
+
key_id: keyId,
|
|
218
|
+
errors: ['E_RECEIPT_INVALID_KEY_FORMAT'],
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
// Perform real Ed25519 signature verification
|
|
222
|
+
let signatureValid = false;
|
|
223
|
+
try {
|
|
224
|
+
const result = await (0, crypto_1.verify)(jws, publicKeyBytes);
|
|
225
|
+
signatureValid = result.valid;
|
|
226
|
+
if (!signatureValid) {
|
|
227
|
+
errors.push('E_RECEIPT_SIGNATURE_INVALID');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
// Handle crypto errors
|
|
232
|
+
if (err instanceof crypto_1.CryptoError) {
|
|
233
|
+
errors.push(`E_RECEIPT_CRYPTO_ERROR:${err.code}`);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
errors.push('E_RECEIPT_SIGNATURE_INVALID');
|
|
237
|
+
}
|
|
238
|
+
signatureValid = false;
|
|
239
|
+
}
|
|
240
|
+
// Validate claims
|
|
241
|
+
const claimsResult = verifyReceiptClaims(payload, options.now ?? new Date());
|
|
242
|
+
errors.push(...claimsResult.errors);
|
|
243
|
+
return {
|
|
244
|
+
receipt_id: receiptId,
|
|
245
|
+
signature_valid: signatureValid,
|
|
246
|
+
claims_valid: claimsResult.valid,
|
|
247
|
+
key_id: keyId,
|
|
248
|
+
errors,
|
|
249
|
+
claims: signatureValid && claimsResult.valid && errors.length === 0 ? payload : undefined,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Build key usage tracking.
|
|
254
|
+
*/
|
|
255
|
+
function buildKeyUsage(results) {
|
|
256
|
+
const usage = new Map();
|
|
257
|
+
for (const result of results) {
|
|
258
|
+
if (result.key_id) {
|
|
259
|
+
const existing = usage.get(result.key_id) ?? [];
|
|
260
|
+
existing.push(result.receipt_id);
|
|
261
|
+
usage.set(result.key_id, existing);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const entries = [];
|
|
265
|
+
for (const [kid, receiptIds] of usage) {
|
|
266
|
+
entries.push({
|
|
267
|
+
kid,
|
|
268
|
+
receipts_signed: receiptIds.length,
|
|
269
|
+
receipt_ids: receiptIds.sort(),
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
// Sort by kid for determinism
|
|
273
|
+
return entries.sort((a, b) => a.kid.localeCompare(b.kid));
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Generate auditor-friendly summary.
|
|
277
|
+
*/
|
|
278
|
+
function generateAuditorSummary(totalReceipts, validCount, invalidCount, results) {
|
|
279
|
+
const headline = `${validCount}/${totalReceipts} receipts valid`;
|
|
280
|
+
const issues = [];
|
|
281
|
+
for (const result of results) {
|
|
282
|
+
if (!result.signature_valid || !result.claims_valid) {
|
|
283
|
+
const errorSummary = result.errors.length > 0 ? result.errors.join(', ') : 'validation failed';
|
|
284
|
+
issues.push(`Receipt ${result.receipt_id}: ${errorSummary}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// Sort issues for determinism
|
|
288
|
+
issues.sort();
|
|
289
|
+
let recommendation;
|
|
290
|
+
if (invalidCount === 0) {
|
|
291
|
+
recommendation = 'valid';
|
|
292
|
+
}
|
|
293
|
+
else if (invalidCount === totalReceipts) {
|
|
294
|
+
recommendation = 'invalid';
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
recommendation = 'needs_review';
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
headline,
|
|
301
|
+
issues,
|
|
302
|
+
recommendation,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Verify the bundle.sig if present.
|
|
307
|
+
* The bundle.sig is a JWS over { content_hash: "..." } signed with a key from the JWKS.
|
|
308
|
+
*/
|
|
309
|
+
async function verifyBundleSignature(bundleContents) {
|
|
310
|
+
const { bundle_sig, keys, manifest } = bundleContents;
|
|
311
|
+
// No bundle.sig present
|
|
312
|
+
if (!bundle_sig) {
|
|
313
|
+
return { present: false };
|
|
314
|
+
}
|
|
315
|
+
// Parse the JWS to get the key ID
|
|
316
|
+
const parsed = parseJws(bundle_sig);
|
|
317
|
+
if (!parsed) {
|
|
318
|
+
return {
|
|
319
|
+
present: true,
|
|
320
|
+
valid: false,
|
|
321
|
+
error: 'E_BUNDLE_SIGNATURE_INVALID_FORMAT',
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
const keyId = typeof parsed.header.kid === 'string' ? parsed.header.kid : undefined;
|
|
325
|
+
if (!keyId) {
|
|
326
|
+
return {
|
|
327
|
+
present: true,
|
|
328
|
+
valid: false,
|
|
329
|
+
error: 'E_BUNDLE_SIGNATURE_MISSING_KID',
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
// Find the key in JWKS
|
|
333
|
+
const jwk = keys.keys.find((k) => k.kid === keyId);
|
|
334
|
+
if (!jwk) {
|
|
335
|
+
return {
|
|
336
|
+
present: true,
|
|
337
|
+
valid: false,
|
|
338
|
+
key_id: keyId,
|
|
339
|
+
error: dispute_bundle_js_2.BundleErrorCodes.KEY_MISSING,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
// Convert JWK to raw public key bytes
|
|
343
|
+
const publicKeyBytes = jwkToPublicKeyBytes(jwk);
|
|
344
|
+
if (!publicKeyBytes) {
|
|
345
|
+
return {
|
|
346
|
+
present: true,
|
|
347
|
+
valid: false,
|
|
348
|
+
key_id: keyId,
|
|
349
|
+
error: 'E_BUNDLE_SIGNATURE_INVALID_KEY_FORMAT',
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
// Verify the signature
|
|
353
|
+
try {
|
|
354
|
+
const result = await (0, crypto_1.verify)(bundle_sig, publicKeyBytes);
|
|
355
|
+
if (!result.valid) {
|
|
356
|
+
return {
|
|
357
|
+
present: true,
|
|
358
|
+
valid: false,
|
|
359
|
+
key_id: keyId,
|
|
360
|
+
error: dispute_bundle_js_2.BundleErrorCodes.SIGNATURE_INVALID,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
// Verify the content_hash in the payload matches the manifest
|
|
364
|
+
if (result.payload.content_hash !== manifest.content_hash) {
|
|
365
|
+
return {
|
|
366
|
+
present: true,
|
|
367
|
+
valid: false,
|
|
368
|
+
key_id: keyId,
|
|
369
|
+
error: 'E_BUNDLE_SIGNATURE_CONTENT_MISMATCH',
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
return {
|
|
373
|
+
present: true,
|
|
374
|
+
valid: true,
|
|
375
|
+
key_id: keyId,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
catch (err) {
|
|
379
|
+
const errorMsg = err instanceof crypto_1.CryptoError
|
|
380
|
+
? `E_BUNDLE_SIGNATURE_CRYPTO_ERROR:${err.code}`
|
|
381
|
+
: dispute_bundle_js_2.BundleErrorCodes.SIGNATURE_INVALID;
|
|
382
|
+
return {
|
|
383
|
+
present: true,
|
|
384
|
+
valid: false,
|
|
385
|
+
key_id: keyId,
|
|
386
|
+
error: errorMsg,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Verify all receipts in a dispute bundle and generate a deterministic report.
|
|
392
|
+
*
|
|
393
|
+
* @param zipBuffer - Buffer containing the ZIP archive
|
|
394
|
+
* @param options - Verification options
|
|
395
|
+
* @returns Promise resolving to a deterministic verification report
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* ```typescript
|
|
399
|
+
* const zipData = fs.readFileSync('dispute-bundle.zip');
|
|
400
|
+
* const result = await verifyBundle(zipData, { offline: true });
|
|
401
|
+
*
|
|
402
|
+
* if (result.ok) {
|
|
403
|
+
* console.log('Report hash:', result.value.report_hash);
|
|
404
|
+
* console.log('Summary:', result.value.auditor_summary.headline);
|
|
405
|
+
* }
|
|
406
|
+
* ```
|
|
407
|
+
*/
|
|
408
|
+
async function verifyBundle(zipBuffer, options) {
|
|
409
|
+
// Read and parse the bundle
|
|
410
|
+
const readResult = await (0, dispute_bundle_js_1.readDisputeBundle)(zipBuffer);
|
|
411
|
+
if (!readResult.ok) {
|
|
412
|
+
return readResult;
|
|
413
|
+
}
|
|
414
|
+
const bundleContents = readResult.value;
|
|
415
|
+
const { manifest, receipts } = bundleContents;
|
|
416
|
+
// Verify bundle.sig if present (authenticity)
|
|
417
|
+
const bundleSignature = await verifyBundleSignature(bundleContents);
|
|
418
|
+
// Verify each receipt with real Ed25519 signature verification
|
|
419
|
+
const results = [];
|
|
420
|
+
for (const receiptEntry of manifest.receipts) {
|
|
421
|
+
const jws = receipts.get(receiptEntry.receipt_id);
|
|
422
|
+
if (!jws) {
|
|
423
|
+
results.push({
|
|
424
|
+
receipt_id: receiptEntry.receipt_id,
|
|
425
|
+
signature_valid: false,
|
|
426
|
+
claims_valid: false,
|
|
427
|
+
errors: ['E_BUNDLE_RECEIPT_NOT_FOUND'],
|
|
428
|
+
});
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
const result = await verifyReceipt(receiptEntry.receipt_id, jws, bundleContents, options);
|
|
432
|
+
results.push(result);
|
|
433
|
+
}
|
|
434
|
+
// Sort results by receipt_id for determinism
|
|
435
|
+
results.sort((a, b) => a.receipt_id.localeCompare(b.receipt_id));
|
|
436
|
+
// Count valid/invalid
|
|
437
|
+
const validCount = results.filter((r) => r.signature_valid && r.claims_valid && r.errors.length === 0).length;
|
|
438
|
+
const invalidCount = results.length - validCount;
|
|
439
|
+
// Build key usage
|
|
440
|
+
const keysUsed = buildKeyUsage(results);
|
|
441
|
+
// Generate auditor summary
|
|
442
|
+
const auditorSummary = generateAuditorSummary(results.length, validCount, invalidCount, results);
|
|
443
|
+
// Build report without report_hash
|
|
444
|
+
const reportWithoutHash = {
|
|
445
|
+
version: dispute_bundle_types_js_1.VERIFICATION_REPORT_VERSION,
|
|
446
|
+
bundle_content_hash: manifest.content_hash,
|
|
447
|
+
bundle_signature: bundleSignature,
|
|
448
|
+
summary: {
|
|
449
|
+
total_receipts: results.length,
|
|
450
|
+
valid: validCount,
|
|
451
|
+
invalid: invalidCount,
|
|
452
|
+
},
|
|
453
|
+
receipts: results,
|
|
454
|
+
keys_used: keysUsed,
|
|
455
|
+
auditor_summary: auditorSummary,
|
|
456
|
+
};
|
|
457
|
+
// Compute report_hash = SHA-256 of JCS(report without report_hash)
|
|
458
|
+
// Strip undefined values first since JCS cannot handle them
|
|
459
|
+
const cleanedReport = stripUndefined(reportWithoutHash);
|
|
460
|
+
const reportHash = sha256Hex((0, crypto_1.canonicalize)(cleanedReport));
|
|
461
|
+
const report = {
|
|
462
|
+
...reportWithoutHash,
|
|
463
|
+
report_hash: reportHash,
|
|
464
|
+
};
|
|
465
|
+
return {
|
|
466
|
+
ok: true,
|
|
467
|
+
value: report,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Serialize a verification report to JSON.
|
|
472
|
+
*
|
|
473
|
+
* @param report - Verification report
|
|
474
|
+
* @param pretty - Pretty print (default: false)
|
|
475
|
+
* @returns JSON string
|
|
476
|
+
*/
|
|
477
|
+
function serializeReport(report, pretty = false) {
|
|
478
|
+
if (pretty) {
|
|
479
|
+
return JSON.stringify(report, null, 2);
|
|
480
|
+
}
|
|
481
|
+
return JSON.stringify(report);
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Format verification report as human-readable text.
|
|
485
|
+
*
|
|
486
|
+
* @param report - Verification report
|
|
487
|
+
* @returns Human-readable text summary
|
|
488
|
+
*/
|
|
489
|
+
function formatReportText(report) {
|
|
490
|
+
const lines = [];
|
|
491
|
+
lines.push('PEAC Dispute Bundle Verification Report');
|
|
492
|
+
lines.push('========================================');
|
|
493
|
+
lines.push('');
|
|
494
|
+
lines.push(`Bundle content hash: ${report.bundle_content_hash}`);
|
|
495
|
+
lines.push(`Report hash: ${report.report_hash}`);
|
|
496
|
+
lines.push('');
|
|
497
|
+
// Bundle signature status
|
|
498
|
+
lines.push('Bundle Signature');
|
|
499
|
+
lines.push('----------------');
|
|
500
|
+
if (!report.bundle_signature.present) {
|
|
501
|
+
lines.push(' Status: NOT SIGNED');
|
|
502
|
+
}
|
|
503
|
+
else if (report.bundle_signature.valid) {
|
|
504
|
+
lines.push(' Status: VALID');
|
|
505
|
+
lines.push(` Key ID: ${report.bundle_signature.key_id}`);
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
lines.push(' Status: INVALID');
|
|
509
|
+
if (report.bundle_signature.key_id) {
|
|
510
|
+
lines.push(` Key ID: ${report.bundle_signature.key_id}`);
|
|
511
|
+
}
|
|
512
|
+
if (report.bundle_signature.error) {
|
|
513
|
+
lines.push(` Error: ${report.bundle_signature.error}`);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
lines.push('');
|
|
517
|
+
lines.push('Summary');
|
|
518
|
+
lines.push('-------');
|
|
519
|
+
lines.push(`Total receipts: ${report.summary.total_receipts}`);
|
|
520
|
+
lines.push(`Valid: ${report.summary.valid}`);
|
|
521
|
+
lines.push(`Invalid: ${report.summary.invalid}`);
|
|
522
|
+
lines.push('');
|
|
523
|
+
lines.push(`Recommendation: ${report.auditor_summary.recommendation.toUpperCase()}`);
|
|
524
|
+
lines.push(`Headline: ${report.auditor_summary.headline}`);
|
|
525
|
+
lines.push('');
|
|
526
|
+
if (report.auditor_summary.issues.length > 0) {
|
|
527
|
+
lines.push('Issues');
|
|
528
|
+
lines.push('------');
|
|
529
|
+
for (const issue of report.auditor_summary.issues) {
|
|
530
|
+
lines.push(` - ${issue}`);
|
|
531
|
+
}
|
|
532
|
+
lines.push('');
|
|
533
|
+
}
|
|
534
|
+
if (report.keys_used.length > 0) {
|
|
535
|
+
lines.push('Keys Used');
|
|
536
|
+
lines.push('---------');
|
|
537
|
+
for (const keyUsage of report.keys_used) {
|
|
538
|
+
lines.push(` ${keyUsage.kid}: ${keyUsage.receipts_signed} receipt(s)`);
|
|
539
|
+
}
|
|
540
|
+
lines.push('');
|
|
541
|
+
}
|
|
542
|
+
lines.push('Receipt Details');
|
|
543
|
+
lines.push('---------------');
|
|
544
|
+
for (const receipt of report.receipts) {
|
|
545
|
+
const status = receipt.signature_valid && receipt.claims_valid ? 'VALID' : 'INVALID';
|
|
546
|
+
lines.push(` ${receipt.receipt_id}: ${status}`);
|
|
547
|
+
if (receipt.key_id) {
|
|
548
|
+
lines.push(` Key: ${receipt.key_id}`);
|
|
549
|
+
}
|
|
550
|
+
if (receipt.errors.length > 0) {
|
|
551
|
+
lines.push(` Errors: ${receipt.errors.join(', ')}`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return lines.join('\n');
|
|
555
|
+
}
|
|
556
|
+
//# sourceMappingURL=verification-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-report.js","sourceRoot":"","sources":["../src/verification-report.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;AAkdH,oCA+EC;AASD,0CAKC;AAQD,4CAuEC;AA5nBD,6CAAyC;AACzC,yCAA8E;AAE9E,2DAAwD;AACxD,2DAAuD;AAavD,uEAAwE;AAExE;;;GAGG;AACH,SAAS,SAAS,CAAC,IAAqB;IACtC,MAAM,IAAI,GAAG,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAClB,OAAO,UAAU,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AACxC,CAAC;AAED,iCAAiC;AACjC,SAAS,eAAe,CAAC,GAAW;IAClC,MAAM,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AACvC,CAAC;AAED,8CAA8C;AAC9C,SAAS,QAAQ,CAAC,GAAW;IAK3B,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC9D,MAAM,WAAW,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC/D,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAA4B;YACzD,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAA4B;YAC3D,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;SACpB,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,4BAA4B;AAC5B,SAAS,WAAW,CAClB,IAAY,EACZ,OAAe,EACf,OAAiC;IAEjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,SAAS,cAAc,CAAI,GAAM;IAC/B,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,GAAG,CAAC,cAAc,CAAM,CAAC;IACtC,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAA8B,CAAC,EAAE,CAAC;YAC1E,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;QACD,OAAO,MAAW,CAAC;IACrB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,mBAAmB,CAC1B,OAAgC,EAChC,GAAS;IAET,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IAEhD,kBAAkB;IAClB,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACjB,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACjB,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACvC,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QAChE,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACvC,CAAC;aAAM,IAAI,GAAG,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC;YAC9B,yBAAyB;YACzB,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAED,0BAA0B;IAC1B,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QAChE,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACvC,CAAC;aAAM,IAAI,GAAG,GAAG,MAAM,EAAE,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM;KACP,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,MAA+B;IACtD,OAAO,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACjE,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAAC,GAAe;IAC1C,IAAI,GAAG,CAAC,GAAG,KAAK,KAAK,IAAI,GAAG,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACzD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,4BAA4B;IAC5B,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC5C,IAAI,KAAK,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,SAAS,OAAO,CAAC,IAAkB,EAAE,GAAW;IAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;AACzC,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,aAAa,CAC1B,SAAiB,EACjB,GAAW,EACX,cAAqC,EACrC,OAA4B;IAE5B,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAE7B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO;YACL,UAAU,EAAE,SAAS;YACrB,eAAe,EAAE,KAAK;YACtB,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,CAAC,0BAA0B,CAAC;SACrC,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IACnC,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,wBAAwB;IACxB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,UAAU,EAAE,SAAS;YACrB,eAAe,EAAE,KAAK;YACtB,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,CAAC,uBAAuB,CAAC;SAClC,CAAC;IACJ,CAAC;IAED,oCAAoC;IACpC,MAAM,GAAG,GAAG,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACrD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,gDAAgD;QAChD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO;gBACL,UAAU,EAAE,SAAS;gBACrB,eAAe,EAAE,KAAK;gBACtB,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,KAAK;gBACb,MAAM,EAAE,CAAC,oCAAgB,CAAC,WAAW,CAAC;aACvC,CAAC;QACJ,CAAC;QACD,kEAAkE;QAClE,OAAO;YACL,UAAU,EAAE,SAAS;YACrB,eAAe,EAAE,KAAK;YACtB,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,CAAC,oCAAgB,CAAC,WAAW,CAAC;SACvC,CAAC;IACJ,CAAC;IAED,sCAAsC;IACtC,MAAM,cAAc,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO;YACL,UAAU,EAAE,SAAS;YACrB,eAAe,EAAE,KAAK;YACtB,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,CAAC,8BAA8B,CAAC;SACzC,CAAC;IACJ,CAAC;IAED,8CAA8C;IAC9C,IAAI,cAAc,GAAG,KAAK,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,eAAS,EAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QACpD,cAAc,GAAG,MAAM,CAAC,KAAK,CAAC;QAC9B,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,uBAAuB;QACvB,IAAI,GAAG,YAAY,oBAAW,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC,0BAA0B,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAC7C,CAAC;QACD,cAAc,GAAG,KAAK,CAAC;IACzB,CAAC;IAED,kBAAkB;IAClB,MAAM,YAAY,GAAG,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;IAC7E,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAEpC,OAAO;QACL,UAAU,EAAE,SAAS;QACrB,eAAe,EAAE,cAAc;QAC/B,YAAY,EAAE,YAAY,CAAC,KAAK;QAChC,MAAM,EAAE,KAAK;QACb,MAAM;QACN,MAAM,EAAE,cAAc,IAAI,YAAY,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;KAC1F,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,OAAoC;IACzD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE1C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YAChD,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACjC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,KAAK,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,KAAK,EAAE,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC;YACX,GAAG;YACH,eAAe,EAAE,UAAU,CAAC,MAAM;YAClC,WAAW,EAAE,UAAU,CAAC,IAAI,EAAE;SAC/B,CAAC,CAAC;IACL,CAAC;IAED,8BAA8B;IAC9B,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAC7B,aAAqB,EACrB,UAAkB,EAClB,YAAoB,EACpB,OAAoC;IAEpC,MAAM,QAAQ,GAAG,GAAG,UAAU,IAAI,aAAa,iBAAiB,CAAC;IAEjE,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,eAAe,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;YACpD,MAAM,YAAY,GAChB,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC;YAC5E,MAAM,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,UAAU,KAAK,YAAY,EAAE,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,8BAA8B;IAC9B,MAAM,CAAC,IAAI,EAAE,CAAC;IAEd,IAAI,cAAgD,CAAC;IACrD,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QACvB,cAAc,GAAG,OAAO,CAAC;IAC3B,CAAC;SAAM,IAAI,YAAY,KAAK,aAAa,EAAE,CAAC;QAC1C,cAAc,GAAG,SAAS,CAAC;IAC7B,CAAC;SAAM,CAAC;QACN,cAAc,GAAG,cAAc,CAAC;IAClC,CAAC;IAED,OAAO;QACL,QAAQ;QACR,MAAM;QACN,cAAc;KACf,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,qBAAqB,CAClC,cAAqC;IAErC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAC;IAEtD,wBAAwB;IACxB,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5B,CAAC;IAED,kCAAkC;IAClC,MAAM,MAAM,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO;YACL,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,mCAAmC;SAC3C,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IACpF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,gCAAgC;SACxC,CAAC;IACJ,CAAC;IAED,uBAAuB;IACvB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC;IACnD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO;YACL,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,oCAAgB,CAAC,WAAW;SACpC,CAAC;IACJ,CAAC;IAED,sCAAsC;IACtC,MAAM,cAAc,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,uCAAuC;SAC/C,CAAC;IACJ,CAAC;IAED,uBAAuB;IACvB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,eAAS,EAA2B,UAAU,EAAE,cAAc,CAAC,CAAC;QAErF,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,KAAK;gBACb,KAAK,EAAE,oCAAgB,CAAC,iBAAiB;aAC1C,CAAC;QACJ,CAAC;QAED,8DAA8D;QAC9D,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,KAAK,QAAQ,CAAC,YAAY,EAAE,CAAC;YAC1D,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,KAAK;gBACb,KAAK,EAAE,qCAAqC;aAC7C,CAAC;QACJ,CAAC;QAED,OAAO;YACL,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,KAAK;SACd,CAAC;IACJ,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,QAAQ,GACZ,GAAG,YAAY,oBAAW;YACxB,CAAC,CAAC,mCAAmC,GAAG,CAAC,IAAI,EAAE;YAC/C,CAAC,CAAC,oCAAgB,CAAC,iBAAiB,CAAC;QAEzC,OAAO;YACL,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,QAAQ;SAChB,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,YAAY,CAChC,SAAiB,EACjB,OAA4B;IAE5B,4BAA4B;IAC5B,MAAM,UAAU,GAAG,MAAM,IAAA,qCAAiB,EAAC,SAAS,CAAC,CAAC;IACtD,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC;QACnB,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC;IACxC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAC;IAE9C,8CAA8C;IAC9C,MAAM,eAAe,GAAG,MAAM,qBAAqB,CAAC,cAAc,CAAC,CAAC;IAEpE,+DAA+D;IAC/D,MAAM,OAAO,GAAgC,EAAE,CAAC;IAEhD,KAAK,MAAM,YAAY,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;QAC7C,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;QAClD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,CAAC,IAAI,CAAC;gBACX,UAAU,EAAE,YAAY,CAAC,UAAU;gBACnC,eAAe,EAAE,KAAK;gBACtB,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,CAAC,4BAA4B,CAAC;aACvC,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;QAC1F,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvB,CAAC;IAED,6CAA6C;IAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjE,sBAAsB;IACtB,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAC/B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CACpE,CAAC,MAAM,CAAC;IACT,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,UAAU,CAAC;IAEjD,kBAAkB;IAClB,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAExC,2BAA2B;IAC3B,MAAM,cAAc,GAAG,sBAAsB,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IAEjG,mCAAmC;IACnC,MAAM,iBAAiB,GAA4C;QACjE,OAAO,EAAE,qDAA2B;QACpC,mBAAmB,EAAE,QAAQ,CAAC,YAAY;QAC1C,gBAAgB,EAAE,eAAe;QACjC,OAAO,EAAE;YACP,cAAc,EAAE,OAAO,CAAC,MAAM;YAC9B,KAAK,EAAE,UAAU;YACjB,OAAO,EAAE,YAAY;SACtB;QACD,QAAQ,EAAE,OAAO;QACjB,SAAS,EAAE,QAAQ;QACnB,eAAe,EAAE,cAAc;KAChC,CAAC;IAEF,mEAAmE;IACnE,4DAA4D;IAC5D,MAAM,aAAa,GAAG,cAAc,CAAC,iBAAiB,CAAC,CAAC;IACxD,MAAM,UAAU,GAAG,SAAS,CAAC,IAAA,qBAAY,EAAC,aAAa,CAAC,CAAC,CAAC;IAE1D,MAAM,MAAM,GAAuB;QACjC,GAAG,iBAAiB;QACpB,WAAW,EAAE,UAAU;KACxB,CAAC;IAEF,OAAO;QACL,EAAE,EAAE,IAAI;QACR,KAAK,EAAE,MAAM;KACd,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,eAAe,CAAC,MAA0B,EAAE,SAAkB,KAAK;IACjF,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED;;;;;GAKG;AACH,SAAgB,gBAAgB,CAAC,MAA0B;IACzD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;IACtD,KAAK,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;IACvD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,wBAAwB,MAAM,CAAC,mBAAmB,EAAE,CAAC,CAAC;IACjE,KAAK,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IACjD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,0BAA0B;IAC1B,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC/B,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACrC,CAAC;SAAM,IAAI,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;QACzC,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5D,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAChC,IAAI,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,IAAI,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;YAClC,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtB,KAAK,CAAC,IAAI,CAAC,mBAAmB,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAC/D,KAAK,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IAC7C,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACjD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,mBAAmB,MAAM,CAAC,eAAe,CAAC,cAAc,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACrF,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,IAAI,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC;YAClD,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;QAC7B,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxB,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACxC,KAAK,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,GAAG,KAAK,QAAQ,CAAC,eAAe,aAAa,CAAC,CAAC;QAC1E,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC9B,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC9B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,OAAO,CAAC,eAAe,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QACrF,KAAK,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,UAAU,KAAK,MAAM,EAAE,CAAC,CAAC;QACjD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,YAAY,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3C,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC,eAAe,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow DAG Verification (v0.10.1+)
|
|
3
|
+
*
|
|
4
|
+
* Verifies the structural integrity of workflow DAGs by checking:
|
|
5
|
+
* - ID format validity (workflow_id, step_id)
|
|
6
|
+
* - Unique step IDs
|
|
7
|
+
* - Consistent workflow IDs
|
|
8
|
+
* - Parent existence (no dangling references)
|
|
9
|
+
* - No self-parenting
|
|
10
|
+
* - Acyclicity (no cycles in the DAG)
|
|
11
|
+
* - Limits enforcement (node count, parent count, depth, edges)
|
|
12
|
+
*
|
|
13
|
+
* Each violation maps to a specific E_WORKFLOW_* error code for structured
|
|
14
|
+
* error handling and audit trails.
|
|
15
|
+
*/
|
|
16
|
+
import type { WorkflowContext } from '@peac/schema';
|
|
17
|
+
import { ERROR_CODES } from '@peac/kernel';
|
|
18
|
+
/**
|
|
19
|
+
* DAG verification limits (DoS protection for full DAG analysis)
|
|
20
|
+
*/
|
|
21
|
+
export declare const DAG_LIMITS: {
|
|
22
|
+
/** Maximum steps in a workflow DAG */
|
|
23
|
+
readonly maxSteps: 10000;
|
|
24
|
+
/** Maximum depth (longest path from root) */
|
|
25
|
+
readonly maxDepth: 100;
|
|
26
|
+
/** Maximum total edges (sum of all parent references) */
|
|
27
|
+
readonly maxTotalEdges: 100000;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Workflow error category for structured violations
|
|
31
|
+
*/
|
|
32
|
+
export type WorkflowErrorCode = typeof ERROR_CODES.E_WORKFLOW_ID_INVALID | typeof ERROR_CODES.E_WORKFLOW_STEP_ID_INVALID | typeof ERROR_CODES.E_WORKFLOW_DAG_INVALID | typeof ERROR_CODES.E_WORKFLOW_CYCLE_DETECTED | typeof ERROR_CODES.E_WORKFLOW_PARENT_NOT_FOUND | typeof ERROR_CODES.E_WORKFLOW_LIMIT_EXCEEDED;
|
|
33
|
+
/**
|
|
34
|
+
* DAG violation details
|
|
35
|
+
*/
|
|
36
|
+
export interface DagViolation {
|
|
37
|
+
/** Error code from E_WORKFLOW_* family */
|
|
38
|
+
code: WorkflowErrorCode;
|
|
39
|
+
/** Human-readable message */
|
|
40
|
+
message: string;
|
|
41
|
+
/** Structured details for debugging/audit */
|
|
42
|
+
details: {
|
|
43
|
+
/** Specific reason for the violation */
|
|
44
|
+
reason: string;
|
|
45
|
+
/** Step ID involved (if applicable) */
|
|
46
|
+
step_id?: string;
|
|
47
|
+
/** Parent step ID involved (if applicable) */
|
|
48
|
+
parent_step_id?: string;
|
|
49
|
+
/** Workflow ID involved (if applicable) */
|
|
50
|
+
workflow_id?: string;
|
|
51
|
+
/** Limit that was exceeded (if applicable) */
|
|
52
|
+
limit?: string;
|
|
53
|
+
/** Current value (if applicable) */
|
|
54
|
+
value?: number;
|
|
55
|
+
/** Maximum allowed value (if applicable) */
|
|
56
|
+
max?: number;
|
|
57
|
+
/** Cycle path (if applicable) */
|
|
58
|
+
cycle_path?: string[];
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Result of DAG verification
|
|
63
|
+
*/
|
|
64
|
+
export interface DagVerificationResult {
|
|
65
|
+
/** Whether the DAG is valid */
|
|
66
|
+
valid: boolean;
|
|
67
|
+
/** List of violations (empty if valid) */
|
|
68
|
+
violations: DagViolation[];
|
|
69
|
+
/** DAG statistics (computed during verification) */
|
|
70
|
+
stats: {
|
|
71
|
+
/** Number of steps */
|
|
72
|
+
stepCount: number;
|
|
73
|
+
/** Number of root steps (no parents) */
|
|
74
|
+
rootCount: number;
|
|
75
|
+
/** Maximum depth reached */
|
|
76
|
+
maxDepth: number;
|
|
77
|
+
/** Total edge count */
|
|
78
|
+
totalEdges: number;
|
|
79
|
+
/** Number of unique workflow IDs (should be 1) */
|
|
80
|
+
uniqueWorkflowIds: number;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Verify a workflow DAG for structural integrity
|
|
85
|
+
*
|
|
86
|
+
* @param contexts - Array of WorkflowContext objects representing the DAG
|
|
87
|
+
* @returns DagVerificationResult with violations and statistics
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* import { verifyWorkflowDag } from '@peac/audit';
|
|
92
|
+
*
|
|
93
|
+
* const result = verifyWorkflowDag(contexts);
|
|
94
|
+
* if (!result.valid) {
|
|
95
|
+
* for (const v of result.violations) {
|
|
96
|
+
* console.error(`${v.code}: ${v.message}`);
|
|
97
|
+
* }
|
|
98
|
+
* }
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare function verifyWorkflowDag(contexts: WorkflowContext[]): DagVerificationResult;
|
|
102
|
+
/**
|
|
103
|
+
* Extract a specific violation by reason
|
|
104
|
+
*
|
|
105
|
+
* Utility for tests and error handling
|
|
106
|
+
*/
|
|
107
|
+
export declare function findViolationByReason(result: DagVerificationResult, reason: string): DagViolation | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* Check if result has a specific violation code
|
|
110
|
+
*/
|
|
111
|
+
export declare function hasViolationCode(result: DagVerificationResult, code: WorkflowErrorCode): boolean;
|
|
112
|
+
//# sourceMappingURL=workflow-dag.d.ts.map
|