ktg-plugin-marketplace/plugins/llm-security/docs/plans/jetbrains-research-brief.md
Kjell Tore Guttormsen fb9eb79d17 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.
2026-04-17 18:47:01 +02:00

32 KiB
Raw Blame History

type created question confidence dimensions mcp_servers_used local_agents_used external_agents_used
ultraresearch-brief 2026-04-17 Extend /security ide-scan (llm-security v6.6.0) to cover JetBrains / IntelliJ plugins, mirroring the VS Code v6.3-v6.5 work (discovery + URL fetch + OS sandbox). 0.88 7
architecture-mapper
task-finder
docs-researcher
community-researcher
security-researcher

JetBrains/IntelliJ Extension Security Scanning — v6.6.0 Research Brief

Generated by ultraresearch-local v1.0 on 2026-04-17

Research Question

Can we replicate the v6.3v6.5 VS Code/VSIX work for JetBrains/IntelliJ plugins in /security ide-scan, and what is the authoritative technical + threat context needed to plan it?

Specifically: (1) plugin format, (2) install locations per OS and product, (3) JetBrains Marketplace API, (4) 20242026 threat landscape, (5) check-mapping from 7 VS Code checks to JetBrains equivalents, (6) reusability of existing sandbox/zip/fetch primitives, (7) risks and JetBrains-only concerns.

Executive Summary

Feasible and well-scoped. The existing VS Code pipeline (lib/zip-extract.mjs, lib/vsix-sandbox.mjs, lib/vsix-fetch.mjs, lib/ide-extension-discovery.mjs, lib/ide-extension-parser.mjs) is ~80% reusable — all routing, CLI flags, envelope counters, data loader, and sandbox primitives already accept a 'jetbrains' type. The 4 real implementation gaps are: (1) directory walk in discoverJetBrainsExtensions, (2) META-INF/plugin.xml XML parser in parseIntelliJPlugin (nested-jar extraction required), (3) populated knowledge/top-jetbrains-plugins.json, (4) type === 'jetbrains' branch in scanOneExtension. Key caveat: zero publicly-confirmed malicious plugins on JetBrains Marketplace — the blocklist must be a typosquat-seed corpus, not a confirmed-bad list. Secondary caveat: signing is advisory (warning-not-block, YouTrack IJPL-212393 confirms warning sometimes absent); the highest-value IDE-specific check is sideload detection, per OX Security 2025 research demonstrating verified-badge bypass on IntelliJ.

Dimensions

Plugin Format — Confidence: high

Local findings:

  • lib/ide-extension-parser.mjs:101-112parseVsixFile and parseIntelliJPlugin are both stubs; only parseVSCodeExtension is implemented. JetBrains path returns null.
  • lib/zip-extract.mjs (entire file) — format-agnostic ZIP parser with zip-slip/symlink/absolute/ratio defences. ZIP-inside-ZIP extraction is supported (caller concatenates calls).

External findings:

  • Distribution: either .jar (no bundled deps, legacy) or .zip (with deps, modern Gradle default). Source: plugin-content.html.
  • ZIP layout: <artifact>/lib/<main>.jar + lib/<dep>.jar. META-INF/plugin.xml lives INSIDE the main jar, not at ZIP root. Scanner must open outer ZIP → find main jar in lib/ → open main jar → read META-INF/plugin.xml.
  • Installed plugins on disk are already extracted by the IDE (directory form, not zip). Sideloaded URL downloads are zip form.
  • plugin.xml schema elements relevant for scanning: <id>, <name>, <version>, <vendor url email>, <description>, <depends optional config-file>, <idea-version since-build until-build>, <actions>, <extensions defaultExtensionNs="com.intellij"> with children like applicationService, projectService, postStartupActivity, backgroundPostStartupActivity, applicationListener, projectListener, themeProvider. Legacy <application-components> / <project-components> still supported and appear in older plugins.
  • require-restart attribute on <idea-plugin> controls dynamic loading. Premain-Class in META-INF/MANIFEST.MF = Java agent (bytecode instrumentation) — extremely high-risk signal.

