@fedify/fedify 2.4.0-dev.1964 → 2.4.0-dev.1995

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 (94) hide show
  1. package/dist/{builder-B4zpKIFU.mjs → builder-D0DxgOV5.mjs} +2 -2
  2. package/dist/{client-CtxvmazV.mjs → client-DpnpgjyX.mjs} +78 -4
  3. package/dist/compat/mod.d.cts +1 -1
  4. package/dist/compat/mod.d.ts +1 -1
  5. package/dist/compat/transformers.test.mjs +1 -1
  6. package/dist/compound-proof-CJnWc6c-.mjs +495 -0
  7. package/dist/{context-COLwHogJ.d.cts → context-BeDnDuF3.d.cts} +4 -3
  8. package/dist/{context-DiiCt4S7.d.ts → context-uyRUWoZQ.d.ts} +4 -3
  9. package/dist/{deno-CtG93ohj.mjs → deno-CABhzCb_.mjs} +1 -1
  10. package/dist/{docloader-dk_k3S8d.mjs → docloader-BbGSL0Xq.mjs} +2 -2
  11. package/dist/federation/builder.test.mjs +1 -1
  12. package/dist/federation/handler-compound-proof.test.d.mts +2 -0
  13. package/dist/federation/handler-compound-proof.test.mjs +152 -0
  14. package/dist/federation/handler.test.mjs +122 -4
  15. package/dist/federation/idempotency.test.mjs +2 -2
  16. package/dist/federation/keycache.test.mjs +52 -9
  17. package/dist/federation/metrics.test.mjs +1 -1
  18. package/dist/federation/middleware.test.mjs +111 -15
  19. package/dist/federation/mod.cjs +1 -1
  20. package/dist/federation/mod.d.cts +2 -2
  21. package/dist/federation/mod.d.ts +2 -2
  22. package/dist/federation/mod.js +1 -1
  23. package/dist/federation/send.test.mjs +139 -4
  24. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  25. package/dist/federation/tasks/tasks.test.mjs +2 -2
  26. package/dist/federation/temporal.test.mjs +1 -1
  27. package/dist/federation/webfinger.test.mjs +1 -1
  28. package/dist/{federation-Drjlrrax.cjs → federation-DCEHQk4G.cjs} +632 -57
  29. package/dist/{http-6TI0tI03.d.cts → http-65PYi9jY.d.ts} +34 -2
  30. package/dist/{http-Dj4s1UQ0.js → http-9WyuldYh.js} +170 -8
  31. package/dist/{http-CdrSn2ip.d.ts → http-CP-7OdSq.d.cts} +34 -2
  32. package/dist/{http-B3skRvGo.cjs → http-CRFns-UE.cjs} +181 -7
  33. package/dist/{http-D7LX-BL9.mjs → http-D9urSO2D.mjs} +3 -3
  34. package/dist/{key-BVjgbs_9.mjs → key-Ccs8cnd7.mjs} +169 -7
  35. package/dist/{keycache-ZLqIfrkL.mjs → keycache-BUDHB5rY.mjs} +12 -4
  36. package/dist/{kv-cache-DtsjH_bY.cjs → kv-cache-DXT9vmIg.cjs} +2 -2
  37. package/dist/{kv-cache-tGNbvDCF.js → kv-cache-DtmXhPXG.js} +2 -2
  38. package/dist/{kv-cache-xRcC_xxP.mjs → kv-cache-lKyFn_Dn.mjs} +1 -1
  39. package/dist/{ld-OuCeQFny.mjs → ld-qup__MAW.mjs} +3 -3
  40. package/dist/map-local-context-conflict-DOYEEJ3u.mjs +204 -0
  41. package/dist/map-local-create-note-B0SeXJms.mjs +214 -0
  42. package/dist/{metrics-B9N27cCB.mjs → metrics-CISXCvB1.mjs} +1 -1
  43. package/dist/{middleware-TeLjzoxn.mjs → middleware-CzvJvPHK.mjs} +58 -15
  44. package/dist/{middleware-Cd_vPqDH.mjs → middleware-DkrU2WhY.mjs} +1 -1
  45. package/dist/{middleware-CPTDFMmJ.js → middleware-DsfWajaD.js} +593 -18
  46. package/dist/{mod-DypxPS9H.d.ts → mod-C0BYc0OU.d.ts} +1 -1
  47. package/dist/{mod-CnGLNoVw.d.cts → mod-CbKDOnCt.d.cts} +1 -1
  48. package/dist/{mod-CLHkCwLm.d.cts → mod-CyiSjRkk.d.cts} +1 -1
  49. package/dist/{mod-CSH1Ee-I.d.ts → mod-D882_Xsg.d.ts} +1 -1
  50. package/dist/{mod-DzRXACM5.d.ts → mod-UDwMjuHM.d.ts} +1 -1
  51. package/dist/{mod-BZldCEU0.d.cts → mod-x9W0d0Pk.d.cts} +1 -1
  52. package/dist/mod.cjs +20 -20
  53. package/dist/mod.d.cts +6 -6
  54. package/dist/mod.d.ts +6 -6
  55. package/dist/mod.js +5 -5
  56. package/dist/nodeinfo/client.test.mjs +28 -1
  57. package/dist/nodeinfo/handler.test.mjs +1 -1
  58. package/dist/nodeinfo/mod.cjs +1 -1
  59. package/dist/nodeinfo/mod.js +1 -1
  60. package/dist/{owner-DGSfntI-.d.ts → owner-BF0L9cMt.d.cts} +12 -1
  61. package/dist/{proof-BO_esQii.js → owner-BIPEZGiU.js} +1255 -1098
  62. package/dist/{owner-r_waTiQj.d.cts → owner-BSzr5-I8.d.ts} +12 -1
  63. package/dist/owner-CRoq8Spb.mjs +175 -0
  64. package/dist/{proof-Dua0d906.cjs → owner-CVA7L9MA.cjs} +1266 -1097
  65. package/dist/{proof-D5Ajk4DJ.mjs → proof-BDxNVZ3E.mjs} +186 -83
  66. package/dist/{send-DVXyUqhO.mjs → send-BzM5GLyn.mjs} +68 -11
  67. package/dist/sig/compound-proof-discovery.test.d.mts +2 -0
  68. package/dist/sig/compound-proof-discovery.test.mjs +331 -0
  69. package/dist/sig/compound-proof-verification.test.d.mts +2 -0
  70. package/dist/sig/compound-proof-verification.test.mjs +465 -0
  71. package/dist/sig/compound-proof.test.d.mts +2 -0
  72. package/dist/sig/compound-proof.test.mjs +324 -0
  73. package/dist/sig/http.test.mjs +2 -2
  74. package/dist/sig/key.test.mjs +169 -2
  75. package/dist/sig/ld.test.mjs +55 -2
  76. package/dist/sig/mod.cjs +17 -17
  77. package/dist/sig/mod.d.cts +3 -3
  78. package/dist/sig/mod.d.ts +3 -3
  79. package/dist/sig/mod.js +2 -2
  80. package/dist/sig/owner.test.mjs +103 -4
  81. package/dist/sig/proof.test.mjs +120 -1
  82. package/dist/{temporal-BoDw51A-.mjs → temporal-Dv6Qv-8k.mjs} +1 -1
  83. package/dist/testing/kv.test.mjs +1 -1
  84. package/dist/testing/mod.d.mts +2 -1
  85. package/dist/{types-RXm5YxpC.cjs → types-Be2zbyeG.cjs} +94 -2
  86. package/dist/{types-Dtyf8lVj.js → types-C_cNlNVK.js} +78 -4
  87. package/dist/utils/docloader.test.mjs +27 -3
  88. package/dist/utils/kv-cache.test.mjs +1 -1
  89. package/dist/utils/mod.cjs +1 -1
  90. package/dist/utils/mod.d.cts +1 -1
  91. package/dist/utils/mod.d.ts +1 -1
  92. package/dist/utils/mod.js +1 -1
  93. package/package.json +6 -6
  94. package/dist/owner-DTEqTzSr.mjs +0 -120
