@@ -1349,6 +1349,7 @@ async def _persist_finding(
13491349 ) -> Dict [str , Any ]:
13501350 u_id = str (uuid .uuid4 ()).replace ("-" , "" )
13511351 finding_id = f"finding:{ task_id } :{ u_id [:8 ]} "
1352+ finding_group_id = finding .get ("finding_group_id" )
13521353
13531354 _validate_risk_fields (finding )
13541355 exploitability = finding .get ("exploitability" )
@@ -1395,6 +1396,41 @@ async def _persist_finding(
13951396 asset_exposure, risk_score, risk_factors_json
13961397 ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
13971398 ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1399+ ON CONFLICT (owner_id, finding_group_id) DO UPDATE SET
1400+ task_id = EXCLUDED.task_id,
1401+ plugin_id = EXCLUDED.plugin_id,
1402+ title = EXCLUDED.title,
1403+ category = EXCLUDED.category,
1404+ severity = EXCLUDED.severity,
1405+ target = EXCLUDED.target,
1406+ description = EXCLUDED.description,
1407+ remediation = EXCLUDED.remediation,
1408+ proof = EXCLUDED.proof,
1409+ cvss = EXCLUDED.cvss,
1410+ cve = EXCLUDED.cve,
1411+ metadata_json = EXCLUDED.metadata_json,
1412+ discovered_at = EXCLUDED.discovered_at,
1413+ exploitability = EXCLUDED.exploitability,
1414+ confidence = EXCLUDED.confidence,
1415+ validated = EXCLUDED.validated,
1416+ validation_method = EXCLUDED.validation_method,
1417+ confidence_reason = EXCLUDED.confidence_reason,
1418+ finding_kind = EXCLUDED.finding_kind,
1419+ asset_id = EXCLUDED.asset_id,
1420+ last_seen_at = EXCLUDED.last_seen_at,
1421+ occurrence_count = COALESCE(findings.occurrence_count, 0) + EXCLUDED.occurrence_count,
1422+ corroborating_sources_json = EXCLUDED.corroborating_sources_json,
1423+ evidence_count = EXCLUDED.evidence_count,
1424+ analyst_status = EXCLUDED.analyst_status,
1425+ retest_status = EXCLUDED.retest_status,
1426+ evidence_json = EXCLUDED.evidence_json,
1427+ asset_refs_json = EXCLUDED.asset_refs_json,
1428+ service_fingerprint = EXCLUDED.service_fingerprint,
1429+ cpe = EXCLUDED.cpe,
1430+ references_json = EXCLUDED.references_json,
1431+ asset_exposure = EXCLUDED.asset_exposure,
1432+ risk_score = EXCLUDED.risk_score,
1433+ risk_factors_json = EXCLUDED.risk_factors_json
13981434 """ ,
13991435 (
14001436 finding_id ,
@@ -1418,7 +1454,7 @@ async def _persist_finding(
14181454 finding .get ("validation_method" ),
14191455 finding .get ("confidence_reason" ),
14201456 str (finding .get ("finding_kind" ) or "observation" ),
1421- finding . get ( " finding_group_id" ) ,
1457+ finding_group_id ,
14221458 finding .get ("asset_id" ),
14231459 first_seen_at ,
14241460 last_seen_at ,
@@ -1437,6 +1473,13 @@ async def _persist_finding(
14371473 json .dumps (risk_factors ),
14381474 ),
14391475 )
1476+
1477+ row = await db .fetchone (
1478+ "SELECT id, occurrence_count FROM findings WHERE owner_id = ? AND finding_group_id = ?" ,
1479+ (owner_id , finding_group_id ),
1480+ )
1481+ finding_id = row ["id" ] if row else finding_id
1482+ occurrence_count = int (row ["occurrence_count" ]) if row else occurrence_count
14401483 return {
14411484 ** finding ,
14421485 "id" : finding_id ,
@@ -1468,25 +1511,25 @@ async def _upsert_findings_and_report(self, db, task_id: str, owner_id: str, plu
14681511 result = parsed ,
14691512 )
14701513 findings_data : List [Dict [str , Any ]] = []
1471- for finding in structured_result .get ("findings" , []):
1472- findings_data .append (
1473- await self ._persist_finding (
1474- db ,
1475- owner_id = owner_id ,
1476- task_id = task_id ,
1477- plugin_id = plugin_id ,
1478- target = target ,
1479- finding = finding ,
1514+ async with db .transaction ():
1515+ for finding in structured_result .get ("findings" , []):
1516+ findings_data .append (
1517+ await self ._persist_finding (
1518+ db ,
1519+ owner_id = owner_id ,
1520+ task_id = task_id ,
1521+ plugin_id = plugin_id ,
1522+ target = target ,
1523+ finding = finding ,
1524+ )
14801525 )
1481- )
14821526
1483- structured_result ["findings" ] = findings_data
1484- structured_result ["severity_counts" ] = self ._build_severity_counts (findings_data )
1485- structured_result ["finding_groups" ] = build_finding_groups (findings_data )
1486- structured_result ["asset_summary" ] = build_asset_summary (findings_data , asset_services )
1487- structured_result ["scan_diff" ] = build_scan_diff (findings_data , previous_findings )
1527+ structured_result ["findings" ] = findings_data
1528+ structured_result ["severity_counts" ] = self ._build_severity_counts (findings_data )
1529+ structured_result ["finding_groups" ] = build_finding_groups (findings_data )
1530+ structured_result ["asset_summary" ] = build_asset_summary (findings_data , asset_services )
1531+ structured_result ["scan_diff" ] = build_scan_diff (findings_data , previous_findings )
14881532
1489- async with db .transaction ():
14901533 await db .execute (
14911534 "UPDATE tasks SET structured_json = ? WHERE id = ?" ,
14921535 (json .dumps (structured_result ), task_id )
@@ -1534,25 +1577,25 @@ async def _upsert_findings_and_report_from_scanner(self, db, task_id: str, owner
15341577 result = result ,
15351578 )
15361579 findings_data : List [Dict [str , Any ]] = []
1537- for finding in structured_result .get ("findings" , []):
1538- findings_data .append (
1539- await self ._persist_finding (
1540- db ,
1541- owner_id = owner_id ,
1542- task_id = task_id ,
1543- plugin_id = plugin_id ,
1544- target = target ,
1545- finding = finding ,
1580+ async with db .transaction ():
1581+ for finding in structured_result .get ("findings" , []):
1582+ findings_data .append (
1583+ await self ._persist_finding (
1584+ db ,
1585+ owner_id = owner_id ,
1586+ task_id = task_id ,
1587+ plugin_id = plugin_id ,
1588+ target = target ,
1589+ finding = finding ,
1590+ )
15461591 )
1547- )
15481592
1549- structured_result ["findings" ] = findings_data
1550- structured_result ["severity_counts" ] = self ._build_severity_counts (findings_data )
1551- structured_result ["finding_groups" ] = build_finding_groups (findings_data )
1552- structured_result ["asset_summary" ] = build_asset_summary (findings_data , asset_services )
1553- structured_result ["scan_diff" ] = build_scan_diff (findings_data , previous_findings )
1593+ structured_result ["findings" ] = findings_data
1594+ structured_result ["severity_counts" ] = self ._build_severity_counts (findings_data )
1595+ structured_result ["finding_groups" ] = build_finding_groups (findings_data )
1596+ structured_result ["asset_summary" ] = build_asset_summary (findings_data , asset_services )
1597+ structured_result ["scan_diff" ] = build_scan_diff (findings_data , previous_findings )
15541598
1555- async with db .transaction ():
15561599 await db .execute (
15571600 "UPDATE tasks SET structured_json = ? WHERE id = ?" ,
15581601 (json .dumps (structured_result ), task_id )
0 commit comments