e5f25f1c2c
CD - Develop / build-and-deploy (push) Failing after 11m31s
- Spring Boot service on port 8095, database hiveiq_aria - Flyway schema: threat_ioc, threat_event, precursor_sequence_alert, device_security_posture - FBI FLASH-20260219-001 IOC seed data (executables, MD5s, directories, registry keys, services) - ThreatEventClassifier — maps 20+ signal keys to severity + attack phase + IOC match - ThreatEventIngestionService — persists events, publishes HIGH/CRITICAL to Kafka - Kafka producer: topic hiveops.aria.threats (AriaThreatEvent payload) - ThreatIocController — CRUD IOC management (MSP_ADMIN/BCOS_ADMIN) - ThreatEventIngestionController — agent-facing ingest endpoint with service secret auth - CI/CD: cd-develop.yml + cd-main.yml
171 lines
6.7 KiB
Java
171 lines
6.7 KiB
Java
package com.hiveops.aria.service;
|
|
|
|
import com.hiveops.aria.dto.ThreatEventItem;
|
|
import com.hiveops.aria.entity.ThreatEvent;
|
|
import com.hiveops.aria.entity.ThreatEvent.AttackPhase;
|
|
import com.hiveops.aria.entity.ThreatEvent.ThreatSeverity;
|
|
import com.hiveops.aria.entity.ThreatIoc;
|
|
import com.hiveops.aria.repository.ThreatIocRepository;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import java.util.Optional;
|
|
|
|
@Service
|
|
@RequiredArgsConstructor
|
|
@Slf4j
|
|
public class ThreatEventClassifier {
|
|
|
|
private final ThreatIocRepository iocRepository;
|
|
|
|
public ClassificationResult classify(ThreatEventItem item) {
|
|
String signal = item.getSignalKey();
|
|
ThreatSeverity severity;
|
|
AttackPhase phase = null;
|
|
ThreatIoc matchedIoc = null;
|
|
|
|
switch (signal) {
|
|
// Windows Security Event IDs — FBI FLASH-20260219-001
|
|
case "EVENT_1102" -> {
|
|
severity = ThreatSeverity.CRITICAL;
|
|
phase = AttackPhase.CLEANUP;
|
|
}
|
|
case "EVENT_4719" -> {
|
|
severity = ThreatSeverity.HIGH;
|
|
phase = AttackPhase.CLEANUP;
|
|
}
|
|
case "EVENT_6416", "EVENT_2003" -> {
|
|
severity = ThreatSeverity.HIGH;
|
|
phase = AttackPhase.PHYSICAL_ACCESS;
|
|
matchedIoc = matchUsb(item);
|
|
}
|
|
case "EVENT_4663" -> {
|
|
severity = ThreatSeverity.HIGH;
|
|
phase = AttackPhase.MALWARE_STAGING;
|
|
matchedIoc = matchFilePath(item);
|
|
}
|
|
case "EVENT_4688" -> {
|
|
matchedIoc = matchProcessName(item);
|
|
if (matchedIoc != null) {
|
|
severity = ThreatSeverity.HIGH;
|
|
phase = AttackPhase.MALWARE_EXECUTION;
|
|
} else {
|
|
severity = ThreatSeverity.INFO;
|
|
}
|
|
}
|
|
case "EVENT_4697" -> {
|
|
matchedIoc = matchServiceName(item);
|
|
if (matchedIoc != null) {
|
|
severity = ThreatSeverity.HIGH;
|
|
phase = AttackPhase.PERSISTENCE;
|
|
} else {
|
|
severity = ThreatSeverity.MEDIUM;
|
|
phase = AttackPhase.PERSISTENCE;
|
|
}
|
|
}
|
|
// Physical sensor signals
|
|
case "DOOR_OPEN" -> {
|
|
severity = ThreatSeverity.MEDIUM;
|
|
phase = AttackPhase.PHYSICAL_ACCESS;
|
|
}
|
|
case "CARD_READER_TAMPER" -> {
|
|
severity = ThreatSeverity.HIGH;
|
|
phase = AttackPhase.PHYSICAL_ACCESS;
|
|
}
|
|
case "USB_INSERTION" -> {
|
|
severity = ThreatSeverity.HIGH;
|
|
phase = AttackPhase.PHYSICAL_ACCESS;
|
|
}
|
|
case "POWER_CYCLE_UNEXPECTED", "OUT_OF_SERVICE" -> {
|
|
severity = ThreatSeverity.MEDIUM;
|
|
}
|
|
case "VIBRATION_ANOMALY" -> {
|
|
severity = ThreatSeverity.MEDIUM;
|
|
}
|
|
case "CASH_LOW_UNEXPECTED" -> {
|
|
severity = ThreatSeverity.HIGH;
|
|
}
|
|
// File system signals
|
|
case "HASH_DRIFT" -> {
|
|
severity = ThreatSeverity.HIGH;
|
|
phase = AttackPhase.MALWARE_STAGING;
|
|
}
|
|
case "IOC_PROCESS_MATCH", "IOC_FILENAME_MATCH" -> {
|
|
matchedIoc = matchProcessName(item);
|
|
severity = ThreatSeverity.HIGH;
|
|
phase = AttackPhase.MALWARE_EXECUTION;
|
|
}
|
|
case "REGISTRY_PERSISTENCE_DETECTED" -> {
|
|
matchedIoc = matchRegistryKey(item);
|
|
severity = ThreatSeverity.HIGH;
|
|
phase = AttackPhase.PERSISTENCE;
|
|
}
|
|
case "SUSPICIOUS_DIRECTORY" -> {
|
|
matchedIoc = matchDirectory(item);
|
|
severity = ThreatSeverity.HIGH;
|
|
phase = AttackPhase.MALWARE_STAGING;
|
|
}
|
|
case "REMOTE_TOOL_DETECTED" -> {
|
|
matchedIoc = matchProcessName(item);
|
|
severity = ThreatSeverity.MEDIUM;
|
|
}
|
|
default -> {
|
|
severity = ThreatSeverity.INFO;
|
|
log.debug("Unclassified signal key: {}", signal);
|
|
}
|
|
}
|
|
|
|
return new ClassificationResult(severity, phase, matchedIoc);
|
|
}
|
|
|
|
private ThreatIoc matchProcessName(ThreatEventItem item) {
|
|
String name = extractString(item, "processName", "imageName", "fileName");
|
|
if (name == null) return null;
|
|
return iocRepository.findByActiveTrueAndIocTypeAndValueIgnoreCase(ThreatIoc.IocType.FILENAME, name).orElse(null);
|
|
}
|
|
|
|
private ThreatIoc matchServiceName(ThreatEventItem item) {
|
|
String name = extractString(item, "serviceName", "serviceDisplayName");
|
|
if (name == null) return null;
|
|
return iocRepository.findByActiveTrueAndIocTypeAndValueIgnoreCase(ThreatIoc.IocType.SERVICE_NAME, name).orElse(null);
|
|
}
|
|
|
|
private ThreatIoc matchFilePath(ThreatEventItem item) {
|
|
String path = extractString(item, "objectName", "filePath");
|
|
if (path == null) return null;
|
|
Optional<ThreatIoc> dir = iocRepository.findByActiveTrueAndIocTypeAndValueIgnoreCase(ThreatIoc.IocType.DIRECTORY, path);
|
|
return dir.orElseGet(() ->
|
|
iocRepository.findByActiveTrueAndIocTypeAndValueIgnoreCase(ThreatIoc.IocType.FILE_PATH, path).orElse(null));
|
|
}
|
|
|
|
private ThreatIoc matchRegistryKey(ThreatEventItem item) {
|
|
String key = extractString(item, "registryKey", "keyPath");
|
|
if (key == null) return null;
|
|
return iocRepository.findByActiveTrueAndIocTypeAndValueIgnoreCase(ThreatIoc.IocType.REGISTRY_KEY, key).orElse(null);
|
|
}
|
|
|
|
private ThreatIoc matchDirectory(ThreatEventItem item) {
|
|
String path = extractString(item, "directory", "path");
|
|
if (path == null) return null;
|
|
return iocRepository.findByActiveTrueAndIocTypeAndValueIgnoreCase(ThreatIoc.IocType.DIRECTORY, path).orElse(null);
|
|
}
|
|
|
|
private ThreatIoc matchUsb(ThreatEventItem item) {
|
|
String deviceId = extractString(item, "deviceId", "deviceDescription");
|
|
if (deviceId == null) return null;
|
|
return iocRepository.findByActiveTrueAndIocTypeAndValueIgnoreCase(ThreatIoc.IocType.IP, deviceId).orElse(null);
|
|
}
|
|
|
|
private String extractString(ThreatEventItem item, String... keys) {
|
|
if (item.getRawPayload() == null) return null;
|
|
for (String key : keys) {
|
|
Object val = item.getRawPayload().get(key);
|
|
if (val instanceof String s && !s.isBlank()) return s;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public record ClassificationResult(ThreatSeverity severity, AttackPhase attackPhase, ThreatIoc matchedIoc) {}
|
|
}
|