From fe7c238c0b00b5af2f7fa7798843c91ecfb1cb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Thu, 20 Aug 2026 08:30:30 +0300 Subject: [PATCH] =?UTF-8?q?v0.0.53:=20=D1=83=D1=81=D1=82=D0=BE=D0=B9=D1=87?= =?UTF-8?q?=D0=B8=D0=B2=D0=BE=D1=81=D1=82=D1=8C=20=D0=BA=20=D0=B1=D0=B8?= =?UTF-8?q?=D1=82=D1=8B=D0=BC=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0=D0=BC=20?= =?UTF-8?q?=E2=80=94=20=D0=B2=D0=BE=D1=80=D0=BA=D0=B5=D1=80=20=D0=BD=D0=B5?= =?UTF-8?q?=20=D0=BF=D0=B0=D0=B4=D0=B0=D0=B5=D1=82,=20=D0=B1=D0=B8=D1=82?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=BF=D1=80=D0=BE=D0=BF=D1=83=D1=81=D0=BA=D0=B0?= =?UTF-8?q?=D1=8E=D1=82=D1=81=D1=8F,=20=D0=BE=D1=81=D1=82=D0=B0=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=B0?= =?UTF-8?q?=D1=82=D1=8B=D0=B2=D0=B0=D1=8E=D1=82=D1=81=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- History/2026-08-20-broken-files.md | 22 ++++++++++++++++++++++ drhider/obfuscator.py | 16 +++++++++++++++- site/app.py | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 History/2026-08-20-broken-files.md diff --git a/History/2026-08-20-broken-files.md b/History/2026-08-20-broken-files.md new file mode 100644 index 0000000..6b89304 --- /dev/null +++ b/History/2026-08-20-broken-files.md @@ -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 (мусор из корзины). +Их можно удалить из тестового набора (или оставить как кейс устойчивости). diff --git a/drhider/obfuscator.py b/drhider/obfuscator.py index b8ce424..8f51c89 100644 --- a/drhider/obfuscator.py +++ b/drhider/obfuscator.py @@ -114,6 +114,7 @@ class TwoPassObfuscator: all_texts: Dict[str, str] = {} total = len(files) file_times: List[float] = [0.0] * total + skipped: set = set() # имена файлов, которые не удалось обработать (битые) for i, (fname, content, ctype) in enumerate(files): display_name = os.path.basename(fname) or fname @@ -121,7 +122,14 @@ class TwoPassObfuscator: progress_cb("start", i, total, display_name, 0.0) 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 # Regex-сканирование (быстрое, локальное) @@ -145,6 +153,12 @@ class TwoPassObfuscator: fname = 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() obf_content = content # По умолчанию — без изменений diff --git a/site/app.py b/site/app.py index 2722fa9..e9be552 100644 --- a/site/app.py +++ b/site/app.py @@ -21,7 +21,7 @@ if _sys_path_root not in sys.path: sys.path.insert(0, _sys_path_root) # Версия приложения (меняется при изменениях) -VERSION = "0.0.52" +VERSION = "0.0.53" def setup_logging():