@kanonak-protocol/sdk 5.37.0 → 6.0.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 (43) hide show
  1. package/dist/auth/CredentialProvider.d.ts +69 -49
  2. package/dist/auth/ExecCredentialProvider.d.ts +55 -0
  3. package/dist/auth/OAuthDiscovery.d.ts +38 -10
  4. package/dist/auth/TokenCredentialProvider.d.ts +26 -6
  5. package/dist/auth/challenges.d.ts +17 -0
  6. package/dist/auth/credentialAnswer.d.ts +34 -0
  7. package/dist/auth/credentialConfig.d.ts +32 -0
  8. package/dist/auth/credentialedFetch.d.ts +33 -22
  9. package/dist/auth/index.d.ts +10 -4
  10. package/dist/auth/scope.d.ts +30 -0
  11. package/dist/browser.d.ts +4 -2
  12. package/dist/browser.js +1 -1
  13. package/dist/chunk-22DSABP6.js +1 -0
  14. package/dist/{chunk-HKX27KBZ.js → chunk-7F2EAT5Y.js} +1 -1
  15. package/dist/{chunk-LE26SEJU.js → chunk-A3HRXFVX.js} +1 -1
  16. package/dist/{chunk-4CXAZH3C.js → chunk-ADSDFJUO.js} +1 -1
  17. package/dist/{chunk-W5AOJ5SJ.js → chunk-GKIX3OD6.js} +1 -1
  18. package/dist/chunk-HKQTB65D.js +2 -0
  19. package/dist/{chunk-R4QCVB5M.js → chunk-O6SOU5Y2.js} +1 -1
  20. package/dist/chunk-PNBTIGX7.js +2 -0
  21. package/dist/{chunk-DONOB6RA.js → chunk-SBO34ENZ.js} +1 -1
  22. package/dist/chunk-TYVZXABI.js +155 -0
  23. package/dist/http/HttpError.d.ts +38 -0
  24. package/dist/http/kanonakFetch.d.ts +5 -2
  25. package/dist/index.d.ts +8 -2
  26. package/dist/index.js +2 -1
  27. package/dist/parsing/index.js +1 -1
  28. package/dist/producer/index.js +1 -1
  29. package/dist/reasoning/index.js +1 -1
  30. package/dist/repositories/browser.js +1 -1
  31. package/dist/repositories/index.js +1 -1
  32. package/dist/search/index.js +1 -1
  33. package/dist/server/index.js +5 -5
  34. package/dist/transformations/index.js +2 -2
  35. package/dist/transformations/v3/CompiledTransformationV3.d.ts +28 -27
  36. package/dist/transformations/v3/TransformationUriConstantsV3.d.ts +27 -0
  37. package/dist/validation/index.js +1 -1
  38. package/package.json +5 -4
  39. package/vectors/credential-helper-vectors.json +1039 -0
  40. package/dist/chunk-2WUZKSLB.js +0 -2
  41. package/dist/chunk-3353RWDC.js +0 -1
  42. package/dist/chunk-EG47OOJC.js +0 -155
  43. package/dist/chunk-OOYGILH4.js +0 -2
