- remove codex auth mounts from opencode run/shell paths - reject opencode login and invalid backend values - harden opencode config writes against symlink clobbering - fix opencode build args and packages_extra handling - enforce cap-drop and read-only rootfs in runtime commands - reject dangerous runtime/build env overrides - update README and test docs to match actual behavior - extend regression coverage for backend safety and hardening
1070 lines
36 KiB
Bash
Executable File
1070 lines
36 KiB
Bash
Executable File
#!/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=()
|
|
|
|
can_run_script_pty() {
|
|
if ! command -v script >/dev/null 2>&1; then
|
|
return 1
|
|
fi
|
|
if ! script -q -c "true" /dev/null >/dev/null 2>&1; then
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
maybe_create_helper_pidfile() {
|
|
local -a args=("$@")
|
|
local codex_source=""
|
|
local helper_dir=""
|
|
local idx=0
|
|
while (( idx < ${#args[@]} )); do
|
|
local arg=${args[$idx]}
|
|
case "$arg" in
|
|
-v)
|
|
idx=$((idx + 1))
|
|
if (( idx < ${#args[@]} )); then
|
|
local spec=${args[$idx]}
|
|
case "$spec" in
|
|
*:/codex|*:/codex:* )
|
|
codex_source=${spec%%:/codex*}
|
|
;;
|
|
esac
|
|
fi
|
|
;;
|
|
-e)
|
|
idx=$((idx + 1))
|
|
if (( idx < ${#args[@]} )); then
|
|
local envspec=${args[$idx]}
|
|
case "$envspec" in
|
|
SLOPTRAP_HELPER_DIR=*)
|
|
helper_dir=${envspec#SLOPTRAP_HELPER_DIR=}
|
|
;;
|
|
esac
|
|
fi
|
|
;;
|
|
esac
|
|
idx=$((idx + 1))
|
|
done
|
|
if [[ -z $codex_source || $helper_dir != /codex/* ]]; then
|
|
return 0
|
|
fi
|
|
local helper_host=${codex_source}/${helper_dir#/codex/}
|
|
mkdir -p "$helper_host"
|
|
printf '12345\n' >"$helper_host/helperd.pid"
|
|
}
|
|
|
|
if [[ ${1-} == "image" && ${2-} == "inspect" && ${FAKE_PODMAN_INSPECT_FAIL:-0} == 1 ]]; then
|
|
if [[ " $* " == *" --format "* ]]; then
|
|
printf 'fake-image-id\n'
|
|
exit 0
|
|
fi
|
|
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
|
|
|
|
if [[ ${1-} == "run" ]]; then
|
|
maybe_create_helper_pidfile "$@"
|
|
fi
|
|
|
|
echo "FAKE PODMAN: $*" >>"$FAKE_PODMAN_LOG"
|
|
exit 0
|
|
EOF
|
|
chmod +x "$STUB_BIN/podman"
|
|
cp "$STUB_BIN/podman" "$STUB_BIN/docker"
|
|
chmod +x "$STUB_BIN/docker"
|
|
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
|
|
if [[ ${1-} == "-n" ]]; then
|
|
shift
|
|
exec /usr/bin/jq -n "$@"
|
|
fi
|
|
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
|
|
if [[ -x /usr/bin/sha256sum ]]; then
|
|
exec /usr/bin/sha256sum "$@"
|
|
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:-}"
|
|
}
|
|
|
|
extract_embedded_helper() {
|
|
local helper=$1
|
|
local output=$2
|
|
if ! awk -v helper="$helper" '
|
|
$0 ~ "^[[:space:]]*" helper "\\)$" { state=1; next }
|
|
state == 1 && /cat <<'\''EOF'\''$/ { state=2; next }
|
|
state == 2 && /^EOF$/ { exit }
|
|
state == 2 { print }
|
|
' "$SLOPTRAP_BIN" >"$output"; then
|
|
return 1
|
|
fi
|
|
chmod +x "$output"
|
|
}
|
|
|
|
wait_for_path() {
|
|
local path=$1
|
|
local attempts=${2:-100}
|
|
while (( attempts > 0 )); do
|
|
if [[ -e $path ]]; then
|
|
return 0
|
|
fi
|
|
sleep 0.1
|
|
((attempts-=1))
|
|
done
|
|
return 1
|
|
}
|
|
|
|
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 >/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 >/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 >/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 >/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 >/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 >/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 >/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 >/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_runtime_context_prompt() {
|
|
local scenario_dir="$TEST_ROOT/host_network_repo"
|
|
printf '==> runtime_context_prompt\n'
|
|
setup_stub_env
|
|
if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" "$scenario_dir" </dev/null >/dev/null 2>&1; then
|
|
record_failure "runtime_context_prompt: sloptrap exited non-zero"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
local login_line run_line
|
|
login_line=$(grep "FAKE PODMAN: run " "$STUB_LOG" | head -n 1 || true)
|
|
run_line=$(grep "FAKE PODMAN: run " "$STUB_LOG" | tail -n 1 || true)
|
|
if [[ -z $run_line || $run_line != *"You are running inside sloptrap"* ]]; then
|
|
record_failure "runtime_context_prompt: startup prompt missing from fresh run"
|
|
fi
|
|
if ! grep -q -- "name=host-network-repo" "$STUB_LOG" \
|
|
|| ! grep -q -- "network_mode=host" "$STUB_LOG"; then
|
|
record_failure "runtime_context_prompt: runtime summary missing manifest state"
|
|
fi
|
|
if [[ -n $login_line && $login_line == *"You are running inside sloptrap"* ]]; then
|
|
record_failure "runtime_context_prompt: login flow should not receive startup prompt"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
run_sh_reexec() {
|
|
local scenario_dir="$TEST_ROOT/host_network_repo"
|
|
printf '==> sh_reexec\n'
|
|
setup_stub_env
|
|
if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
sh "$SLOPTRAP_BIN" "$scenario_dir" </dev/null >/dev/null 2>&1; then
|
|
record_failure "sh_reexec: sloptrap exited non-zero when launched via sh"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
if ! grep -q -- "You are running inside sloptrap" "$STUB_LOG"; then
|
|
record_failure "sh_reexec: startup prompt missing after sh re-exec"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
run_resume_omits_runtime_context() {
|
|
local scenario_dir="$TEST_ROOT/host_network_repo"
|
|
local session_id="019a81b7-32d2-7622-8639-6698c6579625"
|
|
printf '==> resume_omits_runtime_context\n'
|
|
setup_stub_env
|
|
if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" "$scenario_dir" resume "$session_id" </dev/null >/dev/null 2>&1; then
|
|
record_failure "resume_omits_runtime_context: sloptrap exited non-zero"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
if grep -q -- "You are running inside sloptrap" "$STUB_LOG"; then
|
|
record_failure "resume_omits_runtime_context: resume should not receive startup prompt"
|
|
fi
|
|
if ! grep -q -- "--sandbox danger-full-access --ask-for-approval never resume $session_id" "$STUB_LOG"; then
|
|
record_failure "resume_omits_runtime_context: resume invocation missing"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
run_shell_target_uses_entrypoint() {
|
|
local scenario_dir="$TEST_ROOT/opencode_localhost"
|
|
printf '==> shell_target_uses_entrypoint\n'
|
|
setup_stub_env
|
|
if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" "$scenario_dir" shell </dev/null >/dev/null 2>&1; then
|
|
record_failure "shell_target_uses_entrypoint: shell target failed"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
if ! grep -q -- "--entrypoint /bin/bash" "$STUB_LOG"; then
|
|
record_failure "shell_target_uses_entrypoint: missing entrypoint override"
|
|
fi
|
|
if grep -q -- "/codex/auth.json" "$STUB_LOG"; then
|
|
record_failure "shell_target_uses_entrypoint: codex auth mount should not be present for opencode shell"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
run_auth_file_mount() {
|
|
local scenario_dir
|
|
scenario_dir=$(cd "$TEST_ROOT/resume_target" && pwd -P)
|
|
printf '==> auth_file_mount\n'
|
|
setup_stub_env
|
|
mkdir -p "$STUB_HOME/.codex"
|
|
printf '{"access_token":"test"}\n' >"$STUB_HOME/.codex/auth.json"
|
|
if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" "$scenario_dir" </dev/null >/dev/null 2>&1; then
|
|
record_failure "auth_file_mount: sloptrap exited non-zero"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
if ! grep -q -- "-v ${STUB_HOME}/.codex/auth.json:/codex/auth.json:Z,ro" "$STUB_LOG"; then
|
|
record_failure "auth_file_mount: auth file should be mounted read-only for normal runs"
|
|
fi
|
|
if ! grep -q -- "-v ${STUB_HOME}/.codex/sloptrap/state/" "$STUB_LOG"; then
|
|
record_failure "auth_file_mount: missing project state bind mount"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
run_runtime_hardening_flags() {
|
|
local scenario_dir
|
|
scenario_dir=$(cd "$TEST_ROOT/resume_target" && pwd -P)
|
|
printf '==> runtime_hardening_flags\n'
|
|
setup_stub_env
|
|
mkdir -p "$STUB_HOME/.codex"
|
|
printf '{"access_token":"test"}\n' >"$STUB_HOME/.codex/auth.json"
|
|
if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" "$scenario_dir" </dev/null >/dev/null 2>&1; then
|
|
record_failure "runtime_hardening_flags: sloptrap exited non-zero"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
if ! grep -q -- "--cap-drop ALL" "$STUB_LOG"; then
|
|
record_failure "runtime_hardening_flags: cap-drop flag missing"
|
|
fi
|
|
if ! grep -q -- "--read-only" "$STUB_LOG"; then
|
|
record_failure "runtime_hardening_flags: read-only rootfs flag missing"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
run_codex_home_override() {
|
|
local scenario_dir codex_root
|
|
scenario_dir=$(cd "$TEST_ROOT/resume_target" && pwd -P)
|
|
printf '==> codex_home_override\n'
|
|
setup_stub_env
|
|
codex_root="$STUB_HOME/codex-root"
|
|
mkdir -p "$codex_root"
|
|
printf '{"access_token":"test"}\n' >"$codex_root/auth.json"
|
|
if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" CODEX_HOME="$codex_root" SLOPTRAP_PREFER_CODEX_HOME=1 \
|
|
FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" "$scenario_dir" </dev/null >/dev/null 2>&1; then
|
|
record_failure "codex_home_override: sloptrap exited non-zero"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
if ! grep -q -- "-v ${codex_root}/auth.json:/codex/auth.json:Z,ro" "$STUB_LOG"; then
|
|
record_failure "codex_home_override: CODEX_HOME auth file should be mounted read-only for normal runs"
|
|
fi
|
|
if ! grep -q -- "-v ${codex_root}/sloptrap/state/" "$STUB_LOG"; then
|
|
record_failure "codex_home_override: missing CODEX_HOME project state bind mount"
|
|
fi
|
|
if grep -q -- "-v ${STUB_HOME}/.codex/auth.json:/codex/auth.json:Z" "$STUB_LOG"; then
|
|
record_failure "codex_home_override: should not fall back to HOME/.codex when CODEX_HOME is set"
|
|
fi
|
|
local first_run
|
|
first_run=$(grep "FAKE PODMAN: run " "$STUB_LOG" | head -n 1 || true)
|
|
if [[ -z $first_run || $first_run == *" login" ]]; then
|
|
record_failure "codex_home_override: existing CODEX_HOME auth should avoid login target"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
|
|
run_project_state_isolation() {
|
|
local scenario_a scenario_b
|
|
scenario_a=$(cd "$TEST_ROOT/resume_target" && pwd -P)
|
|
scenario_b=$(cd "$TEST_ROOT/secret_mask" && pwd -P)
|
|
printf '==> project_state_isolation\n'
|
|
setup_stub_env
|
|
mkdir -p "$STUB_HOME/.codex"
|
|
printf '{"access_token":"test"}\n' >"$STUB_HOME/.codex/auth.json"
|
|
if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" "$scenario_a" </dev/null >/dev/null 2>&1; then
|
|
record_failure "project_state_isolation: first project run failed"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" \
|
|
"$SLOPTRAP_BIN" "$scenario_b" </dev/null >/dev/null 2>&1; then
|
|
record_failure "project_state_isolation: second project run failed"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
local -a codex_mounts=()
|
|
mapfile -t codex_mounts < <(
|
|
{
|
|
grep "FAKE PODMAN: run " "$STUB_LOG" \
|
|
| grep -oE -- "-v [^ ]+:/codex:Z" \
|
|
| sed -e 's/^-v //' -e 's/:\/codex:Z$//'
|
|
} || true
|
|
)
|
|
if [[ ${#codex_mounts[@]} -lt 2 ]]; then
|
|
record_failure "project_state_isolation: missing /codex state mounts"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
if [[ ${codex_mounts[0]} == "${codex_mounts[1]}" ]]; then
|
|
record_failure "project_state_isolation: projects reused same Codex state mount"
|
|
fi
|
|
if [[ ${codex_mounts[0]} != */.codex/sloptrap/state/* || ${codex_mounts[1]} != */.codex/sloptrap/state/* ]]; then
|
|
record_failure "project_state_isolation: state mounts did not use sloptrap namespace"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
run_auto_login_empty_auth() {
|
|
local scenario_dir
|
|
scenario_dir=$(cd "$TEST_ROOT/resume_target" && pwd -P)
|
|
printf '==> auto_login_empty_auth\n'
|
|
setup_stub_env
|
|
mkdir -p "$STUB_HOME/.codex"
|
|
: >"$STUB_HOME/.codex/auth.json"
|
|
if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" "$scenario_dir" </dev/null >/dev/null 2>&1; then
|
|
record_failure "auto_login_empty_auth: sloptrap exited non-zero"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
local first_run
|
|
first_run=$(grep "FAKE PODMAN: run " "$STUB_LOG" | head -n 1 || true)
|
|
if [[ -z $first_run || $first_run != *" login" ]]; then
|
|
record_failure "auto_login_empty_auth: expected login before primary run"
|
|
fi
|
|
if [[ -z $first_run || $first_run != *"-v ${STUB_HOME}/.codex/auth.json:/codex/auth.json:Z "* ]]; then
|
|
record_failure "auto_login_empty_auth: login target should keep auth file writable"
|
|
fi
|
|
if ! grep -q -- "-v ${STUB_HOME}/.codex/auth.json:/codex/auth.json:Z,ro" "$STUB_LOG"; then
|
|
record_failure "auto_login_empty_auth: post-login runtime should remount auth file read-only"
|
|
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 >/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 >/dev/null 2>&1; then
|
|
record_failure "root_directory_project: expected rejection for '/' project root"
|
|
fi
|
|
rm -rf "$tmp_home"
|
|
}
|
|
|
|
|
|
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 >/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 >/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 >/dev/null 2>&1; then
|
|
record_failure "invalid_manifest_packages: expected rejection for bad packages"
|
|
fi
|
|
}
|
|
|
|
run_invalid_manifest_agent() {
|
|
printf '==> invalid_manifest_agent\n'
|
|
local scenario_dir
|
|
scenario_dir=$(mktemp -d)
|
|
cat >"$scenario_dir/.sloptrap" <<'EOF'
|
|
name=invalid-agent
|
|
agent=bogus
|
|
EOF
|
|
if "$SLOPTRAP_BIN" --dry-run "$scenario_dir" </dev/null >/dev/null 2>&1; then
|
|
record_failure "invalid_manifest_agent: expected rejection for invalid agent"
|
|
fi
|
|
rm -rf "$scenario_dir"
|
|
}
|
|
|
|
run_invalid_agent_env_override() {
|
|
local scenario_dir="$TEST_ROOT/opencode_print_config"
|
|
printf '==> invalid_agent_env_override\n'
|
|
if SLOPTRAP_AGENT=bogus "$SLOPTRAP_BIN" --dry-run "$scenario_dir" </dev/null >/dev/null 2>&1; then
|
|
record_failure "invalid_agent_env_override: expected rejection for invalid SLOPTRAP_AGENT"
|
|
fi
|
|
}
|
|
|
|
run_removed_runtime_override_envs() {
|
|
local scenario_dir="$TEST_ROOT/resume_target"
|
|
printf '==> removed_runtime_override_envs\n'
|
|
if SLOPTRAP_SECURITY_OPTS_EXTRA='--privileged' "$SLOPTRAP_BIN" --dry-run "$scenario_dir" </dev/null >/dev/null 2>&1; then
|
|
record_failure "removed_runtime_override_envs: expected rejection for SLOPTRAP_SECURITY_OPTS_EXTRA"
|
|
fi
|
|
if SLOPTRAP_ROOTFS_READONLY=0 "$SLOPTRAP_BIN" --dry-run "$scenario_dir" </dev/null >/dev/null 2>&1; then
|
|
record_failure "removed_runtime_override_envs: expected rejection for SLOPTRAP_ROOTFS_READONLY"
|
|
fi
|
|
if SLOPTRAP_NETWORK_NAME=host "$SLOPTRAP_BIN" --dry-run "$scenario_dir" </dev/null >/dev/null 2>&1; then
|
|
record_failure "removed_runtime_override_envs: expected rejection for SLOPTRAP_NETWORK_NAME"
|
|
fi
|
|
}
|
|
|
|
run_removed_build_override_envs() {
|
|
local scenario_dir="$TEST_ROOT/resume_target"
|
|
printf '==> removed_build_override_envs\n'
|
|
if SLOPTRAP_DOCKERFILE_PATH=/etc/passwd "$SLOPTRAP_BIN" --dry-run "$scenario_dir" build </dev/null >/dev/null 2>&1; then
|
|
record_failure "removed_build_override_envs: expected rejection for SLOPTRAP_DOCKERFILE_PATH"
|
|
fi
|
|
if SLOPTRAP_CODEX_URL=https://example.invalid/codex.tgz "$SLOPTRAP_BIN" --dry-run "$scenario_dir" build </dev/null >/dev/null 2>&1; then
|
|
record_failure "removed_build_override_envs: expected rejection for SLOPTRAP_CODEX_URL"
|
|
fi
|
|
if SLOPTRAP_CODEX_ARCHIVE=codex-custom "$SLOPTRAP_BIN" --dry-run "$scenario_dir" build </dev/null >/dev/null 2>&1; then
|
|
record_failure "removed_build_override_envs: expected rejection for SLOPTRAP_CODEX_ARCHIVE"
|
|
fi
|
|
if SLOPTRAP_CODEX_BIN=custom-codex "$SLOPTRAP_BIN" --dry-run "$scenario_dir" build </dev/null >/dev/null 2>&1; then
|
|
record_failure "removed_build_override_envs: expected rejection for SLOPTRAP_CODEX_BIN"
|
|
fi
|
|
}
|
|
|
|
run_wizard_create_manifest() {
|
|
local scenario_dir="$TEST_ROOT/wizard_empty"
|
|
printf '==> wizard_create_manifest\n'
|
|
if ! can_run_script_pty; then
|
|
printf 'skipping wizard_create_manifest: script PTY support not available\n'
|
|
return
|
|
fi
|
|
rm -f "$scenario_dir/.sloptrap"
|
|
# Wizard now has: name, packages_extra, agent (codex), allow_host_network
|
|
# Use empty for name (default), empty for packages_extra, empty for agent (uses default), false for allow_host_network
|
|
if ! printf '\n\n\nfalse\n\n' | "$SLOPTRAP_BIN" "$scenario_dir" wizard >/dev/null 2>&1; then
|
|
record_failure "wizard_create_manifest: wizard failed"
|
|
return
|
|
fi
|
|
if [[ ! -f $scenario_dir/.sloptrap ]]; then
|
|
record_failure "wizard_create_manifest: manifest not created"
|
|
return
|
|
fi
|
|
if ! grep -qx "name=wizard_empty" "$scenario_dir/.sloptrap"; then
|
|
record_failure "wizard_create_manifest: name default mismatch"
|
|
fi
|
|
if ! grep -qx "packages_extra=" "$scenario_dir/.sloptrap"; then
|
|
record_failure "wizard_create_manifest: packages_extra mismatch"
|
|
fi
|
|
if ! grep -qx "agent=codex" "$scenario_dir/.sloptrap"; then
|
|
record_failure "wizard_create_manifest: agent mismatch"
|
|
fi
|
|
if ! grep -qx "allow_host_network=false" "$scenario_dir/.sloptrap"; then
|
|
record_failure "wizard_create_manifest: allow_host_network mismatch"
|
|
fi
|
|
}
|
|
|
|
run_wizard_existing_defaults() {
|
|
local scenario_dir="$TEST_ROOT/wizard_existing"
|
|
printf '==> wizard_existing_defaults\n'
|
|
if ! can_run_script_pty; then
|
|
printf 'skipping wizard_existing_defaults: script PTY support not available\n'
|
|
return
|
|
fi
|
|
# Create initial manifest with custom-wizard name
|
|
cat > "$scenario_dir/.sloptrap" <<EOF
|
|
name=custom-wizard
|
|
packages_extra=make git
|
|
agent=codex
|
|
allow_host_network=true
|
|
EOF
|
|
# Wizard now has: name, packages_extra, agent (codex), allow_host_network
|
|
# Use empty for name (default), make git for packages_extra, empty for agent (uses default), false for allow_host_network
|
|
if ! printf '\nmake git\n\n\nfalse\n\n' | "$SLOPTRAP_BIN" "$scenario_dir" wizard >/dev/null 2>&1; then
|
|
record_failure "wizard_existing_defaults: wizard failed"
|
|
return
|
|
fi
|
|
if ! grep -qx "name=custom-wizard" "$scenario_dir/.sloptrap"; then
|
|
record_failure "wizard_existing_defaults: name not preserved"
|
|
fi
|
|
if ! grep -qx "packages_extra=make git" "$scenario_dir/.sloptrap"; then
|
|
record_failure "wizard_existing_defaults: packages_extra not preserved"
|
|
fi
|
|
if ! grep -qx "agent=codex" "$scenario_dir/.sloptrap"; then
|
|
record_failure "wizard_existing_defaults: agent not preserved"
|
|
fi
|
|
if ! grep -qx "allow_host_network=true" "$scenario_dir/.sloptrap"; then
|
|
record_failure "wizard_existing_defaults: allow_host_network not preserved"
|
|
fi
|
|
}
|
|
|
|
run_wizard_build_trigger() {
|
|
local scenario_dir="$TEST_ROOT/wizard_build"
|
|
printf '==> wizard_build_trigger\n'
|
|
if ! can_run_script_pty; then
|
|
printf 'skipping wizard_build_trigger: script PTY support not available\n'
|
|
return
|
|
fi
|
|
setup_stub_env
|
|
rm -f "$scenario_dir/.sloptrap"
|
|
# Wizard now has: name, packages_extra, agent (codex), allow_host_network
|
|
# Use empty for name (default), empty for packages_extra, empty for agent (uses default), false for allow_host_network
|
|
if ! env PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
printf '\n\n\nfalse\n\n' | "$SLOPTRAP_BIN" "$scenario_dir" wizard >/dev/null 2>&1; then
|
|
record_failure "wizard_build_trigger: wizard failed"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
if [[ ! -f $scenario_dir/.sloptrap ]]; then
|
|
record_failure "wizard_build_trigger: manifest not created"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
# Run build to trigger image build
|
|
if ! env PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" "$scenario_dir" build >/dev/null 2>&1; then
|
|
record_failure "wizard_build_trigger: build failed"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
if ! grep -q -- "FAKE PODMAN: build " "$STUB_LOG"; then
|
|
record_failure "wizard_build_trigger: build not invoked"
|
|
fi
|
|
}
|
|
|
|
run_opencode_build_downloads_release_cli() {
|
|
local scenario_dir="$TEST_ROOT/opencode_build"
|
|
printf '==> opencode_build_downloads_release_cli\n'
|
|
setup_stub_env
|
|
mkdir -p "$scenario_dir"
|
|
cat > "$scenario_dir/.sloptrap" <<'EOF'
|
|
name=opencode-build
|
|
packages_extra=htop
|
|
agent=opencode
|
|
allow_host_network=false
|
|
EOF
|
|
if ! env PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" "$scenario_dir" build >/dev/null 2>&1; then
|
|
record_failure "opencode_build_downloads_release_cli: build failed"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
if ! grep -q -- "FAKE PODMAN: build " "$STUB_LOG"; then
|
|
record_failure "opencode_build_downloads_release_cli: build not invoked"
|
|
fi
|
|
if ! grep -q -- "--build-arg OPENCODE_BIN=opencode" "$STUB_LOG"; then
|
|
record_failure "opencode_build_downloads_release_cli: default OPENCODE_BIN build arg missing"
|
|
fi
|
|
if ! grep -q -- "--build-arg EXTRA_PACKAGES=htop" "$STUB_LOG"; then
|
|
record_failure "opencode_build_downloads_release_cli: EXTRA_PACKAGES build arg missing"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
run_opencode_localhost_rewrite() {
|
|
local scenario_dir="$TEST_ROOT/opencode_localhost"
|
|
printf '==> opencode_localhost_rewrite\n'
|
|
setup_stub_env
|
|
mkdir -p "$scenario_dir"
|
|
cat > "$scenario_dir/.sloptrap" <<'EOF'
|
|
name=opencode-localhost
|
|
packages_extra=
|
|
agent=opencode
|
|
opencode_server=http://localhost:8080
|
|
allow_host_network=false
|
|
EOF
|
|
cat > "$STUB_BIN/slirp4netns" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
exit 0
|
|
EOF
|
|
chmod +x "$STUB_BIN/slirp4netns"
|
|
if ! env PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" "$scenario_dir" </dev/null >/dev/null 2>&1; then
|
|
record_failure "opencode_localhost_rewrite: run failed"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
local config_path
|
|
config_path=$(find "$STUB_HOME" -path '*/config/opencode/opencode.json' | head -n 1 || true)
|
|
if [[ -z $config_path || ! -f $config_path ]]; then
|
|
record_failure "opencode_localhost_rewrite: opencode config not written"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
if ! grep -q -- "--network slirp4netns:allow_host_loopback=true" "$STUB_LOG"; then
|
|
record_failure "opencode_localhost_rewrite: slirp host loopback not enabled"
|
|
fi
|
|
if ! grep -q -- "--add-host sloptrap.host:host-gateway" "$STUB_LOG"; then
|
|
record_failure "opencode_localhost_rewrite: host alias not injected"
|
|
fi
|
|
if ! grep -q -- "OPENCODE_CONFIG=/codex/config/opencode/opencode.json" "$STUB_LOG"; then
|
|
record_failure "opencode_localhost_rewrite: opencode config path not exported"
|
|
fi
|
|
if grep -q -- "/codex/auth.json" "$STUB_LOG"; then
|
|
record_failure "opencode_localhost_rewrite: codex auth mount should not be present for opencode"
|
|
fi
|
|
if ! grep -q -- '"baseURL": "http://sloptrap.host:8080/v1"' "$config_path"; then
|
|
record_failure "opencode_localhost_rewrite: localhost server not rewritten in config"
|
|
fi
|
|
if [[ $(jq -r '.provider["llama.cpp"].name' "$config_path") != "llama-server (local)" ]]; then
|
|
record_failure "opencode_localhost_rewrite: provider name not merged into config"
|
|
fi
|
|
if [[ $(jq -r '.model' "$config_path") != "llama.cpp/bartowski/Qwen_Qwen3.5-9B-GGUF:Q8_0 - 262144" ]]; then
|
|
record_failure "opencode_localhost_rewrite: opencode model not written to config"
|
|
fi
|
|
if [[ $(jq -r '.enabled_providers[0]' "$config_path") != "llama.cpp" ]]; then
|
|
record_failure "opencode_localhost_rewrite: enabled_providers not merged into config"
|
|
fi
|
|
if [[ $(jq -r '.provider["llama.cpp"].models["bartowski/Qwen_Qwen3.5-9B-GGUF:Q8_0 - 262144"].limit.context' "$config_path") != "262144" ]]; then
|
|
record_failure "opencode_localhost_rewrite: opencode context not written to config"
|
|
fi
|
|
if grep -q -- "--server " "$STUB_LOG"; then
|
|
record_failure "opencode_localhost_rewrite: deprecated --server flag should not be passed"
|
|
fi
|
|
if grep -q -- "--sandbox workspace-write" "$STUB_LOG"; then
|
|
record_failure "opencode_localhost_rewrite: codex sandbox args leaked into opencode run"
|
|
fi
|
|
if grep -q -- "You are running inside sloptrap" "$STUB_LOG"; then
|
|
record_failure "opencode_localhost_rewrite: codex-style startup prompt should not be passed to opencode"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
run_opencode_print_config_runtime_flags() {
|
|
local scenario_dir="$TEST_ROOT/opencode_print_config"
|
|
printf '==> opencode_print_config_runtime_flags\n'
|
|
setup_stub_env
|
|
mkdir -p "$scenario_dir"
|
|
cat > "$scenario_dir/.sloptrap" <<'EOF'
|
|
name=opencode-print-config
|
|
packages_extra=
|
|
agent=opencode
|
|
allow_host_network=false
|
|
EOF
|
|
local output
|
|
if ! output=$(env PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" --print-config "$scenario_dir" 2>/dev/null); then
|
|
record_failure "opencode_print_config_runtime_flags: print-config failed"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
if ! grep -q 'runtime_flags=' <<<"$output"; then
|
|
record_failure "opencode_print_config_runtime_flags: runtime_flags line missing for opencode"
|
|
fi
|
|
if ! grep -q 'opencode_config=' <<<"$output"; then
|
|
record_failure "opencode_print_config_runtime_flags: opencode config path missing"
|
|
fi
|
|
if grep -q -- '--sandbox danger-full-access --ask-for-approval never' <<<"$output"; then
|
|
record_failure "opencode_print_config_runtime_flags: codex runtime flags leaked into opencode config"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
run_opencode_env_overrides() {
|
|
local scenario_dir="$TEST_ROOT/opencode_print_config"
|
|
printf '==> opencode_env_overrides\n'
|
|
setup_stub_env
|
|
mkdir -p "$scenario_dir"
|
|
cat > "$scenario_dir/.sloptrap" <<'EOF'
|
|
name=opencode-print-config
|
|
packages_extra=
|
|
agent=opencode
|
|
opencode_server=http://manifest:8080
|
|
opencode_model=manifest-model
|
|
opencode_context=128K
|
|
allow_host_network=false
|
|
EOF
|
|
local output
|
|
local plain_output
|
|
if ! output=$(env PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
SLOPTRAP_OPENCODE_SERVER=http://env:8080 SLOPTRAP_OPENCODE_MODEL=env-model SLOPTRAP_OPENCODE_CONTEXT=64K \
|
|
"$SLOPTRAP_BIN" --print-config "$scenario_dir" 2>/dev/null); then
|
|
record_failure "opencode_env_overrides: print-config failed"
|
|
teardown_stub_env
|
|
return
|
|
fi
|
|
plain_output=$(printf '%s' "$output" | sed -E $'s/\x1B\\[[0-9;]*m//g')
|
|
if ! grep -q 'opencode_server=http://env:8080' <<<"$plain_output"; then
|
|
record_failure "opencode_env_overrides: server env override missing"
|
|
fi
|
|
if ! grep -q 'opencode_model=env-model' <<<"$plain_output"; then
|
|
record_failure "opencode_env_overrides: model env override missing"
|
|
fi
|
|
if ! grep -q 'opencode_context=64K' <<<"$plain_output"; then
|
|
record_failure "opencode_env_overrides: context env override missing"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
run_opencode_config_symlink_rejected() {
|
|
local scenario_dir="$TEST_ROOT/opencode_localhost"
|
|
printf '==> opencode_config_symlink_rejected\n'
|
|
setup_stub_env
|
|
mkdir -p "$scenario_dir"
|
|
cat > "$scenario_dir/.sloptrap" <<'EOF'
|
|
name=opencode-localhost
|
|
packages_extra=
|
|
agent=opencode
|
|
allow_host_network=false
|
|
EOF
|
|
local state_key
|
|
state_key=$(printf '%s' "$scenario_dir" | sha256sum | awk '{print $1}')
|
|
local config_dir="$STUB_HOME/.codex/sloptrap/state/$state_key/config/opencode"
|
|
mkdir -p "$config_dir"
|
|
ln -s "$STUB_HOME/target.json" "$config_dir/opencode.json"
|
|
if env PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" "$scenario_dir" </dev/null >/dev/null 2>&1; then
|
|
record_failure "opencode_config_symlink_rejected: expected rejection for symlinked config"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
run_opencode_login_rejected() {
|
|
local scenario_dir="$TEST_ROOT/opencode_localhost"
|
|
printf '==> opencode_login_rejected\n'
|
|
setup_stub_env
|
|
if env PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
|
|
"$SLOPTRAP_BIN" "$scenario_dir" login </dev/null >/dev/null 2>&1; then
|
|
record_failure "opencode_login_rejected: expected login rejection for opencode"
|
|
fi
|
|
teardown_stub_env
|
|
}
|
|
|
|
run_symlink_escape
|
|
run_manifest_injection
|
|
run_helper_symlink
|
|
run_secret_mask
|
|
run_resume_target
|
|
run_runtime_context_prompt
|
|
run_sh_reexec
|
|
run_resume_omits_runtime_context
|
|
run_shell_target_uses_entrypoint
|
|
run_auth_file_mount
|
|
run_runtime_hardening_flags
|
|
run_codex_home_override
|
|
run_project_state_isolation
|
|
run_auto_login_empty_auth
|
|
run_codex_symlink_home
|
|
run_root_directory_project
|
|
run_wizard_create_manifest
|
|
run_wizard_existing_defaults
|
|
run_wizard_build_trigger
|
|
run_invalid_manifest_agent
|
|
run_invalid_agent_env_override
|
|
run_removed_runtime_override_envs
|
|
run_removed_build_override_envs
|
|
run_opencode_build_downloads_release_cli
|
|
run_opencode_localhost_rewrite
|
|
run_opencode_print_config_runtime_flags
|
|
run_opencode_env_overrides
|
|
run_opencode_config_symlink_rejected
|
|
run_opencode_login_rejected
|
|
|
|
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'
|