File: //remove_htaccess_hacked/remove_htaccess_strict_debug.sh
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
MODE="${1:-dry}" # dry | apply
BACKUP_EXT=".bak"
TMPDIR="$(mktemp -d)"
echo "Mode: $MODE"
echo "Temp dir: $TMPDIR"
# Perl program: legge dallo stdin il contenuto normalizzato, rimuove blocchi matching criteri,
# e stampa due righe diagnostiche al termine: "REMOVED:N" e il contenuto risultante.
# Questo permette allo script shell di capire se รจ cambiato qualcosa.
PERL_PROG='
use strict;
use warnings;
local $/ = undef;
my $orig = do { local $/; <STDIN> // "" };
$orig =~ s/\r\n?/\n/g;
my $s = $orig;
my $removed = 0;
# match all FilesMatch blocks (non-greedy)
# we capture the full match in $full, attributes in $attrs, inner in $inner
while ($s =~ m{(<FilesMatch\b([^>]*)>(.*?)</FilesMatch>)}gis) {
my $full = $1;
my $attrs = $2 // "";
my $inner = $3 // "";
my $test = lc($attrs . " " . $inner);
# Criterion short: contains .(py|exe|php or suspected) and "deny from all"
if ($test =~ qr/\.\(py\|exe\|php\|suspected/ && $test =~ /deny\s+from\s+all/) {
$s =~ s/\Q$full\E//si;
$removed++;
next;
}
# Criterion long: contains wp-blog-header.php and "allow from all"
if ($test =~ /wp-blog-header\.php/ && $test =~ /allow\s+from\s+all/) {
$s =~ s/\Q$full\E//si;
$removed++;
next;
}
# otherwise keep block
}
# collapse multiple blank lines and ensure final newline
$s =~ s/\n{3,}/\n\n/g;
$s .= "\n" unless $s =~ /\n\z/;
# print diagnostic header then content (caller will parse)
print "REMOVED:$removed\n";
print $s;
exit 0;
'
# find all .htaccess
mapfile -d '' FILES < <(find . -type f -name ".htaccess" -print0)
if [ "${#FILES[@]}" -eq 0 ]; then
echo "Nessun file .htaccess trovato."
rmdir "$TMPDIR"
exit 0
fi
echo "Trovati ${#FILES[@]} file .htaccess"
for f in "${FILES[@]}"; do
echo "---- Processing: $f ----"
out="$TMPDIR/$(basename "$f").out"
# pass file content to perl via stdin to avoid quoting problems
if ! perl -0777 -e "$PERL_PROG" < "$f" > "$out".tmp 2>&1; then
echo " ERROR: perl failed on $f (see $out.tmp for stderr/stdout)"
cat "$out".tmp | sed -n '1,200p'
rm -f "$out".tmp
continue
fi
# read first line REMOVED:N
head -n1 "$out".tmp > "$out.header"
removed=$(sed -n '1p' "$out".tmp | sed -n 's/^REMOVED:\([0-9]*\).*/\1/p')
# write the rest as new content file
tail -n +2 "$out".tmp > "$out.content"
rm -f "$out".tmp
if [ -z "$removed" ]; then
echo " DIAGNOSTIC ERROR: header REMOVED missing. Skipping."
rm -f "$out.header" "$out.content"
continue
fi
if [ "$removed" -eq 0 ]; then
echo " -> No matching FilesMatch blocks found (removed=0)."
rm -f "$out.header" "$out.content"
continue
fi
echo " -> Found and removed $removed block(s) in $f"
if [ "$MODE" = "dry" ]; then
echo " (dry) mostrando diff (prima 200 righe):"
diff -u --label "orig: $f" --label "new: $f.new" "$f" "$out.content" | sed -n '1,200p' || true
rm -f "$out.header" "$out.content"
continue
fi
# apply mode: write backup and replace
cp --preserve=mode,timestamps "$f" "$f${BACKUP_EXT}"
mv "$out.content" "$f"
echo " (apply) aggiornato: $f (backup: $f${BACKUP_EXT})"
rm -f "$out.header"
done
# cleanup
rmdir "$TMPDIR" 2>/dev/null || rm -rf "$TMPDIR"
echo "Completato."
exit 0