Parent: #108
Problem
crates/relay/src/main.rs:177-207:
async fn topic_announce_listener(
mut events: <willow_network::iroh::IrohNetwork as Network>::Events,
network: willow_network::iroh::IrohNetwork,
) {
let mut subscribed: HashSet<String> = HashSet::new();
while let Some(Ok(event)) = events.next().await {
if let GossipEvent::Received(msg) = event {
if let Some((willow_common::WireMessage::TopicAnnounce { topics }, _)) =
willow_common::unpack_wire(&msg.content)
{
for topic_str in topics {
if subscribed.insert(topic_str.clone()) {
let topic_id = willow_network::topic_id(&topic_str);
match network.subscribe(topic_id, vec![]).await {
// ...
}
}
}
}
}
}
}
subscribed: HashSet<String> grows without any cap.
- Each unique announcement triggers
network.subscribe.
- Topic strings are not validated (length, charset).
unpack_wire does require a valid Ed25519 signature, so only authenticated peers can hit this — but any misbehaving peer (intentional or buggy) can eat arbitrary relay memory by announcing unique random strings.
Fix
- Cap
subscribed at MAX_TOPICS = 10_000 (tunable constant). Reject additional announces once the cap is hit, logging at warn level.
- Validate each
topic_str: reject len() > 256, reject anything outside a conservative charset (e.g. [A-Za-z0-9_/:.-]).
- Optional: track announce rate per signer and rate-limit egregious peers.
Suggested constants
const MAX_TOPICS: usize = 10_000;
const MAX_TOPIC_LEN: usize = 256;
fn topic_str_is_valid(s: &str) -> bool {
s.len() <= MAX_TOPIC_LEN
&& s.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '/' | ':' | '.' | '-'))
}
Test
Relay currently has zero tests. Add one:
- Start the relay.
- From a signed test peer, publish many
TopicAnnounce messages with unique random topic strings.
- Assert the relay's
subscribed set plateaus at MAX_TOPICS.
- Assert topic strings violating the charset or length are rejected.
Parent: #108
Problem
crates/relay/src/main.rs:177-207:subscribed: HashSet<String>grows without any cap.network.subscribe.unpack_wiredoes require a valid Ed25519 signature, so only authenticated peers can hit this — but any misbehaving peer (intentional or buggy) can eat arbitrary relay memory by announcing unique random strings.Fix
subscribedatMAX_TOPICS = 10_000(tunable constant). Reject additional announces once the cap is hit, logging atwarnlevel.topic_str: rejectlen() > 256, reject anything outside a conservative charset (e.g.[A-Za-z0-9_/:.-]).Suggested constants
Test
Relay currently has zero tests. Add one:
TopicAnnouncemessages with unique random topic strings.subscribedset plateaus atMAX_TOPICS.