provared 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1083 -0
- package/README.md +6 -3
- package/bin/provared-check.mjs +115 -11
- package/docs/guide.md +464 -15
- package/docs/threat-model.md +315 -9
- package/package.json +2 -2
- package/page/check-page.js +11 -32
- package/page/index.html +4 -0
- package/page/render.js +157 -9
- package/spec/provared-format.md +809 -23
- package/src/book.js +539 -18
- package/src/check.d.ts +3 -0
- package/src/check.js +2 -0
- package/src/denial.js +123 -0
- package/src/der.js +55 -6
- package/src/dev.d.ts +1 -1
- package/src/dev.js +1 -1
- package/src/documents.js +149 -0
- package/src/guard.js +13 -2
- package/src/index.js +18 -3
- package/src/jws.js +8 -0
- package/src/keys.js +48 -1
- package/src/pass.js +1 -1
- package/src/recorder.js +267 -15
- package/src/seal.js +36 -7
- package/src/sealer.js +812 -0
- package/src/service.js +4 -4
- package/src/signatures.js +281 -5
- package/src/standing.js +4 -4
- package/src/stub.js +18 -5
- package/src/timestamp.js +55 -16
- package/src/tools.js +30 -13
- package/src/tree.js +139 -7
- package/src/types.d.ts +314 -20
- package/src/witness.js +63 -0
- package/src/witnessing.js +362 -0
package/README.md
CHANGED
|
@@ -86,6 +86,9 @@ agent = create_agent(model, tools=[order_supplies, send_message],
|
|
|
86
86
|
})]) # last among the middleware that wrap tool calls
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
The package is listed among the middleware in
|
|
90
|
+
[LangChain's own documentation](https://docs.langchain.com/oss/python/integrations/middleware).
|
|
91
|
+
|
|
89
92
|
## The four parts
|
|
90
93
|
|
|
91
94
|
| Part | What it is |
|
|
@@ -125,7 +128,7 @@ each part protects against, is in
|
|
|
125
128
|
|
|
126
129
|
- **[The guide](docs/guide.md)**: the whole library, in one document.
|
|
127
130
|
Installing and importing; the command-line checker; what a slip can
|
|
128
|
-
say; seals, time-stamps and the chain of block headers; cancelling;
|
|
131
|
+
say; seals, time-stamps, witnesses and the chain of block headers; cancelling;
|
|
129
132
|
who stands behind a name; covered fields; passing a slip on; the check
|
|
130
133
|
before acting; the stub writer beside an agent and the connector for
|
|
131
134
|
its tools; what the checker returns; what it never does; what is not
|
|
@@ -156,9 +159,9 @@ each part protects against, is in
|
|
|
156
159
|
|
|
157
160
|
## Status
|
|
158
161
|
|
|
159
|
-
**Version 0.
|
|
162
|
+
**Version 0.6.0, a draft.** Published as it is, by one author in their
|
|
160
163
|
own time, built with AI tools since 3 October 2026; fixes as time
|
|
161
|
-
allows. The code has been through
|
|
164
|
+
allows. The code has been through more than 80 rounds of review by AI reviewers,
|
|
162
165
|
each reviewer a fresh session given the code, the documents and a brief
|
|
163
166
|
of what to look at; every fault was fixed, or stated as a limit, and
|
|
164
167
|
kept as a test. No person other than the author has reviewed it yet.
|
package/bin/provared-check.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
// provared-check <file> [--issuer <thumbprint>]... [--sealer <fingerprint>]... [--stamp-service <fingerprint>]...
|
|
5
5
|
// [--block <fingerprint>]... [--voucher <fingerprint>]... [--disclosures <file>] [--cancellation <file>]...
|
|
6
|
-
// [--root <top fingerprint>] [--size <entries>] [--json]
|
|
6
|
+
// [--seal <file>]... [--witness <fingerprint>]... [--root <top fingerprint>] [--size <entries>] [--json]
|
|
7
7
|
//
|
|
8
8
|
// Exit codes:
|
|
9
9
|
// 0 the record is intact (no problem found, every signature checked),
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
import { readFileSync } from 'node:fs';
|
|
22
22
|
import { fileURLToPath } from 'node:url';
|
|
23
|
-
import { MIN_BLOCKS_AFTER, REFUSAL_REASONS, blocksInChain, checkBook, checkHeaderChain, checkShow, conditionWords, limitWords, neverWords } from '../src/check.js';
|
|
23
|
+
import { DENIAL_SOURCES, MIN_BLOCKS_AFTER, REFUSAL_REASONS, blocksInChain, checkBook, checkHeaderChain, checkShow, conditionWords, limitWords, neverWords } from '../src/check.js';
|
|
24
24
|
|
|
25
25
|
// Node.js marks its ML-DSA and SLH-DSA as experimental and says so on every
|
|
26
26
|
// run. The checker does not repeat that warning; any other warning is shown.
|
|
@@ -31,7 +31,8 @@ process.on('warning', (w) => {
|
|
|
31
31
|
|
|
32
32
|
const USAGE = `Usage: provared-check <file> [--issuer <thumbprint>]... [--sealer <fingerprint>]... [--stamp-service <fingerprint>]...
|
|
33
33
|
[--block <fingerprint>]... [--voucher <fingerprint>]... [--disclosures <file>] [--cancellation <file>]...
|
|
34
|
-
[--
|
|
34
|
+
[--seal <file>]... [--witness <fingerprint>]... [--root <top fingerprint>] [--size <entries>]
|
|
35
|
+
[--headers <file>] [--json]
|
|
35
36
|
provared-check --sample [--json]
|
|
36
37
|
|
|
37
38
|
<file> a book (one entry to a line) or a Show (one JSON object)
|
|
@@ -47,6 +48,12 @@ const USAGE = `Usage: provared-check <file> [--issuer <thumbprint>]... [--sealer
|
|
|
47
48
|
--cancellation a JSON file of the person's own copy of a cancellation, as the entry it would be in a book:
|
|
48
49
|
{"cancellation": {...}, "stamps": [...]}. It counts even where the book leaves it out; may be repeated.
|
|
49
50
|
It may hold "acknowledgements": what the agent's side signed when it was handed the cancellation
|
|
51
|
+
--seal a JSON file of a seal held elsewhere (by you, an auditor, a copy kept apart), as the entry it is in the book:
|
|
52
|
+
{"seal": {...}, "stamps": [...]}. The book must hold that seal at the place it states: one that ends
|
|
53
|
+
before it was cut short. It counts only with --sealer naming its recorder; may be repeated.
|
|
54
|
+
It may hold "witnesses": statements that witnesses signed for that seal, such as ones that came later
|
|
55
|
+
--witness the fingerprint of the key set of a witness you trust: a seal counts as witnessed only by such a
|
|
56
|
+
witness. Being witnessed excuses nothing; may be repeated
|
|
50
57
|
--root the top fingerprint you already trust
|
|
51
58
|
--size the number of entries you already trust; keep it with the top fingerprint
|
|
52
59
|
--headers a file of Bitcoin block headers, as provared-headers keeps it. It is checked here, from the chain's
|
|
@@ -61,14 +68,14 @@ const FINGERPRINT = /^[A-Za-z0-9_-]{43}$/;
|
|
|
61
68
|
const BLOCK = /^[0-9a-f]{64}$/;
|
|
62
69
|
|
|
63
70
|
// Options that may be repeated, and those that may be given once.
|
|
64
|
-
const LISTS = { '--issuer': 'issuerKeys', '--sealer': 'sealKeys', '--stamp-service': 'stampServices', '--block': 'blocks', '--voucher': 'vouchers', '--cancellation': 'cancellations' };
|
|
71
|
+
const LISTS = { '--issuer': 'issuerKeys', '--sealer': 'sealKeys', '--stamp-service': 'stampServices', '--block': 'blocks', '--voucher': 'vouchers', '--witness': 'witnesses', '--cancellation': 'cancellations', '--seal': 'seals' };
|
|
65
72
|
const ONCE = { '--disclosures': 'disclosures', '--root': 'expectedRoot', '--size': 'expectedSize', '--headers': 'headers' };
|
|
66
73
|
|
|
67
74
|
// The arguments, or the reason they cannot be used. A value that is not a
|
|
68
75
|
// fingerprint is refused, never set aside: a typing error must not quietly
|
|
69
76
|
// leave a time-stamp or a name uncounted.
|
|
70
77
|
function parseArguments(argv) {
|
|
71
|
-
const out = { file: null, sample: false, issuerKeys: [], sealKeys: [], stampServices: [], blocks: [], vouchers: [], cancellations: [], disclosures: undefined, expectedRoot: undefined, expectedSize: undefined, json: false };
|
|
78
|
+
const out = { file: null, sample: false, issuerKeys: [], sealKeys: [], stampServices: [], blocks: [], vouchers: [], witnesses: [], cancellations: [], seals: [], disclosures: undefined, expectedRoot: undefined, expectedSize: undefined, json: false };
|
|
72
79
|
for (let i = 0; i < argv.length; i++) {
|
|
73
80
|
const a = argv[i];
|
|
74
81
|
if (a === '--help' || a === '-h') return { help: true };
|
|
@@ -82,7 +89,7 @@ function parseArguments(argv) {
|
|
|
82
89
|
if (i + 1 >= argv.length) return { error: `${a} needs a value.` };
|
|
83
90
|
const value = argv[++i];
|
|
84
91
|
if (Object.hasOwn(ONCE, a) && out[ONCE[a]] !== undefined) return { error: `${a} may be given once.` };
|
|
85
|
-
if (['--issuer', '--sealer', '--stamp-service', '--voucher', '--root'].includes(a) && !FINGERPRINT.test(value)) {
|
|
92
|
+
if (['--issuer', '--sealer', '--stamp-service', '--voucher', '--witness', '--root'].includes(a) && !FINGERPRINT.test(value)) {
|
|
86
93
|
return { error: `The value of ${a} is not a fingerprint. A fingerprint is 43 letters, digits, hyphens or underscores, with no spaces.` };
|
|
87
94
|
}
|
|
88
95
|
if (a === '--block' && !BLOCK.test(value)) return { error: 'The value of --block is not the fingerprint of a block: 64 characters, the digits 0 to 9 and the letters a to f.' };
|
|
@@ -94,7 +101,7 @@ function parseArguments(argv) {
|
|
|
94
101
|
}
|
|
95
102
|
if (out.sample) {
|
|
96
103
|
if (out.file !== null) return { error: '--sample names the sample record itself: give no file with it.' };
|
|
97
|
-
for (const name of ['issuerKeys', 'sealKeys', 'stampServices', 'blocks', 'vouchers', 'cancellations']) {
|
|
104
|
+
for (const name of ['issuerKeys', 'sealKeys', 'stampServices', 'blocks', 'vouchers', 'witnesses', 'cancellations', 'seals']) {
|
|
98
105
|
if (out[name].length) return { error: '--sample names what to trust itself: give no other option with it but --json.' };
|
|
99
106
|
}
|
|
100
107
|
if (out.disclosures !== undefined || out.expectedRoot !== undefined || out.expectedSize !== undefined || out.headers !== undefined) {
|
|
@@ -282,10 +289,22 @@ function printSeal(e) {
|
|
|
282
289
|
console.log(` Recorder's key set fingerprint: ${e.sealer}`);
|
|
283
290
|
printStamps(e.stamps);
|
|
284
291
|
if (e.stamps.length === 0) console.log(' No outside time-stamp: the time is only the recorder\'s own word.');
|
|
292
|
+
printWitnesses(e.witnesses);
|
|
285
293
|
}
|
|
286
294
|
if (e.signatures && e.signatures.length) console.log(` Recorder's signatures: ${e.signatures.map((s) => `${s.method} ${s.state}`).join('; ')}`);
|
|
287
295
|
}
|
|
288
296
|
|
|
297
|
+
// The witness statements beside a seal. What a statement says is shown only where it passed its check.
|
|
298
|
+
function printWitnesses(list) {
|
|
299
|
+
for (const w of list ?? []) {
|
|
300
|
+
if (w.state === 'invalid') console.log(' Witness statement, NOT USED: it did not pass its check (see the problems)');
|
|
301
|
+
else if (w.state === 'unavailable') console.log(' Witness statement, NOT CHECKED: this device cannot check both of its signatures');
|
|
302
|
+
else if (w.counted) console.log(` WITNESSED by ${label(w.name)} (a label), a witness you named as trusted (key set ${w.witness}), at ${w.when} (the witness's own word)`);
|
|
303
|
+
else if (w.trusted) console.log(` Witness statement, NOT COUNTED: from a witness you named as trusted (key set ${w.witness}), but it was not counted (see the notes beside the seal)`);
|
|
304
|
+
else console.log(` Witness statement, NOT COUNTED: from a witness you did not name as trusted (key set ${w.witness})`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
289
308
|
function printCancellation(e) {
|
|
290
309
|
console.log(`\nEntry ${e.index}: CANCELLATION ${e.fingerprint ?? ''}`);
|
|
291
310
|
if (hidden(e)) console.log(hidden(e));
|
|
@@ -313,8 +332,53 @@ function printAcknowledgement(e) {
|
|
|
313
332
|
if (e.signatures && e.signatures.length) console.log(` Agent's signatures: ${e.signatures.map((s) => `${s.method} ${s.state}`).join('; ')}`);
|
|
314
333
|
}
|
|
315
334
|
|
|
335
|
+
// A denial: the agent's own side says it did not take an action, and why.
|
|
336
|
+
function printDenial(e) {
|
|
337
|
+
console.log(`\nEntry ${e.index}: DENIAL ${e.fingerprint ?? ''}`);
|
|
338
|
+
if (hidden(e)) console.log(hidden(e));
|
|
339
|
+
else {
|
|
340
|
+
const c = e.content;
|
|
341
|
+
const amount = c.amount ? `, ${c.amount.value} ${safe(c.amount.unit)}` : '';
|
|
342
|
+
const service = c.with ? `, with [${safe(c.with)}]` : '';
|
|
343
|
+
console.log(` ${c.pass ? `The helper agent of pass ${c.pass}` : 'The slip\'s own agent'} states that it did not take an action`);
|
|
344
|
+
console.log(` Under slip ${c.slip}`);
|
|
345
|
+
console.log(` ${c.when} (the agent's side's own word): not taken ${safe(c.action)}${amount}${service}`);
|
|
346
|
+
if (c.details) console.log(` Documents named: ${c.details.length}, by their fingerprints only`);
|
|
347
|
+
console.log(` Who said no: ${DENIAL_SOURCES[c.source]}`);
|
|
348
|
+
if (c.codes) console.log(` Reasons given: ${c.codes.join(', ')}`);
|
|
349
|
+
console.log(' This shows what the agent\'s side said. It is not counted against the agent, and it does not show that the action was not taken in some other way.');
|
|
350
|
+
}
|
|
351
|
+
if (e.signatures && e.signatures.length) console.log(` Agent's signatures: ${e.signatures.map((s) => `${s.method} ${s.state}`).join('; ')}`);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// A seal held elsewhere, handed over beside the book.
|
|
355
|
+
function printHeldSeal(h, n) {
|
|
356
|
+
console.log(`\nHanded over beside the book, ${n}: SEAL HELD ELSEWHERE ${h.fingerprint ?? ''}`);
|
|
357
|
+
if (h.problems.length) {
|
|
358
|
+
console.log(' It did not pass its check, so it was not used. See the problems at the top.');
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
// What a seal says is shown only where this device checked at least one of its signatures.
|
|
362
|
+
if (h.verified === 'none') {
|
|
363
|
+
console.log(' What this seal says is not shown, because this device could not check its signatures.');
|
|
364
|
+
for (const n of h.notes) console.log(` Note: ${n}`);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const c = h.content;
|
|
368
|
+
console.log(` Covers the first ${c.size} entries, with the top fingerprint ${c.root}, and stands at entry ${h.place}`);
|
|
369
|
+
console.log(` Sealed at (the recorder's own word): ${c.when}`);
|
|
370
|
+
console.log(` Recorder: ${label(c.by.name)} (a label, not checked)`);
|
|
371
|
+
console.log(` Recorder's key set fingerprint: ${h.sealer}`);
|
|
372
|
+
printStamps(h.stamps);
|
|
373
|
+
printWitnesses(h.witnesses);
|
|
374
|
+
console.log(h.used ? (h.inBook === null ? ' In the book at that place: NO' : ` In the book at that place: yes, at entry ${h.inBook}`) : ' In the book at that place: not compared, because it was not used');
|
|
375
|
+
console.log(` Recorder's signatures: ${h.signatures.map((s) => `${s.method} ${s.state}`).join('; ')}`);
|
|
376
|
+
for (const n of h.notes) console.log(` Note: ${n}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
316
379
|
// A cancellation the person kept, handed over beside the book.
|
|
317
380
|
function printHeld(h, n) {
|
|
381
|
+
if (h.kind === 'seal') return printHeldSeal(h, n);
|
|
318
382
|
console.log(`\nHanded over beside the book, ${n}: CANCELLATION ${h.fingerprint ?? ''}`);
|
|
319
383
|
if (h.problems.length) {
|
|
320
384
|
console.log(' It did not pass its check, so it was not used. See the problems at the top.');
|
|
@@ -403,6 +467,7 @@ function print(result, isShow) {
|
|
|
403
467
|
else if (e.kind === 'seal') printSeal(e);
|
|
404
468
|
else if (e.kind === 'cancellation') printCancellation(e);
|
|
405
469
|
else if (e.kind === 'acknowledgement') printAcknowledgement(e);
|
|
470
|
+
else if (e.kind === 'denial') printDenial(e);
|
|
406
471
|
else if (e.kind === 'pass') printPass(e);
|
|
407
472
|
else if (e.kind === 'vouching') printVouching(e);
|
|
408
473
|
else if (e.kind === 'withdrawal') printWithdrawal(e);
|
|
@@ -436,7 +501,9 @@ function print(result, isShow) {
|
|
|
436
501
|
if (!s.problemFound) {
|
|
437
502
|
const stamped = s.sealed ? `yes: the first ${s.sealed.entries} entries existed by ${s.sealed.when}` : 'no';
|
|
438
503
|
console.log(` Time-stamped in a way you trust? ${stamped}`);
|
|
504
|
+
if (s.counts.seals || args.witnesses.length) console.log(` Witnessed by a witness you trust? ${witnessedWords(s)}${isShow && s.witnessed ? ' (single pages: this says nothing about totals over the whole book)' : ''}`);
|
|
439
505
|
console.log(` Checked against keys you named? ${trustWords(s)}`);
|
|
506
|
+
if (args.seals.length) console.log(` Seals held elsewhere compared? ${heldWords(result)}`);
|
|
440
507
|
}
|
|
441
508
|
console.log(` Slips ${s.counts.slips}, stubs ${s.counts.stubs} (countersigned ${s.counts.countersigned}, one-sided ${s.counts.oneSided})${s.counts.seals ? `, seals ${s.counts.seals}` : ''}`);
|
|
442
509
|
if (s.counts.cancellations || s.counts.acknowledgements || s.counts.vouchings || s.counts.passes) {
|
|
@@ -445,15 +512,39 @@ function print(result, isShow) {
|
|
|
445
512
|
if (s.counts.approved || s.counts.refusals || s.counts.terms) {
|
|
446
513
|
console.log(` Approved by the person ${s.counts.approved}, refusals ${s.counts.refusals}, terms for agents ${s.counts.terms}`);
|
|
447
514
|
}
|
|
515
|
+
if (s.counts.denials) console.log(` Denials (the agent's side's own word, not counted against it) ${s.counts.denials}`);
|
|
448
516
|
console.log('\nWhat this check does not show');
|
|
449
517
|
for (const l of s.limits) console.log(` - ${l}`);
|
|
450
518
|
}
|
|
451
519
|
|
|
452
520
|
// Whether the check rests on keys the person named: the passkey they trust
|
|
453
|
-
// and, where the record has a seal
|
|
454
|
-
//
|
|
521
|
+
// and, where the record has a seal or seals held elsewhere were handed over,
|
|
522
|
+
// the recorder they expect. Without them a record agrees only with the keys
|
|
523
|
+
// inside it, which anyone could have made, and a seal handed over is not
|
|
524
|
+
// compared with the book.
|
|
455
525
|
function named(s) {
|
|
456
|
-
return args.issuerKeys.length > 0 && (s.counts.seals === 0 || args.sealKeys.length > 0);
|
|
526
|
+
return args.issuerKeys.length > 0 && ((s.counts.seals === 0 && args.seals.length === 0) || args.sealKeys.length > 0);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// How far a witness the person named reaches. It excuses nothing, and is not needed for exit code 0.
|
|
530
|
+
function witnessedWords(s) {
|
|
531
|
+
if (!s.witnessed) return args.witnesses.length ? 'no' : 'no (you named no witness you trust, --witness)';
|
|
532
|
+
const w = s.witnessed;
|
|
533
|
+
return `yes: the seal at entry ${w.seal}, which covers the first ${w.entries} entries (${w.seals.length} of ${s.counts.seals} seals witnessed)`;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Whether the seals held elsewhere that were handed over were compared with the book.
|
|
537
|
+
function heldWords(result) {
|
|
538
|
+
if (args.sealKeys.length === 0) return 'NO: you named no recorder you expect (--sealer), so the seals handed over were not compared with the book. This is not a pass.';
|
|
539
|
+
const seals = (result.held ?? []).filter((h) => h.kind === 'seal');
|
|
540
|
+
const used = seals.filter((h) => h.used).length;
|
|
541
|
+
return used === seals.length ? 'yes' : `NO: ${seals.length - used} of ${seals.length} handed over were not compared with the book (see the note beside each one). This is not a pass.`;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Whether every seal handed over beside the book was compared with it. One that was not (it gives another book
|
|
545
|
+
// number, or could not be used) gives no pass, as one handed over with no recorder named does not.
|
|
546
|
+
function heldAllCompared(result) {
|
|
547
|
+
return (result.held ?? []).filter((h) => h.kind === 'seal').every((h) => h.used);
|
|
457
548
|
}
|
|
458
549
|
|
|
459
550
|
function trustWords(s) {
|
|
@@ -461,6 +552,7 @@ function trustWords(s) {
|
|
|
461
552
|
const recorders = new Set(args.sealKeys).size === 1 ? 'the recorder you expect' : 'the recorders you expect';
|
|
462
553
|
if (named(s)) return s.counts.seals ? `yes: ${passkeys}, and ${recorders}` : `yes: ${passkeys}`;
|
|
463
554
|
if (args.issuerKeys.length === 0) return 'NO: you named no passkey you trust (--issuer), so anyone could have made this record. This is not a pass.';
|
|
555
|
+
if (s.counts.seals === 0) return `NO: you named ${passkeys}, but no recorder you expect (--sealer) for the seals handed over beside the book. This is not a pass.`;
|
|
464
556
|
return 'NO: the record has a seal, and you named no recorder you expect (--sealer), so anyone could have sealed it. This is not a pass.';
|
|
465
557
|
}
|
|
466
558
|
|
|
@@ -520,6 +612,7 @@ if (args.sealKeys.length) options.sealKeys = args.sealKeys;
|
|
|
520
612
|
if (args.stampServices.length) options.stampServices = args.stampServices;
|
|
521
613
|
if (args.blocks.length) options.blocks = args.blocks;
|
|
522
614
|
if (args.vouchers.length) options.vouchers = args.vouchers;
|
|
615
|
+
if (args.witnesses.length) options.witnesses = args.witnesses;
|
|
523
616
|
if (args.disclosures !== undefined) {
|
|
524
617
|
try {
|
|
525
618
|
options.disclosures = JSON.parse(readFileSync(args.disclosures, 'utf8'));
|
|
@@ -539,6 +632,17 @@ if (args.cancellations.length) {
|
|
|
539
632
|
}
|
|
540
633
|
}
|
|
541
634
|
}
|
|
635
|
+
if (args.seals.length) {
|
|
636
|
+
options.seals = [];
|
|
637
|
+
for (const file of args.seals) {
|
|
638
|
+
try {
|
|
639
|
+
options.seals.push(JSON.parse(readFileSync(file, 'utf8')));
|
|
640
|
+
} catch {
|
|
641
|
+
console.error(`The file of a seal could not be read: ${file}`);
|
|
642
|
+
process.exit(2);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
542
646
|
if (args.expectedRoot !== undefined) options.expectedRoot = args.expectedRoot;
|
|
543
647
|
if (args.expectedSize !== undefined) options.expectedSize = args.expectedSize;
|
|
544
648
|
|
|
@@ -594,7 +698,7 @@ try {
|
|
|
594
698
|
if (chain) printChain(chain, fromChain);
|
|
595
699
|
}
|
|
596
700
|
const s = result.summary;
|
|
597
|
-
code = s.problemFound ? 2 : s.intact && s.withinSlips && named(s) ? 0 : 1;
|
|
701
|
+
code = s.problemFound ? 2 : s.intact && s.withinSlips && named(s) && heldAllCompared(result) ? 0 : 1;
|
|
598
702
|
} catch {
|
|
599
703
|
// The checker is written never to throw. If it ever does, that is not a pass.
|
|
600
704
|
console.error('The check could not be completed. This is not a pass.');
|