JoeiBanana commited on
Commit
36f1ad5
·
verified ·
1 Parent(s): 1f6f38d

Upload batch 1/8

Browse files
Files changed (50) hide show
  1. .gitattributes +5 -32
  2. .gitignore +45 -0
  3. backend/app/api/incident.py +0 -0
  4. backend/app/core/config.py +30 -0
  5. backend/app/llm/translator_llm.py +55 -0
  6. backend/app/main.py +0 -0
  7. backend/app/net/restconf_client.py +55 -0
  8. backend/app/pipeline/incident_router.py +178 -0
  9. backend/app/pipeline/pipeline.py +54 -0
  10. cases/bgp1_test_incidents/incident1_session_flap/CORE-R1/show_interface_gi0_1.txt +12 -0
  11. cases/bgp1_test_incidents/incident1_session_flap/CORE-R1/show_ip_bgp_neighbors_192.0.2.2.txt +9 -0
  12. cases/bgp1_test_incidents/incident1_session_flap/CORE-R1/show_ip_bgp_summary.txt +8 -0
  13. cases/bgp1_test_incidents/incident1_session_flap/CORE-R1/show_logging_include_bgp.txt +6 -0
  14. cases/bgp1_test_incidents/incident1_session_flap/EDGE-R1/show_ip_bgp_neighbors.txt +4 -0
  15. cases/bgp1_test_incidents/incident1_session_flap/EDGE-R1/show_ip_bgp_summary.txt +2 -0
  16. cases/bgp1_test_incidents/incident1_session_flap/EDGE-RTR1/show_ip_bgp_neighbors_203.0.113.1.txt +5 -0
  17. cases/bgp1_test_incidents/incident1_session_flap/EDGE-RTR1/show_ip_bgp_summary.txt +5 -0
  18. cases/bgp1_test_incidents/incident1_session_flap/incident_info.json +3 -0
  19. cases/bgp1_test_incidents/incident1_session_flap/wazuh_alert.json +8 -0
  20. cases/bgp1_test_incidents/incident2_hold_timer_expiration/BRANCH-RTR1/show_ip_bgp_neighbors_198.51.100.1.txt +3 -0
  21. cases/bgp1_test_incidents/incident2_hold_timer_expiration/BRANCH-RTR1/show_ip_bgp_summary.txt +3 -0
  22. cases/bgp1_test_incidents/incident2_hold_timer_expiration/CORE-R1/show_interface_gi0_2.txt +7 -0
  23. cases/bgp1_test_incidents/incident2_hold_timer_expiration/CORE-R1/show_ip_bgp_neighbors_198.51.100.2.txt +7 -0
  24. cases/bgp1_test_incidents/incident2_hold_timer_expiration/CORE-R1/show_ip_bgp_summary.txt +3 -0
  25. cases/bgp1_test_incidents/incident2_hold_timer_expiration/CORE-R1/show_logging_include_bgp.txt +4 -0
  26. cases/bgp1_test_incidents/incident2_hold_timer_expiration/incident_info.json +3 -0
  27. cases/bgp1_test_incidents/incident2_hold_timer_expiration/wazuh_alert.json +8 -0
  28. cases/bgp1_test_incidents/incident3_neighborship_reset/CORE-R1/show_ip_bgp_neighbors_192.0.2.2.txt +3 -0
  29. cases/bgp1_test_incidents/incident3_neighborship_reset/CORE-R1/show_ip_bgp_summary.txt +3 -0
  30. cases/bgp1_test_incidents/incident3_neighborship_reset/EDGE-RTR1/show_ip_bgp_neighbors_203.0.113.1.txt +6 -0
  31. cases/bgp1_test_incidents/incident3_neighborship_reset/EDGE-RTR1/show_ip_bgp_summary.txt +3 -0
  32. cases/bgp1_test_incidents/incident3_neighborship_reset/EDGE-RTR1/show_logging_include_bgp.txt +3 -0
  33. cases/bgp1_test_incidents/incident3_neighborship_reset/incident_info.json +3 -0
  34. cases/bgp1_test_incidents/incident3_neighborship_reset/wazuh_alert.json +8 -0
  35. cases/bgp2_test_incidents/incident1_prefix_limit_exceeded/CORE-R1/show_ip_bgp_neighbors_192.0.2.2.txt +10 -0
  36. cases/bgp2_test_incidents/incident1_prefix_limit_exceeded/CORE-R1/show_ip_bgp_summary.txt +8 -0
  37. cases/bgp2_test_incidents/incident1_prefix_limit_exceeded/CORE-R1/show_logging_include_bgp.txt +3 -0
  38. cases/bgp2_test_incidents/incident1_prefix_limit_exceeded/EDGE-RTR1/show_ip_bgp_summary.txt +3 -0
  39. cases/bgp2_test_incidents/incident1_prefix_limit_exceeded/incident_info.json +3 -0
  40. cases/bgp2_test_incidents/incident1_prefix_limit_exceeded/wazuh_alert.json +8 -0
  41. cases/bgp2_test_incidents/incident2_route_leak_suspected/BRANCH-RTR1/show_run_bgp_snippet.txt +4 -0
  42. cases/bgp2_test_incidents/incident2_route_leak_suspected/CORE-R1/show_ip_bgp_community_no_export.txt +3 -0
  43. cases/bgp2_test_incidents/incident2_route_leak_suspected/CORE-R1/show_ip_bgp_neighbors_198.51.100.2.txt +10 -0
  44. cases/bgp2_test_incidents/incident2_route_leak_suspected/CORE-R1/show_ip_bgp_summary.txt +4 -0
  45. cases/bgp2_test_incidents/incident2_route_leak_suspected/CORE-R1/show_logging_include_bgp.txt +3 -0
  46. cases/bgp2_test_incidents/incident2_route_leak_suspected/incident_info.json +3 -0
  47. cases/bgp2_test_incidents/incident2_route_leak_suspected/wazuh_alert.json +8 -0
  48. cases/bgp2_test_incidents/incident3_path_selection_incorrect/CORE-R1/show_ip_bgp_203.0.113.0_24.txt +14 -0
  49. cases/bgp2_test_incidents/incident3_path_selection_incorrect/CORE-R1/show_ip_bgp_bestpath.txt +4 -0
  50. cases/bgp2_test_incidents/incident3_path_selection_incorrect/CORE-R1/show_ip_bgp_neighbors_192.0.2.2_policy.txt +4 -0
