v0.0.53: устойчивость к битым файлам — воркер не падает, битые пропускаются, остальные обрабатываются
Deploy drhider / validate (push) Canceled after 0s
Deploy drhider / validate (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
# 2026-08-20 — Устойчивость к битым файлам (v0.0.53)
|
||||||
|
|
||||||
|
## Root cause (найден по логу /tmp/drhider.log)
|
||||||
|
SSE "connection failed" при обработке НЕ был обрывом шлюза. Воркер падал целиком:
|
||||||
|
```
|
||||||
|
worker: exception: PdfminerException(PDFSyntaxError('No /Root object! - Is this really a PDF?'))
|
||||||
|
```
|
||||||
|
на битом PDF (миниатюра 142 байта). Исключение в `extract_text` не было обёрнуто
|
||||||
|
по-файлово → воркер ронялся → SSE слал `error` → фронт показывал "SSE connection failed".
|
||||||
|
|
||||||
|
## Фикс (drhider/obfuscator.py)
|
||||||
|
Проход 1: `extract_text` обёрнут в try/except. При ошибке файл добавляется в
|
||||||
|
`skipped`, логируется warning, файл пропускается, остальные обрабатываются.
|
||||||
|
Проход 2: файлы из `skipped` пропускаются (не попадают в результат).
|
||||||
|
|
||||||
|
Проверено на flat/ (10 файлов: 5 валидных + 5 битых миниатюр):
|
||||||
|
- битые PDF/docx пропущены (PDFSyntaxError/BadZipFile), валидные 5 → в zip.
|
||||||
|
- воркер не падает, complete доходит.
|
||||||
|
|
||||||
|
## Примечание
|
||||||
|
Миниатюры 54-142 B в /mnt/y/T/flat — НЕ валидные PDF/docx (мусор из корзины).
|
||||||
|
Их можно удалить из тестового набора (или оставить как кейс устойчивости).
|
||||||
+15
-1
@@ -114,6 +114,7 @@ class TwoPassObfuscator:
|
|||||||
all_texts: Dict[str, str] = {}
|
all_texts: Dict[str, str] = {}
|
||||||
total = len(files)
|
total = len(files)
|
||||||
file_times: List[float] = [0.0] * total
|
file_times: List[float] = [0.0] * total
|
||||||
|
skipped: set = set() # имена файлов, которые не удалось обработать (битые)
|
||||||
|
|
||||||
for i, (fname, content, ctype) in enumerate(files):
|
for i, (fname, content, ctype) in enumerate(files):
|
||||||
display_name = os.path.basename(fname) or fname
|
display_name = os.path.basename(fname) or fname
|
||||||
@@ -121,7 +122,14 @@ class TwoPassObfuscator:
|
|||||||
progress_cb("start", i, total, display_name, 0.0)
|
progress_cb("start", i, total, display_name, 0.0)
|
||||||
|
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
text = extractor.extract_text(fname, content, ctype)
|
try:
|
||||||
|
text = extractor.extract_text(fname, content, ctype)
|
||||||
|
except Exception as e:
|
||||||
|
log.warning("obfuscate: skip file %r: %r", fname, e)
|
||||||
|
skipped.add(fname)
|
||||||
|
if progress_cb:
|
||||||
|
progress_cb("done", i, total, display_name, 0.0)
|
||||||
|
continue
|
||||||
all_texts[fname] = text
|
all_texts[fname] = text
|
||||||
|
|
||||||
# Regex-сканирование (быстрое, локальное)
|
# Regex-сканирование (быстрое, локальное)
|
||||||
@@ -145,6 +153,12 @@ class TwoPassObfuscator:
|
|||||||
fname = in_fname
|
fname = in_fname
|
||||||
display_name = os.path.basename(in_fname) or in_fname
|
display_name = os.path.basename(in_fname) or in_fname
|
||||||
|
|
||||||
|
# Битый файл (не удалось извлечь текст) — пропускаем
|
||||||
|
if fname in skipped:
|
||||||
|
if progress_cb:
|
||||||
|
progress_cb("done", i, total, display_name, 0.0)
|
||||||
|
continue
|
||||||
|
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
obf_content = content # По умолчанию — без изменений
|
obf_content = content # По умолчанию — без изменений
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ if _sys_path_root not in sys.path:
|
|||||||
sys.path.insert(0, _sys_path_root)
|
sys.path.insert(0, _sys_path_root)
|
||||||
|
|
||||||
# Версия приложения (меняется при изменениях)
|
# Версия приложения (меняется при изменениях)
|
||||||
VERSION = "0.0.52"
|
VERSION = "0.0.53"
|
||||||
|
|
||||||
|
|
||||||
def setup_logging():
|
def setup_logging():
|
||||||
|
|||||||
Reference in New Issue
Block a user