🏗 Фаза 1-4: Архитектура, ядро, проверки, API, генератор отчетов
- scanner/engine.py — Playwright-движок сканирования - scanner/models.py — Pydantic модели - scanner/checks/ — 5 проверок (cookie, privacy, forms, server, external) - api/server.py — FastAPI REST API - reports/generator.py — PDF через WeasyPrint + Jinja2 - main.py — CLI + сервер Проверки: ✅ cookie_banner — поиск cookie-баннера ✅ privacy_link — проверка ссылки на политику ✅ form_checkbox — проверка чекбоксов согласия ✅ server_location — геолокация сервера (RФ или нет) ✅ external_scripts — внешние скрипты аналитики
This commit is contained in:
commit
de18090304
20 files changed
+1665
No files matched your search
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
Проверка наличия cookie-баннера (уведомления о cookies).
|
||||
Согласно 152-ФЗ, сайт обязан получать согласие пользователя
|
||||
на обработку персональных данных, включая cookies.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from playwright.async_api import Page
|
||||
|
||||
from scanner.checks.base import BaseCheck
|
||||
from scanner.models import CheckResult, RiskLevel
|
||||
|
||||
|
||||
class CookieBannerCheck(BaseCheck):
|
||||
"""Проверяет наличие cookie-баннера на сайте."""
|
||||
|
||||
name = "cookie_banner"
|
||||
title = "Cookie-баннер"
|
||||
description = "Проверяет, отображается ли на сайте уведомление о cookie"
|
||||
risk_on_fail = RiskLevel.HIGH
|
||||
default_weight = 15
|
||||
|
||||
# Паттерны для поиска cookie-баннера
|
||||
COOKIE_PATTERNS = [
|
||||
r"cookie",
|
||||
r"куки",
|
||||
r"уведомление\s*о\s*cookie",
|
||||
r"согласие\s*на\s*использование\s*cookie",
|
||||
r"we\s*use\s*cookies",
|
||||
r"privacy\s*policy",
|
||||
r"данные\s*пользователя",
|
||||
r"обработка\s*персональных\s*данных",
|
||||
r"personal\s*data",
|
||||
r"consent",
|
||||
r"согласие",
|
||||
]
|
||||
|
||||
async def check(self, page: Page, url: str) -> CheckResult:
|
||||
try:
|
||||
# Получаем весь текст страницы (включая динамически загруженный)
|
||||
page_text = await page.evaluate("() => document.body.innerText")
|
||||
page_html = await page.evaluate("() => document.documentElement.outerHTML")
|
||||
|
||||
# Ищем cookie-баннер по видимым элементам
|
||||
cookie_elements = await page.query_selector_all(
|
||||
'[id*="cookie"], [class*="cookie"], [id*="consent"], [class*="consent"], [id*="privacy"], [class*="privacy"], [id*="policy"], [class*="policy"]'
|
||||
)
|
||||
|
||||
found_banner = False
|
||||
banner_texts = []
|
||||
|
||||
for el in cookie_elements:
|
||||
text = await el.inner_text()
|
||||
if text:
|
||||
banner_texts.append(text[:200])
|
||||
|
||||
# Также ищем по тексту всей страницы
|
||||
has_keyword = any(
|
||||
re.search(pattern, page_text, re.IGNORECASE | re.UNICODE)
|
||||
for pattern in self.COOKIE_PATTERNS
|
||||
)
|
||||
|
||||
if banner_texts:
|
||||
found_banner = True
|
||||
return self._pass_result(
|
||||
message=f"Найден cookie-баннер ({len(banner_texts)} элементов)",
|
||||
details={
|
||||
"banner_count": len(banner_texts),
|
||||
"sample_text": banner_texts[0][:100],
|
||||
},
|
||||
)
|
||||
|
||||
if has_keyword:
|
||||
return self._pass_result(
|
||||
message="На странице найдены упоминания cookie/согласия",
|
||||
details={"keyword_found": True},
|
||||
)
|
||||
|
||||
return self._fail_result(
|
||||
message="Cookie-баннер не найден. Сайт собирает данные без явного согласия.",
|
||||
details={
|
||||
"page_text_sample": page_text[:200],
|
||||
"search_patterns": self.COOKIE_PATTERNS,
|
||||
},
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return self._error_result(message=f"Ошибка при проверке cookie-баннера: {str(e)}")
|
||||
Reference in new issue
Block a user