attenu-guard 0.11.0 → 0.13.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 +38 -0
- package/README.md +27 -0
- package/dist/cjs/audit.d.ts.map +1 -1
- package/dist/cjs/audit.js +45 -11
- package/dist/cjs/audit.js.map +1 -1
- package/dist/cjs/authority.d.ts.map +1 -1
- package/dist/cjs/authority.js +15 -9
- package/dist/cjs/authority.js.map +1 -1
- package/dist/cjs/canonical.d.ts +24 -0
- package/dist/cjs/canonical.d.ts.map +1 -1
- package/dist/cjs/canonical.js +49 -0
- package/dist/cjs/canonical.js.map +1 -1
- package/dist/cjs/ceilings.d.ts +17 -0
- package/dist/cjs/ceilings.d.ts.map +1 -1
- package/dist/cjs/ceilings.js +48 -0
- package/dist/cjs/ceilings.js.map +1 -1
- package/dist/cjs/cli.d.ts +16 -4
- package/dist/cjs/cli.d.ts.map +1 -1
- package/dist/cjs/cli.js +284 -21
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/display.d.ts +76 -0
- package/dist/cjs/display.d.ts.map +1 -0
- package/dist/cjs/display.js +238 -0
- package/dist/cjs/display.js.map +1 -0
- package/dist/cjs/evidence.d.ts +74 -5
- package/dist/cjs/evidence.d.ts.map +1 -1
- package/dist/cjs/evidence.js +655 -243
- package/dist/cjs/evidence.js.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/audit.d.ts.map +1 -1
- package/dist/esm/audit.js +46 -12
- package/dist/esm/audit.js.map +1 -1
- package/dist/esm/authority.d.ts.map +1 -1
- package/dist/esm/authority.js +15 -9
- package/dist/esm/authority.js.map +1 -1
- package/dist/esm/canonical.d.ts +24 -0
- package/dist/esm/canonical.d.ts.map +1 -1
- package/dist/esm/canonical.js +46 -0
- package/dist/esm/canonical.js.map +1 -1
- package/dist/esm/ceilings.d.ts +17 -0
- package/dist/esm/ceilings.d.ts.map +1 -1
- package/dist/esm/ceilings.js +46 -0
- package/dist/esm/ceilings.js.map +1 -1
- package/dist/esm/cli.d.ts +16 -4
- package/dist/esm/cli.d.ts.map +1 -1
- package/dist/esm/cli.js +285 -22
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/display.d.ts +76 -0
- package/dist/esm/display.d.ts.map +1 -0
- package/dist/esm/display.js +226 -0
- package/dist/esm/display.js.map +1 -0
- package/dist/esm/evidence.d.ts +74 -5
- package/dist/esm/evidence.d.ts.map +1 -1
- package/dist/esm/evidence.js +638 -229
- package/dist/esm/evidence.js.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +1 -1
package/dist/esm/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* attenu-guard — command-line tool.
|
|
3
3
|
*
|
|
4
4
|
* attenu-guard verify <log.jsonl | bundle.json> [--hs256-key HEX | --pubkey HEX] [--kid KID]
|
|
5
|
-
* [--witness-keys FILE]
|
|
5
|
+
* [--witness-keys FILE] [--entries]
|
|
6
6
|
*
|
|
7
7
|
* A `.jsonl` audit log is checked for hash-chain integrity. A bundle (the
|
|
8
8
|
* output of `exportBundle`, or of the Python library's `export_bundle`) is
|
|
@@ -10,53 +10,252 @@
|
|
|
10
10
|
* bundle alone; the signed anchor is verified when a key is given and reported
|
|
11
11
|
* as "not checked" otherwise. `--witness-keys FILE` supplies the trusted witness
|
|
12
12
|
* keys for a bundle carrying observer envelopes; without it every envelope fails
|
|
13
|
-
* `envelope_unknown_witness`, and the output says which flag to pass.
|
|
13
|
+
* `envelope_unknown_witness`, and the output says which flag to pass. A trust-set row may carry
|
|
14
|
+
* `not_after`; a row expired at the time of the run is not trusted. A trust file that is not one
|
|
15
|
+
* (not JSON, not an array of rows, or a row the verifier refuses) is one line naming the file and,
|
|
16
|
+
* for a bad row, the kid.
|
|
14
17
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
18
|
+
* `--entries` prints, after everything else, `entries:` and one line per ledger entry: its seq,
|
|
19
|
+
* event, node and scope; on a bundle, its envelope state, with the observed result and the
|
|
20
|
+
* witness's kid when that state is witness-signed; and the checks that failed on that entry.
|
|
21
|
+
* Without the flag the output is unchanged.
|
|
22
|
+
*
|
|
23
|
+
* A ledger with zero events is reported EMPTY, not OK.
|
|
24
|
+
*
|
|
25
|
+
* Exit codes: 0 = ok, 2 = a check failed, there was nothing to check, or the trust file is
|
|
26
|
+
* malformed, 1 = usage (a missing argument, or a file that cannot be read).
|
|
27
|
+
* The output lines match the Python CLI's, so either implementation can stand in
|
|
28
|
+
* for the other in a script.
|
|
17
29
|
*/
|
|
18
30
|
import { readFileSync } from "node:fs";
|
|
19
31
|
import { AuditLog } from "./audit.js";
|
|
20
|
-
import {
|
|
32
|
+
import { intOr, parseJson } from "./canonical.js";
|
|
33
|
+
import { BARE, escaped, integerText, oneLine } from "./display.js";
|
|
34
|
+
import { PROCESS_ASSERTED, WITNESS_SIGNED, integrityBreak, parseBundle, stateKey, validateWitnessKeys, verifyBundle, } from "./evidence.js";
|
|
21
35
|
import { Ed25519Verifier, HS256TestSigner } from "./wire.js";
|
|
36
|
+
/** The Python CLI prints the same line for the same file. */
|
|
37
|
+
const EMPTY = "EMPTY — no events to verify";
|
|
22
38
|
const USAGE = `attenu-guard — command-line tool.
|
|
23
39
|
|
|
24
40
|
attenu-guard verify <log.jsonl | bundle.json> [--hs256-key HEX | --pubkey HEX] [--kid KID]
|
|
25
|
-
[--witness-keys FILE]
|
|
41
|
+
[--witness-keys FILE] [--entries]
|
|
26
42
|
verify a hash-chained audit log, or an evidence bundle
|
|
27
43
|
(integrity · child ⊆ parent · containment;
|
|
28
44
|
--hs256-key/--pubkey checks the anchor;
|
|
29
|
-
--witness-keys FILE supplies the trusted observer-envelope keys
|
|
45
|
+
--witness-keys FILE supplies the trusted observer-envelope keys;
|
|
46
|
+
--entries adds one line per entry: its envelope state and the checks that failed on it;
|
|
47
|
+
a line is key=value tokens split by single spaces, the key before the first "=",
|
|
48
|
+
and a value never contains a space; one that starts with " is a JSON string)
|
|
30
49
|
`;
|
|
50
|
+
/** A `--witness-keys` file that was read and is not a trust set. The message says why. */
|
|
51
|
+
class NotATrustSet extends Error {
|
|
52
|
+
}
|
|
31
53
|
/**
|
|
32
54
|
* The trust set for a bundle's observer envelopes, read from `--witness-keys FILE`.
|
|
33
55
|
*
|
|
34
56
|
* The file is the `witness_keys` array the interop vectors carry — `[{kid, alg,
|
|
35
|
-
* public_key_hex}]` — or one whole vector case, in which case
|
|
36
|
-
* Without a trust set every envelope in a bundle fails
|
|
37
|
-
* correct (an unknown key is not a trusted one) and useless as
|
|
38
|
-
* carrying envelopes is verified from the command line.
|
|
57
|
+
* public_key_hex}]`, each row optionally with `not_after` — or one whole vector case, in which case
|
|
58
|
+
* its `witness_keys` member is used. Without a trust set every envelope in a bundle fails
|
|
59
|
+
* `envelope_unknown_witness`, which is correct (an unknown key is not a trusted one) and useless as
|
|
60
|
+
* a default, so this is how a bundle carrying envelopes is verified from the command line.
|
|
61
|
+
*
|
|
62
|
+
* Every row is validated here, by the same checks the verifier runs, so a bad row is reported
|
|
63
|
+
* against this file before any bundle is read. Throws the file system's error when the file cannot
|
|
64
|
+
* be read, and `NotATrustSet` when it is not a trust set: not JSON (JSON is UTF-8 text, so bytes
|
|
65
|
+
* that are not UTF-8 are not JSON either), not an array of rows, or a row the verifier refuses,
|
|
66
|
+
* whose message names the kid. The messages are the Python CLI's, word for word.
|
|
39
67
|
*/
|
|
40
68
|
function readWitnessKeys(path) {
|
|
41
|
-
|
|
69
|
+
const bytes = readFileSync(path);
|
|
70
|
+
let parsed;
|
|
71
|
+
try {
|
|
72
|
+
// `ignoreBOM` keeps a byte-order mark in the text, where JSON.parse refuses it as Python does.
|
|
73
|
+
parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes));
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
throw new NotATrustSet("the file is not valid JSON");
|
|
77
|
+
}
|
|
42
78
|
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && "witness_keys" in parsed) {
|
|
43
79
|
parsed = parsed["witness_keys"];
|
|
44
80
|
}
|
|
45
81
|
if (!Array.isArray(parsed)) {
|
|
46
|
-
throw new
|
|
82
|
+
throw new NotATrustSet("expected a JSON array of {kid, alg, public_key_hex} rows, or a vector case carrying one as witness_keys");
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
validateWitnessKeys(parsed);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
throw new NotATrustSet(err.message);
|
|
47
89
|
}
|
|
48
90
|
return parsed;
|
|
49
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* The C library's text for the file-system errors a path argument meets — what Python prints as
|
|
94
|
+
* `strerror` — so `cannot read FILE: REASON` reads the same from both CLIs. `null` for an error
|
|
95
|
+
* that is not a file-system one. A code outside this table prints Node's own message, which is
|
|
96
|
+
* worded differently.
|
|
97
|
+
*/
|
|
98
|
+
function readFailure(err) {
|
|
99
|
+
const code = err?.code;
|
|
100
|
+
if (typeof code !== "string")
|
|
101
|
+
return null;
|
|
102
|
+
return STRERROR[code] ?? err.message;
|
|
103
|
+
}
|
|
104
|
+
const STRERROR = {
|
|
105
|
+
ENOENT: "No such file or directory",
|
|
106
|
+
EACCES: "Permission denied",
|
|
107
|
+
EISDIR: "Is a directory",
|
|
108
|
+
ENOTDIR: "Not a directory",
|
|
109
|
+
ELOOP: "Too many levels of symbolic links",
|
|
110
|
+
ENAMETOOLONG: "File name too long",
|
|
111
|
+
EPERM: "Operation not permitted",
|
|
112
|
+
EMFILE: "Too many open files",
|
|
113
|
+
EINVAL: "Invalid argument",
|
|
114
|
+
EIO: "Input/output error",
|
|
115
|
+
};
|
|
50
116
|
/** Render a boolean the way Python does, so the two CLIs print the same line. */
|
|
51
117
|
function py(value) {
|
|
52
118
|
return value ? "True" : "False";
|
|
53
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Entry index -> the checks that failed on that entry: the `reason` of every failure about it,
|
|
122
|
+
* each once, in the order the verifier reported them.
|
|
123
|
+
*
|
|
124
|
+
* `failureEntries` is the verifier's own record of the entry each failure is about, by index
|
|
125
|
+
* (`VerifyReport.failure_entries`), so a failure lands on its entry even when that entry's seq is
|
|
126
|
+
* missing, null, a boolean, a string or another entry's. A failure about no single entry (a
|
|
127
|
+
* version, root, anchor or chain-id failure, or an envelope whose subject names no entry) is in
|
|
128
|
+
* the bundle-level output already and lands on no line.
|
|
129
|
+
*/
|
|
130
|
+
function failedByEntry(failureEntries, details) {
|
|
131
|
+
const failed = new Map();
|
|
132
|
+
failureEntries.forEach((index, k) => {
|
|
133
|
+
if (index === null)
|
|
134
|
+
return;
|
|
135
|
+
const reason = details[k].reason;
|
|
136
|
+
const reasons = failed.get(index) ?? [];
|
|
137
|
+
if (!reasons.includes(reason))
|
|
138
|
+
reasons.push(reason);
|
|
139
|
+
failed.set(index, reasons);
|
|
140
|
+
});
|
|
141
|
+
return failed;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* One value on an `--entries` line, byte for byte what the Python CLI prints.
|
|
145
|
+
*
|
|
146
|
+
* The values come from the bundle, which is attacker-supplied, and a line is only worth reading if
|
|
147
|
+
* no value can end it early or start a forged one: a `scope` carrying a newline and a clean-looking
|
|
148
|
+
* second line would otherwise print a fake entry with the real entry's `failed=` attached to it. So
|
|
149
|
+
* a value is printed as it is only when it is printable ASCII with no space, `"` or `\`, and an
|
|
150
|
+
* integer in decimal. Anything else is printed in the `escaped` JSON form (display.ts), which holds
|
|
151
|
+
* no whitespace and which a JSON parser reads back.
|
|
152
|
+
*/
|
|
153
|
+
function entryValue(value) {
|
|
154
|
+
if (typeof value === "string" && BARE.test(value))
|
|
155
|
+
return value;
|
|
156
|
+
return integerText(value) ?? escaped(value);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* One `--entries` line: two spaces, then `key=value` tokens separated by single spaces, in a fixed
|
|
160
|
+
* order. `seq` is always printed, as the integer when it is integral (`1.0` prints 1, as the
|
|
161
|
+
* verifier reads it), and as `seq=null` for an entry that has none: an entry without a seq is
|
|
162
|
+
* exactly the entry a reader must see. Any other key with no value is left out.
|
|
163
|
+
*
|
|
164
|
+
* How a line parses: no token contains whitespace; the key is the text before the token's first
|
|
165
|
+
* `=`, and keys never contain one, though a value may (`scope=failed=containment` is the scope
|
|
166
|
+
* "failed=containment"); a value that starts with `"` is a JSON string, and any other value is
|
|
167
|
+
* printed as it is.
|
|
168
|
+
*/
|
|
169
|
+
function entryLine(e, rest) {
|
|
170
|
+
const tokens = [`seq=${entryValue((intOr(e["seq"]) ?? null))}`];
|
|
171
|
+
const pairs = [
|
|
172
|
+
["event", e["event"]],
|
|
173
|
+
["node", e["node"]],
|
|
174
|
+
["scope", e["scope"]],
|
|
175
|
+
...rest,
|
|
176
|
+
];
|
|
177
|
+
for (const [key, value] of pairs) {
|
|
178
|
+
if (value !== null && value !== undefined)
|
|
179
|
+
tokens.push(`${key}=${entryValue(value)}`);
|
|
180
|
+
}
|
|
181
|
+
return ` ${tokens.join(" ")}`;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* A bundle's entries with the state the verifier computed for each, read by index: an entry is
|
|
185
|
+
* witness-signed exactly when `envelopes.witnesses` holds its index, which is the entry the
|
|
186
|
+
* verifier resolved an envelope to, so of two entries sharing a seq only the one the witness signed
|
|
187
|
+
* reads witness-signed. `observed` and `witness` are printed only on such an entry: they are what
|
|
188
|
+
* its envelope says, and an entry that is process-asserted carries no observation the reader may
|
|
189
|
+
* rely on.
|
|
190
|
+
*/
|
|
191
|
+
function bundleEntryLines(entries, rep) {
|
|
192
|
+
const failed = failedByEntry(rep.failure_entries, rep.failure_details);
|
|
193
|
+
const { results, witnesses } = rep.envelopes;
|
|
194
|
+
return entries.map((e, i) => {
|
|
195
|
+
const kid = Object.hasOwn(witnesses, String(i)) ? witnesses[String(i)] : null;
|
|
196
|
+
// `results` is keyed the way `states` is (`stateKey`), and for a covered entry the result filed
|
|
197
|
+
// there is its own envelope's.
|
|
198
|
+
const key = stateKey(e, i);
|
|
199
|
+
return entryLine(e, [
|
|
200
|
+
["state", kid === null ? PROCESS_ASSERTED : WITNESS_SIGNED],
|
|
201
|
+
["observed", kid !== null && Object.hasOwn(results, key) ? results[key] : null],
|
|
202
|
+
["witness", kid],
|
|
203
|
+
["failed", failed.get(i)?.join(",") ?? null],
|
|
204
|
+
]);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/** A plain ledger's entries. A plain ledger carries no envelopes, so there is no state to print. */
|
|
208
|
+
function ledgerEntryLines(entries, failureEntries, details) {
|
|
209
|
+
const failed = failedByEntry(failureEntries, details);
|
|
210
|
+
return entries.map((e, i) => entryLine(e, [["failed", failed.get(i)?.join(",") ?? null]]));
|
|
211
|
+
}
|
|
212
|
+
/** One JSON object with an `entries` member: what `exportBundle` writes. */
|
|
213
|
+
function isBundleShaped(value) {
|
|
214
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && "entries" in value;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Why a file that is neither a bundle nor a ledger could not be read, in one line.
|
|
218
|
+
*
|
|
219
|
+
* A file that is ONE JSON object with `entries`, read by JavaScript's lenient parser, is a bundle
|
|
220
|
+
* the strict one refused — a duplicate member, a number past the double range, a lone surrogate —
|
|
221
|
+
* and that refusal is the reason. Anything else was read as JSON Lines, and the reason is the first
|
|
222
|
+
* line that does not parse. A parser's message can quote the input, so it goes through `oneLine`.
|
|
223
|
+
*/
|
|
224
|
+
function unparsable(text, bundleError, ledgerError) {
|
|
225
|
+
if (bundleError !== null) {
|
|
226
|
+
let lenient = null;
|
|
227
|
+
try {
|
|
228
|
+
lenient = JSON.parse(text);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
lenient = null;
|
|
232
|
+
}
|
|
233
|
+
if (isBundleShaped(lenient))
|
|
234
|
+
return oneLine(bundleError.message);
|
|
235
|
+
}
|
|
236
|
+
const lines = text.split(/\r?\n/);
|
|
237
|
+
for (let n = 0; n < lines.length; n++) {
|
|
238
|
+
if (lines[n].trim() === "")
|
|
239
|
+
continue;
|
|
240
|
+
try {
|
|
241
|
+
parseJson(lines[n]);
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
return `line ${n + 1}: ${oneLine(err.message)}`;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return oneLine(ledgerError.message);
|
|
248
|
+
}
|
|
249
|
+
function writeEntries(lines) {
|
|
250
|
+
process.stdout.write(`entries:\n${lines.map((line) => `${line}\n`).join("")}`);
|
|
251
|
+
}
|
|
54
252
|
function verify(args) {
|
|
55
253
|
let path = null;
|
|
56
254
|
let keyHex = null;
|
|
57
255
|
let pubHex = null;
|
|
58
256
|
let kid = null;
|
|
59
257
|
let witnessPath = null;
|
|
258
|
+
let listEntries = false;
|
|
60
259
|
for (let i = 0; i < args.length; i++) {
|
|
61
260
|
const a = args[i];
|
|
62
261
|
if (a === "--hs256-key")
|
|
@@ -67,6 +266,8 @@ function verify(args) {
|
|
|
67
266
|
kid = args[++i] ?? null;
|
|
68
267
|
else if (a === "--witness-keys")
|
|
69
268
|
witnessPath = args[++i] ?? null;
|
|
269
|
+
else if (a === "--entries")
|
|
270
|
+
listEntries = true;
|
|
70
271
|
else if (path === null)
|
|
71
272
|
path = a;
|
|
72
273
|
}
|
|
@@ -74,18 +275,28 @@ function verify(args) {
|
|
|
74
275
|
process.stdout.write(USAGE);
|
|
75
276
|
return 1;
|
|
76
277
|
}
|
|
77
|
-
|
|
278
|
+
let text;
|
|
279
|
+
try {
|
|
280
|
+
text = readFileSync(path, "utf8");
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
// A wrong path is a usage error, not a stack trace.
|
|
284
|
+
const reason = readFailure(err);
|
|
285
|
+
if (reason === null)
|
|
286
|
+
throw err;
|
|
287
|
+
process.stdout.write(`cannot read ${path}: ${reason}\n`);
|
|
288
|
+
return 1;
|
|
289
|
+
}
|
|
78
290
|
let bundle = null;
|
|
291
|
+
let bundleError = null;
|
|
79
292
|
try {
|
|
80
293
|
// A bundle is ONE JSON object; a ledger is JSON Lines.
|
|
81
294
|
const parsed = parseBundle(text);
|
|
82
|
-
bundle =
|
|
83
|
-
parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && "entries" in parsed
|
|
84
|
-
? parsed
|
|
85
|
-
: null;
|
|
295
|
+
bundle = isBundleShaped(parsed) ? parsed : null;
|
|
86
296
|
}
|
|
87
|
-
catch {
|
|
297
|
+
catch (err) {
|
|
88
298
|
bundle = null;
|
|
299
|
+
bundleError = err;
|
|
89
300
|
}
|
|
90
301
|
if (bundle !== null) {
|
|
91
302
|
const anchorKid = bundle.anchor?.["kid"] ?? undefined;
|
|
@@ -95,12 +306,34 @@ function verify(args) {
|
|
|
95
306
|
else if (pubHex) {
|
|
96
307
|
signer = new Ed25519Verifier(Buffer.from(pubHex, "hex"), kid ?? anchorKid ?? "k1");
|
|
97
308
|
}
|
|
98
|
-
|
|
309
|
+
let witnessKeys = null;
|
|
310
|
+
if (witnessPath) {
|
|
311
|
+
try {
|
|
312
|
+
witnessKeys = readWitnessKeys(witnessPath);
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
// Read, and not a trust set: one line, naming the file and, for a bad row, the kid.
|
|
316
|
+
if (err instanceof NotATrustSet) {
|
|
317
|
+
process.stdout.write(`cannot use --witness-keys ${witnessPath}: ${err.message}\n`);
|
|
318
|
+
return 2;
|
|
319
|
+
}
|
|
320
|
+
// Unreadable, as for the bundle path above.
|
|
321
|
+
const reason = readFailure(err);
|
|
322
|
+
if (reason === null)
|
|
323
|
+
throw err;
|
|
324
|
+
process.stdout.write(`cannot read ${witnessPath}: ${reason}\n`);
|
|
325
|
+
return 1;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
99
328
|
const rep = verifyBundle(bundle, signer, { witnessKeys });
|
|
100
329
|
const c = rep.checks;
|
|
330
|
+
// An allow marked `policy: "unlisted"` passed through un-gated and is excused from containment,
|
|
331
|
+
// so `actions_checked` does not count it. Say how many there were, only when there were any:
|
|
332
|
+
// every other bundle prints this line exactly as it always has.
|
|
333
|
+
const ungated = rep.ungated > 0 ? ` ungated=${rep.ungated}` : "";
|
|
101
334
|
process.stdout.write(`integrity=${py(c.integrity)} monotonicity=${py(c.monotonicity)} ` +
|
|
102
335
|
`containment=${py(c.containment)} anchor=${c.anchor} ` +
|
|
103
|
-
`nodes=${rep.nodes} actions_checked=${rep.actions_checked}\n`);
|
|
336
|
+
`nodes=${rep.nodes} actions_checked=${rep.actions_checked}${ungated}\n`);
|
|
104
337
|
for (const f of rep.failures)
|
|
105
338
|
process.stdout.write(` - ${f}\n`);
|
|
106
339
|
// A bundle carrying envelopes and no trust set fails every one of them, correctly and
|
|
@@ -110,11 +343,41 @@ function verify(args) {
|
|
|
110
343
|
process.stdout.write("hint: pass --witness-keys FILE to supply the trusted witness keys\n");
|
|
111
344
|
}
|
|
112
345
|
process.stdout.write(rep.ok ? "OK\n" : "FAILED\n");
|
|
346
|
+
if (listEntries)
|
|
347
|
+
writeEntries(bundleEntryLines(bundle.entries ?? [], rep));
|
|
113
348
|
return rep.ok ? 0 : 2;
|
|
114
349
|
}
|
|
115
|
-
|
|
350
|
+
let entries;
|
|
351
|
+
try {
|
|
352
|
+
entries = AuditLog.parseLines(text);
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
// Neither a bundle nor a ledger this build reads: one line, not a stack trace. Exit 1, as the
|
|
356
|
+
// Python CLI exits on a file it cannot parse.
|
|
357
|
+
process.stdout.write(`cannot parse ${path}: ${unparsable(text, bundleError, err)}\n`);
|
|
358
|
+
return 1;
|
|
359
|
+
}
|
|
360
|
+
if (entries.length === 0) {
|
|
361
|
+
// A ledger with no entries has nothing to verify. Reporting it OK would be a fail-open: a
|
|
362
|
+
// truncated or never-written file would pass the same check as a clean chain.
|
|
363
|
+
process.stdout.write(`${EMPTY}\n`);
|
|
364
|
+
if (listEntries)
|
|
365
|
+
writeEntries([]);
|
|
366
|
+
return 2;
|
|
367
|
+
}
|
|
116
368
|
const [ok, reason] = AuditLog.verify(entries);
|
|
117
369
|
process.stdout.write(ok ? "OK\n" : `TAMPERED — ${reason}\n`);
|
|
370
|
+
if (listEntries) {
|
|
371
|
+
// A plain ledger has one check, the hash chain, and its failure is about the entry a bundle's
|
|
372
|
+
// integrity failure is about: the first one the chain does not reproduce at.
|
|
373
|
+
const at = [];
|
|
374
|
+
const details = [];
|
|
375
|
+
if (!ok) {
|
|
376
|
+
at.push(integrityBreak(entries));
|
|
377
|
+
details.push({ reason: "integrity", seq: null, node: null, call_id: null, detail: `integrity: ${reason}` });
|
|
378
|
+
}
|
|
379
|
+
writeEntries(ledgerEntryLines(entries, at, details));
|
|
380
|
+
}
|
|
118
381
|
return ok ? 0 : 2;
|
|
119
382
|
}
|
|
120
383
|
export function main(argv = process.argv.slice(2)) {
|
package/dist/esm/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,QAAQ,EAAoB,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAc,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,WAAW,EACX,QAAQ,EACR,mBAAmB,EACnB,YAAY,GAKb,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAe,MAAM,WAAW,CAAC;AAE1E,6DAA6D;AAC7D,MAAM,KAAK,GAAG,6BAA6B,CAAC;AAE5C,MAAM,KAAK,GAAG;;;;;;;;;;;CAWb,CAAC;AAEF,0FAA0F;AAC1F,MAAM,YAAa,SAAQ,KAAK;CAAG;AAEnC;;;;;;;;;;;;;;GAcG;AACH,SAAS,eAAe,CAAC,IAAY;IACnC,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,+FAA+F;QAC/F,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAChG,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,YAAY,CAAC,4BAA4B,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,cAAc,IAAI,MAAM,EAAE,CAAC;QACxG,MAAM,GAAI,MAAkC,CAAC,cAAc,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,YAAY,CACpB,yGAAyG,CAC1G,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,mBAAmB,CAAC,MAA+B,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,YAAY,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,MAA+B,CAAC;AACzC,CAAC;AAED;;;;;GAKG;AACH,SAAS,WAAW,CAAC,GAAY;IAC/B,MAAM,IAAI,GAAI,GAAiC,EAAE,IAAI,CAAC;IACtD,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAK,GAAa,CAAC,OAAO,CAAC;AAClD,CAAC;AAED,MAAM,QAAQ,GAAqC;IACjD,MAAM,EAAE,2BAA2B;IACnC,MAAM,EAAE,mBAAmB;IAC3B,MAAM,EAAE,gBAAgB;IACxB,OAAO,EAAE,iBAAiB;IAC1B,KAAK,EAAE,mCAAmC;IAC1C,YAAY,EAAE,oBAAoB;IAClC,KAAK,EAAE,yBAAyB;IAChC,MAAM,EAAE,qBAAqB;IAC7B,MAAM,EAAE,kBAAkB;IAC1B,GAAG,EAAE,oBAAoB;CAC1B,CAAC;AAEF,iFAAiF;AACjF,SAAS,EAAE,CAAC,KAAc;IACxB,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;AAClC,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,aAAa,CACpB,cAA0C,EAC1C,OAAiC;IAEjC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC3C,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAClC,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO;QAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAE,CAAC,MAAM,CAAC;QAClC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,UAAU,CAAC,KAAY;IAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAChE,OAAO,WAAW,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,SAAS,CAAC,CAAc,EAAE,IAAuD;IACxF,MAAM,MAAM,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,CAAU,CAAC,EAAE,CAAC,CAAC;IACzE,MAAM,KAAK,GAA6C;QACtD,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;QACrB,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;QACrB,GAAG,IAAI;KACR,CAAC;IACF,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;QACjC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AACjC,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,gBAAgB,CAAC,OAA+B,EAAE,GAAiB;IAC1E,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,eAAe,EAAE,GAAG,CAAC,eAAe,CAAC,CAAC;IACvE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,SAAS,CAAC;IAC7C,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC1B,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/E,gGAAgG;QAChG,+BAA+B;QAC/B,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3B,OAAO,SAAS,CAAC,CAAC,EAAE;YAClB,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,cAAc,CAAC;YAC3D,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAChF,CAAC,SAAS,EAAE,GAAG,CAAC;YAChB,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;SAC7C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,oGAAoG;AACpG,SAAS,gBAAgB,CACvB,OAA+B,EAC/B,cAA0C,EAC1C,OAAiC;IAEjC,MAAM,MAAM,GAAG,aAAa,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IACtD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7F,CAAC;AAED,4EAA4E;AAC5E,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,SAAS,IAAI,KAAK,CAAC;AACpG,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,UAAU,CAAC,IAAY,EAAE,WAAoB,EAAE,WAAoB;IAC1E,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;QACzB,IAAI,OAAO,GAAY,IAAI,CAAC;QAC5B,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;QACD,IAAI,cAAc,CAAC,OAAO,CAAC;YAAE,OAAO,OAAO,CAAE,WAAqB,CAAC,OAAO,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,SAAS;QACtC,IAAI,CAAC;YACH,SAAS,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;QACvB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,QAAQ,CAAC,GAAG,CAAC,KAAK,OAAO,CAAE,GAAa,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7D,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAE,WAAqB,CAAC,OAAO,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,YAAY,CAAC,KAAwB;IAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,MAAM,CAAC,IAAc;IAC5B,IAAI,IAAI,GAAkB,IAAI,CAAC;IAC/B,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,GAAG,GAAkB,IAAI,CAAC;IAC9B,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QACnB,IAAI,CAAC,KAAK,aAAa;YAAE,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC;aAC/C,IAAI,CAAC,KAAK,UAAU;YAAE,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC;aACjD,IAAI,CAAC,KAAK,OAAO;YAAE,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC;aAC3C,IAAI,CAAC,KAAK,gBAAgB;YAAE,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC;aAC5D,IAAI,CAAC,KAAK,WAAW;YAAE,WAAW,GAAG,IAAI,CAAC;aAC1C,IAAI,IAAI,KAAK,IAAI;YAAE,IAAI,GAAG,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,oDAAoD;QACpD,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,MAAM,KAAK,IAAI;YAAE,MAAM,GAAG,CAAC;QAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC;QACzD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,WAAW,GAAY,IAAI,CAAC;IAChC,IAAI,CAAC;QACH,uDAAuD;QACvD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAY,CAAC;QAC5C,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,MAAiB,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,IAAI,CAAC;QACd,WAAW,GAAG,GAAG,CAAC;IACpB,CAAC;IAED,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,SAAS,GAAI,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAwB,IAAI,SAAS,CAAC;QAC9E,IAAI,MAAM,GAAkB,IAAI,CAAC;QACjC,IAAI,MAAM;YAAE,MAAM,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,IAAI,SAAS,IAAI,IAAI,CAAC,CAAC;aAC1F,IAAI,MAAM,EAAE,CAAC;YAChB,MAAM,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,IAAI,SAAS,IAAI,IAAI,CAAC,CAAC;QACrF,CAAC;QACD,IAAI,WAAW,GAAiC,IAAI,CAAC;QACrD,IAAI,WAAW,EAAE,CAAC;YAChB,IAAI,CAAC;gBACH,WAAW,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;YAC7C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,oFAAoF;gBACpF,IAAI,GAAG,YAAY,YAAY,EAAE,CAAC;oBAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,WAAW,KAAK,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;oBACnF,OAAO,CAAC,CAAC;gBACX,CAAC;gBACD,4CAA4C;gBAC5C,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;gBAChC,IAAI,MAAM,KAAK,IAAI;oBAAE,MAAM,GAAG,CAAC;gBAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,WAAW,KAAK,MAAM,IAAI,CAAC,CAAC;gBAChE,OAAO,CAAC,CAAC;YACX,CAAC;QACH,CAAC;QACD,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;QAC1D,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC;QACrB,gGAAgG;QAChG,6FAA6F;QAC7F,gEAAgE;QAChE,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,aAAa,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG;YAChE,eAAe,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG;YACtD,SAAS,GAAG,CAAC,KAAK,oBAAoB,GAAG,CAAC,eAAe,GAAG,OAAO,IAAI,CAC1E,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ;YAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACjE,sFAAsF;QACtF,0FAA0F;QAC1F,8EAA8E;QAC9E,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YAChE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qEAAqE,CAAC,CAAC;QAC9F,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QACnD,IAAI,WAAW;YAAE,YAAY,CAAC,gBAAgB,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;QAC3E,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC;IAED,IAAI,OAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,8FAA8F;QAC9F,8CAA8C;QAC9C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,IAAI,KAAK,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QACtF,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,0FAA0F;QAC1F,8EAA8E;QAC9E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;QACnC,IAAI,WAAW;YAAE,YAAY,CAAC,EAAE,CAAC,CAAC;QAClC,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,MAAM,IAAI,CAAC,CAAC;IAC7D,IAAI,WAAW,EAAE,CAAC;QAChB,8FAA8F;QAC9F,6EAA6E;QAC7E,MAAM,EAAE,GAAsB,EAAE,CAAC;QACjC,MAAM,OAAO,GAAoB,EAAE,CAAC;QACpC,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,MAAM,EAAE,EAAE,CAAC,CAAC;QAC9G,CAAC;QACD,YAAY,CAAC,gBAAgB,CAAC,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,OAAiB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACzD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5B,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;QACvF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC5B,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* display.ts — how a value from a ledger, a bundle or a trust file is printed into a line of text.
|
|
3
|
+
*
|
|
4
|
+
* A ledger, a bundle and the envelopes beside it are untrusted input, and their values reach a
|
|
5
|
+
* terminal through the verifier's finding messages, `attenu-guard verify`, and `--entries`. A value
|
|
6
|
+
* that carries a line break can end the line it is printed on and start a forged one: a node named
|
|
7
|
+
* "n1\nOK" would print a line reading `OK` under a failed verification. Two rules cover every place
|
|
8
|
+
* such a value is printed, and both are the Python implementation's, byte for byte:
|
|
9
|
+
*
|
|
10
|
+
* - a value printed bare goes through `shown`: as its text when that text is printable ASCII
|
|
11
|
+
* 0x21-0x7E other than `"` and `\`, so clean input prints exactly as it always has, and as its
|
|
12
|
+
* `escaped` JSON form otherwise, which holds no whitespace and no line break;
|
|
13
|
+
* - a value printed the way Python's `repr` prints it goes through `pyRepr`, which quotes and
|
|
14
|
+
* escapes a string exactly as `repr` does. Python's `{x!r}` needs no other rule, so neither
|
|
15
|
+
* does this.
|
|
16
|
+
*/
|
|
17
|
+
import { type CJson } from "./canonical.js";
|
|
18
|
+
/** The bare form: printable ASCII 0x21-0x7E without `"` (0x22) or `\` (0x5C). */
|
|
19
|
+
export declare const BARE: RegExp;
|
|
20
|
+
/**
|
|
21
|
+
* Python's `repr` of a `str`: single quotes unless the text holds a `'` and no `"`, the quote and
|
|
22
|
+
* the backslash escaped, `\t` `\n` `\r` by name, any other control character or DEL as `\xHH`, and
|
|
23
|
+
* a non-ASCII character as it is when Python counts it printable, as `\xHH`, `\uHHHH` or
|
|
24
|
+
* `\UHHHHHHHH` when it does not. Lower-case hex throughout, as CPython writes it.
|
|
25
|
+
*/
|
|
26
|
+
export declare function pyStrRepr(s: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* `text` with every character Python's `repr` would escape written as that escape, and nothing
|
|
29
|
+
* else changed: no quotes added, no backslash doubled. For a parser's message, which can quote a
|
|
30
|
+
* fragment of the input it refused: one line, always.
|
|
31
|
+
*/
|
|
32
|
+
export declare function oneLine(text: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Python-style `repr` for the value types a finding message carries. A number that kept its
|
|
35
|
+
* literal prints as Python prints what `json` reads from it (`1` an int, `1.0` a float); a number
|
|
36
|
+
* that has lost its literal prints as JavaScript prints it, as this module always has.
|
|
37
|
+
*/
|
|
38
|
+
export declare function pyRepr(value: CJson): string;
|
|
39
|
+
/**
|
|
40
|
+
* Python's `str()` where a message interpolates a value bare (`{x}`) rather than through `repr`
|
|
41
|
+
* (`{x!r}`): the same rendering for every type except a string, which `str` leaves unquoted.
|
|
42
|
+
*/
|
|
43
|
+
export declare function pyStr(value: CJson): string;
|
|
44
|
+
/**
|
|
45
|
+
* `value` as a finding message prints it bare: its text (Python's `str`) when that text is bare,
|
|
46
|
+
* its `escaped` JSON form otherwise. Clean values print exactly as they always have. An absent
|
|
47
|
+
* value is Python's `None`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function shown(value: CJson | undefined): string;
|
|
50
|
+
/**
|
|
51
|
+
* `shown`, for a caller that already holds the text Python's `str` gives for `value` (a ceiling
|
|
52
|
+
* renders its numbers the way Python renders a float): the text when it is bare, `value`'s
|
|
53
|
+
* `escaped` form otherwise.
|
|
54
|
+
*/
|
|
55
|
+
export declare function shownText(text: string, value: CJson): string;
|
|
56
|
+
/**
|
|
57
|
+
* `value` as whitespace-free, ASCII-only JSON: Python's
|
|
58
|
+
* `json.dumps(value, ensure_ascii=True, separators=(",", ":"))`, with every space then written as
|
|
59
|
+
* `\u0020`. Inside a string that is `\"` and `\\`, the short escapes `\n` `\t` `\r` `\b` `\f`, and
|
|
60
|
+
* a lower-case `\uXXXX` for every other UTF-16 unit outside printable ASCII. That form holds no
|
|
61
|
+
* whitespace and no line break, and a JSON parser gives the value back.
|
|
62
|
+
*/
|
|
63
|
+
export declare function escaped(value: CJson): string;
|
|
64
|
+
/**
|
|
65
|
+
* A number Python's `json` reads as an `int`, in decimal, or `null` for anything else. A parsed
|
|
66
|
+
* bundle keeps every number's literal, so `1` and `1.0` stay as distinct as they are in Python. A
|
|
67
|
+
* number that has lost its literal is read as an integer when it is integral.
|
|
68
|
+
*/
|
|
69
|
+
export declare function integerText(value: CJson): string | null;
|
|
70
|
+
/**
|
|
71
|
+
* Python's `repr` of a float, which `json` writes: the shortest digits that read back as the same
|
|
72
|
+
* number, in positional form from 1e-4 up to below 1e16 with `.0` on an integral value, and in
|
|
73
|
+
* exponent form, with a two-digit exponent at least, outside that range.
|
|
74
|
+
*/
|
|
75
|
+
export declare function pyFloat(n: number): string;
|
|
76
|
+
//# sourceMappingURL=display.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../../src/display.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAa,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEvD,iFAAiF;AACjF,eAAO,MAAM,IAAI,QAAmB,CAAC;AAErC;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAM3C;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI5C;AA0BD;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAe3C;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAE1C;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,GAAG,MAAM,CAGtD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAE5D;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAE5C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI,CAMvD;AAuCD;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAiBzC"}
|