.gitattributes CHANGED
@@ -1,35 +1,8 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
  *.pt filter=lfs diff=lfs merge=lfs -text
23
  *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
 
2
  *.bin filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  *.pt filter=lfs diff=lfs merge=lfs -text
4
  *.pth filter=lfs diff=lfs merge=lfs -text
5
+ *.gguf filter=lfs diff=lfs merge=lfs -text
6
+ *.ggml filter=lfs diff=lfs merge=lfs -text
7
+ *.h5 filter=lfs diff=lfs merge=lfs -text
8
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
.gitignore ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Runtime outputs
2
+ show_outputs/
3
+ show_outputs*.zip
4
+ pipeline_stage1.jsonl
5
+ pipeline_fix_output.jsonl
6
+ pipeline_results.jsonl
7
+ results.jsonl
8
+ results_raw.jsonl
9
+ results_raw1.jsonl
10
+ selector_output.jsonl
11
+ selector_output1.jsonl
12
+ s_alerts.jsonl
13
+ alerts_transformed.jsonl
14
+ filtered.json
15
+ filtered_enriched.jsonl
16
+ show_commands.jsonl
17
+ test_llmt_output.jsonl
18
+ cisco_input.jsonl
19
+ cisco_input1.jsonl
20
+ cisco_output.jsonl
21
+
22
+ # Logs
23
+ *.log
24
+ logs.txt
25
+ alerts.txt
26
+
27
+ # Python
28
+ __pycache__/
29
+ *.py[cod]
30
+ *.egg-info/
31
+ .venv/
32
+ venv/
33
+ env/
34
+
35
+ # LoRA checkpoints (keep only final adapter_model.safetensors)
36
+ **/checkpoint-*/
37
+
38
+ # Scratch / test files
39
+ aftertest*.py
40
+ analyze_logs.py
41
+ count_tokens.py
42
+ tesp_ospf4.py
43
+
44
+ # Large temp files
45
+ *.tmp
backend/app/api/incident.py ADDED
File without changes
backend/app/core/config.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from pathlib import Path
3
+
4
+ BASE_DIR = Path(__file__).resolve().parents[3]
5
+ MODELS_DIR = BASE_DIR / "models"
6
+
7
+
8
+ class ModelRegistry:
9
+ def __init__(self, registry_path: Path):
10
+ if not registry_path.exists():
11
+ raise FileNotFoundError(f"Registry not found: {registry_path}")
12
+
13
+ with open(registry_path, "r", encoding="utf-8") as f:
14
+ self.registry = json.load(f)
15
+
16
+ self.base_model_path = Path(self.registry["base_model_path"])
17
+ self.adapters = self.registry["adapters"]
18
+
19
+ def get_base_model(self) -> Path:
20
+ return self.base_model_path
21
+
22
+ def get_adapter(self, name: str) -> Path:
23
+ if name not in self.adapters:
24
+ raise KeyError(f"Adapter '{name}' not found in registry")
25
+ return Path(self.adapters[name])
26
+
27
+
28
+ REGISTRY = ModelRegistry(
29
+ registry_path=MODELS_DIR / "registry.json"
30
+ )
backend/app/llm/translator_llm.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import AutoTokenizer, AutoModelForCausalLM
2
+ from peft import PeftModel
3
+ import torch
4
+
5
+ from app.core.config import REGISTRY
6
+
7
+
8
+ class TranslatorLLM:
9
+ def __init__(self):
10
+ base_path = REGISTRY.get_base_model()
11
+ adapter_path = REGISTRY.get_adapter("llm_t")
12
+
13
+ self.tokenizer = AutoTokenizer.from_pretrained(base_path)
14
+ base_model = AutoModelForCausalLM.from_pretrained(
15
+ base_path,
16
+ torch_dtype=torch.float16,
17
+ device_map="auto"
18
+ )
19
+ self.model = PeftModel.from_pretrained(base_model, adapter_path)
20
+
21
+ def translate(self, cli_commands: list[str], mode: str) -> list[dict]:
22
+ """
23
+ mode = 'show' -> RESTCONF GET
24
+ mode = 'fix' -> RESTCONF PATCH/POST
25
+ """
26
+ prompt = self._build_prompt(cli_commands, mode)
27
+
28
+ inputs = self.tokenizer(prompt, return_tensors="pt").to(self.model.device)
29
+ outputs = self.model.generate(
30
+ **inputs,
31
+ max_new_tokens=512,
32
+ do_sample=False
33
+ )
34
+
35
+ text = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
36
+ return self._parse_restconf(text)
37
+
38
+ def _build_prompt(self, cli_commands: list[str], mode: str) -> str:
39
+ header = (
40
+ "Convert the following Cisco CLI commands into RESTCONF API calls.\n"
41
+ f"Mode: {mode.upper()}\n"
42
+ "Return ONLY valid JSON.\n\n"
43
+ )
44
+ body = "\n".join(cli_commands)
45
+ return header + body
46
+
47
+ def _parse_restconf(self, output: str) -> list[dict]:
48
+ """
49
+ Očakáva JSON array RESTCONF callov.
50
+ """
51
+ import json
52
+ try:
53
+ return json.loads(output)
54
+ except Exception:
55
+ raise ValueError(f"Invalid RESTCONF output:\n{output}")
backend/app/main.py ADDED
File without changes
backend/app/net/restconf_client.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import AutoTokenizer, AutoModelForCausalLM
2
+ from peft import PeftModel
3
+ import torch
4
+
5
+ from app.core.config import REGISTRY
6
+
7
+
8
+ class TranslatorLLM:
9
+ def __init__(self):
10
+ base_path = REGISTRY.get_base_model()
11
+ adapter_path = REGISTRY.get_adapter("llm_t")
12
+
13
+ self.tokenizer = AutoTokenizer.from_pretrained(base_path)
14
+ base_model = AutoModelForCausalLM.from_pretrained(
15
+ base_path,
16
+ torch_dtype=torch.float16,
17
+ device_map="auto"
18
+ )
19
+ self.model = PeftModel.from_pretrained(base_model, adapter_path)
20
+
21
+ def translate(self, cli_commands: list[str], mode: str) -> list[dict]:
22
+ """
23
+ mode = 'show' -> RESTCONF GET
24
+ mode = 'fix' -> RESTCONF PATCH/POST
25
+ """
26
+ prompt = self._build_prompt(cli_commands, mode)
27
+
28
+ inputs = self.tokenizer(prompt, return_tensors="pt").to(self.model.device)
29
+ outputs = self.model.generate(
30
+ **inputs,
31
+ max_new_tokens=512,
32
+ do_sample=False
33
+ )
34
+
35
+ text = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
36
+ return self._parse_restconf(text)
37
+
38
+ def _build_prompt(self, cli_commands: list[str], mode: str) -> str:
39
+ header = (
40
+ "Convert the following Cisco CLI commands into RESTCONF API calls.\n"
41
+ f"Mode: {mode.upper()}\n"
42
+ "Return ONLY valid JSON.\n\n"
43
+ )
44
+ body = "\n".join(cli_commands)
45
+ return header + body
46
+
47
+ def _parse_restconf(self, output: str) -> list[dict]:
48
+ """
49
+ Očakáva JSON array RESTCONF callov.
50
+ """
51
+ import json
52
+ try:
53
+ return json.loads(output)
54
+ except Exception:
55
+ raise ValueError(f"Invalid RESTCONF output:\n{output}")
backend/app/pipeline/incident_router.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ from dataclasses import dataclass
3
+ from typing import Dict, Any, Optional
4
+
5
+
6
+ @dataclass(frozen=True)
7
+ class RouteDecision:
8
+ """
9
+ Výsledok rozhodnutia: ktorý incident model použiť.
10
+ adapter_key musí existovať v registry.json (REGISTRY.get_adapter(adapter_key))
11
+ """
12
+ adapter_key: str
13
+ domain: str
14
+ reason: str
15
+
16
+
17
+ class IncidentRouter:
18
+ """
19
+ Deterministický router pre výber incident LLM.
20
+
21
+ Vstupy:
22
+ - wazuh_alert: dict (z LLM1 / priamo z Wazuh)
23
+ - evidence_text: Optional[str] (normalizované outputs zo show/RESTCONF)
24
+
25
+ Výstup:
26
+ - RouteDecision(adapter_key=..., domain=..., reason=...)
27
+ """
28
+
29
+ # --- Regex pomocníky ---
30
+ _re_ospf = re.compile(r"\bospf\b|neighbor|adjacency|lsa|lsdb|exstart|exchange", re.IGNORECASE)
31
+ _re_bgp = re.compile(r"\bbgp\b|neighbor|hold|keepalive|prefix|route leak|afi|safi|rib|next-hop", re.IGNORECASE)
32
+ _re_switch = re.compile(r"\bstp\b|spanning-tree|vlan|trunk|native vlan|mac flapp|err-disable|storm", re.IGNORECASE)
33
+ _re_sec = re.compile(r"\bacl\b|access-list|nat\b|spoof|ssh brute|failed login|snmp", re.IGNORECASE)
34
+ _re_service = re.compile(r"\bdns\b|nxdomain|ntp\b|unsync|clock is unsynchronized|arp spoof", re.IGNORECASE)
35
+
36
+ # OSPF sub-incident patterns
37
+ _re_ospf_exstart = re.compile(r"exstart|exchange|mtu mismatch|database exchange", re.IGNORECASE)
38
+ _re_ospf_auth = re.compile(r"auth|authentication|md5|key mismatch", re.IGNORECASE)
39
+ _re_ospf_timers = re.compile(r"hello|dead|timer mismatch", re.IGNORECASE)
40
+ _re_ospf_lsa = re.compile(r"lsa flood|lsdb inconsisten|too many lsa|throttle", re.IGNORECASE)
41
+
42
+ # BGP sub-incident patterns
43
+ _re_bgp_session = re.compile(r"session flap|hold timer|reset|cease|neighbor down|not established", re.IGNORECASE)
44
+ _re_bgp_policy = re.compile(r"prefix limit|route leak|path selection|weight|local-pref", re.IGNORECASE)
45
+ _re_bgp_rib = re.compile(r"missing routes|not in rib|next-hop self|afi|safi|address-family", re.IGNORECASE)
46
+
47
+ # SWITCH sub-incident patterns
48
+ _re_sw_l2 = re.compile(r"mac flapp|stp topology change|err-disable", re.IGNORECASE)
49
+ _re_sw_trunk = re.compile(r"vlan mismatch|trunk negotiation|native vlan mismatch", re.IGNORECASE)
50
+ _re_sw_misc = re.compile(r"lldp|cdp|storm control|broadcast storm", re.IGNORECASE)
51
+
52
+ # SECURITY sub-incident patterns
53
+ _re_sec_acl = re.compile(r"acl blocking|acl misconfig|excessive deny|access-list", re.IGNORECASE)
54
+ _re_sec_fw_nat_spoof = re.compile(r"connection exhaust|nat translation|spoofing suspected|uRPF|ip verify source", re.IGNORECASE)
55
+ _re_sec_bruteforce = re.compile(r"ssh brute|failed login|snmp brute", re.IGNORECASE)
56
+
57
+ # SERVICE sub-incident patterns
58
+ _re_svc_dhcp = re.compile(r"dhcp conflict|dhcp starvation|pool utilization|discover rate|ip conflict", re.IGNORECASE)
59
+ _re_svc_dns_ntp_arp = re.compile(r"dns|nxdomain|ntp|unsync|arp spoof", re.IGNORECASE)
60
+
61
+ def decide(self, wazuh_alert: Dict[str, Any], evidence_text: Optional[str] = None) -> RouteDecision:
62
+ text = self._join_text(wazuh_alert, evidence_text)
63
+
64
+ # 1) Hrubá doména
65
+ domain = self._detect_domain(text)
66
+
67
+ # 2) Sub-router podľa domény
68
+ if domain == "ospf":
69
+ return self._route_ospf(text)
70
+ if domain == "bgp":
71
+ return self._route_bgp(text)
72
+ if domain == "switch":
73
+ return self._route_switch(text)
74
+ if domain == "security":
75
+ return self._route_security(text)
76
+ if domain == "service":
77
+ return self._route_service(text)
78
+
79
+ # fallback: bezpečný default (nič nemení) – môžeš premapovať na "manual_review"
80
+ return RouteDecision(
81
+ adapter_key="incident_manual_review",
82
+ domain="unknown",
83
+ reason="No reliable domain match; route to manual review."
84
+ )
85
+
86
+ # --------------------------
87
+ # Domain detection
88
+ # --------------------------
89
+ def _detect_domain(self, text: str) -> str:
90
+ # poradie je zámerné – aby sa napr. "arp spoofing" neprilepilo k switch
91
+ if self._re_ospf.search(text):
92
+ return "ospf"
93
+ if self._re_bgp.search(text):
94
+ return "bgp"
95
+ if self._re_sec.search(text):
96
+ return "security"
97
+ if self._re_service.search(text):
98
+ return "service"
99
+ if self._re_switch.search(text):
100
+ return "switch"
101
+ return "unknown"
102
+
103
+ # --------------------------
104
+ # OSPF routing
105
+ # --------------------------
106
+ def _route_ospf(self, text: str) -> RouteDecision:
107
+ if self._re_ospf_exstart.search(text):
108
+ return RouteDecision("incident_ospf2", "ospf", "Matched EXSTART/EXCHANGE/MTU patterns.")
109
+ if self._re_ospf_auth.search(text) or self._re_ospf_timers.search(text):
110
+ return RouteDecision("incident_ospf3", "ospf", "Matched auth/timers/network-type mismatch patterns.")
111
+ if self._re_ospf_lsa.search(text):
112
+ return RouteDecision("incident_ospf4", "ospf", "Matched LSA/LSDB/redistribution patterns.")
113
+ # default OSPF adjacency loss/basic
114
+ return RouteDecision("incident_ospf1", "ospf", "Default OSPF adjacency/basic incident.")
115
+
116
+ # --------------------------
117
+ # BGP routing
118
+ # --------------------------
119
+ def _route_bgp(self, text: str) -> RouteDecision:
120
+ if self._re_bgp_session.search(text):
121
+ return RouteDecision("incident_bgp1", "bgp", "Matched session flap/hold timer/reset patterns.")
122
+ if self._re_bgp_policy.search(text):
123
+ return RouteDecision("incident_bgp2", "bgp", "Matched prefix-limit/route-leak/path selection patterns.")
124
+ if self._re_bgp_rib.search(text):
125
+ return RouteDecision("incident_bgp3", "bgp", "Matched RIB/next-hop/AFI-SAFI patterns.")
126
+ # fallback
127
+ return RouteDecision("incident_bgp1", "bgp", "Default BGP incident (session-level fallback).")
128
+
129
+ # --------------------------
130
+ # Switch routing
131
+ # --------------------------
132
+ def _route_switch(self, text: str) -> RouteDecision:
133
+ if self._re_sw_l2.search(text):
134
+ return RouteDecision("incident_switch1", "switch", "Matched MAC flap/STP change/err-disable patterns.")
135
+ if self._re_sw_trunk.search(text):
136
+ return RouteDecision("incident_switch2", "switch", "Matched VLAN/trunk/native VLAN mismatch patterns.")
137
+ if self._re_sw_misc.search(text):
138
+ return RouteDecision("incident_switch3", "switch", "Matched LLDP/CDP/storm-control patterns.")
139
+ return RouteDecision("incident_switch1", "switch", "Default switch incident (L2 fallback).")
140
+
141
+ # --------------------------
142
+ # Security routing
143
+ # --------------------------
144
+ def _route_security(self, text: str) -> RouteDecision:
145
+ if self._re_sec_acl.search(text):
146
+ return RouteDecision("incident_sec1", "security", "Matched ACL-related patterns.")
147
+ if self._re_sec_fw_nat_spoof.search(text):
148
+ return RouteDecision("incident_sec2", "security", "Matched FW/NAT/spoofing patterns.")
149
+ if self._re_sec_bruteforce.search(text):
150
+ return RouteDecision("incident_sec3", "security", "Matched SSH/SNMP brute force patterns.")
151
+ return RouteDecision("incident_sec1", "security", "Default security incident (ACL fallback).")
152
+
153
+ # --------------------------
154
+ # Service routing
155
+ # --------------------------
156
+ def _route_service(self, text: str) -> RouteDecision:
157
+ if self._re_svc_dhcp.search(text):
158
+ return RouteDecision("incident_service1", "service", "Matched DHCP/IP conflict/starvation patterns.")
159
+ if self._re_svc_dns_ntp_arp.search(text):
160
+ return RouteDecision("incident_service2", "service", "Matched DNS/NTP/ARP spoofing patterns.")
161
+ return RouteDecision("incident_service2", "service", "Default service incident fallback.")
162
+
163
+ # --------------------------
164
+ # Helpers
165
+ # --------------------------
166
+ @staticmethod
167
+ def _join_text(wazuh_alert: Dict[str, Any], evidence_text: Optional[str]) -> str:
168
+ parts = []
169
+ # vyber najdôležitejšie polia – robustné aj pri rozdielnych schemách
170
+ for k in ("rule", "event", "description", "severity", "device", "src_ip", "agent_name"):
171
+ v = wazuh_alert.get(k)
172
+ if v is not None:
173
+ parts.append(f"{k}: {v}")
174
+ # ak je alert celý už normalizovaný, prilep aj jeho raw json
175
+ parts.append(str(wazuh_alert))
176
+ if evidence_text:
177
+ parts.append(evidence_text)
178
+ return "\n".join(parts)
backend/app/pipeline/pipeline.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from app.llm.translator_llm import TranslatorLLM
2
+ from app.net.restconf_client import RestconfClient
3
+
4
+
5
+ class IncidentPipeline:
6
+ def __init__(self, inventory: dict):
7
+ """
8
+ inventory = {
9
+ "CORE-R1": {
10
+ "restconf_url": "...",
11
+ "username": "...",
12
+ "password": "..."
13
+ }
14
+ }
15
+ """
16
+ self.inventory = inventory
17
+ self.translator = TranslatorLLM()
18
+
19
+ def run(
20
+ self,
21
+ device: str,
22
+ show_cli: list[str],
23
+ fix_cli: list[str] | None = None
24
+ ) -> dict:
25
+ # ---- SHOW PHASE ----
26
+ show_rest = self.translator.translate(show_cli, mode="show")
27
+
28
+ client = self._get_client(device)
29
+ show_results = client.execute(show_rest)
30
+
31
+ # ---- INCIDENT LLM by tu reálne bežala ----
32
+ # fix_cli = incident_llm(wazuh_alert, show_results)
33
+
34
+ fix_results = None
35
+ if fix_cli:
36
+ fix_rest = self.translator.translate(fix_cli, mode="fix")
37
+ fix_results = client.execute(fix_rest)
38
+
39
+ return {
40
+ "device": device,
41
+ "show": show_results,
42
+ "fix": fix_results
43
+ }
44
+
45
+ def _get_client(self, device: str) -> RestconfClient:
46
+ if device not in self.inventory:
47
+ raise KeyError(f"Device '{device}' not found in inventory")
48
+
49
+ d = self.inventory[device]
50
+ return RestconfClient(
51
+ base_url=d["restconf_url"],
52
+ username=d["username"],
53
+ password=d["password"]
54
+ )
cases/bgp1_test_incidents/incident1_session_flap/CORE-R1/show_interface_gi0_1.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GigabitEthernet0/1 is up, line protocol is up
2
+ Hardware is iGbE, address is 0c01.1111.0001 (bia 0c01.1111.0001)
3
+ Internet address is 203.0.113.1/30
4
+ MTU 1500 bytes, BW 1000000 Kbit/sec, DLY 10 usec,
5
+ reliability 255/255, txload 1/255, rxload 1/255
6
+ Encapsulation ARPA, loopback not set
7
+ Keepalive set (10 sec)
8
+ Full Duplex, 1000Mbps, media type is RJ45
9
+ 5 minute input rate 12000 bits/sec, 15 packets/sec
10
+ 5 minute output rate 11000 bits/sec, 14 packets/sec
11
+ 5 minute input errors 0, CRC 0, frame 0, overrun 0, ignored 0
12
+ 5 minute output errors 0, collisions 0, interface resets 6
cases/bgp1_test_incidents/incident1_session_flap/CORE-R1/show_ip_bgp_neighbors_192.0.2.2.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ BGP neighbor is 192.0.2.2, remote AS 65002, external link
2
+ BGP version 4, remote router ID 2.2.2.2
3
+ BGP state = Established, up for 00:02:14
4
+ Last reset 00:00:31, due to Peer reset
5
+ External BGP neighbor may be up to 1 hops away.
6
+ Connection state is ESTAB, I/O status: 1, unread input bytes: 0
7
+ Keepalive interval is 10 seconds, hold time is 30 seconds
8
+ Received 1250 messages, 0 notifications, 0 in queue
9
+ Sent 1310 messages, 1 notifications, 0 in queue
cases/bgp1_test_incidents/incident1_session_flap/CORE-R1/show_ip_bgp_summary.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ BGP router identifier 1.1.1.1, local AS number 65001
2
+ BGP table version is 112, main routing table version 112
3
+ 2 network entries using 288 bytes of memory
4
+ 2 path entries using 160 bytes of memory
5
+
6
+ Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
7
+ 192.0.2.2 4 65002 1250 1310 112 0 0 00:02:14 15
8
+ 198.51.100.2 4 65100 980 965 112 0 0 03:12:40 20
cases/bgp1_test_incidents/incident1_session_flap/CORE-R1/show_logging_include_bgp.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Nov 21 12:07:11.123: %BGP-5-ADJCHANGE: neighbor 192.0.2.2 Down Peer reset
2
+ Nov 21 12:07:18.447: %BGP-5-ADJCHANGE: neighbor 192.0.2.2 Up
3
+ Nov 21 12:08:02.002: %BGP-5-ADJCHANGE: neighbor 192.0.2.2 Down Peer reset
4
+ Nov 21 12:08:10.887: %BGP-5-ADJCHANGE: neighbor 192.0.2.2 Up
5
+ Nov 21 12:09:44.551: %BGP-5-ADJCHANGE: neighbor 192.0.2.2 Down Peer reset
6
+ Nov 21 12:09:52.331: %BGP-5-ADJCHANGE: neighbor 192.0.2.2 Up
cases/bgp1_test_incidents/incident1_session_flap/EDGE-R1/show_ip_bgp_neighbors.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ BGP neighbor is 192.0.2.2, remote AS 65002
2
+ BGP state = Idle
3
+ Last reset due to Hold Timer Expired
4
+ Connections dropped 17 times
cases/bgp1_test_incidents/incident1_session_flap/EDGE-R1/show_ip_bgp_summary.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
2
+ 192.0.2.2 4 65002 1023 998 0 0 0 00:01:12 Idle
cases/bgp1_test_incidents/incident1_session_flap/EDGE-RTR1/show_ip_bgp_neighbors_203.0.113.1.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ BGP neighbor is 203.0.113.1, remote AS 65001, external link
2
+ BGP version 4, remote router ID 1.1.1.1
3
+ BGP state = Established, up for 00:02:13
4
+ Last reset 00:00:39, due to Peer reset
5
+ Keepalive interval is 10 seconds, hold time is 30 seconds
cases/bgp1_test_incidents/incident1_session_flap/EDGE-RTR1/show_ip_bgp_summary.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ BGP router identifier 2.2.2.2, local AS number 65002
2
+ BGP table version is 98, main routing table version 98
3
+
4
+ Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
5
+ 203.0.113.1 4 65001 1309 1249 98 0 0 00:02:13 15
cases/bgp1_test_incidents/incident1_session_flap/incident_info.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "description": "BGP session flap between CORE-R1 (AS65001) and EDGE-RTR1 (AS65002) over Gi0/1"
3
+ }
cases/bgp1_test_incidents/incident1_session_flap/wazuh_alert.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "rule": "BGP session flap detected",
3
+ "device": "CORE-R1",
4
+ "severity": "high",
5
+ "description": "BGP neighbor 192.0.2.2 is flapping frequently (multiple resets in short time).",
6
+ "timestamp": "2025-11-21T12:10:00Z",
7
+ "neighbor": "192.0.2.2"
8
+ }
cases/bgp1_test_incidents/incident2_hold_timer_expiration/BRANCH-RTR1/show_ip_bgp_neighbors_198.51.100.1.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ BGP neighbor is 198.51.100.1, remote AS 65001, external link
2
+ BGP state = Established, up for 00:01:02
3
+ Keepalive interval is 60 seconds, hold time is 180 seconds
cases/bgp1_test_incidents/incident2_hold_timer_expiration/BRANCH-RTR1/show_ip_bgp_summary.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ BGP router identifier 3.3.3.3, local AS number 65010
2
+ Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
3
+ 198.51.100.1 4 65001 610 522 55 0 0 00:01:02 12
cases/bgp1_test_incidents/incident2_hold_timer_expiration/CORE-R1/show_interface_gi0_2.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ GigabitEthernet0/2 is up, line protocol is up
2
+ Internet address is 198.51.100.1/30
3
+ MTU 1500 bytes
4
+ 5 minute input rate 2000 bits/sec, 3 packets/sec
5
+ 5 minute output rate 1800 bits/sec, 2 packets/sec
6
+ input errors 0, CRC 0, frame 0, overrun 0, ignored 0
7
+ output errors 0, collisions 0, interface resets 0
cases/bgp1_test_incidents/incident2_hold_timer_expiration/CORE-R1/show_ip_bgp_neighbors_198.51.100.2.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ BGP neighbor is 198.51.100.2, remote AS 65010, external link
2
+ BGP version 4, remote router ID 3.3.3.3
3
+ BGP state = Active
4
+ Last reset 00:00:12, due to Hold timer expired
5
+ Keepalive interval is 10 seconds, hold time is 30 seconds
6
+ Last read 00:00:31, Last write 00:00:08
7
+ Connections established 14; dropped 14
cases/bgp1_test_incidents/incident2_hold_timer_expiration/CORE-R1/show_ip_bgp_summary.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ BGP router identifier 1.1.1.1, local AS number 65001
2
+ Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
3
+ 198.51.100.2 4 65010 522 610 112 0 0 00:00:12 Active
cases/bgp1_test_incidents/incident2_hold_timer_expiration/CORE-R1/show_logging_include_bgp.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ Nov 21 12:19:40.900: %BGP-3-NOTIFICATION: sent to neighbor 198.51.100.2 4/0 (hold time expired) 0 bytes
2
+ Nov 21 12:19:40.901: %BGP-5-ADJCHANGE: neighbor 198.51.100.2 Down Hold Timer Expired
3
+ Nov 21 12:19:53.114: %BGP-5-ADJCHANGE: neighbor 198.51.100.2 Up
4
+ Nov 21 12:19:59.227: %BGP-5-ADJCHANGE: neighbor 198.51.100.2 Down Hold Timer Expired
cases/bgp1_test_incidents/incident2_hold_timer_expiration/incident_info.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "description": "BGP resets due to hold timer expiration between CORE-R1 (AS65001) and BRANCH-RTR1 (AS65010)"
3
+ }
cases/bgp1_test_incidents/incident2_hold_timer_expiration/wazuh_alert.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "rule": "BGP hold timer expiration",
3
+ "device": "CORE-R1",
4
+ "severity": "critical",
5
+ "description": "BGP neighbor 198.51.100.2 reset due to hold timer expiration.",
6
+ "timestamp": "2025-11-21T12:20:00Z",
7
+ "neighbor": "198.51.100.2"
8
+ }
cases/bgp1_test_incidents/incident3_neighborship_reset/CORE-R1/show_ip_bgp_neighbors_192.0.2.2.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ BGP neighbor is 192.0.2.2, remote AS 65002, external link
2
+ BGP state = Established, up for 00:10:24
3
+ Route refresh: advertised and received (old & new)
cases/bgp1_test_incidents/incident3_neighborship_reset/CORE-R1/show_ip_bgp_summary.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ BGP router identifier 1.1.1.1, local AS number 65001
2
+ Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
3
+ 192.0.2.2 4 65002 2190 2251 112 0 0 00:10:24 15
cases/bgp1_test_incidents/incident3_neighborship_reset/EDGE-RTR1/show_ip_bgp_neighbors_203.0.113.1.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ BGP neighbor is 203.0.113.1, remote AS 65001, external link
2
+ BGP version 4, remote router ID 1.1.1.1
3
+ BGP state = Established, up for 00:10:22
4
+ Last reset 00:00:58, due to Cease/other configuration change
5
+ Route refresh: advertised and received (old & new)
6
+ Inbound soft reconfiguration allowed
cases/bgp1_test_incidents/incident3_neighborship_reset/EDGE-RTR1/show_ip_bgp_summary.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ BGP router identifier 2.2.2.2, local AS number 65002
2
+ Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
3
+ 203.0.113.1 4 65001 2250 2190 98 0 0 00:10:22 15
cases/bgp1_test_incidents/incident3_neighborship_reset/EDGE-RTR1/show_logging_include_bgp.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ Nov 21 12:28:59.010: %BGP-5-ADJCHANGE: neighbor 203.0.113.1 Down Cease/other configuration change
2
+ Nov 21 12:29:03.552: %BGP-5-ADJCHANGE: neighbor 203.0.113.1 Up
3
+ Nov 21 12:29:05.120: %BGP-5-ROUTECHANGE: neighbor 203.0.113.1 soft reset (inbound)
cases/bgp1_test_incidents/incident3_neighborship_reset/incident_info.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "description": "BGP neighborship reset after policy change; prefer soft clear to refresh routes"
3
+ }
cases/bgp1_test_incidents/incident3_neighborship_reset/wazuh_alert.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "rule": "BGP neighborship reset detected",
3
+ "device": "EDGE-RTR1",
4
+ "severity": "medium",
5
+ "description": "BGP session reset/cleared detected for neighbor 203.0.113.1 (policy change).",
6
+ "timestamp": "2025-11-21T12:30:00Z",
7
+ "neighbor": "203.0.113.1"
8
+ }
cases/bgp2_test_incidents/incident1_prefix_limit_exceeded/CORE-R1/show_ip_bgp_neighbors_192.0.2.2.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ BGP neighbor is 192.0.2.2, remote AS 65002, external link
2
+ BGP version 4, remote router ID 2.2.2.2
3
+ BGP state = Idle (Admin), down for 00:00:18
4
+ Last reset 00:00:18, due to Cease/Maximum Number of Prefixes Reached
5
+ Last notification: Cease/Maximum Number of Prefixes Reached
6
+ Graceful Restart is enabled
7
+ Route refresh: advertised and received (new)
8
+ Keepalive interval is 30 seconds, hold time is 180 seconds
9
+ Maximum prefixes allowed 1000 (threshold 80%), warning-only is disabled
10
+ Prefixes received 1254, accepted 1254
cases/bgp2_test_incidents/incident1_prefix_limit_exceeded/CORE-R1/show_ip_bgp_summary.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ BGP router identifier 1.1.1.1, local AS number 65001
2
+ BGP table version is 812, main routing table version 812
3
+ 2005 network entries using 288720 bytes of memory
4
+ 4100 path entries using 328000 bytes of memory
5
+
6
+ Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
7
+ 192.0.2.2 4 65002 98231 98190 812 0 0 00:00:18 Idle (Admin)
8
+ 198.51.100.2 4 65100 22110 22005 812 0 0 06:22:10 420
cases/bgp2_test_incidents/incident1_prefix_limit_exceeded/CORE-R1/show_logging_include_bgp.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ Nov 22 09:09:41.120: %BGP-3-NOTIFICATION: sent to neighbor 192.0.2.2 6/1 (Maximum Number of Prefixes Reached) 0 bytes
2
+ Nov 22 09:09:41.121: %BGP-5-ADJCHANGE: neighbor 192.0.2.2 Down Cease/Maximum Number of Prefixes Reached
3
+ Nov 22 09:09:42.005: %BGP-5-ADJCHANGE: neighbor 192.0.2.2 Down (Admin)
cases/bgp2_test_incidents/incident1_prefix_limit_exceeded/EDGE-RTR1/show_ip_bgp_summary.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ BGP router identifier 2.2.2.2, local AS number 65002
2
+ Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
3
+ 203.0.113.1 4 65001 98190 98231 511 0 0 00:00:20 980
cases/bgp2_test_incidents/incident1_prefix_limit_exceeded/incident_info.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "description": "Prefix limit exceeded on CORE-R1 from neighbor 192.0.2.2 (transit). Session torn down by max-prefix protection."
3
+ }
cases/bgp2_test_incidents/incident1_prefix_limit_exceeded/wazuh_alert.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "rule": "BGP maximum-prefix limit exceeded",
3
+ "device": "CORE-R1",
4
+ "severity": "critical",
5
+ "description": "Neighbor 192.0.2.2 exceeded maximum-prefix limit; session reset to protect routing table.",
6
+ "timestamp": "2025-11-22T09:10:00Z",
7
+ "neighbor": "192.0.2.2"
8
+ }
cases/bgp2_test_incidents/incident2_route_leak_suspected/BRANCH-RTR1/show_run_bgp_snippet.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ router bgp 65010
2
+ neighbor 198.51.100.1 remote-as 65001
3
+ ! WARNING: missing outbound route-map to restrict advertisements
4
+ ! no prefix-list / route-map applied
cases/bgp2_test_incidents/incident2_route_leak_suspected/CORE-R1/show_ip_bgp_community_no_export.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ CORE-R1#show ip bgp community no-export
2
+ % Network not in table
3
+ % No entries were displayed.
cases/bgp2_test_incidents/incident2_route_leak_suspected/CORE-R1/show_ip_bgp_neighbors_198.51.100.2.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ BGP neighbor is 198.51.100.2, remote AS 65010, external link
2
+ BGP version 4, remote router ID 3.3.3.3
3
+ BGP state = Established, up for 01:12:44
4
+ Route refresh: advertised and received (new)
5
+ Keepalive interval is 30 seconds, hold time is 180 seconds
6
+ Inbound prefix policy: NONE
7
+ Outbound prefix policy: NONE
8
+ Community policy: send-community both
9
+ Prefixes received 18420, accepted 18400
10
+ Neighbor capabilities: route-refresh, 4-byte AS
cases/bgp2_test_incidents/incident2_route_leak_suspected/CORE-R1/show_ip_bgp_summary.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ BGP router identifier 1.1.1.1, local AS number 65001
2
+ Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
3
+ 198.51.100.2 4 65010 55210 55001 812 0 0 01:12:44 18400
4
+ 192.0.2.2 4 65002 98231 98190 812 0 0 06:40:10 980
cases/bgp2_test_incidents/incident2_route_leak_suspected/CORE-R1/show_logging_include_bgp.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ Nov 22 09:20:05.331: %BGP-5-ADJCHANGE: neighbor 198.51.100.2 Up
2
+ Nov 22 09:22:10.110: %BGP-4-MAXPREFIX: No max-prefix configured for neighbor 198.51.100.2; received-prefixes=18420
3
+ Nov 22 09:22:12.771: %BGP-4-ROUTELEAK: Possible route leak detected from 198.51.100.2 (prefix-count anomaly)
cases/bgp2_test_incidents/incident2_route_leak_suspected/incident_info.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "description": "Route leak suspected: customer BRANCH-RTR1 is advertising full internet table to CORE-R1 (missing no-export / wrong policy)."
3
+ }
cases/bgp2_test_incidents/incident2_route_leak_suspected/wazuh_alert.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "rule": "BGP route leak suspected",
3
+ "device": "CORE-R1",
4
+ "severity": "high",
5
+ "description": "Unusual spike of prefixes from neighbor 198.51.100.2; possible customer route leak.",
6
+ "timestamp": "2025-11-22T09:25:00Z",
7
+ "neighbor": "198.51.100.2"
8
+ }
cases/bgp2_test_incidents/incident3_path_selection_incorrect/CORE-R1/show_ip_bgp_203.0.113.0_24.txt ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ BGP routing table entry for 203.0.113.0/24, version 999
2
+ Paths: (2 available, best #1, table default)
3
+ Advertised to update-groups:
4
+ 1
5
+ 65100
6
+ 198.51.100.2 from 198.51.100.2 (4.4.4.4)
7
+ Origin IGP, metric 0, localpref 200, valid, external, best
8
+ Community: 65100:50
9
+ rx pathid: 0, tx pathid: 0
10
+ 65002
11
+ 192.0.2.2 from 192.0.2.2 (2.2.2.2)
12
+ Origin IGP, metric 0, localpref 100, valid, external
13
+ Community: 65002:100
14
+ rx pathid: 0, tx pathid: 0
cases/bgp2_test_incidents/incident3_path_selection_incorrect/CORE-R1/show_ip_bgp_bestpath.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ CORE-R1#show ip bgp bestpath
2
+ BGP Bestpath Algorithm is enabled
3
+ Paths are selected based on:
4
+ Weight, Local Preference, Locally originated, AS path, Origin, MED, eBGP/iBGP, IGP metric, Router ID
cases/bgp2_test_incidents/incident3_path_selection_incorrect/CORE-R1/show_ip_bgp_neighbors_192.0.2.2_policy.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ BGP neighbor is 192.0.2.2, remote AS 65002, external link
2
+ Inbound route-map: NONE
3
+ Outbound route-map: NONE
4
+ Route refresh: advertised and received (new)