Skip to content

Commit 662fa81

Browse files
Merge branch 'release/3.7.4'
2 parents 8a16f14 + ff119ff commit 662fa81

File tree

3 files changed

+67
-30
lines changed

3 files changed

+67
-30
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88

99
- \[FR\] Clusterhawk template enhancement [\#1410](https://github.com/TheHive-Project/Cortex-Analyzers/issues/1410)
1010

11+
**Merged pull requests:**
12+
13+
- AbuseIPDB - Report Responder & improvements [\#1411](https://github.com/TheHive-Project/Cortex-Analyzers/pull/1411) ([nusantara-self](https://github.com/nusantara-self))
14+
1115
## [3.7.2](https://github.com/TheHive-Project/Cortex-Analyzers/tree/3.7.2) (2026-01-13)
1216

1317
[Full Changelog](https://github.com/TheHive-Project/Cortex-Analyzers/compare/3.7.1...3.7.2)

responders/Slack/README.md

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ This directory contains two Slack responders for TheHive integration:
1616
- Invites default participants by email
1717
- Sets channel visibility (private or public)
1818
- Posts case summary and/or case description (optional)
19-
- **Automatically tags the case** with `slack:<channel_name>` for easy tracking
19+
- **Automatically tags the case** with `slack:<channel_name>` and `slack-id:<channel_id>` for fast lookups
2020

2121
### Slack_SyncChannel
2222
- **Syncs all Slack channels tagged with `slack:` prefix** on the case
@@ -89,19 +89,28 @@ Log into your Cortex instance, go to Organization > Responders and enable the de
8989
## Privacy & Security Considerations
9090

9191
### Channel Tagging
92-
When `Slack_CreateChannel` runs, it automatically adds a `slack:<channel_name>` tag to the case. This tag:
93-
- Makes it easy to identify which Slack channel(s) are associated with a case
94-
- Enables `Slack_SyncChannel` to reliably find channels without reconstructing names
95-
- Can be manually added to sync additional channels (see warning below)
92+
When `Slack_CreateChannel` runs, it automatically adds two tags to the case:
93+
- `slack:<channel_name>` - Human-readable channel name
94+
- `slack-id:<channel_id>` - Channel ID for fast direct lookups
95+
96+
This enables `Slack_SyncChannel` to find channels instantly without searching through all workspace channels.
97+
98+
### Syncing Existing Channels
99+
To sync a channel that wasn't created via `Slack_CreateChannel`:
100+
1. Invite the Slack bot to the channel
101+
2. Add `slack:<channel-name>` tag to the case
102+
3. (Optional) Add `slack-id:<channel-id>` for faster lookups
103+
- Get the ID: right-click channel in Slack → "View channel details" → ID at bottom
104+
4. Run `Slack_SyncChannel`
96105

97106
### Multi-Channel Syncing
98107
`Slack_SyncChannel` will sync **all** channels that have `slack:` tags on the case:
99108
- Each channel creates its own separate task in TheHive
100109
- Partial failures are handled gracefully (some channels may sync, others may fail)
101110
- Failed channels are reported in the responder output
102111

103-
### Access Control Warning ⚠️
104-
- The bot can only read channels it has been **invited to**
112+
### Access Control
113+
- The bot only searches channels it's a **member of** (security + performance)
114+
- Channels created via `Slack_CreateChannel` automatically include the bot
105115
- Syncing brings Slack conversations into TheHive: ensure case permissions align with channel access
106-
- Private Slack channels synced to non-private TheHive cases may expose sensitive information
107-
- Consider your organization's data governance policies before syncing channels
116+
- Private Slack channels synced to non-private TheHive cases may expose sensitive information

responders/Slack/slack.py

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,31 @@ def __init__(self):
3131
self.thehive_base_url = self.get_param("config.thehive_base_url", None)
3232
self.thehive_apikey = self.get_param("config.thehive_apikey", None)
3333

34-
def find_existing_channel(self, name, headers):
35-
url = "https://slack.com/api/conversations.list"
34+
def get_channel_info(self, channel_id, headers):
35+
"""Direct lookup by channel ID"""
36+
try:
37+
url = "https://slack.com/api/conversations.info"
38+
resp = requests.get(url, headers=headers, params={"channel": channel_id}).json()
39+
if resp.get("ok"):
40+
return resp.get("channel")
41+
except Exception:
42+
pass
43+
return None
44+
45+
def find_existing_channel(self, name, headers, channel_ids=None):
46+
"""
47+
Find channel by name. If channel_ids provided, tries direct lookup first.
48+
Falls back to searching only channels the bot is a member of.
49+
"""
50+
# Try direct lookup if we have IDs
51+
if channel_ids:
52+
for cid in channel_ids:
53+
ch = self.get_channel_info(cid, headers)
54+
if ch and ch.get("name") == name:
55+
return cid
56+
57+
# Fallback: search through bot's channels only (security + performance)
58+
url = "https://slack.com/api/users.conversations"
3659
cursor = None
3760
while True:
3861
params = {
@@ -43,6 +66,8 @@ def find_existing_channel(self, name, headers):
4366
if cursor:
4467
params["cursor"] = cursor
4568
resp = requests.get(url, headers=headers, params=params).json()
69+
if not resp.get("ok"):
70+
break
4671
for ch in resp.get("channels", []):
4772
if ch["name"] == name:
4873
return ch["id"]
@@ -96,10 +121,7 @@ def run(self):
96121

97122
# 2. create channel only after we have valid users
98123
channel_id = self.find_existing_channel(channel_name, headers)
99-
if channel_id:
100-
# Channel already exists, reuse it
101-
pass
102-
else:
124+
if not channel_id:
103125
create_url = "https://slack.com/api/conversations.create"
104126
payload = {
105127
"name": channel_name,
@@ -113,6 +135,8 @@ def run(self):
113135
)
114136
channel_id = create_data["channel"]["id"]
115137

138+
self.channel_id = channel_id
139+
116140
# 3. invite users
117141
if user_ids:
118142
invite_url = "https://slack.com/api/conversations.invite"
@@ -192,13 +216,16 @@ def format_tlp(level):
192216
elif self.service == "syncchannel":
193217
case_id = self.get_param("data.caseId")
194218
case_unique_id = self.get_param("data.id")
219+
case_tags = self.get_param("data.tags", [])
195220

196-
# Collect all channel names from tags
221+
# Extract channel IDs and names from tags
222+
channel_ids = []
197223
channel_names = []
198-
case_tags = self.get_param("data.tags", [])
199224
for tag in case_tags:
200-
if tag.startswith("slack:"):
201-
channel_names.append(tag[6:]) # Remove "slack:" prefix
225+
if tag.startswith("slack-id:"):
226+
channel_ids.append(tag[9:])
227+
elif tag.startswith("slack:"):
228+
channel_names.append(tag[6:])
202229

203230
# Fallback to reconstructing channel name if no tags found
204231
if not channel_names:
@@ -207,8 +234,7 @@ def format_tlp(level):
207234
.replace(".", "")
208235
.replace(",", "")
209236
.lower()
210-
)
211-
channel_name = channel_name[:80]
237+
)[:80]
212238
channel_names.append(channel_name)
213239

214240
headers = {
@@ -222,8 +248,8 @@ def format_tlp(level):
222248

223249
for channel_name in channel_names:
224250
try:
225-
# Find the channel
226-
channel_id = self.find_existing_channel(channel_name, headers)
251+
# Find the channel (uses IDs for direct lookup if available)
252+
channel_id = self.find_existing_channel(channel_name, headers, channel_ids)
227253
if not channel_id:
228254
errors.append(f"Channel '{channel_name}' not found")
229255
continue
@@ -731,15 +757,13 @@ def create_or_update_thehive_task(self, case_id, channel_name, conversations):
731757
self.error(f"Failed to create task: {str(e)}")
732758

733759
def operations(self, raw):
734-
artifacts = []
735-
# AddTagToArtifact ({ "type": "AddTagToArtifact", "tag": "tag to add" }): add a tag to the artifact related to the object
736-
# AddTagToCase ({ "type": "AddTagToCase", "tag": "tag to add" }): add a tag to the case related to the object
737-
# MarkAlertAsRead: mark the alert related to the object as read
738-
# AddCustomFields ({"name": "key", "value": "value", "tpe": "type"): add a custom field to the case related to the object
760+
ops = []
739761
if self.service == "createchannel":
740762
if hasattr(self, 'channel_name'):
741-
artifacts.append(self.build_operation("AddTagToCase", tag=f"slack:{self.channel_name}"))
742-
return artifacts
763+
ops.append(self.build_operation("AddTagToCase", tag=f"slack:{self.channel_name}"))
764+
if hasattr(self, 'channel_id'):
765+
ops.append(self.build_operation("AddTagToCase", tag=f"slack-id:{self.channel_id}"))
766+
return ops
743767

744768

745769
if __name__ == "__main__":

0 commit comments

Comments
 (0)