created terraform#13
Conversation
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds two Terraform outputs exposing ChangesEC2 Instance Outputs
CI, Security, and Deployment Workflows
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/code-quality-check.yml:
- Line 22: Replace the floating action tags with immutable commit SHAs for all
"uses:" entries currently referenced by tags (e.g., actions/checkout@v4,
actions/setup-node@v5, actions/cache@v6) so the workflow pins exact commits;
locate each "uses:" line in the workflow and swap the tag (e.g., `@v4/`@v5/@v6)
with the corresponding full commit SHA from the action's GitHub repo, update any
README/maintainer note if present to document the pinned SHAs, and run a quick
local workflow lint to ensure the YAML remains valid.
- Around line 21-23: The Checkout Code step (name: "Checkout Code", uses:
actions/checkout@v4) currently leaves credentials persisted; update the step to
add persist-credentials: false so the GITHUB_TOKEN is not written to the local
git config for later steps, ensuring the checkout step explicitly disables
credential persistence by adding that key under the same step.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0e44a5bc-3542-4b1f-823a-f17d36487cc8
📒 Files selected for processing (1)
.github/workflows/code-quality-check.yml
| - name: Checkout Code | ||
| uses: actions/checkout@v4 | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify checkout steps explicitly disable credential persistence
rg -n -C2 'uses:\s*actions/checkout@' .github/workflows
rg -n 'persist-credentials:\s*false' .github/workflowsRepository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 945
🏁 Script executed:
cat -n .github/workflows/code-quality-check.yml | sed -n '15,30p'Repository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 596
🏁 Script executed:
cat -n .github/workflows/cd-k8s.yml | sed -n '14,22p'Repository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 391
🌐 Web query:
GitHub actions checkout persist-credentials security best practice
💡 Result:
Best-practice answer (security): set persist-credentials: false on actions/checkout unless you specifically need later steps to run authenticated git commands using the checkout token. Why: actions/checkout can persist the auth token in the local git config so scripts can run authenticated git commands, and the token is removed during post-job cleanup; opting out via persist-credentials: false prevents that persistence. [1][2] Recommended pattern - Always harden checkout steps: - uses: actions/checkout@v4 (or newer) - with: persist-credentials: false This ensures the token is not persisted to the repository’s git configuration for subsequent steps. [1][2] When you might keep it enabled - Only when you truly need later steps to run authenticated git operations without manually reconfiguring credentials (for example, actions that assume the persisted credentials are available after checkout). [1][3] Additional defense-in-depth (separate from persist-credentials) - Constrain GITHUB_TOKEN permissions (e.g., use a permissions block / least privilege) so that even if credentials were exposed, the blast radius is reduced. [4] Concrete example - name: Checkout uses: actions/checkout@v4 with: persist-credentials: false This is the explicit remediation repeatedly called out by the checkout action maintainers’ guidance and security discussions. [2][5]
Citations:
- 1: https://github.com/actions/checkout
- 2: https://github.com/actions/checkout/blob/main/README.md
- 3: Reconfigure credentials when `persist-credentials: false` is set stefanzweifel/git-auto-commit-action#356
- 4: https://www.aikido.dev/blog/checklist-github-actions
- 5: Remove
persist-credentialsor change the default tofalseactions/checkout#485
Add persist-credentials: false to disable credential persistence.
The actions/checkout@v4 step should set persist-credentials: false to prevent the GITHUB_TOKEN from being left in the local git config for subsequent steps, reducing the window of exposure if a step is compromised.
Suggested patch
- name: Checkout Code
uses: actions/checkout@v4
+ with:
+ persist-credentials: false📝 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: Checkout Code | |
| uses: actions/checkout@v4 | |
| - name: Checkout Code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false |
🧰 Tools
🪛 zizmor (1.25.2)
[warning] 21-26: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 22-22: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/code-quality-check.yml around lines 21 - 23, The Checkout
Code step (name: "Checkout Code", uses: actions/checkout@v4) currently leaves
credentials persisted; update the step to add persist-credentials: false so the
GITHUB_TOKEN is not written to the local git config for later steps, ensuring
the checkout step explicitly disables credential persistence by adding that key
under the same step.
| # ============================== | ||
|
|
||
| - name: Checkout Code | ||
| uses: actions/checkout@v4 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify all workflow action refs are SHA-pinned (40 hex chars)
rg -n '^\s*uses:\s*[^@]+@' .github/workflows \
| rg -n -v '@[0-9a-fA-F]{40}$'Repository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 495
Pin all GitHub Actions to commit SHAs.
Lines 22, 29, and 81 use tag refs (@v4, @v5, @v6) instead of immutable commit SHAs. This is a supply-chain hardening gap and will fail strict pinning policies.
Suggested patch
- - name: Checkout Code
- uses: actions/checkout@v4
+ - name: Checkout Code
+ uses: actions/checkout@<full-length-commit-sha>
- - name: Setup Go ${{ matrix.go-version }}
- uses: actions/setup-go@v5
+ - name: Setup Go ${{ matrix.go-version }}
+ uses: actions/setup-go@<full-length-commit-sha>
with:
go-version: ${{ matrix.go-version }}
- name: Run golangci-lint
- uses: golangci/golangci-lint-action@v6
+ uses: golangci/golangci-lint-action@<full-length-commit-sha>
with:
version: latest🧰 Tools
🪛 zizmor (1.25.2)
[error] 22-22: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/code-quality-check.yml at line 22, Replace the floating
action tags with immutable commit SHAs for all "uses:" entries currently
referenced by tags (e.g., actions/checkout@v4, actions/setup-node@v5,
actions/cache@v6) so the workflow pins exact commits; locate each "uses:" line
in the workflow and swap the tag (e.g., `@v4/`@v5/@v6) with the corresponding full
commit SHA from the action's GitHub repo, update any README/maintainer note if
present to document the pinned SHAs, and run a quick local workflow lint to
ensure the YAML remains valid.
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/code-quality-check.yml (1)
28-49:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove the second
setup-go; it conflicts with matrix intent.This workflow sets Go from
matrix.go-version, then reconfigures Go again frombackend/go.mod. That can invalidate the matrix and produce misleading coverage across versions.Suggested patch
- name: Setup Go ${{ matrix.go-version }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} + cache: true @@ - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version-file: backend/go.mod - cache: true🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/code-quality-check.yml around lines 28 - 49, The workflow currently runs two conflicting setup-go steps: "Setup Go ${{ matrix.go-version }}" (uses actions/setup-go@v5 with go-version: ${{ matrix.go-version }}) and a later "Setup Go" step that uses go-version-file: backend/go.mod; remove or disable the second "Setup Go" step so the matrix-controlled Go version is the single source of truth (keep the initial actions/setup-go@v5 step and the "Verify Go Version" step) — if you intended to validate go.mod, replace the second step with a simple check (e.g., verify go.mod exists) rather than re-invoking actions/setup-go.
🧹 Nitpick comments (2)
.github/workflows/devsecops-pipeline.yml (1)
14-44: 🏗️ Heavy liftAvoid blanket
secrets: inherit; pass only required secrets per job.Unconditional inheritance increases secret exposure across reusable workflows. Prefer explicit secret mapping for each called workflow.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/devsecops-pipeline.yml around lines 14 - 44, Replace blanket "secrets: inherit" on reusable workflow calls (secrets-scan, dependency-scan, docker-scan, build, trivy, deploy) with explicit secret mappings: identify the minimal secrets each called workflow actually needs and pass them with "secrets: { NAME: ${{ secrets.NAME }} }" for that job; for example map build to only registry credentials (e.g., DOCKER_USERNAME, DOCKER_PASSWORD or GCP_SERVICE_ACCOUNT), trivy to only the scanner token/credentials it requires (e.g., TRIVY_TOKEN), deploy to only deployment keys (e.g., SSH_PRIVATE_KEY or DEPLOY_KEY), and similarly limit secrets for secrets-scan/dependency-scan to their specific credentials—update the jobs named "build", "trivy", and "deploy" (and any other jobs using secrets: inherit) to use explicit per-secret mappings instead of inheriting all secrets.Ansible/playbook.yml (1)
31-47: ⚡ Quick winProvision Compose here too, or the host still depends on deploy-time package installs.
The paired deploy workflow runs
docker compose, but this playbook only installs Docker Engine. That leaves the server partially provisioned and forces every deploy to mutate the machine again just to make Compose available.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Ansible/playbook.yml` around lines 31 - 47, The playbook installs Docker Engine but not Compose, so hosts still need per-deploy installs; add an apt task to install the Docker Compose plugin (e.g., package name "docker-compose-plugin" or "docker-compose" depending on target OS) after the "Install Docker" task and before any deploys, and ensure the "Ensure Docker is running and enabled" task (service: name: docker) runs after package installation; reference the existing task names "Install Docker", "Ensure Docker is running and enabled", and "Add user to docker group" when adding the new apt task and ordering to guarantee `docker compose` is available for the paired deploy workflow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/dependency-scan.yml:
- Around line 22-23: Replace all GitHub Action uses: entries that reference
version tags (e.g., actions/checkout@v4, actions/setup-node@v5,
docker/build-push-action@0.24.0) with their corresponding commit SHAs to remove
supply-chain ambiguity, and for the checkout step(s) (the job step using
actions/checkout) add persist-credentials: false so the GITHUB_TOKEN is not
written to git config since we only read the repo; update the other occurrences
called out (the steps around lines 29-32, 77-79, 91-93) similarly to use pinned
SHAs and, where they are checkout steps, set persist-credentials: false.
- Around line 77-97: The Trivy scan step ("Run Trivy Filesystem Scan") currently
uses format: table and no output, so it doesn't generate trivy-results.sarif for
the "Upload Trivy Report" step; change the scan step to use format: sarif and
add an output parameter to write the SARIF to trivy-results.sarif (e.g., set
output: trivy-results.sarif) so the "Upload Trivy Report" upload step can find
and persist the report; ensure only the scan step settings (format and output
filename) and any related path references are updated, leaving the "Upload Trivy
Report" step intact.
In @.github/workflows/deploy-to-server.yml:
- Around line 31-52: In the "SSH to Prod server & run the app" step, remove the
--build flag from the docker compose up command in the script (the line
currently running cd ~/devops && docker compose down && docker compose up -d
--build --force-recreate --pull always) so Compose doesn't try to build local
contexts; keep --pull always (and --force-recreate, -d) so the server pulls the
pre-built images pushed by CI and recreates containers from those images.
In @.github/workflows/devsecops-pipeline.yml:
- Around line 24-43: The pipeline currently allows build/trivy/deploy to run
before the tests job completes; update the job dependencies so tests are
required before building/deploying by adding the tests job to the build job's
needs array (i.e., change build's needs to include "tests" alongside
code-quality, secrets-scan, dependency-scan, docker-scan) so that build -> trivy
-> deploy cannot run unless tests succeed.
- Around line 3-9: Add an explicit top-level GitHub Actions permissions block
(placed at the workflow root alongside the existing "on: push" and before
"jobs:") that enumerates only the token scopes your pipeline needs; update the
workflow to replace implicit repo/org defaults with least-privilege entries (for
example set contents: read and any other minimal scopes required by your jobs
such as id-token: write only if using OIDC, or checks: write if you create
checks), and ensure the permissions list is kept minimal and aligned to the
actions run in the jobs section.
In @.github/workflows/docker-build-push.yml:
- Around line 21-29: The workflow's "Build & Push to Docker Hub" step creates a
single root image "github-actions-app" which doesn't match downstream consumers;
update the docker/build-push-action@v6 usage to build and push separate images
for each component (e.g., backend and frontend) by invoking the action twice or
using multiple build targets with distinct contexts (./backend and ./frontend),
set per-image tags that downstream expects (use ${{ vars.DOCKERHUB_USER
}}/<repo-name>-backend:${{ github.ref_name }}, ...-frontend:${{ github.ref_name
}} and include :latest and ${{ github.sha }} variants), and ensure push: true so
the scan/deploy stages can reference the exact repository names and tags they
require.
In @.github/workflows/docker-lint.yml:
- Around line 12-25: Update the workflow to pin mutable references: replace
uses: actions/checkout@v4 with the full commit SHA for that action and add the
persist-credentials: false input to the checkout step (refer to the Checkout
Code step); also pin the hadolint container image used in the Lint Backend
Dockerfile and Lint Frontend Dockerfile steps to a specific, immutable tag or
digest (e.g., hadolint/hadolint:v2.14.0 or a digest) instead of the floating
hadolint/hadolint reference so the docker run invocations are deterministic.
In @.github/workflows/image-scan.yml:
- Around line 28-43: The Trivy scan steps "Scan Backend Image" and "Scan
Frontend Image" reference image refs `${{ secrets.DOCKER_USERNAME
}}/skillpulse-backend:latest` and `/skillpulse-frontend:latest` but the
build/push workflow only publishes `${DOCKERHUB_USER}/github-actions-app:*`;
update the image-ref values in the Trivy steps to match the artifact names/tags
produced by the docker-build-push workflow (e.g., change both image-ref entries
to use `${{ secrets.DOCKER_USERNAME }}/github-actions-app:latest` or the exact
tag name emitted by the build), or alternatively modify the build workflow to
publish `skillpulse-backend` and `skillpulse-frontend` images so names align
with the Trivy `image-ref` entries; ensure the change is applied to the two
Trivy steps that use `aquasecurity/trivy-action@v0.36.0`.
- Around line 19-24: The Docker Hub login step uses the wrong secret name;
update the "Login to Docker Hub" step that calls docker/login-action@v3 to use
the declared secret name for the password (secrets.DOCKER_PASSWORD) instead of
secrets.DOCKER_TOKEN so the password input matches the workflow_call
declaration; ensure the with: password: field references secrets.DOCKER_PASSWORD
while username remains secrets.DOCKER_USERNAME.
In @.github/workflows/secret-scan.yml:
- Around line 11-17: Update the GitHub Actions steps to use and pin fixed
releases and disable credential persistence: replace the uses: reference for
actions/checkout from actions/checkout@v3 to actions/checkout@v4 pinned to its
full commit SHA and add with: persist-credentials: false under that step;
similarly replace uses: gitleaks/gitleaks-action@v2 with the action pinned to a
specific commit SHA instead of the moving tag
(gitleaks/gitleaks-action@<full-sha>) so both actions are immutably pinned and
the checkout step stops passing the runner token.
In @.github/workflows/tests.yml:
- Around line 45-92: The workflow runs Go commands at the repository root but
the module lives in backend/, so update the Go-related steps to operate in the
backend module: for each step named "Check go.mod", "Cache Go Modules", "Install
Dependencies", "Run Go Tests", and "Generate Coverage Report" either add
working-directory: backend or prepend commands with cd backend && so the
go.mod/go.sum and module cache are resolved from backend; ensure the cache key
still hashes backend/go.sum (e.g., hashFiles('backend/**/go.sum')) so the
actions/cache and go test/cover commands run in the backend module context.
---
Outside diff comments:
In @.github/workflows/code-quality-check.yml:
- Around line 28-49: The workflow currently runs two conflicting setup-go steps:
"Setup Go ${{ matrix.go-version }}" (uses actions/setup-go@v5 with go-version:
${{ matrix.go-version }}) and a later "Setup Go" step that uses go-version-file:
backend/go.mod; remove or disable the second "Setup Go" step so the
matrix-controlled Go version is the single source of truth (keep the initial
actions/setup-go@v5 step and the "Verify Go Version" step) — if you intended to
validate go.mod, replace the second step with a simple check (e.g., verify
go.mod exists) rather than re-invoking actions/setup-go.
---
Nitpick comments:
In @.github/workflows/devsecops-pipeline.yml:
- Around line 14-44: Replace blanket "secrets: inherit" on reusable workflow
calls (secrets-scan, dependency-scan, docker-scan, build, trivy, deploy) with
explicit secret mappings: identify the minimal secrets each called workflow
actually needs and pass them with "secrets: { NAME: ${{ secrets.NAME }} }" for
that job; for example map build to only registry credentials (e.g.,
DOCKER_USERNAME, DOCKER_PASSWORD or GCP_SERVICE_ACCOUNT), trivy to only the
scanner token/credentials it requires (e.g., TRIVY_TOKEN), deploy to only
deployment keys (e.g., SSH_PRIVATE_KEY or DEPLOY_KEY), and similarly limit
secrets for secrets-scan/dependency-scan to their specific credentials—update
the jobs named "build", "trivy", and "deploy" (and any other jobs using secrets:
inherit) to use explicit per-secret mappings instead of inheriting all secrets.
In `@Ansible/playbook.yml`:
- Around line 31-47: The playbook installs Docker Engine but not Compose, so
hosts still need per-deploy installs; add an apt task to install the Docker
Compose plugin (e.g., package name "docker-compose-plugin" or "docker-compose"
depending on target OS) after the "Install Docker" task and before any deploys,
and ensure the "Ensure Docker is running and enabled" task (service: name:
docker) runs after package installation; reference the existing task names
"Install Docker", "Ensure Docker is running and enabled", and "Add user to
docker group" when adding the new apt task and ordering to guarantee `docker
compose` is available for the paired deploy workflow.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 91354154-4066-4473-bf9d-17389e2d2a97
📒 Files selected for processing (10)
.github/workflows/code-quality-check.yml.github/workflows/dependency-scan.yml.github/workflows/deploy-to-server.yml.github/workflows/devsecops-pipeline.yml.github/workflows/docker-build-push.yml.github/workflows/docker-lint.yml.github/workflows/image-scan.yml.github/workflows/secret-scan.yml.github/workflows/tests.ymlAnsible/playbook.yml
| - name: Checkout Code | ||
| uses: actions/checkout@v4 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/dependency-scan.ymlRepository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 3075
Pin actions to commit SHAs and disable checkout token persistence.
All uses: statements use version tags (v4, v5, 0.24.0) instead of commit SHAs, which leaves a supply-chain gap that zizmor flags. Additionally, actions/checkout@v4 persists the GITHUB_TOKEN to git config by default, but since all subsequent steps only read from the repository (never push), this token is unnecessary and should be disabled via persist-credentials: false.
Also applies to: 29-32, 77-79, 91-93
🧰 Tools
🪛 zizmor (1.25.2)
[warning] 22-27: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 23-23: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/dependency-scan.yml around lines 22 - 23, Replace all
GitHub Action uses: entries that reference version tags (e.g.,
actions/checkout@v4, actions/setup-node@v5, docker/build-push-action@0.24.0)
with their corresponding commit SHAs to remove supply-chain ambiguity, and for
the checkout step(s) (the job step using actions/checkout) add
persist-credentials: false so the GITHUB_TOKEN is not written to git config
since we only read the repo; update the other occurrences called out (the steps
around lines 29-32, 77-79, 91-93) similarly to use pinned SHAs and, where they
are checkout steps, set persist-credentials: false.
| - name: Run Trivy Filesystem Scan | ||
| uses: aquasecurity/trivy-action@0.24.0 | ||
| with: | ||
| scan-type: fs | ||
| scan-ref: . | ||
| format: table | ||
| exit-code: 1 | ||
| ignore-unfixed: true | ||
| severity: CRITICAL,HIGH | ||
|
|
||
| # ============================== | ||
| # Upload Trivy Report (Optional) | ||
| # ============================== | ||
|
|
||
| - name: Upload Trivy Report | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: trivy-dependency-report | ||
| path: trivy-results.sarif | ||
| if-no-files-found: ignore |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
trivy-action aquasecurity format table output files
💡 Result:
To generate a table-formatted output file using aquasecurity/trivy-action, you must configure both the format and output inputs within your workflow step [1][2][3]. By default, trivy-action outputs the results to the job logs in a human-readable table format [4][5]. To capture this output into a file instead of (or in addition to) seeing it in the logs, set the format input to table and provide a file path via the output input [1][2]. Example workflow configuration: - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@v0.36.0 with: image-ref: 'your-image:tag' format: 'table' output: 'trivy-results.txt' Because the table format uses characters that may not render correctly in GitHub's markdown-based Step Summaries or PR comments, it is a common practice to wrap the file content in a markdown code block if you intend to post it to a PR or summary [6][7]. For example, when posting the results as a comment: - name: Format Trivy Scan Result run: | if [ -s trivy-results.txt ]; then echo -e "## Vulnerability Scan Results\n\n```\n$(cat trivy-results.txt)\n```" > formatted-trivy-result.md fi This ensures that the table remains aligned and readable within the GitHub interface [6][7].
Citations:
- 1: https://github.com/aquasecurity/trivy-action
- 2: https://github.com/aquasecurity/trivy-action?tab=readme-ov-file
- 3: https://github.com/marketplace/actions/aqua-security-trivy
- 4: https://deepwiki.com/aquasecurity/trivy-action/3.2-output-formats
- 5: https://trivy.dev/docs/latest/configuration/reporting/
- 6: Publish to Summary aquasecurity/trivy-action#340
- 7: https://dev.to/suzuki0430/enhance-code-security-with-github-actions-automatically-commenting-prs-with-docker-scans-48ap
🌐 Web query:
trivy-action@0.24.0 SARIF format output filename
💡 Result:
In trivy-action, including version 0.24.0, the SARIF format output filename is not determined by a hardcoded default; rather, it is specified by the user using the output input parameter [1][2][3]. To generate a SARIF report and save it to a specific filename, you must explicitly configure the format and output parameters within your workflow step [3][4]: - format: 'sarif' - output: 'filename.sarif' Common usage involves defining a filename, such as trivy-results.sarif, and then referencing that same filename in subsequent steps, such as when uploading to the GitHub Security tab via the codeql-action [1][5][6]. Example configuration: - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@0.24.0 with: scan-type: 'fs' format: 'sarif' output: 'trivy-results.sarif' - name: Upload Trivy scan results to GitHub Security tab uses: github/codeql-action/upload-sarif@v3 with: sarif_file: 'trivy-results.sarif' Please note that for trivy-action versions (including 0.24.0), there is an input parameter called limit-severities-for-sarif [2][3][4]. By default, this is false, meaning SARIF output will include all detected vulnerabilities regardless of the configured severity filters [5][7][3]. If you wish to enforce your severity filters in the SARIF output, you must set limit-severities-for-sarif: true [2][3].
Citations:
- 1: Issue with uploading the SARIF report to GitHub aquasecurity/trivy-action#408
- 2: Trivy is failing on sarif mode aquasecurity/trivy-action#386
- 3: https://github.com/aquasecurity/trivy-action?tab=readme-ov-file
- 4: https://github.com/marketplace/actions/aqua-security-trivy
- 5: https://github.com/aquasecurity/trivy-action/tree/v0.35.0
- 6: https://thomasthornton.cloud/setting-up-trivy-in-your-github-actions/
- 7: https://github.com/aquasecurity/trivy-action
Trivy scan with format: table will not produce the trivy-results.sarif file.
The scan is configured to output in table format without specifying an output parameter. This causes Trivy to write only to the job logs, not to any file. Lines 95–96 attempt to upload trivy-results.sarif, but since no file is created, the upload step silently no-ops due to if-no-files-found: ignore. Scan findings are never preserved as artifacts.
To fix, set format: sarif and specify the output filename:
Proposed fix
- name: Run Trivy Filesystem Scan
uses: aquasecurity/trivy-action@0.24.0
with:
scan-type: fs
scan-ref: .
- format: table
+ format: sarif
+ output: trivy-results.sarif
exit-code: 1
ignore-unfixed: true
severity: CRITICAL,HIGH📝 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: Run Trivy Filesystem Scan | |
| uses: aquasecurity/trivy-action@0.24.0 | |
| with: | |
| scan-type: fs | |
| scan-ref: . | |
| format: table | |
| exit-code: 1 | |
| ignore-unfixed: true | |
| severity: CRITICAL,HIGH | |
| # ============================== | |
| # Upload Trivy Report (Optional) | |
| # ============================== | |
| - name: Upload Trivy Report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: trivy-dependency-report | |
| path: trivy-results.sarif | |
| if-no-files-found: ignore | |
| - name: Run Trivy Filesystem Scan | |
| uses: aquasecurity/trivy-action@0.24.0 | |
| with: | |
| scan-type: fs | |
| scan-ref: . | |
| format: sarif | |
| output: trivy-results.sarif | |
| exit-code: 1 | |
| ignore-unfixed: true | |
| severity: CRITICAL,HIGH | |
| # ============================== | |
| # Upload Trivy Report (Optional) | |
| # ============================== | |
| - name: Upload Trivy Report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: trivy-dependency-report | |
| path: trivy-results.sarif | |
| if-no-files-found: ignore |
🧰 Tools
🪛 zizmor (1.25.2)
[error] 78-78: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 93-93: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/dependency-scan.yml around lines 77 - 97, The Trivy scan
step ("Run Trivy Filesystem Scan") currently uses format: table and no output,
so it doesn't generate trivy-results.sarif for the "Upload Trivy Report" step;
change the scan step to use format: sarif and add an output parameter to write
the SARIF to trivy-results.sarif (e.g., set output: trivy-results.sarif) so the
"Upload Trivy Report" upload step can find and persist the report; ensure only
the scan step settings (format and output filename) and any related path
references are updated, leaving the "Upload Trivy Report" step intact.
| - name: Copy the docker-compose file | ||
| uses: appleboy/scp-action@v1 | ||
| with: | ||
| host: ${{ secrets.EC2_SSH_HOST }} | ||
| username: ${{ secrets.EC2_SSH_USER }} | ||
| key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} | ||
| source: docker-compose.yml | ||
| target: ~/devops | ||
|
|
||
| - name: SSH to Prod server & run the app | ||
| uses: appleboy/ssh-action@v1.0.3 | ||
| with: | ||
| host: ${{ secrets.EC2_SSH_HOST }} | ||
| username: ${{ secrets.EC2_SSH_USER }} | ||
| key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} | ||
| script: | | ||
| echo "Running App" | ||
|
|
||
| export DOCKERHUB_USER=${{ vars.DOCKERHUB_USER }} | ||
| export DOCKER_TAG=${{ github.sha }} | ||
| echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login --username ${{ vars.DOCKERHUB_USER }} --password-stdin | ||
| cd ~/devops && docker compose down && docker compose up -d --build --force-recreate --pull always |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the full deploy-to-server.yml workflow
cat -n .github/workflows/deploy-to-server.ymlRepository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 2456
🏁 Script executed:
# Check if docker-compose.yml exists and examine its content
if [ -f docker-compose.yml ]; then
echo "=== docker-compose.yml ==="
cat -n docker-compose.yml
else
echo "docker-compose.yml not found in root"
# Search for docker-compose files
find . -name "docker-compose*.yml" -o -name "docker-compose*.yaml" | head -20
fiRepository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 1733
🏁 Script executed:
# Check for Docker build workflows in the repo
find .github/workflows -type f \( -name "*.yml" -o -name "*.yaml" \) | head -20Repository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 505
Remove --build flag—the server doesn't have the build contexts.
The docker-compose.yml defines build: ./backend and build: ./frontend, but the workflow only copies the compose file itself. When Docker Compose runs with --build, it will fail trying to locate those directories on the server. Since images are already built in CI and pushed to DockerHub (per docker-build-push.yml), use --pull always to fetch pre-built images instead.
Proposed fix
- cd ~/devops && docker compose down && docker compose up -d --build --force-recreate --pull always
+ cd ~/devops && docker compose down && docker compose up -d --force-recreate --pull always📝 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: Copy the docker-compose file | |
| uses: appleboy/scp-action@v1 | |
| with: | |
| host: ${{ secrets.EC2_SSH_HOST }} | |
| username: ${{ secrets.EC2_SSH_USER }} | |
| key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} | |
| source: docker-compose.yml | |
| target: ~/devops | |
| - name: SSH to Prod server & run the app | |
| uses: appleboy/ssh-action@v1.0.3 | |
| with: | |
| host: ${{ secrets.EC2_SSH_HOST }} | |
| username: ${{ secrets.EC2_SSH_USER }} | |
| key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} | |
| script: | | |
| echo "Running App" | |
| export DOCKERHUB_USER=${{ vars.DOCKERHUB_USER }} | |
| export DOCKER_TAG=${{ github.sha }} | |
| echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login --username ${{ vars.DOCKERHUB_USER }} --password-stdin | |
| cd ~/devops && docker compose down && docker compose up -d --build --force-recreate --pull always | |
| - name: Copy the docker-compose file | |
| uses: appleboy/scp-action@v1 | |
| with: | |
| host: ${{ secrets.EC2_SSH_HOST }} | |
| username: ${{ secrets.EC2_SSH_USER }} | |
| key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} | |
| source: docker-compose.yml | |
| target: ~/devops | |
| - name: SSH to Prod server & run the app | |
| uses: appleboy/ssh-action@v1.0.3 | |
| with: | |
| host: ${{ secrets.EC2_SSH_HOST }} | |
| username: ${{ secrets.EC2_SSH_USER }} | |
| key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} | |
| script: | | |
| echo "Running App" | |
| export DOCKERHUB_USER=${{ vars.DOCKERHUB_USER }} | |
| export DOCKER_TAG=${{ github.sha }} | |
| echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login --username ${{ vars.DOCKERHUB_USER }} --password-stdin | |
| cd ~/devops && docker compose down && docker compose up -d --force-recreate --pull always |
🧰 Tools
🪛 zizmor (1.25.2)
[info] 49-49: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[info] 51-51: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 32-32: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 41-41: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/deploy-to-server.yml around lines 31 - 52, In the "SSH to
Prod server & run the app" step, remove the --build flag from the docker compose
up command in the script (the line currently running cd ~/devops && docker
compose down && docker compose up -d --build --force-recreate --pull always) so
Compose doesn't try to build local contexts; keep --pull always (and
--force-recreate, -d) so the server pulls the pre-built images pushed by CI and
recreates containers from those images.
| on: | ||
| push: | ||
| branches: | ||
| - main | ||
| - feat/dev | ||
|
|
||
| jobs: |
There was a problem hiding this comment.
Add explicit workflow permissions to enforce least privilege.
Without a permissions: block, token scope is left to repo/org defaults, which is weaker than explicit least-privilege.
Suggested patch
on:
push:
branches:
- main
- feat/dev
+
+permissions:
+ contents: read📝 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: | |
| push: | |
| branches: | |
| - main | |
| - feat/dev | |
| jobs: | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - feat/dev | |
| permissions: | |
| contents: read | |
| jobs: |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/devsecops-pipeline.yml around lines 3 - 9, Add an explicit
top-level GitHub Actions permissions block (placed at the workflow root
alongside the existing "on: push" and before "jobs:") that enumerates only the
token scopes your pipeline needs; update the workflow to replace implicit
repo/org defaults with least-privilege entries (for example set contents: read
and any other minimal scopes required by your jobs such as id-token: write only
if using OIDC, or checks: write if you create checks), and ensure the
permissions list is kept minimal and aligned to the actions run in the jobs
section.
| # Build once the security scans and tests are complete | ||
| build: | ||
| needs: [code-quality, secrets-scan, dependency-scan, docker-scan] | ||
| uses: ./.github/workflows/docker-build-push.yml | ||
| secrets: inherit | ||
|
|
||
| tests: | ||
| uses: ./.github/workflows/tests.yml | ||
|
|
||
| # Image scan | ||
| trivy: | ||
| needs: [build] | ||
| uses: ./.github/workflows/image-scan.yml | ||
| secrets: inherit | ||
|
|
||
|
|
||
| # CD : Deploy to Production | ||
| deploy: | ||
| needs: [trivy] | ||
| uses: ./.github/workflows/deploy-to-server.yml |
There was a problem hiding this comment.
Gate build/deploy on tests to prevent shipping unverified code.
tests is not in the dependency chain for build/trivy/deploy, so deployment can proceed even if tests fail.
Suggested patch
# Build once the security scans and tests are complete
build:
- needs: [code-quality, secrets-scan, dependency-scan, docker-scan]
+ needs: [code-quality, secrets-scan, dependency-scan, docker-scan, tests]
uses: ./.github/workflows/docker-build-push.yml
secrets: inherit📝 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.
| # Build once the security scans and tests are complete | |
| build: | |
| needs: [code-quality, secrets-scan, dependency-scan, docker-scan] | |
| uses: ./.github/workflows/docker-build-push.yml | |
| secrets: inherit | |
| tests: | |
| uses: ./.github/workflows/tests.yml | |
| # Image scan | |
| trivy: | |
| needs: [build] | |
| uses: ./.github/workflows/image-scan.yml | |
| secrets: inherit | |
| # CD : Deploy to Production | |
| deploy: | |
| needs: [trivy] | |
| uses: ./.github/workflows/deploy-to-server.yml | |
| # Build once the security scans and tests are complete | |
| build: | |
| needs: [code-quality, secrets-scan, dependency-scan, docker-scan, tests] | |
| uses: ./.github/workflows/docker-build-push.yml | |
| secrets: inherit | |
| tests: | |
| uses: ./.github/workflows/tests.yml | |
| # Image scan | |
| trivy: | |
| needs: [build] | |
| uses: ./.github/workflows/image-scan.yml | |
| secrets: inherit | |
| # CD : Deploy to Production | |
| deploy: | |
| needs: [trivy] | |
| uses: ./.github/workflows/deploy-to-server.yml |
🧰 Tools
🪛 zizmor (1.25.2)
[warning] 25-28: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
[warning] 30-33: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
[warning] 34-40: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
[warning] 27-27: secrets unconditionally inherited by called workflow (secrets-inherit): this reusable workflow
(secrets-inherit)
[warning] 36-36: secrets unconditionally inherited by called workflow (secrets-inherit): this reusable workflow
(secrets-inherit)
[warning] 43-43: secrets unconditionally inherited by called workflow (secrets-inherit): this reusable workflow
(secrets-inherit)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/devsecops-pipeline.yml around lines 24 - 43, The pipeline
currently allows build/trivy/deploy to run before the tests job completes;
update the job dependencies so tests are required before building/deploying by
adding the tests job to the build job's needs array (i.e., change build's needs
to include "tests" alongside code-quality, secrets-scan, dependency-scan,
docker-scan) so that build -> trivy -> deploy cannot run unless tests succeed.
| - name: Checkout Code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Lint Backend Dockerfile | ||
| run: | | ||
| docker run --rm \ | ||
| -v ${{ github.workspace }}/backend/Dockerfile:/Dockerfile \ | ||
| hadolint/hadolint hadolint --ignore DL3018 /Dockerfile | ||
|
|
||
| - name: Lint Frontend Dockerfile | ||
| run: | | ||
| docker run --rm \ | ||
| -v ${{ github.workspace }}/frontend/Dockerfile:/Dockerfile \ | ||
| hadolint/hadolint hadolint --ignore DL3018 /Dockerfile |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, check if the workflow file exists and read its contents
cat -n .github/workflows/docker-lint.ymlRepository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 902
🏁 Script executed:
# Verify GitHub Actions best practices for checkout action
# Search for documentation on persist-credentials and commit SHA pinning
web_search_results=$(curl -s "https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions" 2>/dev/null || echo "offline")
echo "Checking GitHub Actions security practices..."Repository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 144
🏁 Script executed:
# Check if hadolint provides versioned images
# Look for information on hadolint Docker image tagging
echo "Checking hadolint image availability..."Repository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 138
🏁 Script executed:
cat -n .github/workflows/docker-lint.ymlRepository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 902
🌐 Web query:
GitHub Actions actions/checkout@v4 commit SHA pinning security best practices
💡 Result:
Pinning GitHub Actions, including actions/checkout@v4, to a full-length commit SHA is considered a critical security best practice for protecting CI/CD pipelines against supply chain attacks [1][2][3]. Security Rationale Using a version tag (like @v4) creates a mutable reference, meaning the code executed can change if the tag is moved, deleted, or if the repository hosting the action is compromised [2][4][3]. A malicious actor who gains access to an action's repository could update a tag to point to a compromised commit, automatically injecting malicious code into every workflow using that tag [5][2][4]. Pinning to a full-length commit SHA provides an immutable reference, ensuring the exact same code is executed every time, which prevents such silent redirection [1][4][3]. Implementation Best Practices - Pin to Full SHA: Always use the full-length commit SHA (e.g., uses: actions/checkout@b4ffde6) [1][3]. - Use Version Comments: When pinning to a SHA, include a comment indicating the corresponding version tag for readability and maintainability (e.g., uses: actions/checkout@b4ffde6 # v4.1.1) [5][6]. - Use Automation Tools: Manually updating pinned SHAs is error-prone. Use tools like Dependabot, Renovate, or specialized CLI tools like "ratchet" to automate the process of keeping your pinned SHAs up to date with the latest releases [5][7][2]. - Enforce via Policy: GitHub supports organization and repository-level policies that can mandate SHA pinning, causing workflows to fail if they use unpinned actions [8][3]. Workflow Governance While SHA pinning provides the strongest security guarantee, it requires more maintenance than using tags [7][2]. Organizations often choose to: - Prioritize high-risk workflows: If maintaining pinned SHAs for every single action is impractical, prioritize pinning for high-risk workflows, such as those that handle sensitive secrets, perform deployments, or have broad repository permissions [9][2][10]. - Use Trusted Actions: For actions that are not pinned, rely only on trusted creators (e.g., those with the Verified Creator badge) [3]. Note, however, that even trusted actions are vulnerable to account compromise if not pinned [4][3]. To find the commit SHA for a specific action version, you can navigate to the action's GitHub repository, switch to the desired tag in the branch/tag menu, and copy the full-length commit SHA [1][9]. Alternatively, you can run the workflow once with the tag and check the "Setup Job" section of the workflow execution logs to see the exact SHA that was resolved and downloaded [1].
Citations:
- 1: https://stackoverflow.com/questions/78903499/how-do-i-pin-an-action-to-a-specific-sha
- 2: https://www.aikido.dev/blog/checklist-github-actions
- 3: https://docs.github.com/en/actions/reference/security/secure-use
- 4: https://gbhackers.com/compromised-github-action/
- 5: Pin GitHub Actions to commit SHAs in release workflow github/gh-aw#10021
- 6: Pin all GitHub Actions to immutable SHA commits for supply chain security github/gh-aw#5691
- 7: https://dev.to/jonasbn/pinning-github-actions-and-using-ratchet-3lmc
- 8: https://github.blog/changelog/2025-08-15-github-actions-policy-now-supports-blocking-and-sha-pinning-actions/
- 9: https://www.stepsecurity.io/blog/pinning-github-actions-for-enhanced-security-a-complete-guide
- 10: https://dev.to/duriantaco/github-actions-security-and-gitlab-ci-security-static-analysis-for-cicd-g9h
🌐 Web query:
hadolint Docker image versioning tags available
💡 Result:
Hadolint is distributed as a Docker image on Docker Hub and the GitHub Container Registry (ghcr.io), providing various tags to support different use cases and versions [1][2][3]. The available tags generally fall into the following categories: 1. Version-specific tags: These tags correspond to specific releases of Hadolint (e.g., v2.14.0) [1][2]. You can also find variants of these specific versions, such as v2.14.0-alpine or v2.14.0-debian, to suit different base image requirements [1][2]. 2. Latest/Rolling tags: The tag latest provides the most recent stable release [1]. Similar to versioned tags, there are variants such as latest-alpine and latest-debian [1][2][3]. 3. Architecture and commit-specific tags: The registry also includes tags linked to specific Git commit SHAs and CPU architectures (e.g., amd64, arm64), often formatted as -- [1]. Best practices for using these images include: - In CI/CD pipelines, it is recommended to use a specific version tag or pin the image by its digest to ensure consistent build environments and avoid the risks associated with mutable tags like latest [4][5]. - If you require shell access within the container, use the -alpine or -debian variants [2][3][6]. - You can pull images from the primary repository at hadolint/hadolint or from the GitHub Container Registry at ghcr.io/hadolint/hadolint [2][3]. You can view the full, up-to-date list of available tags directly on the official Docker Hub page [1].
Citations:
- 1: https://hub.docker.com/r/hadolint/hadolint/tags
- 2: https://hub.docker.com/r/hadolint/hadolint
- 3: https://github.com/hadolint/hadolint
- 4: https://nickjanetakis.com/blog/docker-tip-89-lint-your-dockerfile-with-hadolint
- 5: https://dev.to/nkuziev-sec/why-fixed-container-image-versions-matter-lessons-from-the-trivy-supply-chain-attack-29ke
- 6: https://bell-sw.com/blog/how-to-use-a-dockerfile-linter/
Pin actions and container images to specific, immutable versions for deterministic and secure workflows.
actions/checkout@v4 uses a mutable tag reference instead of a commit SHA, and neither persist-credentials: false nor explicit hadolint/hadolint image pinning is set. This allows workflow behavior to drift silently if the tag moves, the action is compromised, or the container image is updated. Pin actions/checkout to a full-length commit SHA with a version comment, add persist-credentials: false to limit credential exposure, and pin hadolint/hadolint to a specific version tag (e.g., v2.14.0) or digest.
🧰 Tools
🪛 zizmor (1.25.2)
[warning] 12-13: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 13-13: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/docker-lint.yml around lines 12 - 25, Update the workflow
to pin mutable references: replace uses: actions/checkout@v4 with the full
commit SHA for that action and add the persist-credentials: false input to the
checkout step (refer to the Checkout Code step); also pin the hadolint container
image used in the Lint Backend Dockerfile and Lint Frontend Dockerfile steps to
a specific, immutable tag or digest (e.g., hadolint/hadolint:v2.14.0 or a
digest) instead of the floating hadolint/hadolint reference so the docker run
invocations are deterministic.
| - name: Login to Docker Hub | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| # FIXED: Swapped out mismatched names to match the inputs declared above exactly | ||
| username: ${{ secrets.DOCKER_USERNAME }} | ||
| password: ${{ secrets.DOCKER_TOKEN }} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find .github/workflows -name "image-scan.yml" -type fRepository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 131
🏁 Script executed:
cat -n .github/workflows/image-scan.ymlRepository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 1837
Use the declared secret name for Docker Hub login.
The workflow declares DOCKER_PASSWORD as a required secret (line 8), but line 24 references secrets.DOCKER_TOKEN, which is undefined. On workflow_call, this will cause the login step to fail.
Proposed fix
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
- password: ${{ secrets.DOCKER_TOKEN }}
+ password: ${{ secrets.DOCKER_PASSWORD }}📝 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: Login to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| # FIXED: Swapped out mismatched names to match the inputs declared above exactly | |
| username: ${{ secrets.DOCKER_USERNAME }} | |
| password: ${{ secrets.DOCKER_TOKEN }} | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| # FIXED: Swapped out mismatched names to match the inputs declared above exactly | |
| username: ${{ secrets.DOCKER_USERNAME }} | |
| password: ${{ secrets.DOCKER_PASSWORD }} |
🧰 Tools
🪛 actionlint (1.7.12)
[error] 24-24: property "docker_token" is not defined in object type {actions_runner_debug: string; actions_step_debug: string; docker_password: string; docker_username: string; github_token: string}
(expression)
🪛 zizmor (1.25.2)
[error] 20-20: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/image-scan.yml around lines 19 - 24, The Docker Hub login
step uses the wrong secret name; update the "Login to Docker Hub" step that
calls docker/login-action@v3 to use the declared secret name for the password
(secrets.DOCKER_PASSWORD) instead of secrets.DOCKER_TOKEN so the password input
matches the workflow_call declaration; ensure the with: password: field
references secrets.DOCKER_PASSWORD while username remains
secrets.DOCKER_USERNAME.
| - name: Scan Backend Image | ||
| uses: aquasecurity/trivy-action@v0.36.0 # Fixed the version prefix to prevent resolution errors | ||
| with: | ||
| image-ref: ${{ secrets.DOCKER_USERNAME }}/skillpulse-backend:latest | ||
| format: 'table' | ||
| exit-code: 1 | ||
| severity: 'CRITICAL,HIGH' | ||
| trivyignores: .trivyignore | ||
|
|
||
| - name: Scan Frontend Image | ||
| uses: aquasecurity/trivy-action@v0.36.0 # Fixed all version prefix to prevent resolution errors | ||
| with: | ||
| image-ref: ${{ secrets.DOCKER_USERNAME }}/skillpulse-frontend:latest | ||
| format: 'table' | ||
| exit-code: 1 | ||
| severity: 'CRITICAL,HIGH' |
There was a problem hiding this comment.
Align the scanned image names with the build workflow output.
This job scans skillpulse-backend and skillpulse-frontend, but .github/workflows/docker-build-push.yml currently publishes ${DOCKERHUB_USER}/github-actions-app:* only. As-is, this workflow will either scan stale images or fail to pull the artifacts built by this PR.
🧰 Tools
🪛 zizmor (1.25.2)
[error] 29-29: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 38-38: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/image-scan.yml around lines 28 - 43, The Trivy scan steps
"Scan Backend Image" and "Scan Frontend Image" reference image refs `${{
secrets.DOCKER_USERNAME }}/skillpulse-backend:latest` and
`/skillpulse-frontend:latest` but the build/push workflow only publishes
`${DOCKERHUB_USER}/github-actions-app:*`; update the image-ref values in the
Trivy steps to match the artifact names/tags produced by the docker-build-push
workflow (e.g., change both image-ref entries to use `${{
secrets.DOCKER_USERNAME }}/github-actions-app:latest` or the exact tag name
emitted by the build), or alternatively modify the build workflow to publish
`skillpulse-backend` and `skillpulse-frontend` images so names align with the
Trivy `image-ref` entries; ensure the change is applied to the two Trivy steps
that use `aquasecurity/trivy-action@v0.36.0`.
| - name: checkout code | ||
| uses: actions/checkout@v3 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: run gitleaks | ||
| uses: gitleaks/gitleaks-action@v2 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat .github/workflows/secret-scan.ymlRepository: LondheShubham153/github-actions-kubernetes-masterclass
Length of output: 534
🌐 Web query:
GitHub Actions checkout v3 deprecated current version 2024 2025
💡 Result:
There is no single “checkout v3 is deprecated and will be removed” announcement in 2024/2025. What is supported (as of 2026-05-20): - The current major version line recommended by the actions/checkout repo is v4 (and newer majors exist, e.g., releases show v5/v6 in the repo), so your workflow should use actions/checkout@v4 (or the newest major you choose) rather than hard-coding v3 [1][2]. Why people upgraded from v3 around 2024/2025: - actions/checkout v3 runs on an older Node.js runtime (node16), which GitHub and the actions ecosystem have been deprecating/retiring over time. Community discussions and PR reviews explicitly call out that v3 is based on end-of-life Node.js and recommend switching to v4 to avoid that [3][4]. What you should do in practice: - Update your workflows from uses: actions/checkout@v3 to uses: actions/checkout@v4 (or a newer major) [5][6]. - If you want maximum safety/immutability, pin to a specific release tag or commit SHA instead of a moving tag [7]. Notes on the wording “deprecated current version 2024 2025”: - The GitHub Changelog item about “deprecation notice: v3 …” that circulated in 2024 is specifically about actions/upload-artifact and actions/download-artifact v3, not actions/checkout v3 [8].
Citations:
- 1: https://github.com/actions/checkout/releases/tag/v4.0.0
- 2: https://github.com/actions/checkout/releases
- 3: [Github Actions] actions/checkout@v3 should be upgraded to v4 denoland/fresh#2285
- 4: https://github.com/actions/checkout/pull/1697](https:/redirect.github.com/actions/checkout/pull/1697)
- 5: https://github.com/actions/checkout
- 6: actions/checkout@v4...v6
- 7: https://www.aikido.dev/blog/checklist-github-actions
- 8: https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/
🌐 Web query:
GitHub Actions actionlint checkout v3 too old
💡 Result:
If actionlint reports “the runner of actions/checkout@v3 action is too old to run on GitHub Actions”, the fix is to update your workflow to use a newer major of actions/checkout (typically actions/checkout@v4 or later). This check is intended to flag action versions whose runtime requirements are no longer supported by GitHub Actions. What’s happening - actionlint has a check that flags “outdated” popular actions referenced under uses: when the action version requires a runner/runtime that GitHub no longer supports. For example, actionlint explicitly mentions this error for actions/checkout@v3. [1] What to change - Update the workflow step from: - uses: actions/checkout@v3 - to: - uses: actions/checkout@v4 (or @v5/@v6 if your org allows), then re-run your lint. Notes - This is specifically about “runner/runtime too old” (not about YAML syntax). actionlint’s “Outdated” checks are described as detecting when an action version is outdated/out of compatibility with GitHub’s execution runtime. [2] - If you’re using actionlint’s ignore configuration, be aware that the ignore feature and matching strings depend on actionlint version; there were issues/changes around ignoring this specific message. [3] Example - Replace in your workflow: - - uses: actions/checkout@v3 - With: - - uses: actions/checkout@v4 (If you want, paste your exact workflow snippet and the full actionlint error line, and I’ll suggest the minimal diff.)
Citations:
- 1: Warn about outdated
actions/cacheuse rhysd/actionlint#622 - 2: https://github.com/rhysd/actionlint/blob/main/docs/checks.md
- 3: Unable to ignore errors by
pathsconfig rhysd/actionlint#481
🌐 Web query:
GitHub Actions best practices pin commit SHA vs tag version
💡 Result:
For GitHub Actions, the industry-standard security best practice is to pin all actions to a full-length commit SHA [1][2][3]. Why Pin to a Commit SHA: Commit SHAs are immutable, meaning they point to a specific, unchangeable state of the repository [1][4][5]. Using a SHA ensures that your workflow runs exactly the code you expect every time, protecting you against supply chain attacks where a malicious actor might move a version tag or compromise a maintainer's account to inject malicious code into a previously "trusted" tag [1][6][7][8]. GitHub explicitly identifies full-length commit SHA pinning as the only way to treat an action as an immutable release [9][3]. Best Practices for Implementation: 1. Use Full-Length SHAs: Always use the complete 40-character commit SHA, not a shortened version [2][10][3]. 2. Include a Human-Readable Comment: To maintain clarity in your codebase, append the version tag as a comment in the YAML file [11][5][8]. This allows you to track which version a specific SHA corresponds to without affecting execution [1][7]. Example: - uses: actions/checkout@f43a0e5 # v4.1.1 [11] 3. Automate Updates: Because SHA pinning prevents automatic updates, use dependency management tools like Dependabot or Renovate to monitor for and propose updates to new SHA versions [1][2][8][9]. 4. Enforce via Policy: Organizations can configure repository or organization-level policies to mandate full commit SHA pinning for all actions, causing workflows to fail if they use mutable tags or branches [1][11][9][3]. When to Use Tags: While tags (e.g., @v4) are convenient and widely used, they are mutable—meaning they can be re-released or deleted [12][3]. You might choose to use tags for internal, low-risk, or non-production workflows if you trust the action's creator implicitly [4][5][3], but for any release-sensitive, high-security, or production environment, commit SHA pinning is strongly recommended [5][10][13].
Citations:
- 1: https://www.romainlespinasse.dev/posts/github-actions-commit-sha-pinning/
- 2: https://www.aikido.dev/blog/checklist-github-actions
- 3: https://docs.github.com/en/actions/reference/security/secure-use
- 4: https://www.stepsecurity.io/blog/pinning-github-actions-for-enhanced-security-a-complete-guide
- 5: https://cloudnativeengineer.substack.com/p/github-actions-reproducibility-security
- 6: https://gbhackers.com/compromised-github-action/
- 7: https://blog.rafaelgss.dev/why-you-should-pin-actions-by-commit-hash
- 8: Pin GitHub Actions to commit SHAs in release workflow github/gh-aw#10021
- 9: https://github.blog/changelog/2025-08-15-github-actions-policy-now-supports-blocking-and-sha-pinning-actions/
- 10: https://dev.to/nkuziev-sec/why-fixed-container-image-versions-matter-lessons-from-the-trivy-supply-chain-attack-29ke
- 11: https://dev.to/ciberconscientes/github-actions-why-you-should-pin-your-actions-to-a-specific-version-1513
- 12: https://proaction.io/docs/unstable-github-ref/
- 13: https://dev.to/duriantaco/github-actions-security-and-gitlab-ci-security-static-analysis-for-cicd-g9h
🌐 Web query:
gitleaks-action current version commit SHA
💡 Result:
For gitleaks-action, the most up-to-date “current version” I can verify from public sources as of 2026-05-20 is v2.3.9 [1], and the corresponding commit SHA is the commit that the v2.3.9 tag points to. However, the search results I retrieved do not include the actual v2.3.9 tag commit SHA value itself (they show the tags page landing content but not the specific SHA). [1] What I can state with evidence right now: - Latest release tag shown on GitHub for gitleaks/gitleaks-action is v2.3.9 [2]. If you want, tell me whether you mean: 1) gitleaks/gitleaks-action (the GitHub Action repository), or 2) gitleaks (the underlying scanner), and whether you want the SHA for the v2.3.9 tag specifically. Then I can run a targeted lookup for the tag → commit SHA mapping.
Citations:
Upgrade and pin GitHub Actions to fix actionlint warning and harden security.
Line 12 uses actions/checkout@v3, which actionlint flags because v3 runs on end-of-life Node.js 16 that GitHub runners no longer support. Upgrade to actions/checkout@v4 (or newer) pinned to its full commit SHA. Also add persist-credentials: false to prevent token leakage, and pin gitleaks/gitleaks-action@v2 to a specific commit SHA instead of the moving tag.
🧰 Tools
🪛 actionlint (1.7.12)
[error] 12-12: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
🪛 zizmor (1.25.2)
[warning] 11-14: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 12-12: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 17-17: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/secret-scan.yml around lines 11 - 17, Update the GitHub
Actions steps to use and pin fixed releases and disable credential persistence:
replace the uses: reference for actions/checkout from actions/checkout@v3 to
actions/checkout@v4 pinned to its full commit SHA and add with:
persist-credentials: false under that step; similarly replace uses:
gitleaks/gitleaks-action@v2 with the action pinned to a specific commit SHA
instead of the moving tag (gitleaks/gitleaks-action@<full-sha>) so both actions
are immutably pinned and the checkout step stops passing the runner token.
| - name: Check go.mod | ||
| run: | | ||
| if [ ! -f go.mod ]; then | ||
| echo "go.mod file not found!" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # ============================== | ||
| # Cache Go Modules | ||
| # ============================== | ||
|
|
||
| - name: Cache Go Modules | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: | | ||
| ~/go/pkg/mod | ||
| ~/.cache/go-build | ||
|
|
||
| key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} | ||
|
|
||
| restore-keys: | | ||
| ${{ runner.os }}-go- | ||
|
|
||
| # ============================== | ||
| # Install Dependencies | ||
| # ============================== | ||
|
|
||
| - name: Install Dependencies | ||
| run: | | ||
| go mod tidy | ||
| go mod download | ||
|
|
||
| # ============================== | ||
| # Run Unit Tests | ||
| # ============================== | ||
|
|
||
| - name: Run Go Tests | ||
| run: | | ||
| go test ./... -v | ||
|
|
||
| # ============================== | ||
| # Generate Coverage Report | ||
| # ============================== | ||
|
|
||
| - name: Generate Coverage Report | ||
| run: | | ||
| go test ./... -coverprofile=coverage.out | ||
|
|
There was a problem hiding this comment.
Align test job with module location (backend/) to avoid false failures.
This workflow runs root-level Go checks/commands, while the companion quality workflow references backend/go.mod. The mismatch can break tests or run them from the wrong module.
Suggested patch
- name: Check go.mod
run: |
- if [ ! -f go.mod ]; then
+ if [ ! -f backend/go.mod ]; then
echo "go.mod file not found!"
exit 1
fi
@@
- name: Install Dependencies
+ working-directory: backend
run: |
go mod tidy
go mod download
@@
- name: Run Go Tests
+ working-directory: backend
run: |
go test ./... -v
@@
- name: Generate Coverage Report
+ working-directory: backend
run: |
go test ./... -coverprofile=coverage.out
@@
- name: Upload Coverage Report
uses: actions/upload-artifact@v4
with:
name: go-coverage-report
- path: coverage.out
+ path: backend/coverage.out
if-no-files-found: ignore📝 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: Check go.mod | |
| run: | | |
| if [ ! -f go.mod ]; then | |
| echo "go.mod file not found!" | |
| exit 1 | |
| fi | |
| # ============================== | |
| # Cache Go Modules | |
| # ============================== | |
| - name: Cache Go Modules | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/go/pkg/mod | |
| ~/.cache/go-build | |
| key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} | |
| restore-keys: | | |
| ${{ runner.os }}-go- | |
| # ============================== | |
| # Install Dependencies | |
| # ============================== | |
| - name: Install Dependencies | |
| run: | | |
| go mod tidy | |
| go mod download | |
| # ============================== | |
| # Run Unit Tests | |
| # ============================== | |
| - name: Run Go Tests | |
| run: | | |
| go test ./... -v | |
| # ============================== | |
| # Generate Coverage Report | |
| # ============================== | |
| - name: Generate Coverage Report | |
| run: | | |
| go test ./... -coverprofile=coverage.out | |
| - name: Check go.mod | |
| run: | | |
| if [ ! -f backend/go.mod ]; then | |
| echo "go.mod file not found!" | |
| exit 1 | |
| fi | |
| # ============================== | |
| # Cache Go Modules | |
| # ============================== | |
| - name: Cache Go Modules | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/go/pkg/mod | |
| ~/.cache/go-build | |
| key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} | |
| restore-keys: | | |
| ${{ runner.os }}-go- | |
| # ============================== | |
| # Install Dependencies | |
| # ============================== | |
| - name: Install Dependencies | |
| working-directory: backend | |
| run: | | |
| go mod tidy | |
| go mod download | |
| # ============================== | |
| # Run Unit Tests | |
| # ============================== | |
| - name: Run Go Tests | |
| working-directory: backend | |
| run: | | |
| go test ./... -v | |
| # ============================== | |
| # Generate Coverage Report | |
| # ============================== | |
| - name: Generate Coverage Report | |
| working-directory: backend | |
| run: | | |
| go test ./... -coverprofile=coverage.out |
🧰 Tools
🪛 zizmor (1.25.2)
[error] 57-57: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/tests.yml around lines 45 - 92, The workflow runs Go
commands at the repository root but the module lives in backend/, so update the
Go-related steps to operate in the backend module: for each step named "Check
go.mod", "Cache Go Modules", "Install Dependencies", "Run Go Tests", and
"Generate Coverage Report" either add working-directory: backend or prepend
commands with cd backend && so the go.mod/go.sum and module cache are resolved
from backend; ensure the cache key still hashes backend/go.sum (e.g.,
hashFiles('backend/**/go.sum')) so the actions/cache and go test/cover commands
run in the backend module context.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)
21-51: ⚡ Quick winAdd
persist-credentials: falseand consider SHA-pinning actions.Line 26's checkout step should set
persist-credentials: falseto limit credential exposure. Additionally, actions at lines 29, 32, 37, 42 use version tags rather than commit SHAs, which is a supply-chain hardening gap flagged by static analysis.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/ci.yml around lines 21 - 51, The checkout step (uses: actions/checkout@v4) must include persist-credentials: false to avoid leaking GITHUB_TOKEN; add that input under the checkout step. Also replace floating tags with SHA-pinned references for external actions used in this workflow (gitleaks/gitleaks-action@v2, hadolint/hadolint-action@v3.1.0 and actions/setup-go@v5) by updating their uses fields to the corresponding commit SHAs (or a fully pinned ref) so the Gitleaks, Hadolint Backend, Hadolint Frontend, and Setup Go steps are pinned to immutable commits.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 13-15: Remove id-token: write from the top-level permissions and
add it only to the build-scan-push job-level permissions; keep contents: read at
the appropriate scope (either workflow or job) but ensure the security job does
not get id-token. Locate the workflow permissions block and the job definitions
for build-scan-push and security, delete the global id-token: write entry, and
insert id-token: write under the build-scan-push job's permissions block so only
that job can request OIDC tokens.
- Around line 92-99: The CI step named "Trivy Scan" currently uses the mutable
ref aquasecurity/trivy-action@master; update the Trivy action reference in the
Trivy Scan step (look for the "Trivy Scan" job/name and the uses:
aquasecurity/trivy-action@master line) to a stable, immutable ref such as a
release tag (e.g., v0.36.0) or a specific commit SHA, and ensure any
documentation/comments reflect the chosen pin to prevent accidental drift.
In @.github/workflows/code-quality-check.yml:
- Line 12: Fix the YAML syntax in the workflow's go-version value: the list
entry for "1.24" is missing a closing quote; locate the go-version: ["1.22",
"1.23","1.24] list and add the missing double-quote so the entry reads "1.24"
(ensuring the entire array is valid YAML/JSON-style list).
---
Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 21-51: The checkout step (uses: actions/checkout@v4) must include
persist-credentials: false to avoid leaking GITHUB_TOKEN; add that input under
the checkout step. Also replace floating tags with SHA-pinned references for
external actions used in this workflow (gitleaks/gitleaks-action@v2,
hadolint/hadolint-action@v3.1.0 and actions/setup-go@v5) by updating their uses
fields to the corresponding commit SHAs (or a fully pinned ref) so the Gitleaks,
Hadolint Backend, Hadolint Frontend, and Setup Go steps are pinned to immutable
commits.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5c0c62c6-85dd-425e-9cac-962f74673b12
📒 Files selected for processing (3)
.github/workflows/ci.yml.github/workflows/code-quality-check.ymlbackend/go.mod
✅ Files skipped from review due to trivial changes (1)
- backend/go.mod
| permissions: | ||
| id-token: write | ||
| contents: read |
There was a problem hiding this comment.
Scope id-token: write to the job that needs it.
id-token: write is set at the workflow level but only the build-scan-push job requires OIDC for AWS credentials. The security job doesn't need this permission. Move it to job-level permissions to follow least-privilege.
Proposed fix
permissions:
- id-token: write
contents: read
...
build-scan-push:
name: Build, Scan & Push
runs-on: ubuntu-latest
needs: security
+ permissions:
+ id-token: write
+ contents: read📝 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.
| permissions: | |
| id-token: write | |
| contents: read | |
| permissions: | |
| contents: read | |
| ... | |
| build-scan-push: | |
| name: Build, Scan & Push | |
| runs-on: ubuntu-latest | |
| needs: security | |
| permissions: | |
| id-token: write | |
| contents: read |
🧰 Tools
🪛 zizmor (1.25.2)
[error] 14-14: overly broad permissions (excessive-permissions): id-token: write is overly broad at the workflow level
(excessive-permissions)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ci.yml around lines 13 - 15, Remove id-token: write from
the top-level permissions and add it only to the build-scan-push job-level
permissions; keep contents: read at the appropriate scope (either workflow or
job) but ensure the security job does not get id-token. Locate the workflow
permissions block and the job definitions for build-scan-push and security,
delete the global id-token: write entry, and insert id-token: write under the
build-scan-push job's permissions block so only that job can request OIDC
tokens.
| - name: Trivy Scan | ||
| uses: aquasecurity/trivy-action@master | ||
| with: | ||
| image-ref: ${{ env.REGISTRY }}/skillpulse-${{ matrix.service }}:${{ env.TAG }} | ||
| format: table | ||
| severity: CRITICAL,HIGH | ||
| exit-code: 1 | ||
| ignore-unfixed: true |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What is the latest version of aquasecurity/trivy-action GitHub Action?
💡 Result:
As of May 21, 2026, the latest version of the aquasecurity/trivy-action GitHub Action is v0.36.0, which was released on April 22, 2026 [1][2]. Users are advised to pin their GitHub Action versions to specific commit SHAs rather than using tags to ensure security and immutability, particularly in light of supply chain security incidents that occurred in March 2026 involving the compromise of several action tags [3][4][5].
Citations:
- 1: https://github.com/aquasecurity/trivy-action
- 2: https://github.com/aquasecurity/trivy-action/releases/tag/v0.36.0
- 3: https://www.stepsecurity.io/blog/trivy-compromised-a-second-time---malicious-v0-69-4-release
- 4: https://dev.to/nkuziev-sec/why-fixed-container-image-versions-matter-lessons-from-the-trivy-supply-chain-attack-29ke
- 5: https://www.aikido.dev/blog/checklist-github-actions
Avoid aquasecurity/trivy-action@master in CI
aquasecurity/trivy-action@master is mutable and can change without notice; pin the action to an immutable ref. Latest release is v0.36.0 (tag it to at least this version, or better pin a commit SHA).
Proposed fix
- name: Trivy Scan
- uses: aquasecurity/trivy-action@master
+ uses: aquasecurity/trivy-action@0.36.0
with:🧰 Tools
🪛 zizmor (1.25.2)
[error] 93-93: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ci.yml around lines 92 - 99, The CI step named "Trivy
Scan" currently uses the mutable ref aquasecurity/trivy-action@master; update
the Trivy action reference in the Trivy Scan step (look for the "Trivy Scan"
job/name and the uses: aquasecurity/trivy-action@master line) to a stable,
immutable ref such as a release tag (e.g., v0.36.0) or a specific commit SHA,
and ensure any documentation/comments reflect the chosen pin to prevent
accidental drift.
Summary by CodeRabbit
New Features
Chores