From 85a9877582af4a76eb31f0083e3dcd6e3c564523 Mon Sep 17 00:00:00 2001 From: Samuel Gulart Date: Tue, 26 Nov 2024 21:05:43 -0300 Subject: [PATCH 01/22] Useless interpolation of literal string at sentra.pl --- sentra.pl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sentra.pl b/sentra.pl index 4ed4694..268ccd9 100644 --- a/sentra.pl +++ b/sentra.pl @@ -14,13 +14,13 @@ sub main { my ($org, $token, $webhook, $message, $maintained, $dependency, $per_page); GetOptions( - "o|org=s" => \$org, - "t|token=s" => \$token, - "w|webhook=s" => \$webhook, - "m|message=s" => \$message, - "mt|maintained" => \$maintained, - "d|dependency" => \$dependency, - "p|per_page=i" => \$per_page + 'o|org=s' => \$org, + 't|token=s' => \$token, + 'w|webhook=s' => \$webhook, + 'm|message=s' => \$message, + 'mt|maintained' => \$maintained, + 'd|dependency' => \$dependency, + 'p|per_page=i' => \$per_page ); $per_page ||= 100; From d4c28a6a3fdac6a89df68cd907e0667ba3e7207c Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 16:56:43 +0000 Subject: [PATCH 02/22] update linter severity --- .perlcriticrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.perlcriticrc b/.perlcriticrc index db19e58..fcd6d87 100644 --- a/.perlcriticrc +++ b/.perlcriticrc @@ -1,4 +1,4 @@ -severity = 3 +severity = 2 [-TestingAndDebugging::RequireUseStrict] [-TestingAndDebugging::RequireUseWarnings] From d9e5ecab24b509f88009bfa5264da716b25dd642 Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 16:59:50 +0000 Subject: [PATCH 03/22] pushing docker image --- Dockerfile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Dockerfile b/Dockerfile index e69de29..2eb10d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM perl:5.40 + +COPY . /usr/src/sentra +WORKDIR /usr/src/sentra + +RUN cpanm --installdeps . + +ENTRYPOINT [ "perl", "./sentra.pl" ] \ No newline at end of file From 613b2fc4dca7d5a0726cfeb1529fc0f2970fbf0d Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:02:25 +0000 Subject: [PATCH 04/22] now dependabot has coverage to docker ecosystem as well --- .github/dependabot.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ca79ca5..d603467 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,10 @@ version: 2 updates: - - package-ecosystem: github-actions + - package-ecosystem: docker directory: / schedule: interval: weekly + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly \ No newline at end of file From d756b9842192800177a52bcd4744abed62f3d1c1 Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:03:38 +0000 Subject: [PATCH 05/22] enabling zarn as a sast tool --- .github/workflows/zarn.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/zarn.yml diff --git a/.github/workflows/zarn.yml b/.github/workflows/zarn.yml new file mode 100644 index 0000000..5f077c2 --- /dev/null +++ b/.github/workflows/zarn.yml @@ -0,0 +1,25 @@ +name: ZARN SAST + +on: + push: + branches: [ "main", "develop" ] + pull_request: + branches: [ "main", "develop" ] + schedule: + - cron: '28 23 * * 1' + +jobs: + zarn: + name: Security Static Analysis with ZARN + runs-on: ubuntu-20.04 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Perform Static Analysis + uses: htrgouvea/zarn@0.0.9 + + - name: Send result to Github Security + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: result.sarif \ No newline at end of file From ea066434d1d6a07de0da6623cda23a4dc762db01 Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:07:18 +0000 Subject: [PATCH 06/22] remove blank line --- .github/workflows/test_suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_suite.yml b/.github/workflows/test_suite.yml index 89d2c1c..75d6eb2 100644 --- a/.github/workflows/test_suite.yml +++ b/.github/workflows/test_suite.yml @@ -25,4 +25,4 @@ jobs: - name: Run tests working-directory: ./tests - run: prove -r + run: prove -r \ No newline at end of file From 9af6e4209aeee20a88a41fb4c535222509013984 Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:07:34 +0000 Subject: [PATCH 07/22] add security gate --- .github/workflows/security-gate.yaml | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/security-gate.yaml diff --git a/.github/workflows/security-gate.yaml b/.github/workflows/security-gate.yaml new file mode 100644 index 0000000..f81b7ab --- /dev/null +++ b/.github/workflows/security-gate.yaml @@ -0,0 +1,44 @@ +name: Security Gate - LESIS + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - main + - develop + +permissions: + security-events: read + contents: read + +jobs: + build: + runs-on: ubuntu-latest + env: + MAX_CRITICAL: 0 + MAX_HIGH: 0 + MAX_MEDIUM: 0 + MAX_LOW: 0 + GITHUB_TOKEN: ${{ secrets.TOKEN }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Pull Docker image from GitHub Container Registry + run: docker pull ghcr.io/instriq/security-gate/security-gate:latest + + - name: Verify security alerts from GHAS + run: | + docker run ghcr.io/instriq/security-gate/security-gate:latest \ + -t "$GITHUB_TOKEN" \ + -r "${{ github.repository }}" \ + -c "$MAX_CRITICAL" \ + -h "$MAX_HIGH" \ + -m "$MAX_MEDIUM" \ + -l "$MAX_LOW" \ + --dependency-alerts + --secrets-alerts + --code-alerts \ No newline at end of file From 2180ff30d536947e3a7ae4295d462191b72c9113 Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:08:40 +0000 Subject: [PATCH 08/22] add linter to the project --- .github/workflows/linter.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/linter.yml diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml new file mode 100644 index 0000000..4573e30 --- /dev/null +++ b/.github/workflows/linter.yml @@ -0,0 +1,21 @@ +name: Linter + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - main + - develop + +jobs: + critic: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run Perl::Critic + uses: natanlao/perl-critic-action@v1.1 + with: + files: critic \ No newline at end of file From 79336c032a0da33879145915c083c9134019a698 Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:10:13 +0000 Subject: [PATCH 09/22] add a workflow to validate the docker image --- .github/workflows/docker-image.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..d400d08 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,18 @@ +name: Docker Image CI + +on: + push: + branches: [ "main", "develop" ] + pull_request: + branches: [ "main", "develop" ] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag sentra:$(date +%s) \ No newline at end of file From 3954cfc9bfca859e69a14bd6c96f29275a6ceb3f Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:11:57 +0000 Subject: [PATCH 10/22] workflow to deploy docker images based on main branch content to the github registry --- .github/workflows/deploy-image.yml | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/deploy-image.yml diff --git a/.github/workflows/deploy-image.yml b/.github/workflows/deploy-image.yml new file mode 100644 index 0000000..d5900a6 --- /dev/null +++ b/.github/workflows/deploy-image.yml @@ -0,0 +1,37 @@ +name: Deploy to GitHub Container Registry + +on: + push: + branches: + - main + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + id: build + run: | + COMMIT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7) + IMAGE_NAME="ghcr.io/${{ github.repository }}/sentra" + docker build --file Dockerfile --tag ${IMAGE_NAME}:latest --tag ${IMAGE_NAME}:${COMMIT_SHA} . + echo "IMAGE_NAME=${IMAGE_NAME}" >> $GITHUB_ENV + echo "COMMIT_SHA=${COMMIT_SHA}" >> $GITHUB_ENV + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.LESIS_DEPLOY }} + + - name: Push Docker image to GitHub Container Registry + run: | + docker push ${{ env.IMAGE_NAME }}:latest + docker push ${{ env.IMAGE_NAME }}:${{ env.COMMIT_SHA }} \ No newline at end of file From 8e0d36541117a16152aaa512197ea4ffe51ebfcb Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:15:01 +0000 Subject: [PATCH 11/22] delete deprecated actions --- .github/workflows/dependabot_metrics.yml | 26 ------------------------ .github/workflows/maintaned.yml | 26 ------------------------ .github/workflows/search_files.yml | 26 ------------------------ 3 files changed, 78 deletions(-) delete mode 100644 .github/workflows/dependabot_metrics.yml delete mode 100644 .github/workflows/maintaned.yml delete mode 100644 .github/workflows/search_files.yml diff --git a/.github/workflows/dependabot_metrics.yml b/.github/workflows/dependabot_metrics.yml deleted file mode 100644 index e0533cb..0000000 --- a/.github/workflows/dependabot_metrics.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: DependaBot Metrics - -on: - schedule: - - cron: '28 23 * * 1' - -jobs: - build: - runs-on: ubuntu-latest - env: - TOKEN: ${{ secrets.COLLECT_ANALYCTS }} - ORGANIZATION: ${{ secrets.ORGANIZATION }} - WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - cpanm --installdeps . - - - name: Run DependaBot metrics and send to Slack - run: | - message=$(perl sentra.pl -o $ORGANIZATION -t $TOKEN) - perl sentra.pl -w $WEBHOOK -m "$message" diff --git a/.github/workflows/maintaned.yml b/.github/workflows/maintaned.yml deleted file mode 100644 index 18d1386..0000000 --- a/.github/workflows/maintaned.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Check for maintained repositories - -on: - schedule: - - cron: '28 23 * * 3' - -jobs: - build: - runs-on: ubuntu-latest - env: - TOKEN: ${{ secrets.READ_CONTENT }} - ORGANIZATION: ${{ secrets.ORGANIZATION }} - WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - cpanm --installdeps . - - - name: Check maintained repositories and send to Slack - run: | - message=$(perl sentra.pl -o $ORGANIZATION -t $TOKEN --maintained) - perl sentra.pl -w $WEBHOOK -m "$message" diff --git a/.github/workflows/search_files.yml b/.github/workflows/search_files.yml deleted file mode 100644 index 50b538f..0000000 --- a/.github/workflows/search_files.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Search for DependaBot files - -on: - schedule: - - cron: '28 23 * * 2' - -jobs: - build: - runs-on: ubuntu-latest - env: - TOKEN: ${{ secrets.READ_CONTENT }} - ORGANIZATION: ${{ secrets.ORGANIZATION }} - WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - cpanm --installdeps . - - - name: Search for DependaBot files and send to Slack - run: | - message=$(perl sentra.pl -o $ORGANIZATION -t $TOKEN --dependency) - perl sentra.pl -w $WEBHOOK -m "$message" From 21c6df75aded19ff942f5ecebdb62e55b0bc6d6e Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:15:59 +0000 Subject: [PATCH 12/22] remove blank lines --- .perlcriticrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.perlcriticrc b/.perlcriticrc index fcd6d87..a31400b 100644 --- a/.perlcriticrc +++ b/.perlcriticrc @@ -2,4 +2,4 @@ severity = 2 [-TestingAndDebugging::RequireUseStrict] [-TestingAndDebugging::RequireUseWarnings] -[-Subroutines::ProhibitManyArgs] +[-Subroutines::ProhibitManyArgs] \ No newline at end of file From 8b731cf890fa70fd000195fa3533f20b710fcc2c Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:26:04 +0000 Subject: [PATCH 13/22] add .editorconfig --- .editorconfig | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..733f173 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true \ No newline at end of file From 4312ee0373eb5f4d985add54165c4619990733c0 Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:27:25 +0000 Subject: [PATCH 14/22] add .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ccc9fd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.DS_Store \ No newline at end of file From b37dac5c871e72aed0f06486603058d577ae8b87 Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:27:44 +0000 Subject: [PATCH 15/22] update --- .perlcriticrc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.perlcriticrc b/.perlcriticrc index a31400b..efe23bc 100644 --- a/.perlcriticrc +++ b/.perlcriticrc @@ -2,4 +2,6 @@ severity = 2 [-TestingAndDebugging::RequireUseStrict] [-TestingAndDebugging::RequireUseWarnings] -[-Subroutines::ProhibitManyArgs] \ No newline at end of file + +[TestingAndDebugging::ProhibitNoWarnings] +allow = once \ No newline at end of file From 670793beab56177703a79782bdba6d34265050dd Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:28:06 +0000 Subject: [PATCH 16/22] fix some code style issues --- lib/Sentra/Engine/DependabotMetrics.pm | 35 +++++++++++++++++--------- lib/Sentra/Engine/SearchFiles.pm | 8 +++++- lib/Sentra/Engine/SlackWebhook.pm | 7 +++--- lib/Sentra/Utils/Helper.pm | 2 +- tests/DependabotMetrics.t | 2 +- tests/SearchFiles.t | 2 +- tests/SlackWebhook.t | 2 +- 7 files changed, 38 insertions(+), 20 deletions(-) diff --git a/lib/Sentra/Engine/DependabotMetrics.pm b/lib/Sentra/Engine/DependabotMetrics.pm index f6501e7..93d1818 100644 --- a/lib/Sentra/Engine/DependabotMetrics.pm +++ b/lib/Sentra/Engine/DependabotMetrics.pm @@ -7,26 +7,31 @@ package Sentra::Engine::DependabotMetrics { sub new { my ($class, $org, $token, $per_page) = @_; - my $ua = Mojo::UserAgent->new; + my $ua = Mojo::UserAgent -> new(); + my $headers = { 'X-GitHub-Api-Version' => '2022-11-28', - 'Accept' => 'application/vnd.github+json', - 'User-Agent' => 'Sentra 0.0.1', - 'Authorization' => "Bearer $token" + 'Accept' => 'application/vnd.github+json', + 'User-Agent' => 'Sentra 0.0.1', + '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 = $ua->get($repo_url => $headers); - - my $res = $repo_tx->result or return "Error fetching repositories: " . $repo_tx->error->{message} . "\n"; + my $repo_tx = $ua -> 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; + my $repo_data = $res -> json; + last unless @$repo_data; + push @repos, map { "$org/$_->{name}" } grep { !$_->{archived} } @$repo_data; + $repo_page++; } @@ -37,25 +42,31 @@ package Sentra::Engine::DependabotMetrics { for my $repo (@repos) { 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 = $ua->get($alert_url => $headers); - - my $res = $alert_tx->result or return "Error fetching alerts for $repo: " . $alert_tx->error->{message} . "\n"; + my $alert_tx = $ua->get($alert_url => $headers); + my $res = $alert_tx->result or return "Error fetching alerts for $repo: " . $alert_tx->error->{message} . "\n"; + $res->is_success or return "Error fetching alerts for $repo: " . $res->message . "\n"; my $alert_data = $res->json; + 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"; @@ -63,4 +74,4 @@ package Sentra::Engine::DependabotMetrics { } } -1; +1; \ No newline at end of file diff --git a/lib/Sentra/Engine/SearchFiles.pm b/lib/Sentra/Engine/SearchFiles.pm index 3f984dd..5104b12 100644 --- a/lib/Sentra/Engine/SearchFiles.pm +++ b/lib/Sentra/Engine/SearchFiles.pm @@ -25,13 +25,16 @@ package Sentra::Engine::SearchFiles { $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}"; if ($dependency) { my $dependabot_url = "https://api.github.com/repos/$full_name/contents/.github/dependabot.yaml"; my $dependabot_tx = $ua->get($dependabot_url => $headers); + $output .= "The dependabot.yml file was not found in this repository: https://github.com/$full_name\n" if $dependabot_tx->result->code == 404; } @@ -40,11 +43,14 @@ package Sentra::Engine::SearchFiles { my $commits_url = "https://api.github.com/repos/$full_name/commits"; my $commits_tx = $ua->get($commits_url => $headers); my $commits_res = $commits_tx->result; + if ($commits_res && $commits_res->is_success) { my $commits = $commits_res->json; + 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; } @@ -56,4 +62,4 @@ package Sentra::Engine::SearchFiles { } } -1; +1; \ No newline at end of file diff --git a/lib/Sentra/Engine/SlackWebhook.pm b/lib/Sentra/Engine/SlackWebhook.pm index 029fcfe..fb3b372 100644 --- a/lib/Sentra/Engine/SlackWebhook.pm +++ b/lib/Sentra/Engine/SlackWebhook.pm @@ -7,14 +7,15 @@ package Sentra::Engine::SlackWebhook { sub new { my ($class, $message, $webhook) = @_; - my $ua = Mojo::UserAgent->new; + my $ua = Mojo::UserAgent -> new(); my $payload = encode_json({text => $message}); my $tx = $ua->post($webhook => { 'Content-Type' => 'application/json' } => $payload); - my $res = $tx->result; + my $res = $tx -> result; + unless ($res) { my $err = $tx->error; return "Failed to send message: [" . ($err->{message} || "Unknown error") . "]\n"; @@ -26,4 +27,4 @@ package Sentra::Engine::SlackWebhook { } } -1; +1; \ No newline at end of file diff --git a/lib/Sentra/Utils/Helper.pm b/lib/Sentra/Utils/Helper.pm index 55d78c8..552e26a 100644 --- a/lib/Sentra/Utils/Helper.pm +++ b/lib/Sentra/Utils/Helper.pm @@ -22,4 +22,4 @@ EOT } } -1; +1; \ No newline at end of file diff --git a/tests/DependabotMetrics.t b/tests/DependabotMetrics.t index b282d8b..3fff1eb 100644 --- a/tests/DependabotMetrics.t +++ b/tests/DependabotMetrics.t @@ -53,4 +53,4 @@ subtest 'DependabotMetrics' => sub { done_testing(); -1; +1; \ No newline at end of file diff --git a/tests/SearchFiles.t b/tests/SearchFiles.t index 302d5da..6675fc8 100644 --- a/tests/SearchFiles.t +++ b/tests/SearchFiles.t @@ -53,4 +53,4 @@ subtest 'SearchFiles' => sub { done_testing(); -1; +1; \ No newline at end of file diff --git a/tests/SlackWebhook.t b/tests/SlackWebhook.t index eef08d8..56ddf2a 100644 --- a/tests/SlackWebhook.t +++ b/tests/SlackWebhook.t @@ -25,4 +25,4 @@ subtest 'SlackWebhook' => sub { done_testing(); -1; +1; \ No newline at end of file From cec7494e140e38e458740b31c0d3de997301bbc7 Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:28:36 +0000 Subject: [PATCH 17/22] fix some code style issues --- sentra.pl | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/sentra.pl b/sentra.pl index 268ccd9..566ff29 100644 --- a/sentra.pl +++ b/sentra.pl @@ -11,39 +11,41 @@ use Sentra::Utils::Helper; sub main { - my ($org, $token, $webhook, $message, $maintained, $dependency, $per_page); + my ($org, $token, $webhook, $message, $maintained, $dependency); + + my $per_page = 100; GetOptions( - 'o|org=s' => \$org, - 't|token=s' => \$token, - 'w|webhook=s' => \$webhook, - 'm|message=s' => \$message, - 'mt|maintained' => \$maintained, - 'd|dependency' => \$dependency, - 'p|per_page=i' => \$per_page + 'o|org=s' => \$org, + 't|token=s' => \$token, + 'w|webhook=s' => \$webhook, + 'm|message=s' => \$message, + 'mt|maintained' => \$maintained, + 'd|dependency' => \$dependency, + 'p|per_page=i' => \$per_page ); - $per_page ||= 100; - my %actions = ( 'dependabot-metrics' => ($org && $token && !$maintained && !$dependency) - ? sub { Sentra::Engine::DependabotMetrics->new($org, $token, $per_page) } + ? sub { Sentra::Engine::DependabotMetrics -> new($org, $token, $per_page) } : undef, 'repository-check' => ($org && $token && ($maintained || $dependency)) - ? sub { Sentra::Engine::SearchFiles->new($org, $token, $maintained, $dependency, $per_page) } + ? sub { Sentra::Engine::SearchFiles -> new($org, $token, $maintained, $dependency, $per_page) } : undef, 'send-webhook' => ($webhook) - ? sub { Sentra::Engine::SlackWebhook->new($message, $webhook) } + ? sub { Sentra::Engine::SlackWebhook -> new($message, $webhook) } : undef, ); for my $action (grep { defined } values %actions) { - print $action->(); + print $action -> (); + return 0; } - print Sentra::Utils::Helper->new(); + print Sentra::Utils::Helper -> new(); + return 1; } -exit main(); +exit main(); \ No newline at end of file From 168183879e2547bdca7536eacb8442779c838a44 Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:43:21 +0000 Subject: [PATCH 18/22] update version and rename variable to to be more reliable --- lib/Sentra/Engine/DependabotMetrics.pm | 8 +++---- lib/Sentra/Engine/SearchFiles.pm | 13 ++++++------ lib/Sentra/Engine/SlackWebhook.pm | 6 +++--- lib/Sentra/Utils/Helper.pm | 29 ++++++++++++-------------- 4 files changed, 27 insertions(+), 29 deletions(-) diff --git a/lib/Sentra/Engine/DependabotMetrics.pm b/lib/Sentra/Engine/DependabotMetrics.pm index 93d1818..62fc92e 100644 --- a/lib/Sentra/Engine/DependabotMetrics.pm +++ b/lib/Sentra/Engine/DependabotMetrics.pm @@ -7,12 +7,12 @@ package Sentra::Engine::DependabotMetrics { sub new { my ($class, $org, $token, $per_page) = @_; - my $ua = Mojo::UserAgent -> new(); + my $userAgent = Mojo::UserAgent -> new(); my $headers = { 'X-GitHub-Api-Version' => '2022-11-28', 'Accept' => 'application/vnd.github+json', - 'User-Agent' => 'Sentra 0.0.1', + 'User-Agent' => 'Sentra 0.0.2', 'Authorization' => "Bearer $token" }; @@ -21,7 +21,7 @@ package Sentra::Engine::DependabotMetrics { while (1) { my $repo_url = "https://api.github.com/orgs/$org/repos?per_page=$per_page&page=$repo_page"; - my $repo_tx = $ua -> get($repo_url => $headers); + 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"; @@ -45,7 +45,7 @@ package Sentra::Engine::DependabotMetrics { 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 = $ua->get($alert_url => $headers); + 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"; $res->is_success or return "Error fetching alerts for $repo: " . $res->message . "\n"; diff --git a/lib/Sentra/Engine/SearchFiles.pm b/lib/Sentra/Engine/SearchFiles.pm index 5104b12..6bd5037 100644 --- a/lib/Sentra/Engine/SearchFiles.pm +++ b/lib/Sentra/Engine/SearchFiles.pm @@ -9,17 +9,18 @@ package Sentra::Engine::SearchFiles { sub new { my ($class, $org, $token, $maintained, $dependency, $per_page) = @_; - my $ua = Mojo::UserAgent->new; + my $userAgent = Mojo::UserAgent->new; + my $headers = { - 'Authorization' => "Bearer $token", - 'Accept' => 'application/vnd.github+json', + '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 = $ua->get($repo_url => $headers); + 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"; @@ -33,7 +34,7 @@ package Sentra::Engine::SearchFiles { if ($dependency) { my $dependabot_url = "https://api.github.com/repos/$full_name/contents/.github/dependabot.yaml"; - my $dependabot_tx = $ua->get($dependabot_url => $headers); + my $dependabot_tx = $userAgent->get($dependabot_url => $headers); $output .= "The dependabot.yml file was not found in this repository: https://github.com/$full_name\n" if $dependabot_tx->result->code == 404; @@ -41,7 +42,7 @@ package Sentra::Engine::SearchFiles { if ($maintained) { my $commits_url = "https://api.github.com/repos/$full_name/commits"; - my $commits_tx = $ua->get($commits_url => $headers); + my $commits_tx = $userAgent->get($commits_url => $headers); my $commits_res = $commits_tx->result; if ($commits_res && $commits_res->is_success) { diff --git a/lib/Sentra/Engine/SlackWebhook.pm b/lib/Sentra/Engine/SlackWebhook.pm index fb3b372..ecf9d42 100644 --- a/lib/Sentra/Engine/SlackWebhook.pm +++ b/lib/Sentra/Engine/SlackWebhook.pm @@ -7,10 +7,10 @@ package Sentra::Engine::SlackWebhook { sub new { my ($class, $message, $webhook) = @_; - my $ua = Mojo::UserAgent -> new(); - my $payload = encode_json({text => $message}); + my $userAgent = Mojo::UserAgent -> new(); + my $payload = encode_json({text => $message}); - my $tx = $ua->post($webhook => { + my $tx = $userAgent -> post($webhook => { 'Content-Type' => 'application/json' } => $payload); diff --git a/lib/Sentra/Utils/Helper.pm b/lib/Sentra/Utils/Helper.pm index 552e26a..e9187a8 100644 --- a/lib/Sentra/Utils/Helper.pm +++ b/lib/Sentra/Utils/Helper.pm @@ -3,22 +3,19 @@ package Sentra::Utils::Helper { use warnings; sub new { - return <<"EOT"; - -Sentra v0.0.1 -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) - -EOT + return " + \rSentra v0.0.2 + \rCore Commands + \r============== + \r\tCommand Description + \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-w, --webhook Set the webhook address for Slack + \r\t-m, --message Message to send via Slack webhook + \r\t-mt, --maintained Check last commit date of repositories + \r\t-d, --dependency Check for dependabot.yaml file in repositories + \r\t-p, --per_page Set the number of items per page in API requests (default: 100)\n\n"; } } From 903721870de54c58074fd685495e2ed8f8e4dc96 Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 17:49:09 +0000 Subject: [PATCH 19/22] fix some code style issues --- tests/DependabotMetrics.t | 7 +++++++ tests/Helper.t | 3 +++ tests/SearchFiles.t | 4 ++++ tests/SlackWebhook.t | 5 +++++ 4 files changed, 19 insertions(+) diff --git a/tests/DependabotMetrics.t b/tests/DependabotMetrics.t index 3fff1eb..30fbe6e 100644 --- a/tests/DependabotMetrics.t +++ b/tests/DependabotMetrics.t @@ -12,25 +12,32 @@ my $repo_page_count = 0; my $alert_page_count = 0; my $mock_ua = Test::MockModule->new('Mojo::UserAgent'); + $mock_ua->mock('get', sub { my ($self, $url) = @_; my $tx = Mojo::Transaction::HTTP->new; if ($url =~ /\/repos\?/x) { $repo_page_count++; + if ($repo_page_count < 3) { $tx->res->code(200); $tx->res->body('[{"name":"repo1","archived":false},{"name":"repo2","archived":true}]'); + return $tx; } + $tx->res->code(200); $tx->res->body('[]'); } + elsif ($url =~ /\/dependabot\/alerts/x) { $alert_page_count++; + if ($alert_page_count == 1) { $tx->res->code(200); $tx->res->body('[{"security_vulnerability":{"severity":"high"}},{"security_vulnerability":{"severity":"low"}}]'); + return $tx; } diff --git a/tests/Helper.t b/tests/Helper.t index 8321977..1881b04 100644 --- a/tests/Helper.t +++ b/tests/Helper.t @@ -28,6 +28,7 @@ subtest 'Helper' => sub { ); my @missing_options; + for my $option (@expected_options) { push @missing_options, $option unless $helper_output =~ m/\Q$option\E/x; } @@ -36,9 +37,11 @@ subtest 'Helper' => sub { or diag "Missing options: " . join(", ", @missing_options); my $options_debug = "Options found:\n"; + for my $option (@expected_options) { $options_debug .= sprintf("%s: %s\n", $option, $helper_output =~ m/\Q$option\E/x ? "Yes" : "No"); } + diag $options_debug; pass('Printed debug information about options'); }; diff --git a/tests/SearchFiles.t b/tests/SearchFiles.t index 6675fc8..2ee36fc 100644 --- a/tests/SearchFiles.t +++ b/tests/SearchFiles.t @@ -10,6 +10,7 @@ use DateTime; use Mojo::Transaction::HTTP; my $mock_ua = Test::MockModule->new('Mojo::UserAgent'); + $mock_ua->mock('get', sub { my ($self, $url) = @_; my $tx = Mojo::Transaction::HTTP->new; @@ -18,14 +19,17 @@ $mock_ua->mock('get', sub { $tx->res->code(200); $tx->res->body('[{"name":"repo1","archived":false},{"name":"repo2","archived":true}]'); } + elsif ($url =~ /\/contents\/\.github\/dependabot\.yaml/x) { $tx->res->code(404); } + elsif ($url =~ /\/commits/x) { my $date = DateTime->now->subtract(days => 100)->iso8601; $tx->res->code(200); $tx->res->body(qq{[{"commit":{"committer":{"date":"$date"}}}]}); } + return $tx; }); diff --git a/tests/SlackWebhook.t b/tests/SlackWebhook.t index 56ddf2a..151ee76 100644 --- a/tests/SlackWebhook.t +++ b/tests/SlackWebhook.t @@ -9,17 +9,22 @@ use Sentra::Engine::SlackWebhook; use Mojo::Transaction::HTTP; my $mock_ua = Test::MockModule->new('Mojo::UserAgent'); + $mock_ua->mock('post', sub { my ($self, $url, $headers, $payload) = @_; my $tx = Mojo::Transaction::HTTP->new; + $tx->res->code(200); $tx->res->body('ok'); + return $tx; }); subtest 'SlackWebhook' => sub { plan tests => 1; + my $webhook = Sentra::Engine::SlackWebhook->new('Test message', 'https://hooks.slack.com/services/xxx/yyy/zzz'); + like($webhook, qr/Message sent successfully/, 'Webhook message sent successfully'); }; From 0e1740f16a121b73747e2264e523f0ae72cd8aad Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 18:29:49 +0000 Subject: [PATCH 20/22] split code --- lib/Sentra/Engine/Maintained.pm | 57 ++++++++++++++++++++++++++++++++ lib/Sentra/Engine/SearchFiles.pm | 39 +++++----------------- 2 files changed, 66 insertions(+), 30 deletions(-) create mode 100644 lib/Sentra/Engine/Maintained.pm diff --git a/lib/Sentra/Engine/Maintained.pm b/lib/Sentra/Engine/Maintained.pm new file mode 100644 index 0000000..d1ddffc --- /dev/null +++ b/lib/Sentra/Engine/Maintained.pm @@ -0,0 +1,57 @@ +package Sentra::Engine::Maintained { + use strict; + use warnings; + use Mojo::UserAgent; + use Mojo::JSON qw(decode_json); + use DateTime; + use DateTime::Format::ISO8601; + + sub new { + my ($class, $org, $token, $per_page) = @_; + + my $userAgent = Mojo::UserAgent -> new(); + + 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 $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 (@$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; + } + } + } + + return $output || "No issues found."; + } +} + +1; \ No newline at end of file diff --git a/lib/Sentra/Engine/SearchFiles.pm b/lib/Sentra/Engine/SearchFiles.pm index 6bd5037..0b2f3ad 100644 --- a/lib/Sentra/Engine/SearchFiles.pm +++ b/lib/Sentra/Engine/SearchFiles.pm @@ -3,13 +3,11 @@ package Sentra::Engine::SearchFiles { use warnings; use Mojo::UserAgent; use Mojo::JSON qw(decode_json); - use DateTime; - use DateTime::Format::ISO8601; sub new { - my ($class, $org, $token, $maintained, $dependency, $per_page) = @_; + my ($class, $org, $token, $per_page) = @_; - my $userAgent = Mojo::UserAgent->new; + my $userAgent = Mojo::UserAgent -> new(); my $headers = { 'Authorization' => "Bearer $token", @@ -28,35 +26,16 @@ package Sentra::Engine::SearchFiles { my $repos = $res->json; for my $repo (@$repos) { - next if $repo->{archived}; + next if $repo -> {archived}; my $full_name = "$org/$repo->{name}"; - if ($dependency) { - my $dependabot_url = "https://api.github.com/repos/$full_name/contents/.github/dependabot.yaml"; - my $dependabot_tx = $userAgent->get($dependabot_url => $headers); - - $output .= "The dependabot.yml file was not found in this repository: https://github.com/$full_name\n" - if $dependabot_tx->result->code == 404; - } - - if ($maintained) { - 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 (@$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 $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"; + } } return $output || "No issues found."; From 4f59d70ac48f4e73724e34d9bb7e98d4910bd263 Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 18:30:23 +0000 Subject: [PATCH 21/22] trying to fix issues --- lib/Sentra/Engine/DependabotMetrics.pm | 16 ++++++--- lib/Sentra/Utils/Helper.pm | 2 +- sentra.pl | 46 ++++++++++++++------------ 3 files changed, 37 insertions(+), 27 deletions(-) diff --git a/lib/Sentra/Engine/DependabotMetrics.pm b/lib/Sentra/Engine/DependabotMetrics.pm index 62fc92e..557905d 100644 --- a/lib/Sentra/Engine/DependabotMetrics.pm +++ b/lib/Sentra/Engine/DependabotMetrics.pm @@ -12,7 +12,7 @@ package Sentra::Engine::DependabotMetrics { my $headers = { 'X-GitHub-Api-Version' => '2022-11-28', 'Accept' => 'application/vnd.github+json', - 'User-Agent' => 'Sentra 0.0.2', + 'User-Agent' => 'Sentra 0.0.3', 'Authorization' => "Bearer $token" }; @@ -38,26 +38,32 @@ package Sentra::Engine::DependabotMetrics { return "Error when trying to request information from GitHub, please review the parameters provided." unless @repos; my $total_alerts = 0; - my %severity_count = (low => 0, medium => 0, high => 0, critical => 0); + + my %severity_count = ( + low => 0, + medium => 0, + high => 0, + critical => 0 + ); for my $repo (@repos) { 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 $alert_tx = $userAgent -> get($alert_url => $headers); my $res = $alert_tx->result or return "Error fetching alerts for $repo: " . $alert_tx->error->{message} . "\n"; $res->is_success or return "Error fetching alerts for $repo: " . $res->message . "\n"; - my $alert_data = $res->json; + my $alert_data = $res -> json; last unless @$alert_data; $total_alerts += scalar @$alert_data; for my $alert (@$alert_data) { - my $severity = $alert->{security_vulnerability}{severity} || 'unknown'; + my $severity = $alert -> {security_vulnerability}{severity} || 'unknown'; $severity_count{$severity}++ if exists $severity_count{$severity}; } diff --git a/lib/Sentra/Utils/Helper.pm b/lib/Sentra/Utils/Helper.pm index e9187a8..f9480c5 100644 --- a/lib/Sentra/Utils/Helper.pm +++ b/lib/Sentra/Utils/Helper.pm @@ -4,7 +4,7 @@ package Sentra::Utils::Helper { sub new { return " - \rSentra v0.0.2 + \rSentra v0.0.3 \rCore Commands \r============== \r\tCommand Description diff --git a/sentra.pl b/sentra.pl index 566ff29..7e550da 100644 --- a/sentra.pl +++ b/sentra.pl @@ -3,48 +3,52 @@ use 5.030; use strict; use warnings; -use Getopt::Long; +use Getopt::Long qw(:config no_ignore_case); use lib './lib/'; use Sentra::Engine::DependabotMetrics; use Sentra::Engine::SearchFiles; use Sentra::Engine::SlackWebhook; +use Sentra::Engine::Maintained; use Sentra::Utils::Helper; sub main { - my ($org, $token, $webhook, $message, $maintained, $dependency); - + my ($org, $token, $webhook, $message, $help, %options); + my $per_page = 100; - + GetOptions( 'o|org=s' => \$org, 't|token=s' => \$token, 'w|webhook=s' => \$webhook, 'm|message=s' => \$message, - 'mt|maintained' => \$maintained, - 'd|dependency' => \$dependency, - 'p|per_page=i' => \$per_page + 'h|help' => \$help, + 'mt|maintained' => \$options{'maintained'}, + 'd|dependency' => \$options{'dependency'}, + 'M|metrics' => \$options{'metrics'}, ); - my %actions = ( - 'dependabot-metrics' => ($org && $token && !$maintained && !$dependency) - ? sub { Sentra::Engine::DependabotMetrics -> new($org, $token, $per_page) } - : undef, - 'repository-check' => ($org && $token && ($maintained || $dependency)) - ? sub { Sentra::Engine::SearchFiles -> new($org, $token, $maintained, $dependency, $per_page) } - : undef, - 'send-webhook' => ($webhook) - ? sub { Sentra::Engine::SlackWebhook -> new($message, $webhook) } - : undef, + my %dispatch_table = ( + 'metrics' => sub { Sentra::Engine::DependabotMetrics->new($org, $token, $per_page) }, + 'dependency' => sub { Sentra::Engine::SearchFiles->new($org, $token, $per_page) }, + 'maintained' => sub { Sentra::Engine::Maintained->new($org, $token, $per_page) }, ); - for my $action (grep { defined } values %actions) { - print $action -> (); + for my $option (keys %options) { + if ($options{$option} && exists $dispatch_table{$option}) { + print $dispatch_table{$option}->(); + } + } + + if ($webhook && $message) { + Sentra::Engine::SlackWebhook->new($message, $webhook)->send(); + } + + if ($help) { + print Sentra::Utils::Helper -> new(); return 0; } - print Sentra::Utils::Helper -> new(); - return 1; } From ba8026f5c57f8bfe21c32f4aa61a92135e924cba Mon Sep 17 00:00:00 2001 From: htrgouvea Date: Thu, 28 Nov 2024 18:37:36 +0000 Subject: [PATCH 22/22] update helper menu --- lib/Sentra/Utils/Helper.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Sentra/Utils/Helper.pm b/lib/Sentra/Utils/Helper.pm index f9480c5..5205ebc 100644 --- a/lib/Sentra/Utils/Helper.pm +++ b/lib/Sentra/Utils/Helper.pm @@ -11,11 +11,11 @@ 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-w, --webhook Set the webhook address for Slack - \r\t-m, --message Message to send via Slack webhook \r\t-mt, --maintained Check last commit date of repositories \r\t-d, --dependency Check for dependabot.yaml file in repositories - \r\t-p, --per_page Set the number of items per page in API requests (default: 100)\n\n"; + \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"; } }