First commit
This commit is contained in:
commit
e81a3b4b25
|
@ -0,0 +1 @@
|
||||||
|
.*.sw*
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Webhook endpoints
|
||||||
|
|
||||||
|
This repository contains the specification and tests for the forge webhook endpoints.
|
||||||
|
|
||||||
|
# Running tests
|
||||||
|
|
||||||
|
Running tests requires the bats framework (`apt install bats`). You can run the `test.sh` script to start the tests. If you are not running from the implementation's folder, you may pass it as first argument the path to the program starting the local server (for tests).
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./test.sh ~/endpoints.php/server
|
||||||
|
```
|
|
@ -0,0 +1,29 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
SCRIPTDIR="$(dirname "$0")"
|
||||||
|
|
||||||
|
# Call me with the path to the script/program running the server
|
||||||
|
# Otherwise we try looking in the parent folder (if the tests are a submodule)
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
potential_server="$(readlink -m "$SCRIPTDIR"/server)"
|
||||||
|
echo $potential_server
|
||||||
|
if [ -x "$potential_server" ]; then
|
||||||
|
export FORGEHOOKENDPOINT="$potential_server"
|
||||||
|
elif [ -x "$(readlink -m "$potential_server"/../../server)" ]; then
|
||||||
|
export FORGEHOOKENDPOINT="$(readlink -m "$potential_server"/../../server)"
|
||||||
|
else
|
||||||
|
echo "test.sh SERVER"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
[ ! -x "$1" ] && echo "Cannot execute "$1"" && exit 2
|
||||||
|
export FORGEHOOKENDPOINT="$(readlink -m $1)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ORIGDIR="$(pwd)"
|
||||||
|
cd "$SCRIPTDIR"
|
||||||
|
|
||||||
|
export FORGEHOOK="$(pwd)/tests/mock-forgehook.sh"
|
||||||
|
bats tests/*.bats
|
||||||
|
|
||||||
|
cd "$ORIGDIR"
|
|
@ -0,0 +1,46 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
function setup {
|
||||||
|
# Load helper functions
|
||||||
|
load helper
|
||||||
|
# Which forgehook implementation to use?
|
||||||
|
if [ -z "$FORGEHOOK" ]; then FORGEHOOK="forgehook"; fi
|
||||||
|
port=$(find_free_port)
|
||||||
|
[ ! -z "$FORGEHOOKENDPOINT" ]
|
||||||
|
|
||||||
|
TMPFILE=$(mktemp)
|
||||||
|
|
||||||
|
# Need 3>&- so bats doesn't hang because of background task
|
||||||
|
$FORGEHOOKENDPOINT $port 3>&- &
|
||||||
|
export FORGEHOOKPID="$!"
|
||||||
|
[[ $? = 0 ]]
|
||||||
|
export FORGEHOOKSRV="http://localhost:$port"
|
||||||
|
}
|
||||||
|
|
||||||
|
function teardown {
|
||||||
|
# If setup fails, $FORGEHOOKPID will be empty so nothing to clean
|
||||||
|
if [ ! -z "$FORGEHOOKPID" ]; then
|
||||||
|
# Also kill the PID's children processes
|
||||||
|
kill $(ps -o pid= --ppid $FORGEHOOKPID)
|
||||||
|
fi
|
||||||
|
if [ -f $TMPFILE ]; then rm $TMPFILE; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "correct signature works" {
|
||||||
|
repo="https://tildegit.org/forge/hook.sh"
|
||||||
|
webhook="$(gen_webhook tests/gitea.json "$repo")"
|
||||||
|
sig="$(hash_hmac sha256 "$webhook" "$($FORGEHOOK secret $repo)")"
|
||||||
|
run send_webhook "${FORGEHOOKSRV}?action=gitea" "$webhook" "$sig" "X-Gitea-Signature"
|
||||||
|
[ $status -eq 0 ]
|
||||||
|
[[ "$output" -eq "200" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "incorrect signature fails" {
|
||||||
|
repo="https://tildegit.org/forge/hook.sh"
|
||||||
|
webhook="$(gen_webhook tests/gitea.json "$repo")"
|
||||||
|
# Calculate wrong signature
|
||||||
|
sig="$(hash_hmac sha256 "EXTRA$webhook" "$($FORGEHOOK secret $repo)")"
|
||||||
|
run send_webhook "${FORGEHOOKSRV}?action=gitea" "$webhook" "$sig" "X-Gitea-Signature"
|
||||||
|
[ "$status" -eq 2 ]
|
||||||
|
[[ "$output" = "403" ]]
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"repository": {
|
||||||
|
"html_url": "$repo_url"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
function setup {
|
||||||
|
# Load helper functions
|
||||||
|
load helper
|
||||||
|
# Which forgehook implementation to use?
|
||||||
|
if [ -z "$FORGEHOOK" ]; then FORGEHOOK="forgehook"; fi
|
||||||
|
port=$(find_free_port)
|
||||||
|
[ ! -z "$FORGEHOOKENDPOINT" ]
|
||||||
|
|
||||||
|
TMPFILE=$(mktemp)
|
||||||
|
|
||||||
|
# Need 3>&- so bats doesn't hang because of background task
|
||||||
|
$FORGEHOOKENDPOINT $port 3>&- &
|
||||||
|
export FORGEHOOKPID="$!"
|
||||||
|
[[ $? = 0 ]]
|
||||||
|
export FORGEHOOKSRV="http://localhost:$port"
|
||||||
|
}
|
||||||
|
|
||||||
|
function teardown {
|
||||||
|
# If setup fails, $FORGEHOOKPID will be empty so nothing to clean
|
||||||
|
if [ ! -z "$FORGEHOOKPID" ]; then
|
||||||
|
# Also kill the PID's children processes
|
||||||
|
kill $(ps -o pid= --ppid $FORGEHOOKPID)
|
||||||
|
fi
|
||||||
|
if [ -f $TMPFILE ]; then rm $TMPFILE; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "correct signature works" {
|
||||||
|
repo="https://tildegit.org/forge/hook.sh"
|
||||||
|
webhook="$(gen_webhook tests/github.json "$repo")"
|
||||||
|
sig="$(hash_hmac sha256 "$webhook" "$($FORGEHOOK secret $repo)")"
|
||||||
|
run send_webhook "${FORGEHOOKSRV}?action=github" "$webhook" "$sig" "X-Hub-Signature"
|
||||||
|
[ $status -eq 0 ]
|
||||||
|
[[ "$output" -eq "200" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "incorrect signature fails" {
|
||||||
|
repo="https://tildegit.org/forge/hook.sh"
|
||||||
|
webhook="$(gen_webhook tests/gitea.json "$repo")"
|
||||||
|
# Calculate wrong signature
|
||||||
|
sig="$(hash_hmac sha256 "EXTRA$webhook" "$($FORGEHOOK secret $repo)")"
|
||||||
|
run send_webhook "${FORGEHOOKSRV}?action=github" "$webhook" "$sig" "X-Hub-Signature"
|
||||||
|
[ "$status" -eq 2 ]
|
||||||
|
[[ "$output" = "403" ]]
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"repository": {
|
||||||
|
"html_url": "$repo_url"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
function setup {
|
||||||
|
# Load helper functions
|
||||||
|
load helper
|
||||||
|
# Which forgehook implementation to use?
|
||||||
|
if [ -z "$FORGEHOOK" ]; then FORGEHOOK="forgehook"; fi
|
||||||
|
port=$(find_free_port)
|
||||||
|
[ ! -z "$FORGEHOOKENDPOINT" ]
|
||||||
|
|
||||||
|
TMPFILE=$(mktemp)
|
||||||
|
|
||||||
|
# Need 3>&- so bats doesn't hang because of background task
|
||||||
|
$FORGEHOOKENDPOINT $port 3>&- &
|
||||||
|
export FORGEHOOKPID="$!"
|
||||||
|
[[ $? = 0 ]]
|
||||||
|
export FORGEHOOKSRV="http://localhost:$port"
|
||||||
|
}
|
||||||
|
|
||||||
|
function teardown {
|
||||||
|
# If setup fails, $FORGEHOOKPID will be empty so nothing to clean
|
||||||
|
if [ ! -z "$FORGEHOOKPID" ]; then
|
||||||
|
# Also kill the PID's children processes
|
||||||
|
kill $(ps -o pid= --ppid $FORGEHOOKPID)
|
||||||
|
fi
|
||||||
|
if [ -f $TMPFILE ]; then rm $TMPFILE; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "correct token works" {
|
||||||
|
repo="https://tildegit.org/forge/hook.sh"
|
||||||
|
webhook="$(gen_webhook tests/gitlab.json "$repo")"
|
||||||
|
run send_webhook "${FORGEHOOKSRV}?action=gitlab" "$webhook" "$($FORGEHOOK secret $repo)" "X-Gitlab-Token"
|
||||||
|
[ $status -eq 0 ]
|
||||||
|
[[ "$output" = "200" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "incorrect token fails" {
|
||||||
|
repo="https://tildegit.org/forge/hook.sh"
|
||||||
|
webhook="$(gen_webhook tests/gitlab.json "$repo")"
|
||||||
|
# Send FAKE token
|
||||||
|
run send_webhook "${FORGEHOOKSRV}?action=gitlab" "$webhook" "FAKE" "X-Gitlab-Token"
|
||||||
|
[ "$status" -eq 2 ]
|
||||||
|
[[ "$output" = "403" ]]
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"project": {
|
||||||
|
"git_http_url": "$repo_url"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
# https://unix.stackexchange.com/a/423052
|
||||||
|
function find_free_port {
|
||||||
|
comm -23 <(seq 49152 65535 | sort) <(ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# gen_webhook "REPO_URL"
|
||||||
|
#function gen_webhook {
|
||||||
|
# echo "{ \"project\": { \"git_http_url\": \"$1\" } }"
|
||||||
|
#}
|
||||||
|
|
||||||
|
function gen_webhook() {
|
||||||
|
export repo_url="$2"
|
||||||
|
envsubst < "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# send_webhook "ENDPOINT" "PAYLOAD" "SECRET" "HEADER"
|
||||||
|
# ENDPOINT: where to send the request
|
||||||
|
# PAYLOAD: POST body
|
||||||
|
# SECRET: the secret for this transaction
|
||||||
|
# HEADER: where to store the secret
|
||||||
|
function send_webhook {
|
||||||
|
echo "$2" > $TMPFILE
|
||||||
|
|
||||||
|
# We can make a few attempts, just in case the webserver hasn't started yet
|
||||||
|
n=0
|
||||||
|
while [[ "$status" != "0" ]]; do
|
||||||
|
if [ $n -eq 3 ]; then
|
||||||
|
# Failed to reach server after 3 attempts
|
||||||
|
return 1;
|
||||||
|
fi
|
||||||
|
# --data-binary so that newlines aren't broken
|
||||||
|
# (otherwise, signature won't match)
|
||||||
|
run curl --header "Content-Type: application/json" \
|
||||||
|
--header ""$4": "$3"" \
|
||||||
|
--request POST \
|
||||||
|
--data-binary @$TMPFILE \
|
||||||
|
-s -w "%{http_code}" \
|
||||||
|
"$1"
|
||||||
|
# Requested succeeded, break out of loop
|
||||||
|
if [ $status -eq 0 ]; then
|
||||||
|
echo "$output"
|
||||||
|
if [[ ! "$output" = 200 ]]; then return 2; fi
|
||||||
|
return 0;
|
||||||
|
fi
|
||||||
|
((n++))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# https://stackoverflow.com/a/7385197
|
||||||
|
function hash_hmac {
|
||||||
|
digest="$1"
|
||||||
|
data="$2"
|
||||||
|
key="$3"
|
||||||
|
shift 3
|
||||||
|
# Don't print (stdin)= ...
|
||||||
|
echo -n "$data" | openssl dgst "-$digest" -hmac "$key" | awk '{print $2}'
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
# This script mocks forgehook for use by webhook endpoints, only for testing purposes
|
||||||
|
# It returns a secret being simply the hashed URL of the repo
|
||||||
|
|
||||||
|
[[ "$#" != 2 ]] && echo "WRONG ARGUMENTS ($#): $@" && exit 1
|
||||||
|
|
||||||
|
[[ "$1" != "secret" ]] && echo "UNIMPLEMENTED" && exit 2
|
||||||
|
|
||||||
|
echo -n "$2" | sha256sum | cut -d ' ' -f 1
|
Loading…
Reference in New Issue