Files
Albrecht Degering 8b93bbc6b6
Some checks failed
Dependency Audit / dependency-audit (push) Successful in 1m43s
Security Audit / security-audit (push) Failing after 4m10s
ci: mount audit workspace safely
2026-07-23 00:30:28 +02:00

206 lines
6.6 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
IMAGE="${SECURITY_AUDIT_IMAGE:-govoplan/security-audit:local}"
SCOPE="${SECURITY_AUDIT_SCOPE:-current}"
MODE="${SECURITY_AUDIT_MODE:-full}"
REPORTS_DIR="${SECURITY_AUDIT_REPORTS_DIR:-audit-reports}"
REBUILD="${SECURITY_AUDIT_REBUILD:-0}"
UPDATE="${SECURITY_AUDIT_UPDATE:-0}"
BUILD_ONLY=0
SCRIPT_ARGS=()
while [[ $# -gt 0 ]]; do
case "$1" in
--mode)
MODE="${2:?missing mode}"
shift 2
;;
--scope)
SCOPE="${2:?missing scope}"
shift 2
;;
--reports-dir)
REPORTS_DIR="${2:?missing reports dir}"
shift 2
;;
--rebuild)
REBUILD=1
shift
;;
--update)
UPDATE=1
REBUILD=1
shift
;;
--build-only)
BUILD_ONLY=1
shift
;;
-h|--help)
cat <<'EOF'
Usage: tools/checks/security-audit/run.sh [--mode quick|ci|full] [--scope current|govoplan] [--reports-dir DIR] [--rebuild] [--update] [--build-only] [--strict|--report-only]
Builds or reuses the containerized GovOPlaN audit toolbox, then runs
tools/checks/check-security-audit.sh inside it.
Image caching:
The image is tagged by a fingerprint of tools/checks/security-audit/Dockerfile and
requirements-audit.txt. Existing matching images are reused.
Flags:
--rebuild Rebuild the fingerprinted image using Docker cache.
--update Rebuild with --pull --no-cache to refresh base images/tools.
--build-only Build or refresh the image, then exit without running an audit.
EOF
exit 0
;;
--strict|--report-only)
SCRIPT_ARGS+=("$1")
shift
;;
*)
SCRIPT_ARGS+=("$1")
shift
;;
esac
done
REBUILD="${REBUILD:-0}"
UPDATE="${UPDATE:-0}"
BUILD_ONLY="${BUILD_ONLY:-0}"
declare -a DOCKER_CLI=()
DOCKER_LOCATION=""
if command -v docker >/dev/null 2>&1; then
DOCKER_CLI=(docker)
DOCKER_LOCATION="current environment"
elif command -v flatpak-spawn >/dev/null 2>&1 \
&& flatpak-spawn --host sh -lc 'command -v docker >/dev/null 2>&1'; then
DOCKER_CLI=(flatpak-spawn --host docker)
DOCKER_LOCATION="Flatpak host"
else
echo "Docker is not available in this environment or through a Flatpak host bridge." >&2
echo "Install Docker, expose its CLI/socket, or run this script from the host." >&2
exit 1
fi
if ! DOCKER_VERSION_OUTPUT="$("${DOCKER_CLI[@]}" version --format '{{.Server.Version}}' 2>&1)"; then
echo "Docker was found on the $DOCKER_LOCATION, but its daemon is not accessible:" >&2
printf ' %s\n' "$DOCKER_VERSION_OUTPUT" >&2
if [[ "$DOCKER_LOCATION" == "Flatpak host" && "$DOCKER_VERSION_OUTPUT" == *"permission denied"* ]]; then
echo "Grant the host account access to the Docker socket, then fully log out and back in." >&2
echo "On a conventional rootful Docker installation: sudo usermod -aG docker \"$(id -un)\"" >&2
echo "Membership in the docker group is root-equivalent; use rootless Docker instead where that is preferred." >&2
fi
exit 1
fi
IMAGE_NAME="${IMAGE##*/}"
if [[ "$IMAGE_NAME" == *:* ]]; then
IMAGE_REPOSITORY="${IMAGE%:*}"
IMAGE_ALIAS="$IMAGE"
else
IMAGE_REPOSITORY="$IMAGE"
IMAGE_ALIAS="$IMAGE:local"
fi
IMAGE_FINGERPRINT="$(
{
sha256sum "$ROOT/tools/checks/security-audit/Dockerfile"
sha256sum "$ROOT/requirements-audit.txt"
sha256sum "$ROOT/tools/checks/security-audit/semgrep-govoplan.yml"
sha256sum "$ROOT/tools/checks/check-version-alignment.py"
} | sha256sum | cut -c1-16
)"
FINGERPRINT_IMAGE="${SECURITY_AUDIT_FINGERPRINT_IMAGE:-$IMAGE_REPOSITORY:$IMAGE_FINGERPRINT}"
build_image() {
local -a build_args=(-t "$FINGERPRINT_IMAGE" -t "$IMAGE_ALIAS" -f "$ROOT/tools/checks/security-audit/Dockerfile")
if [[ "$UPDATE" == "1" ]]; then
build_args=(--pull --no-cache "${build_args[@]}")
fi
"${DOCKER_CLI[@]}" build "${build_args[@]}" "$ROOT"
}
if [[ "$REBUILD" == "1" ]] || ! "${DOCKER_CLI[@]}" image inspect "$FINGERPRINT_IMAGE" >/dev/null 2>&1; then
echo "Building security audit toolbox image: $FINGERPRINT_IMAGE"
build_image
else
echo "Reusing security audit toolbox image: $FINGERPRINT_IMAGE"
if ! "${DOCKER_CLI[@]}" image inspect "$IMAGE_ALIAS" >/dev/null 2>&1; then
"${DOCKER_CLI[@]}" tag "$FINGERPRINT_IMAGE" "$IMAGE_ALIAS"
fi
fi
if [[ "$BUILD_ONLY" == "1" ]]; then
"${DOCKER_CLI[@]}" image inspect "$FINGERPRINT_IMAGE" --format 'Built audit toolbox image {{.RepoTags}} {{.Id}}'
exit 0
fi
declare -a WORKSPACE_MOUNT=()
if [[ "${GITEA_ACTIONS:-}" == "true" ]]; then
ACTIONS_CONTAINER_ID="$(hostname)"
if ! ACTIONS_MOUNTS_JSON="$(
"${DOCKER_CLI[@]}" container inspect \
--format '{{json .Mounts}}' \
"$ACTIONS_CONTAINER_ID"
)"; then
echo "Gitea Actions is using a host Docker daemon, but the current job container could not be resolved." >&2
echo "Cannot safely share the checked-out workspace with the audit container." >&2
exit 1
fi
if command -v python3 >/dev/null 2>&1; then
MOUNT_PYTHON=python3
elif command -v python >/dev/null 2>&1; then
MOUNT_PYTHON=python
else
echo "Python is required to validate the Gitea Actions workspace mount." >&2
exit 1
fi
if ! ACTIONS_WORKSPACE_MOUNT="$(
printf '%s' "$ACTIONS_MOUNTS_JSON" \
| "$MOUNT_PYTHON" "$ROOT/tools/checks/security-audit/resolve_workspace_mount.py" \
--root "$ROOT" \
--scope "$SCOPE" \
--reports-dir "$REPORTS_DIR"
)"; then
echo "Cannot safely share the Gitea Actions workspace with the audit container." >&2
exit 1
fi
WORKSPACE_MOUNT=(--mount "$ACTIONS_WORKSPACE_MOUNT")
MOUNT_ROOT="$(dirname "$ROOT")"
WORKDIR="$ROOT"
if [[ "$SCOPE" == "govoplan" ]]; then
EXTRA_ENV=(-e "GOVOPLAN_REPOS_ROOT=$MOUNT_ROOT")
else
EXTRA_ENV=()
fi
elif [[ "$SCOPE" == "govoplan" ]]; then
MOUNT_ROOT="$(dirname "$ROOT")"
WORKDIR="/workspace/$(basename "$ROOT")"
WORKSPACE_MOUNT=(-v "$MOUNT_ROOT:/workspace")
EXTRA_ENV=(-e "GOVOPLAN_REPOS_ROOT=/workspace")
else
MOUNT_ROOT="$ROOT"
WORKDIR="/workspace"
WORKSPACE_MOUNT=(-v "$MOUNT_ROOT:/workspace")
EXTRA_ENV=()
fi
"${DOCKER_CLI[@]}" run --rm \
--user "$(id -u):$(id -g)" \
-e HOME=/tmp/security-audit-home \
-e SECURITY_AUDIT_SCOPE="$SCOPE" \
-e SECURITY_AUDIT_MODE="$MODE" \
-e SECURITY_AUDIT_REPORTS_DIR="$REPORTS_DIR" \
-e SECURITY_AUDIT_FAIL_ON_FINDINGS="${SECURITY_AUDIT_FAIL_ON_FINDINGS:-0}" \
-e SECURITY_AUDIT_REQUIRE_TOOLS="${SECURITY_AUDIT_REQUIRE_TOOLS:-0}" \
-e SECURITY_AUDIT_TOOLBOX_FINGERPRINT="$IMAGE_FINGERPRINT" \
"${EXTRA_ENV[@]}" \
"${WORKSPACE_MOUNT[@]}" \
-w "$WORKDIR" \
"$FINGERPRINT_IMAGE" \
bash tools/checks/check-security-audit.sh --mode "$MODE" --scope "$SCOPE" --reports-dir "$REPORTS_DIR" "${SCRIPT_ARGS[@]}"