vulkro extension-audit
Audit installed editor and browser extensions for supply-chain and permission risks. Editor extensions (VS Code, Cursor, Windsurf, VSCodium) and browser extensions (Chrome, Chromium, Brave, Edge, Firefox) run with broad authority on the user's machine: they read every editor buffer or every browser tab, persist data across sessions, and call out to remote endpoints with whatever credentials the host process can reach. A compromised extension is a session- token incident for every site the user is signed into.
By default only editor extensions are scanned. Pass
--include-browser to also walk the Chromium-family per-profile
Extensions/<id>/<version>/manifest.json tree and the Firefox
per-profile extensions.json.
Usage
vulkro extension-audit [PATH] [FLAGS]
Arguments
| Argument | Description | Default |
|---|---|---|
PATH | A specific extension manifest file (Chromium manifest.json, VS Code / Cursor / Windsurf / VSCodium extensions.json, or Firefox extensions.json), or a directory containing one. When omitted, the well-known editor locations are scanned (and with --include-browser, browser locations too). | (omitted) |
Flags
| Flag | Description | Default |
|---|---|---|
--format, -f <FMT> | table, json, ndjson, sarif, gh-pr, junit, csv. | table |
--require-extension | Treat "no extensions found" as an error (exit 2) instead of a clean exit. Useful in CI jobs that expect an extension manifest to be present. | false |
--include-browser | Also walk Chromium-family + Firefox per-profile extension manifests. Editor-only by default to keep the audit scoped. | false |
--fail-on <SEVERITIES> | Comma-separated severities that should produce a non-zero exit. Same shape as vulkro scan --fail-on. | critical,high |
Exit codes
| Code | Meaning |
|---|---|
0 | No findings, or --require-extension not set and no extensions found. |
1 | Findings at or above the --fail-on threshold. |
2 | Error: malformed JSON, IO failure, or --require-extension set with no extensions found. |
Discovery paths
When <path> is omitted, the auditor walks the following:
Editors (always scanned)
~/.vscode/extensions/extensions.json~/.vscode-server/extensions/extensions.json~/.cursor/extensions/extensions.json~/.windsurf/extensions/extensions.json~/.vscodium/extensions/extensions.json~/Library/Application Support/{Code,Code - Insiders,Cursor,Windsurf,VSCodium}/User/extensions/extensions.json(macOS)~/.config/{Code,Cursor,Windsurf,VSCodium}/User/extensions/extensions.json(Linux)
Browsers (only with --include-browser)
Chromium-family per-profile extension directories
(<profile>/Extensions/<id>/<version>/manifest.json):
~/Library/Application Support/Google/Chrome/Default/Extensions(macOS)~/Library/Application Support/Chromium/Default/Extensions~/Library/Application Support/BraveSoftware/Brave-Browser/Default/Extensions~/Library/Application Support/Microsoft Edge/Default/Extensions~/Library/Application Support/Arc/User Data/Default/Extensions~/.config/{google-chrome,chromium,BraveSoftware/Brave-Browser,microsoft-edge}/Default/Extensions(Linux)Profile 1sibling of eachDefaultprofile
Firefox profile directories (the auditor reads the
extensions.json addons array under each profile dir):
~/Library/Application Support/Firefox/Profiles/(macOS)~/.mozilla/firefox/(Linux)
Missing paths are skipped silently.
Rules
The auditor emits stable finding IDs in the EXT-NNN family.
| ID | Rule | Severity |
|---|---|---|
| EXT-001 | Installed (ecosystem, identifier, version) matches the Vulkro compromised-extension catalog. | Critical / High (from catalog row) |
| EXT-002 | Manifest declares overbroad permissions: <all_urls> (or *://*/*), the debugger API, webRequestBlocking, or multiple broad API permissions declared together. | High / Medium |
| EXT-003 | Manifest CSP allows unsafe-eval or unsafe-inline inside script-src or default-src. Lets the extension load arbitrary JavaScript at runtime. | High |
Inventory envelope
--format json emits both findings and a discovery inventory:
{
"findings": [ /* SecurityFinding records */ ],
"extension_inventory": {
"paths_checked": [
"/Users/me/.cursor/extensions/extensions.json",
"/Users/me/Library/Application Support/Google/Chrome/Default/Extensions/pajkjnmeojmbapicmbpliphjmcekeaac/24.10.4/manifest.json"
],
"paths_with_configs": [
"/Users/me/.cursor/extensions/extensions.json"
],
"extensions": [
{
"identifier": "ms-python.python",
"version": "2025.0.0",
"source": "/Users/me/.cursor/extensions/extensions.json",
"ecosystem": "vscode-marketplace"
}
]
}
}
ecosystem is one of vscode-marketplace, open-vsx,
chrome-webstore, firefox-amo, or unknown. Aligns with the
catalog's ecosystem column so the same identifier can be looked up
across surfaces.
Examples
# Default: editor extensions only.
vulkro extension-audit
# Editor + browser surfaces.
vulkro extension-audit --include-browser
# Audit a single browser extension manifest explicitly.
vulkro extension-audit ~/Library/Application\ Support/Google/Chrome/Default/Extensions/pajkjnmeojmbapicmbpliphjmcekeaac/24.10.4/manifest.json
# CI gate: require at least one extension to exist and fail on
# critical or high findings.
vulkro extension-audit --require-extension --fail-on critical,high
# Stream NDJSON into jq.
vulkro extension-audit --include-browser --format ndjson | jq 'select(.severity == "critical")'
Related
vulkro mcp-audit- same shape, but for MCP host configs.vulkro scan- the broader pipeline; SUP-COMPROMISE-006 (compromised extension via the catalog) also surfaces here when an extension manifest appears in a scanned tree.vulkro respond- "is THIS advisory or package in my project?" in under a second.vulkro explain EXT-001(and the rest) renders per-rule rationale even when no live finding hits.- Confidence model - how EXT findings are calibrated against the per-category rubric.