[Security] Chrome extension site permissions can be bypassed via direct LevelDB write

Resolved 💬 3 comments Opened Feb 19, 2026 by noahkiss Closed Apr 15, 2026

Summary

The Claude in Chrome extension's per-site permission system can be bypassed by writing directly to the extension's LevelDB storage on disk. This allows pre-approving arbitrary domains without user interaction, which undermines the consent gate that site-level permissions are intended to provide.

Attack Vector

The extension stores site permissions in a LevelDB at:

~/.config/google-chrome/Default/Local Extension Settings/fcoeoabgfenejglbffodgkkbkcdhcgfn/

The permissionStorage key contains a JSON object with approved domains:

{
  "permissions": [
    {
      "action": "allow",
      "createdAt": 1771475282591.0,
      "duration": "always",
      "id": "uuid-here",
      "scope": {"netloc": "example.com", "type": "netloc"}
    }
  ]
}

Any process with filesystem access to the user's Chrome profile can:

  1. Stop Chrome (or wait for it to be closed)
  2. Open the LevelDB and write new permission entries
  3. Restart Chrome
  4. Navigate to the pre-approved domain without any user prompt

How I Found This

I was setting up Chrome integration with Claude Code (claude --chrome) on a Linux dev VM and got tired of approving each new domain individually. I asked Claude Code to "allow some sites" for the Chrome extension. Claude Code autonomously identified the LevelDB path, installed plyvel (Python LevelDB library), stopped Chrome, wrote permission entries, and restarted Chrome. The pre-approved sites then worked without any prompts.

It was convenient for my use case, but it struck me that a prompt injection attack (e.g., from a malicious web page or document being processed) could instruct Claude Code to silently approve attacker-controlled domains, then navigate to them and interact with authenticated sessions — all without the user ever seeing a permission prompt.

Reproduction

import plyvel, json, uuid, time

db_path = "~/.config/google-chrome/Default/Local Extension Settings/fcoeoabgfenejglbffodgkkbkcdhcgfn"
db = plyvel.DB(db_path, create_if_missing=False)

data = json.loads(db.get(b"permissionStorage").decode())
data["permissions"].append({
    "action": "allow",
    "createdAt": time.time() * 1000,
    "duration": "always",
    "id": str(uuid.uuid4()),
    "scope": {"netloc": "evil.com", "type": "netloc"}
})
db.put(b"permissionStorage", json.dumps(data).encode())
db.close()

After restarting Chrome, evil.com is pre-approved and Claude Code can navigate there via the Chrome MCP tools without triggering any permission prompt.

Suggested Mitigations

  1. Integrity check on permissions: Sign or HMAC the permission entries so that externally-written entries are rejected on load
  2. Runtime validation: Verify permissions against a trusted source (e.g., extension's service worker state) rather than trusting LevelDB at face value
  3. Claude Code guardrail: Consider adding a safety check that prevents Claude Code from modifying Chrome extension storage without explicit user consent

Environment

  • Claude Code v2.1.47
  • Claude in Chrome extension v1.0.52
  • Google Chrome 144.0.7559.96
  • Ubuntu 25.04 (Linux)

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