@certrev/cert-block 0.5.5 → 1.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 (190) hide show
  1. package/CHANGELOG.md +164 -26
  2. package/LICENSE +21 -0
  3. package/README.md +415 -54
  4. package/dist/builder/cert-chrome-data.d.ts +26 -8
  5. package/dist/builder/cert-chrome-data.d.ts.map +1 -1
  6. package/dist/builder/cert-chrome-data.js +5 -5
  7. package/dist/builder/cert-chrome-data.js.map +1 -1
  8. package/dist/builder/cert-review-card.d.ts +35 -5
  9. package/dist/builder/cert-review-card.d.ts.map +1 -1
  10. package/dist/builder/cert-review-card.js +115 -34
  11. package/dist/builder/cert-review-card.js.map +1 -1
  12. package/dist/builder/index.d.ts +33 -16
  13. package/dist/builder/index.d.ts.map +1 -1
  14. package/dist/builder/index.js +33 -16
  15. package/dist/builder/index.js.map +1 -1
  16. package/dist/components/CertBadge.d.ts +2 -2
  17. package/dist/components/CertBadge.d.ts.map +1 -1
  18. package/dist/components/CertBadge.js +11 -5
  19. package/dist/components/CertBadge.js.map +1 -1
  20. package/dist/components/CertJsonLd.d.ts.map +1 -1
  21. package/dist/components/CertJsonLd.js.map +1 -1
  22. package/dist/components/CertRevBacklink.d.ts.map +1 -1
  23. package/dist/components/CertRevBacklink.js +3 -2
  24. package/dist/components/CertRevBacklink.js.map +1 -1
  25. package/dist/components/CertReview.d.ts +1 -1
  26. package/dist/components/ExpertBio.d.ts.map +1 -1
  27. package/dist/components/ExpertBio.js +12 -4
  28. package/dist/components/ExpertBio.js.map +1 -1
  29. package/dist/components/escape.d.ts +28 -1
  30. package/dist/components/escape.d.ts.map +1 -1
  31. package/dist/components/escape.js +37 -8
  32. package/dist/components/escape.js.map +1 -1
  33. package/dist/components/format.d.ts +51 -2
  34. package/dist/components/format.d.ts.map +1 -1
  35. package/dist/components/format.js +103 -6
  36. package/dist/components/format.js.map +1 -1
  37. package/dist/components/preview-block.d.ts +1 -1
  38. package/dist/components/preview-block.js +3 -3
  39. package/dist/components/preview-block.js.map +1 -1
  40. package/dist/components/rel.d.ts +24 -0
  41. package/dist/components/rel.d.ts.map +1 -0
  42. package/dist/components/rel.js +24 -0
  43. package/dist/components/rel.js.map +1 -0
  44. package/dist/components/render-cert-block.d.ts +50 -22
  45. package/dist/components/render-cert-block.d.ts.map +1 -1
  46. package/dist/components/render-cert-block.js +146 -80
  47. package/dist/components/render-cert-block.js.map +1 -1
  48. package/dist/components/render-def.d.ts +13 -14
  49. package/dist/components/render-def.d.ts.map +1 -1
  50. package/dist/components/render-def.js +14 -15
  51. package/dist/components/render-def.js.map +1 -1
  52. package/dist/index.d.ts +1 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +4 -2
  55. package/dist/index.js.map +1 -1
  56. package/dist/jsonld/project.d.ts +46 -13
  57. package/dist/jsonld/project.d.ts.map +1 -1
  58. package/dist/jsonld/project.js +139 -56
  59. package/dist/jsonld/project.js.map +1 -1
  60. package/dist/modal/avatar-resize.d.ts +10 -10
  61. package/dist/modal/avatar-resize.js +14 -14
  62. package/dist/modal/avatar-resize.js.map +1 -1
  63. package/dist/modal/cert-modal-view.d.ts +27 -7
  64. package/dist/modal/cert-modal-view.d.ts.map +1 -1
  65. package/dist/modal/cert-modal-view.js +49 -25
  66. package/dist/modal/cert-modal-view.js.map +1 -1
  67. package/dist/modal/certrev-cert-modal.d.ts +5 -5
  68. package/dist/modal/certrev-cert-modal.js +9 -9
  69. package/dist/modal/certrev-cert-modal.js.map +1 -1
  70. package/dist/modal/certrev-cert-modal.styles.d.ts +6 -6
  71. package/dist/modal/certrev-cert-modal.styles.d.ts.map +1 -1
  72. package/dist/modal/certrev-cert-modal.styles.js +8 -8
  73. package/dist/modal/certrev-cert-modal.styles.js.map +1 -1
  74. package/dist/modal/certrev-cert.entry.d.ts +8 -6
  75. package/dist/modal/certrev-cert.entry.d.ts.map +1 -1
  76. package/dist/modal/certrev-cert.entry.js +9 -7
  77. package/dist/modal/certrev-cert.entry.js.map +1 -1
  78. package/dist/modal/certrev-cert.js +6 -6
  79. package/dist/modal/display-strings.d.ts +25 -5
  80. package/dist/modal/display-strings.d.ts.map +1 -1
  81. package/dist/modal/display-strings.js +27 -5
  82. package/dist/modal/display-strings.js.map +1 -1
  83. package/dist/modal/escape.d.ts +26 -3
  84. package/dist/modal/escape.d.ts.map +1 -1
  85. package/dist/modal/escape.js +32 -3
  86. package/dist/modal/escape.js.map +1 -1
  87. package/dist/modal/font-face.d.ts.map +1 -1
  88. package/dist/modal/font-face.js +8 -11
  89. package/dist/modal/font-face.js.map +1 -1
  90. package/dist/modal/ftc-disclosure.d.ts +2 -2
  91. package/dist/modal/ftc-disclosure.js +2 -2
  92. package/dist/modal/ftc-guard.d.ts +44 -9
  93. package/dist/modal/ftc-guard.d.ts.map +1 -1
  94. package/dist/modal/ftc-guard.js +50 -17
  95. package/dist/modal/ftc-guard.js.map +1 -1
  96. package/dist/modal/index.d.ts +1 -1
  97. package/dist/modal/index.js +1 -1
  98. package/dist/modal/interactions.d.ts +2 -2
  99. package/dist/modal/interactions.js +2 -2
  100. package/dist/modal/logo-paths.js +2 -2
  101. package/dist/modal/logo-paths.js.map +1 -1
  102. package/dist/modal/placement.d.ts +4 -3
  103. package/dist/modal/placement.d.ts.map +1 -1
  104. package/dist/modal/placement.js +7 -6
  105. package/dist/modal/placement.js.map +1 -1
  106. package/dist/modal/revalidate.d.ts +2 -3
  107. package/dist/modal/revalidate.d.ts.map +1 -1
  108. package/dist/modal/revalidate.js +2 -3
  109. package/dist/modal/revalidate.js.map +1 -1
  110. package/dist/modal/verify-client.d.ts +2 -2
  111. package/dist/modal/verify-client.js +2 -2
  112. package/dist/verify/cache.d.ts +21 -6
  113. package/dist/verify/cache.d.ts.map +1 -1
  114. package/dist/verify/cache.js +29 -8
  115. package/dist/verify/cache.js.map +1 -1
  116. package/dist/verify/fetch-with-deadline.d.ts +35 -0
  117. package/dist/verify/fetch-with-deadline.d.ts.map +1 -0
  118. package/dist/verify/fetch-with-deadline.js +55 -0
  119. package/dist/verify/fetch-with-deadline.js.map +1 -0
  120. package/dist/verify/get-verified-envelope.d.ts +23 -9
  121. package/dist/verify/get-verified-envelope.d.ts.map +1 -1
  122. package/dist/verify/get-verified-envelope.js +89 -40
  123. package/dist/verify/get-verified-envelope.js.map +1 -1
  124. package/dist/verify/resolve-kid.d.ts +3 -0
  125. package/dist/verify/resolve-kid.d.ts.map +1 -1
  126. package/dist/verify/resolve-kid.js +17 -8
  127. package/dist/verify/resolve-kid.js.map +1 -1
  128. package/dist/webcomponent/certrev-badge.d.ts.map +1 -1
  129. package/dist/webcomponent/certrev-badge.js +24 -2
  130. package/dist/webcomponent/certrev-badge.js.map +1 -1
  131. package/dist/webcomponent/render-badge-html.d.ts +1 -1
  132. package/dist/webcomponent/render-badge-html.d.ts.map +1 -1
  133. package/dist/webcomponent/render-badge-html.js +36 -17
  134. package/dist/webcomponent/render-badge-html.js.map +1 -1
  135. package/package.json +5 -2
  136. package/src/builder/cert-chrome-data.ts +26 -8
  137. package/src/builder/cert-review-card.tsx +147 -38
  138. package/src/builder/index.tsx +34 -17
  139. package/src/components/CertBadge.tsx +14 -9
  140. package/src/components/CertJsonLd.tsx +1 -1
  141. package/src/components/CertRevBacklink.tsx +10 -3
  142. package/src/components/CertReview.tsx +1 -1
  143. package/src/components/ExpertBio.tsx +12 -7
  144. package/src/components/escape.ts +37 -7
  145. package/src/components/format.ts +102 -6
  146. package/src/components/preview-block.ts +3 -3
  147. package/src/components/rel.ts +23 -0
  148. package/src/components/render-cert-block.ts +192 -109
  149. package/src/components/render-def.ts +14 -15
  150. package/src/index.ts +4 -2
  151. package/src/jsonld/project.ts +155 -59
  152. package/src/modal/avatar-resize.ts +14 -14
  153. package/src/modal/cert-modal-view.ts +52 -27
  154. package/src/modal/certrev-cert-modal.styles.ts +8 -8
  155. package/src/modal/certrev-cert-modal.ts +9 -9
  156. package/src/modal/certrev-cert.entry.ts +9 -7
  157. package/src/modal/display-strings.ts +26 -5
  158. package/src/modal/escape.ts +32 -4
  159. package/src/modal/font-face.ts +8 -11
  160. package/src/modal/ftc-disclosure.ts +2 -2
  161. package/src/modal/ftc-guard.ts +73 -18
  162. package/src/modal/index.ts +1 -1
  163. package/src/modal/interactions.ts +2 -2
  164. package/src/modal/logo-paths.ts +2 -2
  165. package/src/modal/placement.ts +7 -6
  166. package/src/modal/revalidate.ts +2 -3
  167. package/src/modal/verify-client.ts +2 -2
  168. package/src/verify/cache.ts +34 -8
  169. package/src/verify/fetch-with-deadline.ts +60 -0
  170. package/src/verify/get-verified-envelope.ts +131 -48
  171. package/src/verify/resolve-kid.ts +22 -6
  172. package/src/webcomponent/certrev-badge.ts +24 -2
  173. package/src/webcomponent/render-badge-html.ts +66 -19
  174. package/src/__tests__/__snapshots__/render-cert-block.test.ts.snap +0 -17
  175. package/src/__tests__/components.test.tsx +0 -206
  176. package/src/__tests__/preview-block.test.ts +0 -84
  177. package/src/__tests__/project.test.ts +0 -128
  178. package/src/__tests__/readme-version.test.ts +0 -51
  179. package/src/__tests__/render-cert-block.test.ts +0 -657
  180. package/src/__tests__/render-def.test.tsx +0 -295
  181. package/src/__tests__/verify.test.ts +0 -239
  182. package/src/__tests__/webcomponent.test.tsx +0 -116
  183. package/src/builder/__tests__/builder.test.tsx +0 -158
  184. package/src/modal/cert-modal-view.test.ts +0 -175
  185. package/src/modal/certrev-cert-modal.test.ts +0 -125
  186. package/src/modal/ftc-guard.test.ts +0 -153
  187. package/src/modal/interactions.test.ts +0 -250
  188. package/src/modal/placement.test.ts +0 -398
  189. package/src/modal/revalidate.test.ts +0 -111
  190. package/src/modal/verify-client.test.ts +0 -164
