Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions .github/steps/trivy/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: "Vulnerability Scan"
description: "Trivy PR security scans. scan: code = repo dependencies (HIGH/CRITICAL) + Dockerfile base images (CRITICAL), image = image built in the pipeline (HIGH/CRITICAL). Each scan uploads its TSV report as an artifact"

inputs:
scan:
description: "code | image"
required: true
maven-token:
description: "Token for internal GitHub Packages: Maven resolution (scan: code) and docker build secret (scan: image)"
required: false
default: ${{ github.token }}
image-name:
description: "Service name from the matrix; used in the artifact name"
required: false
path:
description: "Directory to scan (scan: code), e.g. a single service; defaults to the whole repo"
required: false
default: "."
skip-dirs:
description: "Comma-separated directories to exclude from the code scan (e.g. service dirs already scanned by the matrix)"
required: false
default: ""
dockerfile:
description: "Path to the service Dockerfile (scan: image)"
required: false
context:
description: "Docker build context (scan: image)"
required: false

runs:
using: composite
steps:
- name: Install Trivy
uses: aquasecurity/setup-trivy@v0.3.1
with:
version: v0.73.0
cache: true
Comment on lines +33 to +37

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ref="$(awk '/uses: aquasecurity\/setup-trivy@/ {print $2}' .github/steps/trivy/action.yml)"
[[ "$ref" =~ ^aquasecurity/setup-trivy@[0-9a-f]{40}$ ]]

Repository: flamingo-stack/meshcentral

Length of output: 164


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## action.yml relevant lines"
sed -n '1,45p' .github/steps/trivy/action.yml

echo
echo "## current Trivy pin"
ref="$(awk '/uses: aquasecurity\/setup-trivy@/ {print $2}' .github/steps/trivy/action.yml)"
printf 'pin=%s\n' "$ref"
[[ "$ref" =~ ^aquasecurity/setup-trivy@[0-9a-f]{40}$ ]]
exit_code=$?
echo "matches_40_char_sha=$exit_code"

Repository: flamingo-stack/meshcentral

Length of output: 2174


🌐 Web query:

aquasecurity setup-trivy advisory GHSA-69fq-xp46-6x23 March 2026 full commit SHA pin recommendation

💡 Result:

In response to the supply chain compromise identified as GHSA-69fq-xp46-6x23 (CVE-2026-33634) in March 2026, where a threat actor force-pushed malicious code to all existing tags (v0.2.0–v0.2.6) of the aquasecurity/setup-trivy GitHub Action, users are strongly advised to pin to a verified, safe commit SHA [1][2][3]. The specific commit SHA identified as the safe version for setup-trivy v0.2.6 following the incident is 3fb12ec12f41e471780db15c232d5dd185dcb514 [4][5]. Security best practices for using GitHub Actions, particularly following this incident, include: 1. Pinning to full, immutable commit SHA hashes rather than relying on mutable version tags (e.g., v0.2.6) to prevent similar supply chain attacks where tags are overwritten [6][3]. 2. Treating any pipeline secrets used during the exposure window (March 19, 2026, 17:43 UTC to approximately March 19, 2026, 21:44 UTC for setup-trivy) as compromised and rotating them immediately [1][4][2]. For additional context, aquasecurity restored setup-trivy v0.2.6 with safe content after the incident, but because the attacker force-pushed to the tags, users who had previously cached or pulled the malicious version of v0.2.6 must ensure they update to the corrected SHA [1][2].

Citations:


Pin aquasecurity/setup-trivy to a full commit SHA.

The pin aquasecurity/setup-trivy@v0.3.1 uses a mutable third-party action tag. A changed tag can execute code with the Maven token. Use a reviewed 40-character commit SHA instead.

🤖 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/steps/trivy/action.yml around lines 13 - 17, Update the Install
Trivy step’s uses reference from the mutable aquasecurity/setup-trivy@v0.3.1 tag
to a reviewed, immutable 40-character commit SHA, keeping the existing version
and cache inputs unchanged.


- name: Scan dependencies and Dockerfile base images
if: inputs.scan == 'code'
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.maven-token }}
GITHUB_ACTOR: ${{ github.actor }}
run: |
set -euo pipefail
dir="${{ inputs.path }}"
if [ -f "$dir/pom.xml" ]; then # warm ~/.m2 via Google's Central mirror so trivy sees parent-managed versions
echo '<settings><mirrors><mirror><id>google-central</id><url>https://maven-central.storage-download.googleapis.com/maven2/</url><mirrorOf>central</mirrorOf></mirror></mirrors></settings>' > /tmp/mirror.xml
mvn -B -q -fn -DskipTests -gs /tmp/mirror.xml -f "$dir/pom.xml" $([ -f .mvn/settings.xml ] && echo '-s .mvn/settings.xml') dependency:go-offline | tee /tmp/mvn.log || true
! grep -q '\[ERROR\]' /tmp/mvn.log || echo "::warning::Some Maven dependencies could not be resolved; scan depth may be reduced"
fi
skip="${{ inputs.skip-dirs }}"
targs=(--no-progress --scanners vuln --severity HIGH,CRITICAL --ignore-unfixed --offline-scan --format json)
[ -n "$skip" ] && targs+=(--skip-dirs "$skip")
trivy fs "${targs[@]}" "$dir" |
jq -r '.Results[]? as $r | $r.Vulnerabilities[]? | [.Severity, .PkgName, .InstalledVersion, (.FixedVersion // "-"), .VulnerabilityID, $r.Target] | @tsv' | sort -u > "trivy-${GITHUB_REPOSITORY##*/}-code-report.tsv"

