@interop/was-conformance-suite 0.1.2 → 0.3.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 (70) hide show
  1. package/README.md +2 -2
  2. package/dist/suites/authz-ordering-api.d.ts +9 -0
  3. package/dist/suites/authz-ordering-api.d.ts.map +1 -0
  4. package/dist/suites/authz-ordering-api.js +286 -0
  5. package/dist/suites/authz-ordering-api.js.map +1 -0
  6. package/dist/suites/blinded-index-api.d.ts +11 -0
  7. package/dist/suites/blinded-index-api.d.ts.map +1 -0
  8. package/dist/suites/blinded-index-api.js +468 -0
  9. package/dist/suites/blinded-index-api.js.map +1 -0
  10. package/dist/suites/changes-query-api.d.ts.map +1 -1
  11. package/dist/suites/changes-query-api.js +61 -0
  12. package/dist/suites/changes-query-api.js.map +1 -1
  13. package/dist/suites/chunks-api.d.ts +27 -0
  14. package/dist/suites/chunks-api.d.ts.map +1 -0
  15. package/dist/suites/chunks-api.js +613 -0
  16. package/dist/suites/chunks-api.js.map +1 -0
  17. package/dist/suites/client-backends.d.ts.map +1 -1
  18. package/dist/suites/client-backends.js +23 -0
  19. package/dist/suites/client-backends.js.map +1 -1
  20. package/dist/suites/client-delegation.d.ts.map +1 -1
  21. package/dist/suites/client-delegation.js +12 -0
  22. package/dist/suites/client-delegation.js.map +1 -1
  23. package/dist/suites/client-export-import.d.ts.map +1 -1
  24. package/dist/suites/client-export-import.js +1 -0
  25. package/dist/suites/client-export-import.js.map +1 -1
  26. package/dist/suites/client-resources.d.ts.map +1 -1
  27. package/dist/suites/client-resources.js +30 -0
  28. package/dist/suites/client-resources.js.map +1 -1
  29. package/dist/suites/client-spaces.d.ts.map +1 -1
  30. package/dist/suites/client-spaces.js +27 -0
  31. package/dist/suites/client-spaces.js.map +1 -1
  32. package/dist/suites/collection-api.d.ts.map +1 -1
  33. package/dist/suites/collection-api.js +151 -0
  34. package/dist/suites/collection-api.js.map +1 -1
  35. package/dist/suites/conditional-requests-api.d.ts +11 -0
  36. package/dist/suites/conditional-requests-api.d.ts.map +1 -0
  37. package/dist/suites/conditional-requests-api.js +444 -0
  38. package/dist/suites/conditional-requests-api.js.map +1 -0
  39. package/dist/suites/digest-api.d.ts +7 -0
  40. package/dist/suites/digest-api.d.ts.map +1 -0
  41. package/dist/suites/digest-api.js +157 -0
  42. package/dist/suites/digest-api.js.map +1 -0
  43. package/dist/suites/encryption-marker-api.d.ts.map +1 -1
  44. package/dist/suites/encryption-marker-api.js +132 -8
  45. package/dist/suites/encryption-marker-api.js.map +1 -1
  46. package/dist/suites/index.d.ts +8 -1
  47. package/dist/suites/index.d.ts.map +1 -1
  48. package/dist/suites/index.js +16 -2
  49. package/dist/suites/index.js.map +1 -1
  50. package/dist/suites/invocation-target-api.d.ts +12 -0
  51. package/dist/suites/invocation-target-api.d.ts.map +1 -0
  52. package/dist/suites/invocation-target-api.js +252 -0
  53. package/dist/suites/invocation-target-api.js.map +1 -0
  54. package/dist/suites/policy-api.d.ts.map +1 -1
  55. package/dist/suites/policy-api.js +23 -0
  56. package/dist/suites/policy-api.js.map +1 -1
  57. package/dist/suites/resource-api.d.ts.map +1 -1
  58. package/dist/suites/resource-api.js +269 -0
  59. package/dist/suites/resource-api.js.map +1 -1
  60. package/dist/suites/server.d.ts.map +1 -1
  61. package/dist/suites/server.js +1 -0
  62. package/dist/suites/server.js.map +1 -1
  63. package/dist/suites/spaces-api.d.ts.map +1 -1
  64. package/dist/suites/spaces-api.js +565 -0
  65. package/dist/suites/spaces-api.js.map +1 -1
  66. package/dist/suites/write-validation-api.d.ts +7 -0
  67. package/dist/suites/write-validation-api.d.ts.map +1 -0
  68. package/dist/suites/write-validation-api.js +186 -0
  69. package/dist/suites/write-validation-api.js.map +1 -0
  70. package/package.json +8 -3
