webhooks: implement authentication for GitHub and Gitea

- Add HMAC-SHA256 signature verification for webhooks
- Support multiple secrets for rotation
- Add security logging for authentication events
- Maintain backward compatibility (auth optional during migration)
- Add comprehensive test coverage

Without authentication, anyone could trigger job evaluations by sending
POST requests to webhook endpoints. This could lead to resource exhaustion
through repeated requests or manipulation of build scheduling. While not
a data breach risk, it allows unauthorized control over CI/CD operations.
This commit is contained in:
Jörg Thalheim
2025-08-03 07:31:13 +02:00
committed by ahuston-0
parent 4d2d0f9722
commit f2cbf14f7e
7 changed files with 596 additions and 19 deletions

View File

@@ -6,6 +6,7 @@ use Catalyst::Test ();
use HTTP::Request;
use HTTP::Request::Common;
use JSON::MaybeXS qw(decode_json encode_json);
use Digest::SHA qw(hmac_sha256_hex);
sub is_json {
my ($response, $message) = @_;
@@ -21,7 +22,13 @@ sub is_json {
return $data;
}
my $ctx = test_context();
my $ctx = test_context(hydra_config => qq|
<webhooks>
<github>
secret = test
</github>
</webhooks>
|);
Catalyst::Test->import('Hydra');
# Create a user to log in to
@@ -188,16 +195,20 @@ subtest "/api/push-github" => sub {
my $jobsetinput = $jobset->jobsetinputs->create({name => "src", type => "git"});
$jobsetinput->jobsetinputalts->create({altnr => 0, value => "https://github.com/OWNER/LEGACY-REPO.git"});
my $payload = encode_json({
repository => {
owner => {
name => "OWNER",
},
name => "LEGACY-REPO",
}
});
my $signature = "sha256=" . hmac_sha256_hex($payload, 'test');
my $req = POST '/api/push-github',
"Content-Type" => "application/json",
"Content" => encode_json({
repository => {
owner => {
name => "OWNER",
},
name => "LEGACY-REPO",
}
});
"X-Hub-Signature-256" => $signature,
"Content" => $payload;
my $response = request($req);
ok($response->is_success, "The API enpdoint for triggering jobsets returns 200.");
@@ -214,16 +225,20 @@ subtest "/api/push-github" => sub {
emailoverride => ""
});
my $payload = encode_json({
repository => {
owner => {
name => "OWNER",
},
name => "FLAKE-REPO",
}
});
my $signature = "sha256=" . hmac_sha256_hex($payload, 'test');
my $req = POST '/api/push-github',
"Content-Type" => "application/json",
"Content" => encode_json({
repository => {
owner => {
name => "OWNER",
},
name => "FLAKE-REPO",
}
});
"X-Hub-Signature-256" => $signature,
"Content" => $payload;
my $response = request($req);
ok($response->is_success, "The API enpdoint for triggering jobsets returns 200.");