From 037a91276db66298f63d5fc5eb7edc67fc6b6000 Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Sat, 18 Apr 2026 10:02:04 +0200 Subject: [PATCH] docs(llm-security): add knowledge/jetbrains-marketplace-api-notes.md --- .../jetbrains-marketplace-api-notes.md | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 plugins/llm-security/knowledge/jetbrains-marketplace-api-notes.md diff --git a/plugins/llm-security/knowledge/jetbrains-marketplace-api-notes.md b/plugins/llm-security/knowledge/jetbrains-marketplace-api-notes.md new file mode 100644 index 0000000..909cb92 --- /dev/null +++ b/plugins/llm-security/knowledge/jetbrains-marketplace-api-notes.md @@ -0,0 +1,118 @@ +# 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 ` (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 `` 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: ` 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