Hver /security <cmd> som produserer rapport printer nå en klikkbar file://-lenke til en self-contained HTML-versjon. Levert over fem sesjoner; sesjon 5 wirer de 14 resterende skill-filene + slipper v7.7.0 (versjonsbump + docs). Sesjon-historikk: - Sesjon 1 (0dc7ff4) — playground katalog list-view + builder-pane med copy-knapp på alle 18 rapporter - Sesjon 2 (86d6ecd) — playground prosjekt-surface opprydding (stub-screen + topbar-splitt) - Sesjon 3 (fa5fb48) — extract 18 inline parsers + 18 inline renderers fra playground til canonical ESM-modul scripts/lib/report-renderers.mjs (playground beholder bit-identisk inline-kopi siden ESM import ikke fungerer fra file://) - Sesjon 4 (db80854) — ny zero-dep CLI scripts/render-report.mjs (stdin/file/stdout-modus, kebab→camel commandId-routing, ~140 KB self-contained HTML med 6 inlined DS-stylesheets + lokal .report-table, absolutte file://-paths for Ghostty cmd-click). 4 skills wired: scan, audit, posture, deep-scan. - Sesjon 5 (denne) — 14 resterende skills wired: plugin-audit, mcp-audit, mcp-inspect, ide-scan, supply-check, dashboard, pre-deploy, diff, watch, registry, clean, harden, threat-model, red-team. Hver skill-fil har nå en HTML Report-step som instruerer Claude å skrive markdown verbatim, kjøre CLI, og appende klikkbar file://-lenke til respons. Release-arbeid: - Versjonsbump v7.6.1 → v7.7.0 i 6 plugin-filer + 2 rot-filer (package.json, .claude-plugin/plugin.json, README badge, CLAUDE.md header + state-seksjon, docs/version-history.md, plugin Recent versions- tabell, rot README plugin-entry, rot CLAUDE.md plugin-katalog) - CHANGELOG [7.7.0] med full historikk fra sesjon 1-5 - docs/version-history.md v7.7.0-seksjon Verifisert: - 18/18 commandIds i CLI gir > 138 KB self-contained HTML - 1819/1820 tester grønne (pre-compact-scan-perf-flake fyrte under last, passerer i isolasjon på 1582 ms — pre-eksisterende, defer til v7.7.x) - 18/18 skill-filer har HTML Report-step - Ingen kildefil-treff på 7.6.1 utenfor historiske changelog/version- history/README releases-tabell Ingen scanner- eller hook-atferdsendringer — purely additive surface. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
126 lines
6.2 KiB
Markdown
126 lines
6.2 KiB
Markdown
---
|
|
name: security:ide-scan
|
|
description: Scan installed VS Code + JetBrains (IntelliJ IDEA, PyCharm, GoLand, WebStorm, Android Studio, …) extensions/plugins for supply-chain risk, typosquats, obfuscation, and malicious patterns. Accepts Marketplace / OpenVSX / direct VSIX URLs and JetBrains Marketplace URLs.
|
|
allowed-tools: Read, Glob, Grep, Bash
|
|
model: sonnet
|
|
---
|
|
|
|
# /security ide-scan
|
|
|
|
Scan installed IDE extensions. Both families covered:
|
|
|
|
- **VS Code + forks** — Cursor, Windsurf, VSCodium, code-server, Insiders, Remote-SSH.
|
|
- **JetBrains plugins** — discovery from installed plugin dirs, URL fetch from JetBrains Marketplace. IntelliJ IDEA, PyCharm, GoLand, WebStorm, RubyMine, PhpStorm, CLion, DataGrip, RustRover, Rider, Aqua, Writerside, Android Studio. Fleet and Toolbox are excluded (different plugin model).
|
|
|
|
Runs the IDE scanner plus reused scanners (UNI, ENT, NET, TNT, MEM, SCR) per extension. Offline by default.
|
|
|
|
## Step 1: Run Scanner
|
|
|
|
Run the IDE extension scanner:
|
|
|
|
```
|
|
node <this plugin's scanners/ide-extension-scanner.mjs> [target]
|
|
```
|
|
|
|
Arguments (pass through as provided by the user):
|
|
- `[target]` — one of:
|
|
- omit, `.`, or `all` → discover all installed extensions
|
|
- absolute path to an extracted extension directory → single-scan mode
|
|
- `https://marketplace.visualstudio.com/items?itemName=<publisher>.<name>` → fetch from VS Code Marketplace
|
|
- `https://open-vsx.org/extension/<publisher>/<name>[/<version>]` → fetch from OpenVSX
|
|
- `https://example.com/path/foo.vsix` → direct VSIX download (HTTPS only)
|
|
- `https://plugins.jetbrains.com/plugin/7973-intellivue` → JetBrains Marketplace (numericId resolved to xmlId via metadata, then downloaded)
|
|
- `https://plugins.jetbrains.com/plugin/download?pluginId=com.example.plugin[&version=1.2.3]` → direct JetBrains plugin download
|
|
- GitHub repo URLs are NOT supported (would require build step)
|
|
- `--vscode-only` / `--intellij-only` — restrict discovery
|
|
- `--include-builtin` — include Microsoft builtin extensions (default: excluded)
|
|
- `--online` — enable Marketplace/OSV.dev lookups (opt-in; default: fully offline)
|
|
- `--format compact|json` — output format
|
|
- `--fail-on <severity>` — exit 1 if findings at/above severity
|
|
|
|
URL mode notes:
|
|
- Hardened ZIP extractor with caps: 50MB compressed, 500MB uncompressed, 100x expansion ratio, 10 000 entries, depth 20. No fuzz-testing results published to date.
|
|
- Rejects: zip-slip paths, symlink entries, absolute paths, drive letters, encrypted entries, ZIP64.
|
|
- TLS verified, HTTPS only, 30s timeout. Cross-host redirects rejected.
|
|
- Temp directory always cleaned up (success, error, abort).
|
|
- `meta.source` in the envelope contains `{ type: "url", kind, url, finalUrl, sha256, size, publisher, name, version }`.
|
|
|
|
Parse the JSON output. The result contains:
|
|
- `meta.scanner`, `meta.version`, `meta.target`, `meta.extensions_discovered` (per type), `meta.roots_scanned`, `meta.warnings`
|
|
- `extensions[]` — per-extension results with `id`, `version`, `type`, `publisher`, `source`, `is_builtin`, `signed`, `scanner_results` (IDE/UNI/ENT/NET/TNT/MEM/SCR), `aggregate` (counts, risk_score, risk_band, verdict), `warnings`
|
|
- `aggregate` — top-level counts, risk_score, risk_band, verdict, extensions_total, extensions_blocked, extensions_warning
|
|
|
|
## Step 2: Format Report
|
|
|
|
Present the results:
|
|
|
|
```
|
|
# IDE Extension Scan
|
|
|
|
| Field | Value |
|
|
|-------|-------|
|
|
| **Scanner** | ide-extension-scanner v[version] |
|
|
| **Target** | [target] |
|
|
| **Roots** | [comma-separated roots_scanned] |
|
|
| **Extensions** | [vscode] VS Code, [jetbrains] JetBrains |
|
|
| **Top Verdict** | [ALLOW/WARNING/BLOCK] |
|
|
| **Risk** | [risk_score]/100 ([risk_band]) |
|
|
| **Duration** | [duration_ms]ms |
|
|
|
|
## Counts
|
|
|
|
crit=[N] high=[N] medium=[N] low=[N] info=[N]
|
|
|
|
## Per-Extension Results
|
|
|
|
[One row per extension, sorted: BLOCK first, then WARNING, then ALLOW with findings]
|
|
|
|
| Extension | Version | Source | Verdict | Risk | Top Issue |
|
|
|-----------|---------|--------|---------|------|-----------|
|
|
|
|
Omit ALLOW rows with zero findings unless the user passed `--verbose`.
|
|
|
|
## Top Findings
|
|
|
|
[For each extension with verdict != ALLOW, list up to 3 findings as:
|
|
- [SEV] [SCANNER]: title — file:line — recommendation]
|
|
|
|
## Warnings
|
|
|
|
[Any top-level or per-extension `warnings` entries, if present]
|
|
```
|
|
|
|
## Step 3: Recommendations
|
|
|
|
- `aggregate.verdict === 'BLOCK'`: "One or more extensions are block-listed. Uninstall immediately — `code --uninstall-extension <id>`."
|
|
- `aggregate.verdict === 'WARNING'`: "High/medium findings detected. Review the Top Findings list. Audit suspicious extensions before continuing."
|
|
- `aggregate.verdict === 'ALLOW'` and counts.info > 0: "Extensions look clean. Info-level findings are observational only."
|
|
- `aggregate.extensions_total === 0`: "No extensions discovered. Run `code --list-extensions` to confirm, or pass a specific path."
|
|
|
|
If the user has many sideloaded (`source=vsix`) extensions: suggest re-installing from Marketplace where possible.
|
|
|
|
## Notes
|
|
|
|
- First run with no `--online` is fully offline.
|
|
- Pass a single extracted extension directory to scan just one extension.
|
|
- JetBrains plugins are additionally checked for `Premain-Class` javaagents, `application-components` lifecycle hooks, native binaries (`.so`/`.dylib`/`.dll`/`.jnilib`), long `<depends>` chains, typosquats vs top JetBrains plugins, and shaded-jar advisories (see `knowledge/ide-extension-threat-patterns.md`).
|
|
|
|
## Step 4: HTML Report
|
|
|
|
After producing the markdown IDE-scan report above:
|
|
|
|
1. Compute a temp markdown path:
|
|
```bash
|
|
node -p "require('path').join(require('os').tmpdir(), 'sec-ide-scan-' + Date.now() + '.md')"
|
|
```
|
|
2. Use the Write tool to save the **entire markdown report you just produced** (scanner header table + counts + Per-Extension Results table + Top Findings + Warnings + Recommendations + Notes) to that temp path. Verbatim.
|
|
3. Run the renderer:
|
|
```bash
|
|
node <plugin-root>/scripts/render-report.mjs ide-scan --in "<temp-md-path>"
|
|
```
|
|
The CLI writes `reports/ide-scan-<YYYYMMDD-HHmmss>.html` relative to CWD and prints `file:///abs/path.html` on stdout.
|
|
4. Append to your response (markdown link, no bare URL):
|
|
|
|
> **HTML-rapport:** [Åpne i nettleser](file:///abs/path.html)
|
|
|
|
If the CLI exits non-zero, mention the error but do not block — the markdown report above is the primary deliverable.
|