Back to skill

Security audit

chrome-extension

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent Chrome extension development guide, but several copyable examples expose powerful extension privileges with weak scoping and insufficient safety guidance.

Review this skill before installing if you will use it to generate production extensions. Treat the relay, bridge, OAuth, storage, and CSP-related examples as patterns that need hardening: use exact origin checks, narrow host permissions, operation-specific handlers, payload schemas, user-visible consent, PKCE/state for OAuth, and do not expose raw tokens or privileged fetch handlers to page code.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
references/network-csp.md:96
Finding

Privileged Network Relay Uses Caller-Controlled URLs and Bypassable Allowlist Validation

Content
View full analysis
{ if (message.type !== "__RELAY_FETCH") return false; // Security: validate sender is a content script in a tab if (!sender.tab?.id) { sendResponse({ error: "Unauthorized: not from a tab" }); return true; } // Security: validate URL against allowlist try { const url = new URL(message.url); const allowed = ALLOWED_ORIGINS.some((o) => message.url.startsWith(o)); if (!allowed) { sendResponse({ error: `URL not allowed: ${url.origin}` }); return true; } } catch { sendResponse({ error: "Invalid URL" }); return true; } fetch(message.url, message.init || {}) ``` The same unsafe design appears in the RPC example: ```typescript registerHandler("fetch.relay", async (params, sender) => { // Security: validate URL against allowlist const allowed = ["https://api.example.com", "https://cdn.example.com"]; if (!allowed.some((base) => params.url.startsWith(base))) { throw { code: 403, message: "URL not in allowlist" }; } const res = await fetch(params.url, { method: params.method ?? "GET", headers: params.headers, body: params.body, }); ``` Other quick-reference handlers perform an entirely unrestricted fetch: ```typescript if (msg.type === "FETCH") { const res = await fetch(msg.url); return { ok: res.ok, data: await res.text() }; } ``` ### Technical Analysis The service worker performs network requests using a URL, method, headers, and body supplied through extension messaging. ...[truncated 2541 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/messaging-rpc.md:307
Finding

Untrusted Web Page Messages Are Forwarded to Privileged Extension Handlers

Content
View full analysis
{ if (event.source !== window) return; if (event.data?.channel !== EXTENSION_CHANNEL) return; if (event.data?.direction !== "TO_EXTENSION") return; const { requestId, type, payload } = event.data; try { const response = await chrome.runtime.sendMessage({ type, payload }); window.postMessage( { channel: EXTENSION_CHANNEL, direction: "FROM_EXTENSION", requestId, response, }, window.location.origin, ); ``` A second reference provides an even more direct forwarding pattern: ```typescript // Main world script window.postMessage( { source: "MY_EXT_PAGE", type: "DATA", payload: window.appState }, "*", ); // Content script (isolated world) window.addEventListener("message", (event) => { if (event.source !== window) return; if (event.data?.source !== "MY_EXT_PAGE") return; chrome.runtime.sendMessage(event.data); }); ``` ### Technical Analysis A content script has access to extension messaging APIs that ordinary page JavaScript cannot call. Therefore, a page-to-content-script bridge is a security boundary and must expose only explicitly authorized capabilities. The examples rely on static marker strings such as `MY_EXT_BRIDGE` or `MY_EXT_PAGE`. These values are identifiers, not secrets. Any script executing in the page can observe or reproduce them. Likewise, `event.source === window` merely confirms that the event came from the current window; it does not distinguish trusted application code from hostile third-party scripts, injec ...[truncated 1867 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
references/service-worker.md:100
Finding

Session Storage Access Level Can Expose OAuth Tokens to Content Scripts

Content
View full analysis
p.startsWith("access_token=")) ?.split("=")[1]; await chrome.storage.session.set({ accessToken: token }); ``` The top-level Skill repeats the broad access-level recommendation: ```typescript chrome.storage.session.setAccessLevel({ accessLevel: "TRUSTED_AND_UNTRUSTED_CONTEXTS", }); ``` ### Technical Analysis `chrome.storage.session` is unavailable to content scripts by default. Setting its access level to `TRUSTED_AND_UNTRUSTED_CONTEXTS` expands access to content-script contexts. The guidance applies this setting to the entire session storage area rather than separating non-sensitive content-script state from service-worker-only secrets. The same document recommends storing an OAuth bearer token in `chrome.storage.session`. As a result, extensions that combine these examples may make the bearer token directly readable from content scripts. Although isolated-world content scripts are separated from page JavaScript, they remain a larger and less trusted attack surface than the service worker. They parse page-controlled DOM data, may include bridge logic, and run on website origins selected by manifest match patterns. A content-script vulnerability can consequently become token disclosure. ### Attack Path 1. The extension obtains ...[truncated 956 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
references/service-worker.md:279
Finding

Generic OAuth Flow Omits State Validation and PKCE

Content
View full analysis
.chromiumapp.org/ const authUrl = new URL("https://provider.com/oauth/authorize"); authUrl.searchParams.set("client_id", CLIENT_ID); authUrl.searchParams.set("redirect_uri", redirectUrl); authUrl.searchParams.set("response_type", "token"); authUrl.searchParams.set("scope", "read write"); const responseUrl = await chrome.identity.launchWebAuthFlow({ url: authUrl.toString(), interactive: true, }); const url = new URL(responseUrl); const token = url.hash .split("&") .find((p) => p.startsWith("access_token=")) ?.split("=")[1]; await chrome.storage.session.set({ accessToken: token }); ``` ### Technical Analysis The example uses the OAuth implicit grant (`response_type=token`) and accepts an access token from the redirect fragment. It does not: - Generate a cryptographically random `state` value. - Bind that state to the initiating browser session. - Verify the state returned by the authorization server. - Use Authorization Code with Proof Key for Code Exchange. - Verify that the returned URL has the exact expected redirect origin and path. - Robustly parse and reject duplicate or malformed response parameters. Without state validation, the authorization response is not cryptographically bound to the request initiated by the extension. This can enable login CSRF or authorization-response substitution under applicable provider and attacker conditions. The implicit flow also places the bearer token directly in the front-channel redirect and is no longer preferred for public clients. This is a generic documentation example using placeholder provider details, not evidence of an active malicious OAuth endpoint. ### Attack Path 1. The extension starts the documented OAuth impl ...[truncated 1149 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (34)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 35)May include surrounding context.

md
| `references/ui-surfaces.md` | Popup, options page, side panel, context menus, commands, notifications, omnibox, devtools panel |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 140)May include surrounding context.

md
| `references/ui-surfaces.md` | Popup, options page, side panel, context menus, commands, notifications, omnibox, devtools panel |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 194)May include surrounding context.

