#!/usr/bin/env bash set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" TARGET_IMAGE="${1:-geointel-ci:local}" OUTPUT="${2:-artifacts/geointel-sbom.spdx.json}" SYFT_IMAGE="anchore/syft:v1.44.0@sha256:86fde6445b483d902fe011dd9f68c4987dd94e07da1e9edc004e3c2422650de6" TMP_DIR="$(mktemp -d)" SYFT_CONTAINER="" cleanup() { if [ -n "$SYFT_CONTAINER" ]; then docker rm -f "$SYFT_CONTAINER" >/dev/null 2>&1 || true fi rm -rf "$TMP_DIR" } trap cleanup EXIT case "$OUTPUT" in /*|*..*) echo "SBOM output must be a repository-relative path without '..'." >&2 exit 1 ;; esac docker image inspect "$TARGET_IMAGE" >/dev/null mkdir -p "$ROOT/$(dirname "$OUTPUT")" # Keep Syft supply-chain pinned to the audited container image, but execute the # extracted static binary in the Actions job itself. This lets Syft use the # job's existing DOCKER_HOST directly and avoids a second scanner-container -> # host-gateway -> DinD network hop for large AI images. if ! docker image inspect "$SYFT_IMAGE" >/dev/null 2>&1; then docker pull "$SYFT_IMAGE" >/dev/null fi SYFT_CONTAINER="$(docker create "$SYFT_IMAGE")" docker cp "$SYFT_CONTAINER:/syft" "$TMP_DIR/syft" docker rm "$SYFT_CONTAINER" >/dev/null SYFT_CONTAINER="" chmod +x "$TMP_DIR/syft" SYFT_CHECK_FOR_APP_UPDATE=false \ "$TMP_DIR/syft" \ "$TARGET_IMAGE" \ --from docker \ -o spdx-json > "$ROOT/$OUTPUT" test -s "$ROOT/$OUTPUT" echo "SBOM written to $OUTPUT"