Skip to content

Commit ec3e000

Browse files
committed
Added support to do a Slack Bot instead of just a Slack WebHook
1 parent 13b8fe5 commit ec3e000

File tree

6 files changed

+361
-9
lines changed

6 files changed

+361
-9
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
66

77
[project]
88
name = "socketsecurity"
9-
version = "2.2.60"
9+
version = "2.2.61"
1010
requires-python = ">= 3.10"
1111
license = {"file" = "LICENSE"}
1212
dependencies = [

session.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Session Directions: Add Slack Bot Mode Support
2+
3+
## Context
4+
The Socket Python CLI currently supports Slack notifications via incoming webhooks. We need to add an alternative "bot" mode that uses a Slack App with Bot Token for more flexible channel routing.
5+
6+
## Current Implementation
7+
- File: `socketsecurity/plugins/slack.py`
8+
- File: `socketsecurity/config.py`
9+
- Env var: `SOCKET_SLACK_CONFIG_JSON`
10+
- Current config uses `url` and `url_configs` for webhook routing
11+
12+
## Requirements
13+
14+
### 1. Add Mode Selection
15+
- Add top-level `mode` field to Slack config
16+
- Valid values: "webhook" (default), "bot"
17+
- Mode determines which authentication and routing method to use
18+
19+
### 2. Webhook Mode (existing, default)
20+
```json
21+
{
22+
"enabled": true,
23+
"mode": "webhook",
24+
"url": ["https://hooks.slack.com/..."],
25+
"url_configs": {
26+
"webhook_0": {"repos": ["repo1"], "severities": ["critical"]}
27+
}
28+
}
29+
```
30+
Keep all existing webhook functionality unchanged.
31+
32+
### 3. Bot Mode (new)
33+
```json
34+
{
35+
"enabled": true,
36+
"mode": "bot",
37+
"bot_configs": [
38+
{
39+
"name": "critical_alerts",
40+
"channels": ["security-alerts", "critical-incidents"],
41+
"repos": ["prod-app"],
42+
"severities": ["critical"],
43+
"reachability_alerts_only": true
44+
},
45+
{
46+
"name": "all_alerts",
47+
"channels": ["dev-alerts"],
48+
"severities": ["high", "medium"]
49+
}
50+
]
51+
}
52+
```
53+
54+
- New env var: `SOCKET_SLACK_BOT_TOKEN` (Bot User OAuth Token starting with `xoxb-`)
55+
- Use `bot_configs` array instead of `url` + `url_configs`
56+
- Each bot_config has:
57+
- `name`: identifier for logging
58+
- `channels`: array of Slack channel names or IDs to post to
59+
- All existing filter options: `repos`, `severities`, `alert_types`, `reachability_alerts_only`, `always_send_reachability`
60+
61+
### 4. Channel Routing
62+
- Slack API accepts both channel names (without #) and channel IDs (C1234567890)
63+
- Recommend supporting both: try name first, fallback to ID if needed
64+
- API endpoint: `https://slack.com/api/chat.postMessage`
65+
- Request format:
66+
```python
67+
{
68+
"channel": "channel-name", # or "C1234567890"
69+
"blocks": blocks
70+
}
71+
```
72+
- Headers: `{"Authorization": f"Bearer {bot_token}", "Content-Type": "application/json"}`
73+
74+
### 5. Implementation Tasks
75+
76+
#### config.py
77+
- No changes needed (config is loaded from JSON env var)
78+
79+
#### slack.py
80+
1. Update `send()` method:
81+
- Check `self.config.get("mode", "webhook")`
82+
- If "webhook": call existing `_send_webhook_alerts()` (refactor current logic)
83+
- If "bot": call new `_send_bot_alerts()`
84+
85+
2. Create `_send_bot_alerts()` method:
86+
- Get bot token from env: `os.getenv("SOCKET_SLACK_BOT_TOKEN")`
87+
- Validate token exists and starts with "xoxb-"
88+
- Get `bot_configs` from config
89+
- For each bot_config, filter alerts same way as webhooks
90+
- For each channel in bot_config's channels array, post message via chat.postMessage API
91+
92+
3. Create `_post_to_slack_api()` helper method:
93+
- Takes bot_token, channel, blocks
94+
- Posts to https://slack.com/api/chat.postMessage
95+
- Returns response
96+
- Log errors with channel name/ID for debugging
97+
98+
4. Error handling:
99+
- Log if bot token missing when mode is "bot"
100+
- Handle API errors (invalid channel, missing permissions, rate limits)
101+
- Parse Slack API response JSON (it returns 200 with error in body)
102+
103+
5. Reuse existing:
104+
- All filtering logic (`_filter_alerts`)
105+
- All block building (`create_slack_blocks_from_diff`, `_create_reachability_slack_blocks_from_structured`)
106+
- All reachability data loading
107+
108+
### 6. Testing Considerations
109+
- Test both modes don't interfere with each other
110+
- Test channel name resolution
111+
- Test channel ID usage
112+
- Test multiple channels per bot_config
113+
- Test error handling when bot token invalid or missing
114+
- Verify block count limits still respected (50 blocks)
115+
116+
### 7. Documentation Updates (README.md)
117+
Add bot mode configuration examples and SOCKET_SLACK_BOT_TOKEN env var documentation.
118+
119+
## Key Files to Modify
120+
1. `socketsecurity/plugins/slack.py` - main implementation
121+
2. `README.md` - add bot mode documentation
122+
123+
## Notes
124+
- Slack chat.postMessage returns HTTP 200 even on errors. Check response JSON for `"ok": false`
125+
- Rate limit: 1 message per second per channel (more generous than webhooks)
126+
- Channel names are case-insensitive, don't need # prefix
127+
- Public and private channels both work if bot is invited

socketsecurity/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
__author__ = 'socket.dev'
2-
__version__ = '2.2.60'
2+
__version__ = '2.2.61'
33
USER_AGENT = f'SocketPythonCLI/{__version__}'

socketsecurity/core/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,10 @@ def create_full_scan(self, files: List[str], params: FullScanParams, base_paths:
553553

554554
# Finalize tier1 scan if reachability analysis was enabled
555555
if self.cli_config and self.cli_config.reach:
556-
facts_file_path = self.cli_config.reach_output_file or ".socket.facts.json"
556+
facts_file_path = os.path.join(
557+
self.cli_config.target_path or ".",
558+
self.cli_config.reach_output_file
559+
)
557560
log.debug(f"Reachability analysis enabled, finalizing tier1 scan for full scan {full_scan.id}")
558561
try:
559562
success = self.finalize_tier1_scan(full_scan.id, facts_file_path)

0 commit comments

Comments
 (0)