Skip to content

Conversation

@tmikula-dev
Copy link
Collaborator

@tmikula-dev tmikula-dev commented Oct 17, 2025

Release Notes:

  • Changes that can make Terraform Static Check a mandatory action

Summary by CodeRabbit

  • Chores

    • Added targeted change-detection flows and conditional CI jobs for Terraform, Docker, and Python so analyses run only when relevant files change; added noop paths when no changes detected.
    • Added a full-repository security scan that publishes SARIF, posts a summarized findings table on PRs, and updated workflow metadata and checkout behavior.
    • Removed a legacy multi-job CI workflow.
  • Documentation

    • Updated developer guidance and examples for security scanning and outputs; reformatted local development/tools section.
  • New Features

    • Added credential detection rules for secret/password discovery in scans.

@coderabbitai
Copy link

coderabbitai bot commented Oct 17, 2025

Walkthrough

Adds multiple detect-and-run GitHub Actions workflows (Terraform, Docker, Python), a full-repo Trivy scan with SARIF summary/commenting, refactors Terraform workflow to conditional TFLint, removes the old end-to-end test.yml, updates Trivy docs and rules, and makes a minor Terraform formatting tweak.

Changes

Cohort / File(s) Summary
Terraform Workflow Refactor
.github/workflows/check_terraform.yml
Replaces Trivy step with git-diff detection that exports terraform_changed; runs TFLint only when true; adds noop path; updates actions (checkout@v5, setup-tflint@v6, codeql-action/upload-sarif@v4), concurrency naming, and fetch-depth: 0.
New Docker Workflow
.github/workflows/check_docker.yml
Adds detect job exporting docker_changed via git-diff; runs Trivy config scan on Dockerfile and uploads SARIF when true; includes noop job otherwise and fetch-depth: 0.
New Python Workflow
.github/workflows/check_python.yml
Adds detect job exporting python_changed for *.py diffs; conditionally runs Pylint, Black (format check), Pytest (coverage gate), and Mypy when true; includes noop job when no changes.
Full-Repository Trivy Scan & Rules
.github/workflows/trivy_auto_repository_scan.yml, trivy-secret.yaml
Adds full-repo Trivy filesystem scan (vuln/secret/misconfig/license) with SARIF upload; parses SARIF to produce a severity summary table and posts a PR comment; introduces trivy-secret.yaml with custom plaintext credential rules and enabled builtins.
Workflow Metadata & Versions
.github/workflows/check_pr_release_notes.yml, various workflows
Renames/updates workflow metadata and action versions (e.g., actions/checkout@v5, codeql-action/upload-sarif@v4), standardizes fetch-depth: 0, and adjusts concurrency naming.
Removed CI Workflow
.github/workflows/test.yml
Deletes monolithic test.yml that previously ran pylint, Black, pytest, and mypy.
Docs
DEVELOPER.md, README.md
DEVELOPER.md: updates Trivy examples to trivy fs and notes trivy_scan.txt output. README.md: reformats Local Development & Testing into a two-column table and adjusts labels/spacing.
Minor Terraform Whitespace
terraform/lambda.tf
Removes an empty line; no logic change.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant GH as GitHub Event
    participant Detect as Detect Job
    participant Cond as Condition (output *_changed)
    participant Analysis as Analysis Job(s)
    participant SARIF as SARIF Upload / Summary
    participant Noop as Noop Job

    GH->>Detect: trigger workflow (PR / push / dispatch)
    Detect->>Detect: compute git-diff range (PR: base..head, Push: prev..current)
    Detect->>Detect: inspect target paths (terraform/, Dockerfile, *.py, repo)
    Detect->>Cond: export *_changed (true/false)

    alt *_changed == "true"
        Cond->>Analysis: run relevant analysis (TFLint / Trivy / Pylint/Black/Pytest/Mypy)
        Analysis->>SARIF: produce & upload SARIF (if applicable)
        SARIF->>GH: optionally post summary comment (full-repo Trivy)
    else *_changed == "false"
        Cond->>Noop: run noop (log no changes)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay attention to:
    • check_terraform.yml — diff-range logic, output export, and conditional TFLint gating.
    • trivy_auto_repository_scan.yml — SARIF parsing Python script, output formatting, and PR comment permissions.
    • check_python.yml / check_docker.yml — repeated environment/setup steps and dependency handling.
    • trivy-secret.yaml — rule accuracy and false-positive risk.

Possibly related issues

Possibly related PRs

Suggested reviewers

  • Zejnilovic
  • ABMC831
  • petr-pokorny-absa
  • oto-macenauer-absa
  • miroslavpojer

Poem

