alpha
This commit is contained in:
12
tests/README.md
Normal file
12
tests/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Test Scenarios
|
||||
|
||||
This directory contains cases that stress Sloptrap's hardening and deployment flow. Each subdirectory mimics a user repository and focuses on a single class of behaviour. Use `run_tests.sh` to execute the automated checks with stubbed tooling.
|
||||
|
||||
Current scenarios:
|
||||
|
||||
- `mount_injection/` — exercises `.sloptrapignore` entries with `,` and `=` to ensure mount escape characters remain escaped and forces `build_if_missing` to execute the Codex download/build path.
|
||||
- `root_target/` — ensures attempts to mask the project root are rejected.
|
||||
- `symlink_escape/` — confirms symlink targets resolving outside the project are blocked.
|
||||
- `manifest_injection/` — ensures disallowed `make.*` overrides abort parsing.
|
||||
- `helper_symlink/` — ensures `.sloptrap-ignores` cannot be a symlink to directories outside the project.
|
||||
- `secret_mask/` — verifies masked files remain hidden even when Sloptrap remaps the workspace mount.
|
||||
1
tests/helper_symlink/.sloptrap-ignores
Symbolic link
1
tests/helper_symlink/.sloptrap-ignores
Symbolic link
@@ -0,0 +1 @@
|
||||
../..
|
||||
1
tests/helper_symlink/.sloptrapignore
Normal file
1
tests/helper_symlink/.sloptrapignore
Normal file
@@ -0,0 +1 @@
|
||||
secrets/
|
||||
1
tests/manifest_injection/.sloptrap
Normal file
1
tests/manifest_injection/.sloptrap
Normal file
@@ -0,0 +1 @@
|
||||
make.BAD-FLAG=1
|
||||
1
tests/mount_injection/.gitignore
vendored
Normal file
1
tests/mount_injection/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.sloptrap-ignores
|
||||
1
tests/mount_injection/.sloptrapignore
Normal file
1
tests/mount_injection/.sloptrapignore
Normal file
@@ -0,0 +1 @@
|
||||
attack,source=/etc/passwd
|
||||
1
tests/mount_injection/attack,source=/etc/passwd
Normal file
1
tests/mount_injection/attack,source=/etc/passwd
Normal file
@@ -0,0 +1 @@
|
||||
fake passwd content
|
||||
1
tests/root_target/.sloptrapignore
Normal file
1
tests/root_target/.sloptrapignore
Normal file
@@ -0,0 +1 @@
|
||||
.
|
||||
308
tests/run_tests.sh
Executable file
308
tests/run_tests.sh
Executable file
@@ -0,0 +1,308 @@
|
||||
#!/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_shellcheck
|
||||
run_mount_injection
|
||||
run_root_target
|
||||
run_symlink_escape
|
||||
run_manifest_injection
|
||||
run_helper_symlink
|
||||
run_secret_mask
|
||||
|
||||
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'
|
||||
1
tests/secret_mask/.sloptrapignore
Normal file
1
tests/secret_mask/.sloptrapignore
Normal file
@@ -0,0 +1 @@
|
||||
secret.txt
|
||||
1
tests/secret_mask/secret.txt
Normal file
1
tests/secret_mask/secret.txt
Normal file
@@ -0,0 +1 @@
|
||||
super-secret-token
|
||||
1
tests/symlink_escape/.sloptrapignore
Normal file
1
tests/symlink_escape/.sloptrapignore
Normal file
@@ -0,0 +1 @@
|
||||
cheat/secrets.txt
|
||||
1
tests/symlink_escape/cheat
Symbolic link
1
tests/symlink_escape/cheat
Symbolic link
@@ -0,0 +1 @@
|
||||
../..
|
||||
Reference in New Issue
Block a user