mirror of
https://github.com/DaLaw2/NetGuardia.git
synced 2026-08-24 14:10:28 +09:00
feat: multi-task inference pipeline, remove backward compat
T6: Replace AE hard gate with multi-task model (3 output heads): - anomaly_head (sigmoid) > threshold → is_attack - class_head (softmax 7 classes) argmax → attack_type - c2_head (sigmoid) override when confidence > class_head C2 prob - "Normal" class → force benign, skip SOAR T7: Update SOAR routing for 7-class taxonomy: - C2 Communication → c2_communication - Cryptomining → cryptomining - DoS/DDoS (merged) → threat_detected - Normal → skip (no SOAR trigger) T8: Update models and config: - New classifier.onnx (950KB multi-task, 34 features, 3 outputs) - New deep_autoencoder.onnx (444KB, 33 features) - inference_config.json: 33→33 AE features, 7 classes, thresholds Remove all backward compatibility code: - Delete 8 pub use re-exports from model/mod.rs, fix ~30 imports - Delete config_loader::InferenceConfig type alias - Delete model/config re-export of system::config::* - Delete legacy single-output classifier inference path - Delete serde defaults for new MLInferenceConfig fields - Delete old attack label aliases (DDoS, DoS → only DoS/DDoS) - Delete SOAR condition_threshold legacy fallback - Delete feature_extractor old CSV column name aliases - Remove condition_threshold from runtime Playbook struct Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4216906dd3
commit
4bfb01c1a2
Binary file not shown.
Binary file not shown.
@ -1,11 +1,12 @@
|
||||
{
|
||||
"created_at": "2026-03-05T14:04:08.736531",
|
||||
"created_at": "2026-04-03T15:37:09.215359",
|
||||
"framework": "PyTorch",
|
||||
"model_type": "MultiTaskModel",
|
||||
"model": {
|
||||
"deep_autoencoder": {
|
||||
"file": "deep_autoencoder.onnx",
|
||||
"input_dim": 27,
|
||||
"encoding_dim": 16,
|
||||
"input_dim": 33,
|
||||
"encoding_dim": 24,
|
||||
"ae_feature_names": [
|
||||
"flow_duration",
|
||||
"fwd_packets",
|
||||
@ -33,15 +34,26 @@
|
||||
"fwd_pkt_len_std",
|
||||
"bwd_pkt_len_std",
|
||||
"fwd_seg_size_min",
|
||||
"fwd_act_data_pkts"
|
||||
"fwd_act_data_pkts",
|
||||
"fwd_iat_std",
|
||||
"bwd_iat_std",
|
||||
"flow_iat_std",
|
||||
"fwd_bwd_bytes_ratio",
|
||||
"pkt_len_variance",
|
||||
"fwd_iat_skewness"
|
||||
],
|
||||
"ae_threshold": 0.17166012525558472
|
||||
"ae_threshold": 0.23287349939346313
|
||||
},
|
||||
"classifier": {
|
||||
"file": "classifier.onnx",
|
||||
"type": "ResNet MLP",
|
||||
"n_features": 28,
|
||||
"n_classes": 5,
|
||||
"type": "MultiTaskModel",
|
||||
"n_features": 34,
|
||||
"n_classes": 7,
|
||||
"outputs": [
|
||||
"anomaly",
|
||||
"class_probs",
|
||||
"c2_score"
|
||||
],
|
||||
"classifier_feature_names": [
|
||||
"flow_duration",
|
||||
"fwd_packets",
|
||||
@ -70,6 +82,12 @@
|
||||
"bwd_pkt_len_std",
|
||||
"fwd_seg_size_min",
|
||||
"fwd_act_data_pkts",
|
||||
"fwd_iat_std",
|
||||
"bwd_iat_std",
|
||||
"flow_iat_std",
|
||||
"fwd_bwd_bytes_ratio",
|
||||
"pkt_len_variance",
|
||||
"fwd_iat_skewness",
|
||||
"ae_anomaly_score"
|
||||
]
|
||||
}
|
||||
@ -77,68 +95,68 @@
|
||||
"preprocessing": {
|
||||
"ae_clip_params": {
|
||||
"flow_duration": {
|
||||
"lower": 1.0,
|
||||
"upper": 118485085.19000001
|
||||
"lower": 0.0,
|
||||
"upper": 70550165.55999511
|
||||
},
|
||||
"fwd_packets": {
|
||||
"lower": 0.0,
|
||||
"upper": 54.0
|
||||
"lower": 1.0,
|
||||
"upper": 120.0
|
||||
},
|
||||
"bwd_packets": {
|
||||
"lower": 0.0,
|
||||
"upper": 82.0
|
||||
"upper": 112.0
|
||||
},
|
||||
"fwd_bytes": {
|
||||
"lower": 0.0,
|
||||
"upper": 6960.0
|
||||
"upper": 21320.685742187383
|
||||
},
|
||||
"bwd_bytes": {
|
||||
"lower": 0.0,
|
||||
"upper": 89558.6950000003
|
||||
"upper": 78556.0
|
||||
},
|
||||
"flow_bytes_per_sec": {
|
||||
"lower": 0.0,
|
||||
"upper": 12500000.0
|
||||
"upper": 1457941.3831249783
|
||||
},
|
||||
"flow_pkts_per_sec": {
|
||||
"lower": 0.0226829350855,
|
||||
"upper": 2000000.0
|
||||
"lower": 0.0,
|
||||
"upper": 30612.244140625
|
||||
},
|
||||
"fwd_win_bytes": {
|
||||
"lower": -1.0,
|
||||
"upper": 65535.0
|
||||
"lower": 0.0,
|
||||
"upper": 65280.0
|
||||
},
|
||||
"bwd_win_bytes": {
|
||||
"lower": -1.0,
|
||||
"upper": 64000.0
|
||||
"lower": 0.0,
|
||||
"upper": 65535.0
|
||||
},
|
||||
"fwd_pkt_len_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 744.7272727272729
|
||||
"upper": 1500.0
|
||||
},
|
||||
"bwd_pkt_len_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 1393.9601153594992
|
||||
"upper": 690.0833129882812
|
||||
},
|
||||
"fwd_iat_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 86366722.035
|
||||
"upper": 5468977.71249968
|
||||
},
|
||||
"bwd_iat_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 21700000.0
|
||||
"upper": 5162028.484999921
|
||||
},
|
||||
"flow_iat_mean": {
|
||||
"lower": 1.0,
|
||||
"upper": 86353400.64500001
|
||||
"lower": 0.0,
|
||||
"upper": 166524640.0
|
||||
},
|
||||
"pkt_len_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 945.0294113974604
|
||||
"upper": 1312.2810998535142
|
||||
},
|
||||
"dst_port": {
|
||||
"lower": 0.0,
|
||||
"upper": 64499.0
|
||||
"upper": 63375.0
|
||||
},
|
||||
"protocol": {
|
||||
"lower": 0.0,
|
||||
@ -146,103 +164,139 @@
|
||||
},
|
||||
"psh_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 240.0
|
||||
"upper": 1.0
|
||||
},
|
||||
"ack_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 655.0
|
||||
"upper": 1.0
|
||||
},
|
||||
"syn_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 4.0
|
||||
"upper": 1.0
|
||||
},
|
||||
"fin_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 2.0
|
||||
"upper": 1.0
|
||||
},
|
||||
"rst_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
"upper": 0.0
|
||||
},
|
||||
"pkt_len_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 943.9221842600019
|
||||
"upper": 1051.5718536376853
|
||||
},
|
||||
"fwd_pkt_len_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 721.4199648708689
|
||||
"upper": 227.21978759765625
|
||||
},
|
||||
"bwd_pkt_len_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 911.0272448420104
|
||||
"upper": 777.4441128540009
|
||||
},
|
||||
"fwd_seg_size_min": {
|
||||
"lower": 0.0,
|
||||
"upper": 40.0
|
||||
"upper": 1902198.5299999905
|
||||
},
|
||||
"fwd_act_data_pkts": {
|
||||
"lower": 0.0,
|
||||
"upper": 58.0
|
||||
"upper": 3.0
|
||||
},
|
||||
"fwd_iat_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 4873774.342499985
|
||||
},
|
||||
"bwd_iat_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 4677281.179999962
|
||||
},
|
||||
"flow_iat_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 4679977.562499992
|
||||
},
|
||||
"fwd_bwd_bytes_ratio": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"pkt_len_variance": {
|
||||
"lower": 0.0,
|
||||
"upper": 483294.3096875
|
||||
},
|
||||
"fwd_iat_skewness": {
|
||||
"lower": 0.0,
|
||||
"upper": 0.0
|
||||
}
|
||||
},
|
||||
"ae_scaler": {
|
||||
"mean": [
|
||||
9783470.902880548,
|
||||
3.6532605393470496,
|
||||
3.2219949045990472,
|
||||
311.35281195367173,
|
||||
1407.508354811673,
|
||||
298997.28705051675,
|
||||
32378.36735569276,
|
||||
6158.3256759444475,
|
||||
6125.023325143747,
|
||||
58.1683929498305,
|
||||
149.72168204849987,
|
||||
2991407.86700246,
|
||||
543337.1970583026,
|
||||
2727511.340181723,
|
||||
105.03829359866097,
|
||||
13463.473246959382,
|
||||
8.920585011866685,
|
||||
5.457814320208867,
|
||||
12.214631683524651,
|
||||
0.4885976379312313,
|
||||
0.246598391240437,
|
||||
0.08478201266637703,
|
||||
151.71809799252043,
|
||||
76.89649795414182,
|
||||
143.21014925828936,
|
||||
21.447338538177014,
|
||||
3.2607463735155715
|
||||
553573.8261433974,
|
||||
5.537481338484507,
|
||||
4.545714712966507,
|
||||
732.9122877032092,
|
||||
1594.293060562387,
|
||||
14524.457216310835,
|
||||
408.40078292070046,
|
||||
5041.6718942499765,
|
||||
8858.56079223287,
|
||||
293.4158400234335,
|
||||
68.50546049610982,
|
||||
48053.28191342106,
|
||||
39629.174786987496,
|
||||
2756276.585577172,
|
||||
24.55453629422013,
|
||||
13882.541797179327,
|
||||
9.155290259001973,
|
||||
0.02040285405796568,
|
||||
0.04693573572753199,
|
||||
0.009265432030886539,
|
||||
0.005048852822986429,
|
||||
0.0,
|
||||
23.07985361268022,
|
||||
2.8912962051498923,
|
||||
10.050823938165351,
|
||||
21807.221235950656,
|
||||
0.027325504830147902,
|
||||
47055.31297750816,
|
||||
37633.95415201727,
|
||||
49435.96380716072,
|
||||
0.5572453787198782,
|
||||
5243.747976892553,
|
||||
0.0
|
||||
],
|
||||
"std": [
|
||||
27895851.239715174,
|
||||
6.4263677035863696,
|
||||
8.326275195918974,
|
||||
739.2548064174216,
|
||||
7898.711708881367,
|
||||
1187804.3239195002,
|
||||
190280.1548511611,
|
||||
15556.664474188405,
|
||||
17012.235322987635,
|
||||
98.89458775902833,
|
||||
242.79522892109904,
|
||||
13146157.964503227,
|
||||
2242915.166631539,
|
||||
12878527.145353919,
|
||||
151.95316919115754,
|
||||
21264.915159327567,
|
||||
4.943209145195101,
|
||||
23.02750953458606,
|
||||
57.0633472601567,
|
||||
1.2662749497077417,
|
||||
0.4553262784517569,
|
||||
0.278557037237646,
|
||||
211.1373807979287,
|
||||
143.47790203327,
|
||||
228.24531573376805,
|
||||
9.323458649060111,
|
||||
7.228913366949205
|
||||
5769069.210865143,
|
||||
12.731563443887879,
|
||||
12.145072707978656,
|
||||
2227.8956432424648,
|
||||
7544.580451556596,
|
||||
131574.08066204368,
|
||||
3062.4073062866864,
|
||||
11584.347904359805,
|
||||
19321.807845687003,
|
||||
456.85162824410736,
|
||||
84.09080802725346,
|
||||
457034.0398467031,
|
||||
414969.2560970854,
|
||||
21002348.667211507,
|
||||
134.6784018236429,
|
||||
20905.109391832277,
|
||||
5.217401538588734,
|
||||
0.13728916522249587,
|
||||
0.21092403535986087,
|
||||
0.09575684583550836,
|
||||
0.07080745853335217,
|
||||
1.0,
|
||||
123.51344190707131,
|
||||
22.368753242426347,
|
||||
79.86295403283017,
|
||||
173264.80375874366,
|
||||
0.25736117191332675,
|
||||
435636.77402945235,
|
||||
389941.84503591515,
|
||||
432399.5330873597,
|
||||
0.3392136503327526,
|
||||
44261.86086917189,
|
||||
1.0
|
||||
],
|
||||
"feature_names": [
|
||||
"flow_duration",
|
||||
@ -271,7 +325,13 @@
|
||||
"fwd_pkt_len_std",
|
||||
"bwd_pkt_len_std",
|
||||
"fwd_seg_size_min",
|
||||
"fwd_act_data_pkts"
|
||||
"fwd_act_data_pkts",
|
||||
"fwd_iat_std",
|
||||
"bwd_iat_std",
|
||||
"flow_iat_std",
|
||||
"fwd_bwd_bytes_ratio",
|
||||
"pkt_len_variance",
|
||||
"fwd_iat_skewness"
|
||||
]
|
||||
},
|
||||
"post_scaling_clip": {
|
||||
@ -281,9 +341,11 @@
|
||||
},
|
||||
"attack_labels": {
|
||||
"0": "Brute Force",
|
||||
"1": "DDoS",
|
||||
"2": "DoS",
|
||||
"3": "Exploitation",
|
||||
"4": "Reconnaissance"
|
||||
"1": "C2 Communication",
|
||||
"2": "Cryptomining",
|
||||
"3": "DoS\/DDoS",
|
||||
"4": "Exploitation",
|
||||
"5": "Normal",
|
||||
"6": "Reconnaissance"
|
||||
}
|
||||
}
|
||||
@ -26,179 +26,78 @@
|
||||
"fwd_pkt_len_std",
|
||||
"bwd_pkt_len_std",
|
||||
"fwd_seg_size_min",
|
||||
"fwd_act_data_pkts"
|
||||
"fwd_act_data_pkts",
|
||||
"fwd_iat_std",
|
||||
"bwd_iat_std",
|
||||
"flow_iat_std",
|
||||
"fwd_bwd_bytes_ratio",
|
||||
"pkt_len_variance",
|
||||
"fwd_iat_skewness"
|
||||
],
|
||||
"ae_clip_params": {
|
||||
"flow_duration": {
|
||||
"lower": 1.0,
|
||||
"upper": 118485085.19000001
|
||||
},
|
||||
"fwd_packets": {
|
||||
"lower": 0.0,
|
||||
"upper": 54.0
|
||||
},
|
||||
"bwd_packets": {
|
||||
"lower": 0.0,
|
||||
"upper": 82.0
|
||||
},
|
||||
"fwd_bytes": {
|
||||
"lower": 0.0,
|
||||
"upper": 6960.0
|
||||
},
|
||||
"bwd_bytes": {
|
||||
"lower": 0.0,
|
||||
"upper": 89558.6950000003
|
||||
},
|
||||
"flow_bytes_per_sec": {
|
||||
"lower": 0.0,
|
||||
"upper": 12500000.0
|
||||
},
|
||||
"flow_pkts_per_sec": {
|
||||
"lower": 0.0226829350855,
|
||||
"upper": 2000000.0
|
||||
},
|
||||
"fwd_win_bytes": {
|
||||
"lower": -1.0,
|
||||
"upper": 65535.0
|
||||
},
|
||||
"bwd_win_bytes": {
|
||||
"lower": -1.0,
|
||||
"upper": 64000.0
|
||||
},
|
||||
"fwd_pkt_len_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 744.7272727272729
|
||||
},
|
||||
"bwd_pkt_len_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 1393.9601153594992
|
||||
},
|
||||
"fwd_iat_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 86366722.035
|
||||
},
|
||||
"bwd_iat_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 21700000.0
|
||||
},
|
||||
"flow_iat_mean": {
|
||||
"lower": 1.0,
|
||||
"upper": 86353400.64500001
|
||||
},
|
||||
"pkt_len_mean": {
|
||||
"lower": 0.0,
|
||||
"upper": 945.0294113974604
|
||||
},
|
||||
"dst_port": {
|
||||
"lower": 0.0,
|
||||
"upper": 64499.0
|
||||
},
|
||||
"protocol": {
|
||||
"lower": 0.0,
|
||||
"upper": 17.0
|
||||
},
|
||||
"psh_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 240.0
|
||||
},
|
||||
"ack_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 655.0
|
||||
},
|
||||
"syn_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 4.0
|
||||
},
|
||||
"fin_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 2.0
|
||||
},
|
||||
"rst_flag_cnt": {
|
||||
"lower": 0.0,
|
||||
"upper": 1.0
|
||||
},
|
||||
"pkt_len_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 943.9221842600019
|
||||
},
|
||||
"fwd_pkt_len_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 721.4199648708689
|
||||
},
|
||||
"bwd_pkt_len_std": {
|
||||
"lower": 0.0,
|
||||
"upper": 911.0272448420104
|
||||
},
|
||||
"fwd_seg_size_min": {
|
||||
"lower": 0.0,
|
||||
"upper": 40.0
|
||||
},
|
||||
"fwd_act_data_pkts": {
|
||||
"lower": 0.0,
|
||||
"upper": 58.0
|
||||
}
|
||||
"flow_duration": { "lower": 0.0, "upper": 70550165.55999511 },
|
||||
"fwd_packets": { "lower": 1.0, "upper": 120.0 },
|
||||
"bwd_packets": { "lower": 0.0, "upper": 112.0 },
|
||||
"fwd_bytes": { "lower": 0.0, "upper": 21320.685742187383 },
|
||||
"bwd_bytes": { "lower": 0.0, "upper": 78556.0 },
|
||||
"flow_bytes_per_sec": { "lower": 0.0, "upper": 1457941.3831249783 },
|
||||
"flow_pkts_per_sec": { "lower": 0.0, "upper": 30612.244140625 },
|
||||
"fwd_win_bytes": { "lower": 0.0, "upper": 65280.0 },
|
||||
"bwd_win_bytes": { "lower": 0.0, "upper": 65535.0 },
|
||||
"fwd_pkt_len_mean": { "lower": 0.0, "upper": 1500.0 },
|
||||
"bwd_pkt_len_mean": { "lower": 0.0, "upper": 690.0833129882812 },
|
||||
"fwd_iat_mean": { "lower": 0.0, "upper": 5468977.71249968 },
|
||||
"bwd_iat_mean": { "lower": 0.0, "upper": 5162028.484999921 },
|
||||
"flow_iat_mean": { "lower": 0.0, "upper": 166524640.0 },
|
||||
"pkt_len_mean": { "lower": 0.0, "upper": 1312.2810998535142 },
|
||||
"dst_port": { "lower": 0.0, "upper": 63375.0 },
|
||||
"protocol": { "lower": 0.0, "upper": 17.0 },
|
||||
"psh_flag_cnt": { "lower": 0.0, "upper": 1.0 },
|
||||
"ack_flag_cnt": { "lower": 0.0, "upper": 1.0 },
|
||||
"syn_flag_cnt": { "lower": 0.0, "upper": 1.0 },
|
||||
"fin_flag_cnt": { "lower": 0.0, "upper": 1.0 },
|
||||
"rst_flag_cnt": { "lower": 0.0, "upper": 0.0 },
|
||||
"pkt_len_std": { "lower": 0.0, "upper": 1051.5718536376853 },
|
||||
"fwd_pkt_len_std": { "lower": 0.0, "upper": 227.21978759765625 },
|
||||
"bwd_pkt_len_std": { "lower": 0.0, "upper": 777.4441128540009 },
|
||||
"fwd_seg_size_min": { "lower": 0.0, "upper": 1902198.5299999905 },
|
||||
"fwd_act_data_pkts": { "lower": 0.0, "upper": 3.0 },
|
||||
"fwd_iat_std": { "lower": 0.0, "upper": 4873774.342499985 },
|
||||
"bwd_iat_std": { "lower": 0.0, "upper": 4677281.179999962 },
|
||||
"flow_iat_std": { "lower": 0.0, "upper": 4679977.562499992 },
|
||||
"fwd_bwd_bytes_ratio": { "lower": 0.0, "upper": 1.0 },
|
||||
"pkt_len_variance": { "lower": 0.0, "upper": 483294.3096875 },
|
||||
"fwd_iat_skewness": { "lower": 0.0, "upper": 0.0 }
|
||||
},
|
||||
"ae_scaler_mean": [
|
||||
9783470.902880548,
|
||||
3.6532605393470496,
|
||||
3.2219949045990472,
|
||||
311.35281195367173,
|
||||
1407.508354811673,
|
||||
298997.28705051675,
|
||||
32378.36735569276,
|
||||
6158.3256759444475,
|
||||
6125.023325143747,
|
||||
58.1683929498305,
|
||||
149.72168204849987,
|
||||
2991407.86700246,
|
||||
543337.1970583026,
|
||||
2727511.340181723,
|
||||
105.03829359866097,
|
||||
13463.473246959382,
|
||||
8.920585011866685,
|
||||
5.457814320208867,
|
||||
12.214631683524651,
|
||||
0.4885976379312313,
|
||||
0.246598391240437,
|
||||
0.08478201266637703,
|
||||
151.71809799252043,
|
||||
76.89649795414182,
|
||||
143.21014925828936,
|
||||
21.447338538177014,
|
||||
3.2607463735155715
|
||||
553573.8261433974, 5.537481338484507, 4.545714712966507,
|
||||
732.9122877032092, 1594.293060562387, 14524.457216310835,
|
||||
408.40078292070046, 5041.6718942499765, 8858.56079223287,
|
||||
293.4158400234335, 68.50546049610982, 48053.28191342106,
|
||||
39629.174786987496, 2756276.585577172, 24.55453629422013,
|
||||
13882.541797179327, 9.155290259001973, 0.02040285405796568,
|
||||
0.04693573572753199, 0.009265432030886539, 0.005048852822986429,
|
||||
0.0, 23.07985361268022, 2.8912962051498923, 10.050823938165351,
|
||||
21807.221235950656, 0.027325504830147902, 47055.31297750816,
|
||||
37633.95415201727, 49435.96380716072, 0.5572453787198782,
|
||||
5243.747976892553, 0.0
|
||||
],
|
||||
"ae_scaler_std": [
|
||||
27895851.239715174,
|
||||
6.4263677035863696,
|
||||
8.326275195918974,
|
||||
739.2548064174216,
|
||||
7898.711708881367,
|
||||
1187804.3239195002,
|
||||
190280.1548511611,
|
||||
15556.664474188405,
|
||||
17012.235322987635,
|
||||
98.89458775902833,
|
||||
242.79522892109904,
|
||||
13146157.964503227,
|
||||
2242915.166631539,
|
||||
12878527.145353919,
|
||||
151.95316919115754,
|
||||
21264.915159327567,
|
||||
4.943209145195101,
|
||||
23.02750953458606,
|
||||
57.0633472601567,
|
||||
1.2662749497077417,
|
||||
0.4553262784517569,
|
||||
0.278557037237646,
|
||||
211.1373807979287,
|
||||
143.47790203327,
|
||||
228.24531573376805,
|
||||
9.323458649060111,
|
||||
7.228913366949205
|
||||
5769069.210865143, 12.731563443887879, 12.145072707978656,
|
||||
2227.8956432424648, 7544.580451556596, 131574.08066204368,
|
||||
3062.4073062866864, 11584.347904359805, 19321.807845687003,
|
||||
456.85162824410736, 84.09080802725346, 457034.0398467031,
|
||||
414969.2560970854, 21002348.667211507, 134.6784018236429,
|
||||
20905.109391832277, 5.217401538588734, 0.13728916522249587,
|
||||
0.21092403535986087, 0.09575684583550836, 0.07080745853335217,
|
||||
1.0, 123.51344190707131, 22.368753242426347, 79.86295403283017,
|
||||
173264.80375874366, 0.25736117191332675, 435636.77402945235,
|
||||
389941.84503591515, 432399.5330873597, 0.3392136503327526,
|
||||
44261.86086917189, 1.0
|
||||
],
|
||||
"ae_post_clip_min": -5.0,
|
||||
"ae_post_clip_max": 5.0,
|
||||
"ae_threshold": 0.17166012525558472,
|
||||
"ae_threshold": 0.23287349939346313,
|
||||
"classifier_feature_names": [
|
||||
"flow_duration",
|
||||
"fwd_packets",
|
||||
@ -227,13 +126,60 @@
|
||||
"bwd_pkt_len_std",
|
||||
"fwd_seg_size_min",
|
||||
"fwd_act_data_pkts",
|
||||
"fwd_iat_std",
|
||||
"bwd_iat_std",
|
||||
"flow_iat_std",
|
||||
"fwd_bwd_bytes_ratio",
|
||||
"pkt_len_variance",
|
||||
"fwd_iat_skewness",
|
||||
"ae_anomaly_score"
|
||||
],
|
||||
"attack_labels": {
|
||||
"0": "Brute Force",
|
||||
"1": "DDoS",
|
||||
"2": "DoS",
|
||||
"3": "Exploitation",
|
||||
"4": "Reconnaissance"
|
||||
"1": "C2 Communication",
|
||||
"2": "Cryptomining",
|
||||
"3": "DoS/DDoS",
|
||||
"4": "Exploitation",
|
||||
"5": "Normal",
|
||||
"6": "Reconnaissance"
|
||||
},
|
||||
"anomaly_threshold": 0.5,
|
||||
"c2_threshold": 0.5,
|
||||
"model_type": "MultiTaskModel",
|
||||
"output_names": ["anomaly", "class_probs", "c2_score"],
|
||||
"ae_feature_weights": {
|
||||
"flow_duration": 1.0,
|
||||
"fwd_packets": 1.0,
|
||||
"bwd_packets": 1.0,
|
||||
"fwd_bytes": 1.0,
|
||||
"bwd_bytes": 1.0,
|
||||
"flow_bytes_per_sec": 1.0,
|
||||
"flow_pkts_per_sec": 1.0,
|
||||
"fwd_win_bytes": 4.0,
|
||||
"bwd_win_bytes": 4.0,
|
||||
"fwd_pkt_len_mean": 1.0,
|
||||
"bwd_pkt_len_mean": 1.0,
|
||||
"fwd_iat_mean": 1.0,
|
||||
"bwd_iat_mean": 1.0,
|
||||
"flow_iat_mean": 1.0,
|
||||
"pkt_len_mean": 1.0,
|
||||
"dst_port": 1.0,
|
||||
"protocol": 1.0,
|
||||
"psh_flag_cnt": 2.0,
|
||||
"ack_flag_cnt": 1.0,
|
||||
"syn_flag_cnt": 2.0,
|
||||
"fin_flag_cnt": 2.0,
|
||||
"rst_flag_cnt": 2.0,
|
||||
"pkt_len_std": 1.0,
|
||||
"fwd_pkt_len_std": 1.0,
|
||||
"bwd_pkt_len_std": 1.0,
|
||||
"fwd_seg_size_min": 1.0,
|
||||
"fwd_act_data_pkts": 1.0,
|
||||
"fwd_iat_std": 1.5,
|
||||
"bwd_iat_std": 1.5,
|
||||
"flow_iat_std": 0.1,
|
||||
"fwd_bwd_bytes_ratio": 2.0,
|
||||
"pkt_len_variance": 0.1,
|
||||
"fwd_iat_skewness": 0.1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,9 +5,9 @@ use async_trait::async_trait;
|
||||
|
||||
use crate::core::ebpf::access_control::AccessControl;
|
||||
use crate::interface::port::access_control::AccessControlPort;
|
||||
use crate::model::direction::FlowDirection;
|
||||
use crate::model::access_control::list_type::ListType;
|
||||
use crate::model::error::Error;
|
||||
use crate::model::list_type::ListType;
|
||||
use crate::model::monitoring::direction::FlowDirection;
|
||||
|
||||
/// Adapter that implements AccessControlPort by delegating to the eBPF AccessControl.
|
||||
pub struct EbpfAccessControlAdapter {
|
||||
|
||||
@ -4,8 +4,8 @@ use actix_web::{HttpResponse, Responder, Scope, web};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::core::acl_service::AclService;
|
||||
use crate::model::direction::FlowDirection;
|
||||
use crate::model::list_type::ListType;
|
||||
use crate::model::access_control::list_type::ListType;
|
||||
use crate::model::monitoring::direction::FlowDirection;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CountryCodesRequest {
|
||||
|
||||
@ -116,7 +116,6 @@ async fn login(body: web::Json<LoginRequest>, db: web::Data<Repo>, jwt: web::Dat
|
||||
// Permissions come exclusively from groups — no role-based fallback
|
||||
let permissions = db.get_user_permissions(id).unwrap_or_default();
|
||||
|
||||
// Derive role from groups for backwards compat in JWT
|
||||
let groups = db.get_user_groups(id).unwrap_or_default();
|
||||
let role = if groups.iter().any(|(_id, name, _desc, _perms)| name == "Administrator") {
|
||||
"admin".to_string()
|
||||
@ -257,7 +256,6 @@ async fn list_users(_auth: AuthClaims, db: web::Data<Repo>) -> impl Responder {
|
||||
.iter()
|
||||
.map(|(gid, name)| serde_json::json!({"id": gid, "name": name}))
|
||||
.collect();
|
||||
// Derive role from groups for backwards compat
|
||||
let role = if user_groups.iter().any(|(_id, name)| name == "Administrator") {
|
||||
"admin"
|
||||
} else {
|
||||
|
||||
@ -1048,7 +1048,7 @@ impl Database {
|
||||
|
||||
/// Validate an API key and return Claims if valid.
|
||||
/// Computes SHA-256 hash of the key and looks it up in api_keys table.
|
||||
pub fn validate_api_key(&self, api_key: &str) -> Result<Option<crate::model::auth::Claims>, Error> {
|
||||
pub fn validate_api_key(&self, api_key: &str) -> Result<Option<crate::model::identity::auth::Claims>, Error> {
|
||||
use std::fmt::Write;
|
||||
|
||||
// SHA-256 hash the key
|
||||
@ -1115,7 +1115,7 @@ impl Database {
|
||||
],
|
||||
};
|
||||
|
||||
Ok(Some(crate::model::auth::Claims {
|
||||
Ok(Some(crate::model::identity::auth::Claims {
|
||||
sub: -id, // negative ID to distinguish from user IDs
|
||||
username: format!("api:{}", name),
|
||||
role: level,
|
||||
@ -2026,7 +2026,7 @@ impl crate::interface::port::audit::AuditPort for Database {
|
||||
}
|
||||
|
||||
impl crate::interface::port::api_key::ApiKeyPort for Database {
|
||||
fn validate_api_key(&self, api_key: &str) -> Result<Option<crate::model::auth::Claims>, Error> {
|
||||
fn validate_api_key(&self, api_key: &str) -> Result<Option<crate::model::identity::auth::Claims>, Error> {
|
||||
self.validate_api_key(api_key)
|
||||
}
|
||||
fn insert_api_key(&self, key_hash: &str, name: &str, permission_level: &str) -> Result<i64, Error> {
|
||||
|
||||
@ -8,7 +8,7 @@ use crate::core::ml::alert::MLAlert;
|
||||
use crate::model::error::http::HttpError;
|
||||
use crate::model::error::misc::MiscError;
|
||||
use crate::model::log::http::HttpLog;
|
||||
use crate::model::ml_detection::AlertMessage;
|
||||
use crate::model::detection::ml_detection::AlertMessage;
|
||||
|
||||
pub async fn websocket_alert(req: HttpRequest, body: web::Payload, ai: web::Data<MLAlert>) -> Result<HttpResponse> {
|
||||
let (response, session, msg_stream) = handle(&req, body)?;
|
||||
|
||||
@ -5,7 +5,7 @@ use macros::log;
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
use crate::core::ebpf::drop_monitor::DropMonitor;
|
||||
use crate::model::drop_event::DropEventMessage;
|
||||
use crate::model::monitoring::drop_event::DropEventMessage;
|
||||
use crate::model::error::http::HttpError;
|
||||
use crate::model::error::misc::MiscError;
|
||||
use crate::model::log::http::HttpLog;
|
||||
|
||||
@ -6,7 +6,7 @@ use futures_util::StreamExt;
|
||||
use tokio::time::interval;
|
||||
|
||||
use crate::infrastructure::statistics::FlowStatistics;
|
||||
use crate::model::flow_stats::FlowSubscription;
|
||||
use crate::model::monitoring::flow_stats::FlowSubscription;
|
||||
|
||||
/// Default subscription: all flows, no filter, 5 second interval
|
||||
fn default_subscription() -> FlowSubscription {
|
||||
|
||||
@ -7,7 +7,7 @@ use tokio::sync::broadcast;
|
||||
use crate::infrastructure::health::SystemHealth;
|
||||
use crate::model::error::http::HttpError;
|
||||
use crate::model::error::misc::MiscError;
|
||||
use crate::model::health::SystemHealthMetrics;
|
||||
use crate::model::system::health::SystemHealthMetrics;
|
||||
use crate::model::log::http::HttpLog;
|
||||
|
||||
pub async fn websocket_system_health(
|
||||
|
||||
@ -4,12 +4,12 @@ use std::sync::Arc;
|
||||
use crate::core::ebpf::access_control::AccessControl;
|
||||
use crate::core::ebpf::geo_block::GeoBlock;
|
||||
use crate::interface::port::repository::RepositoryPort;
|
||||
use crate::model::direction::FlowDirection;
|
||||
use crate::model::monitoring::direction::FlowDirection;
|
||||
use macros::log;
|
||||
|
||||
use crate::model::error::Error;
|
||||
use crate::model::error::ebpf::EbpfError;
|
||||
use crate::model::list_type::ListType;
|
||||
use crate::model::access_control::list_type::ListType;
|
||||
|
||||
/// Domain service that coordinates ACL changes between DB persistence and eBPF data plane.
|
||||
/// Atomic write: eBPF first, then DB. If DB fails, rollback eBPF.
|
||||
|
||||
@ -3,7 +3,7 @@ use std::future::{Ready, ready};
|
||||
use actix_web::dev::Payload;
|
||||
use actix_web::{FromRequest, HttpMessage, HttpRequest};
|
||||
|
||||
use crate::model::auth::Claims;
|
||||
use crate::model::identity::auth::Claims;
|
||||
|
||||
/// Actix-web extractor that pulls `Claims` from request extensions.
|
||||
///
|
||||
|
||||
@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation, decode, encode, errors::ErrorKind};
|
||||
|
||||
use crate::interface::port::secret_store::SecretStorePort;
|
||||
use crate::model::auth::Claims;
|
||||
use crate::model::identity::auth::Claims;
|
||||
use crate::model::error::Error;
|
||||
use crate::model::error::auth::AuthError;
|
||||
|
||||
|
||||
@ -7,11 +7,11 @@ use common::model::ip_address::{IPv4, IPv6, Port};
|
||||
use common::model::port_rule::PortRule;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::model::direction::FlowDirection;
|
||||
use crate::model::access_control::ip_address::NativeConvert;
|
||||
use crate::model::access_control::list_type::ListType;
|
||||
use crate::model::error::Error;
|
||||
use crate::model::error::ebpf::EbpfError;
|
||||
use crate::model::ip_address::NativeConvert;
|
||||
use crate::model::list_type::ListType;
|
||||
use crate::model::monitoring::direction::FlowDirection;
|
||||
|
||||
pub struct AccessControl {
|
||||
ipv4_src_whitelist: RwLock<MapWrapper<IPv4>>,
|
||||
|
||||
@ -10,7 +10,7 @@ use common::model::drop_event::DropEvent as RawDropEvent;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use crate::model::config::constants::DROP_CHANNEL_CAPACITY;
|
||||
use crate::model::drop_event::{DropCounters, DropEventMessage};
|
||||
use crate::model::monitoring::drop_event::{DropCounters, DropEventMessage};
|
||||
|
||||
pub struct DropMonitor {
|
||||
broadcast_tx: broadcast::Sender<DropEventMessage>,
|
||||
|
||||
@ -10,7 +10,7 @@ use tokio::sync::RwLock;
|
||||
|
||||
use crate::model::error::Error;
|
||||
use crate::model::error::ebpf::EbpfError;
|
||||
use crate::model::ip_address::NativeConvert;
|
||||
use crate::model::access_control::ip_address::NativeConvert;
|
||||
|
||||
pub struct ProtocolFilter {
|
||||
ipv4_http_service: RwLock<HttpServiceWrapper<AddrPortV4>>,
|
||||
|
||||
@ -20,8 +20,8 @@ use crate::core::ebpf::dns_filter::DnsFilter;
|
||||
use crate::core::ml::engine::Engine;
|
||||
use crate::core::ml::flow_tracker::FlowTracker;
|
||||
use crate::infrastructure::app_config::AppConfig;
|
||||
use crate::model::config::NetworkConfig;
|
||||
use crate::model::direction::Direction;
|
||||
use crate::model::monitoring::direction::Direction;
|
||||
use crate::model::system::config::NetworkConfig;
|
||||
use crate::model::error::Error;
|
||||
use crate::model::error::ebpf::EbpfError;
|
||||
use crate::model::error::system::SystemError;
|
||||
|
||||
@ -25,8 +25,7 @@ impl SmtpClient {
|
||||
///
|
||||
/// Returns `None` if any required setting (`smtp_host`, `smtp_port`,
|
||||
/// `smtp_username`, `smtp_password`) is missing.
|
||||
/// If a `SecretStorePort` is provided, reads the password from the secret store
|
||||
/// (falling back to the settings table for backward compat before migration).
|
||||
/// If a `SecretStorePort` is provided, reads the password from the secret store.
|
||||
pub fn from_database(
|
||||
db: &dyn RepositoryPort,
|
||||
secrets: Option<&dyn SecretStorePort>,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::model::ml_detection::FlowKey;
|
||||
use crate::model::detection::ml_detection::FlowKey;
|
||||
|
||||
pub struct AttackAggregator {
|
||||
detections: HashMap<FlowKey, Vec<(Instant, f32)>>,
|
||||
@ -31,7 +31,7 @@ impl AttackAggregator {
|
||||
// DDoS/DoS: high frequency, need more confirmations to avoid alert storms
|
||||
// C2/Cryptomining: low frequency, alert on first detection
|
||||
let effective_min = match attack_type {
|
||||
Some("DDoS") | Some("DoS") => self.min_detections.saturating_mul(2).max(1),
|
||||
Some("DoS/DDoS") => self.min_detections.saturating_mul(2).max(1),
|
||||
Some("C2 Communication") | Some("Cryptomining") => 1,
|
||||
_ => self.min_detections,
|
||||
};
|
||||
@ -80,14 +80,14 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ddos_requires_double_min_detections() {
|
||||
fn dos_ddos_requires_double_min_detections() {
|
||||
let mut agg = AttackAggregator::new(60, 3);
|
||||
let key = test_key();
|
||||
// DDoS needs 6 detections (3 * 2)
|
||||
// DoS/DDoS needs 6 detections (3 * 2)
|
||||
for _ in 0..5 {
|
||||
assert!(!agg.should_alert(&key, 5.0, 1.0, Some("DDoS")));
|
||||
assert!(!agg.should_alert(&key, 5.0, 1.0, Some("DoS/DDoS")));
|
||||
}
|
||||
assert!(agg.should_alert(&key, 5.0, 1.0, Some("DDoS")));
|
||||
assert!(agg.should_alert(&key, 5.0, 1.0, Some("DoS/DDoS")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -3,7 +3,7 @@ use tokio::sync::broadcast;
|
||||
|
||||
use crate::model::config::constants::ML_ALERT_CHANNEL_CAPACITY;
|
||||
use crate::model::log::ml::MLLog;
|
||||
use crate::model::ml_detection::{AlertMessage, DetectionResult};
|
||||
use crate::model::detection::ml_detection::{AlertMessage, DetectionResult};
|
||||
|
||||
pub struct MLAlert {
|
||||
broadcast_tx: broadcast::Sender<AlertMessage>,
|
||||
|
||||
@ -2,11 +2,7 @@ use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::model::error::ml::MLError;
|
||||
|
||||
use crate::model::config::MLInferenceConfig;
|
||||
|
||||
/// Backward-compatible alias so existing `use config_loader::InferenceConfig` paths still compile.
|
||||
pub type InferenceConfig = MLInferenceConfig;
|
||||
use crate::model::system::config::MLInferenceConfig;
|
||||
|
||||
impl MLInferenceConfig {
|
||||
pub fn load_file(file: &str) -> Result<Self, MLError> {
|
||||
@ -23,6 +19,11 @@ impl MLInferenceConfig {
|
||||
if config.ae_scaler_std.len() != config.ae_feature_names.len() {
|
||||
return Err(MLError::ConfigParseFailed("scaler std length mismatch"));
|
||||
}
|
||||
if config.output_names.len() != 3 {
|
||||
return Err(MLError::ConfigParseFailed(
|
||||
"MultiTaskModel requires exactly 3 output_names (anomaly, class_probs, c2_score)",
|
||||
));
|
||||
}
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ use tokio::sync::oneshot;
|
||||
use tokio::time::interval;
|
||||
|
||||
use super::aggregator::AttackAggregator;
|
||||
use super::config_loader::InferenceConfig;
|
||||
use crate::model::system::config::MLInferenceConfig;
|
||||
use super::drift_detector::DriftDetector;
|
||||
use super::flow_tracker::{FlowData, FlowTracker};
|
||||
use super::inference::Inference;
|
||||
@ -17,7 +17,7 @@ use crate::model::detection::flow_features::FlowFeatures;
|
||||
|
||||
use super::alert::MLAlert;
|
||||
use crate::model::log::ml::MLLog;
|
||||
use crate::model::ml_detection::{EngineConfig, InferenceStats};
|
||||
use crate::model::detection::ml_detection::{EngineConfig, InferenceStats};
|
||||
|
||||
/// Per-queue tracker. With symmetric hash in eBPF, both directions of a flow
|
||||
/// land on the same queue, so per-queue trackers correctly see bidirectional flows.
|
||||
@ -38,7 +38,7 @@ pub struct Engine {
|
||||
impl Engine {
|
||||
pub fn new(
|
||||
models: Arc<MLModels>,
|
||||
config: Arc<InferenceConfig>,
|
||||
config: Arc<MLInferenceConfig>,
|
||||
ml_alert: Arc<MLAlert>,
|
||||
drift_detector: Arc<Mutex<DriftDetector>>,
|
||||
engine_config: EngineConfig,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use common::define::tcp_flags::*;
|
||||
|
||||
use super::flow_tracker::FlowData;
|
||||
use crate::model::ml_detection::PacketData;
|
||||
use crate::model::detection::ml_detection::PacketData;
|
||||
|
||||
use crate::model::detection::flow_features::FlowFeatures;
|
||||
|
||||
@ -356,10 +356,10 @@ impl PrecomputedStats {
|
||||
"Subflow Fwd Bytes" => self.fwd_total_bytes,
|
||||
"Subflow Bwd Packets" => self.bwd_count,
|
||||
"Subflow Bwd Bytes" => self.bwd_total_bytes,
|
||||
"Init_Win_bytes_forward" | "Init Fwd Win Byts" | "fwd_win_bytes" => self.init_win_bytes_fwd,
|
||||
"Init_Win_bytes_backward" | "Init Bwd Win Byts" | "bwd_win_bytes" => self.init_win_bytes_bwd,
|
||||
"act_data_pkt_fwd" | "Fwd Act Data Pkts" | "fwd_act_data_pkts" => self.act_data_pkt_fwd,
|
||||
"min_seg_size_forward" | "Fwd Seg Size Min" | "fwd_seg_size_min" => self.min_seg_size_forward,
|
||||
"fwd_win_bytes" => self.init_win_bytes_fwd,
|
||||
"bwd_win_bytes" => self.init_win_bytes_bwd,
|
||||
"fwd_act_data_pkts" => self.act_data_pkt_fwd,
|
||||
"fwd_seg_size_min" => self.min_seg_size_forward,
|
||||
"Active Mean" => self.active_mean,
|
||||
"Active Std" => self.active_std,
|
||||
"Active Max" => self.active_max,
|
||||
|
||||
@ -7,9 +7,9 @@ use crate::model::config::constants::{
|
||||
FLOW_BULK_MIN_BYTES, FLOW_BULK_MIN_PACKETS, FLOW_IDLE_THRESHOLD_US, FLOW_IDLE_TIMEOUT_US,
|
||||
FLOW_MAX_PACKETS_PER_DIRECTION, FLOW_MAX_PERIODS, FLOW_TERMINATED_TIMEOUT_US,
|
||||
};
|
||||
use crate::model::direction::Direction;
|
||||
use crate::model::ml_detection::{BulkState, FlowKey, PacketData};
|
||||
use crate::model::user_packet::UserPacket;
|
||||
use crate::model::monitoring::direction::Direction;
|
||||
use crate::model::detection::ml_detection::{BulkState, FlowKey, PacketData};
|
||||
use crate::model::monitoring::user_packet::UserPacket;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FlowData {
|
||||
|
||||
@ -3,20 +3,20 @@ use std::sync::Arc;
|
||||
use macros::log;
|
||||
use tract_onnx::prelude::*;
|
||||
|
||||
use super::config_loader::InferenceConfig;
|
||||
use super::flow_tracker::FlowData;
|
||||
use super::model_loader::MLModels;
|
||||
use crate::model::detection::flow_features::FlowFeatures;
|
||||
use crate::model::detection::ml_detection::DetectionResult;
|
||||
use crate::model::log::ml::MLLog;
|
||||
use crate::model::ml_detection::DetectionResult;
|
||||
use crate::model::system::config::MLInferenceConfig;
|
||||
|
||||
pub struct Inference {
|
||||
pub models: Arc<MLModels>,
|
||||
pub config: Arc<InferenceConfig>,
|
||||
pub config: Arc<MLInferenceConfig>,
|
||||
}
|
||||
|
||||
impl Inference {
|
||||
pub fn new(models: Arc<MLModels>, config: Arc<InferenceConfig>) -> Self {
|
||||
pub fn new(models: Arc<MLModels>, config: Arc<MLInferenceConfig>) -> Self {
|
||||
Self { models, config }
|
||||
}
|
||||
|
||||
@ -53,16 +53,6 @@ impl Inference {
|
||||
|
||||
let cls_input = self.build_classifier_input(&ae_features, ae_score);
|
||||
|
||||
let (attack_type, confidence) = match self.run_classifier(cls_input) {
|
||||
Ok(result) => result,
|
||||
Err(e) => {
|
||||
log!(MLLog::InferenceFailed("LightGBM".to_string(), e.to_string()));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let is_attack = ae_score >= self.config.ae_threshold;
|
||||
|
||||
let flow_key = format!(
|
||||
"{}:{} -> {}:{} (proto {}) [{}]",
|
||||
flow.flow_key.src_ip_string(),
|
||||
@ -73,6 +63,86 @@ impl Inference {
|
||||
flow.direction
|
||||
);
|
||||
|
||||
let result = match self.models.classifier.run(tvec![cls_input.into_tensor().into()]) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
log!(MLLog::InferenceFailed("MultiTaskModel".to_string(), e.to_string()));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Output 0: anomaly (shape [1,1], sigmoid)
|
||||
let anomaly_score = match result[0].to_array_view::<f32>() {
|
||||
Ok(v) => *v.iter().next().unwrap_or(&0.0),
|
||||
Err(e) => {
|
||||
log!(MLLog::InferenceFailed("anomaly_head".to_string(), e.to_string()));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Output 1: class_probs (shape [1, n_classes], softmax)
|
||||
let class_probs = match result[1].to_array_view::<f32>() {
|
||||
Ok(v) => v.iter().copied().collect::<Vec<f32>>(),
|
||||
Err(e) => {
|
||||
log!(MLLog::InferenceFailed("class_head".to_string(), e.to_string()));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Output 2: c2_score (shape [1,1], sigmoid)
|
||||
let c2_score = match result[2].to_array_view::<f32>() {
|
||||
Ok(v) => *v.iter().next().unwrap_or(&0.0),
|
||||
Err(e) => {
|
||||
log!(MLLog::InferenceFailed("c2_head".to_string(), e.to_string()));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let is_attack = anomaly_score > self.config.anomaly_threshold;
|
||||
|
||||
// Determine attack type from class_head argmax
|
||||
let (predicted_class, class_confidence) = class_probs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
|
||||
.map(|(i, &p)| (i, p))
|
||||
.unwrap_or((0, 0.0));
|
||||
|
||||
let mut attack_type = self
|
||||
.config
|
||||
.attack_labels
|
||||
.get(&predicted_class.to_string())
|
||||
.cloned()
|
||||
.unwrap_or_else(|| "UNKNOWN".to_string());
|
||||
|
||||
let mut confidence = class_confidence;
|
||||
|
||||
// C2 head override: if C2 head fires and its confidence exceeds the class head's
|
||||
// C2 probability, prefer the dedicated C2 head judgment.
|
||||
if c2_score > self.config.c2_threshold {
|
||||
let c2_class_prob = self.find_class_prob_for("C2 Communication", &class_probs);
|
||||
if c2_score > c2_class_prob {
|
||||
attack_type = "C2 Communication".to_string();
|
||||
confidence = c2_score;
|
||||
}
|
||||
}
|
||||
|
||||
// "Normal" class prediction means benign
|
||||
if attack_type == "Normal" {
|
||||
return Some(DetectionResult {
|
||||
flow_key,
|
||||
flow_key_raw: flow.flow_key.clone(),
|
||||
direction: flow.direction,
|
||||
is_attack: false,
|
||||
attack_type: None,
|
||||
confidence: class_confidence,
|
||||
ae_score,
|
||||
threshold: self.config.anomaly_threshold,
|
||||
packet_count: flow.packet_count() as u64,
|
||||
flow_duration_us: flow.duration_us(),
|
||||
});
|
||||
}
|
||||
|
||||
Some(DetectionResult {
|
||||
flow_key,
|
||||
flow_key_raw: flow.flow_key.clone(),
|
||||
@ -81,12 +151,22 @@ impl Inference {
|
||||
attack_type: if is_attack { Some(attack_type) } else { None },
|
||||
confidence,
|
||||
ae_score,
|
||||
threshold: self.config.ae_threshold,
|
||||
threshold: self.config.anomaly_threshold,
|
||||
packet_count: flow.packet_count() as u64,
|
||||
flow_duration_us: flow.duration_us(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Find the softmax probability for a given label name in class_probs.
|
||||
fn find_class_prob_for(&self, label: &str, class_probs: &[f32]) -> f32 {
|
||||
for (key, name) in &self.config.attack_labels {
|
||||
if name == label && let Ok(idx) = key.parse::<usize>() {
|
||||
return class_probs.get(idx).copied().unwrap_or(0.0);
|
||||
}
|
||||
}
|
||||
0.0
|
||||
}
|
||||
|
||||
fn preprocess_ae_features(&self, flow: &FlowData) -> Vec<f32> {
|
||||
let mut features = FlowFeatures::extract(flow, &self.config.ae_feature_names);
|
||||
features.winsorize(&self.config.ae_clip_params, &self.config.ae_feature_names);
|
||||
@ -99,7 +179,7 @@ impl Inference {
|
||||
tract_ndarray::Array2::from_shape_fn((1, v.len()), |(_, j)| v[j])
|
||||
}
|
||||
|
||||
/// Classifier 輸入 = 已預處理的 ae_features ++ [ae_anomaly_score]
|
||||
/// Classifier input = preprocessed ae_features ++ [ae_anomaly_score]
|
||||
fn build_classifier_input(&self, ae_features: &[f32], ae_score: f32) -> tract_ndarray::Array2<f32> {
|
||||
let n = ae_features.len() + 1;
|
||||
tract_ndarray::Array2::from_shape_fn((1, n), |(_, j)| {
|
||||
@ -124,29 +204,4 @@ impl Inference {
|
||||
|
||||
Ok(mse)
|
||||
}
|
||||
|
||||
fn run_classifier(&self, input: tract_ndarray::Array2<f32>) -> TractResult<(String, f32)> {
|
||||
let result = self.models.classifier.run(tvec![input.into_tensor().into()])?;
|
||||
|
||||
let output = result[0].to_array_view::<f32>()?;
|
||||
|
||||
let mut max_prob: f32 = 0.0;
|
||||
let mut predicted_class: usize = 0;
|
||||
|
||||
for (i, &prob) in output.iter().enumerate() {
|
||||
if prob > max_prob {
|
||||
max_prob = prob;
|
||||
predicted_class = i;
|
||||
}
|
||||
}
|
||||
|
||||
let attack_type = self
|
||||
.config
|
||||
.attack_labels
|
||||
.get(&predicted_class.to_string())
|
||||
.cloned()
|
||||
.unwrap_or_else(|| "UNKNOWN".to_string());
|
||||
|
||||
Ok((attack_type, max_prob))
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,16 +3,16 @@ use tract_onnx::prelude::*;
|
||||
|
||||
use crate::infrastructure::app_config::AppConfig;
|
||||
use crate::model::error::ml::MLError;
|
||||
use crate::model::ml_detection::RunnableModel;
|
||||
use crate::model::detection::ml_detection::RunnableModel;
|
||||
|
||||
use super::config_loader::InferenceConfig;
|
||||
use crate::model::system::config::MLInferenceConfig;
|
||||
|
||||
pub struct MLModels {
|
||||
pub deep_autoencoder: RunnableModel,
|
||||
pub classifier: RunnableModel,
|
||||
}
|
||||
impl MLModels {
|
||||
pub fn load_models(app_config: &Arc<AppConfig>, inference_config: &Arc<InferenceConfig>) -> Result<Self, MLError> {
|
||||
pub fn load_models(app_config: &Arc<AppConfig>, inference_config: &Arc<MLInferenceConfig>) -> Result<Self, MLError> {
|
||||
Ok(Self {
|
||||
deep_autoencoder: Self::loader(
|
||||
&app_config.inference.deep_autoencoder_name,
|
||||
|
||||
@ -107,12 +107,12 @@ impl SoarEngine {
|
||||
// Check if this row belongs to the same playbook as the last one
|
||||
let needs_new = playbooks.last().is_none_or(|last| last.id != pb_id);
|
||||
if needs_new {
|
||||
let _ = threshold; // stored in DB but no longer used at runtime
|
||||
playbooks.push(Playbook {
|
||||
id: pb_id,
|
||||
name,
|
||||
enabled,
|
||||
trigger_event,
|
||||
condition_threshold: threshold,
|
||||
cooldown_secs: cooldown,
|
||||
actions: Vec::new(),
|
||||
conditions: Vec::new(),
|
||||
@ -265,13 +265,9 @@ impl SoarEngine {
|
||||
}
|
||||
|
||||
/// Evaluate all conditions on a playbook (AND logic).
|
||||
/// If conditions vec is empty, falls back to legacy `condition_threshold` check.
|
||||
/// If no conditions are configured, the playbook matches unconditionally.
|
||||
fn evaluate_conditions(&self, pb: &Playbook, event: &ThreatDetectedEvent) -> bool {
|
||||
if pb.conditions.is_empty() {
|
||||
// Legacy: use inline threshold if present
|
||||
if let Some(threshold) = pb.condition_threshold {
|
||||
return (event.confidence as f64) >= threshold;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1485,7 +1481,6 @@ mod tests {
|
||||
id: 999,
|
||||
name: "test-playbook".to_string(),
|
||||
trigger_event: "threat_detected".to_string(),
|
||||
condition_threshold: None,
|
||||
cooldown_secs: 60,
|
||||
enabled: true,
|
||||
actions: vec![],
|
||||
|
||||
@ -11,7 +11,7 @@ use crate::core::config_service::ConfigService;
|
||||
use crate::core::dns_filter_service::DnsFilterService;
|
||||
use crate::core::ebpf::EbpfServices;
|
||||
use crate::core::email::scheduler::ReportScheduler;
|
||||
use crate::core::ml::config_loader::InferenceConfig;
|
||||
use crate::model::system::config::MLInferenceConfig;
|
||||
use crate::core::ml::drift_detector::DriftDetector;
|
||||
use crate::core::notification_service::NotificationService;
|
||||
use crate::core::playbook_service::PlaybookService;
|
||||
@ -32,7 +32,7 @@ use crate::model::event::{DetectionEvent, DetectionSource, DriftDetectedEvent};
|
||||
use crate::model::log::detection::DetectionLog;
|
||||
use crate::model::log::ml::MLLog;
|
||||
use crate::model::log::system::SystemLog;
|
||||
use crate::model::ml_detection::AlertMessage;
|
||||
use crate::model::detection::ml_detection::AlertMessage;
|
||||
use crate::model::system::readiness::ReadinessState;
|
||||
|
||||
/// API-triggered shutdown mode.
|
||||
@ -70,7 +70,7 @@ impl ShutdownHandle {
|
||||
/// Setup mode is handled by main.rs — System only runs when setup is complete.
|
||||
pub struct System {
|
||||
pub app_config: Arc<AppConfig>,
|
||||
pub inference_config: Arc<InferenceConfig>,
|
||||
pub inference_config: Arc<MLInferenceConfig>,
|
||||
pub ebpf_services: Arc<EbpfServices>,
|
||||
pub app_services: Arc<AppServices>,
|
||||
pub db: Arc<Database>,
|
||||
@ -337,9 +337,12 @@ impl System {
|
||||
fn normalize_attack_type(raw: &str) -> String {
|
||||
match raw {
|
||||
"Brute Force" => "brute_force".to_string(),
|
||||
"DDoS" | "DoS" => "threat_detected".to_string(),
|
||||
"C2 Communication" => "c2_communication".to_string(),
|
||||
"Cryptomining" => "cryptomining".to_string(),
|
||||
"DoS/DDoS" => "threat_detected".to_string(),
|
||||
"Exploitation" => "threat_detected".to_string(),
|
||||
"Reconnaissance" => "port_scan".to_string(),
|
||||
"Normal" => "normal".to_string(),
|
||||
other => {
|
||||
log!(DetectionLog::UnknownMlAttackType(other.to_string()));
|
||||
"threat_detected".to_string()
|
||||
@ -383,11 +386,17 @@ impl System {
|
||||
loop {
|
||||
match rx.recv().await {
|
||||
Ok(alert) => {
|
||||
let raw_type = alert.attack_type.as_deref().unwrap_or("unknown");
|
||||
let normalized = Self::normalize_attack_type(raw_type);
|
||||
|
||||
// "Normal" class means benign — no SOAR trigger needed
|
||||
if normalized == "normal" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let event = DetectionEvent {
|
||||
source: DetectionSource::ML,
|
||||
attack_type: Self::normalize_attack_type(
|
||||
&alert.attack_type.unwrap_or_else(|| "unknown".into()),
|
||||
),
|
||||
attack_type: normalized,
|
||||
confidence: alert.confidence,
|
||||
source_ip: alert.src_ip,
|
||||
dest_ip: alert.dst_ip,
|
||||
@ -439,13 +448,18 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_ddos() {
|
||||
assert_eq!(System::normalize_attack_type("DDoS"), "threat_detected");
|
||||
fn normalize_c2_communication() {
|
||||
assert_eq!(System::normalize_attack_type("C2 Communication"), "c2_communication");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_dos() {
|
||||
assert_eq!(System::normalize_attack_type("DoS"), "threat_detected");
|
||||
fn normalize_cryptomining() {
|
||||
assert_eq!(System::normalize_attack_type("Cryptomining"), "cryptomining");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_dos_ddos() {
|
||||
assert_eq!(System::normalize_attack_type("DoS/DDoS"), "threat_detected");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -458,6 +472,11 @@ mod tests {
|
||||
assert_eq!(System::normalize_attack_type("Reconnaissance"), "port_scan");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_normal() {
|
||||
assert_eq!(System::normalize_attack_type("Normal"), "normal");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_unknown_falls_back_to_threat_detected() {
|
||||
assert_eq!(System::normalize_attack_type("SomethingNew"), "threat_detected");
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
use crate::adapter::persistence::Database;
|
||||
use crate::model::config::{HttpConfig, InferenceConfig as InfConfig, MiscConfig, NetworkConfig, PipelineConfig};
|
||||
use crate::model::system::config::{HttpConfig, InferenceConfig, MiscConfig, NetworkConfig, PipelineConfig};
|
||||
use crate::model::error::Error;
|
||||
use crate::model::error::system::SystemError;
|
||||
|
||||
pub struct AppConfig {
|
||||
pub http: HttpConfig,
|
||||
pub network: NetworkConfig,
|
||||
pub inference: InfConfig,
|
||||
pub inference: InferenceConfig,
|
||||
pub misc: MiscConfig,
|
||||
pub pipeline: PipelineConfig,
|
||||
}
|
||||
@ -109,7 +109,7 @@ impl AppConfig {
|
||||
packet_buffer_size: 2048,
|
||||
buffer_pool_capacity: 1024,
|
||||
},
|
||||
inference: InfConfig {
|
||||
inference: InferenceConfig {
|
||||
deep_autoencoder_name: "deep_autoencoder.onnx".into(),
|
||||
classifier_name: "classifier.onnx".into(),
|
||||
models_config_name: "inference_config.json".into(),
|
||||
|
||||
@ -6,7 +6,7 @@ use macros::log;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use crate::core::ml::alert::MLAlert;
|
||||
use crate::core::ml::config_loader::InferenceConfig;
|
||||
use crate::model::system::config::MLInferenceConfig;
|
||||
use crate::core::ml::drift_detector::DriftDetector;
|
||||
use crate::core::ml::engine::Engine;
|
||||
use crate::core::ml::model_loader::MLModels;
|
||||
@ -19,7 +19,7 @@ use crate::model::error::Error;
|
||||
use crate::model::error::misc::MiscError;
|
||||
use crate::model::error::system::SystemError;
|
||||
use crate::model::log::system::SystemLog;
|
||||
use crate::model::ml_detection::EngineConfig;
|
||||
use crate::model::detection::ml_detection::EngineConfig;
|
||||
|
||||
/// Application-level service orchestrator.
|
||||
/// Holds all runtime services (health monitoring, ML inference, flow statistics)
|
||||
@ -36,7 +36,7 @@ pub struct AppServices {
|
||||
impl AppServices {
|
||||
pub fn new(
|
||||
app_config: Arc<AppConfig>,
|
||||
inference_config: Arc<InferenceConfig>,
|
||||
inference_config: Arc<MLInferenceConfig>,
|
||||
drift_detector: Arc<parking_lot::Mutex<DriftDetector>>,
|
||||
) -> Result<Self, Error> {
|
||||
let health = SystemHealth::new(app_config.clone())?;
|
||||
|
||||
@ -8,7 +8,7 @@ use tokio::time::interval;
|
||||
|
||||
use crate::infrastructure::app_config::AppConfig;
|
||||
use crate::model::error::Error;
|
||||
use crate::model::health::{
|
||||
use crate::model::system::health::{
|
||||
ConfiguredNetworkStats, CpuCoreInfo, CpuDetails, LoadAverage, MemoryUsage, NetworkStats, SystemHealthMetrics,
|
||||
SystemHealthStatus, SystemInfo,
|
||||
};
|
||||
|
||||
@ -17,7 +17,7 @@ use crate::core::auth::setup_guard::{SetupCompleteFlag, SetupGuard};
|
||||
use crate::core::config_service::ConfigService;
|
||||
use crate::core::dns_filter_service::DnsFilterService;
|
||||
use crate::core::ebpf::EbpfServices;
|
||||
use crate::core::ml::config_loader::InferenceConfig;
|
||||
use crate::model::system::config::MLInferenceConfig;
|
||||
use crate::core::notification_service::NotificationService;
|
||||
use crate::core::playbook_service::PlaybookService;
|
||||
use crate::core::rate_limit_service::RateLimitService;
|
||||
@ -42,7 +42,7 @@ use crate::model::system::readiness::ReadinessState;
|
||||
/// Parameters for starting the HTTP server, avoiding `#[cfg]` on function params.
|
||||
pub struct HttpServerParams {
|
||||
pub app_config: Arc<AppConfig>,
|
||||
pub inference_config: Arc<InferenceConfig>,
|
||||
pub inference_config: Arc<MLInferenceConfig>,
|
||||
pub ebpf_services: Arc<EbpfServices>,
|
||||
pub app_services: Arc<AppServices>,
|
||||
pub db: Arc<Database>,
|
||||
|
||||
@ -19,7 +19,7 @@ use crate::core::config_service::ConfigService;
|
||||
use crate::core::dns_filter_service::DnsFilterService;
|
||||
use crate::core::ebpf::EbpfServices;
|
||||
use crate::core::email::scheduler::ReportScheduler;
|
||||
use crate::core::ml::config_loader::InferenceConfig;
|
||||
use crate::model::system::config::MLInferenceConfig;
|
||||
use crate::core::ml::drift_detector::DriftDetector;
|
||||
use crate::core::notification_service::NotificationService;
|
||||
use crate::core::playbook_service::PlaybookService;
|
||||
@ -40,11 +40,11 @@ use crate::interface::port::repository::RepositoryPort;
|
||||
use crate::interface::port::secret_store::SecretStorePort;
|
||||
use crate::interface::port::soar::SoarPort;
|
||||
use crate::model::detection::drift::FeatureBaselines;
|
||||
use crate::model::direction::FlowDirection;
|
||||
use crate::model::monitoring::direction::FlowDirection;
|
||||
use crate::model::error::Error;
|
||||
use crate::model::error::ebpf::EbpfError;
|
||||
use crate::model::error::misc::MiscError;
|
||||
use crate::model::list_type::ListType;
|
||||
use crate::model::access_control::list_type::ListType;
|
||||
use crate::model::log::ebpf::EbpfLog;
|
||||
use crate::model::log::system::SystemLog;
|
||||
use macros::log;
|
||||
@ -52,7 +52,7 @@ use macros::log;
|
||||
/// Holds all Arc-wrapped services that make up the running application.
|
||||
pub struct AppState {
|
||||
pub app_config: Arc<AppConfig>,
|
||||
pub inference_config: Arc<InferenceConfig>,
|
||||
pub inference_config: Arc<MLInferenceConfig>,
|
||||
pub ebpf_services: Arc<EbpfServices>,
|
||||
pub app_services: Arc<AppServices>,
|
||||
pub db: Arc<Database>,
|
||||
@ -101,7 +101,7 @@ impl ServiceFactory {
|
||||
|
||||
let ingress_program_array = Self::configure_ingress_pipeline(&mut ingress_ebpf, &app_config.pipeline.ingress)?;
|
||||
|
||||
let inference_config = Arc::new(InferenceConfig::load_file(&app_config.inference.models_config_name)?);
|
||||
let inference_config = Arc::new(MLInferenceConfig::load_file(&app_config.inference.models_config_name)?);
|
||||
|
||||
// Write queue count to eBPF maps for symmetric hash redirect
|
||||
let num_queues = app_config.network.combined_queue_count;
|
||||
|
||||
@ -3,8 +3,8 @@ use std::time;
|
||||
|
||||
use crate::core::ml::engine::Engine;
|
||||
use crate::core::ml::flow_tracker::FlowData;
|
||||
use crate::model::direction::Direction;
|
||||
use crate::model::flow_stats::{FlowPushPayload, FlowStatsEntry, FlowSubscription, FlowSummary, StatsSummary};
|
||||
use crate::model::monitoring::direction::Direction;
|
||||
use crate::model::monitoring::flow_stats::{FlowPushPayload, FlowStatsEntry, FlowSubscription, FlowSummary, StatsSummary};
|
||||
|
||||
/// Conversion from core::ml::FlowData to model::FlowStatsEntry.
|
||||
/// Placed here (core layer) to maintain dependency rule: model/ must not import core/.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::model::auth::Claims;
|
||||
use crate::model::identity::auth::Claims;
|
||||
use crate::model::error::Error;
|
||||
|
||||
/// Type alias for API key list items: (id, name, permission_level, created_at, last_used_at)
|
||||
|
||||
@ -1,5 +1 @@
|
||||
pub mod constants;
|
||||
|
||||
// Backward-compatible re-exports: `crate::model::config::*` continues to resolve
|
||||
// the domain config types that previously lived at `crate::model::system::config::*`.
|
||||
pub use super::system::config::*;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::model::ml_detection::ClipParams;
|
||||
use crate::model::detection::ml_detection::ClipParams;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FlowFeatures {
|
||||
|
||||
@ -3,8 +3,8 @@ use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tract_onnx::prelude::{Graph, SimplePlan, TypedFact, TypedOp};
|
||||
|
||||
use crate::model::direction::Direction;
|
||||
use crate::model::user_packet::UserPacket;
|
||||
use crate::model::monitoring::direction::Direction;
|
||||
use crate::model::monitoring::user_packet::UserPacket;
|
||||
|
||||
pub type RunnableModel = SimplePlan<TypedFact, Box<dyn TypedOp>, Graph<TypedFact, Box<dyn TypedOp>>>;
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
// Bounded Context subdirectories
|
||||
pub mod access_control;
|
||||
pub mod config;
|
||||
pub mod detection;
|
||||
@ -10,14 +9,3 @@ pub mod monitoring;
|
||||
pub mod report;
|
||||
pub mod soar;
|
||||
pub mod system;
|
||||
|
||||
// Backward-compatible re-exports (existing imports continue to work)
|
||||
pub use access_control::ip_address;
|
||||
pub use access_control::list_type;
|
||||
pub use detection::ml_detection;
|
||||
pub use identity::auth;
|
||||
pub use monitoring::direction;
|
||||
pub use monitoring::drop_event;
|
||||
pub use monitoring::flow_stats;
|
||||
pub use monitoring::user_packet;
|
||||
pub use system::health;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::model::direction::Direction;
|
||||
use crate::model::monitoring::direction::Direction;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct FlowStatsEntry {
|
||||
|
||||
@ -7,10 +7,8 @@ pub struct Playbook {
|
||||
pub name: String,
|
||||
pub enabled: bool,
|
||||
pub trigger_event: String,
|
||||
pub condition_threshold: Option<f64>,
|
||||
pub cooldown_secs: i64,
|
||||
pub actions: Vec<PlaybookAction>,
|
||||
/// Multi-condition rules (AND logic). Empty = legacy threshold-only mode.
|
||||
pub conditions: Vec<PlaybookCondition>,
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::model::ml_detection::ClipParams;
|
||||
use crate::model::detection::ml_detection::ClipParams;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct HttpConfig {
|
||||
@ -86,6 +86,11 @@ pub struct MLInferenceConfig {
|
||||
pub ae_threshold: f32,
|
||||
pub classifier_feature_names: Vec<String>,
|
||||
pub attack_labels: HashMap<String, String>,
|
||||
pub anomaly_threshold: f32,
|
||||
pub c2_threshold: f32,
|
||||
pub model_type: String,
|
||||
pub output_names: Vec<String>,
|
||||
pub ae_feature_weights: HashMap<String, f64>,
|
||||
}
|
||||
|
||||
impl MLInferenceConfig {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::time;
|
||||
|
||||
use crate::model::user_packet::UserPacket;
|
||||
use crate::model::monitoring::user_packet::UserPacket;
|
||||
|
||||
pub fn parse_packet(packet_data: &[u8]) -> Option<(UserPacket, usize)> {
|
||||
if packet_data.len() < 14 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user