GHSA-97jj-33gv-5xf9: league/commonmark DisallowedRawHtml Bypassed When A Disallowed Vulnerability

MODERATE
CVSS 6.1
CWE-78 CWE-79
Published
Advisory Summary

GHSA-97jj-33gv-5xf9 is a MODERATE (CVSS 6.1) disallowedrawhtml bypassed when a disallowed vulnerability (CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')) affecting league/commonmark libraries in the Packagist ecosystem. The issue involves disallowedrawhtml bypassed when a disallowed tag name ends the raw-html literal. Remediation is available by upgrading affected packages to version 2.10.2.

GHSA-97jj-33gv-5xf9 at a Glance

CVE Identifier N/A
GHSA Identifier GHSA-97jj-33gv-5xf9
Severity MODERATE
CVSS v3.1 Base Score 6.1
CWE Classification CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
Affected Product league/commonmark
Package Ecosystem Packagist
Published Date
Last Updated

Security Impact

CVSS: 6.1

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction Required (UI:R)
Scope Changed (S:C)
Confidentiality Low (C:L)
Integrity Low (I:L)
Availability None (A:N)

How to Fix GHSA-97jj-33gv-5xf9

To remediate this vulnerability, upgrade all affected league/commonmark dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

league/commonmark fixed: 2.10.2
Upgrade league/commonmark to 2.10.2
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for league/commonmark

How to Check if You Are Affected

To determine whether your application is affected by GHSA-97jj-33gv-5xf9, inspect your project dependencies for league/commonmark:

inspect dependencies for league/commonmark

Analyze the terminal output to locate any version of league/commonmark in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 2.10.2), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection'), where the product constructs all or part of an os command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended os command when it is sent to a downstream component. In league/commonmark, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary The `DisallowedRawHtml` extension does not escape a disallowed tag when the tag name is the last thing in the raw HTML. A Markdown line containing just `<script` is emitted unchanged, and the next block can supply its attributes. With the shipped GFM defaults this allows stored XSS by anyone who can post Markdown. ## Details `DisallowedRawHtmlRenderer` escapes tags with this regex: ``` /<(\/?(?:title|textarea|style|xmp|iframe|noembed|noframes|script|plaintext)[\s\/>])/i ``` The trailing character class requires one character after the tag name. The block parser does not: `RegexHelper::PARTIAL_HTMLBLOCKOPEN` accepts end of line after a tag name, so `<script` alone opens an HTML block. Because a rendered `HtmlBlock` has no trailing newline, the regex has nothing to match and the `<` passes through. In the browser the newline is still present, so the tag name terminates there and whatever follows becomes attributes. This is the same filter that GHSA-4v6x-c7xx-hw9f fixed in 2.8.1. That fix widened the character class but still requires one character, so this case was not covered. ## Reproduction Render this with `GithubFlavoredMarkdownConverter` and default settings: ``` <div> <script <span src="/evil.js"> ``` Output: ```html <div> <script <span src="/evil.js"> ``` A browser parses that as `<script src="/evil.js">` with a junk `<span` attribute, and the script runs. `<iframe` with `<span onload="...">` works the same way and does not need a later `</script>` in the page. Control: `<script src="/evil.js"></script>` is correctly escaped to `&lt;script src="/evil.js">&lt;/script>`. ## Affected versions 1.3.0 (when the extension was added) through the current release. The `</style` and mid-line forms are only affected as continuation lines inside an already-open HTML block. ## Preconditions - `html_input` is `allow` (the default) - The `DisallowedRawHtml` extension is active, which the GFM extension enables automatically - Untrusted users can post Markdown Setting `html_input` to `escape` or `strip` fully mitigates this. ## Suggested fix Allow end of string after the tag name: ```php $regex = \sprintf('/<(\/?(?:%s))([\s\/>]|$)/i', \implode('|', \array_map('preg_quote', $tags))); return \preg_replace($regex, '&lt;$1$2', $rendered); ``` This escapes every bypass shape above and leaves `<div>`, `<scripts>` and `<span class="a">` untouched. The existing unit test only covers tag names followed by another character, so a case for a bare tag name should be added.

GHSA-97jj-33gv-5xf9 Affected league/commonmark Versions

1 component(s)
Fixed Versions:
2.10.2
Product / Package Ecosystem Affected Versions Fixed Version
league/commonmark
Packagist
≥ 1.3.0
< 2.10.2
2.10.2

References

Frequently Asked Questions