@@ -0,0 +1,444 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * WAS conformance tests -- conditional requests & caching (spec "Caching" and
6
+ * "Conditional Requests").
7
+ *
8
+ * Conditional writes are an OPTIONAL feature: a backend advertises the
9
+ * `conditional-writes` token in its Backend description, and only then are the
10
+ * 412 semantics required (MUST). This suite is therefore not marked
11
+ * suite-level optional. Its `setup` probes the Space's default backend for the
12
+ * token and stashes the result; the write tests call `ctx.skip(...)` when the
13
+ * backend does not advertise it, while the two ETag tests reflect the weaker
14
+ * `SHOULD emit ETag` guidance and are marked `optional: true`.
15
+ *
16
+ * The tests work at the Resource level in a plain JSON Collection, driving raw
17
+ * `ZcapClient.request()` calls so the `If-Match` / `If-None-Match` precondition
18
+ * headers can be attached directly. Those preconditions describe the request
19
+ * rather than the capability target, so they need not be covered by the
20
+ * signature; the server reads them off the request headers after authorization.
21
+ */
22
+ import { signCapabilityInvocation } from '@interop/http-signature-zcap-invoke';
23
+ import assert from '../harness/assert.js';
24
+ /**
25
+ * Asserts a rejected conditional write reports a precondition failure: status
26
+ * 412, an `application/problem+json` body, and the `precondition-failed` type.
27
+ *
28
+ * @param expectedError {any} the error thrown by `ZcapClient.request()`
29
+ */
30
+ function assertPreconditionFailed(expectedError) {
31
+ assert.ok(expectedError, 'expected the conditional write to fail with 412');
32
+ assert.equal(expectedError.response.status, 412);
33
+ assert.match(expectedError.response.headers.get('content-type'), /application\/problem\+json/);
34
+ assert.equal(expectedError.data.type, 'https://wallet.storage/spec#precondition-failed');
35
+ }
36
+ /**
37
+ * Asserts a rejected request carries the privacy-merged `not-found` mask:
38
+ * status 404, `application/problem+json`, and the merged `not-found` type. An
39
+ * under-authorized conditional write MUST surface this mask, never a 412 that
40
+ * would confirm the target exists.
41
+ *
42
+ * @param expectedError {any} the error thrown by `ZcapClient.request()`
43
+ */
44
+ function assertNotFoundMask(expectedError) {
45
+ assert.ok(expectedError, 'expected the under-authorized write to fail');
46
+ assert.equal(expectedError.response.status, 404);
47
+ assert.match(expectedError.response.headers.get('content-type'), /application\/problem\+json/);
48
+ assert.equal(expectedError.data.type, 'https://wallet.storage/spec#not-found');
49
+ }
50
+ /**
51
+ * Signs and sends a bodyless root HEAD invocation via raw `fetch`, returning
52
+ * the response so headers (the `ETag`) can be read. The low-level primitive is
53
+ * used because the high-level client parses the response body as JSON, which a
54
+ * bodyless HEAD 200 has none of.
55
+ *
56
+ * @param options {object}
57
+ * @param options.url {string} the Resource URL to HEAD
58
+ * @param options.invocationSigner {ISigner} the caller's signer
59
+ * @returns {Promise<Response>}
60
+ */
61
+ async function headResource({ url, invocationSigner }) {
62
+ const signatureHeaders = await signCapabilityInvocation({
63
+ url,
64
+ method: 'HEAD',
65
+ headers: { date: new Date().toUTCString() },
66
+ invocationSigner,
67
+ capabilityAction: 'HEAD'
68
+ });
69
+ return fetch(url, {
70
+ method: 'HEAD',
71
+ headers: signatureHeaders
72
+ });
73
+ }
74
+ export const conditionalRequestsApi = {
75
+ id: 'conditional-requests-api',
76
+ name: 'Conditional requests & caching',
77
+ specRefs: [
78
+ 'https://wallet.storage/spec#caching',
79
+ 'https://wallet.storage/spec#conditional-requests'
80
+ ],
81
+ setup: async (ctx) => {
82
+ const alice = { ...ctx.actors.alice };
83
+ const bob = { ...ctx.actors.bob };
84
+ alice.space1 = { id: ctx.generateId() };
85
+ await ctx.createSpace({
86
+ spaceDescription: {
87
+ id: alice.space1.id,
88
+ name: "Alice's Conditional-Requests Space",
89
+ controller: alice.did
90
+ },
91
+ rootClient: alice.rootClient
92
+ });
93
+ // A plain JSON Collection to hold the per-test Resources.
94
+ await alice.rootClient.request({
95
+ url: new URL(`/space/${alice.space1.id}/`, ctx.serverUrl).toString(),
96
+ method: 'POST',
97
+ action: 'POST',
98
+ json: { id: 'items', name: 'Items' }
99
+ });
100
+ const collectionUrl = new URL(`/space/${alice.space1.id}/items/`, ctx.serverUrl).toString();
101
+ // Probe the Space's default backend for the `conditional-writes` feature
102
+ // token. The write tests below skip when it is absent (the feature is
103
+ // OPTIONAL); the 412 semantics are only required once advertised.
104
+ const backendsResponse = await alice.rootClient.request({
105
+ url: new URL(`/space/${alice.space1.id}/backends`, ctx.serverUrl).toString(),
106
+ method: 'GET'
107
+ });
108
+ const backends = backendsResponse.data;
109
+ const defaultBackend = backends.find(backend => backend.id === 'default');
110
+ const conditionalWritesSupported = Boolean(defaultBackend?.features?.includes('conditional-writes'));
111
+ return {
112
+ alice,
113
+ bob,
114
+ collectionUrl,
115
+ conditionalWritesSupported,
116
+ defaultBackend
117
+ };
118
+ },
119
+ teardown: async (ctx, state) => {
120
+ const { alice } = state;
121
+ try {
122
+ await alice.rootClient.request({
123
+ url: new URL(`/space/${alice.space1.id}`, ctx.serverUrl).toString(),
124
+ method: 'DELETE'
125
+ });
126
+ }
127
+ catch {
128
+ /* best-effort cleanup */
129
+ }
130
+ },
131
+ tests: [
132
+ {
133
+ id: 'conditional.backend-advertises-conditional-writes',
134
+ name: '[root] the default backend descriptor advertises the ' +
135
+ '`conditional-writes` feature',
136
+ specRefs: [
137
+ 'https://wallet.storage/spec#conditional-requests',
138
+ 'https://wallet.storage/spec#backend-data-model'
139
+ ],
140
+ run: async (ctx, state) => {
141
+ const { defaultBackend } = state;
142
+ assert.ok(defaultBackend, 'expected a `default` backend descriptor');
143
+ assert.ok(Array.isArray(defaultBackend.features), 'expected the backend descriptor to carry a `features` array');
144
+ assert.ok(defaultBackend.features.includes('conditional-writes'), 'expected `features` to include the `conditional-writes` token');
145
+ }
146
+ },
147
+ {
148
+ id: 'conditional.stale-if-match-412',
149
+ name: '[root] a PUT with a stale `If-Match` performs no write and returns ' +
150
+ '412 precondition-failed',
151
+ specRefs: [
152
+ 'https://wallet.storage/spec#conditional-requests',
153
+ 'https://wallet.storage/spec#precondition-failed'
154
+ ],
155
+ run: async (ctx, state) => {
156
+ const { alice, collectionUrl, conditionalWritesSupported } = state;
157
+ if (!conditionalWritesSupported) {
158
+ ctx.skip('backend does not advertise conditional-writes');
159
+ }
160
+ const resourceUrl = `${collectionUrl}stale-if-match`;
161
+ // Create (version 1) then update (version 2): the ETag the client
162
+ // captured at version 1 is now stale.
163
+ const created = await alice.rootClient.request({
164
+ url: resourceUrl,
165
+ method: 'PUT',
166
+ action: 'PUT',
167
+ json: { name: 'v1' }
168
+ });
169
+ const staleEtag = created.headers.get('etag');
170
+ assert.equal(staleEtag, '"1"');
171
+ await alice.rootClient.request({
172
+ url: resourceUrl,
173
+ method: 'PUT',
174
+ action: 'PUT',
175
+ json: { name: 'v2' }
176
+ });
177
+ // A PUT carrying the stale validator MUST NOT write and MUST 412.
178
+ let expectedError;
179
+ try {
180
+ await alice.rootClient.request({
181
+ url: resourceUrl,
182
+ method: 'PUT',
183
+ action: 'PUT',
184
+ json: { name: 'v3' },
185
+ headers: { 'if-match': staleEtag }
186
+ });
187
+ }
188
+ catch (err) {
189
+ expectedError = err;
190
+ }
191
+ assertPreconditionFailed(expectedError);
192
+ // The stored content is unchanged: still the version-2 write.
193
+ const check = await alice.rootClient.request({
194
+ url: resourceUrl,
195
+ method: 'GET'
196
+ });
197
+ assert.equal(check.status, 200);
198
+ assert.equal(check.data.name, 'v2');
199
+ assert.equal(check.headers.get('etag'), '"2"');
200
+ }
201
+ },
202
+ {
203
+ id: 'conditional.if-none-match-existing-412',
204
+ name: '[root] a PUT with `If-None-Match: *` against an existing Resource ' +
205
+ 'returns 412 and does not overwrite',
206
+ specRefs: [
207
+ 'https://wallet.storage/spec#conditional-requests',
208
+ 'https://wallet.storage/spec#precondition-failed'
209
+ ],
210
+ run: async (ctx, state) => {
211
+ const { alice, collectionUrl, conditionalWritesSupported } = state;
212
+ if (!conditionalWritesSupported) {
213
+ ctx.skip('backend does not advertise conditional-writes');
214
+ }
215
+ const resourceUrl = `${collectionUrl}if-none-match-existing`;
216
+ await alice.rootClient.request({
217
+ url: resourceUrl,
218
+ method: 'PUT',
219
+ action: 'PUT',
220
+ json: { name: 'original' }
221
+ });
222
+ // create-if-absent against an existing Resource MUST 412.
223
+ let expectedError;
224
+ try {
225
+ await alice.rootClient.request({
226
+ url: resourceUrl,
227
+ method: 'PUT',
228
+ action: 'PUT',
229
+ json: { name: 'overwrite' },
230
+ headers: { 'if-none-match': '*' }
231
+ });
232
+ }
233
+ catch (err) {
234
+ expectedError = err;
235
+ }
236
+ assertPreconditionFailed(expectedError);
237
+ // The original content survives.
238
+ const check = await alice.rootClient.request({
239
+ url: resourceUrl,
240
+ method: 'GET'
241
+ });
242
+ assert.equal(check.status, 200);
243
+ assert.equal(check.data.name, 'original');
244
+ }
245
+ },
246
+ {
247
+ id: 'conditional.current-if-match-succeeds',
248
+ name: '[root] a PUT with the current `If-Match` succeeds and advances the ' +
249
+ 'ETag',
250
+ specRefs: ['https://wallet.storage/spec#conditional-requests'],
251
+ run: async (ctx, state) => {
252
+ const { alice, collectionUrl, conditionalWritesSupported } = state;
253
+ if (!conditionalWritesSupported) {
254
+ ctx.skip('backend does not advertise conditional-writes');
255
+ }
256
+ const resourceUrl = `${collectionUrl}current-if-match`;
257
+ const created = await alice.rootClient.request({
258
+ url: resourceUrl,
259
+ method: 'PUT',
260
+ action: 'PUT',
261
+ json: { name: 'v1' }
262
+ });
263
+ const currentEtag = created.headers.get('etag');
264
+ assert.equal(currentEtag, '"1"');
265
+ // The matching precondition is satisfied: the write proceeds and the
266
+ // strong validator advances.
267
+ const updated = await alice.rootClient.request({
268
+ url: resourceUrl,
269
+ method: 'PUT',
270
+ action: 'PUT',
271
+ json: { name: 'v2' },
272
+ headers: { 'if-match': currentEtag }
273
+ });
274
+ assert.equal(updated.status, 204);
275
+ assert.equal(updated.headers.get('etag'), '"2"');
276
+ const check = await alice.rootClient.request({
277
+ url: resourceUrl,
278
+ method: 'GET'
279
+ });
280
+ assert.equal(check.data.name, 'v2');
281
+ }
282
+ },
283
+ {
284
+ id: 'conditional.if-none-match-create-succeeds',
285
+ name: '[root] a PUT with `If-None-Match: *` on a fresh id creates the ' +
286
+ 'Resource',
287
+ specRefs: ['https://wallet.storage/spec#conditional-requests'],
288
+ run: async (ctx, state) => {
289
+ const { alice, collectionUrl, conditionalWritesSupported } = state;
290
+ if (!conditionalWritesSupported) {
291
+ ctx.skip('backend does not advertise conditional-writes');
292
+ }
293
+ const resourceUrl = `${collectionUrl}if-none-match-create`;
294
+ // create-if-absent on an absent id proceeds.
295
+ const created = await alice.rootClient.request({
296
+ url: resourceUrl,
297
+ method: 'PUT',
298
+ action: 'PUT',
299
+ json: { name: 'fresh' },
300
+ headers: { 'if-none-match': '*' }
301
+ });
302
+ assert.equal(created.status, 204);
303
+ assert.equal(created.headers.get('etag'), '"1"');
304
+ const check = await alice.rootClient.request({
305
+ url: resourceUrl,
306
+ method: 'GET'
307
+ });
308
+ assert.equal(check.status, 200);
309
+ assert.equal(check.data.name, 'fresh');
310
+ }
311
+ },
312
+ {
313
+ id: 'conditional.authz-before-precondition-404',
314
+ name: '[root] an under-authorized conditional PUT yields the 404 mask, ' +
315
+ 'never 412',
316
+ specRefs: [
317
+ 'https://wallet.storage/spec#conditional-requests',
318
+ 'https://wallet.storage/spec#error-handling',
319
+ 'https://wallet.storage/spec#not-found'
320
+ ],
321
+ run: async (ctx, state) => {
322
+ const { alice, bob, collectionUrl, conditionalWritesSupported } = state;
323
+ if (!conditionalWritesSupported) {
324
+ ctx.skip('backend does not advertise conditional-writes');
325
+ }
326
+ const resourceUrl = `${collectionUrl}authz-before-precondition`;
327
+ // Alice owns an existing Resource.
328
+ await alice.rootClient.request({
329
+ url: resourceUrl,
330
+ method: 'PUT',
331
+ action: 'PUT',
332
+ json: { name: 'alice-owned' }
333
+ });
334
+ // Bob -- who has no privilege over Alice's Space -- sends a conditional
335
+ // PUT with a stale validator. Authorization MUST run before the
336
+ // precondition, so Bob gets the 404 mask, never the existence-revealing
337
+ // 412.
338
+ let expectedError;
339
+ try {
340
+ await bob.rootClient.request({
341
+ url: resourceUrl,
342
+ method: 'PUT',
343
+ action: 'PUT',
344
+ json: { name: 'bob-overwrite' },
345
+ headers: { 'if-match': '"1"' }
346
+ });
347
+ }
348
+ catch (err) {
349
+ expectedError = err;
350
+ }
351
+ assertNotFoundMask(expectedError);
352
+ // The write was not performed: Alice still sees her content.
353
+ const check = await alice.rootClient.request({
354
+ url: resourceUrl,
355
+ method: 'GET'
356
+ });
357
+ assert.equal(check.status, 200);
358
+ assert.equal(check.data.name, 'alice-owned');
359
+ }
360
+ },
361
+ {
362
+ id: 'conditional.get-carries-etag',
363
+ name: '[root] GET of a Resource carries an ETag that changes after the ' +
364
+ 'content changes',
365
+ optional: true,
366
+ specRefs: [
367
+ 'https://wallet.storage/spec#caching',
368
+ 'https://wallet.storage/spec#conditional-requests'
369
+ ],
370
+ run: async (ctx, state) => {
371
+ const { alice, collectionUrl } = state;
372
+ const resourceUrl = `${collectionUrl}get-etag`;
373
+ await alice.rootClient.request({
374
+ url: resourceUrl,
375
+ method: 'PUT',
376
+ action: 'PUT',
377
+ json: { name: 'v1' }
378
+ });
379
+ const first = await alice.rootClient.request({
380
+ url: resourceUrl,
381
+ method: 'GET'
382
+ });
383
+ const firstEtag = first.headers.get('etag');
384
+ assert.ok(firstEtag, 'expected GET to carry an ETag');
385
+ await alice.rootClient.request({
386
+ url: resourceUrl,
387
+ method: 'PUT',
388
+ action: 'PUT',
389
+ json: { name: 'v2' }
390
+ });
391
+ const second = await alice.rootClient.request({
392
+ url: resourceUrl,
393
+ method: 'GET'
394
+ });
395
+ const secondEtag = second.headers.get('etag');
396
+ assert.ok(secondEtag, 'expected GET to carry an ETag');
397
+ // A strong validator changes whenever the stored content changes.
398
+ assert.notEqual(secondEtag, firstEtag);
399
+ }
400
+ },
401
+ {
402
+ id: 'conditional.head-carries-etag',
403
+ name: '[root] HEAD of a Resource carries an ETag that changes after the ' +
404
+ 'content changes',
405
+ optional: true,
406
+ specRefs: [
407
+ 'https://wallet.storage/spec#caching',
408
+ 'https://wallet.storage/spec#conditional-requests'
409
+ ],
410
+ run: async (ctx, state) => {
411
+ const { alice, collectionUrl } = state;
412
+ const resourceUrl = `${collectionUrl}head-etag`;
413
+ await alice.rootClient.request({
414
+ url: resourceUrl,
415
+ method: 'PUT',
416
+ action: 'PUT',
417
+ json: { name: 'v1' }
418
+ });
419
+ const first = await headResource({
420
+ url: resourceUrl,
421
+ invocationSigner: alice.rootClient.invocationSigner
422
+ });
423
+ assert.equal(first.status, 200);
424
+ const firstEtag = first.headers.get('etag');
425
+ assert.ok(firstEtag, 'expected HEAD to carry an ETag');
426
+ await alice.rootClient.request({
427
+ url: resourceUrl,
428
+ method: 'PUT',
429
+ action: 'PUT',
430
+ json: { name: 'v2' }
431
+ });
432
+ const second = await headResource({
433
+ url: resourceUrl,
434
+ invocationSigner: alice.rootClient.invocationSigner
435
+ });
436
+ assert.equal(second.status, 200);
437
+ const secondEtag = second.headers.get('etag');
438
+ assert.ok(secondEtag, 'expected HEAD to carry an ETag');
439
+ assert.notEqual(secondEtag, firstEtag);
440
+ }
441
+ }
442
+ ]
443
+ };
444
+ //# sourceMappingURL=conditional-requests-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conditional-requests-api.js","sourceRoot":"","sources":["../../src/suites/conditional-requests-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { Suite } from '../harness/types.js';
2
+ interface State {
3
+ alice: any;
4
+ }
5
+ export declare const digestApi: Suite<State>;
6
+ export {};
7
+ //# sourceMappingURL=digest-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digest-api.d.ts","sourceRoot":"","sources":["../../src/suites/digest-api.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAEhD,UAAU,KAAK;IACb,KAAK,EAAE,GAAG,CAAA;CACX;AAwBD,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,KAAK,CAsIlC,CAAA"}
@@ -0,0 +1,157 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * WAS conformance tests -- request-body integrity (Digest) negatives.
6
+ *
7
+ * The spec's "Request Body Integrity (Digest Header)" profile binds a request
8
+ * body to its HTTP Signature: for any request carrying a `Content-Type`, the
9
+ * server MUST require `digest` among the signature's covered headers, and
10
+ * SHOULD independently recompute the digest of the received body and compare
11
+ * it. Both failures are rejected with `invalid-authorization-header` (400).
12
+ * A well-behaved client never produces such requests, so these tests sign
13
+ * with the low-level `signCapabilityInvocation` primitive and send the
14
+ * malformed request with a raw `fetch`.
15
+ */
16
+ import { createHeaderValue } from '@interop/http-digest-header';
17
+ import { signCapabilityInvocation } from '@interop/http-signature-zcap-invoke';
18
+ import assert from '../harness/assert.js';
19
+ /**
20
+ * Asserts a rejected request reports the digest-binding failure: status 400,
21
+ * `application/problem+json`, and the `invalid-authorization-header` type
22
+ * (the registry's single type for all authorization-header failures).
23
+ *
24
+ * @param response {Response} the raw fetch response
25
+ */
26
+ async function assertInvalidAuthorizationHeader(response) {
27
+ assert.equal(response.status, 400);
28
+ assert.match(response.headers.get('content-type') ?? '', /application\/problem\+json/);
29
+ const problem = await response.json();
30
+ assert.equal(problem.type, 'https://wallet.storage/spec#invalid-authorization-header');
31
+ }
32
+ export const digestApi = {
33
+ id: 'digest-api',
34
+ name: 'Request body integrity (Digest) negatives',
35
+ specRefs: [
36
+ 'https://wallet.storage/spec#request-body-integrity-digest-header'
37
+ ],
38
+ setup: async (ctx) => {
39
+ const alice = { ...ctx.actors.alice };
40
+ alice.space1 = { id: ctx.generateId() };
41
+ await ctx.createSpace({
42
+ spaceDescription: {
43
+ id: alice.space1.id,
44
+ name: "Alice's Digest Space",
45
+ controller: alice.did
46
+ },
47
+ rootClient: alice.rootClient
48
+ });
49
+ await alice.rootClient.request({
50
+ url: new URL(`/space/${alice.space1.id}/`, ctx.serverUrl).toString(),
51
+ method: 'POST',
52
+ action: 'POST',
53
+ json: { id: 'docs', name: 'Documents' }
54
+ });
55
+ return { alice };
56
+ },
57
+ teardown: async (ctx, state) => {
58
+ const { alice } = state;
59
+ try {
60
+ await alice.rootClient.request({
61
+ url: new URL(`/space/${alice.space1.id}`, ctx.serverUrl).toString(),
62
+ method: 'DELETE'
63
+ });
64
+ }
65
+ catch {
66
+ /* best-effort cleanup */
67
+ }
68
+ },
69
+ tests: [
70
+ {
71
+ id: 'digest.signature-not-covering-digest',
72
+ name: '[root] a signed body request whose signature does not cover the ' +
73
+ '`digest` header is rejected with 400 invalid-authorization-header',
74
+ specRefs: [
75
+ 'https://wallet.storage/spec#request-body-integrity-digest-header',
76
+ 'https://wallet.storage/spec#invalid-authorization-header'
77
+ ],
78
+ run: async (ctx, state) => {
79
+ const { serverUrl } = ctx;
80
+ const { alice } = state;
81
+ const url = new URL(`/space/${alice.space1.id}/docs/${ctx.generateId()}`, serverUrl).toString();
82
+ const body = JSON.stringify({ name: 'Uncovered Digest Probe' });
83
+ // Sign as if the request were bodyless: a valid signature whose
84
+ // covered headers omit `content-type` and `digest`.
85
+ const signatureHeaders = await signCapabilityInvocation({
86
+ url,
87
+ method: 'PUT',
88
+ headers: { date: new Date().toUTCString() },
89
+ invocationSigner: alice.rootClient.invocationSigner,
90
+ capabilityAction: 'PUT'
91
+ });
92
+ // The `Digest` header itself is present and correct for the body sent
93
+ // -- only its signature coverage is missing, which the server MUST
94
+ // require for any request carrying a `Content-Type`.
95
+ const digest = await createHeaderValue({
96
+ data: body,
97
+ algorithm: 'sha256',
98
+ useMultihash: true
99
+ });
100
+ const response = await fetch(url, {
101
+ method: 'PUT',
102
+ headers: {
103
+ ...signatureHeaders,
104
+ 'content-type': 'application/json',
105
+ digest
106
+ },
107
+ body
108
+ });
109
+ await assertInvalidAuthorizationHeader(response);
110
+ }
111
+ },
112
+ {
113
+ id: 'digest.body-tampered-after-signing',
114
+ name: '[root] a request whose body does not match its signed `digest` ' +
115
+ '(tampered after signing) is rejected with 400',
116
+ // Independent recomputation of the received body's digest is a SHOULD.
117
+ optional: true,
118
+ specRefs: [
119
+ 'https://wallet.storage/spec#request-body-integrity-digest-header',
120
+ 'https://wallet.storage/spec#invalid-authorization-header'
121
+ ],
122
+ run: async (ctx, state) => {
123
+ const { serverUrl } = ctx;
124
+ const { alice } = state;
125
+ const url = new URL(`/space/${alice.space1.id}/docs/${ctx.generateId()}`, serverUrl).toString();
126
+ // A fully well-formed signing of the original body: the signature
127
+ // covers `digest`, and the `Digest` header matches the original.
128
+ const signatureHeaders = await signCapabilityInvocation({
129
+ url,
130
+ method: 'PUT',
131
+ headers: { date: new Date().toUTCString() },
132
+ json: { name: 'Original Body' },
133
+ invocationSigner: alice.rootClient.invocationSigner,
134
+ capabilityAction: 'PUT'
135
+ });
136
+ // ...but the body actually sent was swapped after signing.
137
+ const response = await fetch(url, {
138
+ method: 'PUT',
139
+ headers: signatureHeaders,
140
+ body: JSON.stringify({ name: 'Tampered Body' })
141
+ });
142
+ await assertInvalidAuthorizationHeader(response);
143
+ // The tampered write must not have been performed.
144
+ let expectedError;
145
+ try {
146
+ await alice.rootClient.request({ url, method: 'GET' });
147
+ }
148
+ catch (err) {
149
+ expectedError = err;
150
+ }
151
+ assert.ok(expectedError, 'expected the tampered resource to not exist');
152
+ assert.equal(expectedError.response.status, 404);
153
+ }
154
+ }
155
+ ]
156
+ };
157
+ //# sourceMappingURL=digest-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digest-api.js","sourceRoot":"","sources":["../../src/suites/digest-api.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"encryption-marker-api.d.ts","sourceRoot":"","sources":["../../src/suites/encryption-marker-api.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAgBhD,UAAU,KAAK;IACb,KAAK,EAAE,GAAG,CAAA;IACV,GAAG,EAAE,GAAG,CAAA;IACR,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;CACjD;AAED,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,KAAK,CAoS5C,CAAA"}
1
+ {"version":3,"file":"encryption-marker-api.d.ts","sourceRoot":"","sources":["../../src/suites/encryption-marker-api.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAgBhD,UAAU,KAAK;IACb,KAAK,EAAE,GAAG,CAAA;IACV,GAAG,EAAE,GAAG,CAAA;IACR,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;CACjD;AAED,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,KAAK,CAqb5C,CAAA"}