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)
This commit is contained in:
1 parent
7fb417dad5
commit
b9db935389
24 files changed
+1725
-286
No files matched your search
@@ -60,15 +60,15 @@ class PrivacyLinkCheck(BaseCheck):
|
||||
details={"total_links": len(link_texts), "page_text_sample": link_texts[:3]},
|
||||
)
|
||||
|
||||
# Проверяем, что ссылка ведет на рабочую страницу
|
||||
# Проверяем доступность страницы политики через aiohttp (не page.goto — гонка данных!)
|
||||
policy_url = found_links[0]["url"]
|
||||
try:
|
||||
# Проверяем, не является ли URL документом (PDF, DOCX, XLSX)
|
||||
import aiohttp
|
||||
|
||||
doc_extensions = ['.pdf', '.docx', '.doc', '.xlsx', '.xls', '.rtf', '.txt']
|
||||
is_document = any(policy_url.lower().endswith(ext) for ext in doc_extensions)
|
||||
|
||||
if is_document:
|
||||
# Документ для скачивания — это допустимо для политики
|
||||
return self._pass_result(
|
||||
message=f"Найдена ссылка на Политику конфиденциальности ({policy_url})",
|
||||
details={
|
||||
@@ -77,18 +77,30 @@ class PrivacyLinkCheck(BaseCheck):
|
||||
"is_document": True,
|
||||
},
|
||||
)
|
||||
|
||||
# Для HTML-страниц — проверяем доступность
|
||||
await page.goto(policy_url, timeout=5000)
|
||||
policy_text = await page.evaluate("() => document.body.innerText")
|
||||
await page.goto(url, timeout=5000) # Возвращаемся на главную
|
||||
|
||||
|
||||
# Для HTML-страниц — проверяем через aiohttp (не меняет URL страницы!)
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(policy_url, timeout=aiohttp.ClientTimeout(total=8)) as resp:
|
||||
if resp.status != 200:
|
||||
return self._fail_result(
|
||||
message=f"Страница политики недоступна (HTTP {resp.status}): {policy_url}",
|
||||
details={"policy_url": policy_url, "http_status": resp.status},
|
||||
)
|
||||
html = await resp.text()
|
||||
|
||||
# Извлекаем текст из HTML
|
||||
from bs4 import BeautifulSoup
|
||||
soup = BeautifulSoup(html, "html.parser")
|
||||
for tag in soup(["script", "style", "nav", "footer", "header"]):
|
||||
tag.decompose()
|
||||
policy_text = soup.get_text(separator=" ").strip()
|
||||
|
||||
if len(policy_text) < 50:
|
||||
return self._fail_result(
|
||||
message=f"Страница политики ({policy_url}) слишком короткая ({len(policy_text)} символов).",
|
||||
details={"policy_url": policy_url, "char_count": len(policy_text)},
|
||||
)
|
||||
|
||||
|
||||
return self._pass_result(
|
||||
message=f"Найдена ссылка на Политику конфиденциальности ({policy_url})",
|
||||
details={
|
||||
@@ -97,7 +109,7 @@ class PrivacyLinkCheck(BaseCheck):
|
||||
"char_count": len(policy_text),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
return self._fail_result(
|
||||
message=f"Страница политики не доступна: {str(e)}",
|
||||
|
||||
Reference in new issue
Block a user