diff --git a/README.md b/README.md index c5bba6a..a55f798 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Core Commands Command Description ------- ----------- -t, --token GitHub token - -r, --repo GitHub repository, organization/repository-name + -r, --repo GitHub repository, organization/repository-name -c, --critical Critical severity limit -h, --high High severity limit -m, --medium Medium severity limit @@ -122,4 +122,4 @@ Your contributions and suggestions are heartily ♥ welcome. [See here the contr ### License -This work is licensed under [MIT License.](/LICENSE.md) \ No newline at end of file +This work is licensed under [MIT License.](/LICENSE.md) diff --git a/lib/SecurityGate/Engine/Code.pm b/lib/SecurityGate/Engine/Code.pm index 9b2e549..1848391 100644 --- a/lib/SecurityGate/Engine/Code.pm +++ b/lib/SecurityGate/Engine/Code.pm @@ -5,55 +5,55 @@ package SecurityGate::Engine::Code { use Mojo::JSON; sub new { - my ($class, $token, $repository, $severity_limits) = @_; - my $alerts_endpoint = "https://api.github.com/repos/$repository/code-scanning/alerts"; - - my $userAgent = Mojo::UserAgent -> new(); - my $alerts_request = $userAgent -> get($alerts_endpoint, {Authorization => "Bearer $token"}) -> result(); - - if ($alerts_request -> code() == 200) { - my $alerts_data = $alerts_request -> json(); - my $open_alerts = 0; - my %severity_counts = map {$_ => 0} keys %$severity_limits; - - foreach my $alert (@$alerts_data) { - if ($alert -> {state} eq "open") { - $open_alerts++; - - my $severity = $alert -> {rule} -> {severity}; - $severity_counts{$severity}++ if exists $severity_counts{$severity}; - } - } + my ($class, $token, $repository, $severity_limits) = @_; + my $alerts_endpoint = "https://api.github.com/repos/$repository/code-scanning/alerts"; - print "[!] Total of open code scanning alerts: $open_alerts\n"; - - foreach my $severity (keys %severity_counts) { - print "[-] $severity: $severity_counts{$severity}\n"; - } + my $userAgent = Mojo::UserAgent->new(); + my $alerts_request = $userAgent->get($alerts_endpoint, {Authorization => "Bearer $token"})->result(); - my $threshold_exceeded = 0; - - foreach my $severity (keys %severity_counts) { - if ($severity_counts{$severity} > $severity_limits -> {$severity}) { - print "[+] More than $severity_limits->{$severity} $severity code scanning alerts found.\n"; - - $threshold_exceeded = 1; - } - } + if ($alerts_request->code() == 200) { + my $alerts_data = $alerts_request->json(); + my $open_alerts = 0; + my %severity_counts = map {$_ => 0} keys %$severity_limits; + + foreach my $alert (@$alerts_data) { + if ($alert->{state} eq "open") { + $open_alerts++; + + my $severity = $alert->{rule}->{security_severity_level} // 'unknown'; + $severity_counts{$severity}++ if exists $severity_counts{$severity}; + } + } + + print "\n[!] Total of open code scanning alerts: $open_alerts\n\n"; - if ($threshold_exceeded) { - return 1; + foreach my $severity (keys %severity_counts) { + print "[-] $severity: $severity_counts{$severity}\n"; + } + + print "\n"; + + my $threshold_exceeded = 0; + + foreach my $severity (keys %severity_counts) { + if ($severity_counts{$severity} > $severity_limits->{$severity}) { + print "[+] More than $severity_limits->{$severity} $severity code scanning alerts found.\n"; + $threshold_exceeded = 1; + } + } + + if ($threshold_exceeded) { + return 1; + } } - } - else { - print "Error: Unable to fetch code scanning alerts. HTTP status code: " . $alerts_request -> code() . "\n"; - - return 1; - } + else { + print "Error: Unable to fetch code scanning alerts. HTTP status code: " . $alerts_request->code() . "\n"; + return 1; + } - return 0; + return 0; } } -1; \ No newline at end of file +1; diff --git a/lib/SecurityGate/Engine/Dependencies.pm b/lib/SecurityGate/Engine/Dependencies.pm index 951829d..7fe3434 100644 --- a/lib/SecurityGate/Engine/Dependencies.pm +++ b/lib/SecurityGate/Engine/Dependencies.pm @@ -12,22 +12,22 @@ package SecurityGate::Engine::Dependencies { my ($class, $token, $repository, $severity_limits) = @_; my %severity_counts = map { $_ => 0 } @SEVERITIES; - + my $endpoint = "https://api.github.com/repos/$repository/dependabot/alerts"; - my $userAgent = Mojo::UserAgent -> new(); - my $request = $userAgent -> get($endpoint, {Authorization => "Bearer $token"}) -> result(); + my $userAgent = Mojo::UserAgent->new(); + my $request = $userAgent->get($endpoint, {Authorization => "Bearer $token"})->result(); + + if ($request->code() == 200) { + my $data = $request->json(); - if ($request -> code() == 200) { - my $data = $request -> json(); - foreach my $alert (@$data) { - if ($alert -> {state} eq "open") { - my $severity = $alert -> {security_vulnerability} -> {severity}; + if ($alert->{state} eq "open") { + my $severity = $alert->{security_vulnerability}->{severity}; $severity_counts{$severity}++; } } - print "[!] Total of security alerts:\n\n"; + print "\n[!] Total of dependency alerts:\n\n"; foreach my $severity (@SEVERITIES) { print "[-] $severity: $severity_counts{$severity}\n"; @@ -38,7 +38,7 @@ package SecurityGate::Engine::Dependencies { my $threshold_exceeded = 0; foreach my $severity (@SEVERITIES) { - if ($severity_counts{$severity} > $severity_limits -> {$severity}) { + if ($severity_counts{$severity} > $severity_limits->{$severity}) { print "[+] More than $severity_limits->{$severity} $severity security alerts found.\n"; $threshold_exceeded = 1; } @@ -46,12 +46,12 @@ package SecurityGate::Engine::Dependencies { return $threshold_exceeded; } - + else { - print "Error: Unable to fetch alerts. HTTP status code: " . $request -> code() . "\n"; + print "Error: Unable to fetch alerts. HTTP status code: " . $request->code() . "\n"; return 1; } } } -1; \ No newline at end of file +1; diff --git a/lib/SecurityGate/Engine/Secrets.pm b/lib/SecurityGate/Engine/Secrets.pm index deb44dd..5afcac9 100644 --- a/lib/SecurityGate/Engine/Secrets.pm +++ b/lib/SecurityGate/Engine/Secrets.pm @@ -8,43 +8,48 @@ package SecurityGate::Engine::Secrets { 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 $userAgent = Mojo::UserAgent->new(); + my $request = $userAgent->get($endpoint, {Authorization => "Bearer $token"})->result(); - if ($request -> code() == 200) { - my $data = $request -> json(); + if ($request->code() == 200) { + my $data = $request->json(); my $open_alerts = 0; my @alert_details; foreach my $alert (@$data) { - if ($alert -> {state} eq "open") { + 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(); + if ($locations_request->code() == 200) { + my $locations = $locations_request->json(); push @alert_details, { - alert_number => $alert -> {number}, + alert_number => $alert->{number}, locations => $locations, }; } } } - print "[!] Total of open secret scanning alerts: $open_alerts\n"; + print "\n[!] Total of open secret scanning alerts: $open_alerts\n\n"; foreach my $detail (@alert_details) { - print "[-] Alert " . $detail -> {alert_number} . " found in the following locations:\n"; + 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"; + foreach my $location (@{$detail->{locations}}) { + my $file_path = $location->{details}->{path} // 'Unknown file'; + my $start_line = $location->{details}->{start_line} // 'Unknown line'; + + print " File: $file_path, Start line: $start_line\n"; } } - my $threshold = $severity_limits -> {high}; + print "\n"; + + my $threshold = $severity_limits->{high}; if ($open_alerts > $threshold) { print "[+] More than $threshold secret scanning alerts found. Blocking pipeline.\n"; return 1; @@ -57,7 +62,7 @@ package SecurityGate::Engine::Secrets { } else { - print "Error: Unable to fetch secret scanning alerts. HTTP status code: " . $request -> code() . "\n"; + print "Error: Unable to fetch secret scanning alerts. HTTP status code: " . $request->code() . "\n"; return 1; } } diff --git a/security-gate.pl b/security-gate.pl index de12b6d..1961dc6 100644 --- a/security-gate.pl +++ b/security-gate.pl @@ -30,23 +30,13 @@ sub main { my $result = 0; my %alert_checks = ( - 'dependency-alerts' => sub { SecurityGate::Engine::Dependencies -> new($token, $repository, \%severity_limits) }, - 'secret-alerts' => sub { SecurityGate::Engine::Secrets -> new($token, $repository, \%severity_limits) }, - 'code-alerts' => sub { SecurityGate::Engine::Code -> new($token, $repository, \%severity_limits) } + 'dependency-alerts' => $dependency_alerts ? sub { SecurityGate::Engine::Dependencies->new($token, $repository, \%severity_limits) } : undef, + 'secret-alerts' => $secret_alerts ? sub { SecurityGate::Engine::Secrets->new($token, $repository, \%severity_limits) } : undef, + 'code-alerts' => $code_alerts ? sub { SecurityGate::Engine::Code->new($token, $repository, \%severity_limits) } : undef ); - for my $alert_type (keys %alert_checks) { - if ($alert_type eq 'dependency-alerts' && $dependency_alerts) { - $result += $alert_checks{$alert_type}->(); - } - - elsif ($alert_type eq 'secret-alerts' && $secret_alerts) { - $result += $alert_checks{$alert_type}->(); - } - - elsif ($alert_type eq 'code-alerts' && $code_alerts) { - $result += $alert_checks{$alert_type}->(); - } + for my $check (grep { defined } values %alert_checks) { + $result += $check->(); } return $result;