GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration
Common Weakness Enumeration

CWE-625

Allowed

Permissive Regular Expression

Abstraction: Base · Status: Draft

The product uses a regular expression that does not sufficiently restrict the set of allowed values.

33 vulnerabilities reference this CWE, most recent first.

GHSA-PV25-75Q9-7735

Vulnerability from github – Published: 2026-03-06 00:31 – Updated: 2026-03-06 00:31
VLAI
Details

Permissive regular expression in Azure Compute Gallery allows an authorized attacker to elevate privileges locally.

Show details on source website

{
  "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:18
VLAI
Summary
Rack::Sendfile header-based X-Accel-Mapping regex injection enables unauthorized X-Accel-Redirect
Details

Summary

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-Mapping values as literal strings rather than regular expressions.
  • Strip or overwrite inbound X-Accel-Mapping headers 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-Mapping is consistently set on all backend routes.
Show details on source website

{
  "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:02
VLAI
Summary
xmldom: requireWellFormed DocType publicId/systemId validation is bypassable via an embedded line terminator
Details

Summary

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

  1. A shared regexp builder compiles anchored productions with the m flag.
  2. ^…$ under m are line anchors, not string anchors.
  3. 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. PubidChar excluding </> 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: true to 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 all serializeToString() 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.

Show details on source website

{
  "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
Implementation

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.