is-antibot 1.3.4 → 1.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +53 -56
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "is-antibot",
3
3
  "description": "Identify if a response is an antibot challenge from CloudFlare, Akamai, DataDome, Vercel, PerimeterX, Shape Security, and more, including CAPTCHA providers like reCAPTCHA and hCaptcha.",
4
4
  "homepage": "https://github.com/microlinkhq/is-antibot",
5
- "version": "1.3.4",
5
+ "version": "1.3.6",
6
6
  "exports": {
7
7
  ".": "./src/index.js"
8
8
  },
package/src/index.js CHANGED
@@ -66,7 +66,7 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
66
66
  }
67
67
 
68
68
  // Akamai: Check for additional identifying headers (akamai-grn, x-akamai-session-info)
69
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/akamai.json
69
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-akamai.json
70
70
  if (getHeader('akamai-grn') || getHeader('x-akamai-session-info')) {
71
71
  return createResult(true, 'akamai')
72
72
  }
@@ -88,7 +88,7 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
88
88
  }
89
89
 
90
90
  // DataDome: Check for x-datadome or x-datadome-cid header presence
91
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/datadome.json
91
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-datadome.json
92
92
  if (getHeader('x-datadome') || getHeader('x-datadome-cid')) {
93
93
  return createResult(true, 'datadome')
94
94
  }
@@ -99,13 +99,13 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
99
99
  }
100
100
 
101
101
  // PerimeterX: Check for X-PX-Authorization header (primary indicator)
102
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/perimeterx.json#L71-L84
102
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-perimeterx.json
103
103
  if (getHeader('x-px-authorization')) {
104
104
  return createResult(true, 'perimeterx')
105
105
  }
106
106
 
107
107
  // PerimeterX: Check for window._pxAppId, pxInit, or _pxAction in html
108
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/perimeterx.json#L130-L137
108
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-perimeterx.json
109
109
  if (htmlHas('window._pxAppId') || htmlHas('pxInit') || htmlHas('_pxAction')) {
110
110
  return createResult(true, 'perimeterx')
111
111
  }
@@ -117,7 +117,7 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
117
117
 
118
118
  // Shape Security: Check for dynamic header patterns x-[8chars]-[abcdfz]
119
119
  // These headers use 8 random characters followed by suffixes like -a, -b, -c, -d, -f, or -z
120
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/shapesecurity.json#L30-L113
120
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-shapesecurity.json
121
121
  const headerNames = Object.keys(headers)
122
122
  for (const name of headerNames) {
123
123
  if (/^x-[a-z0-9]{8}-[abcdfz]$/i.test(name)) {
@@ -126,37 +126,37 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
126
126
  }
127
127
 
128
128
  // Shape Security: Check for 'shapesecurity' text in response html
129
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/shapesecurity.json#L136-L142
129
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-shapesecurity.json
130
130
  if (htmlHas('shapesecurity')) {
131
131
  return createResult(true, 'shapesecurity')
132
132
  }
133
133
 
134
134
  // Kasada: Check for x-kasada or x-kasada-challenge headers
135
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/kasada.json#L57-L85
135
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-kasada.json
136
136
  if (getHeader('x-kasada') || getHeader('x-kasada-challenge')) {
137
137
  return createResult(true, 'kasada')
138
138
  }
139
139
 
140
140
  // Kasada: Check for __kasada global object or kasada.js script in html
141
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/kasada.json#L117-L144
141
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-kasada.json
142
142
  if (htmlHas('__kasada') || htmlHas('kasada.js')) {
143
143
  return createResult(true, 'kasada')
144
144
  }
145
145
 
146
146
  // Imperva/Incapsula: Check for x-cdn header with 'Incapsula' value or x-iinfo header
147
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/incapsula.json#L86-L109
147
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-incapsula.json
148
148
  if (getHeader('x-cdn') === 'Incapsula' || getHeader('x-iinfo')) {
149
149
  return createResult(true, 'imperva')
150
150
  }
151
151
 
152
152
  // Imperva/Incapsula: Check for 'incapsula' or 'imperva' text in response html
153
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/incapsula.json#L111-L124
153
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-incapsula.json
154
154
  if (htmlHas('incapsula') || htmlHas('imperva')) {
155
155
  return createResult(true, 'imperva')
156
156
  }
157
157
 
158
158
  // Imperva/Incapsula: incap_ses_, visid_incap_, or reese84 cookies
159
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/incapsula.json
159
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-incapsula.json
160
160
  if (
161
161
  hasCookie('incap_ses_') ||
162
162
  hasCookie('visid_incap_') ||
@@ -166,7 +166,7 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
166
166
  }
167
167
 
168
168
  // Reblaze: rbzid or rbzsessionid cookies
169
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/reblaze.json
169
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-reblaze.json
170
170
  if (hasCookie('rbzid=') || hasCookie('rbzsessionid=')) {
171
171
  return createResult(true, 'reblaze')
172
172
  }
@@ -177,7 +177,7 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
177
177
  }
