Skip to content

Commit 68f9b18

Browse files
committed
Added Slack Support
1 parent a1ed392 commit 68f9b18

File tree

6 files changed

+159
-4
lines changed

6 files changed

+159
-4
lines changed

connection.yml.sample

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,12 @@ sources:
6161
- private
6262
- venv
6363
- node_modules
64+
65+
slack:
66+
slack_example:
67+
token: xoxp-XXXXXXXXXXXXXXXXXXXXXXXXX # get your slack app these permissiosn https://api.slack.com/methods/team.info and https://api.slack.com/methods/conversations.list
68+
channel_types: "public_channel,private_channel"
69+
# Optional: List of channel names to check
70+
# channel_names:
71+
# - general
72+
# - random

hawk_scanner/commands/slack.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import re
2+
from slack_sdk import WebClient
3+
from slack_sdk.errors import SlackApiError
4+
from hawk_scanner.internals import system
5+
from rich.console import Console
6+
7+
console = Console()
8+
9+
def connect_slack(token):
10+
try:
11+
client = WebClient(token=token)
12+
# Test the connection by making an API call
13+
response = client.auth_test()
14+
if response["ok"]:
15+
system.print_info("Connected to Slack")
16+
return client
17+
else:
18+
system.print_error("Failed to authenticate with Slack")
19+
return None
20+
except SlackApiError as e:
21+
system.print_error(f"Failed to connect to Slack with error: {e.response['error']}")
22+
return None
23+
24+
def check_slack_messages(client, patterns, profile_name, channel_types, channel_names=None):
25+
results = []
26+
try:
27+
team_info = client.team_info()
28+
workspace_url = team_info["team"]["url"].rstrip('/')
29+
# Get all channels of specified types
30+
channels = client.conversations_list(types=channel_types)["channels"]
31+
32+
# Filter channels by names if provided
33+
if channel_names:
34+
channels = [channel for channel in channels if channel['name'] in channel_names]
35+
36+
system.print_info(f"Found {len(channels)} channels of type {channel_types}")
37+
system.print_info(f"Checking messages in channels: {', '.join([channel['name'] for channel in channels])}")
38+
39+
for channel in channels:
40+
channel_name = channel["name"]
41+
channel_id = channel["id"]
42+
43+
# Get messages from the channel
44+
system.print_info(f"Checking messages in channel {channel_name} ({channel_id})")
45+
messages = client.conversations_history(channel=channel_id)["messages"]
46+
47+
for message in messages:
48+
user = message.get("user", "")
49+
text = message.get("text")
50+
if text:
51+
matches = system.match_strings(text)
52+
if matches:
53+
for match in matches:
54+
results.append({
55+
'channel_id': channel_id,
56+
'channel_name': channel_name,
57+
'user': user,
58+
'pattern_name': match['pattern_name'],
59+
'matches': list(set(match['matches'])),
60+
'sample_text': match['sample_text'],
61+
'profile': profile_name,
62+
'message_link': workspace_url + f"/archives/{channel_id}/p{message['ts'].replace('.', '')}",
63+
'data_source': 'slack'
64+
})
65+
return results
66+
except SlackApiError as e:
67+
system.print_error(f"Failed to fetch messages from Slack with error: {e.response['error']}")
68+
return results
69+
70+
def execute(args):
71+
results = []
72+
system.print_info("Running Checks for Slack Sources")
73+
connections = system.get_connection()
74+
75+
if 'sources' in connections:
76+
sources_config = connections['sources']
77+
slack_config = sources_config.get('slack')
78+
79+
if slack_config:
80+
patterns = system.get_fingerprint_file()
81+
82+
for key, config in slack_config.items():
83+
token = config.get('token')
84+
channel_types = config.get('channel_types', "public_channel,private_channel")
85+
channel_names = config.get('channel_names', None)
86+
87+
if token:
88+
system.print_info(f"Checking Slack Profile {key}")
89+
else:
90+
system.print_error(f"Incomplete Slack configuration for key: {key}")
91+
continue
92+
93+
client = connect_slack(token)
94+
if client:
95+
results += check_slack_messages(client, patterns, key, channel_types, channel_names)
96+
else:
97+
system.print_error("No Slack connection details found in connection.yml")
98+
else:
99+
system.print_error("No 'sources' section found in connection.yml")
100+
101+
return results

hawk_scanner/main.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def clear_screen():
2121
console = Console()
2222

2323
## Now separate the results by data_source
24-
data_sources = ['s3', 'mysql', 'redis', 'firebase', 'gcs', 'fs', 'postgresql', 'mongodb']
24+
data_sources = ['s3', 'mysql', 'redis', 'firebase', 'gcs', 'fs', 'postgresql', 'mongodb', 'slack']
2525

