Scriptlets icon indicating copy to clipboard operation
Scriptlets copied to clipboard

Improve `prevent-fetch` — return original url in response

Open AdamWr opened this issue 3 years ago • 1 comments

  • [ ] Return original URL
  • [ ] Improve Response.type - https://github.com/AdguardTeam/Scriptlets/issues/206#issuecomment-1158621281 (first part of the comment)
  • [ ] Fix issue with : in URL - https://github.com/AdguardTeam/Scriptlets/issues/216#issuecomment-1178591463

At the moment, if prevent-fetch is used then Response.url returns empty string, but sometimes websites are checking Response.url to detect ad blockers.

Steps to reproduce

  1. Add this rule to user rules:
example.org#%#//scriptlet('prevent-fetch', 'pagead2.googlesyndication.com')
  1. Go to - https://example.org/
  2. Run this script:
(async () => {
  const ads = "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js";
  const getAds = await fetch(ads);

  if (getAds.status === 200 && getAds.url === ads) {
    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;
  } else {
    alert("Request blocked - AdBlock");
  }
})();

Alert is displayed because getAds.url returns '', if request is not blocked then video player should be loaded.

What if would add fetchData.url as another argument in: https://github.com/AdguardTeam/Scriptlets/blob/2a72d26aaca94c9e32bc30703beca43103935346/src/scriptlets/prevent-fetch.js#L133 something like:

noopPromiseResolve(strResponseBody, fetchData.url); 

and add Object.defineProperty(response, 'url', { value: url, writable: false }); to: https://github.com/AdguardTeam/Scriptlets/blob/af1d5ad03ff80ade59a990b45b71f6589db5ff99/src/helpers/noop.js#L59-L70 For example:

export const noopPromiseResolve = (responseBody = '{}', url) => { 
     if (typeof Response === 'undefined') { 
         return; 
     } 
     // eslint-disable-next-line compat/compat 
     const response = new Response(responseBody, { 
         status: 200, 
         statusText: 'OK', 
     }); 
     Object.defineProperty(response, 'url', { value: url, writable: false });
     // eslint-disable-next-line compat/compat, consistent-return 
     return Promise.resolve(response); 
 };

It should causes that original url will be returned.

AdamWr avatar May 29 '22 15:05 AdamWr

One more thing. If there is : in URL which we want to "block" then rule doesn't work correctly. Steps to reproduce

  1. Add this rule:
example.org#%#//scriptlet("prevent-fetch", "/^https?:\/\/example\.org/")
  1. Go to - https://example.org/
  2. Run this script in console
fetch('https://example.org/', { method: 'GET' });

Request should be "blocked", but it isn't. It works fine if I replace : with .:

example.org#%#//scriptlet("prevent-fetch", "/^https?.\/\/example\.org/")

As far as I understand, problem is in parseMatchProps function https://github.com/AdguardTeam/Scriptlets/blob/3baf8d9468d9686f478c7492c21311dfc8c46b0f/src/scriptlets/prevent-fetch.js#L114

https://github.com/AdguardTeam/Scriptlets/blob/3baf8d9468d9686f478c7492c21311dfc8c46b0f/src/helpers/fetch-utils.js#L67-L86 because it splits /^https?:\/\/example\.org/ into /^https? and \/\/example\.org/, so it doesn't work like it should. I suppose that it's for init options, like method or headers, but currently it also splits URL.

I guess that the same issue is with prevent-xhr.

AdamWr avatar Jul 08 '22 06:07 AdamWr