@@ -13,9 +13,9 @@
13
13
  * still recovering quickly (short negative TTL).
14
14
  *
15
15
  * It is deliberately tiny + dependency-free (a Map + timestamps) so it runs in any server
16
- * runtime (Node, edge, workerd). For multi-instance deployments this is per-instance L1;
17
- * pair with a shared CDN/KV cache (the Delivery API is CDN-cacheable on
18
- * `lifecycle.revision`) for L2 — see README.
16
+ * runtime (Node, edge, workerd). For multi-instance deployments this is per-instance L1 and
17
+ * nothing more: each instance keeps its own copy, so an L2 (the CDN in front of the Delivery
18
+ * API, which is cacheable on `lifecycle.revision`, or a shared KV) is the caller's to add.
19
19
  */
20
20
  export class TtlCache {
21
21
  store = new Map();
@@ -48,8 +48,14 @@ export class TtlCache {
48
48
  * TTL; if `isNegative(value)` returns true (e.g. a 'suppress' verdict) it's cached
49
49
  * with the shorter negative TTL so a transient failure recovers fast. A thrown loader
50
50
  * is NOT cached (it rejects all current waiters and the next call retries).
51
+ *
52
+ * `positiveTtlMs` overrides the cache's positive TTL FOR THIS WRITE only, so one caller
53
+ * can hold a verdict longer/shorter than the cache's default without owning the cache.
54
+ * It deliberately does NOT apply to a negative result: a caller asking for a 10-minute
55
+ * positive TTL is asking for fewer origin reads, not for a transient failure to be
56
+ * remembered for ten minutes (see the negative-TTL rationale in the module header).
51
57
  */
52
- async getOrLoad(key, loader, isNegative) {
58
+ async getOrLoad(key, loader, isNegative, positiveTtlMs) {
53
59
  const cached = this.peek(key);
54
60
  if (cached !== undefined)
55
61
  return cached;
@@ -59,7 +65,7 @@ export class TtlCache {
59
65
  const promise = (async () => {
60
66
  const value = await loader();
61
67
  const negative = isNegative ? isNegative(value) : false;
62
- this.set(key, value, negative);
68
+ this.set(key, value, negative, negative ? undefined : positiveTtlMs);
63
69
  return value;
64
70
  })().finally(() => {
65
71
  this.inflight.delete(key);
@@ -67,21 +73,36 @@ export class TtlCache {
67
73
  this.inflight.set(key, promise);
68
74
  return promise;
69
75
  }
70
- /** Insert/overwrite an entry with the appropriate TTL. */
71
- set(key, value, negative = false) {
76
+ /** Insert/overwrite an entry. `ttlMsOverride` replaces the TTL this write would otherwise
77
+ * get (positive or negative); omit it for the cache's configured TTLs. */
78
+ set(key, value, negative = false, ttlMsOverride) {
72
79
  if (this.store.size >= this.maxEntries && !this.store.has(key)) {
73
80
  // Evict the oldest inserted key (Map preserves insertion order).
74
81
  const oldest = this.store.keys().next().value;
75
82
  if (oldest !== undefined)
76
83
  this.store.delete(oldest);
77
84
  }
78
- const ttl = negative ? this.negativeTtlMs : this.ttlMs;
85
+ const configured = negative ? this.negativeTtlMs : this.ttlMs;
86
+ const ttl = ttlMsOverride !== undefined && Number.isFinite(ttlMsOverride) ? ttlMsOverride : configured;
79
87
  this.store.set(key, { value, expiresAt: this.now() + ttl, negative });
80
88
  }
81
89
  /** Drop a key (e.g. on a known revocation push). */
82
90
  delete(key) {
83
91
  this.store.delete(key);
84
92
  }
93
+ /**
94
+ * Drop every key under a prefix. WHY it exists: a cache key identifies everything the
95
+ * cached value depends on, which for a verdict includes the live content hash the render
96
+ * observed — a fact a revocation webhook does not know. Invalidating by prefix lets the
97
+ * webhook drop a placement without having to reconstruct every render context that ever
98
+ * cached it.
99
+ */
100
+ deletePrefix(prefix) {
101
+ for (const key of this.store.keys()) {
102
+ if (key.startsWith(prefix))
103
+ this.store.delete(key);
104
+ }
105
+ }
85
106
  clear() {
86
107
  this.store.clear();
87
108
  this.inflight.clear();
@@ -1 +1 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/verify/cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/verify/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * `fetchWithDeadline` — every network read on the render path gets a deadline.
3
+ *
4
+ * WHY: the verify layer runs INSIDE a server render (a Hydrogen loader, a Next server
5
+ * component, a Builder server fetch). A hung origin with no deadline holds that render open
6
+ * for as long as the runtime's socket timeout allows — one confirmed case was still pending
7
+ * at 45 SECONDS, i.e. a page that never answered because a decorative badge could not be
8
+ * verified. A badge is never worth a page: past the deadline the request is ABORTED (the
9
+ * connection goes back) and the caller fails closed, the same safe outcome as any other
10
+ * fetch failure.
11
+ *
12
+ * Two deliberate choices:
13
+ * • The deadline is enforced by a RACE, not by the AbortSignal alone. A `fetchImpl` that
14
+ * ignores its signal (a wrapper, a proxy, a non-conforming polyfill) must not be able to
15
+ * hold a render open. The abort still fires, so a conforming implementation also releases
16
+ * the socket rather than merely being abandoned.
17
+ * • `setTimeout` + `AbortController`, not `AbortSignal.timeout()`. The one-liner reads
18
+ * better but its timer is not the one a test's fake clock controls, and a deadline that
19
+ * can only be tested by really waiting is a deadline nobody keeps tested.
20
+ */
21
+ /**
22
+ * Default deadline for a render-path fetch. The Delivery API is CDN-cacheable and answers
23
+ * well under a second at p99; three seconds is generous for a cold origin and still bounded
24
+ * by what a page can afford to spend on a decoration whose failure mode is "render nothing".
25
+ * Callers on a tighter TTFB budget pass their own.
26
+ */
27
+ export declare const DEFAULT_FETCH_TIMEOUT_MS = 3000;
28
+ /**
29
+ * `fetchImpl(url, init)` with a hard deadline. Rejects when the deadline passes (after
30
+ * aborting the request); every caller on the verify path treats that rejection like any other
31
+ * fetch failure — fail closed. A non-finite or non-positive `timeoutMs` is an explicit opt-out
32
+ * (no deadline), for a caller that owns its own cancellation.
33
+ */
34
+ export declare function fetchWithDeadline(fetchImpl: typeof fetch, url: string, init: RequestInit, timeoutMs?: number): Promise<Response>;
35
+ //# sourceMappingURL=fetch-with-deadline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-with-deadline.d.ts","sourceRoot":"","sources":["../../src/verify/fetch-with-deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,OAAQ,CAAA;AAE7C;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACtC,SAAS,EAAE,OAAO,KAAK,EACvB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,WAAW,EACjB,SAAS,GAAE,MAAiC,GAC1C,OAAO,CAAC,QAAQ,CAAC,CAmBnB"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * `fetchWithDeadline` — every network read on the render path gets a deadline.
3
+ *
4
+ * WHY: the verify layer runs INSIDE a server render (a Hydrogen loader, a Next server
5
+ * component, a Builder server fetch). A hung origin with no deadline holds that render open
6
+ * for as long as the runtime's socket timeout allows — one confirmed case was still pending
7
+ * at 45 SECONDS, i.e. a page that never answered because a decorative badge could not be
8
+ * verified. A badge is never worth a page: past the deadline the request is ABORTED (the
9
+ * connection goes back) and the caller fails closed, the same safe outcome as any other
10
+ * fetch failure.
11
+ *
12
+ * Two deliberate choices:
13
+ * • The deadline is enforced by a RACE, not by the AbortSignal alone. A `fetchImpl` that
14
+ * ignores its signal (a wrapper, a proxy, a non-conforming polyfill) must not be able to
15
+ * hold a render open. The abort still fires, so a conforming implementation also releases
16
+ * the socket rather than merely being abandoned.
17
+ * • `setTimeout` + `AbortController`, not `AbortSignal.timeout()`. The one-liner reads
18
+ * better but its timer is not the one a test's fake clock controls, and a deadline that
19
+ * can only be tested by really waiting is a deadline nobody keeps tested.
20
+ */
21
+ /**
22
+ * Default deadline for a render-path fetch. The Delivery API is CDN-cacheable and answers
23
+ * well under a second at p99; three seconds is generous for a cold origin and still bounded
24
+ * by what a page can afford to spend on a decoration whose failure mode is "render nothing".
25
+ * Callers on a tighter TTFB budget pass their own.
26
+ */
27
+ export const DEFAULT_FETCH_TIMEOUT_MS = 3_000;
28
+ /**
29
+ * `fetchImpl(url, init)` with a hard deadline. Rejects when the deadline passes (after
30
+ * aborting the request); every caller on the verify path treats that rejection like any other
31
+ * fetch failure — fail closed. A non-finite or non-positive `timeoutMs` is an explicit opt-out
32
+ * (no deadline), for a caller that owns its own cancellation.
33
+ */
34
+ export async function fetchWithDeadline(fetchImpl, url, init, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
35
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
36
+ return fetchImpl(url, init);
37
+ const controller = new AbortController();
38
+ let timer;
39
+ const deadline = new Promise((_, reject) => {
40
+ timer = setTimeout(() => {
41
+ controller.abort();
42
+ reject(new Error(`cert-block: fetch exceeded its ${timeoutMs}ms deadline — ${url}`));
43
+ }, timeoutMs);
44
+ });
45
+ try {
46
+ // Promise.race subscribes to both, so an abort-rejection that lands after the race has
47
+ // settled is still handled — it never surfaces as an unhandled rejection.
48
+ return await Promise.race([fetchImpl(url, { ...init, signal: controller.signal }), deadline]);
49
+ }
50
+ finally {
51
+ if (timer !== undefined)
52
+ clearTimeout(timer);
53
+ }
54
+ }
55
+ //# sourceMappingURL=fetch-with-deadline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-with-deadline.js","sourceRoot":"","sources":["../../src/verify/fetch-with-deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAA;AAE7C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,SAAuB,EACvB,GAAW,EACX,IAAiB,EACjB,YAAoB,wBAAwB;IAE5C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAE9E,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,IAAI,KAAgD,CAAA;IACpD,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QACjD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACvB,UAAU,CAAC,KAAK,EAAE,CAAA;YAClB,MAAM,CAAC,IAAI,KAAK,CAAC,kCAAkC,SAAS,iBAAiB,GAAG,EAAE,CAAC,CAAC,CAAA;QACrF,CAAC,EAAE,SAAS,CAAC,CAAA;IACd,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC;QACJ,uFAAuF;QACvF,0EAA0E;QAC1E,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;IAC9F,CAAC;YAAS,CAAC;QACV,IAAI,KAAK,KAAK,SAAS;YAAE,YAAY,CAAC,KAAK,CAAC,CAAA;IAC7C,CAAC;AACF,CAAC"}
@@ -16,8 +16,13 @@
16
16
  * JSON-LD; `{ decision: 'suppress', reason }` → render NOTHING. Any error (network,
17
17
  * malformed JSON, throwing resolver) collapses to a `suppress` verdict — never throws
18
18
  * into the render path, never renders an unverified credential.
19
+ *
20
+ * `verdict.reason` is the ONLY signal a consumer gets when a badge does not appear (this
21
+ * package logs nothing), so every failure path here reports what ACTUALLY failed rather than
22
+ * a convenient catch-all. Every fetch also carries a deadline: a hung origin fails closed at
23
+ * `timeoutMs` instead of holding the render open (see ./fetch-with-deadline).
19
24
  */
20
- import type { CertDeliveryEnvelope, CertVerdict, RenderContext, ResolvePublicKeyByKid } from '../contract/kernel.js';
25
+ import type { CertDeliveryArtifact, CertVerdict, RenderContext, ResolvePublicKeyByKid } from '../contract/kernel.js';
21
26
  import { TtlCache } from './cache.js';
22
27
  /** Where the signed envelope comes from. Exactly one of the two source shapes. */
23
28
  export type EnvelopeSource =
@@ -29,27 +34,31 @@ export type EnvelopeSource =
29
34
  readonly platform: string;
30
35
  readonly externalId: string;
31
36
  }
32
- /** PUSH/native: an envelope already read from a Shopify app-owned metafield (or any
37
+ /** PUSH/native: an artifact already read from a Shopify app-owned metafield (or any
33
38
  * native store). The caller fetched it via the Storefront API; we just verify it. The
34
- * value may be the parsed envelope or its JSON string (metafields store strings). */
39
+ * value may be the parsed artifact — a full envelope OR the slim revocation tombstone —
40
+ * or its JSON string (metafields store strings). */
35
41
  | {
36
42
  readonly kind: 'metafield';
37
- readonly value: CertDeliveryEnvelope | string | null | undefined;
43
+ readonly value: CertDeliveryArtifact | string | null | undefined;
38
44
  };
39
45
  export interface GetVerifiedEnvelopeOptions {
40
46
  readonly source: EnvelopeSource;
41
47
  /** kid → public-key resolver (see ./resolve-kid). Required: no verify without keys. */
42
48
  readonly resolveKid: ResolvePublicKeyByKid;
43
49
  /** Render context: the platform + externalId this edge IS, plus optional live hash. */
44
- readonly context: Omit<RenderContext, 'now'> & {
45
- readonly now?: Date;
46
- };
50
+ readonly context: RenderContext;
47
51
  /** Injectable fetch for tests / non-global-fetch runtimes. */
48
52
  readonly fetchImpl?: typeof fetch;
49
53
  /** Cache override (per-instance). Defaults to the module-level shared cache. */
50
54
  readonly cache?: TtlCache<CertVerdict>;
51
- /** Positive verdict TTL ms (default 60_000). */
55
+ /** TTL in ms for a RENDER verdict written by this call (default: the cache's own positive
56
+ * TTL — 60_000 on the shared cache). A suppress verdict keeps the cache's short negative
57
+ * TTL regardless, so a longer `ttlMs` can never make a transient failure sticky. */
52
58
  readonly ttlMs?: number;
59
+ /** Deadline in ms for the Delivery API fetch (default `DEFAULT_FETCH_TIMEOUT_MS`, 3s).
60
+ * Ignored by the metafield source, which does no I/O. */
61
+ readonly timeoutMs?: number;
53
62
  }
54
63
  /** Module-level shared cache so all calls in a process coordinate by default. */
55
64
  declare const sharedVerdictCache: TtlCache<CertVerdict>;
@@ -59,7 +68,12 @@ declare const sharedVerdictCache: TtlCache<CertVerdict>;
59
68
  * single-flight keep the origin load bounded.
60
69
  */
61
70
  export declare function getVerifiedEnvelope(opts: GetVerifiedEnvelopeOptions): Promise<CertVerdict>;
62
- /** Expose the shared cache so a revocation webhook handler can invalidate proactively. */
71
+ /**
72
+ * Expose the shared cache so a revocation webhook handler can invalidate proactively. Drops
73
+ * EVERY entry for this source at this placement, whatever live content hash each was cached
74
+ * under — the webhook knows the article, never the body hash a given render observed, and an
75
+ * invalidation that misses because of a hash mismatch is a revoked cert that keeps rendering.
76
+ */
63
77
  export declare function invalidateVerdict(source: EnvelopeSource, context: RenderContext, cache?: TtlCache<CertVerdict>): void;
64
78
  export { sharedVerdictCache };
65
79
  //# sourceMappingURL=get-verified-envelope.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-verified-envelope.d.ts","sourceRoot":"","sources":["../../src/verify/get-verified-envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,WAAW,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAEpH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,kFAAkF;AAClF,MAAM,MAAM,cAAc;AACzB,2EAA2E;AACzE;IACA,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC1B;AACH;;sFAEsF;GACpF;IACA,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CAC/D,CAAA;AAEJ,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,uFAAuF;IACvF,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAA;IAC1C,uFAAuF;IACvF,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG;QAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAA;KAAE,CAAA;IACtE,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACjC,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAA;IACtC,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,iFAAiF;AACjF,QAAA,MAAM,kBAAkB,uBAAqE,CAAA;AAyE7F;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,0BAA0B,GAAG,OAAO,CAAC,WAAW,CAAC,CAkChG;AAED,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAChC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,aAAa,EACtB,KAAK,GAAE,QAAQ,CAAC,WAAW,CAAsB,GAC/C,IAAI,CAEN;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
1
+ {"version":3,"file":"get-verified-envelope.d.ts","sourceRoot":"","sources":["../../src/verify/get-verified-envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EACX,oBAAoB,EAEpB,WAAW,EACX,aAAa,EACb,qBAAqB,EACrB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAGrC,kFAAkF;AAClF,MAAM,MAAM,cAAc;AACzB,2EAA2E;AACzE;IACA,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC1B;AACH;;;qDAGqD;GACnD;IACA,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CAC/D,CAAA;AAEJ,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,uFAAuF;IACvF,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAA;IAC1C,uFAAuF;IACvF,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;IAC/B,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACjC,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAA;IACtC;;yFAEqF;IACrF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB;8DAC0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,iFAAiF;AACjF,QAAA,MAAM,kBAAkB,uBAAqE,CAAA;AAwI7F;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,0BAA0B,GAAG,OAAO,CAAC,WAAW,CAAC,CA+BhG;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAChC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,aAAa,EACtB,KAAK,GAAE,QAAQ,CAAC,WAAW,CAAsB,GAC/C,IAAI,CAEN;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
@@ -16,24 +16,34 @@
16
16
  * JSON-LD; `{ decision: 'suppress', reason }` → render NOTHING. Any error (network,
17
17
  * malformed JSON, throwing resolver) collapses to a `suppress` verdict — never throws
18
18
  * into the render path, never renders an unverified credential.
19
+ *
20
+ * `verdict.reason` is the ONLY signal a consumer gets when a badge does not appear (this
21
+ * package logs nothing), so every failure path here reports what ACTUALLY failed rather than
22
+ * a convenient catch-all. Every fetch also carries a deadline: a hung origin fails closed at
23
+ * `timeoutMs` instead of holding the render open (see ./fetch-with-deadline).
19
24
  */
20
- import { verifyEnvelope } from '../contract/kernel.js';
25
+ import { verifyArtifact } from '../contract/kernel.js';
21
26
  import { TtlCache } from './cache.js';
27
+ import { DEFAULT_FETCH_TIMEOUT_MS, fetchWithDeadline } from './fetch-with-deadline.js';
22
28
  /** Module-level shared cache so all calls in a process coordinate by default. */
23
29
  const sharedVerdictCache = new TtlCache({ ttlMs: 60_000, negativeTtlMs: 5_000 });
24
30
  function suppress(reason) {
25
31
  return { decision: 'suppress', reason };
26
32
  }
27
- function parseEnvelope(value) {
28
- if (value == null)
29
- return null;
33
+ /** Read the artifact out of a metafield value. Never throws. */
34
+ function parseMetafieldValue(value) {
35
+ // Absent — never certified, or the app cleared the metafield (Shopify writes an empty
36
+ // string rather than deleting it). Nothing is published for this subject.
37
+ if (value == null || (typeof value === 'string' && value.trim() === '')) {
38
+ return { ok: false, reason: 'subject_mismatch' };
39
+ }
30
40
  if (typeof value !== 'string')
31
- return value;
41
+ return { ok: true, artifact: value };
32
42
  try {
33
- return JSON.parse(value);
43
+ return { ok: true, artifact: JSON.parse(value) };
34
44
  }
35
45
  catch {
36
- return null;
46
+ return { ok: false, reason: 'unsupported_contract_version' };
37
47
  }
38
48
  }
39
49
  function deliveryApiUrl(s) {
@@ -63,25 +73,64 @@ function metafieldValueKey(value) {
63
73
  const s = typeof value === 'string' ? value : JSON.stringify(value);
64
74
  return fnv1a(s);
65
75
  }
66
- /** Stable cache key per (source identity × render externalId × envelope value). */
76
+ /** WHICH artifact is being verified. For the delivery source that is the full URL — the base
77
+ * URL is part of the identity (a staging portal and production serve DIFFERENT envelopes for
78
+ * the same platform/externalId); for a metafield it's a hash of the value the caller passed. */
79
+ function sourceKey(source) {
80
+ return source.kind === 'delivery_api' ? `api:${deliveryApiUrl(source)}` : `mf:${metafieldValueKey(source.value)}`;
81
+ }
82
+ /** WHERE it is being rendered. `now` is deliberately NOT part of the key: elapsed time is what
83
+ * the TTL already bounds, and keying on an injected clock would turn the cache into a
84
+ * permanent miss for every caller that passes one. */
85
+ function placementKey(ctx) {
86
+ return `${encodeURIComponent(ctx.platform)}:${encodeURIComponent(ctx.externalId)}`;
87
+ }
88
+ /** Everything in the key ABOVE the live content hash — one placement, all of its hashes. */
89
+ function placementPrefix(source, ctx) {
90
+ return `${sourceKey(source)}|${placementKey(ctx)}|`;
91
+ }
92
+ /**
93
+ * Stable cache key per (artifact identity × placement × live content hash) — i.e. every input
94
+ * the verdict actually depends on. Both source branches now key on the same three things: the
95
+ * old delivery key (`api:<platform>:<externalId>`) named neither the base URL nor the render
96
+ * context, so one entry was shared by a staging and a production portal, and — the dangerous
97
+ * one — a verdict computed while the article matched its `contentDigest` was served for a
98
+ * later render whose body had DRIFTED, rendering a credential the drift check would have
99
+ * suppressed. The live hash goes LAST so `invalidateVerdict` can drop a placement by prefix.
100
+ */
67
101
  function cacheKey(source, ctx) {
68
- if (source.kind === 'delivery_api') {
69
- return `api:${source.platform}:${source.externalId}`;
70
- }
71
- // Metafield: key on the rendering identity AND a short hash of the envelope value, so a
72
- // push update (new envelope at the same placement) busts the entry, and verifying
73
- // different envelopes against one render context never collides on a stale verdict.
74
- return `mf:${ctx.platform}:${ctx.externalId}:${metafieldValueKey(source.value)}`;
102
+ return `${placementPrefix(source, ctx)}${ctx.liveContentHash ?? '-'}`;
75
103
  }
76
- async function fetchFromDeliveryApi(s, fetchImpl) {
77
- const res = await fetchImpl(deliveryApiUrl(s), { headers: { accept: 'application/json' } });
78
- if (!res.ok)
79
- return null; // 404 / 410 (revoked, no longer served) → no envelope → suppress
104
+ /** Fetch the artifact from the Delivery API. Never throws: every failure comes back as the
105
+ * truthful suppress reason (see `SourceOutcome`). */
106
+ async function fetchFromDeliveryApi(s, fetchImpl, timeoutMs) {
107
+ let res;
108
+ try {
109
+ res = await fetchWithDeadline(fetchImpl, deliveryApiUrl(s), { headers: { accept: 'application/json' } }, timeoutMs);
110
+ }
111
+ catch {
112
+ // Unreachable origin, DNS/TLS failure, or the deadline above. No transport member
113
+ // exists in the contract enum; this is the least-wrong one.
114
+ return { ok: false, reason: 'unsupported_contract_version' };
115
+ }
116
+ if (res.status === 404 || res.status === 410) {
117
+ // The API serves nothing for this (platform, externalId): never certified, or the
118
+ // placement was withdrawn. Revocation of a LIVE cert comes back 200 with a tombstone
119
+ // so a 4xx here is genuinely "no credential for this subject" — which is
120
+ // also the question the consumer needs to ask (is this the externalId I think it is?).
121
+ return { ok: false, reason: 'subject_mismatch' };
122
+ }
123
+ if (!res.ok) {
124
+ // 5xx / 429 / anything else: the origin is failing, not the credential. Transport gap
125
+ // again — same least-wrong member, and the short negative TTL keeps it recoverable.
126
+ return { ok: false, reason: 'unsupported_contract_version' };
127
+ }
80
128
  try {
81
- return (await res.json());
129
+ return { ok: true, artifact: (await res.json()) };
82
130
  }
83
131
  catch {
84
- return null;
132
+ // A body arrived and could not be parsed — here the reason is literally true.
133
+ return { ok: false, reason: 'unsupported_contract_version' };
85
134
  }
86
135
  }
87
136
  /**
@@ -96,33 +145,33 @@ export async function getVerifiedEnvelope(opts) {
96
145
  const key = cacheKey(opts.source, ctx);
97
146
  const isSuppress = (v) => v.decision !== 'render';
98
147
  return cache.getOrLoad(key, async () => {
99
- let envelope;
100
- try {
101
- envelope =
102
- opts.source.kind === 'delivery_api'
103
- ? await fetchFromDeliveryApi(opts.source, fetchImpl)
104
- : parseEnvelope(opts.source.value);
105
- }
106
- catch {
107
- // Network/JSON failure → fail closed (do NOT render an unverified credential).
108
- return suppress('unsupported_contract_version');
109
- }
110
- if (!envelope) {
111
- // No envelope present (never certified / revoked + cleared / 404) → suppress.
112
- return suppress('unsupported_contract_version');
113
- }
148
+ const sourced = opts.source.kind === 'delivery_api'
149
+ ? await fetchFromDeliveryApi(opts.source, fetchImpl, opts.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS)
150
+ : parseMetafieldValue(opts.source.value);
151
+ if (!sourced.ok)
152
+ return suppress(sourced.reason);
153
+ // `verifyArtifact` — not `verifyEnvelope` — because the Delivery API answers a
154
+ // revoked cert with the slim signed TOMBSTONE. The envelope-only kernel
155
+ // still fails closed on one, but it reports it as an unparseable envelope; the
156
+ // artifact kernel verifies the tombstone's own signature + subject and says
157
+ // 'revoked', which is both true and the thing a consumer needs to hear.
114
158
  // The kernel itself never throws (it fails closed), but guard the resolver too.
115
159
  try {
116
- return await verifyEnvelope(envelope, opts.resolveKid, ctx);
160
+ return await verifyArtifact(sourced.artifact, opts.resolveKid, ctx);
117
161
  }
118
162
  catch {
119
163
  return suppress('unknown_key');
120
164
  }
121
- }, isSuppress);
165
+ }, isSuppress, opts.ttlMs);
122
166
  }
123
- /** Expose the shared cache so a revocation webhook handler can invalidate proactively. */
167
+ /**
168
+ * Expose the shared cache so a revocation webhook handler can invalidate proactively. Drops
169
+ * EVERY entry for this source at this placement, whatever live content hash each was cached
170
+ * under — the webhook knows the article, never the body hash a given render observed, and an
171
+ * invalidation that misses because of a hash mismatch is a revoked cert that keeps rendering.
172
+ */
124
173
  export function invalidateVerdict(source, context, cache = sharedVerdictCache) {
125
- cache.delete(cacheKey(source, context));
174
+ cache.deletePrefix(placementPrefix(source, context));
126
175
  }
127
176
  export { sharedVerdictCache };
128
177
  //# sourceMappingURL=get-verified-envelope.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-verified-envelope.js","sourceRoot":"","sources":["../../src/verify/get-verified-envelope.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"get-verified-envelope.js","sourceRoot":"","sources":["../../src/verify/get-verified-envelope.ts"],"names":[],"mappings":"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"}
@@ -26,6 +26,9 @@ export interface FetchingKidResolverOptions {
26
26
  readonly jwksUrl: string;
27
27
  /** Key-set cache TTL in ms (default 1h — keys rotate rarely). */
28
28
  readonly ttlMs?: number;
29
+ /** Deadline in ms for the JWKS fetch (default `DEFAULT_FETCH_TIMEOUT_MS`, 3s). This fetch
30
+ * is on the render path too — a hung key endpoint must not hold a page open. */
31
+ readonly timeoutMs?: number;
29
32
  /** Injectable fetch for tests / non-global-fetch runtimes. */
30
33
  readonly fetchImpl?: typeof fetch;
31
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-kid.d.ts","sourceRoot":"","sources":["../../src/verify/resolve-kid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAGzF,0EAA0E;AAC1E,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,CAAC,CAAC,CAAA;AAOnF,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,qBAAqB,CAK3E;AAgCD,MAAM,WAAW,0BAA0B;IAC1C,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,iEAAiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACjC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,0BAA0B,GAAG,qBAAqB,CAqB3F"}
1
+ {"version":3,"file":"resolve-kid.d.ts","sourceRoot":"","sources":["../../src/verify/resolve-kid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAIzF,0EAA0E;AAC1E,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,CAAC,CAAC,CAAA;AAOnF,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,qBAAqB,CAK3E;AAgCD,MAAM,WAAW,0BAA0B;IAC1C,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,iEAAiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB;qFACiF;IACjF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACjC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,0BAA0B,GAAG,qBAAqB,CAiC3F"}
@@ -18,6 +18,7 @@
18
18
  */
19
19
  import { base64urlDecode } from '@certrev/cert-contract';
20
20
  import { TtlCache } from './cache.js';
21
+ import { DEFAULT_FETCH_TIMEOUT_MS, fetchWithDeadline } from './fetch-with-deadline.js';
21
22
  function asInput(v) {
22
23
  // A bare string is treated as PEM (the common deploy-config form).
23
24
  return typeof v === 'string' ? { format: 'pem', pem: v } : v;
@@ -54,14 +55,22 @@ export function fetchingKidResolver(opts) {
54
55
  const CACHE_KEY = opts.jwksUrl;
55
56
  async function loadKeySet() {
56
57
  const map = new Map();
57
- const res = await fetchImpl(opts.jwksUrl, { headers: { accept: 'application/json' } });
58
- if (!res.ok)
59
- return map; // empty → every kid resolves null → fail closed
60
- const doc = (await res.json());
61
- for (const jwk of doc.keys ?? []) {
62
- const input = jwkToInput(jwk);
63
- if (input && jwk.kid)
64
- map.set(jwk.kid, input);
58
+ try {
59
+ const res = await fetchWithDeadline(fetchImpl, opts.jwksUrl, { headers: { accept: 'application/json' } }, opts.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS);
60
+ if (!res.ok)
61
+ return map; // empty → every kid resolves null → fail closed
62
+ const doc = (await res.json());
63
+ for (const jwk of doc.keys ?? []) {
64
+ const input = jwkToInput(jwk);
65
+ if (input && jwk.kid)
66
+ map.set(jwk.kid, input);
67
+ }
68
+ }
69
+ catch {
70
+ // Unreachable / deadline / unreadable body → the EMPTY key set below, negative-cached
71
+ // briefly. The resolver itself never throws: a kid that cannot be resolved must come
72
+ // back as null ('unknown_key' — true) rather than as an exception the caller has to
73
+ // remember to catch.
65
74
  }
66
75
  return map;
67
76
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-kid.js","sourceRoot":"","sources":["../../src/verify/resolve-kid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAExD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAKrC,SAAS,OAAO,CAAC,CAAiC;IACjD,mEAAmE;IACnE,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;AAC7D,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,iBAAiB,CAAC,IAAkB;IACnD,OAAO,CAAC,GAAG,EAAE,EAAE;QACd,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;QACnB,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC7B,CAAC,CAAA;AACF,CAAC;AAkBD,yFAAyF;AACzF,SAAS,gBAAgB,CAAC,MAAc;IACvC,OAAO,eAAe,CAAC,MAAM,CAAC,CAAA;AAC/B,CAAC;AAED,SAAS,UAAU,CAAC,GAAiB;IACpC,IAAI,GAAG,CAAC,GAAG;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAA;IACnD,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QACrC,IAAI,KAAK,CAAC,MAAM,KAAK,EAAE;YAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IACzD,CAAC;IACD,OAAO,IAAI,CAAA;AACZ,CAAC;AAWD;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAgC;IACnE,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAA;IACpD,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAqC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS,EAAE,CAAC,CAAA;IAClG,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAA;IAE9B,KAAK,UAAU,UAAU;QACxB,MAAM,GAAG,GAAG,IAAI,GAAG,EAAiC,CAAA;QACpD,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAA;QACtF,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,GAAG,CAAA,CAAC,gDAAgD;QACxE,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAA;QACpD,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAA;YAC7B,IAAI,KAAK,IAAI,GAAG,CAAC,GAAG;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QAC9C,CAAC;QACD,OAAO,GAAG,CAAA;IACX,CAAC;IAED,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACpB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAA;QAChF,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAA;IAC/B,CAAC,CAAA;AACF,CAAC"}
1
+ {"version":3,"file":"resolve-kid.js","sourceRoot":"","sources":["../../src/verify/resolve-kid.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"certrev-badge.d.ts","sourceRoot":"","sources":["../../src/webcomponent/certrev-badge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAe,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAI/E,eAAO,MAAM,iBAAiB,kBAAkB,CAAA;AAQhD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAE3E;AAED,qBAAa,mBAAoB,SAAQ,WAAW;IACnD,MAAM,KAAK,kBAAkB,IAAI,MAAM,EAAE,CAExC;IAED,iBAAiB,IAAI,IAAI;IAOzB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;YAQjF,oBAAoB;CA4BlC;AAED,4FAA4F;AAC5F,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC"}
1
+ {"version":3,"file":"certrev-badge.d.ts","sourceRoot":"","sources":["../../src/webcomponent/certrev-badge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAe,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAI/E,eAAO,MAAM,iBAAiB,kBAAkB,CAAA;AAQhD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAE3E;AAaD,qBAAa,mBAAoB,SAAQ,WAAW;IACnD,MAAM,KAAK,kBAAkB,IAAI,MAAM,EAAE,CAExC;IAED,iBAAiB,IAAI,IAAI;IAOzB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;YAQjF,oBAAoB;CAuClC;AAED,4FAA4F;AAC5F,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC"}
@@ -36,6 +36,16 @@ let globalResolveKid = null;
36
36
  export function setCertRevKidResolver(resolver) {
37
37
  globalResolveKid = resolver;
38
38
  }
39
+ /**
40
+ * `badge-style` is AUTHOR-typed markup, not signed data — a hand-written attribute on a page
41
+ * we don't control. Only the two real faces are honored; anything else (`"Full"`, a stray
42
+ * value, an empty attribute) reads as ABSENT, so the signed display config decides. The
43
+ * attribute was previously CAST (`as 'full' | 'compact' | null`), which let a typo travel
44
+ * into the renderer as if it were a face.
45
+ */
46
+ function badgeStyleAttribute(value) {
47
+ return value === 'full' || value === 'compact' ? value : undefined;
48
+ }
39
49
  export class CertRevBadgeElement extends HTMLElement {
40
50
  static get observedAttributes() {
41
51
  return ['delivery-api', 'platform', 'external-id', 'accent-color', 'badge-style', 'content-hash'];
@@ -83,8 +93,20 @@ export class CertRevBadgeElement extends HTMLElement {
83
93
  if (verdict.decision !== 'render')
84
94
  return;
85
95
  const accentColor = this.getAttribute('accent-color') ?? undefined;
86
- const badgeStyle = this.getAttribute('badge-style') ?? undefined;
87
- this.innerHTML = renderBadgeHtml(verdict.payload, { accentColor, badgeStyle });
96
+ const badgeStyle = badgeStyleAttribute(this.getAttribute('badge-style'));
97
+ // The render is INSIDE the fail-closed boundary too. `connectedCallback` `void`s this
98
+ // promise, so a throw from the renderer (a payload shape nothing runtime-validates —
99
+ // the kernel checks the signature, never `content`) would escape as an unhandled
100
+ // rejection instead of rendering nothing, which is the one behaviour this element
101
+ // documents. Compute first, assign after, so a failure leaves the element untouched.
102
+ let html;
103
+ try {
104
+ html = renderBadgeHtml(verdict.payload, { accentColor, badgeStyle });
105
+ }
106
+ catch {
107
+ return; // fail closed
108
+ }
109
+ this.innerHTML = html;
88
110
  }
89
111
  }
90
112
  /** Register the element. Idempotent + safe to call only in a browser/customElements env. */
@@ -1 +1 @@
1
- {"version":3,"file":"certrev-badge.js","sourceRoot":"","sources":["../../src/webcomponent/certrev-badge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAExD,MAAM,CAAC,MAAM,iBAAiB,GAAG,eAAe,CAAA;AAEhD;;;;GAIG;AACH,IAAI,gBAAgB,GAAiC,IAAI,CAAA;AACzD,MAAM,UAAU,qBAAqB,CAAC,QAA+B;IACpE,gBAAgB,GAAG,QAAQ,CAAA;AAC5B,CAAC;AAED,MAAM,OAAO,mBAAoB,SAAQ,WAAW;IACnD,MAAM,KAAK,kBAAkB;QAC5B,OAAO,CAAC,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,CAAC,CAAA;IAClG,CAAC;IAED,iBAAiB;QAChB,qFAAqF;QACrF,8EAA8E;QAC9E,IAAI,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC;YAAE,OAAM;QAChD,KAAK,IAAI,CAAC,oBAAoB,EAAE,CAAA;IACjC,CAAC;IAED,wBAAwB,CAAC,KAAa,EAAE,QAAuB,EAAE,QAAuB;QACvF,IAAI,QAAQ,KAAK,QAAQ;YAAE,OAAM;QACjC,mEAAmE;QACnE,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC/D,KAAK,IAAI,CAAC,oBAAoB,EAAE,CAAA;QACjC,CAAC;IACF,CAAC;IAEO,KAAK,CAAC,oBAAoB;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,CAAA;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAA;QAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,CAAA;QACnD,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ,IAAI,CAAC,UAAU;YAAE,OAAM,CAAC,oCAAoC;QACrF,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACvB,qEAAqE;YACrE,OAAM;QACP,CAAC;QACD,IAAI,OAAoB,CAAA;QACxB,IAAI,CAAC;YACJ,OAAO,GAAG,MAAM,mBAAmB,CAAC;gBACnC,MAAM,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE;gBAC/D,UAAU,EAAE,gBAAgB;gBAC5B,OAAO,EAAE;oBACR,QAAQ;oBACR,UAAU;oBACV,eAAe,EAAE,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC;iBAClD;aACD,CAAC,CAAA;QACH,CAAC;QAAC,MAAM,CAAC;YACR,OAAM,CAAC,cAAc;QACtB,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAM;QACzC,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,IAAI,SAAS,CAAA;QAClE,MAAM,UAAU,GAAI,IAAI,CAAC,YAAY,CAAC,aAAa,CAA+B,IAAI,SAAS,CAAA;QAC/F,IAAI,CAAC,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAA;IAC/E,CAAC;CACD;AAED,4FAA4F;AAC5F,MAAM,UAAU,kBAAkB;IACjC,IAAI,OAAO,cAAc,KAAK,WAAW;QAAE,OAAM;IACjD,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,CAAC;QAC5C,cAAc,CAAC,MAAM,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,CAAA;IAC9D,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"certrev-badge.js","sourceRoot":"","sources":["../../src/webcomponent/certrev-badge.ts"],"names":[],"mappings":"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"}
@@ -17,7 +17,7 @@ export interface RenderBadgeOptions {
17
17
  readonly accentColor?: string;
18
18
  readonly badgeStyle?: 'full' | 'compact';
19
19
  /**
20
- * The brand render-def (WS6). ABSENT → the badge renders the CertREV preset
20
+ * The brand render-def. ABSENT → the badge renders the CertREV preset
21
21
  * default (theme output byte-identical to un-themed). PRESENT → validated
22
22
  * themeable tokens are emitted as `--certrev-*` CSS vars + subtractive
23
23
  * visibility applies. Mirrors `<CertBadge>`'s `renderDef` prop exactly.