Skip to content

Commit cdea04d

Browse files
committed
Added more configuration options
1 parent ef19b79 commit cdea04d

2 files changed

Lines changed: 25 additions & 6 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ You can easily tweak any of the classes to your liking in two ways.
107107
- enable unit test running by providing a ```unittest.testSuite``` instance (pullerWebhookBlueprint)
108108
- enable logging by providing a ```logging.Logger``` instance
109109
- change blueprint name to avoid conflicts during blueprint registration
110+
- limit the blueprints to accept webhooks only from one git app or multiple
111+
- limit the blueprints to accept incoming webhooks only from whitelisted IPs
110112
- change the command used to invoke git (pullerWebhookBlueprint)
111113
- change the OS environment used by child git processes (pullerWebhookBlueprint)
112114
2. More advanced changes require creating a subclass from webhookBlueprint

gitWebhook/webhook.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,39 @@ def verifyGitlabRequest(request: Request, token:str) -> bool:
2222
"""Verify the GitLab token of a webhook request"""
2323
return request.headers.get(GITLAB_HEADER) == token
2424

25-
# TODO add IP whitelisting, limiting to one git type, automatic github IP whitelisting
25+
def verifyBasicAuth(request: Request, token:str) -> bool:
26+
"""Verify the basic authorization of a webhook request"""
27+
return str(request.authorization) == token
28+
29+
# TODO add automatic github IP whitelisting
2630

2731
class webhookBlueprint(Blueprint, gitWebhookBlueprintABC):
2832
"""Wrapper over the flask blueprint that creates an endpoint for receiving and processing git webhooks. Overwrite the processWebhook method to process the webhook data."""
2933

30-
def __init__(self, webhookToken:str | None, log:Logger | None = None, name:str="webhook", *args, **kwargs):
34+
def __init__(self, webhookToken:str | None, log:Logger | None = None, name:str="webhook", github:bool=True, gitlab:bool=True, gitea:bool=True, ipWhitelist:list[str] | None = None, *args, **kwargs):
3135
super().__init__(name, self.__class__.__name__, *args, **kwargs)
3236
self.log = log
3337
if webhookToken is None:
3438
if self.log is not None:
3539
self.log.warning("No webhook token provided. THIS IS VERY UNSAFE")
3640
self.webhookToken = webhookToken
41+
self.github = github
42+
self.gitlab = gitlab
43+
self.gitea = gitea
44+
self.ipWhitelist = ipWhitelist
3745
self.route("/", methods=["POST"])(self.receiveWebhook)
3846

3947
def receiveWebhook(self) -> Response:
4048
"""Receive webhook from GitHub and process it using the processWebhook method."""
49+
if self.ipWhitelist is not None:
50+
if request.remote_addr is None:
51+
if self.log is not None:
52+
self.log.warning("Received a request with no IP address")
53+
abort(403)
54+
if request.remote_addr not in self.ipWhitelist:
55+
if self.log is not None:
56+
self.log.warning(f"Received a request from an unauthorized IP address: {request.remote_addr}")
57+
abort(403)
4158
if self.log is not None:
4259
self.log.debug("Received a POST request to the webhook endpoint")
4360
#check if the content type is json
@@ -46,18 +63,18 @@ def receiveWebhook(self) -> Response:
4663
self.log.warning(f"A request with an invalid content type: {request.content_type}")
4764
abort(415)
4865
if self.webhookToken is not None: #verification
49-
if GITHUB_HEADER in request.headers:
66+
if GITHUB_HEADER in request.headers and self.github:
5067
if not verifyGithubRequest(request, self.webhookToken):
5168
if self.log is not None:
5269
self.log.warning("A request with an invalid GitHub signaturez")
5370
abort(401)
54-
elif GITLAB_HEADER in request.headers:
71+
elif GITLAB_HEADER in request.headers and self.github:
5572
if not verifyGitlabRequest(request, self.webhookToken):
5673
if self.log is not None:
5774
self.log.warning("A request with an invalid GitLab token")
5875
abort(401)
59-
elif request.authorization is not None: # basic authorization which is what Gitea uses
60-
if not str(request.authorization) == self.webhookToken:
76+
elif request.authorization is not None and self.gitea: # basic authorization which is what Gitea uses
77+
if not verifyBasicAuth(request, self.webhookToken):
6178
if self.log is not None:
6279
self.log.warning("A request with an invalid basic authorization")
6380
abort(401)

0 commit comments

Comments
 (0)