hook.php/index.php

166 lines
4.9 KiB
PHP

<?php
function error($message, $code = 500) {
http_response_code($code);
// Echo to STDOUT, not STDERR as some servers will hide STDERR for security reasons
echo($message);
exit();
}
// extract_payload()
// Find the JSON payload from the POST request
function extract_payload() {
// check for POST request
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
error('FAILED - not POST - '. $_SERVER['REQUEST_METHOD']);
}
// get content type
$content_type = isset($_SERVER['CONTENT_TYPE']) ? strtolower(trim($_SERVER['CONTENT_TYPE'])) : '';
if ($content_type != 'application/json') {
error('FAILED - not application/json - '. $content_type);
}
// get payload
$payload = file_get_contents("php://input");
if (empty($payload)) {
error('FAILED - no payload');
}
return $payload;
}
// json_to_array(payload)
// payload: JSON string
function json_to_array($payload) {
// convert json to array
$json = json_decode($payload, true);
// check for json decode errors
if (json_last_error() !== JSON_ERROR_NONE) {
error('FAILED - json decode - '. json_last_error());
}
return $json;
}
// Find header value or die
function extract_header($header) {
// PHP embedded server prepends HTTP_ to the header name. WTF?
$value = isset($_SERVER[$header]) ? $_SERVER[$header]
: (isset($_SERVER['HTTP_'.$header]) ? $_SERVER['HTTP_'.$header]
: '');
if (empty($value)) {
error('FAILED - header signature '.$header.' missing');
}
return $value;
}
function verify_signature($payload, $secret, $claimed_signature) {
$payload_signature = hash_hmac('sha256', $payload, $secret, false);
// check payload signature against header signature
if ($claimed_signature != $payload_signature) {
error('FAILED - payload signature mismatch', 403);
}
}
function verify_token($secret, $claimed_secret) {
if ($secret !== $claimed_secret) {
error('FAILED - secret token mismatch', 403);
}
}
// find_secret($repo_url)
// Find the secret corresponding to the repo_url, if any. Returns empty string otherwise
function find_secret($repo_url) {
//$forgehook = getenv('FORGEHOOK') ? : 'forgehook';
// TODO: use in order ENV['FORGEHOOK'], ./forgehook, or PATH['forgehook']
$forgehook = './forgehook';
$repo = escapeshellarg($repo_url);
$lines = [];
$status = NULL;
$secret = exec($forgehook." secret ".$repo, $lines, $status);
if (($secret == NULL) or ($status != 0)) {
error("Secret not found for \"".$repo."\"");
}
if (empty($secret)) {
error("Secret empty for ".$repo);
}
return $secret;
}
function find_url_gitea($array) {
$repo_url = isset($array["repository"]["html_url"]) ?
$array["repository"]["html_url"] : "";
if (empty($repo_url)) {
error('Could not find Gitea repository URL');
}
return $repo_url;
}
function find_url_gitlab($array) {
$repo_url = isset($array["project"]["git_http_url"]) ?
$array["project"]["git_http_url"] : "";
if (empty($repo_url)) {
error('Could not find Gitlab repository URL');
}
return $repo_url;
}
function notify($repo) {
$notify = getenv('FORGEHOOKNOTIFY') ? : 'forgehook-notify';
$output=shell_exec($notify." ".$repo);
if ($output != NULL) {
error("Notify failed (".$notify.") with:\n".$output);
}
}
function action() {
if (!isset($_GET['action'])) {
error("You need to specify an action (gitea, gitlab) like this: ?action=gitea", 404);
}
switch($_GET['action']) {
case 'github':
$claimed_secret = extract_header("HTTP_X_HUB_SIGNATURE");
$payload = extract_payload();
$payload_array = json_to_array($payload);
// Gitea URL is same as Github, for the moment
$repo_url = find_url_gitea($payload_array);
$secret = find_secret($repo_url);
verify_signature($payload, $secret, $claimed_secret);
notify($repo_url);
break;
case 'gitea':
$claimed_secret = extract_header("HTTP_X_GITEA_SIGNATURE");
$payload = extract_payload();
$payload_array = json_to_array($payload);
$repo_url = find_url_gitea($payload_array);
$secret = find_secret($repo_url);
verify_signature($payload, $secret, $claimed_secret);
notify($repo_url);
break;
case 'gitlab':
$claimed_secret = extract_header("HTTP_X_GITLAB_TOKEN");
$payload = extract_payload();
$payload_array = json_to_array($payload);
$repo_url = find_url_gitlab($payload_array);
$secret = find_secret($repo_url);
verify_token($secret, $claimed_secret);
notify($repo_url);
break;
default:
error("Unrecognized action: ".$_GET['action'], 400);
}
}
action();
echo("OK");
?>