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.
Command reference
Generated from vulkro help extension-audit on vulkro 0.26.0. This block is the
authoritative flag, usage, and exit-code reference for this command; the
prose above is the friendly explanation. Do not edit this block by hand;
run npm run docs:cli after a release.
Check installed editor and browser extensions for risky permissions.
Audit installed editor and browser extensions for supply-chain and permission risks. By default only editor extensions are scanned (VS Code, Cursor, Windsurf, VSCodium); pass `--include-browser` to also scan Chromium-family + Firefox extension manifests.
Rules: EXT-001 (known-compromised extension version, catalog lookup), EXT-002 (overbroad permissions: `<all_urls>`, debugger, webRequestBlocking, or multiple broad API permissions together), EXT-003 (manifest CSP allows unsafe-eval / unsafe-inline inside script-src). See `vulkro explain EXT-001` (etc.) for the per-rule rationale and remediation.
Path discovery: * If `<path>` is given and exists as a file, scan that file only. * If `<path>` is given and is a directory, scan it as a well-known editor extensions root (no recursion). * If `<path>` is omitted, scan the well-known editor extensions.json locations: - `~/.vscode/extensions/extensions.json` (Linux / generic) - `~/.cursor/extensions/extensions.json` - `~/.windsurf/extensions/extensions.json` - `~/.vscodium/extensions/extensions.json` - `~/Library/Application Support/Cursor/User/extensions/extensions.json` (macOS) - `~/.config/Cursor/User/extensions/extensions.json` (Linux) * With `--include-browser` the Chromium-family per-profile `Extensions/<id>/<version>/manifest.json` tree and the Firefox per-profile `extensions.json` are also walked under `~/Library/Application Support/Google/Chrome/...`, `~/.config/google-chrome/...`, `~/Library/Application Support/Firefox/Profiles/<random>.default/extensions.json`, and `~/.mozilla/firefox/<random>.default/extensions.json`.
Exit codes: `0` no findings (or `--require-extension` not set and no extensions found). `1` findings at or above `--fail-on` threshold. `2` error: malformed JSON, IO failure, or `--require-extension` set with no extensions found.
Usage: vulkro extension-audit [OPTIONS] [PATH]
Arguments:
[PATH]
Path to a specific extension manifest file (Chromium `manifest.json`, VS Code `extensions.json`, or Firefox `extensions.json`) or to a directory containing one. When omitted, the well-known editor extension locations are scanned (and with `--include-browser`, browser locations too)
Options:
-f, --format <FORMAT>
Output format
Possible values:
- table
- json
- sarif
- gh-pr
- gh-pr-inline-comments: GitHub PR per-finding **inline review comments**. NDJSON, one `{path, line, side, severity, rule_id, fingerprint, body}` object per line. Designed to be piped straight to a `gh api` loop so `vulkro gate` can drop comments next to the offending line on the Files Changed tab without going through a GitHub App. See the GitHub CLI integration guide at vulkro.com/docs
- github-annotations: GitHub Actions / GitLab CI **PR annotations**. One workflow-command line per finding (`::error file=...,line=...,endLine=...,title=<rule id>::<message> (<helpUri>)`). Printed straight to a CI job's stdout, GitHub turns each line into an inline annotation pinned to `file:line` on the Files Changed tab; GitLab CI's annotation parser reads the same grammar. When run against a baseline (`--gate-vs` / a `gate` flow) only NEW findings are annotated so a first run does not paper the PR
- gitlab-mr: GitLab Merge-Request comment: GitLab-flavored Markdown with collapsible blocks
- bitbucket-pr: Bitbucket Pull-Request comment: flat Markdown (Bitbucket does not render `<details>`)
- azure-pr: Azure DevOps Pull-Request comment: flat Markdown (shared with Bitbucket)
- junit
- csv
- cyclonedx: CycloneDX 1.6 JSON SBOM (uses `ScanResult.packages`)
- cyclonedx-1.7: CycloneDX 1.7 JSON SBOM. Same component shape as `cyclonedx`; emits the newer `specVersion`. Offered alongside 1.6 (which stays default)
- spdx: SPDX 2.3 JSON SBOM
- spdx3: SPDX 3.0.1 JSON-LD SBOM (`@context` + `@graph` of typed elements). Offered alongside SPDX 2.3, which stays the default for `--format spdx`
- cbom: CycloneDX 1.6 CBOM (Cryptographic Bill of Materials): one `cryptographic-asset` component per detected weak algorithm (MD5, SHA-1, ECB, RC4, DES, static IV, insecure RNG), with file:line occurrences inlined under `evidence.occurrences`. Compliance buyers (FedRAMP, post-quantum readiness reviews) ask for this as a distinct artefact from the library SBOM
- openvex: OpenVEX 0.2.0 exploitability statements for each dependency CVE (`affected` / `not_affected` / `under_investigation`), with `not_affected` backed by reachability analysis. Pairs with an SBOM
- cyclonedx-vex: CycloneDX 1.6 VEX: the same exploitability verdicts as `openvex`, in a CycloneDX `vulnerabilities[].analysis` document
- cbom-1.7: CycloneDX 1.7 CBOM. Same crypto-asset grouping as `cbom` plus a richer post-quantum descriptor. Offered alongside 1.6 (default)
- pdf: PDF render of the executive HTML report (requires `wkhtmltopdf` on PATH)
- ropa-md: GDPR Article 30 Records-of-Processing template - Markdown
- ropa-html: GDPR Article 30 Records-of-Processing template - HTML
- ndjson: Newline-delimited JSON: one finding per line plus a trailing summary line. Designed for SIEM ingestion and `jq` filter pipelines
- evidence-graph: Evidence-graph JSON (`evidence-graph/1.0`): a stable, versioned, AI-consumable document that composes endpoints, taint source -> sink flows, reachability verdicts, findings, and the dependency SBOM into one graph. Meant to be handed to an external AI agent as deterministic ground truth (Vulkro embeds no model; the agent brings its own). Pairs with `vulkro aggregate` for cross-repo linking. See `docs/ai-tool/`
[default: table]
--offline
Hard-disable every outbound network call for this run (sets VULKRO_OFFLINE=1). Blocks the CVE feed, the license heartbeat, the update check, webhooks, and any cloud AI endpoint; a loopback model (http://127.0.0.1, http://localhost) is still allowed. Equivalent to exporting VULKRO_OFFLINE=1, and the flag wins when both are set
--require-extension
Treat "no extensions found" as an error (exit 2) instead of a success (exit 0). Useful for CI jobs that should fail when the expected extension manifest isn't present. Off by default
--include-browser
Also scan Chromium-family + Firefox per-profile extension manifests. Editor-only by default to keep the audit scoped; pass this flag to widen the surface
--fail-on <SEVERITIES>
Severities that should produce a non-zero exit (comma list). Default `critical,high`. Same shape as `vulkro scan --fail-on`
[default: critical,high]
-h, --help
Print help (see a summary with '-h')