Merge pull request #43 from sbidoul/verify-github-webbhook-sig
Verify GitHub webhook signature
This commit is contained in:
commit
7c80d7f074
6 changed files with 44 additions and 2 deletions
|
|
@ -7,4 +7,5 @@ RUNBOAT_BUILD_ENV={}
|
|||
RUNBOAT_BUILD_SECRET_ENV={"PGPASSWORD": "thepgpassword"}
|
||||
RUNBOAT_BUILD_TEMPLATE_VARS={"storageClassName": "my-storage-class"}
|
||||
RUNBOAT_GITHUB_TOKEN=
|
||||
RUNBOAT_GITHUB_WEBHOOK_SECRET=
|
||||
RUNBOAT_LOG_CONFIG=log-config.yaml
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ dynamic = ["version", "description"]
|
|||
[project.optional-dependencies]
|
||||
test = [
|
||||
"pytest",
|
||||
"pytest-asyncio",
|
||||
"pytest-cov",
|
||||
"pytest-dotenv",
|
||||
"pytest-mock",
|
||||
|
|
@ -42,7 +43,7 @@ profile = 'black'
|
|||
[tool.pytest.ini_options]
|
||||
env_override_existing_values = 1
|
||||
env_files = [".env.test"]
|
||||
|
||||
asyncio_mode = "strict"
|
||||
# flake8 config is in .flake8
|
||||
|
||||
[tool.mypy]
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ pluggy==1.0.0
|
|||
py==1.11.0
|
||||
pyparsing==3.0.7
|
||||
pytest==6.2.5
|
||||
pytest-asyncio==0.17.2
|
||||
pytest-cov==3.0.0
|
||||
pytest-dotenv==0.5.2
|
||||
pytest-mock==3.6.1
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ class Settings(BaseSettings):
|
|||
# The token to use for the GitHub api calls (to query branches and pull requests,
|
||||
# and report build statuses).
|
||||
github_token: str | None
|
||||
# The secret used to verify GitHub webhook signatures
|
||||
github_webhook_secret: bytes | None
|
||||
# The file with the python logging configuration to use for the runboat controller.
|
||||
log_config: str | None
|
||||
# The base url where the runboat UI and API is exposed on internet.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import hmac
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, BackgroundTasks, Header, Request
|
||||
|
|
@ -11,13 +12,33 @@ _logger = logging.getLogger(__name__)
|
|||
router = APIRouter()
|
||||
|
||||
|
||||
def _verify_github_signature(
|
||||
x_hub_signature_256: str | None, secret: bytes | None, body: bytes
|
||||
) -> bool:
|
||||
if not secret:
|
||||
return True
|
||||
if not x_hub_signature_256:
|
||||
_logger.warning("Got payload without X-Hub-Signature-256")
|
||||
return False
|
||||
signature = "sha256=" + hmac.new(secret, body, "sha256").hexdigest()
|
||||
if not hmac.compare_digest(signature, x_hub_signature_256):
|
||||
_logger.warning("Got payload with invalid X-Hub-Signature-256")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@router.post("/webhooks/github")
|
||||
async def receive_payload(
|
||||
background_tasks: BackgroundTasks,
|
||||
request: Request,
|
||||
x_github_event: str = Header(...),
|
||||
x_hub_signature_256: str | None = Header(None),
|
||||
) -> None:
|
||||
# TODO check x-hub-signature
|
||||
body = await request.body()
|
||||
if not _verify_github_signature(
|
||||
x_hub_signature_256, settings.github_webhook_secret, body
|
||||
):
|
||||
return
|
||||
payload = await request.json()
|
||||
if x_github_event == "pull_request":
|
||||
repo = payload["repository"]["full_name"]
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from pytest_mock import MockerFixture
|
|||
from runboat.app import app
|
||||
from runboat.controller import controller
|
||||
from runboat.github import CommitInfo
|
||||
from runboat.webhooks import _verify_github_signature
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
|
@ -137,3 +138,18 @@ def test_webhook_github_pr_close(mocker: MockerFixture) -> None:
|
|||
repo="oca/mis-builder",
|
||||
pr=381,
|
||||
)
|
||||
|
||||
|
||||
def test_verify_github_signature() -> None:
|
||||
assert _verify_github_signature(None, None, b"body") # no secret configured, ok
|
||||
assert not _verify_github_signature(
|
||||
None, b"secret", b"body"
|
||||
) # no X-Hub-Signature-256
|
||||
assert not _verify_github_signature(
|
||||
"sha256=invalid-sig", b"secret", b"body"
|
||||
) # no X-Hub-Signature-256
|
||||
assert _verify_github_signature(
|
||||
"sha256=dc46983557fea127b43af721467eb9b3fde2338fe3e14f51952aa8478c13d355",
|
||||
b"secret",
|
||||
b"body",
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue