@mnemom/mnemom 0.17.37 → 0.17.38-next.1

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.
Files changed (131) hide show
  1. package/dist/commands/agent-doctor.d.ts +2 -2
  2. package/dist/commands/agent-doctor.js +4 -4
  3. package/dist/commands/agent-run.d.ts +11 -0
  4. package/dist/commands/agent-run.js +37 -0
  5. package/dist/commands/agent-setup.d.ts +11 -9
  6. package/dist/commands/agent-setup.js +24 -19
  7. package/dist/commands/agent-share-render.d.ts +5 -5
  8. package/dist/commands/agent-share-render.js +9 -7
  9. package/dist/commands/agent-share-test-issuer.d.ts +1 -1
  10. package/dist/commands/agent-share-test-issuer.js +1 -1
  11. package/dist/commands/agent.d.ts +8 -1
  12. package/dist/commands/agent.js +14 -3
  13. package/dist/commands/card.js +1 -1
  14. package/dist/commands/onboard.js +5 -4
  15. package/dist/commands/scan.d.ts +35 -8
  16. package/dist/commands/scan.js +134 -45
  17. package/dist/commands/verify-card.d.ts +25 -1
  18. package/dist/commands/verify-card.js +29 -2
  19. package/dist/commands/wrap.js +4 -3
  20. package/dist/index.js +8 -6
  21. package/dist/lib/commit-report.d.ts +7 -13
  22. package/dist/lib/commit-report.js +12 -5
  23. package/dist/lib/conduct-receipt.d.ts +8 -3
  24. package/dist/lib/conduct-receipt.js +29 -7
  25. package/dist/lib/experimental.d.ts +1 -1
  26. package/dist/lib/experimental.js +1 -12
  27. package/dist/lib/runner/executor-client.d.ts +5 -0
  28. package/dist/lib/runner/executor-client.js +8 -0
  29. package/dist/lib/runner/job.d.ts +37 -0
  30. package/dist/lib/runner/job.js +74 -1
  31. package/dist/lib/runner/phases.d.ts +7 -2
  32. package/dist/lib/runner/phases.js +7 -2
  33. package/dist/lib/runner/proxy.d.ts +26 -2
  34. package/dist/lib/runner/proxy.js +33 -1
  35. package/dist/lib/runner/record-commit.d.ts +16 -0
  36. package/dist/lib/runner/record-commit.js +7 -0
  37. package/dist/lib/scan/report-email.d.ts +19 -2
  38. package/dist/lib/scan/report-email.js +78 -22
  39. package/dist/lib/scan/scan.d.ts +2 -0
  40. package/dist/lib/scan/scan.js +2 -1
  41. package/dist/lib/scan/share.d.ts +8 -6
  42. package/dist/lib/scan/share.js +28 -11
  43. package/dist/lib/scan/spend/anthropic-usage.d.ts +20 -1
  44. package/dist/lib/scan/spend/anthropic-usage.js +32 -7
  45. package/dist/lib/scan/spend/anthropic-wif.d.ts +42 -0
  46. package/dist/lib/scan/spend/anthropic-wif.js +153 -0
  47. package/dist/lib/starter-integrity.d.ts +20 -0
  48. package/dist/lib/starter-integrity.js +28 -0
  49. package/package.json +2 -5
  50. package/node_modules/@mnemom/aip-verifier/LICENSE +0 -190
  51. package/node_modules/@mnemom/aip-verifier/dist/claim-lint-CVITIRcU.d.cts +0 -478
  52. package/node_modules/@mnemom/aip-verifier/dist/claim-lint-CVITIRcU.d.ts +0 -478
  53. package/node_modules/@mnemom/aip-verifier/dist/index.cjs +0 -971
  54. package/node_modules/@mnemom/aip-verifier/dist/index.cjs.map +0 -1
  55. package/node_modules/@mnemom/aip-verifier/dist/index.d.cts +0 -330
  56. package/node_modules/@mnemom/aip-verifier/dist/index.d.ts +0 -330
  57. package/node_modules/@mnemom/aip-verifier/dist/index.js +0 -933
  58. package/node_modules/@mnemom/aip-verifier/dist/index.js.map +0 -1
  59. package/node_modules/@mnemom/aip-verifier/dist/render-lines.cjs +0 -879
  60. package/node_modules/@mnemom/aip-verifier/dist/render-lines.cjs.map +0 -1
  61. package/node_modules/@mnemom/aip-verifier/dist/render-lines.d.cts +0 -96
  62. package/node_modules/@mnemom/aip-verifier/dist/render-lines.d.ts +0 -96
  63. package/node_modules/@mnemom/aip-verifier/dist/render-lines.js +0 -875
  64. package/node_modules/@mnemom/aip-verifier/dist/render-lines.js.map +0 -1
  65. package/node_modules/@mnemom/aip-verifier/package.json +0 -86
  66. package/node_modules/@noble/ed25519/LICENSE +0 -21
  67. package/node_modules/@noble/ed25519/README.md +0 -371
  68. package/node_modules/@noble/ed25519/index.d.ts +0 -391
  69. package/node_modules/@noble/ed25519/index.js +0 -933
  70. package/node_modules/@noble/ed25519/index.ts +0 -1134
  71. package/node_modules/@noble/ed25519/package.json +0 -54
  72. package/node_modules/@noble/hashes/LICENSE +0 -21
  73. package/node_modules/@noble/hashes/README.md +0 -478
  74. package/node_modules/@noble/hashes/_blake.d.ts +0 -20
  75. package/node_modules/@noble/hashes/_blake.js +0 -59
  76. package/node_modules/@noble/hashes/_md.d.ts +0 -95
  77. package/node_modules/@noble/hashes/_md.js +0 -209
  78. package/node_modules/@noble/hashes/_u64.d.ts +0 -36
  79. package/node_modules/@noble/hashes/_u64.js +0 -77
  80. package/node_modules/@noble/hashes/argon2.d.ts +0 -148
  81. package/node_modules/@noble/hashes/argon2.js +0 -577
  82. package/node_modules/@noble/hashes/blake1.d.ts +0 -162
  83. package/node_modules/@noble/hashes/blake1.js +0 -630
  84. package/node_modules/@noble/hashes/blake2.d.ts +0 -203
  85. package/node_modules/@noble/hashes/blake2.js +0 -573
  86. package/node_modules/@noble/hashes/blake3.d.ts +0 -68
  87. package/node_modules/@noble/hashes/blake3.js +0 -310
  88. package/node_modules/@noble/hashes/eskdf.d.ts +0 -91
  89. package/node_modules/@noble/hashes/eskdf.js +0 -225
  90. package/node_modules/@noble/hashes/hkdf.d.ts +0 -70
  91. package/node_modules/@noble/hashes/hkdf.js +0 -152
  92. package/node_modules/@noble/hashes/hmac.d.ts +0 -50
  93. package/node_modules/@noble/hashes/hmac.js +0 -93
  94. package/node_modules/@noble/hashes/index.d.ts +0 -1
  95. package/node_modules/@noble/hashes/index.js +0 -34
  96. package/node_modules/@noble/hashes/legacy.d.ts +0 -107
  97. package/node_modules/@noble/hashes/legacy.js +0 -348
  98. package/node_modules/@noble/hashes/package.json +0 -91
  99. package/node_modules/@noble/hashes/pbkdf2.d.ts +0 -66
  100. package/node_modules/@noble/hashes/pbkdf2.js +0 -176
  101. package/node_modules/@noble/hashes/scrypt.d.ts +0 -98
  102. package/node_modules/@noble/hashes/scrypt.js +0 -304
  103. package/node_modules/@noble/hashes/sha2.d.ts +0 -176
  104. package/node_modules/@noble/hashes/sha2.js +0 -446
  105. package/node_modules/@noble/hashes/sha3-addons.d.ts +0 -533
  106. package/node_modules/@noble/hashes/sha3-addons.js +0 -973
  107. package/node_modules/@noble/hashes/sha3.d.ts +0 -208
  108. package/node_modules/@noble/hashes/sha3.js +0 -475
  109. package/node_modules/@noble/hashes/src/_blake.ts +0 -67
  110. package/node_modules/@noble/hashes/src/_md.ts +0 -227
  111. package/node_modules/@noble/hashes/src/_u64.ts +0 -111
  112. package/node_modules/@noble/hashes/src/argon2.ts +0 -693
  113. package/node_modules/@noble/hashes/src/blake1.ts +0 -677
  114. package/node_modules/@noble/hashes/src/blake2.ts +0 -648
  115. package/node_modules/@noble/hashes/src/blake3.ts +0 -344
  116. package/node_modules/@noble/hashes/src/eskdf.ts +0 -265
  117. package/node_modules/@noble/hashes/src/hkdf.ts +0 -161
  118. package/node_modules/@noble/hashes/src/hmac.ts +0 -130
  119. package/node_modules/@noble/hashes/src/index.ts +0 -33
  120. package/node_modules/@noble/hashes/src/legacy.ts +0 -361
  121. package/node_modules/@noble/hashes/src/pbkdf2.ts +0 -219
  122. package/node_modules/@noble/hashes/src/scrypt.ts +0 -375
  123. package/node_modules/@noble/hashes/src/sha2.ts +0 -486
  124. package/node_modules/@noble/hashes/src/sha3-addons.ts +0 -1157
  125. package/node_modules/@noble/hashes/src/sha3.ts +0 -525
  126. package/node_modules/@noble/hashes/src/utils.ts +0 -978
  127. package/node_modules/@noble/hashes/src/webcrypto.ts +0 -283
  128. package/node_modules/@noble/hashes/utils.d.ts +0 -560
  129. package/node_modules/@noble/hashes/utils.js +0 -687
  130. package/node_modules/@noble/hashes/webcrypto.d.ts +0 -121
  131. package/node_modules/@noble/hashes/webcrypto.js +0 -228
@@ -1,933 +0,0 @@
1
- import { verifyAsync } from '@noble/ed25519';
2
- import { sha256 } from '@noble/hashes/sha2.js';
3
- import { bytesToHex, utf8ToBytes, hexToBytes } from '@noble/hashes/utils.js';
4
-
5
- // src/crypto.ts
6
- function base64ToUint8(b64) {
7
- const binary = atob(b64);
8
- const bytes = new Uint8Array(binary.length);
9
- for (let i = 0; i < binary.length; i++) {
10
- bytes[i] = binary.charCodeAt(i);
11
- }
12
- return bytes;
13
- }
14
- function hexToUint8(hex) {
15
- return hexToBytes(hex);
16
- }
17
- function sha256Hex(input) {
18
- return bytesToHex(sha256(utf8ToBytes(input)));
19
- }
20
- async function verifySignature(signature, payload, publicKey) {
21
- try {
22
- const sigBytes = base64ToUint8(signature);
23
- const msgBytes = utf8ToBytes(payload);
24
- return await verifyAsync(sigBytes, msgBytes, publicKey);
25
- } catch {
26
- return false;
27
- }
28
- }
29
- function verifyMerkleProof(leafHash, siblings, expectedRoot) {
30
- let current = leafHash;
31
- for (const sibling of siblings) {
32
- const pair = sibling.position === "left" ? sibling.hash + current : current + sibling.hash;
33
- current = sha256Hex(pair);
34
- }
35
- return current === expectedRoot;
36
- }
37
- function computeChainHash(prevChainHash, checkpointId, verdict, thinkingBlockHash, inputCommitment, timestamp) {
38
- const prev = prevChainHash ?? "genesis";
39
- const preimage = [
40
- prev,
41
- checkpointId,
42
- verdict,
43
- thinkingBlockHash,
44
- inputCommitment,
45
- timestamp
46
- ].join("|");
47
- return sha256Hex(preimage);
48
- }
49
-
50
- // src/verify.ts
51
- async function verifyCertificate(certificate, options) {
52
- const publicKey = resolvePublicKey(options.publicKey);
53
- const signature = await checkSignature(certificate, publicKey);
54
- const chain = checkChain(certificate);
55
- const merkle = checkMerkle(certificate, options.merkleRoot);
56
- const verdictDerivation = checkVerdictDerivation(
57
- certificate,
58
- options.verdictDerivationImageId
59
- );
60
- const allValid = signature.valid && chain.valid && (merkle === null || merkle.valid) && (verdictDerivation === null || verdictDerivation.valid);
61
- return {
62
- valid: allValid,
63
- checks: { signature, chain, merkle, verdict_derivation: verdictDerivation },
64
- certificate_id: certificate.certificate_id,
65
- verified_at: (/* @__PURE__ */ new Date()).toISOString()
66
- };
67
- }
68
- function resolvePublicKey(key) {
69
- if (typeof key === "string") {
70
- return hexToUint8(key);
71
- }
72
- return key;
73
- }
74
- async function checkSignature(cert, publicKey) {
75
- const { value, signed_payload } = cert.proofs.signature;
76
- const valid = await verifySignature(value, signed_payload, publicKey);
77
- return {
78
- valid,
79
- details: valid ? "Ed25519 signature is valid" : "Ed25519 signature verification failed"
80
- };
81
- }
82
- function checkChain(cert) {
83
- const expected = computeChainHash(
84
- cert.proofs.chain.prev_chain_hash,
85
- cert.subject.checkpoint_id,
86
- cert.claims.verdict,
87
- cert.input_commitments.thinking_block_hash,
88
- cert.input_commitments.combined_commitment,
89
- cert.issued_at
90
- );
91
- const valid = expected === cert.proofs.chain.chain_hash;
92
- return {
93
- valid,
94
- details: valid ? "Chain hash matches recomputed value" : `Chain hash mismatch: expected ${expected}, got ${cert.proofs.chain.chain_hash}`
95
- };
96
- }
97
- function checkMerkle(cert, merkleRoot) {
98
- if (cert.proofs.merkle === null) {
99
- return null;
100
- }
101
- if (merkleRoot === void 0) {
102
- return null;
103
- }
104
- const { leaf_hash, inclusion_proof, root } = cert.proofs.merkle;
105
- if (root !== merkleRoot) {
106
- return {
107
- valid: false,
108
- details: `Merkle root mismatch: certificate claims ${root}, expected ${merkleRoot}`
109
- };
110
- }
111
- const valid = verifyMerkleProof(leaf_hash, inclusion_proof, merkleRoot);
112
- return {
113
- valid,
114
- details: valid ? "Merkle inclusion proof is valid" : "Merkle inclusion proof verification failed"
115
- };
116
- }
117
- function checkVerdictDerivation(cert, expectedImageId) {
118
- const proof = cert.proofs.verdict_derivation;
119
- if (proof === null || proof === void 0) {
120
- return null;
121
- }
122
- if (!["SP1-STARK", "RISC-Zero-STARK"].includes(proof.method)) {
123
- return {
124
- valid: false,
125
- details: `Unknown verdict derivation method: ${proof.method}`
126
- };
127
- }
128
- if (!proof.journal || !proof.image_id || !proof.receipt) {
129
- return {
130
- valid: false,
131
- details: "Verdict derivation proof is missing required fields (journal, image_id, or receipt)"
132
- };
133
- }
134
- if (expectedImageId !== void 0 && proof.image_id !== expectedImageId) {
135
- return {
136
- valid: false,
137
- details: `Image ID mismatch: proof has ${proof.image_id}, expected ${expectedImageId}`
138
- };
139
- }
140
- let journal;
141
- try {
142
- journal = JSON.parse(proof.journal);
143
- } catch {
144
- return {
145
- valid: false,
146
- details: "Failed to parse verdict derivation journal as JSON"
147
- };
148
- }
149
- if (journal.verdict !== void 0) {
150
- const certVerdict = cert.claims.verdict;
151
- if (journal.verdict !== certVerdict) {
152
- return {
153
- valid: false,
154
- details: `Journal verdict "${journal.verdict}" does not match certificate verdict "${certVerdict}"`
155
- };
156
- }
157
- }
158
- if (journal.thinking_hash !== void 0) {
159
- const certThinkingHash = cert.input_commitments.thinking_block_hash;
160
- if (journal.thinking_hash !== certThinkingHash) {
161
- return {
162
- valid: false,
163
- details: `Journal thinking_hash does not match certificate input_commitments.thinking_block_hash`
164
- };
165
- }
166
- }
167
- if (journal.card_hash !== void 0) {
168
- const certCardHash = cert.input_commitments.card_hash;
169
- if (journal.card_hash !== certCardHash) {
170
- return {
171
- valid: false,
172
- details: `Journal card_hash does not match certificate input_commitments.card_hash`
173
- };
174
- }
175
- }
176
- if (journal.values_hash !== void 0) {
177
- const certValuesHash = cert.input_commitments.values_hash;
178
- if (journal.values_hash !== certValuesHash) {
179
- return {
180
- valid: false,
181
- details: `Journal values_hash does not match certificate input_commitments.values_hash`
182
- };
183
- }
184
- }
185
- return {
186
- valid: true,
187
- details: "Verdict derivation proof is structurally valid (STARK receipt verification deferred to server)"
188
- };
189
- }
190
-
191
- // src/canonical.ts
192
- function canonicalJson(value) {
193
- if (value === null || typeof value !== "object") {
194
- return JSON.stringify(value);
195
- }
196
- if (Array.isArray(value)) {
197
- return `[${value.map(canonicalJson).join(",")}]`;
198
- }
199
- const record = value;
200
- const entries = Object.keys(record).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson(record[k])}`);
201
- return `{${entries.join(",")}}`;
202
- }
203
- function base64urlToUint8(input) {
204
- if (typeof input !== "string" || !/^[A-Za-z0-9_-]*={0,2}$/.test(input)) {
205
- throw new Error("not base64url");
206
- }
207
- const b64 = input.replace(/-/g, "+").replace(/_/g, "/").replace(/=+$/, "");
208
- const padded = b64 + "===".slice(0, (4 - b64.length % 4) % 4);
209
- const binary = atob(padded);
210
- const out = new Uint8Array(binary.length);
211
- for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
212
- return out;
213
- }
214
-
215
- // src/statements.ts
216
- var STATEMENT_TYPES = [
217
- "AAP-ConductRecord/v1",
218
- "AAP-PRRecord/v1",
219
- "AAP-Outcome/v1",
220
- "AAP-ShareCard/v1"
221
- ];
222
- var BUNDLE_TYP = "AAP-RecordBundle/v1";
223
- var CARD_LINE_CLASSES = ["measured", "verdict", "outcome"];
224
- var REASONS = {
225
- not_an_object: "not_an_object",
226
- unknown_typ: "unknown_typ",
227
- unsupported_alg: "unsupported_alg",
228
- signing_key_id_missing: "signing_key_id_missing",
229
- signature_missing: "signature_missing",
230
- signature_malformed: "signature_malformed",
231
- key_not_found: "key_not_found",
232
- key_not_ed25519: "key_not_ed25519",
233
- signature_invalid: "signature_invalid",
234
- bundle_not_an_object: "bundle_not_an_object",
235
- bundle_unknown_typ: "bundle_unknown_typ",
236
- bundle_statements_missing: "bundle_statements_missing",
237
- digest_malformed: "digest_malformed",
238
- linked_statement_wrong_typ: "linked_statement_wrong_typ",
239
- card_line_class_not_allowed: "card_line_class_not_allowed",
240
- card_verdict_without_coverage: "card_verdict_without_coverage"
241
- };
242
- var FLAGS = {
243
- card_hash_not_recorded: "card hash not recorded",
244
- card_not_bound: "card not bound",
245
- linked_statement_not_in_bundle: "linked statement not in bundle"
246
- };
247
-
248
- // src/verify-record.ts
249
- var DIGEST_RE = /^[0-9a-f]{64}$/;
250
- function isObject(v) {
251
- return typeof v === "object" && v !== null && !Array.isArray(v);
252
- }
253
- function statementCommitment(statement) {
254
- const { signature: _signature, ...rest } = statement;
255
- return canonicalJson(rest);
256
- }
257
- function statementDigest(statement) {
258
- return bytesToHex(sha256(utf8ToBytes(statementCommitment(statement))));
259
- }
260
- function selectKey(jwks, kid) {
261
- const keys = isObject(jwks) && Array.isArray(jwks.keys) ? jwks.keys : [];
262
- const jwk = keys.find((k) => isObject(k) && k.kid === kid);
263
- if (!jwk) return { reason: REASONS.key_not_found };
264
- if (jwk.alg !== "EdDSA" || jwk.kty !== "OKP" || jwk.crv !== "Ed25519" || typeof jwk.x !== "string") {
265
- return { reason: REASONS.key_not_ed25519 };
266
- }
267
- let key;
268
- try {
269
- key = base64urlToUint8(jwk.x);
270
- } catch {
271
- return { reason: REASONS.key_not_ed25519 };
272
- }
273
- if (key.length !== 32) return { reason: REASONS.key_not_ed25519 };
274
- return { key };
275
- }
276
- function checkpointFlags(checkpoints) {
277
- if (!Array.isArray(checkpoints)) return [];
278
- const flags = [];
279
- for (const c of checkpoints) {
280
- if (!isObject(c)) continue;
281
- const subject = typeof c.checkpoint_id === "string" ? c.checkpoint_id : "<no checkpoint_id>";
282
- if (typeof c.card_hash !== "string" || c.card_hash.length === 0) {
283
- flags.push({ flag: FLAGS.card_hash_not_recorded, subject });
284
- }
285
- const bound = typeof c.combined_commitment === "string" && c.combined_commitment.length > 0 && typeof c.commitment_signature === "string" && c.commitment_signature.length > 0;
286
- if (!bound) flags.push({ flag: FLAGS.card_not_bound, subject });
287
- }
288
- return flags;
289
- }
290
- function shareCardReasons(lines) {
291
- if (!Array.isArray(lines)) return [];
292
- const reasons = [];
293
- for (const line of lines) {
294
- if (!isObject(line) || !CARD_LINE_CLASSES.includes(line.class)) {
295
- reasons.push(REASONS.card_line_class_not_allowed);
296
- continue;
297
- }
298
- if (line.class === "verdict") {
299
- const cov = line.coverage;
300
- const ok = isObject(cov) && Number.isInteger(cov.judged) && Number.isInteger(cov.total) && cov.judged >= 0 && cov.total >= cov.judged;
301
- if (!ok) reasons.push(REASONS.card_verdict_without_coverage);
302
- }
303
- }
304
- return [...new Set(reasons)];
305
- }
306
- function digestFieldsOf(statement) {
307
- const out = [];
308
- if (statement.previous_digest !== void 0 && statement.previous_digest !== null) {
309
- out.push(statement.previous_digest);
310
- }
311
- if (statement.pr_record_digest !== void 0) out.push(statement.pr_record_digest);
312
- if (statement.source_digest !== void 0) out.push(statement.source_digest);
313
- if (statement.conduct_digests !== void 0) {
314
- if (Array.isArray(statement.conduct_digests)) out.push(...statement.conduct_digests);
315
- else out.push(null);
316
- }
317
- return out;
318
- }
319
- async function verifyStatement(statement, jwks) {
320
- const fail = (reasons2, partial = {}) => ({
321
- valid: false,
322
- typ: null,
323
- digest: null,
324
- signing_key_id: null,
325
- flags: [],
326
- ...partial,
327
- reasons: reasons2
328
- });
329
- if (!isObject(statement)) return fail([REASONS.not_an_object]);
330
- const typ = typeof statement.typ === "string" ? statement.typ : null;
331
- const digest = statementDigest(statement);
332
- const kid = typeof statement.signing_key_id === "string" ? statement.signing_key_id : null;
333
- const base = { typ, digest, signing_key_id: kid };
334
- if (!typ || !STATEMENT_TYPES.includes(typ)) {
335
- return fail([REASONS.unknown_typ], base);
336
- }
337
- if (statement.alg !== void 0 && statement.alg !== "EdDSA") {
338
- return fail([REASONS.unsupported_alg], base);
339
- }
340
- if (!kid) return fail([REASONS.signing_key_id_missing], base);
341
- if (typeof statement.signature !== "string" || statement.signature.length === 0) {
342
- return fail([REASONS.signature_missing], base);
343
- }
344
- let sig;
345
- try {
346
- sig = base64urlToUint8(statement.signature);
347
- } catch {
348
- return fail([REASONS.signature_malformed], base);
349
- }
350
- if (sig.length !== 64) return fail([REASONS.signature_malformed], base);
351
- const selected = selectKey(jwks, kid);
352
- if (!selected.key) return fail([selected.reason], base);
353
- let sigOk = false;
354
- try {
355
- sigOk = await verifyAsync(sig, utf8ToBytes(statementCommitment(statement)), selected.key);
356
- } catch {
357
- sigOk = false;
358
- }
359
- if (!sigOk) return fail([REASONS.signature_invalid], base);
360
- const reasons = [];
361
- for (const d of digestFieldsOf(statement)) {
362
- if (typeof d !== "string" || !DIGEST_RE.test(d)) {
363
- reasons.push(REASONS.digest_malformed);
364
- break;
365
- }
366
- }
367
- if (typ === "AAP-ShareCard/v1") reasons.push(...shareCardReasons(statement.lines));
368
- const flags = typ === "AAP-ConductRecord/v1" ? checkpointFlags(statement.checkpoints) : [];
369
- return {
370
- valid: reasons.length === 0,
371
- typ,
372
- digest,
373
- signing_key_id: kid,
374
- reasons,
375
- flags
376
- };
377
- }
378
- var LINK_TARGETS = {
379
- previous_digest: "same",
380
- conduct_digests: ["AAP-ConductRecord/v1"],
381
- pr_record_digest: ["AAP-PRRecord/v1"],
382
- source_digest: ["AAP-ConductRecord/v1", "AAP-PRRecord/v1", "AAP-Outcome/v1"]
383
- };
384
- async function verify(input, jwks) {
385
- const verified_at = (/* @__PURE__ */ new Date()).toISOString();
386
- const result = (valid, reasons2, flags2, statements2) => ({ valid, reasons: reasons2, flags: flags2, statements: statements2, verified_at });
387
- if (!isObject(input)) return result(false, [REASONS.bundle_not_an_object], [], []);
388
- if (input.typ !== BUNDLE_TYP && Array.isArray(input.statements) && !STATEMENT_TYPES.includes(input.typ)) {
389
- return result(false, [REASONS.bundle_unknown_typ], [], []);
390
- }
391
- if (input.typ !== BUNDLE_TYP) {
392
- const one = await verifyStatement(input, jwks);
393
- return result(one.valid, one.reasons.map((r) => `0:${r}`), one.flags, [one]);
394
- }
395
- const rawStatements = Array.isArray(input.statements) ? input.statements : null;
396
- if (!rawStatements) {
397
- return result(false, [REASONS.bundle_statements_missing], [], []);
398
- }
399
- const statements = [];
400
- for (const s of rawStatements) statements.push(await verifyStatement(s, jwks));
401
- const byDigest = /* @__PURE__ */ new Map();
402
- statements.forEach((s) => {
403
- if (s.digest) byDigest.set(s.digest, s);
404
- });
405
- const reasons = [];
406
- const flags = [];
407
- statements.forEach((s, i) => {
408
- s.reasons.forEach((r) => reasons.push(`${i}:${r}`));
409
- flags.push(...s.flags);
410
- const raw = rawStatements[i];
411
- if (!s.valid || !isObject(raw)) return;
412
- for (const [field, targets] of Object.entries(LINK_TARGETS)) {
413
- const value = raw[field];
414
- const digests = Array.isArray(value) ? value : [value];
415
- for (const d of digests) {
416
- if (typeof d !== "string") continue;
417
- const linked = byDigest.get(d);
418
- if (!linked) {
419
- flags.push({ flag: FLAGS.linked_statement_not_in_bundle, subject: d });
420
- continue;
421
- }
422
- const allowed = targets === "same" ? [s.typ] : targets;
423
- if (!linked.typ || !allowed.includes(linked.typ)) {
424
- reasons.push(`${i}:${REASONS.linked_statement_wrong_typ}`);
425
- }
426
- }
427
- }
428
- });
429
- return result(reasons.length === 0, reasons, flags, statements);
430
- }
431
-
432
- // src/claim-table.ts
433
- var CLAIM_CLASSES = ["measured", "verdict", "estimate", "benchmark", "outcome"];
434
- var SURFACES = ["record_page", "check_summary", "pr_comment", "share_card", "period_card"];
435
- var CARD_SURFACES = ["share_card", "period_card"];
436
- var ESTIMATE_FORBIDDEN_SURFACES = ["pr_comment", "share_card", "period_card"];
437
- var MODES = ["off", "observe", "nudge", "enforce"];
438
- var MODE_VERBS = {
439
- off: "not checked",
440
- observe: "flagged",
441
- nudge: "flagged, agent nudged",
442
- enforce: "stopped"
443
- };
444
- var PROTECTION_VERDICTS = ["pass", "warn", "nudge", "quarantine", "block"];
445
- function protectionVerb(mode, verdict) {
446
- if (mode === "off" || verdict === "pass") return null;
447
- if (mode === "observe") return MODE_VERBS.observe;
448
- if (mode === "nudge") return verdict === "nudge" ? MODE_VERBS.nudge : MODE_VERBS.observe;
449
- return verdict === "block" || verdict === "quarantine" ? MODE_VERBS.enforce : MODE_VERBS.observe;
450
- }
451
- var GOAL_MODES = ["sealed", "implicit", "off"];
452
- var CLAIM_TABLE = {
453
- commits_recorded: { class: "measured", verb_by_mode: false, money_allowed: false },
454
- messages_from_person: { class: "measured", verb_by_mode: false, money_allowed: false },
455
- compactions: { class: "measured", verb_by_mode: false, money_allowed: false },
456
- tokens_sent_vs_reaching: { class: "measured", verb_by_mode: false, money_allowed: false },
457
- peak_context: { class: "measured", verb_by_mode: false, money_allowed: false },
458
- model_cost: { class: "measured", verb_by_mode: false, money_allowed: true },
459
- mu_cost: { class: "measured", verb_by_mode: false, money_allowed: true },
460
- approval: { class: "measured", verb_by_mode: false, money_allowed: false },
461
- /** How the goal was set: "sealed contract", "implicit goal" or "not checked" (M7: its own measured line). */
462
- goal_mode: { class: "measured", verb_by_mode: false, money_allowed: false },
463
- goal: { class: "verdict", verb_by_mode: true, money_allowed: false },
464
- /** The protection area's mode when no act is shown: "not checked" under off. */
465
- protection_mode: { class: "measured", verb_by_mode: false, money_allowed: false },
466
- protection: { class: "verdict", verb_by_mode: true, money_allowed: false },
467
- autonomy: { class: "measured", verb_by_mode: false, money_allowed: false },
468
- savings_estimate: {
469
- class: "estimate",
470
- verb_by_mode: false,
471
- money_allowed: true,
472
- surfaces: ["record_page", "check_summary"]
473
- },
474
- benchmark: { class: "benchmark", verb_by_mode: false, money_allowed: false },
475
- outcome: { class: "outcome", verb_by_mode: false, money_allowed: false }
476
- };
477
- function isClaimField(field) {
478
- return typeof field === "string" && Object.prototype.hasOwnProperty.call(CLAIM_TABLE, field);
479
- }
480
- var LINE_KEY_FIELDS = {
481
- "cost.model": "model_cost",
482
- "cost.mu": "mu_cost",
483
- "goal.mode": "goal_mode",
484
- "goal.verdict": "goal",
485
- "protection.mode": "protection_mode",
486
- "people.messages": "messages_from_person",
487
- "context.compactions": "compactions"
488
- };
489
- var PROTECTION_ACT_KEY = /^protection\.act\.\d+$/;
490
- function fieldForLineKey(key) {
491
- if (typeof key !== "string") return void 0;
492
- if (PROTECTION_ACT_KEY.test(key)) return "protection";
493
- return Object.prototype.hasOwnProperty.call(LINE_KEY_FIELDS, key) ? LINE_KEY_FIELDS[key] : void 0;
494
- }
495
-
496
- // src/claim-lint.ts
497
- var LINT_RULES = {
498
- one_class_per_line: "one_class_per_line",
499
- forbidden_word: "forbidden_word",
500
- money_on_goal_or_protection: "money_on_goal_or_protection",
501
- implicit_goal_sealed: "implicit_goal_sealed",
502
- goal_off_not_checked: "goal_off_not_checked",
503
- estimate_on_forbidden_surface: "estimate_on_forbidden_surface",
504
- verb_mismatch: "verb_mismatch",
505
- enforce_without_tool_name: "enforce_without_tool_name",
506
- tool_name_on_card: "tool_name_on_card",
507
- tool_arguments_shown: "tool_arguments_shown",
508
- trust_claim: "trust_claim",
509
- savings_percentage: "savings_percentage",
510
- benchmark_not_published: "benchmark_not_published",
511
- verdict_without_coverage: "verdict_without_coverage",
512
- card_class_not_allowed: "card_class_not_allowed",
513
- period_card_without_revert: "period_card_without_revert",
514
- field_not_on_surface: "field_not_on_surface",
515
- provisional_mu_on_card: "provisional_mu_on_card",
516
- provisional_mu_off_cost_line: "provisional_mu_off_cost_line",
517
- unknown_surface: "unknown_surface"
518
- };
519
- function clauseHas(text, rule) {
520
- const seen = /* @__PURE__ */ new Set();
521
- for (const m of text.matchAll(rule.tokens)) {
522
- const kind = Object.entries(m.groups ?? {}).find(([, v]) => v !== void 0)?.[0];
523
- if (kind === void 0) continue;
524
- if (kind === "brk") {
525
- seen.clear();
526
- continue;
527
- }
528
- if (rule.alone?.includes(kind)) return true;
529
- if (rule.pairs.some(([first, then]) => then === kind && seen.has(first))) return true;
530
- seen.add(kind);
531
- }
532
- return false;
533
- }
534
- var FORBIDDEN_WORD = /\b(safe|safely|unsafe|correct|correctly|incorrect|prevent|prevents|prevented|preventing|prevention)\b/i;
535
- var MONEY = /(?:\$|€|£|¥)\s?\d|\b(?<!\d,+)\d[\d,]*(?:\.\d+)?\s?(?:USD|EUR|GBP|dollars?|cents?)\b/i;
536
- var SEALED = /\bsealed\b/i;
537
- var ESTIMATE_WORDS = /\b(estimate[ds]?|estimation|saving|savings|saved|would have cost|counterfactual)\b/i;
538
- var TRUST_CLAIM = /\b(minutes?|seconds?|hours?)\s+to\s+trust\b|\btrust(ed)?\s+in\s+(under\s+|about\s+)?\d+\s*(minutes?|seconds?|hours?|min|s|h)\b|\breview[- ]time\b|\btime\s+to\s+review\b|\bwithout\s+re-?check(ing)?\b|\bno\s+need\s+to\s+(re-?check|review)\b|\btrust(ed)?\s+without\b/i;
539
- var SAVINGS_PERCENTAGE = {
540
- tokens: /\d+(?<percent>(?:\.\d+)?\s?%)?|\b(?<less>less)\b|\b(?<saving>sav(?:e[sd]?|ings?)|cheaper|reduction|reduced)\b|(?<brk>[.\n])/gi,
541
- pairs: [
542
- ["percent", "saving"],
543
- ["percent", "less"],
544
- ["saving", "percent"]
545
- ]
546
- };
547
- var REVERT = /\brevert(s|ed)?\b/i;
548
- var PROVISIONAL_MU = {
549
- tokens: /(?<approx>(?:≈|~|\bapprox(?:\.|imately)?\b|\babout\b)\s*\d[\d,]*(?:\.\d+)?\s*(?:mmu\b|mu\b|μ|µ))|\d[\d,]*(?<amount>(?:\.\d+)?\s*(?:mmu\b|mu\b|μ|µ))?|\b(?<pending>pending)\b|(?<brk>[.\n])/gi,
550
- alone: ["approx"],
551
- pairs: [
552
- ["amount", "pending"],
553
- ["pending", "amount"]
554
- ]
555
- };
556
- var NOT_CHECKED = /^not checked$/i;
557
- var COVERAGE_IN_TEXT = /\b(\d+)\s+of\s+(\d+)\s+turns\s+judged\b/i;
558
- var MODE_VERB_REGEX = {
559
- off: NOT_CHECKED,
560
- observe: /\bflagged\b/i,
561
- nudge: /\bflagged, agent nudged\b/i,
562
- enforce: /\bstopped\b/i
563
- };
564
- var OTHER_MODE_VERBS = {
565
- off: [/\bflagged\b/i, /\bnudged\b/i, /\bstopped\b/i],
566
- observe: [/\bnudged\b/i, /\bstopped\b/i],
567
- nudge: [/\bstopped\b/i],
568
- enforce: [/\bflagged\b/i, /\bnudged\b/i]
569
- };
570
- function isObject2(v) {
571
- return typeof v === "object" && v !== null && !Array.isArray(v);
572
- }
573
- function coverageObjectValid(cov) {
574
- return isObject2(cov) && Number.isInteger(cov.judged) && Number.isInteger(cov.total) && cov.judged >= 0 && cov.total >= cov.judged;
575
- }
576
- function coverageShown(line) {
577
- const cov = line.coverage;
578
- if (cov !== void 0) return coverageObjectValid(cov);
579
- const m = COVERAGE_IN_TEXT.exec(line.text);
580
- return m !== null && Number(m[2]) >= Number(m[1]);
581
- }
582
- function expectedVerb(mode, verdict) {
583
- if (mode === "off") return "off";
584
- if (verdict === void 0) return mode === "observe" ? "observe" : void 0;
585
- if (!PROTECTION_VERDICTS.includes(verdict)) return void 0;
586
- const verb = protectionVerb(mode, verdict);
587
- return MODES.find((m) => MODE_VERBS[m] === verb);
588
- }
589
- function escapeRegExp(s) {
590
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
591
- }
592
- function argumentsAppear(text, args) {
593
- if (args === void 0 || args === null) return false;
594
- const needles = [];
595
- const walk = (v) => {
596
- if (typeof v === "string") {
597
- if (v.length >= 3) needles.push(v);
598
- } else if (typeof v === "number" || typeof v === "boolean") {
599
- needles.push(String(v));
600
- } else if (Array.isArray(v)) {
601
- v.forEach(walk);
602
- } else if (isObject2(v)) {
603
- Object.values(v).forEach(walk);
604
- }
605
- };
606
- walk(args);
607
- if (needles.length === 0) return false;
608
- try {
609
- needles.push(JSON.stringify(args));
610
- } catch {
611
- }
612
- return needles.some((n) => n.length > 0 && text.includes(n));
613
- }
614
- function lintLine(surface, line, index, out) {
615
- const push = (rule) => {
616
- out.push({ rule, line: index, text: isObject2(line) && typeof line.text === "string" ? line.text : "" });
617
- };
618
- if (!isObject2(line) || typeof line.text !== "string" || typeof line.class !== "string") {
619
- push(LINT_RULES.one_class_per_line);
620
- return;
621
- }
622
- const text = line.text;
623
- const onCard = CARD_SURFACES.includes(surface);
624
- const field = line.field !== void 0 ? line.field : fieldForLineKey(line.key);
625
- const cls = line.class;
626
- if (!CLAIM_CLASSES.includes(cls)) {
627
- push(LINT_RULES.one_class_per_line);
628
- } else if (field !== void 0) {
629
- if (!isClaimField(field)) {
630
- push(LINT_RULES.one_class_per_line);
631
- } else {
632
- const entry = CLAIM_TABLE[field];
633
- if (entry.class !== cls) push(LINT_RULES.one_class_per_line);
634
- if (entry.surfaces && !entry.surfaces.includes(surface)) {
635
- push(LINT_RULES.field_not_on_surface);
636
- }
637
- }
638
- }
639
- if (cls !== "estimate" && ESTIMATE_WORDS.test(text)) push(LINT_RULES.one_class_per_line);
640
- if (FORBIDDEN_WORD.test(text)) push(LINT_RULES.forbidden_word);
641
- const isGoal = field === "goal" || field === "goal_mode";
642
- const isProtection = field === "protection" || field === "protection_mode";
643
- if ((isGoal || isProtection) && MONEY.test(text)) push(LINT_RULES.money_on_goal_or_protection);
644
- if (field !== void 0 && isClaimField(field) && !CLAIM_TABLE[field].money_allowed) {
645
- if (!isGoal && !isProtection && MONEY.test(text)) push(LINT_RULES.one_class_per_line);
646
- }
647
- if (isGoal) {
648
- if (line.goal_mode === "implicit" && SEALED.test(text)) push(LINT_RULES.implicit_goal_sealed);
649
- if (line.goal_mode === "off" && !NOT_CHECKED.test(text.trim())) push(LINT_RULES.goal_off_not_checked);
650
- }
651
- if (cls === "estimate" || ESTIMATE_WORDS.test(text)) {
652
- if (ESTIMATE_FORBIDDEN_SURFACES.includes(surface)) push(LINT_RULES.estimate_on_forbidden_surface);
653
- }
654
- if (line.mode !== void 0) {
655
- const verb = MODES.includes(line.mode) ? expectedVerb(line.mode, line.verdict) : void 0;
656
- const name = typeof line.tool_name === "string" ? line.tool_name.trim() : "";
657
- const nameShown = name.length > 0 && new RegExp(`(^|[^A-Za-z0-9_])${escapeRegExp(name)}([^A-Za-z0-9_]|$)`).test(text);
658
- if (verb === void 0) {
659
- push(LINT_RULES.verb_mismatch);
660
- } else if (!MODE_VERB_REGEX[verb].test(verb === "off" ? text.trim() : text)) {
661
- push(LINT_RULES.verb_mismatch);
662
- } else if (OTHER_MODE_VERBS[verb].some((re) => re.test(text))) {
663
- push(LINT_RULES.verb_mismatch);
664
- } else if (verb !== "enforce" && nameShown && !onCard) {
665
- push(LINT_RULES.verb_mismatch);
666
- }
667
- if (onCard) {
668
- if (nameShown) push(LINT_RULES.tool_name_on_card);
669
- } else if (verb === "enforce" && !nameShown) {
670
- push(LINT_RULES.enforce_without_tool_name);
671
- }
672
- }
673
- if (argumentsAppear(text, line.tool_arguments)) push(LINT_RULES.tool_arguments_shown);
674
- if (clauseHas(text, PROVISIONAL_MU)) {
675
- if (onCard) push(LINT_RULES.provisional_mu_on_card);
676
- if (field !== "mu_cost") push(LINT_RULES.provisional_mu_off_cost_line);
677
- }
678
- if (TRUST_CLAIM.test(text)) push(LINT_RULES.trust_claim);
679
- if (clauseHas(text, SAVINGS_PERCENTAGE)) push(LINT_RULES.savings_percentage);
680
- if (cls === "benchmark") push(LINT_RULES.benchmark_not_published);
681
- if (cls === "verdict") {
682
- const covered = onCard ? coverageObjectValid(line.coverage) : field === "protection" || coverageShown(line);
683
- if (!covered) push(LINT_RULES.verdict_without_coverage);
684
- }
685
- if (onCard && cls !== "measured" && cls !== "verdict" && cls !== "outcome") push(LINT_RULES.card_class_not_allowed);
686
- }
687
- function lintRendering(rendering) {
688
- const violations = [];
689
- if (!isObject2(rendering) || !Array.isArray(rendering.lines)) {
690
- return { ok: false, violations: [{ rule: LINT_RULES.unknown_surface, line: -1, text: "" }] };
691
- }
692
- const surface = rendering.surface;
693
- if (!SURFACES.includes(surface)) {
694
- return { ok: false, violations: [{ rule: LINT_RULES.unknown_surface, line: -1, text: String(surface) }] };
695
- }
696
- const lines = rendering.lines;
697
- lines.forEach((line, i) => lintLine(surface, line, i, violations));
698
- if (surface === "period_card") {
699
- const hasRevert = lines.some(
700
- (l) => isObject2(l) && l.class === "outcome" && typeof l.text === "string" && REVERT.test(l.text)
701
- );
702
- if (!hasRevert) violations.push({ rule: LINT_RULES.period_card_without_revert, line: -1, text: "" });
703
- }
704
- return { ok: violations.length === 0, violations };
705
- }
706
- function lintRenderings(renderings) {
707
- return renderings.map(lintRendering);
708
- }
709
- function attachLineContext(lines, goal, protection) {
710
- return lines.map((line) => {
711
- const out = { class: line.class, key: line.key, text: line.text };
712
- if (line.key.startsWith("goal.") && goal) out.goal_mode = goal.mode;
713
- if (line.key === "protection.mode") out.mode = "off";
714
- const act = /^protection\.act\.(\d+)$/.exec(line.key);
715
- if (act && protection) {
716
- const entry = protection.acts[Number(act[1])];
717
- if (entry) {
718
- out.mode = entry.mode;
719
- out.verdict = entry.verdict;
720
- if (entry.tool_name) out.tool_name = entry.tool_name;
721
- }
722
- }
723
- return out;
724
- });
725
- }
726
-
727
- // src/render-estimate.ts
728
- var ESTIMATE_COUNTING_FLOOR_TOKENS = 2e5;
729
- var ESTIMATE_DISQUALIFIERS = {
730
- not_an_object: "not_an_object",
731
- method_not_lower_bound: "method_not_lower_bound",
732
- counting_floor_below_200k: "counting_floor_below_200k",
733
- not_net_of_mu: "not_net_of_mu",
734
- never_shows_loss: "never_shows_loss",
735
- value_not_finite: "value_not_finite"
736
- };
737
- function isObject3(v) {
738
- return typeof v === "object" && v !== null && !Array.isArray(v);
739
- }
740
- function estimateQualifies(estimate) {
741
- if (!isObject3(estimate)) return { qualifies: false, reasons: [ESTIMATE_DISQUALIFIERS.not_an_object] };
742
- const reasons = [];
743
- if (estimate.method !== "lower_bound") reasons.push(ESTIMATE_DISQUALIFIERS.method_not_lower_bound);
744
- const floor = estimate.counting_floor_tokens;
745
- if (typeof floor !== "number" || !Number.isFinite(floor) || floor < ESTIMATE_COUNTING_FLOOR_TOKENS) {
746
- reasons.push(ESTIMATE_DISQUALIFIERS.counting_floor_below_200k);
747
- }
748
- if (estimate.net_of_mu !== true) reasons.push(ESTIMATE_DISQUALIFIERS.not_net_of_mu);
749
- if (estimate.reports_loss !== true) reasons.push(ESTIMATE_DISQUALIFIERS.never_shows_loss);
750
- if (typeof estimate.net_lower_bound !== "number" || !Number.isFinite(estimate.net_lower_bound)) {
751
- reasons.push(ESTIMATE_DISQUALIFIERS.value_not_finite);
752
- }
753
- return { qualifies: reasons.length === 0, reasons };
754
- }
755
- var RENDER_ESTIMATE_REASONS = {
756
- unknown_surface: "unknown_surface",
757
- surface_never_shows_estimate: "surface_never_shows_estimate",
758
- estimate_does_not_qualify: "estimate_does_not_qualify"
759
- };
760
- function shouldRenderEstimate(estimate, surface) {
761
- if (!SURFACES.includes(surface)) {
762
- return { render: false, reasons: [RENDER_ESTIMATE_REASONS.unknown_surface] };
763
- }
764
- if (ESTIMATE_FORBIDDEN_SURFACES.includes(surface)) {
765
- return { render: false, reasons: [RENDER_ESTIMATE_REASONS.surface_never_shows_estimate] };
766
- }
767
- const q = estimateQualifies(estimate);
768
- if (!q.qualifies) {
769
- return { render: false, reasons: [RENDER_ESTIMATE_REASONS.estimate_does_not_qualify, ...q.reasons] };
770
- }
771
- return { render: true, reasons: [] };
772
- }
773
- var SHARE_CARD_TYP = "AAP-ShareCard/v1";
774
- var IDENTIFYING_KEYS = /* @__PURE__ */ new Set([
775
- "repo",
776
- "repository",
777
- "repo_name",
778
- "branch",
779
- "branch_name",
780
- "ref",
781
- "pr",
782
- "pr_title",
783
- "pull_request",
784
- "title",
785
- "commit",
786
- "commit_message",
787
- "commit_sha",
788
- "message",
789
- "author",
790
- "email",
791
- "url",
792
- "path"
793
- ]);
794
- var CARD_KEYS = ["typ", "card_id", "period", "issued_at", "signing_key_id", "statements", "lines", "signature"];
795
- var PERIOD_KEYS = ["start", "end"];
796
- var STATEMENT_KEYS = ["typ", "hash"];
797
- var LINE_KEYS = ["id", "kind", "label", "value", "statement_hash"];
798
- var HEX64 = /^[0-9a-f]{64}$/;
799
- var DATE = /^\d{4}-\d{2}-\d{2}$/;
800
- var OPAQUE_ID = /^[a-z0-9_-]{1,64}$/;
801
- var LABEL = /^[A-Za-z0-9 .,()+-]{1,40}$/;
802
- var LABEL_MAX = 40;
803
- var SHARE_CARD_STATEMENT_TYPES = [
804
- "AAP-ConductRecord/v1",
805
- "AAP-PRRecord/v1",
806
- "AAP-Outcome/v1"
807
- ];
808
- var SIGNATURE_LEN = 86;
809
- var RFC3339 = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|([+-])(\d{2}):(\d{2}))$/;
810
- function isCalendarDate(y, m, d) {
811
- if (m < 1 || m > 12 || d < 1) return false;
812
- const dim = [31, y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][m - 1];
813
- return d <= dim;
814
- }
815
- function isRfc3339(v) {
816
- const m = RFC3339.exec(v);
817
- if (!m) return false;
818
- const [y, mo, d, h, mi, se] = m.slice(1, 7).map(Number);
819
- if (!isCalendarDate(y, mo, d) || h > 23 || mi > 59 || se > 59) return false;
820
- if (m[7] !== void 0 && (Number(m[8]) > 23 || Number(m[9]) > 59)) return false;
821
- return true;
822
- }
823
- function shareCardSigningInput(card) {
824
- const { signature: _signature, ...rest } = card;
825
- return canonicalJson(rest);
826
- }
827
- function bytesToBase64url(b) {
828
- let bin = "";
829
- for (const x of b) bin += String.fromCharCode(x);
830
- return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
831
- }
832
- function base64urlToBytes(s) {
833
- if (s.length !== SIGNATURE_LEN || !/^[A-Za-z0-9_-]+$/.test(s)) throw new Error("bad base64url");
834
- const padded = s.replace(/-/g, "+").replace(/_/g, "/") + "===".slice(0, (4 - s.length % 4) % 4);
835
- const bin = atob(padded);
836
- const out = new Uint8Array(bin.length);
837
- for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
838
- if (out.length !== 64 || bytesToBase64url(out) !== s) throw new Error("non-canonical base64url");
839
- return out;
840
- }
841
- function isObj(v) {
842
- return typeof v === "object" && v !== null && !Array.isArray(v);
843
- }
844
- function exactKeys(obj, allowed, where, errors) {
845
- for (const k of Object.keys(obj)) {
846
- if (IDENTIFYING_KEYS.has(k.toLowerCase())) errors.push(`${where}: identifying field "${k}" is not allowed`);
847
- else if (!allowed.includes(k)) errors.push(`${where}: unknown field "${k}"`);
848
- }
849
- for (const k of allowed) if (!(k in obj)) errors.push(`${where}: missing field "${k}"`);
850
- }
851
- function validateShareCardShape(card) {
852
- const errors = [];
853
- if (!isObj(card)) return ["card: not an object"];
854
- exactKeys(card, CARD_KEYS, "card", errors);
855
- if (card.typ !== SHARE_CARD_TYP) errors.push(`card: typ must be ${SHARE_CARD_TYP}`);
856
- if (typeof card.card_id !== "string" || !OPAQUE_ID.test(card.card_id)) errors.push("card: card_id must be an opaque id matching ^[a-z0-9_-]{1,64}$");
857
- if (typeof card.issued_at !== "string" || !isRfc3339(card.issued_at)) errors.push("card: issued_at must be an RFC 3339 date-time");
858
- if (typeof card.signing_key_id !== "string" || !card.signing_key_id) errors.push("card: signing_key_id must be a non-empty string");
859
- if (typeof card.signature !== "string") errors.push("card: signature must be a string");
860
- else {
861
- try {
862
- base64urlToBytes(card.signature);
863
- } catch {
864
- errors.push("card: signature must be a canonical 86-char unpadded base64url Ed25519 signature");
865
- }
866
- }
867
- if (!isObj(card.period)) errors.push("period: not an object");
868
- else {
869
- exactKeys(card.period, PERIOD_KEYS, "period", errors);
870
- for (const k of PERIOD_KEYS) {
871
- const v = card.period[k];
872
- if (typeof v !== "string" || !DATE.test(v) || !isCalendarDate(Number(v.slice(0, 4)), Number(v.slice(5, 7)), Number(v.slice(8, 10)))) {
873
- errors.push(`period.${k}: must be a real calendar date YYYY-MM-DD`);
874
- }
875
- }
876
- const { start, end } = card.period;
877
- if (typeof start === "string" && typeof end === "string" && DATE.test(start) && DATE.test(end) && start > end) {
878
- errors.push("period: start must be <= end");
879
- }
880
- }
881
- const hashes = /* @__PURE__ */ new Set();
882
- const referenced = /* @__PURE__ */ new Set();
883
- const lineIds = /* @__PURE__ */ new Set();
884
- if (!Array.isArray(card.statements) || card.statements.length === 0) errors.push("statements: must be a non-empty array");
885
- else
886
- card.statements.forEach((s, i) => {
887
- if (!isObj(s)) return void errors.push(`statements[${i}]: not an object`);
888
- exactKeys(s, STATEMENT_KEYS, `statements[${i}]`, errors);
889
- if (typeof s.typ !== "string" || !SHARE_CARD_STATEMENT_TYPES.includes(s.typ)) {
890
- errors.push(`statements[${i}].typ: must be one of ${SHARE_CARD_STATEMENT_TYPES.join(", ")}`);
891
- }
892
- if (typeof s.hash !== "string" || !HEX64.test(s.hash)) errors.push(`statements[${i}].hash: must be 64 lowercase hex`);
893
- else hashes.add(s.hash);
894
- });
895
- if (!Array.isArray(card.lines) || card.lines.length === 0) errors.push("lines: must be a non-empty array");
896
- else
897
- card.lines.forEach((l, i) => {
898
- if (!isObj(l)) return void errors.push(`lines[${i}]: not an object`);
899
- exactKeys(l, LINE_KEYS, `lines[${i}]`, errors);
900
- if (typeof l.id !== "string" || !OPAQUE_ID.test(l.id)) errors.push(`lines[${i}].id: must be an opaque id matching ^[a-z0-9_-]{1,64}$`);
901
- else if (lineIds.has(l.id)) errors.push(`lines[${i}].id: duplicate line id "${l.id}"`);
902
- else lineIds.add(l.id);
903
- if (l.kind !== "count" && l.kind !== "revert") errors.push(`lines[${i}].kind: must be "count" or "revert"`);
904
- if (typeof l.label !== "string" || !l.label || l.label.length > LABEL_MAX || /[/#]|:\/\/|www\./i.test(l.label) || !LABEL.test(l.label)) {
905
- errors.push(`lines[${i}].label: must be short plain text (max ${LABEL_MAX}), no "/", "#" or URLs`);
906
- }
907
- if (typeof l.value !== "number" || !Number.isSafeInteger(l.value) || l.value < 0) errors.push(`lines[${i}].value: must be a non-negative integer`);
908
- if (typeof l.statement_hash !== "string" || !HEX64.test(l.statement_hash)) errors.push(`lines[${i}].statement_hash: must be 64 lowercase hex`);
909
- else if (!hashes.has(l.statement_hash)) errors.push(`lines[${i}].statement_hash: not committed in statements`);
910
- else referenced.add(l.statement_hash);
911
- });
912
- for (const h of hashes) if (!referenced.has(h)) errors.push(`statements: ${h} is not referenced by any line`);
913
- return errors;
914
- }
915
- async function verifyShareCard(card, options) {
916
- const fail = (errors) => ({ valid: false, lines: [], errors });
917
- const shapeErrors = validateShareCardShape(card);
918
- if (shapeErrors.length > 0) return fail(shapeErrors);
919
- const c = card;
920
- let ok = false;
921
- try {
922
- const pk = typeof options.publicKey === "string" ? hexToUint8(options.publicKey) : options.publicKey;
923
- ok = await verifyAsync(base64urlToBytes(c.signature), utf8ToBytes(shareCardSigningInput(c)), pk, { zip215: false });
924
- } catch {
925
- ok = false;
926
- }
927
- if (!ok) return fail(["signature: verification failed"]);
928
- return { valid: true, lines: c.lines.map((l) => ({ ...l })), errors: [] };
929
- }
930
-
931
- export { BUNDLE_TYP, CARD_LINE_CLASSES, CARD_SURFACES, CLAIM_CLASSES, CLAIM_TABLE, ESTIMATE_COUNTING_FLOOR_TOKENS, ESTIMATE_DISQUALIFIERS, ESTIMATE_FORBIDDEN_SURFACES, FLAGS, GOAL_MODES, LINE_KEY_FIELDS, LINT_RULES, MODES, MODE_VERBS, PROTECTION_VERDICTS, REASONS, RENDER_ESTIMATE_REASONS, SHARE_CARD_TYP, STATEMENT_TYPES, SURFACES, attachLineContext, canonicalJson, estimateQualifies, fieldForLineKey, isClaimField, lintRendering, lintRenderings, protectionVerb, shareCardSigningInput, shouldRenderEstimate, statementCommitment, statementDigest, validateShareCardShape, verify, verifyCertificate, verifyShareCard, verifyStatement };
932
- //# sourceMappingURL=index.js.map
933
- //# sourceMappingURL=index.js.map