Skip to content

Commit

Permalink
Merge pull request #10 from instriq/develop
Browse files Browse the repository at this point in the history
Improve tests and fix linter warnings
  • Loading branch information
htrgouvea authored Sep 2, 2024
2 parents 174276c + f78f3c9 commit 8d4bf0d
Show file tree
Hide file tree
Showing 16 changed files with 954 additions and 101 deletions.
87 changes: 44 additions & 43 deletions lib/SecurityGate/Engine/Secrets.pm
Original file line number Diff line number Diff line change
Expand Up @@ -5,61 +5,62 @@ package SecurityGate::Engine::Secrets {
use Mojo::JSON;

sub new {
my ($class, $token, $repository) = @_;
my ($class, $token, $repository, $severity_limits) = @_;

my $endpoint = "https://api.github.com/repos/$repository/secret-scanning/alerts";
my $userAgent = Mojo::UserAgent -> new();
my $request = $userAgent -> get($endpoint, {Authorization => "Bearer $token"}) -> result();
my $endpoint = "https://api.github.com/repos/$repository/secret-scanning/alerts";
my $userAgent = Mojo::UserAgent -> new();
my $request = $userAgent -> get($endpoint, {Authorization => "Bearer $token"}) -> result();

if ($request -> code() == 200) {
my $data = $request -> json();
my $open_alerts = 0;
my @alert_details;
if ($request -> code() == 200) {
my $data = $request -> json();
my $open_alerts = 0;
my @alert_details;

foreach my $alert (@$data) {
if ($alert -> {state} eq "open") {
$open_alerts++;
foreach my $alert (@$data) {
if ($alert -> {state} eq "open") {
$open_alerts++;

my $locations_endpoint = "https://api.github.com/repos/$repository/secret-scanning/alerts/$alert -> {number}/locations";
my $locations_request = $userAgent -> get($locations_endpoint, {Authorization => "Bearer $token"}) -> result();
my $locations_endpoint = "https://api.github.com/repos/$repository/secret-scanning/alerts/$alert -> {number}/locations";
my $locations_request = $userAgent -> get($locations_endpoint, {Authorization => "Bearer $token"}) -> result();

if ($locations_request -> code() == 200) {
my $locations = $locations_request -> json();

push @alert_details, {
alert_number => $alert -> {number},
locations => $locations,
};
if ($locations_request -> code() == 200) {
my $locations = $locations_request -> json();

push @alert_details, {
alert_number => $alert -> {number},
locations => $locations,
};
}
}
}
}
}

if ($open_alerts > 0) {
print "[!] Total of open secret scanning alerts: $open_alerts\n";

foreach my $detail (@alert_details) {
print "[-] Alert $detail -> {alert_number} found in the following locations:\n";

foreach my $location (@{$detail -> {locations}}) {
print " File: $location -> {path}, Start line: $location -> {start_line}\n";
print "[!] Total of open secret scanning alerts: $open_alerts\n";

foreach my $detail (@alert_details) {
print "[-] Alert " . $detail -> {alert_number} . " found in the following locations:\n";

foreach my $location (@{$detail -> {locations}}) {
print " File: " . $location -> {path} . ", Start line: " . $location -> {start_line} . "\n";
}
}
}

print "[+] Secret scanning alert(s) found. Blocking pipeline.\n";
return 1;
my $threshold = $severity_limits -> {high};
if ($open_alerts > $threshold) {
print "[+] More than $threshold secret scanning alerts found. Blocking pipeline.\n";
return 1;
}

else {
print "[-] Number of secret scanning alerts ($open_alerts) is within the acceptable limit ($threshold).\n";
return 0;
}
}

else {
print "[-] No secret scanning alerts found.\n";
return 0;
print "Error: Unable to fetch secret scanning alerts. HTTP status code: " . $request -> code() . "\n";
return 1;
}
}

else {
print "Error: Unable to fetch secret scanning alerts. HTTP status code: " . $request -> code() . "\n";
return 1;
}
}
}
}

1;
1;
41 changes: 22 additions & 19 deletions lib/SecurityGate/Utils/Helper.pm
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
package SecurityGate::Utils::Helper {
use strict;
use warnings;
use warnings;

sub new {
return "
\rSecurity Gate v0.1.0
\rCore Commands
\r====================
\r\tCommand Description
\r\t------- -----------
\r\t-t, --token GitHub token
\r\t-r, --repo GitHub repository, organization/repository-name
\r\t-c, --critical Critical severity limit
\r\t-h, --high High severity limit
\r\t-m, --medium Medium severity limit
\r\t-l, --low Low severity limit
\r\t--dependency-alerts Check dependency alerts
\r\t--secret-alerts Check secret scanning alerts
\r\t--code-alerts Check code scanning alerts\n\n";
}
sub new {
return <<"EOT";
Security Gate v0.0.3
Core Commands
==============
Command Description
------- -----------
-t, --token GitHub token
-r, --repo GitHub repository
-c, --critical Critical severity limit
-h, --high High severity limit
-m, --medium Medium severity limit
-l, --low Low severity limit
--dependency-alerts Check for dependency alerts
--secret-scanning-alerts Check for secret scanning alerts
--code-scanning-alerts Check for code scanning alerts
EOT
}
}

1;
1;
4 changes: 2 additions & 2 deletions security-gate.pl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ sub main {

my %alert_checks = (
'dependency-alerts' => sub { SecurityGate::Engine::Dependencies -> new($token, $repository, \%severity_limits) },
'secret-alerts' => sub { SecurityGate::Engine::Secrets -> new($token, $repository) },
'secret-alerts' => sub { SecurityGate::Engine::Secrets -> new($token, $repository, \%severity_limits) },
'code-alerts' => sub { SecurityGate::Engine::Code -> new($token, $repository, \%severity_limits) }
);

Expand Down Expand Up @@ -66,4 +66,4 @@ sub main {

else {
exit main();
}
}
64 changes: 64 additions & 0 deletions tests/code-api-request-error.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env perl

use strict;
use warnings;
use Test::More;
use Test::Exception;
use Test::MockObject;
use Test::Output;
use Capture::Tiny qw(capture_stdout);

{
package Mojo::UserAgent;
use Test::MockObject;

my $mock_response;

sub new {
my $class = shift;
return Test::MockObject -> new -> mock('get', sub {
my ($self, $url, $headers) = @_;
return Test::MockObject -> new -> mock('result', sub {
return $mock_response;
});
});
}

sub set_mock_response {
my ($class, $response) = @_;
$mock_response = $response;
return $mock_response;
}
}

use lib '../lib';
use SecurityGate::Engine::Code;

subtest 'API request error' => sub {
plan tests => 2;

my $mock_response = Mojo::UserAgent -> set_mock_response(Test::MockObject -> new);
$mock_response -> set_always('code', 401);

my %severity_limits = (
critical => 0,
high => 0,
medium => 0,
low => 0
);

my $result;
my $error_message = qr/Error: \s Unable \s to \s fetch \s code \s scanning \s alerts\./x;
my $status_code = qr/\s HTTP \s status \s code: \s 401/x;
my $full_error_pattern = qr/$error_message$status_code/x;

stdout_like(
sub { $result = SecurityGate::Engine::Code -> new('test_token', 'test_repo', \%severity_limits) },
$full_error_pattern,
'Correct error message for API request failure'
);

is($result, 1, 'Returns 1 when API request fails');
};

done_testing();
57 changes: 57 additions & 0 deletions tests/dependencies-api-error-handling.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env perl

use strict;
use warnings;
use Test::More;
use Test::Exception;
use Test::MockObject;
use Test::Output;

{
package Mojo::UserAgent;
use Test::MockObject;

my $mock_response;

sub new {
my $class = shift;
return Test::MockObject -> new -> mock('get', sub {
my ($self, $url, $headers) = @_;
return Test::MockObject -> new -> mock('result', sub {
return $mock_response;
});
});
}

sub set_mock_response {
my ($class, $response) = @_;
$mock_response = $response;
return;
}
}

use lib '../lib';
use SecurityGate::Engine::Dependencies;

subtest 'API error handling' => sub {
plan tests => 1;

my $mock_response = Test::MockObject -> new;
Mojo::UserAgent -> set_mock_response($mock_response);
$mock_response -> set_always('code', 401);

my %severity_limits = (
critical => 0,
high => 0,
medium => 0,
low => 0
);

is(
SecurityGate::Engine::Dependencies -> new('invalid_token', 'test_repo', \%severity_limits),
1,
'Returns 1 when API request fails'
);
};

done_testing();
63 changes: 63 additions & 0 deletions tests/dependencies-severity-counting.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env perl

use strict;
use warnings;
use Test::More;
use Test::Exception;
use Test::MockObject;
use Test::Output;

{
package Mojo::UserAgent;
use Test::MockObject;

my $mock_response;

sub new {
my $class = shift;
return Test::MockObject -> new -> mock('get', sub {
my ($self, $url, $headers) = @_;
return Test::MockObject -> new -> mock('result', sub {
return $mock_response;
});
});
}

sub set_mock_response {
my ($class, $response) = @_;
$mock_response = $response;
return;
}
}

use lib '../lib';
use SecurityGate::Engine::Dependencies;

subtest 'Severity counting' => sub {
plan tests => 1;

my $mock_response = Test::MockObject -> new;
Mojo::UserAgent -> set_mock_response($mock_response);
$mock_response -> set_always('code', 200);
$mock_response -> set_always('json', [
{ state => 'open', security_vulnerability => { severity => 'high' } },
{ state => 'open', security_vulnerability => { severity => 'critical' } },
{ state => 'open', security_vulnerability => { severity => 'medium' } },
{ state => 'closed', security_vulnerability => { severity => 'low' } },
]);

my %severity_limits = (
critical => 0,
high => 0,
medium => 0,
low => 0
);

stdout_like(
sub { SecurityGate::Engine::Dependencies -> new('test_token', 'test_repo', \%severity_limits) },
qr/critical:\ 1.*high:\ 1.*medium:\ 1.*low:\ 0/xs,
'Severity counts are correct'
);
};

done_testing();
Loading

0 comments on commit 8d4bf0d

Please sign in to comment.