Skip to content

Commit 0c7c90f

Browse files
committed
Added tests
1 parent 73c9b81 commit 0c7c90f

File tree

3 files changed

+106
-3
lines changed

3 files changed

+106
-3
lines changed

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ dependencies = [
1919
[project.urls]
2020
Homepage = "https://github.com/TCA166/gitWebhook"
2121

22+
[options]
23+
package_dir = src
24+

src/webhook.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
GITLAB_HEADER = "X-Gitlab-Token"
1111

12-
def verifyGithubSignature(request: Request, token:str) -> bool:
12+
def verifyGithubRequest(request: Request, token:str) -> bool:
1313
"""Verify the GitHub signature of a webhook request"""
1414
signature = request.headers.get(GITHUB_HEADER)
1515
if signature is None:
@@ -18,6 +18,10 @@ def verifyGithubSignature(request: Request, token:str) -> bool:
1818
expected_signature = "sha256=" + hash_object.hexdigest()
1919
return signature == expected_signature
2020

21+
def verifyGitlabRequest(request: Request, token:str) -> bool:
22+
"""Verify the GitLab token of a webhook request"""
23+
return request.headers.get(GITLAB_HEADER) == token
24+
2125
class webhookBlueprint(Blueprint, gitWebhookBlueprintABC):
2226
"""Wrapper over the flask blueprint that creates an endpoint for receiving and processing git webhooks. Overwrite the processWebhook method to process the webhook data."""
2327
def __init__(self, webhookToken:str | None, log:Logger | None = None, name:str="webhook", *args, **kwargs):
@@ -39,12 +43,12 @@ def receiveWebhook(self) -> Response:
3943
abort(415)
4044
if self.webhookToken is not None: #verification
4145
if GITHUB_HEADER in request.headers:
42-
if not verifyGithubSignature(request, self.webhookToken):
46+
if not verifyGithubRequest(request, self.webhookToken):
4347
if self.log is not None:
4448
self.log.warning("A request with an invalid GitHub signature")
4549
abort(401)
4650
elif GITLAB_HEADER:
47-
if request.headers.get(GITLAB_HEADER) != self.webhookToken:
51+
if not verifyGitlabRequest(request, self.webhookToken):
4852
if self.log is not None:
4953
self.log.warning("A request with an invalid GitLab token")
5054
abort(401)

test.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import unittest
2+
from flask import Request, request, Flask
3+
from src.webhook import verifyGithubRequest, verifyGitlabRequest, webhookBlueprint
4+
from src.functionWebhook import functionWebhookBlueprint
5+
import random
6+
from hmac import new as hmacNew
7+
from hashlib import sha256
8+
9+
VALID_TOKEN = "1234"
10+
11+
class testingRequest(Request):
12+
def __init__(self, token:str):
13+
self.data = random.randbytes(50)
14+
hash_object = hmacNew(token.encode("utf-8"), msg=self.data, digestmod=sha256)
15+
self.headers = {"X-Hub-Signature-256": f"sha256={hash_object.hexdigest()}", "X-Gitlab-Token":token}
16+
def get_data(self):
17+
return self.data
18+
19+
class TestVerification(unittest.TestCase):
20+
def setUp(self) -> None:
21+
self.validRequest = testingRequest(VALID_TOKEN)
22+
self.invalidRequest = testingRequest("12345")
23+
return super().setUp()
24+
def testVerifyGithubRequest(self):
25+
self.assertTrue(verifyGithubRequest(self.validRequest, VALID_TOKEN))
26+
self.assertFalse(verifyGithubRequest(self.validRequest, "12345"))
27+
self.assertFalse(verifyGithubRequest(self.invalidRequest, VALID_TOKEN))
28+
self.assertFalse(verifyGithubRequest(self.invalidRequest, "12346"))
29+
def testVerifyGitlabRequest(self):
30+
self.assertTrue(verifyGitlabRequest(self.validRequest, "1234"))
31+
self.assertFalse(verifyGitlabRequest(self.validRequest, "12345"))
32+
self.assertFalse(verifyGitlabRequest(self.invalidRequest, "1234"))
33+
34+
class TestWehbookBlueprint(unittest.TestCase):
35+
def setUp(self) -> None:
36+
self.webhook = webhookBlueprint(VALID_TOKEN, name="valid")
37+
self.app = Flask(__name__)
38+
self.app.register_blueprint(self.webhook, url_prefix="/valid")
39+
self.webhookNoToken = webhookBlueprint(None, name="invalid")
40+
self.app.register_blueprint(self.webhookNoToken, url_prefix="/noToken")
41+
self.app.config.update({"TESTING": True})
42+
self.client = self.app.test_client()
43+
return super().setUp()
44+
def testReceiveWebhookValid(self):
45+
request = testingRequest(VALID_TOKEN)
46+
resp = self.client.post("/valid/", headers=request.headers, data=request.data)
47+
self.assertEqual(resp.status_code, 415)
48+
request.headers["Content-Type"] = "application/json"
49+
resp = self.client.post("/valid/", headers=request.headers, data=request.data)
50+
self.assertEqual(resp.status_code, 400) #no json
51+
def testReceiveWebhookInvalidNoCheck(self):
52+
request = testingRequest("123")
53+
resp = self.client.post("/noToken/", headers=request.headers, data=request.data)
54+
self.assertEqual(resp.status_code, 415)
55+
request.headers["Content-Type"] = "application/json"
56+
resp = self.client.post("/noToken/", headers=request.headers, data=request.data)
57+
self.assertEqual(resp.status_code, 400)
58+
def testReceiveWebhookInvalidCheck(self):
59+
request = testingRequest("123")
60+
resp = self.client.post("/valid/", headers=request.headers, data=request.data)
61+
self.assertEqual(resp.status_code, 415)
62+
request.headers["Content-Type"] = "application/json"
63+
resp = self.client.post("/valid/", headers=request.headers, data=request.data)
64+
self.assertEqual(resp.status_code, 401)
65+
def testProcessWebhook(self):
66+
self.assertEqual(self.webhook.processWebhook({"test":"test"}), (200, "OK"))
67+
self.assertEqual(self.webhookNoToken.processWebhook({"test":"test"}), (200, "OK"))
68+
69+
class TestFunctionWebhookBlueprint(unittest.TestCase):
70+
def setUp(self) -> None:
71+
func = lambda x: x != x
72+
self.webhook = functionWebhookBlueprint(VALID_TOKEN, name="valid", functions=[func])
73+
self.app = Flask(__name__)
74+
self.app.register_blueprint(self.webhook, url_prefix="/valid")
75+
self.app.config.update({"TESTING": True})
76+
self.client = self.app.test_client()
77+
return super().setUp()
78+
def testReceiveWebhookValid(self):
79+
request = testingRequest(VALID_TOKEN)
80+
resp = self.client.post("/valid/", headers=request.headers, data=request.data)
81+
self.assertEqual(resp.status_code, 415)
82+
request.headers["Content-Type"] = "application/json"
83+
resp = self.client.post("/valid/", headers=request.headers, data=request.data)
84+
self.assertEqual(resp.status_code, 400) #no json
85+
def testReceiveWebhookInvalidCheck(self):
86+
request = testingRequest("123")
87+
resp = self.client.post("/valid/", headers=request.headers, data=request.data)
88+
self.assertEqual(resp.status_code, 415)
89+
request.headers["Content-Type"] = "application/json"
90+
resp = self.client.post("/valid/", headers=request.headers, data=request.data)
91+
self.assertEqual(resp.status_code, 401)
92+
def testProcessWebhook(self):
93+
self.assertEqual(self.webhook.processWebhook({"test":"test"}), (400, "Function <lambda> returned false"))
94+
95+
if __name__ == "__main__":
96+
unittest.main()

0 commit comments

Comments
 (0)