From 55400a64c6789998fc8065d4a27b579c1a415022 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 31 Aug 2025 02:04:51 +0000 Subject: [PATCH 1/2] feat: Add feature for excluded websites with injection fix This commit introduces a new feature that allows users to specify a list of excluded websites where the extension will not run. It also includes a fix to ensure programmatic script injections respect this exclusion list. The following changes have been made: - **options.html**: Added a new input field for excluded websites. - **options.js**: Implemented the logic to save and load the list of excluded websites from `chrome.storage.sync`. - **background.js**: - Modified the background script to read the list of excluded websites from storage and use it in the `excludeMatches` property when registering content scripts. - Added a helper function to convert match patterns to regular expressions. - Updated `updateExtensionScripts` to check a tab's URL against the exclusion list before programmatically injecting scripts with `executeScript`, addressing an oversight in the initial implementation. --- background.js | 55 +++++++++++++++++++++++++++++++++++++++++++++++++-- options.html | 6 ++++++ options.js | 9 ++++++++- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/background.js b/background.js index cf864cf..9d2b42a 100644 --- a/background.js +++ b/background.js @@ -40,6 +40,7 @@ async function save() { let result = await chrome.storage.session.set({state: temp}); } +var exclude = []; async function restore() { let result = await chrome.storage.session.get('state'); if (typeof result.state === 'object' && result.state !== null) { @@ -49,9 +50,10 @@ async function restore() { } state = result.state; } - let result2 = await chrome.storage.sync.get('options'); + let result2 = await chrome.storage.sync.get(['options', 'exclude']); if (typeof result2.options === 'object' && result2.options !== null) options = result2.options; + if (Array.isArray(result2.exclude)) exclude = result2.exclude; resolveInitialization(); } @@ -61,6 +63,10 @@ restore(); chrome.storage.onChanged.addListener((result) => { if (typeof result.options === 'object' && result.options !== null) options = result.options.newValue; + if (typeof result.exclude === 'object' && result.exclude !== null) { + exclude = result.exclude.newValue; + updateContentScripts(); + } }); // On install display the options page so the user can give permissions. @@ -580,6 +586,48 @@ function toggleOption(o) { }); } +function matchPatternToRegExp(pattern) { + if (pattern === '') { + return /^(https?|file|ftp):\/\/.*/; + } + const match = /^(.*):\/\/([^/]+)(\/.*)$/.exec(pattern); + if (!match) { + console.error('Invalid pattern:', pattern); + return /^(?!)/; // Matches nothing + } + const [, scheme, host, path] = match; + const specialChars = /[\[\]\(\)\{\}\^\$\+\.\?]/g; + let re = '^'; + if (scheme === '*') { + re += '(https?|ftp)'; + } else { + re += scheme.replace(specialChars, '\\$&'); + } + re += ':\\/\\/'; + if (host === '*') { + re += '[^/]+'; + } else if (host.startsWith('*.')) { + re += '([^/]+\\.)?'; + re += host.substring(2).replace(specialChars, '\\$&'); + } else { + re += host.replace(specialChars, '\\$&'); + } + re += path.replace(specialChars, '\\$&').replace(/\*/g, '.*'); + re += '$'; + return new RegExp(re); +} + +function isUrlExcluded(url) { + return exclude.some((pattern) => { + try { + return matchPatternToRegExp(pattern).test(url); + } catch (e) { + console.error('Error matching pattern:', pattern, e); + return false; + } + }); +} + async function updateContentScripts() { await initializationCompletePromise; await chrome.scripting.unregisterContentScripts(); @@ -590,6 +638,7 @@ async function updateContentScripts() { id: 'ContentScript', js: ['ContentScript.js'], matches: p.origins, + excludeMatches: exclude, allFrames: true, matchOriginAsFallback: true, runAt: 'document_start' @@ -598,6 +647,7 @@ async function updateContentScripts() { id: 'WindowScript', js: ['WindowScript.js'], matches: p.origins, + excludeMatches: exclude, allFrames: true, runAt: 'document_start', world: 'MAIN' @@ -611,8 +661,9 @@ async function updateExtensionScripts() { await updateContentScripts(); const tabs = await chrome.tabs.query({}); tabs.forEach(async (tab) => { - if (!tab.url || !tab.id) return; + if (!tab.url || !tab.id || isUrlExcluded(tab.url)) return; chrome.tabs.sendMessage(tab.id, {type: 'hi ya!'}).catch(async () => { + if (isUrlExcluded(tab.url)) return; await chrome.scripting.executeScript({ target: { tabId: tab.id, diff --git a/options.html b/options.html index 5033b0d..e658824 100644 --- a/options.html +++ b/options.html @@ -15,6 +15,12 @@

After changes press enter.

size="500" placeholder="Examples: youtube or or https://*.youtube.com/* https://soundcloud.com/* https://example.com/*" /> +

This permission is needed to pause, resume and fast forward media its not needed to detect audio. diff --git a/options.js b/options.js index 8f584ae..cd62a13 100644 --- a/options.js +++ b/options.js @@ -21,6 +21,7 @@ const supported = [ ]; var userinput = document.getElementById('userinput'); +var exclude = document.getElementById('exclude'); // User presses enter window.addEventListener('keyup', (event) => { @@ -34,11 +35,14 @@ chrome.permissions.onAdded.addListener(getPermissions); chrome.permissions.onRemoved.addListener(getPermissions); // Security: chrome.storage.sync is not safe from website content scripts. -chrome.storage.sync.get('options', (result) => { +chrome.storage.sync.get(['options', 'exclude'], (result) => { if (typeof result.options === 'object' && result.options !== null) { options = result.options; applyChanges(); } + if (Array.isArray(result.exclude)) { + exclude.value = result.exclude.join(' '); + } }); chrome.storage.onChanged.addListener((result) => { @@ -145,4 +149,7 @@ async function permissionUpdate() { } ); } + chrome.storage.sync.set({ + exclude: exclude.value.split(' ').filter((domain) => domain) + }); } From 49b7a25e196a07508c057c2e26206a5143f5c849 Mon Sep 17 00:00:00 2001 From: NDevTK <31563761+NDevTK@users.noreply.github.com> Date: Sun, 31 Aug 2025 03:07:42 +0100 Subject: [PATCH 2/2] Potential fix for code scanning alert no. 1: Incomplete string escaping or encoding Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- background.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/background.js b/background.js index 9d2b42a..b5a9662 100644 --- a/background.js +++ b/background.js @@ -596,7 +596,7 @@ function matchPatternToRegExp(pattern) { return /^(?!)/; // Matches nothing } const [, scheme, host, path] = match; - const specialChars = /[\[\]\(\)\{\}\^\$\+\.\?]/g; + const specialChars = /[\\[\]\(\)\{\}\^\$\+\.\?]/g; let re = '^'; if (scheme === '*') { re += '(https?|ftp)';