Giter VIP home page Giter VIP logo

yett's Introduction

Yett

npm-badge license-badge size-badge ci-badge bundle-badge

🔐 A small webpage library to control the execution of (third party) scripts like analytics

Simply drop yett at the top of your html and it will allow you to block and delay the execution of other scripts.

Background

[❓] So, why on Earth would I want to block scripts on my own website?

One way to use yett would be to build a GDPR compliant consent-first-analytics, via an UI like below.


bar

Analytics scripts are blocked until users Accepts, (previously) in production at https://snips.ai

Blocking execution of analytics script (until consent is given) can be done manually, but the problem is that analytics providers often provide minified code embeds that you have to include in your html as they are. If you want to exercise control over their execution, then you have to tamper with this minified JS yourself, which is complex and does not scale well if you load several 3rd party scripts.

Another thing to consider is that these scripts first setup a local buffer that record user actions locally, and then upload the data only after a remote script is loaded asynchronously. Meaning that if the whole thing is simply wrapped inside a callback (as some other libraries do) then every action performed by the user on the web page before the callback gets executed won't get recorded and will never appear in your analytics dashboard.

Thus we invented yett. Just drop in the script and define a domain blacklist - yett will take care of the rest ✨.


And on a side note, it is technically quite amazing to know that a few lines of js is all you need to control execution of other scripts, even those included with a script tag. 😉

Also, yett has an interesting meaning.

Usage

Small example

<!DOCTYPE html>
<html>
  <head>
    <!-- Regular head items here… -->

    <!-- 1) Add a blacklist -->
    <script>
      window.YETT_BLACKLIST = [
        /my-blacklisted-domain/,
      ]
      // Or a whitelist
      window.YETT_WHITELIST = [
        /my-whitelisted-domain/,
      ]
    </script>
    <!-- 2) Include Yett -->
    <script src="https://unpkg.com/yett"></script>
    <!-- If you target only modern browsers (!= IE) you should use the following version. It is way smaller! -->
    <!-- <script src="https://unpkg.com/yett/dist/yett.min.modern.js"></script -->
    <!-- 3) Profit! -->
    <!-- This script is blocked -->
    <script src="https://my-blacklisted-domain.com/file.js"></script>
    <script>
      // This one too
      (function() {
        var script = document.createElement('script')
        script.setAttribute('src', 'https://my-blacklisted-domain.com/some-file.js')
        script.setAttribute('type', 'application/javascript')
        document.head.appendChild(script)
      })()
    </script>
  </head>
  <body>
    <button onclick="window.yett.unblock()">Unblock</button>
  </body>
</html>

⚠️ It is strongly recommended (but not necessary) that you add type attributes to <script> tags having src attributes that you want to block. It has the benefit of preventing the scripts from begin downloaded in major browsers.

💡 In any case, if you would like to ensure that cookies are not sent to third-party servers during the initial request you can use the crossorigin="anonymous" attribute. Check this link for more details.

Add a blacklist

Yett needs a blacklist, which is an array of regexes to test urls against.

<script>
    // Add a global variable *before* yett is loaded.
    YETT_BLACKLIST = [
        /www\.google-analytics\.com/,
        /piwik\.php/,
        /cdn\.mxpnl\.com/
    ]
    // OR
    YETT_WHITELIST = [
        /my-whitelisted-domain/
    ]
</script>

CDN

Finally, include yett with a script tag before other scripts you want to delay:

<script src='unpkg.com/yett'></script>

Then, use window.yett.unblock() to resume execution of the blocked scripts.

NPM

You can also use npm to install yett:

npm i yett
window.YETT_BLACKLIST = [
    // ... //
]
// OR
window.YETT_WHITELIST = [
    // ... //
]
// Side effects here! Do not import more than once!
import { unblock } from 'yett'

unblock()

Unblock

unblock(...scriptUrlsOrRegexes: (String | RegExp)[])

Unblocks blacklisted scripts.

If you don't specify a scriptUrlsOrRegexes argument, all the scripts that were previously blocked will be executed. Otherwise, the scriptUrlsOrRegexes provided will be either removed from the blacklist or added to the whitelist and executed.

Build locally

# Clone
git clone https://github.com/elbywan/yett
cd yett
# Install
pnpm i
# Serves demo @ localhost:8080
pnpm dev
# Build for release
pnpm build

Browser compatibility

<script> <script type="javascript/blocked"> document.createElement('script')
Prevents loading
Prevents execution

The most 'advanced' javascript feature that yett uses is MutationObserver, which is compatible with all major browsers as well as IE11.

