fix: аудит ветки source — критические и высокие замечания

- api_settings: убраны реальные API-ключи из кода (чтение из env/файла) — 1.1
- variables: {{is_security_requirements}} больше не подставляет всегда УЗ-4;
  extended_fields/defence_level передаются в контекст — 1.2
- defence.py: эталонная таблица ПП-1119 (п.9-12), 48 комбинаций;
  ui.py: авторасчёт УЗ переведён на неё (public больше не 'всегда 4',
  спец/био/общедоступные/иные разделены) — 2.1
- generator: убран дубль хвоста параграфа в _highlight_unmatched — 2.2
- importer: категории ПДн взаимоисключающие ('иные' только если нет
  спец/био); УЗ пересчитывается при импорте — 2.3
- import_settings(.py/.json): убран перехват 'администратора безопасности'
  правилом ответственного за ПДн — 3.1
- variables: {{category}}/{{is.category}} резолвятся из personal_data_category — 3.2
- verify: ИИ-предложения ИНН/ОГРН/КПП валидируются контрольными суммами
  перед применением — 3.3
- models: defence_level None вместо '' (не маскирует 'не рассчитано')
- .gitignore: api_settings.json/.env не попадают в git
- test_defence.py: юнит-тест всех 48 комбинаций таблицы УЗ
This commit is contained in:
LocoAgent
2026-08-05 08:58:17 +04:00
parent ac122451b3
commit 230a392a15
12 changed files with 277 additions and 57 deletions
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Юнит-тесты расчёта УЗ по ПП РФ № 1119 (все комбинации таблицы).
Эталонные значения — из текста ПП-1119 п. 9–12 (сверено с legalacts.ru).
Комбинации: 4 группы категорий × 2 признака «только сотрудники» ×
2 объёма (>100к) × 3 типа угроз = 48.
"""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from dokogen.defence import calc_defence_level
def _cases():
"""(categories, employees, large, threat, expected_uz)"""
c = []
# --- Специальные ---
for emp in (False, True):
for large in (False, True):
c.append((['специальные'], emp, large, '1', 1)) # п.9а
c.append((['специальные'], emp, large, '2',
1 if (not emp and large) else 2)) # п.9б / п.10б
c.append((['специальные'], emp, large, '3',
2 if (not emp and large) else 3)) # п.10е / п.11в
# --- Биометрические (без разбивки по количеству) ---
for emp in (False, True):
for large in (False, True):
c.append((['биометрические'], emp, large, '1', 1)) # п.9а
c.append((['биометрические'], emp, large, '2', 2)) # п.10в
c.append((['биометрические'], emp, large, '3', 3)) # п.11г
# --- Общедоступные ---
for emp in (False, True):
for large in (False, True):
c.append((['общедоступные'], emp, large, '1', 2)) # п.10а
c.append((['общедоступные'], emp, large, '2',
2 if (not emp and large) else 3)) # п.10г / п.11а
c.append((['общедоступные'], emp, large, '3', 4)) # п.12а
# --- Иные ---
for emp in (False, True):
for large in (False, True):
c.append((['иные'], emp, large, '1', 1)) # п.9а
c.append((['иные'], emp, large, '2',
2 if (not emp and large) else 3)) # п.10д / п.11б
c.append((['иные'], emp, large, '3',
3 if (not emp and large) else 4)) # п.11д / п.12б
return c
def main():
cases = _cases()
assert len(cases) == 48, f"ожидалось 48 комбинаций, получено {len(cases)}"
failed = 0
for cats, emp, large, threat, expected in cases:
got = calc_defence_level(cats, emp, large, threat)
if got != expected:
failed += 1
print(f"{cats} emp={emp} large={large} threat={threat}: "
f"ожидалось УЗ-{expected}, получено УЗ-{got}")
if failed:
print(f"\nFAILED: {failed} из {len(cases)}")
return 1
print(f"✅ УЗ по ПП-1119: все {len(cases)} комбинаций верны")
return 0
if __name__ == '__main__':
sys.exit(main())