Security Guide
MCP server CSS abs() and sign() consent security — sign() zero collapse at mousedown, negative-sign dimension clamp to zero, abs() obfuscated dimension, custom property sign-chain
CSS abs() and sign() (Chrome 116+, Firefox 118+) are CSS math functions in the CSS Values Level 4 specification. sign(x) returns -1, 0, or 1 based on the sign of its argument. abs(x) returns the absolute value, always non-negative. When used inside calc() for element dimensions, these functions produce consent-element widths and heights that collapse to zero when a JavaScript event changes a custom property from a positive value to zero or negative — without any explicit zero appearing in the CSS, and with no transition or animation property involved.
How CSS sign() and abs() create consent dimension attacks
The CSS spec requires that dimension values — width, height, max-height — are clamped to a minimum of zero. A calc() expression that produces a negative number is clamped to 0px. The sign() function exploits this clamping rule: width: calc(sign(var(--sv, 1)) * 100px) evaluates to 100px when --sv is positive, to 0px when --sv is zero (sign(0) = 0), and to -100px → clamped to 0px when --sv is negative.
An MCP server that adds a mousedown event listener on the install button — with capture: true to fire before any other handlers — can set --sv to 0 or -1 at the exact moment the install is committed. The consent wrapper collapses instantly, with no transition and no animation. After the click, --sv is restored to 1, consent reappears, and no audit of the post-install page state reveals anything suspicious.
The abs() function enables obfuscation of the same attack: width: calc(abs(var(--coord, 100px)) - abs(var(--coord, 100px)) + abs(var(--offset, 100px))) looks like coordinate geometry but simplifies to abs(--offset). When --offset is negative, abs(--offset) is still positive — this removes the clamp-to-zero behavior. But abs(var(--offset, 100px)) + var(--flip, 0px) where --flip is set to a large negative value at mousedown still collapses to zero via the clamp.
Attack 1: sign() zero collapse at install mousedown (SA-CSS-AS-001)
The cleanest attack uses a single custom property that starts positive and is set to zero at mousedown. sign(0) = 0, multiplied by any dimension, produces zero. The consent wrapper's overflow: hidden ensures zero width means zero visible content.
/* CSS: consent width is 100px when --sv = 1, collapses when --sv = 0 */
.consent-wrapper {
width: calc(sign(var(--sv, 1)) * 100px);
overflow: hidden;
white-space: nowrap;
}
/*
* --sv default: 1 → sign(1) = 1 → width = 100px (full consent visible)
* At mousedown: --sv set to 0 → sign(0) = 0 → width = 0px (consent collapsed)
* After click: --sv restored to 1 → width = 100px (consent reappears)
*
* No transition declared on width.
* No animation declared on width.
* No explicit width:0 anywhere in the stylesheet.
* getComputedStyle(el).width at page load → "100px"
* getComputedStyle(el).width at mousedown → "0px"
* Only interaction simulation catches the collapse.
*/
// Attack JS: set --sv to 0 at mousedown (capture), restore at click
document.querySelector('#install-btn').addEventListener('mousedown', () => {
document.documentElement.style.setProperty('--sv', '0');
// Restore after click
document.querySelector('#install-btn').addEventListener('click', () => {
document.documentElement.style.setProperty('--sv', '1');
}, { once: true });
}, { capture: true });
/*
* sign(0) = 0 → calc(0 * 100px) = 0px → width collapsed
*
* Static CSS audit: sees calc(sign(var(--sv, 1)) * 100px)
* At default --sv=1: sign(1)=1, width=100px → PASS
* Does not evaluate what happens when --sv=0 or --sv=-1
*
* SkillAudit behavioral detection: synthetic mousedown captures
* --sv change to 0 → re-measures width → 0px → CRITICAL finding
*/
sign(0) = 0 is a guaranteed zero with no explicit zero in source: Setting --sv to 0 rather than a negative value avoids even the clamp-to-zero step — sign(0) returns exactly 0 directly. The CSS source contains no literal 0px or 0 dimension value anywhere. The only 0 in the codebase is the custom property value assigned in JavaScript at mousedown — typically obfuscated as a computed arithmetic result or passed through another variable.
Attack 2: Negative-sign dimension clamp — sign(-1) = -1 times width clamped to zero (SA-CSS-AS-002)
Rather than passing zero, this attack passes a negative value to produce sign(-1) = -1, multiplied by the dimension producing a negative value that CSS clamps to zero. The JavaScript uses a computed arithmetic result that appears semantically meaningful — a "direction indicator" or "polarity" — making it harder for code review to identify as a consent attack.
/* CSS: uses --polarity custom property — appears to be a layout direction flag */
.consent-panel {
/* --polarity: 1 = normal, -1 = reversed (or so the code implies) */
width: calc(sign(var(--polarity, 1)) * 100%);
overflow: hidden;
}
/*
* --polarity = 1: sign(1) = 1 → width = 100% (full width, consent visible)
* --polarity = -1: sign(-1) = -1 → width = -100% → clamped to 0 (consent hidden)
*
* The variable name '--polarity' and its usage as a layout-direction flag
* provides plausible deniability for the property name.
* Code reviewers may accept 'polarity' as a legitimate directional parameter.
*/
// Attack JS: "flip polarity" at install mousedown
const btn = document.querySelector('#install-btn');
btn.addEventListener('mousedown', () => {
// "Flip to install mode" — changes polarity direction
document.documentElement.style.setProperty('--polarity', '-1');
btn.addEventListener('click', () => {
// "Return to normal mode" — restores polarity
document.documentElement.style.setProperty('--polarity', '1');
}, { once: true });
}, { capture: true });
/*
* --polarity → -1 → sign(-1) = -1 → width = -100% → clamp → 0 → hidden
*
* Code review sees: "flip polarity to install mode" — appears to be a UI
* state flag for an installation flow, not a consent-hiding attack.
*
* Only analysis of the CSS sign() expression combined with the JS property
* change reveals that 'install mode polarity = -1' means 'consent width = 0'.
*/
Attack 3: abs() obfuscated dimension — consent width expressed as absolute value of a signed coordinate (SA-CSS-AS-003)
This attack uses abs() to express a consent dimension as the absolute value of a variable that appears to be a coordinate or offset. Normally, abs() would prevent a zero-dimension attack — the absolute value of any non-zero number is positive. The attack uses abs() on an expression that evaluates to zero: abs(var(--cx) - var(--cx-offset)) where both variables are driven to the same value at mousedown, producing abs(N - N) = abs(0) = 0.
/* CSS: consent width expressed as distance between two coordinate variables */
.consent-box {
/*
* Width is the horizontal distance between --cx (cursor x) and --cx-offset
* "Appears to be a responsive layout that tracks cursor position"
*/
width: calc(abs(var(--cx, 200px) - var(--cx-offset, 100px)));
overflow: hidden;
/* Default: abs(200px - 100px) = abs(100px) = 100px → consent visible */
}
/*
* When --cx == --cx-offset: abs(N - N) = abs(0) = 0 → consent hidden
* This attack drives both variables to the same value at install time.
*/
// Attack JS: drive --cx and --cx-offset to the same value at mousedown
document.querySelector('#install-btn').addEventListener('mousedown', () => {
// "Sync cursor position to viewport center" — appears to be UI alignment
const centerX = window.innerWidth / 2;
document.documentElement.style.setProperty('--cx', centerX + 'px');
document.documentElement.style.setProperty('--cx-offset', centerX + 'px');
// abs(centerX - centerX) = abs(0) = 0 → width = 0 → consent hidden
document.querySelector('#install-btn').addEventListener('click', () => {
// Restore default values
document.documentElement.style.setProperty('--cx', '200px');
document.documentElement.style.setProperty('--cx-offset', '100px');
}, { once: true });
}, { capture: true });
/*
* The variable names --cx and --cx-offset suggest cursor coordinate tracking.
* The JS comment "sync cursor position to viewport center" provides cover.
* abs() in the CSS expression initially appears to prevent zero-width attacks.
* Only analysis of the abs() argument expression reveals the zero-distance trap.
*
* abs() prevents negative-dimension attacks, but does not prevent zero-dimension attacks.
* If the expression inside abs() evaluates to zero, abs(0) = 0.
*/
abs() does not prevent zero-width attacks: The abs() function prevents negative values from being clamped to zero by returning a positive value. But it cannot prevent the argument expression from evaluating to zero itself. Any abs(expr) where expr can be driven to 0 at install time produces a zero dimension. Auditors that see abs() and conclude "this is safe from zero-dimension attacks" are incorrect.
Attack 4: Custom property sign-chain — chained sign() calls across multiple variables (SA-CSS-AS-004)
This attack obscures the sign-based collapse by chaining sign() calls across multiple custom properties, each appearing independently benign. The final expression only collapses to zero when all properties are simultaneously set to the attack state by JavaScript. An auditor evaluating each property in isolation sees only normal-range values.
/* CSS: chained sign() across three custom properties */
.consent-text-wrapper {
/*
* --phase-a, --phase-b, --phase-c are initialized to positive values.
* Width = sign(--phase-a) * sign(--phase-b) * sign(--phase-c) * 100px
*
* All positive: 1 * 1 * 1 * 100px = 100px
* One zero: 1 * 0 * 1 * 100px = 0px
* Any negative: clamp(-1 * 1 * 1 * 100px) = clamp(-100px) = 0px
*/
width: calc(sign(var(--phase-a, 1)) * sign(var(--phase-b, 1)) * sign(var(--phase-c, 1)) * 100px);
overflow: hidden;
}
/*
* An auditor checking each custom property default:
* --phase-a = 1: sign(1) = 1 → intermediate = 1 → PASS (small positive)
* --phase-b = 1: sign(1) = 1 → intermediate = 1 → PASS
* --phase-c = 1: sign(1) = 1 → final = 100px → PASS
*
* The collapse requires ALL THREE to be driven to specific sign values simultaneously.
* The JavaScript may set only one property to 0 at mousedown (e.g., --phase-b = 0)
* and the other two are set by normal UI interaction logic.
*/
// Attack JS: set --phase-b to 0 at mousedown (phase-a and phase-c set by normal UI)
// --phase-a is set to 1 by the accordion open animation
// --phase-c is set to 1 by the install step counter
// Only --phase-b is directly controlled by the attack
document.querySelector('#install-btn').addEventListener('mousedown', () => {
document.documentElement.style.setProperty('--phase-b', '0');
// sign(1) * sign(0) * sign(1) * 100px = 1 * 0 * 1 * 100px = 0px
document.querySelector('#install-btn').addEventListener('click', () => {
document.documentElement.style.setProperty('--phase-b', '1');
}, { once: true });
}, { capture: true });
/*
* Sign-chain obfuscation makes the collapse harder to identify in code review:
* - --phase-a, --phase-c appear to be legitimate UI state variables
* - Only --phase-b is directly attack-controlled
* - The CSS expression looks like a three-factor layout calculation
* - Static analysis must trace all three variables through the sign() chain
* to identify the zero-product collapse scenario.
*/
Detection: SkillAudit's static analysis flags any calc() expression containing sign() or abs() used in dimension properties on consent elements. For each such expression, the analysis evaluates the expression at: (1) all default custom property values, (2) with each custom property set to 0, and (3) with each custom property set to -1. Any combination that produces a dimension of 0 on a consent element is flagged for behavioral confirmation. Phase 2 interaction simulation then verifies whether JavaScript changes these properties at install mousedown.
Why abs() and sign() attacks evade conventional auditors
CSS consent auditors that look for known-bad dimension values (height: 0, width: 0px, max-height: 0) will not find sign() or abs() attacks. The declared dimension is a calc() expression that evaluates to a positive value at page load. No transition or animation is declared on the dimension property. The computed style at page load shows the correct full-size value.
Auditors that check for suspicious calc() expressions may flag sign() and abs() as unusual, but without evaluating all possible custom property states that trigger the collapse, cannot determine whether the expression is genuinely a consent bypass or a legitimate responsive layout calculation.
Behavioral simulation is the reliable detection path: change each dimension-expression custom property to zero and negative values via JavaScript, then re-measure computed dimensions after each change. Any change that produces a zero dimension on a consent element is a confirmed consent-collapse trigger.
Findings summary
sign() zero collapse at install mousedown — --sv set to 0 drives sign(0)=0, width collapses to 0px; no transition, no animation, no explicit zero in CSS source; only behavioral interaction simulation detects the collapse.--polarity set to -1 at mousedown, sign(-1)=-1, width=-100% clamped to 0; variable name and JS comment provide plausible deniability as a UI direction flag; code review unlikely to identify.abs() obfuscated zero — two coordinate variables driven to the same value at mousedown: abs(N-N)=abs(0)=0; abs() presence gives false impression of safety against zero-dimension attacks; auditors may skip further analysis.sign() product; one JS-controlled variable set to 0, other two set by normal UI state; requires tracing all three variables through the sign-chain product to identify zero-product collapse scenario.Summary table
| Attack | Severity | Function | Browser support | Detection |
|---|---|---|---|---|
SA-CSS-AS-001: sign() zero collapse |
Critical | sign(0) = 0 — custom property set to 0 at mousedown |
Chrome 116+, Firefox 118+ | Evaluate sign() expression at 0 and negative values; interaction simulation |
| SA-CSS-AS-002: Negative-sign dimension clamp | High | sign(-1) = -1 × dimension → negative → clamped to 0 |
Chrome 116+, Firefox 118+ | Evaluate sign() expression at negative values; check if collapse is possible |
SA-CSS-AS-003: abs() obfuscated zero |
High | abs(N - N) = 0 — two variables driven to same value |
Chrome 116+, Firefox 118+ | Evaluate abs() argument expression; test all zero-producing variable combinations |
| SA-CSS-AS-004: Sign-chain product collapse | Medium | Three-factor sign() product: any zero factor produces 0 |
Chrome 116+, Firefox 118+ | Trace all variables in sign-chain product; evaluate zero-product combinations |
Defences and detection recommendations
Flag sign() and abs() in dimension calc() expressions on consent elements: Any calc() expression containing sign() or abs() in a dimension property (width, height, max-height, max-width) on a consent-labelled element should be flagged for deeper analysis. Evaluate the expression at default custom property values, at zero, and at negative values for each custom property in the expression.
Multi-state custom property evaluation: For each custom property used in a dimension expression on a consent element, programmatically set it to: (1) its default value, (2) 0, (3) -1, (4) a large negative value. Re-measure the computed dimension after each change. Any configuration that produces a near-zero dimension should be flagged.
Phase 2 interaction simulation: Dispatch a synthetic mousedown event with capture: true on the install button. Monitor for setProperty calls that change custom property values during the event handler. After the synthetic mousedown, re-measure all consent element dimensions. Any consent element that collapses to near-zero width or height during the synthetic install interaction should be flagged as critical.
abs() is not a safe-harbor: Do not skip analysis of calc() expressions because they contain abs(). While abs() prevents dimension collapse via sign flipping, it does not prevent collapse when the expression inside abs() evaluates to zero.