1313import requests
1414from bs4 import BeautifulSoup
1515from packageurl import PackageURL
16- from univers .version_range import VersionRange
16+ from univers .version_constraint import VersionConstraint
17+ from univers .version_range import ApacheVersionRange
1718from univers .versions import SemverVersion
1819
1920from vulnerabilities .importer import AdvisoryData
21+ from vulnerabilities .importer import AffectedPackage
2022from vulnerabilities .importer import Importer
2123from vulnerabilities .importer import Reference
2224from vulnerabilities .importer import VulnerabilitySeverity
23- from vulnerabilities .package_managers import GitHubTagsAPI
2425from vulnerabilities .severity_systems import APACHE_HTTPD
25- from vulnerabilities .utils import nearest_patched_package
2626
2727
2828class ApacheHTTPDImporter (Importer ):
2929
3030 base_url = "https://httpd.apache.org/security/json/"
31+ spdx_license_expression = "Apache-2.0"
32+ license_url = "https://www.apache.org/licenses/LICENSE-2.0"
3133
32- def set_api (self ):
33- self .version_api = GitHubTagsAPI ()
34- asyncio .run (self .version_api .load_api (["apache/httpd" ]))
35- self .version_api .cache ["apache/httpd" ] = set (
36- filter (
37- lambda version : version .value not in ignore_tags ,
38- self .version_api .cache ["apache/httpd" ],
39- )
40- )
41-
42- def updated_advisories (self ):
34+ def advisory_data (self ):
4335 links = fetch_links (self .base_url )
44- self .set_api ()
45- advisories = []
4636 for link in links :
4737 data = requests .get (link ).json ()
48- advisories .append (self .to_advisory (data ))
49- return self .batch_advisories (advisories )
38+ yield self .to_advisory (data )
5039
5140 def to_advisory (self , data ):
52- cve = data ["CVE_data_meta" ]["ID" ]
41+ alias = data ["CVE_data_meta" ]["ID" ]
5342 descriptions = data ["description" ]["description_data" ]
5443 description = None
5544 for desc in descriptions :
@@ -66,12 +55,13 @@ def to_advisory(self, data):
6655 VulnerabilitySeverity (
6756 system = APACHE_HTTPD ,
6857 value = value ,
58+ scoring_elements = "" ,
6959 )
7060 )
7161 break
7262 reference = Reference (
73- reference_id = cve ,
74- url = urllib .parse .urljoin (self .base_url , f"{ cve } .json" ),
63+ reference_id = alias ,
64+ url = urllib .parse .urljoin (self .base_url , f"{ alias } .json" ),
7565 severities = severities ,
7666 )
7767
@@ -81,56 +71,68 @@ def to_advisory(self, data):
8171 for version_data in products ["version" ]["version_data" ]:
8272 versions_data .append (version_data )
8373
84- fixed_version_ranges , affected_version_ranges = self .to_version_ranges (versions_data )
74+ fixed_versions = []
75+ for timeline_object in data .get ("timeline" ) or []:
76+ timeline_value = timeline_object ["value" ]
77+ if "release" in timeline_value :
78+ split_timeline_value = timeline_value .split (" " )
79+ if "never" in timeline_value :
80+ continue
81+ if "release" in split_timeline_value [- 1 ]:
82+ fixed_versions .append (split_timeline_value [0 ])
83+ if "release" in split_timeline_value [0 ]:
84+ fixed_versions .append (split_timeline_value [- 1 ])
8585
8686 affected_packages = []
87- fixed_packages = []
88-
89- for version_range in fixed_version_ranges :
90- fixed_packages .extend (
91- [
92- PackageURL (type = "apache" , name = "httpd" , version = version )
93- for version in self .version_api .get ("apache/httpd" ).valid_versions
94- if SemverVersion (version ) in version_range
95- ]
96- )
97-
98- for version_range in affected_version_ranges :
99- affected_packages .extend (
100- [
101- PackageURL (type = "apache" , name = "httpd" , version = version )
102- for version in self .version_api .get ("apache/httpd" ).valid_versions
103- if SemverVersion (version ) in version_range
104- ]
87+ affected_version_range = self .to_version_ranges (versions_data , fixed_versions )
88+ if affected_version_range :
89+ affected_packages .append (
90+ AffectedPackage (
91+ package = PackageURL (
92+ type = "apache" ,
93+ name = "httpd" ,
94+ ),
95+ affected_version_range = affected_version_range ,
96+ )
10597 )
10698
10799 return AdvisoryData (
108- vulnerability_id = cve ,
100+ aliases = [ alias ] ,
109101 summary = description ,
110- affected_packages = nearest_patched_package ( affected_packages , fixed_packages ) ,
102+ affected_packages = affected_packages ,
111103 references = [reference ],
112104 )
113105
114- def to_version_ranges (self , versions_data ):
115- fixed_version_ranges = []
116- affected_version_ranges = []
106+ def to_version_ranges (self , versions_data , fixed_versions ):
107+ constraints = []
117108 for version_data in versions_data :
118109 version_value = version_data ["version_value" ]
119110 range_expression = version_data ["version_affected" ]
120- if range_expression == "<" :
121- fixed_version_ranges .append (
122- VersionRange .from_scheme_version_spec_string (
123- "semver" , ">={}" .format (version_value )
124- )
125- )
126- elif range_expression == "=" or range_expression == "?=" :
127- affected_version_ranges .append (
128- VersionRange .from_scheme_version_spec_string (
129- "semver" , "{}" .format (version_value )
130- )
111+ if range_expression not in {"<=" , ">=" , "?=" , "!<" , "=" }:
112+ raise ValueError (f"unknown comparator found! { range_expression } " )
113+ comparator_by_range_expression = {
114+ ">=" : ">=" ,
115+ "!<" : ">=" ,
116+ "<=" : "<=" ,
117+ "=" : "=" ,
118+ }
119+ comparator = comparator_by_range_expression .get (range_expression )
120+ if comparator :
121+ constraints .append (
122+ VersionConstraint (comparator = comparator , version = SemverVersion (version_value ))
131123 )
132124
133- return (fixed_version_ranges , affected_version_ranges )
125+ for fixed_version in fixed_versions :
126+ # The VersionConstraint method `invert()` inverts the fixed_version's comparator,
127+ # enabling inclusion of multiple fixed versions with the `affected_version_range` values.
128+ constraints .append (
129+ VersionConstraint (
130+ comparator = "=" ,
131+ version = SemverVersion (fixed_version ),
132+ ).invert ()
133+ )
134+
135+ return ApacheVersionRange (constraints = constraints )
134136
135137
136138def fetch_links (url ):
0 commit comments