File: //remove_htaccess_hacked/remove_htaccess_debug.sh
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
# Verbose e tracing opzionale: imposta TRACE=1 per vedere ogni comando eseguito
TRACE="${TRACE:-0}"
if [ "$TRACE" -eq 1 ]; then
set -x
fi
MODE="${1:-dry}" # dry (default) | apply
LOGFILE="${2:-./remove_htaccess_debug.log}"
TMPDIR="$(mktemp -d)"
BLOCK_MATCH_SHORT="\\.(py|exe|php"
BLOCK_MATCH_LONG="wp-blog-header\\.php"
# Trap per errori non gestiti
trap 'rc=$?; echo "ERROR: script terminated with code $rc" | tee -a "$LOGFILE"; echo "Last 50 lines of log:" | tee -a "$LOGFILE"; tail -n 50 "$LOGFILE" || true; rm -rf "$TMPDIR"; exit $rc' ERR INT TERM
echo "[$(date --iso-8601=seconds)] Start script (mode=$MODE). Log: $LOGFILE" | tee "$LOGFILE"
echo "Temp dir: $TMPDIR" | tee -a "$LOGFILE"
# Funzione helper per stampare snippet con numeri di linea e hex
dump_snippet() {
local file="$1"; local start="$2"; local lines="${3:-20}"
echo "----- snippet from $file (line $start, $lines lines) -----" | tee -a "$LOGFILE"
sed -n "${start},$((start+lines-1))p" "$file" | sed -n '1,200p' | nl -ba -w3 -s': ' | tee -a "$LOGFILE"
echo "----- hexdump (first 256 bytes) -----" | tee -a "$LOGFILE"
head -c 256 "$file" | hexdump -C | tee -a "$LOGFILE"
echo "--------------------------------------" | tee -a "$LOGFILE"
}
# Perl program robusto che:
# - slurp file, normalizza CRLF -> LF
# - trova tutti i blocchi <FilesMatch ...>...</FilesMatch>
# - stampa su stdout il numero di blocchi rimossi (altrimenti 0)
PERL_PROGRAM='
use strict;
use warnings;
local $/ = undef;
my $bt = <>; # slurp file
$bt =~ s/\r\n?/\n/g;
my $orig = $bt;
my $removed = 0;
# regex per matchare blocchi <FilesMatch ...> ... </FilesMatch> in modo greedy non-greedy
while ($bt =~ m{<FilesMatch\b([^>]*)>(.*?)</FilesMatch>}gis) {
my $attrs = $1;
my $inner = $2;
my $test = lc($attrs . " " . $inner);
# criteri permissivi per il blocco corto o lungo
if ($test =~ qr/(\.\(py\|exe\|php|suspected)/i && $test =~ /deny from all/) {
$bt =~ s/\Q<FilesMatch$attrs>$inner</FilesMatch>\E//si;
$removed++;
next;
}
if ($test =~ /wp-blog-header\.php/ && $test =~ /allow from all/) {
$bt =~ s/\Q<FilesMatch$attrs>$inner</FilesMatch>\E//si;
$removed++;
next;
}
}
# pulizia di eventuali linee vuote multiple
$bt =~ s/\n{3,}/\n\n/g;
print $removed ? $bt : ""; # se non rimosso nulla non stampare nulla (caller usa exit status)
exit($removed ? 0 : 1);
'
# Trova file .htaccess (gestisce spazi e caratteri speciali)
mapfile -d '' FILES < <(find . -type f -name ".htaccess" -print0)
if [ "${#FILES[@]}" -eq 0 ]; then
echo "[$(date --iso-8601=seconds)] Nessun file .htaccess trovato." | tee -a "$LOGFILE"
rm -rf "$TMPDIR"
exit 0
fi
echo "[$(date --iso-8601=seconds)] Trovati ${#FILES[@]} file .htaccess" | tee -a "$LOGFILE"
modified_count=0
checked_count=0
for f in "${FILES[@]}"; do
checked_count=$((checked_count+1))
echo "Processing [$checked_count/${#FILES[@]}]: $f" | tee -a "$LOGFILE"
# quick checks per capire se il file contiene le substring attese (veloce)
if ! grep -Piq "$BLOCK_MATCH_SHORT|$BLOCK_MATCH_LONG" "$f"; then
echo " -> No quick pattern match (skip): non contiene '$BLOCK_MATCH_SHORT' né '$BLOCK_MATCH_LONG'." | tee -a "$LOGFILE"
continue
fi
# stampa prima righe per diagnostica
echo " -> Quick match passato: mostro prime 40 righe per controllo" | tee -a "$LOGFILE"
sed -n '1,40p' "$f" | nl -ba -w3 -s': ' | tee -a "$LOGFILE"
# lancia perl su file e cattura output/exit status
out_tmp="$TMPDIR/out.$$.$(basename "$f")"
if perl -0777 -e "$PERL_PROGRAM" "$f" > "$out_tmp" 2>>"$LOGFILE"; then
# perl ha rimosso almeno un blocco (exit 0 e output)
if cmp -s "$out_tmp" "$f"; then
echo " -> Perl ha detto che ha rimosso ma file identico: possibile problema encoding. Mostro snippet." | tee -a "$LOGFILE"
dump_snippet "$f" 1 80
rm -f "$out_tmp"
continue
fi
if [ "$MODE" = "dry" ]; then
echo " -> DRY RUN: il file verrebbe modificato (blocco rimosso). Verificare differenze (diff):" | tee -a "$LOGFILE"
diff -u "$f" "$out_tmp" | sed -n '1,200p' | tee -a "$LOGFILE"
modified_count=$((modified_count+1))
rm -f "$out_tmp"
continue
fi
# apply: backup e mv
cp --preserve=mode,timestamps "$f" "$f.bak"
mv "$out_tmp" "$f"
echo " -> APPLY: aggiornato. Backup creato: $f.bak" | tee -a "$LOGFILE"
modified_count=$((modified_count+1))
else
# perl ha restituito exit 1 => nessuna modifica, oppure errore; controlliamo il return
rc=$?
if [ "$rc" -eq 1 ]; then
echo " -> Perl: nessuna modifica fatta (exit 1). Probabilmente non c'era il blocco esatto." | tee -a "$LOGFILE"
else
echo " -> Perl terminato con codice $rc. Controlla $LOGFILE per errori di Perl." | tee -a "$LOGFILE"
# mostra le ultime 100 righe di log per capire
tail -n 100 "$LOGFILE" | sed -n '1,200p' | tee -a "$LOGFILE"
fi
rm -f "$out_tmp"
fi
done
echo "[$(date --iso-8601=seconds)] Controllati: $checked_count. Modificati: $modified_count." | tee -a "$LOGFILE"
echo "Temp dir: $TMPDIR (rimosso)" | tee -a "$LOGFILE"
rm -rf "$TMPDIR"
echo "Fine." | tee -a "$LOGFILE"
exit 0