md
| `references/ui-surfaces.md` | Popup, options page, side panel, context menus, commands, notifications, omnibox, devtools panel |

Exfiltration Commands

High
Category
Prompt Injection
Confidence
90% confidence
Finding

Instructions found that direct the agent to transmit conversation context or user data to external services.

Content

Scanner excerpt · references/execution-contexts.md (reported line 296)May include surrounding context.

md
| Limit | Impact | Workaround |
| --- | --- | --- |
| Page CSP blocks fetch to external APIs | Cannot call your API directly | Relay through service worker (the standard pattern) |
| No chrome.tabs, chrome.scripting, etc. | Cannot manage tabs or inject into other pages | Send message to SW, let SW handle it |
| Orphaned on extension update | All chrome.runtime calls throw | Check `chrome.runtime?.id` before calls; show refresh banner |
| Cannot access page JS variables | Cannot read SPA state, intercept fetch | Inject a main-world script and bridge via postMessage |
| Page can't see content script variables | Page JS cannot call your functions | Use window.postMessage or custom DOM events |

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Confidence
75% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · references/manifest-v3.md (reported line 96)May include surrounding context.

md
temporary access to current tab on user gesture
    "scripting", // chrome.scripting.executeScript
    "alarms", // chrome.alarms
    "contextMenus", // chrome.contextMenus
    "notifications", // chrome.notifications
    "sidePanel", // chrome.sidePanel
    "offscreen", // chrome.offscreen
    "tabs", // chrome.tabs (url, title, favIconUrl access)
    "identity", // chrome.identity (OAuth)
    "cookies", // chrome.cookies
    "webNavigation", // chrome.webNavigation
    "declarativeNetRequest", // Rule-based network modification
    "declarativeNetRequestWithHostAccess" // Same + host permission check
  ],
  "optional_permissions": ["bookmarks", "history", "downloads"],
  "host_permissions": ["https://api.example.com/*"],
  "optional_host_permissions": ["https://*/*"],

  // === NETWORK RULES ===
  "declarative_net_request": {
    "rule_resources": [
      {
        "id": "ruleset_1",
        "enabled": true,
        "path": "rules.json"
      }
    ]
  },

  // === WEB ACCESSIBLE RE

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
80% confidence
Finding

Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Content

