Skip to content

OSV Security Vulnerability Scan #24

OSV Security Vulnerability Scan

OSV Security Vulnerability Scan #24

Workflow file for this run

name: OSV Security Vulnerability Scan
on:
schedule:
- cron: "0 17 * * *"
workflow_dispatch:
inputs:
branches:
description: "Allowlisted branch or branches to scan"
type: choice
default: all
options:
- all
- main
- v2.2.x
- v2.3.x
- v2.4.x
permissions:
actions: read
contents: read
packages: read
security-events: write
env:
IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }}
OSV_SCANNER_IMAGE: ghcr.io/google/osv-scanner-action:v2.3.5
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
determine_branches:
name: Determine Branches
runs-on: ubuntu-22.04
outputs:
branches: ${{ steps.determine.outputs.branches }}
steps:
- name: Determine branch matrix
id: determine
shell: bash
run: |
set -euo pipefail
branches='["main","v2.2.x","v2.3.x","v2.4.x"]'
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
case "${{ inputs.branches }}" in
all)
;;
main)
branches='["main"]'
;;
v2.2.x)
branches='["v2.2.x"]'
;;
v2.3.x)
branches='["v2.3.x"]'
;;
v2.4.x)
branches='["v2.4.x"]'
;;
*)
echo "::error::Unsupported branch selection: ${{ inputs.branches }}"
exit 1
;;
esac
fi
{
echo "branches=${branches}"
} >> "$GITHUB_OUTPUT"
# Source dependency scan (one analysis per branch, platform-independent).
# Uploaded under category: osv-scanner-<branch>
scan_source:
needs: determine_branches
name: Scan source (${{ matrix.branch }})
runs-on: ubuntu-22.04
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
branch: ${{ fromJson(needs.determine_branches.outputs.branches) }}
steps:
- name: Checkout branch
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
persist-credentials: false
ref: ${{ matrix.branch }}
- name: Collect upload metadata
id: metadata
shell: bash
run: |
set -euo pipefail
{
echo "ref=refs/heads/${{ matrix.branch }}"
echo "sha=$(git rev-parse HEAD)"
echo "category=osv-scanner-$(echo '${{ matrix.branch }}' | tr '/.' '--')"
} >> "$GITHUB_OUTPUT"
- name: Run source scanner
uses: google/osv-scanner-action/osv-scanner-action@c51854704019a247608d928f370c98740469d4b5 # v2.3.5
with:
scan-args: |-
--output=results.json
--format=json
--no-call-analysis=go
--no-call-analysis=rust
-r
./
continue-on-error: true
- name: Convert source results to SARIF
uses: google/osv-scanner-action/osv-reporter-action@c51854704019a247608d928f370c98740469d4b5 # v2.3.5
with:
scan-args: |-
--output=results.sarif
--new=results.json
--gh-annotations=false
--fail-on-vuln=false
- name: Upload source SARIF
if: ${{ !cancelled() && hashFiles('results.sarif') != '' }}
uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10
with:
sarif_file: results.sarif
ref: ${{ steps.metadata.outputs.ref }}
sha: ${{ steps.metadata.outputs.sha }}
category: ${{ steps.metadata.outputs.category }}
- name: Print Code Scanning URL
if: ${{ !cancelled() && hashFiles('results.sarif') != '' }}
shell: bash
run: |
echo "View the branch-specific OSV-Scanner results in Security -> Code scanning:"
echo "GitHub defaults that page to the repository default branch, so use the branch filter in this link:"
echo "${{ github.server_url }}/${{ github.repository }}/security/code-scanning?query=is%3Aopen+branch%3A${{ matrix.branch }}+tool%3Aosv-scanner"
# Container image scan. One analysis per (branch, image, platform) so each
# alert names the exact image it came from.
# Uploaded under category: osv-scanner-<branch>-images-<image>-<platform>
scan_image:
needs: determine_branches
name: Scan image (${{ matrix.branch }}, ${{ matrix.image }}, ${{ matrix.platform }})
runs-on: ubuntu-22.04
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
branch: ${{ fromJson(needs.determine_branches.outputs.branches) }}
platform:
- linux/amd64
- linux/arm64
image:
- kgateway
- sds
- envoy-wrapper
steps:
- name: Checkout branch
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
persist-credentials: false
ref: ${{ matrix.branch }}
- name: Collect upload metadata
id: metadata
shell: bash
run: |
set -euo pipefail
{
echo "ref=refs/heads/${{ matrix.branch }}"
echo "sha=$(git rev-parse HEAD)"
echo "image_category=osv-scanner-$(echo '${{ matrix.branch }}' | tr '/.' '--')-images-$(echo '${{ matrix.image }}' | tr '/.' '--')-$(echo '${{ matrix.platform }}' | tr '/.' '--')"
} >> "$GITHUB_OUTPUT"
- name: Resolve image scan target
id: image-target
shell: bash
run: |
set -euo pipefail
branch="${{ matrix.branch }}"
case "${branch}" in
main)
image_version="$(make --no-print-directory print-ROLLING_MAIN_VERSION)"
;;
v[0-9]*.[0-9]*.x)
series="${branch%.x}"
series_regex="${series//./\\.}"
image_version="$(
git ls-remote --tags --refs origin "${series}.*" \
| sed -E 's#.*refs/tags/##' \
| grep -E "^${series_regex}\\.[0-9]+$" \
| sort -V \
| tail -n 1 || true
)"
if [[ -z "${image_version}" ]]; then
echo "::error::No stable patch release tag found for ${branch}"
exit 1
fi
;;
*)
echo "::error::Unsupported branch for image scan: ${branch}"
exit 1
;;
esac
{
echo "image_version=${image_version}"
echo "image=${IMAGE_REGISTRY}/${{ matrix.image }}:${image_version}"
} >> "$GITHUB_OUTPUT"
echo "Resolved image for ${branch}: ${IMAGE_REGISTRY}/${{ matrix.image }}:${image_version}"
- name: Log in to GHCR for image scans
shell: bash
env:
GITHUB_TOKEN: ${{ github.token }}
run: echo "${GITHUB_TOKEN}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
- name: Install skopeo
shell: bash
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install --no-install-recommends -y skopeo
- name: Run image scanner
shell: bash
env:
OSV_DOCKER_IMAGE: ${{ steps.image-target.outputs.image }}
OSV_DOCKER_IMAGE_NAME: ${{ matrix.image }}
OSV_DOCKER_PLATFORM: ${{ matrix.platform }}
run: |
set -euo pipefail
archive_dir="${RUNNER_TEMP}/osv-image-archives"
mkdir -p "${archive_dir}"
scanner_status=0
reporter_status=0
image="${OSV_DOCKER_IMAGE}"
safe_image="$(printf '%s' "${image}" | sed 's/[^A-Za-z0-9_.-]/-/g')"
image_archive="${archive_dir}/${safe_image}.tar"
result_json="image-result.json"
result_sarif="image-result.sarif"
image_platform="${OSV_DOCKER_PLATFORM}"
image_os="${image_platform%%/*}"
image_arch="${image_platform#*/}"
image_arch="${image_arch%%/*}"
rm -f "${image_archive}"
skopeo copy \
--override-os "${image_os}" \
--override-arch "${image_arch}" \
"docker://${image}" \
"docker-archive:${image_archive}:${image}"
echo "Running OSV-Scanner for Docker image: ${image}"
if docker run \
--rm \
-v "${PWD}:/workspace" \
-v "${archive_dir}:/archives:ro" \
-w /workspace \
--entrypoint /root/osv-scanner \
"${OSV_SCANNER_IMAGE}" \
scan image \
--archive \
--config=/workspace/osv-scanner.toml \
--output-file="/workspace/${result_json}" \
--format=json \
--verbosity=warn \
"/archives/${safe_image}.tar"; then
:
else
scanner_status=$?
fi
if [[ ! -f "${result_json}" ]]; then
echo "::error::osv-scanner did not produce ${result_json}"
exit 1
fi
rm -f "${image_archive}"
if docker run \
--rm \
-v "${PWD}:/workspace" \
-w /workspace \
--entrypoint /root/osv-reporter \
"${OSV_SCANNER_IMAGE}" \
--output-files="sarif:/workspace/${result_sarif}" \
--new="/workspace/${result_json}" \
--fail-on-vuln=false; then
:
else
reporter_status=$?
fi
if [[ ! -f "${result_sarif}" ]]; then
echo "::error::osv-reporter did not produce ${result_sarif}"
exit 1
fi
image_without_digest="${image%%@*}"
if [[ "${image_without_digest}" == *:* ]]; then
image_version="${image_without_digest##*:}"
else
image_version="untagged"
fi
safe_platform="$(printf '%s' "${OSV_DOCKER_PLATFORM}" | sed 's/[^A-Za-z0-9_.-]/-/g')"
safe_image_version="$(printf '%s' "${image_version}" | sed 's/[^A-Za-z0-9_.-]/-/g')"
safe_image_name="$(printf '%s' "${OSV_DOCKER_IMAGE_NAME}" | sed 's/[^A-Za-z0-9_.-]/-/g')"
# Rewrite in-container file paths to a synthetic, image-qualified path so the
# uploaded alert's location names the exact image:
# container-images/<image>/<version>/<platform>/<path>
# Also rename the SARIF tool driver to "osv-image-scanner" so it is
# distinguishable from the source scan (osvtool keys image findings off this).
jq \
--arg image "${image}" \
--arg image_name "${OSV_DOCKER_IMAGE_NAME}" \
--arg safe_image_name "${safe_image_name}" \
--arg image_version "${image_version}" \
--arg safe_image_version "${safe_image_version}" \
--arg platform "${OSV_DOCKER_PLATFORM}" \
--arg safe_platform "${safe_platform}" \
'
def rewrite_artifact_location:
if ((.uri? // "") | startswith("file:///")) then
. as $location |
($location.uri | sub("^file://"; "")) as $container_path |
.uri = ("container-images/" + $safe_image_name + "/" + $safe_image_version + "/" + $safe_platform + "/" + ($container_path | sub("^/"; ""))) |
.description = {
text: ("Container image " + $image + " (" + $platform + "): " + $container_path)
} |
.properties = ((.properties // {}) + {
containerImage: $image,
containerImageName: $image_name,
containerImageVersion: $image_version,
containerPlatform: $platform,
originalContainerPath: $container_path
}) |
del(.index)
else
.
end;
(.runs[]?.artifacts[]?.location) |= rewrite_artifact_location |
(.runs[]?.results[]?.locations[]?.physicalLocation.artifactLocation) |= rewrite_artifact_location |
(.runs[]?) |= (
.tool.driver.name = "osv-image-scanner" |
.tool.driver.fullName = "OSV Image Scanner" |
.tool.driver.informationUri = "https://google.github.io/osv-scanner/usage/scan-image/" |
.tool.driver.properties = ((.tool.driver.properties // {}) + {
sourceTool: "osv-scanner",
scanTarget: "container-image",
containerImage: $image,
containerImageName: $image_name,
containerImageVersion: $image_version,
containerPlatform: $platform
})
)
' "${result_sarif}" > "${result_sarif}.tmp"
mv "${result_sarif}.tmp" "${result_sarif}"
if [[ "${scanner_status}" -ne 0 || "${reporter_status}" -ne 0 ]]; then
echo "OSV image scan completed and wrote results despite non-zero scanner/reporter exit status."
fi
- name: Upload image SARIF
if: ${{ !cancelled() && hashFiles('image-result.sarif') != '' }}
uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10
with:
sarif_file: image-result.sarif
ref: ${{ steps.metadata.outputs.ref }}
sha: ${{ steps.metadata.outputs.sha }}
category: ${{ steps.metadata.outputs.image_category }}