Skip to content

Commit 7aa415f

Browse files
fix: improve the robustness of the payload extract logic (#1464)
1 parent f1bc61f commit 7aa415f

File tree

2 files changed

+46
-21
lines changed

2 files changed

+46
-21
lines changed

slack_bolt/request/internals.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ def extract_enterprise_id(payload: Dict[str, Any]) -> Optional[str]:
6565
return extract_enterprise_id(payload["authorizations"][0])
6666
if "enterprise_id" in payload:
6767
return payload.get("enterprise_id")
68-
if payload.get("team") is not None and "enterprise_id" in payload["team"]:
68+
if isinstance(payload.get("team"), dict) and "enterprise_id" in payload["team"]:
6969
# In the case where the type is view_submission
7070
return payload["team"].get("enterprise_id")
71-
if payload.get("event") is not None:
71+
if isinstance(payload.get("event"), dict):
7272
return extract_enterprise_id(payload["event"])
7373
return None
7474

@@ -88,13 +88,13 @@ def extract_actor_enterprise_id(payload: Dict[str, Any]) -> Optional[str]:
8888

8989

9090
def extract_team_id(payload: Dict[str, Any]) -> Optional[str]:
91-
app_installed_team_id = payload.get("view", {}).get("app_installed_team_id")
92-
if app_installed_team_id is not None:
91+
view = payload.get("view")
92+
if isinstance(view, dict) and view.get("app_installed_team_id") is not None:
9393
# view_submission payloads can have `view.app_installed_team_id` when a modal view that was opened
9494
# in a different workspace via some operations inside a Slack Connect channel.
9595
# Note that the same for enterprise_id does not exist. When you need to know the enterprise_id as well,
9696
# you have to run some query toward your InstallationStore to know the org where the team_id belongs to.
97-
return app_installed_team_id
97+
return view["app_installed_team_id"]
9898
if payload.get("team") is not None:
9999
# With org-wide installations, payload.team in interactivity payloads can be None
100100
# You need to extract either payload.user.team_id or payload.view.team_id as below
@@ -109,12 +109,12 @@ def extract_team_id(payload: Dict[str, Any]) -> Optional[str]:
109109
return extract_team_id(payload["authorizations"][0])
110110
if "team_id" in payload:
111111
return payload.get("team_id")
112-
if payload.get("event") is not None:
112+
if isinstance(payload.get("event"), dict):
113113
return extract_team_id(payload["event"])
114-
if payload.get("user") is not None:
114+
if isinstance(payload.get("user"), dict):
115115
return payload["user"]["team_id"]
116-
if payload.get("view") is not None:
117-
return payload.get("view", {})["team_id"]
116+
if isinstance(payload.get("view"), dict):
117+
return payload["view"]["team_id"]
118118
return None
119119

120120

@@ -169,12 +169,12 @@ def extract_user_id(payload: Dict[str, Any]) -> Optional[str]:
169169
return user.get("id")
170170
if "user_id" in payload:
171171
return payload.get("user_id")
172-
if payload.get("event") is not None:
172+
if isinstance(payload.get("event"), dict):
173173
return extract_user_id(payload["event"])
174-
if payload.get("message") is not None:
174+
if isinstance(payload.get("message"), dict):
175175
# message_changed: body["event"]["message"]
176176
return extract_user_id(payload["message"])
177-
if payload.get("previous_message") is not None:
177+
if isinstance(payload.get("previous_message"), dict):
178178
# message_deleted: body["event"]["previous_message"]
179179
return extract_user_id(payload["previous_message"])
180180
return None
@@ -202,12 +202,12 @@ def extract_channel_id(payload: Dict[str, Any]) -> Optional[str]:
202202
return channel.get("id")
203203
if "channel_id" in payload:
204204
return payload.get("channel_id")
205-
if payload.get("event") is not None:
205+
if isinstance(payload.get("event"), dict):
206206
return extract_channel_id(payload["event"])
207-
if payload.get("item") is not None:
207+
if isinstance(payload.get("item"), dict):
208208
# reaction_added: body["event"]["item"]
209209
return extract_channel_id(payload["item"])
210-
if payload.get("assistant_thread") is not None:
210+
if isinstance(payload.get("assistant_thread"), dict):
211211
# assistant_thread_started
212212
return extract_channel_id(payload["assistant_thread"])
213213
return None
@@ -217,7 +217,7 @@ def extract_thread_ts(payload: Dict[str, Any]) -> Optional[str]:
217217
thread_ts = payload.get("thread_ts")
218218
if thread_ts is not None:
219219
return thread_ts
220-
if payload.get("event") is not None:
220+
if isinstance(payload.get("event"), dict):
221221
return extract_thread_ts(payload["event"])
222222
if isinstance(payload.get("assistant_thread"), dict):
223223
return extract_thread_ts(payload["assistant_thread"])
@@ -231,25 +231,25 @@ def extract_thread_ts(payload: Dict[str, Any]) -> Optional[str]:
231231
def extract_function_execution_id(payload: Dict[str, Any]) -> Optional[str]:
232232
if payload.get("function_execution_id") is not None:
233233
return payload.get("function_execution_id")
234-
if payload.get("event") is not None:
234+
if isinstance(payload.get("event"), dict):
235235
return extract_function_execution_id(payload["event"])
236-
if payload.get("function_data") is not None:
236+
if isinstance(payload.get("function_data"), dict):
237237
return payload["function_data"].get("execution_id")
238238
return None
239239

240240

241241
def extract_function_bot_access_token(payload: Dict[str, Any]) -> Optional[str]:
242242
if payload.get("bot_access_token") is not None:
243243
return payload.get("bot_access_token")
244-
if payload.get("event") is not None:
244+
if isinstance(payload.get("event"), dict):
245245
return payload["event"].get("bot_access_token")
246246
return None
247247

248248

249249
def extract_function_inputs(payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
250-
if payload.get("event") is not None:
250+
if isinstance(payload.get("event"), dict):
251251
return payload["event"].get("inputs")
252-
if payload.get("function_data") is not None:
252+
if isinstance(payload.get("function_data"), dict):
253253
return payload["function_data"].get("inputs")
254254
return None
255255

tests/slack_bolt/request/test_internals.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,3 +1248,28 @@ def test_slack_connect_patterns(self):
12481248
assert extract_actor_enterprise_id(request) == actor_enterprise_id
12491249
assert extract_actor_team_id(request) == actor_team_id
12501250
assert extract_actor_user_id(request) == actor_user_id
1251+
1252+
def test_extraction_functions_invalid_dict_keys(self):
1253+
invalid_payloads = {
1254+
"event": {"event": "some_event_type"},
1255+
"user": {"user": "U12345"},
1256+
"team": {"team": "T12345"},
1257+
"view": {"view": "V12345"},
1258+
"message": {"message": "some text"},
1259+
"item": {"item": "item_id"},
1260+
"function_data": {"function_data": "fd_123"},
1261+
"assistant_thread": {"assistant_thread": "at_123"},
1262+
"previous_message": {"previous_message": "old_msg"},
1263+
}
1264+
1265+
for _, payload in invalid_payloads.items():
1266+
# We only verify no TypeError/AttributeError is raised and that functions which
1267+
# would try to subscript the string value return None instead of crashing.
1268+
extract_enterprise_id(payload)
1269+
extract_team_id(payload)
1270+
extract_user_id(payload)
1271+
extract_channel_id(payload)
1272+
extract_thread_ts(payload)
1273+
extract_function_execution_id(payload)
1274+
extract_function_bot_access_token(payload)
1275+
extract_function_inputs(payload)

0 commit comments

Comments
 (0)