← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Client-Side Sanitisation

JavaScript ES2015 Intermediate
debt(d5/e3/b5/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). ESLint and Semgrep can detect the common pattern of innerHTML with unsanitized user input, but detection requires configuration and these tools may miss sophisticated bypasses or conditional sanitisation logic. Runtime XSS from misconfigured DOMPurify allowlists or server-side sanitisation gaps remain silent until penetration testing or user-reported attacks.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix states 'Use DOMPurify.sanitize(html) before innerHTML' — a one-line addition to the vulnerable code. However, the full remediation is slightly heavier: configuring DOMPurify's allowlist correctly for your use case, and adding complementary server-side sanitisation with HTMLPurifier, touches multiple layers (client and backend). Scoring e3 for the minimum viable fix; e5 would apply if ensuring comprehensive server-side + client-side coverage.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). Client-side sanitisation is not a load-bearing architectural choice (not b7), but it imposes ongoing cognitive load: every rich-text-input, markdown renderer, and dynamic HTML insertion point requires sanitisation logic; developers must remember to sanitise before innerHTML; misconfiguration (allowlist tuning) is per-component; and the false sense of security from DOMPurify-only (misconception) means architectural decisions about where validation happens are repeated across teams. The burden is distributed, not centralised.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The canonical misconception is that 'Client-side DOMPurify is sufficient XSS protection,' which directly contradicts how server-side sanitisation is supposed to work (server-side is the primary defence, client-side is defence-in-depth). This trap contradicts the principle that security boundaries should not trust the client. Additionally, the common mistake 'sanitising after insertion' is a catastrophic logic error that looks correct but completely fails. Score is t7 rather than t9 because the trap is well-documented in security literature; a competent developer who reads the misconception field will learn the right model, though many in the wild still fall for it.

About DEBT scoring →

Also Known As

DOMPurify Sanitizer API Trusted Types XSS prevention JS

TL;DR

DOMPurify and the Sanitizer API remove dangerous HTML before insertion — complementing PHP's server-side htmlspecialchars for rich-text scenarios.

Explanation

When rich HTML must be rendered (user-formatted posts, markdown output), textContent is too restrictive. DOMPurify.sanitize(html) strips script tags, event handlers, and dangerous attributes while keeping safe formatting. The native Sanitizer API (Chrome 105+) provides built-in sanitisation. Trusted Types (Chrome) enforce that only sanitised values reach innerHTML. Server-side sanitisation (PHP's HTMLPurifier) is the primary defence — client-side sanitisation is defence-in-depth.

Common Misconception

Client-side DOMPurify is sufficient XSS protection — DOMPurify can be bypassed if the attacker controls the execution environment; server-side sanitisation (HTMLPurifier, strip_tags with allowlist) must be the primary defence.

Why It Matters

Rich text editors and markdown renderers must insert HTML — without sanitisation any stored user content becomes a stored XSS attack vector.

Common Mistakes

  • Relying on DOMPurify as the only XSS defence (no server-side sanitisation)
  • Not configuring DOMPurify allowlist for your specific allowed tags
  • Sanitising after insertion — sanitise before innerHTML, not after

Code Examples

✗ Vulnerable
// Raw innerHTML — stored XSS:
el.innerHTML = post.htmlContent; // attacker's script executes
✓ Fixed
// DOMPurify — safe HTML insertion:
import DOMPurify from 'dompurify';

const clean = DOMPurify.sanitize(post.htmlContent, {
    ALLOWED_TAGS: ['b','i','em','strong','a','p','ul','li'],
    ALLOWED_ATTR: ['href', 'target'],
});
el.innerHTML = clean;

// Native Sanitizer API (modern browsers):
const sanitizer = new Sanitizer();
el.setHTML(post.htmlContent, { sanitizer });

Added 17 Mar 2026
Edited 22 Mar 2026
Views 63
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 1 ping W 0 pings T 2 pings F 1 ping S 4 pings S 3 pings M 1 ping T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 1 ping T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
ChatGPT 10 Scrapy 8 Amazonbot 7 Google 5 Ahrefs 4 Perplexity 4 SEMrush 4 Claude 2 Majestic 1 Meta AI 1 Bing 1 PetalBot 1
crawler 43 crawler_json 5
DEV INTEL Tools & Severity
🔴 Critical ⚙ Fix effort: Low
⚡ Quick Fix
Use DOMPurify.sanitize(html) before any innerHTML assignment of user-generated content — and also sanitise server-side with HTMLPurifier
📦 Applies To
javascript ES2015 web
🔗 Prerequisites
🔍 Detection Hints
innerHTML with variable from API response or user input without DOMPurify; markdown rendered HTML inserted without sanitisation
Auto-detectable: ✓ Yes eslint semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Medium ✗ Manual fix Fix: Medium Context: Function Tests: Update
CWE-79 CWE-20


✓ schema.org compliant