Files
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

100 lines
4.4 KiB
Python
Raw Permalink 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.
"""
Проверка внешних скриптов, собирающих данные пользователей.
Загружает словари трекеров из dictionaries/trackers.yaml.
Разделяет на категории: CRITICAL (запрещены), WARNING (рискованны), RUSSIAN (допустимы).
"""
from __future__ import annotations
import re
from pathlib import Path
import yaml
from playwright.async_api import Page
from scanner.checks.base import BaseCheck
from scanner.models import CheckResult, RiskLevel
# Путь к словарю трекеров
TRACKERS_YAML = Path(__file__).parent.parent.parent / "dictionaries" / "trackers.yaml"
class ExternalScriptsCheck(BaseCheck):
"""Проверяет наличие внешних скриптов, передающих данные за пределы РФ."""
name = "external_scripts"
title = "Внешние скрипты аналитики"
description = "Ищет скрипты Google Analytics, Meta Pixel и другие иностранные сервисы"
risk_on_fail = RiskLevel.HIGH
default_weight = 15
def _load_trackers(self) -> dict:
"""Загрузить словарь трекеров из YAML."""
if not TRACKERS_YAML.exists():
# Fallback: базовый набор
return {
"critical": [{"domain": "google-analytics.com"}, {"domain": "facebook.com/tr"}],
"warning": [{"domain": "fonts.googleapis.com"}, {"domain": "google.com/recaptcha"}],
"russian": [{"domain": "mc.yandex.ru"}, {"domain": "vk.com/rtrg"}],
}
with open(TRACKERS_YAML, "r", encoding="utf-8") as f:
return yaml.safe_load(f)
async def check(self, page: Page, url: str) -> CheckResult:
try:
html = await page.evaluate("() => document.documentElement.outerHTML")
trackers = self._load_trackers()
found = {"critical": [], "warning": [], "russian": []}
for category in ("critical", "warning", "russian"):
for entry in trackers.get(category, []):
domain = entry["domain"]
if re.search(domain, html, re.IGNORECASE):
found[category].append(entry)
if not any(found.values()):
return self._pass_result(
message="Внешние скрипты аналитики не найдены.",
)
# Формируем сообщение
parts = []
if found["critical"]:
domains = [e["domain"] for e in found["critical"]]
parts.append(f"ЗАПРЕЩЁННЫЕ ({len(found['critical'])}): {', '.join(domains)}")
if found["warning"]:
domains = [e["domain"] for e in found["warning"]]
parts.append(f"РИСКОВАННЫЕ ({len(found['warning'])}): {', '.join(domains)}")
if found["russian"]:
domains = [e["domain"] for e in found["russian"]]
parts.append(f"Российские ({len(found['russian'])}): {', '.join(domains)}")
message = "Найдены внешние скрипты: " + " | ".join(parts)
# Только российские → PASS с уведомлением
if not found["critical"] and not found["warning"]:
return self._pass_result(
message=f"Найдены только российские сервисы: {message}",
details={"found": found},
)
# Есть критические → FAIL
if found["critical"]:
actions = [f"{e['domain']}: {e.get('action', 'Удалить')}" for e in found["critical"]]
message += f"\nРекомендации: {'; '.join(actions)}"
return self._fail_result(
message=message,
risk_level=RiskLevel.CRITICAL,
details={"found": found},
)
# Только warning → FAIL с HIGH
return self._fail_result(
message=message,
risk_level=RiskLevel.HIGH,
details={"found": found},
)
except Exception as e:
return self._error_result(message=f"Ошибка при проверке внешних скриптов: {str(e)}")