🐇 I hopped through diffs with eager feet,
I nudged the workflows to make checks neat.
When nothing changed, I munched a calm carrot,
Else I ran scans, then posted the merit.
Hop, report, repeat — CI's tidy treat!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Trivy Scanning tool pilot implementation" is partially related to the changeset. It accurately refers to a significant and real component of the changes—Trivy scanning is indeed being added and integrated across multiple new workflows (check_terraform.yml, check_docker.yml, trivy_auto_repository_scan.yml, and trivy-secret.yaml). However, the title does not capture the broader main objective, which is a restructuring of the CI/CD pipeline into modular, language-specific workflows that include multiple tools beyond Trivy (TFLint, Pylint, Black, Pytest, Mypy). The title focuses on one prominent tool being piloted rather than the overarching structural refactoring and standardization effort mentioned in the PR description.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/terraform-ci-linter-pilot-update

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
.github/workflows/check_terraform.yml (1)

19-19: Minor: Clarify the comment explaining workflow intent.

The comment "This job is implemented this way, because we need workflow mandatory" is unclear. Consider rephrasing to explain that the detect job ensures the workflow always executes (even with no changes) to maintain mandatory status in branch protection rules.

-  ## This job is implemented this way, because we need workflow mandatory
+  ## Detect job ensures workflow runs regardless of changes, making it mandatory in branch protection rules.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4af9f10 and 8beba93.

📒 Files selected for processing (1)
  • .github/workflows/check_terraform.yml (3 hunks)
🔇 Additional comments (4)
.github/workflows/check_terraform.yml (4)

45-46: Conditional execution structure looks good.

The needs: detect dependency and if: needs.detect.outputs.changed == 'true' conditions correctly gate the trivy and tflint jobs, ensuring they skip when terraform/ has no changes while maintaining overall workflow status via the noop job.

Also applies to: 83-84


122-127: Noop job effectively preserves mandatory workflow status.

The noop job ensures at least one job always completes successfully, preventing the workflow from being skipped entirely when terraform/ hasn't changed—a critical design pattern for making this workflow mandatory in branch protection.


25-25: Consistent checkout action upgrade to v5.

All three jobs (detect, trivy, tflint) uniformly upgrade to actions/checkout@v5 while preserving the fetch-depth: 0 configuration needed for git history operations.

Also applies to: 49-49, 87-87


32-42: I'll verify the critical claim about the git diff edge case and GitHub Actions behavior.

Let me verify one more critical detail about how HEAD~1...HEAD behaves on the first commit.

Critical issue is valid, but one suggested fix is flawed. The HEAD~1...HEAD approach does not gracefully handle first commits—it also fails with a fatal error.

Using git diff HEAD^ HEAD on the first commit results in "fatal: ambiguous argument 'HEAD^': unknown revision or path not in the working tree". The same applies to HEAD~1...HEAD. The review's claim that this approach "handles the initial commit gracefully" is incorrect.

The conditional check approach (using git rev-parse "${{ github.sha }}~1" to verify the parent exists) remains the correct solution. Remove the misleading suggestion about HEAD~1...HEAD.

Likely an incorrect or invalid review comment.

@tmikula-dev tmikula-dev self-assigned this Oct 17, 2025
@tmikula-dev tmikula-dev added the enhancement New feature or request label Oct 17, 2025
@tmikula-dev tmikula-dev linked an issue Oct 21, 2025 that may be closed by this pull request
@tmikula-dev tmikula-dev added the work in progress Work on this item is not yet finished (mainly intended for PRs) label Oct 21, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
Dockerfile (3)

21-26: Critical: Docker build fails due to missing COPY source paths.

The pipeline failures show COPY commands referencing build args that don't exist by default: ./trusted_certs and ./sasl_ssl_artifacts. When these directories are not provided at build time, the COPY operations fail, breaking the Docker image build.

Either provide default directories that exist in the repository, make these COPY operations conditional with fallbacks, or document that these build args are required and must be supplied during builds.

Suggested fix: Make COPY operations conditional or require the build args:

# Option 1: Make paths optional by checking if they exist
ARG TRUSTED_SSL_CERTS=./trusted_certs
ARG SASL_SSL_ARTIFACTS=./sasl_ssl_artifacts

# Create empty directories as fallbacks
RUN mkdir -p /opt/certs/ /opt/sasl_ssl_artifacts/

