webhook
The atago project wrote these specs on its own initiative and runs them in its own CI, to exercise atago against a real program. They are not webhook’s official test suite, and the webhook project is not affiliated with atago.
Summary #
1 suite · 5 scenarios
Contents #
- webhook (self-hosted webhook receiver) — 5 scenarios
webhook (self-hosted webhook receiver) #
webhook turns an HTTP request into a command execution, which makes it a piece of security-relevant plumbing: everything about it is a question of what should and should not run.
The happy path is verified by its effect, not its response — the triggered command writes a file, and that file is checked on disk.
The refusals are pinned just as carefully. When a trigger rule is not satisfied the command must not run at all, which is asserted by the absence of its effect rather than by the status code. An HMAC signature is verified against an independently computed value, so a forged one is rejected. The allowed-methods list rejects the wrong verb. And a command that exits non-zero surfaces as a 500 instead of being reported as success.
Source: test/e2e/thirdparty/webhook/webhook.atago.yaml
Scenario: a post runs the command, returns its output, and writes its file #
Given #
- Background service
webhookis started:webhook -hooks hooks.json -ip 127.0.0.1 -port 18094. - Fixture file
handler.shis created. - Fixture file
hooks.jsonis created.
Inputs #
Fixture handler.sh:
#!/bin/sh
printf 'handled %s\n' "$1" > out.txt
printf 'ran for %s' "$1"
Fixture hooks.json:
[
{
"id": "greet",
"execute-command": "${workdir}/handler.sh",
"command-working-directory": "${workdir}",
"include-command-output-in-response": true,
"pass-arguments-to-command": [
{ "source": "payload", "name": "name" }
]
}
]
When #
# HTTP POST /hooks/greet
# HTTP POST /hooks/nope
Then #
- after
HTTP POST /hooks/greet:- HTTP status is
200 - body contains
ran for Alice - file
out.txtcontainshandled Alice
- HTTP status is
- after
HTTP POST /hooks/nope:- HTTP status is
404
- HTTP status is
Scenario: a trigger-rule gates execution and blocks it when unsatisfied #
Given #
- Background service
webhookis started:webhook -hooks hooks.json -ip 127.0.0.1 -port 18095. - Fixture file
handler.shis created. - Fixture file
hooks.jsonis created.
Inputs #
Fixture handler.sh:
#!/bin/sh
printf 'executed\n' > ran.txt
printf 'ok'
Fixture hooks.json:
[
{
"id": "guarded",
"execute-command": "${workdir}/handler.sh",
"command-working-directory": "${workdir}",
"include-command-output-in-response": true,
"trigger-rule": {
"match": {
"type": "value",
"value": "open-sesame",
"parameter": { "source": "payload", "name": "token" }
}
}
}
]
When #
# HTTP POST /hooks/guarded
# HTTP POST /hooks/guarded
Then #
- after
HTTP POST /hooks/guarded:- HTTP status is
200 - body contains
Hook rules were not satisfied - file
ran.txtdoes not exist
- HTTP status is
- after
HTTP POST /hooks/guarded:- HTTP status is
200 - file
ran.txtcontainsexecuted
- HTTP status is
Scenario: an HMAC signature is verified against an independent oracle #
Given #
- Background service
webhookis started:webhook -hooks hooks.json -ip 127.0.0.1 -port 18096. - Fixture file
handler.shis created. - Fixture file
hooks.jsonis created.
Inputs #
Fixture handler.sh:
#!/bin/sh
printf 'signed\n' > ran.txt
printf 'ok'
Fixture hooks.json:
[
{
"id": "signed",
"execute-command": "${workdir}/handler.sh",
"command-working-directory": "${workdir}",
"include-command-output-in-response": true,
"trigger-rule": {
"match": {
"type": "payload-hmac-sha256",
"secret": "atago-demo-secret",
"parameter": { "source": "header", "name": "X-Hub-Signature-256" }
}
}
}
]
When #
# HTTP POST /hooks/signed
# HTTP POST /hooks/signed
Then #
- after
HTTP POST /hooks/signed:- HTTP status is
500 - body contains
Error occurred while evaluating hook rules - file
ran.txtdoes not exist
- HTTP status is
- after
HTTP POST /hooks/signed:- HTTP status is
200 - file
ran.txtcontainssigned
- HTTP status is
Scenario: the http-methods allowlist rejects the wrong verb #
Given #
- Background service
webhookis started:webhook -hooks hooks.json -ip 127.0.0.1 -port 18097. - Fixture file
handler.shis created. - Fixture file
hooks.jsonis created.
Inputs #
Fixture handler.sh:
#!/bin/sh
printf 'ok'
Fixture hooks.json:
[
{
"id": "postonly",
"execute-command": "${workdir}/handler.sh",
"command-working-directory": "${workdir}",
"http-methods": ["POST"],
"include-command-output-in-response": true
}
]
When #
# HTTP GET /hooks/postonly
Then #
- HTTP status is
405
Scenario: a command that exits non-zero surfaces as a 500 #
Given #
- Background service
webhookis started:webhook -hooks hooks.json -ip 127.0.0.1 -port 18098. - Fixture file
handler.shis created. - Fixture file
hooks.jsonis created.
Inputs #
Fixture handler.sh:
#!/bin/sh
printf 'boom\n' >&2
exit 3
Fixture hooks.json:
[
{
"id": "failer",
"execute-command": "${workdir}/handler.sh",
"command-working-directory": "${workdir}",
"include-command-output-in-response": true
}
]
When #
# HTTP POST /hooks/failer
Then #
- HTTP status is
500 - body contains
Error occurred while executing the hook's command