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

118 lines
5 KiB
Markdown

# 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
- JetBrains plugin-content — https://plugins.jetbrains.com/docs/intellij/plugin-content.html
- JetBrains plugin-configuration-file — https://plugins.jetbrains.com/docs/intellij/plugin-configuration-file.html
- JetBrains Marketplace REST (informal) — https://plugins.jetbrains.com/docs/marketplace/api.html
- YouTrack IJPL-212393 — signature-warning inconsistency
- OX Security 2025 — JetBrains verified-badge bypass