178
178
 
179
179
  // Cheq: Check for CheqSdk or cheqzone.com in html
180
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/cheq.json
180
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-cheq.json
181
181
  if (htmlHas('CheqSdk') || htmlHas('cheqzone.com')) {
182
182
  return createResult(true, 'cheq')
183
183
  }
@@ -188,13 +188,13 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
188
188
  }
189
189
 
190
190
  // Sucuri: Check for 'sucuri' text in response html
191
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/sucuri.json
191
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-sucuri.json
192
192
  if (htmlHas('sucuri')) {
193
193
  return createResult(true, 'sucuri')
194
194
  }
195
195
 
196
196
  // ThreatMetrix: Check for 'ThreatMetrix' in html
197
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/threatmetrix.json
197
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-threatmetrix.json
198
198
  if (htmlHas('ThreatMetrix')) {
199
199
  return createResult(true, 'threatmetrix')
200
200
  }
@@ -205,7 +205,7 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
205
205
  }
206
206
 
207
207
  // Meetrics: Check for 'meetrics' text in response html
208
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/meetrics.json
208
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-meetrics.json
209
209
  if (htmlHas('meetrics')) {
210
210
  return createResult(true, 'meetrics')
211
211
  }
@@ -216,7 +216,7 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
216
216
  }
217
217
 
218
218
  // Ocule: Check for ocule.co.uk in html
219
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/ocule.json
219
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-ocule.json
220
220
  if (htmlHas('ocule.co.uk')) {
221
221
  return createResult(true, 'ocule')
222
222
  }
@@ -227,7 +227,7 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
227
227
  }
228
228
 
229
229
  // reCAPTCHA: Check for recaptcha/api, google.com/recaptcha, gstatic.com/recaptcha, or recaptcha.net in URL
230
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/recaptcha.json#L13-L48
230
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/detect-recaptcha.json
231
231
  if (
232
232
  urlHas('recaptcha/api') ||
233
233
  urlHas('google\\.com/recaptcha', true) ||
@@ -237,83 +237,80 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
237
237
  return createResult(true, 'recaptcha')
238
238
  }
239
239
 
