#!/bin/bash set -euo pipefail PLUGIN_DIR="$(cd "$(dirname "$0")/.." && pwd)" REAL_SCRIPT="$PLUGIN_DIR/scripts/da-integration/alt_mysql_user_restore_post_pre_cleanup.sh" TMP_DIR="$(mktemp -d)" trap 'rm -rf "$TMP_DIR" "/tmp/alt-mysql-traversal-proof"' EXIT fail() { echo "FAIL: $*" >&2 exit 1 } # Extract the exact, real find_payload_in_dir()/extract_payload_from_archive() # function bodies from the real script (rather than reimplementing them), so # this test exercises the actual production logic without triggering the # script's own main() (which needs a full DA restore environment). FUNCS_FILE="$TMP_DIR/funcs.sh" { echo '#!/bin/bash' echo 'log() { :; }' awk '/^find_payload_in_dir\(\)/,/^\}/' "$REAL_SCRIPT" awk '/^extract_payload_from_archive\(\)/,/^\}/' "$REAL_SCRIPT" } > "$FUNCS_FILE" grep -q '^find_payload_in_dir()' "$FUNCS_FILE" || fail "could not extract find_payload_in_dir() from $REAL_SCRIPT" grep -q '^extract_payload_from_archive()' "$FUNCS_FILE" || fail "could not extract extract_payload_from_archive() from $REAL_SCRIPT" # shellcheck source=/dev/null source "$FUNCS_FILE" # --- A legitimate archive must still extract normally --- GOOD_STAGE="$TMP_DIR/good_stage" mkdir -p "$GOOD_STAGE/backup/alt_mysql/databases" printf 'x' > "$GOOD_STAGE/backup/alt_mysql/manifest.json" printf 'x' > "$GOOD_STAGE/backup/alt_mysql/databases/demo_db.sql.gz" GOOD_ARCHIVE="$TMP_DIR/good.tar" ( cd "$GOOD_STAGE" && tar -cf "$GOOD_ARCHIVE" backup/alt_mysql/manifest.json backup/alt_mysql/databases/demo_db.sql.gz ) GOOD_PAYLOAD="$(extract_payload_from_archive "$GOOD_ARCHIVE")" \ || fail "a legitimate archive with a valid manifest must be extracted" [ -r "$GOOD_PAYLOAD/manifest.json" ] || fail "extracted payload must contain manifest.json" # --- An archive containing a path-traversal member must be refused entirely --- EVIL_STAGE="$TMP_DIR/evil_stage" mkdir -p "$EVIL_STAGE/backup/alt_mysql" "$EVIL_STAGE/evil_payload" printf 'x' > "$EVIL_STAGE/backup/alt_mysql/manifest.json" printf 'malicious content' > "$EVIL_STAGE/evil_payload/evil.sh" EVIL_ARCHIVE="$TMP_DIR/evil.tar" ( cd "$EVIL_STAGE" && { tar -cf "$EVIL_ARCHIVE" -s ',^evil_payload/,../../../../../../tmp/alt-mysql-traversal-proof/,' \ backup/alt_mysql/manifest.json evil_payload/evil.sh 2>/dev/null \ || tar -cf "$EVIL_ARCHIVE" --transform 's,^evil_payload/,../../../../../../tmp/alt-mysql-traversal-proof/,' \ backup/alt_mysql/manifest.json evil_payload/evil.sh } ) tar -tf "$EVIL_ARCHIVE" | grep -Fq '../../../../../../tmp/alt-mysql-traversal-proof/evil.sh' \ || fail "test setup broken: the crafted archive does not actually contain a traversal member" EVIL_OUTPUT="" if EVIL_OUTPUT="$(extract_payload_from_archive "$EVIL_ARCHIVE" 2>&1)"; then fail "an archive containing a path-traversal member must be refused, got payload: $EVIL_OUTPUT" fi [ ! -e "/tmp/alt-mysql-traversal-proof/evil.sh" ] \ || fail "SECURITY: path-traversal member was actually extracted outside the temp dir" echo "da_restore_archive_traversal_test: OK"