An extension that injects external scripts into web pages becomes a forced intermediary between the user and every site they visit. The injected script runs with the page's origin permissions and can read DOM content, capture form data, and exfiltrate it to any server the injected script chooses — entirely outside the extension's declared permissions. CWE-94 (Code Injection) and OWASP 2021 A03 describe this attack class. Chrome Web Store policy prohibits executing remote code, and Manifest V3 enforces this mechanically — but content scripts that dynamically create <script> tags pointing to external domains achieve the same effect and are a common policy violation. Any injected external script is a persistent supply chain risk: if that third party is compromised, every user of your extension is compromised.
High because injecting external scripts into web pages grants an uncontrolled third party the ability to read and exfiltrate any data visible to that page — the extension becomes an involuntary attack vector for every site the user visits.
Remove all dynamic external script injection from content scripts. Bundle dependencies locally and use fetch-based API calls for external data:
// BAD — injects external script into every page
const s = document.createElement('script');
s.src = 'https://cdn.example.com/tracker.js';
document.head.appendChild(s);
// GOOD — bundle locally or use fetch
import { feature } from './local-bundle.js';
// For external data, use the extension's background service worker
chrome.runtime.sendMessage({ type: 'FETCH_DATA' }, (response) => {
// Use response.data in content script
});
For Manifest V3, the content_security_policy in manifest.json can enforce this at the browser level — set "script-src 'self'" to block injection from external origins entirely.
ID: extension-data-privacy.privacy-disclosures.no-script-injection
Severity: high
What to look for: Examine manifest and content scripts for any patterns that inject external scripts into web pages. Look for <script src="https://external-service.com/...">, use of eval(), innerHTML with untrusted content, or chrome.scripting.executeScript() that loads from external sources. Check for "execute remote code" patterns.
Pass criteria: Count all document.createElement('script') and chrome.scripting.executeScript calls in the codebase. No external scripts are injected into web pages -- 0% of script injections may load from external domains. Content scripts are bundled locally and evaluated only after validation. If scripting is necessary, it uses trusted sources only with strict CSP or manifest v3 sandbox rules.
Fail criteria: Extension injects third-party scripts from external domains without user knowledge (e.g., loading Google Analytics, tracking pixels, or ad networks into every page). Remote code execution patterns found. Do not pass when eval() or new Function() is used with externally sourced content.
Cross-reference: For DOM capture concerns in content scripts, see the no-sensitive-dom-capture check in the Data Collection category.
Skip (N/A) when: Never — script injection is a critical security concern.
Detail on fail: Name the injected scripts. Example: "Content script injects <script> tags pointing to 'https://ads.example.com/tracker.js' into every page" or "Background script executes downloaded JavaScript from external API without validation."
Remediation: Bundle scripts locally, don't inject external ones. If integration with external services is necessary, use APIs rather than script injection:
// BAD: script injection
const script = document.createElement('script');
script.src = 'https://external.com/lib.js';
document.head.appendChild(script);
// GOOD: local bundle or API call
import { feature } from './local-lib.js';
fetch('https://api.example.com/data')
.then(r => r.json())
.then(data => {/* use data */});