Install Locations per OS — Confidence: high

External findings (per-product confirmed):

Product Directory string macOS root Linux plugins Windows root
IntelliJ IDEA Ultimate IntelliJIdea{YYYY.N} ~/Library/Application Support/JetBrains/ ~/.local/share/JetBrains/ %APPDATA%\JetBrains\
IntelliJ IDEA Community IdeaIC{YYYY.N} same same same
PyCharm Professional PyCharm{YYYY.N} same same same
PyCharm Community PyCharmCE{YYYY.N} same same same
WebStorm WebStorm{YYYY.N} same same same
GoLand GoLand{YYYY.N} same same same
PhpStorm PhpStorm{YYYY.N} same same same
RubyMine RubyMine{YYYY.N} same same same
CLion CLion{YYYY.N} same same same
DataGrip DataGrip{YYYY.N} same same same
Rider Rider{YYYY.N} same same same
RustRover RustRover{YYYY.N} same same same
DataSpell DataSpell{YYYY.N} same same same
Android Studio AndroidStudio{version} ~/Library/Application Support/Google/ (NOT JetBrains) ~/.config/Google/ %APPDATA%\Google\
Aqua Aqua{YYYY.N} same (discontinued April 2025, legacy installs remain) same same
Fleet separate ecosystem — exclude from scanner n/a n/a n/a

Critical Linux quirk: config lives under ~/.config/JetBrains/<Product>/ but plugins live under ~/.local/share/JetBrains/<Product>/plugins/. This is different from macOS/Windows where both are co-located.

Local findings:

  • lib/ide-extension-discovery.mjs:38-53getJetBrainsBaseDir() already returns macOS/Windows/Linux base dirs correctly, but Linux points to ~/.local/share/JetBrains (correct for plugins).
  • Currently does NOT handle Android Studio (Google/ prefix) — needs second base-dir function.

Contradictions:

  • The existing getJetBrainsBaseDir() returns a single base dir, but Android Studio uses a separate vendor prefix (Google/ not JetBrains/). Resolution: add a second discovery root for Android Studio, or generalize to accept a list of {vendor, productRegex} tuples.

JetBrains Marketplace API — Confidence: high (live calls confirmed)

External findings:

  • Base: https://plugins.jetbrains.com/
  • Lookup by numeric ID: GET /api/plugins/{numericId} → returns {id, name, xmlId, vendor:{name, isVerified}, downloads, pricingModel, family, tags, link}. Confirmed live with id=1347 (Scala).
  • Lookup by xmlId: GET /plugins/list?pluginId={xmlId} (documented). GET /api/plugins?xmlId=... returns HTTP 400 — do NOT use.
  • Version listing: GET /api/plugins/{numericId}/updates?size=N → array of {id, version, file, cdate, since, until, size, downloads, pluginId, author:{isJetBrains, name}}. Confirmed live.
  • Download latest compatible: GET /pluginManager?action=download&id={xmlId}&build={productCode}-{buildNumber} → redirect to file.
  • Download specific version: GET /plugin/download?pluginId={xmlId}&version={v} → redirect.
  • Direct file download: GET /files/{file-path-from-update-object} (e.g. /files/1347/991561/scala-intellij-bin-2025.3.39.zip).
  • URL-to-ID resolution: https://plugins.jetbrains.com/plugin/7973-intellivue → numeric ID is 7973 (first path segment before dash).
  • Auth: none for reads. Rate limits not publicly documented.
  • OpenVSX equivalent: none confirmed. JetBrains Marketplace is effectively the only public registry. Custom repositories supported via updatePlugins.xml on arbitrary HTTPS servers (enterprise).

Threat Landscape 20242026 — Confidence: high (for documented incidents); medium (for absence-of-evidence)

