Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a scriptlet to change element.src/element.setAttribute #180

Closed
AdamWr opened this issue Jan 19, 2022 · 1 comment
Closed

Add a scriptlet to change element.src/element.setAttribute #180

AdamWr opened this issue Jan 19, 2022 · 1 comment

Comments

@AdamWr
Copy link
Member

AdamWr commented Jan 19, 2022

Sometimes websites are checking if some scripts/images are loaded (by using onload/onerror). If script/image is blocked then website displays alert about adblock or do not display content.

For example:

Code
(() => {
  var createScript = document.createElement("script");
  createScript.type = "text/javascript";
  createScript.async = !0;
  createScript.src = "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js";
  // createScript.setAttribute("src", "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js");

  createScript.onload = () => {
    let video = '<iframe width="560" height="315" src="https://www.youtube.com/embed/Fy2rtb95QhY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>';
    let createDiv = document.createElement("div");
    document.body.appendChild(createDiv);
    createDiv.innerHTML = video;
  };

  createScript.onerror = () => {
    alert("adblock");
  };

  document.body.appendChild(createScript);
})();

It can be checked on example.org, if you run above code in console and pagead2.googlesyndication.com/pagead/js/adsbygoogle.js is blocked, then alert adblock will be displayed, otherwise video player will be loaded.
It can be easily fixed by $redirect rule, but sometimes users have DNS filtering enabled and we have to use $replace rules.
Or, if I'm not wrong, it could be also useful in case of extension for Safari/iOS, as it doesn't support redirect rules.

So, maybe we could add a scriptlet which could replace src to empty function/image, by using data: (though it can be blocked by CSP on some websites, so maybe there is a better solution).

For example, for Element.prototype.setAttribute:

Code
// reg is a value of src which should be replacde by empty function/image
function wrapSetAttribute(reg) {
  var regex = new RegExp(reg);

  const setAttributeWrapper = (target, thisArg, args) => {
    if (args[0] && args[0] === 'src' && args[1] && typeof args[1] === 'string' && regex.test(args[1])) {
      console.log(`${args[0]}: ${args[1]}`);
	/* if script */
      if (thisArg instanceof HTMLScriptElement) {
        return Reflect.apply(target, thisArg, [args[0], 'data:text/javascript;base64,KCk9Pnt9']); /* "KCk9Pnt9" = "()=>{}" */
      } /* if image */
      else if (thisArg instanceof HTMLImageElement) {
        return Reflect.apply(target, thisArg, [args[0], 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==']);
      } else {
        return Reflect.apply(target, thisArg, args);
      }
    } else {
      return Reflect.apply(target, thisArg, args);
    }
  };

  const setAttributeHandler = {
    apply: setAttributeWrapper
  };
  Element.prototype.setAttribute = new Proxy(Element.prototype.setAttribute, setAttributeHandler);
}
// wrapSetAttribute("adsbygoogle");

And for HTMLScriptElement.prototype.src/HTMLImageElement.prototype.src, something like:

Code
// reg is a value of src which should be replaced by empty function/image
// type - "script" for "HTMLScriptElement"; "image" for "HTMLImageElement"
function wrapSRC(reg, type) {
  var regex = new RegExp(reg);
  var tag = type;

  if (!type) {
    return;
  }

  if (type === "script") {
    tag = HTMLScriptElement;
  }

  if (type === "image") {
    tag = HTMLImageElement;
  }

  Object.defineProperty(tag.prototype, "src", {
    enumerable: true,
    configurable: true,
    get: function () {
      if (!this.getAttribute("src")) {
        return "";
      } else {
        return this.getAttribute("src");
      }
    },
    set: function (src) {
      if (typeof src === "string" && (this instanceof HTMLScriptElement || this instanceof HTMLImageElement)) {
        console.log(`src: ${src}`);

        if (regex.test(src)) {
          if (type === "script") {
            this.setAttribute("src", "data:text/javascript;base64,KCk9Pnt9");
          }

          if (type === "image") {
            this.setAttribute("src", "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==");
          }
        } else {
          this.setAttribute("src", src);
        }
      } else if (src instanceof TrustedScriptURL) {
        /* it's needed for websites using Trusted Types, for example https://www.google.com/imghp
        https://w3c.github.io/webappsec-trusted-types/dist/spec/ */
        const dp = trustedTypes.createPolicy("default", {
          createHTML: function createHTML(arg) {
            return arg;
          },
          createScript: function createScript(arg) {
            return arg;
          },
          createScriptURL: function createScriptURL(arg) {
            return arg;
          }
        });
        this.setAttribute("src", dp.createScriptURL(src.toString()));
      } else {
        try {
          this.setAttribute("src", src.toString());
        } catch (e) {
          console.log(e);
        }
      }
    }
  });
}
// wrapSRC("adsbygoogle", "image");
// wrapSRC("adsbygoogle", "script");

I'm not sure if it's a good idea and if it's done correctly, but maybe it will be helpful to create a scriptlet for this.

@slavaleleka
Copy link
Contributor

so it's going to be new prevent-element-src-loading scriptlet

Prevents element source loading silently
example.org#%#//scriptlet('prevent-element-src-loading', tagName, match)

- tagName — required, case-insensitive target element tagName which `src` (property or attribute) resource loading will be prevented without it's onload breaking; possible values:
    - script
    - img
    - iframe
- match — required, string or regular expression for matching the element's URL;
example.org#%#//scriptlet('prevent-element-src-loading', 'script', 'adsbygoogle')

adguard pushed a commit that referenced this issue Feb 10, 2022
Merge in ADGUARD-FILTERS/scriptlets from feature/AG-12328 to release/v1.6

Squashed commit of the following:

commit 60a6328
Merge: a5d38bc 9af886f
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Wed Feb 9 19:51:52 2022 +0300

    Merge branch 'release/v1.6' into feature/AG-12328

commit a5d38bc
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Wed Feb 9 16:37:05 2022 +0300

    organize global vars for tests

commit dff7139
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Wed Feb 9 14:58:17 2022 +0300

    fix helper doc

commit 87b7eb8
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Wed Feb 9 14:56:46 2022 +0300

    add another falsy argument case

commit f1cc121
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Wed Feb 9 14:52:19 2022 +0300

    add mismatch and falsy arguments for setAttribute test

commit a1f06f7
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 19:30:35 2022 +0300

    move 'src' to a constant

commit a8ab0ad
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 19:18:05 2022 +0300

    change argument order in tests

commit d9591fd
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 19:09:41 2022 +0300

    fix arguments checking for setAttribute wrapper

commit 70ba79a
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 18:49:26 2022 +0300

    fix typo & description

commit 1e1f8f4
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 18:45:35 2022 +0300

    improve matching

commit d861485
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 18:15:44 2022 +0300

    fix description & swap 'search' param to 'match'

commit a7b1c22
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 17:22:38 2022 +0300

    revert wiki about-redirects

commit 1a92d7a
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 17:13:35 2022 +0300

    rename scriptlet to prevent-element-src-loading

commit daa6c1e
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 15:55:39 2022 +0300

    add proxy and reflect check

commit 3a45178
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 15:53:20 2022 +0300

    remove excessive assertions

commit 36c71c2
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 15:51:40 2022 +0300

    fix incorrect TrustedType checking

commit 23f7ccc
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 15:00:54 2022 +0300

    fix tests

commit 4119737
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 14:06:32 2022 +0300

    add getSafedescriptor helper & use original setter for src prop

commit db56a64
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 13:03:18 2022 +0300

    fix arguments checking in setAttribute

commit efcb160
Author: Stanislav A <s.atroschenko@adguard.com>
Date:   Tue Feb 8 12:54:16 2022 +0300

    start mocking only target element

... and 3 more commits
adguard pushed a commit that referenced this issue Mar 24, 2022
* commit 'c06a8e539488e1ebfcf72c85a39a0ef5b9c028bb': (32 commits)
  AG-13382 update docs, validate noopjson
  push updated dist to github while release build. AG-13356
  remove tagcommander redirect AG-11959
  add readme info about testing ad debugging AG-12716
  add artifacts to scriptlets builds AG-12487
  improve set-constant scriptlet #65 AG-149
  improve prevent-xhr scriptlet #199 AG-13007
  improve googletagservices-gpt redirect #193 AG-12767
  add wcslog redirect #94 AG-3906
  improve ati-smarttag redirect #200 AG-13141
  improve json-prune scriptlet #171 AG-11879
  fix yandex metrika on avito #198 AG-12973
  improve metrika-yandex-tag redirect #189 AG-12550
  add prebid-ads redirect #190 AG-12558
  remove no-floc scriptlet AG-12869
  add noopjson redirect. AG-12796 #195
  Revert "revert tests and fix converter again"
  revert tests and fix converter again
  add conversion test for wildcard tld AG-12636
  add prevent-element-src-loading scriptlet #180 AG-12328
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants