Skip to content

Commit 58bcf76

Browse files
authored
Added support to do a Slack Bot instead of just a Slack WebHook (#145)
* Added support to do a Slack Bot instead of just a Slack WebHook * Updated README with new Slack bot directions
1 parent 13b8fe5 commit 58bcf76

File tree

7 files changed

+439
-17
lines changed

7 files changed

+439
-17
lines changed

README.md

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -221,43 +221,113 @@ Example `SOCKET_JIRA_CONFIG_JSON` value
221221
222222
| Environment Variable | Required | Default | Description |
223223
|:-------------------------|:---------|:--------|:-----------------------------------|
224-
| SOCKET_SLACK_CONFIG_JSON | False | None | Slack webhook configuration (enables plugin when set). Alternatively, use --slack-webhook CLI flag. |
224+
| SOCKET_SLACK_CONFIG_JSON | False | None | Slack configuration (enables plugin when set). Supports webhook or bot mode. Alternatively, use --slack-webhook CLI flag for simple webhook mode. |
225+
| SOCKET_SLACK_BOT_TOKEN | False | None | Slack Bot User OAuth Token (starts with `xoxb-`). Required when using bot mode. |
225226
226-
Example `SOCKET_SLACK_CONFIG_JSON` value (simple webhook):
227+
**Slack supports two modes:**
228+
229+
1. **Webhook Mode** (default): Posts to incoming webhooks
230+
2. **Bot Mode**: Posts via Slack API with bot token authentication
231+
232+
###### Webhook Mode Examples
233+
234+
Simple webhook:
227235
228236
````json
229-
{"url": "https://REPLACE_ME_WEBHOOK"}
237+
{"url": "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"}
230238
````
231239
232-
Example with advanced filtering (reachability-only alerts):
240+
Multiple webhooks with advanced filtering:
233241
234242
````json
235243
{
244+
"mode": "webhook",
236245
"url": [
237246
{
238247
"name": "prod_alerts",
239248
"url": "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
249+
},
250+
{
251+
"name": "critical_only",
252+
"url": "https://hooks.slack.com/services/YOUR/OTHER/WEBHOOK/URL"
240253
}
241254
],
242255
"url_configs": {
243256
"prod_alerts": {
244257
"reachability_alerts_only": true,
245-
"always_send_reachability": true
258+
"severities": ["high", "critical"]
259+
},
260+
"critical_only": {
261+
"severities": ["critical"]
246262
}
247263
}
248264
}
249265
````
250266
251-
**Advanced Configuration Options:**
267+
###### Bot Mode Examples
268+
269+
**Setting up a Slack Bot:**
270+
1. Go to https://api.slack.com/apps and create a new app
271+
2. Under "OAuth & Permissions", add the `chat:write` bot scope
272+
3. Install the app to your workspace and copy the "Bot User OAuth Token"
273+
4. Invite the bot to your channels: `/invite @YourBotName`
274+
275+
Basic bot configuration:
276+
277+
````json
278+
{
279+
"mode": "bot",
280+
"bot_configs": [
281+
{
282+
"name": "security_alerts",
283+
"channels": ["security-alerts", "dev-team"]
284+
}
285+
]
286+
}
287+
````
288+
289+
Bot with filtering (reachability-only alerts):
290+
291+
````json
292+
{
293+
"mode": "bot",
294+
"bot_configs": [
295+
{
296+
"name": "critical_reachable",
297+
"channels": ["security-critical"],
298+
"severities": ["critical", "high"],
299+
"reachability_alerts_only": true
300+
},
301+
{
302+
"name": "all_alerts",
303+
"channels": ["security-all"],
304+
"repos": ["myorg/backend", "myorg/frontend"]
305+
}
306+
]
307+
}
308+
````
309+
310+
Set the bot token:
311+
```bash
312+
export SOCKET_SLACK_BOT_TOKEN="xoxb-your-bot-token-here"
313+
```
252314
253-
The `url_configs` object allows per-webhook filtering:
315+
**Configuration Options:**
254316
317+
Webhook mode (`url_configs`):
255318
- `reachability_alerts_only` (boolean, default: false): When `--reach` is enabled, only send blocking alerts (error=true) from diff scans
256-
- `always_send_reachability` (boolean, default: true): Send reachability alerts even on non-diff scans when `--reach` is enabled. Set to false to only send reachability alerts when there are diff alerts.
257319
- `repos` (array): Only send alerts for specific repositories (e.g., `["owner/repo1", "owner/repo2"]`)
258320
- `alert_types` (array): Only send specific alert types (e.g., `["malware", "typosquat"]`)
259321
- `severities` (array): Only send alerts with specific severities (e.g., `["high", "critical"]`)
260322
323+
Bot mode (`bot_configs` array items):
324+
- `name` (string, required): Friendly name for this configuration
325+
- `channels` (array, required): Channel names (without #) where alerts will be posted
326+
- `severities` (array, optional): Only send alerts with specific severities (e.g., `["high", "critical"]`)
327+
- `repos` (array, optional): Only send alerts for specific repositories
328+
- `alert_types` (array, optional): Only send specific alert types
329+
- `reachability_alerts_only` (boolean, default: false): Only send reachable vulnerabilities when using `--reach`
330+
261331
## Automatic Git Detection
262332
263333
The CLI now automatically detects repository information from your git environment, significantly simplifying usage in CI/CD pipelines:

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.62"
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.62'
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)