CWE-625
AllowedPermissive Regular Expression
Abstraction: Base · Status: Draft
The product uses a regular expression that does not sufficiently restrict the set of allowed values.
36 vulnerabilities reference this CWE, most recent first.
GHSA-F8R2-VG7X-GH8M
Vulnerability from github – Published: 2026-03-13 20:55 – Updated: 2026-03-13 20:55Summary
matchesExecAllowlistPattern normalized patterns and targets with lowercasing and compiled glob matching too broadly on POSIX. In addition, the ? wildcard could match /, which allowed matches to cross path segments.
Impact
These matching rules could overmatch allowlist entries and permit commands or executable paths that an operator did not intend to approve.
Affected versions
openclaw <= 2026.3.8
Patch
Fixed in openclaw 2026.3.11 and included in later releases such as 2026.3.12. Exec allowlist matching now respects the intended path semantics, and regression tests cover the POSIX case-folding and slash-crossing cases.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2026.3.8"
},
"package": {
"ecosystem": "npm",
"name": "openclaw"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2026.3.11"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-178",
"CWE-625"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-13T20:55:03Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Summary\n\n`matchesExecAllowlistPattern` normalized patterns and targets with lowercasing and compiled glob matching too broadly on POSIX. In addition, the `?` wildcard could match `/`, which allowed matches to cross path segments.\n\n### Impact\n\nThese matching rules could overmatch allowlist entries and permit commands or executable paths that an operator did not intend to approve.\n\n### Affected versions\n\n`openclaw` `\u003c= 2026.3.8`\n\n### Patch\n\nFixed in `openclaw` `2026.3.11` and included in later releases such as `2026.3.12`. Exec allowlist matching now respects the intended path semantics, and regression tests cover the POSIX case-folding and slash-crossing cases.",
"id": "GHSA-f8r2-vg7x-gh8m",
"modified": "2026-03-13T20:55:03Z",
"published": "2026-03-13T20:55:03Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-f8r2-vg7x-gh8m"
},
{
"type": "PACKAGE",
"url": "https://github.com/openclaw/openclaw"
},
{
"type": "WEB",
"url": "https://github.com/openclaw/openclaw/releases/tag/v2026.3.11"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "OpenClaw: Exec approval allowlist patterns overmatched on POSIX paths"
}
GHSA-JXJR-3G7G-3944
Vulnerability from github – Published: 2026-09-08 21:03 – Updated: 2026-09-08 21:03Summary
An embedded line terminator bypasses the requireWellFormed serializer check for element and
attribute names. The check was added to fix GHSA-w2rr-34g9-rvrj and GHSA-4w3w-2rp5-g8jm; a name whose
first line is well-formed slips past it and is serialized verbatim, so the characters after the line
terminator break out of the start/end tag or attribute. Callers who enabled requireWellFormed
specifically to neutralize those name-injection issues remain exposed.
Details
xmldom builds every grammar production through a shared regexp builder that compiles with the m
flag. The anchored full-string matcher used for element and attribute names, QName_exact =
reg('^', QName, '$'), therefore inherits m. When it is applied as QName_exact.test(name) against
an already-assembled node name, the m flag makes $ match at an interior line terminator, so the
matcher accepts any value in which at least one line is a valid QName; the other lines are never
constrained. A payload whose first line is a valid QName, followed by a line terminator and breakout
markup, is what yields a working injection.
The serializer emits the accepted name verbatim into element start/end tags and attribute names, so
the bytes after the line terminator break out of the intended syntactic position. The check is
reached whenever a caller serializes, with requireWellFormed: true, a node whose name was set
through programmatic DOM construction (createElement, createElementNS, createAttribute,
createAttributeNS) with attacker-influenced input.
Root Cause
- A shared regexp builder compiles anchored productions with the
mflag. ^…$undermare line anchors, not string anchors.- A full-string validator built on such a production (
.test()) accepts any string with one conforming line, so a line terminator followed by breakout markup passes.
The triggering line terminators are the ECMAScript LineTerminator set: U+000A, U+000D, U+2028, U+2029.
Proof of Concept
const { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');
// Element name carrying an embedded line terminator + breakout markup:
const doc = new DOMImplementation().createDocument(null, 'root', null);
const el = doc.createElement('a\n><script>alert(1)</script');
doc.documentElement.appendChild(el);
// Caller opted into well-formed serialization, expecting invalid names to be rejected:
console.log(new XMLSerializer().serializeToString(doc, { requireWellFormed: true }));
// Observed on the affected version: NO throw; the output contains the injected `><script>…`
// breakout, because the name's first line ("a") satisfies the m-anchored QName check.
// Expected: InvalidStateError (the name is not a valid XML QName).
// Control — a single-line invalid name IS correctly rejected, proving the check is active and
// that only the line terminator defeats it:
const ctrl = new DOMImplementation().createDocument(null, 'root', null);
ctrl.documentElement.appendChild(ctrl.createElement('a b'));
new XMLSerializer().serializeToString(ctrl, { requireWellFormed: true });
// => throws InvalidStateError: The element name "a b" is not a valid XML QName
Impact
- Bypass of a previously shipped security mitigation. Applications that adopted
requireWellFormed: truespecifically to neutralize GHSA-w2rr-34g9-rvrj / GHSA-4w3w-2rp5-g8jm remain exposed to element/attribute name injection. - XML / markup structure injection, and, where the serialized output is placed into an HTML context, downstream XSS.
Fix Applied
The anchored XML Name/QName validators used by the requireWellFormed serializer no
longer treat interior line terminators as satisfying the anchors, so a name is validated against the
whole string. A name containing a line terminator is rejected with InvalidStateError, closing the
bypass for element and attribute names. The default serialization path is unchanged.
⚠ Opt-in required. Protection is not automatic. Existing serialization calls remain vulnerable unless
{ requireWellFormed: true }is explicitly passed. Applications that serialize untrusted DOM content should audit allserializeToString()call sites and add it.
Proof of Concept - fixed path
const { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');
const doc = new DOMImplementation().createDocument(null, 'root', null);
const el = doc.createElement('a\n><script>alert(1)</script');
doc.documentElement.appendChild(el);
// Default path (require-well-formed off) — unchanged, still emits the name verbatim,
// so the `><script>…` bytes break out of the start tag:
new XMLSerializer().serializeToString(doc);
// Opted-in path — now rejected:
new XMLSerializer().serializeToString(doc, { requireWellFormed: true });
// throws InvalidStateError: The element name "a\n><script>alert(1)</script" is not a valid XML QName
Why the default stays verbatim
The W3C DOM Parsing require-well-formed flag defaults to false, and browser XMLSerializer emits
names verbatim when it is unset. Throwing unconditionally would be an unjustified breaking change, so
the check stays gated on the caller opting in with { requireWellFormed: true }.
Residual limitation
The default serialization path (no requireWellFormed) still emits names verbatim by design (above).
Names introduced through createElement / setAttribute are never validated at creation — those APIs
store the name unchecked by design — so the opt-in serializer check remains the only guard on that
path.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@xmldom/xmldom"
},
"ranges": [
{
"events": [
{
"introduced": "0.9.11"
},
{
"fixed": "0.9.12"
}
],
"type": "ECOSYSTEM"
}
],
"versions": [
"0.9.11"
]
}
],
"aliases": [
"CVE-2026-83617"
],
"database_specific": {
"cwe_ids": [
"CWE-625",
"CWE-91"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-08T21:03:17Z",
"nvd_published_at": "2026-09-01T15:17:40Z",
"severity": "HIGH"
},
"details": "## Summary\n\nAn embedded line terminator bypasses the `requireWellFormed` serializer check for element and\nattribute names. The check was added to fix GHSA-w2rr-34g9-rvrj and GHSA-4w3w-2rp5-g8jm; a name whose\nfirst line is well-formed slips past it and is serialized verbatim, so the characters after the line\nterminator break out of the start/end tag or attribute. Callers who enabled `requireWellFormed`\nspecifically to neutralize those name-injection issues remain exposed.\n\n## Details\n\nxmldom builds every grammar production through a shared regexp builder that compiles with the `m`\nflag. The anchored full-string matcher used for element and attribute names, `QName_exact =\nreg(\u0027^\u0027, QName, \u0027$\u0027)`, therefore inherits `m`. When it is applied as `QName_exact.test(name)` against\nan already-assembled node name, the `m` flag makes `$` match at an interior line terminator, so the\nmatcher accepts any value in which **at least one line** is a valid `QName`; the other lines are never\nconstrained. A payload whose first line is a valid `QName`, followed by a line terminator and breakout\nmarkup, is what yields a working injection.\n\nThe serializer emits the accepted name verbatim into element start/end tags and attribute names, so\nthe bytes after the line terminator break out of the intended syntactic position. The check is\nreached whenever a caller serializes, with `requireWellFormed: true`, a node whose name was set\nthrough programmatic DOM construction (`createElement`, `createElementNS`, `createAttribute`,\n`createAttributeNS`) with attacker-influenced input.\n\n### Root Cause\n\n1. A shared regexp builder compiles anchored productions with the `m` flag.\n2. `^\u2026$` under `m` are line anchors, not string anchors.\n3. A full-string validator built on such a production (`.test()`) accepts any string with one\n conforming line, so a line terminator followed by breakout markup passes.\n\nThe triggering line terminators are the ECMAScript `LineTerminator` set: U+000A, U+000D, U+2028, U+2029.\n\n## Proof of Concept\n\n```js\nconst { DOMImplementation, XMLSerializer } = require(\u0027@xmldom/xmldom\u0027);\n\n// Element name carrying an embedded line terminator + breakout markup:\nconst doc = new DOMImplementation().createDocument(null, \u0027root\u0027, null);\nconst el = doc.createElement(\u0027a\\n\u003e\u003cscript\u003ealert(1)\u003c/script\u0027);\ndoc.documentElement.appendChild(el);\n\n// Caller opted into well-formed serialization, expecting invalid names to be rejected:\nconsole.log(new XMLSerializer().serializeToString(doc, { requireWellFormed: true }));\n// Observed on the affected version: NO throw; the output contains the injected `\u003e\u003cscript\u003e\u2026`\n// breakout, because the name\u0027s first line (\"a\") satisfies the m-anchored QName check.\n// Expected: InvalidStateError (the name is not a valid XML QName).\n\n// Control \u2014 a single-line invalid name IS correctly rejected, proving the check is active and\n// that only the line terminator defeats it:\nconst ctrl = new DOMImplementation().createDocument(null, \u0027root\u0027, null);\nctrl.documentElement.appendChild(ctrl.createElement(\u0027a b\u0027));\nnew XMLSerializer().serializeToString(ctrl, { requireWellFormed: true });\n// =\u003e throws InvalidStateError: The element name \"a b\" is not a valid XML QName\n```\n\n## Impact\n\n- **Bypass of a previously shipped security mitigation.** Applications that adopted\n `requireWellFormed: true` specifically to neutralize GHSA-w2rr-34g9-rvrj / GHSA-4w3w-2rp5-g8jm\n remain exposed to element/attribute name injection.\n- **XML / markup structure injection**, and, where the serialized output is placed into an HTML\n context, downstream XSS.\n\n## Fix Applied\n\nThe anchored XML `Name`/`QName` validators used by the `requireWellFormed` serializer no\nlonger treat interior line terminators as satisfying the anchors, so a name is validated against the\nwhole string. A name containing a line terminator is rejected with `InvalidStateError`, closing the\nbypass for element and attribute names. The default serialization path is unchanged.\n\n\u003e **\u26a0 Opt-in required.** Protection is not automatic. Existing serialization calls remain\n\u003e vulnerable unless `{ requireWellFormed: true }` is explicitly passed. Applications that\n\u003e serialize untrusted DOM content should audit all `serializeToString()` call sites and add it.\n\n### Proof of Concept - fixed path\n\n```js\nconst { DOMImplementation, XMLSerializer } = require(\u0027@xmldom/xmldom\u0027);\nconst doc = new DOMImplementation().createDocument(null, \u0027root\u0027, null);\nconst el = doc.createElement(\u0027a\\n\u003e\u003cscript\u003ealert(1)\u003c/script\u0027);\ndoc.documentElement.appendChild(el);\n\n// Default path (require-well-formed off) \u2014 unchanged, still emits the name verbatim,\n// so the `\u003e\u003cscript\u003e\u2026` bytes break out of the start tag:\nnew XMLSerializer().serializeToString(doc);\n\n// Opted-in path \u2014 now rejected:\nnew XMLSerializer().serializeToString(doc, { requireWellFormed: true });\n// throws InvalidStateError: The element name \"a\\n\u003e\u003cscript\u003ealert(1)\u003c/script\" is not a valid XML QName\n```\n\n### Why the default stays verbatim\n\nThe W3C DOM Parsing require-well-formed flag defaults to false, and browser `XMLSerializer` emits\nnames verbatim when it is unset. Throwing unconditionally would be an unjustified breaking change, so\nthe check stays gated on the caller opting in with `{ requireWellFormed: true }`.\n\n### Residual limitation\n\nThe default serialization path (no `requireWellFormed`) still emits names verbatim by design (above).\nNames introduced through `createElement` / `setAttribute` are never validated at creation \u2014 those APIs\nstore the name unchecked by design \u2014 so the opt-in serializer check remains the only guard on that\npath.",
"id": "GHSA-jxjr-3g7g-3944",
"modified": "2026-09-08T21:03:17Z",
"published": "2026-09-08T21:03:17Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/xmldom/xmldom/security/advisories/GHSA-jxjr-3g7g-3944"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-83617"
},
{
"type": "WEB",
"url": "https://github.com/xmldom/xmldom/pull/1071"
},
{
"type": "WEB",
"url": "https://github.com/xmldom/xmldom/commit/7b2ec67e1750daadd0bb06c92e875e726544a362"
},
{
"type": "PACKAGE",
"url": "https://github.com/xmldom/xmldom"
},
{
"type": "WEB",
"url": "https://github.com/xmldom/xmldom/releases/tag/0.9.12"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "xmldom: requireWellFormed element/attribute name validation is bypassable via an embedded line terminator"
}
GHSA-M4X6-WX7H-HWVH
Vulnerability from github – Published: 2026-08-10 09:31 – Updated: 2026-08-10 09:31Tegalog -Fumy Otegaru Memo Logger- provided by Nishishi Factory contains a vulnerability due to a permissive regular expression, which may allow an attacker who can access the affected product to log in to the management console. As a result, the attacker may perform any operations available from the management console.
{
"affected": [],
"aliases": [
"CVE-2026-64940"
],
"database_specific": {
"cwe_ids": [
"CWE-625"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-08-10T08:16:48Z",
"severity": "HIGH"
},
"details": "Tegalog -Fumy Otegaru Memo Logger- provided by Nishishi Factory contains a vulnerability due to a permissive regular expression, which may allow an attacker who can access the affected product to log in to the management console. As a result, the attacker may perform any operations available from the management console.",
"id": "GHSA-m4x6-wx7h-hwvh",
"modified": "2026-08-10T09:31:23Z",
"published": "2026-08-10T09:31:22Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-64940"
},
{
"type": "WEB",
"url": "https://jvn.jp/en/jp/JVN99975039"
},
{
"type": "WEB",
"url": "https://www.nishishi.com/cgi/tegalog/info/notice20260629.shtml"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-PV25-75Q9-7735
Vulnerability from github – Published: 2026-03-06 00:31 – Updated: 2026-03-06 00:31Permissive regular expression in Azure Compute Gallery allows an authorized attacker to elevate privileges locally.
{
"affected": [],
"aliases": [
"CVE-2026-23651"
],
"database_specific": {
"cwe_ids": [
"CWE-625"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-03-05T23:16:19Z",
"severity": "MODERATE"
},
"details": "Permissive regular expression in Azure Compute Gallery allows an authorized attacker to elevate privileges locally.",
"id": "GHSA-pv25-75q9-7735",
"modified": "2026-03-06T00:31:34Z",
"published": "2026-03-06T00:31:34Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-23651"
},
{
"type": "WEB",
"url": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-23651"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-QV7J-4883-HWH7
Vulnerability from github – Published: 2026-04-02 20:35 – Updated: 2026-05-13 16:18Summary
Rack::Sendfile#map_accel_path interpolates the value of the X-Accel-Mapping request header directly into a regular expression when rewriting file paths for X-Accel-Redirect. Because the header value is not escaped, an attacker who can supply X-Accel-Mapping to the backend can inject regex metacharacters and control the generated X-Accel-Redirect response header.
In deployments using Rack::Sendfile with x-accel-redirect, this can allow an attacker to cause nginx to serve unintended files from configured internal locations.
Details
Rack::Sendfile#map_accel_path processes header-supplied mappings using logic equivalent to:
mapping.split(',').map(&:strip).each do |m|
internal, external = m.split('=', 2).map(&:strip)
new_path = path.sub(/\A#{internal}/i, external)
return new_path unless path == new_path
end
Here, internal comes from the HTTP_X_ACCEL_MAPPING request header and is inserted directly into a regular expression without escaping. This gives the header value regex semantics rather than treating it as a literal prefix.
As a result, an attacker can supply metacharacters such as .* or capture groups to alter how the path substitution is performed. For example, a mapping such as:
X-Accel-Mapping: .*=/protected/secret.txt
causes the entire source path to match and rewrites the redirect target to a clean attacker-chosen internal path.
This differs from the documented behavior of the header-based mapping path, which is described as a simple substitution. While application-supplied mappings may intentionally support regular expressions, header-supplied mappings should be treated as literal path prefixes.
The issue is only exploitable when untrusted X-Accel-Mapping headers can reach Rack. One realistic case is a reverse proxy configuration that intends to set X-Accel-Mapping itself, but fails to do so on some routes, allowing a client-supplied header to pass through unchanged.
Impact
Applications using Rack::Sendfile with x-accel-redirect may be affected if the backend accepts attacker-controlled X-Accel-Mapping headers.
In affected deployments, an attacker may be able to control the X-Accel-Redirect response header and cause nginx to serve files from internal locations that were not intended to be reachable through the application. This can lead to unauthorized file disclosure.
The practical impact depends on deployment architecture. If the proxy always strips or overwrites X-Accel-Mapping, or if the application uses explicit configured mappings instead of the request header, exploitability may be eliminated.
Mitigation
- Update to a patched version of Rack that treats header-supplied
X-Accel-Mappingvalues as literal strings rather than regular expressions. - Strip or overwrite inbound
X-Accel-Mappingheaders at the reverse proxy so client-supplied values never reach Rack. - Prefer explicit application-configured sendfile mappings instead of relying on request-header mappings.
- Review proxy sub-locations and inherited header settings to ensure
X-Accel-Mappingis consistently set on all backend routes.
{
"affected": [
{
"package": {
"ecosystem": "RubyGems",
"name": "rack"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.2.23"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "RubyGems",
"name": "rack"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0.beta1"
},
{
"fixed": "3.1.21"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "RubyGems",
"name": "rack"
},
"ranges": [
{
"events": [
{
"introduced": "3.2.0"
},
{
"fixed": "3.2.6"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-34830"
],
"database_specific": {
"cwe_ids": [
"CWE-625"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-02T20:35:23Z",
"nvd_published_at": "2026-04-02T17:16:26Z",
"severity": "MODERATE"
},
"details": "## Summary\n\n`Rack::Sendfile#map_accel_path` interpolates the value of the `X-Accel-Mapping` request header directly into a regular expression when rewriting file paths for `X-Accel-Redirect`. Because the header value is not escaped, an attacker who can supply `X-Accel-Mapping` to the backend can inject regex metacharacters and control the generated `X-Accel-Redirect` response header.\n\nIn deployments using `Rack::Sendfile` with `x-accel-redirect`, this can allow an attacker to cause nginx to serve unintended files from configured internal locations.\n\n## Details\n\n`Rack::Sendfile#map_accel_path` processes header-supplied mappings using logic equivalent to:\n\n```ruby\nmapping.split(\u0027,\u0027).map(\u0026:strip).each do |m|\n internal, external = m.split(\u0027=\u0027, 2).map(\u0026:strip)\n new_path = path.sub(/\\A#{internal}/i, external)\n return new_path unless path == new_path\nend\n```\n\nHere, `internal` comes from the `HTTP_X_ACCEL_MAPPING` request header and is inserted directly into a regular expression without escaping. This gives the header value regex semantics rather than treating it as a literal prefix.\n\nAs a result, an attacker can supply metacharacters such as `.*` or capture groups to alter how the path substitution is performed. For example, a mapping such as:\n\n```http\nX-Accel-Mapping: .*=/protected/secret.txt\n```\n\ncauses the entire source path to match and rewrites the redirect target to a clean attacker-chosen internal path.\n\nThis differs from the documented behavior of the header-based mapping path, which is described as a simple substitution. While application-supplied mappings may intentionally support regular expressions, header-supplied mappings should be treated as literal path prefixes.\n\nThe issue is only exploitable when untrusted `X-Accel-Mapping` headers can reach Rack. One realistic case is a reverse proxy configuration that intends to set `X-Accel-Mapping` itself, but fails to do so on some routes, allowing a client-supplied header to pass through unchanged.\n\n## Impact\n\nApplications using `Rack::Sendfile` with `x-accel-redirect` may be affected if the backend accepts attacker-controlled `X-Accel-Mapping` headers.\n\nIn affected deployments, an attacker may be able to control the `X-Accel-Redirect` response header and cause nginx to serve files from internal locations that were not intended to be reachable through the application. This can lead to unauthorized file disclosure.\n\nThe practical impact depends on deployment architecture. If the proxy always strips or overwrites `X-Accel-Mapping`, or if the application uses explicit configured mappings instead of the request header, exploitability may be eliminated.\n\n## Mitigation\n\n* Update to a patched version of Rack that treats header-supplied `X-Accel-Mapping` values as literal strings rather than regular expressions.\n* Strip or overwrite inbound `X-Accel-Mapping` headers at the reverse proxy so client-supplied values never reach Rack.\n* Prefer explicit application-configured sendfile mappings instead of relying on request-header mappings.\n* Review proxy sub-locations and inherited header settings to ensure `X-Accel-Mapping` is consistently set on all backend routes.",
"id": "GHSA-qv7j-4883-hwh7",
"modified": "2026-05-13T16:18:57Z",
"published": "2026-04-02T20:35:23Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/rack/rack/security/advisories/GHSA-qv7j-4883-hwh7"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34830"
},
{
"type": "PACKAGE",
"url": "https://github.com/rack/rack"
},
{
"type": "WEB",
"url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/rack/CVE-2026-34830.yml"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Rack::Sendfile header-based X-Accel-Mapping regex injection enables unauthorized X-Accel-Redirect"
}
GHSA-VR34-HP96-76PP
Vulnerability from github – Published: 2026-09-08 21:02 – Updated: 2026-09-08 21:02Summary
An embedded line terminator bypasses the requireWellFormed serializer check for a DocumentType's
publicId and systemId. The check was added to fix GHSA-f6ww-3ggp-fr8h; an id whose first line is a
valid literal slips past it and is emitted verbatim into the <!DOCTYPE …> declaration, so the markup
after the line terminator breaks out into the surrounding document. Callers who enabled
requireWellFormed to neutralize DocumentType injection remain exposed.
Details
publicId and systemId are stored as raw values including their surrounding quotes, and the
PubidLiteral/SystemLiteral productions include those quotes. The serializer validates them with
g.PubidLiteral_match.test(publicId) and g.SystemLiteral_match.test(systemId), where both matchers
are reg('^', …, '$') and inherit the m flag from xmldom's shared regexp builder. Under m, $
matches at an interior line terminator, so a value such as "valid pubid"\n"><!ENTITY …> satisfies
the matcher on its first line ("valid pubid" is a complete PubidLiteral) and the whole value —
including the post-newline breakout — is emitted after PUBLIC/SYSTEM.
Root Cause
- A shared regexp builder compiles anchored productions with the
mflag. ^…$undermare line anchors, not string anchors.- A full-string validator built on such a production (
.test()) accepts any string with one conforming line, so a complete, valid literal on the first line passes even though a line terminator and breakout markup follow.PubidCharexcluding</>does not prevent it — the breakout is appended after the literal, not embedded inside it.
The triggering line terminators are the ECMAScript LineTerminator set: U+000A, U+000D, U+2028, U+2029.
Affected Versions
Only @xmldom/xmldom 0.9.x is affected. The vulnerable matchers are built by lib/grammar.js's
m-flagged reg() builder, and the DocType publicId/systemId requireWellFormed check that
consumes them was introduced in 0.9.10 (the GHSA-f6ww-3ggp-fr8h fix); 0.9.10 and 0.9.11 carry it.
0.8.x performs the same requireWellFormed check with inline, non-m regular expressions and is not
affected. The unscoped xmldom package has no grammar.js and no requireWellFormed serializer, so
there is no check to bypass.
Proof of Concept
const { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');
const impl = new DOMImplementation();
// publicId: complete literal on line 1, then newline + breakout
const dt = impl.createDocumentType('html', '"valid pubid"\n"><!ENTITY xxe SYSTEM "file:///etc/passwd">', '');
const doc = impl.createDocument(null, 'root', dt);
console.log(new XMLSerializer().serializeToString(doc, { requireWellFormed: true }));
// Observed (no throw):
// <!DOCTYPE html PUBLIC "valid pubid"
// "><!ENTITY xxe SYSTEM "file:///etc/passwd">><root/>
// Expected: InvalidStateError (publicId is not a valid PubidLiteral).
// Control: a single-line invalid publicId ("no-surrounding-quotes<>") DOES throw InvalidStateError,
// confirming the check is active and specifically bypassed by the line terminator.
Impact
- Bypass of the GHSA-f6ww-3ggp-fr8h mitigation. Applications that adopted
requireWellFormed: trueto neutralize DocumentType injection remain exposed. - XML structure injection into the DOCTYPE, including injected markup / entity declarations after the public or system identifier.
Fix Applied
The anchored PubidLiteral/SystemLiteral validators used by the requireWellFormed
serializer no longer treat an interior line terminator as satisfying the $ anchor, so a publicId
or systemId containing any ECMAScript LineTerminator (U+000A, U+000D, U+2028, U+2029) is rejected
with InvalidStateError. Valid single-line identifiers serialize unchanged, and the default
serialization path is unaffected.
⚠ Opt-in required. Protection is not automatic. Existing serialization calls remain vulnerable unless
{ requireWellFormed: true }is explicitly passed. Applications that serialize untrusted DOM content should audit allserializeToString()call sites and add it.
Proof of Concept - fixed path
const { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');
const impl = new DOMImplementation();
const dt = impl.createDocumentType('html', '"valid pubid"\n"><!ENTITY xxe SYSTEM "file:///etc/passwd">', '');
const doc = impl.createDocument(null, 'root', dt);
// Default path (requireWellFormed off) — unchanged, still emits verbatim:
console.log(new XMLSerializer().serializeToString(doc));
// <!DOCTYPE html PUBLIC "valid pubid"
// "><!ENTITY xxe SYSTEM "file:///etc/passwd">><root/>
// Opt-in path — now throws instead of emitting the breakout:
new XMLSerializer().serializeToString(doc, { requireWellFormed: true });
// InvalidStateError: DocumentType publicId is not a valid PubidLiteral
Why the default stays verbatim
The W3C DOM Parsing "require well-formed" flag defaults to false, and a browser XMLSerializer emits
the DOCTYPE verbatim. Unconditionally throwing on a malformed publicId/systemId would be an
unjustified breaking change to the default path, so the fix tightens only the opt-in
requireWellFormed validator, matching browser and spec defaults.
Residual limitation
The guarantee holds only for callers that pass { requireWellFormed: true }; the default
serialization path still emits publicId/systemId verbatim. publicId and systemId are not
validated at creation (createDocumentType) or on direct property assignment
(documentType.publicId = …) — the WHATWG DOM specification places no well-formedness constraint on
these fields at creation time, so the serializer is the spec-aligned enforcement point.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.9.11"
},
"package": {
"ecosystem": "npm",
"name": "@xmldom/xmldom"
},
"ranges": [
{
"events": [
{
"introduced": "0.9.10"
},
{
"fixed": "0.9.12"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-83618"
],
"database_specific": {
"cwe_ids": [
"CWE-625",
"CWE-91"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-08T21:02:25Z",
"nvd_published_at": "2026-09-01T15:17:40Z",
"severity": "HIGH"
},
"details": "## Summary\n\nAn embedded line terminator bypasses the `requireWellFormed` serializer check for a `DocumentType`\u0027s\npublicId and systemId. The check was added to fix GHSA-f6ww-3ggp-fr8h; an id whose first line is a\nvalid literal slips past it and is emitted verbatim into the `\u003c!DOCTYPE \u2026\u003e` declaration, so the markup\nafter the line terminator breaks out into the surrounding document. Callers who enabled\n`requireWellFormed` to neutralize DocumentType injection remain exposed.\n\n## Details\n\n`publicId` and `systemId` are stored as raw values **including their surrounding quotes**, and the\n`PubidLiteral`/`SystemLiteral` productions include those quotes. The serializer validates them with\n`g.PubidLiteral_match.test(publicId)` and `g.SystemLiteral_match.test(systemId)`, where both matchers\nare `reg(\u0027^\u0027, \u2026, \u0027$\u0027)` and inherit the `m` flag from xmldom\u0027s shared regexp builder. Under `m`, `$`\nmatches at an interior line terminator, so a value such as `\"valid pubid\"\\n\"\u003e\u003c!ENTITY \u2026\u003e` satisfies\nthe matcher on its first line (`\"valid pubid\"` is a complete `PubidLiteral`) and the whole value \u2014\nincluding the post-newline breakout \u2014 is emitted after `PUBLIC`/`SYSTEM`.\n\n### Root Cause\n\n1. A shared regexp builder compiles anchored productions with the `m` flag.\n2. `^\u2026$` under `m` are line anchors, not string anchors.\n3. A full-string validator built on such a production (`.test()`) accepts any string with one\n conforming line, so a complete, valid literal on the first line passes even though a line terminator\n and breakout markup follow. `PubidChar` excluding `\u003c`/`\u003e` does not prevent it \u2014 the breakout is\n appended *after* the literal, not embedded inside it.\n\nThe triggering line terminators are the ECMAScript `LineTerminator` set: U+000A, U+000D, U+2028, U+2029.\n\n## Affected Versions\n\nOnly `@xmldom/xmldom` 0.9.x is affected. The vulnerable matchers are built by `lib/grammar.js`\u0027s\n`m`-flagged `reg()` builder, and the DocType `publicId`/`systemId` `requireWellFormed` check that\nconsumes them was introduced in 0.9.10 (the GHSA-f6ww-3ggp-fr8h fix); 0.9.10 and 0.9.11 carry it.\n`0.8.x` performs the same `requireWellFormed` check with inline, non-`m` regular expressions and is not\naffected. The unscoped `xmldom` package has no `grammar.js` and no `requireWellFormed` serializer, so\nthere is no check to bypass.\n\n## Proof of Concept\n\n```js\nconst { DOMImplementation, XMLSerializer } = require(\u0027@xmldom/xmldom\u0027);\nconst impl = new DOMImplementation();\n\n// publicId: complete literal on line 1, then newline + breakout\nconst dt = impl.createDocumentType(\u0027html\u0027, \u0027\"valid pubid\"\\n\"\u003e\u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\"\u003e\u0027, \u0027\u0027);\nconst doc = impl.createDocument(null, \u0027root\u0027, dt);\nconsole.log(new XMLSerializer().serializeToString(doc, { requireWellFormed: true }));\n// Observed (no throw):\n// \u003c!DOCTYPE html PUBLIC \"valid pubid\"\n// \"\u003e\u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\"\u003e\u003e\u003croot/\u003e\n// Expected: InvalidStateError (publicId is not a valid PubidLiteral).\n// Control: a single-line invalid publicId (\"no-surrounding-quotes\u003c\u003e\") DOES throw InvalidStateError,\n// confirming the check is active and specifically bypassed by the line terminator.\n```\n\n## Impact\n\n- **Bypass of the GHSA-f6ww-3ggp-fr8h mitigation.** Applications that adopted `requireWellFormed:\n true` to neutralize DocumentType injection remain exposed.\n- **XML structure injection into the DOCTYPE**, including injected markup / entity declarations after\n the public or system identifier.\n\n## Fix Applied\n\nThe anchored `PubidLiteral`/`SystemLiteral` validators used by the `requireWellFormed`\nserializer no longer treat an interior line terminator as satisfying the `$` anchor, so a `publicId`\nor `systemId` containing any ECMAScript `LineTerminator` (U+000A, U+000D, U+2028, U+2029) is rejected\nwith `InvalidStateError`. Valid single-line identifiers serialize unchanged, and the default\nserialization path is unaffected.\n\n\u003e **\u26a0 Opt-in required.** Protection is not automatic. Existing serialization calls remain vulnerable\n\u003e unless `{ requireWellFormed: true }` is explicitly passed. Applications that serialize untrusted DOM\n\u003e content should audit all `serializeToString()` call sites and add it.\n\n### Proof of Concept - fixed path\n\n```js\nconst { DOMImplementation, XMLSerializer } = require(\u0027@xmldom/xmldom\u0027);\nconst impl = new DOMImplementation();\nconst dt = impl.createDocumentType(\u0027html\u0027, \u0027\"valid pubid\"\\n\"\u003e\u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\"\u003e\u0027, \u0027\u0027);\nconst doc = impl.createDocument(null, \u0027root\u0027, dt);\n\n// Default path (requireWellFormed off) \u2014 unchanged, still emits verbatim:\nconsole.log(new XMLSerializer().serializeToString(doc));\n// \u003c!DOCTYPE html PUBLIC \"valid pubid\"\n// \"\u003e\u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\"\u003e\u003e\u003croot/\u003e\n\n// Opt-in path \u2014 now throws instead of emitting the breakout:\nnew XMLSerializer().serializeToString(doc, { requireWellFormed: true });\n// InvalidStateError: DocumentType publicId is not a valid PubidLiteral\n```\n\n### Why the default stays verbatim\n\nThe W3C DOM Parsing \"require well-formed\" flag defaults to false, and a browser `XMLSerializer` emits\nthe DOCTYPE verbatim. Unconditionally throwing on a malformed `publicId`/`systemId` would be an\nunjustified breaking change to the default path, so the fix tightens only the opt-in\n`requireWellFormed` validator, matching browser and spec defaults.\n\n### Residual limitation\n\nThe guarantee holds only for callers that pass `{ requireWellFormed: true }`; the default\nserialization path still emits `publicId`/`systemId` verbatim. `publicId` and `systemId` are not\nvalidated at creation (`createDocumentType`) or on direct property assignment\n(`documentType.publicId = \u2026`) \u2014 the WHATWG DOM specification places no well-formedness constraint on\nthese fields at creation time, so the serializer is the spec-aligned enforcement point.",
"id": "GHSA-vr34-hp96-76pp",
"modified": "2026-09-08T21:02:25Z",
"published": "2026-09-08T21:02:25Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/xmldom/xmldom/security/advisories/GHSA-vr34-hp96-76pp"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-83618"
},
{
"type": "WEB",
"url": "https://github.com/xmldom/xmldom/pull/1071"
},
{
"type": "WEB",
"url": "https://github.com/xmldom/xmldom/commit/7b2ec67e1750daadd0bb06c92e875e726544a362"
},
{
"type": "PACKAGE",
"url": "https://github.com/xmldom/xmldom"
},
{
"type": "WEB",
"url": "https://github.com/xmldom/xmldom/releases/tag/0.9.12"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "xmldom: requireWellFormed DocType publicId/systemId validation is bypassable via an embedded line terminator"
}
Mitigation
When applicable, ensure that the regular expression marks beginning and ending string patterns, such as "/^string$/" for Perl.
No CAPEC attack patterns related to this CWE.