standdown 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +221 -18
  2. package/dist/{chunk-25A7QG5Z.mjs → chunk-26BG7FFX.mjs} +3 -3
  3. package/dist/{chunk-25A7QG5Z.mjs.map → chunk-26BG7FFX.mjs.map} +1 -1
  4. package/dist/{chunk-SEZFAOUU.mjs → chunk-JKQTHHIP.mjs} +49 -4
  5. package/dist/chunk-JKQTHHIP.mjs.map +1 -0
  6. package/dist/{chunk-SVN6UWA4.mjs → chunk-VE7YBR5A.mjs} +142 -24
  7. package/dist/chunk-VE7YBR5A.mjs.map +1 -0
  8. package/dist/content.cjs +188 -28
  9. package/dist/content.cjs.map +1 -1
  10. package/dist/content.d.cts +11 -4
  11. package/dist/content.d.ts +11 -4
  12. package/dist/content.mjs +18 -8
  13. package/dist/content.mjs.map +1 -1
  14. package/dist/index.cjs +140 -26
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +3 -3
  17. package/dist/index.d.ts +3 -3
  18. package/dist/index.mjs +3 -7
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/policies.cjs +18 -11
  21. package/dist/policies.cjs.map +1 -1
  22. package/dist/policies.d.cts +98 -86
  23. package/dist/policies.d.ts +98 -86
  24. package/dist/policies.mjs +18 -12
  25. package/dist/policies.mjs.map +1 -1
  26. package/dist/{session-C5gH-LaQ.d.ts → session-DA9hkYlD.d.ts} +10 -1
  27. package/dist/{session-n0px3Agb.d.cts → session-iosT1mEL.d.cts} +10 -1
  28. package/dist/{stores-CELX9aHN.d.ts → stores-Behkya7C.d.ts} +1 -1
  29. package/dist/{stores-CKCdKDdX.d.cts → stores-DfovpSqe.d.cts} +1 -1
  30. package/dist/{types-A9OJ7SWj.d.cts → types-DgKvVCmQ.d.cts} +53 -7
  31. package/dist/{types-A9OJ7SWj.d.ts → types-DgKvVCmQ.d.ts} +53 -7
  32. package/dist/webext.cjs +191 -28
  33. package/dist/webext.cjs.map +1 -1
  34. package/dist/webext.d.cts +11 -4
  35. package/dist/webext.d.ts +11 -4
  36. package/dist/webext.mjs +22 -9
  37. package/dist/webext.mjs.map +1 -1
  38. package/package.json +1 -1
  39. package/dist/chunk-SEZFAOUU.mjs.map +0 -1
  40. package/dist/chunk-SVN6UWA4.mjs.map +0 -1
package/README.md CHANGED
@@ -1,14 +1,88 @@
1
- # standdown
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/dupe-com/standdown/main/assets/logo.png" alt="standdown — affiliate stand-down, done right" width="620">
3
+ </p>
2
4
 
3
- Affiliate stand-down, done right, for browser extensions.
5
+ <p align="center">
6
+ <a href="https://www.npmjs.com/package/standdown"><img src="https://img.shields.io/npm/v/standdown?color=F5A623&label=npm&labelColor=1C1917" alt="npm version"></a>
7
+ <a href="https://github.com/dupe-com/standdown/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/dupe-com/standdown/ci.yml?branch=main&label=CI&labelColor=1C1917" alt="CI status"></a>
8
+ <img src="https://img.shields.io/badge/dependencies-0-2ea043?labelColor=1C1917" alt="zero runtime dependencies">
9
+ <img src="https://img.shields.io/badge/types-included-3178C6?labelColor=1C1917" alt="TypeScript types included">
10
+ <a href="./LICENSE"><img src="https://img.shields.io/npm/l/standdown?color=8A8175&labelColor=1C1917" alt="MIT license"></a>
11
+ <a href="https://affiliatecoc.org"><img src="https://img.shields.io/badge/aligned-Affiliate%20CoC-F5A623?labelColor=1C1917" alt="aligned with the Affiliate Code of Conduct"></a>
12
+ </p>
4
13
 
5
- Built and maintained by Dupe.
14
+ > **Your extension shouldn't steal the sale.** `standdown` detects existing
15
+ > affiliate attribution, suppresses competing activation, and proves the
16
+ > decision was made locally — never on a server.
17
+
18
+ Built and maintained by [Dupe](https://dupe.com).
6
19
 
7
20
  `standdown` is a zero-runtime-dependency TypeScript library for extension
8
21
  developers who need to detect existing affiliate attribution, suppress
9
22
  competing activation, and prove that suppression decisions were made locally and
10
23
  deterministically.
11
24
 
25
+ ## What makes it different
26
+
27
+ Affiliate stand-down is easy to claim and hard to prove. `standdown` is built so
28
+ the guarantees are structural — enforced by the type system and the architecture,
29
+ not by a promise in a blog post.
30
+
31
+ - 🔒 **Decisions never leave the device.** No network call participates in a
32
+ stand-down decision, ever. The decision path is a pure function of local
33
+ signals and bundled policies.
34
+ - 🛡️ **User data can't leak into a decision — by construction.** `Signals` is a
35
+ closed type: identity, accounts, balances, email, and login state are
36
+ structurally unable to enter it. No profiling, no compliance-tester detection.
37
+ - ⚖️ **Fails toward standing down.** Ambiguity, storage errors, or a malformed
38
+ policy all resolve to *suppress*. The library never hijacks a sale by accident.
39
+ - 🧾 **Provably compliant.** Every decision is deterministic and appended to a
40
+ local, exportable audit log — reproducible evidence you can hand to a
41
+ third-party auditor.
42
+ - 🎯 **Detects attribution the way networks actually set it.** Landing params,
43
+ redirect-chain hops, first-party cookie *names* (never values), and
44
+ referrer/initiator classification — across eight verified network packs.
45
+ - ✍️ **Tamper-evident updates.** Signed policy bundles (Ed25519 / ECDSA-P256)
46
+ that can only *broaden* coverage or *lengthen* durations — a remote update can
47
+ never weaken a guard.
48
+ - 🅰️ **It grades itself, F→A+.** A [black-box conformance harness](#conformance-grading)
49
+ loads a real extension into a real browser and scores whether it respects
50
+ existing attribution — with an inert-code guard so "disciplined" can't be faked
51
+ by shipping nothing.
52
+ - 📦 **Zero runtime dependencies.** Ships ESM + CJS + types. MV3, with a
53
+ content-script path for Safari and reduced-permission contexts.
54
+
55
+ ## Works across the major affiliate networks
56
+
57
+ Each bundled pack implements a network's *stand-down* expectations — the
58
+ detection signals it sets, the suppression behavior it asks for, and how long it
59
+ lasts — so you don't have to reverse-engineer them. Eight **verified** packs
60
+ ship enabled by default: seven named networks, plus a universal
61
+ redirect-fingerprint set.
62
+
63
+ <p align="center">
64
+ <a href="https://www.cj.com/legal/software-policy"><img src="https://img.shields.io/badge/CJ%20Affiliate-00857C?style=for-the-badge&logoColor=white" alt="CJ Affiliate"></a>
65
+ <a href="https://impact.com/stand-down-policy.ihtml"><img src="https://img.shields.io/badge/Impact-0E1C36?style=for-the-badge&logoColor=white" alt="Impact"></a>
66
+ <a href="https://github.com/rakutenrewards/PublisherStandown-SDK"><img src="https://img.shields.io/badge/Rakuten%20Advertising-BF0000?style=for-the-badge&logo=rakuten&logoColor=white" alt="Rakuten Advertising"></a>
67
+ <a href="https://success.awin.com/s/article/Downloadable-Software-Guidelines"><img src="https://img.shields.io/badge/Awin-E4097E?style=for-the-badge&logoColor=white" alt="Awin"></a>
68
+ <a href="https://success.awin.com/s/article/Downloadable-Software-Guidelines"><img src="https://img.shields.io/badge/ShareASale-1F6FB2?style=for-the-badge&logoColor=white" alt="ShareASale"></a>
69
+ <a href="https://partnernetwork.ebay.com/browser-extension-policy"><img src="https://img.shields.io/badge/eBay%20Partner%20Network-E53238?style=for-the-badge&logo=ebay&logoColor=white" alt="eBay Partner Network"></a>
70
+ <a href="https://affiliate-program.amazon.com/help/operating/policies"><img src="https://img.shields.io/badge/Amazon%20Associates-FF9900?style=for-the-badge&logoColor=white" alt="Amazon Associates"></a>
71
+ </p>
72
+
73
+ The eighth verified pack is a **universal** set of publisher-contributed
74
+ redirect fingerprints ([piedotorg/standdown-domains](https://github.com/piedotorg/standdown-domains)).
75
+ Two more **experimental** packs are inferred from domain knowledge and stay
76
+ opt-in until you verify them for your integration:
77
+
78
+ <p align="center">
79
+ <a href="https://www.sovrn.com/sovrn-commerce-publisher-code-of-conduct/"><img src="https://img.shields.io/badge/Sovrn%20%2F%20Skimlinks-FF5A00?style=for-the-badge&logoColor=white" alt="Sovrn / Skimlinks"></a>
80
+ <a href="https://partnerize.com/legal/terms-and-conditions/"><img src="https://img.shields.io/badge/Partnerize-00B0A6?style=for-the-badge&logoColor=white" alt="Partnerize"></a>
81
+ </p>
82
+
83
+ > Network names and logos identify the stand-down policies each pack implements.
84
+ > They don't imply endorsement, partnership, or certification by these networks.
85
+
12
86
  It ships four surfaces:
13
87
 
14
88
  | Import | Purpose |
@@ -24,8 +98,8 @@ It ships four surfaces:
24
98
  npm install standdown
25
99
  ```
26
100
 
27
- This package is pre-release. Publishing is a human launch step; the repo should
28
- not publish from CI.
101
+ `standdown` is published to npm and versioned with semver from `0.1.0`. Releases
102
+ are cut by a human; the repo does not publish from CI.
29
103
 
30
104
  ## Webext Quickstart
31
105
 
@@ -40,14 +114,18 @@ const standdown = createStanddown({
40
114
  });
41
115
  ```
42
116
 
43
- The adapter observes `chrome.webRequest.onBeforeRequest` redirect chains when
44
- available and falls back to `chrome.webNavigation.onCommitted` final-URL signal
45
- collection when reduced permissions or Safari-style APIs require it. Content
46
- scripts and popups can query the background worker with:
117
+ The `standdown/webext` adapter is the Chromium-MV3 path. It observes
118
+ `chrome.webRequest.onBeforeRequest` redirect chains when available and uses
119
+ `chrome.webNavigation.onCommitted` final-URL signal collection when `webRequest`
120
+ is reduced or unavailable. `chrome.webNavigation.onCommitted` itself is
121
+ **required**: `createStanddown()` throws when that API is absent, because an
122
+ adapter that observes no navigations would fail open.
47
123
 
48
- `chrome.webNavigation.onCommitted` is required. `createStanddown()` throws when
49
- that API is unavailable because an adapter that observes no navigations would
50
- fail open.
124
+ Safari and other reduced-permission contexts do **not** run this adapter — they
125
+ use the page-level `standdown/content` adapter below, which collects signals from
126
+ the page (`location.href`, `document.referrer`, first-party cookie names) and
127
+ needs no `chrome.*` network APIs. Content scripts and popups can query the
128
+ background worker with:
51
129
 
52
130
  ```ts
53
131
  const response = await chrome.runtime.sendMessage({
@@ -96,11 +174,66 @@ The content adapter collects only local page signals: `location.href`,
96
174
  included. SPA navigations are re-evaluated via `pushState`, `replaceState`, and
97
175
  `popstate` hooks.
98
176
 
177
+ **Cookie matching is name-only, by design.** `CookiePattern` rules (`exact` and
178
+ `substring`) match against cookie **names**, never values — that is what keeps
179
+ user data structurally out of `Signals` (invariant I2). If you are migrating from
180
+ an implementation that matches against the whole `document.cookie` string (names
181
+ *and* values), verify that your cookie rules only depend on names before porting
182
+ them; a rule that secretly relied on matching a cookie *value* will not fire here.
183
+ This is intentional and not configurable: matching values would require cookie
184
+ values to enter `Signals`, which the closed-signal privacy guarantee forbids.
185
+
99
186
  `storage: 'local-ttl'` stores session records in `localStorage` with a sliding
100
187
  24-hour envelope TTL by default. The TTL clears session records, not audit
101
188
  history; per-policy stand-down durations remain enforced by the core state
102
189
  machine.
103
190
 
191
+ ### Degraded decisions
192
+
193
+ The content adapter (and a webext adapter running without the `webRequest`
194
+ plane) cannot observe redirect chains, so it sets `Signals.signalCoverage =
195
+ 'partial'`. When a decision comes back `standDown: false` from a partial signal
196
+ set, it carries `degraded: true` — the "no stand-down" may be a false negative
197
+ because a redirect-only attribution could have been missed. Stand-down decisions
198
+ are never marked degraded (over-suppression is the safe direction). Integrators
199
+ that want to fail fully closed can treat a degraded non-stand-down as a
200
+ stand-down:
201
+
202
+ ```ts
203
+ const decision = await standdown.ready;
204
+ const suppress = decision.standDown || decision.degraded;
205
+ ```
206
+
207
+ ## Self-exemption scope
208
+
209
+ `selfPatterns` declare the params that are *your own* attribution — when one
210
+ matches, the library does not stand down against that network (it's your click,
211
+ not a competitor's). By default this exemption lasts only for the navigation that
212
+ carries the param (`selfExemptionScope: 'policy'`). Merchants often strip the
213
+ param on later internal navigations while your attribution (a first-party cookie,
214
+ say) lingers — under policy scope that lingering signal would then read as a
215
+ competitor and stand you down.
216
+
217
+ `selfExemptionScope: 'session'` fixes that: once your param is seen on a host, the
218
+ exemption is remembered for the session and re-applied to *that same network's*
219
+ signals on later param-less navigations. This is Dupe's `ignore_param` behavior.
220
+
221
+ ```ts
222
+ const session = new StanddownSession(store, { selfExemptionScope: 'session' });
223
+ // or, through an adapter:
224
+ createStanddown({ policies, selfPatterns, selfExemptionScope: 'session' });
225
+ ```
226
+
227
+ It is deliberately **network-precise, not host-blanket**, and monotone:
228
+
229
+ - A fresh signal from a *different* network on the same host still stands down —
230
+ claiming the host for your network never silences a competitor's.
231
+ - A session exemption **never lifts an already-active stand-down**. If a
232
+ competitor stand-down formed first, a later self-param does not clear it (no
233
+ exemption is even recorded while a stand-down is active).
234
+ - A `disableHosts` match is immune — a hard-disabled host stands down regardless
235
+ of any exemption.
236
+
104
237
  ## Core Usage
105
238
 
106
239
  ```ts
@@ -139,6 +272,59 @@ const guard = guardActivation({
139
272
  - **I7: Deterministic and loggable.** Given the same local signals, policies,
140
273
  state, and clock, decisions are reproducible.
141
274
 
275
+ ## Conformance grading
276
+
277
+ Unit tests prove the library's decisions in isolation. The
278
+ [`audit/`](./audit) harness proves the thing they can't: whether a *real MV3
279
+ extension*, loaded into a *real browser* against realistic merchant pages,
280
+ actually stands down instead of hijacking existing attribution.
281
+
282
+ It serves synthetic merchant fixtures carrying pre-existing attribution for
283
+ each network, drives an extension through every scenario, and scores the run
284
+ **F → A+**:
285
+
286
+ ```sh
287
+ cd audit && npm install
288
+ npx tsx grade/grade.ts /path/to/your/unpacked-extension
289
+ # standdown grade: A+ (100/100)
290
+ # Respected existing attribution across all tested networks and activated when allowed.
291
+ ```
292
+
293
+ The rubric includes an **inert cap**: an extension that never activates even when
294
+ activation is *allowed* hasn't proven it does anything, so it can't score above a
295
+ C. That stops "disciplined stand-down" from being faked by shipping dead code.
296
+ Three reference extensions (`good` / `bad` / `inert`) ship alongside to validate
297
+ the grader itself.
298
+
299
+ The harness is opt-in: it is not part of the npm package and is not on the
300
+ required CI path. See [`audit/README.md`](./audit/README.md) for the full guide.
301
+
302
+ ## Per-host disable
303
+
304
+ Some merchants are ones where competing activation is never acceptable — the
305
+ integrator wants to go fully quiet on that host rather than detect-then-suppress.
306
+ `detection.disableHosts` expresses that: any navigation whose advertiser host
307
+ matches stands down **unconditionally**, regardless of params, cookies, or
308
+ self-exemption. It is the strongest match kind (`disabled-host`), and it is how
309
+ you model a "we do not operate here at all" list (the extension's
310
+ `disable_domains`).
311
+
312
+ ```ts
313
+ const merchantBlocklistPolicy = {
314
+ // ...id, network, standdown, activation, metadata...
315
+ detection: {
316
+ disableHosts: [
317
+ { pattern: '(^|\\.)ebay\\.[a-z.]+$', kind: 'regex' },
318
+ { pattern: 'homedepot.com', kind: 'suffix' },
319
+ ],
320
+ },
321
+ };
322
+ ```
323
+
324
+ A `disableHosts` match cannot be cleared by a `selfPatterns` exemption — if you
325
+ list a host here, your own attribution on that host still stands down. Use it
326
+ only for hosts where you never want to activate.
327
+
142
328
  ## Interop
143
329
 
144
330
  `fromRakutenPolicy()` and `toRakutenPolicy()` convert Rakuten
@@ -152,17 +338,34 @@ bundled `rakuten` policy itself intentionally does not round-trip exactly.
152
338
 
153
339
  | Policy | Main signals | Stand-down | Activation |
154
340
  | --- | --- | --- | --- |
155
- | `cj` | `cjevent`, `cjdata`, `utm_source=cj`, `sf_cs=cj`, `afsrc=1`, CJ redirect domains, CJ cookie names | Session-or-min 30m | User click |
156
- | `impact` | `afsrc=1`, `irclickid`, `irgwc`, `im_ref` cookie names | Session-or-min 30m | User click |
157
- | `rakuten` | `ranMID`, `ranEAID`, `ranSiteID`, `siteID`, LinkSynergy redirect domains, LinkShare cookie names | Session-or-min fallback | User click |
341
+ | `cj` | `cjevent`, `cjdata`, `utm_source=cj`, `sf_cs=cj`, `afsrc=1`, CJ redirect domains, CJ cookie names | Session-or-min 60m | User click |
342
+ | `impact` | `afsrc=1`, `irclickid`, `irgwc`, `im_ref` cookie names | Session-only | User click |
343
+ | `rakuten` | `ranMID`, `ranEAID`, `ranSiteID`, `siteID`, LinkSynergy redirect domains, LinkShare cookie names | Session-only | User click |
158
344
  | `awin` | `awc`, `utm_source=aw`, `source=aw`, `awin1.com` | CoC defaults | User click |
159
345
  | `shareasale` | `sscid`, ShareASale redirect domains, `sscid` cookie name | CoC defaults | User click |
160
- | `ebay-epn` | eBay EPN params, `rover.ebay.com`, scoped referrer classification | CoC defaults | User click, max 2 prompts |
346
+ | `ebay-epn` | eBay EPN params, `rover.ebay.com`, scoped referrer classification | CoC defaults | User click |
161
347
  | `amazon` | Amazon `tag` on Amazon advertiser hosts | Suppression visibility only | Never |
162
- | `sovrn-skimlinks` | Skimlinks redirect domains | CoC defaults | User click |
163
- | `partnerize` | `clickref`, `prf.hn` | CoC defaults | User click |
348
+ | `sovrn-skimlinks` | Skimlinks redirect domains | CoC defaults | User click |
349
+ | `partnerize` | `clickref`, `prf.hn` | CoC defaults | User click |
164
350
  | `universal` | Full `piedotorg/standdown-domains` list plus `afsrc=1` | CoC defaults | User click |
165
351
 
352
+ `allPolicies` is the **verified** default set. Packs marked † (`sovrn-skimlinks`,
353
+ `partnerize`) have redirect domains inferred from domain knowledge rather than
354
+ verified against network documentation, so they are excluded from `allPolicies`
355
+ and exported separately as `experimentalPolicies`. Opt in explicitly once you
356
+ have verified them for your integration:
357
+
358
+ ```ts
359
+ import { allPolicies, experimentalPolicies } from 'standdown/policies';
360
+
361
+ const policies = [...allPolicies, ...experimentalPolicies];
362
+ ```
363
+
364
+ `amazon` is detect-only: it reports attribution for suppression visibility but
365
+ its `activation.mode` is `never`, so the guard will never allow activation on an
366
+ Amazon advertiser host. Integrators with their own Amazon arrangement can supply
367
+ a policy that overrides this.
368
+
166
369
  See [POLICIES.md](./POLICIES.md) for citations and attribution.
167
370
 
168
371
  ## Example
@@ -1,4 +1,4 @@
1
- import { validatePolicies } from './chunk-SVN6UWA4.mjs';
1
+ import { validatePolicies } from './chunk-VE7YBR5A.mjs';
2
2
 
3
3
  // src/bundle.ts
4
4
  var textEncoder = new TextEncoder();
@@ -270,5 +270,5 @@ function messageFromError(error) {
270
270
  }
271
271
 
272
272
  export { canonicalJson, canonicalPolicyBundlePayload, verifyPolicyBundle };
273
- //# sourceMappingURL=chunk-25A7QG5Z.mjs.map
274
- //# sourceMappingURL=chunk-25A7QG5Z.mjs.map
273
+ //# sourceMappingURL=chunk-26BG7FFX.mjs.map
274
+ //# sourceMappingURL=chunk-26BG7FFX.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bundle.ts"],"names":[],"mappings":"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","file":"chunk-25A7QG5Z.mjs","sourcesContent":["import type {\n CookieRule,\n DomainRule,\n InitiatorRule,\n ParamRule,\n PolicyBundleVerificationResult,\n SignedBundleSignatureAlgorithm,\n SignedPolicyBundle,\n StanddownPolicy,\n} from './types';\nimport { validatePolicies } from './validation';\n\nconst textEncoder = new TextEncoder();\nconst MAX_SIGNED_BUNDLE_REGEX_LENGTH = 256;\n\nexport async function verifyPolicyBundle(\n current: readonly StanddownPolicy[],\n update: SignedPolicyBundle,\n publicKeyJwk: JsonWebKey,\n): Promise<PolicyBundleVerificationResult> {\n try {\n validatePolicies(current);\n validatePolicies(update.policies);\n } catch (error) {\n return { ok: false, violation: `malformed-policy: ${messageFromError(error)}` };\n }\n\n if (update.schemaVersion !== 1) {\n return { ok: false, violation: 'unsupported-bundle-version' };\n }\n\n const signatureOk = await verifySignature(\n update.signature.algorithm,\n publicKeyJwk,\n update.signature.value,\n canonicalPolicyBundlePayload(update),\n );\n\n if (!signatureOk) {\n return { ok: false, violation: 'bad-signature' };\n }\n\n const regexViolation = regexComplexityViolation(update.policies);\n\n if (regexViolation !== undefined) {\n return { ok: false, violation: regexViolation };\n }\n\n const monotonicityViolation = checkMonotonicity(current, update.policies);\n\n if (monotonicityViolation !== undefined) {\n return { ok: false, violation: monotonicityViolation };\n }\n\n return { ok: true, policies: update.policies.map(clonePolicy) };\n}\n\nexport function canonicalPolicyBundlePayload(\n bundle: Pick<SignedPolicyBundle, 'schemaVersion' | 'policies'>,\n): string {\n return canonicalJson({\n schemaVersion: bundle.schemaVersion,\n policies: bundle.policies,\n });\n}\n\nexport function canonicalJson(value: unknown): string {\n if (value === null || typeof value !== 'object') {\n if (typeof value === 'number' && !Number.isFinite(value)) {\n throw new TypeError('canonical JSON only supports finite numbers');\n }\n\n return JSON.stringify(value);\n }\n\n if (Array.isArray(value)) {\n return `[${value.map((item) => canonicalJson(item)).join(',')}]`;\n }\n\n const record = value as Record<string, unknown>;\n const entries = Object.keys(record)\n .filter((key) => record[key] !== undefined)\n .sort()\n .map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`);\n\n return `{${entries.join(',')}}`;\n}\n\nfunction checkMonotonicity(\n current: readonly StanddownPolicy[],\n update: readonly StanddownPolicy[],\n): string | undefined {\n const updateById = new Map(update.map((policy) => [policy.id, policy]));\n\n for (const currentPolicy of current) {\n const updatedPolicy = updateById.get(currentPolicy.id);\n\n if (updatedPolicy === undefined) {\n return `policy-removed:${currentPolicy.id}`;\n }\n\n const detectionResult = detectionViolation(currentPolicy, updatedPolicy);\n\n if (detectionResult !== undefined) {\n return `${currentPolicy.id}:${detectionResult}`;\n }\n\n const standdownResult = standdownViolation(currentPolicy, updatedPolicy);\n\n if (standdownResult !== undefined) {\n return `${currentPolicy.id}:${standdownResult}`;\n }\n\n if (!sameJson(currentPolicy.activation, updatedPolicy.activation)) {\n return `${currentPolicy.id}:activation-edited`;\n }\n }\n\n return undefined;\n}\n\nfunction detectionViolation(\n currentPolicy: StanddownPolicy,\n updatedPolicy: StanddownPolicy,\n): string | undefined {\n if (\n !domainRulesSurvive(\n currentPolicy.detection.advertiserHosts,\n updatedPolicy.detection.advertiserHosts,\n { advertiserHosts: true },\n )\n ) {\n return 'advertiser-hosts-narrowed';\n }\n\n if (\n !paramRulesSurvive(\n currentPolicy.detection.landingParams,\n updatedPolicy.detection.landingParams,\n )\n ) {\n return 'landing-params-narrowed';\n }\n\n if (\n !domainRulesSurvive(\n currentPolicy.detection.redirectDomains,\n updatedPolicy.detection.redirectDomains,\n )\n ) {\n return 'redirect-domains-narrowed';\n }\n\n if (\n !cookieRulesSurvive(\n currentPolicy.detection.cookiePatterns,\n updatedPolicy.detection.cookiePatterns,\n )\n ) {\n return 'cookie-patterns-narrowed';\n }\n\n if (\n !initiatorRulesSurvive(\n currentPolicy.detection.initiatorRules,\n updatedPolicy.detection.initiatorRules,\n )\n ) {\n return 'initiator-rules-narrowed';\n }\n\n return undefined;\n}\n\nfunction standdownViolation(\n currentPolicy: StanddownPolicy,\n updatedPolicy: StanddownPolicy,\n): string | undefined {\n if (updatedPolicy.standdown.scope !== currentPolicy.standdown.scope) {\n return 'standdown-scope-edited';\n }\n\n if (updatedPolicy.standdown.sessionRule !== currentPolicy.standdown.sessionRule) {\n return 'session-rule-edited';\n }\n\n if (updatedPolicy.standdown.minDurationMs < currentPolicy.standdown.minDurationMs) {\n return 'min-duration-shortened';\n }\n\n if (\n currentPolicy.standdown.inactivityMs !== undefined &&\n (updatedPolicy.standdown.inactivityMs === undefined ||\n updatedPolicy.standdown.inactivityMs < currentPolicy.standdown.inactivityMs)\n ) {\n return 'inactivity-duration-shortened';\n }\n\n for (const behavior of currentPolicy.standdown.behaviors) {\n if (!updatedPolicy.standdown.behaviors.includes(behavior)) {\n return 'standdown-behavior-removed';\n }\n }\n\n return undefined;\n}\n\nfunction domainRulesSurvive(\n current: readonly DomainRule[] | undefined,\n update: readonly DomainRule[] | undefined,\n opts: { advertiserHosts?: boolean } = {},\n): boolean {\n if (opts.advertiserHosts && current === undefined) {\n return update === undefined;\n }\n\n if (current === undefined || current.length === 0) {\n return true;\n }\n\n if (opts.advertiserHosts && update === undefined) {\n return true;\n }\n\n if (update === undefined || update.length === 0) {\n return false;\n }\n\n return current.every((rule) =>\n update.some((candidate) => domainRuleCovers(rule, candidate)),\n );\n}\n\nfunction regexComplexityViolation(\n policies: readonly StanddownPolicy[],\n): string | undefined {\n for (const policy of policies) {\n for (const [field, rules] of domainRuleEntries(policy)) {\n for (const rule of rules ?? []) {\n if (rule.kind === 'regex' && isComplexRegex(rule.pattern)) {\n return `${policy.id}:complex-regex:${field}`;\n }\n }\n }\n }\n\n return undefined;\n}\n\nfunction domainRuleEntries(\n policy: StanddownPolicy,\n): readonly (readonly [string, readonly DomainRule[] | undefined])[] {\n return [\n ['detection.advertiserHosts', policy.detection.advertiserHosts],\n ['detection.redirectDomains', policy.detection.redirectDomains],\n ] as const;\n}\n\nfunction isComplexRegex(pattern: string): boolean {\n return (\n pattern.length > MAX_SIGNED_BUNDLE_REGEX_LENGTH ||\n hasRegexBackreference(pattern) ||\n hasRegexLookaround(pattern) ||\n hasNestedUnboundedQuantifier(pattern)\n );\n}\n\nfunction hasRegexBackreference(pattern: string): boolean {\n return /(^|[^\\\\])\\\\[1-9]/.test(pattern);\n}\n\nfunction hasRegexLookaround(pattern: string): boolean {\n return /\\(\\?(?:[=!]|<[=!])/.test(pattern);\n}\n\nfunction hasNestedUnboundedQuantifier(pattern: string): boolean {\n return /\\((?:[^()\\\\]|\\\\.)*[+*](?:[^()\\\\]|\\\\.)*\\)(?:[+*]|\\{\\d*,?\\})/.test(\n pattern,\n );\n}\n\nfunction domainRuleCovers(current: DomainRule, update: DomainRule): boolean {\n if (current.kind === 'suffix' && update.kind === 'suffix') {\n return domainSuffixMatches(current.pattern, update.pattern);\n }\n\n return sameJson(current, update);\n}\n\nfunction paramRulesSurvive(\n current: readonly ParamRule[] | undefined,\n update: readonly ParamRule[] | undefined,\n): boolean {\n return exactRulesSurvive(current, update);\n}\n\nfunction cookieRulesSurvive(\n current: readonly CookieRule[] | undefined,\n update: readonly CookieRule[] | undefined,\n): boolean {\n return exactRulesSurvive(current, update);\n}\n\nfunction initiatorRulesSurvive(\n current: readonly InitiatorRule[] | undefined,\n update: readonly InitiatorRule[] | undefined,\n): boolean {\n return exactRulesSurvive(current, update);\n}\n\nfunction exactRulesSurvive<T>(\n current: readonly T[] | undefined,\n update: readonly T[] | undefined,\n): boolean {\n if (current === undefined || current.length === 0) {\n return true;\n }\n\n if (update === undefined || update.length === 0) {\n return false;\n }\n\n const updatedRules = new Set(update.map((rule) => canonicalJson(rule)));\n\n return current.every((rule) => updatedRules.has(canonicalJson(rule)));\n}\n\nasync function verifySignature(\n algorithm: SignedBundleSignatureAlgorithm,\n publicKeyJwk: JsonWebKey,\n signatureValue: string,\n payload: string,\n): Promise<boolean> {\n try {\n const publicKey = await importVerificationKey(algorithm, publicKeyJwk);\n const signature = base64UrlToBytes(signatureValue);\n const data = bytesToArrayBuffer(textEncoder.encode(payload));\n\n if (algorithm === 'ECDSA-P256') {\n return crypto.subtle.verify(\n { name: 'ECDSA', hash: 'SHA-256' },\n publicKey,\n bytesToArrayBuffer(signature),\n data,\n );\n }\n\n return crypto.subtle.verify(\n 'Ed25519',\n publicKey,\n bytesToArrayBuffer(signature),\n data,\n );\n } catch {\n return false;\n }\n}\n\nasync function importVerificationKey(\n algorithm: SignedBundleSignatureAlgorithm,\n publicKeyJwk: JsonWebKey,\n): Promise<CryptoKey> {\n if (algorithm === 'ECDSA-P256') {\n return crypto.subtle.importKey(\n 'jwk',\n publicKeyJwk,\n { name: 'ECDSA', namedCurve: 'P-256' },\n false,\n ['verify'],\n );\n }\n\n return crypto.subtle.importKey('jwk', publicKeyJwk, 'Ed25519', false, [\n 'verify',\n ]);\n}\n\nfunction base64UrlToBytes(value: string): Uint8Array {\n const base64 = value.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n\n return bytes;\n}\n\nfunction bytesToArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n return bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength,\n ) as ArrayBuffer;\n}\n\nfunction sameJson(left: unknown, right: unknown): boolean {\n return canonicalJson(left) === canonicalJson(right);\n}\n\nfunction domainSuffixMatches(host: string, pattern: string): boolean {\n const normalizedHost = host.toLowerCase().replace(/^\\./, '').replace(/\\.$/, '');\n const normalizedPattern = pattern\n .toLowerCase()\n .replace(/^\\./, '')\n .replace(/\\.$/, '');\n\n return (\n normalizedHost === normalizedPattern ||\n normalizedHost.endsWith(`.${normalizedPattern}`)\n );\n}\n\nfunction clonePolicy(policy: StanddownPolicy): StanddownPolicy {\n return JSON.parse(JSON.stringify(policy)) as StanddownPolicy;\n}\n\nfunction messageFromError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
1
+ {"version":3,"sources":["../src/bundle.ts"],"names":[],"mappings":"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","file":"chunk-26BG7FFX.mjs","sourcesContent":["import type {\n CookieRule,\n DomainRule,\n InitiatorRule,\n ParamRule,\n PolicyBundleVerificationResult,\n SignedBundleSignatureAlgorithm,\n SignedPolicyBundle,\n StanddownPolicy,\n} from './types';\nimport { validatePolicies } from './validation';\n\nconst textEncoder = new TextEncoder();\nconst MAX_SIGNED_BUNDLE_REGEX_LENGTH = 256;\n\nexport async function verifyPolicyBundle(\n current: readonly StanddownPolicy[],\n update: SignedPolicyBundle,\n publicKeyJwk: JsonWebKey,\n): Promise<PolicyBundleVerificationResult> {\n try {\n validatePolicies(current);\n validatePolicies(update.policies);\n } catch (error) {\n return { ok: false, violation: `malformed-policy: ${messageFromError(error)}` };\n }\n\n if (update.schemaVersion !== 1) {\n return { ok: false, violation: 'unsupported-bundle-version' };\n }\n\n const signatureOk = await verifySignature(\n update.signature.algorithm,\n publicKeyJwk,\n update.signature.value,\n canonicalPolicyBundlePayload(update),\n );\n\n if (!signatureOk) {\n return { ok: false, violation: 'bad-signature' };\n }\n\n const regexViolation = regexComplexityViolation(update.policies);\n\n if (regexViolation !== undefined) {\n return { ok: false, violation: regexViolation };\n }\n\n const monotonicityViolation = checkMonotonicity(current, update.policies);\n\n if (monotonicityViolation !== undefined) {\n return { ok: false, violation: monotonicityViolation };\n }\n\n return { ok: true, policies: update.policies.map(clonePolicy) };\n}\n\nexport function canonicalPolicyBundlePayload(\n bundle: Pick<SignedPolicyBundle, 'schemaVersion' | 'policies'>,\n): string {\n return canonicalJson({\n schemaVersion: bundle.schemaVersion,\n policies: bundle.policies,\n });\n}\n\nexport function canonicalJson(value: unknown): string {\n if (value === null || typeof value !== 'object') {\n if (typeof value === 'number' && !Number.isFinite(value)) {\n throw new TypeError('canonical JSON only supports finite numbers');\n }\n\n return JSON.stringify(value);\n }\n\n if (Array.isArray(value)) {\n return `[${value.map((item) => canonicalJson(item)).join(',')}]`;\n }\n\n const record = value as Record<string, unknown>;\n const entries = Object.keys(record)\n .filter((key) => record[key] !== undefined)\n .sort()\n .map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`);\n\n return `{${entries.join(',')}}`;\n}\n\nfunction checkMonotonicity(\n current: readonly StanddownPolicy[],\n update: readonly StanddownPolicy[],\n): string | undefined {\n const updateById = new Map(update.map((policy) => [policy.id, policy]));\n\n for (const currentPolicy of current) {\n const updatedPolicy = updateById.get(currentPolicy.id);\n\n if (updatedPolicy === undefined) {\n return `policy-removed:${currentPolicy.id}`;\n }\n\n const detectionResult = detectionViolation(currentPolicy, updatedPolicy);\n\n if (detectionResult !== undefined) {\n return `${currentPolicy.id}:${detectionResult}`;\n }\n\n const standdownResult = standdownViolation(currentPolicy, updatedPolicy);\n\n if (standdownResult !== undefined) {\n return `${currentPolicy.id}:${standdownResult}`;\n }\n\n if (!sameJson(currentPolicy.activation, updatedPolicy.activation)) {\n return `${currentPolicy.id}:activation-edited`;\n }\n }\n\n return undefined;\n}\n\nfunction detectionViolation(\n currentPolicy: StanddownPolicy,\n updatedPolicy: StanddownPolicy,\n): string | undefined {\n if (\n !domainRulesSurvive(\n currentPolicy.detection.advertiserHosts,\n updatedPolicy.detection.advertiserHosts,\n { advertiserHosts: true },\n )\n ) {\n return 'advertiser-hosts-narrowed';\n }\n\n if (\n !paramRulesSurvive(\n currentPolicy.detection.landingParams,\n updatedPolicy.detection.landingParams,\n )\n ) {\n return 'landing-params-narrowed';\n }\n\n if (\n !domainRulesSurvive(\n currentPolicy.detection.redirectDomains,\n updatedPolicy.detection.redirectDomains,\n )\n ) {\n return 'redirect-domains-narrowed';\n }\n\n if (\n !cookieRulesSurvive(\n currentPolicy.detection.cookiePatterns,\n updatedPolicy.detection.cookiePatterns,\n )\n ) {\n return 'cookie-patterns-narrowed';\n }\n\n if (\n !initiatorRulesSurvive(\n currentPolicy.detection.initiatorRules,\n updatedPolicy.detection.initiatorRules,\n )\n ) {\n return 'initiator-rules-narrowed';\n }\n\n return undefined;\n}\n\nfunction standdownViolation(\n currentPolicy: StanddownPolicy,\n updatedPolicy: StanddownPolicy,\n): string | undefined {\n if (updatedPolicy.standdown.scope !== currentPolicy.standdown.scope) {\n return 'standdown-scope-edited';\n }\n\n if (updatedPolicy.standdown.sessionRule !== currentPolicy.standdown.sessionRule) {\n return 'session-rule-edited';\n }\n\n if (updatedPolicy.standdown.minDurationMs < currentPolicy.standdown.minDurationMs) {\n return 'min-duration-shortened';\n }\n\n if (\n currentPolicy.standdown.inactivityMs !== undefined &&\n (updatedPolicy.standdown.inactivityMs === undefined ||\n updatedPolicy.standdown.inactivityMs < currentPolicy.standdown.inactivityMs)\n ) {\n return 'inactivity-duration-shortened';\n }\n\n for (const behavior of currentPolicy.standdown.behaviors) {\n if (!updatedPolicy.standdown.behaviors.includes(behavior)) {\n return 'standdown-behavior-removed';\n }\n }\n\n return undefined;\n}\n\nfunction domainRulesSurvive(\n current: readonly DomainRule[] | undefined,\n update: readonly DomainRule[] | undefined,\n opts: { advertiserHosts?: boolean } = {},\n): boolean {\n if (opts.advertiserHosts && current === undefined) {\n return update === undefined;\n }\n\n if (current === undefined || current.length === 0) {\n return true;\n }\n\n if (opts.advertiserHosts && update === undefined) {\n return true;\n }\n\n if (update === undefined || update.length === 0) {\n return false;\n }\n\n return current.every((rule) =>\n update.some((candidate) => domainRuleCovers(rule, candidate)),\n );\n}\n\nfunction regexComplexityViolation(\n policies: readonly StanddownPolicy[],\n): string | undefined {\n for (const policy of policies) {\n for (const [field, rules] of domainRuleEntries(policy)) {\n for (const rule of rules ?? []) {\n if (rule.kind === 'regex' && isComplexRegex(rule.pattern)) {\n return `${policy.id}:complex-regex:${field}`;\n }\n }\n }\n }\n\n return undefined;\n}\n\nfunction domainRuleEntries(\n policy: StanddownPolicy,\n): readonly (readonly [string, readonly DomainRule[] | undefined])[] {\n return [\n ['detection.advertiserHosts', policy.detection.advertiserHosts],\n ['detection.redirectDomains', policy.detection.redirectDomains],\n ] as const;\n}\n\nfunction isComplexRegex(pattern: string): boolean {\n return (\n pattern.length > MAX_SIGNED_BUNDLE_REGEX_LENGTH ||\n hasRegexBackreference(pattern) ||\n hasRegexLookaround(pattern) ||\n hasNestedUnboundedQuantifier(pattern)\n );\n}\n\nfunction hasRegexBackreference(pattern: string): boolean {\n return /(^|[^\\\\])\\\\[1-9]/.test(pattern);\n}\n\nfunction hasRegexLookaround(pattern: string): boolean {\n return /\\(\\?(?:[=!]|<[=!])/.test(pattern);\n}\n\nfunction hasNestedUnboundedQuantifier(pattern: string): boolean {\n return /\\((?:[^()\\\\]|\\\\.)*[+*](?:[^()\\\\]|\\\\.)*\\)(?:[+*]|\\{\\d*,?\\})/.test(\n pattern,\n );\n}\n\nfunction domainRuleCovers(current: DomainRule, update: DomainRule): boolean {\n if (current.kind === 'suffix' && update.kind === 'suffix') {\n return domainSuffixMatches(current.pattern, update.pattern);\n }\n\n return sameJson(current, update);\n}\n\nfunction paramRulesSurvive(\n current: readonly ParamRule[] | undefined,\n update: readonly ParamRule[] | undefined,\n): boolean {\n return exactRulesSurvive(current, update);\n}\n\nfunction cookieRulesSurvive(\n current: readonly CookieRule[] | undefined,\n update: readonly CookieRule[] | undefined,\n): boolean {\n return exactRulesSurvive(current, update);\n}\n\nfunction initiatorRulesSurvive(\n current: readonly InitiatorRule[] | undefined,\n update: readonly InitiatorRule[] | undefined,\n): boolean {\n return exactRulesSurvive(current, update);\n}\n\nfunction exactRulesSurvive<T>(\n current: readonly T[] | undefined,\n update: readonly T[] | undefined,\n): boolean {\n if (current === undefined || current.length === 0) {\n return true;\n }\n\n if (update === undefined || update.length === 0) {\n return false;\n }\n\n const updatedRules = new Set(update.map((rule) => canonicalJson(rule)));\n\n return current.every((rule) => updatedRules.has(canonicalJson(rule)));\n}\n\nasync function verifySignature(\n algorithm: SignedBundleSignatureAlgorithm,\n publicKeyJwk: JsonWebKey,\n signatureValue: string,\n payload: string,\n): Promise<boolean> {\n try {\n const publicKey = await importVerificationKey(algorithm, publicKeyJwk);\n const signature = base64UrlToBytes(signatureValue);\n const data = bytesToArrayBuffer(textEncoder.encode(payload));\n\n if (algorithm === 'ECDSA-P256') {\n return crypto.subtle.verify(\n { name: 'ECDSA', hash: 'SHA-256' },\n publicKey,\n bytesToArrayBuffer(signature),\n data,\n );\n }\n\n return crypto.subtle.verify(\n 'Ed25519',\n publicKey,\n bytesToArrayBuffer(signature),\n data,\n );\n } catch {\n return false;\n }\n}\n\nasync function importVerificationKey(\n algorithm: SignedBundleSignatureAlgorithm,\n publicKeyJwk: JsonWebKey,\n): Promise<CryptoKey> {\n if (algorithm === 'ECDSA-P256') {\n return crypto.subtle.importKey(\n 'jwk',\n publicKeyJwk,\n { name: 'ECDSA', namedCurve: 'P-256' },\n false,\n ['verify'],\n );\n }\n\n return crypto.subtle.importKey('jwk', publicKeyJwk, 'Ed25519', false, [\n 'verify',\n ]);\n}\n\nfunction base64UrlToBytes(value: string): Uint8Array {\n const base64 = value.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n\n return bytes;\n}\n\nfunction bytesToArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n return bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength,\n ) as ArrayBuffer;\n}\n\nfunction sameJson(left: unknown, right: unknown): boolean {\n return canonicalJson(left) === canonicalJson(right);\n}\n\nfunction domainSuffixMatches(host: string, pattern: string): boolean {\n const normalizedHost = host.toLowerCase().replace(/^\\./, '').replace(/\\.$/, '');\n const normalizedPattern = pattern\n .toLowerCase()\n .replace(/^\\./, '')\n .replace(/\\.$/, '');\n\n return (\n normalizedHost === normalizedPattern ||\n normalizedHost.endsWith(`.${normalizedPattern}`)\n );\n}\n\nfunction clonePolicy(policy: StanddownPolicy): StanddownPolicy {\n return JSON.parse(JSON.stringify(policy)) as StanddownPolicy;\n}\n\nfunction messageFromError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
@@ -176,6 +176,7 @@ function dropSessionBoundRecords(state, now) {
176
176
  delete next.sessions[host];
177
177
  }
178
178
  }
179
+ delete next.exemptions;
179
180
  return next;
180
181
  }
181
182
  function createSessionId() {
@@ -209,10 +210,36 @@ function parseState(value) {
209
210
  for (const [host, record] of Object.entries(value.sessions)) {
210
211
  sessions[host] = parseSessionRecord(record);
211
212
  }
212
- return {
213
+ const state = {
213
214
  sessions,
214
215
  auditLog: value.auditLog.map(parseAuditEntry)
215
216
  };
217
+ if (value.exemptions !== void 0) {
218
+ if (!isRecord(value.exemptions)) {
219
+ throw new Error("invalid standdown state");
220
+ }
221
+ const exemptions = {};
222
+ for (const [host, record] of Object.entries(value.exemptions)) {
223
+ exemptions[host] = parseExemptionRecord(record);
224
+ }
225
+ state.exemptions = exemptions;
226
+ }
227
+ return state;
228
+ }
229
+ function parseExemptionRecord(value) {
230
+ if (!isRecord(value)) {
231
+ throw new Error("invalid exemption record");
232
+ }
233
+ const { advertiserHost, policyIds, networkIds, grantedAt } = value;
234
+ if (typeof advertiserHost !== "string" || typeof grantedAt !== "number" || !Array.isArray(policyIds) || !policyIds.every((id) => typeof id === "string") || !Array.isArray(networkIds) || !networkIds.every((id) => typeof id === "string")) {
235
+ throw new Error("invalid exemption record");
236
+ }
237
+ return {
238
+ advertiserHost,
239
+ policyIds: [...policyIds],
240
+ networkIds: [...networkIds],
241
+ grantedAt
242
+ };
216
243
  }
217
244
  function parseSessionRecord(value) {
218
245
  if (!isRecord(value)) {
@@ -270,7 +297,7 @@ function parseAuditEntry(value) {
270
297
  return entry;
271
298
  }
272
299
  function cloneState(state) {
273
- return {
300
+ const next = {
274
301
  sessions: Object.fromEntries(
275
302
  Object.entries(state.sessions).map(([host, record]) => [
276
303
  host,
@@ -279,6 +306,19 @@ function cloneState(state) {
279
306
  ),
280
307
  auditLog: state.auditLog.map(cloneAuditEntry)
281
308
  };
309
+ if (state.exemptions) {
310
+ next.exemptions = Object.fromEntries(
311
+ Object.entries(state.exemptions).map(([host, record]) => [
312
+ host,
313
+ {
314
+ ...record,
315
+ policyIds: [...record.policyIds],
316
+ networkIds: [...record.networkIds]
317
+ }
318
+ ])
319
+ );
320
+ }
321
+ return next;
282
322
  }
283
323
  function cloneSessionRecord(record) {
284
324
  const next = {
@@ -314,6 +354,11 @@ function cloneAuditEntry(entry) {
314
354
  if (entry.detection.strongest !== void 0) {
315
355
  next.detection.strongest = { ...entry.detection.strongest };
316
356
  }
357
+ if (entry.detection.selfExemptScopes !== void 0) {
358
+ next.detection.selfExemptScopes = entry.detection.selfExemptScopes.map(
359
+ (scope) => ({ ...scope })
360
+ );
361
+ }
317
362
  if (entry.detection.failClosedReason !== void 0) {
318
363
  next.detection.failClosedReason = entry.detection.failClosedReason;
319
364
  }
@@ -378,5 +423,5 @@ function isBehavior(value) {
378
423
  }
379
424
 
380
425
  export { ChromeLocalStateStore, LocalStorageTtlStateStore, SessionStorageStateStore };
381
- //# sourceMappingURL=chunk-SEZFAOUU.mjs.map
382
- //# sourceMappingURL=chunk-SEZFAOUU.mjs.map
426
+ //# sourceMappingURL=chunk-JKQTHHIP.mjs.map
427
+ //# sourceMappingURL=chunk-JKQTHHIP.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stores.ts"],"names":[],"mappings":"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","file":"chunk-JKQTHHIP.mjs","sourcesContent":["import type {\n AuditEntry,\n Behavior,\n Decision,\n Detection,\n ExemptionRecord,\n SessionRecord,\n StanddownState,\n StateStore,\n} from './types';\n\nconst DEFAULT_STATE_KEY = 'standdown:state:v1';\nconst DEFAULT_SESSION_ID_KEY = 'standdown:session-id:v1';\n\nexport interface ChromeRuntimeLike {\n lastError?: { message?: string };\n}\n\nexport interface ChromeStorageAreaLike {\n get(\n keys?: string | string[] | Record<string, unknown> | null,\n callback?: (items: Record<string, unknown>) => void,\n ): undefined | Promise<Record<string, unknown>>;\n set(\n items: Record<string, unknown>,\n callback?: () => void,\n ): undefined | Promise<void>;\n remove?(\n keys: string | string[],\n callback?: () => void,\n ): undefined | Promise<void>;\n}\n\nexport interface WebStorageLike {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\nexport interface ChromeLocalStateStoreOptions {\n key?: string;\n identityKey?: string;\n runtime?: ChromeRuntimeLike;\n sessionStorage?: ChromeStorageAreaLike;\n sessionId?: string;\n now?: () => number;\n createSessionId?: () => string;\n}\n\nexport class ChromeLocalStateStore implements StateStore {\n readonly #localStorage: ChromeStorageAreaLike;\n readonly #sessionStorage: ChromeStorageAreaLike | undefined;\n readonly #runtime: ChromeRuntimeLike | undefined;\n readonly #key: string;\n readonly #identityKey: string;\n readonly #fixedSessionId: string | undefined;\n readonly #now: () => number;\n readonly #createSessionId: () => string;\n #cachedSessionId: string | undefined;\n\n constructor(\n localStorage: ChromeStorageAreaLike,\n opts: ChromeLocalStateStoreOptions = {},\n ) {\n this.#localStorage = localStorage;\n this.#sessionStorage = opts.sessionStorage;\n this.#runtime = opts.runtime;\n this.#key = opts.key ?? DEFAULT_STATE_KEY;\n this.#identityKey = opts.identityKey ?? DEFAULT_SESSION_ID_KEY;\n this.#fixedSessionId = opts.sessionId;\n this.#now = opts.now ?? Date.now;\n this.#createSessionId = opts.createSessionId ?? createSessionId;\n }\n\n async load(): Promise<StanddownState | undefined> {\n const sessionId = await this.#currentSessionId();\n const items = await chromeGet(this.#localStorage, this.#key, this.#runtime);\n const envelope = items[this.#key];\n\n if (envelope === undefined) {\n return undefined;\n }\n\n const persisted = parsePersistedState(envelope);\n const state = cloneState(persisted.state);\n\n if (sessionIdentityMismatch(persisted.sessionId, sessionId)) {\n return dropSessionBoundRecords(state, this.#now());\n }\n\n return state;\n }\n\n async save(state: StanddownState): Promise<void> {\n const sessionId = await this.#currentSessionId();\n const envelope: {\n schemaVersion: 1;\n sessionId?: string;\n state: StanddownState;\n } = {\n schemaVersion: 1,\n state: cloneState(state),\n };\n\n if (sessionId !== undefined) {\n envelope.sessionId = sessionId;\n }\n\n await chromeSet(\n this.#localStorage,\n {\n [this.#key]: envelope,\n },\n this.#runtime,\n );\n }\n\n async #currentSessionId(): Promise<string | undefined> {\n if (this.#fixedSessionId !== undefined) {\n return this.#fixedSessionId;\n }\n\n if (this.#cachedSessionId !== undefined) {\n return this.#cachedSessionId;\n }\n\n if (this.#sessionStorage === undefined) {\n return undefined;\n }\n\n const items = await chromeGet(\n this.#sessionStorage,\n this.#identityKey,\n this.#runtime,\n );\n const existing = items[this.#identityKey];\n\n if (typeof existing === 'string' && existing.length > 0) {\n this.#cachedSessionId = existing;\n return existing;\n }\n\n const next = this.#createSessionId();\n await chromeSet(\n this.#sessionStorage,\n { [this.#identityKey]: next },\n this.#runtime,\n );\n this.#cachedSessionId = next;\n return next;\n }\n}\n\nexport interface SessionStorageStateStoreOptions {\n key?: string;\n}\n\nexport class SessionStorageStateStore implements StateStore {\n readonly #storage: WebStorageLike;\n readonly #key: string;\n\n constructor(storage: WebStorageLike, opts: SessionStorageStateStoreOptions = {}) {\n this.#storage = storage;\n this.#key = opts.key ?? DEFAULT_STATE_KEY;\n }\n\n async load(): Promise<StanddownState | undefined> {\n const raw = this.#storage.getItem(this.#key);\n\n if (raw === null) {\n return undefined;\n }\n\n return cloneState(parseState(JSON.parse(raw)));\n }\n\n async save(state: StanddownState): Promise<void> {\n this.#storage.setItem(this.#key, JSON.stringify(cloneState(state)));\n }\n}\n\nexport interface LocalStorageTtlStateStoreOptions {\n key?: string;\n identityKey?: string;\n /**\n * Sliding envelope TTL for persisted session records. Every save refreshes\n * `savedAt`; expiry removes session state only. Audit entries remain\n * available so per-policy decisions can still be inspected. Policy-specific\n * stand-down durations remain enforced by the core state machine.\n */\n ttlMs: number;\n /**\n * Optional shared identity storage. Defaults to `localStorage` so multiple\n * tabs in the same browser session do not invalidate each other's records.\n */\n identityStorage?: WebStorageLike;\n /** @deprecated Local-TTL identity is browser-session scoped, not tab scoped. */\n sessionStorage?: WebStorageLike;\n sessionId?: string;\n now?: () => number;\n createSessionId?: () => string;\n}\n\nexport class LocalStorageTtlStateStore implements StateStore {\n readonly #localStorage: WebStorageLike;\n readonly #identityStorage: WebStorageLike;\n readonly #key: string;\n readonly #identityKey: string;\n readonly #ttlMs: number;\n readonly #fixedSessionId: string | undefined;\n readonly #now: () => number;\n readonly #createSessionId: () => string;\n #cachedSessionId: string | undefined;\n\n constructor(\n localStorage: WebStorageLike,\n opts: LocalStorageTtlStateStoreOptions,\n ) {\n this.#localStorage = localStorage;\n this.#identityStorage = opts.identityStorage ?? localStorage;\n this.#key = opts.key ?? DEFAULT_STATE_KEY;\n this.#identityKey = opts.identityKey ?? DEFAULT_SESSION_ID_KEY;\n this.#ttlMs = opts.ttlMs;\n this.#fixedSessionId = opts.sessionId;\n this.#now = opts.now ?? Date.now;\n this.#createSessionId = opts.createSessionId ?? createSessionId;\n }\n\n async load(): Promise<StanddownState | undefined> {\n const sessionId = this.#currentSessionId();\n const raw = this.#localStorage.getItem(this.#key);\n\n if (raw === null) {\n return undefined;\n }\n\n const persisted = parsePersistedState(JSON.parse(raw));\n const state = cloneState(persisted.state);\n\n if (this.#now() - persisted.savedAt >= this.#ttlMs) {\n return {\n sessions: {},\n auditLog: state.auditLog,\n };\n }\n\n if (sessionIdentityMismatch(persisted.sessionId, sessionId)) {\n return dropSessionBoundRecords(state, this.#now());\n }\n\n return state;\n }\n\n async save(state: StanddownState): Promise<void> {\n const sessionId = this.#currentSessionId();\n this.#localStorage.setItem(\n this.#key,\n JSON.stringify({\n schemaVersion: 1,\n sessionId,\n savedAt: this.#now(),\n state: cloneState(state),\n }),\n );\n }\n\n #currentSessionId(): string {\n if (this.#fixedSessionId !== undefined) {\n return this.#fixedSessionId;\n }\n\n if (this.#cachedSessionId !== undefined) {\n return this.#cachedSessionId;\n }\n\n const existing = this.#identityStorage.getItem(this.#identityKey);\n\n if (existing !== null && existing.length > 0) {\n this.#cachedSessionId = existing;\n return existing;\n }\n\n const next = this.#createSessionId();\n this.#identityStorage.setItem(this.#identityKey, next);\n this.#cachedSessionId = next;\n return next;\n }\n}\n\nexport function dropSessionBoundRecords(\n state: StanddownState,\n now: number,\n): StanddownState {\n const next = cloneState(state);\n\n for (const [host, record] of Object.entries(next.sessions)) {\n if (\n record.sessionRule === 'session-or-min' &&\n now >= record.startedAt + record.minDurationMs\n ) {\n delete next.sessions[host];\n }\n }\n\n // Self-exemptions live only for the session that granted them; a new browser\n // session must not inherit them (dropping them errs toward standing down).\n delete next.exemptions;\n\n return next;\n}\n\nexport function createSessionId(): string {\n const cryptoApi = globalThis.crypto as Crypto | undefined;\n\n if (typeof cryptoApi?.randomUUID === 'function') {\n return cryptoApi.randomUUID();\n }\n\n return `session-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction parsePersistedState(value: unknown): {\n sessionId: string | undefined;\n savedAt: number;\n state: StanddownState;\n} {\n if (!isRecord(value)) {\n throw new Error('invalid persisted state');\n }\n\n const sessionId = value.sessionId;\n const savedAt = value.savedAt ?? 0;\n const state = value.state;\n\n if (\n (sessionId !== undefined && typeof sessionId !== 'string') ||\n typeof savedAt !== 'number'\n ) {\n throw new Error('invalid persisted state envelope');\n }\n\n return {\n sessionId,\n savedAt,\n state: parseState(state),\n };\n}\n\nfunction parseState(value: unknown): StanddownState {\n if (!isRecord(value) || !isRecord(value.sessions) || !Array.isArray(value.auditLog)) {\n throw new Error('invalid standdown state');\n }\n\n const sessions: Record<string, SessionRecord> = {};\n\n for (const [host, record] of Object.entries(value.sessions)) {\n sessions[host] = parseSessionRecord(record);\n }\n\n const state: StanddownState = {\n sessions,\n auditLog: value.auditLog.map(parseAuditEntry),\n };\n\n if (value.exemptions !== undefined) {\n if (!isRecord(value.exemptions)) {\n throw new Error('invalid standdown state');\n }\n\n const exemptions: Record<string, ExemptionRecord> = {};\n\n for (const [host, record] of Object.entries(value.exemptions)) {\n exemptions[host] = parseExemptionRecord(record);\n }\n\n state.exemptions = exemptions;\n }\n\n return state;\n}\n\nfunction parseExemptionRecord(value: unknown): ExemptionRecord {\n if (!isRecord(value)) {\n throw new Error('invalid exemption record');\n }\n\n const { advertiserHost, policyIds, networkIds, grantedAt } = value;\n\n if (\n typeof advertiserHost !== 'string' ||\n typeof grantedAt !== 'number' ||\n !Array.isArray(policyIds) ||\n !policyIds.every((id) => typeof id === 'string') ||\n !Array.isArray(networkIds) ||\n !networkIds.every((id) => typeof id === 'string')\n ) {\n throw new Error('invalid exemption record');\n }\n\n return {\n advertiserHost,\n policyIds: [...policyIds],\n networkIds: [...networkIds],\n grantedAt,\n };\n}\n\nfunction parseSessionRecord(value: unknown): SessionRecord {\n if (!isRecord(value)) {\n throw new Error('invalid session record');\n }\n\n const {\n advertiserHost,\n policyId,\n startedAt,\n lastActivityAt,\n expiresAt,\n sessionRule,\n minDurationMs,\n inactivityMs,\n behaviors,\n } = value;\n\n if (\n typeof advertiserHost !== 'string' ||\n typeof policyId !== 'string' ||\n typeof startedAt !== 'number' ||\n typeof lastActivityAt !== 'number' ||\n (expiresAt !== undefined && typeof expiresAt !== 'number') ||\n (sessionRule !== 'session-or-min' && sessionRule !== 'inactivity-window') ||\n typeof minDurationMs !== 'number' ||\n (inactivityMs !== undefined && typeof inactivityMs !== 'number') ||\n !Array.isArray(behaviors) ||\n !behaviors.every(isBehavior)\n ) {\n throw new Error('invalid session record');\n }\n\n const record: SessionRecord = {\n advertiserHost,\n policyId,\n startedAt,\n lastActivityAt,\n sessionRule,\n minDurationMs,\n behaviors,\n };\n\n if (expiresAt !== undefined) {\n record.expiresAt = expiresAt;\n }\n\n if (inactivityMs !== undefined) {\n record.inactivityMs = inactivityMs;\n }\n\n return record;\n}\n\nfunction parseAuditEntry(value: unknown): AuditEntry {\n if (!isRecord(value)) {\n throw new Error('invalid audit entry');\n }\n\n const { time, action, advertiserHost, detection, decision } = value;\n\n if (\n typeof time !== 'number' ||\n (action !== 'ingest' &&\n action !== 'shouldStandDown' &&\n action !== 'recordActivity' &&\n action !== 'refresh') ||\n (advertiserHost !== undefined && typeof advertiserHost !== 'string')\n ) {\n throw new Error('invalid audit entry');\n }\n\n const entry: AuditEntry = { time, action };\n\n if (advertiserHost !== undefined) {\n entry.advertiserHost = advertiserHost;\n }\n\n if (detection !== undefined) {\n entry.detection = detection as Detection;\n }\n\n if (decision !== undefined) {\n entry.decision = decision as Decision;\n }\n\n return entry;\n}\n\nfunction cloneState(state: StanddownState): StanddownState {\n const next: StanddownState = {\n sessions: Object.fromEntries(\n Object.entries(state.sessions).map(([host, record]) => [\n host,\n cloneSessionRecord(record),\n ]),\n ),\n auditLog: state.auditLog.map(cloneAuditEntry),\n };\n\n if (state.exemptions) {\n next.exemptions = Object.fromEntries(\n Object.entries(state.exemptions).map(([host, record]) => [\n host,\n {\n ...record,\n policyIds: [...record.policyIds],\n networkIds: [...record.networkIds],\n },\n ]),\n );\n }\n\n return next;\n}\n\nfunction cloneSessionRecord(record: SessionRecord): SessionRecord {\n const next: SessionRecord = {\n advertiserHost: record.advertiserHost,\n policyId: record.policyId,\n startedAt: record.startedAt,\n lastActivityAt: record.lastActivityAt,\n sessionRule: record.sessionRule,\n minDurationMs: record.minDurationMs,\n behaviors: [...record.behaviors],\n };\n\n if (record.expiresAt !== undefined) {\n next.expiresAt = record.expiresAt;\n }\n\n if (record.inactivityMs !== undefined) {\n next.inactivityMs = record.inactivityMs;\n }\n\n return next;\n}\n\nfunction cloneAuditEntry(entry: AuditEntry): AuditEntry {\n const next: AuditEntry = {\n time: entry.time,\n action: entry.action,\n };\n\n if (entry.advertiserHost !== undefined) {\n next.advertiserHost = entry.advertiserHost;\n }\n\n if (entry.detection !== undefined) {\n next.detection = {\n matched: entry.detection.matched.map((match) => ({ ...match })),\n selfMatch: entry.detection.selfMatch,\n };\n\n if (entry.detection.strongest !== undefined) {\n next.detection.strongest = { ...entry.detection.strongest };\n }\n\n if (entry.detection.selfExemptScopes !== undefined) {\n next.detection.selfExemptScopes = entry.detection.selfExemptScopes.map(\n (scope) => ({ ...scope }),\n );\n }\n\n if (entry.detection.failClosedReason !== undefined) {\n next.detection.failClosedReason = entry.detection.failClosedReason;\n }\n }\n\n if (entry.decision !== undefined) {\n next.decision = {\n ...entry.decision,\n behaviors: [...entry.decision.behaviors],\n };\n }\n\n return next;\n}\n\nfunction chromeGet(\n storage: ChromeStorageAreaLike,\n key: string,\n runtime: ChromeRuntimeLike | undefined,\n): Promise<Record<string, unknown>> {\n return new Promise((resolve, reject) => {\n try {\n const result = storage.get(key, (items) => {\n const error = runtime?.lastError;\n\n if (error !== undefined) {\n reject(new Error(error.message ?? 'chrome storage error'));\n return;\n }\n\n resolve(items ?? {});\n });\n\n if (isPromiseLike(result)) {\n result.then(resolve, reject);\n }\n } catch (error) {\n reject(error);\n }\n });\n}\n\nfunction chromeSet(\n storage: ChromeStorageAreaLike,\n items: Record<string, unknown>,\n runtime: ChromeRuntimeLike | undefined,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n const result = storage.set(items, () => {\n const error = runtime?.lastError;\n\n if (error !== undefined) {\n reject(new Error(error.message ?? 'chrome storage error'));\n return;\n }\n\n resolve();\n });\n\n if (isPromiseLike(result)) {\n result.then(resolve, reject);\n }\n } catch (error) {\n reject(error);\n }\n });\n}\n\nfunction isPromiseLike<T>(value: unknown): value is Promise<T> {\n return (\n isRecord(value) &&\n typeof value.then === 'function' &&\n typeof value.catch === 'function'\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction sessionIdentityMismatch(\n persistedSessionId: string | undefined,\n currentSessionId: string | undefined,\n): boolean {\n return (\n persistedSessionId !== undefined &&\n currentSessionId !== undefined &&\n persistedSessionId !== currentSessionId\n );\n}\n\nfunction isBehavior(value: unknown): value is Behavior {\n return (\n value === 'suppress-prompts' ||\n value === 'no-cookie-write' ||\n value === 'no-redirect' ||\n value === 'no-background-tracking'\n );\n}\n"]}