Skip to content

Commit b857809

Browse files
fix(flowtriq): address review feedback
- Remove unused `confidence` variable (flake8 F841) - Break long line in source IP extraction for Black compliance - Persist `last_incident_time` in connector state so deduplication works across runs - Add `severity` parameter to `get_incidents()` so callers can request a specific severity value - Iterate all configured severities in `get_all_incidents()` instead of silently dropping all but the first
1 parent f5c6516 commit b857809

3 files changed

Lines changed: 54 additions & 17 deletions

File tree

external-import/flowtriq/src/external_import_connector/client_api.py

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ def __init__(self, helper: "OpenCTIConnectorHelper", config: "ConnectorSettings"
2727
}
2828
)
2929

30-
def get_incidents(self, limit: int = 50, offset: int = 0) -> dict | None:
30+
def get_incidents(
31+
self, limit: int = 50, offset: int = 0, severity: str | None = None
32+
) -> dict | None:
3133
"""
3234
Fetch incidents from the Flowtriq API.
3335
@@ -43,10 +45,9 @@ def get_incidents(self, limit: int = 50, offset: int = 0) -> dict | None:
4345
if self.config.flowtriq.incident_status:
4446
params["status"] = self.config.flowtriq.incident_status
4547

46-
if self.config.flowtriq.incident_severity:
47-
# API accepts a single severity filter; if multiple are configured,
48-
# we make separate calls per severity in the connector layer.
49-
# Here we pass the first one if set.
48+
if severity:
49+
params["severity"] = severity
50+
elif self.config.flowtriq.incident_severity:
5051
params["severity"] = self.config.flowtriq.incident_severity[0]
5152

5253
try:
@@ -86,29 +87,52 @@ def get_incident_detail(self, incident_uuid: str) -> dict | None:
8687
)
8788
return None
8889

89-
def get_all_incidents(self, max_total: int = 100) -> list[dict]:
90-
"""
91-
Paginate through incidents up to max_total.
92-
Returns a flat list of incident dicts.
93-
"""
94-
all_incidents: list[dict] = []
90+
def _paginate_incidents(
91+
self, max_total: int, severity: str | None = None
92+
) -> list[dict]:
93+
"""Paginate through incidents for a single severity filter."""
94+
results: list[dict] = []
9595
offset = 0
9696
page_size = min(max_total, 100)
9797

9898
while offset < max_total:
99-
data = self.get_incidents(limit=page_size, offset=offset)
99+
data = self.get_incidents(
100+
limit=page_size, offset=offset, severity=severity
101+
)
100102
if not data or "incidents" not in data:
101103
break
102104

103105
incidents = data["incidents"]
104106
if not incidents:
105107
break
106108

107-
all_incidents.extend(incidents)
109+
results.extend(incidents)
108110
offset += len(incidents)
109111

110-
# Stop if we got fewer than requested (last page)
111112
if len(incidents) < page_size:
112113
break
113114

114-
return all_incidents[:max_total]
115+
return results[:max_total]
116+
117+
def get_all_incidents(self, max_total: int = 100) -> list[dict]:
118+
"""
119+
Paginate through incidents up to max_total.
120+
If multiple severities are configured, fetches each separately
121+
and merges the results.
122+
Returns a flat list of incident dicts.
123+
"""
124+
severities = self.config.flowtriq.incident_severity
125+
if severities and len(severities) > 1:
126+
all_incidents: list[dict] = []
127+
seen_uuids: set[str] = set()
128+
per_severity_limit = max(max_total // len(severities), 10)
129+
for sev in severities:
130+
incidents = self._paginate_incidents(per_severity_limit, sev)
131+
for inc in incidents:
132+
uid = inc.get("uuid", "")
133+
if uid and uid not in seen_uuids:
134+
seen_uuids.add(uid)
135+
all_incidents.append(inc)
136+
return all_incidents[:max_total]
137+
138+
return self._paginate_incidents(max_total)

external-import/flowtriq/src/external_import_connector/connector.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ def _collect_intelligence(self) -> list:
100100
processed = 0
101101
skipped_severity = 0
102102
skipped_seen = 0
103+
newest_incident_time = last_incident_time
103104

104105
for incident in incidents:
105106
severity = incident.get("severity", "medium")
@@ -135,6 +136,17 @@ def _collect_intelligence(self) -> list:
135136
stix_objects.extend(objects)
136137
processed += 1
137138

139+
# Track the newest incident timestamp for dedup state
140+
if started_at:
141+
if not newest_incident_time or started_at > newest_incident_time:
142+
newest_incident_time = started_at
143+
144+
# Persist the newest incident timestamp for next run deduplication
145+
if newest_incident_time and newest_incident_time != last_incident_time:
146+
state = self.helper.get_state() or {}
147+
state["last_incident_time"] = newest_incident_time
148+
self.helper.set_state(state)
149+
138150
self.helper.connector_logger.info(
139151
"[CONNECTOR] Incident processing summary",
140152
{

external-import/flowtriq/src/external_import_connector/converter_to_stix.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ def create_incident_observable(
171171
peak_bps = incident.get("peak_bps", 0)
172172
started_at = incident.get("started_at")
173173
ended_at = incident.get("ended_at")
174-
confidence = incident.get("confidence")
175174
incident_uuid = incident.get("uuid", "")
176175

177176
if not target_ip:
@@ -295,7 +294,9 @@ def create_incident_observable(
295294
top_sources = incident.get("sp_top_sources")
296295
if top_sources and isinstance(top_sources, list):
297296
for source in top_sources:
298-
source_ip = source.get("ip") if isinstance(source, dict) else str(source)
297+
source_ip = (
298+
source.get("ip") if isinstance(source, dict) else str(source)
299+
)
299300
if not source_ip:
300301
continue
301302

0 commit comments

Comments
 (0)