out="trivy-${GITHUB_REPOSITORY##*/}-docker-report.tsv"; : > "$out"
fargs=(-name 'Dockerfile*' -not -path '*/.git/*' -not -path '*/node_modules/*')
[ -n "$skip" ] && for d in ${skip//,/ }; do fargs+=(-not -path "*/${d}/*" -not -path "${d}/*"); done
find "$dir" "${fargs[@]}" -print0 |
xargs -0 -r awk 'toupper($1)=="FROM" {i=$2; if (i ~ /^--/) i=$3; print i; if (toupper($3)=="AS") print "~"$4; if (toupper($4)=="AS") print "~"$5}' | sort -u > /tmp/from.txt
for img in $(grep -v '^~' /tmp/from.txt); do
[ "$img" = scratch ] && continue
case "$img" in *'$'*) echo "::warning::Skipping base image with unresolved variable: ${img}"; continue ;; esac
grep -qxF "~$img" /tmp/from.txt && continue
trivy image --no-progress --scanners vuln --severity CRITICAL --ignore-unfixed --format json "$img" |
jq -r --arg img "$img" '.Results[]? as $r | $r.Vulnerabilities[]? | [.Severity, .PkgName, .InstalledVersion, (.FixedVersion // "-"), .VulnerabilityID, $img] | @tsv' >> "$out" ||
echo "::warning::Base image ${img} could not be scanned (pull/scan error); it was NOT checked"
done
sort -u "$out" -o "$out"
find . -maxdepth 1 -name 'trivy-*.tsv' -empty -delete

- name: Scan built image
if: inputs.scan == 'image'
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.maven-token }}
GITHUB_ACTOR: ${{ github.actor }}
run: |
set -euo pipefail
docker buildx build --platform linux/amd64 --secret id=GITHUB_TOKEN,env=GITHUB_TOKEN --build-arg GITHUB_ACTOR="$GITHUB_ACTOR" \
-f "${{ inputs.dockerfile }}" --output type=oci,dest=/tmp/image.tar "${{ inputs.context }}"
out="trivy-${GITHUB_REPOSITORY##*/}-image-report-${{ inputs.image-name }}.tsv"
trivy image --no-progress --scanners vuln --severity HIGH,CRITICAL --ignore-unfixed --input /tmp/image.tar --format json |
jq -r '.Results[]? as $r | $r.Vulnerabilities[]? | [.Severity, .PkgName, .InstalledVersion, (.FixedVersion // "-"), .VulnerabilityID, $r.Target] | @tsv' | sort -u > "$out"
find . -maxdepth 1 -name 'trivy-*.tsv' -empty -delete

- name: Upload report
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.scan == 'code' && (inputs.image-name && format('scan_code-{0}', inputs.image-name) || 'scan_code') || format('scan_image-{0}', inputs.image-name) }}
path: trivy-*.tsv
if-no-files-found: ignore

- name: Evaluate
shell: bash
env:
ARTIFACT: ${{ inputs.scan == 'code' && 'scan_code' || 'scan_image' }}
run: |
set -euo pipefail
files=$(ls trivy-*.tsv 2>/dev/null) || { echo "No vulnerabilities found."; exit 0; }
column -t -s "$(printf '\t')" $files
echo "::error::Trivy found $(cut -f1,2,5 $files | sort -u | wc -l | tr -d ' ') unique HIGH/CRITICAL vulnerabilities ($(cat $files | wc -l | tr -d ' ') occurrences across modules) — full report in the ${ARTIFACT} artifact"
exit 1
20 changes: 20 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,26 @@ jobs:
!github.event.pull_request.draft


scan:
name: "Scan"
runs-on: ubuntu-latest
needs: [changes]
permissions:
contents: read
packages: read
if: github.event_name == 'pull_request'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
persist-credentials: false

- name: Scan code
uses: ./.github/steps/trivy
Comment on lines +46 to +58

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 4 \
  'head\.repo|persist-credentials|maven-token|GITHUB_TOKEN|dependency:go-offline' \
  .github/workflows/test.yml .github/steps/trivy/action.yml

Repository: flamingo-stack/meshcentral

Length of output: 2778


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow relevant lines =="
sed -n '1,110p' .github/workflows/test.yml | cat -n

echo
echo "== trivy action relevant lines =="
sed -n '1,80p' .github/steps/trivy/action.yml | cat -n

echo
echo "== workflow references to head repo / forks / persist-credentials =="
rg -n 'pull_request|fork|persist-credentials|maven-token|GITHUB_TOKEN|actions/checkout|trivy' .github/workflows/test.yml .github/steps/trivy/action.yml

Repository: flamingo-stack/meshcentral

Length of output: 10873


Do not run Maven dependency resolution for fork pull requests.

The trivy_scan job runs for all open non-draft pull requests and checks out the pull request head. actions/checkout stores credentials by default, and the Trivy composite action exports GITHUB_TOKEN to Maven during dependency:go-offline. Maven can load pull-request-controlled build configuration such as .mvn/extensions.xml, so a fork PR could use that configuration to read the token. Set persist-credentials: false, remove Maven resolution from fork workflows, or scope tokenized Maven resolution to same-repository pull requests.

🧰 Tools
🪛 zizmor (1.28.0)

[warning] 50-53: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 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/test.yml around lines 45 - 56, Update the trivy_scan
workflow so fork pull requests never run tokenized Maven dependency resolution:
disable credential persistence in the Checkout step and remove or conditionally
skip the Trivy composite action’s Maven resolution for fork-originated pull
requests, while preserving it for same-repository pull requests as appropriate.

Source: Linters/SAST tools

with:
scan: code

test:
name: "Test: ${{ matrix.name }}"
needs: [changes]
Expand Down
3 changes: 3 additions & 0 deletions .trivyignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Vulnerabilities to skip in the Trivy scan: one CVE/GHSA id per line, e.g.:
#
# CVE-2026-12345