
Security News
/Research
Fake Corepack Site Distributes Infostealer and Proxyware to Developers
A fake corepack.org site is impersonating the Node.js tool and delivers an infostealer and proxyware to developers who download it.
webext-content-scripts
Advanced tools
Utility functions to inject content scripts in WebExtensions, for Manifest v2 and v3
Utility functions to inject content scripts in WebExtensions, for Manifest v2 and v3.
scripting permissionchrome.tabs or chrome.scripting APIsSponsored by PixieBrix :tada:
You can download the standalone bundle and include it in your manifest.json. Or use npm:
npm install webext-content-scripts
// This module is only offered as a ES Module
import {
executeScript,
insertCSS,
injectContentScript,
executeFunction,
canAccessTab,
assertTabAccess,
} from 'webext-content-scripts';
executeScriptLike chrome.tabs.executeScript but:
executeScript({
tabId: 1,
frameId: 20,
files: ['react.js', 'main.js'],
});
executeScript({
tabId: 1,
frameId: 20,
files: [
{file: 'react.js'},
{code: 'console.log(42)'}, // This will fail on Manifest v3
],
});
insertCSSLike chrome.tabs.insertCSS but:
insertCSS({
tabId: 1,
frameId: 20,
files: ['bootstrap.css', 'style.css'],
});
insertCSS({
tabId: 1,
frameId: 20,
files: [
{file: 'bootstrap.css'},
{code: 'hmtl { color: red }'}
],
});
injectContentScript(targets, scripts)It combines executeScript and injectCSS in a single call. You can pass the entire content_script object from the manifest too, without change (even with snake_case_keys). It accepts either an object or an array of objects.
This can be a tab ID, an array of tab IDs, a specific tab/frame combination, an array of such combinations:
injectContentScript(1, scripts);
injectContentScript([1, 2], scripts)
injectContentScript({tabId: 1, frameId: 0}, scripts);
injectContentScript([{tabId: 1, frameId: 0}, {tabId: 23, frameId: 98765}], scripts);
// You can also use `webext-tools` to inject by URL as well
import queryTabsByUrl from 'webext-tools/query-tabs-by-url.js';
injectContentScript(await queryTabsByUrl(['https://example.com/*']), scripts);
const tabId = 42;
await injectContentScript(tabId, {
runAt: 'document_idle',
allFrames: true, // Default when passing frame-less tab IDs
matchAboutBlank: true,
js: [
'contentscript.js'
],
css: [
'style.css'
],
})
await injectContentScript({
tabId: 42,
frameId: 56
}, [
{
js: [
'jquery.js',
'contentscript.js'
],
css: [
'bootstrap.css',
'style.css'
],
},
{
runAt: 'document_start',
css: [
'more-styles.css'
],
}
])
const tabId = 42;
const scripts = browser.runtime.getManifest().content_scripts;
// `matches`, `exclude_matches`, etc are ignored, so you can inject them on any host that you have permission to
await injectContentScript(tabId, scripts);
executeFunction(tabId, function, ...arguments)executeFunction({tabId, frameId}, function, ...arguments)Like chrome.tabs.executeScript, except that it accepts a raw function to be executed in the chosen tab.
const tabId = 10;
const tabUrl = await executeFunction(tabId, () => {
alert('This code is run as a content script');
return location.href;
});
console.log(tabUrl);
Note: The function must be self-contained because it will be serialized.
const tabId = 10;
const catsAndDogs = 'cute';
await executeFunction(tabId, () => {
console.log(catsAndDogs); // ERROR: catsAndDogs will be undeclared and will throw an error
});
you must pass it as arguments:
const tabId = 10;
const catsAndDogs = 'cute';
await executeFunction(tabId, (localCatsAndDogs) => {
console.log(localCatsAndDogs); // It logs "cute"
}, catsAndDogs); // Argument
canAccessTab(tabId)canAccessTab({tabId, frameId})Checks whether the extension has access to a specific tab or frame (i.e. content scripts are allowed to run), either via activeTab permission or regular host permissions.
const tabId = 42;
const access = await canAccessTab(tabId);
if (access) {
console.log('We can access this tab');
chrome.tabs.executeScript(tabId, {file: 'my-script.js'});
} else {
console.warn('We have no access to the tab');
}
const access = await canAccessTab({
tabId: 42,
frameId: 56,
});
if (access) {
console.log('We can access this frame');
chrome.tabs.executeScript(42, {file: 'my-script.js', frameId: 56});
} else {
console.warn('We have no access to the frame');
}
assertTabAccess(tabId)assertTabAccess({tabId, frameId})Like canAccessTab, but instead of returning false on failure it throws whatever error the browser throws.
Note that you don't need to verify access before calling executeScript, just call executeScript directly and it will throw exactly the same way.
const tabId = 42;
await assertTabAccess(tabId); // throws if the extension has no access
chrome.tabs.repload(tabId);
isScriptableUrl(url)Browsers block access to some URLs for security reasons. This function will check whether a passed URL is blocked. Permissions and the manifest are not checked, this function is completely static. It will also returns false for any URL that doesn't start with http.
More info may be found on:
const url = 'https://addons.mozilla.org/en-US/firefox/addon/ghosttext/';
if (isScriptableUrl(url)) {
console.log('I can inject content script to this page if permitted');
} else {
console.log('Content scripts are never allowed on this page');
}
MIT © Federico Brigante
FAQs
Utility functions to inject content scripts in WebExtensions, for Manifest v2 and v3
The npm package webext-content-scripts receives a total of 10,839 weekly downloads. As such, webext-content-scripts popularity was classified as popular.
We found that webext-content-scripts demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
/Research
A fake corepack.org site is impersonating the Node.js tool and delivers an infostealer and proxyware to developers who download it.

Research
/Security News
A large-scale campaign abused GitHub Actions in compromised repositories to exploit CVE-2026-41940 in cPanel and WHM and steal server credentials.

Security News
Five frontier LLMs generated the same nonexistent package names, leaving 53 available for potential slopsquatting across PyPI and npm.