From 54591a3a05b008c5e3d42d09071a69fb6da228ae Mon Sep 17 00:00:00 2001 From: "Aaron K. Clark (CryptoJones)" Date: Sat, 1 Aug 2026 16:49:33 -0500 Subject: [PATCH] fix(acp): treat an empty --respond-to-allowlist as absent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Launchers export BUZZ_ACP_RESPOND_TO_ALLOWLIST unconditionally, so an agent with no allowlist gets `""`. clap parses that into Some([""]) — set, but carrying no entries — which tripped two paths: - Non-allowlist modes logged "--respond-to-allowlist is ignored when --respond-to is not 'allowlist'" on every startup, implying an allowlist had been configured when none had. - Allowlist mode passed the blank entry to hex validation instead of reporting the intended "requires at least one pubkey" error. Filter blank entries before both checks so an empty export reads as absent. Desktop already removes the var in non-allowlist modes (build_respond_to_env); this covers every other launcher. Signed-off-by: Aaron K. Clark Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WXtzskvxVRMTnAGwwC7Dxz Signed-off-by: Aaron K. Clark (CryptoJones) --- crates/buzz-acp/src/config.rs | 69 +++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/crates/buzz-acp/src/config.rs b/crates/buzz-acp/src/config.rs index dab61be30a..3245c3e91e 100644 --- a/crates/buzz-acp/src/config.rs +++ b/crates/buzz-acp/src/config.rs @@ -993,16 +993,28 @@ impl Config { ))); } + // An empty `BUZZ_ACP_RESPOND_TO_ALLOWLIST` is how launchers spell "no + // allowlist" — the env var is exported unconditionally and clap turns + // `""` into `Some([""])`, which is *set* but carries no entries. Treat + // blank entries as absent so an empty export neither trips the + // ignored-flag warning below nor reaches hex validation as a bogus + // pubkey. + let allowlist_entries: Vec = args + .respond_to_allowlist + .unwrap_or_default() + .into_iter() + .filter(|entry| !entry.trim().is_empty()) + .collect(); + let respond_to_allowlist = if args.respond_to == RespondTo::Allowlist { - let raw = args.respond_to_allowlist.unwrap_or_default(); - if raw.is_empty() { + if allowlist_entries.is_empty() { return Err(ConfigError::ConfigFile( "--respond-to=allowlist requires --respond-to-allowlist with at least one pubkey".into(), )); } - validate_allowlist(&raw)? + validate_allowlist(&allowlist_entries)? } else { - if args.respond_to_allowlist.is_some() { + if !allowlist_entries.is_empty() { tracing::warn!( "--respond-to-allowlist is ignored when --respond-to is not 'allowlist'" ); @@ -2717,6 +2729,55 @@ channels = "ALL" const TEST_PRIVATE_KEY: &str = "0000000000000000000000000000000000000000000000000000000000000001"; + #[test] + fn empty_allowlist_is_treated_as_absent() { + // Launchers export BUZZ_ACP_RESPOND_TO_ALLOWLIST unconditionally, so an + // agent with no allowlist gets `""`. clap parses that into Some([""]), + // which must not read as "an allowlist was supplied". + let args = CliArgs::try_parse_from([ + "buzz-acp", + "--private-key", + TEST_PRIVATE_KEY, + "--respond-to", + "anyone", + "--respond-to-allowlist", + "", + ]) + .expect("clap should parse args"); + let config = Config::from_args(args).expect("empty allowlist should not fail config"); + + assert!( + config.respond_to_allowlist.is_empty(), + "blank entries must not land in the resolved allowlist" + ); + } + + #[test] + fn empty_allowlist_in_allowlist_mode_reports_the_missing_pubkey_error() { + // Same empty export, but in allowlist mode: the blank entry must surface + // the "requires at least one pubkey" error rather than failing hex + // validation on an empty string. + let args = CliArgs::try_parse_from([ + "buzz-acp", + "--private-key", + TEST_PRIVATE_KEY, + "--respond-to", + "allowlist", + "--respond-to-allowlist", + "", + ]) + .expect("clap should parse args"); + let result = Config::from_args(args); + + let msg = result + .expect_err("allowlist mode with a blank allowlist must fail") + .to_string(); + assert!( + msg.contains("at least one pubkey"), + "error should name the missing pubkey requirement: {msg}" + ); + } + #[test] fn allowed_respond_to_full_path_rejects_disallowed_mode() { // --allowed-respond-to=owner-only,allowlist + --respond-to=anyone → ConfigError