suveren 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. suveren-1.1.0/.gitignore +28 -0
  2. suveren-1.1.0/CHANGELOG.md +550 -0
  3. suveren-1.1.0/COPYRIGHT +27 -0
  4. suveren-1.1.0/LICENSE +661 -0
  5. suveren-1.1.0/PKG-INFO +254 -0
  6. suveren-1.1.0/README.en.md +217 -0
  7. suveren-1.1.0/README.md +216 -0
  8. suveren-1.1.0/pyproject.toml +86 -0
  9. suveren-1.1.0/src/suveren/__init__.py +7 -0
  10. suveren-1.1.0/src/suveren/__main__.py +3 -0
  11. suveren-1.1.0/src/suveren/advice.py +199 -0
  12. suveren-1.1.0/src/suveren/analyze.py +342 -0
  13. suveren-1.1.0/src/suveren/audit.py +103 -0
  14. suveren-1.1.0/src/suveren/blocklist.py +236 -0
  15. suveren-1.1.0/src/suveren/brand.py +60 -0
  16. suveren-1.1.0/src/suveren/cache.py +101 -0
  17. suveren-1.1.0/src/suveren/catalog.py +296 -0
  18. suveren-1.1.0/src/suveren/checks.py +113 -0
  19. suveren-1.1.0/src/suveren/cli.py +1288 -0
  20. suveren-1.1.0/src/suveren/collect.py +655 -0
  21. suveren-1.1.0/src/suveren/company.py +551 -0
  22. suveren-1.1.0/src/suveren/compliance.py +1091 -0
  23. suveren-1.1.0/src/suveren/crawl.py +223 -0
  24. suveren-1.1.0/src/suveren/cutoff.py +524 -0
  25. suveren-1.1.0/src/suveren/data/migration.json +181 -0
  26. suveren-1.1.0/src/suveren/data/report.schema.json +189 -0
  27. suveren-1.1.0/src/suveren/data/services.json +1934 -0
  28. suveren-1.1.0/src/suveren/data/takeover.json +429 -0
  29. suveren-1.1.0/src/suveren/data/vulns.json +37 -0
  30. suveren-1.1.0/src/suveren/dataflow.py +129 -0
  31. suveren-1.1.0/src/suveren/discover.py +77 -0
  32. suveren-1.1.0/src/suveren/dnsutil.py +267 -0
  33. suveren-1.1.0/src/suveren/doctor.py +164 -0
  34. suveren-1.1.0/src/suveren/errors.py +61 -0
  35. suveren-1.1.0/src/suveren/fines.py +239 -0
  36. suveren-1.1.0/src/suveren/formleaks.py +512 -0
  37. suveren-1.1.0/src/suveren/history.py +149 -0
  38. suveren-1.1.0/src/suveren/inn.py +120 -0
  39. suveren-1.1.0/src/suveren/leaks.py +199 -0
  40. suveren-1.1.0/src/suveren/logins.py +533 -0
  41. suveren-1.1.0/src/suveren/mailsec.py +877 -0
  42. suveren-1.1.0/src/suveren/menu.py +174 -0
  43. suveren-1.1.0/src/suveren/migration.py +73 -0
  44. suveren-1.1.0/src/suveren/models.py +416 -0
  45. suveren-1.1.0/src/suveren/page.py +181 -0
  46. suveren-1.1.0/src/suveren/pdpolicy.py +516 -0
  47. suveren-1.1.0/src/suveren/plan.py +88 -0
  48. suveren-1.1.0/src/suveren/plugins.py +130 -0
  49. suveren-1.1.0/src/suveren/recommend.py +94 -0
  50. suveren-1.1.0/src/suveren/registries.py +217 -0
  51. suveren-1.1.0/src/suveren/render.py +694 -0
  52. suveren-1.1.0/src/suveren/report.py +319 -0
  53. suveren-1.1.0/src/suveren/sanctions.py +526 -0
  54. suveren-1.1.0/src/suveren/sarif.py +120 -0
  55. suveren-1.1.0/src/suveren/scan.py +425 -0
  56. suveren-1.1.0/src/suveren/scenarios.py +226 -0
  57. suveren-1.1.0/src/suveren/scriptgraph.py +160 -0
  58. suveren-1.1.0/src/suveren/services.py +195 -0
  59. suveren-1.1.0/src/suveren/subdomains.py +437 -0
  60. suveren-1.1.0/src/suveren/takeover_signatures.py +60 -0
  61. suveren-1.1.0/src/suveren/templates/base.html.j2 +217 -0
  62. suveren-1.1.0/src/suveren/templates/batch.html.j2 +46 -0
  63. suveren-1.1.0/src/suveren/templates/company.html.j2 +76 -0
  64. suveren-1.1.0/src/suveren/templates/report.html.j2 +250 -0
  65. suveren-1.1.0/src/suveren/unknown.py +108 -0
  66. suveren-1.1.0/src/suveren/utils.py +153 -0
  67. suveren-1.1.0/src/suveren/view.py +324 -0
  68. suveren-1.1.0/src/suveren/vulns.py +187 -0
  69. suveren-1.1.0/src/suveren/websec.py +1143 -0
  70. suveren-1.1.0/tests/conftest.py +12 -0
  71. suveren-1.1.0/tests/fixtures/eu_sample.csv +5 -0
  72. suveren-1.1.0/tests/fixtures/logins/blog.html +6 -0
  73. suveren-1.1.0/tests/fixtures/logins/extidp_forms.html +12 -0
  74. suveren-1.1.0/tests/fixtures/logins/google_apple.html +8 -0
  75. suveren-1.1.0/tests/fixtures/logins/gsi_only.html +2 -0
  76. suveren-1.1.0/tests/fixtures/logins/home_with_login_link.html +4 -0
  77. suveren-1.1.0/tests/fixtures/logins/own_routes.html +7 -0
  78. suveren-1.1.0/tests/fixtures/logins/russian_only.html +15 -0
  79. suveren-1.1.0/tests/fixtures/logins/sign_route.html +5 -0
  80. suveren-1.1.0/tests/fixtures/logins/telegram_login.html +3 -0
  81. suveren-1.1.0/tests/fixtures/logins/ulogin.html +4 -0
  82. suveren-1.1.0/tests/fixtures/policies/full.txt +26 -0
  83. suveren-1.1.0/tests/fixtures/policies/offer_with_consent.txt +4 -0
  84. suveren-1.1.0/tests/fixtures/policies/partial.txt +2 -0
  85. suveren-1.1.0/tests/fixtures/sdn_sample.xml +43 -0
  86. suveren-1.1.0/tests/fixtures/uk_sample.csv +5 -0
  87. suveren-1.1.0/tests/golden.py +190 -0
  88. suveren-1.1.0/tests/lab.py +1255 -0
  89. suveren-1.1.0/tests/test_analyze.py +217 -0
  90. suveren-1.1.0/tests/test_audit.py +82 -0
  91. suveren-1.1.0/tests/test_brand.py +58 -0
  92. suveren-1.1.0/tests/test_cache.py +92 -0
  93. suveren-1.1.0/tests/test_catalog.py +114 -0
  94. suveren-1.1.0/tests/test_check_services.py +58 -0
  95. suveren-1.1.0/tests/test_cli.py +89 -0
  96. suveren-1.1.0/tests/test_collect.py +145 -0
  97. suveren-1.1.0/tests/test_collect_offline.py +228 -0
  98. suveren-1.1.0/tests/test_company.py +235 -0
  99. suveren-1.1.0/tests/test_company_offline.py +111 -0
  100. suveren-1.1.0/tests/test_compliance.py +310 -0
  101. suveren-1.1.0/tests/test_core.py +112 -0
  102. suveren-1.1.0/tests/test_corpus.py +29 -0
  103. suveren-1.1.0/tests/test_crawl.py +174 -0
  104. suveren-1.1.0/tests/test_cutoff.py +206 -0
  105. suveren-1.1.0/tests/test_dataflow.py +102 -0
  106. suveren-1.1.0/tests/test_dnsutil.py +198 -0
  107. suveren-1.1.0/tests/test_doctor.py +44 -0
  108. suveren-1.1.0/tests/test_e2e.py +597 -0
  109. suveren-1.1.0/tests/test_errors.py +55 -0
  110. suveren-1.1.0/tests/test_evidence.py +80 -0
  111. suveren-1.1.0/tests/test_fines.py +125 -0
  112. suveren-1.1.0/tests/test_formleaks.py +328 -0
  113. suveren-1.1.0/tests/test_formleaks_browser.py +175 -0
  114. suveren-1.1.0/tests/test_inn.py +76 -0
  115. suveren-1.1.0/tests/test_lab.py +16 -0
  116. suveren-1.1.0/tests/test_leaks.py +98 -0
  117. suveren-1.1.0/tests/test_logins.py +292 -0
  118. suveren-1.1.0/tests/test_mailsec.py +465 -0
  119. suveren-1.1.0/tests/test_menu.py +58 -0
  120. suveren-1.1.0/tests/test_migration.py +51 -0
  121. suveren-1.1.0/tests/test_nightly.py +64 -0
  122. suveren-1.1.0/tests/test_offline_modules.py +202 -0
  123. suveren-1.1.0/tests/test_pdpolicy.py +329 -0
  124. suveren-1.1.0/tests/test_plugins.py +105 -0
  125. suveren-1.1.0/tests/test_pypi_readme.py +44 -0
  126. suveren-1.1.0/tests/test_recommend.py +38 -0
  127. suveren-1.1.0/tests/test_registries_blocklist.py +148 -0
  128. suveren-1.1.0/tests/test_report_safety.py +45 -0
  129. suveren-1.1.0/tests/test_sanctions.py +208 -0
  130. suveren-1.1.0/tests/test_scenarios.py +107 -0
  131. suveren-1.1.0/tests/test_schema.py +109 -0
  132. suveren-1.1.0/tests/test_scriptgraph.py +58 -0
  133. suveren-1.1.0/tests/test_services.py +299 -0
  134. suveren-1.1.0/tests/test_subdomains.py +348 -0
  135. suveren-1.1.0/tests/test_update_vulns.py +76 -0
  136. suveren-1.1.0/tests/test_view.py +406 -0
  137. suveren-1.1.0/tests/test_visual.py +36 -0
  138. suveren-1.1.0/tests/test_vulns.py +96 -0
  139. suveren-1.1.0/tests/test_websec.py +640 -0
  140. suveren-1.1.0/tools/check_services.py +137 -0
  141. suveren-1.1.0/tools/ci.py +88 -0
  142. suveren-1.1.0/tools/nightly-sites.txt +341 -0
  143. suveren-1.1.0/tools/nightly.bat +15 -0
  144. suveren-1.1.0/tools/nightly.py +196 -0
  145. suveren-1.1.0/tools/pypi_readme.py +51 -0
  146. suveren-1.1.0/tools/report_shots.py +129 -0
  147. suveren-1.1.0/tools/update_vulns.py +142 -0
