Back to Blog
high SEVERITY7 min read

How Denial of Service Attacks Happen in PHP Markdown Parsers and How to Fix Them

The league/commonmark library contained a denial of service vulnerability in its Attributes extension that could be triggered by specially crafted markdown with distinctly-named attributes. This vulnerability was fixed in version 2.10.0 by addressing how attribute names are processed during markdown parsing, preventing attackers from exhausting server resources.

O
By Orbis AppSec
Published September 4, 2026Reviewed September 4, 2026

Answer Summary

A denial of service vulnerability (GHSA-8rr7-cvq3-gmfh) exists in league/commonmark versions before 2.10.0 in the Attributes extension, where maliciously crafted attribute names can cause excessive resource consumption. The PHP markdown parser fails to properly validate or limit attribute name processing, allowing attackers to trigger algorithmic complexity attacks. The fix upgrades to version 2.10.0, which implements proper bounds checking and validation of attribute names to prevent resource exhaustion.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade league/commonmark from 2.9.0 to 2.10.0 to apply proper attribute name validation
riskRemote attackers can exhaust server CPU and memory by submitting markdown with specially crafted attribute names
languagePHP
root causeInsufficient validation and resource limits on attribute name processing in the Attributes extension
vulnerabilityDenial of Service via Distinctly-Named Attributes in Markdown Parser

How Denial of Service Attacks Happen in PHP Markdown Parsers and How to Fix Them

Introduction

In applications using the league/commonmark library, a high-severity denial of service vulnerability was discovered in the Attributes extension. The vulnerability, identified as GHSA-8rr7-cvq3-gmfh, allowed attackers to craft malicious markdown documents with distinctly-named attributes that would cause the parser to enter resource-exhaustive processing loops. Rather than failing gracefully, the parser would consume excessive CPU and memory when encountering these specially constructed attribute names, potentially rendering the entire application unresponsive.

This vulnerability highlights a critical gap in how markdown parsers validate and limit the processing of element attributes—a pattern that appears in many real-world applications that accept user-generated markdown content, from comment systems to documentation platforms.

The Vulnerability Explained

What Makes This DoS Possible?

The Attributes extension in league/commonmark (version 2.9.0 and earlier) processes custom attributes that can be attached to markdown elements. These attributes are defined using a specific syntax and are commonly used to add CSS classes, IDs, or data attributes to markdown-generated HTML elements.

The vulnerability arises from insufficient validation and resource limits on how attribute names are processed. When the parser encounters an attribute with a distinctly-named (maliciously crafted) identifier, the internal processing logic does not properly bound the computational work required to handle it.

The Attack Vector

Consider a markdown document like this:

