AdguardTeam / Scriptlets

AdGuard scriptlets library
GNU General Public License v3.0
150 stars 30 forks source link

Fix 'trusted-replace-node-text' — output literal quotes for escaped quotes #440

Open Yuki2718 opened 3 months ago

Yuki2718 commented 3 months ago

Filters like

exploader.net#%#//scriptlet('trusted-replace-node-text', 'script', 'ダウンロード', '/(window\.[0-9A-z]+[\S\s]+ele\.outerHTML;\})/', '$1;document.addEventListener("DOMContentLoaded", (async function(){const t="undefined"!=typeof dl_link?Object.values(dl_link):null,e=document.querySelector(\'a.dl_button[href*="?"]\');if(!t||!e)return;const n=e.getAttribute("href");let l=null;t.forEach((async t=>{const e=n.replace(/\?.*/,`?${t}`);await(async t=>{const e=await fetch(t);return!(await e.text()).includes("広告ブロック")})(e)&&(l=t,document.querySelectorAll(\'a.dl_button[href*="?"]\').forEach((t=>{let e=t.getAttribute("href");e=e.replace(/\?.*/,`?${l}`),t.setAttribute("href",e)})))}))}))')

outputs literal \' and not ' so can't be used.

Yuki2718 commented 3 months ago

Somewhat related but different: https://github.com/AdguardTeam/Scriptlets/issues/286

slavaleleka commented 3 months ago

if single quotes are used to wrap a scriptlet parameter, the same single quote should be escaped inside the parameter:

valid: 'prop[\'nested\']' not valid: 'prop['nested']' https://github.com/AdguardTeam/Scriptlets?tab=readme-ov-file#scriptlet-syntax

Yuki2718 commented 3 months ago

So how to make

exploader.net#%#//scriptlet('trusted-replace-node-text', 'script', 'ダウンロード', '/(window\.[0-9A-z]+[\S\s]+ele\.outerHTML;\})/', '$1;document.addEventListener("DOMContentLoaded", (async function(){const t="undefined"!=typeof dl_link?Object.values(dl_link):null,e=document.querySelector(\'a.dl_button[href*="?"]\');if(!t||!e)return;const n=e.getAttribute("href");let l=null;t.forEach((async t=>{const e=n.replace(/\?.*/,`?${t}`);await(async t=>{const e=await fetch(t);return!(await e.text()).includes("広告ブロック")})(e)&&(l=t,document.querySelectorAll(\'a.dl_button[href*="?"]\').forEach((t=>{let e=t.getAttribute("href");e=e.replace(/\?.*/,`?${l}`),t.setAttribute("href",e)})))}))}))')

work? Adam proposed a workaround on Slack:

exploader.net#%#//scriptlet('trusted-replace-node-text', 'script', 'ダウンロード', '/(window\.[0-9A-z]+[\S\s]+ele\.outerHTML;\})/', '$1;document.addEventListener("DOMContentLoaded", (async function(){const t="undefined"!=typeof dl_link?Object.values(dl_link):null,e=document.querySelector("a.dl_button[href*=\"?\"]");if(!t||!e)return;const n=e.getAttribute("href");let l=null;t.forEach((async t=>{const e=n.replace(/\?.*/,`?${t}`);await(async t=>{const e=await fetch(t);return!(await e.text()).includes("広告ブロック")})(e)&&(l=t,document.querySelectorAll("a.dl_button[href*=\"?\"]").forEach((t=>{let e=t.getAttribute("href");e=e.replace(/\?.*/,`?${l}`),t.setAttribute("href",e)})))}))}))')

but this really is just a workaround and not the fundamental cure.

ameshkov commented 3 months ago

Debug it via browser console, make it a valid JS function call:

var scriptlet = function() {
    console.log('test');
}

scriptlet('trusted-replace-node-text', 'script', 'ダウンロード', '/(window\.[0-9A-z]+[\S\s]+ele\.outerHTML;\})/', '$1;document.addEventListener("DOMContentLoaded", (async function(){const t="undefined"!=typeof dl_link?Object.values(dl_link):null,e=document.querySelector("a.dl_button[href*=\"?\"]");if(!t||!e)return;const n=e.getAttribute("href");let l=null;t.forEach((async t=>{const e=n.replace(/\?.*/,`?${t}`);await(async t=>{const e=await fetch(t);return!(await e.text()).includes("広告ブロック")})(e)&&(l=t,document.querySelectorAll("a.dl_button[href*=\"?\"]").forEach((t=>{let e=t.getAttribute("href");e=e.replace(/\?.*/,`?${l}`),t.setAttribute("href",e)})))}))}))')
AdamWr commented 3 months ago

I think that it's a correct issue.

Simple steps to reproduce:

  1. Add this rule:
    fiddle.jshell.net#%#//scriptlet('trusted-replace-node-text', 'script', 'alert', '/alert\(\'test\'\)/', 'alert(\'replaced\')')
  2. Go to - https://jsfiddle.net/6p7eq9an/
    <script>
    alert('test');
    </script>

It should replaces alert('test') with alert('replaced') but it changes it to alert(\'replaced\'), so script is broken and alert is not displayed.

Screenshot ![image](https://github.com/user-attachments/assets/0dc65227-bde0-4f2b-ad8d-a7ddadf7abd1)