standdown 0.1.0 → 0.2.1
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.
- package/README.md +262 -18
- package/dist/{chunk-25A7QG5Z.mjs → chunk-26BG7FFX.mjs} +3 -3
- package/dist/{chunk-25A7QG5Z.mjs.map → chunk-26BG7FFX.mjs.map} +1 -1
- package/dist/{chunk-SEZFAOUU.mjs → chunk-JKQTHHIP.mjs} +49 -4
- package/dist/chunk-JKQTHHIP.mjs.map +1 -0
- package/dist/{chunk-SVN6UWA4.mjs → chunk-VE7YBR5A.mjs} +142 -24
- package/dist/chunk-VE7YBR5A.mjs.map +1 -0
- package/dist/content.cjs +188 -28
- package/dist/content.cjs.map +1 -1
- package/dist/content.d.cts +11 -4
- package/dist/content.d.ts +11 -4
- package/dist/content.mjs +18 -8
- package/dist/content.mjs.map +1 -1
- package/dist/index.cjs +140 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +3 -7
- package/dist/index.mjs.map +1 -1
- package/dist/policies.cjs +18 -11
- package/dist/policies.cjs.map +1 -1
- package/dist/policies.d.cts +98 -86
- package/dist/policies.d.ts +98 -86
- package/dist/policies.mjs +18 -12
- package/dist/policies.mjs.map +1 -1
- package/dist/{session-C5gH-LaQ.d.ts → session-DA9hkYlD.d.ts} +10 -1
- package/dist/{session-n0px3Agb.d.cts → session-iosT1mEL.d.cts} +10 -1
- package/dist/{stores-CELX9aHN.d.ts → stores-Behkya7C.d.ts} +1 -1
- package/dist/{stores-CKCdKDdX.d.cts → stores-DfovpSqe.d.cts} +1 -1
- package/dist/{types-A9OJ7SWj.d.cts → types-DgKvVCmQ.d.cts} +53 -7
- package/dist/{types-A9OJ7SWj.d.ts → types-DgKvVCmQ.d.ts} +53 -7
- package/dist/webext.cjs +191 -28
- package/dist/webext.cjs.map +1 -1
- package/dist/webext.d.cts +11 -4
- package/dist/webext.d.ts +11 -4
- package/dist/webext.mjs +22 -9
- package/dist/webext.mjs.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-SEZFAOUU.mjs.map +0 -1
- package/dist/chunk-SVN6UWA4.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,14 +1,88 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,49 @@ It ships four surfaces:
|
|
|
24
98
|
npm install standdown
|
|
25
99
|
```
|
|
26
100
|
|
|
27
|
-
|
|
28
|
-
|
|
101
|
+
`standdown` is published to npm and versioned with semver from `0.1.0`. Releases
|
|
102
|
+
are cut by a human with `npm run release` (see [RELEASING.md](./RELEASING.md));
|
|
103
|
+
the repo does not publish from CI.
|
|
104
|
+
|
|
105
|
+
## Set it up with an AI agent
|
|
106
|
+
|
|
107
|
+
The fastest path: hand the whole integration to your coding agent. Copy this
|
|
108
|
+
prompt into Claude Code, Cursor, Copilot, etc. — pointed at your extension's repo:
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
Integrate the `standdown` npm library into this browser extension so it stops
|
|
112
|
+
hijacking affiliate attribution when a partner already owns the sale. Follow the
|
|
113
|
+
official guide at https://raw.githubusercontent.com/dupe-com/standdown/main/AGENTS.md.
|
|
114
|
+
|
|
115
|
+
Do the full loop:
|
|
116
|
+
1. `npm install standdown`.
|
|
117
|
+
2. Pick the adapter: `standdown/webext` for a Chromium MV3 extension, or
|
|
118
|
+
`standdown/content` for Safari / content-script-only.
|
|
119
|
+
3. Find every place this extension fires affiliate attribution (redirects, link
|
|
120
|
+
rewrites, cookie writes) and gate each behind the stand-down decision — do
|
|
121
|
+
nothing when `decision.standDown` is true.
|
|
122
|
+
4. Bundle per examples/mv3-extension (subpath imports don't resolve raw in
|
|
123
|
+
extension contexts).
|
|
124
|
+
5. Build the unpacked extension, then grade it:
|
|
125
|
+
git clone https://github.com/dupe-com/standdown && cd standdown/audit && \
|
|
126
|
+
npm install && npx tsx grade/grade.ts <path-to-unpacked-extension>
|
|
127
|
+
Report the letter grade and fix anything below A.
|
|
128
|
+
|
|
129
|
+
Preserve the invariants: decisions stay local and synchronous (no network in the
|
|
130
|
+
decision path), no user identity in signals, and fail toward standing down.
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Already have homegrown stand-down logic?** Use the brownfield prompt in
|
|
134
|
+
[`ADOPTING.md`](./ADOPTING.md) instead — it migrates your existing decision path
|
|
135
|
+
onto the library provably, without risking revenue in the switch.
|
|
136
|
+
|
|
137
|
+
**Claude Code users** can skip the prompt: this repo ships two skills in
|
|
138
|
+
[`.claude/skills/standdown`](./.claude/skills/standdown) (greenfield install) and
|
|
139
|
+
[`skills/adopt-standdown`](./skills/adopt-standdown) (brownfield migration). Copy
|
|
140
|
+
the one you want into your project's `.claude/skills/` (or `~/.claude/skills/`)
|
|
141
|
+
and run `/standdown` (or `/adopt-standdown`). Agents that read
|
|
142
|
+
[`AGENTS.md`](./AGENTS.md) or
|
|
143
|
+
[`llms.txt`](./llms.txt) get the same playbook automatically.
|
|
29
144
|
|
|
30
145
|
## Webext Quickstart
|
|
31
146
|
|
|
@@ -40,14 +155,18 @@ const standdown = createStanddown({
|
|
|
40
155
|
});
|
|
41
156
|
```
|
|
42
157
|
|
|
43
|
-
The adapter
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
158
|
+
The `standdown/webext` adapter is the Chromium-MV3 path. It observes
|
|
159
|
+
`chrome.webRequest.onBeforeRequest` redirect chains when available and uses
|
|
160
|
+
`chrome.webNavigation.onCommitted` final-URL signal collection when `webRequest`
|
|
161
|
+
is reduced or unavailable. `chrome.webNavigation.onCommitted` itself is
|
|
162
|
+
**required**: `createStanddown()` throws when that API is absent, because an
|
|
163
|
+
adapter that observes no navigations would fail open.
|
|
47
164
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
165
|
+
Safari and other reduced-permission contexts do **not** run this adapter — they
|
|
166
|
+
use the page-level `standdown/content` adapter below, which collects signals from
|
|
167
|
+
the page (`location.href`, `document.referrer`, first-party cookie names) and
|
|
168
|
+
needs no `chrome.*` network APIs. Content scripts and popups can query the
|
|
169
|
+
background worker with:
|
|
51
170
|
|
|
52
171
|
```ts
|
|
53
172
|
const response = await chrome.runtime.sendMessage({
|
|
@@ -96,11 +215,66 @@ The content adapter collects only local page signals: `location.href`,
|
|
|
96
215
|
included. SPA navigations are re-evaluated via `pushState`, `replaceState`, and
|
|
97
216
|
`popstate` hooks.
|
|
98
217
|
|
|
218
|
+
**Cookie matching is name-only, by design.** `CookiePattern` rules (`exact` and
|
|
219
|
+
`substring`) match against cookie **names**, never values — that is what keeps
|
|
220
|
+
user data structurally out of `Signals` (invariant I2). If you are migrating from
|
|
221
|
+
an implementation that matches against the whole `document.cookie` string (names
|
|
222
|
+
*and* values), verify that your cookie rules only depend on names before porting
|
|
223
|
+
them; a rule that secretly relied on matching a cookie *value* will not fire here.
|
|
224
|
+
This is intentional and not configurable: matching values would require cookie
|
|
225
|
+
values to enter `Signals`, which the closed-signal privacy guarantee forbids.
|
|
226
|
+
|
|
99
227
|
`storage: 'local-ttl'` stores session records in `localStorage` with a sliding
|
|
100
228
|
24-hour envelope TTL by default. The TTL clears session records, not audit
|
|
101
229
|
history; per-policy stand-down durations remain enforced by the core state
|
|
102
230
|
machine.
|
|
103
231
|
|
|
232
|
+
### Degraded decisions
|
|
233
|
+
|
|
234
|
+
The content adapter (and a webext adapter running without the `webRequest`
|
|
235
|
+
plane) cannot observe redirect chains, so it sets `Signals.signalCoverage =
|
|
236
|
+
'partial'`. When a decision comes back `standDown: false` from a partial signal
|
|
237
|
+
set, it carries `degraded: true` — the "no stand-down" may be a false negative
|
|
238
|
+
because a redirect-only attribution could have been missed. Stand-down decisions
|
|
239
|
+
are never marked degraded (over-suppression is the safe direction). Integrators
|
|
240
|
+
that want to fail fully closed can treat a degraded non-stand-down as a
|
|
241
|
+
stand-down:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
const decision = await standdown.ready;
|
|
245
|
+
const suppress = decision.standDown || decision.degraded;
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Self-exemption scope
|
|
249
|
+
|
|
250
|
+
`selfPatterns` declare the params that are *your own* attribution — when one
|
|
251
|
+
matches, the library does not stand down against that network (it's your click,
|
|
252
|
+
not a competitor's). By default this exemption lasts only for the navigation that
|
|
253
|
+
carries the param (`selfExemptionScope: 'policy'`). Merchants often strip the
|
|
254
|
+
param on later internal navigations while your attribution (a first-party cookie,
|
|
255
|
+
say) lingers — under policy scope that lingering signal would then read as a
|
|
256
|
+
competitor and stand you down.
|
|
257
|
+
|
|
258
|
+
`selfExemptionScope: 'session'` fixes that: once your param is seen on a host, the
|
|
259
|
+
exemption is remembered for the session and re-applied to *that same network's*
|
|
260
|
+
signals on later param-less navigations. This is Dupe's `ignore_param` behavior.
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
const session = new StanddownSession(store, { selfExemptionScope: 'session' });
|
|
264
|
+
// or, through an adapter:
|
|
265
|
+
createStanddown({ policies, selfPatterns, selfExemptionScope: 'session' });
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
It is deliberately **network-precise, not host-blanket**, and monotone:
|
|
269
|
+
|
|
270
|
+
- A fresh signal from a *different* network on the same host still stands down —
|
|
271
|
+
claiming the host for your network never silences a competitor's.
|
|
272
|
+
- A session exemption **never lifts an already-active stand-down**. If a
|
|
273
|
+
competitor stand-down formed first, a later self-param does not clear it (no
|
|
274
|
+
exemption is even recorded while a stand-down is active).
|
|
275
|
+
- A `disableHosts` match is immune — a hard-disabled host stands down regardless
|
|
276
|
+
of any exemption.
|
|
277
|
+
|
|
104
278
|
## Core Usage
|
|
105
279
|
|
|
106
280
|
```ts
|
|
@@ -139,6 +313,59 @@ const guard = guardActivation({
|
|
|
139
313
|
- **I7: Deterministic and loggable.** Given the same local signals, policies,
|
|
140
314
|
state, and clock, decisions are reproducible.
|
|
141
315
|
|
|
316
|
+
## Conformance grading
|
|
317
|
+
|
|
318
|
+
Unit tests prove the library's decisions in isolation. The
|
|
319
|
+
[`audit/`](./audit) harness proves the thing they can't: whether a *real MV3
|
|
320
|
+
extension*, loaded into a *real browser* against realistic merchant pages,
|
|
321
|
+
actually stands down instead of hijacking existing attribution.
|
|
322
|
+
|
|
323
|
+
It serves synthetic merchant fixtures carrying pre-existing attribution for
|
|
324
|
+
each network, drives an extension through every scenario, and scores the run
|
|
325
|
+
**F → A+**:
|
|
326
|
+
|
|
327
|
+
```sh
|
|
328
|
+
cd audit && npm install
|
|
329
|
+
npx tsx grade/grade.ts /path/to/your/unpacked-extension
|
|
330
|
+
# standdown grade: A+ (100/100)
|
|
331
|
+
# Respected existing attribution across all tested networks and activated when allowed.
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
The rubric includes an **inert cap**: an extension that never activates even when
|
|
335
|
+
activation is *allowed* hasn't proven it does anything, so it can't score above a
|
|
336
|
+
C. That stops "disciplined stand-down" from being faked by shipping dead code.
|
|
337
|
+
Three reference extensions (`good` / `bad` / `inert`) ship alongside to validate
|
|
338
|
+
the grader itself.
|
|
339
|
+
|
|
340
|
+
The harness is opt-in: it is not part of the npm package and is not on the
|
|
341
|
+
required CI path. See [`audit/README.md`](./audit/README.md) for the full guide.
|
|
342
|
+
|
|
343
|
+
## Per-host disable
|
|
344
|
+
|
|
345
|
+
Some merchants are ones where competing activation is never acceptable — the
|
|
346
|
+
integrator wants to go fully quiet on that host rather than detect-then-suppress.
|
|
347
|
+
`detection.disableHosts` expresses that: any navigation whose advertiser host
|
|
348
|
+
matches stands down **unconditionally**, regardless of params, cookies, or
|
|
349
|
+
self-exemption. It is the strongest match kind (`disabled-host`), and it is how
|
|
350
|
+
you model a "we do not operate here at all" list (the extension's
|
|
351
|
+
`disable_domains`).
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
const merchantBlocklistPolicy = {
|
|
355
|
+
// ...id, network, standdown, activation, metadata...
|
|
356
|
+
detection: {
|
|
357
|
+
disableHosts: [
|
|
358
|
+
{ pattern: '(^|\\.)ebay\\.[a-z.]+$', kind: 'regex' },
|
|
359
|
+
{ pattern: 'homedepot.com', kind: 'suffix' },
|
|
360
|
+
],
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
A `disableHosts` match cannot be cleared by a `selfPatterns` exemption — if you
|
|
366
|
+
list a host here, your own attribution on that host still stands down. Use it
|
|
367
|
+
only for hosts where you never want to activate.
|
|
368
|
+
|
|
142
369
|
## Interop
|
|
143
370
|
|
|
144
371
|
`fromRakutenPolicy()` and `toRakutenPolicy()` convert Rakuten
|
|
@@ -152,17 +379,34 @@ bundled `rakuten` policy itself intentionally does not round-trip exactly.
|
|
|
152
379
|
|
|
153
380
|
| Policy | Main signals | Stand-down | Activation |
|
|
154
381
|
| --- | --- | --- | --- |
|
|
155
|
-
| `cj` | `cjevent`, `cjdata`, `utm_source=cj`, `sf_cs=cj`, `afsrc=1`, CJ redirect domains, CJ cookie names | Session-or-min
|
|
156
|
-
| `impact` | `afsrc=1`, `irclickid`, `irgwc`, `im_ref` cookie names | Session-
|
|
157
|
-
| `rakuten` | `ranMID`, `ranEAID`, `ranSiteID`, `siteID`, LinkSynergy redirect domains, LinkShare cookie names | Session-
|
|
382
|
+
| `cj` | `cjevent`, `cjdata`, `utm_source=cj`, `sf_cs=cj`, `afsrc=1`, CJ redirect domains, CJ cookie names | Session-or-min 60m | User click |
|
|
383
|
+
| `impact` | `afsrc=1`, `irclickid`, `irgwc`, `im_ref` cookie names | Session-only | User click |
|
|
384
|
+
| `rakuten` | `ranMID`, `ranEAID`, `ranSiteID`, `siteID`, LinkSynergy redirect domains, LinkShare cookie names | Session-only | User click |
|
|
158
385
|
| `awin` | `awc`, `utm_source=aw`, `source=aw`, `awin1.com` | CoC defaults | User click |
|
|
159
386
|
| `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
|
|
387
|
+
| `ebay-epn` | eBay EPN params, `rover.ebay.com`, scoped referrer classification | CoC defaults | User click |
|
|
161
388
|
| `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 |
|
|
389
|
+
| `sovrn-skimlinks` † | Skimlinks redirect domains | CoC defaults | User click |
|
|
390
|
+
| `partnerize` † | `clickref`, `prf.hn` | CoC defaults | User click |
|
|
164
391
|
| `universal` | Full `piedotorg/standdown-domains` list plus `afsrc=1` | CoC defaults | User click |
|
|
165
392
|
|
|
393
|
+
`allPolicies` is the **verified** default set. Packs marked † (`sovrn-skimlinks`,
|
|
394
|
+
`partnerize`) have redirect domains inferred from domain knowledge rather than
|
|
395
|
+
verified against network documentation, so they are excluded from `allPolicies`
|
|
396
|
+
and exported separately as `experimentalPolicies`. Opt in explicitly once you
|
|
397
|
+
have verified them for your integration:
|
|
398
|
+
|
|
399
|
+
```ts
|
|
400
|
+
import { allPolicies, experimentalPolicies } from 'standdown/policies';
|
|
401
|
+
|
|
402
|
+
const policies = [...allPolicies, ...experimentalPolicies];
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
`amazon` is detect-only: it reports attribution for suppression visibility but
|
|
406
|
+
its `activation.mode` is `never`, so the guard will never allow activation on an
|
|
407
|
+
Amazon advertiser host. Integrators with their own Amazon arrangement can supply
|
|
408
|
+
a policy that overrides this.
|
|
409
|
+
|
|
166
410
|
See [POLICIES.md](./POLICIES.md) for citations and attribution.
|
|
167
411
|
|
|
168
412
|
## Example
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { validatePolicies } from './chunk-
|
|
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-
|
|
274
|
-
//# sourceMappingURL=chunk-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
382
|
-
//# sourceMappingURL=chunk-
|
|
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"]}
|