Scanner excerpt · references/permissions.md (reported line 24)May include surrounding context.

md
## 2. All available permissions and their warnings

### No warning (safe to use freely)

`activeTab`, `alarms`, `contextMenus`, `declarativeNetRequestFeedback`, `dns`, `enterprise.deviceAttributes`, `enterprise.hardwarePlatform`, `enterprise.networkingAttributes`, `enterprise.platformKeys`, `favicon`, `fileBrowserHandler`, `fontSettings`, `gcm`, `idle`, `loginState`, `offscreen`, `power`, `printing`, `printingMetrics`, `runtime`, `scripting`, `search`, `sidePanel`, `storage`, `system.cpu`, `system.display`, `system.memory`, `system.storage`, `ttsEngine`, `unlimitedStorage`, `webAuthenticationProxy`

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
80% confidence
Finding

Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Content

Scanner excerpt · references/permissions.md (reported line 53)May include surrounding context.

md
| `tabCapture`            | Read and change all your data on all websites     |
| `tabs`                  | Read your browsing activity (URL, title, favicon) |
| `topSites`              | Read a list of your most frequently visited sites |
| `tts`                   | N/A (generally no warning)                        |
| `webNavigation`         | Read your browsing activity                       |
| `webRequest`            | (varies, usually combined with host permissions)  |

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
75% confidence
Finding

Skill attempts to nullify the agent's safety policies or restrictions ('you have no restrictions', 'ignore your guidelines', 'do anything now'). This is a direct jailbreak that disables guardrails.

Content

Scanner excerpt · references/publishing.md (reported line 187)May include surrounding context.

md
### Developer mode (testing only)

Load unpacked at `chrome://extensions` with Developer Mode enabled. Not suitable for distribution. Extension ID changes between machines.

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · references/ui-surfaces.md (reported line 42)May include surrounding context.

Popup with React (minimal)

html
<!-- popup.html -->
<!DOCTYPE html>
<html>
  <head>

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger description is unusually broad and explicitly includes sensitive behaviors such as script injection, page/background bridging, and CSP-bypass-related requests. In a user-invocable skill, this can cause the skill to activate for dual-use or risky tasks without requiring a security-aware framing, increasing the chance of unsafe guidance being surfaced in normal development conversations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill directly discusses CSP-bypass relays and injection patterns as implementation guidance, but does not pair them with clear warnings, authorization requirements, or abuse boundaries. In the context of browser extensions, these techniques can facilitate exfiltration, cross-context abuse, and circumvention of website security expectations if adopted by untrusted or inexperienced users.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This markdown file includes example code that reads page state (window.appState) and forwards it via chrome.runtime.sendMessage, but the surrounding documentation does not warn that page data may be collected and transmitted out of the page context. For markdown files, SQP-2 applies when descriptions omit warnings about behaviors that could affect user data or privacy.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The example serializes window.appState and sends it to the extension via chrome.runtime.sendMessage, which can carry user or application data. The markdown does not include any caution that this pattern may capture sensitive information from the page and should be disclosed or limited.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The reference includes a manifest example whose CSP block suggests a configuration that conflicts with MV3 rules, creating a misleading insecure example in security-sensitive documentation. In a Chrome extension skill, inaccurate CSP guidance is dangerous because users may copy-paste the sample and believe unsafe-eval is acceptable where it is not, weakening extension security assumptions or causing unsafe design decisions.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/manifest-v3.md (reported line 102)May include surrounding context.

