From ee95225eaf07e190a405df5fb4974bc2064197a1 Mon Sep 17 00:00:00 2001 From: Jean Date: Fri, 17 Jul 2026 13:53:53 +0000 Subject: [PATCH] operserv: OPER ADD rejects unknown privilege names instead of silently dropping them --- modules/operserv/src/oper.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/modules/operserv/src/oper.rs b/modules/operserv/src/oper.rs index 6845e90..99eef84 100644 --- a/modules/operserv/src/oper.rs +++ b/modules/operserv/src/oper.rs @@ -27,14 +27,22 @@ fn add(me: &str, from: &Sender, account: Option<&str>, privs: Option<&str>, dur: ctx.notice(me, from.uid, format!("\x02{account}\x02 isn't registered.")); return; }; - // Keep only recognised privilege names. - let names: Vec = privs - .split(',') - .map(|p| p.trim().to_ascii_lowercase()) - .filter(|p| matches!(p.as_str(), "auspex" | "suspend" | "admin")) - .collect(); + // Parse the privilege names, rejecting the whole grant on a typo rather than + // silently dropping it (which would grant less than the oper intended). + let mut names = Vec::new(); + let mut unknown = Vec::new(); + for p in privs.split(',').map(str::trim).filter(|p| !p.is_empty()) { + match Priv::from_name(p) { + Some(pr) => names.push(pr.name().to_string()), + None => unknown.push(p.to_string()), + } + } + if !unknown.is_empty() { + ctx.notice(me, from.uid, format!("Unknown privilege(s): \x02{}\x02 (valid: {}).", unknown.join(", "), Priv::valid_names())); + return; + } if names.is_empty() { - ctx.notice(me, from.uid, "No valid privileges given (auspex, suspend, admin)."); + ctx.notice(me, from.uid, format!("No privileges given (valid: {}).", Priv::valid_names())); return; } let expires = dur.and_then(|d| d.strip_prefix('+')).and_then(parse_duration).map(|secs| now() + secs);