Code Monkey home page Code Monkey logo

botd's Introduction

FingerprintJS logo

Build status Current NPM version Monthly downloads from NPM Monthly downloads from jsDelivr

Discord server

FingerprintJS is a source-available, client-side, browser fingerprinting library that queries browser attributes and computes a hashed visitor identifier from them. Unlike cookies and local storage, a fingerprint stays the same in incognito/private mode and even when browser data is purged.

License

Starting version 4.0.0, FingerprintJS is licensed under Business Source License 1.1. The BSL allows use only for non-production purposes. You can learn more details in our announcement.

Use Case Is a commercial license required?
Exploring FingerprintJS for your own research, hobbies, and testing purposes No
Using FingerprintJS to build a proof-of-concept application No
Using FingerprintJS to build revenue-generating applications Yes
Using FingerprintJS to build software that is provided as a service (SaaS) Yes
Forking FingerprintJS for any production purposes Yes

To purchase a license for uses not authorized by BSL, please contact us at [email protected].

Demo

Visit https://fingerprintjs.github.io/fingerprintjs to know your visitor identifier.

Now, try visiting the same page in private / incognito mode and notice how the visitor identifier remains the same!

Getting Started

<script>
  // Initialize the agent at application startup.
  // If you're using an ad blocker or Brave/Firefox, this import will not work.
  // Please use the NPM package instead: https://t.ly/ORyXk
  const fpPromise = import('https://openfpcdn.io/fingerprintjs/v4')
    .then(FingerprintJS => FingerprintJS.load())

  // Get the visitor identifier when you need it.
  fpPromise
    .then(fp => fp.get())
    .then(result => {
      // This is the visitor identifier:
      const visitorId = result.visitorId
      console.log(visitorId)
    })
</script>

Run this code

Resources

๐Ÿ“• API Reference

โš›๏ธ Sample usage with React on the StackBlitz platform

Limitations

Accuracy

Since FingerprintJS processes and generates the fingerprints from within the browser itself, the accuracy is limited (40% - 60%). For example, when 2 different users send requests using identical (i.e. same version, same vendor, same platform), browsers, FingerprintJS will not be able to tell these two browsers apart, primarily because the attribitutes from these browsers will be identical.

Security

Because of how the fingerprints are processed and generated from within the browser itself, they are vulnerable to spoofing and reverse engineering.

Get 99.5% accuracy

Fingerprint Identification is a closed-source, commercial device identification product designed for fraud detection, device identification, marketing attribution, and analytics. This product is an enhanced version of FingerprintJS and has been fully re-designed to solve the most challenging identification use cases. Its source is not available in this or any other public repository.

Fingerprint Identification is able to achieve 99.5% accuracy, because it processes the browser attributes on the server and also analyzes vast amounts of auxiliary data (e.g. IP addresses, time of visit patterns, URL changes, etc.). Because of these advanced matching techniques, Fingerprint Identification is able to reliably deduplicate different users that have identical devices. For a comprehensive list of advantages over FingerprintJS, please visit Fingerprint Identification vs. FingerprintJS.

Fingerprint Identification is available for Web, Android, iOS, and other platforms. Our plans start at $99 per month and include with them 20K API calls. You can easily get started by signing up for a free, no-obligation 14-day trial.

Resources

๐Ÿฟ Fingerprint Identification live demo

โ–ถ๏ธ Video: Use Fingerprint Identification to prevent multiple signups by same user

๐Ÿ—‚๏ธ Sample responses for the different Fingerprint Identification plans

โฑ๏ธ How to upgrade from FingerprintJS to Fingerprint Identification in 30 seconds

๐Ÿ“• Fingerprint Identification documentation

Migrating to v4

Migrating from Migration Guide Documentation
v3 Migrating from v3 to v4 v3 documentation
v2 Migrating from v2 to v4 v2 documentation
v1 Migrating from v1 to v4 v1 documentation

Version policy

See the compatibility policy for the API and visitor identifiers in the version policy guide.

Supported browsers

The library supports all popular browsers. See more details and learn how to run the library in old browsers in the browser support guide.

Where to get support

Using Issues and Discussions publicly will help the open-source community and other users with similar issues. However, if you require private support, please email us at [email protected].

Contributing

See the contribution guidelines to learn how to start a playground, test, and build.

botd's People

Contributors

