MCP Security Reference

MCP server CSS font-style: oblique <angle> security

CSS Fonts Level 4 extended font-style with an optional slant angle parameter: font-style: oblique <angle> where angle is any value from −90deg to 90deg. A plain italic or oblique keyword typically applies a 14–20 degree slant — the conventional italic appearance. Values above 45 degrees produce extreme forward slant; values near 80–89 degrees shear glyphs until they are nearly horizontal lines, indistinguishable from noise or decorative rules to a typical reader. The key evasion: font-style: oblique 80deg is completely distinct from transform: skewX() — a detector checking the transform property finds no transformation. Detection requires parsing the getComputedStyle(el).fontStyle string for the oblique keyword followed by an angle component exceeding 45 degrees.

Attack findings

HIGHSA-CSS-FOBL-001 — font-style: oblique 80deg on consent text; synthesized glyph shear applied by browser (no italic font file required); glyphs rendered as near-horizontal strokes; text unreadable at normal viewing distance; getComputedStyle.fontStyle returns 'oblique 80deg'; transform property is 'none'
HIGHSA-CSS-FOBL-002 — font-style: oblique -80deg (backward extreme slant); glyphs lean sharply backward, opposite direction from standard italic; unusual appearance but equally illegible; both ±80deg variants must be caught; abs(angle) check required
HIGHSA-CSS-FOBL-003 — font-style: oblique 45deg + font-weight:200 compound attack; oblique 45deg is moderately slanted; weight 200 reduces stroke prominence; compound rendering: slanted hairlines against white background; each property individually within "normal-looking" range; combined legibility score reveals attack
MEDIUMSA-CSS-FOBL-004 — JS mousedown sets font-style: oblique 89deg + CSS transition 200ms; consent text shears to horizontal in one smooth animation during the install click gesture; static check at page load shows font-style: normal — transform property unchanged throughout

Background: CSS Fonts Level 4 oblique angle parameter

The CSS font-style property historically accepted three keywords: normal, italic, and oblique. CSS Fonts Level 4 (published 2021, widely supported from 2022 in Chrome 88+, Firefox 85+, Safari 14.1+) added an angle parameter to oblique: font-style: oblique <angle>. This angle specifies the desired slant of the text. If the loaded font has a dedicated italic or oblique face, the browser selects the face whose slant best matches the requested angle. If no matching face exists, the browser synthesizes the oblique by applying a shear transformation to the upright glyphs — the same mathematical operation as transform: skewX() but applied at the character-rendering level, invisible to the CSS transform API.

A conventional italic runs at 14 degrees. An extreme oblique of 80 degrees applies a shear factor of tan(80°) ≈ 5.67 to every glyph. A 14px-tall letter 'l' would have its top shifted 5.67 × 14 = 79px to the right of its base. The glyph effectively becomes a diagonal line, not a readable character. The synthesized shear is applied inside the font rasterizer — the computed bounding box of the element (getBoundingClientRect) does not expand to accommodate this overflow; glyphs may clip at the line box edges or overflow visually while the logical box remains unchanged.

Detection gap: getComputedStyle(el).transform returns 'none' — no CSS transform is applied. textContent, color, opacity, and visibility all return normal values. getComputedStyle(el).fontStyle returns the string 'oblique 80deg'. A detector that checks fontStyle === 'italic' or fontStyle === 'normal' (binary check) does not examine the angle component. Detection requires parsing the fontStyle string: split on space, detect the oblique keyword, and parse the degree value as a float.

Attack 1 — oblique 80deg forward extreme shear (SA-CSS-FOBL-001)

The consent element receives font-style: oblique 80deg. The browser, finding no italic or oblique face in the font stack matching 80 degrees, synthesizes the oblique by shearing each glyph with a matrix equivalent to skewX(-80deg) applied per-glyph during rasterization. At 80 degrees, the shear factor is tan(80°) ≈ 5.67 — every vertical stroke in the font is extended by 5.67 times the glyph height in the horizontal direction. The character 'B' at 14px normally about 9px wide becomes visually about 9 + (5.67 × 14) ≈ 88px wide but is clipped to the line box. The rendered glyph appears as an extremely thin diagonal smear. Multiple characters in a line overlap their sheared forms, producing an undifferentiated diagonal texture pattern. The consent text is functionally illegible.

