""" Проверка наличия отдельного документа «Согласие на обработку персональных данных». С 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)}")