-
Couldn't load subscription status.
- Fork 0
Trivy Scanning tool pilot implementation #72
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
base: master
Are you sure you want to change the base?
Conversation
WalkthroughAdds 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 Changes
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this 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
📒 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: detectdependency andif: 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@v5while preserving thefetch-depth: 0configuration 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...HEADbehaves on the first commit.Critical issue is valid, but one suggested fix is flawed. The
HEAD~1...HEADapproach does not gracefully handle first commits—it also fails with a fatal error.Using
git diff HEAD^ HEADon the first commit results in "fatal: ambiguous argument 'HEAD^': unknown revision or path not in the working tree". The same applies toHEAD~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 aboutHEAD~1...HEAD.Likely an incorrect or invalid review comment.
There was a problem hiding this 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_certsand./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 || trueOr:
# 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 buildAlso 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 likesrc/module/file.py(which it does).One edge case: If only configuration files like
setup.cfgorpytest.inichange, 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'; thenOtherwise, document that only the root Dockerfile is scanned.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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=80flag 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
detectjob 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" usingblack $(git ls-files '*.py')andpylint $(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 sarifas a documented output format option. When usingterraform-linters/setup-tflintwithtflint_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.
|
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. |
|
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- read
.github/workflows/check_python.yml
Outdated
| id: check-types | ||
| run: mypy . | ||
|
|
||
| trivy-python: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 | ||
|
|
||
|
|
||
|
|
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this 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.sarifbeing 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.
| on: | ||
| workflow_dispatch: | ||
| pull_request: | ||
| types: [ opened, synchronize ] | ||
|
|
||
| permissions: | ||
| contents: read | ||
| issues: write | ||
| pull-requests: write | ||
| security-events: write |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
- Restricting the workflow to PR events only, or
- Making the comment step conditional on the
pull_requestevent.
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.
| 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.
| - 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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"
fiLength 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.
| - 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 |
| - 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 | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
|
Trivy has completed a full security repository scan ✅
|
Release Notes:
Summary by CodeRabbit
Chores
Documentation
New Features