Skip to content

Commit a54bf56

Browse files
committed
Require signatures for ssl_check request verification
1 parent ae0ba57 commit a54bf56

6 files changed

Lines changed: 130 additions & 1 deletion

File tree

slack_bolt/middleware/request_verification/request_verification.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def process(
4949

5050
@staticmethod
5151
def _can_skip(mode: str, body: Dict[str, Any]) -> bool:
52-
return mode == "socket_mode" or (body is not None and body.get("ssl_check") == "1")
52+
return mode == "socket_mode"
5353

5454
@staticmethod
5555
def _build_error_response() -> BoltResponse:

tests/adapter_tests/wsgi/test_wsgi_http.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,47 @@ def command_handler(ack):
8989
assert response.headers.get("content-type") == "text/plain;charset=utf-8"
9090
assert_auth_test_count(self, 1)
9191

92+
def test_ssl_check_param_does_not_bypass_request_verification(self):
93+
app = App(
94+
client=self.web_client,
95+
signing_secret=self.signing_secret,
96+
ssl_check_enabled=False,
97+
)
98+
command_called = False
99+
100+
def command_handler(ack):
101+
nonlocal command_called
102+
command_called = True
103+
ack()
104+
105+
app.command("/hello-world")(command_handler)
106+
107+
body = (
108+
"token=verification_token"
109+
"&team_id=T111"
110+
"&team_domain=test-domain"
111+
"&channel_id=C111"
112+
"&channel_name=random"
113+
"&user_id=W111"
114+
"&user_name=primary-owner"
115+
"&command=%2Fhello-world"
116+
"&text=Hi"
117+
"&enterprise_id=E111"
118+
"&enterprise_name=Org+Name"
119+
"&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT111%2F111%2Fxxxxx"
120+
"&trigger_id=111.111.xxx"
121+
"&ssl_check=1"
122+
)
123+
headers = self.build_raw_headers("0", body)
124+
headers["x-slack-signature"] = "v0=invalid"
125+
126+
wsgi_server = WsgiTestServer(SlackRequestHandler(app))
127+
response = wsgi_server.http(method="POST", headers=headers, body=body)
128+
129+
assert response.status == "401 Unauthorized"
130+
assert response.body == """{"error": "invalid request"}"""
131+
assert command_called is False
132+
92133
def test_events(self):
93134
app = App(
94135
client=self.web_client,

tests/scenario_tests/test_slash_command.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,34 @@ def test_failure(self):
9393
assert response.status == 404
9494
assert_auth_test_count(self, 1)
9595

96+
def test_ssl_check_param_does_not_bypass_request_verification(self):
97+
app = App(
98+
client=self.web_client,
99+
signing_secret=self.signing_secret,
100+
ssl_check_enabled=False,
101+
)
102+
command_called = False
103+
104+
def command_handler(ack):
105+
nonlocal command_called
106+
command_called = True
107+
ack()
108+
109+
app.command("/hello-world")(command_handler)
110+
111+
request = BoltRequest(
112+
body=f"{slash_command_body}&ssl_check=1",
113+
headers={
114+
"content-type": ["application/x-www-form-urlencoded"],
115+
"x-slack-signature": ["v0=invalid"],
116+
"x-slack-request-timestamp": ["0"],
117+
},
118+
)
119+
response = app.dispatch(request)
120+
assert response.status == 401
121+
assert response.body == """{"error": "invalid request"}"""
122+
assert command_called is False
123+
96124

97125
slash_command_body = (
98126
"token=verification_token"

tests/scenario_tests_async/test_slash_command.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,35 @@ async def test_failure(self):
100100
assert response.status == 404
101101
await assert_auth_test_count_async(self, 1)
102102

103+
@pytest.mark.asyncio
104+
async def test_ssl_check_param_does_not_bypass_request_verification(self):
105+
app = AsyncApp(
106+
client=self.web_client,
107+
signing_secret=self.signing_secret,
108+
ssl_check_enabled=False,
109+
)
110+
command_called = False
111+
112+
async def command_handler(ack):
113+
nonlocal command_called
114+
command_called = True
115+
await ack()
116+
117+
app.command("/hello-world")(command_handler)
118+
119+
request = AsyncBoltRequest(
120+
body=f"{slash_command_body}&ssl_check=1",
121+
headers={
122+
"content-type": ["application/x-www-form-urlencoded"],
123+
"x-slack-signature": ["v0=invalid"],
124+
"x-slack-request-timestamp": ["0"],
125+
},
126+
)
127+
response = await app.async_dispatch(request)
128+
assert response.status == 401
129+
assert response.body == """{"error": "invalid request"}"""
130+
assert command_called is False
131+
103132

104133
slash_command_body = (
105134
"token=verification_token"

tests/slack_bolt/middleware/request_verification/test_request_verification.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,18 @@ def test_invalid(self):
4545
resp = middleware.process(req=req, resp=resp, next=next)
4646
assert resp.status == 401
4747
assert resp.body == """{"error": "invalid request"}"""
48+
49+
def test_ssl_check_param_requires_valid_signature(self):
50+
middleware = RequestVerification(signing_secret=self.signing_secret)
51+
req = BoltRequest(
52+
body="token=random&ssl_check=1",
53+
headers={
54+
"content-type": ["application/x-www-form-urlencoded"],
55+
"x-slack-signature": ["v0=invalid"],
56+
"x-slack-request-timestamp": ["0"],
57+
},
58+
)
59+
resp = BoltResponse(status=404)
60+
resp = middleware.process(req=req, resp=resp, next=next)
61+
assert resp.status == 401
62+
assert resp.body == """{"error": "invalid request"}"""

tests/slack_bolt_async/middleware/request_verification/test_request_verification.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,19 @@ async def test_invalid(self):
5050
resp = await middleware.async_process(req=req, resp=resp, next=next)
5151
assert resp.status == 401
5252
assert resp.body == """{"error": "invalid request"}"""
53+
54+
@pytest.mark.asyncio
55+
async def test_ssl_check_param_requires_valid_signature(self):
56+
middleware = AsyncRequestVerification(signing_secret="secret")
57+
req = AsyncBoltRequest(
58+
body="token=random&ssl_check=1",
59+
headers={
60+
"content-type": ["application/x-www-form-urlencoded"],
61+
"x-slack-signature": ["v0=invalid"],
62+
"x-slack-request-timestamp": ["0"],
63+
},
64+
)
65+
resp = BoltResponse(status=404)
66+
resp = await middleware.async_process(req=req, resp=resp, next=next)
67+
assert resp.status == 401
68+
assert resp.body == """{"error": "invalid request"}"""

0 commit comments

Comments
 (0)