docs+tests: architecture diagram, security docs, 75 new edge-case tests

README:
- Add ASCII architecture flow diagram showing all layers
- Add Security Architecture section (Layer 1: Channel Auth,
  Layer 2: Rate Limiting, Layer 3: Tool Sandbox)
- Update test count to 629

New edge-case tests (75 new):
- SecurityPolicy: command injection (semicolon, backtick, dollar-paren,
  env prefix, newline), path traversal (encoded dots, double-dot in
  filename, null byte, symlink, tilde-ssh, /var/run), rate limiter
  boundaries (exactly-at, zero, high), autonomy+command combos,
  from_config fresh tracker
- Discord: exact match not substring, empty user ID, wildcard+specific,
  case sensitivity, base64 edge cases
- Slack: exact match, empty user ID, case sensitivity, wildcard combo
- Telegram: exact match, empty string, case sensitivity, wildcard combo
- Gateway: first-match-wins, empty value, colon in value, different
  headers, empty request, newline-only request
- Config schema: backward compat (Discord/Slack without allowed_users),
  TOML roundtrip, webhook secret presence/absence

629 tests passing, 0 clippy warnings
This commit is contained in:
argenis de la rosa 2026-02-13 16:00:15 -05:00
parent 542bb80743
commit a5887ad2dc
7 changed files with 460 additions and 6 deletions

View file

@ -308,4 +308,51 @@ mod tests {
assert!(!ch.is_user_allowed("333"));
assert!(!ch.is_user_allowed("unknown"));
}
#[test]
fn allowlist_is_exact_match_not_substring() {
let ch = DiscordChannel::new("fake".into(), None, vec!["111".into()]);
assert!(!ch.is_user_allowed("1111"));
assert!(!ch.is_user_allowed("11"));
assert!(!ch.is_user_allowed("0111"));
}
#[test]
fn allowlist_empty_string_user_id() {
let ch = DiscordChannel::new("fake".into(), None, vec!["111".into()]);
assert!(!ch.is_user_allowed(""));
}
#[test]
fn allowlist_with_wildcard_and_specific() {
let ch = DiscordChannel::new("fake".into(), None, vec!["111".into(), "*".into()]);
assert!(ch.is_user_allowed("111"));
assert!(ch.is_user_allowed("anyone_else"));
}
#[test]
fn allowlist_case_sensitive() {
let ch = DiscordChannel::new("fake".into(), None, vec!["ABC".into()]);
assert!(ch.is_user_allowed("ABC"));
assert!(!ch.is_user_allowed("abc"));
assert!(!ch.is_user_allowed("Abc"));
}
#[test]
fn base64_decode_empty_string() {
let decoded = base64_decode("");
assert_eq!(decoded, Some(String::new()));
}
#[test]
fn base64_decode_invalid_chars() {
let decoded = base64_decode("!!!!");
assert!(decoded.is_none());
}
#[test]
fn bot_user_id_from_empty_token() {
let id = DiscordChannel::bot_user_id_from_token("");
assert_eq!(id, Some(String::new()));
}
}

View file

@ -209,4 +209,31 @@ mod tests {
assert!(ch.is_user_allowed("U222"));
assert!(!ch.is_user_allowed("U333"));
}
#[test]
fn allowlist_exact_match_not_substring() {
let ch = SlackChannel::new("xoxb-fake".into(), None, vec!["U111".into()]);
assert!(!ch.is_user_allowed("U1111"));
assert!(!ch.is_user_allowed("U11"));
}
#[test]
fn allowlist_empty_user_id() {
let ch = SlackChannel::new("xoxb-fake".into(), None, vec!["U111".into()]);
assert!(!ch.is_user_allowed(""));
}
#[test]
fn allowlist_case_sensitive() {
let ch = SlackChannel::new("xoxb-fake".into(), None, vec!["U111".into()]);
assert!(ch.is_user_allowed("U111"));
assert!(!ch.is_user_allowed("u111"));
}
#[test]
fn allowlist_wildcard_and_specific() {
let ch = SlackChannel::new("xoxb-fake".into(), None, vec!["U111".into(), "*".into()]);
assert!(ch.is_user_allowed("U111"));
assert!(ch.is_user_allowed("anyone"));
}
}

View file

@ -179,4 +179,34 @@ mod tests {
let ch = TelegramChannel::new("t".into(), vec![]);
assert!(!ch.is_user_allowed("anyone"));
}
#[test]
fn telegram_user_exact_match_not_substring() {
let ch = TelegramChannel::new("t".into(), vec!["alice".into()]);
assert!(!ch.is_user_allowed("alice_bot"));
assert!(!ch.is_user_allowed("alic"));
assert!(!ch.is_user_allowed("malice"));
}
#[test]
fn telegram_user_empty_string_denied() {
let ch = TelegramChannel::new("t".into(), vec!["alice".into()]);
assert!(!ch.is_user_allowed(""));
}
#[test]
fn telegram_user_case_sensitive() {
let ch = TelegramChannel::new("t".into(), vec!["Alice".into()]);
assert!(ch.is_user_allowed("Alice"));
assert!(!ch.is_user_allowed("alice"));
assert!(!ch.is_user_allowed("ALICE"));
}
#[test]
fn telegram_wildcard_with_specific_users() {
let ch = TelegramChannel::new("t".into(), vec!["alice".into(), "*".into()]);
assert!(ch.is_user_allowed("alice"));
assert!(ch.is_user_allowed("bob"));
assert!(ch.is_user_allowed("anyone"));
}
}