External findings (security + community):

  • CVE-2024-37051 (CVSS 9.3, June 2024) — first-party JetBrains GitHub plugin. Malicious PR content exfiltrates OAuth/PAT tokens. 15 IDEs affected 2023.1+. Patched.
  • CVE-2025-64671 (2025) — GitHub Copilot for JetBrains. Same attack-surface pattern.
  • CVE-2025-57729 (CVSS 6.5, Aug 2025) — unexpected LSP plugin startup.
  • CVE-2025-68269 (CVSS 5.4, Dec 2025) — remote project trust bypass.
  • CVE-2025-64456 (CVSS 8.4, 2025) — ReSharper DPA Collector missing sig verification → local privilege escalation.
  • Log4Shell propagation (Dec 2021): JetBrains used "API Watcher" to identify plugins bundling log4j; temporarily hid affected versions, no public list of affected plugin names.
  • OX Security (July 2025, Nir Zadok & Moshe Siman-Tov Bustan): demonstrated verified-badge bypass on IntelliJ IDEA via sideloaded .zip with spoofed verification values. JetBrains response: "not from Marketplace, user responsibility." Sideload is therefore the primary remaining attack vector that JetBrains does not treat as a vendor bug.
  • Third-party Marketplace malicious plugins: zero publicly confirmed cases as of April 2026. This is material: the blocklist file CANNOT be seeded with confirmed-malicious entries because none exist publicly. Seed as typosquat-comparison corpus only.
  • Plugin signing reality: Warning-not-block. YouTrack IJPL-212393 confirms warning sometimes absent. Custom-repository plugins do not get JetBrains CA re-signing — enterprise can self-sign with arbitrary chain.
  • No sandbox: JetBrains' own docs explicitly acknowledge: "Plugins run as part of the IDE and have the same access rights as the IDE itself… Uninstalling does not guarantee all effects are undone." This is structurally identical to VS Code — no sandboxing, full user-privilege execution on project open.

Trust-model observations:

  • vendor.isVerified from Marketplace API is the strongest trust signal but confirms identity, not code safety.
  • vendor.email and vendor.url are optional, unvalidated, template-friendly — weak signals.
  • Plugin ID Lombook Plugin is a legitimate pre-existing typo in the real Lombok plugin (Marketplace ID 6317). Scanner must whitelist this exact string; a plugin named Lombok or LombokPlugin would be the suspicious one.

Check Mapping (7 VS Code checks → JetBrains) — Confidence: high

VS Code Check JetBrains Equivalent Signal source Status
Blocklist match Same concept, different ID namespace (xmlId) plugin.xml <id> vs knowledge/top-jetbrains-plugins.json[blocklist] Reusable — needs populated blocklist (seed-only, no confirmed-bad)
Theme-with-code Plugin declares <themeProvider> as only extension, OR declares it alongside services/actions/listeners plugin.xml extension count + element types Needs-new logic (JetBrains has no categories: ["Themes"] manifest field — infer from <themeProvider> presence)
Sideload detection Sideloaded plugin = installed via "Install Plugin from Disk" or directly dropped into plugins dir On-disk scan: no reliable filesystem marker. Confidence: low — JetBrains does not mark sideloaded vs Marketplace-installed on disk. Only URL-target mode reliably knows provenance. Partial — URL mode = always-sideload; on-disk mode = cannot distinguish reliably. Emit LOW advisory for on-disk plugins with vendor.isVerified=false (queried from Marketplace by xmlId).
Broad activation (* / onStartupFinished) <application-components> OR AppLifecycleListener with appStarted (highest risk); <postStartupActivity> / <backgroundPostStartupActivity> (medium); applicationService with preload="true" (medium) plugin.xml extension/listener declarations Needs-new logic with JetBrains-specific ranking
Typosquat (Levenshtein vs top list) Same algorithm, different corpus (xmlIds) plugin.xml <id> vs knowledge/top-jetbrains-plugins.json[jetbrains] Reusable — needs populated corpus (50+ xmlIds identified, see Q5 in community findings)
Extension-pack expansion <depends> chain — required vs optional="true" plugin.xml <depends> elements Needs-adaptation
Dangerous uninstall hooks No direct equivalent. JetBrains has DynamicPluginListener.beforePluginUnload but plugin's own code runs, not a manifest-declared script like vscode:uninstall Skip (no parallel); document in knowledge as "Known Limitations"

