3535from typing import Set
3636from typing import Tuple
3737
38+ import pytz
3839from binaryornot .helpers import is_binary_string
40+ from dateutil import parser as dateparser
3941from git import DiffIndex
4042from git import Repo
4143from license_expression import Licensing
4244from packageurl import PackageURL
45+ from univers .version_range import RANGE_CLASS_BY_SCHEMES
4346from univers .version_range import VersionRange
4447from univers .versions import Version
4548
@@ -521,13 +524,13 @@ class OvalImporter(Importer):
521524 """
522525
523526 @staticmethod
524- def create_purl (pkg_name : str , pkg_version : str , pkg_data : Mapping ) -> PackageURL :
527+ def create_purl (pkg_name : str , pkg_data : Mapping ) -> PackageURL :
525528 """
526529 Helper method for creating different purls for subclasses without them reimplementing
527530 get_data_from_xml_doc method
528531 Note: pkg_data must include 'type' of package
529532 """
530- return PackageURL (name = pkg_name , version = pkg_version , ** pkg_data )
533+ return PackageURL (name = pkg_name , ** pkg_data )
531534
532535 @staticmethod
533536 def _collect_pkgs (parsed_oval_data : Mapping ) -> Set :
@@ -561,28 +564,17 @@ def advisory_data(self) -> List[AdvisoryData]:
561564 for metadata , oval_file in self ._fetch ():
562565 try :
563566 oval_data = self .get_data_from_xml_doc (oval_file , metadata )
564- yield oval_data
567+ yield from oval_data
565568 except Exception :
566569 logger .error (
567570 f"Failed to get updated_advisories: { oval_file !r} "
568571 f"with { metadata !r} :\n " + traceback .format_exc ()
569572 )
570573 continue
571574
572- def set_api (self , all_pkgs : Iterable [str ]):
573- """
574- This method loads the self.pkg_manager_api with the specified packages.
575- It fetches and caches all the versions of these packages and exposes
576- them through self.pkg_manager_api.get(<package_name>). Example
577-
578- >> self.set_api(['electron'])
579- Assume 'electron' has only versions 1.0.0 and 1.2.0
580- >> assert self.pkg_manager_api.get('electron') == {'1.0.0','1.2.0'}
581-
582- """
583- raise NotImplementedError
584-
585- def get_data_from_xml_doc (self , xml_doc : ET .ElementTree , pkg_metadata = {}) -> List [AdvisoryData ]:
575+ def get_data_from_xml_doc (
576+ self , xml_doc : ET .ElementTree , pkg_metadata = {}
577+ ) -> Iterable [AdvisoryData ]:
586578 """
587579 The orchestration method of the OvalDataSource. This method breaks an
588580 OVAL xml ElementTree into a list of `Advisory`.
@@ -593,12 +585,10 @@ def get_data_from_xml_doc(self, xml_doc: ET.ElementTree, pkg_metadata={}) -> Lis
593585 Example value of pkg_metadata:
594586 {"type":"deb","qualifiers":{"distro":"buster"} }
595587 """
596-
597- all_adv = []
598- oval_doc = OvalParser (self .translations , xml_doc )
599- raw_data = oval_doc .get_data ()
600- all_pkgs = self ._collect_pkgs (raw_data )
601- self .set_api (all_pkgs )
588+ oval_parsed_data = OvalParser (self .translations , xml_doc )
589+ raw_data = oval_parsed_data .get_data ()
590+ oval_doc = oval_parsed_data .oval_document
591+ timestamp = oval_doc .getGenerator ().getTimestamp ()
602592
603593 # convert definition_data to Advisory objects
604594 for definition_data in raw_data :
@@ -610,49 +600,31 @@ def get_data_from_xml_doc(self, xml_doc: ET.ElementTree, pkg_metadata={}) -> Lis
610600 affected_packages = []
611601 for test_data in definition_data ["test_data" ]:
612602 for package_name in test_data ["package_list" ]:
613- if package_name and len (package_name ) >= 50 :
614- continue
615-
616- affected_version_range = test_data ["version_ranges" ] or set ()
617- version_class = version_class_by_package_type [pkg_metadata ["type" ]]
618- version_scheme = version_class .scheme
619-
620- affected_version_range = VersionRange .from_scheme_version_spec_string (
621- version_scheme , affected_version_range
622- )
623- all_versions = self .pkg_manager_api .get (package_name ).valid_versions
624-
625- # FIXME: what is this 50 DB limit? that's too small for versions
626- # FIXME: we should not drop data this way
627- # This filter is for filtering out long versions.
628- # 50 is limit because that's what db permits atm.
629- all_versions = [version for version in all_versions if len (version ) < 50 ]
630- if not all_versions :
631- continue
632-
633- affected_purls = []
634- safe_purls = []
635- for version in all_versions :
636- purl = self .create_purl (
637- pkg_name = package_name ,
638- pkg_version = version ,
639- pkg_data = pkg_metadata ,
603+ affected_version_range = test_data ["version_ranges" ]
604+ vrc = RANGE_CLASS_BY_SCHEMES [pkg_metadata ["type" ]]
605+ if affected_version_range :
606+ try :
607+ affected_version_range = vrc .from_native (affected_version_range )
608+ except Exception as e :
609+ logger .error (
610+ f"Failed to parse version range { affected_version_range !r} "
611+ f"for package { package_name !r} :\n { e } "
612+ )
613+ continue
614+ if package_name :
615+ affected_packages .append (
616+ AffectedPackage (
617+ package = self .create_purl (package_name , pkg_metadata ),
618+ affected_version_range = affected_version_range ,
619+ )
640620 )
641- if version_class (version ) in affected_version_range :
642- affected_purls .append (purl )
643- else :
644- safe_purls .append (purl )
645-
646- affected_packages .extend (
647- nearest_patched_package (affected_purls , safe_purls ),
648- )
649-
650- all_adv .append (
651- AdvisoryData (
652- summary = description ,
653- affected_packages = affected_packages ,
654- vulnerability_id = vuln_id ,
655- references = references ,
656- )
621+ date_published = dateparser .parse (timestamp )
622+ if not date_published .tzinfo :
623+ date_published = date_published .replace (tzinfo = pytz .UTC )
624+ yield AdvisoryData (
625+ aliases = [vuln_id ],
626+ summary = description ,
627+ affected_packages = affected_packages ,
628+ references = sorted (references ),
629+ date_published = date_published ,
657630 )
658- return all_adv
0 commit comments