What is GHSA-97jj-33gv-5xf9?
GHSA-97jj-33gv-5xf9 is a security vulnerability in league/commonmark involving DisallowedRawHtml bypassed when a disallowed tag name ends the raw-HTML literal under CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
Which league/commonmark versions are affected?
Affected versions include releases prior to 2.10.2 across affected packages: league/commonmark.
What version fixes GHSA-97jj-33gv-5xf9?
Upgrading to 2.10.2 (or the latest patched release for your respective branch) resolves the vulnerability.
Is league/commonmark affected?
Yes, league/commonmark is affected in releases prior to 2.10.2. Upgrade your dependency to 2.10.2 or later.
What is the CVSS score of GHSA-97jj-33gv-5xf9?
GHSA-97jj-33gv-5xf9 has a CVSS v3.1 base score of 6.1 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N.
What CWE is associated with GHSA-97jj-33gv-5xf9?
GHSA-97jj-33gv-5xf9 is classified under CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') (The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.).
How can I check Packagist dependencies for GHSA-97jj-33gv-5xf9?
Run the dependency tree command: 'inspect dependencies for league/commonmark'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-97jj-33gv-5xf9",
  "modified" : "2026-09-30T15:36:33Z",
  "published" : "2026-09-30T15:36:33Z",
  "aliases" : [ ],
  "summary" : "league/commonmark: DisallowedRawHtml bypassed when a disallowed tag name ends the raw-HTML literal",
  "details" : "## Summary\n\nThe `DisallowedRawHtml` extension does not escape a disallowed tag when the tag name is the last thing in the raw HTML. A Markdown line containing just `<script` is emitted unchanged, and the next block can supply its attributes. With the shipped GFM defaults this allows stored XSS by anyone who can post Markdown.\n\n## Details\n\n`DisallowedRawHtmlRenderer` escapes tags with this regex:\n\n```\n/<(\\/?(?:title|textarea|style|xmp|iframe|noembed|noframes|script|plaintext)[\\s\\/>])/i\n```\n\nThe trailing character class requires one character after the tag name. The block parser does not: `RegexHelper::PARTIAL_HTMLBLOCKOPEN` accepts end of line after a tag name, so `<script` alone opens an HTML block. Because a rendered `HtmlBlock` has no trailing newline, the regex has nothing to match and the `<` passes through.\n\nIn the browser the newline is still present, so the tag name terminates there and whatever follows becomes attributes.\n\nThis is the same filter that GHSA-4v6x-c7xx-hw9f fixed in 2.8.1. That fix widened the character class but still requires one character, so this case was not covered.\n\n## Reproduction\n\nRender this with `GithubFlavoredMarkdownConverter` and default settings:\n\n```\n<div>\n<script\n\n<span src=\"/evil.js\">\n```\n\nOutput:\n\n```html\n<div>\n<script\n<span src=\"/evil.js\">\n```\n\nA browser parses that as `<script src=\"/evil.js\">` with a junk `<span` attribute, and the script runs. `<iframe` with `<span onload=\"...\">` works the same way and does not need a later `</script>` in the page.\n\nControl: `<script src=\"/evil.js\"></script>` is correctly escaped to `&lt;script src=\"/evil.js\">&lt;/script>`.\n\n## Affected versions\n\n1.3.0 (when the extension was added) through the current release. The `</style` and mid-line forms are only affected as continuation lines inside an already-open HTML block.\n\n## Preconditions\n\n- `html_input` is `allow` (the default)\n- The `DisallowedRawHtml` extension is active, which the GFM extension enables automatically\n- Untrusted users can post Markdown\n\nSetting `html_input` to `escape` or `strip` fully mitigates this.\n\n## Suggested fix\n\nAllow end of string after the tag name:\n\n```php\n$regex = \\sprintf('/<(\\/?(?:%s))([\\s\\/>]|$)/i', \\implode('|', \\array_map('preg_quote', $tags)));\n\nreturn \\preg_replace($regex, '&lt;$1$2', $rendered);\n```\n\nThis escapes every bypass shape above and leaves `<div>`, `<scripts>` and `<span class=\"a\">` untouched. The existing unit test only covers tag names followed by another character, so a case for a bare tag name should be added.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "league/commonmark"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "1.3.0"
      }, {
        "fixed" : "2.10.2"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 2.10.1"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/thephpleague/commonmark/security/advisories/GHSA-97jj-33gv-5xf9"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/thephpleague/commonmark/commit/411afcc2a7402756d96c89af8882c724d12d47ca"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/thephpleague/commonmark"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/thephpleague/commonmark/releases/tag/2.10.2"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-78", "CWE-79" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-09-30T15:36:33Z",
    "nvd_published_at" : null
  }
}