Files
Ludarium-Public/deploy/run-eden-security-gate.sh
T
Ludarium release export df869819ce
Public source validation / source (push) Successful in 2m16s
Publish Ludarium source
2026-09-03 02:08:58 +02:00

94 lines
4.7 KiB
Bash

#!/bin/sh
set -eu
repo="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
image="${1:-${SWITCH_IMAGE:-ludarium/eden-controller:0.4.9-rc.1}}"
container="${2:-${SWITCH_CONTAINER_NAME:-Ludarium-Switch}}"
evidence="${3:?set an evidence directory below /mnt/user/appdata/ludarium-candidate or /tmp}"
vex="$repo/deploy/security/eden-controller-0.4.9.openvex.json"
cache="${LUDARIUM_RELEASE_TOOL_CACHE:-/mnt/user/appdata/ludarium-candidate/release-tools}"
case "$evidence" in
/mnt/user/appdata/ludarium-candidate/*|/tmp/*) ;;
*) echo "Eden evidence must remain below /mnt/user/appdata/ludarium-candidate or /tmp" >&2; exit 2 ;;
esac
command -v docker >/dev/null || { echo "docker is required" >&2; exit 3; }
command -v jq >/dev/null || { echo "jq is required" >&2; exit 3; }
[ -f "$vex" ] || { echo "version-bound Eden OpenVEX document is missing" >&2; exit 3; }
mkdir -p "$evidence" "$cache/bin" "$cache/downloads" "$cache/grype-db"
grype="$cache/bin/grype-0.116.1"
if [ ! -x "$grype" ]; then
archive="$cache/downloads/grype_0.116.1_linux_amd64.tar.gz"
checksum="0122df7b655981abe547ad3d2190d65551dac6a2bfc80b4dc2a989b5d0587458"
if [ ! -f "$archive" ] || ! printf '%s %s\n' "$checksum" "$archive" | sha256sum -c - >/dev/null 2>&1; then
command -v curl >/dev/null || { echo "curl is required to acquire the pinned scanner" >&2; exit 3; }
rm -f "$archive.part"
curl --fail --location --silent --show-error --retry 5 --retry-all-errors \
--connect-timeout 15 --max-time 300 \
https://github.com/anchore/grype/releases/download/v0.116.1/grype_0.116.1_linux_amd64.tar.gz \
-o "$archive.part"
printf '%s %s\n' "$checksum" "$archive.part" | sha256sum -c - >/dev/null
mv "$archive.part" "$archive"
fi
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT INT TERM
tar -xzf "$archive" -C "$work" grype
install -m 0755 "$work/grype" "$grype"
fi
container_image="$(docker inspect "$container" --format '{{.Image}}')"
expected_image="$(docker image inspect "$image" --format '{{.Id}}')"
[ "$container_image" = "$expected_image" ] || {
echo "running Eden container does not use the exact requested image" >&2
exit 4
}
[ "$(docker inspect "$container" --format '{{.State.Health.Status}}')" = "healthy" ] || {
echo "Eden container is not healthy" >&2
exit 4
}
[ "$(docker inspect "$container" --format '{{.RestartCount}}')" = "0" ] || {
echo "Eden candidate has restarted" >&2
exit 4
}
docker inspect "$container" --format '{{range .Mounts}}{{println .Destination .RW}}{{end}}' |
grep -F '/games false' >/dev/null || { echo "Eden /games mount is not read-only" >&2; exit 4; }
docker exec "$container" /opt/ludarium/audit-eden-runtime.sh | tee "$evidence/runtime-audit.txt"
cp "$vex" "$evidence/openvex.json"
run_grype() {
if command -v timeout >/dev/null; then
timeout 600 env GRYPE_DB_CACHE_DIR="$cache/grype-db" "$@"
else
env GRYPE_DB_CACHE_DIR="$cache/grype-db" "$@"
fi
}
run_grype "$grype" db update
run_grype "$grype" "docker:$image" -o json --file "$evidence/grype-raw.json"
run_grype "$grype" "docker:$image" --vex "$vex" --fail-on critical \
-o json --file "$evidence/grype-vex.json"
raw_critical="$(jq '[.matches[] | select(.vulnerability.severity == "Critical")] | length' "$evidence/grype-raw.json")"
active_critical="$(jq '[.matches[] | select(.vulnerability.severity == "Critical")] | length' "$evidence/grype-vex.json")"
ignored_critical="$(jq '[.ignoredMatches[] | select(.vulnerability.severity == "Critical")] | length' "$evidence/grype-vex.json")"
[ "$raw_critical" -gt 0 ] || { echo "expected raw Critical evidence is absent; review the VEX baseline" >&2; exit 5; }
[ "$active_critical" = "0" ] || { echo "unresolved Critical Eden findings remain" >&2; exit 5; }
[ "$ignored_critical" = "$raw_critical" ] || { echo "VEX does not account for every raw Critical match" >&2; exit 5; }
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT INT TERM
jq -r '.matches[] | select(.vulnerability.severity == "Critical") | .vulnerability.id' \
"$evidence/grype-raw.json" | sort -u > "$work/raw-ids"
jq -r '.ignoredMatches[] | select(.vulnerability.severity == "Critical") | .vulnerability.id' \
"$evidence/grype-vex.json" | sort -u > "$work/vex-ids"
cmp -s "$work/raw-ids" "$work/vex-ids" || { echo "VEX Critical ID set differs from raw evidence" >&2; exit 5; }
raw_high="$(jq '[.matches[] | select(.vulnerability.severity == "High")] | length' "$evidence/grype-raw.json")"
unique_critical="$(wc -l < "$work/raw-ids" | tr -d ' ')"
sha256sum "$evidence/runtime-audit.txt" "$evidence/openvex.json" \
"$evidence/grype-raw.json" "$evidence/grype-vex.json" > "$evidence/SHA256SUMS"
printf 'Eden security gate passed image=%s digest=%s raw_critical=%s unique_critical=%s active_critical=0 raw_high=%s\n' \
"$image" "$expected_image" "$raw_critical" "$unique_critical" "$raw_high"