CWE-180
AllowedIncorrect Behavior Order: Validate Before Canonicalize
Abstraction: Variant · Status: Draft
The product validates input before it is canonicalized, which prevents the product from detecting data that becomes invalid after the canonicalization step.
70 vulnerabilities reference this CWE, most recent first.
GHSA-8PVW-JCV7-9CMJ
Vulnerability from github – Published: 2026-07-24 16:43 – Updated: 2026-07-24 16:43Impact
@fastify/static evaluates the allowedPath callback before normalizing dot segments and duplicate slashes in the pathname used for file resolution. Non-canonical pathnames such as //file, /./file, or /public/../private/file bypass allowedPath filtering while resolving to the intended file on disk.
Applications that use allowedPath as a security boundary to restrict access to specific static files or path subtrees may unintentionally expose files that were intended to be denied.
Patches
Upgrade to @fastify/static >= 10.1.2.
Workarounds
None. Upgrade to the patched version.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 10.1.1"
},
"package": {
"ecosystem": "npm",
"name": "@fastify/static"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "10.1.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-7120"
],
"database_specific": {
"cwe_ids": [
"CWE-180"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-24T16:43:44Z",
"nvd_published_at": "2026-07-23T04:16:33Z",
"severity": "MODERATE"
},
"details": "### Impact\n\n`@fastify/static` evaluates the `allowedPath` callback before normalizing dot segments and duplicate slashes in the pathname used for file resolution. Non-canonical pathnames such as `//file`, `/./file`, or `/public/../private/file` bypass `allowedPath` filtering while resolving to the intended file on disk.\n\nApplications that use `allowedPath` as a security boundary to restrict access to specific static files or path subtrees may unintentionally expose files that were intended to be denied.\n\n### Patches\n\nUpgrade to `@fastify/static` \u003e= 10.1.2.\n\n### Workarounds\n\nNone. Upgrade to the patched version.",
"id": "GHSA-8pvw-jcv7-9cmj",
"modified": "2026-07-24T16:43:44Z",
"published": "2026-07-24T16:43:44Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/fastify/fastify-static/security/advisories/GHSA-8pvw-jcv7-9cmj"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-7120"
},
{
"type": "WEB",
"url": "https://github.com/fastify/fastify-static/commit/878c72e920fabacf7e37739bf78057044717bf63"
},
{
"type": "WEB",
"url": "https://cna.openjsf.org/security-advisories.html"
},
{
"type": "PACKAGE",
"url": "https://github.com/fastify/fastify-static"
},
{
"type": "WEB",
"url": "https://github.com/fastify/fastify-static/releases/tag/v10.1.2"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "@fastify/static vulnerable to Authorization Bypass via Non-Canonical URL Paths"
}
GHSA-8R66-VG6C-X88H
Vulnerability from github – Published: 2025-11-25 18:32 – Updated: 2025-11-25 18:32NVIDIA DGX Spark GB10 contains a vulnerability in SROOT firmware, where an attacker could cause improper processing of input data. A successful exploit of this vulnerability might lead to information disclosure or denial of service.
{
"affected": [],
"aliases": [
"CVE-2025-33194"
],
"database_specific": {
"cwe_ids": [
"CWE-180"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-11-25T18:15:51Z",
"severity": "MODERATE"
},
"details": "NVIDIA DGX Spark GB10 contains a vulnerability in SROOT firmware, where an attacker could cause improper processing of input data. A successful exploit of this vulnerability might lead to information disclosure or denial of service.",
"id": "GHSA-8r66-vg6c-x88h",
"modified": "2025-11-25T18:32:22Z",
"published": "2025-11-25T18:32:22Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-33194"
},
{
"type": "WEB",
"url": "https://nvidia.custhelp.com/app/answers/detail/a_id/5720"
},
{
"type": "WEB",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-33194"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:L",
"type": "CVSS_V3"
}
]
}
GHSA-92H7-Q9M9-98H8
Vulnerability from github – Published: 2025-04-23 15:30 – Updated: 2025-04-23 15:30A directory traversal vulnerability exists in Ivanti LANDesk Management Gateway through 4.2-1.9. By appending %3F.php to the URI of the /client/index.php endpoint, an attacker can bypass access controls and gain unauthorized access to various endpoints such as /client/index.php%3F.php/gsb/firewall.php within the management web panel, potentially exposing sensitive device information. NOTE: This vulnerability only affects products that are no longer supported by the maintainer.
{
"affected": [],
"aliases": [
"CVE-2025-43716"
],
"database_specific": {
"cwe_ids": [
"CWE-180"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-04-23T14:15:29Z",
"severity": "MODERATE"
},
"details": "A directory traversal vulnerability exists in Ivanti LANDesk Management Gateway through 4.2-1.9. By appending %3F.php to the URI of the /client/index.php endpoint, an attacker can bypass access controls and gain unauthorized access to various endpoints such as /client/index.php%3F.php/gsb/firewall.php within the management web panel, potentially exposing sensitive device information. NOTE: This vulnerability only affects products that are no longer supported by the maintainer.",
"id": "GHSA-92h7-q9m9-98h8",
"modified": "2025-04-23T15:30:57Z",
"published": "2025-04-23T15:30:57Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-43716"
},
{
"type": "WEB",
"url": "https://forums.ivanti.com/s/article/Graphical-overview-of-the-LANDesk-Management-Gateway-Functionality"
},
{
"type": "WEB",
"url": "https://medium.com/@0xbytehunter/discovery-of-path-traversal-vulnerability-in-landesk-management-gateway-devices-6dba386dd290"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-94VH-GPHV-8PM8
Vulnerability from github – Published: 2025-03-17 21:26 – Updated: 2025-03-19 15:51Summary
In the archive extraction routine of affected versions of the zip crate, symbolic links earlier in the archive are allowed to be used for later files in the archive without validation of the final canonicalized path, allowing maliciously crafted archives to overwrite arbitrary files in the file system when extracted.
Details
This is a variant of the zip-slip vulnerability, we can make the extraction logic step outside of the target directory by creating a symlink to the parent directory and then extracting further files through that symlink.
The documentation of the [::zip::read::ZipArchive::extract] method is in my opinion implying this should not happen:
"Paths are sanitized with ZipFile::enclosed_name." ... [
::zip::read::FileOptions::enclosed_name] ... is resistant to path-based exploits ... can’t resolve to a path outside the current directory.
Most archive software either decline to extract symlinks that traverse out of the directory or defer creation of symlinks after all files have been created to prevent unexpected behavior when later entries depend on earlier symbolic link entries.
PoC
https://gist.github.com/eternal-flame-AD/bf71ef4f6828e741eb12ce7fd47b7b85
Impact
Users who extract untrusted archive files using the following high-level API method may be affected and critical files on the system may be overwritten with arbitrary file permissions, which can potentially lead to code execution.
- zip::unstable::stream::ZipStreamReader::extract
- zip::read::ZipArchive::extract
{
"affected": [
{
"package": {
"ecosystem": "crates.io",
"name": "zip"
},
"ranges": [
{
"events": [
{
"introduced": "1.3.0"
},
{
"fixed": "2.3.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-29787"
],
"database_specific": {
"cwe_ids": [
"CWE-180",
"CWE-22",
"CWE-61"
],
"github_reviewed": true,
"github_reviewed_at": "2025-03-17T21:26:32Z",
"nvd_published_at": "2025-03-17T14:15:22Z",
"severity": "HIGH"
},
"details": "### Summary\n\n\nIn the archive extraction routine of affected versions of the `zip` crate, symbolic links earlier in the archive are allowed to be used for later files in the archive without validation of the final canonicalized path, allowing maliciously crafted archives to overwrite arbitrary files in the file system when extracted.\n\n### Details\n\nThis is a variant of the [zip-slip](https://github.com/snyk/zip-slip-vulnerability) vulnerability, we can make the extraction logic step outside of the target directory by creating a symlink to the parent directory and then extracting further files through that symlink.\n\nThe documentation of the [`::zip::read::ZipArchive::extract`] method is in my opinion implying this should not happen:\n\n\u003e \"Paths are sanitized with ZipFile::enclosed_name.\" ...\n\u003e [`::zip::read::FileOptions::enclosed_name`] ... is resistant to path-based exploits ... can\u2019t resolve to a path outside the current directory.\n\n\nMost archive software either decline to extract symlinks that traverse out of the directory or defer creation of symlinks after all files have been created to prevent unexpected behavior when later entries depend on earlier symbolic link entries.\n\n### PoC\n\nhttps://gist.github.com/eternal-flame-AD/bf71ef4f6828e741eb12ce7fd47b7b85\n\n### Impact\n\nUsers who extract untrusted archive files using the following high-level API method may be affected and critical files on the system may be overwritten with arbitrary file permissions, which can potentially lead to code execution.\n\n- zip::unstable::stream::ZipStreamReader::extract\n- zip::read::ZipArchive::extract",
"id": "GHSA-94vh-gphv-8pm8",
"modified": "2025-03-19T15:51:04Z",
"published": "2025-03-17T21:26:32Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/zip-rs/zip2/security/advisories/GHSA-94vh-gphv-8pm8"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-29787"
},
{
"type": "WEB",
"url": "https://github.com/zip-rs/zip2/commit/a2e062f37066c3b12860a32eb1cb44856cfb7afe"
},
{
"type": "WEB",
"url": "https://gist.github.com/eternal-flame-AD/bf71ef4f6828e741eb12ce7fd47b7b85"
},
{
"type": "PACKAGE",
"url": "https://github.com/zip-rs/zip2"
},
{
"type": "WEB",
"url": "https://github.com/zip-rs/zip2/releases/tag/v2.3.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:H/AT:N/PR:L/UI:N/VC:L/VI:H/VA:N/SC:H/SI:H/SA:H",
"type": "CVSS_V4"
}
],
"summary": "zip Incorrectly Canonicalizes Paths during Archive Extraction Leading to Arbitrary File Write"
}
GHSA-9RFG-V8G9-9367
Vulnerability from github – Published: 2026-05-26 23:38 – Updated: 2026-06-11 13:30As told on Discord earlier, multiple projects are affected, and we would like to coordinate. For now, we are aiming at a May 6th release date, but this is not set in stone yet.
Summary
An attacker can make use of JSON-LD features to restructure a JSON-LD document that would change how Fedify interprets it without changing its Linked Data Signature, allowing them to alter a third-party signed activity they have received.
Details
The vulnerability essentially boils down to the signature being on the canonical RDF graph representation of the JSON-LD document, and JSON-LD offering many ways to represent the same graph.
One of the issues is that by taking a signed Activity with an embedded object, an attacker can move the top-level Activity to a @graph property and move the activity's object to the top-level. Such a transformation preserves the signature and changes how the payload is interpreted by pretty much all ActivityPub implementations, making them process the object and ignore the formely-top-level activity. This can be used when the graph contains an embedded activity. In Mastodon, that is the case of { "type": "Undo", "object": { "type": "Announce" } }, but other implementations may sign other activities that can be exploited in the same way.
The @reverse keyword can also be used to change the shape of a JSON-LD document without changing the underlying graph, and could be used in a similar way to reverse an Activity and its object.
Another problematic feature is @included, which can be used to “move” properties outside of the normal tree, effectively making them invisible to most ActivityPub implementations, while, again, preserving the signature. This allows removing statuses or actor properties once a signed Create or Update activity is received.
Given that we have seen no use of @graph, @included or @reverse in ActivityPub payloads and that they are very complex to handle correctly (the only JSON-LD API functions that “normalize” @included and @reverse are flattening and framing, which both lose the root node), we have decided to reject them, and recommend you do so as well.
Detection of @graph, @included and @reverse should happen after compacting the incoming activity to your context, as aliases can be used for those keywords.
Additionally, after a quick scan of Fedify's source code, I could not verify that JSON-LD documents with a verified Linked Data Signature were compacted against your local JSON-LD context. Not doing that allows an attacker to rename aliases to non-standard names and use non-mapped aliases to replace existing values, while still leaving the signature intact. This allows an attacker to essentially replace arbitrary portions of any signed JSON-LD document and completely forge any activity while still passing verification. A similar issue was fixed in Mastodon a few years ago: https://github.com/mastodon/mastodon/pull/17426.
Impact
The impact is difficult to assess as this depends on the types of activities that are actually signed and processed in the wild.
The @included keyword allows “removing” arbitrary attributes, thus allowing replaying Create and Update activities while stripping away any attribute, such as content or metadata, which can lead to integrity and availability issues, although confidentiality issues are unlikely.
The @graph and @reverse keywords allow changing the root activity, which in the case of Mastodon allows sending an Announce from a Undo { Announce }, but might have wider consequences depending on what various servers sign.
The lack of compacting can allow rewriting any activity arbitrarily, thus leading to major integrity, availability, and possibly confidentiality issues (e.g. by replacing an actor's inbox).
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@fedify/fedify"
},
"ranges": [
{
"events": [
{
"introduced": "2.2.0"
},
{
"fixed": "2.2.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@fedify/fedify"
},
"ranges": [
{
"events": [
{
"introduced": "2.1.0"
},
{
"fixed": "2.1.14"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@fedify/fedify"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0"
},
{
"fixed": "2.0.18"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@fedify/fedify"
},
"ranges": [
{
"events": [
{
"introduced": "1.10.0"
},
{
"fixed": "1.10.10"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@fedify/fedify"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.9.11"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-42462"
],
"database_specific": {
"cwe_ids": [
"CWE-1289",
"CWE-180",
"CWE-347",
"CWE-436"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-26T23:38:37Z",
"nvd_published_at": "2026-06-10T22:16:57Z",
"severity": "HIGH"
},
"details": "As told on Discord earlier, multiple projects are affected, and we would like to coordinate. For now, we are aiming at a May 6th release date, but this is not set in stone yet.\n\n### Summary\n\nAn attacker can make use of JSON-LD features to restructure a JSON-LD document that would change how Fedify interprets it without changing its Linked Data Signature, allowing them to alter a third-party signed activity they have received.\n\n### Details\n\nThe vulnerability essentially boils down to the signature being on the canonical RDF graph representation of the JSON-LD document, and JSON-LD offering many ways to represent the same graph.\n\nOne of the issues is that by taking a signed `Activity` with an embedded `object`, an attacker can move the top-level `Activity` to a `@graph` property and move the activity\u0027s `object` to the top-level. Such a transformation preserves the signature and changes how the payload is interpreted by pretty much all ActivityPub implementations, making them process the object and ignore the formely-top-level activity. This can be used when the graph contains an embedded activity. In Mastodon, that is the case of `{ \"type\": \"Undo\", \"object\": { \"type\": \"Announce\" } }`, but other implementations may sign other activities that can be exploited in the same way.\n\nThe `@reverse` keyword can also be used to change the shape of a JSON-LD document without changing the underlying graph, and could be used in a similar way to reverse an `Activity` and its `object`.\n\nAnother problematic feature is `@included`, which can be used to \u201cmove\u201d properties outside of the normal tree, effectively making them invisible to most ActivityPub implementations, while, again, preserving the signature. This allows removing statuses or actor properties once a signed `Create` or `Update` activity is received.\n\nGiven that we have seen no use of `@graph`, `@included` or `@reverse` in ActivityPub payloads and that they are very complex to handle correctly (the only JSON-LD API functions that \u201cnormalize\u201d `@included` and `@reverse` are flattening and framing, which both lose the root node), we have decided to reject them, and recommend you do so as well.\n\nDetection of `@graph`, `@included` and `@reverse` should happen after compacting the incoming activity to your context, as aliases can be used for those keywords.\n\nAdditionally, after a quick scan of Fedify\u0027s source code, I could not verify that JSON-LD documents with a verified Linked Data Signature were compacted against your local JSON-LD context. Not doing that allows an attacker to rename aliases to non-standard names and use non-mapped aliases to replace existing values, while still leaving the signature intact. This allows an attacker to essentially replace arbitrary portions of any signed JSON-LD document and completely forge any activity while still passing verification. A similar issue was fixed in Mastodon a few years ago: https://github.com/mastodon/mastodon/pull/17426.\n\n### Impact\n\nThe impact is difficult to assess as this depends on the types of activities that are actually signed and processed in the wild.\n\nThe `@included` keyword allows \u201cremoving\u201d arbitrary attributes, thus allowing replaying `Create` and `Update` activities while stripping away any attribute, such as content or metadata, which can lead to integrity and availability issues, although confidentiality issues are unlikely.\n\nThe `@graph` and `@reverse` keywords allow changing the root activity, which in the case of Mastodon allows sending an `Announce` from a `Undo { Announce }`, but might have wider consequences depending on what various servers sign.\n\nThe lack of compacting can allow rewriting any activity arbitrarily, thus leading to major integrity, availability, and possibly confidentiality issues (e.g. by replacing an actor\u0027s `inbox`).",
"id": "GHSA-9rfg-v8g9-9367",
"modified": "2026-06-11T13:30:30Z",
"published": "2026-05-26T23:38:37Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/fedify-dev/fedify/security/advisories/GHSA-9rfg-v8g9-9367"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42462"
},
{
"type": "PACKAGE",
"url": "https://github.com/fedify-dev/fedify"
},
{
"type": "WEB",
"url": "https://github.com/fedify-dev/fedify/releases/tag/2.2.3"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:L",
"type": "CVSS_V3"
}
],
"summary": "Fedify has an LD-Signature Bypass via JSON-LD Named-Graph Restructuring"
}
GHSA-CHQ7-94J8-CJ28
Vulnerability from github – Published: 2026-06-03 21:30 – Updated: 2026-06-03 21:30Summary
Jupyter Enterprise Gateway has a prohibited UID and GID feature that by default prevents launching kernels with UID or GID 0 (root).
This can be bypassed. It is possible to launch kernels with a prohibited UID and/or GID by using a specially crafted KERNEL_UID or KERNEL_GID value.
The feature is described in the documentation:
https://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/docs/source/operators/config-add-env.md?plain=1#L103-L107
https://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/docs/source/operators/config-add-env.md?plain=1#L88-L92
https://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/docs/source/operators/deploy-kubernetes.md?plain=1#L769
Details
The prohibited_uids and prohibited_uids are set based of the OS env var EG_PROHIBITED_UIDS and EG_PROHIBITED_GIDS, and default to the string 0.
https://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/enterprise_gateway/services/processproxies/container.py#L29-L30
The checks https://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/enterprise_gateway/services/processproxies/container.py#L113 and https://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/enterprise_gateway/services/processproxies/container.py#L119 look for the user supplied KERNEL_UID / KERNEL_GID string in the prohibited_uids / prohibited_gids strings. These checks can be bypassed by including whitespace, for example the string 0 (trailing space).
The user supplied string is used in the Kubernetes manifest at https://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/etc/kernel-launchers/kubernetes/scripts/kernel-pod.yaml.j2#L35 and https://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/etc/kernel-launchers/kubernetes/scripts/kernel-pod.yaml.j2#L38 where they are parsed as an integer in the Jinja2 template - which will ignore the whitespace.
PoC
How it is meant to work
Trying 0 gets denied, as expected.
xh http://enterprise-gateway.bdawg.svc.cluster.local:8888/api/kernels name=python_kubernetes env:='{"KERNEL_POD_NAME":"bdawg", "KERNEL_UID": "0", "KERNEL_GID": "0"}'
HTTP/1.1 403 Kernel's UID value of '0' has been denied via EG_PROHIBITED_UIDS!
Content-Length: 94
Content-Type: application/json
Date: Mon, 14 Jul 2025 12:57:09 GMT
Server: TornadoServer/6.4.1
X-Content-Type-Options: nosniff
{
"reason": "Kernel's UID value of '0' has been denied via EG_PROHIBITED_UIDS!",
"message": ""
}
Exploit bypassing the checks
Using 0 with a trailing space, bypasses the check.
xh http://enterprise-gateway.bdawg.svc.cluster.local:8888/api/kernels name=python_kubernetes env:='{"KERNEL_POD_NAME":"bdawg", "KERNEL_UID": "0 ", "KERNEL_GID": "0 "}'
HTTP/1.1 201 Created
Content-Length: 172
Content-Type: application/json
Date: Mon, 14 Jul 2025 14:15:19 GMT
Location: /api/kernels/17eee032-994f-4dd2-8ade-87169c300a40
Server: TornadoServer/6.4.1
X-Content-Type-Options: nosniff
{
"id": "17eee032-994f-4dd2-8ade-87169c300a40",
"name": "python_kubernetes",
"last_activity": "2025-07-14T14:15:21.468155Z",
"execution_state": "starting",
"connections": 0
}
The pod is successfully scheduled.
Inspecting the container we can see it is running as root:
kubectl exec -it pod/bdawg -- bash
(base) root@bdawg3:~# id
uid=0(root) gid=0(root) groups=0(root),100(users)
If we had not supplied the KERNEL_UID / KERNEL_GID the container would have been running as UID:GID 1000:100 (jovyan:users).
Impact
This input validation vulnerability allows running Jupyter kernels as root, which can be dangerous as it allows more attack surface, and may lead to container escapes, compromising the worker node and all workloads running on it. Repeated exploitation can compromise all worker nodes, and thus the entire Kubernetes cluster. It is possible to specify volume mounts, so one vector for a container escape is to use a hostPath R/W volume mount, use this UID/GID bypass to run as root, and then gain code execution in the underlying worker node by creating a crontab entry in the mounted host file system.
Organisations running Jupyter Enterprise Gateway to host Jupyter Kernels on at least Kubernetes clusters (I've tested this), and possibly on any other supported container orchestration systems or systems that utilise the KERNEL_UID and KERNEL_GID variables with the EG_PROHIBITED_UIDS and EG_PROHIBITED_GIDS feature.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "jupyter_enterprise_gateway"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0rc1"
},
{
"fixed": "3.3.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-44180"
],
"database_specific": {
"cwe_ids": [
"CWE-180",
"CWE-20"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-03T21:30:10Z",
"nvd_published_at": null,
"severity": "CRITICAL"
},
"details": "### Summary\n\nJupyter Enterprise Gateway has a prohibited UID and GID feature that by default prevents launching kernels with UID or GID 0 (root).\nThis can be bypassed. It is possible to launch kernels with a prohibited UID and/or GID by using a specially crafted `KERNEL_UID` or `KERNEL_GID` value.\n\nThe feature is described in the documentation: \n\nhttps://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/docs/source/operators/config-add-env.md?plain=1#L103-L107\n\nhttps://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/docs/source/operators/config-add-env.md?plain=1#L88-L92\n\nhttps://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/docs/source/operators/deploy-kubernetes.md?plain=1#L769\n\n### Details\n\nThe `prohibited_uids` and `prohibited_uids` are set based of the OS env var `EG_PROHIBITED_UIDS` and `EG_PROHIBITED_GIDS`, and default to the string `0`.\n\nhttps://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/enterprise_gateway/services/processproxies/container.py#L29-L30\n\nThe checks https://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/enterprise_gateway/services/processproxies/container.py#L113 and https://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/enterprise_gateway/services/processproxies/container.py#L119 look for the user supplied `KERNEL_UID` / `KERNEL_GID` string in the `prohibited_uids` / `prohibited_gids` strings. These checks can be bypassed by including whitespace, for example the string `0 ` (trailing space).\n\nThe user supplied string is used in the Kubernetes manifest at https://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/etc/kernel-launchers/kubernetes/scripts/kernel-pod.yaml.j2#L35 and https://github.com/jupyter-server/enterprise_gateway/blob/152c20f162f2fab700c04c8830ebf8c1e2e2217a/etc/kernel-launchers/kubernetes/scripts/kernel-pod.yaml.j2#L38 where they are parsed as an integer in the Jinja2 template - which will ignore the whitespace.\n\n### PoC\n\n\n#### How it is meant to work\n\nTrying `0` gets denied, as expected.\n\n```bash\nxh http://enterprise-gateway.bdawg.svc.cluster.local:8888/api/kernels name=python_kubernetes env:=\u0027{\"KERNEL_POD_NAME\":\"bdawg\", \"KERNEL_UID\": \"0\", \"KERNEL_GID\": \"0\"}\u0027\n```\n\n```\nHTTP/1.1 403 Kernel\u0027s UID value of \u00270\u0027 has been denied via EG_PROHIBITED_UIDS!\nContent-Length: 94\nContent-Type: application/json\nDate: Mon, 14 Jul 2025 12:57:09 GMT\nServer: TornadoServer/6.4.1\nX-Content-Type-Options: nosniff\n```\n\n```json\n{\n \"reason\": \"Kernel\u0027s UID value of \u00270\u0027 has been denied via EG_PROHIBITED_UIDS!\",\n \"message\": \"\"\n}\n```\n\n#### Exploit bypassing the checks\n\nUsing `0 ` with a trailing space, bypasses the check.\n\n```bash\nxh http://enterprise-gateway.bdawg.svc.cluster.local:8888/api/kernels name=python_kubernetes env:=\u0027{\"KERNEL_POD_NAME\":\"bdawg\", \"KERNEL_UID\": \"0 \", \"KERNEL_GID\": \"0 \"}\u0027\n```\n\n```\nHTTP/1.1 201 Created\nContent-Length: 172\nContent-Type: application/json\nDate: Mon, 14 Jul 2025 14:15:19 GMT\nLocation: /api/kernels/17eee032-994f-4dd2-8ade-87169c300a40\nServer: TornadoServer/6.4.1\nX-Content-Type-Options: nosniff\n```\n\n```\n{\n \"id\": \"17eee032-994f-4dd2-8ade-87169c300a40\",\n \"name\": \"python_kubernetes\",\n \"last_activity\": \"2025-07-14T14:15:21.468155Z\",\n \"execution_state\": \"starting\",\n \"connections\": 0\n}\n```\n\nThe pod is successfully scheduled.\n\nInspecting the container we can see it is running as `root`:\n\n```bash\nkubectl exec -it pod/bdawg -- bash\n```\n\n```\n(base) root@bdawg3:~# id\nuid=0(root) gid=0(root) groups=0(root),100(users)\n```\n\nIf we had not supplied the `KERNEL_UID` / `KERNEL_GID` the container would have been running as UID:GID `1000:100` (`jovyan:users`).\n\n### Impact\n\nThis input validation vulnerability allows running Jupyter kernels as root, which can be dangerous as it allows more attack surface, and may lead to container escapes, compromising the worker node and all workloads running on it. Repeated exploitation can compromise all worker nodes, and thus the entire Kubernetes cluster. It is possible to specify volume mounts, so one vector for a container escape is to use a `hostPath` R/W volume mount, use this UID/GID bypass to run as root, and then gain code execution in the underlying worker node by creating a crontab entry in the mounted host file system.\n\nOrganisations running Jupyter Enterprise Gateway to host Jupyter Kernels on at least Kubernetes clusters (I\u0027ve tested this), and possibly on any other supported container orchestration systems or systems that utilise the `KERNEL_UID` and `KERNEL_GID` variables with the `EG_PROHIBITED_UIDS` and `EG_PROHIBITED_GIDS` feature.",
"id": "GHSA-chq7-94j8-cj28",
"modified": "2026-06-03T21:30:10Z",
"published": "2026-06-03T21:30:10Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jupyter-server/enterprise_gateway/security/advisories/GHSA-chq7-94j8-cj28"
},
{
"type": "PACKAGE",
"url": "https://github.com/jupyter-server/enterprise_gateway"
},
{
"type": "WEB",
"url": "https://github.com/jupyter-server/enterprise_gateway/releases/tag/v3.3.0"
}
],
"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": "Jupyter Enterprise Gateway: ContainerProcessProxy._enforce_prohibited_ids Bypass"
}
GHSA-F3MV-W3V5-88QP
Vulnerability from github – Published: 2022-07-21 00:00 – Updated: 2022-08-05 00:00A vulnerability in multiple Atlassian products allows a remote, unauthenticated attacker to bypass Servlet Filters used by first and third party apps. The impact depends on which filters are used by each app, and how the filters are used. This vulnerability can result in authentication bypass and cross-site scripting. Atlassian has released updates that fix the root cause of this vulnerability, but has not exhaustively enumerated all potential consequences of this vulnerability. Atlassian Bamboo versions are affected before 8.0.9, from 8.1.0 before 8.1.8, and from 8.2.0 before 8.2.4. Atlassian Bitbucket versions are affected before 7.6.16, from 7.7.0 before 7.17.8, from 7.18.0 before 7.19.5, from 7.20.0 before 7.20.2, from 7.21.0 before 7.21.2, and versions 8.0.0 and 8.1.0. Atlassian Confluence versions are affected before 7.4.17, from 7.5.0 before 7.13.7, from 7.14.0 before 7.14.3, from 7.15.0 before 7.15.2, from 7.16.0 before 7.16.4, from 7.17.0 before 7.17.4, and version 7.21.0. Atlassian Crowd versions are affected before 4.3.8, from 4.4.0 before 4.4.2, and version 5.0.0. Atlassian Fisheye and Crucible versions before 4.8.10 are affected. Atlassian Jira versions are affected before 8.13.22, from 8.14.0 before 8.20.10, and from 8.21.0 before 8.22.4. Atlassian Jira Service Management versions are affected before 4.13.22, from 4.14.0 before 4.20.10, and from 4.21.0 before 4.22.4.
{
"affected": [],
"aliases": [
"CVE-2022-26136"
],
"database_specific": {
"cwe_ids": [
"CWE-180",
"CWE-287"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2022-07-20T18:15:00Z",
"severity": "CRITICAL"
},
"details": "A vulnerability in multiple Atlassian products allows a remote, unauthenticated attacker to bypass Servlet Filters used by first and third party apps. The impact depends on which filters are used by each app, and how the filters are used. This vulnerability can result in authentication bypass and cross-site scripting. Atlassian has released updates that fix the root cause of this vulnerability, but has not exhaustively enumerated all potential consequences of this vulnerability. Atlassian Bamboo versions are affected before 8.0.9, from 8.1.0 before 8.1.8, and from 8.2.0 before 8.2.4. Atlassian Bitbucket versions are affected before 7.6.16, from 7.7.0 before 7.17.8, from 7.18.0 before 7.19.5, from 7.20.0 before 7.20.2, from 7.21.0 before 7.21.2, and versions 8.0.0 and 8.1.0. Atlassian Confluence versions are affected before 7.4.17, from 7.5.0 before 7.13.7, from 7.14.0 before 7.14.3, from 7.15.0 before 7.15.2, from 7.16.0 before 7.16.4, from 7.17.0 before 7.17.4, and version 7.21.0. Atlassian Crowd versions are affected before 4.3.8, from 4.4.0 before 4.4.2, and version 5.0.0. Atlassian Fisheye and Crucible versions before 4.8.10 are affected. Atlassian Jira versions are affected before 8.13.22, from 8.14.0 before 8.20.10, and from 8.21.0 before 8.22.4. Atlassian Jira Service Management versions are affected before 4.13.22, from 4.14.0 before 4.20.10, and from 4.21.0 before 4.22.4.",
"id": "GHSA-f3mv-w3v5-88qp",
"modified": "2022-08-05T00:00:28Z",
"published": "2022-07-21T00:00:27Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-26136"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/BAM-21795"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/BSERV-13370"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/CONFSERVER-79476"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/CRUC-8541"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/CWD-5815"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/FE-7410"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/JRASERVER-73897"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/JSDSERVER-11863"
}
],
"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-F7VP-7XGX-4W4R
Vulnerability from github – Published: 2026-08-03 21:05 – Updated: 2026-08-03 21:05Impact
SetCookie::matchesDomain() gives every subdomain of a cookie Domain that cookie unless it recognizes the Domain as an IP literal or a numeric host, and it decides that from the domain's own text, so two spellings a transport reads as an address keep subdomain scope. On 7.15, hexadecimal and mixed-base forms such as 0x7f000001 and 0177.0.0.0x1 go unrecognized while libcurl 8.21.0 reads both as 127.0.0.1, so closing them completes the rule GHSA-g446-98w2-8p5w (CVE-2026-59883) set out to establish, which cited the WHATWG IPv4 parser and so already admitted a 0x part. A percent-escaped Domain keeps that scope on both branches for a different reason: percent-decoding sits above numeric parsing, so 192.168.0.%31 and 127.0.0.1%2e are registered names in the URI grammar rather than address literals, and no numeric rule in any base classifies them, while libcurl decodes the host before it resolves and reads them as 192.168.0.1 and 127.0.0.1. The escape must fall in the rightmost label on 8.0.0, which already catches a decimal, octal or 0x part there, but not on 7.15, where %31%32%37.0.0.0x1 also reads as 127.0.0.1; 8.0.0 is affected in the percent spelling alone.
Both directions of the defect are reproduced over a real socket. A cookie stored for Domain=0x7f000001 is placed in the Cookie header of a request to evil.0x7f000001, disclosing a session identifier or token to a host that is not that address, and a response from evil.0x7f000001 setting Domain=0x7f000001 is accepted into the jar and replayed to the address, so a server answering for the look-alike name can fix a session or set application state.
Exploitation requires the application to enable cookie support, address an origin by one of these spellings, and contact a host whose name ends in it. No public DNS delegation provides such a name, so the exposure is to private, split-horizon and container zones. Applications that do not use Guzzle's cookies, that keep a separate jar for each host or trust boundary, or that address origins only by ordinary names are not affected.
Patches
This is a summary; the patches are the authority. The issue is fixed in 7.15.2 and 8.0.1, which hold a cookie Domain containing a % byte to an exact match against the request host. 7.15.2 additionally holds to an exact match a Domain whose every dot-separated part is decimal, octal or hexadecimal, a rule wide enough that 0x100000000, a name to libcurl, loses subdomain matching too; 8.0.0 already recognizes a 0x-prefixed rightmost label. One class stays open: an IDN-capable transport reads a fullwidth-digit Domain such as 127.0.0.1 as 127.0.0.1 while Guzzle matches it by suffix, and these patches do not close that. Versions before 7.15.2 and version 8.0.0 are affected, 8.0.0 in the percent spelling alone.
Workarounds
If you cannot upgrade, do not share one CookieJar between an origin addressed by a numeric or percent-escaped spelling and any other host ending in that spelling. Use a separate jar for each host or trust boundary, or address such an origin by its canonical dotted-decimal form, writing 127.0.0.1, which released versions already hold to an exact match, rather than 0x7f000001 or 127.0.0.%31. Rejecting a request URI host that contains a percent escape, before you hand the URI to Guzzle and on every redirect hop, closes that spelling, and rejecting a non-ASCII byte closes the fullwidth one, but nothing outside the jar closes the hexadecimal one.
Do not use filter_var() with FILTER_VALIDATE_IP to decide whether a domain is an address: it rejects 0x7f000001 and 127.0.0.%31, which a transport reads as 127.0.0.1.
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "guzzlehttp/guzzle"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "7.15.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Packagist",
"name": "guzzlehttp/guzzle"
},
"ranges": [
{
"events": [
{
"introduced": "8.0.0"
},
{
"fixed": "8.0.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-69245"
],
"database_specific": {
"cwe_ids": [
"CWE-180",
"CWE-346",
"CWE-384"
],
"github_reviewed": true,
"github_reviewed_at": "2026-08-03T21:05:26Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Impact\n\n`SetCookie::matchesDomain()` gives every subdomain of a cookie `Domain` that cookie unless it recognizes the `Domain` as an IP literal or a numeric host, and it decides that from the domain\u0027s own text, so two spellings a transport reads as an address keep subdomain scope. On `7.15`, hexadecimal and mixed-base forms such as `0x7f000001` and `0177.0.0.0x1` go unrecognized while libcurl 8.21.0 reads both as `127.0.0.1`, so closing them completes the rule `GHSA-g446-98w2-8p5w` (`CVE-2026-59883`) set out to establish, which cited the WHATWG IPv4 parser and so already admitted a `0x` part. A percent-escaped `Domain` keeps that scope on both branches for a different reason: percent-decoding sits above numeric parsing, so `192.168.0.%31` and `127.0.0.1%2e` are registered names in the URI grammar rather than address literals, and no numeric rule in any base classifies them, while libcurl decodes the host before it resolves and reads them as `192.168.0.1` and `127.0.0.1`. The escape must fall in the rightmost label on `8.0.0`, which already catches a decimal, octal or `0x` part there, but not on `7.15`, where `%31%32%37.0.0.0x1` also reads as `127.0.0.1`; `8.0.0` is affected in the percent spelling alone.\n\nBoth directions of the defect are reproduced over a real socket. A cookie stored for `Domain=0x7f000001` is placed in the `Cookie` header of a request to `evil.0x7f000001`, disclosing a session identifier or token to a host that is not that address, and a response from `evil.0x7f000001` setting `Domain=0x7f000001` is accepted into the jar and replayed to the address, so a server answering for the look-alike name can fix a session or set application state.\n\nExploitation requires the application to enable cookie support, address an origin by one of these spellings, and contact a host whose name ends in it. No public DNS delegation provides such a name, so the exposure is to private, split-horizon and container zones. Applications that do not use Guzzle\u0027s cookies, that keep a separate jar for each host or trust boundary, or that address origins only by ordinary names are not affected.\n\n### Patches\n\nThis is a summary; the patches are the authority. The issue is fixed in `7.15.2` and `8.0.1`, which hold a cookie `Domain` containing a `%` byte to an exact match against the request host. `7.15.2` additionally holds to an exact match a `Domain` whose every dot-separated part is decimal, octal or hexadecimal, a rule wide enough that `0x100000000`, a name to libcurl, loses subdomain matching too; `8.0.0` already recognizes a `0x`-prefixed rightmost label. One class stays open: an IDN-capable transport reads a fullwidth-digit `Domain` such as `127.0.0.\uff11` as `127.0.0.1` while Guzzle matches it by suffix, and these patches do not close that. Versions before `7.15.2` and version `8.0.0` are affected, `8.0.0` in the percent spelling alone.\n\n### Workarounds\n\nIf you cannot upgrade, do not share one `CookieJar` between an origin addressed by a numeric or percent-escaped spelling and any other host ending in that spelling. Use a separate jar for each host or trust boundary, or address such an origin by its canonical dotted-decimal form, writing `127.0.0.1`, which released versions already hold to an exact match, rather than `0x7f000001` or `127.0.0.%31`. Rejecting a request URI host that contains a percent escape, before you hand the URI to Guzzle and on every redirect hop, closes that spelling, and rejecting a non-ASCII byte closes the fullwidth one, but nothing outside the jar closes the hexadecimal one.\n\nDo not use `filter_var()` with `FILTER_VALIDATE_IP` to decide whether a domain is an address: it rejects `0x7f000001` and `127.0.0.%31`, which a transport reads as `127.0.0.1`.",
"id": "GHSA-f7vp-7xgx-4w4r",
"modified": "2026-08-03T21:05:26Z",
"published": "2026-08-03T21:05:26Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/guzzle/guzzle/security/advisories/GHSA-f7vp-7xgx-4w4r"
},
{
"type": "WEB",
"url": "https://github.com/guzzle/guzzle/pull/3907"
},
{
"type": "WEB",
"url": "https://github.com/guzzle/guzzle/pull/3908"
},
{
"type": "WEB",
"url": "https://github.com/guzzle/guzzle/commit/3aeea0406aab88cbbd86531313d7cebf8ae149a4"
},
{
"type": "WEB",
"url": "https://github.com/guzzle/guzzle/commit/744101956d78b7c1384d0cbf379db13e859167bf"
},
{
"type": "PACKAGE",
"url": "https://github.com/guzzle/guzzle"
},
{
"type": "WEB",
"url": "https://github.com/guzzle/guzzle/releases/tag/7.15.2"
},
{
"type": "WEB",
"url": "https://github.com/guzzle/guzzle/releases/tag/8.0.1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Guzzle: Noncanonical cookie domain keeps subdomain scope"
}
GHSA-FPJ5-PCGC-34G7
Vulnerability from github – Published: 2022-07-21 00:00 – Updated: 2024-10-03 18:30A vulnerability in multiple Atlassian products allows a remote, unauthenticated attacker to cause additional Servlet Filters to be invoked when the application processes requests or responses. Atlassian has confirmed and fixed the only known security issue associated with this vulnerability: Cross-origin resource sharing (CORS) bypass. Sending a specially crafted HTTP request can invoke the Servlet Filter used to respond to CORS requests, resulting in a CORS bypass. An attacker that can trick a user into requesting a malicious URL can access the vulnerable application with the victim’s permissions. Atlassian Bamboo versions are affected before 8.0.9, from 8.1.0 before 8.1.8, and from 8.2.0 before 8.2.4. Atlassian Bitbucket versions are affected before 7.6.16, from 7.7.0 before 7.17.8, from 7.18.0 before 7.19.5, from 7.20.0 before 7.20.2, from 7.21.0 before 7.21.2, and versions 8.0.0 and 8.1.0. Atlassian Confluence versions are affected before 7.4.17, from 7.5.0 before 7.13.7, from 7.14.0 before 7.14.3, from 7.15.0 before 7.15.2, from 7.16.0 before 7.16.4, from 7.17.0 before 7.17.4, and version 7.21.0. Atlassian Crowd versions are affected before 4.3.8, from 4.4.0 before 4.4.2, and version 5.0.0. Atlassian Fisheye and Crucible versions before 4.8.10 are affected. Atlassian Jira versions are affected before 8.13.22, from 8.14.0 before 8.20.10, and from 8.21.0 before 8.22.4. Atlassian Jira Service Management versions are affected before 4.13.22, from 4.14.0 before 4.20.10, and from 4.21.0 before 4.22.4.
{
"affected": [],
"aliases": [
"CVE-2022-26137"
],
"database_specific": {
"cwe_ids": [
"CWE-180",
"CWE-346"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2022-07-20T18:15:00Z",
"severity": "HIGH"
},
"details": "A vulnerability in multiple Atlassian products allows a remote, unauthenticated attacker to cause additional Servlet Filters to be invoked when the application processes requests or responses. Atlassian has confirmed and fixed the only known security issue associated with this vulnerability: Cross-origin resource sharing (CORS) bypass. Sending a specially crafted HTTP request can invoke the Servlet Filter used to respond to CORS requests, resulting in a CORS bypass. An attacker that can trick a user into requesting a malicious URL can access the vulnerable application with the victim\u2019s permissions. Atlassian Bamboo versions are affected before 8.0.9, from 8.1.0 before 8.1.8, and from 8.2.0 before 8.2.4. Atlassian Bitbucket versions are affected before 7.6.16, from 7.7.0 before 7.17.8, from 7.18.0 before 7.19.5, from 7.20.0 before 7.20.2, from 7.21.0 before 7.21.2, and versions 8.0.0 and 8.1.0. Atlassian Confluence versions are affected before 7.4.17, from 7.5.0 before 7.13.7, from 7.14.0 before 7.14.3, from 7.15.0 before 7.15.2, from 7.16.0 before 7.16.4, from 7.17.0 before 7.17.4, and version 7.21.0. Atlassian Crowd versions are affected before 4.3.8, from 4.4.0 before 4.4.2, and version 5.0.0. Atlassian Fisheye and Crucible versions before 4.8.10 are affected. Atlassian Jira versions are affected before 8.13.22, from 8.14.0 before 8.20.10, and from 8.21.0 before 8.22.4. Atlassian Jira Service Management versions are affected before 4.13.22, from 4.14.0 before 4.20.10, and from 4.21.0 before 4.22.4.",
"id": "GHSA-fpj5-pcgc-34g7",
"modified": "2024-10-03T18:30:34Z",
"published": "2022-07-21T00:00:27Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-26137"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/BAM-21795"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/BSERV-13370"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/CONFSERVER-79476"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/CRUC-8541"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/CWD-5815"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/FE-7410"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/JRASERVER-73897"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/JSDSERVER-11863"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-G966-83W7-6W38
Vulnerability from github – Published: 2026-02-12 15:29 – Updated: 2026-02-12 22:07Summary
FrankenPHP’s CGI path splitting logic improperly handles Unicode characters during case conversion. The logic computes the split index (for finding .php) on a lowercased copy of the request path but applies that byte index to the original path.
Because strings.ToLower() in Go can increase the byte length of certain UTF-8 characters (e.g., Ⱥ expands when lowercased), the computed index may not align with the correct position in the original string. This results in an incorrect SCRIPT_NAME and SCRIPT_FILENAME, potentially causing FrankenPHP to execute a file other than the one intended by the URI.
Details
The vulnerability resides in the splitPos() function and its usage within splitCgiPath(). The logic attempts to find the script extension (e.g., .php) in a case-insensitive manner by lowercasing the path:
lowerPath := strings.ToLower(path)
idx := strings.Index(lowerPath, strings.ToLower(split))
return idx + len(split)
The issue is that the returned idx represents a byte offset within lowerPath. However, splitCgiPath() uses this index to slice the original path:
fc.docURI = path[:splitPos]
fc.pathInfo = path[splitPos:]
fc.scriptName = strings.TrimSuffix(path, fc.pathInfo)
fc.scriptFilename = sanitizedPathJoin(fc.documentRoot, fc.scriptName)
This logic relies on the assumption that len(strings.ToLower(path)) == len(path). This assumption is false for certain Unicode characters. For example, the character Ⱥ (U+023A) requires 2 bytes in UTF-8 (0xC8 0xBA), but its lowercase equivalent ⱥ (U+2C65) requires 3 bytes (0xE2 0xB1 0xA5).
If the path contains such characters before the .php extension, the index calculated on lowerPath will be larger than the corresponding visual point in the original path. When applied to the original path, the split occurs at the wrong byte offset. This can cause the server to treat a larger portion of the path as the script name, effectively allowing an attacker to manipulate SCRIPT_FILENAME.
PoC
The following Go program demonstrates the discrepancy between the byte index in the lowercased string versus the original string.
- Save the following as
poc.go:
package main
import (
"fmt"
"strings"
)
func splitPos(path string, split string) int {
lowerPath := strings.ToLower(path)
idx := strings.Index(lowerPath, strings.ToLower(split))
if idx < 0 {
return -1
}
return idx + len(split)
}
func main() {
// U+023A: Ⱥ (UTF-8: C8 BA). Lowercase is ⱥ (UTF-8: E2 B1 A5), longer in bytes.
// We construct a path where the byte expansion shifts the index.
path := "/ȺȺȺȺshell.php.txt.php"
split := ".php"
pos := splitPos(path, split)
fmt.Printf("orig bytes=%d\n", len(path))
fmt.Printf("lower bytes=%d\n", len(strings.ToLower(path)))
fmt.Printf("splitPos=%d\n", pos)
// Current Unsafe Behavior:
fmt.Printf("orig[:pos] (Calculated Script)=%q\n", path[:pos])
fmt.Printf("orig[pos:] (Calculated PathInfo)=%q\n", path[pos:])
// Expected Safe Behavior:
want := strings.Index(path, split) + len(split)
fmt.Printf("expected splitPos=%d\n", want)
fmt.Printf("expected orig[:]=%q\n", path[:want])
}
- Run the PoC:
go run poc.go
- Output:
orig bytes=26
lower bytes=30
splitPos=22
orig[:pos]="/ȺȺȺȺshell.php.txt"
orig[pos:]=".php"
expected splitPos=18
expected orig[:]="/ȺȺȺȺshell.php"
In this example, FrankenPHP would identify /ȺȺȺȺshell.php.txt as the PHP script to execute, ignoring the fact that the actual file extension in the file system might be .txt.
Impact*
This is a Security Boundary Bypass and Path Confusion vulnerability.
In setups where users can upload files (e.g., avatars, text files) that are stored within the document root or a reachable path, an attacker can upload a file containing malicious PHP code with a safe extension (e.g., payload.txt). By crafting a request with specific Unicode characters, the attacker can force FrankenPHP to calculate the SCRIPT_FILENAME as ending in payload.txt, while the request appears to contain .php to the internal router logic.
This results in the execution of non-PHP files as PHP scripts, leading to Remote Code Execution (RCE).
Patched Versions
- This issue is fixed in FrankenPHP version 1.11.2.
Workarounds
- Ensure that user-uploaded files are stored outside of the public document root.
- Implement strict WAF rules to reject requests containing specific multi-byte Unicode characters in the URL path if an upgrade is not immediately possible.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/dunglas/frankenphp"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.11.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-24895"
],
"database_specific": {
"cwe_ids": [
"CWE-180",
"CWE-20"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-12T15:29:36Z",
"nvd_published_at": "2026-02-12T20:16:10Z",
"severity": "HIGH"
},
"details": "### Summary\n\nFrankenPHP\u2019s CGI path splitting logic improperly handles Unicode characters during case conversion. The logic computes the split index (for finding `.php`) on a lowercased copy of the request path but applies that byte index to the original path.\n\nBecause `strings.ToLower()` in Go can increase the byte length of certain UTF-8 characters (e.g., `\u023a` expands when lowercased), the computed index may not align with the correct position in the original string. This results in an incorrect `SCRIPT_NAME` and `SCRIPT_FILENAME`, potentially causing FrankenPHP to execute a file other than the one intended by the URI.\n\n### **Details**\n\nThe vulnerability resides in the `splitPos()` function and its usage within `splitCgiPath()`. The logic attempts to find the script extension (e.g., `.php`) in a case-insensitive manner by lowercasing the path:\n\n```go\nlowerPath := strings.ToLower(path)\nidx := strings.Index(lowerPath, strings.ToLower(split))\nreturn idx + len(split)\n```\n\nThe issue is that the returned `idx` represents a byte offset within `lowerPath`. However, `splitCgiPath()` uses this index to slice the **original** `path`:\n\n```go\nfc.docURI = path[:splitPos]\nfc.pathInfo = path[splitPos:]\nfc.scriptName = strings.TrimSuffix(path, fc.pathInfo)\nfc.scriptFilename = sanitizedPathJoin(fc.documentRoot, fc.scriptName)\n```\n\nThis logic relies on the assumption that `len(strings.ToLower(path)) == len(path)`. This assumption is false for certain Unicode characters. For example, the character `\u023a` (U+023A) requires 2 bytes in UTF-8 (`0xC8 0xBA`), but its lowercase equivalent `\u2c65` (U+2C65) requires 3 bytes (`0xE2 0xB1 0xA5`).\n\nIf the path contains such characters before the `.php` extension, the index calculated on `lowerPath` will be larger than the corresponding visual point in the original `path`. When applied to the original path, the split occurs at the wrong byte offset. This can cause the server to treat a larger portion of the path as the script name, effectively allowing an attacker to manipulate `SCRIPT_FILENAME`.\n\n### **PoC**\n\nThe following Go program demonstrates the discrepancy between the byte index in the lowercased string versus the original string.\n\n1. Save the following as `poc.go`:\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"strings\"\n)\n\nfunc splitPos(path string, split string) int {\n lowerPath := strings.ToLower(path)\n idx := strings.Index(lowerPath, strings.ToLower(split))\n if idx \u003c 0 {\n return -1\n }\n return idx + len(split)\n}\n\nfunc main() {\n // U+023A: \u023a (UTF-8: C8 BA). Lowercase is \u2c65 (UTF-8: E2 B1 A5), longer in bytes.\n // We construct a path where the byte expansion shifts the index.\n path := \"/\u023a\u023a\u023a\u023ashell.php.txt.php\"\n split := \".php\"\n\n pos := splitPos(path, split)\n\n fmt.Printf(\"orig bytes=%d\\n\", len(path))\n fmt.Printf(\"lower bytes=%d\\n\", len(strings.ToLower(path)))\n fmt.Printf(\"splitPos=%d\\n\", pos)\n\n // Current Unsafe Behavior:\n fmt.Printf(\"orig[:pos] (Calculated Script)=%q\\n\", path[:pos])\n fmt.Printf(\"orig[pos:] (Calculated PathInfo)=%q\\n\", path[pos:])\n\n // Expected Safe Behavior:\n want := strings.Index(path, split) + len(split)\n fmt.Printf(\"expected splitPos=%d\\n\", want)\n fmt.Printf(\"expected orig[:]=%q\\n\", path[:want])\n}\n```\n\n2. Run the PoC:\n\n```console\ngo run poc.go\n```\n\n3. **Output:**\n\n```text\norig bytes=26\nlower bytes=30\nsplitPos=22\norig[:pos]=\"/\u023a\u023a\u023a\u023ashell.php.txt\"\norig[pos:]=\".php\"\nexpected splitPos=18\nexpected orig[:]=\"/\u023a\u023a\u023a\u023ashell.php\"\n```\n\nIn this example, FrankenPHP would identify `/\u023a\u023a\u023a\u023ashell.php.txt` as the PHP script to execute, ignoring the fact that the actual file extension in the file system might be `.txt`.\n\n### Impact*\n\nThis is a **Security Boundary Bypass** and **Path Confusion** vulnerability.\n\nIn setups where users can upload files (e.g., avatars, text files) that are stored within the document root or a reachable path, an attacker can upload a file containing malicious PHP code with a safe extension (e.g., `payload.txt`). By crafting a request with specific Unicode characters, the attacker can force FrankenPHP to calculate the `SCRIPT_FILENAME` as ending in `payload.txt`, while the request appears to contain `.php` to the internal router logic.\n\nThis results in the execution of non-PHP files as PHP scripts, leading to **Remote Code Execution (RCE)**.\n\n### **Patched Versions**\n\n* This issue is fixed in FrankenPHP version **1.11.2**.\n\n### **Workarounds**\n\n* Ensure that user-uploaded files are stored outside of the public document root.\n* Implement strict WAF rules to reject requests containing specific multi-byte Unicode characters in the URL path if an upgrade is not immediately possible.",
"id": "GHSA-g966-83w7-6w38",
"modified": "2026-02-12T22:07:57Z",
"published": "2026-02-12T15:29:36Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/php/frankenphp/security/advisories/GHSA-g966-83w7-6w38"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-24895"
},
{
"type": "WEB",
"url": "https://github.com/php/frankenphp/commit/04fdc0c1e8fde94e2c1ad86217e962c88d27c53e"
},
{
"type": "PACKAGE",
"url": "https://github.com/php/frankenphp"
},
{
"type": "WEB",
"url": "https://github.com/php/frankenphp/releases/tag/v1.11.2"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/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"
}
],
"summary": "FrankenPHP\u0027s unicode case-folding length expansion causes incorrect split_path index (SCRIPT_NAME/PATH_INFO confusion) in FrankenPHP"
}
Mitigation MIT-20
Strategy: Input Validation
Inputs should be decoded and canonicalized to the application's current internal representation before being validated (CWE-180). Make sure that the application does not decode the same input twice (CWE-174). Such errors could be used to bypass allowlist validation schemes by introducing dangerous inputs after they have been checked.
CAPEC-267: Leverage Alternate Encoding
An adversary leverages the possibility to encode potentially harmful input or content used by applications such that the applications are ineffective at validating this encoding standard.
CAPEC-3: Using Leading 'Ghost' Character Sequences to Bypass Input Filters
Some APIs will strip certain leading characters from a string of parameters. An adversary can intentionally introduce leading "ghost" characters (extra characters that don't affect the validity of the request at the API layer) that enable the input to pass the filters and therefore process the adversary's input. This occurs when the targeted API will accept input data in several syntactic forms and interpret it in the equivalent semantic way, while the filter does not take into account the full spectrum of the syntactic forms acceptable to the targeted API.
CAPEC-71: Using Unicode Encoding to Bypass Validation Logic
An attacker may provide a Unicode string to a system component that is not Unicode aware and use that to circumvent the filter or cause the classifying mechanism to fail to properly understanding the request. That may allow the attacker to slip malicious data past the content filter and/or possibly cause the application to route the request incorrectly.
CAPEC-78: Using Escaped Slashes in Alternate Encoding
This attack targets the use of the backslash in alternate encoding. An adversary can provide a backslash as a leading character and causes a parser to believe that the next character is special. This is called an escape. By using that trick, the adversary tries to exploit alternate ways to encode the same character which leads to filter problems and opens avenues to attack.
CAPEC-79: Using Slashes in Alternate Encoding
This attack targets the encoding of the Slash characters. An adversary would try to exploit common filtering problems related to the use of the slashes characters to gain access to resources on the target host. Directory-driven systems, such as file systems and databases, typically use the slash character to indicate traversal between directories or other container components. For murky historical reasons, PCs (and, as a result, Microsoft OSs) choose to use a backslash, whereas the UNIX world typically makes use of the forward slash. The schizophrenic result is that many MS-based systems are required to understand both forms of the slash. This gives the adversary many opportunities to discover and abuse a number of common filtering problems. The goal of this pattern is to discover server software that only applies filters to one version, but not the other.
CAPEC-80: Using UTF-8 Encoding to Bypass Validation Logic
This attack is a specific variation on leveraging alternate encodings to bypass validation logic. This attack leverages the possibility to encode potentially harmful input in UTF-8 and submit it to applications not expecting or effective at validating this encoding standard making input filtering difficult. UTF-8 (8-bit UCS/Unicode Transformation Format) is a variable-length character encoding for Unicode. Legal UTF-8 characters are one to four bytes long. However, early version of the UTF-8 specification got some entries wrong (in some cases it permitted overlong characters). UTF-8 encoders are supposed to use the "shortest possible" encoding, but naive decoders may accept encodings that are longer than necessary. According to the RFC 3629, a particularly subtle form of this attack can be carried out against a parser which performs security-critical validity checks against the UTF-8 encoded form of its input, but interprets certain illegal octet sequences as characters.