What Happened
The marked package, a popular Markdown parser with over 10 million weekly downloads on npm, shipped with its sanitize option turned off by default. This choice exposed applications processing user-generated Markdown to cross-site scripting (XSS) attacks. An attacker could inject malicious JavaScript through crafted Markdown input, which marked would convert to executable HTML without sanitization.
The vulnerability was reported in May 2015, but it took years for vulnerability databases to formally log it, leaving engineering teams without a systematic way to track their exposure.
Timeline
May 2015: Initial vulnerability report filed against marked.
2015-2023: The package remains widely used with the insecure default. No CVE assigned during this period.
Recent: Vulnerability databases catalog the issue. Snyk releases patches. Security teams discover their exposure through dependency scanning.
The eight-year delay between report and formal tracking is significant. During this window, your application could have processed untrusted Markdown with zero protection, and your dependency scanner wouldn't have flagged it.
Which Controls Failed or Were Missing
Secure-by-default configuration: The marked maintainers shipped with sanitize: false as the default. This required developers to actively opt into security, a pattern that fails at scale.
Dependency security monitoring: Most teams lacked visibility into this vulnerability because it wasn't cataloged in standard databases. If you weren't manually auditing every dependency's security posture, you missed it.
Input validation architecture: Applications relying solely on marked for sanitization had no defense-in-depth. When the parser failed to sanitize, nothing caught the malicious payload.
Maintenance status awareness: Teams using marked didn't track whether the project was actively maintained or had a clear security response process. When a vulnerability surfaced, there was no guarantee of a timely fix.
What the Relevant Standards Require
OWASP ASVS v4.0.3 Requirement 5.3.3: "Verify that output encoding is relevant for the interpreter and context required." The marked default violated this by converting Markdown to HTML without context-appropriate encoding.
PCI DSS v4.0.1 Requirement 6.4.3: For public-facing web applications, an automated solution must detect and prevent web-based attacks. If your payment application used marked, this required configuring sanitization or deploying a Web Application Firewall (WAF).
ISO/IEC 27001 Control 8.3: Organizations must ensure security is built into information systems throughout the development lifecycle. Accepting insecure defaults from dependencies contradicts this control.
OWASP Top 10 2021 - A03:2021 Injection: XSS is explicitly called out as an injection vulnerability. The standard recommends using safe APIs that automatically escape untrusted data. The marked default was the opposite of a safe API.
Lessons and Action Items for Your Team
1. Audit every dependency's security-critical defaults
Document every package in your application that touches user input, renders HTML, or handles authentication. For each one, note:
- Security features available
- Which are enabled by default
- Which require explicit configuration
For marked, set sanitize: true immediately, or switch to a maintained alternative like markdown-it with a sanitization plugin.
2. Build a dependency maintenance scorecard
Before adopting any open-source package, check:
- Date of last commit
- Number of open security issues
- Average time to close security issues
- Presence of a SECURITY.md file
If a package hasn't been updated in 12+ months and has open security issues, assume it's unmaintained. Plan to replace it or fork it.
3. Implement defense-in-depth for user-generated content
Don't rely on a single library to sanitize untrusted input. Your architecture should include:
- Input validation at the API boundary
- Sanitization in the rendering library (like
marked) - Content Security Policy (CSP) headers that block inline script execution
- Output encoding appropriate to the context (HTML, JavaScript, URL)
For Markdown, consider using DOMPurify on the HTML output from marked as a second sanitization layer.
4. Make dependency scanning mandatory in CI/CD
Tools like Snyk, Dependabot, or npm audit should run on every pull request. Additionally:
- Set a policy for maximum vulnerability age (e.g., no high/critical findings older than 30 days)
- Require security review for any package with no update in 6+ months
- Track "vulnerability debt" as a metric alongside technical debt
5. Test your sanitization
Write integration tests that attempt XSS injection through every user input field. For Markdown rendering:
// Test case: Does malicious Markdown get sanitized?
const maliciousMarkdown = '[Click me](javascript:alert("XSS"))';
const html = marked(maliciousMarkdown, { sanitize: true });
expect(html).not.toContain('javascript:');
Run these tests in your CI pipeline. If they fail after a dependency update, you've caught a regression before production.
6. Create a "dependency incident response" runbook
When a vulnerability like this surfaces, your team needs a playbook:
- Who assesses impact? (Map the vulnerable package to affected applications)
- What's the SLA for patching? (Based on CVSS score and exploitability)
- How do you communicate risk to stakeholders?
- When do you pull an emergency release vs. wait for the next sprint?
Document this before the next incident. The marked vulnerability sat for eight years partly because teams had no process to respond when it finally surfaced.
The marked XSS vulnerability highlights the need for proactive security measures in open-source software, especially when dealing with user-generated content. Your job is to build systems where the secure path is the default path — and when you can't control the default, you add layers that catch the failure.



