This document defines security measures to prevent accidental data exposure and destructive operations.
- NEVER store tokens in plaintext files
- ALWAYS use OS file storage (macOS file storage, Windows Credential Manager, Linux Secret Service)
- Config file (
profiles.json) must contain only non-secret metadata
- NEVER log tokens in any form
- Mask
Authorizationheaders in debug logs:- Before:
Authorization: Bearer xoxp-1234567890-abcdefghij - After:
Authorization: Bearer xoxp-****
- Before:
- Mask tokens in error messages:
- Before:
Failed to authenticate with token xoxp-1234567890 - After:
Failed to authenticate with token xoxp-****
- Before:
- NEVER include tokens in JSON or text output
- API responses should not echo the token used
- Scope: All write operations (
msg post/update/delete,react add/remove) - Enforcement: Check at command execution time
- Error if missing:
Error: Write operations require --allow-write flag. Example: slack-rs --profile myworkspace --allow-write msg post --channel C123 --text "Hello" - Exit code: 1
- Scope: Destructive operations only (
msg delete, future:files delete, etc.) - Enforcement: Check after
--allow-writevalidation - Behavior if missing:
- Display confirmation prompt (localized):
You are about to delete a message: Channel: #general (C123ABC) Timestamp: 1234567890.123456 This action cannot be undone. Continue? [y/N]: - If user enters
yoryes(case-insensitive): proceed - Otherwise: abort with exit code 1
- Display confirmation prompt (localized):
- With
--yesflag: Skip prompt, execute immediately
fn execute_write_command(allow_write: bool, needs_confirmation: bool, yes: bool) -> Result<()> {
// Tier 1: Check --allow-write
if !allow_write {
return Err(Error::WriteNotAllowed);
}
// Tier 2: Check confirmation for destructive ops
if needs_confirmation && !yes {
if !prompt_user_confirmation()? {
return Err(Error::OperationCancelled);
}
}
// Proceed with operation
Ok(())
}- All API-hitting commands require explicit
--profilespecification - No default profile (prevents accidental operations on wrong workspace)
- Error if missing:
Error: --profile is required. Run 'slack-rs auth list' to see available profiles.
- All JSON responses must include profile context:
{ "meta": { "profile_name": "acme-work", "team_id": "T123ABC", "team_name": "Acme Corp", "user_id": "U456DEF" }, "data": { ... } } - Text output must show workspace identifier:
[acme-work / Acme Corp] Message posted successfully
- Honor HTTP 429 responses
- Parse
Retry-Afterheader (seconds) - Wait before retrying
- Initial retry delay: 1 second
- Multiply by 2 on each retry (with jitter)
- Maximum retry delay: 60 seconds
- Maximum retry attempts: 5
let jitter = rand::random::<f64>() * 0.3; // ±30%
let delay = base_delay * (1.0 + jitter);- Must match pattern:
^[CDG][A-Z0-9]{8,}$ - Reject invalid formats early
- Must match pattern:
^\d{10}\.\d{6}$ - Example:
1234567890.123456
- Must match pattern:
^[UW][A-Z0-9]{8,}$
- Must match pattern:
^:[a-z0-9_+-]+:$ - Example:
:white_check_mark:
- ❌ Bad:
Failed to post to channel C123ABC with token xoxp-1234567890 - ✅ Good:
Failed to post to channel C123ABC: missing_scope
- ❌ Bad:
Error: 403 - ✅ Good:
Error: Insufficient permissions. Required scope: chat:write. Run 'slack-rs auth status --profile myworkspace' to check current scopes.
- Error messages, prompts, and instructions should respect
--langflag - API error codes remain in English (e.g.,
missing_scope,channel_not_found)
- Optional audit log for write operations
- Format: JSON lines
- Fields: timestamp, profile, command, channel, result
- Location:
~/.local/share/slack-rs/audit.log - Disabled by default; enable with
SLACKRS_AUDIT=1