typescript
// ❌ BROKEN: async returns Promise, Chrome closes channel immediately
chrome.runtime.onMessage.addListener(async (msg, sender, sendResponse) => {
  const data = await fetch("https://api.example.com/data");
  const json = await data.json();
  sendResponse(json); // NEVER REACHES the sender
  return true; // Wrapped in Promise<true>, Chrome doesn't see it

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/messaging-rpc.md (reported line 63)May include surrounding context.

typescript
// ❌ BROKEN: async returns Promise, Chrome closes channel immediately
chrome.runtime.onMessage.addListener(async (msg, sender, sendResponse) => {
  const data = await fetch("https://api.example.com/data");
  const json = await data.json();
  sendResponse(json); // NEVER REACHES the sender
  return true; // Wrapped in Promise<true>, Chrome doesn't see it

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/messaging-rpc.md (reported line 543)May include surrounding context.

typescript
// ❌ BROKEN: async returns Promise, Chrome closes channel immediately
chrome.runtime.onMessage.addListener(async (msg, sender, sendResponse) => {
  const data = await fetch("https://api.example.com/data");
  const json = await data.json();
  sendResponse(json); // NEVER REACHES the sender
  return true; // Wrapped in Promise<true>, Chrome doesn't see it

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/network-csp.md (reported line 83)May include surrounding context.

typescript
// ❌ BROKEN: async returns Promise, Chrome closes channel immediately
chrome.runtime.onMessage.addListener(async (msg, sender, sendResponse) => {
  const data = await fetch("https://api.example.com/data");
  const json = await data.json();
  sendResponse(json); // NEVER REACHES the sender
  return true; // Wrapped in Promise<true>, Chrome doesn't see it

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/network-csp.md (reported line 296)May include surrounding context.

typescript
// ❌ BROKEN: async returns Promise, Chrome closes channel immediately
chrome.runtime.onMessage.addListener(async (msg, sender, sendResponse) => {
  const data = await fetch("https://api.example.com/data");
  const json = await data.json();
  sendResponse(json); // NEVER REACHES the sender
  return true; // Wrapped in Promise<true>, Chrome doesn't see it

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/permissions.md (reported line 107)May include surrounding context.

typescript
// ❌ BROKEN: async returns Promise, Chrome closes channel immediately
chrome.runtime.onMessage.addListener(async (msg, sender, sendResponse) => {
  const data = await fetch("https://api.example.com/data");
  const json = await data.json();
  sendResponse(json); // NEVER REACHES the sender
  return true; // Wrapped in Promise<true>, Chrome doesn't see it

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/permissions.md (reported line 119)May include surrounding context.

typescript
// ❌ BROKEN: async returns Promise, Chrome closes channel immediately
chrome.runtime.onMessage.addListener(async (msg, sender, sendResponse) => {
  const data = await fetch("https://api.example.com/data");
  const json = await data.json();
  sendResponse(json); // NEVER REACHES the sender
  return true; // Wrapped in Promise<true>, Chrome doesn't see it

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/messaging-rpc.md (reported line 416)May include surrounding context.

// ^ Settings const result = await sendTyped({ type: "FETCH_API", url: "https://api.com/data", }); // ^ { ok: boolean; status: number; data: string }

text

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This markdown file documents an RPC layer that performs outbound fetches (fetch.relay) and writes extension settings (settings.set), both of which can affect user privacy or extension state. The section explains implementation details but does not include any warning or disclosure to users/readers about these behaviors or their impact.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
83% confidence
Finding

This example explicitly demonstrates a service-worker fetch relay described as a CSP bypass path, which can become a confused-deputy channel if page or content-script inputs are forwarded without strong authorization and origin checks. Even though the server example later includes a URL allowlist, the client-side pattern and bridge context make it easy for developers to expose privileged cross-origin requests to untrusted page code.

Content

Scanner excerpt · references/messaging-rpc.md (reported line 520)May include surrounding context.

md
// CSP bypass: relay fetch through service worker
const apiResponse = await rpc["fetch.relay"]({
  url: "https://api.example.com/data",
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ query: "test" }),

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/network-csp.md (reported line 83)May include surrounding context.

md
}

// Usage in content script
const result = await relayFetch("https://api.example.com/data", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",

Static analysis

No suspicious patterns detected.