MCP server CSS overflow-clip-margin-inline-start security: asymmetric inline-start clipping, LTR authorization framing removal, RTL end-clipping, and writing-mode axis attacks
Published 2026-09-25 — SkillAudit Research
The CSS overflow-clip-margin-inline-start property is one of the four individual longhands of overflow-clip-margin. It controls the extension of the clip region beyond the element's padding box on the inline-start edge only — in left-to-right documents, the left edge; in right-to-left documents, the right edge. The other three longhands (-inline-end, -block-start, -block-end) control the remaining edges independently.
This per-edge control creates a consent-manipulation attack surface: an MCP server can set overflow-clip-margin-inline-start: 0 while granting a generous margin to all other edges, creating an asymmetric clip that removes only the inline-start side of overflow content. In a standard LTR consent dialog, this clips the beginning of each line — the first words of each sentence, which typically carry the authorization framing ("By proceeding, you authorize…", "You irrevocably grant…", "You waive…").
Asymmetry is the evasion: Auditors checking overflow-clip-margin (the shorthand) look for the entire box being clipped. An audit that checks overflow-clip-margin: 0 reports findings. But when only overflow-clip-margin-inline-start: 0 is set with all other edges positive, the shorthand reads as non-zero and the element appears to have adequate clip margins. The asymmetric longhand is what evades detection. See related: CSS overflow-clip-margin-inline shorthand security.
Attack findings
In a standard LTR consent dialog with
overflow: clip on a narrow container, setting overflow-clip-margin-inline-start: 0 means the clip boundary starts exactly at the element's left padding edge. Content that overflows to the left (text rendered at negative inline offsets due to text-indent, letter-spacing interactions, or parent negative-margin constructions) is clipped at the left edge. More importantly, when text-align: center or text-align: right is applied, the left portion of centered or right-aligned lines is the start of each sentence — and with a zero inline-start margin, any paint outside the left padding edge is clipped. Authorization framing words at the start of lines are removed from the rendered view.
.consent-dialog {
overflow: clip;
width: 320px;
/* Clip margin on all other edges: 40px.
Inline-start edge: 0px. */
overflow-clip-margin-block-start: 40px;
overflow-clip-margin-block-end: 40px;
overflow-clip-margin-inline-end: 40px;
overflow-clip-margin-inline-start: 0; /* start edge: hard clip */
}
/* In LTR: left edge of each consent line clipped at padding boundary.
Any content displaced slightly left (via negative text-indent, kerning,
or inline-start margin on first word) is painted outside the clip box
and invisible. Authorization framing words removed. */
In RTL documents (
direction: rtl), the inline-start direction is right-to-left — the inline-start edge is the physical right edge of the element. Setting overflow-clip-margin-inline-start: 0 in an RTL context clips the right side of content. In RTL text, each line starts on the right — the first words of each sentence (including acceptance clauses: "אני מאשר", "أوافق على") are on the right side of each line. A zero inline-start clip in RTL removes the sentence beginnings for every line, just as it does in LTR. The distinction is that an auditor must resolve the logical-to-physical mapping from direction before interpreting the clip.
.consent-dialog[dir="rtl"] {
direction: rtl;
overflow: clip;
/* In RTL: inline-start is the RIGHT edge */
overflow-clip-margin-inline-start: 0; /* clips RIGHT edge — LTR auditors miss this */
overflow-clip-margin-inline-end: 60px; /* inline-end is LEFT in RTL — generous */
}
/* User reads right to left. First words of each line (on right side)
are in the zero-margin clip zone. Acceptance framing clipped. */
In
writing-mode: vertical-rl or writing-mode: vertical-lr, the inline axis rotates 90 degrees. The inline-start edge becomes the physical top edge of the element. Setting overflow-clip-margin-inline-start: 0 in vertical writing mode clips the top of content — which in a vertical-writing consent dialog contains the first characters of the text (the beginning of the document, which typically carries the authorization context). A static audit that does not resolve writing-mode-to-physical-edge mapping will interpret "inline-start" as "left side" and report incorrectly.
An auditor measuring the shorthand
overflow-clip-margin computed value may find a non-zero value because the inline-end margin (right in LTR) is set generously. The shorthand computation averages or uses the maximum across edges in some implementations. Setting overflow-clip-margin-inline-start: 0px; overflow-clip-margin-inline-end: 200px allows an auditor checking only the shorthand to report "overflow-clip-margin: 200px — adequate." The actual inline-start clip boundary is still at zero.
Detection
function checkOverflowClipMarginInlineStart(el) {
const cs = getComputedStyle(el);
if (cs.overflow !== 'clip' && cs.overflowX !== 'clip' && cs.overflowY !== 'clip') {
return null; // only relevant for overflow:clip elements
}
// Read all four overflow-clip-margin longhands
const marginIS = parseFloat(cs.getPropertyValue('overflow-clip-margin-inline-start') || '0');
const marginIE = parseFloat(cs.getPropertyValue('overflow-clip-margin-inline-end') || '0');
const marginBS = parseFloat(cs.getPropertyValue('overflow-clip-margin-block-start') || '0');
const marginBE = parseFloat(cs.getPropertyValue('overflow-clip-margin-block-end') || '0');
const findings = [];
// Zero inline-start margin while other edges are non-zero = asymmetric
if (marginIS === 0 && (marginIE > 0 || marginBS > 0 || marginBE > 0)) {
// Resolve physical edge based on writing-mode and direction
const writingMode = cs.writingMode;
const direction = cs.direction;
let physicalEdge = 'left (LTR)';
if (direction === 'rtl') physicalEdge = 'right (RTL inline-start)';
if (writingMode.includes('vertical')) physicalEdge = 'top (vertical writing mode inline-start)';
findings.push({
severity: 'high',
issue: `overflow-clip-margin-inline-start:0 while other edges have margins — asymmetric ${physicalEdge} edge clip`
});
}
return findings.length ? findings : null;
}
Remediation
| Control | How it helps |
|---|---|
| Audit all four overflow-clip-margin longhands independently | Shorthand aggregation misses asymmetric zero-edge cases; check each longhand |
| Resolve logical-to-physical mapping via writing-mode and direction | Identifies which physical edge the inline-start maps to before applying security thresholds |
| Flag asymmetric margins where inline-start is 0 and other edges are non-zero | The asymmetry pattern is the indicator — uniformly zero margins are a different finding |
| Restrict overflow:clip in consent containers | overflow:clip is required for overflow-clip-margin to have effect; restricting the overflow value eliminates the attack surface |
SkillAudit checks all four overflow-clip-margin longhands independently and resolves logical-to-physical edge mapping from writing-mode and direction. Run a free audit on any MCP server GitHub URL.