@affiliateo/web 2.4.0 → 3.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/dist/bot-detect.d.ts +37 -0
- package/dist/bot-detect.js +138 -0
- package/dist/provider.d.ts +1 -1
- package/dist/provider.js +360 -143
- package/dist/queue.d.ts +99 -0
- package/dist/queue.js +266 -0
- package/dist/types.d.ts +74 -0
- package/package.json +34 -34
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side bot detection. Mirrors Datafast / PostHog / Plausible's
|
|
3
|
+
* approach: catch the obvious automation tools at SDK init so they
|
|
4
|
+
* never fire a single network request.
|
|
5
|
+
*
|
|
6
|
+
* Defense in depth:
|
|
7
|
+
* 1. This check — catches Selenium / Puppeteer / curl / etc
|
|
8
|
+
* BEFORE anything hits the wire. Saves bandwidth.
|
|
9
|
+
* Can be bypassed by a determined attacker who
|
|
10
|
+
* edits the bundled SDK.
|
|
11
|
+
* 2. Server UA regex — catches the same patterns plus legit crawlers
|
|
12
|
+
* (Googlebot, FB crawler, etc). Server is
|
|
13
|
+
* authoritative. Can't be bypassed.
|
|
14
|
+
* 3. Cloudflare — invisible browser-attestation challenge. Real
|
|
15
|
+
* Turnstile browsers pass in 50-200ms; scripts that don't
|
|
16
|
+
* run real JS can't pass it. Catches sophisticated
|
|
17
|
+
* attackers who spoofed their UA past layer 2.
|
|
18
|
+
*
|
|
19
|
+
* This file implements layer 1 only. Order: layer 1 first (cheap), then
|
|
20
|
+
* layer 3 (kicks in on the request), then layer 2 (server checks before
|
|
21
|
+
* writing). If any one layer rejects, the event drops.
|
|
22
|
+
*
|
|
23
|
+
* Patterns sourced from the Datafast script tag + npm SDK (publicly
|
|
24
|
+
* inspectable). We include their full list because it represents years
|
|
25
|
+
* of accumulated bot-shape knowledge that's expensive to reproduce.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Returns true if the current browser context looks like a bot or
|
|
29
|
+
* automation tool. Safe to call in SSR (returns true since there's no
|
|
30
|
+
* real visitor in that context anyway — a server-rendered page
|
|
31
|
+
* shouldn't be firing analytics).
|
|
32
|
+
*
|
|
33
|
+
* Wraps every check in try/catch so a hostile environment (frozen
|
|
34
|
+
* navigator, locked-down iframe) can't crash the SDK init. Any throw
|
|
35
|
+
* == "not a bot, let them through" so we err on the side of measuring.
|
|
36
|
+
*/
|
|
37
|
+
export declare function isLikelyBot(): boolean;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side bot detection. Mirrors Datafast / PostHog / Plausible's
|
|
3
|
+
* approach: catch the obvious automation tools at SDK init so they
|
|
4
|
+
* never fire a single network request.
|
|
5
|
+
*
|
|
6
|
+
* Defense in depth:
|
|
7
|
+
* 1. This check — catches Selenium / Puppeteer / curl / etc
|
|
8
|
+
* BEFORE anything hits the wire. Saves bandwidth.
|
|
9
|
+
* Can be bypassed by a determined attacker who
|
|
10
|
+
* edits the bundled SDK.
|
|
11
|
+
* 2. Server UA regex — catches the same patterns plus legit crawlers
|
|
12
|
+
* (Googlebot, FB crawler, etc). Server is
|
|
13
|
+
* authoritative. Can't be bypassed.
|
|
14
|
+
* 3. Cloudflare — invisible browser-attestation challenge. Real
|
|
15
|
+
* Turnstile browsers pass in 50-200ms; scripts that don't
|
|
16
|
+
* run real JS can't pass it. Catches sophisticated
|
|
17
|
+
* attackers who spoofed their UA past layer 2.
|
|
18
|
+
*
|
|
19
|
+
* This file implements layer 1 only. Order: layer 1 first (cheap), then
|
|
20
|
+
* layer 3 (kicks in on the request), then layer 2 (server checks before
|
|
21
|
+
* writing). If any one layer rejects, the event drops.
|
|
22
|
+
*
|
|
23
|
+
* Patterns sourced from the Datafast script tag + npm SDK (publicly
|
|
24
|
+
* inspectable). We include their full list because it represents years
|
|
25
|
+
* of accumulated bot-shape knowledge that's expensive to reproduce.
|
|
26
|
+
*/
|
|
27
|
+
// Selenium/WebDriver/headless flags set on window or navigator. Modern
|
|
28
|
+
// automation frameworks try to hide these but the defaults still leak.
|
|
29
|
+
const AUTOMATION_GLOBALS = [
|
|
30
|
+
'__webdriver_evaluate',
|
|
31
|
+
'__selenium_evaluate',
|
|
32
|
+
'__webdriver_script_function',
|
|
33
|
+
'__webdriver_unwrapped',
|
|
34
|
+
'__fxdriver_evaluate',
|
|
35
|
+
'__driver_evaluate',
|
|
36
|
+
'_Selenium_IDE_Recorder',
|
|
37
|
+
'_selenium',
|
|
38
|
+
'calledSelenium',
|
|
39
|
+
// ChromeDriver's signature global. The dollar-sign-cdc prefix is
|
|
40
|
+
// a known fingerprint that vanilla undetected-chromedriver tries to
|
|
41
|
+
// strip but base ChromeDriver still includes.
|
|
42
|
+
'$cdc_asdjflasutopfhvcZLmcfl_',
|
|
43
|
+
];
|
|
44
|
+
// User-agent substrings (lowercased before match). These cover both the
|
|
45
|
+
// headless browser families AND the tool families (curl/python/etc).
|
|
46
|
+
// Real human UAs do contain things like "Mobile/15E148" so we match on
|
|
47
|
+
// distinctive automation tokens only.
|
|
48
|
+
const BOT_UA_PATTERNS = [
|
|
49
|
+
'headlesschrome',
|
|
50
|
+
'phantomjs',
|
|
51
|
+
'selenium',
|
|
52
|
+
'webdriver',
|
|
53
|
+
'puppeteer',
|
|
54
|
+
'playwright',
|
|
55
|
+
'python',
|
|
56
|
+
'curl',
|
|
57
|
+
'wget',
|
|
58
|
+
'java/',
|
|
59
|
+
'go-http',
|
|
60
|
+
'node.js',
|
|
61
|
+
'axios',
|
|
62
|
+
'postman',
|
|
63
|
+
];
|
|
64
|
+
// documentElement attributes some Selenium variants set. Cheap to check.
|
|
65
|
+
const BOT_DOC_ATTRS = ['webdriver', 'selenium', 'driver'];
|
|
66
|
+
/**
|
|
67
|
+
* Returns true if the current browser context looks like a bot or
|
|
68
|
+
* automation tool. Safe to call in SSR (returns true since there's no
|
|
69
|
+
* real visitor in that context anyway — a server-rendered page
|
|
70
|
+
* shouldn't be firing analytics).
|
|
71
|
+
*
|
|
72
|
+
* Wraps every check in try/catch so a hostile environment (frozen
|
|
73
|
+
* navigator, locked-down iframe) can't crash the SDK init. Any throw
|
|
74
|
+
* == "not a bot, let them through" so we err on the side of measuring.
|
|
75
|
+
*/
|
|
76
|
+
export function isLikelyBot() {
|
|
77
|
+
// SSR or non-browser context. We can't run analytics here anyway
|
|
78
|
+
// because there's no localStorage / sendBeacon / DOM events.
|
|
79
|
+
if (typeof window === 'undefined' || typeof navigator === 'undefined')
|
|
80
|
+
return true;
|
|
81
|
+
try {
|
|
82
|
+
const nav = window.navigator;
|
|
83
|
+
const win = window;
|
|
84
|
+
// Selenium / Puppeteer / Playwright signature on navigator.
|
|
85
|
+
// Modern stealth plugins try to override this but the override
|
|
86
|
+
// is detectable too (the property descriptor looks wrong) — we
|
|
87
|
+
// catch the easy case here and let Turnstile catch the rest.
|
|
88
|
+
if (nav.webdriver === true)
|
|
89
|
+
return true;
|
|
90
|
+
// Legacy headless flags (PhantomJS / Nightmare). Still present in
|
|
91
|
+
// some old scraper toolchains; cheap to check.
|
|
92
|
+
if (win.callPhantom !== undefined)
|
|
93
|
+
return true;
|
|
94
|
+
if (win._phantom !== undefined)
|
|
95
|
+
return true;
|
|
96
|
+
if (win.__nightmare !== undefined)
|
|
97
|
+
return true;
|
|
98
|
+
// Missing / suspicious UA. Real browsers always set a useful UA
|
|
99
|
+
// string; a missing or stub UA strongly implies a HTTP client
|
|
100
|
+
// (curl --user-agent "" / a python script that forgot to set one).
|
|
101
|
+
if (!nav.userAgent
|
|
102
|
+
|| nav.userAgent === ''
|
|
103
|
+
|| nav.userAgent === 'undefined'
|
|
104
|
+
|| nav.userAgent.length < 5) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
const ua = nav.userAgent.toLowerCase();
|
|
108
|
+
// Substring match on the known automation tokens. We split into a
|
|
109
|
+
// single loop instead of one giant regex for readability — the cost
|
|
110
|
+
// difference at <20 patterns is microseconds either way.
|
|
111
|
+
for (const pattern of BOT_UA_PATTERNS) {
|
|
112
|
+
if (ua.includes(pattern))
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
// Selenium globals on window. Some frameworks reset navigator but
|
|
116
|
+
// forget about these.
|
|
117
|
+
for (const key of AUTOMATION_GLOBALS) {
|
|
118
|
+
if (win[key] !== undefined)
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
// documentElement attributes. Cheap belt-and-suspenders check; some
|
|
122
|
+
// Selenium variants annotate the HTML root for their own bookkeeping.
|
|
123
|
+
const docEl = document.documentElement;
|
|
124
|
+
if (docEl) {
|
|
125
|
+
for (const attr of BOT_DOC_ATTRS) {
|
|
126
|
+
if (docEl.getAttribute(attr))
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Defensive: if any of the checks throw (locked-down iframe,
|
|
134
|
+
// proxy-wrapped navigator, etc.), don't block the visitor. Server
|
|
135
|
+
// regex + Turnstile are still in place to catch whatever this is.
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
package/dist/provider.d.ts
CHANGED
|
@@ -18,6 +18,6 @@ export declare function useAffiliateRef(): AffiliateoContextValue;
|
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
20
|
export declare function getAttribution(): Record<string, string>;
|
|
21
|
-
export declare function AffiliateoProvider({ campaignId, apiUrl, children, }: AffiliateoConfig & {
|
|
21
|
+
export declare function AffiliateoProvider({ campaignId, apiUrl, allowedHostnames, children, }: AffiliateoConfig & {
|
|
22
22
|
children: React.ReactNode;
|
|
23
23
|
}): import("react/jsx-runtime").JSX.Element;
|