Files
152-fz-scanner/scanner/checks/consent_doc.py
T
neo b9db935389 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)
2026-06-10 00:49:07 +05:00

77 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Проверка наличия отдельного документа «Согласие на обработку персональных данных».
С 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)}")