/* Attack: extreme oblique forward slant */
.consent-text {
  font-style: oblique 80deg;  /* extreme forward shear */
  font-size: 14px;            /* passes size check */
  color: #1a1a1a;             /* passes color check */
  opacity: 1;                 /* passes opacity check */
  transform: none;            /* transform check clean */
}

SA-CSS-FOBL-001 (High). Detection: parse getComputedStyle(el).fontStyle — if it starts with 'oblique', extract the angle component and flag if Math.abs(angle) > 45. The string format is always 'oblique Ndeg' (degrees unit) in resolved values. Angles above 45 degrees produce shear factors above tan(45°) = 1.0, making the shear horizontal-offset exceed the glyph height — highly suspicious on consent text.

/* Detection */
function checkObliqueAngle(consentEl) {
  const fs = getComputedStyle(consentEl).fontStyle;
  if (!fs.startsWith('oblique')) return null;
  const parts = fs.split(/\s+/);
  if (parts.length < 2) return null;
  const angleStr = parts[1];
  const angle = parseFloat(angleStr);
  if (isNaN(angle)) return null;
  if (Math.abs(angle) > 45) {
    return {
      vuln: 'SA-CSS-FOBL-001',
      detail: `font-style oblique ${angle}deg — shear factor ${Math.abs(Math.tan(angle * Math.PI / 180)).toFixed(2)} — glyphs illegibly distorted`
    };
  }
  return null;
}

Attack 2 — oblique -80deg backward extreme slant (SA-CSS-FOBL-002)

The backward slant variant uses font-style: oblique -80deg. CSS Fonts Level 4 allows negative angles down to −90 degrees, producing a shear in the opposite direction — the top of each glyph is displaced to the left rather than the right. A conventional backward lean (negative oblique) is unusual but not unheard of in decorative fonts. At −80 degrees, the effect is identical in illegibility to +80 degrees — just mirrored. A detector checking only for positive angles (e.g., angle > 45) misses the negative variant. The Math.abs(angle) > 45 check catches both. The rendered result looks like a backward-slanting version of the +80deg case — diagonals going up-left instead of up-right — equally unreadable.

/* Attack: extreme oblique backward slant */
.consent-text {
  font-style: oblique -80deg;  /* backward extreme shear */
  /* getComputedStyle.fontStyle returns 'oblique -80deg' */
}

/* Detection: abs() handles both positive and negative extreme angles */
function checkObliqueAngleBothDirections(consentEl) {
  const fs = getComputedStyle(consentEl).fontStyle;
  if (!fs.startsWith('oblique')) return null;
  const angle = parseFloat(fs.replace('oblique', '').trim());
  if (Math.abs(angle) > 45) {
    return {
      vuln: 'SA-CSS-FOBL-002',
      detail: `oblique ${angle}deg (${angle < 0 ? 'backward' : 'forward'} extreme slant)`
    };
  }
  return null;
}

Attack 3 — compound oblique + low-weight attack (SA-CSS-FOBL-003)

An oblique angle of 45 degrees sits at the boundary of moderate and extreme. Many developers and automated tools might accept 45 degrees as "unusual but plausible design choice." Combined with font-weight: 200 (thin weight), the 45-degree slant creates diagonal hairlines — the shear factor is 1.0 (tan 45°), meaning the top of each glyph is displaced sideways by exactly the glyph height. Thin strokes at weight 200 add to the illegibility. The compound renders as a sparse diagonal grid of thin lines. Neither 45 degrees nor weight 200 alone necessarily crosses a simple threshold (45 is not > 45; weight 200 is not < 100). The compound legibility score below catches the combination.

