Skip to content

Commit 6533b6e

Browse files
committed
💯% test coverage
1 parent e3f70fd commit 6533b6e

File tree

7 files changed

+92
-16
lines changed

7 files changed

+92
-16
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ else
2121
DOCKER_VOLUMES = -v $(shell pwd):/app
2222
DOCKER_VOLUMES += -v /app/.eggs
2323
DOCKER_VOLUMES += -v /app/.pytest_cache
24-
DOCKER_VOLUMES += -v /app/.slacktools.egg-info
24+
DOCKER_VOLUMES += -v /app/slacktools.egg-info
2525
endif
2626

2727
test-%:

README.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ Roadmap
7272

7373
- Wider python support? This lib only `supports python 3 <.travis.yml>`_ right
7474
now. However, if someone needs it, I can add python 2 compatibility.
75-
- 100% test coverage
7675
- Friendly message builder API?
7776
- Build deep links into clients
7877

src/slacktools/authorization.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ class SignatureVersionException(ValueError):
88
pass
99

1010

11-
def verify_signature(slack_signing_secret: str,
12-
timestamp: int,
11+
def verify_signature(signing_secret: str,
12+
request_timestamp: int,
1313
body: str,
14-
slack_signature: str):
14+
signature: str,
15+
current_timestamp: int = None) -> bool:
1516
"""
1617
Verifies a signature from X-Slack-Signature and X-Slack-Request-Timestamp
1718
@@ -24,29 +25,33 @@ def verify_signature(slack_signing_secret: str,
2425
:raises: SignatureVersionException if the signature version is something other than v0
2526
"""
2627

27-
if not slack_signing_secret:
28+
if not signing_secret:
2829
raise ValueError('slack_signing_secret not provided')
29-
if not timestamp or not isinstance(timestamp, int):
30+
if not request_timestamp or not isinstance(request_timestamp, int):
3031
raise ValueError('timestamp is not good int')
3132
if not body:
3233
raise ValueError('body not provided')
33-
if not slack_signature:
34+
if not signature:
3435
raise ValueError('signature not provided')
35-
if not slack_signature.startswith('v0'):
36+
if not signature.startswith('v0'):
3637
raise SignatureVersionException(
37-
"expected the signature to be version 'v0' but got '{}'".format(slack_signature[:2]))
38+
"expected the signature to be version 'v0' but got '{}'".format(signature[:2]))
3839

39-
if abs(time() - timestamp) > 60 * 5:
40+
if current_timestamp is None:
41+
current_timestamp = int(time())
42+
43+
if abs(current_timestamp - request_timestamp) > 60 * 5:
4044
# The request timestamp is more than five minutes from local time.
4145
# It could be a replay attack, so let's ignore it.
4246
return False
4347

44-
sig_basestring = "v0:{}:{}".format(timestamp, body)
48+
return hmac.compare_digest(make_signature(signing_secret, current_timestamp, body), signature)
49+
4550

