@dreamdata_io/verify-tracking 0.1.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.
- package/LICENSE +21 -0
- package/README.md +756 -0
- package/dist/ai/checker.js +222 -0
- package/dist/ai/checker.js.map +1 -0
- package/dist/ai/checklist.js +190 -0
- package/dist/ai/checklist.js.map +1 -0
- package/dist/ai/cli-presets.js +27 -0
- package/dist/ai/cli-presets.js.map +1 -0
- package/dist/ai/evidence-view.js +143 -0
- package/dist/ai/evidence-view.js.map +1 -0
- package/dist/ai/local-cli.js +58 -0
- package/dist/ai/local-cli.js.map +1 -0
- package/dist/ai/openai-compatible.js +78 -0
- package/dist/ai/openai-compatible.js.map +1 -0
- package/dist/ai/response-parser.js +105 -0
- package/dist/ai/response-parser.js.map +1 -0
- package/dist/ai/types.js +2 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/analysis/consent-mode.js +225 -0
- package/dist/analysis/consent-mode.js.map +1 -0
- package/dist/analysis/deployment.js +2658 -0
- package/dist/analysis/deployment.js.map +1 -0
- package/dist/analysis/diagnosis/copy.en.js +303 -0
- package/dist/analysis/diagnosis/copy.en.js.map +1 -0
- package/dist/analysis/diagnosis/diagnose.js +172 -0
- package/dist/analysis/diagnosis/diagnose.js.map +1 -0
- package/dist/analysis/diagnosis/docs.js +118 -0
- package/dist/analysis/diagnosis/docs.js.map +1 -0
- package/dist/analysis/diagnosis/index.js +5 -0
- package/dist/analysis/diagnosis/index.js.map +1 -0
- package/dist/analysis/diagnosis/rules.js +656 -0
- package/dist/analysis/diagnosis/rules.js.map +1 -0
- package/dist/analysis/diagnosis/types.js +2 -0
- package/dist/analysis/diagnosis/types.js.map +1 -0
- package/dist/cli.js +344 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/env.js +100 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/preflight.js +167 -0
- package/dist/config/preflight.js.map +1 -0
- package/dist/config/user-config.js +79 -0
- package/dist/config/user-config.js.map +1 -0
- package/dist/evidence/collect.js +527 -0
- package/dist/evidence/collect.js.map +1 -0
- package/dist/evidence/consent-accept.js +377 -0
- package/dist/evidence/consent-accept.js.map +1 -0
- package/dist/evidence/consent.js +340 -0
- package/dist/evidence/consent.js.map +1 -0
- package/dist/evidence/constants.js +615 -0
- package/dist/evidence/constants.js.map +1 -0
- package/dist/evidence/globals.js +132 -0
- package/dist/evidence/globals.js.map +1 -0
- package/dist/evidence/gtm-container.js +432 -0
- package/dist/evidence/gtm-container.js.map +1 -0
- package/dist/evidence/heuristic-banner.js +107 -0
- package/dist/evidence/heuristic-banner.js.map +1 -0
- package/dist/evidence/http-probe.js +139 -0
- package/dist/evidence/http-probe.js.map +1 -0
- package/dist/evidence/init-script.js +112 -0
- package/dist/evidence/init-script.js.map +1 -0
- package/dist/evidence/network.js +365 -0
- package/dist/evidence/network.js.map +1 -0
- package/dist/evidence/preference-center.js +368 -0
- package/dist/evidence/preference-center.js.map +1 -0
- package/dist/evidence/script.js +425 -0
- package/dist/evidence/script.js.map +1 -0
- package/dist/evidence/second-page.js +332 -0
- package/dist/evidence/second-page.js.map +1 -0
- package/dist/evidence/selector-probe.js +53 -0
- package/dist/evidence/selector-probe.js.map +1 -0
- package/dist/evidence/stealth.js +21 -0
- package/dist/evidence/stealth.js.map +1 -0
- package/dist/evidence/types.js +14 -0
- package/dist/evidence/types.js.map +1 -0
- package/dist/interactive/prompt.js +31 -0
- package/dist/interactive/prompt.js.map +1 -0
- package/dist/mcp/server.js +308 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/summary.js +41 -0
- package/dist/mcp/summary.js.map +1 -0
- package/dist/report/context.js +184 -0
- package/dist/report/context.js.map +1 -0
- package/dist/report/projection.js +125 -0
- package/dist/report/projection.js.map +1 -0
- package/dist/report/quote-check.js +90 -0
- package/dist/report/quote-check.js.map +1 -0
- package/dist/report/render.js +178 -0
- package/dist/report/render.js.map +1 -0
- package/dist/report/serializers.js +124 -0
- package/dist/report/serializers.js.map +1 -0
- package/dist/report/store.js +139 -0
- package/dist/report/store.js.map +1 -0
- package/dist/run/verify.js +77 -0
- package/dist/run/verify.js.map +1 -0
- package/dist/utils/duration.js +10 -0
- package/dist/utils/duration.js.map +1 -0
- package/dist/utils/errors.js +17 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.js +59 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/runTimingsLog.js +27 -0
- package/dist/utils/runTimingsLog.js.map +1 -0
- package/dist/utils/slug.js +27 -0
- package/dist/utils/slug.js.map +1 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/package.json +57 -0
- package/scripts/install.sh +314 -0
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
export const DREAMDATA_CDN_HOST = "cdn.dreamdata.cloud";
|
|
2
|
+
// The cookieless bundle is served from a distinct CDN host; event delivery
|
|
3
|
+
// (the POST requests) from either bundle still targets DREAMDATA_CDN_HOST by
|
|
4
|
+
// default — these hosts only matter for detecting the <script> tag itself.
|
|
5
|
+
export const DREAMDATA_COOKIELESS_SCRIPT_HOST = "cdn.drda.io";
|
|
6
|
+
export const DEFAULT_SCRIPT_HOSTS = [DREAMDATA_CDN_HOST, DREAMDATA_COOKIELESS_SCRIPT_HOST];
|
|
7
|
+
/**
|
|
8
|
+
* Hostnames Dreamdata serves from: the shared CDNs plus per-tenant subdomains
|
|
9
|
+
* of the first-party zones (api.s.dreamdata.io, evs.s.dreamdata.io, …). Fully
|
|
10
|
+
* anchored and requiring at least one subdomain label, so the bare
|
|
11
|
+
* dreamdata.io apex does not match — it shows up in unrelated referer strings.
|
|
12
|
+
*
|
|
13
|
+
* Tested against a URL's *hostname*, never the whole URL, so a third party's
|
|
14
|
+
* `?referer=dreamdata.io` query string cannot trigger it.
|
|
15
|
+
*/
|
|
16
|
+
export const DREAMDATA_HOST_PATTERN = /^(?:[a-z0-9-]+\.)+(?:dreamdata\.(?:io|cloud)|drda\.io)$/i;
|
|
17
|
+
/** Matches a Dreamdata-owned origin written out inside an inline snippet. */
|
|
18
|
+
export const DREAMDATA_ORIGIN_IN_SNIPPET = /https?:\/\/(?:[a-z0-9-]+\.)+(?:dreamdata\.(?:io|cloud)|drda\.io)/i;
|
|
19
|
+
/**
|
|
20
|
+
* Hosts that receive event requests. Installs that front Dreamdata with a
|
|
21
|
+
* first-party CNAME (e.g. evs.s.dreamdata.io on dreamdata.io itself) deliver
|
|
22
|
+
* to their own host instead, so this is configurable via --tracking-host.
|
|
23
|
+
*/
|
|
24
|
+
export const DEFAULT_TRACKING_HOSTS = [DREAMDATA_CDN_HOST];
|
|
25
|
+
/**
|
|
26
|
+
* Real Segment's own CDN — a `<script src>` pointing here is Segment's
|
|
27
|
+
* actual analytics.js/analytics-next library, NOT a Dreamdata bundle, even
|
|
28
|
+
* though the site may still be forwarding events to Dreamdata behind the
|
|
29
|
+
* scenes. See ThirdPartySegmentEvidence for why this harness can only ever
|
|
30
|
+
* report that this script exists, never whether Dreamdata receives anything.
|
|
31
|
+
*/
|
|
32
|
+
export const THIRD_PARTY_SEGMENT_SCRIPT_HOST = "cdn.segment.com";
|
|
33
|
+
/**
|
|
34
|
+
* Segment's own event-collection endpoint. A request here proves the site is
|
|
35
|
+
* instrumented with real Segment and actively sending it events — nothing
|
|
36
|
+
* more. Whether Segment then forwards a copy to Dreamdata is a server-side
|
|
37
|
+
* destination/connection this harness cannot observe.
|
|
38
|
+
*/
|
|
39
|
+
export const THIRD_PARTY_SEGMENT_TRACKING_HOST = "api.segment.io";
|
|
40
|
+
/**
|
|
41
|
+
* Event endpoints appear as /api/v1/<letter> on the shared CDN and as plain
|
|
42
|
+
* /v1/<letter> on first-party delivery hosts (e.g. api.s.dreamdata.io/v1/p),
|
|
43
|
+
* so the /api/ segment is optional.
|
|
44
|
+
*/
|
|
45
|
+
export const ENDPOINT_REGEX = /\/(?:api\/)?v1\/([a-z])(?:[/?]|$)/;
|
|
46
|
+
// The single most reliable per-request signal for which bundle sent an
|
|
47
|
+
// event: the delivery plugin stamps this into every payload's context.
|
|
48
|
+
export const COOKIELESS_LIBRARY_NAME_MARKER = "cookieless";
|
|
49
|
+
export const COOKIELESS_GLOBAL_KEY = "dreamdata-cl";
|
|
50
|
+
/**
|
|
51
|
+
* Segment-compatible installs expose the queue/SDK as window.analytics
|
|
52
|
+
* rather than window.dreamdata. Both are valid Dreamdata deployments.
|
|
53
|
+
*/
|
|
54
|
+
export const SEGMENT_COMPAT_GLOBAL_KEY = "analytics";
|
|
55
|
+
// Matches the writeKey segment out of the installed script's src, e.g.
|
|
56
|
+
// https://cdn.dreamdata.cloud/scripts/analytics.js/v1/{writeKey}/analytics.min.js
|
|
57
|
+
// or .../analytics/js/v1/{writeKey}/analytics...
|
|
58
|
+
export const WRITE_KEY_FROM_SCRIPT_SRC = /\/analytics(?:\.js|\/js)\/v1\/([^/]+)/i;
|
|
59
|
+
/**
|
|
60
|
+
* Segment-style snippets assign the key instead of putting it in the src:
|
|
61
|
+
* `analytics._writeKey="…"` or `writeKey: "…"`. The `=` is why the older
|
|
62
|
+
* colon/quote-only pattern missed real installs.
|
|
63
|
+
*/
|
|
64
|
+
export const WRITE_KEY_FROM_SNIPPET = /\b_?writeKey["']?\s*[:=]\s*["']([\w-]+)["']/i;
|
|
65
|
+
/** `analytics.load("{writeKey}")` — the last-resort snippet fallback. */
|
|
66
|
+
export const WRITE_KEY_FROM_LOAD_CALL = /\.load\(\s*["']([\w-]+)["']/i;
|
|
67
|
+
/**
|
|
68
|
+
* Dreamdata ships auxiliary scripts (e.g. the identify-form helper) from the
|
|
69
|
+
* same CDN. They are not tracking bundles, so matching one must not count as
|
|
70
|
+
* "the script is installed".
|
|
71
|
+
*/
|
|
72
|
+
export const HELPER_SCRIPT_PATH_MARKERS = ["/scripts/identify-form/"];
|
|
73
|
+
export const ENDPOINT_NAMES = {
|
|
74
|
+
p: "page",
|
|
75
|
+
i: "identify",
|
|
76
|
+
t: "track",
|
|
77
|
+
a: "alias",
|
|
78
|
+
g: "group",
|
|
79
|
+
s: "screen",
|
|
80
|
+
b: "batch",
|
|
81
|
+
};
|
|
82
|
+
export const DD_COOKIE_NAMES = [
|
|
83
|
+
"dd_anonymous_id",
|
|
84
|
+
"dd_user_id",
|
|
85
|
+
"dd_user_traits",
|
|
86
|
+
"dd_group_id",
|
|
87
|
+
"dd_group_properties",
|
|
88
|
+
];
|
|
89
|
+
export const AJS_COOKIE_PREFIX = "ajs_";
|
|
90
|
+
/** Segment-compatibility equivalents of DD_COOKIE_NAMES, same order. */
|
|
91
|
+
export const AJS_COOKIE_NAMES = [
|
|
92
|
+
"ajs_anonymous_id",
|
|
93
|
+
"ajs_user_id",
|
|
94
|
+
"ajs_user_traits",
|
|
95
|
+
"ajs_group_id",
|
|
96
|
+
"ajs_group_properties",
|
|
97
|
+
];
|
|
98
|
+
export const MESSAGE_ID_PATTERN = /^ajs-next-/;
|
|
99
|
+
export const DREAMDATA_METHODS = [
|
|
100
|
+
"page",
|
|
101
|
+
"track",
|
|
102
|
+
"identify",
|
|
103
|
+
"group",
|
|
104
|
+
"alias",
|
|
105
|
+
"screen",
|
|
106
|
+
"ready",
|
|
107
|
+
"setAnonymousId",
|
|
108
|
+
"trackForm",
|
|
109
|
+
"trackLink",
|
|
110
|
+
];
|
|
111
|
+
/**
|
|
112
|
+
* How long a parsed document is given to go quiet before a run carries on.
|
|
113
|
+
*
|
|
114
|
+
* A bounded settle, not a completion condition. Waiting on `networkidle` for
|
|
115
|
+
* a whole navigation timeout reads as "wait until the page has finished", but
|
|
116
|
+
* a page that keeps a socket open — a chat widget, an analytics heartbeat, a
|
|
117
|
+
* bot wall polling its own challenge endpoint — never reaches idle at all, so
|
|
118
|
+
* such a wait always runs to the full timeout. A page that does go quiet is
|
|
119
|
+
* waited for exactly as before; one that never does costs this much and no
|
|
120
|
+
* more, and that is not an error.
|
|
121
|
+
*/
|
|
122
|
+
export const NETWORK_SETTLE_MS = 5_000;
|
|
123
|
+
/**
|
|
124
|
+
* How long an interaction-gated banner is given to mount after the nudge that
|
|
125
|
+
* should reveal it. It is a synchronous DOM build in every case seen so far —
|
|
126
|
+
* this is slack for a framework's render tick, not a wait for a timer.
|
|
127
|
+
*/
|
|
128
|
+
export const BANNER_REVEAL_SETTLE_MS = 750;
|
|
129
|
+
/**
|
|
130
|
+
* How far the nudge scrolls. Enough to cross a scroll-depth trigger's first
|
|
131
|
+
* threshold — catonetworks.com's GTM tags need one — and small enough that the
|
|
132
|
+
* page is still showing what it shows on arrival. Restored to 0 straight after.
|
|
133
|
+
*/
|
|
134
|
+
export const BANNER_REVEAL_SCROLL_PX = 300;
|
|
135
|
+
/**
|
|
136
|
+
* CMP vendors we can detect and, where possible, drive programmatically.
|
|
137
|
+
* `globalKey` is probed on window; order is most-specific first.
|
|
138
|
+
*/
|
|
139
|
+
export const CMP_VENDORS = [
|
|
140
|
+
{ id: "cookiebot", globalKey: "Cookiebot" },
|
|
141
|
+
{ id: "onetrust", globalKey: "OneTrust" },
|
|
142
|
+
{ id: "onetrust", globalKey: "OptanonActiveGroups" },
|
|
143
|
+
{ id: "onetrust", globalKey: "OnetrustActiveGroups" },
|
|
144
|
+
{ id: "usercentrics", globalKey: "UC_UI" },
|
|
145
|
+
{ id: "cookieyes", globalKey: "getCkyConsent" },
|
|
146
|
+
{ id: "generic-cookieconsent", globalKey: "CookieConsent" },
|
|
147
|
+
{ id: "iab-tcf", globalKey: "__tcfapi" },
|
|
148
|
+
{ id: "iab-cmp", globalKey: "__cmp" },
|
|
149
|
+
{ id: "osano", globalKey: "Osano" },
|
|
150
|
+
{ id: "complianz", globalKey: "complianz" },
|
|
151
|
+
];
|
|
152
|
+
export const KNOWN_CMP_GLOBALS = CMP_VENDORS.map((v) => v.globalKey);
|
|
153
|
+
/**
|
|
154
|
+
* Fallback used only when no named vendor/DOM banner matched: a bespoke,
|
|
155
|
+
* hand-rolled banner exposes no global and no recognisable id/class, so the
|
|
156
|
+
* only thing left to go on is a clickable control's own text. Kept to exact
|
|
157
|
+
* phrases actually seen on cookie banners, not "contains accept", so an
|
|
158
|
+
* unrelated page control (a signup form's "Accept" button) is never matched
|
|
159
|
+
* on text alone — see heuristic-banner.ts for the container check that is
|
|
160
|
+
* the real safeguard against a false match.
|
|
161
|
+
*/
|
|
162
|
+
export const GENERIC_ACCEPT_PHRASES = [
|
|
163
|
+
"accept all",
|
|
164
|
+
"accept all cookies",
|
|
165
|
+
"allow all",
|
|
166
|
+
"allow all cookies",
|
|
167
|
+
"i accept",
|
|
168
|
+
"agree",
|
|
169
|
+
"agree to all",
|
|
170
|
+
"accept cookies",
|
|
171
|
+
"allow cookies",
|
|
172
|
+
// A banner with no reject button often has no "all" to accept either, so
|
|
173
|
+
// its one control just says what happens next. meteomatics.com's
|
|
174
|
+
// "Accept and continue" is the case this list was missing: nothing matched
|
|
175
|
+
// it, so the banner was reported as absent and all three phases measured
|
|
176
|
+
// the same unanswered page.
|
|
177
|
+
"accept and continue",
|
|
178
|
+
"accept & continue",
|
|
179
|
+
"agree and continue",
|
|
180
|
+
"agree & continue",
|
|
181
|
+
"accept and close",
|
|
182
|
+
"agree and close",
|
|
183
|
+
"accept and proceed",
|
|
184
|
+
"agree and proceed",
|
|
185
|
+
];
|
|
186
|
+
/** Reject-side equivalents of GENERIC_ACCEPT_PHRASES. */
|
|
187
|
+
export const GENERIC_REJECT_PHRASES = [
|
|
188
|
+
"necessary only",
|
|
189
|
+
"only necessary",
|
|
190
|
+
"essential only",
|
|
191
|
+
"only essential",
|
|
192
|
+
"reject all",
|
|
193
|
+
"decline all",
|
|
194
|
+
"reject",
|
|
195
|
+
"decline",
|
|
196
|
+
"deny",
|
|
197
|
+
"refuse all",
|
|
198
|
+
// Osano's own reject button says what it rejects rather than "all":
|
|
199
|
+
// achievers.com offers "Accept All" and "Reject Non-Essential", so nothing
|
|
200
|
+
// here matched, the banner failed the counterpart test in
|
|
201
|
+
// heuristic-banner.ts, and a banner sitting in plain markup went unseen.
|
|
202
|
+
"reject non-essential",
|
|
203
|
+
"reject non-essential cookies",
|
|
204
|
+
"decline non-essential",
|
|
205
|
+
"decline non-essential cookies",
|
|
206
|
+
];
|
|
207
|
+
/**
|
|
208
|
+
* A matched accept/reject control only counts as a real banner when some
|
|
209
|
+
* ancestor's text also mentions one of these — otherwise text matching alone
|
|
210
|
+
* would happily click an unrelated "Accept"/"Decline" button elsewhere on
|
|
211
|
+
* the page.
|
|
212
|
+
*/
|
|
213
|
+
export const BANNER_CONTEXT_KEYWORDS = ["cookie", "consent", "privacy", "gdpr"];
|
|
214
|
+
/**
|
|
215
|
+
* Banners recognised by a DOM element rather than a window global.
|
|
216
|
+
*
|
|
217
|
+
* HubSpot's banner is the reason this list exists: window._hsp is present on
|
|
218
|
+
* every HubSpot-tracked page whether or not a banner is shown, so probing it
|
|
219
|
+
* would invent a consent gate on sites that deliberately have none. The
|
|
220
|
+
* banner element only exists when the banner is actually rendered.
|
|
221
|
+
*/
|
|
222
|
+
export const DOM_ONLY_CMP_VENDORS = [
|
|
223
|
+
{ id: "hubspot", bannerElementId: "hs-eu-cookie-confirmation", responseCookiePrefix: "__hs_cookie_cat_pref" },
|
|
224
|
+
// Salesforce Pardot's standard tracking-code opt-in snippet. piEnableTracking
|
|
225
|
+
// and piDisableTracking are defined on window as part of every Pardot
|
|
226
|
+
// tracking snippet whether or not a banner is ever shown to this visitor
|
|
227
|
+
// (much like HubSpot's window._hsp), so probing for those globals the way
|
|
228
|
+
// CMP_VENDORS does would invent a consent gate on any Pardot-tracked page.
|
|
229
|
+
// The banner div only exists when the banner is actually rendered.
|
|
230
|
+
{ id: "pardot", bannerElementId: "pi_tracking_opt_in_div", responseCookiePrefix: "pi_opt_in987432" },
|
|
231
|
+
];
|
|
232
|
+
/**
|
|
233
|
+
* True when a URL's host belongs to Dreamdata, or is one the operator named
|
|
234
|
+
* explicitly via --tracking-host (needed for a fully custom CNAME).
|
|
235
|
+
*/
|
|
236
|
+
export function isDreamdataUrl(url, extraHosts = []) {
|
|
237
|
+
if (extraHosts.some((host) => url.includes(host)))
|
|
238
|
+
return true;
|
|
239
|
+
try {
|
|
240
|
+
return DREAMDATA_HOST_PATTERN.test(new URL(url).hostname);
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Path fragments a second-page probe refuses to follow.
|
|
248
|
+
*
|
|
249
|
+
* These pages are the ones most likely to sit outside the site's own tracking
|
|
250
|
+
* setup — a separate template, a third-party host, an auth redirect — so a
|
|
251
|
+
* missing request there would say nothing about the install, and a consent
|
|
252
|
+
* banner's own "privacy policy" link is exactly the kind of link that would
|
|
253
|
+
* otherwise get picked.
|
|
254
|
+
*/
|
|
255
|
+
export const SECOND_PAGE_PATH_DENYLIST = [
|
|
256
|
+
"privacy",
|
|
257
|
+
"cookie",
|
|
258
|
+
"legal",
|
|
259
|
+
"terms",
|
|
260
|
+
"gdpr",
|
|
261
|
+
"imprint",
|
|
262
|
+
"login",
|
|
263
|
+
"signin",
|
|
264
|
+
"sign-in",
|
|
265
|
+
"signup",
|
|
266
|
+
"sign-up",
|
|
267
|
+
"register",
|
|
268
|
+
"logout",
|
|
269
|
+
"account",
|
|
270
|
+
"checkout",
|
|
271
|
+
"cart",
|
|
272
|
+
"search",
|
|
273
|
+
];
|
|
274
|
+
/** Extensions that are a download or an asset rather than a tracked page view. */
|
|
275
|
+
export const SECOND_PAGE_ASSET_EXTENSIONS = [
|
|
276
|
+
".pdf",
|
|
277
|
+
".zip",
|
|
278
|
+
".dmg",
|
|
279
|
+
".exe",
|
|
280
|
+
".csv",
|
|
281
|
+
".xlsx",
|
|
282
|
+
".doc",
|
|
283
|
+
".docx",
|
|
284
|
+
".ppt",
|
|
285
|
+
".pptx",
|
|
286
|
+
".jpg",
|
|
287
|
+
".jpeg",
|
|
288
|
+
".png",
|
|
289
|
+
".gif",
|
|
290
|
+
".svg",
|
|
291
|
+
".webp",
|
|
292
|
+
".mp4",
|
|
293
|
+
".mp3",
|
|
294
|
+
".xml",
|
|
295
|
+
".json",
|
|
296
|
+
".rss",
|
|
297
|
+
];
|
|
298
|
+
/** Attribute stamped on the link the second-page probe decided to follow. */
|
|
299
|
+
export const SECOND_PAGE_LINK_ATTRIBUTE = "data-dd-second-page-link";
|
|
300
|
+
/**
|
|
301
|
+
* How many ranked links the probe keeps in reserve, and how many of them it is
|
|
302
|
+
* willing to walk past when they land on a page with no install on it.
|
|
303
|
+
*
|
|
304
|
+
* Each retry costs a navigation back to the first page plus a navigation out
|
|
305
|
+
* again, three times a run. Three is enough to clear a site with two or three
|
|
306
|
+
* separately-built sections near the top of the nav (whereby.com's Gatsby blog
|
|
307
|
+
* being the case this exists for) without letting a site with nothing
|
|
308
|
+
* instrumented anywhere spend the whole budget proving it.
|
|
309
|
+
*/
|
|
310
|
+
export const SECOND_PAGE_MAX_RETRIES = 3;
|
|
311
|
+
/** Ranked links kept per page — enough to survive the retries above. */
|
|
312
|
+
export const SECOND_PAGE_MAX_CANDIDATES = 12;
|
|
313
|
+
/** Filenames the two bundles are served under, used to classify a tag by src. */
|
|
314
|
+
export const DREAMDATA_NORMAL_SCRIPT_FILE = "dreamdata.min.js";
|
|
315
|
+
export const DREAMDATA_COOKIELESS_SCRIPT_FILE = "dreamdata.cl.min.js";
|
|
316
|
+
/** Element ids the loader snippets stamp onto the <script> tags they inject. */
|
|
317
|
+
export const NORMAL_BUNDLE_ELEMENT_ID = "dreamdata-analytics";
|
|
318
|
+
export const COOKIELESS_BUNDLE_ELEMENT_ID = "dreamdata-analytics-cl";
|
|
319
|
+
/**
|
|
320
|
+
* The cookieless loader names its normal-bundle companion by element id
|
|
321
|
+
* (`getElementById("dreamdata-analytics")`) so it can stand down when the
|
|
322
|
+
* normal bundle is unblocked. That reference is the strongest in-page proof
|
|
323
|
+
* that a normal bundle belongs to the deployment even when consent has kept
|
|
324
|
+
* it out of the DOM entirely.
|
|
325
|
+
*
|
|
326
|
+
* Quoted deliberately: the cookieless tag's own id is "dreamdata-analytics-cl",
|
|
327
|
+
* which an unanchored substring match would happily mistake for the companion.
|
|
328
|
+
*/
|
|
329
|
+
export const NORMAL_COMPANION_REFERENCE = /["']dreamdata-analytics["']|data-global-dreamdata-analytics-key/;
|
|
330
|
+
/**
|
|
331
|
+
* How CMPs neutralise a tag they are gating. A tag matching any of these is
|
|
332
|
+
* installed but deliberately not executing — the opposite of "not installed",
|
|
333
|
+
* and the distinction the cookieless-by-design verdict hinges on.
|
|
334
|
+
*/
|
|
335
|
+
export const CMP_SCRIPT_BLOCK_MARKERS = [
|
|
336
|
+
{ pattern: /type=["']text\/plain["']/i, vendor: "generic (type=text/plain)" },
|
|
337
|
+
{ pattern: /type=["']text\/gtmscript["']/i, vendor: "google-tag-manager" },
|
|
338
|
+
{ pattern: /optanon-category/i, vendor: "onetrust" },
|
|
339
|
+
{ pattern: /data-ot-ignore/i, vendor: "onetrust" },
|
|
340
|
+
{ pattern: /data-cookieconsent=/i, vendor: "cookiebot" },
|
|
341
|
+
{ pattern: /cmplz-script|data-cmplz/i, vendor: "complianz" },
|
|
342
|
+
{ pattern: /data-cookieyes|class=["'][^"']*cky-/i, vendor: "cookieyes" },
|
|
343
|
+
{ pattern: /data-usercentrics/i, vendor: "usercentrics" },
|
|
344
|
+
];
|
|
345
|
+
/**
|
|
346
|
+
* Which consent platform a document is wired to, read off the raw markup
|
|
347
|
+
* alone — the script the CMP loads, not a window global or a rendered banner.
|
|
348
|
+
*
|
|
349
|
+
* Used only by the plain-HTTP probe, which executes nothing and so can never
|
|
350
|
+
* see a global or a banner element. Its job there is corroboration: a served
|
|
351
|
+
* document that loads OneTrust, next to a rendered page where
|
|
352
|
+
* consent.cmpDetected came back false in every phase, is the browser having
|
|
353
|
+
* been shown a different document — not a site without a banner.
|
|
354
|
+
*/
|
|
355
|
+
export const CMP_MARKUP_MARKERS = [
|
|
356
|
+
{ pattern: /cdn\.cookielaw\.org|otSDKStub|optanon/i, vendor: "onetrust" },
|
|
357
|
+
{ pattern: /consent\.cookiebot\.com|Cookiebot/i, vendor: "cookiebot" },
|
|
358
|
+
{ pattern: /app\.usercentrics\.eu|usercentrics/i, vendor: "usercentrics" },
|
|
359
|
+
{ pattern: /cdn-cookieyes\.com|cookieyes/i, vendor: "cookieyes" },
|
|
360
|
+
{ pattern: /cmp\.osano\.com|osano/i, vendor: "osano" },
|
|
361
|
+
{ pattern: /consent\.trustarc\.com|trustarc/i, vendor: "trustarc" },
|
|
362
|
+
{ pattern: /app\.termly\.io|termly/i, vendor: "termly" },
|
|
363
|
+
{ pattern: /global\.ketchcdn\.com|ketch/i, vendor: "ketch" },
|
|
364
|
+
{ pattern: /cookie-?script\.com/i, vendor: "cookiescript" },
|
|
365
|
+
{ pattern: /cmplz-|complianz/i, vendor: "complianz" },
|
|
366
|
+
{ pattern: /hs-eu-cookie-confirmation/i, vendor: "hubspot" },
|
|
367
|
+
{ pattern: /pi_tracking_opt_in_div/i, vendor: "pardot" },
|
|
368
|
+
];
|
|
369
|
+
/**
|
|
370
|
+
* Markup that names a framework shipping a client-side router.
|
|
371
|
+
*
|
|
372
|
+
* A marker is NOT evidence of client-side routing, and nothing may read this
|
|
373
|
+
* list as if it were. yougov.com serves ng-version, ships the Angular router,
|
|
374
|
+
* and loaded a new document for every link the run followed; Astro and Gatsby
|
|
375
|
+
* ship routers and are routinely deployed as plain multi-page sites. What a
|
|
376
|
+
* marker buys is the one case where behaviour is unavailable: zoominfo.com's
|
|
377
|
+
* bot wall left secondPage.kind null in all three phases, and its served
|
|
378
|
+
* markup names Next.js 115 times over — the only routing hint that survived
|
|
379
|
+
* the wall. See RoutingMode, where a marker can reach "suspected" and never
|
|
380
|
+
* an observed mode.
|
|
381
|
+
*
|
|
382
|
+
* Deliberately excludes the root-container markers (id="root", id="app"):
|
|
383
|
+
* jumpcloud.com is WordPress and has id="app", which makes that pattern a
|
|
384
|
+
* false-positive generator rather than a signal. Excludes data-reactroot for
|
|
385
|
+
* the same reason at one remove — it says the document was server-rendered by
|
|
386
|
+
* React, which is an argument about how the page arrived, not about how it
|
|
387
|
+
* navigates. Excludes astro-island, which argues against client routing.
|
|
388
|
+
*
|
|
389
|
+
* Case-sensitive on purpose: __NEXT_DATA__ and __NUXT__ are exact
|
|
390
|
+
* identifiers, and a case-insensitive /next/ matches ordinary prose.
|
|
391
|
+
*/
|
|
392
|
+
export const CSR_FRAMEWORK_MARKERS = [
|
|
393
|
+
{ pattern: /__NEXT_DATA__|\/_next\/static\//, framework: "next" },
|
|
394
|
+
{ pattern: /id="__nuxt"|__NUXT__|\/_nuxt\//, framework: "nuxt" },
|
|
395
|
+
{ pattern: /__remixContext/, framework: "remix" },
|
|
396
|
+
{ pattern: /__sveltekit_|data-sveltekit-/, framework: "sveltekit" },
|
|
397
|
+
{ pattern: /page-data\/app-data\.json|\/page-data\//, framework: "gatsby" },
|
|
398
|
+
{ pattern: /\sng-version="/, framework: "angular" },
|
|
399
|
+
];
|
|
400
|
+
/**
|
|
401
|
+
* Tag managers inject the bundle at runtime, so a consent-gated normal bundle
|
|
402
|
+
* never reaches the DOM to be found. Reading the published container is the
|
|
403
|
+
* only way to see that it exists at all.
|
|
404
|
+
*/
|
|
405
|
+
export const GTM_CONTAINER_ID_PATTERN = /^GTM-[A-Z0-9]+$/;
|
|
406
|
+
/**
|
|
407
|
+
* The same id lifted out of page markup, for sites that keep it in config and
|
|
408
|
+
* load GTM later (or not at all under automation) — window.google_tag_manager
|
|
409
|
+
* is empty exactly when the container is what we most need to read.
|
|
410
|
+
*/
|
|
411
|
+
export const GTM_CONTAINER_ID_IN_TEXT = /GTM-[A-Z0-9]{4,10}/g;
|
|
412
|
+
export function gtmContainerUrl(containerId) {
|
|
413
|
+
return `https://www.googletagmanager.com/gtm.js?id=${encodeURIComponent(containerId)}`;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Banners that offer no "Reject all" at all — only "Accept all" and a
|
|
417
|
+
* settings/preferences link — are common enough that refusing them is a
|
|
418
|
+
* distinct flow rather than an edge case: open the preference centre, leave
|
|
419
|
+
* every non-essential category off, and save. These are the vendor controls
|
|
420
|
+
* that open that panel. Kept vendor-namespaced so a wrong guess simply
|
|
421
|
+
* matches nothing rather than clicking an unrelated page control.
|
|
422
|
+
*/
|
|
423
|
+
export const PREFERENCE_CENTER_OPEN_SELECTORS = [
|
|
424
|
+
".cky-btn-customize",
|
|
425
|
+
"#onetrust-pc-btn-handler",
|
|
426
|
+
".ot-sdk-show-settings",
|
|
427
|
+
".optanon-toggle-display",
|
|
428
|
+
"#usercentrics-root >>> button[data-testid='uc-more-button']",
|
|
429
|
+
"#usercentrics-root >>> button[data-testid='uc-customize-anchor']",
|
|
430
|
+
"#CybotCookiebotDialogBodyLevelButtonLevelOptinShowDetails",
|
|
431
|
+
"#CybotCookiebotDialogBodyButtonDetails",
|
|
432
|
+
".osano-cm-manage",
|
|
433
|
+
".cmplz-manage-options",
|
|
434
|
+
"#cookiescript_manage",
|
|
435
|
+
".iubenda-cs-customize-btn",
|
|
436
|
+
".t-preference-button",
|
|
437
|
+
"#hs-eu-cookie-settings-button",
|
|
438
|
+
];
|
|
439
|
+
/**
|
|
440
|
+
* The "commit what the toggles now say" control inside that panel — NOT an
|
|
441
|
+
* accept-all/reject-all shortcut, which would discard the toggle states we
|
|
442
|
+
* just set.
|
|
443
|
+
*/
|
|
444
|
+
export const PREFERENCE_CENTER_SAVE_SELECTORS = [
|
|
445
|
+
".cky-btn-preferences",
|
|
446
|
+
".save-preference-btn-handler",
|
|
447
|
+
"#usercentrics-root >>> button[data-testid='uc-save-button']",
|
|
448
|
+
"#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowallSelection",
|
|
449
|
+
"#CybotCookiebotDialogBodyButtonAcceptSelected",
|
|
450
|
+
".osano-cm-save",
|
|
451
|
+
".cmplz-save-preferences",
|
|
452
|
+
"#cookiescript_save",
|
|
453
|
+
".iubenda-cs-save-btn",
|
|
454
|
+
".t-saveButton",
|
|
455
|
+
];
|
|
456
|
+
/**
|
|
457
|
+
* Text fallback for the same two controls on a banner with no recognised
|
|
458
|
+
* vendor. Ordered most-specific-first: the least ambiguous phrase that
|
|
459
|
+
* matches wins, so a panel containing both "Save my preferences" and a bare
|
|
460
|
+
* "Save" never picks the vague one. The bare entries at the tail are only
|
|
461
|
+
* safe because both searches are scoped to a container that mentions cookies
|
|
462
|
+
* and holds the consent toggles — see preference-center.ts.
|
|
463
|
+
*/
|
|
464
|
+
export const GENERIC_PREFERENCE_OPEN_PHRASES = [
|
|
465
|
+
"cookie settings",
|
|
466
|
+
"cookies settings",
|
|
467
|
+
"cookie preferences",
|
|
468
|
+
"cookies preferences",
|
|
469
|
+
// Osano's drawer link. Names the storage it governs rather than cookies
|
|
470
|
+
// outright, which is specific enough to prove a banner is one.
|
|
471
|
+
"storage preferences",
|
|
472
|
+
"manage cookies",
|
|
473
|
+
"manage preferences",
|
|
474
|
+
"manage options",
|
|
475
|
+
"manage choices",
|
|
476
|
+
"manage settings",
|
|
477
|
+
"customize settings",
|
|
478
|
+
"customise settings",
|
|
479
|
+
"customize cookies",
|
|
480
|
+
"customise cookies",
|
|
481
|
+
"cookie options",
|
|
482
|
+
"let me choose",
|
|
483
|
+
"more options",
|
|
484
|
+
"customize",
|
|
485
|
+
"customise",
|
|
486
|
+
"preferences",
|
|
487
|
+
"settings",
|
|
488
|
+
];
|
|
489
|
+
/**
|
|
490
|
+
* The cookie-explicit half of GENERIC_PREFERENCE_OPEN_PHRASES, used where a
|
|
491
|
+
* settings link has to stand in for the missing reject button rather than
|
|
492
|
+
* merely be clicked once a banner is already established.
|
|
493
|
+
*
|
|
494
|
+
* heuristic-banner.ts proves a banner is a banner by finding the
|
|
495
|
+
* OPPOSITE-direction control next to the one it matched. A banner offering
|
|
496
|
+
* only "Accept and continue" and "Cookie Settings" has no opposite control at
|
|
497
|
+
* all, so that test failed and the whole banner went unseen — including by
|
|
498
|
+
* the preference-centre flow written for exactly this shape. A settings link
|
|
499
|
+
* is allowed to play the counterpart's part, but only when it names cookies
|
|
500
|
+
* outright: the vaguer tail of the open list ("settings", "preferences",
|
|
501
|
+
* "customize") sits in far too many page footers to prove anything.
|
|
502
|
+
*/
|
|
503
|
+
export const PREFERENCE_CENTER_COUNTERPART_PHRASES = [
|
|
504
|
+
"cookie settings",
|
|
505
|
+
"cookies settings",
|
|
506
|
+
"cookie preferences",
|
|
507
|
+
"cookies preferences",
|
|
508
|
+
"storage preferences",
|
|
509
|
+
"cookie options",
|
|
510
|
+
"manage cookies",
|
|
511
|
+
"customize cookies",
|
|
512
|
+
"customise cookies",
|
|
513
|
+
"manage preferences",
|
|
514
|
+
"manage choices",
|
|
515
|
+
"manage options",
|
|
516
|
+
"let me choose",
|
|
517
|
+
];
|
|
518
|
+
/**
|
|
519
|
+
* Context keywords for the CATEGORY TOGGLES inside a preference centre —
|
|
520
|
+
* deliberately narrower than BANNER_CONTEXT_KEYWORDS, which keeps "privacy".
|
|
521
|
+
*
|
|
522
|
+
* Nearly every marketing form on the web has a "I agree to the Terms and
|
|
523
|
+
* Privacy Policy" checkbox, so "privacy" alone pulls unrelated form controls
|
|
524
|
+
* into the toggle set; the panel is then computed as the smallest element
|
|
525
|
+
* containing all of them, which is the whole page, and the save-button search
|
|
526
|
+
* is scoped to the whole page with it. A consent category names cookies or
|
|
527
|
+
* consent.
|
|
528
|
+
*/
|
|
529
|
+
export const PREFERENCE_PANEL_CONTEXT_KEYWORDS = ["cookie", "consent", "gdpr"];
|
|
530
|
+
/**
|
|
531
|
+
* How much text a container may hold and still count as the panel a toggle
|
|
532
|
+
* lives in. A preference centre's own copy is a few hundred to a few thousand
|
|
533
|
+
* characters; the <main> that happens to contain both a newsletter form and a
|
|
534
|
+
* cookie banner is one to two orders of magnitude larger (168k on
|
|
535
|
+
* meteomatics.com). Without this bound the keyword narrowing above is not
|
|
536
|
+
* enough on its own, since a page-level ancestor mentions cookies via the
|
|
537
|
+
* banner it contains.
|
|
538
|
+
*/
|
|
539
|
+
export const PREFERENCE_PANEL_MAX_CONTEXT_CHARS = 6000;
|
|
540
|
+
/** Save-side equivalents of GENERIC_PREFERENCE_OPEN_PHRASES. */
|
|
541
|
+
export const GENERIC_SAVE_PREFERENCES_PHRASES = [
|
|
542
|
+
"save my preferences",
|
|
543
|
+
"save my choices",
|
|
544
|
+
"save my settings",
|
|
545
|
+
"save preferences",
|
|
546
|
+
"save settings",
|
|
547
|
+
"save choices",
|
|
548
|
+
"save selection",
|
|
549
|
+
"allow selection",
|
|
550
|
+
"confirm my choices",
|
|
551
|
+
"confirm my preferences",
|
|
552
|
+
"confirm choices",
|
|
553
|
+
"confirm selection",
|
|
554
|
+
"save and close",
|
|
555
|
+
"save & close",
|
|
556
|
+
"save and exit",
|
|
557
|
+
"save & exit",
|
|
558
|
+
"save and accept",
|
|
559
|
+
"save",
|
|
560
|
+
"confirm",
|
|
561
|
+
"apply",
|
|
562
|
+
"done",
|
|
563
|
+
];
|
|
564
|
+
/**
|
|
565
|
+
* A category toggle matching any of these is the always-on essential bucket.
|
|
566
|
+
* Turning it off is not something a visitor can do, and attempting it is how
|
|
567
|
+
* a panel ends up in a state it refuses to save.
|
|
568
|
+
*/
|
|
569
|
+
export const ESSENTIAL_CATEGORY_PATTERN = /necessar|essential|strictly|required|mandatory|always active|always on/i;
|
|
570
|
+
/**
|
|
571
|
+
* Everything after "Content Security Policy directive:" in a CSP violation
|
|
572
|
+
* message — the site's whole policy, reproduced verbatim by the browser.
|
|
573
|
+
*
|
|
574
|
+
* It is quoted evidence about the SITE, not about the resource that was
|
|
575
|
+
* blocked, and matching against it is how idtdna.com got a failing
|
|
576
|
+
* console-clean check for a blocked ad.doubleclick.net request: the policy it
|
|
577
|
+
* violated happens to allow cdn.dreamdata.cloud, so the word "dreamdata"
|
|
578
|
+
* appeared in a message that has nothing to do with Dreamdata. The blocked
|
|
579
|
+
* URL always precedes this clause, so a CSP error that really does block
|
|
580
|
+
* Dreamdata still matches on the part that names it.
|
|
581
|
+
*/
|
|
582
|
+
const CSP_POLICY_CLAUSE = /Content Security Policy directive:[\s\S]*/i;
|
|
583
|
+
/**
|
|
584
|
+
* Is this console message ABOUT Dreamdata, rather than merely containing the
|
|
585
|
+
* word somewhere?
|
|
586
|
+
*/
|
|
587
|
+
export function mentionsDreamdata(text) {
|
|
588
|
+
return /dreamdata/i.test(text.replace(CSP_POLICY_CLAUSE, ""));
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* The vocabulary a console message uses when something actually went wrong.
|
|
592
|
+
*
|
|
593
|
+
* Matched against the message the SITE wrote, not the browser's own wrapper,
|
|
594
|
+
* so it stays deliberately plain: the words a developer reaches for when
|
|
595
|
+
* reporting a failure, not a catalogue of Dreamdata error strings.
|
|
596
|
+
*/
|
|
597
|
+
const FAILURE_VOCABULARY = /\b(fail(ed|s|ure)?|error|refus(ed|es)|block(ed|s)|denied|reject(ed|s)|unable|cannot|can't|couldn't|invalid|missing|not found|undefined is not|is not a function|null is not|exception|uncaught|unhandled|timed? ?out|timeout|retry|retrying|abort(ed)?|crash(ed)?|unsupported|deprecated|twice|duplicate snippet|conflict)\b/i;
|
|
598
|
+
/**
|
|
599
|
+
* Is this console.error a real failure report, or a debug line the site chose
|
|
600
|
+
* to print at error level?
|
|
601
|
+
*
|
|
602
|
+
* blackline.com's Nuxt plugin logs "🔌 [DREAMDATA] Plugin loaded - YOU SHOULD
|
|
603
|
+
* SEE THIS IN PRODUCTION" through console.error on every page load. Nothing
|
|
604
|
+
* went wrong — the plugin is announcing itself — but console-clean read the
|
|
605
|
+
* severity the site picked and sank an otherwise clean run. Severity alone is
|
|
606
|
+
* not the site's report of a failure; the words are.
|
|
607
|
+
*
|
|
608
|
+
* Judged from the message and nothing else. Whether the events were delivered
|
|
609
|
+
* is a different check's business, and letting it gate this one would make a
|
|
610
|
+
* console message mean different things on different runs.
|
|
611
|
+
*/
|
|
612
|
+
export function describesFailure(text) {
|
|
613
|
+
return FAILURE_VOCABULARY.test(text.replace(CSP_POLICY_CLAUSE, ""));
|
|
614
|
+
}
|
|
615
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/evidence/constants.ts"],"names":[],"mappings":"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"}
|