docs(llm-security): research brief + URL-support plan for ide-scan

- jetbrains-research-brief.md: 29 sources, confidence 0.88 — input for v6.6.0 JetBrains/IntelliJ extension scanning. Covers plugin format, install paths per OS+product, Marketplace API, threat landscape (zero confirmed-malicious cases), check-mapping table, sandbox reuse verdict, risk register.
- ide-scan-url-support.md: retroactive plan doc for v6.4.0 URL-fetch feature.
This commit is contained in:
Kjell Tore Guttormsen 2026-04-17 18:47:01 +02:00
commit fb9eb79d17
2 changed files with 442 additions and 0 deletions

View file

@ -0,0 +1,140 @@
# Plan: `/security ide-scan <url>` — ekstern URL-support (v6.4.0)
**Status:** Planlagt
**Mål-release:** v6.4.0
**Skrevet:** 2026-04-17
**Motivasjon:** Hovedbruk av `ide-scan` er pre-installasjonsverifisering — sjekk en extension FØR du installerer den. Dagens scanner krever at extension allerede er installert (`~/.vscode/extensions/`) eller at target peker til en allerede utpakket mappe.
## Støttede URL-typer
| Type | Eksempel | Fetch-strategi |
|------|----------|----------------|
| VS Code Marketplace | `https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code` | POST til `/_apis/public/gallery/.../vspackage` (undokumentert men stabilt mønster) |
| OpenVSX | `https://open-vsx.org/extension/anthropic/claude-code` | Offentlig API: `/api/{pub}/{name}/{version}/file/{pub}.{name}-{version}.vsix` |
| Direkte .vsix | `https://example.com/ext.vsix` | Enkel GET |
| GitHub repo | `https://github.com/anthropic/claude-code` | Bygg fra source? (se "Åpen beslutning") |
## Sikkerhetsmodell
VSIX er en ZIP-fil. Extraction er den største angrepsflaten — vi må forhindre:
- **Zip-slip** (`../../etc/passwd` i filnavn)
- **Symlink-angrep** (VSIX-spec tillater ikke symlinks, men parser må avvise dem)
- **Zip-bomber** (10MB komprimert → 100GB ukomprimert)
- **Path traversal** via absolutte paths i entry-navn
- **Unicode-normalization-angrep** (NFC/NFD-forskjeller som omgår path-checks)
**Caps:**
- Max komprimert størrelse: 50MB (VSIX over dette er mistenkelig)
- Max ukomprimert størrelse: 500MB
- Max entries: 10 000
- Max depth: 20
- Max expansion ratio: 100x (sum ukomprimert / sum komprimert)
## Arkitektur
```
/security ide-scan <url>
→ commands/ide-scan.md (dispatcher)
→ bin/llm-security.mjs eller direkte
→ scanners/ide-extension-scanner.mjs
├─ url-detect: pattern-match for supported URL typer
├─ lib/vsix-fetch.mjs (NY)
│ ├─ detectUrlType(url) → 'marketplace' | 'openvsx' | 'vsix' | 'github'
│ ├─ fetchMarketplaceVsix(publisher, name)
│ ├─ fetchOpenVsxVsix(publisher, name, version)
│ ├─ fetchDirectVsix(url)
│ └─ returnerer { buffer, url, contentType, size, sha256 }
├─ lib/zip-extract.mjs (NY)
│ ├─ zero-dep ZIP-parser (central directory + local file header + deflate via node:zlib)
│ ├─ validateEntry(name) — reject zip-slip, absolute paths, symlinks
│ └─ extractToDir(buffer, tempDir, caps) → Promise<void>
└─ Eksisterende scan-pipeline mot tempDir
```
## Implementasjonssteg
### Steg 1: `lib/vsix-fetch.mjs` (~150 linjer)
- Bruk `fetch()` (Node 18+, zero deps)
- Kun HTTPS, ingen redirect til HTTP
- Timeout 30s, size-cap 50MB via streaming + abort
- TLS-verifisering default (ikke tillat `--insecure`)
- SHA-256-beregning underveis
- Marketplace: POST-payload + header `Accept: application/octet-stream` — se knowledge/marketplace-api-notes.md (lag den)
- OpenVSX: offentlig dokumentert API, enklest
- Returnerer `{ buffer, sourceUrl, sha256, publisher, name, version }`
### Steg 2: `lib/zip-extract.mjs` (~250 linjer)
- Parse End of Central Directory (EOCD) fra slutten
- Les Central Directory headers → entries-array
- For hver entry: les Local File Header, inflate med `node:zlib.createInflateRaw`
- Valideringer PER entry (før skriv):
- Normaliser path (remove `.`, resolve `..`, reject absolute)
- Avvis hvis path escaper targetDir (via `path.resolve` + prefix-sjekk)
- Avvis `external_attr` som indikerer symlink (0xA000 flag)
- Akkumulere expansion ratio, abort hvis > 100x
- Kun filer, ingen dirs (opprett dirs on-the-fly)
- Tests: fixtures med kjente angrep (zip-slip fixture, symlink fixture, bomb fixture)
### Steg 3: Utvid `ide-extension-scanner.mjs`
- Early-detect: `if (target.startsWith('http')) → fetch + extract → scan(tempDir)`
- Ny option: `--online-source <marketplace|openvsx>` (default: auto-detect fra URL)
- Cleanup: `try/finally` med `rm(tempDir, { recursive: true, force: true })`
- Error-mapping: network errors → "unreachable", signature fail → "tamper", zip fail → "malformed"
- Envelope.meta.source: `{ type: 'url', url, sha256, publisher, name, version }`
### Steg 4: CLI + command-dispatcher
- `bin/llm-security.mjs`: passthrough, ingen endring nødvendig (target er allerede første arg)
- `commands/ide-scan.md`: oppdater eksempler + flagg-dokumentasjon
### Steg 5: Tester (~20 nye)
- Mock HTTP-server (node:http) for fetch-tester
- Fixture-VSIX: bygg en fra `tests/fixtures/ide-extensions/root-benign/publisher.benign-ext-1.0.0/` via `zip -r` i test-setup
- Zip-slip fixture: VSIX med `../../etc/passwd` entry
- Zip-bomb fixture: 1KB komprimert → 10GB ukomprimert (syntetisk)
- Integration: OpenVSX-mock → fetch → scan → envelope
- Unit: detectUrlType, extractToDir med caps
### Steg 6: Knowledge + docs
- `knowledge/marketplace-api-notes.md`: dokumenter undokumenterte Marketplace-endpoints + stabilitet
- `docs/ide-scan-url-usage.md`: eksempler for pre-install-workflow
- CHANGELOG: v6.4.0-seksjon
- Plugin README: legg til URL-eksempler på `/security ide-scan`
- Rot-README: oppdater til v6.4.0 (hvis tests passerer 1300+ og URL-support er feature-highlight)
### Steg 7: Versjon + ship
- `npm run bump -- 6.4.0`
- Test-suite grønn
- Smoke test: `llm-security ide-scan https://open-vsx.org/extension/anthropic/claude-code` — skal funke (åpen API)
- Commit + push til Forgejo (main, pre-autorisert)
## Åpne beslutninger (diskuter i neste sesjon)
1. **GitHub repo som URL** — skal vi bygge extension fra source? Legger til build-step (node+npm installert + `npm install + vsce package`). Stor kompleksitet. Foreslår: IKKE støtt GitHub i v6.4.0 — bare VSIX-baserte kilder.
2. **Marketplace API-stabilitet** — undokumentert endpoint kan endres. Fall-back: OpenVSX-lookup hvis Marketplace feiler (de fleste Marketplace-extensions finnes også på OpenVSX).
3. **Cache** — bør vi cache nedlastede VSIX i `~/.cache/llm-security/vsix/` (med SHA-256 som key, 7-dagers TTL)? Sparer båndbredde ved gjentatte scans. JA — enkelt og skru-av-bart via `--no-cache`.
4. **Signatur-verifisering av VSIX** — VS Code har begynt å signere publisher uploads (`.signature.p7s` i VSIX). Bør vi verifisere? Krever X.509-parsing (Node har `node:crypto` men P7S er kompleks). Foreslår: v6.5.0, ikke v6.4.0.
## Akseptansekriterier
- [ ] `llm-security ide-scan https://open-vsx.org/extension/anthropic/claude-code` returnerer envelope med korrekt publisher/name/version
- [ ] `llm-security ide-scan https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code` fungerer (forutsatt endpoint er stabilt)
- [ ] Zip-slip-fixture resulterer i BLOCK-verdict, ingen filer skrevet utenfor tempDir
- [ ] Zip-bomb-fixture stoppes ved 100x expansion ratio
- [ ] Temp-dir renses i alle exit-paths (success, error, abort)
- [ ] Nettverksfeil → tydelig feilmelding, ikke stack trace
- [ ] Scan av ekte Marketplace-extension (f.eks. `ms-python.python`) fullfører på < 30s på normal forbindelse
- [ ] Test-suite 1300+ grønn
## Estimat
- Kode: ~600 linjer (vsix-fetch: 150, zip-extract: 250, integrasjon: 100, tester: 100)
- Tid: 1 fokusert sesjon med auto mode
- Risiko: Moderat — zip-extraction er kjent angrepsvektor, trenger grundige tester. Fetch er enkelt.
## Referanser
- OpenVSX API: https://open-vsx.org/swagger-ui
- VS Code Marketplace (undokumentert): https://github.com/microsoft/vscode-vsce/blob/main/src/publish.ts
- ZIP format spec: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
- Zip-slip CVE: https://snyk.io/research/zip-slip-vulnerability
- Node fetch + streaming: https://nodejs.org/api/globals.html#fetch