diff --git a/charts/openab/templates/configmap.yaml b/charts/openab/templates/configmap.yaml index fdd55f68..2347f8c5 100644 --- a/charts/openab/templates/configmap.yaml +++ b/charts/openab/templates/configmap.yaml @@ -11,6 +11,9 @@ metadata: data: config.toml: | {{- if ($cfg.discord).enabled }} + {{- if not $cfg.discord.allowedChannels }} + {{- fail (printf "agents.%s.discord.allowedChannels is required — empty means the bot will not respond to any messages. Add at least one channel ID." $name) }} + {{- end }} [discord] bot_token = "${DISCORD_BOT_TOKEN}" {{- range $cfg.discord.allowedChannels }} @@ -52,6 +55,9 @@ data: {{- end }} {{- if and ($cfg.slack).enabled }} + {{- if not (($cfg.slack).allowedChannels) }} + {{- fail (printf "agents.%s.slack.allowedChannels is required — empty means the bot will not respond to any messages. Add at least one channel ID." $name) }} + {{- end }} [slack] bot_token = "${SLACK_BOT_TOKEN}" app_token = "${SLACK_APP_TOKEN}" diff --git a/charts/openab/tests/adapter-enablement_test.yaml b/charts/openab/tests/adapter-enablement_test.yaml index 2e924cc2..6d7fcac5 100644 --- a/charts/openab/tests/adapter-enablement_test.yaml +++ b/charts/openab/tests/adapter-enablement_test.yaml @@ -33,6 +33,8 @@ tests: - it: renders [slack] when enabled=true set: agents.kiro.slack.enabled: true + agents.kiro.slack.allowedChannels: + - "C0123456789" asserts: - matchRegex: path: data["config.toml"] @@ -49,6 +51,8 @@ tests: - it: renders [slack] with placeholder tokens when enabled=true set: agents.kiro.slack.enabled: true + agents.kiro.slack.allowedChannels: + - "C0123456789" asserts: - matchRegex: path: data["config.toml"] @@ -61,6 +65,8 @@ tests: set: agents.kiro.discord.enabled: true agents.kiro.slack.enabled: true + agents.kiro.slack.allowedChannels: + - "C0123456789" asserts: - matchRegex: path: data["config.toml"] diff --git a/charts/openab/tests/configmap_test.yaml b/charts/openab/tests/configmap_test.yaml index 6af408c8..0326e24e 100644 --- a/charts/openab/tests/configmap_test.yaml +++ b/charts/openab/tests/configmap_test.yaml @@ -92,8 +92,27 @@ tests: - it: renders slack allow_user_messages = "multibot-mentions" set: agents.kiro.slack.enabled: true + agents.kiro.slack.allowedChannels: + - "C0123456789" agents.kiro.slack.allowUserMessages: multibot-mentions asserts: - matchRegex: path: data["config.toml"] pattern: 'allow_user_messages = "multibot-mentions"' + + - it: rejects empty discord allowedChannels + set: + agents.kiro.discord.enabled: true + agents.kiro.discord.allowedChannels: [] + asserts: + - failedTemplate: + errorPattern: "discord.allowedChannels is required" + + - it: rejects empty slack allowedChannels + set: + agents.kiro.discord.enabled: false + agents.kiro.slack.enabled: true + agents.kiro.slack.allowedChannels: [] + asserts: + - failedTemplate: + errorPattern: "slack.allowedChannels is required" diff --git a/charts/openab/values.yaml b/charts/openab/values.yaml index 7955cc34..d19cd8eb 100644 --- a/charts/openab/values.yaml +++ b/charts/openab/values.yaml @@ -135,7 +135,7 @@ agents: enabled: false botToken: "" # Bot User OAuth Token (xoxb-...) appToken: "" # App-Level Token (xapp-...) for Socket Mode - allowedChannels: [] # empty = allow all channels + allowedChannels: [] # required — empty = deny all channels (secure by default) allowedUsers: [] # empty = allow all users # allowBotMessages: "off" (default) | "mentions" | "all" allowBotMessages: "off" diff --git a/config.toml.example b/config.toml.example index 52e03822..bd72a096 100644 --- a/config.toml.example +++ b/config.toml.example @@ -2,7 +2,7 @@ [discord] bot_token = "${DISCORD_BOT_TOKEN}" -allowed_channels = ["1234567890"] # empty or omitted = allow all channels +allowed_channels = ["1234567890"] # required — empty or omitted = deny all channels (secure by default) # allowed_users = [""] # empty or omitted = allow all users # allow_bot_messages = "off" # "off" (default) | "mentions" | "all" # "mentions" is recommended for multi-agent collaboration @@ -14,7 +14,7 @@ allowed_channels = ["1234567890"] # empty or omitted = allow all channels # [slack] # bot_token = "${SLACK_BOT_TOKEN}" # Bot User OAuth Token (xoxb-...) # app_token = "${SLACK_APP_TOKEN}" # App-Level Token (xapp-...) for Socket Mode -# allowed_channels = ["C0123456789"] # empty or omitted = allow all channels +# allowed_channels = ["C0123456789"] # required — empty or omitted = deny all channels (secure by default) # allowed_users = ["U0123456789"] # empty or omitted = allow all users # allow_bot_messages = "off" # "off" (default) | "mentions" | "all" # trusted_bot_ids = [] # empty = any bot (mode permitting); set to restrict diff --git a/docs/discord.md b/docs/discord.md index 7b7896f5..7f71f098 100644 --- a/docs/discord.md +++ b/docs/discord.md @@ -64,7 +64,7 @@ Complete guide to setting up, configuring, and running OpenAB with Discord. ```toml [discord] bot_token = "${DISCORD_BOT_TOKEN}" -allowed_channels = ["123456789"] # channel ID allowlist (empty = all) +allowed_channels = ["123456789"] # channel ID allowlist (empty = deny all) allowed_users = ["987654321"] # user ID allowlist (empty = all) allow_bot_messages = "off" # off | mentions | all allow_user_messages = "involved" # involved | mentions @@ -75,11 +75,12 @@ trusted_bot_ids = [] # bot user IDs allowed through (empty = an | `allowed_channels` | `allowed_users` | Result | |---|---|---| -| empty | empty | All users, all channels (default) | +| empty | empty | **No channels, no users — bot ignores all messages** | | set | empty | Only these channels, all users | -| empty | set | All channels, only these users | +| empty | set | **Bot ignores all messages** (channels must be configured first) | | set | set | **AND** — must be in allowed channel AND allowed user | +- Empty `allowed_channels` = bot will not respond anywhere (secure by default) - Empty `allowed_users` (default) = no user filtering - Denied users get a 🚫 reaction and no reply diff --git a/docs/slack-bot-howto.md b/docs/slack-bot-howto.md index 8fa774e5..2a3a04dc 100644 --- a/docs/slack-bot-howto.md +++ b/docs/slack-bot-howto.md @@ -63,10 +63,22 @@ Add the `[slack]` section to your `config.toml`: [slack] bot_token = "${SLACK_BOT_TOKEN}" app_token = "${SLACK_APP_TOKEN}" -allowed_channels = [] # empty = allow all channels +allowed_channels = ["C0123456789"] # required — empty = deny all channels (secure by default) # allowed_users = ["U0123456789"] # empty = allow all users ``` +### Access control behavior + +| `allowed_channels` | `allowed_users` | Result | +|---|---|---| +| empty | empty | **No channels, no users — bot ignores all messages** | +| set | empty | Only these channels, all users | +| empty | set | **Bot ignores all messages** (channels must be configured first) | +| set | set | **AND** — must be in allowed channel AND allowed user | + +- Empty `allowed_channels` = bot will not respond anywhere (secure by default) +- Empty `allowed_users` (default) = no user filtering + Set the environment variables: ```bash diff --git a/src/discord.rs b/src/discord.rs index 35508c8f..00d3ee7d 100644 --- a/src/discord.rs +++ b/src/discord.rs @@ -234,8 +234,11 @@ impl EventHandler for Handler { }).clone(); let channel_id = msg.channel_id.get(); - let in_allowed_channel = - self.allowed_channels.is_empty() || self.allowed_channels.contains(&channel_id); + if self.allowed_channels.is_empty() { + debug!("allowed_channels is empty — ignoring message in channel {}", channel_id); + return; + } + let in_allowed_channel = self.allowed_channels.contains(&channel_id); let is_mentioned = msg.mentions_user_id(bot_id) || msg.content.contains(&format!("<@{}>", bot_id)); diff --git a/src/main.rs b/src/main.rs index 28d38d4c..015d5e59 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ use serenity::prelude::*; use std::collections::HashSet; use std::path::PathBuf; use std::sync::Arc; -use tracing::{error, info}; +use tracing::{error, info, warn}; #[derive(Parser)] #[command(name = "openab")] @@ -111,6 +111,9 @@ async fn main() -> anyhow::Result<()> { // Spawn Slack adapter (background task) let slack_handle = if let Some(slack_cfg) = cfg.slack { + if slack_cfg.allowed_channels.is_empty() { + warn!("no allowed_channels configured for Slack — bot will not respond to any messages. Add at least one channel ID to [slack] allowed_channels."); + } info!( channels = slack_cfg.allowed_channels.len(), users = slack_cfg.allowed_users.len(), @@ -148,6 +151,9 @@ async fn main() -> anyhow::Result<()> { if let Some(discord_cfg) = cfg.discord { let allowed_channels = parse_id_set(&discord_cfg.allowed_channels, "discord.allowed_channels")?; + if allowed_channels.is_empty() { + warn!("no allowed_channels configured for Discord — bot will not respond to any messages. Add at least one channel ID to [discord] allowed_channels."); + } let allowed_users = parse_id_set(&discord_cfg.allowed_users, "discord.allowed_users")?; let trusted_bot_ids = parse_id_set(&discord_cfg.trusted_bot_ids, "discord.trusted_bot_ids")?; info!( diff --git a/src/slack.rs b/src/slack.rs index 68e5b418..5ddd190c 100644 --- a/src/slack.rs +++ b/src/slack.rs @@ -803,8 +803,12 @@ async fn handle_message( }; let thread_ts = event["thread_ts"].as_str().map(|s| s.to_string()); - // Check allowed channels (empty = allow all) - if !allowed_channels.is_empty() && !allowed_channels.contains(&channel_id) { + // Check allowed channels (empty = deny all) + if allowed_channels.is_empty() { + tracing::debug!("allowed_channels is empty — ignoring message in channel {}", channel_id); + return; + } + if !allowed_channels.contains(&channel_id) { return; }