@@ -0,0 +1,28 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ build/
6
+ dist/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ .coverage
11
+ htmlcov/
12
+
13
+ # Scan output: may contain data about third-party infrastructure
14
+ reports/
15
+ *.json
16
+
17
+ .idea/
18
+ .vscode/
19
+ .DS_Store
20
+ Thumbs.db
21
+
22
+ # Golden corpus of real sites: local only (tests/golden.py)
23
+ corpus/
24
+ # ...but the data files shipped with the package are code
25
+ !src/suveren/data/*.json
26
+
27
+ # graphify knowledge graph (local, rebuilt with `graphify update .`)
28
+ graphify-out/
@@ -0,0 +1,550 @@
1
+ # Changelog
2
+
3
+ ## Не выпущено
4
+
5
+ - **Нужен Python 3.11 или новее.** Поддержка Python 3.10 закончилась в октябре 2026 года, и
6
+ исправления свежих уязвимостей dnspython (CVE-2026-114605) и filelock для него уже не
7
+ выходят. dnspython теперь не ниже 2.9. Exe и Docker это не затрагивает: Python у них свой.
8
+ - **Пакет на PyPI: `pip install "suveren[browser]"`.** Релиз по тегу теперь не только собирает
9
+ exe, но и публикует пакет на PyPI через Trusted Publishing: токенов нигде не хранится, PyPI
10
+ доверяет только релизному процессу этого репозитория. Перед сборкой ссылки и картинки README
11
+ становятся абсолютными (`tools/pypi_readme.py`) и ведут на файлы того же тега. Релиз
12
+ останавливается, если тег не совпадает с версией в `pyproject.toml`: версию на PyPI нельзя
13
+ загрузить дважды. В исходный пакет попадают только код, тесты и инструменты.
14
+ - **Новое описание и картинки.** README стал коротким: что проверяется, как запустить (exe,
15
+ `Suveren.bat`, pip, Docker), отчёт, команды и главные параметры. Подробности по каждой проверке
16
+ переехали в [docs/guide.md](docs/guide.md). Картинки пересняты на версии 1.1: терминал, меню,
17
+ отчёт, раздел 152-ФЗ, проверка компании и сводка, у каждой — светлый и тёмный вариант. Сайты на
18
+ картинках выдуманные (`lab.test` и соседи в зоне `.test`): данные настоящих проверок с
19
+ заменёнными именами, адресами и IP, чтобы не показывать чужие компании.
20
+ - **Сводка `batch`: имя сайта и счётчики 152-ФЗ не переносятся посередине** («habr.c om»), а
21
+ в терминале — «4 сайта», а не «4 сайтов».
22
+
23
+ - **Молчащий DNS сайта делает оценку неполной.** Если DNS не ответил на запрос NS, MX или
24
+ A-записей сайта, сервисы за ними (DNS-хостинг, почта, хостинг) не видны, а не отсутствуют.
25
+ Теперь оценка помечается «неполная, не проверены: …», обе оценки не выше B (как при
26
+ недоступной странице: не увиденное — не чистое), а проверка «IP-адреса сайта» (SUV-BLK-002)
27
+ показывает «не проверено» вместо того, чтобы пропасть из отчёта. «Хранение данных в России»
28
+ (SUV-PD-007) называет причину: IP-адреса сайта не получены, а не «например, сайт за CDN». В ночном
29
+ прогоне 25.10 у sravni.ru из-за этого «пропали» четыре сервиса, и отчёт выглядел полным.
30
+ Схема JSON 1.7 (`facts.dns_unanswered`); самоаудит (`suveren audit`) ловит такие старые
31
+ отчёты правилом `dns-gap-complete`.
32
+ - **Таймаут на `_mta-sts` больше не гасит весь раздел почты.** Раньше одна неответившая запись
33
+ MTA-STS делала «не проверено» все пять проверок почты (kamaz.ru, 25.10). Теперь «не
34
+ проверено» только у «Шифрования входящей почты» (SUV-MAIL-005); SPF, DMARC и DKIM
35
+ проверяются как обычно. Неответившая запись TLS-RPT просто не упоминается.
36
+
37
+ - **Изменилось поведение: неполная оценка не проходит пороги CI (код выхода 6).** Если задан
38
+ `--fail-under` или `--fail-on`, а оценка неполная (стена от ботов, не ответил реестр или журнал
39
+ сертификатов), код выхода теперь 6: то, чего не увидели, порог проверить не может.
40
+ `--allow-incomplete` принимает неполную оценку. Без порогов всё как раньше.
41
+ - **`batch` для CI:** `--fail-under` и `--fail-on` по худшему сайту (код 1 за непроверенный сайт
42
+ важнее), `-q` с пропусками и сбоями в stderr, `--max-time` на сайт.
43
+ - **`--max-time` — предел на всю проверку** (у `scan` 600 с, у `batch` 300 с на сайт); `--timeout`
44
+ теперь честно назван таймаутом одного запроса. В CI (переменная `CI`) и при выводе не в
45
+ терминал отчёт больше не открывается в браузере. Коды выхода перечислены в `--help`.
46
+ - **`diff` и `--since` предупреждают о неравноценном сравнении.** Если одна из проверок была
47
+ неполной, «убранные» сервисы могли быть просто не видны: об этом теперь сказано и в терминале,
48
+ и в HTML. Показаны проверки, которые появились или пропали, а `diff` начинается с общей оценки.
49
+ Схема JSON 1.6 (`history.incomplete`, `appeared`, `vanished`).
50
+
51
+ - **Возможный штраф за согласие — только там, где согласия нет совсем (SUV-PD-003).** Если
52
+ согласие есть, но на следующем шаге формы, рядом с формой или с заранее поставленной
53
+ галочкой, строка «возможен штраф по ст. 13.11 ч. 1 КоАП» больше не показывается.
54
+ `batch` печатает «пропущена строка N» под заголовком, а не над ним.
55
+
56
+ - **«Хранение данных в России» называет исключения закона (SUV-PD-007).** Запрет хранить
57
+ данные граждан РФ в зарубежных базах (152-ФЗ, ст. 18 ч. 5) действует «за исключением
58
+ случаев, указанных в пунктах 2, 3, 4, 8 части 1 статьи 6»: международный договор или закон
59
+ (бронирование международных авиабилетов), правосудие, госуслуги, журналисты и СМИ. Совет
60
+ цитировал норму без них.
61
+
62
+ - **Новая проверка SUV-LAW-002 «Рекомендательные технологии».** С 01.10.2023 владелец
63
+ сайта, который подбирает показ по интересам посетителя, обязан разместить «документ,
64
+ устанавливающий правила применения рекомендательных технологий», адрес почты для
65
+ юридически значимых сообщений и своё наименование (149-ФЗ, ст. 10.2-2 ч. 1 п. 3). С
66
+ 07.07.2026 за неразмещение — штраф организации от 500 до 700 тыс. ₽ (КоАП, ст. 13.56 ч. 3,
67
+ введена 199-ФЗ от 26.06.2026). Проверка ищет блоки с заголовками «Персональные
68
+ рекомендации», «Вам может понравиться», «Вы недавно смотрели» и ссылку на правила. Нет
69
+ правил — только «внимание»: снаружи не видно, строится ли подборка по интересам. Есть
70
+ правила — «в порядке» (в корпусе: habr.com, hh.ru, citilink.ru, litres.ru, kassir.ru,
71
+ kontur.ru, profi.ru, tutu.ru). Нет ни того, ни другого — проверки в отчёте нет. Штраф
72
+ при «внимании» показан как возможный и в сумму не входит.
73
+
74
+ - **Меньше случайных «не открылся».** Главная страница и WHOIS теперь запрашиваются ещё раз
75
+ после таймаута, как внутренние страницы. В ночных прогонах регистратор perekrestok.ru и
76
+ reg.ru то был, то пропадал, и оценка прыгала на ступень между ночами.
77
+
78
+ - **Российские регистраторы не из базы засчитываются как российские.** MASTERHOST-RU,
79
+ RD-RU, NETHOUSE-RU, FLEXBE-RU и другие аккредитованные регистраторы зон .ru и .рф шли
80
+ в отчёт со страной «не определена». Теперь регистратор с окончанием «-RU» — российский.
81
+
82
+ - **Отчёт говорит о переадресации на другой домен.** pochtabank.ru открывает vtb.ru,
83
+ ecwid.ru — ecwid.com: хостинг, DNS и почта измерялись у первого домена, а скрипты и
84
+ страницы — у второго, и отчёт смешивал две компании молча. Теперь в замечаниях сказано,
85
+ куда ведёт сайт и что этот домен стоит проверить отдельно.
86
+
87
+ - **База сервисов: NGENIX и региональные адреса.** Сеть NGENIX (AS34879) записана на
88
+ ООО «Современные сетевые технологии» без слова «ngenix», поэтому aeroflot.ru, kfc.ru,
89
+ nalog.gov.ru и другие получали «Хостинг: OOO Sovremennye setevye tekhnologii» без
90
+ замечания, что настоящий хостинг скрыт за CDN. Теперь NGENIX узнаётся как CDN. Узнаются и
91
+ адреса, которые раньше проходили мимо: HubSpot в регионах (`js-eu1.hs-scripts.com`),
92
+ `script.hotjar.com`, `static.criteo.com`, виджет Jivo, классическая встроенная карта
93
+ `maps.google.com/maps?…&output=embed`.
94
+
95
+ - **Ссылка — не подключённый сервис.** Обычная ссылка в подвале («Мы на YouTube», новость на
96
+ news.rambler.ru) считалась тем, что сайт этот сервис использует, и могла снизить оценку
97
+ независимости. Теперь ссылки `<a href>` в расчёт не идут. Исключение — ссылки на формы
98
+ (Google Forms, Яндекс Формы, Typeform и др.): ответы посетителей уходят туда.
99
+
100
+ - **Страницы в windows-1251 читаются.** Если сервер не называл кодировку в заголовке
101
+ `Content-Type`, страница читалась как UTF-8 и превращалась в «��», так что проверки
102
+ 152-ФЗ по тексту и поиск ИНН ничего не находили. Теперь кодировка берётся из
103
+ `<meta charset>`, а без него текст, который не читается как UTF-8, читается как
104
+ windows-1251.
105
+
106
+ - **Капча в форме — не «защита от ботов».** Признаки стены искались во всём коде страницы,
107
+ и маленький лендинг с reCAPTCHA в форме заявки получал «сайт показал страницу защиты от
108
+ ботов»: внутренние страницы не читались, заголовки — «не проверено». Теперь слова
109
+ «captcha», «не робот», «enable javascript» ищутся в том, что видит посетитель (текст и
110
+ заголовок), а в коде — только метки сервисов защиты (Cloudflare challenge, DDoS-Guard,
111
+ Qrator, Servicepipe). Ответ 404 или 500 больше не называется защитой от ботов: это ответ
112
+ самого сайта. На banki.ru и skillbox.ru страница входа, собранная скриптами, раньше
113
+ считалась стеной (SUV-LAW-001 «не проверено»).
114
+
115
+ - **Санкции: Васильев находится как Vasilyev.** Мягкий знак и «ё» терялись по-разному:
116
+ «Васильев» давал vasilev, а Vasilyev и Vasiliev — vasiliev; так же Соловьёв/Solovyov,
117
+ Воробьев/Vorobyov, Фёдор/Fyodor. Список OFAC только латиницей, и такие руководители
118
+ получали «совпадений нет». Чистый ответ о санкциях теперь говорит, что учредители и
119
+ владельцы не сверялись (правило 50% OFAC касается и их).
120
+
121
+ - **`company` не подменяет компанию и не путает сбой реестра с «не найдена».** Если ЕГРЮЛ
122
+ возвращал строки без этого ИНН, бралась первая — чужая организация, и по ней шли санкции и
123
+ возраст. Теперь это «не проверено». Из нескольких строк с одним ИНН (ИП закрылся и
124
+ зарегистрировался заново) берётся действующая, а не первая — раньше выходило «прекратила
125
+ деятельность». Ответ ЕГРЮЛ в другом формате и страница реестра операторов ПДн без таблицы
126
+ результатов (капча, новая вёрстка) — тоже «не проверено», а не «не найдена» (SUV-CO-001,
127
+ SUV-CO-006).
128
+
129
+ - **Изменилось поведение: старая копия списков — «не проверено».** Если санкционный список
130
+ или реестр блокировок не удалось обновить, использовалась копия любой давности, а проверка
131
+ оставалась «в порядке». Теперь копия старше 14 дней делает чистый ответ «не проверено» с
132
+ возрастом копии и советом повторить с `--refresh` (SUV-CO-003, SUV-BLK-001…003). Найденное
133
+ по старой копии по-прежнему показывается. Так же «не проверено», если ЕГРЮЛ не ответил и
134
+ санкции сверены только по ИНН и ОГРН: у большинства записей в списках реквизитов нет.
135
+
136
+ - **Причина сбоя написана словами.** Вместо «(HTTPStatusError)», «(ConnectError)» и «DNS lookup
137
+ … failed: NoNameservers» отчёт и замечания теперь говорят «сервер ответил кодом 503», «нет
138
+ связи с сервером», «сервер не ответил вовремя», «не удалось установить защищённое
139
+ соединение», «имя сервера не найдено в DNS». Это касается скачивания списков, WHOIS,
140
+ главной страницы и политики, ЕГРЮЛ, реестра Роскомнадзора, журналов сертификатов, DNS и
141
+ `doctor`.
142
+
143
+ - **Изменилось поведение: «Хранение данных в России» не судит по адресу CDN (SUV-PD-007).**
144
+ CloudFront отвечает из той же сети Amazon, что и обычный хостинг AWS, поэтому aviasales.ru
145
+ получал «база за рубежом» и до 6 млн ₽ штрафа по адресу пограничного сервера. Теперь
146
+ CloudFront узнаётся по заголовкам ответа (`x-amz-cf-id`, `via`), сеть DDoS-Guard в ОАЭ
147
+ (AS59692) — как его CDN, и такой сайт получает «не проверено: сервер за CDN». Если сервер
148
+ действительно за рубежом, «проблема» ставится, только когда форма с данными отправляется на
149
+ сам сайт. Если заявки уходят в другое место, например в российскую CRM, — «внимание».
150
+
151
+ - **Изменилось поведение: форма без согласия — «внимание», а не «проблема» (SUV-PD-003).**
152
+ Нужно ли согласие, зависит от цели формы, а её снаружи не видно: для заявки на заключение
153
+ договора (152-ФЗ, ст. 6 ч. 1 п. 5) или обращения в госорган (п. 2, 59-ФЗ) оно не требуется.
154
+ Штраф по ст. 13.11 ч. 1 КоАП теперь показан как возможный и в сумму не входит. В ночном
155
+ прогоне 23.10 «проблема» стояла у 21 сайта, среди них kremlin.ru и pochta.ru.
156
+
157
+ - **Индивидуальные предприниматели проверяются на санкции.** ИНН ИП искался только среди
158
+ организаций, хотя в списках ИП — это запись о человеке, а ФИО сверялось вместе с
159
+ «ИНДИВИДУАЛЬНЫЙ ПРЕДПРИНИМАТЕЛЬ», которое принималось за фамилию и имя. Подсанкционный ИП
160
+ получал «не найдена». Теперь ИНН ищется и среди людей, а ФИО — без приставки. Если ИП
161
+ найден по ИНН, проверка лица тоже «проблема», без совета «может быть полный тёзка».
162
+
163
+ - **Изменилось поведение: санкции по названию больше не дают ложную «проблему».** Названия в
164
+ России не уникальны: ООО «Арсенал» со своим ИНН получало «Организация в санкционных
165
+ списках», потому что в них есть LLC ARSENAL с другим ИНН; «Гранит» совпадал с GARANTIYA по
166
+ согласным. Теперь три уровня. Совпали ИНН или ОГРН — «проблема». Совпало название, а
167
+ сверить реквизиты нечем — «внимание»: под санкциями бывают и новые юрлица со старым
168
+ названием. Название совпало, но ИНН или ОГРН у записи другой — это другая организация: она
169
+ показана в таблице справочно, проверка в порядке. Совпадение названия теперь сверяется по
170
+ словам целиком, с гласными и цифрами.
171
+
172
+ - **Изменилось поведение: санкционная проверка не выдаёт неполную сверку за чистую.** Если
173
+ какой-то список (США, ЕС, Великобритания) не загрузился или разобрался в ноль записей,
174
+ «не найдена» и «совпадений нет» теперь «не проверено» с перечнем того, что не загрузилось.
175
+ Ответ с HTML-заглушкой или пустым телом больше не затирает хорошую копию списка в кэше
176
+ (это касается и реестра блокировок): старая копия остаётся, об этом есть замечание.
177
+
178
+ - **Реестр блокировок находит сайты в зоне .рф.** Около 7 000 записей реестра записаны
179
+ кириллицей («01инвест.рф»), а адрес сайта сверялся в punycode, поэтому заблокированный
180
+ .рф-сайт получал «Домен не заблокирован». Теперь записи переводятся в punycode при чтении.
181
+
182
+ - **`--json -` и `--sarif -` выводят результат в stdout**, а заголовок и вердикты в это время
183
+ уходят в stderr: `suveren scan сайт.ru --json - --no-report | jq .overall_grade`. Раньше
184
+ создавался файл с именем «-». Ошибки теперь всегда идут в stderr, в том числе при `-q` с
185
+ неверным `--checks`. Если сработали оба порога, `--fail-under` и `--fail-on`, печатаются оба
186
+ сообщения (код выхода по-прежнему 3). Файл, который не удалось записать, — сообщение с кодом
187
+ 1, а не трассировка.
188
+
189
+ - **Изменилось поведение: `audit`, `diff` и `--since` проверяют, что им дали отчёт.** Раньше
190
+ `audit` на отсутствующем файле, на `{}` или на пустой папке писал «✓ Противоречий нет» с
191
+ кодом 0, `diff` падал с трассировкой на JSON-списке и сравнивал отчёты двух разных сайтов как
192
+ изменения одного. Теперь это ошибка с причиной и кодом 2; `--since` проверяет файл и сайт
193
+ до начала проверки. Чужие JSON-файлы в папке `batch` по-прежнему пропускаются молча.
194
+
195
+ - **Изменилось поведение: `batch` честно говорит о пропусках.** Если хотя бы один сайт не
196
+ проверен, код выхода теперь 1 (отчёты по остальным записаны). Раньше был 0, даже когда не
197
+ проверился ни один. Строка, которая не похожа на домен, больше не обрывает весь список: она
198
+ пропускается с номером («пропущена строка 7: …»). Непроверенные сайты теперь есть и в
199
+ `summary.csv`, с причиной в новой последней колонке `error`.
200
+
201
+ - **Изменилось поведение: `company` не выдаёт сбой реестра за «не найдена».** Если ЕГРЮЛ или
202
+ реестр операторов ПДн Роскомнадзора не ответил, проверка теперь «не проверено» с советом
203
+ повторить позже. Раньше без сети Сбербанк получал «✗ Организация с такими реквизитами не
204
+ найдена» под плашкой «ЕСТЬ ПРОБЛЕМЫ» и «в реестре не найдена», а причина была только в
205
+ замечаниях.
206
+
207
+ - **Изменилось поведение: недоступный сайт больше не получает A.** Если страницу сайта увидеть
208
+ не удалось (ничего не пришло, стена от ботов или страница ошибки вместо неё), скрипты и
209
+ виджеты не проверены. Теперь обе оценки, общая и независимость, не выше B, отчёт помечен
210
+ «неполная» (в JSON — `overall_incomplete` и новое `facts.page_unseen`, схема 1.5), а в
211
+ замечаниях сказано почему. Раньше сайт, до которого не дошли запросы, получал в `--quick`
212
+ A 100/100, и `--fail-under A` его пропускал. Тонкая страница, собранная скриптами, по-прежнему
213
+ считается увиденной: её скрипты есть в коде. В ночном прогоне 23.10 из 289 сайтов таких было
214
+ 94 (из-за стен от ботов и зарубежного IP), у 67 из них независимость была A.
215
+
216
+ - **Политика и реальность (SUV-PD-011) сравнивает больше.** Раньше проверка ловила только
217
+ категоричные фразы («не передаём третьим лицам»). Теперь ещё:
218
+ - **Чужая политика.** Политика говорит о другом сайте и ни разу не называет этот, или
219
+ оператор в ней с другим ИНН, чем в реквизитах сайта. Так бывает и в группе компаний: на
220
+ drom.ru политика ООО «Фарпост» ссылается на правила farpost.ru, на litres.ru оператор —
221
+ другое юрлицо группы. Поэтому вердикт «проверьте», а не «нарушение».
222
+ - **Данные из форм не в перечне.** Форма просит телефон, почту или имя, а в перечне данных
223
+ политики их нет. Перечень для каждой цели обязателен по ст. 18.1 ч. 1 п. 2 152-ФЗ
224
+ (текст сверен на consultant.ru). Если перечня нет совсем, об этом говорит «Содержание
225
+ политики».
226
+ - **Cookies.** На сайте счётчики, а политика о cookies и веб-аналитике молчит, и отдельной
227
+ политики cookies нет (ссылка из баннера на ту же политику не считается).
228
+
229
+ Полигон вырос до 79 случаев, все совпадают. В корпусе три новых «внимания» (drom.ru,
230
+ litres.ru, netology.ru), каждое сверено с текстом политики.
231
+ - **Доказательство к каждой находке.** Под проблемой в HTML-отчёте раскрывается то, на чём
232
+ основан вывод, как его увидел Suveren:
233
+ - почта: записи SPF, DMARC, DKIM, MX, MTA-STS и TLS-RPT;
234
+ - веб: заголовки ответа (пустые, если их нет), cookies с флагами, сертификат, адреса
235
+ сторонних скриптов и скриптов без integrity, адрес скрипта с уязвимой версией,
236
+ замаскированный ключ из кода;
237
+ - 152-ФЗ: формы с полями и согласием рядом, цитаты из соглашений, адрес политики, сервисы и
238
+ их адреса;
239
+ - вход через иностранные сервисы, CNAME поддоменов, записи реестра блокировок, запросы с
240
+ данными из форм, отключённые в эксперименте `--cutoff` хосты.
241
+
242
+ В JSON у каждой проверки поле `evidence` (схема 1.4), в SARIF — `properties.evidence`. На
243
+ полигоне и в корпусе доказательство есть у каждой «проблемы» и «внимания» (36 из 36 в
244
+ корпусе); тест следит, чтобы так и осталось.
245
+ - **Эксперимент `--form-leaks`: утекают ли данные из форм до отправки (SUV-PD-012).** В
246
+ браузере в поля имени, телефона, почты и пароля печатаются тестовые значения, форма не
247
+ отправляется, а во всём, что страница шлёт (запросы, маяки, WebSocket, а также то, что
248
+ уходит при закрытии вкладки), ищутся эти значения: открытым текстом, в base64, в gzip и как
249
+ хеши MD5, SHA-1, SHA-256. Так работают запись сессий, «ловцы брошенных заявок» и рекламные
250
+ пиксели с автоматическим сопоставлением. Методика — Leaky Forms (USENIX Security 2022).
251
+ Пароль у стороннего сервиса или данные у иностранного — «проблема», у российского —
252
+ «внимание»: до отправки согласия нет, а без него раскрывать данные третьим лицам и поручать
253
+ им обработку нельзя (152-ФЗ, ст. 7 и ст. 6 ч. 3, текст сверен на consultant.ru). Запускается
254
+ только своим флагом. Почта на `example.com`, у телефона несуществующий код 100, а номер,
255
+ который маска поля превратила в другой, стирается до выхода из поля, чтобы «ловец заявок» не
256
+ передал менеджеру чужой настоящий номер. Если маска принимает только номера на 9, отчёт так
257
+ и говорит, а на своём сайте можно указать свой номер: `--form-phone`. Проверка на локальных
258
+ страницах и прототип на 22 сайтах — в `docs/accuracy.md`. Там же сказано, чего эксперимент не
259
+ видит (Вебвизор Метрики, iframe, зашифрованное).
260
+ - База сервисов: Carrot quest распознаётся и по домену API `api.carrottrack.app`.
261
+ - **Проверка «как безопасник» (блок 1b).** Каждая проверка сверена со стандартом (RFC 7208,
262
+ 7489, 8461, 6797, 8996, OWASP Secure Headers, текст 152-ФЗ и КоАП), с Mozilla Observatory и
263
+ SSL Labs и с ревью двух независимых агентов. Полигон из 65 синтетических сайтов
264
+ (`tests/lab.py`) держит ответы. До блока Suveren верно отвечал на 40 из 65 случаев, теперь на
265
+ все. Методика и таблицы — в `docs/accuracy.md` и `docs/standards-review.md`. Главное:
266
+ - TLS: сертификат проверяется так же, как в браузере (корни Mozilla). Недоверенный,
267
+ самоподписанный или выписанный на другое имя — FAIL; НУЦ Минцифры — WARN; недостающий
268
+ промежуточный сертификат отличается от неизвестного корня через AIA.
269
+ - Почта: MTA-STS читается так же, как это делают отправители, и MX сверяются с политикой;
270
+ SPF считается по RFC: include в каждом вычислении, цепочки redirect, пустые ответы, ptr;
271
+ DMARC `pct=0` понижает политику.
272
+ - Веб: повторяющиеся заголовки, несколько CSP, `frame-ancestors *`, повтор HSTS
273
+ (bi.zone), `ALLOW-FROM`. Строка заголовка PEM без ключа и идентификатор AWS без секрета
274
+ больше не считаются утечкой секрета.
275
+ - 152-ФЗ: битая ссылка на политику, галочка рассылки вместо согласия, ссылка «Политика
276
+ cookie» вместо уведомления, ИНН рекламодателя из маркировки, согласие вместе с
277
+ рекламной рассылкой, трансграничная передача по получателю (ст. 3 п. 11), а не по стране
278
+ сервера.
279
+ - **Сверка почты с internet.nl** (8 доменов, `docs/accuracy.md`): DKIM при неизвестном
280
+ селекторе определяется по узлу `_domainkey` (с защитой от зон, где существует любое имя), а
281
+ DMARC предупреждает, если отчёты уходят на чужой домен без его записи `_report._dmarc`
282
+ (RFC 7489, 7.1). Совпадение с internet.nl выросло с 14 до 18 из 21; три оставшихся
283
+ расхождения — Suveren строже нарочно.
284
+ - **`--cutoff`: режим зависания.** Четвёртый прогон в браузере: запросы к иностранным сервисам
285
+ остаются без ответа, как при блокировке с потерей пакетов. Отчёт говорит по факту, а не по
286
+ разметке: нарисовал ли браузер хоть что-то за 10 секунд (first-contentful-paint) и загрузилась
287
+ ли страница. habr.com и vc.ru из-за Google Fonts в `<head>` остаются пустыми; lenta.ru и
288
+ rbc.ru открываются.
289
+ - **Отзыв сертификата (SUV-WEB-008):** OCSP-сервер удостоверяющего центра, а если его нет (Let's
290
+ Encrypt с 2025 года) — список отзыва (CRL) с проверкой подписи. Отозванный сертификат — FAIL;
291
+ revoked.badssl.com теперь найден. Запросы идут к удостоверяющему центру, не к сайту.
292
+ - **Неполная оценка.** Если раздел не проверился целиком (страница не открылась, DNS не
293
+ ответил, журналы сертификатов недоступны), его проблемы пропадали, и общая оценка росла:
294
+ economy.gov.ru из B 77 стал B 89. Теперь отчёт пишет «оценка неполная, не проверены: …» под
295
+ оценкой, в сводке batch, в CSV и в JSON (`overall_incomplete`, схема 1.3), а ночной прогон
296
+ не выдаёт такой рост за улучшение.
297
+ - **Ночной прогон** ждёт сеть после пробуждения ноутбука, ходит только в российский DNS
298
+ (`batch --dns ru`) и работает на снимке последнего коммита, а не на рабочей копии. Неудачная
299
+ проверка DNS повторяется через две минуты, а не запоминается до конца прогона: из-за этого
300
+ в ночь на 23.10 имена всех сайтов ушли в Google DoH.
301
+ - Фраза «вы предоставляете согласие на обработку» в форме снова считается согласием
302
+ (ложный FAIL на cloudpayments.ru).
303
+ - **ИНН владельца сайта** находится точнее: если других реквизитов нет, засчитывается
304
+ маркировка рекламы самой компании (metro-cc.ru, megafon.ru: имя рекламодателя совпадает с
305
+ доменом, отчёт так и пишет); ИНН «партнёра» из документа не берётся (rbc.ru); в политике
306
+ оператор — тот, кто назван первым, а не тот, кто упомянут чаще (kp.ru, hh.ru).
307
+ - DMARC: `v=dmarc1` строчными — предупреждение: по RFC 7489 (6.3) значение должно точно
308
+ совпадать с `DMARC1`, иначе строгие получатели игнорируют запись.
309
+ - **Стабильность от прогона к прогону.** Проверка TLS больше не пропадает из отчёта, если
310
+ одно соединение не удалось (повтор, затем «не проверено»; самоаудит ловит такую пропажу).
311
+ Внутренние страницы выбираются по одной каждого вида, поэтому меняющиеся карточки и
312
+ свежие новости не съедают весь бюджет (fl.ru). Страница, которая не ответила вовремя,
313
+ запрашивается ещё раз, а политика, которая так и не открылась, — «не проверено», а не
314
+ предупреждение. Ответ 404 на странице политики запрашивается дважды.
315
+ - Несуществующий адрес (поддомен с NXDOMAIN) — ошибка «адреса нет», а не оценка по данным
316
+ родительского домена (solar.rt.ru получал B 89).
317
+ - **Безопасность самого инструмента:** ссылка `javascript:` со сканируемой страницы больше не
318
+ попадает в отчёт (фильтр `web_url`, тест с вредоносными данными). Добавлен `SECURITY.md` с
319
+ моделью угроз.
320
+ - JSON-отчёт описан схемой `src/suveren/data/report.schema.json` (`schema_version` 1.0).
321
+ - `tools/update_vulns.py` сверяет `vulns.json` с NVD; добавлена CVE-2025-13465 (lodash).
322
+ - mypy без ошибок и шаг mypy в CI; покрытие тестами 90% (CI падает ниже), сквозные офлайн-тесты
323
+ команд `scan`, `batch`, `company`, `doctor`, `update`. Пакетный режим больше не падает, если
324
+ реестр блокировок не скачался.
325
+ - **Плагины:** `--plugin файл.py` подключает свои проверки (как NSE в Nmap), пример — проверка
326
+ `security.txt` по RFC 9116, руководство — `docs/plugins.md`. JSON-схема 1.1.
327
+ - Релиз: SBOM (CycloneDX), `SHA256SUMS.txt` и подписанное через Sigstore происхождение сборки.
328
+ - Скриншот-тесты отчёта (светлая и тёмная тема, телефон); на телефоне широкие таблицы
329
+ прокручиваются, а не сжимаются в столбик.
330
+ - `scan --since прошлый.json` — раздел «С прошлой проверки» в терминале, HTML и JSON (схема 1.2);
331
+ `diff` сравнивает проверки по ID. `scan -q` — тихий режим для CI, автодополнение в оболочке
332
+ (`--install-completion`), `summary.jsonl` в пакетном режиме.
333
+ - Внутренние страницы с формами (контакты, заказ, корзина) находятся и через sitemap.
334
+ - `tools/check_services.py` сверяет базу сервисов с DNS; обновлены названия владельцев сетей
335
+ Vultr и VK.
336
+ - Ночной прогон: повтор запроса владельца IP; если журналы сертификатов недоступны, берётся
337
+ сохранённый список поддоменов не старше 14 дней.
338
+ - `suveren audit` проверяет отчёты на противоречия: «в порядке» с тревожным текстом, проблема
339
+ без совета, пропавший раздел, проверки, которые спорят друг с другом. Тесты прогоняют его
340
+ по всему золотому корпусу.
341
+ - **Лицензия:** с версии 1.1 — GNU AGPL-3.0-or-later, автор — marsilid (файл COPYRIGHT).
342
+ Любая изменённая версия, в том числе работающая как сервис, должна быть открыта; для
343
+ закрытых продуктов есть коммерческая лицензия. Версии до 1.0.x остаются под MIT.
344
+ - HTML-отчёт: под оценкой — пять плиток (проблемы, внимание, штрафы, сценарии атак, что будет без
345
+ иностранных сервисов), каждая ведёт к своему разделу; липкое оглавление со счётчиками проблем и
346
+ предупреждений; палитра как в терминале (бирюзовый акцент, зелёный, жёлтый, красный) в светлой
347
+ и тёмной теме.
348
+ - DNS- и почтовый сервер с неизвестным именем на известной сети подписывается честно:
349
+ «dyna-ns.net (на сети Cloudflare)», а не «Cloudflare»; DNS Dynadot добавлен в базу.
350
+ - Домену, который не отправляет почту, советуют сразу DMARC p=reject и SPF -all, без
351
+ «режима наблюдения» и без пугалки про спам.
352
+ - «Что сделать в первую очередь» без повторов: вердикт о почте, SPF, DMARC и сценарий фишинга
353
+ дают один пункт; сценарий атаки уступает конкретной проверке.
354
+ - HTML-отчёт: сценарии атак сразу после «Что сделать», замечания в конце, блок штрафов без
355
+ красной рамки, если штрафов нет, скриншоты «до/после» только когда что-то отключали.
356
+ - Новый вид терминала. Вместо ASCII-баннера шапка в одну строку (версия, сайт, профиль), одна
357
+ спокойная палитра без случайной подсветки чисел, этапы с итоговым временем, итоговая панель
358
+ (оценка, страны, счётчики статусов, штрафы, сценарии атак), разделы с тонкой линией и
359
+ счётчиками, ID проверок справа, в конце — файлы и «Что сделать первым». Меню сгруппировано
360
+ (проверка / данные / сервис) с подсказками к каждому пункту. Версия — 1.1.0.
361
+ - В режиме `--browser` заголовки безопасности и флаги cookies берутся из ответа, который получил
362
+ браузер, если обычный запрос упёрся в защиту от ботов: CSP, HSTS и заголовки больше не
363
+ «не проверено» на таких сайтах.
364
+ - Санкционная проверка руководителя учитывает отчество: запись списка с тем же именем, но другим
365
+ отчеством (в списках оно обычно есть) больше не считается совпадением; совпадение по полному
366
+ ФИО отмечено отдельно.
367
+ - База сервисов пополнена по данным живых проверок (`suveren unknown`): Hybrid, Aidata,
368
+ UXFeedback, счётчик Mediascope, СберМаркетинг; Adfox распознаётся на всех своих доменах,
369
+ Яндекс Метрика — и по Вебвизору. Всего 200 сервисов.
370
+ - Сигнатуры захвата поддоменов вынесены в `src/suveren/data/takeover.json` с источником,
371
+ лицензией и датой сверки; загруженные совпадают с прежними до символа (42 сервиса).
372
+ - «Цепочки сторонних скриптов» (`SUV-WEB-011`, режим `--browser`): браузер сообщает, какой скрипт
373
+ подключил какой, и отчёт показывает код, который выбрал не владелец сайта, а чужие скрипты,
374
+ со схемой цепочек (inline SVG). На skillbox.ru 18 из 22 сторонних компаний подключены так.
375
+ - Документация по каждой проверке: `docs/checks.md` (ID, что проверяет, модуль, теги) создаётся
376
+ командой `suveren checks --markdown`; тест следит, чтобы она не отставала от кода.
377
+ - Меню (exe): при проверке сайта можно выбрать глубокую проверку с экспериментом отключения и
378
+ сохранить PDF.
379
+ - `--pdf` в `scan` и `company`: отчёт в PDF (A4) через браузер Playwright, блоки «Как
380
+ переехать» раскрыты, карточки не разрываются между страницами.
381
+ - Отчёты под брендом студии: `--brand-name`, `--brand-logo`, `--brand-url` в `scan`, `batch` и
382
+ `company` (или переменные `SUVEREN_BRAND_*`) — название и логотип в шапке, ссылка в подвале;
383
+ строка «Сделано с помощью Suveren» остаётся.
384
+ - План миграции: у каждой иностранной зависимости в HTML-отчёте — блок «Как переехать» (шаги,
385
+ российские альтернативы, сложность и ориентир по стоимости без цен), в конце отчёта —
386
+ «План переезда», отсортированный по принципу «опаснее и проще — раньше». В JSON — поле
387
+ `migration`. Данные для всех 19 категорий — в `src/suveren/data/migration.json`.
388
+ - Новая проверка «Уязвимые версии библиотек» (`SUV-WEB-010`): jQuery, jQuery UI, Bootstrap,
389
+ lodash, Moment.js и AngularJS с явно указанной версией (имя файла, путь CDN, заголовок
390
+ библиотеки) сверяются с 12 CVE из NVD (диапазоны версий из API NVD, 20.10.2026) и с концом
391
+ поддержки AngularJS 1.x. Версии не угадываются; статус — «внимание». База — в
392
+ `src/suveren/data/vulns.json`. Живой прогон нашёл старые jQuery на drom.ru (1.12.4),
393
+ kontur.ru (1.11.3) и skillbox.ru (3.4.1 внутри бандла).
394
+ - Раздел «Сценарии атак» в начале отчёта (`SUV-ATK-001`…`006`): находки разных модулей
395
+ складываются в цепочки — фишинг от имени компании, чужой код у всех посетителей, захват
396
+ поддомена, перехват при первом заходе, утёкший ключ, отключение сайта и почты. Сценарий
397
+ появляется, только если найдены все звенья; шаги ссылаются на ID проверок, совет называет
398
+ одно действие, которое рвёт цепочку.
399
+ - Новая проверка «Ключи и секреты в коде сайта» (`SUV-WEB-009`): собственные скрипты сайта
400
+ (до 15) и скрипты внутри страниц ищутся по форматам ключей провайдеров. Секрет, который не
401
+ должен попадать в браузер (приватный ключ, токен Telegram-бота, ключи AWS, Stripe, GitHub,
402
+ Slack, SendGrid, OpenAI, Anthropic, Яндекса), — «проблема» с советом отозвать ключ; ключ
403
+ Google API — «внимание». Значения маскируются, заглушки вида `your-key` не срабатывают.
404
+ Библиотеки с чужих CDN не читаются. На 14 крупных сайтах ложных срабатываний нет.
405
+ - **Эксперимент «Что сломается при отключении» (`scan --cutoff`, входит в `--profile deep`).**
406
+ Сайт открывается в браузере как обычно, без иностранных сервисов и снова как обычно;
407
+ отчёт показывает два скриншота и что перестало работать: новые ошибки JavaScript, пропавшие
408
+ кнопки и встроенные блоки, формы с иностранной капчей, картинки, шрифты, пропавший текст,
409
+ изменение первого экрана сверх обычного шума. Ресурсы в `<head>`, из-за которых при зависшем
410
+ сервисе страница останется белой, перечислены отдельно. Проверка уровня инфраструктуры
411
+ показывает, отключат ли иностранные DNS, хостинг, CDN или почта сайт целиком. Новые ID:
412
+ `SUV-CUT-001`, `SUV-CUT-002`.
413
+ - Ядро проверок. У каждой проверки постоянный ID (`SUV-MAIL-003`, `SUV-LAW-001`…), раздел,
414
+ теги и описание в каталоге `catalog.py`; ID есть в HTML-отчёте, JSON (поле `id`) и SARIF.
415
+ Иностранные зависимости получили ID по категории (`SUV-DEP-HOSTING`). Штрафы привязаны к ID
416
+ проверок, а не к их названиям.
417
+ - База сервисов вынесена из кода в файл данных `src/suveren/data/services.json`: чтобы
418
+ добавить сервис, не нужно знать Python, каждую запись проверяет тест. Загруженная база
419
+ совпадает с прежней до символа (195 сервисов).
420
+ - `suveren checks [ID или тег]` — список всех проверок, как `nmap --script-help`.
421
+ - `scan` и `batch`: `--checks` (только выбранные проверки по ID, префиксу или тегу; запускаются
422
+ только нужные модули) и `--profile quick|standard|deep` (deep — браузер и до 12 страниц).
423
+ - `scan --sarif файл` — результат в SARIF 2.1.0 для GitHub/GitLab code scanning и DefectDojo.
424
+ - `scan --fail-on fail|warn` — код выхода 4, если есть проверки с таким статусом (для CI).
425
+ - В `summary.csv` у `batch` новая колонка штрафов добавлена в конец, порядок старых не изменился.
426
+ - Вход через иностранные сервисы: статьи справки (`/help/login/google`), страницы авторов
427
+ (`/authors/?provider=google`) и кнопки «Apple Pay» / «Google Pay» больше не считаются входом;
428
+ если «Войти» ведёт на чужой домен (Яндекс ID, корпоративный SSO), его кнопки и ссылка на
429
+ политику не приписываются сайту.
430
+ - Согласие считается «не отдельным», только если оба документа в одной фразе: список
431
+ документов в подвале и два отдельных чекбокса — не нарушение.
432
+ - Штрафы для ИП по ст. 13.11 ч. 8 и ч. 10 считаются как для юрлица (примечание 1 к ст. 13.11).
433
+ - Эталонный корпус (`tests/golden.py`): реальные сайты записываются байт в байт и
434
+ прогоняются офлайн при каждом изменении; тест показывает, где поменялись выводы. Хранится
435
+ только локально. Первый прогон на 20 сайтах нашёл три ложные «проблемы», исправлены ниже.
436
+ - Политика ищется и на странице со списком документов («Правила и лицензии», «Документы»,
437
+ «Договоры»), если прямой ссылки нет: у beget.com из-за этого была ложная «проблема» со
438
+ штрафом. Ссылки оглавления на саму страницу пропускаются, берётся сам документ (PDF).
439
+ - Номер подарочной карты, промокод и код из СМС в поле `tel` больше не считаются
440
+ персональными данными (vkusvill.ru); решает имя поля, а не CSS-классы.
441
+ - Первый шаг многошаговой формы без согласия («Отправим СМС… Продолжить») — «внимание», а не
442
+ «проблема»: согласие может быть на следующем шаге, которого нет в HTML (profi.ru, sravni.ru).
443
+
444
+ - Калькулятор штрафов: над разделом 152-ФЗ — «Потенциальные штрафы: до N ₽» со статьёй КоАП
445
+ и вилкой для юрлица, ИП, должностного лица и при повторном нарушении. В сумму входят
446
+ найденные проблемы (ст. 13.11 ч. 1, 3 и 8, ст. 13.55) с условием, если оно есть;
447
+ предупреждение о реестре операторов (ч. 10) показано, но не суммируется.
448
+ В терминале одна строка с итогом, в JSON поле `fines`. Суммы сверены с КоАП в ред. от
449
+ 04.08.2026. Это оценка максимального риска, а не юридическое заключение.
450
+ - Ссылка на политику и согласие ищутся и на странице входа: у drom.ru политика есть только там.
451
+ - Страница входа, которая оказалась заглушкой защиты от ботов (drom.ru перенаправляет на
452
+ /verify), не считается проверенной: вход через иностранные сервисы — «не проверено».
453
+ - «Содержание политики» сверяется с рекомендациями Роскомнадзора от 31.07.2017 (10 разделов
454
+ вместо 4 ключевых слов) и показывает, каких не хватает. Если по ссылке на политику пришла
455
+ оболочка сайта без её текста, проверка пишет «не проверено», а не «нет 11 разделов».
456
+ - Новая проверка «Отдельное согласие на обработку» (152-ФЗ, ст. 9 ч. 1 в ред. 156-ФЗ, с
457
+ 1 сентября 2025 года): согласие, объединённое в одной фразе с офертой, пользовательским
458
+ соглашением или правилами сайта, и «использование сайта означает согласие на обработку» в
459
+ политике или соглашении. Текст формы теперь делится на отдельные подписи, чтобы два
460
+ соседних чекбокса не читались как одна фраза.
461
+ - Новый раздел «Другие требования законов» и в нём проверка «Вход через иностранные сервисы»
462
+ (149-ФЗ, ст. 8 ч. 10 в редакции 406-ФЗ; штраф по ст. 13.55 КоАП с 7 июля 2026 года). Ищутся
463
+ OAuth-ссылки, собственные адреса входа сайта (`/auth/google`), виджеты (Google One Tap, «Вход
464
+ с Apple», Telegram Login, uLogin) и надписи на кнопках, в том числе на странице «Войти» /
465
+ «Личный кабинет», которая теперь тоже открывается. VK ID, Яндекс ID, Госуслуги, Сбер ID и
466
+ Т-ID отмечаются как разрешённые способы. Обычные ссылки на Google, App Store, каналы в
467
+ соцсетях и тексты статей не срабатывают.
468
+ - Новый раздел «Безопасность сайта»: сторонние скрипты, скрипты с брошенных (подтверждается
469
+ через RDAP/WHOIS) и скомпрометированных доменов (polyfill.io и связанные с ним), SRI для
470
+ библиотек с публичных CDN, CSP, HSTS, защитные заголовки, флаги сессионных cookies, срок
471
+ сертификата и устаревшие TLS 1.0/1.1. Заголовки заглушек защиты от ботов и чужих доменов
472
+ после перенаправления не оцениваются. Отключается `--no-security`.
473
+ - Пример SPF-записи в совете подбирается по почтовому провайдеру домена (Google Workspace,
474
+ Яндекс 360, VK WorkSpace, Microsoft 365, хостинги и др.), а не всегда для Яндекс 360.
475
+ - Новый раздел «Поддомены»: список поддоменов из журналов сертификатов (crt.sh, запасной
476
+ источник — Cert Spotter, кэш на сутки) и поиск возможного захвата: CNAME на свободный домен
477
+ (подтверждается через RDAP/WHOIS), на удалённый облачный ресурс или на сервис, который
478
+ отвечает «такого проекта нет» (сигнатуры can-i-take-over-xyz). Отключается
479
+ `--no-subdomains`, в `doctor` добавлена проверка crt.sh.
480
+ - Новый раздел «Защита почты от подделки»: можно ли подделать письмо от имени домена
481
+ (да / частично / нет) по DMARC, плюс проверки SPF (одна запись, `+all`/`?all`, лимит в 10
482
+ DNS-запросов по всей цепочке `include`), DMARC (`p`, `sp`, `pct`, `rua`), DKIM по
483
+ распространённым селекторам, MTA-STS и TLS-RPT. Работает в `scan` и `batch`, отключается
484
+ `--no-mail`.
485
+ - Одинаковые советы от разных проверок показываются в списке «что сделать» один раз.
486
+ - DNS: если системный DNS отвечает неполно, используется Яндекс DNS по защищённому каналу
487
+ (DNS-over-TLS), а не Google и Cloudflare. Зарубежный DoH — только последний вариант, и отчёт
488
+ об этом прямо пишет. `--dns ru` вообще не отправляет запросы за рубеж.
489
+ - Оценка больше не «прыгает» между запусками: у DNS- и почтовых серверов с несколькими
490
+ адресами учитываются все их сети, а не случайный первый адрес.
491
+ - Сервисы ищутся в тегах и скриптах страницы, а не в её тексте: статья, где упомянут
492
+ googletagmanager.com, или закомментированный тег больше не считаются зависимостью.
493
+ - Собственная сеть компании (например, почта Хабра на Habr Europe OU) показывается как
494
+ «Собственная сеть» с низким риском, а не как иностранный провайдер.
495
+ - GitHub Actions закреплены по SHA коммитов, обновления предлагает Dependabot.
496
+ - `suveren doctor --abroad`: при запуске не из России недоступные ЕГРЮЛ и реестр операторов
497
+ ПДн помечаются «не проверен», а не ошибкой. Ежедневная проверка источников на GitHub больше
498
+ не падает из-за того, что госсайты не отвечают зарубежным серверам.
499
+
500
+ ## 1.0.0 — 2026-10-06
501
+
502
+ Первый публичный выпуск.
503
+
504
+ ### Проверка сайта (`scan`)
505
+
506
+ - Зависимости от иностранных DNS, почты, хостинга, CDN, конструкторов сайтов и CMS,
507
+ регистратора, доменной зоны, SSL-сертификата и скриптов на странице по базе из 180+ сервисов.
508
+ - Оценка независимости от A до F, список «что сделать в первую очередь», российские альтернативы,
509
+ учёт резервирования.
510
+ - Пометки о сервисах, заблокированных или не работающих в России, и о скомпрометированном
511
+ polyfill.io.
512
+ - 152-ФЗ: политика обработки ПДн и её содержание (с запасным поиском в пользовательском
513
+ соглашении), согласие в формах, cookies, трансграничная передача, локализация, HTTPS,
514
+ реестр операторов ПДн по ИНН с сайта.
515
+ - Реестр блокировок Роскомнадзора: домен и IP-адреса.
516
+ - Режим `--browser`: сайт открывается в установленном Edge или Chrome, учитываются все
517
+ сетевые запросы страницы.
518
+ - Распознавание страниц-заглушек защиты от ботов и пустых JS-оболочек: вместо ложных ошибок
519
+ проверки помечаются как «не проверено».
520
+ - Обход внутренних страниц (`--pages`, по умолчанию 6): «Контакты», заявки, корзина, доставка
521
+ проверяются в первую очередь; сервисы и формы с них попадают в отчёт с указанием страницы.
522
+ - Поддержка сертификатов НУЦ Минцифры.
523
+ - `--quick`, `--details`, `--fail-under`, `--json`, сравнение двух проверок командой `diff`.
524
+
525
+ ### Массовая проверка (`batch`)
526
+
527
+ - Список сайтов из файла, параллельная проверка, сводный `index.html`, `summary.csv` и отчёт по
528
+ каждому сайту.
529
+
530
+ ### Проверка компании (`company`)
531
+
532
+ - Данные ЕГРЮЛ/ЕГРИП по ИНН, ОГРН или домену: статус, возраст, руководитель.
533
+ - Скрининг компании и руководителя по санкционным спискам США, ЕС и Великобритании с учётом
534
+ транслитерации.
535
+ - Сверка ИНН на сайте с владельцем домена в WHOIS.
536
+ - Команда `update` для обновления списков; кэш на сутки.
537
+
538
+ ### Прочее
539
+
540
+ - `Suveren.exe` для Windows: работает без Python, двойной клик открывает меню; собирается
541
+ автоматически при выпуске версии.
542
+ - `suveren menu`, `suveren doctor` (проверка источников, ежедневно в CI), `suveren unknown`
543
+ (домены, которых нет в базе).
544
+ - У каждой проверки 152-ФЗ указана статья закона со ссылкой на текст; формулировки смягчены
545
+ там, где вывод основан на признаках.
546
+ - Шаблоны issue «Добавить сервис» и «Ошибка».
547
+
548
+ - Новый вывод в терминале: сводная панель, этапы проверки с временем, компактные разделы.
549
+ - HTML- и JSON-отчёты, тёмная и светлая тема.
550
+ - `Suveren.bat` — меню для Windows без командной строки, `Dockerfile`.