#!/usr/bin/env bats # Security-Tests fuer darktable-sync load helpers/setup COMMON_SCRIPT="$BATS_TEST_DIRNAME/../scripts/darktable_common.sh" SYNC_SCRIPT="$BATS_TEST_DIRNAME/../scripts/darktable_sync.sh" WRAPPER_SCRIPT="$BATS_TEST_DIRNAME/../scripts/darktable_wrapper.sh" setup() { create_valid_env mkdir -p "$HOME/.config/darktable" touch "$HOME/.config/darktable/library.db" touch "$HOME/.config/darktable/data.db" mkdir -p "$HOME/Pictures" touch "$HOME/Pictures/test.jpg" export DISPLAY=:99 } # --- K1: .env Code-Injection wird geblockt --- @test "security: .env mit Semikolon wird abgelehnt" { cat > "$CONFIG_DIR/.env" <<'EOF' SERVER_IP=192.168.1.100 SERVER_USER=testuser SERVER_SSH_PORT=22 SERVER_DB_DIR=/remote/db SERVER_PHOTO_DIR=/remote/photos LOCAL_DARKTABLE_DB_DIR=/tmp/dt_test LOCAL_PHOTO_DIR=/tmp/photos_test DARKTABLE_BIN=darktable SYNC_BIN=/usr/local/bin/darktable_sync.sh INJECTION_MARKER=injected; touch /tmp/dt_security_test_marker EOF run bash -c "source '$COMMON_SCRIPT'; load_config; echo done" rm -f /tmp/dt_security_test_marker [ "$status" -eq 1 ] [[ "$output" == *"unerlaubte Zeichen"* ]] } @test "security: .env mit Backtick wird abgelehnt" { cat > "$CONFIG_DIR/.env" <<'EOF' SERVER_IP=192.168.1.100 SERVER_USER=testuser SERVER_SSH_PORT=22 SERVER_DB_DIR=/remote/db SERVER_PHOTO_DIR=/remote/photos LOCAL_DARKTABLE_DB_DIR=/tmp/dt_test LOCAL_PHOTO_DIR=/tmp/photos_test DARKTABLE_BIN=darktable SYNC_BIN=/usr/local/bin/darktable_sync.sh EVIL=`touch /tmp/evil` EOF run bash -c "source '$COMMON_SCRIPT'; load_config; echo done" [ "$status" -eq 1 ] [[ "$output" == *"unerlaubte Zeichen"* ]] } # --- K2: validate_path blockt SSH-Injection --- @test "security: SERVER_DB_DIR mit Single-Quote wird geblockt" { create_valid_env # Wert in Double-Quotes damit bash ihn fehlerfrei laedt, validate_path muss dann blockieren printf 'SERVER_DB_DIR="/remote/db'"'"'injection"\n' >> "$CONFIG_DIR/.env" run bash -c "source '$COMMON_SCRIPT'; load_config; validate_config" [ "$status" -eq 1 ] [[ "$output" == *"SERVER_DB_DIR"* ]] } @test "security: SERVER_DB_DIR mit Path-Traversal wird geblockt" { create_valid_env echo 'SERVER_DB_DIR=/../../../etc' >> "$CONFIG_DIR/.env" run bash -c "source '$COMMON_SCRIPT'; load_config; validate_config" [ "$status" -eq 1 ] [[ "$output" == *"SERVER_DB_DIR"* ]] } # --- H1: Atomares Locking mit mkdir --- @test "security: gleichzeitiger Sync wird durch Lockdir geblockt" { mkdir -p "$CONFIG_DIR/sync.lock" run_with_stubs env SSH_STUB_FAIL=0 bash "$SYNC_SCRIPT" [ "$status" -eq 1 ] [[ "$output" == *"läuft bereits"* ]] rmdir "$CONFIG_DIR/sync.lock" } # --- H2: Lockdir nicht durch Symlink angreifbar --- @test "security: Lockdir ist kein Symlink-Angriffspunkt" { # mkdir schlaegt bei existierendem Symlink fehl – kein Ziel wird geloescht TARGET="$BATS_TMPDIR/symlink_target" echo "wichtiger Inhalt" > "$TARGET" ln -sf "$TARGET" "$CONFIG_DIR/sync.lock" run_with_stubs env SSH_STUB_FAIL=0 bash "$SYNC_SCRIPT" # Script muss fehlschlagen (Symlink statt echtes Verzeichnis = mkdir schlaegt fehl) [ "$status" -eq 1 ] # Zieldatei darf nicht geloescht worden sein [ -f "$TARGET" ] rm -f "$CONFIG_DIR/sync.lock" "$TARGET" } # --- H3: DARKTABLE_BIN muss 'darktable' sein --- @test "security: DARKTABLE_BIN mit anderem basename wird geblockt" { create_valid_env echo "DARKTABLE_BIN=/usr/bin/evil_binary" >> "$CONFIG_DIR/.env" run bash -c "source '$COMMON_SCRIPT'; load_config; validate_config" [ "$status" -eq 1 ] [[ "$output" == *"DARKTABLE_BIN"* ]] } # --- M2: .env-Berechtigungen werden gewarnt --- @test "security: .env mit world-readable Berechtigungen loest Warnung aus" { create_valid_env chmod 644 "$CONFIG_DIR/.env" run bash -c "source '$COMMON_SCRIPT'; load_config; echo \$SERVER_IP" [ "$status" -eq 0 ] [[ "$output" == *"world-readable"* ]] } # --- validate_config: fehlende Variablen --- @test "security: validate_config blockt leere SERVER_IP" { create_valid_env echo "SERVER_IP=" >> "$CONFIG_DIR/.env" run bash -c "source '$COMMON_SCRIPT'; load_config; validate_config" [ "$status" -eq 1 ] [[ "$output" == *"SERVER_IP"* ]] } @test "security: validate_config blockt fehlende SERVER_DB_DIR" { create_valid_env sed -i '/^SERVER_DB_DIR/d' "$CONFIG_DIR/.env" run bash -c "source '$COMMON_SCRIPT'; load_config; validate_config" [ "$status" -eq 1 ] [[ "$output" == *"SERVER_DB_DIR"* ]] } # --- Lockdir Cleanup --- @test "security: Lockdir wird bei normalem Exit entfernt" { run_with_stubs env SSH_STUB_FAIL=0 bash "$SYNC_SCRIPT" [ "$status" -eq 0 ] [ ! -d "$CONFIG_DIR/sync.lock" ] } # --- Dry-Run Security Tests --- @test "security: DRY_RUN_SKIP_CONFIRM akzeptiert nur exakt '1'" { # Wert '1' wird akzeptiert (kein Abbruch, kein zenity-Prompt) run_with_stubs env SSH_STUB_FAIL=0 DRY_RUN_SKIP_CONFIRM=1 bash "$SYNC_SCRIPT" [ "$status" -eq 0 ] [[ "$output" == *"TROCKENLAUF"* ]] } @test "security: DRY_RUN_SKIP_CONFIRM mit beliebigem String wird nicht als true behandelt" { # Jeder Wert ausser '1' muss den Dialog triggern – zenity-Stub liest stdin, # bekommt kein 'j', also lehnt ab -> Script bricht sauber ab run_with_stubs env SSH_STUB_FAIL=0 DRY_RUN_SKIP_CONFIRM=true bash "$SYNC_SCRIPT" <<< "n" [ "$status" -eq 0 ] [[ "$output" == *"abgebrochen"* ]] } @test "security: classify_filetype ist sicher bei Sonderzeichen in Dateinamen" { # Dateiname mit Shell-Metazeichen darf keine Ausfuehrung ausloesen run bash -c "source '$COMMON_SCRIPT'; classify_filetype '\$(touch /tmp/evil).jpg'" [ "$status" -eq 0 ] [ "$output" = "Foto" ] [ ! -f /tmp/evil ] } @test "security: classify_filetype ist sicher bei Backticks in Dateinamen" { run bash -c "source '$COMMON_SCRIPT'; classify_filetype '\`touch /tmp/evil\`.jpg'" [ "$status" -eq 0 ] [ "$output" = "Foto" ] [ ! -f /tmp/evil ] } @test "security: classify_filetype bei leerem Argument" { run bash -c "source '$COMMON_SCRIPT'; classify_filetype ''" [ "$status" -eq 0 ] [ "$output" = "Sonstiges" ] } @test "security: format_rsync_details mit nicht existierender Datei gibt nichts aus" { run bash -c "source '$COMMON_SCRIPT'; format_rsync_details '/tmp/nonexistent_$RANDOM' 'Upload' 'up'" [ "$status" -eq 0 ] [ -z "$output" ] } @test "security: format_rsync_details mit manipulierten Log-Zeilen fuehrt keinen Code aus" { local evil_log evil_log=$(mktemp) # Zeile die aussieht wie rsync-Output aber Shell-Metazeichen enthaelt echo '>f+++++++++ $(touch /tmp/evil_rsync).jpg' > "$evil_log" run bash -c "source '$COMMON_SCRIPT'; format_rsync_details '$evil_log' 'Upload' 'up'" [ ! -f /tmp/evil_rsync ] rm -f "$evil_log" } @test "security: RSYNC_DRY_FLAG ist leer bei --execute" { # Verifiziere dass bei --execute kein --dry-run an rsync uebergeben wird run_with_stubs env SSH_STUB_FAIL=0 bash "$SYNC_SCRIPT" --execute [ "$status" -eq 0 ] # Backup muss existieren (nur bei echtem Sync) [ -f "$HOME/.config/darktable/library.db.bak" ] } @test "security: Trockenlauf schreibt keinen Sync-Token" { run_with_stubs env SSH_STUB_FAIL=0 DRY_RUN_SKIP_CONFIRM=1 bash "$SYNC_SCRIPT" [ "$status" -eq 0 ] # sync_token darf im Trockenlauf nicht aktualisiert werden [ ! -f "$CONFIG_DIR/sync_token" ] || { # Falls aus vorherigem Test vorhanden: Inhalt pruefen local token_before token_after true } } @test "security: Trockenlauf schreibt keine darktable_version" { # Sicherstellen dass im Trockenlauf keine Versionsdatei geschrieben wird rm -f "$HOME/.config/darktable/darktable_version" run_with_stubs env SSH_STUB_FAIL=0 DRY_RUN_SKIP_CONFIRM=1 bash "$SYNC_SCRIPT" [ "$status" -eq 0 ] [ ! -f "$HOME/.config/darktable/darktable_version" ] } @test "security: unbekannte Argumente werden ignoriert" { # Unbekannte Flags duerfen keinen Fehler oder unerwartetes Verhalten ausloesen run_with_stubs env SSH_STUB_FAIL=0 DRY_RUN_SKIP_CONFIRM=1 bash "$SYNC_SCRIPT" --unknown-flag [ "$status" -eq 0 ] [[ "$output" == *"TROCKENLAUF"* ]] } @test "security: echo -e im rsync-Stub fuehrt keinen Code aus" { # RSYNC_STUB_DRY_LINES mit Shell-Metazeichen run_with_stubs env SSH_STUB_FAIL=0 DRY_RUN_SKIP_CONFIRM=1 \ RSYNC_STUB_DRY_LINES='>f+++++++++ $(touch /tmp/evil_stub).jpg' bash "$SYNC_SCRIPT" [ "$status" -eq 0 ] [ ! -f /tmp/evil_stub ] } # --- Backup-Pfad Security --- @test "security: LOCAL_PHOTO_DIR mit Path-Traversal wird geblockt" { create_valid_env echo "LOCAL_PHOTO_DIR=/home/user/../../../etc/photos" >> "$CONFIG_DIR/.env" run bash -c "source '$COMMON_SCRIPT'; load_config; validate_config" [ "$status" -eq 1 ] [[ "$output" == *"LOCAL_PHOTO_DIR"* ]] } @test "security: LOCAL_DARKTABLE_DB_DIR mit Path-Traversal wird geblockt" { create_valid_env echo "LOCAL_DARKTABLE_DB_DIR=/home/user/../../../etc/dt" >> "$CONFIG_DIR/.env" run bash -c "source '$COMMON_SCRIPT'; load_config; validate_config" [ "$status" -eq 1 ] [[ "$output" == *"LOCAL_DARKTABLE_DB_DIR"* ]] } @test "security: LOCAL_PHOTO_DIR mit Single-Quote wird geblockt" { create_valid_env printf 'LOCAL_PHOTO_DIR="/home/user/pics'"'"'injection"\n' >> "$CONFIG_DIR/.env" run bash -c "source '$COMMON_SCRIPT'; load_config; validate_config" [ "$status" -eq 1 ] [[ "$output" == *"LOCAL_PHOTO_DIR"* ]] }