🏗 Фаза 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,117 @@
|
||||
"""
|
||||
Проверка наличия чекбокса согласия на обработку персональных данных.
|
||||
Согласно 152-ФЗ, обработка данных допустима только при наличии согласия субъекта.
|
||||
Формы на сайте (обратная связь, регистрация, подписка и т.д.)
|
||||
должны содержать явное согласие пользователя.
|
||||
"""
|
||||
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 FormCheckboxCheck(BaseCheck):
|
||||
"""Проверяет наличие чекбокса согласия в формах на сайте."""
|
||||
|
||||
name = "form_checkbox"
|
||||
title = "Чекбокс согласия в формах"
|
||||
description = "Проверяет, есть ли чекбокс согласия на обработку ПД в формах"
|
||||
risk_on_fail = RiskLevel.CRITICAL
|
||||
default_weight = 20
|
||||
|
||||
CONSENT_PATTERNS = [
|
||||
r"согласие\s*на\s*обработку\s*данных",
|
||||
r"согласие\s*на\s*обработку\s*персональных?\s*данных?",
|
||||
r"я\s*согласен\s*на\s*",
|
||||
r"agree\s*to\s*the\s*",
|
||||
r"agree\s*to\s*privacy",
|
||||
r"я\s*принимаю\s*условия",
|
||||
r"processing\s*of\s*personal\s*data",
|
||||
r"обработк[аею]\s*персональных?\s*данных?",
|
||||
r"политик[ууе]?\s*конфиденциальност[иы]",
|
||||
]
|
||||
|
||||
async def check(self, page: Page, url: str) -> CheckResult:
|
||||
try:
|
||||
# Получаем все формы на странице
|
||||
forms = await page.query_selector_all("form")
|
||||
|
||||
if not forms:
|
||||
return self._skip_result(
|
||||
message="Формы на странице не найдены.",
|
||||
)
|
||||
|
||||
forms_needing_consent = 0
|
||||
forms_with_consent = 0
|
||||
forms_without_consent = []
|
||||
|
||||
for form_idx, form in enumerate(forms):
|
||||
# Проверяем, есть ли чекбокс в форме
|
||||
checkboxes = await form.query_selector_all('input[type="checkbox"]')
|
||||
|
||||
has_consent = False
|
||||
checkbox_texts = []
|
||||
|
||||
for cb in checkboxes:
|
||||
cb_text = await cb.inner_text() if not await cb.get_attribute("value") else await cb.get_attribute("value")
|
||||
cb_label = await form.evaluate(
|
||||
f'(el) => {{\n'
|
||||
f' const cb = el.closest("label") || el.parentElement;\n'
|
||||
f' return cb ? cb.innerText : "";\n'
|
||||
f'}}', form
|
||||
)
|
||||
|
||||
checkbox_texts.append(cb_text or cb_label or "без текста")
|
||||
|
||||
combined = f"{cb_text} {cb_label}".lower()
|
||||
if any(re.search(p, combined, re.UNICODE | re.IGNORECASE) for p in self.CONSENT_PATTERNS):
|
||||
has_consent = True
|
||||
|
||||
forms_needing_consent += 1
|
||||
if has_consent:
|
||||
forms_with_consent += 1
|
||||
else:
|
||||
forms_without_consent.append({
|
||||
"form_index": form_idx,
|
||||
"checkboxes": checkbox_texts[:3],
|
||||
})
|
||||
|
||||
if forms_with_consent == forms_needing_consent:
|
||||
return self._pass_result(
|
||||
message=f"Все формы ({forms_with_consent}) содержат чекбокс согласия.",
|
||||
details={
|
||||
"forms_count": forms_with_consent,
|
||||
"checked_forms": forms_with_consent,
|
||||
},
|
||||
)
|
||||
|
||||
if forms_with_consent == 0:
|
||||
return self._fail_result(
|
||||
message=(
|
||||
f"Формы ({forms_needing_consent}) не содержат чекбокс согласия на обработку ПД. "
|
||||
f"Это прямое нарушение 152-ФЗ."
|
||||
),
|
||||
details={
|
||||
"total_forms": forms_needing_consent,
|
||||
"without_consent": forms_without_consent,
|
||||
"sample_texts": [f["checkboxes"][0] for f in forms_without_consent[:3]],
|
||||
},
|
||||
)
|
||||
|
||||
# Частичное покрытие
|
||||
return self._fail_result(
|
||||
message=(
|
||||
f"Только {forms_with_consent} из {forms_needing_consent} форм содержат чекбокс согласия."
|
||||
),
|
||||
details={
|
||||
"total_forms": forms_needing_consent,
|
||||
"with_consent": forms_with_consent,
|
||||
"without_consent_count": forms_needing_consent - forms_with_consent,
|
||||
},
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return self._error_result(message=f"Ошибка при проверке чекбоксов: {str(e)}")
|
||||
Reference in new issue
Block a user