@sanning/proof 0.19.0 → 0.20.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/README.md +30 -29
- package/dist/agent-record.d.ts +62 -0
- package/dist/agent-record.js +527 -0
- package/dist/agent-schemas.generated.d.ts +15 -0
- package/dist/agent-schemas.generated.js +5759 -0
- package/dist/case-file.d.ts +32 -89
- package/dist/case-file.js +41 -93
- package/dist/case.d.ts +343 -810
- package/dist/case.js +693 -1151
- package/dist/chain.d.ts +19 -47
- package/dist/chain.js +78 -205
- package/dist/claims.d.ts +211 -0
- package/dist/claims.js +1081 -0
- package/dist/cli.d.ts +45 -68
- package/dist/cli.js +618 -1022
- package/dist/compose.js +23 -65
- package/dist/crypto.d.ts +7 -8
- package/dist/crypto.js +60 -147
- package/dist/declaration.d.ts +68 -194
- package/dist/declaration.js +175 -518
- package/dist/environment.d.ts +9 -30
- package/dist/environment.js +17 -66
- package/dist/evidence.d.ts +119 -160
- package/dist/evidence.js +744 -1103
- package/dist/exit-codes.d.ts +11 -27
- package/dist/exit-codes.js +24 -59
- package/dist/file-match.d.ts +33 -79
- package/dist/file-match.js +33 -69
- package/dist/grading.d.ts +98 -135
- package/dist/grading.js +224 -380
- package/dist/handoff.d.ts +62 -160
- package/dist/handoff.js +153 -393
- package/dist/index.d.ts +14 -10
- package/dist/index.js +60 -167
- package/dist/json-schema.d.ts +29 -0
- package/dist/json-schema.js +298 -0
- package/dist/merkle.js +16 -28
- package/dist/message-parts.d.ts +56 -212
- package/dist/message-parts.js +142 -502
- package/dist/side-content.d.ts +11 -22
- package/dist/side-content.js +22 -65
- package/dist/types.d.ts +17 -0
- package/dist/types.js +19 -5
- package/dist/verifier.d.ts +40 -114
- package/dist/verifier.js +196 -535
- package/dist/witness-cache.d.ts +24 -45
- package/dist/witness-cache.js +38 -81
- package/dist/witness-lookup.d.ts +36 -57
- package/dist/witness-lookup.js +89 -139
- package/dist/zip-reader.d.ts +18 -33
- package/dist/zip-reader.js +19 -34
- package/package.json +11 -3
- package/src/agent-record.ts +608 -0
- package/src/agent-schemas.generated.ts +5764 -0
- package/src/case-file.ts +45 -109
- package/src/case.ts +848 -1322
- package/src/chain.ts +85 -224
- package/src/claims.ts +1253 -0
- package/src/cli.ts +714 -1176
- package/src/compose.ts +33 -79
- package/src/crypto.ts +60 -147
- package/src/declaration.ts +219 -656
- package/src/environment.ts +17 -65
- package/src/evidence.ts +958 -1381
- package/src/exit-codes.ts +24 -63
- package/src/file-match.ts +40 -89
- package/src/grading.ts +287 -421
- package/src/handoff.ts +175 -427
- package/src/index.ts +132 -177
- package/src/json-schema.ts +295 -0
- package/src/merkle.ts +16 -28
- package/src/message-parts.ts +155 -533
- package/src/side-content.ts +25 -68
- package/src/types.ts +55 -36
- package/src/verifier.ts +202 -526
- package/src/witness-cache.ts +45 -94
- package/src/witness-lookup.ts +107 -177
- package/src/zip-reader.ts +20 -38
- package/vectors/README.md +52 -34
- package/vectors/agent-record.json +2175 -0
- package/vectors/case-file-index.json +1 -1
- package/vectors/case-split.json +2 -2
- package/vectors/chain-reading.json +3 -3
- package/vectors/file-match.json +285 -148
- package/vectors/jcs-reader.json +6 -6
- package/vectors/pack-grading.json +102 -25
- package/vectors/sandbox-evidence.json +597 -580
- package/vectors/stamp-on-chain.json +18 -18
- package/vectors/witness-cache.json +49 -49
- package/dist/agent-proof.d.ts +0 -28
- package/dist/agent-proof.js +0 -226
- package/dist/run-adapters.d.ts +0 -10
- package/dist/run-adapters.js +0 -35
- package/dist/run.d.ts +0 -209
- package/dist/run.js +0 -1405
- package/src/agent-proof.ts +0 -264
- package/src/run-adapters.ts +0 -44
- package/src/run.ts +0 -1598
package/dist/case-file.d.ts
CHANGED
|
@@ -1,30 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* A case file's index compared with the drop it describes (evidence-bundle §5.6).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* A case file is a ZIP of packs, each under `packs/<ref>/`, plus an index
|
|
5
|
+
* (`contents.json`), `HOW-TO-VERIFY.txt` and an optional `report/` of files no
|
|
6
|
+
* record commits to. Nobody signs it: each pack is byte for byte what its signer
|
|
7
|
+
* produced, and a reader reads the packs, never the file.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* the
|
|
12
|
-
* `report/` of files no record commits to. **Nobody signs it.** Each pack in it
|
|
13
|
-
* is byte for byte the pack its signer produced, and a reader reads the packs,
|
|
14
|
-
* never the file.
|
|
9
|
+
* Trust boundary: the index is never an input to the case reading (`readCase`).
|
|
10
|
+
* Adding, removing or editing it moves no verdict, exception, note or exit code.
|
|
11
|
+
* A lie in the index is a finding here and nothing more.
|
|
15
12
|
*
|
|
16
|
-
*
|
|
17
|
-
* the files the reader was handed and returns findings with NO verdict
|
|
18
|
-
* contribution. **The index is never an input to the case reading**
|
|
19
|
-
* (`readCase`): adding, removing or editing `contents.json` moves no verdict,
|
|
20
|
-
* no exception, no note, no contribution and no exit code. An assembler can lie
|
|
21
|
-
* in every member of the index, and each lie is a finding here and nothing
|
|
22
|
-
* more; it can leave the index out, which is no finding at all.
|
|
23
|
-
*
|
|
24
|
-
* Pure: bytes and a file list in, a reading out. No filesystem, no zip, no
|
|
25
|
-
* network. How the files were listed and hashed is the caller's.
|
|
13
|
+
* Pure: bytes and a file list in, a reading out. No filesystem, zip or network.
|
|
26
14
|
*/
|
|
27
|
-
/** The index's dispatch key (5.6.2)
|
|
15
|
+
/** The index's dispatch key (5.6.2); an absent or unknown value makes the index unreadable. */
|
|
28
16
|
export declare const CASE_FILE_VERSION = "sanning.case-file/v1";
|
|
29
17
|
/** The index, at the root of the case file. */
|
|
30
18
|
export declare const CASE_FILE_INDEX_NAME = "contents.json";
|
|
@@ -32,32 +20,16 @@ export declare const CASE_FILE_INDEX_NAME = "contents.json";
|
|
|
32
20
|
export declare const HOW_TO_VERIFY_NAME = "HOW-TO-VERIFY.txt";
|
|
33
21
|
/** The first line of `report/README.txt`, exactly (5.6.1). */
|
|
34
22
|
export declare const REPORT_README_FIRST_LINE = "Not evidence: no record in this case file commits to the files in this folder.";
|
|
35
|
-
/**
|
|
36
|
-
* How deep `[` and `{` may nest in the index before it is refused unread. The
|
|
37
|
-
* same limit `bundle.json` is read under, so a hostile index cannot make one
|
|
38
|
-
* kernel's parser recurse where the other's does not.
|
|
39
|
-
*/
|
|
23
|
+
/** How deep `[` and `{` may nest in the index before it is refused unread (the limit `bundle.json` is read under). */
|
|
40
24
|
export declare const MAX_INDEX_DEPTH = 512;
|
|
41
|
-
/**
|
|
42
|
-
* Every finding this comparison can report, in the order it reports them
|
|
43
|
-
* (5.6.3). None of them is a verdict word and none contributes to one.
|
|
44
|
-
*/
|
|
25
|
+
/** Every finding this comparison can report, in report order (5.6.3). None is a verdict word. */
|
|
45
26
|
export declare const CASE_FILE_FINDING_KINDS: readonly ["case_index_unreadable", "case_index_pack_missing", "case_index_pack_unlisted", "case_index_file_missing", "case_index_file_unlisted", "case_index_file_differs", "case_index_file_too_large", "case_index_names_absent"];
|
|
46
27
|
export type CaseFileFindingKind = (typeof CASE_FILE_FINDING_KINDS)[number];
|
|
47
|
-
/**
|
|
48
|
-
* Whether `ref` is a legal pack folder name in a case file. For a producer; a
|
|
49
|
-
* reader takes whatever folders it finds and names them.
|
|
50
|
-
*/
|
|
28
|
+
/** Whether `ref` is a legal pack folder name. For producers; a reader takes whatever folders it finds. */
|
|
51
29
|
export declare function isPackRef(ref: unknown): boolean;
|
|
52
|
-
/**
|
|
53
|
-
* `HOW-TO-VERIFY.txt`, exactly (5.6.4): ASCII, LF line endings, with `name` the
|
|
54
|
-
* case file's name without `.zip`.
|
|
55
|
-
*/
|
|
30
|
+
/** `HOW-TO-VERIFY.txt`, exactly (5.6.4): ASCII, LF endings; `name` is the case file's name without `.zip`. */
|
|
56
31
|
export declare function howToVerifyText(name: string): string;
|
|
57
|
-
/**
|
|
58
|
-
* One way the index and the drop disagree, as data. What the index SAYS
|
|
59
|
-
* against what the folder HOLDS, and never a statement about any party.
|
|
60
|
-
*/
|
|
32
|
+
/** One way the index and the folder disagree: what the index says against what the folder holds, never a statement about a party. */
|
|
61
33
|
export interface CaseFileFinding {
|
|
62
34
|
kind: CaseFileFindingKind;
|
|
63
35
|
/** The pack folder name, for the two pack kinds. */
|
|
@@ -68,79 +40,50 @@ export interface CaseFileFinding {
|
|
|
68
40
|
listedSha256?: string;
|
|
69
41
|
/** What the file in the folder hashes to, for `case_index_file_differs`. */
|
|
70
42
|
sha256?: string;
|
|
71
|
-
/**
|
|
72
|
-
* Why the index is unreadable, or the assembler's reason a pack is absent,
|
|
73
|
-
* for `case_index_unreadable` and `case_index_names_absent`.
|
|
74
|
-
*/
|
|
43
|
+
/** Why the index is unreadable, or the assembler's reason a pack is absent. */
|
|
75
44
|
reason?: string;
|
|
76
|
-
/**
|
|
77
|
-
* The key the assembler says the absent pack would be signed by, as the index
|
|
78
|
-
* writes it, for `case_index_names_absent`.
|
|
79
|
-
*/
|
|
45
|
+
/** The key the assembler says the absent pack is signed by, for `case_index_names_absent`. */
|
|
80
46
|
publicKey?: string;
|
|
81
47
|
}
|
|
82
48
|
export interface CaseFileIndexReading {
|
|
83
49
|
/** `absent` (no index was handed in), `unreadable` or `read`. */
|
|
84
50
|
index: "absent" | "unreadable" | "read";
|
|
85
|
-
/**
|
|
86
|
-
* The index's `case_id`, a label the assembler wrote and nothing is compared
|
|
87
|
-
* with, or `null` where the index was not read.
|
|
88
|
-
*/
|
|
51
|
+
/** The index's `case_id`, a label nothing is compared with; `null` where the index was not read. */
|
|
89
52
|
caseId: string | null;
|
|
90
53
|
findings: CaseFileFinding[];
|
|
91
54
|
}
|
|
92
55
|
/**
|
|
93
56
|
* Whether `[` and `{` outside strings ever nest deeper than `limit`. One pass,
|
|
94
|
-
* no recursion
|
|
95
|
-
*
|
|
96
|
-
* character of that code, which is the same answer for UTF-8: the six
|
|
97
|
-
* characters it reads are single bytes and no multi-byte sequence holds one.
|
|
98
|
-
* The CLI's `bundle.json` reader uses it too.
|
|
57
|
+
* no recursion. Bytes are read as character codes, which is exact for UTF-8
|
|
58
|
+
* because the six characters it reads never occur inside a multi-byte sequence.
|
|
99
59
|
*/
|
|
100
60
|
export declare function jsonDepthExceeds(text: string | Uint8Array, limit: number): boolean;
|
|
101
|
-
/**
|
|
102
|
-
* The reading of an index the caller could not hold at all (it is over the
|
|
103
|
-
* run's whole-read total, or it cannot be read): one `case_index_unreadable`
|
|
104
|
-
* finding with the caller's `reason`, and no comparison. The index is never an
|
|
105
|
-
* input to the verdict, so a file that cannot be read keeps nothing from it.
|
|
106
|
-
*/
|
|
61
|
+
/** The reading of an index the caller could not hold (over the run's read total, or unreadable): one `case_index_unreadable` finding, no comparison. */
|
|
107
62
|
export declare function unreadableCaseFileIndex(reason: string): CaseFileIndexReading;
|
|
108
63
|
/**
|
|
109
64
|
* Every path the index lists (each pack's files and `report`), or `null` where
|
|
110
|
-
* the index is unreadable. A caller
|
|
111
|
-
*
|
|
112
|
-
* cannot turn a real mismatch into `too_large`.
|
|
65
|
+
* the index is unreadable. A caller with a hashing budget charges these first,
|
|
66
|
+
* so an unlisted file cannot turn a real mismatch into `too_large`.
|
|
113
67
|
*/
|
|
114
68
|
export declare function caseFileListedPaths(index: Uint8Array): ReadonlySet<string> | null;
|
|
115
69
|
/**
|
|
116
70
|
* Compare a case file's index with the folder it sits at the root of.
|
|
117
71
|
*
|
|
118
72
|
* `index` is the bytes of `contents.json`, or `null` where the folder holds
|
|
119
|
-
* none. `files` is every regular file
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* `case_index_file_too_large` where
|
|
123
|
-
*
|
|
124
|
-
* `packs/` and `report/` are compared, so a caller may list the rest or not. A
|
|
125
|
-
* folder with no index is a folder of packs, with no finding.
|
|
126
|
-
*
|
|
127
|
-
* Findings come in the order of `CASE_FILE_FINDING_KINDS`, and within a kind in
|
|
128
|
-
* the order of their `ref` or `path` by UTF-8 bytes (the order a caller charges
|
|
129
|
-
* its hashing budget in is another, UTF-16 code units: evidence-bundle 5.6.3);
|
|
130
|
-
* `case_index_names_absent`
|
|
131
|
-
* is in the index's own order, one per `absent[]` entry. An unreadable index is
|
|
132
|
-
* one finding and no other comparison runs.
|
|
73
|
+
* none (a folder of packs, no finding). `files` is every regular file, as
|
|
74
|
+
* `{ path, sha256 }` with a `/`-separated path from the root, or
|
|
75
|
+
* `{ path, too_large: true }` for a file left unhashed over the caller's cap
|
|
76
|
+
* (reported as `case_index_file_too_large` where listed). Only files under
|
|
77
|
+
* `packs/` and `report/` are compared.
|
|
133
78
|
*
|
|
134
|
-
*
|
|
79
|
+
* Findings follow `CASE_FILE_FINDING_KINDS`, and within a kind sort by UTF-8
|
|
80
|
+
* bytes of `ref` or `path` (5.6.3); `case_index_names_absent` keeps the index's
|
|
81
|
+
* order. An unreadable index is one finding and no other comparison runs.
|
|
135
82
|
*/
|
|
136
83
|
export declare function readCaseFileIndex(index: Uint8Array | null, files: readonly {
|
|
137
84
|
path: string;
|
|
138
85
|
sha256?: string;
|
|
139
86
|
too_large?: boolean;
|
|
140
87
|
}[]): CaseFileIndexReading;
|
|
141
|
-
/**
|
|
142
|
-
* The house wording for one finding: what the index says against what the
|
|
143
|
-
* folder holds, and the assembler's own statement for an absent pack. Never a
|
|
144
|
-
* sentence about a party. Both kernels return this text.
|
|
145
|
-
*/
|
|
88
|
+
/** The house wording for one finding. Never a sentence about a party. */
|
|
146
89
|
export declare function renderCaseFileFinding(finding: CaseFileFinding): string;
|
package/dist/case-file.js
CHANGED
|
@@ -1,31 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* A case file's index compared with the drop it describes (evidence-bundle §5.6).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* A case file is a ZIP of packs, each under `packs/<ref>/`, plus an index
|
|
5
|
+
* (`contents.json`), `HOW-TO-VERIFY.txt` and an optional `report/` of files no
|
|
6
|
+
* record commits to. Nobody signs it: each pack is byte for byte what its signer
|
|
7
|
+
* produced, and a reader reads the packs, never the file.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* the
|
|
12
|
-
* `report/` of files no record commits to. **Nobody signs it.** Each pack in it
|
|
13
|
-
* is byte for byte the pack its signer produced, and a reader reads the packs,
|
|
14
|
-
* never the file.
|
|
9
|
+
* Trust boundary: the index is never an input to the case reading (`readCase`).
|
|
10
|
+
* Adding, removing or editing it moves no verdict, exception, note or exit code.
|
|
11
|
+
* A lie in the index is a finding here and nothing more.
|
|
15
12
|
*
|
|
16
|
-
*
|
|
17
|
-
* the files the reader was handed and returns findings with NO verdict
|
|
18
|
-
* contribution. **The index is never an input to the case reading**
|
|
19
|
-
* (`readCase`): adding, removing or editing `contents.json` moves no verdict,
|
|
20
|
-
* no exception, no note, no contribution and no exit code. An assembler can lie
|
|
21
|
-
* in every member of the index, and each lie is a finding here and nothing
|
|
22
|
-
* more; it can leave the index out, which is no finding at all.
|
|
23
|
-
*
|
|
24
|
-
* Pure: bytes and a file list in, a reading out. No filesystem, no zip, no
|
|
25
|
-
* network. How the files were listed and hashed is the caller's.
|
|
13
|
+
* Pure: bytes and a file list in, a reading out. No filesystem, zip or network.
|
|
26
14
|
*/
|
|
27
15
|
import { DuplicateKeyError, jcsParse } from "./verifier.js";
|
|
28
|
-
/** The index's dispatch key (5.6.2)
|
|
16
|
+
/** The index's dispatch key (5.6.2); an absent or unknown value makes the index unreadable. */
|
|
29
17
|
export const CASE_FILE_VERSION = "sanning.case-file/v1";
|
|
30
18
|
/** The index, at the root of the case file. */
|
|
31
19
|
export const CASE_FILE_INDEX_NAME = "contents.json";
|
|
@@ -33,16 +21,9 @@ export const CASE_FILE_INDEX_NAME = "contents.json";
|
|
|
33
21
|
export const HOW_TO_VERIFY_NAME = "HOW-TO-VERIFY.txt";
|
|
34
22
|
/** The first line of `report/README.txt`, exactly (5.6.1). */
|
|
35
23
|
export const REPORT_README_FIRST_LINE = "Not evidence: no record in this case file commits to the files in this folder.";
|
|
36
|
-
/**
|
|
37
|
-
* How deep `[` and `{` may nest in the index before it is refused unread. The
|
|
38
|
-
* same limit `bundle.json` is read under, so a hostile index cannot make one
|
|
39
|
-
* kernel's parser recurse where the other's does not.
|
|
40
|
-
*/
|
|
24
|
+
/** How deep `[` and `{` may nest in the index before it is refused unread (the limit `bundle.json` is read under). */
|
|
41
25
|
export const MAX_INDEX_DEPTH = 512;
|
|
42
|
-
/**
|
|
43
|
-
* Every finding this comparison can report, in the order it reports them
|
|
44
|
-
* (5.6.3). None of them is a verdict word and none contributes to one.
|
|
45
|
-
*/
|
|
26
|
+
/** Every finding this comparison can report, in report order (5.6.3). None is a verdict word. */
|
|
46
27
|
export const CASE_FILE_FINDING_KINDS = [
|
|
47
28
|
"case_index_unreadable",
|
|
48
29
|
"case_index_pack_missing",
|
|
@@ -53,21 +34,13 @@ export const CASE_FILE_FINDING_KINDS = [
|
|
|
53
34
|
"case_index_file_too_large",
|
|
54
35
|
"case_index_names_absent",
|
|
55
36
|
];
|
|
56
|
-
//
|
|
57
|
-
// `.` or `..` (5.6.1). The class holds no newline, so `$` cannot match before
|
|
58
|
-
// one here.
|
|
37
|
+
// 1 to 128 of A-Z a-z 0-9 `.` `_` `-`, not `.` or `..` (5.6.1). No newline in the class, so `$` is safe.
|
|
59
38
|
const REF = /^[A-Za-z0-9._-]{1,128}$/;
|
|
60
|
-
/**
|
|
61
|
-
* Whether `ref` is a legal pack folder name in a case file. For a producer; a
|
|
62
|
-
* reader takes whatever folders it finds and names them.
|
|
63
|
-
*/
|
|
39
|
+
/** Whether `ref` is a legal pack folder name. For producers; a reader takes whatever folders it finds. */
|
|
64
40
|
export function isPackRef(ref) {
|
|
65
41
|
return typeof ref === "string" && REF.test(ref) && ref !== "." && ref !== "..";
|
|
66
42
|
}
|
|
67
|
-
/**
|
|
68
|
-
* `HOW-TO-VERIFY.txt`, exactly (5.6.4): ASCII, LF line endings, with `name` the
|
|
69
|
-
* case file's name without `.zip`.
|
|
70
|
-
*/
|
|
43
|
+
/** `HOW-TO-VERIFY.txt`, exactly (5.6.4): ASCII, LF endings; `name` is the case file's name without `.zip`. */
|
|
71
44
|
export function howToVerifyText(name) {
|
|
72
45
|
return ("This case file holds evidence packs. Each pack is signed by its own key.\n" +
|
|
73
46
|
"Nobody signed this file, contents.json, or anything under report/.\n" +
|
|
@@ -81,21 +54,18 @@ export function howToVerifyText(name) {
|
|
|
81
54
|
"\n" +
|
|
82
55
|
` sanning-proof case ${name}\n` +
|
|
83
56
|
"\n" +
|
|
84
|
-
"Exit codes: 0 verified, 1 failed, 2 not
|
|
85
|
-
"3
|
|
86
|
-
"
|
|
57
|
+
"Exit codes: 0 verified, 1 failed, 2 incomplete (not everything could be\n" +
|
|
58
|
+
"checked), 3 verified sandbox evidence (records signed environment dev),\n" +
|
|
59
|
+
"4 could not run (a bad flag, or an input that is not a case).\n" +
|
|
87
60
|
"\n" +
|
|
88
61
|
"To also look each witness up on the public record, add --gateways <url,url>.\n" +
|
|
89
|
-
"The lookup is reported on
|
|
90
|
-
"
|
|
62
|
+
"The lookup is reported on the witness line. A lookup that does not settle\n" +
|
|
63
|
+
"changes nothing; a witness that does not match fails the case, exit 1.\n");
|
|
91
64
|
}
|
|
92
65
|
/**
|
|
93
66
|
* Whether `[` and `{` outside strings ever nest deeper than `limit`. One pass,
|
|
94
|
-
* no recursion
|
|
95
|
-
*
|
|
96
|
-
* character of that code, which is the same answer for UTF-8: the six
|
|
97
|
-
* characters it reads are single bytes and no multi-byte sequence holds one.
|
|
98
|
-
* The CLI's `bundle.json` reader uses it too.
|
|
67
|
+
* no recursion. Bytes are read as character codes, which is exact for UTF-8
|
|
68
|
+
* because the six characters it reads never occur inside a multi-byte sequence.
|
|
99
69
|
*/
|
|
100
70
|
export function jsonDepthExceeds(text, limit) {
|
|
101
71
|
let depth = 0;
|
|
@@ -164,13 +134,12 @@ function fileList(value, where) {
|
|
|
164
134
|
return out;
|
|
165
135
|
}
|
|
166
136
|
/**
|
|
167
|
-
* The index, read under the JSON rule 5.4.2
|
|
168
|
-
*
|
|
169
|
-
*
|
|
137
|
+
* The index, read under the JSON rule of 5.4.2 and checked for every required
|
|
138
|
+
* member and its type. JCS is not required and unknown members are ignored
|
|
139
|
+
* (5.6.2). The first problem found is the reason.
|
|
170
140
|
*/
|
|
171
141
|
function readIndex(raw) {
|
|
172
|
-
// Scanned as
|
|
173
|
-
// in the same order whatever it holds.
|
|
142
|
+
// Scanned as bytes, before any decode.
|
|
174
143
|
if (jsonDepthExceeds(raw, MAX_INDEX_DEPTH)) {
|
|
175
144
|
throw new Unreadable(`nests deeper than ${MAX_INDEX_DEPTH} levels`);
|
|
176
145
|
}
|
|
@@ -179,8 +148,7 @@ function readIndex(raw) {
|
|
|
179
148
|
value = jcsParse(raw);
|
|
180
149
|
}
|
|
181
150
|
catch (e) {
|
|
182
|
-
// Named
|
|
183
|
-
// reads as whichever copy came last (I-JSON, proof#144).
|
|
151
|
+
// Named: a duplicated member name would otherwise read as whichever copy came last (I-JSON).
|
|
184
152
|
if (e instanceof DuplicateKeyError)
|
|
185
153
|
throw new Unreadable(e.message);
|
|
186
154
|
throw new Unreadable("not readable as JSON");
|
|
@@ -220,12 +188,7 @@ function readIndex(raw) {
|
|
|
220
188
|
const report = fileList(own(value, "report"), "report");
|
|
221
189
|
return { caseId, packs: listedPacks, absent: listedAbsent, report };
|
|
222
190
|
}
|
|
223
|
-
/**
|
|
224
|
-
* The reading of an index the caller could not hold at all (it is over the
|
|
225
|
-
* run's whole-read total, or it cannot be read): one `case_index_unreadable`
|
|
226
|
-
* finding with the caller's `reason`, and no comparison. The index is never an
|
|
227
|
-
* input to the verdict, so a file that cannot be read keeps nothing from it.
|
|
228
|
-
*/
|
|
191
|
+
/** The reading of an index the caller could not hold (over the run's read total, or unreadable): one `case_index_unreadable` finding, no comparison. */
|
|
229
192
|
export function unreadableCaseFileIndex(reason) {
|
|
230
193
|
return {
|
|
231
194
|
index: "unreadable",
|
|
@@ -235,9 +198,8 @@ export function unreadableCaseFileIndex(reason) {
|
|
|
235
198
|
}
|
|
236
199
|
/**
|
|
237
200
|
* Every path the index lists (each pack's files and `report`), or `null` where
|
|
238
|
-
* the index is unreadable. A caller
|
|
239
|
-
*
|
|
240
|
-
* cannot turn a real mismatch into `too_large`.
|
|
201
|
+
* the index is unreadable. A caller with a hashing budget charges these first,
|
|
202
|
+
* so an unlisted file cannot turn a real mismatch into `too_large`.
|
|
241
203
|
*/
|
|
242
204
|
export function caseFileListedPaths(index) {
|
|
243
205
|
let read;
|
|
@@ -261,22 +223,15 @@ export function caseFileListedPaths(index) {
|
|
|
261
223
|
* Compare a case file's index with the folder it sits at the root of.
|
|
262
224
|
*
|
|
263
225
|
* `index` is the bytes of `contents.json`, or `null` where the folder holds
|
|
264
|
-
* none. `files` is every regular file
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
* `case_index_file_too_large` where
|
|
268
|
-
*
|
|
269
|
-
* `packs/` and `report/` are compared, so a caller may list the rest or not. A
|
|
270
|
-
* folder with no index is a folder of packs, with no finding.
|
|
226
|
+
* none (a folder of packs, no finding). `files` is every regular file, as
|
|
227
|
+
* `{ path, sha256 }` with a `/`-separated path from the root, or
|
|
228
|
+
* `{ path, too_large: true }` for a file left unhashed over the caller's cap
|
|
229
|
+
* (reported as `case_index_file_too_large` where listed). Only files under
|
|
230
|
+
* `packs/` and `report/` are compared.
|
|
271
231
|
*
|
|
272
|
-
* Findings
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
* `case_index_names_absent`
|
|
276
|
-
* is in the index's own order, one per `absent[]` entry. An unreadable index is
|
|
277
|
-
* one finding and no other comparison runs.
|
|
278
|
-
*
|
|
279
|
-
* Nothing here is a verdict, and nothing here is ever an input to `readCase`.
|
|
232
|
+
* Findings follow `CASE_FILE_FINDING_KINDS`, and within a kind sort by UTF-8
|
|
233
|
+
* bytes of `ref` or `path` (5.6.3); `case_index_names_absent` keeps the index's
|
|
234
|
+
* order. An unreadable index is one finding and no other comparison runs.
|
|
280
235
|
*/
|
|
281
236
|
export function readCaseFileIndex(index, files) {
|
|
282
237
|
if (index === null || index === undefined) {
|
|
@@ -295,9 +250,7 @@ export function readCaseFileIndex(index, files) {
|
|
|
295
250
|
findings: [{ kind: "case_index_unreadable", reason: e.reason }],
|
|
296
251
|
};
|
|
297
252
|
}
|
|
298
|
-
// `null
|
|
299
|
-
// `{ path, too_large: true }`): neither a match nor absent, reported as
|
|
300
|
-
// neither.
|
|
253
|
+
// `null`: present but too large to hash; neither a match nor absent.
|
|
301
254
|
const here = new Map();
|
|
302
255
|
for (const entry of files) {
|
|
303
256
|
const path = isObject(entry) ? own(entry, "path") : undefined;
|
|
@@ -311,8 +264,7 @@ export function readCaseFileIndex(index, files) {
|
|
|
311
264
|
here.set(path, null);
|
|
312
265
|
}
|
|
313
266
|
const listedRefs = new Set(read.packs.map(([ref]) => ref));
|
|
314
|
-
// The first listing of a path wins
|
|
315
|
-
// pack's files in order, then report/. A path listed twice is compared once.
|
|
267
|
+
// The first listing of a path wins (packs in order, then report/).
|
|
316
268
|
const listed = new Map();
|
|
317
269
|
for (const [, packFiles] of read.packs) {
|
|
318
270
|
for (const [path, digest] of packFiles)
|
|
@@ -376,11 +328,7 @@ export function readCaseFileIndex(index, files) {
|
|
|
376
328
|
}
|
|
377
329
|
return { index: "read", caseId: read.caseId, findings };
|
|
378
330
|
}
|
|
379
|
-
/**
|
|
380
|
-
* The house wording for one finding: what the index says against what the
|
|
381
|
-
* folder holds, and the assembler's own statement for an absent pack. Never a
|
|
382
|
-
* sentence about a party. Both kernels return this text.
|
|
383
|
-
*/
|
|
331
|
+
/** The house wording for one finding. Never a sentence about a party. */
|
|
384
332
|
export function renderCaseFileFinding(finding) {
|
|
385
333
|
switch (finding.kind) {
|
|
386
334
|
case "case_index_unreadable":
|