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:
1 parent
7fb417dad5
commit
b9db935389
24 files changed
+1725
-286
No files matched your search
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
Проверка наличия отдельного документа «Согласие на обработку персональных данных».
|
||||
С 01.09.2025 согласие — отдельный документ (нельзя включать в оферту/договор).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from playwright.async_api import Page
|
||||
|
||||
from scanner.checks.base import BaseCheck
|
||||
from scanner.models import CheckResult, RiskLevel
|
||||
|
||||
|
||||
class ConsentDocCheck(BaseCheck):
|
||||
"""Проверяет наличие отдельной страницы/документа согласия на обработку ПД."""
|
||||
|
||||
name = "consent_doc"
|
||||
title = "Документ согласия на обработку ПД"
|
||||
description = "Проверяет, есть ли на сайте отдельный документ согласия (не в составе оферты)"
|
||||
risk_on_fail = RiskLevel.CRITICAL
|
||||
default_weight = 20
|
||||
|
||||
CONSENT_LINK_PATTERNS = [
|
||||
r"согласи[ея]\s*на\s*обработку\s*(персональных)?\s*данных",
|
||||
r"согласи[ея]\s*на\s*обработку\s*пд",
|
||||
r"consent\s*(to|for)\s*(personal\s*)?data\s*processing",
|
||||
r"согласи[ея]\s*субъекта",
|
||||
]
|
||||
|
||||
async def check(self, page: Page, url: str) -> CheckResult:
|
||||
try:
|
||||
links = await page.query_selector_all("a[href]")
|
||||
found = []
|
||||
|
||||
for link in links:
|
||||
href = await link.get_attribute("href") or ""
|
||||
text = (await link.inner_text()).strip()
|
||||
combined = (text + " " + href).lower()
|
||||
|
||||
if not text or len(text) < 3:
|
||||
continue
|
||||
|
||||
if any(re.search(p, combined, re.UNICODE | re.IGNORECASE) for p in self.CONSENT_LINK_PATTERNS):
|
||||
# Не учитываем ссылки на политику конфиденциальности
|
||||
if re.search(r"политик[аеу]|privacy\s*policy|конфиденциальн", combined, re.IGNORECASE):
|
||||
continue
|
||||
# Не учитываем текст внутри чекбоксов форм (это проверяет form_checkbox)
|
||||
full_url = urljoin(url, href)
|
||||
found.append({"text": text[:100], "url": full_url})
|
||||
|
||||
if not found:
|
||||
return self._fail_result(
|
||||
message=(
|
||||
"Отдельный документ «Согласие на обработку ПД» не найден. "
|
||||
"С 01.09.2025 согласие — отдельный документ, не может быть частью оферты."
|
||||
),
|
||||
details={"total_links": len(links)},
|
||||
)
|
||||
|
||||
# Проверяем, что хотя бы одна ссылка ведёт на отдельную страницу (не PDF)
|
||||
for item in found:
|
||||
if not item["url"].lower().endswith(('.pdf', '.doc', '.docx')):
|
||||
return self._pass_result(
|
||||
message=f"Найден документ согласия: {item['text'][:80]}",
|
||||
details={"found_links": found},
|
||||
)
|
||||
|
||||
return self._pass_result(
|
||||
message=f"Найден документ согласия (PDF/DOC): {found[0]['text'][:80]}",
|
||||
details={"found_links": found, "format": "document"},
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return self._error_result(f"Ошибка проверки согласия: {str(e)}")
|
||||
Reference in new issue
Block a user