@@ -0,0 +1,1039 @@
1
+ {
2
+ "protocolVersion": 1,
3
+ "description": "Conformance vectors for the exec credential helper protocol (kanonak-protocol/typescript#126). The CLI runs `<helper command line> get` or `... erase`, writes one JSON request to stdin and reads one JSON answer from stdout; a non-zero exit is an error and never falls back to anonymous. requests: the exact JSON the CLI sends. answers/eraseAnswers: a helper's answer to `request` at Unix time `now`, and whether the CLI accepts it (with the answer it keeps) or refuses it (with the problem code). scopes: whether a credential's scope contains a URL, which is where the CLI attaches it; invalidScopes are refused. challenges: how a WWW-Authenticate value is parsed into the request's challenges; malformedChallenges are errors. Cases are only added, never changed.",
4
+ "requests": [
5
+ {
6
+ "id": "get-pre-emptive",
7
+ "description": "The transport knows the origin is gated (RFC 9728 metadata, or an earlier challenge) and asks before sending: no challenges.",
8
+ "operation": "get",
9
+ "request": {
10
+ "version": 1,
11
+ "operation": "get",
12
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
13
+ "method": "GET",
14
+ "interactive": false
15
+ }
16
+ },
17
+ {
18
+ "id": "get-after-challenge",
19
+ "description": "A 401's challenges, parsed, in the server's order.",
20
+ "operation": "get",
21
+ "request": {
22
+ "version": 1,
23
+ "operation": "get",
24
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
25
+ "method": "GET",
26
+ "challenges": [
27
+ {
28
+ "scheme": "Bearer",
29
+ "params": {
30
+ "resource_metadata": "https://registry.example.net/.well-known/oauth-protected-resource/example.com",
31
+ "error": "invalid_token"
32
+ }
33
+ }
34
+ ],
35
+ "interactive": false
36
+ }
37
+ },
38
+ {
39
+ "id": "erase-rejected",
40
+ "description": "After a 401 to a request that carried a credential: the rejected credential's scope and the server's challenges. get follows once.",
41
+ "operation": "erase",
42
+ "request": {
43
+ "version": 1,
44
+ "operation": "erase",
45
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
46
+ "method": "GET",
47
+ "challenges": [
48
+ {
49
+ "scheme": "Bearer",
50
+ "params": {
51
+ "resource_metadata": "https://registry.example.net/.well-known/oauth-protected-resource/example.com",
52
+ "error": "invalid_token"
53
+ }
54
+ }
55
+ ],
56
+ "interactive": false,
57
+ "scope": "https://registry.example.net/example.com"
58
+ }
59
+ }
60
+ ],
61
+ "answers": [
62
+ {
63
+ "id": "credential",
64
+ "description": "Headers, a scope containing the resource, and an expiry in Unix seconds.",
65
+ "request": {
66
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
67
+ "method": "GET"
68
+ },
69
+ "now": 1791000000,
70
+ "answer": {
71
+ "version": 1,
72
+ "status": "credential",
73
+ "headers": {
74
+ "Authorization": "Bearer example-token"
75
+ },
76
+ "scope": "https://registry.example.net/example.com",
77
+ "expiresAt": 1791400000
78
+ },
79
+ "expected": {
80
+ "ok": true,
81
+ "answer": {
82
+ "status": "credential",
83
+ "headers": {
84
+ "Authorization": "Bearer example-token"
85
+ },
86
+ "scope": "https://registry.example.net/example.com",
87
+ "expiresAt": 1791400000
88
+ }
89
+ }
90
+ },
91
+ {
92
+ "id": "credential-without-expiry",
93
+ "description": "No expiresAt: valid for as long as the process runs.",
94
+ "request": {
95
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
96
+ "method": "GET"
97
+ },
98
+ "now": 1791000000,
99
+ "answer": {
100
+ "version": 1,
101
+ "status": "credential",
102
+ "headers": {
103
+ "Authorization": "Bearer example-token"
104
+ },
105
+ "scope": "https://registry.example.net/example.com"
106
+ },
107
+ "expected": {
108
+ "ok": true,
109
+ "answer": {
110
+ "status": "credential",
111
+ "headers": {
112
+ "Authorization": "Bearer example-token"
113
+ },
114
+ "scope": "https://registry.example.net/example.com"
115
+ }
116
+ }
117
+ },
118
+ {
119
+ "id": "credential-origin-scope",
120
+ "description": "The helper decides how wide the scope is, so long as it contains the resource.",
121
+ "request": {
122
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
123
+ "method": "GET"
124
+ },
125
+ "now": 1791000000,
126
+ "answer": {
127
+ "version": 1,
128
+ "status": "credential",
129
+ "headers": {
130
+ "Authorization": "Bearer example-token"
131
+ },
132
+ "scope": "https://registry.example.net/"
133
+ },
134
+ "expected": {
135
+ "ok": true,
136
+ "answer": {
137
+ "status": "credential",
138
+ "headers": {
139
+ "Authorization": "Bearer example-token"
140
+ },
141
+ "scope": "https://registry.example.net/"
142
+ }
143
+ }
144
+ },
145
+ {
146
+ "id": "several-headers",
147
+ "description": "Any header-based scheme: the transport attaches the headers verbatim.",
148
+ "request": {
149
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
150
+ "method": "GET"
151
+ },
152
+ "now": 1791000000,
153
+ "answer": {
154
+ "version": 1,
155
+ "status": "credential",
156
+ "headers": {
157
+ "Authorization": "DPoP abc",
158
+ "DPoP": "eyJ0eXAiOiJkcG9wK2p3dCJ9.e30.sig"
159
+ },
160
+ "scope": "https://registry.example.net/example.com"
161
+ },
162
+ "expected": {
163
+ "ok": true,
164
+ "answer": {
165
+ "status": "credential",
166
+ "headers": {
167
+ "Authorization": "DPoP abc",
168
+ "DPoP": "eyJ0eXAiOiJkcG9wK2p3dCJ9.e30.sig"
169
+ },
170
+ "scope": "https://registry.example.net/example.com"
171
+ }
172
+ }
173
+ },
174
+ {
175
+ "id": "declined",
176
+ "description": "Declining is not an error: it means no credential for this resource.",
177
+ "request": {
178
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
179
+ "method": "GET"
180
+ },
181
+ "now": 1791000000,
182
+ "answer": {
183
+ "version": 1,
184
+ "status": "declined",
185
+ "reason": "not enrolled for example.com"
186
+ },
187
+ "expected": {
188
+ "ok": true,
189
+ "answer": {
190
+ "status": "declined",
191
+ "reason": "not enrolled for example.com"
192
+ }
193
+ }
194
+ },
195
+ {
196
+ "id": "declined-without-reason",
197
+ "description": "The reason is optional.",
198
+ "request": {
199
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
200
+ "method": "GET"
201
+ },
202
+ "now": 1791000000,
203
+ "answer": {
204
+ "version": 1,
205
+ "status": "declined"
206
+ },
207
+ "expected": {
208
+ "ok": true,
209
+ "answer": {
210
+ "status": "declined"
211
+ }
212
+ }
213
+ },
214
+ {
215
+ "id": "unknown-fields-are-ignored",
216
+ "description": "Fields the protocol does not define are ignored, so a newer helper can add some.",
217
+ "request": {
218
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
219
+ "method": "GET"
220
+ },
221
+ "now": 1791000000,
222
+ "answer": {
223
+ "version": 1,
224
+ "status": "credential",
225
+ "headers": {
226
+ "Authorization": "Bearer example-token"
227
+ },
228
+ "scope": "https://registry.example.net/example.com",
229
+ "tokenType": "Bearer",
230
+ "issuedAt": 1791000000
231
+ },
232
+ "expected": {
233
+ "ok": true,
234
+ "answer": {
235
+ "status": "credential",
236
+ "headers": {
237
+ "Authorization": "Bearer example-token"
238
+ },
239
+ "scope": "https://registry.example.net/example.com"
240
+ }
241
+ }
242
+ },
243
+ {
244
+ "id": "not-an-object",
245
+ "description": "The answer is one JSON object.",
246
+ "request": {
247
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
248
+ "method": "GET"
249
+ },
250
+ "now": 1791000000,
251
+ "answer": [
252
+ {
253
+ "version": 1,
254
+ "status": "credential",
255
+ "headers": {
256
+ "Authorization": "Bearer example-token"
257
+ },
258
+ "scope": "https://registry.example.net/example.com"
259
+ }
260
+ ],
261
+ "expected": {
262
+ "ok": false,
263
+ "problem": "malformed"
264
+ }
265
+ },
266
+ {
267
+ "id": "version-missing",
268
+ "description": "Every answer carries the protocol version.",
269
+ "request": {
270
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
271
+ "method": "GET"
272
+ },
273
+ "now": 1791000000,
274
+ "answer": {
275
+ "status": "credential",
276
+ "headers": {
277
+ "Authorization": "Bearer example-token"
278
+ },
279
+ "scope": "https://registry.example.net/example.com"
280
+ },
281
+ "expected": {
282
+ "ok": false,
283
+ "problem": "version"
284
+ }
285
+ },
286
+ {
287
+ "id": "version-2",
288
+ "description": "Only version 1 is spoken.",
289
+ "request": {
290
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
291
+ "method": "GET"
292
+ },
293
+ "now": 1791000000,
294
+ "answer": {
295
+ "version": 2,
296
+ "status": "credential",
297
+ "headers": {
298
+ "Authorization": "Bearer example-token"
299
+ },
300
+ "scope": "https://registry.example.net/example.com"
301
+ },
302
+ "expected": {
303
+ "ok": false,
304
+ "problem": "version"
305
+ }
306
+ },
307
+ {
308
+ "id": "status-unknown",
309
+ "description": "status is credential or declined.",
310
+ "request": {
311
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
312
+ "method": "GET"
313
+ },
314
+ "now": 1791000000,
315
+ "answer": {
316
+ "version": 1,
317
+ "status": "granted",
318
+ "headers": {
319
+ "Authorization": "Bearer example-token"
320
+ },
321
+ "scope": "https://registry.example.net/example.com"
322
+ },
323
+ "expected": {
324
+ "ok": false,
325
+ "problem": "status"
326
+ }
327
+ },
328
+ {
329
+ "id": "headers-missing",
330
+ "description": "A credential carries headers.",
331
+ "request": {
332
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
333
+ "method": "GET"
334
+ },
335
+ "now": 1791000000,
336
+ "answer": {
337
+ "version": 1,
338
+ "status": "credential",
339
+ "scope": "https://registry.example.net/example.com"
340
+ },
341
+ "expected": {
342
+ "ok": false,
343
+ "problem": "headers"
344
+ }
345
+ },
346
+ {
347
+ "id": "headers-empty",
348
+ "description": "At least one header.",
349
+ "request": {
350
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
351
+ "method": "GET"
352
+ },
353
+ "now": 1791000000,
354
+ "answer": {
355
+ "version": 1,
356
+ "status": "credential",
357
+ "headers": {},
358
+ "scope": "https://registry.example.net/example.com"
359
+ },
360
+ "expected": {
361
+ "ok": false,
362
+ "problem": "headers"
363
+ }
364
+ },
365
+ {
366
+ "id": "header-value-not-a-string",
367
+ "description": "Header values are strings.",
368
+ "request": {
369
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
370
+ "method": "GET"
371
+ },
372
+ "now": 1791000000,
373
+ "answer": {
374
+ "version": 1,
375
+ "status": "credential",
376
+ "headers": {
377
+ "Authorization": 42
378
+ },
379
+ "scope": "https://registry.example.net/example.com"
380
+ },
381
+ "expected": {
382
+ "ok": false,
383
+ "problem": "headers"
384
+ }
385
+ },
386
+ {
387
+ "id": "header-value-with-newline",
388
+ "description": "A header value never carries CR or LF.",
389
+ "request": {
390
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
391
+ "method": "GET"
392
+ },
393
+ "now": 1791000000,
394
+ "answer": {
395
+ "version": 1,
396
+ "status": "credential",
397
+ "headers": {
398
+ "Authorization": "Bearer a\r\nX-Injected: 1"
399
+ },
400
+ "scope": "https://registry.example.net/example.com"
401
+ },
402
+ "expected": {
403
+ "ok": false,
404
+ "problem": "headers"
405
+ }
406
+ },
407
+ {
408
+ "id": "header-name-invalid",
409
+ "description": "Header names are RFC 9110 tokens.",
410
+ "request": {
411
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
412
+ "method": "GET"
413
+ },
414
+ "now": 1791000000,
415
+ "answer": {
416
+ "version": 1,
417
+ "status": "credential",
418
+ "headers": {
419
+ "Bad Header": "x"
420
+ },
421
+ "scope": "https://registry.example.net/example.com"
422
+ },
423
+ "expected": {
424
+ "ok": false,
425
+ "problem": "headers"
426
+ }
427
+ },
428
+ {
429
+ "id": "scope-missing",
430
+ "description": "A credential names its scope.",
431
+ "request": {
432
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
433
+ "method": "GET"
434
+ },
435
+ "now": 1791000000,
436
+ "answer": {
437
+ "version": 1,
438
+ "status": "credential",
439
+ "headers": {
440
+ "Authorization": "Bearer example-token"
441
+ }
442
+ },
443
+ "expected": {
444
+ "ok": false,
445
+ "problem": "scope"
446
+ }
447
+ },
448
+ {
449
+ "id": "scope-http",
450
+ "description": "Scopes are https.",
451
+ "request": {
452
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
453
+ "method": "GET"
454
+ },
455
+ "now": 1791000000,
456
+ "answer": {
457
+ "version": 1,
458
+ "status": "credential",
459
+ "headers": {
460
+ "Authorization": "Bearer example-token"
461
+ },
462
+ "scope": "http://registry.example.net/example.com"
463
+ },
464
+ "expected": {
465
+ "ok": false,
466
+ "problem": "scope"
467
+ }
468
+ },
469
+ {
470
+ "id": "scope-with-query",
471
+ "description": "A scope has no query.",
472
+ "request": {
473
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
474
+ "method": "GET"
475
+ },
476
+ "now": 1791000000,
477
+ "answer": {
478
+ "version": 1,
479
+ "status": "credential",
480
+ "headers": {
481
+ "Authorization": "Bearer example-token"
482
+ },
483
+ "scope": "https://registry.example.net/?publisher=example.com"
484
+ },
485
+ "expected": {
486
+ "ok": false,
487
+ "problem": "scope"
488
+ }
489
+ },
490
+ {
491
+ "id": "scope-excludes-resource",
492
+ "description": "The scope must contain the resource asked for.",
493
+ "request": {
494
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
495
+ "method": "GET"
496
+ },
497
+ "now": 1791000000,
498
+ "answer": {
499
+ "version": 1,
500
+ "status": "credential",
501
+ "headers": {
502
+ "Authorization": "Bearer example-token"
503
+ },
504
+ "scope": "https://registry.example.net/example.org"
505
+ },
506
+ "expected": {
507
+ "ok": false,
508
+ "problem": "scope-excludes-resource"
509
+ }
510
+ },
511
+ {
512
+ "id": "scope-on-a-partial-segment",
513
+ "description": "Containment is on whole segments.",
514
+ "request": {
515
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
516
+ "method": "GET"
517
+ },
518
+ "now": 1791000000,
519
+ "answer": {
520
+ "version": 1,
521
+ "status": "credential",
522
+ "headers": {
523
+ "Authorization": "Bearer example-token"
524
+ },
525
+ "scope": "https://registry.example.net/example.co"
526
+ },
527
+ "expected": {
528
+ "ok": false,
529
+ "problem": "scope-excludes-resource"
530
+ }
531
+ },
532
+ {
533
+ "id": "scope-other-origin",
534
+ "description": "Another origin never contains the resource.",
535
+ "request": {
536
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
537
+ "method": "GET"
538
+ },
539
+ "now": 1791000000,
540
+ "answer": {
541
+ "version": 1,
542
+ "status": "credential",
543
+ "headers": {
544
+ "Authorization": "Bearer example-token"
545
+ },
546
+ "scope": "https://example.com/"
547
+ },
548
+ "expected": {
549
+ "ok": false,
550
+ "problem": "scope-excludes-resource"
551
+ }
552
+ },
553
+ {
554
+ "id": "expires-at-not-a-number",
555
+ "description": "expiresAt is Unix seconds.",
556
+ "request": {
557
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
558
+ "method": "GET"
559
+ },
560
+ "now": 1791000000,
561
+ "answer": {
562
+ "version": 1,
563
+ "status": "credential",
564
+ "headers": {
565
+ "Authorization": "Bearer example-token"
566
+ },
567
+ "scope": "https://registry.example.net/example.com",
568
+ "expiresAt": "2026-10-08T00:00:00Z"
569
+ },
570
+ "expected": {
571
+ "ok": false,
572
+ "problem": "expires-at"
573
+ }
574
+ },
575
+ {
576
+ "id": "expired",
577
+ "description": "An expired credential is never attached: expiresAt must be after now.",
578
+ "request": {
579
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
580
+ "method": "GET"
581
+ },
582
+ "now": 1791000000,
583
+ "answer": {
584
+ "version": 1,
585
+ "status": "credential",
586
+ "headers": {
587
+ "Authorization": "Bearer example-token"
588
+ },
589
+ "scope": "https://registry.example.net/example.com",
590
+ "expiresAt": 1791000000
591
+ },
592
+ "expected": {
593
+ "ok": false,
594
+ "problem": "expired"
595
+ }
596
+ },
597
+ {
598
+ "id": "reason-not-a-string",
599
+ "description": "A decline's reason is a string.",
600
+ "request": {
601
+ "resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
602
+ "method": "GET"
603
+ },
604
+ "now": 1791000000,
605
+ "answer": {
606
+ "version": 1,
607
+ "status": "declined",
608
+ "reason": [
609
+ "no"
610
+ ]
611
+ },
612
+ "expected": {
613
+ "ok": false,
614
+ "problem": "reason"
615
+ }
616
+ }
617
+ ],
618
+ "eraseAnswers": [
619
+ {
620
+ "id": "erased",
621
+ "answer": {
622
+ "version": 1,
623
+ "status": "erased"
624
+ },
625
+ "expected": {
626
+ "ok": true,
627
+ "answer": {
628
+ "status": "erased"
629
+ }
630
+ },
631
+ "description": "The one erase answer."
632
+ },
633
+ {
634
+ "id": "erased-with-extra-fields",
635
+ "answer": {
636
+ "version": 1,
637
+ "status": "erased",
638
+ "removed": 1
639
+ },
640
+ "expected": {
641
+ "ok": true,
642
+ "answer": {
643
+ "status": "erased"
644
+ }
645
+ },
646
+ "description": "Extra fields are ignored."
647
+ },
648
+ {
649
+ "id": "empty-object",
650
+ "answer": {},
651
+ "expected": {
652
+ "ok": false,
653
+ "problem": "version"
654
+ },
655
+ "description": "Empty output is not an answer: erase prints {\"version\": 1, \"status\": \"erased\"}."
656
+ },
657
+ {
658
+ "id": "wrong-status",
659
+ "answer": {
660
+ "version": 1,
661
+ "status": "declined"
662
+ },
663
+ "expected": {
664
+ "ok": false,
665
+ "problem": "status"
666
+ },
667
+ "description": "erase answers erased, even for a credential it no longer holds."
668
+ }
669
+ ],
670
+ "scopes": [
671
+ {
672
+ "id": "origin-contains-every-path",
673
+ "description": "A scope of an origin's root covers every path on it.",
674
+ "scope": "https://registry.example.net/",
675
+ "url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
676
+ "contains": true
677
+ },
678
+ {
679
+ "id": "origin-without-trailing-slash",
680
+ "description": "An origin written without a trailing slash is the same scope.",
681
+ "scope": "https://registry.example.net",
682
+ "url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
683
+ "contains": true
684
+ },
685
+ {
686
+ "id": "publisher-prefix-contains-its-packages",
687
+ "description": "A registry serving many publishers scopes a credential to one publisher's path.",
688
+ "scope": "https://registry.example.net/example.com",
689
+ "url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
690
+ "contains": true
691
+ },
692
+ {
693
+ "id": "prefix-contains-itself",
694
+ "description": "The prefix's own path is inside it.",
695
+ "scope": "https://registry.example.net/example.com",
696
+ "url": "https://registry.example.net/example.com",
697
+ "contains": true
698
+ },
699
+ {
700
+ "id": "trailing-slash-prefix",
701
+ "description": "A prefix written with a trailing slash matches the same paths.",
702
+ "scope": "https://registry.example.net/example.com/",
703
+ "url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
704
+ "contains": true
705
+ },
706
+ {
707
+ "id": "trailing-slash-prefix-contains-its-bare-path",
708
+ "description": "/example.com/ and /example.com name the same segment.",
709
+ "scope": "https://registry.example.net/example.com/",
710
+ "url": "https://registry.example.net/example.com",
711
+ "contains": true
712
+ },
713
+ {
714
+ "id": "segment-boundary",
715
+ "description": "The prefix matches whole path segments: /example.com never covers /example.com.evil.",
716
+ "scope": "https://registry.example.net/example.com",
717
+ "url": "https://registry.example.net/example.com.evil/core/1.0.0.kan.yml",
718
+ "contains": false
719
+ },
720
+ {
721
+ "id": "partial-segment",
722
+ "description": "A prefix that ends mid-segment covers nothing beyond that exact segment.",
723
+ "scope": "https://registry.example.net/example",
724
+ "url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
725
+ "contains": false
726
+ },
727
+ {
728
+ "id": "sibling-publisher",
729
+ "description": "Another publisher on the same registry is outside.",
730
+ "scope": "https://registry.example.net/example.com",
731
+ "url": "https://registry.example.net/example.org/core/1.0.0.kan.yml",
732
+ "contains": false
733
+ },
734
+ {
735
+ "id": "other-host",
736
+ "description": "Another host is outside, so a redirect to a CDN never carries the credential.",
737
+ "scope": "https://registry.example.net/",
738
+ "url": "https://cdn.example.net/example.com/core/1.0.0.kan.yml",
739
+ "contains": false
740
+ },
741
+ {
742
+ "id": "subdomain-is-another-origin",
743
+ "description": "A subdomain is another origin.",
744
+ "scope": "https://example.com/",
745
+ "url": "https://pkg.example.com/core/1.0.0.kan.yml",
746
+ "contains": false
747
+ },
748
+ {
749
+ "id": "other-port",
750
+ "description": "Another port is another origin.",
751
+ "scope": "https://example.com/",
752
+ "url": "https://example.com:8443/core/1.0.0.kan.yml",
753
+ "contains": false
754
+ },
755
+ {
756
+ "id": "default-port-is-the-same-origin",
757
+ "description": "An explicit default port is the same origin.",
758
+ "scope": "https://example.com:443/",
759
+ "url": "https://example.com/core/1.0.0.kan.yml",
760
+ "contains": true
761
+ },
762
+ {
763
+ "id": "host-is-case-insensitive",
764
+ "description": "Hosts compare case-insensitively.",
765
+ "scope": "https://Registry.Example.NET/example.com",
766
+ "url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
767
+ "contains": true
768
+ },
769
+ {
770
+ "id": "path-is-case-sensitive",
771
+ "description": "Paths compare case-sensitively.",
772
+ "scope": "https://registry.example.net/Example.com",
773
+ "url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
774
+ "contains": false
775
+ },
776
+ {
777
+ "id": "http-url-is-never-inside",
778
+ "description": "A credential is never attached to plain http.",
779
+ "scope": "https://example.com/",
780
+ "url": "http://example.com/core/1.0.0.kan.yml",
781
+ "contains": false
782
+ },
783
+ {
784
+ "id": "url-query-is-ignored",
785
+ "description": "A query on the requested URL does not change which path it names.",
786
+ "scope": "https://registry.example.net/example.com",
787
+ "url": "https://registry.example.net/example.com/core/1.0.0.kan.yml?download=1",
788
+ "contains": true
789
+ },
790
+ {
791
+ "id": "dot-segments-are-resolved-first",
792
+ "description": "Dot segments are resolved before matching: this URL names /example.org/...",
793
+ "scope": "https://registry.example.net/example.com",
794
+ "url": "https://registry.example.net/example.com/../example.org/core/1.0.0.kan.yml",
795
+ "contains": false
796
+ }
797
+ ],
798
+ "invalidScopes": [
799
+ {
800
+ "id": "http",
801
+ "scope": "http://example.com/",
802
+ "description": "Only https."
803
+ },
804
+ {
805
+ "id": "relative",
806
+ "scope": "/example.com",
807
+ "description": "A scope is an absolute URL."
808
+ },
809
+ {
810
+ "id": "bare-host",
811
+ "scope": "example.com",
812
+ "description": "A host alone is not a URL."
813
+ },
814
+ {
815
+ "id": "query",
816
+ "scope": "https://example.com/?publisher=example.com",
817
+ "description": "A query cannot bound a path prefix."
818
+ },
819
+ {
820
+ "id": "empty-query",
821
+ "scope": "https://example.com/?",
822
+ "description": "Even an empty query."
823
+ },
824
+ {
825
+ "id": "fragment",
826
+ "scope": "https://example.com/#core",
827
+ "description": "Nor can a fragment."
828
+ },
829
+ {
830
+ "id": "userinfo",
831
+ "scope": "https://user@example.com/",
832
+ "description": "A scope never carries userinfo."
833
+ }
834
+ ],
835
+ "challenges": [
836
+ {
837
+ "id": "bearer-resource-metadata",
838
+ "description": "RFC 9728 §5.1: a 401 naming the resource's metadata.",
839
+ "wwwAuthenticate": "Bearer resource_metadata=\"https://registry.example.net/.well-known/oauth-protected-resource/example.com\", error=\"invalid_token\"",
840
+ "challenges": [
841
+ {
842
+ "scheme": "Bearer",
843
+ "params": {
844
+ "resource_metadata": "https://registry.example.net/.well-known/oauth-protected-resource/example.com",
845
+ "error": "invalid_token"
846
+ }
847
+ }
848
+ ]
849
+ },
850
+ {
851
+ "id": "insufficient-scope",
852
+ "description": "RFC 6750 §3.1: valid but not enough; the transport asks again with no erase.",
853
+ "wwwAuthenticate": "Bearer error=\"insufficient_scope\", scope=\"packages:read\"",
854
+ "challenges": [
855
+ {
856
+ "scheme": "Bearer",
857
+ "params": {
858
+ "error": "insufficient_scope",
859
+ "scope": "packages:read"
860
+ }
861
+ }
862
+ ]
863
+ },
864
+ {
865
+ "id": "bare-scheme",
866
+ "description": "A scheme with no parameters.",
867
+ "wwwAuthenticate": "Bearer",
868
+ "challenges": [
869
+ {
870
+ "scheme": "Bearer",
871
+ "params": {}
872
+ }
873
+ ]
874
+ },
875
+ {
876
+ "id": "two-challenges",
877
+ "description": "Several challenges keep the server's order (RFC 9449 servers send DPoP and Bearer together).",
878
+ "wwwAuthenticate": "DPoP algs=\"ES256 PS256\", Bearer realm=\"example\"",
879
+ "challenges": [
880
+ {
881
+ "scheme": "DPoP",
882
+ "params": {
883
+ "algs": "ES256 PS256"
884
+ }
885
+ },
886
+ {
887
+ "scheme": "Bearer",
888
+ "params": {
889
+ "realm": "example"
890
+ }
891
+ }
892
+ ]
893
+ },
894
+ {
895
+ "id": "param-names-are-case-insensitive",
896
+ "description": "Parameter names are lower-cased; values are kept.",
897
+ "wwwAuthenticate": "Bearer Error=\"invalid_token\"",
898
+ "challenges": [
899
+ {
900
+ "scheme": "Bearer",
901
+ "params": {
902
+ "error": "invalid_token"
903
+ }
904
+ }
905
+ ]
906
+ },
907
+ {
908
+ "id": "scheme-keeps-its-spelling",
909
+ "description": "The scheme keeps the server's spelling; compare it case-insensitively.",
910
+ "wwwAuthenticate": "bearer error=\"invalid_token\"",
911
+ "challenges": [
912
+ {
913
+ "scheme": "bearer",
914
+ "params": {
915
+ "error": "invalid_token"
916
+ }
917
+ }
918
+ ]
919
+ },
920
+ {
921
+ "id": "unquoted-token-value",
922
+ "description": "A parameter value may be a token instead of a quoted-string.",
923
+ "wwwAuthenticate": "Bearer error=invalid_token",
924
+ "challenges": [
925
+ {
926
+ "scheme": "Bearer",
927
+ "params": {
928
+ "error": "invalid_token"
929
+ }
930
+ }
931
+ ]
932
+ },
933
+ {
934
+ "id": "quoted-pair",
935
+ "description": "Backslash escapes inside a quoted-string are removed.",
936
+ "wwwAuthenticate": "Bearer realm=\"the \\\"main\\\" registry\"",
937
+ "challenges": [
938
+ {
939
+ "scheme": "Bearer",
940
+ "params": {
941
+ "realm": "the \"main\" registry"
942
+ }
943
+ }
944
+ ]
945
+ },
946
+ {
947
+ "id": "comma-inside-quotes",
948
+ "description": "A comma inside a quoted-string separates nothing.",
949
+ "wwwAuthenticate": "Bearer realm=\"a, b\", error=\"invalid_token\"",
950
+ "challenges": [
951
+ {
952
+ "scheme": "Bearer",
953
+ "params": {
954
+ "realm": "a, b",
955
+ "error": "invalid_token"
956
+ }
957
+ }
958
+ ]
959
+ },
960
+ {
961
+ "id": "whitespace-around-equals",
962
+ "description": "BWS is allowed around '='.",
963
+ "wwwAuthenticate": "Bearer realm = \"example\"",
964
+ "challenges": [
965
+ {
966
+ "scheme": "Bearer",
967
+ "params": {
968
+ "realm": "example"
969
+ }
970
+ }
971
+ ]
972
+ },
973
+ {
974
+ "id": "token68",
975
+ "description": "A challenge may carry a token68 instead of parameters.",
976
+ "wwwAuthenticate": "Negotiate YIIBhwYGKwYBBQUC",
977
+ "challenges": [
978
+ {
979
+ "scheme": "Negotiate",
980
+ "params": {},
981
+ "token68": "YIIBhwYGKwYBBQUC"
982
+ }
983
+ ]
984
+ },
985
+ {
986
+ "id": "token68-with-padding",
987
+ "description": "A token68's '=' padding is not a parameter.",
988
+ "wwwAuthenticate": "Basic dXNlcjpwYXNz==",
989
+ "challenges": [
990
+ {
991
+ "scheme": "Basic",
992
+ "params": {},
993
+ "token68": "dXNlcjpwYXNz=="
994
+ }
995
+ ]
996
+ },
997
+ {
998
+ "id": "empty-list-elements",
999
+ "description": "Empty list elements are skipped.",
1000
+ "wwwAuthenticate": ", Bearer realm=\"a\" ,, Basic realm=\"b\"",
1001
+ "challenges": [
1002
+ {
1003
+ "scheme": "Bearer",
1004
+ "params": {
1005
+ "realm": "a"
1006
+ }
1007
+ },
1008
+ {
1009
+ "scheme": "Basic",
1010
+ "params": {
1011
+ "realm": "b"
1012
+ }
1013
+ }
1014
+ ]
1015
+ }
1016
+ ],
1017
+ "malformedChallenges": [
1018
+ {
1019
+ "id": "unterminated-quoted-string",
1020
+ "wwwAuthenticate": "Bearer realm=\"example",
1021
+ "description": "A quoted-string that never ends."
1022
+ },
1023
+ {
1024
+ "id": "repeated-parameter",
1025
+ "wwwAuthenticate": "Bearer error=\"a\", error=\"b\"",
1026
+ "description": "RFC 9110: a parameter occurs once per challenge."
1027
+ },
1028
+ {
1029
+ "id": "missing-comma",
1030
+ "wwwAuthenticate": "Bearer realm=\"a\" error=\"b\"",
1031
+ "description": "Parameters are comma-separated."
1032
+ },
1033
+ {
1034
+ "id": "no-space-after-scheme",
1035
+ "wwwAuthenticate": "Bearer=\"x\"",
1036
+ "description": "The scheme is followed by a space before its parameters."
1037
+ }
1038
+ ]
1039
+ }