(manifest: dict[str, Any], findings: dict[str, Any])
| 522 | |
| 523 | |
| 524 | def _enrich_findings(manifest: dict[str, Any], findings: dict[str, Any]) -> None: |
| 525 | scan = _require_dict(manifest, "scan", "manifest") |
| 526 | scan_id = _require_str(scan, "id", "manifest.scan") |
| 527 | target_id = _require_str( |
| 528 | _require_dict(scan, "target", "manifest.scan"), "targetId", "scan.target" |
| 529 | ) |
| 530 | if findings.get("scanId") != scan_id: |
| 531 | raise ContractError("findings.scanId: must match manifest scan id") |
| 532 | |
| 533 | finding_ids: set[str] = set() |
| 534 | occurrence_ids: set[str] = set() |
| 535 | for index, finding in enumerate(_require_list(findings, "findings", "findings")): |
| 536 | context = f"findings.findings[{index}]" |
| 537 | if not isinstance(finding, dict): |
| 538 | raise ContractError(f"{context}: expected an object") |
| 539 | fingerprint = _fingerprint(target_id, finding) |
| 540 | expected_finding_id = _stable_id("csf", fingerprint) |
| 541 | expected_occurrence_id = _stable_id("occ", scan_id, fingerprint) |
| 542 | existing_finding_id = finding.get("findingId") |
| 543 | existing_occurrence_id = finding.get("occurrenceId") |
| 544 | if existing_finding_id not in {None, expected_finding_id}: |
| 545 | raise ContractError(f"{context}.findingId: does not match derived fingerprint identity") |
| 546 | if existing_occurrence_id not in {None, expected_occurrence_id}: |
| 547 | raise ContractError(f"{context}.occurrenceId: does not match scan occurrence identity") |
| 548 | existing_fingerprints = finding.get("fingerprints") |
| 549 | expected_fingerprints = {"algorithm": FINGERPRINT_ALGORITHM, "primary": fingerprint} |
| 550 | if existing_fingerprints is not None and existing_fingerprints != expected_fingerprints: |
| 551 | raise ContractError(f"{context}.fingerprints: does not match derived fingerprint") |
| 552 | finding["findingId"] = expected_finding_id |
| 553 | finding["occurrenceId"] = expected_occurrence_id |
| 554 | finding["fingerprints"] = expected_fingerprints |
| 555 | finding_ids.add(expected_finding_id) |
| 556 | if expected_occurrence_id in occurrence_ids: |
| 557 | raise ContractError( |
| 558 | f"{context}: duplicate occurrence identity; use identity.instance to split siblings" |
| 559 | ) |
| 560 | occurrence_ids.add(expected_occurrence_id) |
| 561 | |
| 562 | if len(finding_ids) != len(occurrence_ids): |
| 563 | raise ContractError("findings: duplicate logical findings in one scan") |
| 564 | |
| 565 | |
| 566 | def _validate_finding(finding: dict[str, Any], context: str) -> None: |
no test coverage detected