46-
my_signature = 'v0=' + hmac.new(
47-
slack_signing_secret.encode(),
51+
def make_signature(signing_secret: str, request_timestamp: int, body: str) -> str:
52+
sig_basestring = "v0:{}:{}".format(request_timestamp, body)
53+
return 'v0=' + hmac.new(
54+
signing_secret.encode(),
4855
sig_basestring.encode(),
4956
sha256
5057
).hexdigest()
51-
52-
return hmac.compare_digest(my_signature, slack_signature)

tests/arguments.py

Whitespace-only changes.

tests/test_arguments.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import pytest
2+
from slacktools.arguments import SlackArgumentParser, SlackArgumentParserException
3+
4+
def test_arguments():
5+
"""haha what a useless test"""
6+
with pytest.raises(SlackArgumentParserException):
7+
p = SlackArgumentParser()
8+
p.error('ayy')

tests/test_authorization.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import pytest
2+
from slacktools.authorization import verify_signature, make_signature, SignatureVersionException
3+
from time import time
4+
5+
def test_verify_signature():
6+
with pytest.raises(ValueError):
7+
verify_signature(None, 1111, 'string', 'v0=ayy')
8+
with pytest.raises(ValueError):
9+
verify_signature('secret', None, 'string', 'v0=ayy')
10+
with pytest.raises(ValueError):
11+
verify_signature('secret', 1111, None, 'v0=ayy')
12+
with pytest.raises(ValueError):
13+
verify_signature('secret', 1111, 'body', None)
14+
with pytest.raises(SignatureVersionException):
15+
verify_signature('secret', 1111, 'body', 'something bogus')
16+
17+
ss = '8f742231b10e8888abcd99yyyzzz85a5'
18+
assert verify_signature(signing_secret=ss,
19+
request_timestamp=1531420618,
20+
body='token=xyzz0WbapA4vBCDEFasx0q6G&team_id=T1DC2JH3J&team_domain=testteamnow&channel_id=G8PSS9T3V&channel_name=foobar&user_id=U2CERLKJA&user_name=roadrunner&command=%2Fwebhook-collect&text=&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT1DC2JH3J%2F397700885554%2F96rGlfmibIGlgcZRskXaIFfN&trigger_id=398738663015.47445629121.803a0bc887a14d10d2c447fce8b6703c',
21+
signature='v0=a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503',
22+
current_timestamp=1531420618)
23+
24+
# bogus key
25+
assert not verify_signature(signing_secret='bogus',
26+
request_timestamp=1531420618,
27+
body='token=xyzz0WbapA4vBCDEFasx0q6G&team_id=T1DC2JH3J&team_domain=testteamnow&channel_id=G8PSS9T3V&channel_name=foobar&user_id=U2CERLKJA&user_name=roadrunner&command=%2Fwebhook-collect&text=&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT1DC2JH3J%2F397700885554%2F96rGlfmibIGlgcZRskXaIFfN&trigger_id=398738663015.47445629121.803a0bc887a14d10d2c447fce8b6703c',
28+
signature='v0=a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503',
29+
current_timestamp=1531420618)
30+
31+
# replay attacks
32+
assert not verify_signature(signing_secret=ss,
33+
request_timestamp=1531420618,
34+
body='token=xyzz0WbapA4vBCDEFasx0q6G&team_id=T1DC2JH3J&team_domain=testteamnow&channel_id=G8PSS9T3V&channel_name=foobar&user_id=U2CERLKJA&user_name=roadrunner&command=%2Fwebhook-collect&text=&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT1DC2JH3J%2F397700885554%2F96rGlfmibIGlgcZRskXaIFfN&trigger_id=398738663015.47445629121.803a0bc887a14d10d2c447fce8b6703c',
35+
signature='v0=a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503',
36+
current_timestamp=1531420618 + (60*6))
37+
38+
t = int(time())
39+
s1 = make_signature(ss, t, 'ayy lmao')
40+
41+
assert verify_signature(signing_secret=ss,
42+
request_timestamp=t,
43+
body='ayy lmao',
44+
signature=s1)

tests/test_chat.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from slacktools.chat import send, reply, send_factory, send_ephemeral, send_ephemeral_factory
2+
from unittest.mock import MagicMock
3+
4+
class FakeClient:
5+
def api_call(self, *args, **kwargs):
6+
return args, kwargs
7+
8+
def test_send():
9+
"""kinda useless tests but whatever"""
10+
slack_client = FakeClient()
11+
assert send(slack_client, 'general', 'ayy lmao')[1]['channel'] == 'general'
12+
assert send(slack_client, 'general', 'ayy lmao')[1]['text'] == 'ayy lmao'
13+
14+
assert reply(slack_client, {'channel': 'general', 'user': 'UXXXYYYY'}, 'ayy lmao')[1]['channel'] == 'general'
15+
assert reply(slack_client, {'channel': 'general', 'user': 'UXXXYYYY'}, 'ayy lmao')[1]['text'] == '<@UXXXYYYY> ayy lmao'
16+
17+
assert send_factory(slack_client, 'general')('ayy')[1]['channel'] == 'general'
18+
assert send_factory(slack_client, 'general')('ayy')[1]['text'] == 'ayy'
19+
20+
assert send_ephemeral_factory(slack_client, 'general', 'UXXXYYYY')('ayy')[1]['user'] == 'UXXXYYYY'

0 commit comments

Comments
 (0)