diff --git a/README.md b/README.md index 2b8d130..2eb7d03 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ docker build -t docbot . Then run it like this: ```sh -docker run -d -p5000:5000 -e GITHUB_TOKEN= --name docbot docbot +docker run -d -p5000:5000 -e GITHUB_TOKEN= -e GITHUB_SIGN_KEY= --name docbot docbot ``` To check that it works try to get `localhost:5000` - it will print the diff --git a/docbot/app.py b/docbot/app.py index fe020b7..698f6d0 100644 --- a/docbot/app.py +++ b/docbot/app.py @@ -2,10 +2,11 @@ from elasticapm.contrib.flask import ElasticAPM from prometheus_flask_exporter import PrometheusMetrics -from flask import Flask, request +from flask import Flask, Response, request from .handlers import webhook_handler, list_events_handler from .logging_config import LOGGING_CONFIG +from .utils import is_verified_signature logging.config.dictConfig(LOGGING_CONFIG) @@ -34,6 +35,11 @@ def index() -> str: labels={'event': lambda: request.headers.get('X-GitHub-Event', None)} ) def webhook() -> str: + if not is_verified_signature( + request.data, + request.headers.get('X-Hub-Signature-256', None) + ): + return Response(status=403) data: dict = request.json event: str = request.headers.get('X-GitHub-Event') extra = { diff --git a/docbot/settings.py b/docbot/settings.py index f3e03ba..827963b 100644 --- a/docbot/settings.py +++ b/docbot/settings.py @@ -1,7 +1,9 @@ import os token = os.environ.get('GITHUB_TOKEN') +github_signature = os.environ.get('GITHUB_SIGN_KEY') assert token is not None +assert github_signature is not None doc_requests = [' document\r\n', ' document\n'] bot_name = '@TarantoolBot' title_header = 'Title:' diff --git a/docbot/utils.py b/docbot/utils.py index c77367d..f13bc36 100644 --- a/docbot/utils.py +++ b/docbot/utils.py @@ -1,4 +1,6 @@ import datetime +import hashlib +import hmac from . import settings @@ -16,3 +18,24 @@ def create_event(author, action, body): last_events.append(result) if len(last_events) > settings.LAST_EVENTS_SIZE: last_events.pop(0) + +def is_verified_signature(body, signature): + """Verify that the payload was sent from GitHub by validating SHA256. + + Returns True if the request is authorized, otherwise False. + + Args: + body: original request body to verify + signature: header received from GitHub + """ + if not signature: + return False + hash_object = hmac.new( + settings.github_signature.encode('utf-8'), + msg=body, + digestmod=hashlib.sha256 + ) + expected_signature = "sha256=" + hash_object.hexdigest() + if not hmac.compare_digest(expected_signature, signature): + return False + return True