fix(validate): add 120s timeout to unzip integrity checks

unzip -tq can block indefinitely on very large or partially corrupt
ZIP archives (stalls in I/O rather than exiting with a CRC error).
All three scripts that call validate_recovered_file() are affected:
recover_, restore_, recreate_bad_signature.sh.

Both the quick check (unzip -tq) and the verbose error pass (unzip -t)
are now wrapped with `timeout 120`. Exit code 124 (timed out) is
reported as UNVERIFIED with a hint for manual follow-up; any other
non-zero exit is still reported as INVALID with the first error line.
This commit is contained in:
2026-09-16 00:44:02 +02:00
parent b33b859cf2
commit 6677f4f0c2
3 changed files with 30 additions and 6 deletions
+10 -2
View File
@@ -360,11 +360,19 @@ validate_recovered_file() {
if [[ "$_head8o" = "d0cf11e0a1b11ae1" ]] ; then if [[ "$_head8o" = "d0cf11e0a1b11ae1" ]] ; then
echo "VALID|OLE2/CFBF container signature ok - this is a password-protected Office file (encrypted package), not a plain zip, so the zip check does not apply; open it with the password to verify content" echo "VALID|OLE2/CFBF container signature ok - this is a password-protected Office file (encrypted package), not a plain zip, so the zip check does not apply; open it with the password to verify content"
elif command -v unzip > /dev/null 2>&1 ; then elif command -v unzip > /dev/null 2>&1 ; then
if unzip -tq "$_f" > /dev/null 2>&1 ; then # Use a 120-second timeout: unzip -tq on a very large or partially
# corrupt ZIP can block indefinitely otherwise (the process simply
# stalls in I/O, unlike a clean CRC error which exits quickly).
local _unzip_exit
timeout 120 unzip -tq "$_f" > /dev/null 2>&1
_unzip_exit=$?
if [[ $_unzip_exit -eq 0 ]] ; then
echo "VALID|zip integrity ok" echo "VALID|zip integrity ok"
elif [[ $_unzip_exit -eq 124 ]] ; then
echo "UNVERIFIED|zip integrity check timed out after 120 s (file may be very large or corrupt; check manually with: unzip -t \"$_f\")"
else else
local _badentry local _badentry
_badentry="$(trim "$(unzip -t "$_f" 2>&1 | grep -v '^Archive:' | grep -v '^[[:space:]]*$' | head -1)")" _badentry="$(trim "$(timeout 120 unzip -t "$_f" 2>&1 | grep -v '^Archive:' | grep -v '^[[:space:]]*$' | head -1)")"
echo "INVALID|zip integrity check failed${_badentry:+ (${_badentry})}" echo "INVALID|zip integrity check failed${_badentry:+ (${_badentry})}"
fi fi
else else
+10 -2
View File
@@ -386,11 +386,19 @@ validate_recovered_file() {
if [[ "$_head8o" = "d0cf11e0a1b11ae1" ]] ; then if [[ "$_head8o" = "d0cf11e0a1b11ae1" ]] ; then
echo "VALID|OLE2/CFBF container signature ok - this is a password-protected Office file (encrypted package), not a plain zip, so the zip check does not apply; open it with the password to verify content" echo "VALID|OLE2/CFBF container signature ok - this is a password-protected Office file (encrypted package), not a plain zip, so the zip check does not apply; open it with the password to verify content"
elif command -v unzip > /dev/null 2>&1 ; then elif command -v unzip > /dev/null 2>&1 ; then
if unzip -tq "$_f" > /dev/null 2>&1 ; then # Use a 120-second timeout: unzip -tq on a very large or partially
# corrupt ZIP can block indefinitely otherwise (the process simply
# stalls in I/O, unlike a clean CRC error which exits quickly).
local _unzip_exit
timeout 120 unzip -tq "$_f" > /dev/null 2>&1
_unzip_exit=$?
if [[ $_unzip_exit -eq 0 ]] ; then
echo "VALID|zip integrity ok" echo "VALID|zip integrity ok"
elif [[ $_unzip_exit -eq 124 ]] ; then
echo "UNVERIFIED|zip integrity check timed out after 120 s (file may be very large or corrupt; check manually with: unzip -t \"$_f\")"
else else
local _badentry local _badentry
_badentry="$(trim "$(unzip -t "$_f" 2>&1 | grep -v '^Archive:' | grep -v '^[[:space:]]*$' | head -1)")" _badentry="$(trim "$(timeout 120 unzip -t "$_f" 2>&1 | grep -v '^Archive:' | grep -v '^[[:space:]]*$' | head -1)")"
echo "INVALID|zip integrity check failed${_badentry:+ (${_badentry})}" echo "INVALID|zip integrity check failed${_badentry:+ (${_badentry})}"
fi fi
else else
+10 -2
View File
@@ -380,11 +380,19 @@ validate_recovered_file() {
if [[ "$_head8o" = "d0cf11e0a1b11ae1" ]] ; then if [[ "$_head8o" = "d0cf11e0a1b11ae1" ]] ; then
echo "VALID|OLE2/CFBF container signature ok - this is a password-protected Office file (encrypted package), not a plain zip, so the zip check does not apply; open it with the password to verify content" echo "VALID|OLE2/CFBF container signature ok - this is a password-protected Office file (encrypted package), not a plain zip, so the zip check does not apply; open it with the password to verify content"
elif command -v unzip > /dev/null 2>&1 ; then elif command -v unzip > /dev/null 2>&1 ; then
if unzip -tq "$_f" > /dev/null 2>&1 ; then # Use a 120-second timeout: unzip -tq on a very large or partially
# corrupt ZIP can block indefinitely otherwise (the process simply
# stalls in I/O, unlike a clean CRC error which exits quickly).
local _unzip_exit
timeout 120 unzip -tq "$_f" > /dev/null 2>&1
_unzip_exit=$?
if [[ $_unzip_exit -eq 0 ]] ; then
echo "VALID|zip integrity ok" echo "VALID|zip integrity ok"
elif [[ $_unzip_exit -eq 124 ]] ; then
echo "UNVERIFIED|zip integrity check timed out after 120 s (file may be very large or corrupt; check manually with: unzip -t \"$_f\")"
else else
local _badentry local _badentry
_badentry="$(trim "$(unzip -t "$_f" 2>&1 | grep -v '^Archive:' | grep -v '^[[:space:]]*$' | head -1)")" _badentry="$(trim "$(timeout 120 unzip -t "$_f" 2>&1 | grep -v '^Archive:' | grep -v '^[[:space:]]*$' | head -1)")"
echo "INVALID|zip integrity check failed${_badentry:+ (${_badentry})}" echo "INVALID|zip integrity check failed${_badentry:+ (${_badentry})}"
fi fi
else else