Files
dokogen187/dokogen/threat_rules.py
T
prog1764 7158a90b76 feat: вкладка «Безопасность» — коды УБИ, автоподбор угроз (программа/ИИ), сравнение, новый UI
- угрозы ФСТЭК теперь с кодами: «УБИ.001 Угроза …» (парсинг кода из БДУ, 227 шт)
- threat_rules.py — логика программы: по характеристикам объекта (тип ИС/АСУ/ИТКС,
  интернет да/нет, нарушитель внутр./внешн.) фильтрует перечень по ключевым словам
- verify.py: suggest_threats_ai — ИИ предлагает актуальные угрозы по описанию объекта
- вкладка перестроена: компактная 2-колоночная раскладка, панель действий сверху:
  «⬇️ Загрузить угрозы ФСТЭК» | «⚙️ Подобрать (программа)» | «🤖 Подобрать (ИИ)» |
  «⚖️ Сравнить» | «🤖 Проверить безопасность через ИИ»
- окно выбора из словаря: 720x680 + поле поиска (фильтр по тексту, важно для 227 угроз)
- сравнение программа↔ИИ: окно с двумя списками, совпавшие подсвечены зелёным
2026-08-04 17:44:54 +04:00

169 lines
6.6 KiB
Python

# -*- coding: utf-8 -*-
"""DokoGen — автоматический подбор угроз безопасности информации (236 приказ).
Логика программы: по характеристикам объекта КИИ (тип, сеть, нарушитель)
подбирает актуальные угрозы из перечня БДУ ФСТЭК (threats_fstec.json).
Если перечень не скачан — использует базовый threats.json.
Правила эвристические; при наличии методического документа (например,
«Методика оценки угроз безопасности информации» ФСТЭК) — адаптируются под него.
"""
import os
import json
def load_threats(dict_dir=None):
"""Список угроз: сначала скачанный с ФСТЭК (с кодами УБИ), иначе базовый."""
if dict_dir is None:
dict_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dictionaries')
fstec = os.path.join(dict_dir, 'threats_fstec.json')
if os.path.exists(fstec):
try:
with open(fstec, encoding='utf-8') as f:
data = json.load(f)
if data:
return data
except Exception:
pass
base = os.path.join(dict_dir, 'threats.json')
try:
with open(base, encoding='utf-8') as f:
return json.load(f)
except Exception:
return []
# ---------------------------------------------------------------
# Ключевые слова (в названии угрозы) по признакам объекта
# ---------------------------------------------------------------
# Базовый набор — актуален для любого объекта КИИ
BASE_KEYWORDS = [
'вредоносн', 'несанкционированн', 'утечк', 'перехват', 'модификац',
'уничтожен', 'отказ в обслуживании', 'нсд', 'биос', 'пароль',
'криптографическ', 'шифрован', 'аутентификац',
]
# Объект имеет подключение к сети Интернет / сеть электросвязи
INTERNET_KEYWORDS = [
'удалённ', 'удаленн', 'сетев', 'фишинг', 'ddos', 'отказ в обслуживании',
'веб', 'электронн', 'почт', 'беспроводн', 'wi-fi', 'wifi', 'vpn',
'маршрутизац', 'протокол', 'интернет',
]
# Объект изолирован (нет подключения к Интернету)
ISOLATED_KEYWORDS = [
'съёмн', 'съемн', 'физическ', 'локальн', 'инсайд', 'носител',
'биос', 'желез', 'аппаратн',
]
# АСУ / технологический процесс
ASU_KEYWORDS = [
'технологическ', 'асутп', 'промышленн', 'scada', 'датчик',
'исполнительн', 'технологическ', 'производств', 'процесс',
]
# ИТКС / сети связи
NETWORK_KEYWORDS = [
'сеть', 'канал', 'протокол', 'маршрутизац', 'связ', 'передач',
'коммутац', 'трафик',
]
# Внутренний нарушитель
INSIDER_KEYWORDS = [
'инсайд', 'сотрудник', 'привилегированн', 'администратор',
'внутренн', 'персонал',
]
# Внешний нарушитель
EXTERNAL_KEYWORDS = [
'внешн', 'удалённ', 'удаленн', 'дистанционн',
]
def _is_asu(isys) -> bool:
t = (getattr(isys, 'object_type', '') or '').lower()
return 'асу' in t or 'автоматизированн' in t or 'технологическ' in t
def _is_network(isys) -> bool:
t = (getattr(isys, 'object_type', '') or '').lower()
return 'телекоммуникац' in t or 'сеть' in t
def _has_internet(isys) -> bool:
"""Есть ли подключение к сети Интернет / внешним сетям."""
if getattr(isys, 'is_internet', False):
return True
# По описанию / сетям: «отсутствует подключение к сети Интернет» → нет
desc = ((getattr(isys, 'description', '') or '') + ' ' +
(getattr(isys, 'net_operator', '') or '')).lower()
if 'отсутств' in desc and ('интернет' in desc or 'подключ' in desc):
return False
net_category = (getattr(isys, 'net_category', '') or '').lower()
if net_category:
return True
# По умолчанию считаем, что сеть есть (осторожная оценка)
return True
def _is_insider(isys) -> bool:
a = (getattr(isys, 'attacker_category', '') or '').lower()
return 'внутрен' in a
def _score(threat_lower, keywords):
"""Сколько ключевых слов встретилось в названии угрозы."""
return sum(1 for kw in keywords if kw in threat_lower)
def suggest_threats_program(isys, threats=None, limit=15):
"""Подбор угроз по характеристикам объекта КИИ.
Возвращает список строк (с кодами УБИ, если они есть в перечне).
"""
if threats is None:
threats = load_threats()
if not threats:
return []
has_internet = _has_internet(isys)
is_asu = _is_asu(isys)
is_network = _is_network(isys)
insider = _is_insider(isys)
# Собираем активные наборы ключевых слов
active = list(BASE_KEYWORDS)
if has_internet:
active += INTERNET_KEYWORDS
else:
active += ISOLATED_KEYWORDS
if is_asu:
active += ASU_KEYWORDS
if is_network:
active += NETWORK_KEYWORDS
if insider:
active += INSIDER_KEYWORDS
else:
active += EXTERNAL_KEYWORDS
scored = []
for t in threats:
tl = str(t).lower()
s = _score(tl, active)
if s > 0:
scored.append((s, t))
# Сортировка: больше совпадений — выше; убираем дубликаты, сохраняя порядок
scored.sort(key=lambda x: -x[0])
result = []
seen = set()
for s, t in scored:
if t in seen:
continue
seen.add(t)
result.append(t)
if len(result) >= limit:
break
return result