Skip to content

[security] CSS injection surface via peer_color inline style attribute #191

@intendednull

Description

@intendednull

Problem

crates/web/src/components/member_list.rs:300 inserts the output of peer_color() directly into an inline style attribute:

<span class="member-name" style=format!("color: {}", super::peer_color(&pid))>

The peer_color() function (crates/web/src/components/mod.rs:6-16) takes a &str and produces an hsl(...) value via arithmetic on a hash. Currently the input is always an EndpointId.to_string() (base32-encoded Ed25519 public key), so the output is always a safe hsl(N, N%, N%) string.

However, peer_color() accepts any &str and has no output validation. If any future code path passes a different, attacker-controlled string through peer_color() and the result is embedded in a style= attribute, a CSS injection could:

  • Break layout for other users
  • Exfiltrate data via CSS attribute selectors and url() backgrounds
  • Overlay content via position: absolute injected in the style

Severity

MEDIUM — currently safe due to EndpointId format constraints, but the pattern is architecturally fragile. A single new callsite with a different input type would make this exploitable.

Fix

Option A: Replace inline style with a CSS custom property or data-peer-hue attribute:

<span class="member-name" data-peer-hue=compute_hue(&pid)>
// CSS: .member-name { color: hsl(attr(data-peer-hue), 70%, 60%); }

Option B: Add output validation to peer_color():

pub fn peer_color(id: &str) -> String {
    let color = format!("hsl({hue}, {sat}%, {lit}%)");
    debug_assert!(color.chars().all(|c| c.is_ascii_alphanumeric() || " (),%.".contains(c)));
    color
}

Option C: Change the signature to accept &EndpointId instead of &str to prevent misuse.

Locations

  • crates/web/src/components/member_list.rs:300 — inline style with peer_color
  • crates/web/src/components/mod.rs:6-16 — peer_color function

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions