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
+86
View File
@@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
"""DokoGen — расчёт уровня защищённости ПДн по ПП РФ № 1119 от 01.11.2012.
Эталонная таблица (п. 9–12 ПП-1119), сверено с текстом постановления:
Специальные категории:
- угрозы 1 типа → УЗ-1 (любое количество, п.9а)
- угрозы 2 типа: >100к не-сотрудников → УЗ-1 (п.9б); сотрудники или ≤100к → УЗ-2 (п.10б)
- угрозы 3 типа: >100к не-сотрудников → УЗ-2 (п.10е); сотрудники или ≤100к → УЗ-3 (п.11в)
Биометрические:
- угрозы 1 типа → УЗ-1 (п.9а); угрозы 2 типа → УЗ-2 (п.10в); угрозы 3 типа → УЗ-3 (п.11г)
(без разбивки по количеству)
Общедоступные:
- угрозы 1 типа → УЗ-2 (п.10а)
- угрозы 2 типа: >100к → УЗ-2 (п.10г); сотрудники или ≤100к → УЗ-3 (п.11а)
- угрозы 3 типа → УЗ-4 (п.12а)
Иные категории:
- угрозы 1 типа → УЗ-1 (п.9а)
- угрозы 2 типа: >100к не-сотрудников → УЗ-2 (п.10д); сотрудники или ≤100к → УЗ-3 (п.11б)
- угрозы 3 типа: >100к не-сотрудников → УЗ-3 (п.11д); сотрудники или ≤100к → УЗ-4 (п.12б)
Используется и в авторасчёте (ui.py), и при импорте (importer.py).
"""
from typing import List
def calc_defence_level(
categories: List[str],
is_employees: bool,
is_large: bool,
threat_type: str,
) -> int:
"""Расчёт уровня защищённости ПДн (1–4) по ПП РФ № 1119.
categories — список категорий ПДн ('специальные', 'биометрические',
'общедоступные', 'иные').
is_employees — True, если обрабатываются ПДн только сотрудников оператора.
is_large — True, если количество субъектов > 100 000.
threat_type — тип актуальных угроз: '1', '2' или '3'.
"""
threat = str(threat_type or '3').strip()
has = lambda *names: any(n in categories for n in names)
# Приоритет категорий: специальные > биометрические > общедоступные > иные
if has('специальные'):
return _special(threat, is_employees, is_large)
if has('биометрические'):
return _bio(threat)
if has('общедоступные') and not has('иные'):
return _public(threat, is_employees, is_large)
return _other(threat, is_employees, is_large)
def _special(threat: str, employees: bool, large: bool) -> int:
if threat == '1':
return 1
if threat == '2':
return 1 if (not employees and large) else 2
# threat == '3'
return 2 if (not employees and large) else 3
def _bio(threat: str) -> int:
return {'1': 1, '2': 2, '3': 3}.get(threat, 3)
def _public(threat: str, employees: bool, large: bool) -> int:
if threat == '1':
return 2
if threat == '2':
return 2 if (not employees and large) else 3
# threat == '3'
return 4
def _other(threat: str, employees: bool, large: bool) -> int:
if threat == '1':
return 1
if threat == '2':
return 2 if (not employees and large) else 3
# threat == '3'
return 3 if (not employees and large) else 4