77from slack_sdk .web .async_client import AsyncWebClient
88from slack_sdk .web .slack_response import SlackResponse
99from slack_sdk .errors import SlackApiError
10+ from slack_sdk .signature import SignatureVerifier
1011
1112from modules import taskmanager
1213
1617 SLACK_DM_TEMPLATE ,
1718 CALENDAR_TIMEZONE ,
1819 WATCHED_CHANNELS ,
20+ SLACK_SIGNING_SECRET ,
1921)
2022
2123from datetime import datetime
3840 "RAHHHHHHHHHHHHHHHHHHHHHHHH HOW DARE YOU :skeleton-shield-banging-here:"
3941)
4042
41- try :
42- client = AsyncWebClient (token = SLACK_API_TOKEN )
43- except Exception as e :
44- logger .error (f"Failed to initialize Slack client: { e } " )
45-
4643current_announcement : dict [str , str ] = {
4744 "content" : "Welcome to Jumpstart!" ,
4845 "user" : "Jumpstart" ,
5249}
5350
5451
52+ _slack_signature_verifier : SignatureVerifier | None = (
53+ SignatureVerifier (SLACK_SIGNING_SECRET ) if SLACK_SIGNING_SECRET else None
54+ )
55+
56+ try :
57+ client = AsyncWebClient (token = SLACK_API_TOKEN )
58+ except Exception as e :
59+ logger .error (f"Failed to initialize Slack client: { e } " )
60+
61+
62+ def is_valid_slack_request (request : Request , raw_body : bytes ) -> bool :
63+ """
64+ Validates Slack's request signature using the signing secret.
65+
66+ Args:
67+ request (Request): The request to be checked
68+ raw_body (bytes): The raw body
69+
70+ Returns
71+ bool: Whether or not it's verified
72+ """
73+
74+ if _slack_signature_verifier is None :
75+ logger .error ("Slack signing secret is not configured" )
76+ return False
77+
78+ try :
79+ return _slack_signature_verifier .is_valid_request (
80+ body = raw_body ,
81+ headers = dict (request .headers ),
82+ )
83+ except TypeError , ValueError :
84+ return False
85+
86+
5587def clean_text (raw : str ) -> str :
5688 """
5789 Strip Slack mrkdwn, HTML entities, and formatting characters.
@@ -186,7 +218,7 @@ async def request_upload_via_dm(user_id: str, announcement_text: str) -> None:
186218 logger .error (f"Error messaging user { user_id } : { e } " )
187219
188220
189- async def process_slack_events (request : Request ) -> dict [str , str ]:
221+ async def process_slack_events (body : dict ) -> dict [str , str ]:
190222 """
191223 Processes a slack event, logging and returning the result from the event
192224
@@ -198,7 +230,6 @@ async def process_slack_events(request: Request) -> dict[str, str]:
198230 """
199231
200232 try :
201- body : dict = await request .json ()
202233 logger .info (f"Received Slack event: { body } " )
203234
204235 event_amounts : int = get_event_retry_amount (body .get ("event_id" , None ))
@@ -208,12 +239,6 @@ async def process_slack_events(request: Request) -> dict[str, str]:
208239 )
209240 return ({"status" : "success" }, 200 )
210241
211- # Challenge from Bot Authentication
212- if request .headers .get ("content-type" ) == "application/json" :
213- if body .get ("type" ) == "url_verification" :
214- logger .info ("SLACK EVENT: Was a challenge!" )
215- return {"challenge" : body .get ("challenge" )}
216-
217242 event : dict = body .get ("event" , {})
218243
219244 if event .get ("subtype" , None ) is not None :
@@ -267,7 +292,9 @@ async def process_slack_message_actions(payload: str):
267292 user_id = form_json .get ("user" , {}).get ("id" )
268293
269294 username : str = await get_username (user_id )
270- username = username [:40 ] # Only get the first 40 characters so it fits on a single line
295+ username = username [
296+ :40
297+ ] # Only get the first 40 characters so it fits on a single line
271298
272299 add_announcement (message_object , username )
273300
0 commit comments