4747logger = logging .getLogger (__name__ )
4848
4949
50- @dataclasses .dataclass
50+ @dataclasses .dataclass ( order = True )
5151class VulnerabilitySeverity :
5252 system : ScoringSystem
5353 value : str
5454
5555
56- @dataclasses .dataclass
56+ @dataclasses .dataclass ( order = True )
5757class Reference :
5858
5959 reference_id : str = ""
@@ -64,8 +64,16 @@ def __post_init__(self):
6464 if not any ([self .url , self .reference_id ]):
6565 raise TypeError
6666
67+ def normalized (self ):
68+ severities = sorted (self .severities )
69+ return Reference (
70+ reference_id = self .reference_id ,
71+ url = self .url ,
72+ severities = severities
73+ )
6774
68- @dataclasses .dataclass
75+
76+ @dataclasses .dataclass (order = True )
6977class Advisory :
7078 """
7179 This data class expresses the contract between data sources and the import runner.
@@ -78,19 +86,27 @@ class Advisory:
7886 """
7987
8088 summary : str
81- impacted_package_urls : Iterable [PackageURL ]
89+ vulnerability_id : Optional [str ] = None
90+ impacted_package_urls : Iterable [PackageURL ] = dataclasses .field (default_factory = list )
8291 resolved_package_urls : Iterable [PackageURL ] = dataclasses .field (default_factory = list )
8392 vuln_references : List [Reference ] = dataclasses .field (default_factory = list )
84- vulnerability_id : Optional [str ] = None
8593
86- def __hash__ (self ):
87- s = "{}{}{}{}" .format (
88- self .summary ,
89- '' .join (sorted ([str (p ) for p in self .impacted_package_urls ])),
90- '' .join (sorted ([str (p ) for p in self .resolved_package_urls ])),
91- self .vulnerability_id ,
94+ def normalized (self ):
95+ impacted_package_urls = {package_url for package_url in self .impacted_package_urls }
96+ resolved_package_urls = {package_url for package_url in self .resolved_package_urls }
97+ vuln_references = sorted (
98+ self .vuln_references , key = lambda reference : (reference .reference_id , reference .url )
99+ )
100+ for index , _ in enumerate (self .vuln_references ):
101+ vuln_references [index ] = (vuln_references [index ].normalized ())
102+
103+ return Advisory (
104+ summary = self .summary ,
105+ vulnerability_id = self .vulnerability_id ,
106+ impacted_package_urls = impacted_package_urls ,
107+ resolved_package_urls = resolved_package_urls ,
108+ vuln_references = vuln_references ,
92109 )
93- return hash (s )
94110
95111
96112class InvalidConfigurationError (Exception ):
@@ -205,11 +221,15 @@ def batch_advisories(self, advisories: List[Advisory]) -> Set[Advisory]:
205221 """
206222 Yield batches of the passed in list of advisories.
207223 """
208- advisories = advisories [:] # copy the list as we are mutating it in the loop below
224+
225+ # TODO make this less cryptic and efficient
226+
227+ advisories = advisories [:]
228+ # copy the list as we are mutating it in the loop below
209229
210230 while advisories :
211231 b , advisories = advisories [: self .batch_size ], advisories [self .batch_size :]
212- yield set ( b )
232+ yield b
213233
214234
215235@dataclasses .dataclass
0 commit comments