Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #12

Merged
merged 11 commits into from
Nov 29, 2024
Merged
35 changes: 19 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
<p align="center">
<h3 align="center"><b>Sentra</b></h3>
<p align="center">The first autonomous source code posture risk score tool.</p>
<p align="center">The first autonomous source code posture risk score tool</p>
<p align="center">
<a href="https://github.com/instriq/sentra/blob/master/LICENSE.md">
<img src="https://img.shields.io/badge/license-MIT-blue.svg">
</a>
<a href="https://github.com/instriq/sentra/releases">
<img src="https://img.shields.io/badge/version-0.0.3-blue.svg">
</a>
<img src="https://github.com/instriq/sentra/actions/workflows/linter.yml/badge.svg">
<img src="https://github.com/instriq/sentra/actions/workflows/zarn.yml/badge.svg">
<img src="https://github.com/instriq/sentra/actions/workflows/security-gate.yml/badge.svg">
</p>
</p>

Expand All @@ -16,16 +22,6 @@ Sentra is a collection of Perl modules designed to help gain speed and increase

---

### Modules

| Name | Description |
|------|-------------|
| DependabotMetrics | Fetches and analyzes Dependabot alerts from GitHub repositories of a specified organization. |
| SearchFiles | Checks repositories for specific files and last update times. |
| SlackWebhook | An output forwarder for sending messages to Slack via webhooks. |

---

### Installation

```bash
Expand All @@ -43,18 +39,25 @@ $ cpanm --installdeps .
```
$ perl sentra.pl

