Skip to content

Commit bbc9e3c

Browse files
committed
Use callables for webhook event filters and require webhook secret
Replace string-based EVENT_FILTER expressions with direct callables so event filtering no longer relies on dynamic evaluation of formatted strings. Require GITHUB_WEBHOOK_SECRET to be configured at startup and always validate the X-Hub-Signature header on incoming requests. Signed-off-by: Mikaël Barbero <mikael.barbero@eclipse-foundation.org>
1 parent 7bb5b9c commit bbc9e3c

1 file changed

Lines changed: 15 additions & 15 deletions

File tree

otterdog/webapp/webhook/github_webhook.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ def init_app(self, app) -> None:
3333
self._endpoint = app.config["GITHUB_WEBHOOK_ENDPOINT"]
3434

3535
secret = app.config["GITHUB_WEBHOOK_SECRET"]
36-
if secret is not None and not isinstance(secret, bytes):
36+
if secret is None or secret == "" or secret == b"":
37+
raise RuntimeError("GITHUB_WEBHOOK_SECRET is not configured.")
38+
if not isinstance(secret, bytes):
3739
secret = secret.encode("utf-8")
3840
self._secret = secret
3941

@@ -59,23 +61,21 @@ def decorator(func):
5961
return decorator
6062

6163
async def _get_digest(self):
62-
"""Return message digest if a secret key was provided"""
64+
"""Return message digest."""
6365

64-
if self._secret:
65-
return hmac.new(self._secret, await request.data, hashlib.sha1).hexdigest()
66+
return hmac.new(self._secret, await request.data, hashlib.sha1).hexdigest()
6667

6768
async def _post_receive(self):
6869
"""Callback from Flask"""
6970

7071
digest = await self._get_digest()
7172

72-
if digest is not None:
73-
sig_parts = _get_header("X-Hub-Signature").split("=", 1)
74-
if not isinstance(digest, str):
75-
digest = str(digest)
73+
sig_parts = _get_header("X-Hub-Signature").split("=", 1)
74+
if not isinstance(digest, str):
75+
digest = str(digest)
7676

77-
if len(sig_parts) < 2 or sig_parts[0] != "sha1" or not hmac.compare_digest(sig_parts[1], digest):
78-
abort(400, "Invalid signature")
77+
if len(sig_parts) < 2 or sig_parts[0] != "sha1" or not hmac.compare_digest(sig_parts[1], digest):
78+
abort(400, "Invalid signature")
7979

8080
event_type = _get_header("X-Github-Event")
8181
content_type = _get_header("content-type")
@@ -150,16 +150,16 @@ def _get_header(key):
150150

151151

152152
EVENT_FILTER = {
153-
"workflow_job": "'{action}' == 'queued'",
154-
"workflow_run": "'{action}' == 'completed'",
153+
"workflow_job": lambda data: data.get("action") == "queued",
154+
"workflow_run": lambda data: data.get("action") == "completed",
155155
}
156156

157157

158158
def _log_event(event_type, data) -> bool:
159-
try:
160-
return eval(EVENT_FILTER[event_type].format(**data)) # noqa: S307
161-
except KeyError:
159+
event_filter = EVENT_FILTER.get(event_type)
160+
if event_filter is None:
162161
return True
162+
return event_filter(data)
163163

164164

165165
def _format_event(event_type, data):

0 commit comments

Comments
 (0)