@@ -0,0 +1,465 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { t as assertEquals } from "../assert_equals-CBlRfA-_.mjs";
5
+ import "../std__assert-CxFT0MNt.mjs";
6
+ import { t as assert } from "../assert-CebeUf0C.mjs";
7
+ import { i as verifyMapLocalProof, s as verifyPortableObjectProofPolicy } from "../proof-BDxNVZ3E.mjs";
8
+ import { i as verifyCompoundProofDocuments, r as verifyCompoundPortableObjectProofs } from "../compound-proof-CJnWc6c-.mjs";
9
+ import { t as map_local_create_note_default } from "../map-local-create-note-B0SeXJms.mjs";
10
+ import { t as map_local_context_conflict_default } from "../map-local-context-conflict-DOYEEJ3u.mjs";
11
+ import { mockDocumentLoader, test } from "@fedify/fixture";
12
+ import { encodeMultibase } from "@fedify/vocab-runtime";
13
+ import serialize from "json-canon";
14
+ //#region src/sig/compound-proof-verification.test.ts
15
+ const limits = {
16
+ maxDepth: 16,
17
+ maxMaps: 32,
18
+ maxProofs: 8,
19
+ maxBytes: 16384
20
+ };
21
+ const options = {
22
+ contextLoader: mockDocumentLoader,
23
+ documentLoader() {
24
+ throw new TypeError("did:key must not use the document loader");
25
+ }
26
+ };
27
+ function createInlineProofContext() {
28
+ return {
29
+ id: "@id",
30
+ type: "@type",
31
+ Note: "https://www.w3.org/ns/activitystreams#Note",
32
+ attributedTo: {
33
+ "@id": "https://www.w3.org/ns/activitystreams#attributedTo",
34
+ "@type": "@id"
35
+ },
36
+ content: "https://www.w3.org/ns/activitystreams#content",
37
+ DataIntegrityProof: "https://w3id.org/security#DataIntegrityProof",
38
+ cryptosuite: "https://w3id.org/security#cryptosuite",
39
+ verificationMethod: {
40
+ "@id": "https://w3id.org/security#verificationMethod",
41
+ "@type": "@id"
42
+ },
43
+ proofPurpose: {
44
+ "@id": "https://w3id.org/security#proofPurpose",
45
+ "@type": "@vocab"
46
+ },
47
+ assertionMethod: "https://w3id.org/security#assertionMethod",
48
+ created: {
49
+ "@id": "http://purl.org/dc/terms/created",
50
+ "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
51
+ },
52
+ proofValue: "https://w3id.org/security#proofValue",
53
+ proof: "https://w3id.org/security#proof"
54
+ };
55
+ }
56
+ function asRecord(value) {
57
+ assert(value != null && typeof value === "object" && !Array.isArray(value));
58
+ return value;
59
+ }
60
+ async function sha256(value) {
61
+ return new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value)));
62
+ }
63
+ async function secureRawDocument(unsecuredDocument, privateJwk, verificationMethod, proofContext = unsecuredDocument["@context"]) {
64
+ const proofConfiguration = {
65
+ "@context": structuredClone(proofContext),
66
+ type: "DataIntegrityProof",
67
+ cryptosuite: "eddsa-jcs-2022",
68
+ verificationMethod,
69
+ proofPurpose: "assertionMethod",
70
+ created: "2023-02-24T23:36:38Z"
71
+ };
72
+ const proofDigest = await sha256(serialize(proofConfiguration));
73
+ const documentDigest = await sha256(serialize(unsecuredDocument));
74
+ const input = new Uint8Array(proofDigest.length + documentDigest.length);
75
+ input.set(proofDigest);
76
+ input.set(documentDigest, proofDigest.length);
77
+ const privateKey = await crypto.subtle.importKey("jwk", privateJwk, "Ed25519", false, ["sign"]);
78
+ const signature = new Uint8Array(await crypto.subtle.sign("Ed25519", privateKey, input));
79
+ return {
80
+ ...unsecuredDocument,
81
+ proof: {
82
+ ...proofConfiguration,
83
+ proofValue: new TextDecoder().decode(encodeMultibase("base58btc", signature))
84
+ }
85
+ };
86
+ }
87
+ test("verifyCompoundProofDocuments() verifies the recorded maps independently", async () => {
88
+ for (const fixture of [map_local_create_note_default, map_local_context_conflict_default]) {
89
+ const input = structuredClone(fixture.documents.finalSecuredCompound);
90
+ const result = await verifyCompoundProofDocuments(input, limits, options);
91
+ assertEquals(result.status, "ok");
92
+ if (result.status !== "ok") continue;
93
+ assert(result.verified);
94
+ assertEquals(result.documents.map(({ path, verified }) => ({
95
+ path,
96
+ verified
97
+ })), [{
98
+ path: "/object",
99
+ verified: true
100
+ }, {
101
+ path: "",
102
+ verified: true
103
+ }]);
104
+ assertEquals(result.documents.map((document) => document.verified ? document.key.id?.href : null), [fixture.keys.inner.verificationMethod, fixture.keys.outer.verificationMethod]);
105
+ assertEquals(result.snapshot, input);
106
+ }
107
+ });
108
+ test("verifyCompoundProofDocuments() does not verify an empty proof set", async () => {
109
+ const result = await verifyCompoundProofDocuments({
110
+ "@context": "https://www.w3.org/ns/activitystreams",
111
+ id: "https://example.com/notes/unsigned",
112
+ type: "Note"
113
+ }, limits, options);
114
+ assertEquals(result.status, "ok");
115
+ if (result.status !== "ok") return;
116
+ assertEquals(result.verified, false);
117
+ assertEquals(result.documents, []);
118
+ assertEquals(result.statistics.proofCount, 0);
119
+ });
120
+ test("verifyCompoundProofDocuments() reports tampering and proof replacement per map", async () => {
121
+ const tampered = structuredClone(map_local_create_note_default.documents.finalSecuredCompound);
122
+ asRecord(tampered.object).content = "Tampered after both proofs";
123
+ const tamperedResult = await verifyCompoundProofDocuments(tampered, limits, options);
124
+ assertEquals(tamperedResult.status, "ok");
125
+ if (tamperedResult.status === "ok") {
126
+ assertEquals(tamperedResult.verified, false);
127
+ assertEquals(tamperedResult.documents.map(({ path, verified }) => ({
128
+ path,
129
+ verified
130
+ })), [{
131
+ path: "/object",
132
+ verified: false
133
+ }, {
134
+ path: "",
135
+ verified: false
136
+ }]);
137
+ }
138
+ const replaced = structuredClone(map_local_create_note_default.documents.finalSecuredCompound);
139
+ asRecord(replaced.object).proof = structuredClone(map_local_create_note_default.documents.replacementInnerProof);
140
+ const replacedResult = await verifyCompoundProofDocuments(replaced, limits, options);
141
+ assertEquals(replacedResult.status, "ok");
142
+ if (replacedResult.status === "ok") {
143
+ assertEquals(replacedResult.verified, false);
144
+ assertEquals(replacedResult.documents.map(({ path, verified }) => ({
145
+ path,
146
+ verified
147
+ })), [{
148
+ path: "/object",
149
+ verified: true
150
+ }, {
151
+ path: "",
152
+ verified: false
153
+ }]);
154
+ }
155
+ });
156
+ test("verifyCompoundProofDocuments() requires a local context on nested secured maps", async () => {
157
+ const input = structuredClone(map_local_create_note_default.documents.finalSecuredCompound);
158
+ delete asRecord(input.object)["@context"];
159
+ const result = await verifyCompoundProofDocuments(input, limits, options);
160
+ assertEquals(result.status, "ok");
161
+ if (result.status !== "ok") return;
162
+ assertEquals(result.verified, false);
163
+ assertEquals(result.documents[0], {
164
+ path: "/object",
165
+ id: map_local_create_note_default.documents.securedInner.id,
166
+ depth: 1,
167
+ verified: false,
168
+ reason: { type: "missingContext" }
169
+ });
170
+ });
171
+ test("verifyCompoundProofDocuments() preserves proof aliases in the JCS input", async () => {
172
+ const secured = await secureRawDocument({
173
+ "@context": [
174
+ "https://www.w3.org/ns/activitystreams",
175
+ "https://w3id.org/security/data-integrity/v1",
176
+ { integrityProof: "https://w3id.org/security#proof" }
177
+ ],
178
+ id: "https://example.com/notes/alias-bound",
179
+ type: "Note",
180
+ content: "Alias-bound content",
181
+ integrityProof: "This value is signed data, not the direct proof."
182
+ }, map_local_create_note_default.keys.outer.testPrivateKeyJwk, map_local_create_note_default.keys.outer.verificationMethod);
183
+ const result = await verifyCompoundProofDocuments(secured, limits, options);
184
+ assertEquals(result.status, "ok");
185
+ if (result.status !== "ok") return;
186
+ assert(result.verified);
187
+ const tampered = structuredClone(secured);
188
+ tampered.integrityProof = "Changed after signing";
189
+ const tamperedResult = await verifyCompoundProofDocuments(tampered, limits, options);
190
+ assertEquals(tamperedResult.status, "ok");
191
+ if (tamperedResult.status === "ok") assertEquals(tamperedResult.verified, false);
192
+ });
193
+ test("verifyCompoundProofDocuments() preserves the received document context", async () => {
194
+ const proofContext = ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"];
195
+ const result = await verifyCompoundProofDocuments(await secureRawDocument({
196
+ "@context": [...proofContext, { ex: "https://example.com/ns#" }],
197
+ id: "https://example.com/notes/distinct-contexts",
198
+ type: "Note",
199
+ content: "The document context is part of the signed input."
200
+ }, map_local_create_note_default.keys.outer.testPrivateKeyJwk, map_local_create_note_default.keys.outer.verificationMethod, proofContext), limits, options);
201
+ assertEquals(result.status, "ok");
202
+ if (result.status !== "ok") return;
203
+ assert(result.verified);
204
+ });
205
+ test("verifyCompoundProofDocuments() authenticates only received JSON values", async () => {
206
+ const secured = await secureRawDocument({
207
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
208
+ id: "https://example.com/notes/attachment-array",
209
+ type: "Note",
210
+ attachment: ["https://example.com/images/1"]
211
+ }, map_local_create_note_default.keys.outer.testPrivateKeyJwk, map_local_create_note_default.keys.outer.verificationMethod);
212
+ const verified = await verifyCompoundProofDocuments(secured, limits, options);
213
+ assertEquals(verified.status, "ok");
214
+ if (verified.status !== "ok") return;
215
+ assert(verified.verified);
216
+ const tampered = structuredClone(secured);
217
+ tampered.attachment = "https://example.com/images/1";
218
+ const result = await verifyCompoundProofDocuments(tampered, limits, options);
219
+ assertEquals(result.status, "ok");
220
+ if (result.status === "ok") assertEquals(result.verified, false);
221
+ });
222
+ test("verifyCompoundProofDocuments() contains malformed contexts per map", async () => {
223
+ const valid = await secureRawDocument({
224
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
225
+ id: "https://example.com/notes/valid-sibling",
226
+ type: "Note",
227
+ content: "This sibling must still produce a result."
228
+ }, map_local_create_note_default.keys.outer.testPrivateKeyJwk, map_local_create_note_default.keys.outer.verificationMethod);
229
+ const malformed = structuredClone(valid);
230
+ malformed["@context"] = 42;
231
+ malformed.id = "https://example.com/notes/malformed-context";
232
+ malformed.proof = {
233
+ "@context": [],
234
+ "@type": ["https://w3id.org/security#DataIntegrityProof"],
235
+ "https://w3id.org/security#cryptosuite": [{ "@value": "eddsa-jcs-2022" }],
236
+ "https://w3id.org/security#verificationMethod": [{ "@id": map_local_create_note_default.keys.outer.verificationMethod }],
237
+ "https://w3id.org/security#proofPurpose": [{ "@id": "https://w3id.org/security#assertionMethod" }],
238
+ "http://purl.org/dc/terms/created": [{
239
+ "@value": "2023-02-24T23:36:38Z",
240
+ "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
241
+ }],
242
+ "https://w3id.org/security#proofValue": [{ "@value": asRecord(valid.proof).proofValue }]
243
+ };
244
+ const result = await verifyCompoundProofDocuments({
245
+ malformed,
246
+ valid
247
+ }, limits, options);
248
+ assertEquals(result.status, "ok");
249
+ if (result.status !== "ok") return;
250
+ assertEquals(result.verified, false);
251
+ assertEquals(result.documents.map((document) => ({
252
+ path: document.path,
253
+ verified: document.verified,
254
+ reason: document.verified ? void 0 : document.reason.type
255
+ })), [{
256
+ path: "/malformed",
257
+ verified: false,
258
+ reason: "invalidProof"
259
+ }, {
260
+ path: "/valid",
261
+ verified: true,
262
+ reason: void 0
263
+ }]);
264
+ });
265
+ test("verifyCompoundProofDocuments() does not fetch proof contexts", async () => {
266
+ let contextLoads = 0;
267
+ const result = await verifyCompoundProofDocuments({
268
+ "@context": "https://attacker.example/context",
269
+ id: "https://example.com/notes/remote-context",
270
+ type: "Note",
271
+ proof: {
272
+ type: "DataIntegrityProof",
273
+ cryptosuite: "eddsa-jcs-2022",
274
+ verificationMethod: map_local_create_note_default.keys.outer.verificationMethod,
275
+ proofPurpose: "assertionMethod",
276
+ created: "2023-02-24T23:36:38Z",
277
+ proofValue: map_local_create_note_default.proofValues.outer
278
+ }
279
+ }, limits, {
280
+ ...options,
281
+ contextLoader() {
282
+ contextLoads++;
283
+ throw new TypeError("unexpected context fetch");
284
+ }
285
+ });
286
+ assertEquals(result.status, "ok");
287
+ if (result.status === "ok") assertEquals(result.verified, false);
288
+ assertEquals(contextLoads, 0);
289
+ });
290
+ test("verifyCompoundPortableObjectProofs() applies policy to every portable map", async () => {
291
+ let contextLoads = 0;
292
+ const result = await verifyCompoundPortableObjectProofs(map_local_create_note_default.documents.finalSecuredCompound, limits, {
293
+ ...options,
294
+ contextLoader() {
295
+ contextLoads++;
296
+ throw new TypeError("unexpected context fetch");
297
+ }
298
+ });
299
+ assertEquals(result.status, "ok");
300
+ if (result.status !== "ok") return;
301
+ assert(result.verified);
302
+ assertEquals(contextLoads, 0);
303
+ assertEquals(result.portableObjects.map((document) => ({
304
+ path: document.path,
305
+ verified: document.verified,
306
+ keys: document.verified ? document.keys.map((key) => key.id?.href) : []
307
+ })), [{
308
+ path: "/object",
309
+ verified: true,
310
+ keys: [map_local_create_note_default.keys.inner.verificationMethod]
311
+ }, {
312
+ path: "",
313
+ verified: true,
314
+ keys: [map_local_create_note_default.keys.outer.verificationMethod]
315
+ }]);
316
+ });
317
+ test("verifyCompoundPortableObjectProofs() reports unsigned portable maps", async () => {
318
+ const context = ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"];
319
+ const result = await verifyCompoundPortableObjectProofs({
320
+ "@context": context,
321
+ id: map_local_create_note_default.documents.outerUnsecuredDocument.id,
322
+ type: "Create",
323
+ actor: map_local_create_note_default.documents.outerUnsecuredDocument.actor,
324
+ object: {
325
+ "@context": structuredClone(context),
326
+ id: map_local_create_note_default.documents.innerUnsecuredDocument.id,
327
+ type: "Note",
328
+ attributedTo: map_local_create_note_default.documents.innerUnsecuredDocument.attributedTo,
329
+ content: "Unsigned portable child"
330
+ }
331
+ }, limits, options);
332
+ assertEquals(result.status, "ok");
333
+ if (result.status !== "ok") return;
334
+ assertEquals(result.verified, false);
335
+ assertEquals(result.proofs, []);
336
+ assertEquals(result.portableObjects.map((document) => ({
337
+ path: document.path,
338
+ verified: document.verified,
339
+ reason: document.verified ? void 0 : document.reason.type
340
+ })), [{
341
+ path: "/object",
342
+ verified: false,
343
+ reason: "missingProof"
344
+ }, {
345
+ path: "",
346
+ verified: false,
347
+ reason: "missingProof"
348
+ }]);
349
+ });
350
+ test("verifyCompoundPortableObjectProofs() reports policy before crypto failure", async () => {
351
+ const mismatched = structuredClone(map_local_create_note_default.documents.finalSecuredCompound);
352
+ asRecord(mismatched.object).id = map_local_create_note_default.documents.outerUnsecuredDocument.id;
353
+ const result = await verifyCompoundPortableObjectProofs(mismatched, limits, options);
354
+ assertEquals(result.status, "ok");
355
+ if (result.status !== "ok") return;
356
+ assertEquals(result.verified, false);
357
+ const inner = result.portableObjects.find(({ path }) => path === "/object");
358
+ assert(inner != null && !inner.verified);
359
+ assertEquals(inner.reason.type, "verificationMethodMismatch");
360
+ });
361
+ test("verifyCompoundPortableObjectProofs() finds unsigned portable maps in arrays", async () => {
362
+ const result = await verifyCompoundPortableObjectProofs({
363
+ "@context": "https://www.w3.org/ns/activitystreams",
364
+ id: "https://social.example/activities/1",
365
+ type: "Create",
366
+ attachment: [{
367
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
368
+ id: map_local_create_note_default.documents.innerUnsecuredDocument.id,
369
+ type: "Note",
370
+ content: "Unsigned portable attachment"
371
+ }]
372
+ }, limits, options);
373
+ assertEquals(result.status, "ok");
374
+ if (result.status !== "ok") return;
375
+ assertEquals(result.verified, false);
376
+ assertEquals(result.portableObjects.map((document) => ({
377
+ path: document.path,
378
+ reason: document.verified ? void 0 : document.reason.type
379
+ })), [{
380
+ path: "/attachment/0",
381
+ reason: "missingProof"
382
+ }]);
383
+ });
384
+ test("verifyCompoundPortableObjectProofs() ignores context definitions", async () => {
385
+ const result = await verifyCompoundPortableObjectProofs({
386
+ "@context": { portable: {
387
+ "@id": map_local_create_note_default.documents.innerUnsecuredDocument.id,
388
+ "@context": { proof: "https://w3id.org/security#proof" }
389
+ } },
390
+ id: "https://social.example/objects/1",
391
+ type: "Note"
392
+ }, limits, options);
393
+ assertEquals(result.status, "ok");
394
+ if (result.status !== "ok") return;
395
+ assertEquals(result.verified, false);
396
+ assertEquals(result.proofs, []);
397
+ assertEquals(result.portableObjects, []);
398
+ });
399
+ test("verifyCompoundPortableObjectProofs() contains malformed portable IDs", async () => {
400
+ const result = await verifyCompoundPortableObjectProofs({
401
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
402
+ id: "ap://did%ZZkey/objects/1",
403
+ type: "Note"
404
+ }, limits, options);
405
+ assertEquals(result.status, "ok");
406
+ if (result.status !== "ok") return;
407
+ assertEquals(result.verified, false);
408
+ assertEquals(result.portableObjects.length, 1);
409
+ const [object] = result.portableObjects;
410
+ assert(!object.verified);
411
+ assertEquals(object.reason, { type: "invalidPortableObject" });
412
+ });
413
+ test("verifyPortableObjectProofPolicy() binds policy to the verified key", async () => {
414
+ const outerKey = await verifyMapLocalProof(map_local_create_note_default.documents.finalSecuredCompound, options);
415
+ assert(outerKey != null);
416
+ assertEquals(await verifyPortableObjectProofPolicy(map_local_create_note_default.documents.securedInner, outerKey, options), {
417
+ verified: false,
418
+ reason: {
419
+ type: "invalidProof",
420
+ proofIndex: 0
421
+ }
422
+ });
423
+ });
424
+ test("verifyPortableObjectProofPolicy() binds policy to the verified proof", async () => {
425
+ const context = createInlineProofContext();
426
+ context.proof = {
427
+ "@id": "https://w3id.org/security#proof",
428
+ "@context": { assertionMethod: "https://w3id.org/security#authentication" }
429
+ };
430
+ const secured = await secureRawDocument({
431
+ "@context": context,
432
+ id: map_local_create_note_default.documents.innerUnsecuredDocument.id,
433
+ type: "Note",
434
+ attributedTo: map_local_create_note_default.documents.innerUnsecuredDocument.attributedTo,
435
+ content: "Property-scoped proof purpose"
436
+ }, map_local_create_note_default.keys.inner.testPrivateKeyJwk, map_local_create_note_default.keys.inner.verificationMethod);
437
+ delete asRecord(secured.proof)["@context"];
438
+ const key = await verifyMapLocalProof(secured, options);
439
+ assert(key != null);
440
+ assertEquals(await verifyPortableObjectProofPolicy(secured, key, options), {
441
+ verified: false,
442
+ reason: {
443
+ type: "invalidProof",
444
+ proofIndex: 0
445
+ }
446
+ });
447
+ });
448
+ test("verifyCompoundPortableObjectProofs() reports the policy-validated ID", async () => {
449
+ const context = createInlineProofContext();
450
+ context.id = "https://example.com/id";
451
+ const result = await verifyCompoundPortableObjectProofs(await secureRawDocument({
452
+ "@context": context,
453
+ id: map_local_create_note_default.documents.outerUnsecuredDocument.id,
454
+ "@id": map_local_create_note_default.documents.innerUnsecuredDocument.id,
455
+ type: "Note",
456
+ content: "Conflicting raw ID"
457
+ }, map_local_create_note_default.keys.inner.testPrivateKeyJwk, map_local_create_note_default.keys.inner.verificationMethod), limits, options);
458
+ assertEquals(result.status, "ok");
459
+ if (result.status !== "ok") return;
460
+ assert(result.verified);
461
+ assertEquals(result.portableObjects.length, 1);
462
+ assertEquals(result.portableObjects[0].id, map_local_create_note_default.documents.innerUnsecuredDocument.id);
463
+ });
464
+ //#endregion
465
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";