JetBrains-only checks (new categories):

  • Java agent declaration (Premain-Class in MANIFEST.MF): HIGH severity — bytecode instrumentation capability.
  • Native binary bundling: .dll/.so/.dylib/.exe inside jar resource directories. Legitimate for some plugins (jssc serial port) but also a confirmed implant delivery vector. Log SHA-256 + size.
  • Legacy <application-components>: blocks dynamic loading, fires at IDE launch. MEDIUM advisory.
  • Shaded jar detection: bundled jar where META-INF/MANIFEST.MF lacks Maven coordinates. Cannot audit via OSV.dev. MEDIUM advisory.

Sandbox / Primitive Reusability — Confidence: high

Local findings (validated against memory note on sandbox reuse):

  • lib/zip-extract.mjsfully reusable, format-agnostic (ZIP is ZIP). Existing zip-slip/symlink/absolute/ratio/depth/entries caps apply unchanged to JetBrains .zip.
  • lib/vsix-sandbox.mjs:buildSandboxProfile, buildBwrapArgsfully reusable, no VS-Code-specific strings.
  • lib/vsix-sandbox.mjs:buildSandboxedWorker, runVsixWorkerneeds-adaptation: WORKER_PATH is hardcoded to vsix-fetch-worker.mjs. Generalize to accept workerPath parameter (or create wrapper runPluginWorker(url, tmpDir, workerPath, opts)).
  • lib/vsix-fetch.mjs — transport primitives (httpsFetch, readBodyCapped, httpsFetchSameHost) fully reusable. Routing (detectUrlType, fetchMarketplaceVsix, fetchOpenVsxVsix) VS-Code-specific: add fetchJetBrainsPlugin() arm and extend detectUrlType to recognize plugins.jetbrains.com/plugin/... URLs.
  • Redirect whitelist: add plugins.jetbrains.com, downloads.marketplace.jetbrains.com, cache-redirector.jetbrains.com (verify during implementation via redirect observation).
  • lib/vsix-fetch-worker.mjs — create sibling lib/jetbrains-fetch-worker.mjs with same IPC protocol (--url, --tmpdir argv → single stdout JSON line with {ok, sha256, size, finalUrl, source, extRoot}). extRoot detection differs: look for top-level dir containing lib/ rather than extension/.
  • Reused scanners (UNI/ENT/NET/TNT/MEM/SCR):
    • UNI, ENT: fully reusable (text scanning, format-agnostic).
    • NET: minor adaptation — add JetBrains CDN hosts to trusted-domains set.
    • TNT: minor adaptation — add .kt, .groovy, .scala to CODE_EXTENSIONS (currently has .java but not Kotlin etc.).
    • MEM: reusable but low-recall for JetBrains — MEMORY_FILE_PATTERNS won't match JetBrains filenames. Workaround: in scanOneExtension JetBrains branch, include plugin.xml and README files in memFiles.
    • SCR: fully reusable — gracefully no-ops on JetBrains plugins (no npm/pip lockfiles).

Risks / Unknowns — Confidence: medium

