Skip to content

[Security][High] CSS injection via unvalidated color property in modal component #101

@numbers-official

Description

@numbers-official

Summary

The color attribute on the <capture-eye> element is passed directly to this.style.setProperty('--primary-color', this._color) at src/modal/modal.ts line 637 without any input validation. This occurs before the hex pattern check at line 652, which only guards the hover color calculation — the primary color is already injected.

Vulnerability Details

Attack Surface:
The color attribute is an HTML attribute set by integrators and can be controlled via:

  • Direct HTML: <capture-eye color="red; --injected: value" ...>
  • JavaScript: element.setAttribute('color', 'malicious value')
  • CMS widget settings (WordPress Elementor, Wix, etc.)

Vulnerable Code Path:

// src/modal/modal.ts:635-667
private updateModalColor() {
  // Line 637: Injected BEFORE any validation
  this.style.setProperty('--primary-color', this._color);
  
  // Lines 652-654: Validation only guards hover color, not primary
  const hexPattern = /^#[0-9a-fA-F]{6}$/;
  if (!hexPattern.test(hoverColor)) {
    return; // Only exits hover color calc, primary already set
  }
}

Attack Vectors:

  1. CSS variable contamination: color="red; --modal-content: none" — could hide modal elements
  2. CSS property injection through browsers that allow semicolons in custom property values
  3. UI spoofing — inject colors that mimic trusted UI patterns

Root Cause:

  • color property declared at src/capture-eye.ts line 44-45 as plain String with no validation
  • No sanitization between attribute ingestion and CSS variable injection
  • Validation at line 652 only checks the canvas-normalized value for hover color calculation, not the raw input

Impact

  • CSS injection can hide/alter UI elements, potentially enabling phishing overlays
  • Data exfiltration via CSS side channels (e.g., url() in custom properties)
  • Affects all integration surfaces: direct HTML, WordPress, Wix, Elementor

Suggested Fix

Add color validation before setProperty:

private isValidColor(color: string): boolean {
  const patterns = [
    /^#[0-9a-fA-F]{3}$/,       // #FFF
    /^#[0-9a-fA-F]{6}$/,       // #FFFFFF
    /^#[0-9a-fA-F]{8}$/,       // #FFFFFF00
    /^rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)$/,
    /^rgba\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*[\d.]+\s*\)$/,
  ];
  const namedColors = [
    'red','blue','green','black','white','orange','yellow',
    'purple','pink','gray','grey','cyan','magenta','transparent'
  ];
  const trimmed = color.trim().toLowerCase();
  return patterns.some(p => p.test(trimmed)) || namedColors.includes(trimmed);
}

private updateModalColor() {
  if (!this._color || !this.isValidColor(this._color)) {
    this.style.setProperty('--primary-color', '');
    this.style.setProperty('--hover-color', '');
    return;
  }
  this.style.setProperty('--primary-color', this._color);
  // ... rest of hover color calc
}

Also move the canvas-based validation before setProperty to ensure a defense-in-depth approach.

Metadata

Metadata

Labels

priority:highHigh prioritysecuritySecurity vulnerabilities and hardening

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions