@mikeargento/bitgraph 1.1.0 → 1.2.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.
Files changed (195) hide show
  1. package/README.md +49 -40
  2. package/dist/__tests__/audit-anchors.test.d.ts +2 -0
  3. package/dist/__tests__/audit-anchors.test.d.ts.map +1 -0
  4. package/dist/__tests__/audit-anchors.test.js +152 -0
  5. package/dist/__tests__/audit-anchors.test.js.map +1 -0
  6. package/dist/__tests__/audit-anomalies.test.d.ts +2 -0
  7. package/dist/__tests__/audit-anomalies.test.d.ts.map +1 -0
  8. package/dist/__tests__/audit-anomalies.test.js +663 -0
  9. package/dist/__tests__/audit-anomalies.test.js.map +1 -0
  10. package/dist/__tests__/audit-attestation.test.d.ts +2 -0
  11. package/dist/__tests__/audit-attestation.test.d.ts.map +1 -0
  12. package/dist/__tests__/audit-attestation.test.js +403 -0
  13. package/dist/__tests__/audit-attestation.test.js.map +1 -0
  14. package/dist/__tests__/audit-bench-smoke.test.d.ts +2 -0
  15. package/dist/__tests__/audit-bench-smoke.test.d.ts.map +1 -0
  16. package/dist/__tests__/audit-bench-smoke.test.js +87 -0
  17. package/dist/__tests__/audit-bench-smoke.test.js.map +1 -0
  18. package/dist/__tests__/audit-boundary.test.d.ts +20 -0
  19. package/dist/__tests__/audit-boundary.test.d.ts.map +1 -0
  20. package/dist/__tests__/audit-boundary.test.js +84 -0
  21. package/dist/__tests__/audit-boundary.test.js.map +1 -0
  22. package/dist/__tests__/audit-cli.test.d.ts +2 -0
  23. package/dist/__tests__/audit-cli.test.d.ts.map +1 -0
  24. package/dist/__tests__/audit-cli.test.js +239 -0
  25. package/dist/__tests__/audit-cli.test.js.map +1 -0
  26. package/dist/__tests__/audit-dependencies.test.d.ts +2 -0
  27. package/dist/__tests__/audit-dependencies.test.d.ts.map +1 -0
  28. package/dist/__tests__/audit-dependencies.test.js +116 -0
  29. package/dist/__tests__/audit-dependencies.test.js.map +1 -0
  30. package/dist/__tests__/audit-fixtures.d.ts +265 -0
  31. package/dist/__tests__/audit-fixtures.d.ts.map +1 -0
  32. package/dist/__tests__/audit-fixtures.js +511 -0
  33. package/dist/__tests__/audit-fixtures.js.map +1 -0
  34. package/dist/__tests__/audit-ingest.test.d.ts +2 -0
  35. package/dist/__tests__/audit-ingest.test.d.ts.map +1 -0
  36. package/dist/__tests__/audit-ingest.test.js +473 -0
  37. package/dist/__tests__/audit-ingest.test.js.map +1 -0
  38. package/dist/__tests__/audit-memory-ingest.test.d.ts +12 -0
  39. package/dist/__tests__/audit-memory-ingest.test.d.ts.map +1 -0
  40. package/dist/__tests__/audit-memory-ingest.test.js +120 -0
  41. package/dist/__tests__/audit-memory-ingest.test.js.map +1 -0
  42. package/dist/__tests__/audit-property.test.d.ts +2 -0
  43. package/dist/__tests__/audit-property.test.d.ts.map +1 -0
  44. package/dist/__tests__/audit-property.test.js +771 -0
  45. package/dist/__tests__/audit-property.test.js.map +1 -0
  46. package/dist/__tests__/audit-real-fixtures.test.d.ts +2 -0
  47. package/dist/__tests__/audit-real-fixtures.test.d.ts.map +1 -0
  48. package/dist/__tests__/audit-real-fixtures.test.js +132 -0
  49. package/dist/__tests__/audit-real-fixtures.test.js.map +1 -0
  50. package/dist/__tests__/audit-real-live.test.d.ts +2 -0
  51. package/dist/__tests__/audit-real-live.test.d.ts.map +1 -0
  52. package/dist/__tests__/audit-real-live.test.js +107 -0
  53. package/dist/__tests__/audit-real-live.test.js.map +1 -0
  54. package/dist/__tests__/audit-reconstruct.test.d.ts +2 -0
  55. package/dist/__tests__/audit-reconstruct.test.d.ts.map +1 -0
  56. package/dist/__tests__/audit-reconstruct.test.js +287 -0
  57. package/dist/__tests__/audit-reconstruct.test.js.map +1 -0
  58. package/dist/__tests__/audit-report.test.d.ts +2 -0
  59. package/dist/__tests__/audit-report.test.d.ts.map +1 -0
  60. package/dist/__tests__/audit-report.test.js +186 -0
  61. package/dist/__tests__/audit-report.test.js.map +1 -0
  62. package/dist/__tests__/audit-roundtrip.test.d.ts +2 -0
  63. package/dist/__tests__/audit-roundtrip.test.d.ts.map +1 -0
  64. package/dist/__tests__/audit-roundtrip.test.js +368 -0
  65. package/dist/__tests__/audit-roundtrip.test.js.map +1 -0
  66. package/dist/__tests__/audit-temporal.test.d.ts +2 -0
  67. package/dist/__tests__/audit-temporal.test.d.ts.map +1 -0
  68. package/dist/__tests__/audit-temporal.test.js +364 -0
  69. package/dist/__tests__/audit-temporal.test.js.map +1 -0
  70. package/dist/__tests__/audit-tiers.test.d.ts +2 -0
  71. package/dist/__tests__/audit-tiers.test.d.ts.map +1 -0
  72. package/dist/__tests__/audit-tiers.test.js +282 -0
  73. package/dist/__tests__/audit-tiers.test.js.map +1 -0
  74. package/dist/__tests__/audit-witness-real.test.d.ts +19 -0
  75. package/dist/__tests__/audit-witness-real.test.d.ts.map +1 -0
  76. package/dist/__tests__/audit-witness-real.test.js +82 -0
  77. package/dist/__tests__/audit-witness-real.test.js.map +1 -0
  78. package/dist/__tests__/audit-witness.test.d.ts +2 -0
  79. package/dist/__tests__/audit-witness.test.d.ts.map +1 -0
  80. package/dist/__tests__/audit-witness.test.js +308 -0
  81. package/dist/__tests__/audit-witness.test.js.map +1 -0
  82. package/dist/__tests__/chain-hash-anchor.test.d.ts +22 -0
  83. package/dist/__tests__/chain-hash-anchor.test.d.ts.map +1 -0
  84. package/dist/__tests__/chain-hash-anchor.test.js +55 -0
  85. package/dist/__tests__/chain-hash-anchor.test.js.map +1 -0
  86. package/dist/__tests__/declared-proof-fixture.d.ts +66 -0
  87. package/dist/__tests__/declared-proof-fixture.d.ts.map +1 -0
  88. package/dist/__tests__/declared-proof-fixture.js +66 -0
  89. package/dist/__tests__/declared-proof-fixture.js.map +1 -0
  90. package/dist/__tests__/fuse-roundtrip.test.d.ts +2 -0
  91. package/dist/__tests__/fuse-roundtrip.test.d.ts.map +1 -0
  92. package/dist/__tests__/fuse-roundtrip.test.js +172 -0
  93. package/dist/__tests__/fuse-roundtrip.test.js.map +1 -0
  94. package/dist/__tests__/fuse-sdk.test.d.ts +2 -0
  95. package/dist/__tests__/fuse-sdk.test.d.ts.map +1 -0
  96. package/dist/__tests__/fuse-sdk.test.js +117 -0
  97. package/dist/__tests__/fuse-sdk.test.js.map +1 -0
  98. package/dist/__tests__/fuse-vectors.test.d.ts +2 -0
  99. package/dist/__tests__/fuse-vectors.test.d.ts.map +1 -0
  100. package/dist/__tests__/fuse-vectors.test.js +175 -0
  101. package/dist/__tests__/fuse-vectors.test.js.map +1 -0
  102. package/dist/__tests__/fuse-verify.test.d.ts +2 -0
  103. package/dist/__tests__/fuse-verify.test.d.ts.map +1 -0
  104. package/dist/__tests__/fuse-verify.test.js +250 -0
  105. package/dist/__tests__/fuse-verify.test.js.map +1 -0
  106. package/dist/__tests__/mock-proof-fixture.d.ts +15 -0
  107. package/dist/__tests__/mock-proof-fixture.d.ts.map +1 -0
  108. package/dist/__tests__/mock-proof-fixture.js +34 -0
  109. package/dist/__tests__/mock-proof-fixture.js.map +1 -0
  110. package/dist/__tests__/proof-hash-regression.test.js +4 -43
  111. package/dist/__tests__/proof-hash-regression.test.js.map +1 -1
  112. package/dist/__tests__/proof-hash.test.js +3 -21
  113. package/dist/__tests__/proof-hash.test.js.map +1 -1
  114. package/dist/__tests__/proof-integrity.test.d.ts +2 -0
  115. package/dist/__tests__/proof-integrity.test.d.ts.map +1 -0
  116. package/dist/__tests__/proof-integrity.test.js +426 -0
  117. package/dist/__tests__/proof-integrity.test.js.map +1 -0
  118. package/dist/__tests__/realistic-proof-fixture.d.ts +56 -0
  119. package/dist/__tests__/realistic-proof-fixture.d.ts.map +1 -0
  120. package/dist/__tests__/realistic-proof-fixture.js +57 -0
  121. package/dist/__tests__/realistic-proof-fixture.js.map +1 -0
  122. package/dist/__tests__/verifier.test.js +74 -0
  123. package/dist/__tests__/verifier.test.js.map +1 -1
  124. package/dist/fuse-cli.d.ts +3 -0
  125. package/dist/fuse-cli.d.ts.map +1 -0
  126. package/dist/fuse-cli.js +210 -0
  127. package/dist/fuse-cli.js.map +1 -0
  128. package/dist/fuse.d.ts +75 -0
  129. package/dist/fuse.d.ts.map +1 -0
  130. package/dist/fuse.js +273 -0
  131. package/dist/fuse.js.map +1 -0
  132. package/dist/index.d.ts +2 -0
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.js +4 -0
  135. package/dist/index.js.map +1 -1
  136. package/package.json +19 -9
  137. package/src/__tests__/audit-anchors.test.ts +188 -0
  138. package/src/__tests__/audit-anomalies.test.ts +815 -0
  139. package/src/__tests__/audit-attestation.test.ts +529 -0
  140. package/src/__tests__/audit-bench-smoke.test.ts +108 -0
  141. package/src/__tests__/audit-boundary.test.ts +96 -0
  142. package/src/__tests__/audit-cli.test.ts +288 -0
  143. package/src/__tests__/audit-dependencies.test.ts +141 -0
  144. package/src/__tests__/audit-fixtures.ts +736 -0
  145. package/src/__tests__/audit-ingest.test.ts +575 -0
  146. package/src/__tests__/audit-memory-ingest.test.ts +140 -0
  147. package/src/__tests__/audit-property.test.ts +892 -0
  148. package/src/__tests__/audit-real-fixtures.test.ts +153 -0
  149. package/src/__tests__/audit-real-live.test.ts +128 -0
  150. package/src/__tests__/audit-reconstruct.test.ts +353 -0
  151. package/src/__tests__/audit-report.test.ts +222 -0
  152. package/src/__tests__/audit-roundtrip.test.ts +464 -0
  153. package/src/__tests__/audit-temporal.test.ts +446 -0
  154. package/src/__tests__/audit-tiers.test.ts +343 -0
  155. package/src/__tests__/audit-witness-real.test.ts +92 -0
  156. package/src/__tests__/audit-witness.test.ts +345 -0
  157. package/src/__tests__/chain-hash-anchor.test.ts +67 -0
  158. package/src/__tests__/declared-proof-fixture.ts +65 -0
  159. package/src/__tests__/fuse-roundtrip.test.ts +174 -0
  160. package/src/__tests__/fuse-sdk.test.ts +142 -0
  161. package/src/__tests__/fuse-vectors.test.ts +218 -0
  162. package/src/__tests__/fuse-verify.test.ts +278 -0
  163. package/src/__tests__/mock-proof-fixture.ts +36 -0
  164. package/src/__tests__/proof-hash-regression.test.ts +4 -44
  165. package/src/__tests__/proof-hash.test.ts +3 -22
  166. package/src/__tests__/proof-integrity.test.ts +541 -0
  167. package/src/__tests__/real-fixtures/anchor-after-112762.json +47 -0
  168. package/src/__tests__/real-fixtures/anchor-with-ethereum-112758.json +47 -0
  169. package/src/__tests__/real-fixtures/live-proof-0.json +42 -0
  170. package/src/__tests__/real-fixtures/live-proof-1.json +43 -0
  171. package/src/__tests__/real-fixtures/live-proof-2.json +43 -0
  172. package/src/__tests__/real-fixtures/successor-112760.json +38 -0
  173. package/src/__tests__/real-fixtures/witness-25525146.json +6 -0
  174. package/src/__tests__/real-fixtures/witness-25525147.json +6 -0
  175. package/src/__tests__/realistic-proof-fixture.ts +57 -0
  176. package/src/__tests__/verifier.test.ts +83 -0
  177. package/src/fuse-cli.ts +187 -0
  178. package/src/fuse.ts +356 -0
  179. package/src/index.ts +6 -0
  180. package/dist/canonical.d.ts +0 -54
  181. package/dist/canonical.d.ts.map +0 -1
  182. package/dist/canonical.js +0 -119
  183. package/dist/canonical.js.map +0 -1
  184. package/dist/proof-hash.d.ts +0 -9
  185. package/dist/proof-hash.d.ts.map +0 -1
  186. package/dist/proof-hash.js +0 -75
  187. package/dist/proof-hash.js.map +0 -1
  188. package/dist/types.d.ts +0 -705
  189. package/dist/types.d.ts.map +0 -1
  190. package/dist/types.js +0 -3
  191. package/dist/types.js.map +0 -1
  192. package/dist/verifier.d.ts +0 -27
  193. package/dist/verifier.d.ts.map +0 -1
  194. package/dist/verifier.js +0 -844
  195. package/dist/verifier.js.map +0 -1
