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
Problem
crates/web/src/components/member_list.rs:300inserts the output ofpeer_color()directly into an inlinestyleattribute:The
peer_color()function (crates/web/src/components/mod.rs:6-16) takes a&strand produces anhsl(...)value via arithmetic on a hash. Currently the input is always anEndpointId.to_string()(base32-encoded Ed25519 public key), so the output is always a safehsl(N, N%, N%)string.However,
peer_color()accepts any&strand has no output validation. If any future code path passes a different, attacker-controlled string throughpeer_color()and the result is embedded in astyle=attribute, a CSS injection could:url()backgroundsposition: absoluteinjected in the styleSeverity
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
stylewith a CSS custom property ordata-peer-hueattribute:Option B: Add output validation to
peer_color():Option C: Change the signature to accept
&EndpointIdinstead of&strto prevent misuse.Locations
crates/web/src/components/member_list.rs:300— inline style with peer_colorcrates/web/src/components/mod.rs:6-16— peer_color function