11from __future__ import annotations
22
33import itertools
4- from abc import abstractmethod
54from dataclasses import dataclass , field
65from pathlib import Path
76from typing import TYPE_CHECKING , Any , ClassVar , Sequence , Type
87
98import libcst as cst
109from boltons .setutils import IndexedSet
1110from libcst ._position import CodeRange
11+ from sarif_pydantic import Location as LocationModel
12+ from sarif_pydantic import Result as ResultModel
13+ from sarif_pydantic import Run
1214from typing_extensions import Self
1315
1416from codemodder .codetf import Finding , Rule
@@ -36,23 +38,30 @@ class Location(ABCDataclass):
3638@dataclass (frozen = True )
3739class SarifLocation (Location ):
3840 @staticmethod
39- @abstractmethod
40- def get_snippet (sarif_location ) -> str :
41- pass
41+ def get_snippet (sarif_location : LocationModel ) -> str | None :
42+ return sarif_location .message .text if sarif_location .message else None
4243
4344 @classmethod
44- def from_sarif (cls , sarif_location ) -> Self :
45- artifact_location = sarif_location ["physicalLocation" ]["artifactLocation" ]
46- file = Path (artifact_location ["uri" ])
45+ def from_sarif (cls , sarif_location : LocationModel ) -> Self :
46+ if not (physical_location := sarif_location .physical_location ):
47+ raise ValueError ("Sarif location does not have a physical location" )
48+ if not (artifact_location := physical_location .artifact_location ):
49+ raise ValueError ("Sarif location does not have an artifact location" )
50+ if not (region := physical_location .region ):
51+ raise ValueError ("Sarif location does not have a region" )
52+ if not (uri := artifact_location .uri ):
53+ raise ValueError ("Sarif location does not have a uri" )
54+
55+ file = Path (uri )
4756 snippet = cls .get_snippet (sarif_location )
4857 start = LineInfo (
49- line = sarif_location [ "physicalLocation" ][ " region" ][ "startLine" ] ,
50- column = sarif_location [ "physicalLocation" ][ " region" ][ "startColumn" ] ,
58+ line = region . start_line or - 1 ,
59+ column = region . start_column or - 1 ,
5160 snippet = snippet ,
5261 )
5362 end = LineInfo (
54- line = sarif_location [ "physicalLocation" ][ " region" ][ "endLine" ] ,
55- column = sarif_location [ "physicalLocation" ][ " region" ][ "endColumn" ] ,
63+ line = region . end_line or - 1 ,
64+ column = region . end_column or - 1 ,
5665 snippet = snippet ,
5766 )
5867 return cls (file = file , start = start , end = end )
@@ -102,7 +111,7 @@ class SarifResult(SASTResult):
102111
103112 @classmethod
104113 def from_sarif (
105- cls , sarif_result , sarif_run , truncate_rule_id : bool = False
114+ cls , sarif_result : ResultModel , sarif_run : Run , truncate_rule_id : bool = False
106115 ) -> Self :
107116 rule_id = cls .extract_rule_id (sarif_result , sarif_run , truncate_rule_id )
108117 finding_id = cls .extract_finding_id (sarif_result ) or rule_id
@@ -124,68 +133,84 @@ def from_sarif(
124133 )
125134
126135 @classmethod
127- def extract_finding_message (cls , sarif_result : dict , sarif_run : dict ) -> str | None :
128- return sarif_result .get ("message" , {}).get ("text" , None )
136+ def extract_finding_message (
137+ cls , sarif_result : ResultModel , sarif_run : Run
138+ ) -> str | None :
139+ del sarif_run
140+ return sarif_result .message .text
129141
130142 @classmethod
131- def rule_url_from_id (cls , result : dict , run : dict , rule_id : str ) -> str | None :
143+ def rule_url_from_id (
144+ cls , result : ResultModel , run : Run , rule_id : str
145+ ) -> str | None :
132146 del result , run , rule_id
133147 return None
134148
135149 @classmethod
136- def extract_locations (cls , sarif_result ) -> Sequence [Location ]:
150+ def extract_locations (cls , sarif_result : ResultModel ) -> Sequence [Location ]:
137151 return tuple (
138152 [
139153 cls .location_type .from_sarif (location )
140- for location in sarif_result [ " locations" ]
154+ for location in sarif_result . locations or [ ]
141155 ]
142156 )
143157
144158 @classmethod
145- def extract_related_locations (cls , sarif_result ) -> Sequence [LocationWithMessage ]:
159+ def extract_related_locations (
160+ cls , sarif_result : ResultModel
161+ ) -> Sequence [LocationWithMessage ]:
146162 return tuple (
147163 [
148164 LocationWithMessage (
149- message = rel_location .get ( " message" , {}). get ( " text" , "" ) ,
165+ message = rel_location .message . text ,
150166 location = cls .location_type .from_sarif (rel_location ),
151167 )
152- for rel_location in sarif_result .get ("relatedLocations" , [])
168+ for rel_location in sarif_result .related_locations or []
169+ if rel_location .message
153170 ]
154171 )
155172
156173 @classmethod
157- def extract_code_flows (cls , sarif_result ) -> Sequence [Sequence [Location ]]:
174+ def extract_code_flows (
175+ cls , sarif_result : ResultModel
176+ ) -> Sequence [Sequence [Location ]]:
158177 return tuple (
159178 [
160179 tuple (
161180 [
162- cls .location_type .from_sarif (locations .get ("location" ))
163- for locations in threadflow .get ("locations" , {})
181+ cls .location_type .from_sarif (locations .location )
182+ for locations in threadflow .locations or []
183+ if locations .location
164184 ]
165185 )
166- for codeflow in sarif_result .get ( "codeFlows" , {})
167- for threadflow in codeflow .get ( "threadFlows" , {})
186+ for codeflow in sarif_result .code_flows or []
187+ for threadflow in codeflow .thread_flows or []
168188 ]
169189 )
170190
171191 @classmethod
172- def extract_rule_id (cls , result , sarif_run , truncate_rule_id : bool = False ) -> str :
173- if rule_id := result .get ("ruleId" ):
192+ def extract_rule_id (
193+ cls , result : ResultModel , sarif_run : Run , truncate_rule_id : bool = False
194+ ) -> str :
195+ if rule_id := result .rule_id :
174196 return rule_id .split ("." )[- 1 ] if truncate_rule_id else rule_id
175197
176198 # it may be contained in the 'rule' field through the tool component in the sarif file
177- if "rule" in result :
178- tool_index = result ["rule" ]["toolComponent" ]["index" ]
179- rule_index = result ["rule" ]["index" ]
180- return sarif_run ["tool" ]["extensions" ][tool_index ]["rules" ][rule_index ][
181- "id"
182- ]
199+ if (
200+ (rule := result .rule )
201+ and sarif_run .tool .extensions
202+ and rule .tool_component
203+ and rule .tool_component .index is not None
204+ ):
205+ tool_index = rule .tool_component .index
206+ rule_index = rule .index
207+ return sarif_run .tool .extensions [tool_index ].rules [rule_index ].id
183208
184209 raise ValueError ("Could not extract rule id from sarif result." )
185210
186211 @classmethod
187- def extract_finding_id (cls , result ) -> str | None :
188- return result .get ( " guid" ) or result .get ( "correlationGuid" )
212+ def extract_finding_id (cls , result : ResultModel ) -> str | None :
213+ return str ( result .guid or "" ) or str ( result .correlation_guid or "" ) or None
189214
190215
191216def same_line (pos : CodeRange , location : Location ) -> bool :
0 commit comments