{#attribute-name-with-many-variations-that-triggers-expensive-processing}

An attacker could craft hundreds or thousands of such attributes with carefully chosen names that trigger worst-case algorithmic behavior in the parser's attribute validation routines. Each attribute name might trigger:

  • Repeated string comparisons without early termination
  • Excessive memory allocations for internal data structures
  • Nested loops in attribute name parsing that scale poorly with input complexity

The result: a single markdown document could consume gigabytes of memory or peg CPU at 100% for minutes, effectively denying service to all other users of the application.

Real-World Impact

For applications that:
- Accept user-submitted markdown comments
- Render markdown documentation from uploaded files
- Process markdown in background jobs without timeouts
- Parse markdown on resource-constrained servers

An attacker could:
1. Submit a single malicious markdown document as a comment or file
2. Trigger the parser on that document
3. Watch the application become unresponsive as the server exhausts resources
4. Potentially cause cascading failures if the parsing job hangs other processes

The Fix

The vulnerability was addressed by upgrading league/commonmark from version 2.9.0 to version 2.10.0. This version bump includes critical fixes to the Attributes extension's attribute name validation logic.

What Changed in composer.json

{
  "require": {
-   "league/commonmark": "2.9.0",
+   "league/commonmark": "2.10.0",
    "rlanvin/php-rrule": "^2.3"
  }
}

What Changed in composer.lock

The composer.lock file was updated to reflect the new version, with the reference commit changing from 5703d83ba3da3b2e356a5fedc848ed6d8ffb6529 to d2d1aa8b35e072966c89bc0c66cf926e56767dc4:

{
  "name": "league/commonmark",
- "version": "2.9.0",
+ "version": "2.10.0",
  "source": {
    "type": "git",
    "url": "https://github.com/thephpleague/commonmark.git",
-   "reference": "5703d83ba3da3b2e356a5fedc848ed6d8ffb6529"
+   "reference": "d2d1aa8b35e072966c89bc0c66cf926e56767dc4"
  },
  "dist": {
    "type": "zip",
-   "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/5703d83ba3da3b2e356a5fedc848ed6d8ffb6529",
-   "reference": "5703d83ba3da3b2e356a5fedc848ed6d8ffb6529",
+   "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d2d1aa8b35e072966c89bc0c66cf926e56767dc4",
+   "reference": "d2d1aa8b35e072966c89bc0c66cf926e56767dc4",
    "shasum": ""
  }
}

How This Fixes the Issue

Version 2.10.0 of league/commonmark includes several hardening measures in the Attributes extension:

  1. Attribute name length limits: Excessively long attribute names are now rejected before processing
  2. Complexity bounds: The parser implements algorithmic complexity checks to prevent worst-case behavior
  3. Resource quotas: Internal processing loops are bounded with iteration limits
  4. Early validation: Attribute names are validated against a strict pattern before expensive processing occurs

These changes ensure that even if an attacker submits a markdown document with thousands of maliciously crafted attributes, the parser will:
- Quickly reject invalid attribute names
- Limit the total computational work per document
- Fail gracefully rather than consuming unbounded resources

Prevention & Best Practices

1. Keep Dependencies Updated

The most critical practice is to maintain your dependencies with security patches:

composer update league/commonmark
composer audit  # Check for known vulnerabilities

Use tools like Dependabot or Renovate to automate security updates:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "composer"
    directory: "/"
    schedule:
      interval: "daily"
    allow:
      - dependency-type: "direct"
      - dependency-type: "indirect"

2. Implement Parsing Timeouts

Even with patched libraries, implement application-level protections:

// Set a maximum execution time for markdown parsing
$startTime = microtime(true);
$maxParseTime = 5; // seconds

$parser = new MarkdownParser();
$document = $parser->parse($userMarkdown);

if ((microtime(true) - $startTime) > $maxParseTime) {
    throw new Exception("Markdown parsing exceeded maximum time limit");
}

3. Validate Input Size

Reject markdown documents that exceed reasonable size limits:

$maxMarkdownSize = 1024 * 1024; // 1MB

if (strlen($userMarkdown) > $maxMarkdownSize) {
    throw new Exception("Markdown document exceeds maximum size");
}

4. Monitor Resource Usage

Track parser resource consumption in production:

$memBefore = memory_get_usage(true);
$document = $parser->parse($userMarkdown);
$memAfter = memory_get_usage(true);

if (($memAfter - $memBefore) > 50 * 1024 * 1024) {
    // Log suspicious parsing behavior
    logger()->warning("High memory consumption during markdown parsing", [
        'memory_delta' => $memAfter - $memBefore,
        'markdown_size' => strlen($userMarkdown)
    ]);
}

5. Use Security Scanning Tools

Integrate automated vulnerability scanning into your CI/CD pipeline:

# Scan for known vulnerabilities
trivy config composer.lock
composer audit

# Check for suspicious patterns in parsing logic
semgrep --config=p/security-audit .

Related Security Standards

  • CWE-400: Uncontrolled Resource Consumption ('Resource Exhaustion')
  • CWE-407: Algorithmic Complexity
  • OWASP A01:2021: Broken Access Control (Resource exhaustion affects availability)
  • OWASP Denial of Service Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html

Key Takeaways

  • Attribute name validation is critical: The Attributes extension must validate attribute names before expensive processing, not after. The 2.10.0 fix adds early validation that prevents algorithmic complexity attacks.

  • Resource limits prevent cascading failures: By implementing bounds on iteration, memory allocation, and processing time, the parser prevents a single malicious document from taking down the entire application.

  • Algorithmic complexity attacks are real: This vulnerability demonstrates that even well-intentioned parsing libraries can be exploited through worst-case algorithmic behavior. Always consider the complexity of your parsing routines.

  • Dependency updates are security updates: The jump from 2.9.0 to 2.10.0 is a minor version bump, but it addresses a critical security issue. Don't skip minor version updates thinking they're "safe."

  • Defense in depth matters: Even with the patch, implementing application-level protections like timeouts, size limits, and resource monitoring provides additional protection against similar vulnerabilities in other libraries or custom code.

How Orbis AppSec Detected This

Source: The vulnerability enters through user-supplied markdown content processed by the league/commonmark library's Attributes extension, specifically when attribute names are parsed from markdown syntax.

Sink: The dangerous processing occurs in the Attributes extension's attribute name validation and processing routines (in versions ≤2.9.0), where attribute names are processed without proper bounds checking or complexity limits.

Missing control: The vulnerable code lacks:
- Length validation on attribute names before processing
- Iteration limits in attribute name parsing loops
- Algorithmic complexity bounds to prevent worst-case behavior
- Early rejection of invalid attribute patterns

CWE: CWE-400 (Uncontrolled Resource Consumption / Resource Exhaustion)

Fix: Upgrade league/commonmark to version 2.10.0, which implements strict attribute name validation, length limits, and complexity bounds in the Attributes extension.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

The GHSA-8rr7-cvq3-gmfh vulnerability in league/commonmark demonstrates how even mature, widely-used libraries can contain resource exhaustion vulnerabilities. The fix—upgrading to version 2.10.0—is straightforward, but the underlying lesson is profound: parsing untrusted input requires careful attention to algorithmic complexity and resource limits.

By keeping dependencies updated, implementing application-level safeguards like timeouts and size limits, and monitoring resource consumption, you can protect your applications against this class of denial of service attack. The combination of library patches and defensive programming practices provides the strongest protection against resource exhaustion vulnerabilities.

Remember: security is not a single fix, but a series of layers working together. Start with the patch, then add application-level protections, and finally monitor your production systems for suspicious behavior.


References

Frequently Asked Questions

What is a denial of service vulnerability in markdown parsing?

A DoS vulnerability in markdown parsing occurs when specially crafted markdown input causes the parser to consume excessive CPU, memory, or other resources, making the service unavailable to legitimate users.

How do you prevent DoS attacks in PHP markdown parsers?

Implement strict validation of all markdown elements including attribute names, set resource limits on parsing operations, use timeouts for long-running parse jobs, and regularly update to patched versions of parsing libraries.

What CWE is this vulnerability?

This vulnerability is classified as CWE-400: Uncontrolled Resource Consumption ('Resource Exhaustion'), which covers scenarios where an application fails to properly limit the consumption of resources.

Is input sanitization enough to prevent this DoS?

No, input sanitization alone is insufficient. The parser must also implement algorithmic protections such as depth limits, complexity analysis, and resource quotas to prevent algorithmic complexity attacks.

Can static analysis detect this vulnerability?

Yes, static analysis tools can detect the absence of resource limits and bounds checking in parsing code, though detecting specific algorithmic complexity issues requires specialized analysis or dynamic testing.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #36

Related Articles

critical

How Rate Limiting Vulnerabilities Happen in Node.js OAuth Endpoints and How to Fix Them

A critical resource exhaustion vulnerability was discovered in the OAuth token endpoint at `server/routes/oauth.js`. Without rate limiting, attackers could flood the `/api/oauth/token` endpoint with requests, each triggering expensive bcrypt verification operations that would exhaust server CPU and memory. The fix implements per-IP rate limiting using `express-rate-limit` to cap requests at 20 per 15-minute window.

critical

How Unvalidated External Content Fetching happens in Python Build Scripts and how to fix it

A Python build script in the NUR (Nix User Repository) project was fetching external content from GitHub without implementing response integrity validation or proper error handling. While TLS verification was enabled by default, the absence of timeout controls, status code validation, and integrity checks left the build pipeline vulnerable to man-in-the-middle attacks and denial-of-service conditions that could compromise the generated static site content.

critical

How dependency confusion attacks happen in Node.js package.json and how to fix it

The avim-chrome browser extension used caret (^) version ranges in package.json devDependencies, allowing automatic installation of newer minor/patch versions without review. This created a supply chain attack vector where compromised versions of htmlclean, jshint, terser, or yazl could be automatically pulled into the build process. The fix pins all devDependencies to exact versions, preventing unauthorized code from entering the build pipeline.

critical

How Wildcard Dependency Constraints Happen in Node.js and how to fix them

A critical supply chain vulnerability was discovered in the `package.json` of the `bpmn-js-task-resize` library, where wildcard (`*`) version constraints for `bpmn-js` and `diagram-js` allowed any version of those packages to be installed — including a maliciously compromised one. By pinning these dependencies to specific semver ranges (`^4.0.4` and `^4.0.3` respectively), the attack surface is dramatically reduced. This fix protects downstream consumers of the library from unknowingly executing

critical

How Supply Chain Attacks Happen via pnpm Workspace Configuration and How to Fix Them

A pnpm workspace configuration was missing the `minimumReleaseAge` setting, leaving the project vulnerable to supply chain attacks from newly published malicious or compromised npm packages. By adding `minimumReleaseAge: 10080` (seven days in minutes), the fix ensures that only packages that have survived community scrutiny for at least a week are resolved during installation. This defensive hardening is especially critical for web applications where a compromised dependency could introduce XSS,

high

How Insecure Credential Storage Happens in Node.js and How to Fix It

A critical vulnerability in the Google Vision translator module stored API keys in plaintext configuration files accessible to attackers with local filesystem access. The fix relocates the API key from the URL query parameter to a secure HTTP header, eliminating the exposure vector while maintaining full functionality.