If you need IE 9/10 compatibility, you will have to use a polyfill:

<script src="https://cdn.jsdelivr.net/npm/mutationobserver-shim/dist/mutationobserver.min.js"></script>

Caveats

Add a type attribute manually

Adding this attribute prevents the browser from downloading the script on Chrome and Firefox.

<script src="..." type="javascript/blocked"></script>

Monkey patch

This library monkey patches document.createElement. No way around this.

This means that yett is not compatible with third-party browser extensions that also monkey patch this native browser function.

Dynamic requests

Scripts loaded using XMLHttpRequest and Fetch are not blocked. It would be trivial to monkey patch them, but most tracking scripts are not loaded using these 2 methods anyway.

Suggestions

If you have any request or feedback for us feel free to open an issue!

So far we’re using this library for analytics, but it could also be used to block advertising until consent, and other things we haven’t thought about yet. We’re excited to see what use cases the community comes up with!

License

MIT

yett's People

Contributors

dependabot[bot] avatar dhruvppatel avatar elbywan avatar pocketjoso avatar ryhinchey 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

yett's Issues

unable to block httponly cookies

Hello,
I'm trying to prevent third-party cookies to be installed without consent and it works perfectly except for httponly cookies I'm not able to block them.
so I suggest if we can block third-party using source instead of type.

Mathjax not working with yett

First of all thank you for this useful library!

I stumble upon an issue trying to use yett (for managing Google Analytics) in a page using Mathjax.

Mathjax seems to patch the current document to inject scripts elements for math display but this seems to be blocked by yett.

here is a small POC:

<!DOCTYPE HTML>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>MathJax / Yett</title>
        <!-- MathJax -->
        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
        <script>
        window.YETT_BLACKLIST = [
            /googletagmanager\.com/,
            ];
        </script>
        <!-- <script src="https://unpkg.com/yett"></script> -->
    </head>
    <body>
        <h1>MathJax & Yett test</h1>
        <p>$$x = {-b \pm \sqrt{b^2-4ac} \over 2a}$$</p>
    </body>
</html>

uncommenting yett loading will break the Mathjax rendering.

Issue with response data while reading `scriptElement.src` after loading with Yett

Hello Team, Cheers to all the contributors for making this simple and powerful library. We have recently implemented this library to our website for GDPR compliance across the globe.

After integrating we have found one particular issue with Google IMA SDK Library.

Screen Shot 2020-04-08 at 11 24 50 PM

After debugging I found that the IMA SDK script internally validates how the script has been loaded. Basically they iterate over all script elements present and read their src property and compare with actual script URL using Regex.

With Yett Script

Screen Shot 2020-04-08 at 11 30 20 PM

As you can see script.src returns //imasdk.googleapis.com/js/sdkloader/ima3.js which we have passed while loading.

Without Yett Script

Screen Shot 2020-04-08 at 11 30 08 PM

As you can see script.src returns https://imasdk.googleapis.com/js/sdkloader/ima3.js with the protocol.

It seems that monkey.js src get method is not working as expected. So for testing, I have forked the repo and modified to use originalDescriptors approach(which I found from your git history).

You can see the working branch code at https://github.com/dhruvppatel/yett/tree/fix/get-src

For testing, you can add the following snippet in doc index.html below dynamic.js script loading code and verify.

        (function() {
            var script = document.createElement('script')
            script.src = '//imasdk.googleapis.com/js/sdkloader/ima3.js'
            document.head.appendChild(script)
            setTimeout(function() {
                console.log(script.src);
            }, 1000);
        })();

yett.unblock() does not unblock scripts

Hi,
I am developing a Cloudflare app that allows cookies on user consent.
All the scripts are loaded via a JSON file. yett blacklist and yett are loaded onto the head and they block the third-party scripts. However, when I try to unblock these scripts from the body, it does not work.

When I call console.log(yett.unblock) from the body, it works and logs the code into the console.
However, window.yett.unblock() does not work and the scripts are not unblocked.

I'm baffled by why this is happening.
If it is of any use:

  1. I loaded jQuery on the head and I am able to call jQuery functions from the body, so I don't presume it is a Cloudflare bug.
  2. When I call yett.unblock() from the body of my own website and not via the Cloudflare app, it works. But the user should block scripts from any website and hence this is not a solution.

Could you please help me? Please do not close the issue if the question is unclear. I can provide any extra information if required.

Script's body is missing

Hey guys,
First, thank you for this great project.