What makes this harder than VS Code:

  1. Nested ZIP extraction for installed plugins on disk. When scanning installed plugins, JetBrains plugins on disk are already extracted — directory form, main jar still a ZIP. Scanner must ZIP-extract the main jar to read plugin.xml. This is one more layer than VS Code (where package.json is plain on disk).
  2. Bundled JAR dependency staleness. Every plugin ships its own lib/*.jar copies. No lockfile. Log4Shell propagated this way in 2021. Tier-1 mitigation: parse META-INF/MANIFEST.MF for Implementation-Title/Implementation-Version, batch-query OSV.dev Maven ecosystem. Shaded jars (coordinates stripped) flag as "cannot audit."
  3. Android Studio vendor prefix divergence. Single getJetBrainsBaseDir() insufficient — Android Studio uses Google/AndroidStudio<version>/ under the normal OS base. Generalize discovery.
  4. No OSV ecosystem for JetBrains plugin IDs. Only the bundled Maven deps are OSV-queryable. Third-party plugin IDs have no canonical vulnerability database.
  5. Signing is advisory. hasSignature field in ParsedManifest is unreliable — IDE warns but does not block; warning sometimes absent (YouTrack IJPL-212393). Use only as a weak INFO signal.
  6. Zero-public-malicious-plugin dataset. Blocklist must be structurally seed-only. Typosquat corpus is high-value; "confirmed bad" section starts empty.
  7. Kotlin (.kt) not in TNT's CODE_EXTENSIONS. Will silently skip Kotlin-authored plugins for taint analysis. Add .kt, .groovy, .scala.
  8. Fleet plugins are a different ecosystem (separate SDK, Kotlin Multiplatform) — explicitly exclude; do not attempt to parse.
  9. Premain-Class = high-severity signal (Java agent / bytecode instrumentation). Community finding — not present in existing check taxonomy.
  10. Native binaries in jars = medium-high signal. Confirmed implant delivery vector (Nextron Systems on trojanized Material Icon Theme). Log SHA-256 + size.

Not found / gaps:

  • Marketplace API rate limits (not publicly documented).
  • Whether IDE re-verifies plugin signatures on every load vs install-time only.
  • Whether sideloaded plugins get any runtime re-validation.
  • No academic paper specifically on JetBrains plugin malware analysis (20232026).

Local Context

Architecture

From architecture-mapper: the orchestrator scanners/ide-extension-scanner.mjs already routes on type === 'vscode' | 'jetbrains', already counts meta.extensions_discovered.jetbrains, already parses --intellij-only CLI flag. The call graph is already built — scanOneExtension is the only place that unconditionally dispatches to parseVSCodeExtension; needs branch.

Dependencies

From architecture-mapper: lib/zip-extract.mjs is the reusable foundation. lib/vsix-sandbox.mjs generalizes with one parameter (WORKER_PATH). The main new dependency is an XML parser for plugin.xml — zero-dep constraint mandates either hand-rolled DOM-lite (regex-based for simple fields + minimal recursive-descent for nested elements) or use of Node.js built-in primitives. No third-party XML deps permitted per CLAUDE.md "Null npm dependencies in hooks/scanners."

Conventions

From project CLAUDE.md and code inspection: test harness is node:test, fixtures under tests/fixtures/<feature>/, scanner prefixes per scanner (IDE for top-level, UNI/ENT/NET/TNT/MEM/SCR for reused). JetBrains additions must preserve scanner prefix IDE; may introduce new finding type codes (e.g. IDE-JB-01 theme-with-code, IDE-JB-02 broad-activation, etc.).

History

Task-finder located the exact canonical backlog entry: TODO.md:14v6.6.0-kandidater: JetBrains-discovery (v1.1-stub i dag). This is the user-sanctioned scope.

External Knowledge

Best Practice

  • Parse META-INF/plugin.xml from inside main jar (inside outer zip). Official pattern.
  • Use Marketplace REST API for xmlId→verified/downloads/vendor enrichment. Batch via /api/plugins/{id}.
  • OSV.dev batch API (POST /v1/querybatch) with Maven ecosystem queries for bundled jars.
  • For sideload detection on URL target: always-sideload (by definition of URL install). For on-disk target: query Marketplace API by xmlId; if vendor.isVerified === false and plugin has no directory-level signature marker, emit LOW advisory.

Alternatives

Considered and rejected:

  • Java bytecode decompilation — requires JVM subprocess. Out of scope for zero-dep v6.6.0. Defer as v6.7+ if demand emerges.
  • Scanning all bundled jars as first-class extensions — explodes scope. Plan: treat bundled jars as dependency data (OSV queries), not as recursive extension scanning.
  • Running IDE's own Plugin Verifier — requires IntelliJ SDK tooling, not a security check. Out of scope.

Security

  • Log4Shell remains the canonical evidence that bundled-jar audit matters.
  • No JetBrains ecosystem prefix in OSV.dev. Only Maven matching works.
  • Plugin signing: 2021.2+ dual-signature (author + JetBrains re-sign via AWS KMS). Sideloads bypass entirely; enterprise custom repos can self-sign.
  • CVE-2024-37051 (GitHub plugin token leak) is the closest analogue to "malicious content → credential exfil" and is the threat model for the LLM01/LLM02 content checks already in MEM.

Known Issues

  • YouTrack IJPL-212393 — unsigned plugin warning sometimes not shown. Do not trust signing as binary gate.
  • JetBrains dismisses sideload bypass as "user responsibility." Scanner fills the gap the vendor declined to fill.
  • postStartupActivity is platform-recommended for plugin project-load code but the platform team explicitly says plugins "must not affect IDE startup sequence." Tension is real; scanner ranks <application-components> + AppLifecycleListener.appStarted as HIGH and postStartupActivity as MEDIUM.

Synthesis

The triangulation surfaces three cross-cutting insights that neither local nor external research alone would produce:

1. Reusability is higher than initial estimation suggested. Task-finder shows wiring is 80% done (routing, envelope, CLI, data-loader). Architecture-mapper shows sandbox primitives generalize via one parameter. Docs-researcher confirms zip structure is nested ZIP (outer + main jar) — and local lib/zip-extract.mjs already handles arbitrary ZIP streams. Result: v6.6.0 is primarily integration work, not greenfield. Net new code estimated at 300500 lines (parser + discovery walker + 23 new check functions + knowledge file population), plus test fixtures.

2. The threat model differs from VS Code in ways that reshape which checks matter. VS Code's Marketplace has documented malware campaigns; JetBrains' has zero. This inverts the check priority: blocklist match is LOW-value (nothing to match), typosquat is HIGH-value (primary vector per community research), sideload is HIGHEST-value (only unmitigated attack per OX research), broad-activation splits into HIGH (<application-components> + AppLifecycleListener) vs MEDIUM (postStartupActivity), and two entirely new signals emerge: Premain-Class Java agents and native-binary bundling.

3. The "blocklist cannot contain confirmed-malicious entries" finding forces a schema rethink. The existing top-vscode-extensions.json schema has "blocklist": ["publisher.name@version"] for confirmed-bad. The JetBrains equivalent must structure this as a typosquat-comparison corpus ("jetbrains": [xmlId, ...]) with an explicitly-empty "blocklist": [] and a comment explaining why. Scanner warnings about "plugin on blocklist" will never fire for JetBrains in v6.6.0 — that's correct behavior. Enterprise users can seed their own private blocklists via policy.json when they discover internal threats.

Open Questions

  • Marketplace API rate limits — not publicly documented. Implementation should implement conservative 1 req/s default with opt-in higher concurrency. Monitor for 429 responses.
  • Custom repository enumeration — JetBrains supports updatePlugins.xml on arbitrary HTTPS servers for enterprise. v6.6.0 focuses on Marketplace; enterprise custom repos deferred to v6.7+.
  • Whether IDE re-verifies sideloaded plugin signatures at runtime — not publicly documented. Assume no; treat sideload as always-elevated-risk.
  • Lifetime of a plugin across IDE upgrades — when user installs IntelliJ IDEA 2025.1 over 2024.3, do plugins copy over? Worth confirming during implementation to decide if discovery walks all version dirs or only the newest.
  • Toolbox App plugin cache location~/Library/Caches/JetBrains/Toolbox/plugins/... is for Toolbox extensions, not IDE plugins. Confirm during implementation that this path should be skipped.

Recommendation

Proceed with v6.6.0 as a focused integration release. Implementation plan should structure as:

Phase A — Discovery + parsing (core)

  1. Implement discoverJetBrainsExtensions in lib/ide-extension-discovery.mjs — walk getJetBrainsBaseDir()/<Product><Version>/plugins/ (every product/version subdir), plus Android Studio under getAndroidStudioBaseDir(). Exclude Fleet and Toolbox paths.
  2. Implement parseIntelliJPlugin in lib/ide-extension-parser.mjs — for each plugin directory, locate main jar in lib/ (convention: jar matching plugin dir name, or largest jar), ZIP-extract META-INF/plugin.xml + META-INF/MANIFEST.MF, return ParsedManifest extended with JetBrains-specific fields (pluginId, since, until, depends, extensionDeclarations, hasPremainClass, nativeBinaries, bundledJars[{name, version, shaded}]).
  3. Populate knowledge/top-jetbrains-plugins.json with ~50 verified xmlIds (see community Q5 — API-verified list). blocklist: [] with comment explaining absence.
  4. Wire type === 'jetbrains' branch into scanOneExtension — dispatch to parseIntelliJPlugin, call runJetBrainsChecks instead of runIdeChecks.

Phase B — Checks

  1. runJetBrainsChecks with: blocklist match (reuses algorithm), typosquat (reuses algorithm, new corpus), theme-with-code (new: infer from <themeProvider> + other extensions), broad-activation (new: rank by extension point), <depends>-chain expansion (new), Java-agent detection (new: Premain-Class in MANIFEST.MF), native-binary detection (new: scan extracted jar entries for .dll/.so/.dylib/.exe).
  2. Minor scanner adaptations: NET trusted-domains (+ JetBrains CDN hosts), TNT CODE_EXTENSIONS (+ .kt, .groovy, .scala), MEM memFiles filter in orchestrator (+ plugin.xml, README*).

Phase C — URL target support

  1. Extend detectUrlType in lib/vsix-fetch.mjs to recognize plugins.jetbrains.com/plugin/... URLs. Add fetchJetBrainsPlugin(publisher?, xmlIdOrNumericId, version?).
  2. Generalize buildSandboxedWorker / runVsixWorker to accept workerPath parameter (maintains backwards compat with existing runVsixWorker signature via default).
  3. Create lib/jetbrains-fetch-worker.mjs — same IPC as vsix-fetch-worker.mjs, uses fetchJetBrainsPlugin + extractToDir, detects extRoot as top-level dir containing lib/.

Phase D — Tests + docs

  1. Fixtures: tests/fixtures/ide-extensions/root-jetbrains/ with benign + adversarial cases (theme-with-code, broad-activation, typosquat, Java agent, native binary, shaded jar).
  2. Tests: mirror tests/scanners/ide-extension-scanner.test.mjs pattern — rootsOverride injection, intellijOnly: true. Plus tests/scanners/jetbrains-fetch.test.mjs with mocked globalThis.fetch.
  3. Documentation: update commands/ide-scan.md (remove "v1.1 stub" language), knowledge/ide-extension-threat-patterns.md (add JetBrains sections), create knowledge/jetbrains-marketplace-api-notes.md (sibling of existing VSIX notes), update root README.md + plugin CLAUDE.md + CHANGELOG.md per project "docs at change" rule.

Risk register for implementation planning

Risk Likelihood Impact Mitigation
plugin.xml parsing scope creep (DTD validation, XSD) Medium Medium Stop at structural extraction of documented-schema fields. Do NOT validate against schema.
Nested ZIP extraction blows memory on large plugins Low Low Existing 500MB uncompressed cap + 100x ratio cap apply to each nested extraction. Confirm during implementation.
Marketplace API rate-limiting Medium Low Single-request-per-second default; opt-in --online-concurrency N.
False positives on legacy <application-components> High Low MEDIUM severity, not HIGH. Document in finding rationale that deprecated does not imply malicious.
Android Studio path divergence missed Low Medium Explicit getAndroidStudioBaseDir() with OS-specific Google/ prefix. Test fixture for Android Studio discovery.
Lombook Plugin legitimate typo triggers false positive Certain Low Whitelist exact match for this single xmlId in typosquat check.
Shaded-jar false negatives (bundled vuln lib, coords stripped) High High Out-of-scope for v6.6.0 — emit INFO/MEDIUM advisory "cannot audit (shaded)" for each coord-less jar.

Estimated effort: 35 focused sessions. No new npm dependencies. No new external network calls beyond optional JetBrains Marketplace API queries (behind --online flag, same convention as VS Code).

Sources

# Source Type Quality Used in
1 https://plugins.jetbrains.com/docs/intellij/plugin-content.html official high Plugin format
2 https://plugins.jetbrains.com/docs/intellij/plugin-configuration-file.html official high Plugin format, plugin.xml schema
3 https://plugins.jetbrains.com/docs/intellij/plugin-components.html official high Broad activation ranking (legacy components)
4 https://plugins.jetbrains.com/docs/intellij/dynamic-plugins.html official high require-restart, dynamic plugin detection
5 https://plugins.jetbrains.com/docs/intellij/theme-structure.html official high Theme-with-code check
6 https://plugins.jetbrains.com/docs/intellij/intellij-platform-extension-point-list.html official high Broad-activation extension point enumeration
7 https://plugins.jetbrains.com/docs/intellij/plugin-signing.html official high Signing model, warning-not-block
8 https://plugins.jetbrains.com/docs/marketplace/api-reference.html official high Marketplace API base + endpoints
9 https://plugins.jetbrains.com/docs/marketplace/plugin-update-download.html official high Download endpoints
10 https://plugins.jetbrains.com/docs/marketplace/understanding-plugin-security.html official high No-sandbox acknowledgement
11 https://plugins.jetbrains.com/docs/marketplace/verified-vendor-badge.html official high vendor.isVerified semantics
12 https://plugins.jetbrains.com/docs/marketplace/jetbrains-marketplace-approval-guidelines.html official high Moderation + removal policy
13 https://www.jetbrains.com/help/idea/directories-used-by-the-ide-to-store-settings-caches-plugins-and-logs.html official high Install locations (IntelliJ IDEA)
14 https://developer.android.com/studio/intro/studio-config official (Google) high Android Studio path divergence
15 https://fleet-support.jetbrains.com/hc/en-us/articles/7648059182226 official high Fleet exclusion
16 https://blog.jetbrains.com/security/2024/06/updates-for-security-issue-affecting-intellij-based-ides-2023-1-and-github-plugin/ official high CVE-2024-37051
17 https://blog.jetbrains.com/platform/2021/12/log4j-vulnerability-and-third-party-plugins-on-jetbrains-marketplace/ official high Log4Shell propagation
18 https://www.darkreading.com/application-security/ide-extensions-risks-software-supply-chain community high OX verified-badge bypass
19 https://www.ox.security/blog/can-you-trust-that-verified-symbol-exploiting-ide-extensions-is-easier-than-it-should-be/ community high OX research primary
20 https://youtrack.jetbrains.com/projects/IJPL/issues/IJPL-212393/Installing-unsigned-plugin-did-not-show-error-redux official (bug tracker) high Signing warning unreliability
21 https://platform.jetbrains.com/t/execute-pre-load-activity/958 official (forum, maintainer) high "plugins must not affect IDE startup sequence"
22 https://workflowotg.com/malware-in-ide-plugins-an-attack-vector-to-look-out-for-in-2025/ community medium Threat-model framing
23 https://arxiv.org/html/2503.22391v1 academic high Maven ecosystem vulnerability base rate
24 https://osv.dev/ official (OSV) high Maven ecosystem matching, bundled-jar audit
25 Live API calls to plugins.jetbrains.com/api/plugins/{id} primary high Marketplace API schema confirmation
26 Task-finder report (in-conversation, see Phase 4 agent results) codebase high Touchpoint inventory (6 must-change files)
27 Architecture-mapper report (in-conversation, see Phase 4 agent results) codebase high Reusability matrix, dependency diagram
28 CLAUDE.md — project instructions at /Users/ktg/.claude/plugins/marketplaces/ktg-plugin-marketplace/plugins/llm-security/CLAUDE.md codebase high Zero-dep constraint, test-harness convention, docs-at-change rule
29 MEMORY.md — memory note on sandbox reuse memory high Reuse sandbox primitives, do not copy