Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@ Optional. List of arguments to send to trivy.

The default is ``.

### `trivy_checksum`

Optional. Expected SHA256 checksum of the downloaded trivy release. Used to verify the download before extraction to protect against supply chain attacks.

The action automatically looks up the checksum from [`trivy_checksums.txt`](./trivy_checksums.txt) in this repository, which covers all known trivy releases. If a match is found there, no configuration is needed. If no match is found, a warning is printed and extraction proceeds unverified.

Use this input to provide a checksum manually when pinning a trivy version that is not yet configured in `trivy_checksums.txt`, for example shortly after a new trivy release before this repository has been updated.

Note: trivy releases differ per OS and architecture, so the expected checksum will vary depending on the runner platform. Checksums for each release are published by trivy in `trivy_<version>_checksums.txt` on the [trivy releases page](https://github.com/aquasecurity/trivy/releases). `trivy_checksums.txt` is updated via [`scripts/update-trivy-checksums.sh`](./scripts/update-trivy-checksums.sh).

The default is ``.

## Outputs

## `trivy-return-code`
Expand Down Expand Up @@ -152,6 +164,8 @@ jobs:
fail_level: any # Fail action if any level of failures are found
flags: -tee # Add debug flag to reviewdog
trivy_flags: "" # Optional
# trivy_version: v0.70.0 # Optional: pin to a specific version
# trivy_checksum: "" # Optional: override checksum lookup from trivy_checksums.txt
```

## Development
Expand Down
10 changes: 10 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ inputs:
Default is blank.
default: ''
required: false
trivy_checksum:
description: |
Expected SHA256 checksum of the downloaded trivy release.
If provided, the download is verified against this value before extraction.
Note: trivy releases differ per OS and architecture, so the expected checksum
will vary depending on the runner platform.
Default is blank (no verification).
default: ''
required: false

outputs:
trivy-return-code:
Expand Down Expand Up @@ -107,6 +116,7 @@ runs:
INPUT_TRIVY_COMMAND: ${{ inputs.trivy_command }}
INPUT_TRIVY_TARGET: ${{ inputs.trivy_target }}
INPUT_TRIVY_FLAGS: ${{ inputs.trivy_flags }}
INPUT_TRIVY_CHECKSUM: ${{ inputs.trivy_checksum }}

branding:
icon: 'edit'
Expand Down
51 changes: 49 additions & 2 deletions script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ echo '::group::Preparing ...'
*) echo "Unsupported architecture: ${unameArch}. Only AMD64 and ARM64 are supported by the action" && exit 1
esac

case "${os}" in
case "${os}" in
Windows) archive_extension="zip";;
*) archive_extension="tar.gz";;
esac
Expand Down Expand Up @@ -81,8 +81,55 @@ echo "::group:: Installing trivy (${INPUT_TRIVY_VERSION}) ... https://github.com
### TEST
echo "URL: ${url}"
echo "ARCHIVE: ${archive}"
ls
ls
### TEST END

checksum_key="${url##*/}"
input_checksum="${INPUT_TRIVY_CHECKSUM:-}"

# Resolve the expected checksum: prefer user-supplied value, fall back to the bundled lookup table
if [[ -n "${input_checksum}" ]]; then
# Case 1: user provided a checksum explicitly, use it
checksum="${input_checksum}"
else
# Case 2: no user-provided checksum, look up from trivy_checksums.txt
stored_checksum=$(grep -m1 -F "${checksum_key}:" "${GITHUB_ACTION_PATH}/trivy_checksums.txt" | awk '{print $2}' || true)
if [[ -z "${stored_checksum}" ]]; then
# Case 2.1: not found in trivy_checksums.txt
echo "WARNING: Skipping integrity check, set 'trivy_checksum' to verify the download."
checksum=""
else
# Case 2.2: found in trivy_checksums.txt
checksum="${stored_checksum}"
fi
fi

# compare trivy checksum against the downloaded package
if [[ -n "${checksum}" ]]; then
echo "Verifying SHA256 checksum ..."
if command -v sha256sum &>/dev/null; then
# Linux (coreutils) and Windows Git Bash (mingw-w64-x86_64-coreutils)
actual=$(sha256sum "${archive}" | awk '{print $1}')
elif command -v shasum &>/dev/null; then
# macOS (perl-based shasum, ships with Xcode CLI tools)
actual=$(shasum -a 256 "${archive}" | awk '{print $1}')
else
echo "ERROR: No SHA256 tool found (sha256sum / shasum)"
exit 1
fi

if [[ "${actual}" != "${checksum}" ]]; then
echo ""
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "!! ERROR: SHA256 checksum verification FAILED !!"
echo "!! The downloaded trivy release may have been tampered with. !!"
echo "!! Do NOT use this binary. !!"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo ""
exit 1
fi
fi

if [[ "${os}" = "Windows" ]]; then
unzip "${archive}"
else
Expand Down
111 changes: 111 additions & 0 deletions scripts/update-trivy-checksums.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/bin/bash
#
# update-trivy-checksums.sh
#
# Checksums are stored locally in this repository so that a compromised or
# tampered trivy release cannot go undetected - if an attacker modifies a
# release asset upstream, the hash will no longer match what was recorded here.
#
# Fetches all trivy GitHub releases, downloads the official SHA256 checksums
# published by trivy for each release, and writes them into trivy_checksums.txt
# at the root of this repository.
#
# If trivy_checksums.txt already exists, every previously recorded hash is
# compared against the freshly fetched upstream value. A changed hash for an
# existing release asset is a sign of tampering and causes the script to abort
# with an error without updating the file.
#
# Run this script whenever a new trivy release should be added to
# trivy_checksums.txt. Commit the resulting file alongside any trivy_version
# pin change.

set -Eeuo pipefail

CHECKSUMS_FILE="$(dirname "$0")/../trivy_checksums.txt"
REPO="aquasecurity/trivy"

echo "Fetching trivy releases from GitHub ..."
releases=$(curl -sfL \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${REPO}/releases?per_page=100" \
| jq -r '.[].tag_name')

if [[ -z "${releases}" ]]; then
echo "Error: no releases found." >&2
exit 1
fi

# Build new checksums file from scratch into a temp file
TEMP_FILE="$(mktemp)"
trap 'rm -f "${TEMP_FILE}"' EXIT

{
echo "# trivy SHA256 checksums"
echo "# Generated by scripts/update-trivy-checksums.sh"
echo "# Do not edit manually."
echo "# Format: <filename>: <sha256>"
echo ""

for release in ${releases}; do
release_num="${release/#v/}"
checksums_url="https://github.com/${REPO}/releases/download/${release}/trivy_${release_num}_checksums.txt"

checksums=$(curl -sfL "${checksums_url}" 2>/dev/null) || {
echo " Skipping ${release}: checksums file not available." >&2
continue
}

# Only include release archives for supported platforms, sorted by filename.
# grep runs before the awk swap because the original format is "<hash> <filename>"
# so the filename is at the end of the line where grep -E can match it.
entries=$(echo "${checksums}" \
| grep -E '\.(tar\.gz|zip)$' \
| sort -k2 \
| awk '{print $2 ": " $1}' || true)

[[ -z "${entries}" ]] && continue

echo "## ${release}"
echo "${entries}"
echo ""
done
} > "${TEMP_FILE}"

# Compare against existing file to detect tampered upstream checksums
if [[ -f "${CHECKSUMS_FILE}" ]]; then
# Only compare data lines (skip comments and blank lines)
existing_entries=$(grep -v '^#' "${CHECKSUMS_FILE}" | grep -v '^$' || true)

tampered=0
while IFS= read -r line; do
[[ -z "${line}" ]] && continue
filename="${line%%:*}"
existing_hash="${line##*: }"
# Use fixed-string matching to avoid regex interpretation of dots in filenames
new_hash=$(grep -m1 -F "${filename}:" "${TEMP_FILE}" | awk '{print $2}' || true)

if [[ -z "${new_hash}" ]]; then
# Entry disappeared from upstream — warn but do not treat as tampering
echo "WARNING: entry no longer found upstream: ${line}" >&2
elif [[ "${existing_hash}" != "${new_hash}" ]]; then
echo "" >&2
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" >&2
echo "!! ERROR: CHECKSUM MISMATCH - possible tampering detected !!" >&2
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" >&2
echo "!! File : ${filename}" >&2
echo "!! Expected: ${existing_hash}" >&2
echo "!! Got : ${new_hash}" >&2
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" >&2
echo "" >&2
tampered=1
fi
done <<< "${existing_entries}"

if [[ "${tampered}" -eq 1 ]]; then
echo "ERROR: One or more previously recorded checksums differ from upstream. Aborting." >&2
exit 1
fi
fi

cp "${TEMP_FILE}" "${CHECKSUMS_FILE}"
echo "Done. Checksums written to ${CHECKSUMS_FILE}"
Loading