antonymott avatar bayotop avatar david-dick avatar dependabot[bot] avatar dj-stormtrooper avatar finesse avatar fpkamp avatar le0developer avatar makma avatar mgreensmith avatar penyaev avatar r-valitov avatar romchela avatar tarunama avatar valve avatar xnerhu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

botd's Issues

Improve server API docs

  1. Explain why we need to call the server API in one sentence (it's a server-to-server, secure way of getting the botd results back).
  2. Get rid of the the POST /detect section entirely (but copy the response format to GET /results), because users will not be using that.
  3. Explain that you need to detect client-side first, get requestID and then get results with GET /results on the server second, passing the requestID
  4. Make the server API docs read like a book from top to bottom so that a developer is able to quickly solve their server use case w/out having to jump in the document (make most important information to be at the top, not at the bottom).
    /cc @makma

False Positive when Firefox developer tools enabled

image
Logs
{"isError":false,"collectionTime":12,"detectionTime":13,"detectionResult":{"bot":true,"botKind":"unknown"},"detectedBot":"unknown","collectedData":{"rtt":{"state":-1,"error":"BotdError: navigator.connection is undefined"},"process":{"state":-1,"error":"BotdError: window.process is undefined"},"android":{"value":false,"state":0},"browserKind":{"value":"safari","state":0},"browserEngineKind":{"value":"gecko","state":0},"documentFocus":{"value":false,"state":0},"userAgent":{"value":"Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-G973U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/14.2 Chrome/87.0.4280.141 Mobile Safari/537.36","state":0},"appVersion":{"value":"5.0 (Macintosh)","state":0},"windowSize":{"value":{"outerWidth":412,"outerHeight":915,"innerWidth":412,"innerHeight":915},"state":0},"pluginsLength":{"value":5,"state":0},"pluginsArray":{"value":true,"state":0},"errorTrace":{"value":"errorTrace@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:28945\ny/</</</</<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:22812\no/c/</<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21466\no/c/<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21571\ne/<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20487\ne@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20232\ny/</</<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:22649\ny/</<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:22625\no/c/</<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21466\no/c/<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21571\ne/<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20487\ne@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20232\ny@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:22481\nn.prototype.collect/D</<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:34172\no/c/</<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21466\no/c/<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21571\ne/<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20487\ne@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20232\nn.prototype.collect@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:34066\nY/</<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:34646\no/c/</<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21466\no/c/<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21571\ne/<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20487\ne@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20232\nY@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:34329\ntn/</</</<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:35274\no/c/</<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21466\no/c/<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21571\ne/<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20487\ne@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20232\ntn/</<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:35113\no/c/</<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21466\no/c/<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21571\ne/<@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20487\ne@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20232\ntn@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:35013\nwindow.onload@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:37547\nEventHandlerNonNull*@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:37231\n@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:37554\n@https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:37558\n","state":0},"productSub":{"value":"20100101","state":0},"windowExternal":{"value":"[object External]","state":0},"mimeTypesConsistent":{"value":true,"state":0},"evalLength":{"value":37,"state":0},"webGL":{"value":{"vendor":"Mozilla","renderer":"Apple M1"},"state":0},"webDriver":{"value":false,"state":0},"languages":{"value":[["ko-KR"],["ko-KR","ko","en-US","en"]],"state":0},"documentElementKeys":{"value":["lang"],"state":0},"functionBind":{"value":"function bind() {\n    [native code]\n}","state":0},"distinctiveProps":{"value":{"awesomium":false,"cef":false,"cefsharp":false,"coachjs":false,"fminer":false,"geb":false,"nightmarejs":false,"phantomas":false,"phantomjs":false,"rhino":false,"selenium":false,"webdriverio":false,"webdriver":false,"headless_chrome":false},"state":0},"notificationPermissions":{"value":false,"state":0}},"detectorsResults":{"detectAppVersion":{"bot":false},"detectDocumentAttributes":{"bot":false},"detectErrorTrace":{"bot":false},"detectEvalLengthInconsistency":{"bot":false},"detectFunctionBind":{"bot":false},"detectLanguagesLengthInconsistency":{"bot":false},"detectNotificationPermissions":{"bot":false},"detectPluginsArray":{"bot":false},"detectPluginsLengthInconsistency":{"bot":false},"detectProcess":{"bot":false},"detectUserAgent":{"bot":false},"detectWebDriver":{"bot":false},"detectWebGL":{"bot":false},"detectWindowExternal":{"bot":false},"detectWindowSize":{"bot":false},"detectMimeTypesConsistent":{"bot":false},"detectProductSub":{"bot":true,"botKind":"unknown"},"detectDistinctiveProperties":{"bot":false}},"debugData":{"browserEngineKind":"gecko","browserKind":"safari","documentFocus":false,"mozAppearanceSupport":true,"isAndroid":false,"isDesktopWebKit":false}}

System: Apple M1 Pro, macOS Sonoma 14.1.1
Browser: Firefox Browser 120.0.1 (64-bit)

When Firefox developer tools and Responsive Design Mode enabled, Botd detect it as a bot.

False positive when browser internal pdf is enabled

In Edge and Chrome if the setting "Open PDF in Chrome/Edge" is enabled (Settings > Privacy and securityย > Site settingsย > Additional content settingsย > PDF documents.), then the value navigator.plugins is empty which evalute the bot test to true.

Always detects 'selenium'

Hey. I've noticed, that "automationTool" always returns selenium for all my testers. Can you please check it?

[2022/01/24 09:19:42] info: {
   "bot": {
      "automationTool": {
         "status": "processed",
         "probability": 1,
         "type": "selenium"
      },
      "browserSpoofing": {
         "status": "processed",
         "probability": 0
      },
      "searchEngine": {
         "status": "processed",
         "probability": 0
      }
   },
   "vm": {
      "status": "processed",
      "probability": 0
   },
   "ip": "188.232.8.139",
   "requestId": "01FT5P4J20DPX4K80QJ4G3RE9H"
} 
[2022/01/24 08:42:45] info: {
   "bot": {
      "automationTool": {
         "status": "processed",
         "probability": 1,
         "type": "selenium"
      },
      "browserSpoofing": {
         "status": "processed",
         "probability": 0
      },
      "searchEngine": {
         "status": "processed",
         "probability": 0
      }
   },
   "vm": {
      "status": "processed",
      "probability": 0
   },
   "ip": "213.230.88.227",
   "requestId": "01FT5M0W5SH0RMHGZM8RKQHRS5"
} 
[2022/01/24 08:37:30] info: {
   "bot": {
      "automationTool": {
         "status": "processed",
         "probability": 1,
         "type": "selenium"
      },
      "browserSpoofing": {
         "status": "processed",
         "probability": 0
      },
      "searchEngine": {
         "status": "processed",
         "probability": 0
      }
   },
   "vm": {
      "status": "processed",
      "probability": 0
   },
   "ip": "91.231.54.238",
   "requestId": "01FT5KQA5DHNY783XS17XY03FP"
}  

Thx!

Fully open source BotD

We want to make our BotD fully open source library which is working on the client side.
Several things to do:

  1. Architecture of the project (understand how to add detection logic to existing open source client)
  2. Implement auto tool detectors

Looks like we don't want to publish anything except automation tool detectors because of multiple reasons:

  1. How to do a reverse DNS lookup on the client side if we want to check search bots? How should we cache the results?
  2. Browser spoofing: we can add it without any problems, but first, we need to discuss & design "tampering detection".
  3. VM: we can add it as well, but the current logic is not stable and maybe it's better to postpone it/do it on further iterations.

Simplify and improve client-side API docs

  1. Move complex params into integrations.md docs file
  2. Get rid of ambiguous references (BotDetector vs BotDetectorInterface)
  3. Simplify the language, make client-side docs from the point of view of a pure client-side JS developer, not from a pov of a fullstack developer
  4. Every entity should have a deeplink in the API docs and be referenced from all places
  5. Doc sections, common to both client and server APIs (response, error handling etc) should be in their own file, referenced from both api doc files.
  6. Simplify everything overall as much as possible.

/results endpoint does not accept requests id in the cookie

According to my knowledge about the repo, the /results endpoint should accept the request id parameter in both forms - id query string parameter and cookie. Querying with query string parameter works just fine, however, I was not able to receive the correct response using the cookie. I tried various cookie names: botId, botd-id, botd-request-id, botdId, id.

Steps to reproduce:

  1. Send request
GET https://botd.fpapi.io/api/v1/results
Headers: 
Auth-Token: <token>
Content-Type: application/json
Cookie: botId=01F9VRKJJCNDWPRRXMGED6T1K0; botd-id=01F9VRKJJCNDWPRRXMGED6T1K0; botd-request-id=01F9VRKJJCNDWPRRXMGED6T1K0; botdId=01F9VRKJJCNDWPRRXMGED6T1K0; id=01F9VRKJJCNDWPRRXMGED6T1K0

Received response: {"error":{"code":"Failed","message":"'id' parameter is not passed"}}

Expected response:
{"status":"processed","bot":{"automationTool":{"status":"processed","probability":0},"browserSpoofing":{"status":"processed","probability":0},"searchEngine":{"status":"processed","probability":0}},"vm":{"status":"processed","probability":0},"ip":"193.165.236.186","requestId":"01F9VQCY00YBW3DYJXAVQVEK9F"}

BotD is blocked by Easylist

Hi ๐Ÿ‘‹
Since a few days, we notice BotD domain is blocked by Easylist: https://github.com/easylist/easylist/blob/master/easyprivacy/easyprivacy_trackingservers.txt#L78

Screenshot 2022-09-02 at 10 38 06

Is it because BotD script is served on the same domain as Fingerprint? Because BotD is not a tracker.
Would it be possible to use a different domain / a custom domain (this setting is available for Fingerprint in the dashboard)? We had to fallback on captcha, it makes user experience horrible ๐Ÿ˜•

Limit request clarify "3M API calls per month"

Could you clarify for me "3M API calls per month" are calculated based on 30 days successively (from the 1st request) or based on the calendar month (e.g. July 1 - July 31)?
Thank you so much!

False positive on Quark Browser in Android

System: Android 10
Browser: Quark
Log:

Collection time:
60.00ms
Detection time:
61.80ms
Detection result:
{
    "bot": true,
    "botKind": "headless_chrome"
}
Detectors:
{
    "detectAppVersion": {
        "bot": false
    },
    "detectDocumentAttributes": {
        "bot": false
    },
    "detectErrorTrace": {
        "bot": false
    },
    "detectEvalLengthInconsistency": {
        "bot": true,
        "botKind": "unknown"
    },
    "detectFunctionBind": {
        "bot": false
    },
    "detectLanguagesLengthInconsistency": {
        "bot": false
    },
    "detectNotificationPermissions": {
        "bot": false
    },
    "detectPluginsArray": {
        "bot": false
    },
    "detectPluginsLengthInconsistency": {
        "bot": true,
        "botKind": "headless_chrome"
    },
    "detectProcess": {
        "bot": false
    },
    "detectUserAgent": {
        "bot": false
    },
    "detectWebDriver": {
        "bot": false
    },
    "detectWebGL": {
        "bot": false
    },
    "detectWindowExternal": {
        "bot": false
    },
    "detectWindowSize": {
        "bot": false
    },
    "detectMimeTypesConsistent": {
        "bot": false
    },
    "detectProductSub": {
        "bot": false
    },
    "detectDistinctiveProperties": {
        "bot": false
    }
}
Debug data:
{
    "browserEngineKind": "unknown",
    "browserKind": "chrome",
    "documentFocus": false,
    "mozAppearanceSupport": false,
    "isAndroid": false,
    "isDesktopSafari": false
}
Collected data:
{
    "process": {
        "state": -1,
        "error": "BotdError: window.process is undefined"
    },
    "userAgent": {
        "value": "Mozilla/5.0 (Linux; U; Android 10; zh-CN; PDEM30 Build/QKQ1.191222.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/6.5.0.336 Mobile Safari/537.36",
        "state": 0
    },
    "appVersion": {
        "value": "5.0 (Linux; U; Android 10; zh-CN; PDEM30 Build/QKQ1.191222.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/6.5.0.336 Mobile Safari/537.36",
        "state": 0
    },
    "rtt": {
        "value": 0,
        "state": 0
    },
    "windowSize": {
        "value": {
            "outerWidth": 412,
            "outerHeight": 810,
            "innerWidth": 412,
            "innerHeight": 810
        },
        "state": 0
    },
    "pluginsLength": {
        "value": 0,
        "state": 0
    },
    "pluginsArray": {
        "value": true,
        "state": 0
    },
    "errorTrace": {
        "value": "TypeError: Cannot read properties of null (reading '0')\n    at errorTrace (https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:28066)\n    at n.<anonymous> (https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:33600)\n    at https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21466\n    at Object.next (https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:21571)\n    at https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20487\n    at new Promise (<anonymous>)\n    at e (https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:20232)\n    at https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:33437\n    at Array.map (<anonymous>)\n    at n.<anonymous> (https://fingerprintjs.github.io/BotD/main/main.bundle.js:1:33413)",
        "state": 0
    },
    "productSub": {
        "value": "20030107",
        "state": 0
    },
    "windowExternal": {
        "value": "[object External]",
        "state": 0
    },
    "mimeTypesConsistent": {
        "value": true,
        "state": 0
    },
    "evalLength": {
        "value": 33,
        "state": 0
    },
    "webGL": {
        "value": {
            "vendor": "WebKit",
            "renderer": "WebKit WebGL"
        },
        "state": 0
    },
    "webDriver": {
        "value": false,
        "state": 0
    },
    "languages": {
        "value": [
            [
                "zh-CN"
            ],
            [
                "zh-CN",
                "en-US"
            ]
        ],
        "state": 0
    },
    "notificationPermissions": {
        "state": -1,
        "error": "BotdError: window.Notification is undefined"
    },
    "documentElementKeys": {
        "value": [
            "lang"
        ],
        "state": 0
    },
    "functionBind": {
        "value": "function bind() { [native code] }",
        "state": 0
    },
    "distinctiveProps": {
        "value": {
            "awesomium": false,
            "cef": false,
            "cefsharp": false,
            "coachjs": false,
            "fminer": false,
            "geb": false,
            "nightmarejs": false,
            "phantomas": false,
            "phantomjs": false,
            "rhino": false,
            "selenium": false,
            "webdriverio": false,
            "webdriver": false,
            "headless_chrome": false
        },
        "state": 0
    }
}

headed playwright chrome with --disable-blink-features=AutomationControlled not detected

Headed chromium is only detected by navigator.webdriver, which can be disabled with the --disable-blink-features=AutomationControlled launch option.

Technically this also applies to Webkit, as there only navigator.webdriver is detected too, but I haven't found a launch option to do that yet. (It can still be done with stealth scripts but those are easier to detect)

Reproduction code:

from playwright.sync_api import sync_playwright, TimeoutError


with sync_playwright() as p:
    print("Testing chromium")
    # disable headless & navigator.webdriver
    browser = p.chromium.launch(headless=False, args=["--disable-blink-features=AutomationControlled"])
    page = browser.new_page()
    page.goto('https://fingerprintjs.github.io/BotD/main/')
    try:
        handle = page.wait_for_selector("#result.result-detected", timeout=1_000)
    except TimeoutError:
        print("Chromium is undetected.")
    else:
        print("Chromium is detected.")
    
    # input("press enter to continue> ")  # just incase you want to see the site yk
    browser.close()

Additional types in exports

HI! Can you please export BotDetectionResult from package?

Now the type can also be obtained, but this can only be done with a terrible construction like this:

type BotDetectionResult = ReturnType<
  (ReturnType<typeof load> extends Promise<infer T> ? T : never)['detect']
>;

Tag object is not returned by getResult

When using tag object as a detect() function's parameter I expect receiving tag information from getResult() function but there is no tag in the response.

Steps to reproduce

  1. Use botd's detect() function with tag parameter
    const botdPromise = Botd.load({
      token: '<token>',
      mode: 'allData',
    })
    const botd = await botdPromise
    const botdDetectResult = await botd.detect({myMetadata1: "ahoj", myMetadata2: "hello"})

Received response

{
  "status": "processed",
  "bot": {
    "automationTool": {
      "status": "processed",
      "probability": 0
    },
    "browserSpoofing": {
      "status": "processed",
      "probability": 0
    },
    "searchEngine": {
      "status": "processed",
      "probability": 0
    }
  },
  "vm": {
    "status": "processed",
    "probability": 0
  },
  "ip": "193.165.236.186",
  "requestId": "01F9X4ER48RTQZ9K0RYC8BFC7J"
}

Expected response
Same response with the tag object included

Reproduced on

Code: https://github.com/makma/martinmakarsky.com/blob/experiment-page/src/templates/not-very-secret-experiment-page.jsx#L28
Deployed site: https://deploy-preview-10--martinmakarsky-preview-delivery-api.netlify.app/not-very-secret-experiment-page

Improve CI/CD pipeline

Current issues consist of the following:

  • unnecessary duplication of specific steps (e.g. building multiple times)
  • browserstack failures because of timeout
  • release workflow hangs forever

Code review

Hi, Valentin has asked me to do a code review of the project to make it more production ready.

Issues

Not minified distributive files

Since the project is open-source, you can publish not minified versions of the distributive files too. It will help library users debug the code.

Output the not minified files into the dist directory that gets published to NPM, and remove the minified files that aren't supposed to be used by browser: botd.cjs.min.js, botd.esm.min.js (don't remove it if you don't follow the "external dependencies" recommendation). See the FingerprintJS's Rollup configuration as an example.

Copyright banner in distributive files

Add a copyright comment to the start of every distributive file so that people who open the code knows what the code is and where to search for documentation. See an example at https://cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.min.js.

Excess items in the .d.ts

The dist/botd.d.ts file contains not documented items, BotDetector's attributes and methods, to be precise. As far as I understand, they are for internal usage only. Once you publish such .d.ts file to NPM, you'll have to keep these attributes in methods working until you release a new major version.

A good way to fix it is doing this replacement:

- export async function load(options: Options): Promise<BotDetector>
+ export async function load(options: Options): Promise<BotDetectorInterface>

Where BotDetectorInterface is a TypeScript interface that has only the attributes and methods designed for usage by external users.

More specific .d.ts

Instead of using string as the mode option type, create a new type that describes the exact values that the option supports.

Describe the exact type that detect and getResult return. It will be perfect if the file describes how the return type depends on the arguments like FingerprintJS Pro Agent does.

Add a type for errors thrown by the library, and a type with all expected error codes. Having such a type will help you to prevent mistakes in error codes.

Contribution instructions (will be fixed in #32)

If a person wants to make a pull request, they won't know how to run the project locally, how to test it, how to prepare for pull-requesting. Usually contributing.md holds such information.

Handling unexpected source errors

It's important to know when something unexpected happens. So, there should be a separate status for unexpected errors. Some sources in the code throw expected errors and the -1 status is used for expected errors. If you want to handle expected errors differently, you can create separate statuses for them.

"Source" naming

"Source" is a function that gets information about browsers. The source result is called "component".

Add comments to unobvious code

For example, why a Content-Type: text/plain header is added to the API request. It will help prevent regressions.

Wrong error handling documentation

The documentation that the following error type is thrown:

{
  "code": "TokenNotFound",
  "message": "token not found"
}

In fact, the following type is thrown:

{
  "error": {
    "code": "BotdFailed",
    "message": "Foo"
  }
}

Also, consider adding stack traces to unexpected errors, it will help you a lot.

Load time unexpected errors aren't converted to the described type.

getResult purpose (will be fixed in #32)

What's the use case of getResult? If it's supposed to be called on a consecutive visit, then collect will be called and it's result won't be used. Also, there is no way to decide what to call: detect or getResult.

If it's supposed to be called during the same page session, then there is no need to store it in cookies.

Why not to use local storage instead of cookies? It has a simple API and is more reliable.

The getResult method looks like something additional to bot detection, i.e. a part of a higher level product that uses Botd as a dependency. It can be a part of the same NPM package.

Recommendations (optional)

External dependencies

Make the distributive files designed to be used with bundlers like Webpack and Rollbar (ESM, CJS) import the dependencies instead of including the dependencies into the code. It will reduce the size of end users' code by de-duplicating the dependencies.

For example, if somebody makes a project with @fingerprintjs/fingerprintjs and @fpjs-incubator/botd-agent today, they will have 2 copies of tslib in their code. But if dist/botd.esm.js of @fpjs-incubator/botd-agent imports tslib instead of including it, there will be only 1 copy of tslib.

See the FingerprintJS's Rollup configuration to learn how to make a dependency be external.

Documentation in .d.ts (will be fixed in #32)

If you add comments to the functions, attributes and methods in dist/bot.d.ts, users will be able to get information about the JS API without leaving the IDE. It's a common practice. In order for IDEs to turn the comments into hints, they must start with /** and end with */, for example:

/**
 * An option for changing ...
 */

This is called JSDoc, it also supports tags such as @deprecated, you can learn more at https://jsdoc.app.

Screen Shot 2021-08-06 at 13 35 27

Room for options

Make a room for future options in detect by passing options in an objects instead of a plain list of arguments.

A single code style throughout the company

Use the same Prettier settings, and the same naming cases.

Browser support

Add a documentation section that tells what browsers the library supports. Make a BrowserStack test that checks that the library at least can run in these browsers. If it supports only modern browsers, adjust the target field of tsconfig.json to reduce the distributive code size.

Ideally, the whole code should be covered with automatic tests that run in different browsers on BrowserStack.

Turn the playground into a development playground

At the moment the playground runs the latest published version of the library. It can run the current local code instead, and therefore be a convenient tool for developing the library.

BTW the resources/favicon.ico file is not used.

Performance of individual sources

Consider measuring performance of each of the sources. It will let you know what sources tend to decrease the whole performance.

Avoid .then

Mixing await and then looks sloppy. For example, in the detect method of BotDetector.

Add a link to botd-integration

This project's readme should tell about botd-integrations and cloud mode. Please add a link with an explanation to the readme.

BotD claims Microsoft Edge on macOS is a `headless_chrome` bot

With this code:

<script>
	// Initialize an agent at application startup, once per page/app.
	const botdPromise = import('https://openfpcdn.io/botd/v1').then((Botd) => Botd.load())
	// Get detection results when you need them.
	botdPromise
		.then((botd) => botd.detect())
		.then((result) => console.log(result))
		.catch((error) => console.error(error))
</script>

image

Contribution instructions, getResult purpose, and documentation in .d.ts

TODO 1: Contribution instructions

If a person wants to make a pull request, they won't know how to run the project locally, how to test it, how to prepare for pull-requesting. Usually contributing.md holds such information.

TODO 2: getResult purpose

What's the use case of getResult? If it's supposed to be called on a consecutive visit, then collect will be called and it's result won't be used. Also, there is no way to decide what to call: detect or getResult.

If it's supposed to be called during the same page session, then there is no need to store it in cookies.

Why not to use local storage instead of cookies? It has a simple API and is more reliable.

The getResult method looks like something additional to bot detection, i.e. a part of a higher level product that uses Botd as a dependency. It can be a part of the same NPM package.

TODO 3: Documentation in .d.ts

If you add comments to the functions, attributes and methods in dist/bot.d.ts, users will be able to get information about the JS API without leaving the IDE. It's a common practice. In order for IDEs to turn the comments into hints, they must start with /** and end with */, for example:

/**
 * An option for changing ...
 */

This is called JSDoc, it also supports tags such as @deprecated, you can learn more at https://jsdoc.app.

Screen Shot 2021-08-06 at 13 35 27

Different results in the same browser on different websites

We have installed BotD on one of our pages as a test. We always get as result a probability of 0.8 for automationTool and status "puppeteer". The result is the same at different browsers and also at different computers.

But if we visit with the same browsers and same machines your test page https://fingerprintjs.com/products/bot-detection/ we always get the result "You are not a bot".

What could be the reasons for the different results? Our test site is an angular v13 spa build als dev version.

I am looking forward to a solution idea.

export `detectors` and standalone detect/collect functions

We're trying to implement botd by using collect() client-side, sending the components to the server and then running detect() there.
The current BotDetector class interface does not allow that kind of use.

My suggestion: move detect and collect into standalone functions that are exported.
The BotDetector class could use those functions itself too, to prevent code duplication. (this would also allow tree-shaking to remove the BotDetector class or detectors - assuming they are marked as pure)

The client-side is possible to implement manually (by copying the function out of the class and adapting it), because luckily the sources list is exported.
The server-side validation is impossible because detectors is not currently being exported.

Thinks Firefox Ubuntu is Playwright with Probability 1

After receiving a token, I immediately tested the cdn version of your library on a toy app (in debug mode, hosted on localhost on my computer). I'm using Ubuntu 20.04. On my Chromium browser, the result was expected: three 0 probability results for each of the bot scenarios ("automationTool", "browserSpoofing", "searchEngine"). But on my Firefox browser, the result was the same except for the probability=1 conclusion that I was using Playwright.
automationTool: Object { status: "processed", probability: 1, type: "playwright" }

This was using a normal browser window - not incognito - using an unadulterated installation of Firefox, with user agent "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0". This user agent has not been altered.

What's the story here?

Simplify and improve the readme

  1. Have correct and context-aware doc links available by every code example (use entity and section deep-linking for that).
  2. Supported stuff table should be more interesting. Put the stuff that's the hardest first
  3. Make cloud integrations a separate document and just link to that from the readme. The cloud intnegrations doc should explain briefly what's posisble and then link to the actual botd-integrations project.
  4. Simplify as much as possible, use simple English

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.