diff --git a/astro.config.mjs b/astro.config.mjs index 0c5339f..3a8ddef 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,11 +1,69 @@ +import { readFile, writeFile } from 'node:fs/promises'; +import { fileURLToPath } from 'node:url'; import { defineConfig, passthroughImageService } from 'astro/config'; import starlight from '@astrojs/starlight'; import starlightLlmsTxt from 'starlight-llms-txt' +import starlightOpenAPI, { openAPISidebarGroups } from 'starlight-openapi' +import Converter from 'openapi-to-postmanv2' const site_url = process.env.URL; const site = site_url || 'http://localhost:4321'; + +const postmanCollections = [ + { + openapi: './public/schemas/threat-intel-beta.yaml', + output: './public/schemas/threat-intel-beta.postman_collection.json', + }, +]; + +// Recursively drop `id` and `_postman_id` keys so the output is deterministic. +// openapi-to-postmanv2 inserts fresh UUIDs on every run, which would cause +// churn in git. Both fields are optional in Collection v2.1 — Postman assigns +// new ids on import. +function stripIds(value) { + if (Array.isArray(value)) return value.map(stripIds); + if (value && typeof value === 'object') { + const out = {}; + for (const [key, val] of Object.entries(value)) { + if (key === 'id' || key === '_postman_id') continue; + out[key] = stripIds(val); + } + return out; + } + return value; +} + +function postmanFromOpenAPI() { + return { + name: 'postman-from-openapi', + hooks: { + 'astro:config:setup': async ({ logger }) => { + for (const { openapi, output } of postmanCollections) { + const spec = await readFile(fileURLToPath(new URL(openapi, import.meta.url)), 'utf8'); + await new Promise((resolve, reject) => { + Converter.convert( + { type: 'string', data: spec }, + { requestParametersResolution: 'Example' }, + async (err, result) => { + if (err || !result.result) { + return reject(err ?? new Error(result.reason ?? 'Postman conversion failed')); + } + await writeFile( + fileURLToPath(new URL(output, import.meta.url)), + JSON.stringify(stripIds(result.output[0].data), null, 2), + ); + logger.info(`generated ${output}`); + resolve(); + }, + ); + }); + } + }, + }, + }; +} // https://astro.build/config export default defineConfig({ site: site, @@ -18,17 +76,17 @@ export default defineConfig({ }, integrations: [ + postmanFromOpenAPI(), starlight({ plugins: [ - starlightLlmsTxt() - // Generate the OpenAPI documentation pages. - // starlightOpenAPI([ - // { - // base: 'developer-api', - // label: 'My API', - // schema: './schemas/test.yaml', - // }, - // ]) + starlightLlmsTxt(), + starlightOpenAPI([ + { + base: 'api-reference/threat-intelligence-beta', + label: 'Reference', + schema: './public/schemas/threat-intel-beta.yaml', + }, + ]), ], title: 'Patchstack Docs', favicon: '/images/psfavicon.svg', @@ -73,7 +131,33 @@ export default defineConfig({ { label: 'API solutions', collapsed: true, - autogenerate: { directory: 'API solutions', collapsed: true }, + items: [ + { slug: 'api-solutions' }, + { + label: 'App API', + collapsed: true, + autogenerate: { directory: 'API solutions/App API', collapsed: true }, + }, + { + label: 'Threat Intelligence API', + collapsed: true, + items: [ + { slug: 'api-solutions/threat-intelligence-api/overview' }, + { slug: 'api-solutions/threat-intelligence-api/standard' }, + { slug: 'api-solutions/threat-intelligence-api/extended' }, + { slug: 'api-solutions/threat-intelligence-api/api-properties' }, + { + label: 'Beta API', + badge: { text: 'New', variant: 'tip' }, + collapsed: true, + items: [ + { slug: 'api-solutions/threat-intelligence-api/beta' }, + ...openAPISidebarGroups, + ], + }, + ], + }, + ], }, { label: 'Vulnerability Disclosure Program', diff --git a/package-lock.json b/package-lock.json index 8f9d49a..56d2b58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,27 @@ "astro-og-canvas": "^0.5.3", "js-cookie": "^3.0.5", "sharp": "^0.33.4", - "starlight-llms-txt": "^0.5.1" + "starlight-llms-txt": "^0.5.1", + "starlight-openapi": "^0.22.1" + }, + "devDependencies": { + "openapi-to-postmanv2": "^6.0.1" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-13.0.5.tgz", + "integrity": "sha512-xfh4xVJD62gG6spIc7lwxoWT+l16nZu1ELyU8FkjaP/oD2yP09EvLAU6KhtudN9aML2Khhs9pY6Slr7KGTES3w==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" } }, "node_modules/@astrojs/compiler": { @@ -31,6 +51,7 @@ "version": "6.3.2", "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.2.tgz", "integrity": "sha512-bO35JbWpVvyKRl7cmSJD822e8YA8ThR/YbUsciWNA7yTcqpIAL2hJDToWP5KcZBWxGT6IOdOkHSXARSNZc4l/Q==", + "peer": true, "dependencies": { "@astrojs/internal-helpers": "0.6.1", "@astrojs/prism": "3.3.0", @@ -106,6 +127,7 @@ "version": "0.34.8", "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.34.8.tgz", "integrity": "sha512-XuYz0TfCZhje2u1Q9FNtmTdm7/B9QP91RDI1VkPgYvDhSYlME3k8gwgcBMHnR9ASDo2p9gskrqe7t1Pub/qryg==", + "peer": true, "dependencies": { "@astrojs/markdown-remark": "^6.3.1", "@astrojs/mdx": "^4.2.3", @@ -156,6 +178,20 @@ "node": "18.20.8 || ^20.3.0 || >=22.0.0" } }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -165,9 +201,10 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -623,6 +660,13 @@ "node": ">=18" } }, + "node_modules/@exodus/schemasafe": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", + "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==", + "dev": true, + "license": "MIT" + }, "node_modules/@expressive-code/core": { "version": "0.41.3", "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.3.tgz", @@ -664,6 +708,23 @@ "@expressive-code/core": "^0.41.3" } }, + "node_modules/@faker-js/faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==", + "deprecated": "Please update to a newer version.", + "dev": true, + "license": "MIT" + }, + "node_modules/@humanwhocodes/momoa": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-2.0.4.tgz", + "integrity": "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.10.0" + } + }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -1116,6 +1177,55 @@ "win32" ] }, + "node_modules/@readme/better-ajv-errors": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@readme/better-ajv-errors/-/better-ajv-errors-2.4.0.tgz", + "integrity": "sha512-9WODaOAKSl/mU+MYNZ2aHCrkoRSvmQ+1YkLj589OEqqjOAhbn8j7Z+ilYoiTu/he6X63/clsxxAB4qny9/dDzg==", + "license": "Apache-2.0", + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/runtime": "^7.22.5", + "@humanwhocodes/momoa": "^2.0.3", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "ajv": "4.11.8 - 8" + } + }, + "node_modules/@readme/openapi-parser": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@readme/openapi-parser/-/openapi-parser-4.1.2.tgz", + "integrity": "sha512-lAFH88r/CHs5VZDUocEda0OSMSQsr6801sziIjOKyVA+0hSFN+BPuelPF5XvkMROHecnPd+XEJN1iNQqCgER/g==", + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^13.0.5", + "@readme/better-ajv-errors": "^2.3.2", + "@readme/openapi-schemas": "^3.1.0", + "@types/json-schema": "^7.0.15", + "ajv": "^8.12.0", + "ajv-draft-04": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/@readme/openapi-schemas": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@readme/openapi-schemas/-/openapi-schemas-3.1.0.tgz", + "integrity": "sha512-9FC/6ho8uFa8fV50+FPy/ngWN53jaUu4GRXlAjcxIRrzhltJnpKkBG2Tp0IDraFJeWrOpk84RJ9EMEEYzaI1Bw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@rollup/pluginutils": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", @@ -1497,6 +1607,12 @@ "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -1566,6 +1682,7 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1581,6 +1698,55 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -1710,6 +1876,7 @@ "version": "5.11.2", "resolved": "https://registry.npmjs.org/astro/-/astro-5.11.2.tgz", "integrity": "sha512-jKJCqp0PMZ1ZpP2xySghsJ1xK7ZNh/ISTRNBf/7khY3iEGq/zup49ZMhNZXK5Cd/dFWP/pdBNHD91SByA42IvQ==", + "peer": true, "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.6.1", @@ -1816,6 +1983,13 @@ "astro": "^3.0.0 || ^4.0.0 || ^5.0.0" } }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -1944,6 +2118,13 @@ "base64-js": "^1.1.2" } }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "dev": true, + "license": "MIT" + }, "node_modules/camelcase": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", @@ -2019,6 +2200,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/charset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz", + "integrity": "sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -2058,6 +2249,100 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", @@ -2129,11 +2414,41 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/common-ancestor-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==" }, + "node_modules/compute-gcd": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/compute-gcd/-/compute-gcd-1.2.1.tgz", + "integrity": "sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg==", + "dev": true, + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2", + "validate.io-integer-array": "^1.0.0" + } + }, + "node_modules/compute-lcm": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/compute-lcm/-/compute-lcm-1.1.2.tgz", + "integrity": "sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ==", + "dev": true, + "dependencies": { + "compute-gcd": "^1.2.1", + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2", + "validate.io-integer-array": "^1.0.0" + } + }, "node_modules/cookie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", @@ -2342,6 +2657,13 @@ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==" }, + "node_modules/es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", + "dev": true, + "license": "MIT" + }, "node_modules/esast-util-from-estree": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", @@ -2412,6 +2734,16 @@ "@esbuild/win32-x64": "0.25.6" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", @@ -2533,6 +2865,29 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fdir": { "version": "6.4.6", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", @@ -2546,6 +2901,16 @@ } } }, + "node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2590,6 +2955,13 @@ "unicode-trie": "^2.0.0" } }, + "node_modules/foreach": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", + "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==", + "dev": true, + "license": "MIT" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2603,6 +2975,16 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-east-asian-width": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", @@ -2619,6 +3001,16 @@ "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==" }, + "node_modules/graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" + } + }, "node_modules/h3": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.3.tgz", @@ -3042,6 +3434,20 @@ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==" }, + "node_modules/http-reasons": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/http-reasons/-/http-reasons-0.1.0.tgz", + "integrity": "sha512-P6kYh0lKZ+y29T2Gqz+RlC9WBLhKe8kDmcJ+A+611jFfxdPsbMRQ5aNmFRM3lENqFkK+HTTL+tlQviAiv0AbLQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/http2-client": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", + "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==", + "dev": true, + "license": "MIT" + }, "node_modules/i18next": { "version": "23.16.8", "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz", @@ -3064,6 +3470,19 @@ "@babel/runtime": "^7.23.2" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/import-meta-resolve": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", @@ -3211,6 +3630,12 @@ "node": ">=14" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3222,6 +3647,56 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-pointer": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/json-pointer/-/json-pointer-0.6.2.tgz", + "integrity": "sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "foreach": "^2.0.4" + } + }, + "node_modules/json-schema-compare": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/json-schema-compare/-/json-schema-compare-0.2.2.tgz", + "integrity": "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.4" + } + }, + "node_modules/json-schema-merge-allof": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/json-schema-merge-allof/-/json-schema-merge-allof-0.8.1.tgz", + "integrity": "sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "compute-lcm": "^1.1.2", + "json-schema-compare": "^0.2.2", + "lodash": "^4.17.20" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -3238,6 +3713,32 @@ "node": ">= 8" } }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/liquid-json": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/liquid-json/-/liquid-json-0.3.1.tgz", + "integrity": "sha512-wUayTU8MS827Dam6MxgD72Ui+KOSF+u/eIqpatOtjnvgJ0+mnDq33uC2M7J0tPK+upe/DpUAuK4JUU89iBoNKQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -4323,6 +4824,29 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-format": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.2.tgz", + "integrity": "sha512-Y5ERWVcyh3sby9Fx2U5F1yatiTFjNsqF5NltihTWI9QgNtr5o3dbCZdcKa1l2wyfhnwwoP9HGNxga7LqZLA6gw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "charset": "^1.0.0" + } + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -4392,6 +4916,19 @@ } } }, + "node_modules/node-fetch-h2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", + "integrity": "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "http2-client": "^1.2.5" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/node-fetch-native": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz", @@ -4402,6 +4939,16 @@ "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.1.tgz", "integrity": "sha512-0gJJgENizp4ghds/Ywu2FCmcRsgBTmRQzYPZm61wy+Em2sBarSka0OhQS5huLBg6od1zkNpnWMCZloQDFVvOMQ==" }, + "node_modules/node-readfiles": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", + "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promise": "^3.2.1" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4421,6 +4968,152 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/oas-kit-common": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz", + "integrity": "sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "fast-safe-stringify": "^2.0.7" + } + }, + "node_modules/oas-linter": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz", + "integrity": "sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@exodus/schemasafe": "^1.0.0-rc.2", + "should": "^13.2.1", + "yaml": "^1.10.0" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-linter/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/oas-resolver": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz", + "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "node-fetch-h2": "^2.3.0", + "oas-kit-common": "^1.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + }, + "bin": { + "resolve": "resolve.js" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-resolver-browser": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/oas-resolver-browser/-/oas-resolver-browser-2.5.6.tgz", + "integrity": "sha512-Jw5elT/kwUJrnGaVuRWe1D7hmnYWB8rfDDjBnpQ+RYY/dzAewGXeTexXzt4fGEo6PUE4eqKqPWF79MZxxvMppA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "node-fetch-h2": "^2.3.0", + "oas-kit-common": "^1.0.8", + "path-browserify": "^1.0.1", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + }, + "bin": { + "resolve": "resolve.js" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-resolver-browser/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/oas-resolver/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/oas-schema-walker": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz", + "integrity": "sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==", + "dev": true, + "license": "BSD-3-Clause", + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-validator": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-5.0.8.tgz", + "integrity": "sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "call-me-maybe": "^1.0.1", + "oas-kit-common": "^1.0.8", + "oas-linter": "^3.2.2", + "oas-resolver": "^2.5.6", + "oas-schema-walker": "^1.1.5", + "reftools": "^1.1.9", + "should": "^13.2.1", + "yaml": "^1.10.0" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-validator/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/ofetch": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.4.1.tgz", @@ -4451,6 +5144,66 @@ "regex-recursion": "^6.0.2" } }, + "node_modules/openapi-to-postmanv2": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/openapi-to-postmanv2/-/openapi-to-postmanv2-6.0.1.tgz", + "integrity": "sha512-zAjaTwXo07az6jjvZTw4d26QMQsFxZBxTqjj3LQQMDCCuO6+peATQc9bSmAq3QbzvikP+h2WEjTphMcIrcSurg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "ajv": "^8.11.0", + "ajv-draft-04": "1.0.0", + "ajv-formats": "2.1.1", + "async": "3.2.6", + "commander": "2.20.3", + "graphlib": "2.1.8", + "js-yaml": "4.1.0", + "json-pointer": "0.6.2", + "json-schema-merge-allof": "0.8.1", + "lodash": "4.17.21", + "neotraverse": "0.6.15", + "oas-resolver-browser": "2.5.6", + "object-hash": "3.0.0", + "openapi-types": "^12.1.3", + "path-browserify": "1.0.1", + "postman-collection": "^5.0.0", + "swagger2openapi": "7.0.8", + "yaml": "1.10.2" + }, + "bin": { + "openapi2postmanv2": "bin/openapi2postmanv2.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/openapi-to-postmanv2/node_modules/neotraverse": { + "version": "0.6.15", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.15.tgz", + "integrity": "sha512-HZpdkco+JeXq0G+WWpMJ4NsX3pqb5O7eR9uGz3FfoFt+LYzU8iRWp49nJtud6hsDoywM8tIrDo3gjgmOqJA8LA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/openapi-to-postmanv2/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "license": "MIT", + "peer": true + }, "node_modules/p-limit": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", @@ -4578,6 +5331,13 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -4612,6 +5372,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -4657,6 +5418,62 @@ "node": ">=4" } }, + "node_modules/postman-collection": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-5.3.0.tgz", + "integrity": "sha512-PMa5vRheqDFfS1bkRg8WBidWxunRA80sT5YNLP27YC5+ycyfiLMCwPnqQd1zfvxkGk04Pr9UronWmmgsbpsVyQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@faker-js/faker": "5.5.3", + "file-type": "3.9.0", + "http-reasons": "0.1.0", + "iconv-lite": "0.6.3", + "liquid-json": "0.3.1", + "lodash": "4.17.23", + "mime": "3.0.0", + "mime-format": "2.0.2", + "postman-url-encoder": "3.0.8", + "semver": "7.7.1", + "uuid": "8.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/postman-collection/node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/postman-collection/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postman-url-encoder": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.8.tgz", + "integrity": "sha512-EOgUMBazo7JNP4TDrd64TsooCiWzzo4143Ws8E8WYGEpn2PKpq+S4XRTDhuRTYHm3VKOpUZs7ZYZq7zSDuesqA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prismjs": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", @@ -4694,6 +5511,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/radix3": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", @@ -4771,6 +5598,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/reftools": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", + "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==", + "dev": true, + "license": "BSD-3-Clause", + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, "node_modules/regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", @@ -5017,6 +5854,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/restructure": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", @@ -5083,6 +5939,7 @@ "version": "4.45.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -5117,6 +5974,13 @@ "fsevents": "~2.3.2" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", @@ -5186,6 +6050,66 @@ "@types/hast": "^3.0.4" } }, + "node_modules/should": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", + "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "should-equal": "^2.0.0", + "should-format": "^3.0.3", + "should-type": "^1.4.0", + "should-type-adaptors": "^1.0.1", + "should-util": "^1.0.0" + } + }, + "node_modules/should-equal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", + "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "should-type": "^1.4.0" + } + }, + "node_modules/should-format": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "should-type": "^1.3.0", + "should-type-adaptors": "^1.0.1" + } + }, + "node_modules/should-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/should-type-adaptors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", + "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "should-type": "^1.3.0", + "should-util": "^1.0.0" + } + }, + "node_modules/should-util": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", + "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", + "dev": true, + "license": "MIT" + }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -5284,6 +6208,25 @@ "astro": "^5.1.6" } }, + "node_modules/starlight-openapi": { + "version": "0.22.1", + "resolved": "https://registry.npmjs.org/starlight-openapi/-/starlight-openapi-0.22.1.tgz", + "integrity": "sha512-hpYYpomwqb7f1wgpV+aV7O7sQu2QuG1VYFN+PZiqYj5RjbiCK0FKSF5YNnCZSfqiZRW3tCahu2JrxVVSqPUI+A==", + "license": "MIT", + "dependencies": { + "@readme/openapi-parser": "^4.1.2", + "github-slugger": "^2.0.0", + "url-template": "^3.1.1" + }, + "engines": { + "node": ">=18.17.1" + }, + "peerDependencies": { + "@astrojs/markdown-remark": ">=6.0.1", + "@astrojs/starlight": ">=0.34.0", + "astro": ">=5.5.0" + } + }, "node_modules/stream-replace-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", @@ -5348,6 +6291,44 @@ "inline-style-parser": "0.2.4" } }, + "node_modules/swagger2openapi": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz", + "integrity": "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "call-me-maybe": "^1.0.1", + "node-fetch": "^2.6.1", + "node-fetch-h2": "^2.3.0", + "node-readfiles": "^0.2.0", + "oas-kit-common": "^1.0.8", + "oas-resolver": "^2.5.6", + "oas-schema-walker": "^1.1.5", + "oas-validator": "^5.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + }, + "bin": { + "boast": "boast.js", + "oas-validate": "oas-validate.js", + "swagger2openapi": "swagger2openapi.js" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/swagger2openapi/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/tiny-inflate": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", @@ -5761,11 +6742,68 @@ } } }, + "node_modules/url-template": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-3.1.1.tgz", + "integrity": "sha512-4oszoaEKE/mQOtAmdMWqIRHmkxWkUZMnXFnjQ5i01CuRSK3uluxcH1MRVVVWmhlnzT1SCDfKxxficm2G37qzCA==", + "license": "BSD-3-Clause", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate.io-array": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", + "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate.io-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz", + "integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==", + "dev": true + }, + "node_modules/validate.io-integer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/validate.io-integer/-/validate.io-integer-1.0.5.tgz", + "integrity": "sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==", + "dev": true, + "dependencies": { + "validate.io-number": "^1.0.3" + } + }, + "node_modules/validate.io-integer-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/validate.io-integer-array/-/validate.io-integer-array-1.0.0.tgz", + "integrity": "sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA==", + "dev": true, + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-integer": "^1.0.4" + } + }, + "node_modules/validate.io-number": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz", + "integrity": "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==", + "dev": true + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -5809,6 +6847,7 @@ "version": "6.3.5", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -5962,6 +7001,35 @@ "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==" }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", @@ -5970,6 +7038,51 @@ "node": ">=12" } }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", @@ -6010,6 +7123,7 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 0b92ff4..606f7ea 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,10 @@ "astro-og-canvas": "^0.5.3", "js-cookie": "^3.0.5", "sharp": "^0.33.4", - "starlight-llms-txt": "^0.5.1" + "starlight-llms-txt": "^0.5.1", + "starlight-openapi": "^0.22.1" + }, + "devDependencies": { + "openapi-to-postmanv2": "^6.0.1" } } diff --git a/public/schemas/threat-intel-beta.postman_collection.json b/public/schemas/threat-intel-beta.postman_collection.json new file mode 100644 index 0000000..8a6d981 --- /dev/null +++ b/public/schemas/threat-intel-beta.postman_collection.json @@ -0,0 +1,1782 @@ +{ + "item": [ + { + "name": "List all vulnerabilities", + "request": { + "name": "List all vulnerabilities", + "description": { + "content": "Paginated listing of every published vulnerability for the given\nplatform, ordered by descending `id`.\n\nSupports **two independent pagination strategies**:\n\n- **Offset** (`?page=&per_page=`) — returns a `pagination` block with\n totals. Easy to jump to a specific page; slower at depth and\n susceptible to row-shift when new vulnerabilities land while paging.\n- **Cursor** (`?cursor=`) — returns a `cursor` block with `next_cursor`,\n `has_more`, `per_page`. Stable under concurrent inserts and faster at\n any depth. No `total` count.\n\n`cursor` and `page` are mutually exclusive; passing both returns\n`422 Unprocessable Entity`.\n", + "type": "text/plain" + }, + "url": { + "path": [ + "all" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "disabled": false, + "key": "platform", + "value": "wordpress", + "description": "Platform to query. Case-insensitive." + }, + { + "disabled": false, + "key": "page", + "value": "1", + "description": "Offset-pagination page (1-indexed). Mutually exclusive with `cursor`." + }, + { + "disabled": false, + "key": "per_page", + "value": "100", + "description": "Page size." + }, + { + "disabled": false, + "key": "cursor", + "value": "string", + "description": "Opaque cursor. Presence of the param switches to cursor mode (send an\nempty value to bootstrap). Mutually exclusive with `page`.\n" + }, + { + "disabled": false, + "key": "include", + "value": "details", + "description": "Pass `details` to include the full advisory body (`advisory_details`) per item." + } + ], + "variable": [] + }, + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "method": "GET", + "auth": null + }, + "response": [ + { + "name": "Paginated vulnerability listing.", + "originalRequest": { + "url": { + "path": [ + "all" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "platform", + "value": "wordpress" + }, + { + "key": "page", + "value": "1" + }, + { + "key": "per_page", + "value": "100" + }, + { + "key": "cursor", + "value": "string" + }, + { + "key": "include", + "value": "details" + } + ], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "method": "GET", + "body": {} + }, + "status": "OK", + "code": 200, + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": "{\n \"vulnerabilities\": [],\n \"pagination\": {\n \"current_page\": 1,\n \"per_page\": 25,\n \"total\": 6115,\n \"total_pages\": 245,\n \"has_next_page\": true,\n \"has_previous_page\": false,\n \"next_page\": 2,\n \"previous_page\": null,\n \"from\": 1,\n \"to\": 25\n }\n}", + "cookie": [], + "_postman_previewlanguage": "json" + }, + { + "name": "Missing or invalid `PSKey` header.", + "originalRequest": { + "url": { + "path": [ + "all" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "platform", + "value": "wordpress" + }, + { + "key": "page", + "value": "1" + }, + { + "key": "per_page", + "value": "100" + }, + { + "key": "cursor", + "value": "string" + }, + { + "key": "include", + "value": "details" + } + ], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Unauthorized", + "code": 401, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "API key not authorised for the requested endpoint.", + "originalRequest": { + "url": { + "path": [ + "all" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "platform", + "value": "wordpress" + }, + { + "key": "page", + "value": "1" + }, + { + "key": "per_page", + "value": "100" + }, + { + "key": "cursor", + "value": "string" + }, + { + "key": "include", + "value": "details" + } + ], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Forbidden", + "code": 403, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "Invalid parameter combination (e.g. `cursor` + `page`), invalid `platform`, or `per_page > 500`.", + "originalRequest": { + "url": { + "path": [ + "all" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "platform", + "value": "wordpress" + }, + { + "key": "page", + "value": "1" + }, + { + "key": "per_page", + "value": "100" + }, + { + "key": "cursor", + "value": "string" + }, + { + "key": "include", + "value": "details" + } + ], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Unprocessable Entity (WebDAV) (RFC 4918)", + "code": 422, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "Rate limit exceeded.", + "originalRequest": { + "url": { + "path": [ + "all" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "platform", + "value": "wordpress" + }, + { + "key": "page", + "value": "1" + }, + { + "key": "per_page", + "value": "100" + }, + { + "key": "cursor", + "value": "string" + }, + { + "key": "include", + "value": "details" + } + ], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Too Many Requests", + "code": 429, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + } + ], + "event": [], + "protocolProfileBehavior": { + "disableBodyPruning": true + } + }, + { + "name": "Latest vulnerabilities (last 24 hours)", + "request": { + "name": "Latest vulnerabilities (last 24 hours)", + "description": { + "content": "Returns vulnerabilities whose row was inserted into the Patchstack\ndatabase in the last 24 hours. The filter is on `created_at`\n(insertion time), **not** `disclosure_date`.\n\nAccepts the same query parameters as `/all`.\n", + "type": "text/plain" + }, + "url": { + "path": [ + "latest" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "disabled": false, + "key": "platform", + "value": "wordpress", + "description": "Platform to query. Case-insensitive." + }, + { + "disabled": false, + "key": "page", + "value": "1", + "description": "Offset-pagination page (1-indexed). Mutually exclusive with `cursor`." + }, + { + "disabled": false, + "key": "per_page", + "value": "100", + "description": "Page size." + }, + { + "disabled": false, + "key": "cursor", + "value": "string", + "description": "Opaque cursor. Presence of the param switches to cursor mode (send an\nempty value to bootstrap). Mutually exclusive with `page`.\n" + }, + { + "disabled": false, + "key": "include", + "value": "details", + "description": "Pass `details` to include the full advisory body (`advisory_details`) per item." + } + ], + "variable": [] + }, + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "method": "GET", + "auth": null + }, + "response": [ + { + "name": "Paginated 24h vulnerability listing.", + "originalRequest": { + "url": { + "path": [ + "latest" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "platform", + "value": "wordpress" + }, + { + "key": "page", + "value": "1" + }, + { + "key": "per_page", + "value": "100" + }, + { + "key": "cursor", + "value": "string" + }, + { + "key": "include", + "value": "details" + } + ], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "method": "GET", + "body": {} + }, + "status": "OK", + "code": 200, + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": "{\n \"vulnerabilities\": [\n {\n \"id\": 46500,\n \"title\": \"NPM: OpenClaw: ...\",\n \"disclosed_at\": \"2026-04-03T03:15:56+00:00\",\n \"created_at\": \"2026-04-21T08:38:34+00:00\",\n \"url\": \"https://patchstack.com/database/npm/npm/openclaw/vulnerability/...\",\n \"vuln_type\": \"Other Vulnerability Type\",\n \"cve\": \"2026-41331\",\n \"is_exploited\": false,\n \"patch_priority\": 2,\n \"advisory_details\": \"## Summary\\n...\",\n \"product\": {\n \"id\": 23595,\n \"name\": \"openclaw\",\n \"slug\": \"openclaw\"\n },\n \"cvss\": {\n \"score\": 6.9,\n \"vector\": \"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N\"\n },\n \"cwe\": {\n \"id\": 770,\n \"name\": \"Allocation of Resources Without Limits or Throttling\"\n },\n \"capec\": {\n \"id\": null,\n \"name\": null\n },\n \"references\": [\n \"https://github.com/openclaw/openclaw/security/advisories/GHSA-m6fx-m8hc-572m\",\n \"https://github.com/openclaw/openclaw/releases/tag/v2026.3.31\"\n ],\n \"ghsa\": \"GHSA-m6fx-m8hc-572m\",\n \"version_info\": {\n \"affected\": \"<= 2026.3.28\",\n \"fixed\": \"2026.3.31\"\n }\n },\n {\n \"id\": 46500,\n \"title\": \"NPM: OpenClaw: ...\",\n \"disclosed_at\": \"2026-04-03T03:15:56+00:00\",\n \"created_at\": \"2026-04-21T08:38:34+00:00\",\n \"url\": \"https://patchstack.com/database/npm/npm/openclaw/vulnerability/...\",\n \"vuln_type\": \"Other Vulnerability Type\",\n \"cve\": \"2026-41331\",\n \"is_exploited\": false,\n \"patch_priority\": 2,\n \"advisory_details\": \"## Summary\\n...\",\n \"product\": {\n \"id\": 23595,\n \"name\": \"openclaw\",\n \"slug\": \"openclaw\"\n },\n \"cvss\": {\n \"score\": 6.9,\n \"vector\": \"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N\"\n },\n \"cwe\": {\n \"id\": 770,\n \"name\": \"Allocation of Resources Without Limits or Throttling\"\n },\n \"capec\": {\n \"id\": null,\n \"name\": null\n },\n \"references\": [\n \"https://github.com/openclaw/openclaw/security/advisories/GHSA-m6fx-m8hc-572m\",\n \"https://github.com/openclaw/openclaw/releases/tag/v2026.3.31\"\n ],\n \"ghsa\": \"GHSA-m6fx-m8hc-572m\",\n \"version_info\": {\n \"affected\": \"<= 2026.3.28\",\n \"fixed\": \"2026.3.31\"\n }\n }\n ],\n \"pagination\": {\n \"current_page\": 1,\n \"per_page\": 25,\n \"total\": 6115,\n \"total_pages\": 245,\n \"has_next_page\": true,\n \"has_previous_page\": false,\n \"from\": 1,\n \"to\": 25,\n \"next_page\": 2,\n \"previous_page\": null\n }\n}", + "cookie": [], + "_postman_previewlanguage": "json" + }, + { + "name": "Missing or invalid `PSKey` header.", + "originalRequest": { + "url": { + "path": [ + "latest" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "platform", + "value": "wordpress" + }, + { + "key": "page", + "value": "1" + }, + { + "key": "per_page", + "value": "100" + }, + { + "key": "cursor", + "value": "string" + }, + { + "key": "include", + "value": "details" + } + ], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Unauthorized", + "code": 401, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "API key not authorised for the requested endpoint.", + "originalRequest": { + "url": { + "path": [ + "latest" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "platform", + "value": "wordpress" + }, + { + "key": "page", + "value": "1" + }, + { + "key": "per_page", + "value": "100" + }, + { + "key": "cursor", + "value": "string" + }, + { + "key": "include", + "value": "details" + } + ], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Forbidden", + "code": 403, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "Invalid parameter combination (e.g. `cursor` + `page`), invalid `platform`, or `per_page > 500`.", + "originalRequest": { + "url": { + "path": [ + "latest" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "platform", + "value": "wordpress" + }, + { + "key": "page", + "value": "1" + }, + { + "key": "per_page", + "value": "100" + }, + { + "key": "cursor", + "value": "string" + }, + { + "key": "include", + "value": "details" + } + ], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Unprocessable Entity (WebDAV) (RFC 4918)", + "code": 422, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "Rate limit exceeded.", + "originalRequest": { + "url": { + "path": [ + "latest" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "platform", + "value": "wordpress" + }, + { + "key": "page", + "value": "1" + }, + { + "key": "per_page", + "value": "100" + }, + { + "key": "cursor", + "value": "string" + }, + { + "key": "include", + "value": "details" + } + ], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Too Many Requests", + "code": 429, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + } + ], + "event": [], + "protocolProfileBehavior": { + "disableBodyPruning": true + } + }, + { + "name": "product/{type}/{name}/{version}", + "item": [ + { + "name": "Find vulnerabilities for a product", + "request": { + "name": "Find vulnerabilities for a product", + "description": { + "content": "Match a specific product + version against the vulnerability database\nand return every applicable advisory.\n\nnpm package slugs that include a `/` (e.g. `@scope/pkg`) conflict with\nthe route separator. URL-encode the `/` as `%2F`.\n", + "type": "text/plain" + }, + "url": { + "path": [ + "product", + ":type", + ":name", + ":version" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "disabled": false, + "key": "include", + "value": "details", + "description": "Pass `details` to include the full advisory body (`advisory_details`) per item." + } + ], + "variable": [ + { + "disabled": false, + "type": "any", + "value": "npm", + "key": "type", + "description": "(Required) Product ecosystem." + }, + { + "disabled": false, + "type": "any", + "value": "axios", + "key": "name", + "description": "(Required) npm package slug or WordPress plugin/theme slug. Use `wordpress` when `type=wordpress`." + }, + { + "disabled": false, + "type": "any", + "value": "0.21.4", + "key": "version", + "description": "(Required) Concrete version (e.g. `0.21.4`) or `*` to return every advisory for the product." + } + ] + }, + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "method": "GET", + "auth": null + }, + "response": [ + { + "name": "Matched advisories (possibly empty).", + "originalRequest": { + "url": { + "path": [ + "product", + ":type", + ":name", + ":version" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "include", + "value": "details" + } + ], + "variable": [ + { + "disabled": false, + "type": "any", + "value": "npm", + "key": "type", + "description": "(Required) Product ecosystem." + }, + { + "disabled": false, + "type": "any", + "value": "axios", + "key": "name", + "description": "(Required) npm package slug or WordPress plugin/theme slug. Use `wordpress` when `type=wordpress`." + }, + { + "disabled": false, + "type": "any", + "value": "0.21.4", + "key": "version", + "description": "(Required) Concrete version (e.g. `0.21.4`) or `*` to return every advisory for the product." + } + ] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "method": "GET", + "body": {} + }, + "status": "OK", + "code": 200, + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": "{\n \"vulnerabilities\": [\n {\n \"id\": 46500,\n \"title\": \"NPM: OpenClaw: ...\",\n \"disclosed_at\": \"2026-04-03T03:15:56+00:00\",\n \"created_at\": \"2026-04-21T08:38:34+00:00\",\n \"url\": \"https://patchstack.com/database/npm/npm/openclaw/vulnerability/...\",\n \"vuln_type\": \"Other Vulnerability Type\",\n \"cve\": \"2026-41331\",\n \"is_exploited\": false,\n \"patch_priority\": 2,\n \"advisory_details\": \"## Summary\\n...\",\n \"product\": {\n \"id\": 23595,\n \"name\": \"openclaw\",\n \"slug\": \"openclaw\"\n },\n \"cvss\": {\n \"score\": 6.9,\n \"vector\": \"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N\"\n },\n \"cwe\": {\n \"id\": 770,\n \"name\": \"Allocation of Resources Without Limits or Throttling\"\n },\n \"capec\": {\n \"id\": null,\n \"name\": null\n },\n \"references\": [\n \"https://github.com/openclaw/openclaw/security/advisories/GHSA-m6fx-m8hc-572m\",\n \"https://github.com/openclaw/openclaw/releases/tag/v2026.3.31\"\n ],\n \"ghsa\": \"GHSA-m6fx-m8hc-572m\",\n \"version_info\": {\n \"affected\": \"<= 2026.3.28\",\n \"fixed\": \"2026.3.31\"\n }\n },\n {\n \"id\": 46500,\n \"title\": \"NPM: OpenClaw: ...\",\n \"disclosed_at\": \"2026-04-03T03:15:56+00:00\",\n \"created_at\": \"2026-04-21T08:38:34+00:00\",\n \"url\": \"https://patchstack.com/database/npm/npm/openclaw/vulnerability/...\",\n \"vuln_type\": \"Other Vulnerability Type\",\n \"cve\": \"2026-41331\",\n \"is_exploited\": false,\n \"patch_priority\": 2,\n \"advisory_details\": \"## Summary\\n...\",\n \"product\": {\n \"id\": 23595,\n \"name\": \"openclaw\",\n \"slug\": \"openclaw\"\n },\n \"cvss\": {\n \"score\": 6.9,\n \"vector\": \"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N\"\n },\n \"cwe\": {\n \"id\": 770,\n \"name\": \"Allocation of Resources Without Limits or Throttling\"\n },\n \"capec\": {\n \"id\": null,\n \"name\": null\n },\n \"references\": [\n \"https://github.com/openclaw/openclaw/security/advisories/GHSA-m6fx-m8hc-572m\",\n \"https://github.com/openclaw/openclaw/releases/tag/v2026.3.31\"\n ],\n \"ghsa\": \"GHSA-m6fx-m8hc-572m\",\n \"version_info\": {\n \"affected\": \"<= 2026.3.28\",\n \"fixed\": \"2026.3.31\"\n }\n }\n ]\n}", + "cookie": [], + "_postman_previewlanguage": "json" + }, + { + "name": "Missing or invalid `PSKey` header.", + "originalRequest": { + "url": { + "path": [ + "product", + ":type", + ":name", + ":version" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "include", + "value": "details" + } + ], + "variable": [ + { + "disabled": false, + "type": "any", + "value": "npm", + "key": "type", + "description": "(Required) Product ecosystem." + }, + { + "disabled": false, + "type": "any", + "value": "axios", + "key": "name", + "description": "(Required) npm package slug or WordPress plugin/theme slug. Use `wordpress` when `type=wordpress`." + }, + { + "disabled": false, + "type": "any", + "value": "0.21.4", + "key": "version", + "description": "(Required) Concrete version (e.g. `0.21.4`) or `*` to return every advisory for the product." + } + ] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Unauthorized", + "code": 401, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "API key not authorised for the requested endpoint.", + "originalRequest": { + "url": { + "path": [ + "product", + ":type", + ":name", + ":version" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "include", + "value": "details" + } + ], + "variable": [ + { + "disabled": false, + "type": "any", + "value": "npm", + "key": "type", + "description": "(Required) Product ecosystem." + }, + { + "disabled": false, + "type": "any", + "value": "axios", + "key": "name", + "description": "(Required) npm package slug or WordPress plugin/theme slug. Use `wordpress` when `type=wordpress`." + }, + { + "disabled": false, + "type": "any", + "value": "0.21.4", + "key": "version", + "description": "(Required) Concrete version (e.g. `0.21.4`) or `*` to return every advisory for the product." + } + ] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Forbidden", + "code": 403, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "Invalid parameter combination (e.g. `cursor` + `page`), invalid `platform`, or `per_page > 500`.", + "originalRequest": { + "url": { + "path": [ + "product", + ":type", + ":name", + ":version" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "include", + "value": "details" + } + ], + "variable": [ + { + "disabled": false, + "type": "any", + "value": "npm", + "key": "type", + "description": "(Required) Product ecosystem." + }, + { + "disabled": false, + "type": "any", + "value": "axios", + "key": "name", + "description": "(Required) npm package slug or WordPress plugin/theme slug. Use `wordpress` when `type=wordpress`." + }, + { + "disabled": false, + "type": "any", + "value": "0.21.4", + "key": "version", + "description": "(Required) Concrete version (e.g. `0.21.4`) or `*` to return every advisory for the product." + } + ] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Unprocessable Entity (WebDAV) (RFC 4918)", + "code": 422, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "Rate limit exceeded.", + "originalRequest": { + "url": { + "path": [ + "product", + ":type", + ":name", + ":version" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [ + { + "key": "include", + "value": "details" + } + ], + "variable": [ + { + "disabled": false, + "type": "any", + "value": "npm", + "key": "type", + "description": "(Required) Product ecosystem." + }, + { + "disabled": false, + "type": "any", + "value": "axios", + "key": "name", + "description": "(Required) npm package slug or WordPress plugin/theme slug. Use `wordpress` when `type=wordpress`." + }, + { + "disabled": false, + "type": "any", + "value": "0.21.4", + "key": "version", + "description": "(Required) Concrete version (e.g. `0.21.4`) or `*` to return every advisory for the product." + } + ] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Too Many Requests", + "code": 429, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + } + ], + "event": [], + "protocolProfileBehavior": { + "disableBodyPruning": true + } + }, + { + "name": "Boolean exists check for a product", + "request": { + "name": "Boolean exists check for a product", + "description": { + "content": "Boolean-only variant of the product lookup. Returns `{ \"vulnerable\": true }`\nor `{ \"vulnerable\": false }` without the advisory payload — useful for\nlightweight health checks and dashboard tiles where the advisory body\nisn't needed.\n", + "type": "text/plain" + }, + "url": { + "path": [ + "product", + ":type", + ":name", + ":version", + "exists" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [], + "variable": [ + { + "disabled": false, + "type": "any", + "value": "npm", + "key": "type", + "description": "(Required) Product ecosystem." + }, + { + "disabled": false, + "type": "any", + "value": "axios", + "key": "name", + "description": "(Required) npm package slug or WordPress plugin/theme slug. Use `wordpress` when `type=wordpress`." + }, + { + "disabled": false, + "type": "any", + "value": "0.21.4", + "key": "version", + "description": "(Required) Concrete version (e.g. `0.21.4`) or `*` to return every advisory for the product." + } + ] + }, + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "method": "GET", + "auth": null + }, + "response": [ + { + "name": "Boolean result.", + "originalRequest": { + "url": { + "path": [ + "product", + ":type", + ":name", + ":version", + "exists" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [], + "variable": [ + { + "disabled": false, + "type": "any", + "value": "npm", + "key": "type", + "description": "(Required) Product ecosystem." + }, + { + "disabled": false, + "type": "any", + "value": "axios", + "key": "name", + "description": "(Required) npm package slug or WordPress plugin/theme slug. Use `wordpress` when `type=wordpress`." + }, + { + "disabled": false, + "type": "any", + "value": "0.21.4", + "key": "version", + "description": "(Required) Concrete version (e.g. `0.21.4`) or `*` to return every advisory for the product." + } + ] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "method": "GET", + "body": {} + }, + "status": "OK", + "code": 200, + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": "{\n \"vulnerable\": true\n}", + "cookie": [], + "_postman_previewlanguage": "json" + }, + { + "name": "Missing or invalid `PSKey` header.", + "originalRequest": { + "url": { + "path": [ + "product", + ":type", + ":name", + ":version", + "exists" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [], + "variable": [ + { + "disabled": false, + "type": "any", + "value": "npm", + "key": "type", + "description": "(Required) Product ecosystem." + }, + { + "disabled": false, + "type": "any", + "value": "axios", + "key": "name", + "description": "(Required) npm package slug or WordPress plugin/theme slug. Use `wordpress` when `type=wordpress`." + }, + { + "disabled": false, + "type": "any", + "value": "0.21.4", + "key": "version", + "description": "(Required) Concrete version (e.g. `0.21.4`) or `*` to return every advisory for the product." + } + ] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Unauthorized", + "code": 401, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "API key not authorised for the requested endpoint.", + "originalRequest": { + "url": { + "path": [ + "product", + ":type", + ":name", + ":version", + "exists" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [], + "variable": [ + { + "disabled": false, + "type": "any", + "value": "npm", + "key": "type", + "description": "(Required) Product ecosystem." + }, + { + "disabled": false, + "type": "any", + "value": "axios", + "key": "name", + "description": "(Required) npm package slug or WordPress plugin/theme slug. Use `wordpress` when `type=wordpress`." + }, + { + "disabled": false, + "type": "any", + "value": "0.21.4", + "key": "version", + "description": "(Required) Concrete version (e.g. `0.21.4`) or `*` to return every advisory for the product." + } + ] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Forbidden", + "code": 403, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "Rate limit exceeded.", + "originalRequest": { + "url": { + "path": [ + "product", + ":type", + ":name", + ":version", + "exists" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [], + "variable": [ + { + "disabled": false, + "type": "any", + "value": "npm", + "key": "type", + "description": "(Required) Product ecosystem." + }, + { + "disabled": false, + "type": "any", + "value": "axios", + "key": "name", + "description": "(Required) npm package slug or WordPress plugin/theme slug. Use `wordpress` when `type=wordpress`." + }, + { + "disabled": false, + "type": "any", + "value": "0.21.4", + "key": "version", + "description": "(Required) Concrete version (e.g. `0.21.4`) or `*` to return every advisory for the product." + } + ] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "GET", + "body": {} + }, + "status": "Too Many Requests", + "code": 429, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + } + ], + "event": [], + "protocolProfileBehavior": { + "disableBodyPruning": true + } + } + ], + "event": [] + }, + { + "name": "Bulk product check", + "request": { + "name": "Bulk product check", + "description": { + "content": "Check up to 50 products in a single request. Mirrors the format\ndocumented for the Extended tier's `/batch` endpoint.\n\nThe payload is a raw JSON array (not wrapped in an object). Each item\nis `{ \"type\", \"name\", \"version\", \"exists\"? }` — when `exists: true`\nthe result for that item is boolean-only; otherwise it's the full\nadvisory list for the product.\n", + "type": "text/plain" + }, + "url": { + "path": [ + "batch" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "[\n {\n \"type\": \"npm\",\n \"name\": \"axios\",\n \"version\": \"0.21.4\",\n \"exists\": false\n },\n {\n \"type\": \"plugin\",\n \"name\": \"tutor\",\n \"version\": \"1.5.2\",\n \"exists\": true\n },\n {\n \"type\": \"wordpress\",\n \"name\": \"wordpress\",\n \"version\": \"6.0.0\",\n \"exists\": true\n }\n]", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [ + { + "name": "Per-item results, in request order.", + "originalRequest": { + "url": { + "path": [ + "batch" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "method": "POST", + "body": { + "mode": "raw", + "raw": "[\n {\n \"type\": \"npm\",\n \"name\": \"axios\",\n \"version\": \"0.21.4\",\n \"exists\": false\n },\n {\n \"type\": \"plugin\",\n \"name\": \"tutor\",\n \"version\": \"1.5.2\",\n \"exists\": true\n },\n {\n \"type\": \"wordpress\",\n \"name\": \"wordpress\",\n \"version\": \"6.0.0\",\n \"exists\": true\n }\n]", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "status": "OK", + "code": 200, + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": "[]", + "cookie": [], + "_postman_previewlanguage": "json" + }, + { + "name": "Missing or invalid `PSKey` header.", + "originalRequest": { + "url": { + "path": [ + "batch" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "POST", + "body": { + "mode": "raw", + "raw": "[\n {\n \"type\": \"npm\",\n \"name\": \"axios\",\n \"version\": \"0.21.4\",\n \"exists\": false\n },\n {\n \"type\": \"plugin\",\n \"name\": \"tutor\",\n \"version\": \"1.5.2\",\n \"exists\": true\n },\n {\n \"type\": \"wordpress\",\n \"name\": \"wordpress\",\n \"version\": \"6.0.0\",\n \"exists\": true\n }\n]", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "status": "Unauthorized", + "code": 401, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "API key not authorised for the requested endpoint.", + "originalRequest": { + "url": { + "path": [ + "batch" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "POST", + "body": { + "mode": "raw", + "raw": "[\n {\n \"type\": \"npm\",\n \"name\": \"axios\",\n \"version\": \"0.21.4\",\n \"exists\": false\n },\n {\n \"type\": \"plugin\",\n \"name\": \"tutor\",\n \"version\": \"1.5.2\",\n \"exists\": true\n },\n {\n \"type\": \"wordpress\",\n \"name\": \"wordpress\",\n \"version\": \"6.0.0\",\n \"exists\": true\n }\n]", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "status": "Forbidden", + "code": 403, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "Invalid parameter combination (e.g. `cursor` + `page`), invalid `platform`, or `per_page > 500`.", + "originalRequest": { + "url": { + "path": [ + "batch" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "POST", + "body": { + "mode": "raw", + "raw": "[\n {\n \"type\": \"npm\",\n \"name\": \"axios\",\n \"version\": \"0.21.4\",\n \"exists\": false\n },\n {\n \"type\": \"plugin\",\n \"name\": \"tutor\",\n \"version\": \"1.5.2\",\n \"exists\": true\n },\n {\n \"type\": \"wordpress\",\n \"name\": \"wordpress\",\n \"version\": \"6.0.0\",\n \"exists\": true\n }\n]", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "status": "Unprocessable Entity (WebDAV) (RFC 4918)", + "code": 422, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + }, + { + "name": "Rate limit exceeded.", + "originalRequest": { + "url": { + "path": [ + "batch" + ], + "host": [ + "{{baseUrl}}" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "description": { + "content": "Added as a part of security scheme: apikey", + "type": "text/plain" + }, + "key": "PSKey", + "value": "" + } + ], + "method": "POST", + "body": { + "mode": "raw", + "raw": "[\n {\n \"type\": \"npm\",\n \"name\": \"axios\",\n \"version\": \"0.21.4\",\n \"exists\": false\n },\n {\n \"type\": \"plugin\",\n \"name\": \"tutor\",\n \"version\": \"1.5.2\",\n \"exists\": true\n },\n {\n \"type\": \"wordpress\",\n \"name\": \"wordpress\",\n \"version\": \"6.0.0\",\n \"exists\": true\n }\n]", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "status": "Too Many Requests", + "code": 429, + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": "", + "cookie": [], + "_postman_previewlanguage": "text" + } + ], + "event": [], + "protocolProfileBehavior": { + "disableBodyPruning": true + } + } + ], + "event": [], + "variable": [ + { + "type": "string", + "value": "https://patchstack.com/database/api/beta", + "key": "baseUrl" + } + ], + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "PSKey" + }, + { + "key": "value", + "value": "{{apiKey}}" + }, + { + "key": "in", + "value": "header" + } + ] + }, + "info": { + "name": "Patchstack Threat Intelligence API — Beta", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "description": { + "content": "The Beta endpoints live alongside the v2 API and add npm coverage, an optional\nfull advisory body, a consistent nested response shape, and cursor pagination.\nThey are the recommended endpoints for new integrations; v2 remains available\nfor backwards compatibility.\n\n## Authentication\n\nEvery request must include your API key in the `PSKey` HTTP request header.\nRequest a key via .\n\n## Platforms\n\nPass `?platform=npm` (or `?platform=wordpress` — the default) on list\nendpoints. Platform names are case-insensitive.\n\n## Pagination\n\n`/all` and `/latest` support two independent strategies, selected by which\nquery parameter you pass:\n\n- **Offset** (`?page=&per_page=`) — returns totals; easy to jump to a\n specific page; susceptible to row-shift when new rows land while paging.\n- **Cursor** (`?cursor=`) — stable under concurrent inserts, faster at\n depth, no `total` count. Bootstrap with an empty value: `?cursor=`.\n\n`cursor` and `page` are mutually exclusive; passing both returns `422`.\n\n## Including full advisory bodies\n\nPass `?include=details` on any list endpoint to add an `advisory_details`\nmarkdown field to each item.\n\n## Scoped npm packages\n\nnpm package slugs that include a `/` (e.g. `@scope/pkg`) conflict with the\nroute separator. URL-encode the `/` as `%2F`.\n\n## Rate limiting\n\nSame policy as the Extended Threat Intelligence API. Contact\n if you need an elevated quota.\n\n## Errors\n\n| Status | Meaning |\n|---|---|\n| `401` | Missing or invalid `PSKey` header. |\n| `403` | API key not authorised for the requested endpoint. |\n| `422` | Invalid parameter combination or value (e.g. `cursor` + `page`, invalid `platform`, `per_page > 500`). |\n| `429` | Rate limit exceeded. |\n| `500` | Server error — include the request id in any bug report. |\n\nA malformed cursor returns `200` with an empty page rather than an error:\n\n```json\n{ \"vulnerabilities\": [], \"cursor\": { \"next_cursor\": null, \"has_more\": false, \"per_page\": 100 } }\n```\n\n## Related pages\n\n- Narrative guide with code samples: [Beta tier API](/api-solutions/threat-intelligence-api/beta/)\n- OpenAPI spec (import into Postman / Insomnia / Bruno / Hoppscotch): \n- Postman collection (pre-imported): \n\nIntegration questions: .\n\n\nContact Support:\n Name: Patchstack\n Email: dave.jong@patchstack.com", + "type": "text/plain" + } + } +} \ No newline at end of file diff --git a/public/schemas/threat-intel-beta.yaml b/public/schemas/threat-intel-beta.yaml new file mode 100644 index 0000000..5b77a43 --- /dev/null +++ b/public/schemas/threat-intel-beta.yaml @@ -0,0 +1,652 @@ +openapi: 3.1.0 +info: + title: Patchstack Threat Intelligence API — Beta + version: beta + summary: npm + WordPress vulnerability intelligence with nested response shape and cursor pagination. + description: | + The Beta endpoints live alongside the v2 API and add npm coverage, an optional + full advisory body, a consistent nested response shape, and cursor pagination. + They are the recommended endpoints for new integrations; v2 remains available + for backwards compatibility. + + ## Authentication + + Every request must include your API key in the `PSKey` HTTP request header. + Request a key via . + + ## Platforms + + Pass `?platform=npm` (or `?platform=wordpress` — the default) on list + endpoints. Platform names are case-insensitive. + + ## Pagination + + `/all` and `/latest` support two independent strategies, selected by which + query parameter you pass: + + - **Offset** (`?page=&per_page=`) — returns totals; easy to jump to a + specific page; susceptible to row-shift when new rows land while paging. + - **Cursor** (`?cursor=`) — stable under concurrent inserts, faster at + depth, no `total` count. Bootstrap with an empty value: `?cursor=`. + + `cursor` and `page` are mutually exclusive; passing both returns `422`. + + ## Including full advisory bodies + + Pass `?include=details` on any list endpoint to add an `advisory_details` + markdown field to each item. + + ## Scoped npm packages + + npm package slugs that include a `/` (e.g. `@scope/pkg`) conflict with the + route separator. URL-encode the `/` as `%2F`. + + ## Rate limiting + + Same policy as the Extended Threat Intelligence API. Contact + if you need an elevated quota. + + ## Errors + + | Status | Meaning | + |---|---| + | `401` | Missing or invalid `PSKey` header. | + | `403` | API key not authorised for the requested endpoint. | + | `422` | Invalid parameter combination or value (e.g. `cursor` + `page`, invalid `platform`, `per_page > 500`). | + | `429` | Rate limit exceeded. | + | `500` | Server error — include the request id in any bug report. | + + A malformed cursor returns `200` with an empty page rather than an error: + + ```json + { "vulnerabilities": [], "cursor": { "next_cursor": null, "has_more": false, "per_page": 100 } } + ``` + + ## Related pages + + - Narrative guide with code samples: [Beta tier API](/api-solutions/threat-intelligence-api/beta/) + - OpenAPI spec (import into Postman / Insomnia / Bruno / Hoppscotch): + - Postman collection (pre-imported): + + Integration questions: . + contact: + name: Patchstack + url: https://patchstack.com/for-hosts/ + email: dave.jong@patchstack.com + +servers: + - url: https://patchstack.com/database/api/beta + description: Production + +security: + - PSKey: [] + +tags: + - name: Vulnerabilities + description: | + Listing endpoints that return published vulnerabilities for a platform, + ordered by descending `id`. + + - [`GET /all`](/api-reference/threat-intelligence-beta/operations/listallvulnerabilities/) + — every published vulnerability for the platform, paginated. + - [`GET /latest`](/api-reference/threat-intelligence-beta/operations/listlatestvulnerabilities/) + — vulnerabilities whose row was inserted in the last 24 hours (filter + is on `created_at`, not `disclosure_date`). + + Both endpoints share the same response shape and support offset and + cursor pagination — see the overview for details. + - name: Products + description: | + Match a specific product + version against the vulnerability database + and return every applicable advisory. + + - [`GET /product/{type}/{name}/{version}`](/api-reference/threat-intelligence-beta/operations/findproductvulnerabilities/) + — full advisory list for the product. Use `version=*` to return every + advisory for the product regardless of version. + - [`GET /product/{type}/{name}/{version}/exists`](/api-reference/threat-intelligence-beta/operations/productvulnerabilityexists/) + — boolean-only variant for fast "is this vulnerable?" checks. + + `type` accepts `npm`, `plugin`, `theme`, or `wordpress`. For scoped npm + packages, URL-encode the `/` as `%2F`. + - name: Batch + description: | + Bulk endpoint for checking up to 50 products in a single request. Use + this when walking a dependency manifest (e.g. `package.json` or a list + of installed WordPress plugins) — it's significantly cheaper than + issuing one request per product. + + - [`POST /batch`](/api-reference/threat-intelligence-beta/operations/batchproductcheck/) + — payload is a JSON array of up to 50 `{type, name, version, exists?}` + objects. Per-item results are returned in request order. + +paths: + /all: + get: + tags: [Vulnerabilities] + summary: List all vulnerabilities + description: | + Paginated listing of every published vulnerability for the given + platform, ordered by descending `id`. + + Supports **two independent pagination strategies**: + + - **Offset** (`?page=&per_page=`) — returns a `pagination` block with + totals. Easy to jump to a specific page; slower at depth and + susceptible to row-shift when new vulnerabilities land while paging. + - **Cursor** (`?cursor=`) — returns a `cursor` block with `next_cursor`, + `has_more`, `per_page`. Stable under concurrent inserts and faster at + any depth. No `total` count. + + `cursor` and `page` are mutually exclusive; passing both returns + `422 Unprocessable Entity`. + operationId: listAllVulnerabilities + parameters: + - $ref: '#/components/parameters/Platform' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PerPage' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/Include' + responses: + '200': + description: Paginated vulnerability listing. + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/OffsetVulnerabilityList' + - $ref: '#/components/schemas/CursorVulnerabilityList' + examples: + offset: + summary: Offset mode + value: + vulnerabilities: [] + pagination: + current_page: 1 + per_page: 25 + total: 6115 + total_pages: 245 + has_next_page: true + has_previous_page: false + next_page: 2 + previous_page: null + from: 1 + to: 25 + cursor: + summary: Cursor mode + value: + vulnerabilities: [] + cursor: + next_cursor: djE6NDYzMzk + has_more: true + per_page: 25 + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '422': { $ref: '#/components/responses/UnprocessableEntity' } + '429': { $ref: '#/components/responses/TooManyRequests' } + + /latest: + get: + tags: [Vulnerabilities] + summary: Latest vulnerabilities (last 24 hours) + description: | + Returns vulnerabilities whose row was inserted into the Patchstack + database in the last 24 hours. The filter is on `created_at` + (insertion time), **not** `disclosure_date`. + + Accepts the same query parameters as `/all`. + operationId: listLatestVulnerabilities + parameters: + - $ref: '#/components/parameters/Platform' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PerPage' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/Include' + responses: + '200': + description: Paginated 24h vulnerability listing. + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/OffsetVulnerabilityList' + - $ref: '#/components/schemas/CursorVulnerabilityList' + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '422': { $ref: '#/components/responses/UnprocessableEntity' } + '429': { $ref: '#/components/responses/TooManyRequests' } + + /product/{type}/{name}/{version}: + get: + tags: [Products] + summary: Find vulnerabilities for a product + description: | + Match a specific product + version against the vulnerability database + and return every applicable advisory. + + npm package slugs that include a `/` (e.g. `@scope/pkg`) conflict with + the route separator. URL-encode the `/` as `%2F`. + operationId: findProductVulnerabilities + parameters: + - $ref: '#/components/parameters/ProductType' + - $ref: '#/components/parameters/ProductName' + - $ref: '#/components/parameters/ProductVersion' + - $ref: '#/components/parameters/Include' + responses: + '200': + description: Matched advisories (possibly empty). + content: + application/json: + schema: + type: object + properties: + vulnerabilities: + type: array + items: + $ref: '#/components/schemas/Vulnerability' + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '422': { $ref: '#/components/responses/UnprocessableEntity' } + '429': { $ref: '#/components/responses/TooManyRequests' } + + /product/{type}/{name}/{version}/exists: + get: + tags: [Products] + summary: Boolean exists check for a product + description: | + Boolean-only variant of the product lookup. Returns `{ "vulnerable": true }` + or `{ "vulnerable": false }` without the advisory payload — useful for + lightweight health checks and dashboard tiles where the advisory body + isn't needed. + operationId: productVulnerabilityExists + parameters: + - $ref: '#/components/parameters/ProductType' + - $ref: '#/components/parameters/ProductName' + - $ref: '#/components/parameters/ProductVersion' + responses: + '200': + description: Boolean result. + content: + application/json: + schema: + type: object + required: [vulnerable] + properties: + vulnerable: + type: boolean + example: + vulnerable: true + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '429': { $ref: '#/components/responses/TooManyRequests' } + + /batch: + post: + tags: [Batch] + summary: Bulk product check + description: | + Check up to 50 products in a single request. Mirrors the format + documented for the Extended tier's `/batch` endpoint. + + The payload is a raw JSON array (not wrapped in an object). Each item + is `{ "type", "name", "version", "exists"? }` — when `exists: true` + the result for that item is boolean-only; otherwise it's the full + advisory list for the product. + operationId: batchProductCheck + requestBody: + required: true + content: + application/json: + schema: + type: array + maxItems: 50 + items: + $ref: '#/components/schemas/BatchItem' + example: + - { type: npm, name: axios, version: 0.21.4, exists: false } + - { type: plugin, name: tutor, version: 1.5.2, exists: true } + - { type: wordpress, name: wordpress, version: 6.0.0, exists: true } + responses: + '200': + description: Per-item results, in request order. + content: + application/json: + schema: + type: array + items: + type: object + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '422': { $ref: '#/components/responses/UnprocessableEntity' } + '429': { $ref: '#/components/responses/TooManyRequests' } + +components: + securitySchemes: + PSKey: + type: apiKey + in: header + name: PSKey + description: API key issued by Patchstack. Request one via . + + parameters: + Platform: + name: platform + in: query + description: Platform to query. Case-insensitive. + required: false + schema: + type: string + enum: [wordpress, npm] + default: wordpress + Page: + name: page + in: query + description: Offset-pagination page (1-indexed). Mutually exclusive with `cursor`. + required: false + schema: + type: integer + minimum: 1 + default: 1 + PerPage: + name: per_page + in: query + description: Page size. + required: false + schema: + type: integer + minimum: 1 + maximum: 500 + default: 100 + Cursor: + name: cursor + in: query + description: | + Opaque cursor. Presence of the param switches to cursor mode (send an + empty value to bootstrap). Mutually exclusive with `page`. + required: false + schema: + type: string + Include: + name: include + in: query + description: Pass `details` to include the full advisory body (`advisory_details`) per item. + required: false + schema: + type: string + enum: [details] + ProductType: + name: type + in: path + required: true + description: Product ecosystem. + schema: + type: string + enum: [npm, plugin, theme, wordpress] + example: npm + ProductName: + name: name + in: path + required: true + description: npm package slug or WordPress plugin/theme slug. Use `wordpress` when `type=wordpress`. + schema: + type: string + example: axios + ProductVersion: + name: version + in: path + required: true + description: Concrete version (e.g. `0.21.4`) or `*` to return every advisory for the product. + schema: + type: string + example: 0.21.4 + + responses: + Unauthorized: + description: Missing or invalid `PSKey` header. + Forbidden: + description: API key not authorised for the requested endpoint. + UnprocessableEntity: + description: Invalid parameter combination (e.g. `cursor` + `page`), invalid `platform`, or `per_page > 500`. + TooManyRequests: + description: Rate limit exceeded. + + schemas: + Vulnerability: + type: object + description: Per-item shape shared across list endpoints when `platform=npm`. + required: + - id + - title + - disclosed_at + - created_at + - url + - vuln_type + - cve + - is_exploited + - patch_priority + - product + - cvss + - cwe + - capec + - references + - ghsa + - version_info + properties: + id: + type: integer + description: Stable Patchstack vulnerability id. + example: 46500 + title: + type: string + description: Human-readable title (prefixed with `NPM:` for npm advisories). + example: "NPM: OpenClaw: ..." + disclosed_at: + type: string + format: date-time + description: When the vulnerability was publicly disclosed. + created_at: + type: string + format: date-time + description: When the row was inserted into the Patchstack DB. Drives `/latest` windowing. + url: + type: string + format: uri + description: Public Patchstack vulnerability page (token-tagged). + vuln_type: + type: string + description: High-level vulnerability category. + example: Other Vulnerability Type + cve: + type: string + description: First CVE identifier, or empty string when none is assigned. + example: "2026-41331" + is_exploited: + type: boolean + description: Whether exploitation has been observed in the wild. + patch_priority: + type: integer + minimum: 1 + maximum: 3 + description: 1 (low) to 3 (high). + advisory_details: + type: string + description: Full advisory body (markdown). Only present when `?include=details` was passed. + product: + type: object + required: [id, name, slug] + properties: + id: { type: integer } + name: { type: string } + slug: { type: string } + cvss: + type: object + nullable: true + properties: + score: + type: number + format: float + nullable: true + vector: + type: string + nullable: true + cwe: + type: object + nullable: true + properties: + id: + type: integer + nullable: true + name: + type: string + nullable: true + capec: + type: object + nullable: true + properties: + id: + type: integer + nullable: true + name: + type: string + nullable: true + references: + type: array + description: External reference URLs (advisories, commits, tags). + items: + type: string + format: uri + ghsa: + type: string + description: GHSA identifier when the advisory came from the GitHub Advisory Database. + version_info: + type: object + required: [affected, fixed, patched_ranges] + properties: + affected: + type: string + description: Affected version range (e.g. `<= 2026.3.28`). + fixed: + type: string + description: First fixed version. + patched_ranges: + type: array + description: Structured list of patch ranges for advisories with multiple patch ranges. + items: + type: object + properties: + from_version: { type: string } + to_version: { type: string } + fixed_in: { type: string } + example: + id: 46500 + title: "NPM: OpenClaw: ..." + disclosed_at: "2026-04-03T03:15:56+00:00" + created_at: "2026-04-21T08:38:34+00:00" + url: "https://patchstack.com/database/npm/npm/openclaw/vulnerability/..." + vuln_type: Other Vulnerability Type + cve: "2026-41331" + is_exploited: false + patch_priority: 2 + advisory_details: "## Summary\n..." + product: + id: 23595 + name: openclaw + slug: openclaw + cvss: + score: 6.9 + vector: "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N" + cwe: + id: 770 + name: Allocation of Resources Without Limits or Throttling + capec: + id: null + name: null + references: + - "https://github.com/openclaw/openclaw/security/advisories/GHSA-m6fx-m8hc-572m" + - "https://github.com/openclaw/openclaw/releases/tag/v2026.3.31" + ghsa: GHSA-m6fx-m8hc-572m + version_info: + affected: "<= 2026.3.28" + fixed: "2026.3.31" + patched_ranges: [] + + OffsetPagination: + type: object + required: + - current_page + - per_page + - total + - total_pages + - has_next_page + - has_previous_page + - from + - to + properties: + current_page: { type: integer, example: 1 } + per_page: { type: integer, example: 25 } + total: { type: integer, example: 6115 } + total_pages: { type: integer, example: 245 } + has_next_page: { type: boolean, example: true } + has_previous_page: { type: boolean, example: false } + next_page: + type: integer + nullable: true + example: 2 + previous_page: + type: integer + nullable: true + example: null + from: { type: integer, example: 1 } + to: { type: integer, example: 25 } + + CursorPagination: + type: object + required: [next_cursor, has_more, per_page] + properties: + next_cursor: + type: string + nullable: true + description: Opaque cursor for the next page. `null` when there are no more pages. + example: djE6NDYzMzk + has_more: + type: boolean + example: true + per_page: + type: integer + example: 25 + + OffsetVulnerabilityList: + type: object + required: [vulnerabilities, pagination] + properties: + vulnerabilities: + type: array + items: + $ref: '#/components/schemas/Vulnerability' + pagination: + $ref: '#/components/schemas/OffsetPagination' + + CursorVulnerabilityList: + type: object + required: [vulnerabilities, cursor] + properties: + vulnerabilities: + type: array + items: + $ref: '#/components/schemas/Vulnerability' + cursor: + $ref: '#/components/schemas/CursorPagination' + + BatchItem: + type: object + required: [type, name, version] + properties: + type: + type: string + enum: [npm, plugin, theme, wordpress] + name: + type: string + version: + type: string + exists: + type: boolean + description: When `true`, return a boolean-only result for this item. diff --git a/src/content/docs/API solutions/Threat Intelligence API/beta.md b/src/content/docs/API solutions/Threat Intelligence API/beta.md index e34d3b6..489f160 100644 --- a/src/content/docs/API solutions/Threat Intelligence API/beta.md +++ b/src/content/docs/API solutions/Threat Intelligence API/beta.md @@ -1,319 +1,127 @@ --- -title: "Beta tier API" +title: "Beta API" excerpt: "Beta vulnerability endpoints — npm support, Resource-based shape, offset and cursor pagination, include=details." hidden: false metadata: image: [] robots: "index" createdAt: "Tue Apr 21 2026 00:00:00 GMT+0000 (Coordinated Universal Time)" -updatedAt: "Tue Apr 21 2026 00:00:00 GMT+0000 (Coordinated Universal Time)" +updatedAt: "Wed Apr 22 2026 00:00:00 GMT+0000 (Coordinated Universal Time)" sidebar: - order: 4 - label: "Beta tier API" + label: "Guide" --- -_The Beta endpoints live alongside the v2 API and add npm coverage, an optional full advisory body, a consistent nested response shape, and cursor pagination. They are the recommended endpoints for new integrations; v2 remains available for backwards compatibility._ +_The Beta API is a new generation of the Threat Intelligence API, currently available to **selected partners working directly with Patchstack**. It lives alongside the v2 API (Standard / Extended) and adds npm coverage, an optional full advisory body, a consistent nested response shape, and cursor pagination. If you'd like access to run an integration on Beta, [contact us](https://patchstack.com/for-hosts/)._ -## API Usage +> **Interactive reference:** Every endpoint, parameter, request body and response shape is documented in the [Threat Intelligence API (Beta) reference](/api-reference/threat-intelligence-beta/). -### Base URL +This page covers the concepts you need to use the API effectively — authentication, platforms, pagination, rate limiting, and migration from v2. Use it alongside the interactive reference. -``` -https://vdp-api.patchstack.com/database/api/beta/ -``` +## Use with Postman, Insomnia, Bruno or Hoppscotch -### Authentication - -Every request must include your API key in the **`PSKey`** HTTP request header. You can request an API key by reaching out on . - -``` -PSKey: -``` +We publish the API as an [OpenAPI 3.1 spec](https://docs.patchstack.com/schemas/threat-intel-beta.yaml) and a pre-built [Postman collection](https://docs.patchstack.com/schemas/threat-intel-beta.postman_collection.json). Every endpoint, parameter, request body and example is preconfigured — set your `PSKey` once and the whole collection authenticates. -### Response format - -All responses are JSON. Beta responses are cached until the database updates, at which point the cache is cleared. A single response shape is shared across all three list endpoints (`/all`, `/latest`, `/product/npm/...`) so clients can parse them interchangeably. - -### Platforms - -Pass `?platform=npm` (or `?platform=wordpress` — the default) on list endpoints. Platform names are case-insensitive. - -### Rate limiting - -Same policy as the Extended Threat Intelligence API — please contact if you need an elevated quota. +[Download the collection](/schemas/threat-intel-beta.postman_collection.json) and drag it into Postman/Insomnia/Bruno/Hoppscotch, or import it by URL from inside the tool. ---- - -## Endpoints - -| Method | Endpoint | Purpose | -|---|---|---| -| `GET` | `/database/api/beta/all` | Paginated listing of every published vulnerability for a platform | -| `GET` | `/database/api/beta/latest` | Vulnerabilities added in the last 24 hours | -| `GET` | `/database/api/beta/product/{type}/{name}/{version}` | Match a single product/version (npm & WordPress family) | -| `GET` | `/database/api/beta/product/{type}/{name}/{version}/exists` | Boolean-only variant of the above | -| `POST` | `/database/api/beta/batch` | Check up to 50 products in a single request | - ---- - -## List all vulnerabilities - -**Description:** Paginated listing of every published vulnerability for the given platform, ordered by descending `id`. -**Endpoint:** `/database/api/beta/all` -**Method:** `GET` - -### Query parameters +| Tool | How to import | +|---|---| +| **Postman** | `File → Import → Link` and paste the collection URL. | +| **Insomnia** | `Create → Import From → URL` → paste the OpenAPI URL. | +| **Bruno** | `Collection → Import → OpenAPI V3 Spec` → paste the OpenAPI URL. | +| **Hoppscotch** | `Collections → Import/Export → OpenAPI` → paste the OpenAPI URL. | -| Name | Type | Default | Description | -|---|---|---|---| -| `platform` | string | `wordpress` | `npm` or `wordpress`. Case-insensitive. | -| `page` | integer | `1` | Offset-pagination page (1-indexed). | -| `per_page` | integer | `100` | Page size, max `500`. | -| `cursor` | string | — | Opaque cursor (see below). Presence of the param switches to cursor mode. | -| `include` | string | — | Pass `details` to include the full advisory body in each item. | +**Authentication:** in Postman set the collection `Authorization` to **API Key**, key `PSKey`, value `{{PSKEY}}`, and add `PSKEY` as a collection variable with your real key as the **Current value** (leave Initial blank so it doesn't sync to teammates). Other tools work the same way — set `PSKey` as a collection header once. -### Pagination modes +## Use with Claude Code or other LLM coding assistants -`/all` supports **two independent pagination strategies**. Use whichever fits your client: +Point your assistant at the spec. LLMs parse OpenAPI cleanly and will generate clients that match the real field names instead of hallucinating. -- **Offset (`?page=&per_page=`)** — returns a `pagination` block with totals, `has_next_page`, `has_previous_page`, etc. Easy to jump to a specific page; slower at depth and susceptible to row-shift when new vulnerabilities land while you're paging. -- **Cursor (`?cursor=`)** — returns a `cursor` block with `next_cursor`, `has_more`, `per_page`. Stable under concurrent inserts and faster at any depth. No `total` count (deliberately skipped to keep cursor mode fast). +- **Ad hoc:** paste the spec URL into your prompt. Example: *"Write a Python client for `https://docs.patchstack.com/schemas/threat-intel-beta.yaml`. I need cursor-mode iteration over `/all` for npm."* +- **In your repo:** download the spec to `docs/vendor/patchstack-threat-intel-beta.yaml` and reference it from your `CLAUDE.md` / `AGENTS.md`. Your assistant can then grep the YAML for specific fields without refetching. +- **Plain-text fallback:** for tools that don't parse YAML, our [`llms-full.txt`](/llms-full.txt) contains the full reference as flat markdown. -`cursor` and `page` are mutually exclusive; passing both returns `422 Unprocessable Entity`. +## SDK generation -### Example — offset mode +Generate a client in any language from the same spec: ```bash -curl 'https://patchstack.com/database/api/beta/all?platform=npm&page=1&per_page=25' \ - -H 'PSKey: ' -``` - -Response: +# TypeScript +npx @openapitools/openapi-generator-cli generate \ + -i https://docs.patchstack.com/schemas/threat-intel-beta.yaml \ + -g typescript-fetch -o ./patchstack-client -```json -{ - "vulnerabilities": [ /* 25 items */ ], - "pagination": { - "current_page": 1, - "per_page": 25, - "total": 6115, - "total_pages": 245, - "has_next_page": true, - "has_previous_page": false, - "next_page": 2, - "previous_page": null, - "from": 1, - "to": 25 - } -} -``` - -### Example — cursor mode - -First page — send `cursor=` with an empty value to bootstrap: - -```bash -curl 'https://patchstack.com/database/api/beta/all?platform=npm&per_page=25&cursor=' \ - -H 'PSKey: ' +# Python +npx @openapitools/openapi-generator-cli generate \ + -i https://docs.patchstack.com/schemas/threat-intel-beta.yaml \ + -g python -o ./patchstack-client-py ``` -Response: +Speakeasy and Fern also consume the same spec and produce more idiomatic SDKs if you need a polished client library. -```json -{ - "vulnerabilities": [ /* 25 items */ ], - "cursor": { - "next_cursor": "djE6NDYzMzk", - "has_more": true, - "per_page": 25 - } -} -``` +> **Spec stability:** the Beta spec may change without a version bump while the API is in beta. Pin a commit of the YAML in production integrations, or wait for the GA release when we'll publish versioned URLs. -Follow `next_cursor` on the next request: +## Base URL -```bash -curl 'https://patchstack.com/database/api/beta/all?platform=npm&per_page=25&cursor=djE6NDYzMzk' \ - -H 'PSKey: ' ``` - -Stop when `has_more: false` and `next_cursor: null`. - -### Example — with full advisory body - -```bash -curl 'https://patchstack.com/database/api/beta/all?platform=npm&per_page=25&include=details' \ - -H 'PSKey: ' +https://vdp-api.patchstack.com/database/api/beta/ ``` -Adds an `advisory_details` field (markdown) to every item. - ---- - -## Latest (last 24 hours) +## Authentication -**Description:** Returns vulnerabilities whose row was inserted into the Patchstack database in the last 24 hours. The filter is on `created_at` (insertion time), **not** `disclosure_date`. -**Endpoint:** `/database/api/beta/latest` -**Method:** `GET` - -Accepts the **same query parameters** as `/all` — `platform`, `page`, `per_page`, `cursor`, `include`. +Every request must include your API key in the **`PSKey`** HTTP request header. You can request an API key by reaching out on . -```bash -curl 'https://patchstack.com/database/api/beta/latest?platform=npm&per_page=50' \ - -H 'PSKey: ' ``` - -Cursor pagination works identically: - -```bash -curl 'https://patchstack.com/database/api/beta/latest?platform=npm&per_page=50&cursor=' \ - -H 'PSKey: ' +PSKey: ``` ---- - -## Find vulnerability for a product +## Platforms -**Description:** Match a specific product + version against the vulnerability database and return every applicable advisory. -**Endpoint:** `/database/api/beta/product/{type}/{name}/{version}/{exists?}` -**Method:** `GET` - -| Path param | Description | -|---|---| -| `type` | `npm`, `plugin`, `theme`, or `wordpress`. | -| `name` | npm package slug or WordPress plugin/theme slug. Use `wordpress` when `type=wordpress`. | -| `version` | Concrete version (e.g. `0.21.4`) or `*` to return every advisory for the product. | -| `exists` | Optional. Pass the literal string `exists` to get a boolean response only. | - -### Query parameters - -| Name | Type | Description | -|---|---|---| -| `include` | string | Pass `details` to include the full advisory body (`advisory_details`) per item. Applies only to npm. | +Pass `?platform=npm` (or `?platform=wordpress` — the default) on list endpoints. Platform names are case-insensitive. -### Example — npm, concrete version +## Response format -```bash -curl 'https://patchstack.com/database/api/beta/product/npm/axios/0.21.4?include=details' \ - -H 'PSKey: ' -``` +All responses are JSON. Beta responses are cached until the database updates, at which point the cache is cleared. A single response shape is shared across all three list endpoints (`/all`, `/latest`, `/product/npm/...`) so clients can parse them interchangeably. -### Example — npm, wildcard version (all advisories for the package) +## Pagination -```bash -curl 'https://patchstack.com/database/api/beta/product/npm/axios/*' \ - -H 'PSKey: ' -``` +`/all` and `/latest` support **two independent pagination strategies**. Use whichever fits your client: -### Example — boolean exists check +- **Offset (`?page=&per_page=`)** — returns a `pagination` block with totals, `has_next_page`, `has_previous_page`, etc. Easy to jump to a specific page; slower at depth and susceptible to row-shift when new vulnerabilities land while you're paging. +- **Cursor (`?cursor=`)** — returns a `cursor` block with `next_cursor`, `has_more`, `per_page`. Stable under concurrent inserts and faster at any depth. No `total` count (deliberately skipped to keep cursor mode fast). -```bash -curl 'https://patchstack.com/database/api/beta/product/npm/axios/0.21.4/exists' \ - -H 'PSKey: ' -``` +`cursor` and `page` are mutually exclusive; passing both returns `422 Unprocessable Entity`. To bootstrap cursor mode, send `?cursor=` with an empty value. -Response: +## Including full advisory bodies -```json -{ "vulnerable": true } -``` +Pass `?include=details` on any list endpoint to add an `advisory_details` markdown field to each item. Applies to npm results. -### Note on scoped npm packages +## Scoped npm packages npm package slugs that include a `/` (e.g. `@scope/pkg`) conflict with the route separator. URL-encode the `/` as `%2F` or contact us for guidance on the encoding helper. ---- - -## Bulk product check - -**Description:** Check up to 50 products in a single request. Mirrors the format documented for the Extended tier's `/batch` endpoint. -**Endpoint:** `/database/api/beta/batch` -**Method:** `POST` -**Payload:** Raw JSON array, ≤ 50 objects. +## Rate limiting -```bash -curl -X POST 'https://patchstack.com/database/api/beta/batch' \ - -H 'PSKey: ' \ - -H 'Content-Type: application/json' \ - -d '[ - { "type": "npm", "name": "axios", "version": "0.21.4", "exists": false }, - { "type": "plugin", "name": "tutor", "version": "1.5.2", "exists": true }, - { "type": "wordpress","name":"wordpress","version": "6.0.0", "exists": true } - ]' -``` +Same policy as the Extended Threat Intelligence API — please contact if you need an elevated quota. ---- +## Errors -## Response shape (npm) +| Status | Meaning | +|---|---| +| `401 Unauthorized` | Missing or invalid `PSKey` header. | +| `403 Forbidden` | API key not authorised for the requested endpoint. | +| `422 Unprocessable Entity` | Invalid parameter combination (e.g. `cursor` + `page`), invalid `platform`, or `per_page > 500`. | +| `429 Too Many Requests` | Rate limit exceeded. | +| `500` | Server error — please include the request id in any bug report. | -The three list endpoints (`/all`, `/latest`, `/product/npm/...`) share the same per-item shape when `platform=npm`. `advisory_details` is only present when `?include=details` was passed. +When a cursor is malformed (invalid base64 or missing the `v1:` prefix), the endpoint returns `200` with an empty page: ```json { - "id": 46500, - "title": "NPM: OpenClaw: ...", - "disclosed_at": "2026-04-03T03:15:56+00:00", - "created_at": "2026-04-21T08:38:34+00:00", - "url": "https://patchstack.com/database/npm/npm/openclaw/vulnerability/...", - "vuln_type": "Other Vulnerability Type", - "cve": "2026-41331", - "is_exploited": false, - "patch_priority": 2, - "advisory_details": "## Summary\n...", - "product": { - "id": 23595, - "name": "openclaw", - "slug": "openclaw" - }, - "cvss": { - "score": 6.9, - "vector": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N" - }, - "cwe": { - "id": 770, - "name": "Allocation of Resources Without Limits or Throttling" - }, - "capec": { - "id": null, - "name": null - }, - "references": [ - "https://github.com/openclaw/openclaw/security/advisories/GHSA-m6fx-m8hc-572m", - "https://github.com/openclaw/openclaw/releases/tag/v2026.3.31" - ], - "ghsa": "GHSA-m6fx-m8hc-572m", - "version_info": { - "affected": "<= 2026.3.28", - "fixed": "2026.3.31", - "patched_ranges": [] - } + "vulnerabilities": [], + "cursor": { "next_cursor": null, "has_more": false, "per_page": 100 } } ``` -### Field reference - -| Field | Type | Description | -|---|---|---| -| `id` | integer | Stable Patchstack vulnerability id. | -| `title` | string | Human-readable title (prefixed with `NPM:` for npm advisories). | -| `disclosed_at` | ISO-8601 | When the vulnerability was publicly disclosed. | -| `created_at` | ISO-8601 | When the row was inserted into the Patchstack DB. Drives `/latest` windowing. | -| `url` | string | Public Patchstack vulnerability page (token-tagged). | -| `vuln_type` | string | High-level vulnerability category. | -| `cve` | string | First CVE identifier, or `""` when none is assigned. | -| `is_exploited` | boolean | Whether exploitation has been observed in the wild. | -| `patch_priority` | integer | 1 (low) to 3 (high). | -| `advisory_details` | string \| absent | Full advisory body. Only present with `?include=details`. | -| `product.{id,name,slug}` | object | The affected package/product. | -| `cvss.{score,vector}` | object | CVSS score and vector (may be `null`). | -| `cwe.{id,name}` | object | CWE classification (may be `null`). | -| `capec.{id,name}` | object | CAPEC classification (may be `null`). | -| `references` | string[] | External reference URLs (advisories, commits, tags). | -| `ghsa` | string | GHSA identifier when the advisory came from the GitHub Advisory Database. | -| `version_info.affected` | string | Affected version range (e.g. `<= 2026.3.28`). | -| `version_info.fixed` | string | First fixed version. | -| `version_info.patched_ranges` | array | Structured list of `{from_version, to_version, fixed_in}` entries for advisories with multiple patch ranges. | - --- ## Testing @@ -338,16 +146,9 @@ curl 'https://patchstack.com/database/api/beta/product/npm/axios/0.21.4/exists' -H 'PSKey: ' ``` -### Postman +### Postman / Insomnia / Bruno / Hoppscotch -1. Create a new request, set the method, paste the URL. -2. Under **Headers**, add: - - Key: `PSKey` - - Value: your API key -3. Under **Headers**, add (recommended): - - Key: `Accept` - - Value: `application/json` -4. For `POST /batch`, set the body to **raw → JSON** and paste the array payload. +Import the OpenAPI spec directly from [`threat-intel-beta.yaml`](https://docs.patchstack.com/schemas/threat-intel-beta.yaml) — authentication, parameters and example payloads are preconfigured. Set the `PSKey` security value to your API key once and every request in the collection will use it. ### Cursor iteration (JavaScript / Node) @@ -405,27 +206,6 @@ do { --- -## Errors - -| Status | Meaning | -|---|---| -| `401 Unauthorized` | Missing or invalid `PSKey` header. | -| `403 Forbidden` | API key not authorised for the requested endpoint. | -| `422 Unprocessable Entity` | Invalid parameter combination (e.g. `cursor` + `page`), invalid `platform`, or `per_page > 500`. | -| `429 Too Many Requests` | Rate limit exceeded. | -| `500` | Server error — please include the request id in any bug report. | - -When a cursor is malformed (invalid base64 or missing the `v1:` prefix), the endpoint returns `200` with an empty page: - -```json -{ - "vulnerabilities": [], - "cursor": { "next_cursor": null, "has_more": false, "per_page": 100 } -} -``` - ---- - ## Migration notes (v2 → beta) - Beta npm responses use **nested objects** (`product`, `cvss`, `cwe`, `capec`, `version_info`) whereas the v2 shape is flat. Update parsers accordingly. diff --git a/src/content/docs/API solutions/Threat Intelligence API/overview.md b/src/content/docs/API solutions/Threat Intelligence API/overview.md index f3ffd06..e80fa54 100644 --- a/src/content/docs/API solutions/Threat Intelligence API/overview.md +++ b/src/content/docs/API solutions/Threat Intelligence API/overview.md @@ -12,18 +12,39 @@ sidebar: hidden: false --- +Patchstack publishes three ways to consume its vulnerability database. Standard and Extended are the stable tiers of the v2 API; the Beta API is a new generation available to selected partners only. + +## Which API should I use? + +| | **Standard** | **Extended** | **Beta** | +|---|---|---|---| +| **Status** | Stable (v2) | Stable (v2) | Beta — selected partners only | +| **Access** | Purchase via [Patchstack App](https://app.patchstack.com/billing/subscription) | Custom pricing, activated on request — [contact us](https://patchstack.com/for-hosts/) | By invitation. If you're working with Patchstack on an integration, [get in touch](https://patchstack.com/for-hosts/) | +| **Rate limit** | 5,000 calls / 24 hours | Custom | Custom | +| **Ecosystems** | WordPress plugins, themes, core | WordPress plugins, themes, core | WordPress + **npm** | +| **Lookups** | Single plugin/theme/version | Single + bulk | Single + bulk | +| **Pagination** | Offset | Offset | Offset **and** cursor | +| **Response shape** | Flat | Flat | Nested (`product`, `cvss`, `cwe`, `version_info`…) | +| **Full advisory body** | No | Yes | Opt-in via `?include=details` | +| **OpenAPI spec / Postman collection** | — | — | [Yes](/api-solutions/threat-intelligence-api/beta/#use-with-postman-insomnia-bruno-or-hoppscotch) | + +**New integration?** If you have Beta access, use Beta — it's the direction we're heading, and Standard/Extended will eventually adopt the same shape. If not, start with Standard or Extended depending on your data needs. + +**Existing integration?** Standard and Extended remain supported; no migration is required. + +--- + ### Standard Threat Intelligence API -Fetch the latest vulnerability information for a single version of a particular plugin, theme or WordPress core. API is limited for 5000 calls / 24 hours. Access to this API can be purchased through the [Patchstack App](https://app.patchsatck.com/billing/subscription). +Fetch the latest vulnerability information for a single version of a particular plugin, theme or WordPress core. API is limited for 5000 calls / 24 hours. Access to this API can be purchased through the [Patchstack App](https://app.patchstack.com/billing/subscription). [Standard Threat Intelligence API Documentation](/api-solutions/threat-intelligence-api/standard/) ### Extended Threat Intelligence API Access everything included in the Standard tier, bulk-request data for multiple plugins with one API call, and have additional endpoints with more information about vulnerabilities. Extended tier has custom pricing and is activated on request only. For access to these API endpoints, please [contact us here](https://patchstack.com/for-hosts/). - [Extended Threat Intelligence API Documentation](/api-solutions/threat-intelligence-api/extended/) ### Beta Threat Intelligence API -Adds npm ecosystem coverage, an opt-in full advisory body (`?include=details`), a consistent nested response shape, and cursor-based pagination alongside the existing offset pagination. These are the recommended endpoints for new integrations. +A new generation of the Threat Intelligence API, currently available to **selected partners working directly with Patchstack**. Adds npm ecosystem coverage, an opt-in full advisory body (`?include=details`), a consistent nested response shape, and cursor-based pagination alongside the existing offset pagination. If you're already working with us on an integration and would like access, [contact us](https://patchstack.com/for-hosts/). [Beta Threat Intelligence API Documentation](/api-solutions/threat-intelligence-api/beta/) diff --git a/src/styles/custom.css b/src/styles/custom.css index 2d59027..3d4c0c5 100644 --- a/src/styles/custom.css +++ b/src/styles/custom.css @@ -135,4 +135,12 @@ header button { .main-frame { margin-bottom: 30px; +} + +/* Match sidebar group labels that carry a badge (e.g. Beta API [New]) to the + weight/size of sibling leaf links, so the badge carries the emphasis + instead of the label doubling up. */ +.group-label .large:has(+ .sl-badge) { + font-size: var(--sl-text-sm); + font-weight: normal; } \ No newline at end of file