harden: fix reachable panics (parse_duration/parse_iso/dechunk char-boundary+overflow), s2s netburst key/limit loss, rpc set_nick/set_vhost/notice injection, webirc rehash reload, panic-state reset, ws line cap, remote nick collision, per-conn state leaks

This commit is contained in:
Jean Chevronnet 2026-08-15 15:02:36 +00:00
parent 3fa9737ccb
commit e935ee7002
12 changed files with 87 additions and 11 deletions

View file

@ -12,6 +12,9 @@ pub fn handle(s: &mut Server, action: &str, params: &str) -> Result<String, RpcE
.ok_or_else(|| RpcError::invalid_params("missing 'target'"))?;
let text = json::get_str(params, "message")
.ok_or_else(|| RpcError::invalid_params("missing 'message'"))?;
// strip CR/LF so a crafted message/target can't inject extra IRC lines
let strip = |v: String| -> String { v.chars().filter(|c| *c != '\r' && *c != '\n').collect() };
let (target, text) = (strip(target), strip(text));
let src = s.name.clone();
if target == "*" || target == "$*" {
let uids: Vec<crate::Uid> = s.users.keys().copied().collect();

View file

@ -107,6 +107,9 @@ pub fn handle(s: &mut Server, action: &str, params: &str) -> Result<String, RpcE
let host = json::get_str(params, "vhost")
.or_else(|| json::get_str(params, "host"))
.ok_or_else(|| RpcError::invalid_params("missing 'vhost'"))?;
if !crate::users::valid_host(&host) {
return Err(RpcError::invalid_params("invalid vhost"));
}
s.change_host_ident(uid, None, Some(&host));
Ok(obj(&[("result", "true".into())]))
}
@ -114,6 +117,16 @@ pub fn handle(s: &mut Server, action: &str, params: &str) -> Result<String, RpcE
let uid = resolve(s, params).ok_or_else(|| RpcError::not_found("no such user"))?;
let newnick = json::get_str(params, "newnick")
.ok_or_else(|| RpcError::invalid_params("missing 'newnick'"))?;
// validate + collision-check like the NICK / SVSNICK paths: an invalid or
// taken nick would otherwise inject into the wire or hijack the nick index
if !crate::users::valid_nick(&newnick, s.conf_num("maxnick", 30usize)) {
return Err(RpcError::invalid_params("invalid nick"));
}
if s.find_nick(&newnick).is_some_and(|o| o != uid)
|| s.remote_nick.contains_key(&newnick.to_ascii_lowercase())
{
return Err(RpcError::invalid_params("nick in use"));
}
s.set_nick(uid, &newnick);
Ok(obj(&[("result", "true".into())]))
}