Skip to content

Commit 39b53ae

Browse files
committed
Add improver to get valid versions for stored ranges
Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent d0414eb commit 39b53ae

65 files changed

Lines changed: 4555 additions & 3015 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

vulnerabilities/importer.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ class AdvisoryData:
247247
references: List[Reference] = dataclasses.field(default_factory=list)
248248
date_published: Optional[datetime.datetime] = None
249249
weaknesses: List[int] = dataclasses.field(default_factory=list)
250+
created_by: Optional[str] = None
250251

251252
def __post_init__(self):
252253
if self.date_published and not self.date_published.tzinfo:
@@ -260,22 +261,27 @@ def to_dict(self):
260261
"references": [ref.to_dict() for ref in self.references],
261262
"date_published": self.date_published.isoformat() if self.date_published else None,
262263
"weaknesses": self.weaknesses,
264+
"created_by": self.created_by or "",
263265
}
264266

265267
@classmethod
266268
def from_dict(cls, advisory_data):
267269
date_published = advisory_data["date_published"]
268270
transformed = {
269-
"aliases": advisory_data["aliases"],
270-
"summary": advisory_data["summary"],
271+
"aliases": advisory_data.get("aliases") or [],
272+
"summary": advisory_data.get("summary") or "",
271273
"affected_packages": [
272-
AffectedPackage.from_dict(pkg) for pkg in advisory_data["affected_packages"]
274+
AffectedPackage.from_dict(pkg)
275+
for pkg in advisory_data.get("affected_packages") or []
276+
],
277+
"references": [
278+
Reference.from_dict(ref) for ref in advisory_data.get("references") or []
273279
],
274-
"references": [Reference.from_dict(ref) for ref in advisory_data["references"]],
275280
"date_published": datetime.datetime.fromisoformat(date_published)
276281
if date_published
277282
else None,
278-
"weaknesses": advisory_data["weaknesses"],
283+
"weaknesses": advisory_data.get("weaknesses") or [],
284+
"created_by": advisory_data.get("created_by") or "",
279285
}
280286
return cls(**transformed)
281287

vulnerabilities/importers/apache_httpd.py

Lines changed: 0 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,9 @@
99

1010
import logging
1111
import urllib
12-
from datetime import datetime
13-
from typing import Iterable
14-
from typing import List
15-
from typing import Mapping
16-
from typing import Optional
1712

1813
import requests
1914
from bs4 import BeautifulSoup
20-
from django.db.models.query import QuerySet
2115
from packageurl import PackageURL
2216
from univers.version_constraint import VersionConstraint
2317
from univers.version_range import ApacheVersionRange
@@ -27,18 +21,8 @@
2721
from vulnerabilities.importer import AffectedPackage
2822
from vulnerabilities.importer import Importer
2923
from vulnerabilities.importer import Reference
30-
from vulnerabilities.importer import UnMergeablePackageError
3124
from vulnerabilities.importer import VulnerabilitySeverity
32-
from vulnerabilities.improver import Improver
33-
from vulnerabilities.improver import Inference
34-
from vulnerabilities.models import Advisory
35-
from vulnerabilities.package_managers import GitHubTagsAPI
36-
from vulnerabilities.package_managers import VersionAPI
3725
from vulnerabilities.severity_systems import APACHE_HTTPD
38-
from vulnerabilities.utils import AffectedPackage as LegacyAffectedPackage
39-
from vulnerabilities.utils import get_affected_packages_by_patched_package
40-
from vulnerabilities.utils import nearest_patched_package
41-
from vulnerabilities.utils import resolve_version_range
4226

4327
logger = logging.getLogger(__name__)
4428

@@ -227,80 +211,3 @@ def fetch_links(url):
227211
"post_ajp_proxy",
228212
"pre_ajp_proxy",
229213
}
230-
231-
232-
class ApacheHTTPDImprover(Improver):
233-
def __init__(self) -> None:
234-
self.versions_fetcher_by_purl: Mapping[str, VersionAPI] = {}
235-
self.vesions_by_purl = {}
236-
237-
@property
238-
def interesting_advisories(self) -> QuerySet:
239-
return Advisory.objects.filter(created_by=ApacheHTTPDImporter.qualified_name)
240-
241-
def get_package_versions(
242-
self, package_url: PackageURL, until: Optional[datetime] = None
243-
) -> List[str]:
244-
"""
245-
Return a list of `valid_versions` for the `package_url`
246-
"""
247-
api_name = "apache/httpd"
248-
versions_fetcher = GitHubTagsAPI()
249-
return versions_fetcher.get_until(package_name=api_name, until=until).valid_versions
250-
251-
def get_inferences(self, advisory_data: AdvisoryData) -> Iterable[Inference]:
252-
"""
253-
Yield Inferences for the given advisory data
254-
"""
255-
if not advisory_data.affected_packages:
256-
return
257-
try:
258-
purl, affected_version_ranges, _ = AffectedPackage.merge(
259-
advisory_data.affected_packages
260-
)
261-
except UnMergeablePackageError:
262-
logger.error(f"Cannot merge with different purls {advisory_data.affected_packages!r}")
263-
return iter([])
264-
265-
pkg_type = purl.type
266-
pkg_namespace = purl.namespace
267-
pkg_name = purl.name
268-
269-
if not self.vesions_by_purl.get(str(purl)):
270-
valid_versions = self.get_package_versions(
271-
package_url=purl, until=advisory_data.date_published
272-
)
273-
self.vesions_by_purl[str(purl)] = valid_versions
274-
275-
valid_versions = self.vesions_by_purl[str(purl)]
276-
277-
for affected_version_range in affected_version_ranges:
278-
aff_vers, unaff_vers = resolve_version_range(
279-
affected_version_range=affected_version_range,
280-
package_versions=valid_versions,
281-
ignorable_versions=IGNORE_TAGS,
282-
)
283-
affected_purls = [
284-
PackageURL(type=pkg_type, namespace=pkg_namespace, name=pkg_name, version=version)
285-
for version in aff_vers
286-
]
287-
288-
unaffected_purls = [
289-
PackageURL(type=pkg_type, namespace=pkg_namespace, name=pkg_name, version=version)
290-
for version in unaff_vers
291-
]
292-
293-
affected_packages: List[LegacyAffectedPackage] = nearest_patched_package(
294-
vulnerable_packages=affected_purls, resolved_packages=unaffected_purls
295-
)
296-
297-
for (
298-
fixed_package,
299-
affected_packages,
300-
) in get_affected_packages_by_patched_package(affected_packages).items():
301-
yield Inference.from_advisory_data(
302-
advisory_data,
303-
confidence=100, # We are getting all valid versions to get this inference
304-
affected_purls=affected_packages,
305-
fixed_purl=fixed_package,
306-
)

vulnerabilities/importers/debian.py

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -164,74 +164,3 @@ def parse(self, pkg_name: str, records: Mapping[str, Any]) -> Iterable[AdvisoryD
164164
affected_packages=affected_packages,
165165
references=references,
166166
)
167-
168-
169-
class DebianBasicImprover(Improver):
170-
@property
171-
def interesting_advisories(self) -> QuerySet:
172-
return Advisory.objects.filter(created_by=DebianImporter.qualified_name)
173-
174-
def get_inferences(self, advisory_data: AdvisoryData) -> Iterable[Inference]:
175-
"""
176-
Yield Inferences for the given advisory data
177-
"""
178-
if not advisory_data.affected_packages:
179-
return
180-
try:
181-
purl, affected_version_ranges, fixed_versions = AffectedPackage.merge(
182-
advisory_data.affected_packages
183-
)
184-
except UnMergeablePackageError:
185-
logger.error(f"Cannot merge with different purls {advisory_data.affected_packages!r}")
186-
return
187-
188-
pkg_type = purl.type
189-
pkg_namespace = purl.namespace
190-
pkg_name = purl.name
191-
pkg_qualifiers = purl.qualifiers
192-
fixed_purls = [
193-
PackageURL(
194-
type=pkg_type,
195-
namespace=pkg_namespace,
196-
name=pkg_name,
197-
version=str(version),
198-
qualifiers=pkg_qualifiers,
199-
)
200-
for version in fixed_versions
201-
]
202-
if not affected_version_ranges:
203-
for fixed_purl in fixed_purls:
204-
yield Inference.from_advisory_data(
205-
advisory_data, # We are getting all valid versions to get this inference
206-
confidence=MAX_CONFIDENCE,
207-
affected_purls=[],
208-
fixed_purl=fixed_purl,
209-
)
210-
else:
211-
aff_versions = set()
212-
for affected_version_range in affected_version_ranges:
213-
for constraint in affected_version_range.constraints:
214-
aff_versions.add(constraint.version.string)
215-
affected_purls = [
216-
PackageURL(
217-
type=pkg_type,
218-
namespace=pkg_namespace,
219-
name=pkg_name,
220-
version=version,
221-
qualifiers=pkg_qualifiers,
222-
)
223-
for version in aff_versions
224-
]
225-
affected_packages: List[LegacyAffectedPackage] = nearest_patched_package(
226-
vulnerable_packages=affected_purls, resolved_packages=fixed_purls
227-
)
228-
229-
for (fixed_package, affected_packages,) in get_affected_packages_by_patched_package(
230-
affected_packages=affected_packages
231-
).items():
232-
yield Inference.from_advisory_data(
233-
advisory_data,
234-
confidence=MAX_CONFIDENCE, # We are getting all valid versions to get this inference
235-
affected_purls=affected_packages,
236-
fixed_purl=fixed_package,
237-
)

vulnerabilities/importers/github.py

Lines changed: 0 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,10 @@
88
#
99

1010
import logging
11-
from datetime import datetime
1211
from typing import Iterable
13-
from typing import List
14-
from typing import Mapping
1512
from typing import Optional
1613

1714
from dateutil import parser as dateparser
18-
from django.db.models.query import QuerySet
1915
from packageurl import PackageURL
2016
from univers.version_range import RANGE_CLASS_BY_SCHEMES
2117
from univers.version_range import build_range_from_github_advisory_constraint
@@ -26,21 +22,9 @@
2622
from vulnerabilities.importer import AffectedPackage
2723
from vulnerabilities.importer import Importer
2824
from vulnerabilities.importer import Reference
29-
from vulnerabilities.importer import UnMergeablePackageError
3025
from vulnerabilities.importer import VulnerabilitySeverity
31-
from vulnerabilities.improver import Improver
32-
from vulnerabilities.improver import Inference
33-
from vulnerabilities.models import Advisory
34-
from vulnerabilities.package_managers import VERSION_API_CLASSES_BY_PACKAGE_TYPE
35-
from vulnerabilities.package_managers import GoproxyVersionAPI
36-
from vulnerabilities.package_managers import VersionAPI
37-
from vulnerabilities.package_managers import get_api_package_name
38-
from vulnerabilities.utils import AffectedPackage as LegacyAffectedPackage
3926
from vulnerabilities.utils import dedupe
40-
from vulnerabilities.utils import get_affected_packages_by_patched_package
4127
from vulnerabilities.utils import get_item
42-
from vulnerabilities.utils import nearest_patched_package
43-
from vulnerabilities.utils import resolve_version_range
4428

4529
logger = logging.getLogger(__name__)
4630

@@ -319,92 +303,3 @@ def process_response(resp: dict, package_type: str) -> Iterable[AdvisoryData]:
319303
affected_packages=affected_packages,
320304
date_published=date_published,
321305
)
322-
323-
324-
class GitHubBasicImprover(Improver):
325-
def __init__(self) -> None:
326-
self.versions_fetcher_by_purl: Mapping[str, VersionAPI] = {}
327-
328-
@property
329-
def interesting_advisories(self) -> QuerySet:
330-
return Advisory.objects.filter(created_by=GitHubAPIImporter.qualified_name)
331-
332-
def get_package_versions(
333-
self, package_url: PackageURL, until: Optional[datetime] = None
334-
) -> List[str]:
335-
"""
336-
Return a list of `valid_versions` for the `package_url`
337-
"""
338-
api_name = get_api_package_name(package_url)
339-
if not api_name:
340-
logger.error(f"Could not get versions for {package_url!r}")
341-
return []
342-
versions_fetcher = self.versions_fetcher_by_purl.get(package_url)
343-
if not versions_fetcher:
344-
versions_fetcher: VersionAPI = VERSION_API_CLASSES_BY_PACKAGE_TYPE[package_url.type]
345-
self.versions_fetcher_by_purl[package_url] = versions_fetcher()
346-
347-
versions_fetcher = self.versions_fetcher_by_purl[package_url]
348-
349-
self.versions_fetcher_by_purl[package_url] = versions_fetcher
350-
return versions_fetcher.get_until(package_name=api_name, until=until).valid_versions
351-
352-
def get_inferences(self, advisory_data: AdvisoryData) -> Iterable[Inference]:
353-
"""
354-
Yield Inferences for the given advisory data
355-
"""
356-
if not advisory_data.affected_packages:
357-
return
358-
try:
359-
purl, affected_version_ranges, _ = AffectedPackage.merge(
360-
advisory_data.affected_packages
361-
)
362-
except UnMergeablePackageError:
363-
logger.error(f"Cannot merge with different purls {advisory_data.affected_packages!r}")
364-
return iter([])
365-
366-
pkg_type = purl.type
367-
pkg_namespace = purl.namespace
368-
pkg_name = purl.name
369-
if purl.type == "golang":
370-
# Problem with the Golang and Go that they provide full path
371-
# FIXME: We need to get the PURL subpath for Go module
372-
versions_fetcher = self.versions_fetcher_by_purl.get(purl)
373-
if not versions_fetcher:
374-
versions_fetcher = GoproxyVersionAPI()
375-
self.versions_fetcher_by_purl[purl] = versions_fetcher
376-
pkg_name = versions_fetcher.module_name_by_package_name.get(pkg_name, pkg_name)
377-
378-
valid_versions = self.get_package_versions(
379-
package_url=purl, until=advisory_data.date_published
380-
)
381-
for affected_version_range in affected_version_ranges:
382-
aff_vers, unaff_vers = resolve_version_range(
383-
affected_version_range=affected_version_range,
384-
package_versions=valid_versions,
385-
ignorable_versions=WEIRD_IGNORABLE_VERSIONS,
386-
)
387-
affected_purls = [
388-
PackageURL(type=pkg_type, namespace=pkg_namespace, name=pkg_name, version=version)
389-
for version in aff_vers
390-
]
391-
392-
unaffected_purls = [
393-
PackageURL(type=pkg_type, namespace=pkg_namespace, name=pkg_name, version=version)
394-
for version in unaff_vers
395-
]
396-
397-
affected_packages: List[LegacyAffectedPackage] = nearest_patched_package(
398-
vulnerable_packages=affected_purls, resolved_packages=unaffected_purls
399-
)
400-
401-
for (
402-
fixed_package,
403-
affected_packages,
404-
) in get_affected_packages_by_patched_package(affected_packages).items():
405-
yield Inference.from_advisory_data(
406-
advisory_data,
407-
confidence=100, # We are getting all valid versions to get this inference
408-
affected_purls=affected_packages,
409-
fixed_purl=fixed_package,
410-
)

0 commit comments

Comments
 (0)