240
- // reCAPTCHA: Check for grecaptcha global object in html (primary JavaScript indicator)
241
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/recaptcha.json#L51-L58
242
- if (htmlHas('grecaptcha')) {
240
+ // reCAPTCHA: Check for grecaptcha API usage in html (JavaScript indicator)
241
+ // Note: plain "grecaptcha" is too broad (e.g. ".grecaptcha-badge" CSS appears on normal YouTube pages)
242
+ if (
243
+ htmlHas(
244
+ '\\b(?:window\\.)?grecaptcha\\s*\\.(?:execute|render|ready|getResponse|enterprise)\\b',
245
+ true
246
+ ) ||
247
+ htmlHas('\\b(?:window\\.)?grecaptcha\\s*\\(', true) ||
248
+ htmlHas('\\b__grecaptcha_cfg\\b', true)
249
+ ) {
243
250
  return createResult(true, 'recaptcha')
244
251
  }
245
252
 
246
253
  // reCAPTCHA: Check for g-recaptcha container class in html
247
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/recaptcha.json#L66-L73
254
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/detect-recaptcha.json
248
255
  if (htmlHas('g-recaptcha')) {
249
256
  return createResult(true, 'recaptcha')
250
257
  }
251
258
 
252
259
  // hCaptcha: Check for hcaptcha.com domain in URL
253
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/hcaptcha.json#L13-L22
260
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/detect-hcaptcha.json
254
261
  if (urlHas('hcaptcha\\.com', true)) {
255
262
  return createResult(true, 'hcaptcha')
256
263
  }
257
264
 
258
- // hCaptcha: Check for hcaptcha object in html
259
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/hcaptcha.json#L42-L50
260
- if (htmlHas('hcaptcha')) {
261
- return createResult(true, 'hcaptcha')
262
- }
263
-
264
- // hCaptcha: Check for h-captcha container class in html
265
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/hcaptcha.json#L51-L58
266
- if (htmlHas('h-captcha')) {
265
+ // hCaptcha: Check for hcaptcha.com API domain or h-captcha container class in html
266
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/detect-hcaptcha.json
267
+ // Note: bare 'hcaptcha' matches too broadly (could appear in articles discussing hCaptcha)
268
+ if (htmlHas('hcaptcha.com') || htmlHas('h-captcha')) {
267
269
  return createResult(true, 'hcaptcha')
268
270
  }
269
271
 
270
272
  // FunCaptcha (Arkose Labs): Check for arkoselabs.com or funcaptcha in URL
271
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/funcaptcha.json#L13-L40
273
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/detect-funcaptcha.json
272
274
  if (urlHas('arkoselabs\\.com', true) || urlHas('funcaptcha')) {
273
275
  return createResult(true, 'funcaptcha')
274
276
  }
275
277
 
276
- // FunCaptcha (Arkose Labs): Check for funcaptcha or arkose text in html
277
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/funcaptcha.json#L42-L55
278
- if (htmlHas('funcaptcha') || htmlHas('arkose')) {
278
+ // FunCaptcha (Arkose Labs): Check for arkoselabs.com API domain or funcaptcha in html
279
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/detect-funcaptcha.json
280
+ // Note: bare 'arkose' matches too broadly (e.g. Facebook bundles Arkose SDK for login without blocking content)
281
+ if (htmlHas('arkoselabs.com') || htmlHas('funcaptcha')) {
279
282
  return createResult(true, 'funcaptcha')
280
283
  }
281
284
 
282
285
  // GeeTest: Check for geetest.com domain in URL
283
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/geetest.json#L13-L43
286
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/detect-geetest.json
284
287
  if (urlHas('geetest\\.com', true)) {
285
288
  return createResult(true, 'geetest')
286
289
  }
287
290
 
288
291
  // GeeTest: Check for geetest object or text in html
289
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/geetest.json#L45-L52
292
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/detect-geetest.json
293
+ // Note: bare 'gt.js' removed (too generic, any script named gt.js would match)
290
294
  if (htmlHas('geetest')) {
291
295
  return createResult(true, 'geetest')
292
296
  }
293
297
 
294
- // GeeTest: Check for gt.js script in html
295
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/geetest.json#L53-L60
296
- if (htmlHas('gt.js')) {
297
- return createResult(true, 'geetest')
298
- }
299
-
300
298
  // Cloudflare Turnstile: Check for challenges.cloudflare.com/turnstile in URL
301
299
  if (urlHas('challenges\\.cloudflare\\.com/turnstile', true)) {
302
300
  return createResult(true, 'cloudflare-turnstile')
303
301
  }
304
302
 
305
- // Cloudflare Turnstile: Check for cf-turnstile class in html
306
- if (htmlHas('cf-turnstile')) {
307
- return createResult(true, 'cloudflare-turnstile')
308
- }
309
-
310
- // Cloudflare Turnstile: Check for turnstile text in html
311
- if (htmlHas('turnstile')) {
303
+ // Cloudflare Turnstile: Check for cf-turnstile class or turnstile API script in html
304
+ // Note: bare 'turnstile' matches too broadly (common English word)
305
+ if (
306
+ htmlHas('cf-turnstile') ||
307
+ htmlHas('challenges.cloudflare.com/turnstile')
308
+ ) {
312
309
  return createResult(true, 'cloudflare-turnstile')
313
310
  }
314
311
 
315
312
  // Friendly Captcha: Check for friendlycaptcha.com in URL
316
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/friendlycaptcha.json
313
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/detect-friendlycaptcha.json
317
314
  if (urlHas('friendlycaptcha\\.com', true)) {
318
315
  return createResult(true, 'friendly-captcha')
319
316
  }
@@ -324,7 +321,7 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
324
321
  }
325
322
 
326
323
  // Captcha.eu: Check for captcha.eu in URL
327
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/captchaeu.json
324
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/detect-captchaeu.json
328
325
  if (urlHas('captcha\\.eu', true)) {
329
326
  return createResult(true, 'captcha-eu')
330
327
  }
@@ -335,7 +332,7 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
335
332
  }
336
333
 
337
334
  // QCloud Captcha (Tencent): Check for turing.captcha.qcloud.com in URL
338
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/qcloud.json
335
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/detect-qcloud.json
339
336
  if (urlHas('turing\\.captcha\\.qcloud\\.com', true)) {
340
337
  return createResult(true, 'qcloud-captcha')
341
338
  }
@@ -346,7 +343,7 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
346
343
  }
347
344
 
348
345
  // AliExpress CAPTCHA: Check for punish?x5secdata in URL
349
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/aliexpress.json
346
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/captcha/detect-aliexpress.json
350
347
  if (urlHas('punish\\?x5secdata', true)) {
351
348
  return createResult(true, 'aliexpress-captcha')
352
349
  }
@@ -368,7 +365,7 @@ const detect = ({ headers = {}, html = '', url = '' } = {}) => {
368
365
  }
369
366
 
370
367
  // AWS WAF: Check for x-amzn-waf-action or x-amzn-requestid headers
371
- // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/aws-waf.json
368
+ // Reference: https://github.com/scrapfly/Antibot-Detector/blob/main/detectors/antibot/detect-aws-waf.json
372
369
  if (getHeader('x-amzn-waf-action') || getHeader('x-amzn-requestid')) {
373
370
  return createResult(true, 'aws-waf')
374
371
  }