Second, after unlock() is executed, the scripts are back without the body.

<script ...> body </script>

Here you are creating a new element instead just using the old one, and if so, why not copy all the attributes including the body of the scripts.

Third, are you consider adding a mechanism of white-list for domains I want to allow anytime? This way websites will be able to allow their own cookies and block all the others (without list them) until unlock it.

Thanks

Yett lib is throwing errors when trying to test it using WASP plugin

Team,

I am using WASP which is a chrome extension which will give a visual representation of all the tags and scripts loaded into DOM. When I try to test yett library using WASP plugin it throws js errors on the page load after clicking on "WASP" in chrome dev tools. I have tested it using yett demo app and with my custom app as well and its the same issue. Below is the error:

monkey.js:16 Uncaught TypeError: Cannot redefine property: src at Function.defineProperties (<anonymous>) at HTMLDocument.document.createElement (monkey.js:16) at (index):21 at (index):25

Attached are the screen shots of the same
yett_console_error
yett_script_error

Partial unblock from the blacklist

Let me start of by saying that maybe I'm missing something basic.
The situation is this. I use a blacklist. Everything works as advertised. I'm blocking Google, Facebook and Hotjar.
I have an unblock() action attached to a button. It also works as advertised.

So far so good. What I'm wondering is, how can I make a partial unblock? If I click on a button I would like to unblock Facebook from the previous list and keep Google and Hotjar blocked.

Something like yett.unblock('facebook.com') doesn't work;

document.write()

Just wondering, will this capture document.write() statements?

A lot of embeds and other snippets issue document.write() injections synchronously in head or body during page-load.

I'm wondering if MutationObserver would pick these up - and in IE11 in particular.

Might be good to have test-coverage for this?

Execution order and dependency

Hello,
I would like to know if the script execution order will be respected?
Especially for the dependency, One case is the inclusion of Jquery and then a plugin dependent on Jquery. These two scripts have been blocked, when I run the Unblock() function, is that it will be guaranteed that the plugin will not run before Jquery, which will trigger an error?
Thanks

`onload` not fired on blocked scripts

The load event doesn’t seem to fire on blocked script tags created with document.createElement in Chrome.

Reproduction:

<!DOCTYPE html>
<html>
  <head>
    <script>
      window.YETT_BLACKLIST = [/jquery/];
    </script>
    <script src="https://unpkg.com/yett"></script>
    <script>
      let scriptEl = document.createElement('script');
      scriptEl.src = 'https://code.jquery.com/jquery-3.5.1.slim.min.js';
      scriptEl.onload = function () {
        console.log('Loaded');
        console.log(window.jQuery);
      };
      document.head.appendChild(scriptEl);
    </script>
  </head>
  <body>
    <button onclick="window.yett.unblock();">Unblock</button>
  </body>
</html>

I expect "Loaded" and the value of window.jQuery to show in the console, but nothing happens. Preferably it should fire after the script has been loaded and executed.

YETT blocks Tracking also after yett.unblock

Hey Guys! I have a question and I hope you can help me. I have tried to use YETT to block all cookies on our website, until the user presses "Ok" button. After pressing the button i do a yett.unblock() like it is explained in your readme. It also works with unblocking the cookies and all the scripts from the YETT_BLACKLIST.

But the problem that we are having now is that altough all cookies are unblocked on the website, the tracking in Matomo and Google Analytics does not work. Can you explain why?

Thanks in advance for your answer and your help!

Greetings from Germany!

yett.unblock() conflicting with what-input.js / foundation modal

Hi thanks, for this awesome piece :)

I am using foundation's framework modal.
It works perfectly fine when yett is blocking other scripts.
The source is on the whitelist so also that works well.

However when I unblock the scripts with

yett.unblock();

I've found that yett.unblock() prevents whatinput.js to work properly.

So e.g. modal buttons / links do not fire anymore


// foundation CSS
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/css/foundation.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/motion-ui/1.2.3/motion-ui.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/css/foundation-prototype.min.css">


// foundation button
<div class="button">
  <a data-open="modal"  
   aria-controls="modal" 
   aria-haspopup="true" 
   tabindex="0"
   >READ MORE
   </a>
</div>

// foundation modal
<div class="reveal" 
   id="modal" data-reveal="cenwos-reveal" 
   role="dialog" aria-hidden="true" 
   data-yeti-box="modal" 
   data-resize="modal" 
   tabindex="-1" 
   style="display: block; top: 100px;"
   > 
 
   <button class="close-button" data-close="" aria-label="Close modal" type="button">
   <span aria-hidden="true">×</span></button>
 