/* Attack: oblique 45deg + font-weight:200 */
.consent-text {
  font-style: oblique 45deg;  /* moderate but above typical italic range */
  font-weight: 200;           /* thin but not sub-100 */
}

/* Detection: compound oblique + weight legibility score */
function checkCompoundObliqueWeight(consentEl) {
  const cs = getComputedStyle(consentEl);
  const fs = cs.fontStyle;
  const fw = parseInt(cs.fontWeight, 10);
  if (!fs.startsWith('oblique')) return null;
  const angle = Math.abs(parseFloat(fs.replace('oblique', '').trim()));
  const angleFactor = angle / 90;    /* 0=normal, 1=fully horizontal */
  const weightFactor = fw / 400;     /* 1=normal, 0.25=weight-100 */
  const score = (1 - angleFactor) * weightFactor;
  if (score < 0.4 && angle > 30) {
    return {
      vuln: 'SA-CSS-FOBL-003',
      detail: `compound oblique ${angle}deg + font-weight:${fw} — legibility score ${score.toFixed(2)}`
    };
  }
  return null;
}

Attack 4 — JS mousedown oblique injection (SA-CSS-FOBL-004)

At page load the consent element has font-style: normal. A CSS transition on font-style is declared in the stylesheet (legal to transition oblique angles between values). At mousedown, JS sets font-style: oblique 89deg. Over 200ms the font slant transitions from 0 to 89 degrees — the maximum extreme. The transition fires during the click gesture. Because font-style transitions are interpolatable for oblique angle values in CSS Fonts Level 4, the browser smoothly animates the shear angle. A static audit at page load finds font-style: normal — no oblique angle in sight. The transition property includes font-style which could look like an innocent UI animation (italic hover effects are common).

/* Attack: runtime oblique angle injection with transition */
/* In stylesheet: */
.consent-text {
  font-style: normal;
  transition: font-style 200ms ease;  /* looks like italic hover prep */
}

/* At mousedown: */
installBtn.addEventListener('mousedown', () => {
  consentEl.style.fontStyle = 'oblique 89deg';
  /* transition fires: 0deg → 89deg over 200ms */
  /* consent text shears to near-horizontal during click */
});

/* Detection: check font-style in transition-property + MutationObserver */
function checkFontStyleTransition(consentEl) {
  const tp = getComputedStyle(consentEl).transitionProperty;
  if (tp.includes('font-style') || tp === 'all') {
    return { vuln: 'SA-CSS-FOBL-004', detail: 'font-style in transition-property — oblique angle can be animated at mousedown' };
  }
  return null;
}

new MutationObserver(() => {
  const finding = checkObliqueAngle(consentEl);
  if (finding) { flagTampering('SA-CSS-FOBL-004'); installBtn.disabled = true; }
}).observe(consentEl, { attributes: true, attributeFilter: ['style'] });

SkillAudit detection: SkillAudit parses getComputedStyle(el).fontStyle for the oblique <angle> form and flags any angle whose absolute value exceeds 45 degrees. It also checks compound oblique+weight scores and monitors for runtime font-style changes during simulated install click via MutationObserver. Run a free audit →

Detection summary

Attack IDProperties involvedKey detection signal
SA-CSS-FOBL-001font-style: oblique 80deg on consent; synthesized shear; no CSS transformparse getComputedStyle fontStyle; extract oblique angle; Math.abs(angle) > 45
SA-CSS-FOBL-002font-style: oblique -80deg; backward extreme shear; equally illegiblesame as FOBL-001 with Math.abs() — catches both positive and negative extremes
SA-CSS-FOBL-003font-style: oblique 45deg + font-weight:200 compound; neither alone crosses simple thresholdcompound score: (1 - angle/90) × (fontWeight/400) < 0.4 with angle > 30
SA-CSS-FOBL-004font-style:normal at load; CSS transition; JS mousedown sets oblique 89degfont-style in transitionProperty AND MutationObserver on consent fontStyle attribute