@@ -767,6 +767,267 @@ def _populate_unsealed_finding_identities(
767767 finding ["fingerprints" ] = fingerprints
768768
769769
770+ def _finding_strength (finding : dict [str , Any ]) -> tuple [int , int , int ]:
771+ return (
772+ ("informational" , "low" , "medium" , "high" , "critical" ).index (finding ["severity" ]["level" ]),
773+ ("low" , "medium" , "high" ).index (finding ["confidence" ]["level" ]),
774+ len (finding .get ("codeEvidence" ) or []),
775+ )
776+
777+
778+ def _recover_unsealed_findings (
779+ manifest : dict [str , Any ],
780+ findings : dict [str , Any ],
781+ schema_dir : Path ,
782+ scan_dir : Path ,
783+ warnings : list [str ],
784+ ) -> list [str ]:
785+ schema = _read_json (schema_dir / "findings.schema.json" )
786+ _require_safe_schema (schema , "findings.schema.json" )
787+ properties = _require_dict (schema , "properties" , "findings.schema" )
788+ finding_array = _require_dict (properties , "findings" , "findings.schema.properties" )
789+ finding_schema = _require_dict (finding_array , "items" , "findings.schema.properties.findings" )
790+ finding_properties = _require_dict (
791+ finding_schema , "properties" , "findings.schema.properties.findings.items"
792+ )
793+ writeup_schema = _require_dict (
794+ finding_properties , "writeup" , "findings.schema.properties.findings.items.properties"
795+ )
796+ scan = _require_dict (manifest , "scan" , "manifest" )
797+ scan_id = _require_str (scan , "id" , "manifest.scan" )
798+ if findings .get ("scanId" ) != scan_id :
799+ raise ContractError ("findings.scanId: must match manifest scan id" )
800+
801+ recovered : list [dict [str , Any ]] = []
802+ discarded : list [str ] = []
803+ finding_positions : dict [str , int ] = {}
804+ writeup_paths : set [str ] = set ()
805+ for index , finding in enumerate (_require_list (findings , "findings" , "findings" )):
806+ context = f"findings.findings[{ index } ]"
807+ try :
808+ if not isinstance (finding , dict ):
809+ raise ContractError (f"{ context } : expected an object" )
810+ identity = _require_dict (finding , "identity" , context )
811+ fields : list [tuple [dict [str , Any ], str , str , str ]] = [
812+ (finding , "ruleId" , context , "rule identifier" ),
813+ (identity , "anchor" , f"{ context } .identity" , "semantic anchor" ),
814+ ]
815+ if "instance" in identity :
816+ fields .append ((identity , "instance" , f"{ context } .identity" , "instance" ))
817+ normalized_fields = []
818+ for parent , field , field_context , label in fields :
819+ value = _require_str (parent , field , field_context )
820+ if SLUG_RE .fullmatch (value ):
821+ continue
822+ normalized = re .sub (r"[^a-z0-9._/-]+" , "-" , value .lower ()).strip ("._/-" )
823+ if not SLUG_RE .fullmatch (normalized ):
824+ raise ContractError (
825+ f"{ field_context } .{ field } : expected a stable lowercase semantic slug"
826+ )
827+ parent [field ] = normalized
828+ normalized_fields .append (label )
829+
830+ _populate_unsealed_finding_identities (
831+ manifest ,
832+ {"scanId" : scan_id , "findings" : [finding ]},
833+ )
834+ finding_id = finding ["findingId" ]
835+ previous_position = finding_positions .get (finding_id )
836+ _validate_finding (finding , context )
837+ if "writeup" in finding :
838+ try :
839+ _validate_schema_node (finding ["writeup" ], writeup_schema , f"{ context } .writeup" )
840+ report_path = finding ["writeup" ]["reportPath" ]
841+ previous_writeup = (
842+ recovered [previous_position ].get ("writeup" )
843+ if previous_position is not None
844+ else None
845+ )
846+ if report_path in writeup_paths and (
847+ previous_writeup is None or previous_writeup ["reportPath" ] != report_path
848+ ):
849+ raise ContractError (f"{ context } .writeup.reportPath: duplicate report path" )
850+ _require_scan_local_file (scan_dir , report_path , f"{ context } .writeup.reportPath" )
851+ except ContractError as exc :
852+ finding .pop ("writeup" )
853+ warnings .append (f"Skipped malformed writeup for finding { index + 1 } : { exc } ." )
854+ _validate_schema_node (finding , finding_schema , context )
855+ except ContractError as exc :
856+ warning = f"Skipped malformed finding { index + 1 } : { exc } ."
857+ warnings .append (warning )
858+ discarded .append (warning )
859+ continue
860+
861+ if previous_position is not None :
862+ previous = recovered [previous_position ]
863+ if _finding_strength (finding ) <= _finding_strength (previous ):
864+ warnings .append (
865+ f"Skipped malformed finding { index + 1 } : duplicate logical finding."
866+ )
867+ continue
868+ previous_writeup = previous .get ("writeup" )
869+ if previous_writeup is not None :
870+ writeup_paths .discard (previous_writeup ["reportPath" ])
871+ recovered [previous_position ] = finding
872+ warnings .append (
873+ f"Recovered finding { index + 1 } : retained stronger duplicate logical finding."
874+ )
875+ else :
876+ finding_positions [finding_id ] = len (recovered )
877+ recovered .append (finding )
878+
879+ if "writeup" in finding :
880+ writeup_paths .add (finding ["writeup" ]["reportPath" ])
881+ if normalized_fields :
882+ warnings .append (
883+ f"Recovered finding { index + 1 } : normalized { ', ' .join (normalized_fields )} ."
884+ )
885+
886+ findings ["findings" ] = recovered
887+ return discarded
888+
889+
890+ def _recover_unsealed_coverage (
891+ coverage : dict [str , Any ],
892+ schema_dir : Path ,
893+ scan_dir : Path ,
894+ warnings : list [str ],
895+ discarded_findings : list [str ],
896+ ) -> None :
897+ schema = _read_json (schema_dir / "coverage.schema.json" )
898+ _require_safe_schema (schema , "coverage.schema.json" )
899+ properties = _require_dict (schema , "properties" , "coverage.schema" )
900+ completeness = coverage .get ("completeness" )
901+ partial = completeness not in ("complete" , "partial" , "unknown" )
902+ if partial :
903+ warnings .append ("Recovered malformed coverage completeness; marked coverage as partial." )
904+
905+ surface_ids : set [str ] = set ()
906+ for field , label in (
907+ ("surfaces" , "coverage surface" ),
908+ ("explicitExclusions" , "coverage exclusion" ),
909+ ("deferred" , "deferred coverage item" ),
910+ ):
911+ array_schema = _require_dict (properties , field , "coverage.schema.properties" )
912+ item_schema = _require_dict (array_schema , "items" , f"coverage.schema.properties.{ field } " )
913+ items = coverage .get (field )
914+ if not isinstance (items , list ):
915+ warnings .append (f"Skipped malformed { label } records: expected an array." )
916+ coverage [field ] = []
917+ partial = True
918+ continue
919+
920+ recovered : list [dict [str , Any ]] = []
921+ for index , item in enumerate (items ):
922+ context = f"coverage.{ field } [{ index } ]"
923+ try :
924+ if not isinstance (item , dict ):
925+ raise ContractError (f"{ context } : expected an object" )
926+ if field == "surfaces" :
927+ surface_id = _require_str (item , "id" , context )
928+ if surface_id in surface_ids :
929+ raise ContractError (f"{ context } .id: duplicate surface id" )
930+ disposition = item .get ("disposition" )
931+ surface_recovered = False
932+ if not isinstance (disposition , str ) or disposition not in DISPOSITIONS :
933+ warnings .append (
934+ f"Recovered coverage surface { index + 1 } : "
935+ "the review disposition could not be verified."
936+ )
937+ item ["disposition" ] = "needs_follow_up"
938+ surface_recovered = True
939+
940+ receipt_refs = item .get ("receiptRefs" )
941+ if not isinstance (receipt_refs , list ):
942+ warnings .append (
943+ f"Skipped malformed receipt references for coverage surface "
944+ f"{ index + 1 } : expected an array."
945+ )
946+ receipt_refs = []
947+ surface_recovered = True
948+
949+ recovered_receipts : list [str ] = []
950+ for ref_index , ref in enumerate (receipt_refs ):
951+ ref_context = f"{ context } .receiptRefs[{ ref_index } ]"
952+ try :
953+ if not isinstance (ref , str ):
954+ raise ContractError (f"{ ref_context } : expected a string" )
955+ normalized_ref = _require_safe_relative_path (ref , ref_context )
956+ if not normalized_ref .startswith ("artifacts/" ):
957+ raise ContractError (
958+ f"{ ref_context } : expected a file under artifacts/"
959+ )
960+ _require_scan_local_file (scan_dir , normalized_ref , ref_context )
961+ except ContractError as exc :
962+ warnings .append (
963+ f"Skipped malformed coverage receipt "
964+ f"{ index + 1 } .{ ref_index + 1 } : { exc } ."
965+ )
966+ surface_recovered = True
967+ continue
968+ recovered_receipts .append (normalized_ref )
969+
970+ item ["receiptRefs" ] = recovered_receipts
971+ if surface_recovered or item ["disposition" ] == "needs_follow_up" :
972+ if not surface_recovered and completeness != "partial" :
973+ warnings .append (
974+ f"Coverage surface { index + 1 } requires follow-up; "
975+ "marked coverage as partial."
976+ )
977+ item ["disposition" ] = "needs_follow_up"
978+ partial = True
979+
980+ _validate_schema_node (item , item_schema , context )
981+ except ContractError as exc :
982+ warnings .append (f"Skipped malformed { label } { index + 1 } : { exc } ." )
983+ partial = True
984+ continue
985+
986+ if field == "surfaces" :
987+ surface_ids .add (surface_id )
988+ recovered .append (item )
989+
990+ coverage [field ] = recovered
991+
992+ if discarded_findings :
993+ for surface in coverage ["surfaces" ]:
994+ surface ["disposition" ] = "needs_follow_up"
995+ coverage ["deferred" ].extend (
996+ {"id" : f"discarded-finding-{ index } " , "reason" : warning }
997+ for index , warning in enumerate (discarded_findings , 1 )
998+ )
999+ partial = True
1000+
1001+ if coverage ["deferred" ] and completeness != "partial" :
1002+ if not discarded_findings :
1003+ warnings .append ("Coverage has deferred review work; marked coverage as partial." )
1004+ partial = True
1005+ if partial :
1006+ coverage ["completeness" ] = "partial"
1007+
1008+
1009+ def _recover_unsealed_hardening (
1010+ manifest : dict [str , Any ],
1011+ scan_dir : Path ,
1012+ warnings : list [str ],
1013+ ) -> None :
1014+ scan = _require_dict (manifest , "scan" , "manifest" )
1015+ if "hardening" not in scan :
1016+ return
1017+
1018+ try :
1019+ hardening = _require_dict (scan , "hardening" , "manifest.scan" )
1020+ portfolio_path = _require_str (hardening , "portfolioPath" , "manifest.scan.hardening" )
1021+ if portfolio_path != "hardening/hardening.md" :
1022+ raise ContractError (
1023+ "manifest.scan.hardening.portfolioPath: expected hardening/hardening.md"
1024+ )
1025+ _require_hardening_portfolio_file (scan_dir , scan )
1026+ except ContractError as exc :
1027+ scan .pop ("hardening" )
1028+ warnings .append (f"Skipped malformed hardening portfolio: { exc } ." )
1029+
1030+
7701031def _validate_derived_finding_identities (
7711032 manifest : dict [str , Any ],
7721033 findings : dict [str , Any ],
@@ -1792,8 +2053,21 @@ def build_sarif_projection(
17922053 source_root_is_directory = False
17932054 if not source_root_is_directory :
17942055 raise ContractError ("source root: expected an existing directory" )
1795- manifest , findings , _ , _ = _read_sealed_scan (scan_dir , schema_dir , "SARIF projection" )
2056+ manifest , findings , coverage , _ = _read_sealed_scan (scan_dir , schema_dir , "SARIF projection" )
17962057 sarif = build_sarif (manifest , findings , source_root )
2058+ if coverage ["completeness" ] != "complete" :
2059+ run = sarif ["runs" ][0 ]
2060+ run ["properties" ]["codexSecurityCoverageCompleteness" ] = coverage ["completeness" ]
2061+ if coverage ["deferred" ]:
2062+ run ["invocations" ] = [
2063+ {
2064+ "executionSuccessful" : True ,
2065+ "toolExecutionNotifications" : [
2066+ {"level" : "warning" , "message" : {"text" : item ["reason" ]}}
2067+ for item in coverage ["deferred" ]
2068+ ],
2069+ }
2070+ ]
17972071 _validate_sarif (sarif )
17982072 return sarif
17992073
@@ -2009,6 +2283,7 @@ def _prepare_scan_finalization(
20092283 * ,
20102284 expected_coverage_mode : str | None = None ,
20112285 completion_binding : dict [str , Any ] | None = None ,
2286+ completion_warnings : list [str ] | None = None ,
20122287) -> PreparedScanFinalization :
20132288 """Read, populate, and validate a scan without writing any output files."""
20142289
@@ -2061,8 +2336,15 @@ def _prepare_scan_finalization(
20612336 _validate_completion_binding (manifest , findings , coverage , completion_binding )
20622337 if was_sealed :
20632338 _validate_findings (manifest , findings )
2064- if was_sealed :
20652339 _validate_derived_finding_identities (manifest , findings )
2340+ elif completion_warnings is not None :
2341+ discarded_findings = _recover_unsealed_findings (
2342+ manifest , findings , schema_dir , scan_dir , completion_warnings
2343+ )
2344+ _recover_unsealed_coverage (
2345+ coverage , schema_dir , scan_dir , completion_warnings , discarded_findings
2346+ )
2347+ _recover_unsealed_hardening (manifest , scan_dir , completion_warnings )
20662348 else :
20672349 _populate_unsealed_finding_identities (manifest , findings )
20682350 _validate_findings (manifest , findings )
0 commit comments