ktg-plugin-marketplace/plugins/llm-security/knowledge/jetbrains-marketplace-api-notes.md

5 KiB

JetBrains Marketplace API notes

Reference notes for scanners/lib/vsix-fetch.mjs and scanners/lib/jetbrains-fetch-worker.mjs. These endpoints are used to download JetBrains plugin archives for /security ide-scan <url> (v6.6.0).

Base URL

https://plugins.jetbrains.com/

Metadata lookup

GET https://plugins.jetbrains.com/api/plugins/{numericId}
GET https://plugins.jetbrains.com/api/plugins/{numericId}/updates?size=N
  • {numericId} is an integer assigned by JetBrains when the plugin is first published. It is the first path segment (before the dash) in the user-facing URL — e.g. https://plugins.jetbrains.com/plugin/7973-intellivue → numericId 7973, slug intellivue.
  • GET /api/plugins/{numericId} returns plugin metadata including xmlId (the <id> from plugin.xml), name, vendor.name, vendor.isVerified, programmingLanguage, urls. We use this only when --online is set, and never trust vendor.isVerified as a safety signal — it attests publisher identity, not plugin safety (see OX Security 2025).
  • GET /api/plugins/{numericId}/updates?size=N returns the last N versions with id, version, cdate (unix millis), file (CDN path), and recommended. We use this to resolve "latest stable" without trusting the plugin page HTML.

Direct download

Two equivalent URL shapes, both officially supported:

GET https://plugins.jetbrains.com/pluginManager?action=download&id={xmlId}&build={productCode}-{buildNumber}
GET https://plugins.jetbrains.com/plugin/download?pluginId={xmlId}&version={v}
  • action=download is what the IntelliJ IDE itself uses via its plugin manager. {productCode} is IC (IDEA Community), IU (IDEA Ultimate), PY, PS, GO, RD, CL, WS, RM, DB, etc. {buildNumber} is a short build string like 241.18034.62. When both are present, the server returns the compatible version; otherwise it returns a 400.
  • plugin/download?pluginId={xmlId}&version={v} is stable and simpler — we prefer this shape when the caller passes ?version=. If no version is given we resolve latest via the updates endpoint first, then request that version explicitly.
  • The response is a ZIP (or in rare legacy cases a JAR). Content-Type is application/octet-stream or application/java-archive. The fetcher tolerates both.

Redirect host whitelist

The JetBrains CDN reroutes downloads. Our fetcher (vsix-fetch.mjs) accepts redirects only to:

  • plugins.jetbrains.com
  • downloads.marketplace.jetbrains.com
  • cache-redirector.jetbrains.com

Any other host aborts with Error: redirect not allowed: <host> and the worker exits non-zero. This matches the VS Code Marketplace redirect policy in marketplace-api-notes.md.

URL-to-numericId resolution

detectUrlType accepts three JetBrains URL shapes:

https://plugins.jetbrains.com/plugin/{numericId}-{slug}
https://plugins.jetbrains.com/plugin/{numericId}-{slug}/versions/{version}
https://plugins.jetbrains.com/pluginManager?action=download&id={xmlId}
https://plugins.jetbrains.com/plugin/download?pluginId={xmlId}

For the first two, we split the first path segment on - and take the leading integer as numericId. For the action=download / download?pluginId forms we pass the URL through directly.

Rate limits

JetBrains publishes no explicit rate limit. Observed in the field: low-tens of requests per minute per IP is safe. Our default is 1 req/s (conservative) — we issue at most three requests per URL scan (metadata + updates + download) so rate budget is generous.

Caps & defenses (shared with VS Code fetcher)

  • TLS verification enabled (no --insecure opt-in).
  • HTTPS only. Plain HTTP is rejected at detectUrlType and at fetch time.
  • Manual redirect handling. Allowed hosts whitelisted per source type.
  • 30-second total timeout via AbortController.
  • 50MB compressed archive cap. Streaming reader aborts when cap exceeded.
  • SHA-256 computed during streaming for meta.source.sha256.

What is NOT supported (v6.6.0)

  • No equivalent of OpenVSX — JetBrains Marketplace is the only distribution channel, so URL scans fall through to direct-URL VSIX rules if the caller passes a raw .zip or .jar URL on any other host.
  • Custom plugin repositories (updatePlugins.xml feeds on self-hosted servers) are out of scope. A target like https://my-repo.example.com/updatePlugins.xml is not a plugin archive and will fail detectUrlType.
  • Archive signature verification — JetBrains ships plugins unsigned by default; the Marketplace signs metadata at publish time but the ZIP itself carries no embedded signature we can verify offline.

References