2626
def load_command_module(command):
2727
try:
@@ -95,6 +95,8 @@ def main():
9595
table.add_column("File Path")
9696
elif group == 'mongodb':
9797
table.add_column("Host > Database > Collection > Field")
98+
elif group == 'slack':
99+
table.add_column("Channel Name > Message Link")
98100

99101
table.add_column("Pattern Name")
100102
table.add_column("Total Exposed")
@@ -202,7 +204,34 @@ def main():
202204
)
203205

204206
system.SlackNotify(AlertMsg)
205-
207+
elif group == 'slack':
208+
table.add_row(
209+
str(i),
210+
result['profile'],
211+
f"{result['channel_name'] } > {result['message_link']}",
212+
result['pattern_name'],
213+
str(len(result['matches'])),
214+
records_mini,
215+
result['sample_text'],
216+
)
217+
AlertMsg = """
218+
*** PII Or Secret Found ***
219+
Data Source: Slack - {vulnerable_profile}
220+
Channel Name: {channel_name}
221+
Mesasge Link: {message_link}
222+
Pattern Name: {pattern_name}
223+
Total Exposed: {total_exposed}
224+
Exposed Values: {exposed_values}
225+
""".format(
226+
vulnerable_profile=result['profile'],
227+
channel_name=result['channel_name'],
228+
message_link=result['message_link'],
229+
pattern_name=result['pattern_name'],
230+
total_exposed=str(len(result['matches'])),
231+
exposed_values=records_mini
232+
)
233+
234+
system.SlackNotify(AlertMsg)
206235
elif group == 'postgresql':
207236
table.add_row(
208237
str(i),

readme.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
### 🦅 HAWK Eye - Highly Advanced Watchful Keeper Eye
1919

20-
HAWK Eye is a powerful and versatile CLI (Command-Line Interface) tool designed to be your vigilant watchkeeper, guarding against potential data breaches and cyber threats across various platforms. Inspired by the precision and vision of majestic birds of prey, HAWK Eye swiftly scans multiple data sources, including S3, MySQL, PostgreSQL, MongoDB, Redis, Firebase, filesystem, and Google Cloud buckets (GCS), for Personally Identifiable Information (PII) and secrets.
20+
HAWK Eye is a powerful and versatile CLI (Command-Line Interface) tool designed to be your vigilant watchkeeper, guarding against potential data breaches and cyber threats across various platforms. Inspired by the precision and vision of majestic birds of prey, HAWK Eye swiftly scans multiple data sources, including S3, MySQL, PostgreSQL, MongoDB, Slack, Redis, Firebase, filesystem, Slack, and Google Cloud buckets (GCS), for Personally Identifiable Information (PII) and secrets.
2121

2222

2323
### Why "HAWK Eye"?
@@ -127,6 +127,11 @@ Note: If you don't provide any command, it will run all commands (firebase, fs,
127127
mongodb
128128
<td>Scan MongoDB profiles for PII and secrets data.</td>
129129
</tr>
130+
<tr>
131+
<td>
132+
slack
133+
<td>Scan slack profiles for PII and secrets data.</td>
134+
</tr>
130135
<tr>
131136
<td>
132137
postgresql
@@ -246,6 +251,16 @@ sources:
246251
- .docx
247252
- venv
248253
- node_modules
254+
255+
slack:
256+
slack_example:
257+
token: xoxp-XXXXXXXXXXXXXXXXXXXXXXXXX # get your slack app these permissiosn https://api.slack.com/methods/team.info and https://api.slack.com/methods/conversations.list
258+
channel_types: "public_channel,private_channel"
259+
# Optional: List of channel names to check
260+
# channel_names:
261+
# - general
262+
# - random
263+
249264
```
250265

251266
You can add or remove profiles from the connection.yml file as needed. You can also configure only one or two data sources if you don't need to scan all of them.

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ rich
44
mysql-connector-python
55
redis
66
firebase-admin
7+
slack-sdk
78
google-cloud-core
89
google-cloud-storage
910
pymongo==3.13.0

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
setup(
1212
name='hawk_scanner',
1313
version=VERSION,
14-
description='A powerful scanner to scan your Filesystem, S3, MongoDB, MySQL, PostgreSQL, Redis, Google Cloud Storage and Firebase storage for PII and sensitive data.',
14+
description='A powerful scanner to scan your Filesystem, S3, MongoDB, MySQL, PostgreSQL, Redis, Slack, Google Cloud Storage and Firebase storage for PII and sensitive data.',
1515
long_description=long_description,
1616
long_description_content_type="text/markdown",
1717
url='https://github.com/rohitcoder/hawk-eye',

0 commit comments

Comments
 (0)