</div>


// foundation scripts
<script src='https://code.jquery.com/jquery-2.1.4.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/js/foundation.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/motion-ui/1.2.3/motion-ui.min.js'></script>
<script>
    $(document).foundation();
</script>

Gatsby compatibility

using React Helmet the script gets added below scripts injected through plugins such as google analytics, which makes it unusable.

I guess to make this work we would need a gatsby-yett plugin

Block these scripts when non essential toggle is off

I am trying to block a set of tracker scripts. The issue is I cannot figure out where the scripts are from, the domain name of these scripts. I could block google analytics by adding this in the blacklist:
[/www.googletagmanager.com/gtag/js/,
/google-analytics.com/,
/googletagmanager/,]

These are the other scripts I am trying to block. Could you help with where to find the domain name for these scripts:

  • Facebook Pixel
  • Google Tag Manager
  • Hotjar Analytics
  • Google Publisher Tag
  • Youtube embed
  • Vimeo embed
  • Google maps
  • Addthis widget
  • Sharethis widget
  • Twitter widget
  • Soundcloud embed
  • Slideshare embed
  • Linkedin widget
  • Instagram embed
  • Pinterest widget

can we use document.head.appendChild method in blackList

can we add blacklist like document.head.appendChild() instead of drop into html head tag

ex.
addBlacklistScript() {
const script = document.createElement("script");
const blackList = [
/www.google-analytics.com/,
];
//some method to add blacklist in text
document.head.appendChild(script);
},

Unblocking does not work if the passed value is in a variable

This is something that I came across in a more complex environment and almost got me thinking that I must be crazy so I tried to reproduce this issue with almost no libraries at all.

As you can see in your demo site, when someone uses window.yett.unblock like this:

window.yett.unblock('/mypattern/');

everything works without an issue.

Same goes if you enter an array of previously blacklisted items:

window.yett.unblock(['/mypattern1/', '/mypattern2/');

However, when I try to pass the arguments through a variable, it does not work.

I have setup a very basic html page that demonstrates what I'm trying to say. Please check it here:

http://yett.mashup.gr/

Note that inputArr and anotherArr have the exact same initial assignment.

Also, if you execute the unblock function directly from Chrome's console like so: unblockScript([ /testme\.js/ ]); it works without issues. The problem is when the input to the unblock function does not come as a literal but as a variable.

question about ecommerce

Wondering if this will work to block ecommerce sites from inserting unwanted script? I know little to nothing about coding, but I have a small online business and it drives me crazy that my platform lies to everyone and hides a bunch of code behind hidden files. I was able to identify what file my platform uses to inject the code, I spoke with them and of course they refuse to let me delete it or even access the file.

The unwanted scripts are found in a liquid file called "{{ content_for_header }}". Is there a way I can use your code to block this from executing? I've been trying to solve this for months, you are likely my last hope lol Thanks for any held you can offer!

Event Emitter for MutationObserver

How would you feel about adding an EventEmitter and exposing it to be able to watch for new scripts being appended to the DOM so we can check if we want to unblock them?

I wouldn't mind doing the PR

Possible limitations

  1. If another script tag has the async attribute, it will be downloaded in parallel and could be executed before the Yett script. The MutationObserver will be registered after the script and therefore not prevent its execution.
  2. In context of GDPR, the script should prevent communicating personal data cross-domain.
    Since all script's are downloaded (even if not executed), they still send all third-party cookies with the HTTP GET to download the script. Which lets the 3th party know which websites you visit.

Possibility to block and entire <script>block?

Is it possible to block an entire <script> block like this?

If I add a block like this:
<script type="javascript/blocked"> (function(){ alert("loaded"); })() </script>
it isn't executed. But when I call .unblock() this whole block disappears.
We're working with a CMS where we don't know what scripts the author will add to a page.

[feature request] White-list patterns

My idea is to define a YETT_WHITELIST that let the users control sources they are trust to be executed (first-party scripts) when blocks all the others.
This way, unlock('some source') will add this source into YETT_WHITELIST and unlock() will allow everything.
Thanks for considering it.

Uncaught TypeError: t.test is not a function

Implemented in my page. The blocking function work like a charm but i get some TypeErrors

Uncaught TypeError: t.test is not a function
    at yett:1
    at Array.every (<anonymous>)
    at o (yett:1)
    at r (yett:1)
    at MutationObserver.<anonymous> (yett:1)Uncaught TypeError: t.test is not a function

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.