@@ -0,0 +1,473 @@
1
+ // Copyright (c) Mike Argento. All rights reserved. See LICENSE.
2
+ /**
3
+ * Tests for @mikeargento/bitgraph-audit ingest: container handling
4
+ * (directory, tar, tar.gz), discovery by shape, the version policy,
5
+ * duplicate detection, embedded proofHash cross-checks, manifest
6
+ * validation, the deterministic contents hash, and path safety.
7
+ */
8
+ import { describe, test, before, after } from "node:test";
9
+ import * as assert from "node:assert/strict";
10
+ import { rm, writeFile } from "node:fs/promises";
11
+ import { join } from "node:path";
12
+ import { gzipSync } from "node:zlib";
13
+ import { computeProofHash } from "@mikeargento/bitgraph-verify";
14
+ import { computeContentsHashB64, ingestBundle, } from "@mikeargento/bitgraph-audit";
15
+ import { b64, makeChainIdProof, makeConstructorProof, makeTar, makeTempDir, proofJson, storedProofJson, utf8, writeBundleDir, } from "./audit-fixtures.js";
16
+ const tempDirs = [];
17
+ async function newBundleDir() {
18
+ const dir = await makeTempDir("bitgraph-audit-ingest-");
19
+ tempDirs.push(dir);
20
+ return dir;
21
+ }
22
+ after(async () => {
23
+ for (const dir of tempDirs) {
24
+ await rm(dir, { recursive: true, force: true });
25
+ }
26
+ });
27
+ // Shared fixtures
28
+ let fxProof;
29
+ let fxBytes;
30
+ let fxHash;
31
+ before(async () => {
32
+ const made = await makeConstructorProof();
33
+ fxProof = made.proof;
34
+ fxBytes = made.bytes;
35
+ fxHash = computeProofHash(fxProof);
36
+ });
37
+ // ---------------------------------------------------------------------------
38
+ // Directory bundles
39
+ // ---------------------------------------------------------------------------
40
+ describe("audit ingest: directory bundles", () => {
41
+ test("discovers proofs by shape, matches artifacts by content, ignores unrelated files", async () => {
42
+ const dir = await newBundleDir();
43
+ await writeBundleDir(dir, {
44
+ "proofs/anything-goes.txt": storedProofJson(fxProof, fxHash),
45
+ "media/original.bin": fxBytes,
46
+ "notes/README.txt": "operator notes, not evidence",
47
+ });
48
+ const result = await ingestBundle(dir);
49
+ assert.equal(result.container, "directory");
50
+ assert.equal(result.counts.observed, 1);
51
+ assert.equal(result.counts.proofFiles, 1);
52
+ const proof = result.proofs[0];
53
+ assert.equal(proof.proofHash, fxHash);
54
+ assert.equal(proof.sources.length, 1);
55
+ assert.equal(proof.sources[0].path, "proofs/anything-goes.txt");
56
+ assert.equal(proof.embeddedProofHash, "match");
57
+ // Artifact matched content-addressed; the README is an unmatched candidate.
58
+ const matched = result.artifacts.filter((a) => a.matchedProofHashes.length > 0);
59
+ assert.equal(matched.length, 1);
60
+ assert.deepEqual(matched[0].paths, ["media/original.bin"]);
61
+ assert.deepEqual(matched[0].matchedProofHashes, [fxHash]);
62
+ const unmatched = result.artifacts.filter((a) => a.matchedProofHashes.length === 0);
63
+ assert.equal(unmatched.length, 1);
64
+ assert.deepEqual(unmatched[0].paths, ["notes/README.txt"]);
65
+ // Unrelated files produce no findings.
66
+ assert.deepEqual(result.findings, []);
67
+ });
68
+ test("version policy: occ/1 proof-shaped file is rejected with the stable code", async () => {
69
+ const dir = await newBundleDir();
70
+ await writeBundleDir(dir, {
71
+ "legacy.json": JSON.stringify({
72
+ version: "occ/1",
73
+ artifact: { hashAlg: "sha256", digestB64: "AAAA" },
74
+ commit: { nonceB64: "AAAA" },
75
+ signer: { publicKeyB64: "AAAA", signatureB64: "AAAA" },
76
+ }),
77
+ "good.json": proofJson(fxProof),
78
+ });
79
+ const result = await ingestBundle(dir);
80
+ assert.equal(result.counts.observed, 1);
81
+ assert.equal(result.counts.unsupportedVersion, 1);
82
+ const rejected = result.unsupportedVersions[0];
83
+ assert.equal(rejected.code, "unsupported-version");
84
+ assert.equal(rejected.path, "legacy.json");
85
+ assert.equal(rejected.version, "occ/1");
86
+ const finding = result.findings.find((f) => f.code === "unsupported-version");
87
+ assert.ok(finding);
88
+ assert.equal(finding.path, "legacy.json");
89
+ // Excluded from observation entirely.
90
+ assert.ok(result.proofs.every((p) => p.sources[0].path !== "legacy.json"));
91
+ });
92
+ test("unknown fields tolerated: signed chainId is extracted, absence normalizes to global", async () => {
93
+ const withChain = await makeChainIdProof({ chainId: "bitgraph:main", counter: "2", epochId: "epoch-x" });
94
+ const dir = await newBundleDir();
95
+ await writeBundleDir(dir, {
96
+ "a.json": proofJson(withChain.proof),
97
+ "b.json": proofJson(fxProof),
98
+ });
99
+ const result = await ingestBundle(dir);
100
+ assert.equal(result.counts.observed, 2);
101
+ const chained = result.proofs.find((p) => p.sources[0].path === "a.json");
102
+ const plain = result.proofs.find((p) => p.sources[0].path === "b.json");
103
+ assert.equal(chained.chainId, "bitgraph:main");
104
+ assert.equal(chained.counter, "2");
105
+ assert.equal(chained.epochId, "epoch-x");
106
+ assert.equal(chained.chainless, false);
107
+ assert.equal(plain.chainId, "global");
108
+ });
109
+ test("exact duplicates: byte-identical copies collapse to one observed proof", async () => {
110
+ const dir = await newBundleDir();
111
+ const body = proofJson(fxProof);
112
+ await writeBundleDir(dir, {
113
+ "one/copy.json": body,
114
+ "two/copy.json": body,
115
+ });
116
+ const result = await ingestBundle(dir);
117
+ assert.equal(result.counts.observed, 1);
118
+ assert.equal(result.counts.proofFiles, 2);
119
+ assert.equal(result.counts.exactDuplicates, 1);
120
+ assert.equal(result.counts.semanticDuplicates, 0);
121
+ assert.equal(result.proofs[0].sources.length, 2);
122
+ const finding = result.findings.find((f) => f.code === "exact-duplicate");
123
+ assert.ok(finding);
124
+ assert.equal(finding.details?.["proofHash"], fxHash);
125
+ });
126
+ test("semantic duplicates: different encodings of the same canonical identity", async () => {
127
+ const dir = await newBundleDir();
128
+ await writeBundleDir(dir, {
129
+ "compact.json": JSON.stringify(fxProof),
130
+ "pretty.json": JSON.stringify(fxProof, null, 2),
131
+ });
132
+ const result = await ingestBundle(dir);
133
+ assert.equal(result.counts.observed, 1);
134
+ assert.equal(result.counts.exactDuplicates, 0);
135
+ assert.equal(result.counts.semanticDuplicates, 1);
136
+ assert.equal(result.proofs[0].proofHash, fxHash);
137
+ assert.equal(result.proofs[0].sources.length, 2);
138
+ assert.ok(result.findings.find((f) => f.code === "semantic-duplicate"));
139
+ });
140
+ test("embedded proofHash mismatch is flagged with a stable code but still observed", async () => {
141
+ const dir = await newBundleDir();
142
+ await writeBundleDir(dir, {
143
+ "tampered-hash.json": storedProofJson(fxProof, b64(new Uint8Array(32))),
144
+ });
145
+ const result = await ingestBundle(dir);
146
+ assert.equal(result.counts.observed, 1);
147
+ const proof = result.proofs[0];
148
+ assert.equal(proof.embeddedProofHash, "mismatch");
149
+ // Identity is always computed, never read from the file.
150
+ assert.equal(proof.proofHash, fxHash);
151
+ const finding = result.findings.find((f) => f.code === "proofhash-mismatch");
152
+ assert.ok(finding);
153
+ assert.equal(finding.details?.["computed"], proof.proofHash);
154
+ });
155
+ test("chainless classification: no counter and no epochId means observed-but-unchained", async () => {
156
+ const chained = await makeConstructorProof({ withCounter: true, epochId: "epoch-chained" });
157
+ const dir = await newBundleDir();
158
+ await writeBundleDir(dir, {
159
+ "chainless.json": proofJson(fxProof),
160
+ "chained.json": proofJson(chained.proof),
161
+ });
162
+ const result = await ingestBundle(dir);
163
+ const unchained = result.proofs.find((p) => p.sources[0].path === "chainless.json");
164
+ const linked = result.proofs.find((p) => p.sources[0].path === "chained.json");
165
+ assert.equal(unchained.chainless, true);
166
+ assert.equal(linked.chainless, false);
167
+ assert.equal(linked.counter, "1");
168
+ assert.equal(linked.epochId, "epoch-chained");
169
+ // Chainless is not an anomaly: no finding is emitted for it.
170
+ assert.deepEqual(result.findings, []);
171
+ });
172
+ });
173
+ // ---------------------------------------------------------------------------
174
+ // Manifest
175
+ // ---------------------------------------------------------------------------
176
+ describe("audit ingest: manifest", () => {
177
+ test("contents hash verifies over every entry except the root manifest", async () => {
178
+ const dir = await newBundleDir();
179
+ const proofBytes = utf8(proofJson(fxProof));
180
+ const contentsHashB64 = computeContentsHashB64([
181
+ { path: "proofs/p.json", content: proofBytes },
182
+ { path: "media/original.bin", content: fxBytes },
183
+ ]);
184
+ await writeBundleDir(dir, {
185
+ "proofs/p.json": proofBytes,
186
+ "media/original.bin": fxBytes,
187
+ "manifest.json": JSON.stringify({
188
+ version: "bitgraph-bundle/1",
189
+ proofCount: 1,
190
+ contentsHashB64,
191
+ artifactsIncluded: true,
192
+ }),
193
+ });
194
+ const result = await ingestBundle(dir);
195
+ assert.ok(result.manifest);
196
+ assert.equal(result.manifest.recognized, true);
197
+ assert.deepEqual(result.manifest.problems, []);
198
+ assert.equal(result.manifest.contentsHash?.match, true);
199
+ assert.equal(result.computedContentsHashB64, contentsHashB64);
200
+ assert.equal(result.findings.filter((f) => f.code === "manifest-contents-hash-mismatch").length, 0);
201
+ // The manifest is never a proof, artifact, or witness.
202
+ assert.equal(result.counts.observed, 1);
203
+ assert.ok(result.artifacts.every((a) => !a.paths.includes("manifest.json")));
204
+ });
205
+ test("contents hash mismatch is an advisory finding, never a proof failure", async () => {
206
+ const dir = await newBundleDir();
207
+ await writeBundleDir(dir, {
208
+ "proofs/p.json": proofJson(fxProof),
209
+ "manifest.json": JSON.stringify({
210
+ version: "bitgraph-bundle/1",
211
+ contentsHashB64: "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
212
+ }),
213
+ });
214
+ const result = await ingestBundle(dir);
215
+ assert.equal(result.manifest?.contentsHash?.match, false);
216
+ const finding = result.findings.find((f) => f.code === "manifest-contents-hash-mismatch");
217
+ assert.ok(finding);
218
+ // Advisory: the proof is still observed normally.
219
+ assert.equal(result.counts.observed, 1);
220
+ assert.equal(result.proofs[0].proofHash, fxHash);
221
+ });
222
+ test("manifest field type problems are reported per spec section 7.1", async () => {
223
+ const dir = await newBundleDir();
224
+ await writeBundleDir(dir, {
225
+ "manifest.json": JSON.stringify({
226
+ version: "bitgraph-bundle/1",
227
+ proofCount: "five",
228
+ epochIds: "not-an-array",
229
+ futureUnknownField: { tolerated: true },
230
+ }),
231
+ });
232
+ const result = await ingestBundle(dir);
233
+ assert.ok(result.manifest);
234
+ assert.equal(result.manifest.recognized, true);
235
+ assert.equal(result.manifest.problems.length, 2);
236
+ assert.equal(result.findings.filter((f) => f.code === "manifest-field-invalid").length, 2);
237
+ });
238
+ test("unrecognized manifest version is reported and the manifest is not interpreted", async () => {
239
+ const dir = await newBundleDir();
240
+ await writeBundleDir(dir, {
241
+ "manifest.json": JSON.stringify({ version: "bitgraph-bundle/2", proofCount: 1 }),
242
+ "proofs/p.json": proofJson(fxProof),
243
+ });
244
+ const result = await ingestBundle(dir);
245
+ assert.equal(result.manifest?.recognized, false);
246
+ assert.equal(result.manifest?.manifest, undefined);
247
+ assert.ok(result.findings.find((f) => f.code === "manifest-unrecognized-version"));
248
+ // Processing continues as if no manifest were present.
249
+ assert.equal(result.counts.observed, 1);
250
+ });
251
+ });
252
+ // ---------------------------------------------------------------------------
253
+ // Tar and tar.gz containers
254
+ // ---------------------------------------------------------------------------
255
+ describe("audit ingest: tar and tar.gz containers", () => {
256
+ test("plain tar with a PAX long-name entry", async () => {
257
+ const dir = await newBundleDir();
258
+ const longPath = `proofs/${"very-long-directory-name-".repeat(6)}segment/proof.json`;
259
+ assert.ok(longPath.length > 100);
260
+ const tar = makeTar([
261
+ { name: "long-name-truncated", paxPath: longPath, content: proofJson(fxProof) },
262
+ { name: "media/original.bin", content: fxBytes },
263
+ { name: "notes.txt", content: "unrelated" },
264
+ ]);
265
+ const tarPath = join(dir, "bundle.tar");
266
+ await writeFile(tarPath, tar);
267
+ const result = await ingestBundle(tarPath);
268
+ assert.equal(result.container, "tar");
269
+ assert.equal(result.counts.observed, 1);
270
+ assert.equal(result.proofs[0].sources[0].path, longPath);
271
+ const matched = result.artifacts.find((a) => a.matchedProofHashes.length > 0);
272
+ assert.deepEqual(matched?.paths, ["media/original.bin"]);
273
+ });
274
+ test("GNU long-name entries are accepted", async () => {
275
+ const dir = await newBundleDir();
276
+ const longPath = `archive/${"gnu-long-name-".repeat(9)}tail/proof.json`;
277
+ assert.ok(longPath.length > 100);
278
+ const tar = makeTar([
279
+ { name: "gnu-truncated", gnuLongName: longPath, content: proofJson(fxProof) },
280
+ // Root-level entry so the single-top-level-directory rule does not apply.
281
+ { name: "notes.txt", content: "unrelated" },
282
+ ]);
283
+ const tarPath = join(dir, "gnu.tar");
284
+ await writeFile(tarPath, tar);
285
+ const result = await ingestBundle(tarPath);
286
+ assert.equal(result.strippedRootPrefix, undefined);
287
+ assert.equal(result.counts.observed, 1);
288
+ assert.equal(result.proofs[0].sources[0].path, longPath);
289
+ });
290
+ test("tar.gz round-trips identically to the plain tar", async () => {
291
+ const dir = await newBundleDir();
292
+ const tar = makeTar([
293
+ { name: "proofs/p.json", content: proofJson(fxProof) },
294
+ { name: "media/original.bin", content: fxBytes },
295
+ ]);
296
+ const tgzPath = join(dir, "bundle.tgz");
297
+ await writeFile(tgzPath, gzipSync(tar));
298
+ const result = await ingestBundle(tgzPath);
299
+ assert.equal(result.container, "tar-gz");
300
+ assert.equal(result.counts.observed, 1);
301
+ assert.equal(result.proofs[0].proofHash, fxHash);
302
+ assert.equal(result.artifacts.filter((a) => a.matchedProofHashes.length > 0).length, 1);
303
+ });
304
+ test("a single common top-level directory is stripped as the bundle root", async () => {
305
+ const dir = await newBundleDir();
306
+ const proofBytes = utf8(proofJson(fxProof));
307
+ // Contents hash computed over bundle-root-relative (stripped) paths.
308
+ const contentsHashB64 = computeContentsHashB64([
309
+ { path: "proofs/p.json", content: proofBytes },
310
+ { path: "media/original.bin", content: fxBytes },
311
+ ]);
312
+ const tar = makeTar([
313
+ { name: "mybundle/proofs/p.json", content: proofBytes },
314
+ { name: "mybundle/media/original.bin", content: fxBytes },
315
+ {
316
+ name: "mybundle/manifest.json",
317
+ content: JSON.stringify({ version: "bitgraph-bundle/1", contentsHashB64 }),
318
+ },
319
+ ]);
320
+ const tarPath = join(dir, "wrapped.tar");
321
+ await writeFile(tarPath, tar);
322
+ const result = await ingestBundle(tarPath);
323
+ assert.equal(result.strippedRootPrefix, "mybundle");
324
+ assert.equal(result.proofs[0].sources[0].path, "proofs/p.json");
325
+ assert.ok(result.manifest, "manifest at the stripped root is the root manifest");
326
+ assert.equal(result.manifest.contentsHash?.match, true);
327
+ });
328
+ test("unsafe paths are skipped and reported, and never contribute anywhere", async () => {
329
+ const dir = await newBundleDir();
330
+ const tar = makeTar([
331
+ { name: "../escape.json", content: proofJson(fxProof) },
332
+ { name: "/absolute.json", content: proofJson(fxProof) },
333
+ { name: "good/p.json", content: proofJson(fxProof) },
334
+ ]);
335
+ const tarPath = join(dir, "unsafe.tar");
336
+ await writeFile(tarPath, tar);
337
+ const result = await ingestBundle(tarPath);
338
+ assert.equal(result.counts.skippedUnsafePaths, 2);
339
+ assert.equal(result.findings.filter((f) => f.code === "unsafe-path").length, 2);
340
+ assert.equal(result.counts.observed, 1);
341
+ assert.equal(result.proofs[0].sources.length, 1);
342
+ assert.equal(result.proofs[0].sources[0].path, "good/p.json");
343
+ // Skipped entries are excluded from the contents hash.
344
+ assert.equal(result.computedContentsHashB64, computeContentsHashB64([{ path: "good/p.json", content: utf8(proofJson(fxProof)) }]));
345
+ });
346
+ test("duplicate tar paths: the last entry wins and the duplication is reported", async () => {
347
+ const dir = await newBundleDir();
348
+ const tar = makeTar([
349
+ { name: "data/blob.bin", content: "first version" },
350
+ { name: "data/blob.bin", content: "second version" },
351
+ // Root-level entry so the single-top-level-directory rule does not apply.
352
+ { name: "notes.txt", content: "unrelated" },
353
+ ]);
354
+ const tarPath = join(dir, "dupes.tar");
355
+ await writeFile(tarPath, tar);
356
+ const result = await ingestBundle(tarPath);
357
+ assert.ok(result.findings.find((f) => f.code === "duplicate-path" && f.path === "data/blob.bin"));
358
+ const blob = result.artifacts.find((a) => a.paths.includes("data/blob.bin"));
359
+ assert.equal(blob.byteLength, utf8("second version").length);
360
+ // Exactly one entry per path participates: the shadowed first version is gone.
361
+ assert.equal(result.artifacts.filter((a) => a.paths.includes("data/blob.bin")).length, 1);
362
+ });
363
+ });
364
+ // ---------------------------------------------------------------------------
365
+ // Untrusted-archive resource caps (gzip-bomb / entry-flood defense)
366
+ // ---------------------------------------------------------------------------
367
+ describe("audit ingest: untrusted-archive resource caps", () => {
368
+ test("an entry count over the cap aborts cleanly instead of unbounded scanning", async () => {
369
+ const dir = await newBundleDir();
370
+ // Many zero-length entries: a small archive that would scan forever
371
+ // under an unbounded reader.
372
+ const entries = [];
373
+ for (let i = 0; i < 200; i++) {
374
+ entries.push({ name: `empty-${i}.bin`, content: new Uint8Array(0) });
375
+ }
376
+ const tarPath = join(dir, "flood.tar.gz");
377
+ await writeFile(tarPath, gzipSync(makeTar(entries)));
378
+ await assert.rejects(ingestBundle(tarPath, {
379
+ maxEntryCount: 25,
380
+ maxTotalBytes: 1 << 30,
381
+ maxMetadataEntryBytes: 8 * 1024 * 1024,
382
+ }), /archive exceeds the maximum of 25 entries/);
383
+ });
384
+ test("a total decompressed-byte ceiling aborts a bomb before it is fully read", async () => {
385
+ const dir = await newBundleDir();
386
+ // A single large artifact whose decompressed size exceeds a low cap.
387
+ const big = new Uint8Array(512 * 1024).fill(0x41);
388
+ const tarPath = join(dir, "big.tar.gz");
389
+ await writeFile(tarPath, gzipSync(makeTar([{ name: "blob.bin", content: big }])));
390
+ await assert.rejects(ingestBundle(tarPath, {
391
+ maxEntryCount: 1_000_000,
392
+ maxTotalBytes: 64 * 1024, // far below the 512 KiB payload
393
+ maxMetadataEntryBytes: 8 * 1024 * 1024,
394
+ }), /archive exceeds the maximum decompressed size of 65536 bytes/);
395
+ });
396
+ test("a PAX header declaring an absurd size aborts before allocating that buffer", async () => {
397
+ const dir = await newBundleDir();
398
+ // The PAX 'x' header claims 2 GiB; the archive itself is tiny. A reader
399
+ // that trusted the declared size would try to allocate 2 GiB.
400
+ const tar = makeTar([
401
+ {
402
+ name: "pax-truncated",
403
+ paxPath: "renamed/proof.json",
404
+ paxHeaderDeclaredSize: 2 * 1024 * 1024 * 1024,
405
+ content: proofJson(fxProof),
406
+ },
407
+ ]);
408
+ const tarPath = join(dir, "pax-bomb.tar.gz");
409
+ await writeFile(tarPath, gzipSync(tar));
410
+ // Default limits: the 8 MiB metadata cap trips before any allocation.
411
+ await assert.rejects(ingestBundle(tarPath), /PAX metadata entry of \d+ bytes exceeds the \d+-byte cap/);
412
+ });
413
+ test("legitimate archives ingest normally under the default caps", async () => {
414
+ const dir = await newBundleDir();
415
+ const tarPath = join(dir, "ok.tar.gz");
416
+ await writeFile(tarPath, gzipSync(makeTar([
417
+ { name: "proofs/p.json", content: proofJson(fxProof) },
418
+ { name: "media/original.bin", content: fxBytes },
419
+ ])));
420
+ // No limits argument: DEFAULT_INGEST_LIMITS apply and never trip.
421
+ const result = await ingestBundle(tarPath);
422
+ assert.equal(result.counts.observed, 1);
423
+ assert.equal(result.proofs[0].proofHash, fxHash);
424
+ });
425
+ });
426
+ // ---------------------------------------------------------------------------
427
+ // Deterministic contents hash vectors (docs/BUNDLE-FORMAT.md section 8.3)
428
+ // ---------------------------------------------------------------------------
429
+ describe("audit ingest: contents hash test vectors", () => {
430
+ test("empty hashed set reproduces the published vector", () => {
431
+ assert.equal(computeContentsHashB64([]), "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=");
432
+ });
433
+ test("two-entry set reproduces the published vector", () => {
434
+ const entries = [
435
+ // Deliberately supplied out of sorted order; the scheme sorts by path bytes.
436
+ { path: "proofs/example.json", content: utf8('{"hello":"world"}\n') },
437
+ { path: "artifacts/a.bin", content: new Uint8Array([0x00, 0x01, 0x02]) },
438
+ ];
439
+ assert.equal(computeContentsHashB64(entries), "uO+wswRbTl4WWwAuXVrdRjEVDs2jKq72iJhI95XoH3s=");
440
+ });
441
+ test("streaming ingest agrees with the one-shot vector implementation", async () => {
442
+ const dir = await newBundleDir();
443
+ await writeBundleDir(dir, {
444
+ "artifacts/a.bin": new Uint8Array([0x00, 0x01, 0x02]),
445
+ "proofs/example.json": utf8('{"hello":"world"}\n'),
446
+ });
447
+ const result = await ingestBundle(dir);
448
+ assert.equal(result.computedContentsHashB64, "uO+wswRbTl4WWwAuXVrdRjEVDs2jKq72iJhI95XoH3s=");
449
+ });
450
+ });
451
+ // ---------------------------------------------------------------------------
452
+ // Witness discovery
453
+ // ---------------------------------------------------------------------------
454
+ describe("audit ingest: anchor witness discovery", () => {
455
+ test("witnesses are discovered by version discriminator, never by filename", async () => {
456
+ const dir = await newBundleDir();
457
+ await writeBundleDir(dir, {
458
+ "some/dir/whatever.dat": JSON.stringify({
459
+ version: "bitgraph-anchor-witness/1",
460
+ headerRlpHex: "0xf90201",
461
+ blockNumber: 19000000,
462
+ blockHash: `0x${"ab".repeat(32)}`,
463
+ }),
464
+ });
465
+ const result = await ingestBundle(dir);
466
+ assert.equal(result.counts.witnesses, 1);
467
+ assert.equal(result.witnesses[0].path, "some/dir/whatever.dat");
468
+ assert.equal(result.witnesses[0].witness["blockNumber"], 19000000);
469
+ // Witnesses are not artifact candidates.
470
+ assert.equal(result.artifacts.length, 0);
471
+ });
472
+ });
473
+ //# sourceMappingURL=audit-ingest.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-ingest.test.js","sourceRoot":"","sources":["../../src/__tests__/audit-ingest.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * ingestEntries() and auditIngest(): the filesystem-free path.
3
+ *
4
+ * A browser cannot call ingestBundle(path); it hands over bytes. This suite
5
+ * pins that the in-memory ingest classifies, hashes, and matches exactly
6
+ * as the directory ingest does over the same files at the same paths, and
7
+ * that auditIngest() over either produces the same audit (less the run
8
+ * stamp and the container label). It also pins AUDIT_VERSION to
9
+ * package.json, since the constant replaced a runtime file read.
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=audit-memory-ingest.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-memory-ingest.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/audit-memory-ingest.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * ingestEntries() and auditIngest(): the filesystem-free path.
3
+ *
4
+ * A browser cannot call ingestBundle(path); it hands over bytes. This suite
5
+ * pins that the in-memory ingest classifies, hashes, and matches exactly
6
+ * as the directory ingest does over the same files at the same paths, and
7
+ * that auditIngest() over either produces the same audit (less the run
8
+ * stamp and the container label). It also pins AUDIT_VERSION to
9
+ * package.json, since the constant replaced a runtime file read.
10
+ */
11
+ import { describe, it, before, after } from "node:test";
12
+ import * as assert from "node:assert/strict";
13
+ import { readFile, readdir, rm } from "node:fs/promises";
14
+ import { fileURLToPath } from "node:url";
15
+ import { join } from "node:path";
16
+ import { auditIngest, auditToolVersion, AUDIT_VERSION, ingestBundle, ingestEntries, runAudit, streamMatchedArtifacts, } from "@mikeargento/bitgraph-audit";
17
+ import { makeStandardAuditBundle } from "./audit-fixtures.js";
18
+ async function walk(root, rel = "") {
19
+ const out = [];
20
+ const dir = rel === "" ? root : join(root, rel);
21
+ const entries = await readdir(dir, { withFileTypes: true });
22
+ for (const entry of entries) {
23
+ const r = rel === "" ? entry.name : `${rel}/${entry.name}`;
24
+ if (entry.isDirectory())
25
+ out.push(...(await walk(root, r)));
26
+ else if (entry.isFile())
27
+ out.push(r);
28
+ }
29
+ return out;
30
+ }
31
+ async function entriesOf(root) {
32
+ return (await walk(root)).map((p) => ({
33
+ path: p,
34
+ open: () => readFile(join(root, ...p.split("/"))).then((b) => new Uint8Array(b)),
35
+ }));
36
+ }
37
+ /** Everything in an IngestResult that must agree between containers. */
38
+ function comparableIngest(r) {
39
+ const { bundlePath: _p, container: _c, ...rest } = r;
40
+ return JSON.parse(JSON.stringify(rest));
41
+ }
42
+ function comparableAudit(a) {
43
+ const { runMetadata: _m, ingest, ...rest } = a;
44
+ return JSON.parse(JSON.stringify({ ingest: comparableIngest(ingest), ...rest }));
45
+ }
46
+ describe("audit: AUDIT_VERSION is a source constant that tracks package.json", () => {
47
+ it("equals the version in packages/audit/package.json", async () => {
48
+ const pkgPath = fileURLToPath(new URL("../../packages/audit/package.json", import.meta.url));
49
+ const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
50
+ assert.equal(AUDIT_VERSION, pkg.version);
51
+ assert.equal(auditToolVersion(), pkg.version);
52
+ });
53
+ });
54
+ describe("audit: ingestEntries mirrors ingestBundle over the same files", () => {
55
+ let dir;
56
+ let fromDisk;
57
+ let fromMemory;
58
+ before(async () => {
59
+ const bundle = await makeStandardAuditBundle();
60
+ dir = bundle.dir;
61
+ fromDisk = await ingestBundle(dir);
62
+ fromMemory = await ingestEntries(await entriesOf(dir));
63
+ });
64
+ after(async () => {
65
+ if (dir !== undefined)
66
+ await rm(dir, { recursive: true, force: true });
67
+ });
68
+ it("labels the container and keeps the label the caller gave", async () => {
69
+ assert.equal(fromMemory.container, "memory");
70
+ assert.equal(fromMemory.bundlePath, "");
71
+ const labeled = await ingestEntries([], { label: "dropped files" });
72
+ assert.equal(labeled.bundlePath, "dropped files");
73
+ assert.equal(labeled.entriesScanned, 0);
74
+ });
75
+ it("classifies and matches identically: proofs, artifacts, witnesses, findings, counts, contents hash", () => {
76
+ assert.deepEqual(comparableIngest(fromMemory), comparableIngest(fromDisk));
77
+ assert.ok(fromDisk.proofs.length > 0, "the standard bundle has proofs");
78
+ assert.equal(fromMemory.computedContentsHashB64, fromDisk.computedContentsHashB64);
79
+ });
80
+ it("re-reads matched artifact bytes through the registered sources", async () => {
81
+ const disk = [];
82
+ for await (const m of streamMatchedArtifacts(fromDisk))
83
+ disk.push(`${m.path}:${m.sha256Hex}:${m.bytes.length}`);
84
+ const mem = [];
85
+ for await (const m of streamMatchedArtifacts(fromMemory))
86
+ mem.push(`${m.path}:${m.sha256Hex}:${m.bytes.length}`);
87
+ assert.deepEqual(mem, disk);
88
+ assert.ok(mem.length > 0, "the standard bundle has matched artifacts");
89
+ });
90
+ it("yields no artifact bytes for a structurally cloned result (sources are keyed by identity), never throws", async () => {
91
+ const clone = JSON.parse(JSON.stringify(fromMemory));
92
+ const seen = [];
93
+ for await (const m of streamMatchedArtifacts(clone))
94
+ seen.push(m.path);
95
+ assert.deepEqual(seen, []);
96
+ });
97
+ it("skips and reports unsafe paths exactly as archive ingest does", async () => {
98
+ const bad = await ingestEntries([
99
+ { path: "../escape.json", open: () => new Uint8Array([1]) },
100
+ { path: "/abs.json", open: () => new Uint8Array([2]) },
101
+ { path: "ok.bin", open: () => new Uint8Array([3]) },
102
+ ]);
103
+ assert.equal(bad.counts.skippedUnsafePaths, 2);
104
+ assert.equal(bad.entriesScanned, 3);
105
+ assert.deepEqual(bad.findings.map((f) => f.code), ["unsafe-path", "unsafe-path"]);
106
+ assert.equal(bad.artifacts.length, 1);
107
+ });
108
+ it("auditIngest over either ingest matches runAudit over the directory, less the run stamp", async () => {
109
+ const viaRun = await runAudit(dir);
110
+ const viaMemory = await auditIngest(await ingestEntries(await entriesOf(dir)), { startedAt: "" });
111
+ assert.deepEqual(comparableAudit(viaMemory), comparableAudit(viaRun));
112
+ assert.equal(viaMemory.runMetadata.startedAt, "");
113
+ assert.equal(viaMemory.runMetadata.container, "memory");
114
+ assert.equal(viaRun.runMetadata.toolVersion, AUDIT_VERSION);
115
+ // The full-tier pass ran in memory too: matched artifacts verified with bytes.
116
+ assert.equal(viaMemory.verification.verified, viaRun.verification.verified);
117
+ assert.ok(viaMemory.verification.verified > 0);
118
+ });
119
+ });
120
+ //# sourceMappingURL=audit-memory-ingest.test.js.map