v2.0: исправлены критические баги, добавлены 4 новые проверки, 4-фазный cookie-аудит, retry-логика, YAML-словари, LLM-анализ, тесты

Исправлено:
- engine: _calculate_score() при ошибке загрузки (score не 100 для мёртвых сайтов)
- privacy_link: замена page.goto() на aiohttp (гонка данных)
- form_checkbox: 3-уровневый поиск label + dark-pattern detection
- server_location: синхронный urllib → aiohttp
- api/server: перепутанные request/response модели в batch
- main.py: csv --checks флаг
- engine: фазовая модель (cookie_audit отдельно, остальные параллельно)
- engine: retry-логика (3 попытки, exponential backoff)
- PDF: emoji заменены на текст (WeasyPrint)

Добавлено:
- consent_doc: проверка отдельного согласия на обработку ПД
- operator_info: проверка ИНН/ОГРН/DPO (читает главную + политику)
- cookie_audit: 4-фазный аудит (load→detect→reject→compare)
- llm_policy: опциональный LLM-анализ 16 разделов (--deep)
- external_scripts: YAML-словари trackers.yaml (critical/warning/russian)
- logging (output.py), .gitignore, pyproject.toml
- smoke-тесты (16/16)
This commit is contained in:
neo committed 2026-06-10 00:49:07 +05:00
1 parent 7fb417dad5
commit b9db935389
24 files changed
+1725 -286

No files matched your search

+46
View File
@@ -0,0 +1,46 @@
"""
Вывод результатов: консоль + файл.
Прозрачная замена print() — всё что идёт в out(), дублируется в лог-файл.
"""
import sys
from pathlib import Path
from datetime import datetime
from typing import Any
class Output:
"""Дуплексный вывод: консоль + лог-файл."""
def __init__(self, log_dir: str | None = None):
if log_dir is None:
log_dir = Path(__file__).parent.parent / "logs"
Path(log_dir).mkdir(parents=True, exist_ok=True)
self._log_path = Path(log_dir) / f"scan_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
self._file = open(self._log_path, "w", encoding="utf-8")
def __call__(self, *args, **kwargs):
"""out('текст') — вывести на консоль и в лог."""
sep = kwargs.get("sep", " ")
end = kwargs.get("end", "\n")
text = sep.join(str(a) for a in args) + end
# Консоль
sys.stdout.write(text)
sys.stdout.flush()
# Файл (без emoji для читаемости)
clean = text.encode("ascii", errors="replace").decode("ascii")
self._file.write(clean)
self._file.flush()
def close(self):
self._file.close()
@property
def log_path(self) -> Path:
return self._log_path
# Глобальный экземпляр
out = Output()