Sentra v0.0.1
Sentra v0.0.3
Core Commands
==============
Command Description
------- -----------
-o, --org Specify the name of the organization
-t, --token Set the GitHub Token to use during actions
-w, --webhook Set the webhook address for Slack
-m, --message Message to send via Slack webhook
-mt, --maintained Check last commit date of repositories
-d, --dependency Check for dependabot.yaml file in repositories
-p, --per_page Set the number of items per page in API requests (default: 100)
-M, --metrics See some metrics based on GHAS
-w, --webhook Set the webhook address for Slack
-m, --message Message to send via Slack webhook
```

---

### Workflows examples

```yaml
```

---
Expand All @@ -67,4 +70,4 @@ Your contributions and suggestions are heartily ♥ welcome. [See here the contr

### License

This work is licensed under [MIT License.](/LICENSE.md)
This work is licensed under [MIT License.](/LICENSE.md)
62 changes: 15 additions & 47 deletions lib/Sentra/Engine/DependabotMetrics.pm
Original file line number Diff line number Diff line change
@@ -1,42 +1,17 @@
package Sentra::Engine::DependabotMetrics {
use strict;
use warnings;
use Mojo::UserAgent;
use Mojo::JSON qw(decode_json);
use JSON;
use Sentra::Utils::UserAgent;
use Sentra::Utils::Repositories_List;

sub new {
my ($class, $org, $token, $per_page) = @_;

my $userAgent = Mojo::UserAgent -> new();
my $userAgent = Sentra::Utils::UserAgent -> new($token);
my @repositories_list = Sentra::Utils::Repositories_List -> new($org, $token);

my $headers = {
'X-GitHub-Api-Version' => '2022-11-28',
'Accept' => 'application/vnd.github+json',
'User-Agent' => 'Sentra 0.0.3',
'Authorization' => "Bearer $token"
};

my @repos;
my $repo_page = 1;

while (1) {
my $repo_url = "https://api.github.com/orgs/$org/repos?per_page=$per_page&page=$repo_page";
my $repo_tx = $userAgent -> get($repo_url => $headers);
my $res = $repo_tx -> result or return "Error fetching repositories: " . $repo_tx->error->{message} . "\n";

$res->is_success or return "Error fetching repositories: " . $res->message . "\n";

my $repo_data = $res -> json;

last unless @$repo_data;

push @repos, map { "$org/$_->{name}" } grep { !$_->{archived} } @$repo_data;

$repo_page++;
}

return "Error when trying to request information from GitHub, please review the parameters provided." unless @repos;

my $output = "";
my $total_alerts = 0;

my %severity_count = (
Expand All @@ -46,32 +21,25 @@ package Sentra::Engine::DependabotMetrics {
critical => 0
);

for my $repo (@repos) {
foreach my $repository (@repositories_list) {
my $alert_page = 1;

while (1) {
my $alert_url = "https://api.github.com/repos/$repo/dependabot/alerts?state=open&per_page=$per_page&page=$alert_page";
my $alert_tx = $userAgent -> get($alert_url => $headers);
my $res = $alert_tx->result or return "Error fetching alerts for $repo: " . $alert_tx->error->{message} . "\n";
my $alert_url = "https://api.github.com/repos/$repository/dependabot/alerts?state=open&per_page=$per_page&page=$alert_page";
my $request = $userAgent -> get($alert_url);

$res->is_success or return "Error fetching alerts for $repo: " . $res->message . "\n";

my $alert_data = $res -> json;
if ($request -> code() == 200) {
my $alert_data = decode_json($request -> content());

last unless @$alert_data;

$total_alerts += scalar @$alert_data;

for my $alert (@$alert_data) {
my $severity = $alert -> {security_vulnerability}{severity} || 'unknown';

$severity_count{$severity}++ if exists $severity_count{$severity};
}

$alert_page++;
}
}
}

my $output = "";

$output .= "Severity $_: $severity_count{$_}\n" for keys %severity_count;
$output .= "Total DependaBot Alerts: $total_alerts\n";
Expand Down
53 changes: 17 additions & 36 deletions lib/Sentra/Engine/Maintained.pm
Original file line number Diff line number Diff line change
@@ -1,56 +1,37 @@
package Sentra::Engine::Maintained {
use strict;
use warnings;
use Mojo::UserAgent;
use Mojo::JSON qw(decode_json);
use JSON;
use DateTime;
use DateTime::Format::ISO8601;
use Sentra::Utils::UserAgent;
use Sentra::Utils::Repositories_List;

sub new {
my ($class, $org, $token, $per_page) = @_;

my $userAgent = Mojo::UserAgent -> new();
my $output = '';
my $userAgent = Sentra::Utils::UserAgent -> new($token);
my @repositories_list = Sentra::Utils::Repositories_List -> new($org, $token);

my $headers = {
'Authorization' => "Bearer $token",
'Accept' => 'application/vnd.github+json',
'X-GitHub-Api-Version' => '2022-11-28'
};
foreach my $repository (@repositories_list) {
my $get_commits = $userAgent -> get("https://api.github.com/repos/$repository/commits");

my $output = '';

my $repo_url = "https://api.github.com/orgs/$org/repos?per_page=$per_page";
my $repo_tx = $userAgent -> get($repo_url => $headers);

my $res = $repo_tx -> result or return "Error fetching repositories: " . $repo_tx->error->{message} . "\n";
$res -> is_success or return "Error fetching repositories: " . $res->message . "\n";

my $repos = $res->json;

for my $repo (@$repos) {
next if $repo -> {archived};

my $full_name = "$org/$repo->{name}";


my $commits_url = "https://api.github.com/repos/$full_name/commits";
my $commits_tx = $userAgent->get($commits_url => $headers);
my $commits_res = $commits_tx->result;

if ($commits_res && $commits_res->is_success) {
my $commits = $commits_res->json;

if ($get_commits -> code() == 200) {
my $commits = decode_json($get_commits -> content());

if (@$commits) {
my $last_commit_date_str = $commits->[0]{commit}{committer}{date};
my $last_commit_date = DateTime::Format::ISO8601->parse_datetime($last_commit_date_str);

$output .= "The repository https://github.com/$full_name has not been updated for more than 90 days.\n"
if DateTime -> now -> subtract(days => 90) > $last_commit_date;
my $last_commit_date = DateTime::Format::ISO8601 -> parse_datetime($last_commit_date_str);

if (DateTime -> now -> subtract(days => 90) > $last_commit_date) {
$output .= "The repository https://github.com/$repository has not been updated for more than 90 days.\n";
}
}
}
}

return $output || "No issues found.";
return $output;
}
}

Expand Down
45 changes: 14 additions & 31 deletions lib/Sentra/Engine/SearchFiles.pm
Original file line number Diff line number Diff line change
@@ -1,44 +1,27 @@
package Sentra::Engine::SearchFiles {
use strict;
use warnings;
use Mojo::UserAgent;
use Mojo::JSON qw(decode_json);
use JSON;
use Sentra::Utils::UserAgent;
use Sentra::Utils::Repositories_List;

sub new {
my ($class, $org, $token, $per_page) = @_;

my $userAgent = Mojo::UserAgent -> new();
my $output = '';
my $userAgent = Sentra::Utils::UserAgent -> new($token);
my @repositories_list = Sentra::Utils::Repositories_List -> new($org, $token);

my $headers = {
'Authorization' => "Bearer $token",
'Accept' => 'application/vnd.github+json',
'X-GitHub-Api-Version' => '2022-11-28'
};

my $output = '';

my $repo_url = "https://api.github.com/orgs/$org/repos?per_page=$per_page";
my $repo_tx = $userAgent -> get($repo_url => $headers);

my $res = $repo_tx->result or return "Error fetching repositories: " . $repo_tx->error->{message} . "\n";
$res->is_success or return "Error fetching repositories: " . $res->message . "\n";

my $repos = $res->json;

for my $repo (@$repos) {
next if $repo -> {archived};

my $full_name = "$org/$repo->{name}";

my $dependabot_url = "https://api.github.com/repos/$full_name/contents/.github/dependabot.yaml";
my $dependabot_tx = $userAgent->get($dependabot_url => $headers);

if ($dependabot_tx->result->code == 404) {
$output .= "The dependabot.yml file was not found in this repository: https://github.com/$full_name\n";
}
foreach my $repository (@repositories_list) {
my $dependabot_url = "https://api.github.com/repos/$repository/contents/.github/dependabot.yaml";
my $request = $userAgent -> get($dependabot_url);

if ($request -> code == 404) {
$output .= "The dependabot.yml file was not found in this repository: https://github.com/$repository\n";
}
}

return $output || "No issues found.";
return $output;
}
}

Expand Down
8 changes: 4 additions & 4 deletions lib/Sentra/Engine/SlackWebhook.pm
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ package Sentra::Engine::SlackWebhook {
my $userAgent = Mojo::UserAgent -> new();
my $payload = encode_json({text => $message});

my $tx = $userAgent -> post($webhook => {
my $text = $userAgent -> post($webhook => {
'Content-Type' => 'application/json'
} => $payload);

my $res = $tx -> result;
my $res = $text -> result;

unless ($res) {
my $err = $tx->error;
my $err = $text -> error;
return "Failed to send message: [" . ($err->{message} || "Unknown error") . "]\n";
}

return "Failed to send message: [" . $res->message . "]\n" unless $res->is_success;
return "Failed to send message: [" . $res->message . "]\n" unless $res -> is_success;

return "Message sent successfully! [" . $res->body . "]\n";
}
Expand Down
4 changes: 2 additions & 2 deletions lib/Sentra/Utils/Helper.pm
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ package Sentra::Utils::Helper {
\r\t------- -----------
\r\t-o, --org Specify the name of the organization
\r\t-t, --token Set the GitHub Token to use during actions
\r\t-mt, --maintained Check last commit date of repositories
\r\t-d, --dependency Check for dependabot.yaml file in repositories
\r\t-mt, --maintained Get alerts about repositories with a last commit date greater than 90 days old
\r\t-d, --dependency Check if repositories has dependabot.yaml file
\r\t-M, --metrics See some metrics based on GHAS
\r\t-w, --webhook Set the webhook address for Slack
\r\t-m, --message Message to send via Slack webhook\n\n";
Expand Down
37 changes: 37 additions & 0 deletions lib/Sentra/Utils/Repositories_List.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package Sentra::Utils::Repositories_List {
use strict;
use warnings;
use JSON;
use Sentra::Utils::UserAgent;

sub new {
my ($self, $org, $token) = @_;

my @repos;
my $page = 1;
my $userAgent = Sentra::Utils::UserAgent -> new($token);

while (1) {
my $url = "https://api.github.com/orgs/$org/repos?per_page=100&page=$page";
my $response = $userAgent -> get($url);

if ($response -> code() == 200) {
my $data = decode_json($response -> content());

if (scalar(@$data) == 0) {
last;
}

for my $repo (@$data) {
push @repos, "$org/$repo->{name}" unless $repo->{archived};
}

$page++;
}
}

return @repos;
}
}

1;
28 changes: 28 additions & 0 deletions lib/Sentra/Utils/UserAgent.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package Sentra::Utils::UserAgent {
use strict;
use warnings;
use LWP::UserAgent;

sub new {
my ($self, $token) = @_;

my $userAgent = LWP::UserAgent -> new(
timeout => 5,
ssl_opts => {
verify_hostname => 0,
SSL_verify_mode => 0
},
agent => "Sentra 0.0.3"
);

$userAgent -> default_headers -> header (
'X-GitHub-Api-Version' => '2022-11-28',
'Accept' => 'application/vnd.github+json',
'Authorization' => "Bearer $token"
);

return $userAgent;
}
}

1;
Loading
Loading