npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@dataunlocker/defender

v1.0.1

Published

DataUnlocker core module that protects your web app's analytics.

Downloads

141

Readme

DataUnlocker Defender

DataUnlocker Defender protects your web app against network and JavaScript intrusions 🦾

💊 DataUnlocker is like a magic pill that makes all front end data collection and tracking tools work even when browsing with ad blockers, network filters, VPN etc.

In turn, DataUnlocker Defender is a controller script tightly coupled with your web application's source code, making it impossible to cut it out of the JavaScript web app.

This package is a plain "core" DataUnlocker Defender JavaScript implementation. Check other child packages for your specific tooling:

Installation

⚠️ We highly recommend you to follow guided onboarding on admin.dataunlocker.com instead of trying to install this package manually.

DataUnlocker Defender is designed to be installed directly to your client-side JavaScript web app, like you would install any typical NPM package. However, you will need to provide DATAUNLOCKER_ID environment variable before installing this package.

export DATAUNLOCKER_ID=000000000000000000000000
npm install @dataunlocker/defender

You can also append the DATAUNLOCKER_ID variable to .npmrc file (which you can create if it doesn't exist). Note that environment variables will override ones which are defined in .npmrc file.

DATAUNLOCKER_ID=000000000000000000000000
  • DATAUNLOCKER_ID
    • ID of your domain you can find in DataUnlocker Admin.
  • DATAUNLOCKER_ENDPOINT (OPTIONAL)
    • A healthy transport endpoint address without the protocol and trailing slash.
    • For path-based endpoint, supply website.com/eXamPLe.
    • For DNS-based endpoint, eXamPLe.website.com.
    • For custom endpoints, custom.site.com/optional-path.
    • If not provided, the package picks the latest (healthy) transport endpoint for the domain when being installed. For instance, if you have 2 healthy endpoints, the most recently created one will be picked up. If you have none, the installation will fail.

You will be guided at DataUnlocker Admin about further installation steps.

⚠️ Before you continue

DataUnlocker installation is always considered "clean" (working even when ad blockers are on) when you first install it. I.e. the installation is unique to every domain and there's no feasible generic filter against it. However, as the time passes by, DataUnlocker on your web app may or may not get noticed by adblock community, so you have to properly integrate it to avoid DataUnlocker being "voided" over time.

DataUnlocker Defender checks that your DataUnlocker setup works properly within your web app, on every page load. Simply put, it tells you if something is trying to block or bypass DataUnlocker via the corresponding handlers. It is important to program your web app in a way that if this check fails, your web app reacts to it (for example, you can stop interactivity in your web app, stop sending or receiving data, prevent rendering, navigation etc). This is a core paradigm of DataUnlocker self-defense mechanism. No worries: the tracking haters community won't intentionally release a filter to break your web app, as users will start disabling ad blockers and complain on inability to circumvent DataUnlocker.

❗️ If you don’t properly handle the Defender check's “error“ and “load“ in your web app, the blockers will be able to circumvent DataUnlocker and void it over time.

A few more things to note:

  • Mind to embed DataUnlocker Defender's code right into your JavaScript sources (modern JavaScript bundlers do it by default).
    • If DataUnlocker Defender is served as a separate JavaScript resource on a predictable URL, it can be blocked easily.
  • Mind to avoid letting the web app to run if DataUnlocker setup was reported with "error".
    • Just rendering warning popups or overlays can be easily removed by content blockers.
    • Using document.body.innerHTML = '...' can be easily defused too (JavaScript rules in uBlock, Brave etc).
    • Instead, modify the business logic of your web app. Set the scoped (non-window) variables, prevent API calls, navigation etc - these things cannot be "patched" by ad blockers.

JavaScript API

Defender check will pass for 100% of your users (including those using ad blockers), but it has to be preprogrammed in a specific way to ensure nothing (like ad blocker) can "cut" DataUnlocker from your web app.

If you don't have your front end framework listed above ☝️, you can try integrating this DataUnlocker Defender core module, @dataunlocker/defender. Make sure you understand the concepts before you continue.

DataUnlocker Defender's API is very straightforward. It is highly important to handle both load and error events:

import { onLoad, onError } from '@dataunlocker/defender';

// This callback function is invoked after ad blocker detection is done and browser APIs are patched
// according to the domain services config on admin.dataunlocker.com. Init **your web app** right here.
onLoad(() => {
  // + Move your tracking init code into here (or alternatively into DataUnlocker's secure enclave in admin UI).
  // + Handle this function as an actual load of your web application! Don't let business logic to run before it loads.
});

onError(() => {
  // Handle errors, which is EXTREMELY IMPORTANT: exactly handling this check protects adding new filters against
  // your web app. Stop your web application from working and explain why to the "hacky" user.
});

Mind this:

  • Defender may report error at any time (after or before load). onError handler is fired once when DataUnlocker setup is broken or circumvented.
  • Without handling both load and error it will be easy for ad blockers to block DataUnlocker.
    • Don't let your web app to be interactive until there is a load event;
    • Don't let your web app to be interactive if there is an error event; optionally, notify the user about the error.
  • The effect of handling load/error should be observable immediately, in a sense that anyone who contributes to adblock filter lists won't be clicking through your entire web app after finding a set of rules that circumvent DataUnlocker.
  • In case DataUnlocker detects ad blocker at a later stage (after load was emitted), a page refresh will happen along with an error event. This is an intended behavior.
  • When actually integrating DataUnlocker to production, you may want to first skip implementing handlers and observe that no issues arise using console.log.

✅ Correct integration example (1)

Assume we have a simple counter web application, here's the example implementation that demonstrates the proper integration.

index.html:

<html>
  <head>
    <title>Sample counter web app</title>
    <script src="/source.js"></script>
  </head>
  <body>
    <span id="counter-value">0</span>
    <button id="counter-add">Inc by 1</button>
  </body>
</html>

source.js:

import { onLoad, onError, placeholder } from '@dataunlocker/defender';

let error = false; // Stores a DataUnlocker error, if it ever occurs.
let counter = 0; // Your web app's internal state

onLoad(() => {
  // GTM
  window.dataLayer = window.dataLayer || [];
  dataLayer.push(['js', new Date()]);
  dataLayer.push(['config', 'G-279DATA']);
  const script = document.createElement('script');
  script.src = 'https://www.googletagmanager.com/gtag/js?id=G-AAAA';
  document.body.appendChild(script);

  initMyWebApp(); // Finally initialize the web app after tracking is appended.
});

onError((e) => {
  error = true; // Set the global state that would prevent web app from counting.
  document.body.appendChild(placeholder(e));
});

const initMyWebApp = () => {
  document.getElementById('counter-add').addEventListener('click', () => {
    if (error) {
      // IMPORTANT: when DataUnlocker errors, we prevent web app from running.
      return;
    }

    // Business logic of your web app
    document.getElementById('counter-value').textContent =
      (++counter).toString();
  });
};

✅ Correct integration example (2)

Example of DataUnlocker Defender integration into a core module of your JavaScript web app.

import { onError, load } from '@dataunlocker/defender';

let ok = true; // ✓ You can use local variables.

onError((e) => {
  ok = false;
  document.body.appendChild(placeholder(e));
});

// ✓ Defender check is in the function which is widely used across the app.
export async function myApiCall(path, options) {
  await load; // Important: waits until the DataUnlocker is loaded

  if (!ok) {
    return new Response({
      body: `Failed to request ${path}, possibly due to ad blocker`,
    });
  }

  return await fetch(`https://api.example.com/${path}`, options);
}

❌ Incorrect integration example (1)

Avoid using easy-to-mock JavaScript when handling Defender check.

import { onError } from '@dataunlocker/defender';

onError(() => {
  // ❌ Ad blockers can manipulate window variables, localStorage, browser APIs, etc so adding a
  // rule of mocking a "_check_failed" window variable on your website would circumvent Defender.
  window._check_failed = true;
});

const yourBusinessLogic = () => {
  if (window._check_failed) return; // ❌ This can be circumvented.
  // ...your business logic... //
};

❌ Incorrect integration example (2)

Always handle Defender's response within you application's business logic.

import { onError } from '@dataunlocker/defender';

onError(() => {
  // ❌ As you already learned from the example above, ad blockers can manipulate browser APIs,
  // making this call void. Instead, affect your application's business logic.
  document.body.innerHTML = '<b>Please disable your ad blocker.</b>';
});

// ❌ Application business logic is not modified.

❌ Incorrect integration example (3)

Handle both onLoad and onError events. Both are required.

import defender from '@dataunlocker/defender';

let ok = true;

// ❌ Missing onLoad handler

onError(() => {
  ok = false;
});

// Application business logic example.
export function myApiCall(path, options) {
  // ❌ Defender may never load and hence error!
  if (!ok) return new Response({ body: `Failed` });

  return fetch(`https://api.example.com/${path}`, options);
}

FAQ

What if my application is a simple static site without JavaScript?

DataUnlocker Defender won't help you if JavaScript is unused. However, this is not a case for 99% of websites, so think again.

If you don't use JavaScript at all, you can consider coding a small "unique JavaScript" specifically for Defender, like, a tiny handler that will handle clicks on hyperlinks.

I'm unsure what business logic should I cover with Defender

A rule of a thumb is to cover a "core" business logic that would be immediately noticed by the users of your web app. You can also chat in our community to figure out your case.

I'm using website constructor and I can't install npm modules

As an alternative, we're offering a CLI utility to wrap your JavaScript sources with Defender:

npx -y dataunlocker patch source.js --id 000000000000000000000000

In this case Defender will try to "guess" your business logic and insert as many checks as possible next to expressions in the source code, along with obfuscating and minifying the code. Mind that you have to manually maintain the workflow of patching your sources, like doing it in every deployment pipeline or from time to time.

How do I ensure Defender won't break my web app one day?

Defender retrieves a validation code with cryptographic signature from DataUnlocker's servers per each web app page load, which helps it ensure that the connection and the setup is genuine. Without getting a signed response from DataUnlocker servers for whatever reason, Defender will report an interference (onError).

Consider the following to mitigate risks:

  • Add both dev and prod websites to DataUnlocker Admin as separate domains, and ensure their setup is identical.
  • In DataUnlocker Admin UI, you can always manually turn Defender off in case when something went wrong and you need some time to debug.
  • Make sure your project on DataUnlocker is paid. You have a requests overuse allowance to cover spikes in traffic, but running out of traffic for continuous period of time will invalidate your installation and always fire onError.
  • Make sure your reverse proxy is up. If it goes down, Defender's check will fail too.

Facts about Defender:

  • To minimize network issues, DataUnlocker Defender retries a failed network "probe" request just once.
  • DataUnlocker proxy is built on Cloudflare, which guarantees the highest uptime. If you're using Cloudflare as a proxy to DataUnlocker, you're safe.