From ef9e8282639d25c9901c23131f40c0b3120037c6 Mon Sep 17 00:00:00 2001 From: Rishat Valitov Date: Tue, 20 Dec 2022 18:10:23 +0800 Subject: [PATCH 1/2] Updates for pro agent (#108) * Update README.md * Update some signals and add type exports * Remove duplicate signals and fix lint --- README.md | 29 ++++++-------- playground/index.html | 4 +- playground/style.css | 28 +++++++------- src/detectors/document_attributes.ts | 9 ----- src/detectors/document_element_keys.ts | 9 +++++ src/detectors/index.ts | 2 +- src/sources/device_memory.ts | 11 ------ src/sources/document_element_attributes.ts | 8 ---- src/sources/document_element_keys.ts | 12 ++++++ src/sources/hardware_concurrency.ts | 10 ----- src/sources/index.ts | 18 +-------- src/sources/languages.ts | 2 +- src/sources/os_cpu.ts | 9 ----- src/sources/platform.ts | 23 ----------- src/sources/screen_resolution.ts | 21 ----------- src/sources/touch_support.ts | 44 ---------------------- src/sources/vendor.ts | 9 ----- 17 files changed, 52 insertions(+), 196 deletions(-) delete mode 100644 src/detectors/document_attributes.ts create mode 100644 src/detectors/document_element_keys.ts delete mode 100644 src/sources/device_memory.ts delete mode 100644 src/sources/document_element_attributes.ts create mode 100644 src/sources/document_element_keys.ts delete mode 100644 src/sources/hardware_concurrency.ts delete mode 100644 src/sources/os_cpu.ts delete mode 100644 src/sources/platform.ts delete mode 100644 src/sources/screen_resolution.ts delete mode 100644 src/sources/touch_support.ts delete mode 100644 src/sources/vendor.ts diff --git a/README.md b/README.md index e2f52935..31f15738 100644 --- a/README.md +++ b/README.md @@ -24,18 +24,11 @@ ## BotD -```diff -# Before +BotD is an open source library that we created to make it easy for every developer to detect basic bots in their web apps. -- 40% of your website traffic is from bots -- They're taking over accounts, scraping prices and ruining your website reputation +It is available under a permissive MIT license and will always be free for developers and commercial use. -# After - -+ BotD is a browser library for JavaScript bot detection -+ Easily add ability to detect automation tools -+ Requires adding only a few lines of JavaScript on your website -``` +For more demanding applications we created a professional API-based bot detection software that is called [Fingerprint Pro Bot Detection](https://fingerprint.com/products/bot-detection/). [⚡ View Our Demo](https://fingerprintjs.github.io/BotD). @@ -81,15 +74,17 @@ botdPromise 📕 [Full documentation](docs/api.md) -## 🤖 Upgrade to Fingerprint Pro bot detection to detect sophisticated bots easily +## 🤖 Upgrade to Fingerprint Pro Bot Detection to detect sophisticated bots with confidence. **Free for developers (up to 20K API calls / mo), unlimited free trials available, no credit card needed.** -[Fingerprint Pro](https://fingerprint.com/products/bot-detection/) is a professional bot detection service that processes all information server-side and transmits it securely to your servers using server-to-server APIs. +[Fingerprint Pro Bot Detection](https://fingerprint.com/products/bot-detection/) is a professional bot detection service that processes all information server-side and transmits it securely to your servers using server-to-server APIs. -The Pro combines vast amounts of auxiliary data that bots leak (cursor movements, network overrides, browser changes and more) to be able to reliably deduplicate real users from automated software, resulting in the detection of popular automation tools, their derivatives and plugins. +Fingerprint Pro Bot Detection provides both browser and server-side APIs that make the process of bot detection fast and secure. When using the Pro Bot Detection, use the browser JavaScript SDK to obtain the requestID value and then verify this value using our server API. -A big advantage of the Pro is that it's able to distinguish good bots from bad ones that allow you to block malicious traffic without blocking search engine crawlers, monitoring workers, etc. +The Pro product combines vast amounts of auxiliary data that bots leak (cursor movements, network overrides, browser changes and more) to be able to reliably deduplicate real users from automated software, resulting in the detection of popular automation tools, their derivatives and plugins. + +A big advantage of the Pro detection is that it's able to distinguish good bots from bad ones that allow you to block malicious traffic without blocking search engine crawlers, monitoring workers, etc.

@@ -132,7 +127,7 @@ Full product comparison:

Additional Features

Server-side accuracy increase
based on additional server-side data, such as TLS crypto support, ipv4/v6 data and others–✓ - Query API & realtime Webhooks
build flexible workflows–✓ + Query API
build flexible workflows–✓

Operations

Data securityYour infrastructureEncrypted at rest @@ -162,11 +157,9 @@ Pro result example: 🍿 [Live demo](https://fingerprint.com/products/bot-detection/) -⏱ [How to upgrade from Open Source to Pro in 30 seconds](https://dev.fingerprint.com/v3/docs/migrating-from-open-source-v3) - 📕 [Fingerprint Pro documentation](https://dev.fingerprint.com) -## Migrating from v0 +## Migrating from v0 - [Migration guide](docs/migrating_v0_v1.md) - [V0 documentation](https://github.com/fingerprintjs/BotD/tree/v0) diff --git a/playground/index.html b/playground/index.html index 83177502..149060c2 100644 --- a/playground/index.html +++ b/playground/index.html @@ -31,7 +31,9 @@
Detecting...
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Reiciendis, suscipit assumenda dicta, iste, eius possimus sunt asperiores debitis quam voluptatum deleniti obcaecati facere nisi deserunt reprehenderit nobis sapiente explicabo non.
+
+Lorem ipsum dolor sit amet consectetur adipisicing elit. Reiciendis, suscipit assumenda dicta, iste, eius possimus sunt asperiores debitis quam voluptatum deleniti obcaecati facere nisi deserunt reprehenderit nobis sapiente explicabo non.
diff --git a/playground/style.css b/playground/style.css index 94f3dec0..384232a4 100644 --- a/playground/style.css +++ b/playground/style.css @@ -3,10 +3,9 @@ --accent-dark-color: rgb(140, 39, 3); --error-color: #b3261e; --font-family: 'Fira Mono', monospace; - --font-family-consolas: Consolas, "Liberation Mono", Menlo, Courier, monospace; + --font-family-consolas: Consolas, 'Liberation Mono', Menlo, Courier, monospace; } - body { width: 100%; height: 100%; @@ -27,7 +26,6 @@ body { box-sizing: border-box; } - .container { width: calc(100% - 128px); max-width: 1536px; @@ -90,7 +88,7 @@ body { margin-top: 32px; } - #navbar .logo { + #navbar .logo { margin-top: 0; width: 256px; } @@ -98,7 +96,7 @@ body { #navbar #navbar-chevron { transform: rotate(90deg); } - + #navbar #playground-actions { margin-left: 0; margin: 32px 0px; @@ -139,14 +137,15 @@ h2 { transition: 0.15s; } -.github-card:active, .github-card:focus { +.github-card:active, +.github-card:focus { background-color: rgba(0, 0, 0, 0.12); transition: 0.15s; } .github-card::before { display: block; - content: ""; + content: ''; width: 32px; height: 32px; background-image: url(../resources/github_icon.svg); @@ -177,7 +176,6 @@ h2 { font-weight: 600; } - .orange-button { background-color: var(--accent-color); color: #fff; @@ -190,11 +188,15 @@ h2 { background-color: transparent; } -.orange-button:hover, .orange-button:active, .orange-button:focus { +.orange-button:hover, +.orange-button:active, +.orange-button:focus { background-color: var(--accent-dark-color); } -.orange-button-outlined:hover, .orange-button-outlined:active, .orange-button-outlined:focus { +.orange-button-outlined:hover, +.orange-button-outlined:active, +.orange-button-outlined:focus { background-color: rgba(240, 68, 5, 0.12); } @@ -208,7 +210,6 @@ h2 { padding: var(--content-card-padding); } - #result { overflow: hidden; margin: 96px auto; @@ -219,7 +220,6 @@ h2 { --bot-icon-url: url(../resources/robot-off-outline.svg); } - .result-detected { --bot-icon-color: #f44336 !important; --bot-icon-url: url(../resources/robot-outline.svg) !important; @@ -230,7 +230,6 @@ h2 { --bot-icon-url: url(../resources/alert-circle-outline.svg) !important; } - .result-bot-icon-container { width: 128px; height: 128px; @@ -241,7 +240,7 @@ h2 { } .result-bot-icon-container::before { - content: ""; + content: ''; display: block; background-color: var(--bot-icon-color); opacity: 0.08; @@ -276,7 +275,6 @@ h2 { transition: 0.2s color; } - .logs-content { background-color: #282c34; color: #fff; diff --git a/src/detectors/document_attributes.ts b/src/detectors/document_attributes.ts deleted file mode 100644 index 67f43a20..00000000 --- a/src/detectors/document_attributes.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { BotKind, ComponentDict, DetectorResponse, State } from '../types' -import { includes } from '../utils/misc' - -export function detectDocumentAttributes({ documentAttributes }: ComponentDict): DetectorResponse { - if (documentAttributes.state !== State.Success) return false - if (includes(documentAttributes.value, 'selenium', 'webdriver', 'driver')) { - return BotKind.Selenium - } -} diff --git a/src/detectors/document_element_keys.ts b/src/detectors/document_element_keys.ts new file mode 100644 index 00000000..a6d091fa --- /dev/null +++ b/src/detectors/document_element_keys.ts @@ -0,0 +1,9 @@ +import { BotKind, ComponentDict, DetectorResponse, State } from '../types' +import { includes } from '../utils/misc' + +export function detectDocumentAttributes({ documentElementKeys }: ComponentDict): DetectorResponse { + if (documentElementKeys.state !== State.Success) return false + if (includes(documentElementKeys.value, 'selenium', 'webdriver', 'driver')) { + return BotKind.Selenium + } +} diff --git a/src/detectors/index.ts b/src/detectors/index.ts index cfab6ae0..c37b03bc 100644 --- a/src/detectors/index.ts +++ b/src/detectors/index.ts @@ -1,5 +1,5 @@ import { detectAppVersion } from './app_version' -import { detectDocumentAttributes } from './document_attributes' +import { detectDocumentAttributes } from './document_element_keys' import { detectDocumentProperties } from './document_properties' import { detectErrorTrace } from './error_trace' import { detectEvalLengthInconsistency } from './eval_length' diff --git a/src/sources/device_memory.ts b/src/sources/device_memory.ts deleted file mode 100644 index f9638981..00000000 --- a/src/sources/device_memory.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BotdError, State } from '../types' -import { replaceNaN, toFloat } from '../utils/data' - -export default function getDeviceMemory(): number { - // `navigator.deviceMemory` is a string containing a number in some unidentified cases - const deviceMemory = replaceNaN(toFloat(navigator.deviceMemory), undefined) - if (deviceMemory == undefined) { - throw new BotdError(State.Undefined, 'navigator.osdeviceMemorycpu is undefined') - } - return deviceMemory -} diff --git a/src/sources/document_element_attributes.ts b/src/sources/document_element_attributes.ts deleted file mode 100644 index ad2aeddf..00000000 --- a/src/sources/document_element_attributes.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { BotdError, State } from '../types' - -export default function getDocumentAttributes(): string[] { - if (document.documentElement === undefined) { - throw new BotdError(State.Undefined, 'document.documentElement is undefined') - } - return Array.from(document.documentElement.attributes).map((r) => r.name) -} diff --git a/src/sources/document_element_keys.ts b/src/sources/document_element_keys.ts new file mode 100644 index 00000000..33fe491c --- /dev/null +++ b/src/sources/document_element_keys.ts @@ -0,0 +1,12 @@ +import { BotdError, State } from '../types' + +export default function getDocumentElementKeys(): string[] { + if (document.documentElement === undefined) { + throw new BotdError(State.Undefined, 'document.documentElement is undefined') + } + const { documentElement } = document + if (typeof documentElement.getAttributeNames !== 'function') { + throw new BotdError(State.NotFunction, 'document.documentElement.getAttributeNames is not a function') + } + return documentElement.getAttributeNames() +} diff --git a/src/sources/hardware_concurrency.ts b/src/sources/hardware_concurrency.ts deleted file mode 100644 index fb2c9abe..00000000 --- a/src/sources/hardware_concurrency.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BotdError, State } from '../types' -import { replaceNaN, toInt } from '../utils/data' - -export default function getHardwareConcurrency(): number | undefined { - const hardwareConcurrency = replaceNaN(toInt(navigator.hardwareConcurrency), undefined) - if (hardwareConcurrency == undefined) { - throw new BotdError(State.Undefined, 'navigator.hardwareConcurrency is undefined') - } - return hardwareConcurrency -} diff --git a/src/sources/index.ts b/src/sources/index.ts index 35547ae5..bca0df5b 100644 --- a/src/sources/index.ts +++ b/src/sources/index.ts @@ -1,25 +1,18 @@ import getAppVersion from './app_version' -import getDeviceMemory from './device_memory' -import getDocumentAttributes from './document_element_attributes' +import getDocumentElementKeys from './document_element_keys' import getDocumentProperties from './document_properties' import getErrorTrace from './error_trace' import getEvalLength from './eval_length' import getFunctionBind from './function_bind' -import getHardwareConcurrency from './hardware_concurrency' import getLanguages from './languages' import areMimeTypesConsistent from './mime_types_consistence' import getNotificationPermissions from './notification_permissions' -import getOsCpu from './os_cpu' -import getPlatform from './platform' import getPluginsArray from './plugins_array' import getPluginsLength from './plugins_length' import getProcess, { ProcessPayload } from './process' import getProductSub from './product_sub' import getRTT from './rtt' -import getScreenResolution from './screen_resolution' -import getTouchSupport from './touch_support' import getUserAgent from './user_agent' -import getVendor from './vendor' import getWebDriver from './webdriver' import getWebGL from './webgl' import getWindowExternal from './window_external' @@ -42,18 +35,11 @@ export const sources = { webDriver: getWebDriver, languages: getLanguages, notificationPermissions: getNotificationPermissions, - documentAttributes: getDocumentAttributes, + documentElementKeys: getDocumentElementKeys, functionBind: getFunctionBind, process: getProcess, documentProps: getDocumentProperties, windowProps: getWindowProperties, - osCpu: getOsCpu, - deviceMemory: getDeviceMemory, - screenResolution: getScreenResolution, - hardwareConcurrency: getHardwareConcurrency, - platform: getPlatform, - touchSupport: getTouchSupport, - vendor: getVendor, } export { WindowSizePayload, ProcessPayload } diff --git a/src/sources/languages.ts b/src/sources/languages.ts index ab2b8836..7dca2be4 100644 --- a/src/sources/languages.ts +++ b/src/sources/languages.ts @@ -13,7 +13,7 @@ export default function getLanguages(): string[][] { if (Array.isArray(n.languages)) { const browserEngine = getBrowserEngineKind() // Starting from Chromium 86, there is only a single value in `navigator.language` in Incognito mode: - // the value of `navigator.language`. Therefore the value is ignored in this browser. + // the value of `navigator.language`. Therefore, the value is ignored in this browser. if (!(browserEngine === BrowserEngineKind.Chromium && isChromium86OrNewer())) { result.push(n.languages) } diff --git a/src/sources/os_cpu.ts b/src/sources/os_cpu.ts deleted file mode 100644 index 41fc251d..00000000 --- a/src/sources/os_cpu.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { BotdError, State } from '../types' - -export default function getOsCpu(): string { - const oscpu = navigator.oscpu - if (oscpu == undefined) { - throw new BotdError(State.Undefined, 'navigator.oscpu is undefined') - } - return oscpu -} diff --git a/src/sources/platform.ts b/src/sources/platform.ts deleted file mode 100644 index 59133a73..00000000 --- a/src/sources/platform.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { BotdError, BrowserEngineKind, State } from '../types' -import { getBrowserEngineKind, isDesktopSafari, isIPad } from '../utils/browser' - -export default function getPlatform(): string { - // Android Chrome 86 and 87 and Android Firefox 80 and 84 don't mock the platform value when desktop mode is requested - const platform = navigator.platform - if (platform == undefined) { - throw new BotdError(State.Undefined, 'navigator.platform is undefined') - } - - // iOS mocks the platform value when desktop version is requested: https://github.com/fingerprintjs/fingerprintjs/issues/514 - // iPad uses desktop mode by default since iOS 13 - // The value is 'MacIntel' on M1 Macs - // The value is 'iPhone' on iPod Touch - if (platform === 'MacIntel') { - const browserEngine = getBrowserEngineKind() - if (browserEngine === BrowserEngineKind.Webkit && !isDesktopSafari()) { - return isIPad() ? 'iPad' : 'iPhone' - } - } - - return platform -} diff --git a/src/sources/screen_resolution.ts b/src/sources/screen_resolution.ts deleted file mode 100644 index 3abd2887..00000000 --- a/src/sources/screen_resolution.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { replaceNaN, toInt } from '../utils/data' - -export interface ScreenResolutionPayload { - width?: number - height?: number -} - -export default function getScreenResolution(): ScreenResolutionPayload { - const s = screen - - // Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. - // I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. - // Some browsers even return screen resolution as not numbers. - const parseDimension = (value: unknown) => replaceNaN(toInt(value), undefined) - const [height, width] = [parseDimension(s.width), parseDimension(s.height)].sort() - - return { - width, - height, - } -} diff --git a/src/sources/touch_support.ts b/src/sources/touch_support.ts deleted file mode 100644 index 3a245155..00000000 --- a/src/sources/touch_support.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { toInt } from '../utils/data' - -export interface TouchSupportPayload { - maxTouchPoints: number - /** The success or failure of creating a TouchEvent */ - touchEvent: boolean - /** The availability of the "ontouchstart" property */ - touchStart: boolean -} - -/** - * This is a crude and primitive touch screen detection. It's not possible to currently reliably detect the availability - * of a touch screen with a JS, without actually subscribing to a touch event. - * - * @see http://www.stucox.com/blog/you-cant-detect-a-touchscreen/ - * @see https://github.com/Modernizr/Modernizr/issues/548 - */ -export default function getTouchSupport(): TouchSupportPayload { - const n = navigator - - let maxTouchPoints = 0 - let touchEvent: boolean - - if (n.maxTouchPoints !== undefined) { - maxTouchPoints = toInt(n.maxTouchPoints) - } else if (n.msMaxTouchPoints !== undefined) { - maxTouchPoints = n.msMaxTouchPoints - } - - try { - document.createEvent('TouchEvent') - touchEvent = true - } catch { - touchEvent = false - } - - const touchStart = 'ontouchstart' in window - - return { - maxTouchPoints, - touchEvent, - touchStart, - } -} diff --git a/src/sources/vendor.ts b/src/sources/vendor.ts deleted file mode 100644 index d7506f2f..00000000 --- a/src/sources/vendor.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { BotdError, State } from '../types' - -export default function getVendor(): string { - const vendor = navigator.vendor - if (vendor == undefined) { - throw new BotdError(State.Undefined, 'navigator.vendor is undefined') - } - return vendor -} From e2606ad0804aed7af3f8df0fd73c469f3320a88b Mon Sep 17 00:00:00 2001 From: xnerhu Date: Wed, 21 Dec 2022 09:08:36 +0100 Subject: [PATCH 2/2] bump version to 1.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 20ae87b6..65307793 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fingerprintjs/botd", - "version": "1.2.0", + "version": "1.3.0", "description": "botd is a browser library for JavaScript bot detection", "keywords": [ "bot",