#!/usr/bin/env bash set -euo pipefail ROOT_DIR=$( cd "$(dirname "${BASH_SOURCE[0]}")/.." >/dev/null 2>&1 pwd -P ) SLOPTRAP_BIN="$ROOT_DIR/sloptrap" TEST_ROOT="$ROOT_DIR/tests" if [[ ! -x $SLOPTRAP_BIN ]]; then printf 'error: sloptrap launcher not found at %s\n' "$SLOPTRAP_BIN" >&2 exit 1 fi failures=() run_shellcheck() { printf '==> shellcheck\n' if ! command -v shellcheck >/dev/null 2>&1; then record_failure "shellcheck: shellcheck binary not found in PATH" return fi if ! shellcheck "$SLOPTRAP_BIN"; then record_failure "shellcheck: lint errors detected" fi } setup_stub_env() { STUB_BIN=$(mktemp -d) STUB_HOME=$(mktemp -d) STUB_LOG=$(mktemp) cat >"$STUB_BIN/podman" <<'EOF' #!/usr/bin/env bash set -euo pipefail if [[ -z ${FAKE_PODMAN_LOG:-} ]]; then printf 'FAKE_PODMAN_LOG is not set\n' >&2 exit 1 fi verify_secret_mounts() { local -a args=("$@") if [[ -z ${SECRET_MASK_EXPECTED_TARGET:-} ]]; then return 0 fi local saw=0 local idx=0 while (( idx < ${#args[@]} )); do local arg=${args[$idx]} if [[ $arg == "--mount" ]]; then ((idx++)) if (( idx >= ${#args[@]} )); then printf 'podman stub: --mount missing spec\n' >&2 return 1 fi local spec=${args[$idx]} local target="" local source="" IFS=',' read -r -a parts <<< "$spec" for part in "${parts[@]}"; do case $part in target=*) target=${part#target=} ;; source=*) source=${part#source=} ;; esac done if [[ -n ${SECRET_MASK_EXPECTED_TARGET:-} && $target == "$SECRET_MASK_EXPECTED_TARGET" ]]; then saw=1 if [[ -z $source || ! -f $source ]]; then printf 'podman stub: masked source missing for %s\n' "$target" >&2 return 1 fi if [[ -s $source ]]; then printf 'podman stub: masked source leaked contents (%s)\n' "$source" >&2 return 1 fi fi fi ((idx++)) done if [[ $saw -eq 0 ]]; then printf 'podman stub: target %s not mounted\n' "${SECRET_MASK_EXPECTED_TARGET:-}" >&2 return 1 fi return 0 } if [[ ${1-} == "image" && ${2-} == "inspect" && ${FAKE_PODMAN_INSPECT_FAIL:-0} == 1 ]]; then echo "FAKE PODMAN (fail): $*" >>"$FAKE_PODMAN_LOG" exit 1 fi if [[ ${SECRET_MASK_VERIFY:-0} == 1 && ${1-} == "run" ]]; then if ! verify_secret_mounts "$@"; then echo "FAKE PODMAN (secret-check failed): $*" >>"$FAKE_PODMAN_LOG" exit 1 fi fi echo "FAKE PODMAN: $*" >>"$FAKE_PODMAN_LOG" exit 0 EOF chmod +x "$STUB_BIN/podman" cat >"$STUB_BIN/curl" <<'EOF' #!/usr/bin/env bash set -euo pipefail if [[ ${1-} == "-fsSL" ]]; then cat <<'JSON' {"assets":[{"name":"codex-x86_64-unknown-linux-gnu.tar.gz","digest":"sha256:feedface"}]} JSON exit 0 fi if [[ ${1-} == "-Lso" ]]; then if [[ $# -lt 3 ]]; then echo "curl stub expected output path" >&2 exit 1 fi output=$2 : >"$output" exit 0 fi printf 'curl stub encountered unsupported args: %s\n' "$*" >&2 exit 1 EOF chmod +x "$STUB_BIN/curl" cat >"$STUB_BIN/jq" <<'EOF' #!/usr/bin/env bash set -euo pipefail while [[ $# -gt 0 ]]; do case "$1" in -r) shift continue ;; *) break ;; esac done cat >/dev/null printf 'sha256:feedface\n' EOF chmod +x "$STUB_BIN/jq" cat >"$STUB_BIN/sha256sum" <<'EOF' #!/usr/bin/env bash set -euo pipefail if [[ ${1-} == "-c" ]]; then shift if [[ ${1-} == "-" ]]; then shift cat >/dev/null exit 0 fi fi printf 'sha256sum stub encountered unsupported args: %s\n' "$*" >&2 exit 1 EOF chmod +x "$STUB_BIN/sha256sum" cat >"$STUB_BIN/tar" <<'EOF' #!/usr/bin/env bash set -euo pipefail dest="" new_name="codex" while [[ $# -gt 0 ]]; do case "$1" in -C) shift if [[ $# -eq 0 ]]; then echo "tar stub missing directory for -C" >&2 exit 1 fi dest=$1 ;; --transform=*) transform=${1#--transform=} transform=${transform#s/} rest=${transform#*/} new_name=${rest%%/*} ;; esac shift done [[ -n $dest ]] || dest="." mkdir -p "$dest" cat >"$dest/$new_name" <<'BIN' #!/usr/bin/env bash echo "fake codex" BIN chmod +x "$dest/$new_name" EOF chmod +x "$STUB_BIN/tar" } teardown_stub_env() { rm -rf "${STUB_BIN:-}" "${STUB_HOME:-}" rm -f "${STUB_LOG:-}" } record_failure() { failures+=("$1") } run_mount_injection() { local scenario_dir="$TEST_ROOT/mount_injection" printf '==> mount_injection\n' setup_stub_env rm -rf "$scenario_dir/.sloptrap-ignores" if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \ "$SLOPTRAP_BIN" "$scenario_dir" >/dev/null 2>&1; then record_failure "mount_injection: sloptrap exited non-zero" teardown_stub_env return fi if ! grep -q -- "--mount type=bind" "$STUB_LOG"; then record_failure "mount_injection: bind mount invocation missing" fi if grep -q -- "attack,source=/etc/passwd" "$STUB_LOG"; then record_failure "mount_injection: unescaped mount key detected" fi if ! grep -q -- "attack\\\\,source\\\\=/etc/passwd" "$STUB_LOG"; then record_failure "mount_injection: escaped mount key missing" fi if ! grep -q -- "FAKE PODMAN: build " "$STUB_LOG"; then record_failure "mount_injection: image build path did not trigger" fi teardown_stub_env } run_root_target() { local scenario_dir="$TEST_ROOT/root_target" printf '==> root_target\n' if "$SLOPTRAP_BIN" --dry-run "$scenario_dir" >/dev/null 2>&1; then record_failure "root_target: expected rejection for project-root mask" return fi } run_symlink_escape() { local scenario_dir="$TEST_ROOT/symlink_escape" printf '==> symlink_escape\n' local secret_path="$ROOT_DIR/secrets.txt" touch "$secret_path" if "$SLOPTRAP_BIN" --dry-run "$scenario_dir" >/dev/null 2>&1; then record_failure "symlink_escape: expected failure for symlink escape" rm -f "$secret_path" return fi rm -f "$secret_path" } run_manifest_injection() { local scenario_dir="$TEST_ROOT/manifest_injection" printf '==> manifest_injection\n' if "$SLOPTRAP_BIN" --dry-run "$scenario_dir" >/dev/null 2>&1; then record_failure "manifest_injection: expected rejection of bad make override" return fi } run_helper_symlink() { local scenario_dir="$TEST_ROOT/helper_symlink" printf '==> helper_symlink\n' if "$SLOPTRAP_BIN" --dry-run "$scenario_dir" >/dev/null 2>&1; then record_failure "helper_symlink: expected rejection when helper directory is a symlink" fi if "$SLOPTRAP_BIN" "$scenario_dir" clean >/dev/null 2>&1; then record_failure "helper_symlink: expected rejection for clean when helper directory is a symlink" fi } run_secret_mask() { local scenario_dir="$TEST_ROOT/secret_mask" printf '==> secret_mask\n' setup_stub_env local custom_workdir="/alt-workspace" if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" \ FAKE_PODMAN_INSPECT_FAIL=1 SECRET_MASK_VERIFY=1 \ SECRET_MASK_EXPECTED_TARGET="${custom_workdir}/secret.txt" \ SLOPTRAP_WORKDIR="$custom_workdir" \ "$SLOPTRAP_BIN" "$scenario_dir" >/dev/null 2>&1; then record_failure "secret_mask: masking check failed" teardown_stub_env return fi teardown_stub_env } run_resume_target() { local scenario_dir="$TEST_ROOT/resume_target" printf '==> resume_target\n' setup_stub_env local session_id="019a81b7-32d2-7622-8639-6698c6579625" if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" \ "$SLOPTRAP_BIN" "$scenario_dir" resume "$session_id" >/dev/null 2>&1; then record_failure "resume_target: sloptrap exited non-zero" teardown_stub_env return fi if ! grep -q -- "resume $session_id" "$STUB_LOG"; then record_failure "resume_target: resume invocation missing" fi teardown_stub_env } run_codex_symlink_home() { local scenario_dir scenario_dir=$(cd "$TEST_ROOT/resume_target" && pwd -P) printf '==> codex_symlink_home\n' local tmp_home tmp_home=$(mktemp -d) ln -s /etc "$tmp_home/.codex" if HOME="$tmp_home" "$SLOPTRAP_BIN" --dry-run "$scenario_dir" >/dev/null 2>&1; then record_failure "codex_symlink_home: expected rejection when ~/.codex is a symlink" fi rm -rf "$tmp_home" } run_root_directory_project() { printf '==> root_directory_project\n' local tmp_home tmp_home=$(mktemp -d) if HOME="$tmp_home" "$SLOPTRAP_BIN" --dry-run / >/dev/null 2>&1; then record_failure "root_directory_project: expected rejection for '/' project root" fi rm -rf "$tmp_home" } run_shared_dir_override() { local scenario_dir scenario_dir=$(cd "$TEST_ROOT/resume_target" && pwd -P) printf '==> shared_dir_override\n' setup_stub_env local bogus_shared bogus_shared=$(mktemp -d) if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" \ SLOPTRAP_SHARED_DIR="$bogus_shared" FAKE_PODMAN_INSPECT_FAIL=1 \ "$SLOPTRAP_BIN" "$scenario_dir" >/dev/null 2>&1; then record_failure "shared_dir_override: sloptrap exited non-zero" teardown_stub_env rm -rf "$bogus_shared" return fi if grep -q "$bogus_shared" "$STUB_LOG"; then record_failure "shared_dir_override: respected SLOPTRAP_SHARED_DIR override" fi if ! grep -q -- "-v ${scenario_dir}:/workspace" "$STUB_LOG"; then record_failure "shared_dir_override: missing expected project bind mount" fi teardown_stub_env rm -rf "$bogus_shared" } run_packages_env_validation() { local scenario_dir scenario_dir=$(cd "$TEST_ROOT/resume_target" && pwd -P) printf '==> packages_env_validation\n' local tmp_home tmp_home=$(mktemp -d) if HOME="$tmp_home" SLOPTRAP_PACKAGES='curl";touch /tmp/pwn #' \ "$SLOPTRAP_BIN" --dry-run "$scenario_dir" >/dev/null 2>&1; then record_failure "packages_env_validation: expected rejection of invalid SLOPTRAP_PACKAGES" fi rm -rf "$tmp_home" } run_abs_path_ignore() { local scenario_dir="$TEST_ROOT/abs_path_ignore" printf '==> abs_path_ignore\n' if "$SLOPTRAP_BIN" --dry-run "$scenario_dir" >/dev/null 2>&1; then record_failure "abs_path_ignore: expected rejection for anchored parent traversal entry" fi } run_dotdot_ignore() { local scenario_dir="$TEST_ROOT/dotdot_ignore" printf '==> dotdot_ignore\n' if "$SLOPTRAP_BIN" --dry-run "$scenario_dir" >/dev/null 2>&1; then record_failure "dotdot_ignore: expected rejection for parent traversal entry" fi } run_invalid_manifest_name() { local scenario_dir="$TEST_ROOT/invalid_manifest_name" printf '==> invalid_manifest_name\n' if "$SLOPTRAP_BIN" --dry-run "$scenario_dir" >/dev/null 2>&1; then record_failure "invalid_manifest_name: expected rejection for illegal name" fi } run_invalid_manifest_sandbox() { local scenario_dir="$TEST_ROOT/invalid_manifest_sandbox" printf '==> invalid_manifest_sandbox\n' if "$SLOPTRAP_BIN" --dry-run "$scenario_dir" >/dev/null 2>&1; then record_failure "invalid_manifest_sandbox: expected rejection for sandbox mode" fi } run_invalid_manifest_packages() { local scenario_dir="$TEST_ROOT/invalid_manifest_packages" printf '==> invalid_manifest_packages\n' if "$SLOPTRAP_BIN" --dry-run "$scenario_dir" >/dev/null 2>&1; then record_failure "invalid_manifest_packages: expected rejection for bad packages" fi } run_invalid_allow_host_network() { local scenario_dir="$TEST_ROOT/invalid_allow_host_network" printf '==> invalid_allow_host_network\n' if "$SLOPTRAP_BIN" --dry-run "$scenario_dir" >/dev/null 2>&1; then record_failure "invalid_allow_host_network: expected rejection for invalid value" fi } run_shellcheck run_mount_injection run_root_target run_symlink_escape run_manifest_injection run_helper_symlink run_secret_mask run_resume_target run_codex_symlink_home run_root_directory_project run_shared_dir_override run_packages_env_validation run_abs_path_ignore run_dotdot_ignore run_invalid_manifest_name run_invalid_manifest_sandbox run_invalid_manifest_packages run_invalid_allow_host_network if [[ ${#failures[@]} -gt 0 ]]; then printf '\nTest failures:\n' for entry in "${failures[@]}"; do printf ' - %s\n' "$entry" done exit 1 fi printf '\nAll regression checks passed.\n'