# Conditionally copy only if directories exist and have files
COPY --chown=0:0 ${TRUSTED_SSL_CERTS}/*.pem /opt/certs/ 2>/dev/null || true
COPY ${SASL_SSL_ARTIFACTS} /opt/sasl_ssl_artifacts/ 2>/dev/null || true

Or:

# Option 2: Require the build args and ensure they're provided
ARG TRUSTED_SSL_CERTS
ARG SASL_SSL_ARTIFACTS

COPY ${TRUSTED_SSL_CERTS} /opt/certs/
COPY ${SASL_SSL_ARTIFACTS} /opt/sasl_ssl_artifacts/

18-18: Minor: FROM --platform flag uses constant value.

Hadolint warns against using constant platform flags in FROM statements, as this limits portability and should typically be left to runtime configuration or build context.

This is a low-priority warning but can be addressed by removing the platform specification if cross-platform builds aren't a requirement, or documenting the ARM64-specific constraint.


66-66: Verify LDFLAGS path for librdkafka.

Line 66 sets LDFLAGS="-L/opt", but line 53 installs librdkafka to the default location (likely /usr/local/lib). The LDFLAGS path should match the installation directory:

- CPPFLAGS="-I/usr/local/include" LDFLAGS="-L/opt" python setup.py install
+ CPPFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib" python setup.py install
🧹 Nitpick comments (3)
Dockerfile (1)

50-53: Consider adding checksums for downloaded binaries.

Lines 50 and 63 download source archives without verifying integrity. Adding checksums improves security and reproducibility:

RUN \
  mkdir -p /tmp/env-install-workdir/librdkafka && \
  cd /tmp/env-install-workdir/librdkafka && \
  wget https://github.com/edenhill/librdkafka/archive/v2.4.0.tar.gz && \
  echo "expected_sha256_hash  v2.4.0.tar.gz" | sha256sum -c - && \
  tar -xf v2.4.0.tar.gz && \
  # ... rest of build

Also applies to: 63-66

.github/workflows/check_python.yml (1)

35-44: Change detection logic is sound but consider edge cases.

The detect job correctly uses git diff with proper branching logic for PRs vs pushes. However, the pattern ^(src|tests)/ matches files at any path depth; ensure this covers nested directories like src/module/file.py (which it does).

One edge case: If only configuration files like setup.cfg or pytest.ini change, Python analysis still runs. Consider whether config-only changes should trigger the workflow.

.github/workflows/check_docker.yml (1)

40-40: Dockerfile detection pattern matches only root-level Dockerfile.

Line 40 uses pattern ^Dockerfile$, which only matches the Dockerfile at the repository root. If other Dockerfiles exist in subdirectories (e.g., services/api/Dockerfile), they won't trigger this workflow.

If the repo has multiple Dockerfiles, update the pattern:

if git diff --name-only "$RANGE" | grep -qE '(^|/)Dockerfile'; then

Otherwise, document that only the root Dockerfile is scanned.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c972f06 and c65866f.

📒 Files selected for processing (7)
  • .github/workflows/check_docker.yml (1 hunks)
  • .github/workflows/check_python.yml (1 hunks)
  • .github/workflows/check_terraform.yml (2 hunks)
  • .github/workflows/test.yml (0 hunks)
  • DEVELOPER.md (1 hunks)
  • Dockerfile (1 hunks)
  • README.md (1 hunks)
💤 Files with no reviewable changes (1)
  • .github/workflows/test.yml
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • DEVELOPER.md
🧰 Additional context used
🪛 GitHub Actions: Static Docker Check
Dockerfile

[error] 5-5: failed to calculate checksum of ref ac1187db-1850-4397-a0f7-3b6e6ef293d6::/trusted_certs: "/trusted_certs" not found


[error] 7-7: failed to calculate checksum of ref ac1187db-1850-4397-a0f7-3b6e6ef293d6::/sasl_ssl_artifacts: not found


[warning] 18-18: FromPlatformFlagConstDisallowed: FROM --platform flag should not use constant value "linux/arm64"


[error] 74-74: COPY $SASL_SSL_ARTIFACTS /opt/sasl_ssl_artifacts/ exited with error due to missing source path

🔇 Additional comments (5)
.github/workflows/check_python.yml (2)

127-128: Pytest coverage threshold at 80% is strict—verify it's achievable.

The --cov-fail-under=80 flag enforces 80% coverage on tests. Ensure the test suite actually meets this threshold; if tests fail due to coverage gaps, CI will block merges.

Consider running locally to verify coverage:

pytest --cov=. -v tests/ --cov-fail-under=80

103-105: This review comment is incorrect and should be dismissed.

The workflow's approach is intentional and documented. The detect job gates all downstream analysis jobs to run only when Python files change (lines 48-49: if: needs.detect.outputs.python_changed == 'true'). When Python changes are detected, validating the full codebase—not just diffs—is the documented, recommended practice per DEVELOPER.md, which explicitly instructs developers to run checks on "all files currently tracked by Git" using black $(git ls-files '*.py') and pylint $(git ls-files '*.py').

This two-tier design (detect → validate full consistency) is sound: it avoids redundant checks when no Python changes exist while ensuring codebase-wide quality standards when changes occur.

Likely an incorrect or invalid review comment.

.github/workflows/check_terraform.yml (3)

35-44: Terraform change detection aligns with Python and Docker workflows.

The detect job uses identical logic and structure to the Python and Docker workflows, which is excellent for consistency. The pattern ^terraform/ correctly identifies changes in the terraform directory.


91-97: Trivy scan from terraform/ subdirectory is appropriate.

The Trivy scan at line 94 runs from the terraform/ working directory, scanning the current directory (.). This is correct and aligns with the Python workflow's approach of scanning only the relevant code paths.


48-49: I need to gather more specific information about TFLint's version history and when SARIF support was introduced.

TFLint SARIF support is well-established; no action needed.

SARIF format support was added to TFLint in v0.35.0, and the CLI currently supports -f sarif as a documented output format option. When using terraform-linters/setup-tflint with tflint_version: latest, the action retrieves the latest TFLint version, which includes stable SARIF support. The concern about version-dependent SARIF availability is not applicable—TFLint's 0.x version scheme does not have a v1.40, and SARIF has been a standard, supported output format for years without breaking changes.

@github-advanced-security
Copy link

This pull request sets up GitHub code scanning for this repository. Once the scans have completed and the checks have passed, the analysis results for this pull request branch will appear on this overview. Once you merge this pull request, the 'Security' tab will show more code scanning analysis results (for example, for the default branch). Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results. For more information about GitHub code scanning, check out the documentation.

@tmikula-dev
Copy link
Collaborator Author

Please look at the suggested solution. This PILOT has a team goal to understand and set the standard of working with workflows and scanners inside our projects. I did follow the splitting the workflows for determined purposes. Open to the discussion.

Copy link
Contributor

@miroslavpojer miroslavpojer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • read

id: check-types
run: mypy .

trivy-python:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that in PR Trivy should should be limited to all chages files.
I believe we should introduce Trivy run once a "day"|"???" across entrire workspace.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the new version in commit: 0e6e234. And share your thoughts:)

with:
sarif_file: ${{ github.workspace }}/tflint_terraform.sarif

trivy-terraform:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I do not agree with solution. I would isolate Trivy to its own PR changes related flow.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is now isolated in the automatic midnight test scan. Only place, where the Trivy is still implemented is in docker-check. This decision was due to your recommendation. You can see the implementation in: 0e6e234.

src/conf_path.py Outdated
import os



Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and others are fake changes for testing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, these changes are made, so I can show, how workflow behaves here on GH. Will not be part of final solution.

DEVELOPER.md Outdated
### Run Trivy

For running Trivy tool locally run the following command from the root file:
For running Trivy tool locally run you can run one of following commands from the root file:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
For running Trivy tool locally run you can run one of following commands from the root file:
For running Trivy tool locally you can run one of following commands from the root file:

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as off-topic.

@tmikula-dev tmikula-dev changed the title Terraform Static Check - PILOT update Trivy Scanning tool pilot implementation Oct 29, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
.github/workflows/trivy_auto_repository_scan.yml (1)

28-33: Add explicit validation to ensure Trivy SARIF output exists and is valid before downstream steps consume it.

The workflow's subsequent steps—the GitHub SARIF upload and Python script parsing—depend directly on trivy_repository_report.sarif being created and valid. While Trivy exits non-zero on internal failures, an edge-case scenario where it produces truncated or malformed SARIF but still exits successfully could cause confusing failures downstream. The suggested validation step catches this by confirming the file exists and contains valid JSON, providing clear error messages upfront rather than propagating issues to dependent steps.

The proposed diff is sound and improves workflow robustness.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b657be8 and 99c4124.

📒 Files selected for processing (1)
  • .github/workflows/trivy_auto_repository_scan.yml (1 hunks)

Comment on lines +3 to +12
on:
workflow_dispatch:
pull_request:
types: [ opened, synchronize ]

permissions:
contents: read
issues: write
pull-requests: write
security-events: write
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add conditional logic for PR context-dependent steps.

The workflow triggers on both workflow_dispatch (manual trigger) and pull_request events. However, the final PR comment step (lines 85–99) assumes a PR context (context.issue.number), which doesn't exist for manual workflow_dispatch runs. This will cause the GitHub Script step to fail.

Mitigate by either:

  1. Restricting the workflow to PR events only, or
  2. Making the comment step conditional on the pull_request event.

Option 1: Restrict to PR events only

 on:
-  workflow_dispatch:
   pull_request:
     types: [ opened, synchronize ]

Option 2: Conditionally run the PR comment step

Add a condition to the GitHub scan summary comment step (line 85):

       - name: GitHub scan summary comment
+        if: github.event_name == 'pull_request'
         uses: actions/github-script@v8
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
on:
workflow_dispatch:
pull_request:
types: [ opened, synchronize ]
permissions:
contents: read
issues: write
pull-requests: write
security-events: write
on:
pull_request:
types: [ opened, synchronize ]
permissions:
contents: read
issues: write
pull-requests: write
security-events: write
🤖 Prompt for AI Agents
In .github/workflows/trivy_auto_repository_scan.yml around lines 3 to 12, the
workflow triggers both workflow_dispatch and pull_request but the later PR
comment step assumes a PR context and will fail for manual runs; update the
workflow so the final "GitHub scan summary comment" step only runs when the
event is a pull_request (e.g., add a condition checking github.event_name ==
'pull_request' or presence of context.issue.number) or alternatively remove
workflow_dispatch if manual runs aren't needed; implement the conditional on
that step so it is skipped for non-PR runs.

Comment on lines +40 to +83
- name: Create scan summary table
id: scan_summary_table
run: |
python <<'PY'
import os
import json
from collections import Counter
SARIF_PATH = "trivy_repository_report.sarif"
SEVERITIES = ["CRITICAL", "HIGH", "MEDIUM", "LOW"]
# Parse results from SARIF
with open(SARIF_PATH, "r", encoding="utf-8") as f:
sarif = json.load(f)
run = sarif["runs"][0]
rules = run["tool"]["driver"]["rules"]
results = run["results"]
counts = Counter()
# Count results by severity
for res in results:
rule = rules[res["ruleIndex"]]
tags = rule["properties"]["tags"]
sev = next((s for s in SEVERITIES if s in tags), None)
if sev:
counts[sev] += 1
headers = SEVERITIES + ["TOTAL"]
values = [str(counts.get(s, 0)) for s in SEVERITIES]
total = sum(counts.get(s, 0) for s in SEVERITIES)
values.append(str(total))
# Build Markdown summary table
summary_table = "| " + " | ".join(headers) + " |\n"
summary_table += "|" + "|".join(["---"] * len(headers)) + "|\n"
summary_table += "| " + " | ".join(values) + " |"

# Set output
if "GITHUB_OUTPUT" in os.environ:
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
f.write("table<<EOF\n")
f.write(summary_table + "\n")
f.write("EOF\n")
PY
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Add error handling and SARIF structure validation in Python script.

The inline Python script lacks defensive error handling and assumes a specific SARIF structure without validation. If Trivy produces unexpected output, the JSON parsing or key access will fail without a clear error message.

Add try-catch blocks and validate the SARIF structure. Below is a refactored version with error handling:

       - name: Create scan summary table
         id: scan_summary_table
         run: |
           python <<'PY'
           import os
           import json
+          import sys
           from collections import Counter
           
           SARIF_PATH = "trivy_repository_report.sarif"
           SEVERITIES = ["CRITICAL", "HIGH", "MEDIUM", "LOW"]
           
-          # Parse results from SARIF
-          with open(SARIF_PATH, "r", encoding="utf-8") as f:
-              sarif = json.load(f)
-          run = sarif["runs"][0]
-          rules = run["tool"]["driver"]["rules"]
-          results = run["results"]
-          counts = Counter()
+          try:
+              # Parse results from SARIF
+              with open(SARIF_PATH, "r", encoding="utf-8") as f:
+                  sarif = json.load(f)
+              
+              # Validate SARIF structure
+              if "runs" not in sarif or not sarif["runs"]:
+                  raise ValueError("SARIF file contains no runs")
+              
+              run = sarif["runs"][0]
+              if "tool" not in run or "driver" not in run["tool"]:
+                  raise ValueError("SARIF structure missing expected tool/driver keys")
+              
+              rules = run["tool"]["driver"].get("rules", [])
+              results = run.get("results", [])
+              counts = Counter()
+              
+          except (IOError, json.JSONDecodeError, KeyError, ValueError) as e:
+              print(f"Error parsing SARIF: {e}", file=sys.stderr)
+              sys.exit(1)
           
           # Count results by severity
           for res in results:
-              rule = rules[res["ruleIndex"]]
-              tags = rule["properties"]["tags"]
-              sev = next((s for s in SEVERITIES if s in tags), None)
-              if sev:
-                  counts[sev] += 1
+              try:
+                  rule_idx = res.get("ruleIndex")
+                  if rule_idx is None or rule_idx >= len(rules):
+                      continue
+                  rule = rules[rule_idx]
+                  tags = rule.get("properties", {}).get("tags", [])
+                  sev = next((s for s in SEVERITIES if s in tags), None)
+                  if sev:
+                      counts[sev] += 1
+              except (KeyError, IndexError, TypeError) as e:
+                  print(f"Warning: Error processing result: {e}", file=sys.stderr)
+                  continue
           
           headers = SEVERITIES + ["TOTAL"]
           values = [str(counts.get(s, 0)) for s in SEVERITIES]
           total = sum(counts.get(s, 0) for s in SEVERITIES)
           values.append(str(total))
       
           # Build Markdown summary table
           summary_table = "| " + " | ".join(headers) + " |\n"
           summary_table += "|" + "|".join(["---"] * len(headers)) + "|\n"
           summary_table += "| " + " | ".join(values) + " |"
           
           # Set output
-          if "GITHUB_OUTPUT" in os.environ:
-            with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
-              f.write("table<<EOF\n")
-              f.write(summary_table + "\n")
-              f.write("EOF\n")
+          try:
+              if "GITHUB_OUTPUT" in os.environ:
+                  with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
+                      f.write("table<<EOF\n")
+                      f.write(summary_table + "\n")
+                      f.write("EOF\n")
+              else:
+                  print("Warning: GITHUB_OUTPUT not set", file=sys.stderr)
+          except IOError as e:
+              print(f"Error writing output: {e}", file=sys.stderr)
+              sys.exit(1)
           PY

🏁 Script executed:

#!/bin/bash
# Verify the file exists and check lines 40-83
if [ -f ".github/workflows/trivy_auto_repository_scan.yml" ]; then
  echo "=== File found ==="
  sed -n '40,83p' ".github/workflows/trivy_auto_repository_scan.yml"
else
  echo "File not found"
fi

Length of output: 1668


🌐 Web query:

Trivy SARIF output format structure runs results tool driver rules

💡 Result:

Short summary of Trivy's SARIF structure and the important SARIF fields (how Trivy populates them)

  • Top level

    • version, $schema, runs[] — SARIF log contains one or more run objects. [2]
  • run (each element of runs[])

    • tool.driver — tool metadata: name, fullName, version, informationUri; Trivy sets this to identify itself. [1][3]
    • tool.driver.rules — array of reportingDescriptor objects describing rule IDs that the tool reports (rule id, shortDescription/fullDescription, messageStrings, help, and optional properties). Trivy fills rules with per-finding metadata (CVE id, package/license identifiers, etc.). [2][3]
    • artifacts (optional) — scanned artifacts (files, image metadata) referenced by results (uri, hashes, etc.). Trivy includes image/file metadata in run.artifacts and run.propertyBag. [2][3]
    • originalUriBaseIds / columnKind / propertyBag — run-level context (e.g., ROOTPATH, imageName, imageID) that Trivy uses to store image and scan metadata. [3]
  • results[] (each finding)

    • ruleId — stable identifier of the rule (e.g., CVE-xxxx or synthesized id). Trivy emits CVE or rule identifiers here. [2][3]
    • rule (optional) — { "index": N } referencing run.tool.driver.rules[N] when the rule metadata is included. [2]
    • level — SARIF severity level (e.g., error, warning, note). Trivy maps its severity to SARIF level. [2]
    • message — multiformatMessageString with .text describing the finding (Trivy puts vuln description / fix info here). [2][3]
    • locations[] — location objects (artifact/uri, region.startLine/startColumn/endLine/endColumn) pointing at where the issue was found; Trivy uses these for file/line references where applicable. [2][3]
    • fingerprints / relatedLocations / codeFlows (optional) — additional linking or flow info if available. [2]
    • properties — tool-specific properties object. Trivy uses properties extensively to store vulnerability metadata such as severity, pkgName, installedVersion, fixedVersion, primaryURL, references/links, vulnerability type (os/library/misconfig/secret/license), CVSS, and CI/image info (imageName, imageID, repoDigests). These live under result.properties and/or run.propertyBag. [3]
  • How rule <-> result linking works

    • A result typically contains ruleId and may contain rule.index that references the corresponding reportingDescriptor in tool.driver.rules so viewers can show full rule metadata without repeating it per result. Trivy populates rules (reportingDescriptor) and links results via ruleId and rule.index when possible. [2][3]

References

  • Trivy reporting docs (formats/SARIF support). [1]
  • SARIF 2.1.0 specification (runs, tool.driver, reportingDescriptor/rules, results, message, locations, ruleId). [2]
  • Trivy SARIF writer & tests showing actual fields Trivy emits (tool.driver, rules entries, run.propertyBag, result.properties). [3]

Add error handling and SARIF structure validation to prevent silent failures in the workflow.

The Python script lacks defensive checks for SARIF file access, JSON parsing, and structure validation. If Trivy produces unexpected output or the file is missing, the script will crash with unhelpful errors, making CI/CD debugging difficult.

Add try-except blocks and validate structure before accessing nested keys:

       - name: Create scan summary table
         id: scan_summary_table
         run: |
           python <<'PY'
           import os
           import json
+          import sys
           from collections import Counter
           
           SARIF_PATH = "trivy_repository_report.sarif"
           SEVERITIES = ["CRITICAL", "HIGH", "MEDIUM", "LOW"]
           
-          # Parse results from SARIF
-          with open(SARIF_PATH, "r", encoding="utf-8") as f:
-              sarif = json.load(f)
-          run = sarif["runs"][0]
-          rules = run["tool"]["driver"]["rules"]
-          results = run["results"]
-          counts = Counter()
+          try:
+              # Parse results from SARIF
+              with open(SARIF_PATH, "r", encoding="utf-8") as f:
+                  sarif = json.load(f)
+              
+              # Validate SARIF structure
+              if "runs" not in sarif or not sarif["runs"]:
+                  raise ValueError("SARIF file contains no runs")
+              
+              run = sarif["runs"][0]
+              if "tool" not in run or "driver" not in run["tool"]:
+                  raise ValueError("SARIF structure missing expected tool/driver keys")
+              
+              rules = run["tool"]["driver"].get("rules", [])
+              results = run.get("results", [])
+              counts = Counter()
+              
+          except (IOError, json.JSONDecodeError, KeyError, ValueError) as e:
+              print(f"Error parsing SARIF: {e}", file=sys.stderr)
+              sys.exit(1)
           
           # Count results by severity
           for res in results:
-              rule = rules[res["ruleIndex"]]
-              tags = rule["properties"]["tags"]
-              sev = next((s for s in SEVERITIES if s in tags), None)
-              if sev:
-                  counts[sev] += 1
+              try:
+                  rule_idx = res.get("ruleIndex")
+                  if rule_idx is None or rule_idx >= len(rules):
+                      continue
+                  rule = rules[rule_idx]
+                  tags = rule.get("properties", {}).get("tags", [])
+                  sev = next((s for s in SEVERITIES if s in tags), None)
+                  if sev:
+                      counts[sev] += 1
+              except (KeyError, IndexError, TypeError) as e:
+                  print(f"Warning: Error processing result: {e}", file=sys.stderr)
+                  continue
           
           headers = SEVERITIES + ["TOTAL"]
           values = [str(counts.get(s, 0)) for s in SEVERITIES]
           total = sum(counts.get(s, 0) for s in SEVERITIES)
           values.append(str(total))
       
           # Build Markdown summary table
           summary_table = "| " + " | ".join(headers) + " |\n"
           summary_table += "|" + "|".join(["---"] * len(headers)) + "|\n"
           summary_table += "| " + " | ".join(values) + " |"
           
           # Set output
-          if "GITHUB_OUTPUT" in os.environ:
-            with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
-              f.write("table<<EOF\n")
-              f.write(summary_table + "\n")
-              f.write("EOF\n")
+          try:
+              if "GITHUB_OUTPUT" in os.environ:
+                  with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
+                      f.write("table<<EOF\n")
+                      f.write(summary_table + "\n")
+                      f.write("EOF\n")
+              else:
+                  print("Warning: GITHUB_OUTPUT not set", file=sys.stderr)
+          except IOError as e:
+              print(f"Error writing output: {e}", file=sys.stderr)
+              sys.exit(1)
           PY
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Create scan summary table
id: scan_summary_table
run: |
python <<'PY'
import os
import json
from collections import Counter
SARIF_PATH = "trivy_repository_report.sarif"
SEVERITIES = ["CRITICAL", "HIGH", "MEDIUM", "LOW"]
# Parse results from SARIF
with open(SARIF_PATH, "r", encoding="utf-8") as f:
sarif = json.load(f)
run = sarif["runs"][0]
rules = run["tool"]["driver"]["rules"]
results = run["results"]
counts = Counter()
# Count results by severity
for res in results:
rule = rules[res["ruleIndex"]]
tags = rule["properties"]["tags"]
sev = next((s for s in SEVERITIES if s in tags), None)
if sev:
counts[sev] += 1
headers = SEVERITIES + ["TOTAL"]
values = [str(counts.get(s, 0)) for s in SEVERITIES]
total = sum(counts.get(s, 0) for s in SEVERITIES)
values.append(str(total))
# Build Markdown summary table
summary_table = "| " + " | ".join(headers) + " |\n"
summary_table += "|" + "|".join(["---"] * len(headers)) + "|\n"
summary_table += "| " + " | ".join(values) + " |"
# Set output
if "GITHUB_OUTPUT" in os.environ:
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
f.write("table<<EOF\n")
f.write(summary_table + "\n")
f.write("EOF\n")
PY
- name: Create scan summary table
id: scan_summary_table
run: |
python <<'PY'
import os
import json
import sys
from collections import Counter
SARIF_PATH = "trivy_repository_report.sarif"
SEVERITIES = ["CRITICAL", "HIGH", "MEDIUM", "LOW"]
try:
# Parse results from SARIF
with open(SARIF_PATH, "r", encoding="utf-8") as f:
sarif = json.load(f)
# Validate SARIF structure
if "runs" not in sarif or not sarif["runs"]:
raise ValueError("SARIF file contains no runs")
run = sarif["runs"][0]
if "tool" not in run or "driver" not in run["tool"]:
raise ValueError("SARIF structure missing expected tool/driver keys")
rules = run["tool"]["driver"].get("rules", [])
results = run.get("results", [])
counts = Counter()
except (IOError, json.JSONDecodeError, KeyError, ValueError) as e:
print(f"Error parsing SARIF: {e}", file=sys.stderr)
sys.exit(1)
# Count results by severity
for res in results:
try:
rule_idx = res.get("ruleIndex")
if rule_idx is None or rule_idx >= len(rules):
continue
rule = rules[rule_idx]
tags = rule.get("properties", {}).get("tags", [])
sev = next((s for s in SEVERITIES if s in tags), None)
if sev:
counts[sev] += 1
except (KeyError, IndexError, TypeError) as e:
print(f"Warning: Error processing result: {e}", file=sys.stderr)
continue
headers = SEVERITIES + ["TOTAL"]
values = [str(counts.get(s, 0)) for s in SEVERITIES]
total = sum(counts.get(s, 0) for s in SEVERITIES)
values.append(str(total))
# Build Markdown summary table
summary_table = "| " + " | ".join(headers) + " |\n"
summary_table += "|" + "|".join(["---"] * len(headers)) + "|\n"
summary_table += "| " + " | ".join(values) + " |"
# Set output
try:
if "GITHUB_OUTPUT" in os.environ:
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
f.write("table<<EOF\n")
f.write(summary_table + "\n")
f.write("EOF\n")
else:
print("Warning: GITHUB_OUTPUT not set", file=sys.stderr)
except IOError as e:
print(f"Error writing output: {e}", file=sys.stderr)
sys.exit(1)
PY

Comment on lines +85 to +99
- name: GitHub scan summary comment
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const link = `https://github.com/${context.repo.owner}/${context.repo.repo}/security/code-scanning?query=pr%3A${context.issue.number}+is%3Aopen`;
const sentence = `Trivy has completed a full security repository scan ✅\n You can find the analysis results for this PR branch on [this overview](${link}).\n Below is the summary of the findings:`;
const summaryTable = `${{ steps.scan_summary_table.outputs.table }}`;
const body = `${sentence}\n\n${summaryTable}`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

GitHub Script context will be undefined on workflow_dispatch.

The github.rest.issues.createComment() call uses context.issue.number, which is only defined for pull_request events. When the workflow is triggered via workflow_dispatch, this will be undefined, and the API call will fail.

Pair this with the conditional fix suggested in the earlier comment to ensure this step only runs during PR events.

See earlier review comment (lines 3–12) for recommended conditional logic.

🤖 Prompt for AI Agents
.github/workflows/trivy_auto_repository_scan.yml around lines 85 to 99: the step
unconditionally calls github.rest.issues.createComment using
context.issue.number which is undefined for workflow_dispatch; restrict this
step to run only for pull_request events (or guard the script with a check such
as github.event_name === 'pull_request' or existence of
context.payload.pull_request) and skip or no-op when running under
workflow_dispatch; also reuse the conditional pattern suggested in the earlier
comment (lines 3–12) so the body/comment creation only executes when a PR number
is present.

@github-actions
Copy link

Trivy has completed a full security repository scan ✅
You can find the analysis results for this PR branch on this overview.
Below is the summary of the findings:

CRITICAL HIGH MEDIUM LOW TOTAL
1 2 2 10 15

@AbsaOSS AbsaOSS deleted a comment from github-actions bot Oct 29, 2025
@AbsaOSS AbsaOSS deleted a comment from github-actions bot Oct 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request work in progress Work on this item is not yet finished (mainly intended for PRs)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Trivy Scanning tool pilot implementation

3 participants