-
-
Notifications
You must be signed in to change notification settings - Fork 268
[For Review Purpose Only-Do not Merge] First Draft for Glibc Importer #1414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,204 @@ | ||
| # | ||
| # Copyright (c) nexB Inc. and others. All rights reserved. | ||
| # VulnerableCode is a trademark of nexB Inc. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # See http://www.apache.org/licenses/LICENSE-2.0 for the license text. | ||
| # See https://github.com/nexB/vulnerablecode for support or download. | ||
| # See https://aboutcode.org for more information about nexB OSS projects. | ||
| # | ||
| from datetime import datetime | ||
| from pathlib import Path | ||
| from typing import Any | ||
| from typing import Dict | ||
| from typing import Iterable | ||
| from typing import Optional | ||
|
|
||
| from packageurl import PackageURL | ||
| from univers.version_range import PURL_TYPE_BY_GITLAB_SCHEME | ||
| from univers.version_range import RANGE_CLASS_BY_SCHEMES | ||
| from univers.version_range import VersionRange | ||
| from univers.versions import SemverVersion | ||
|
|
||
| from vulnerabilities.importer import AdvisoryData | ||
| from vulnerabilities.importer import AffectedPackage | ||
| from vulnerabilities.importer import Importer | ||
| from vulnerabilities.importer import Reference | ||
|
|
||
|
|
||
| class GNUVersion(VersionRange): | ||
| # TODO: Open PR for this in univers | ||
| scheme = "gnu" | ||
| version_class = SemverVersion | ||
|
|
||
|
|
||
| RANGE_CLASS_BY_SCHEMES["gnu"] = GNUVersion | ||
| PURL_TYPE_BY_GITLAB_SCHEME["gnu"] = "gnu" | ||
|
|
||
| RANGE_CLASS_BY_SCHEMES["gnu"] = GNUVersion | ||
|
|
||
|
|
||
| class GlibcImporter(Importer): | ||
| repo_url = "git+https://sourceware.org/git/glibc.git" | ||
| license_url = "https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=LICENSES" | ||
| spdx_license_expression = "LGPL-2.1-only" | ||
| importer_name = "Glibc Importer" | ||
|
|
||
| def advisory_data(self) -> Iterable[AdvisoryData]: | ||
| try: | ||
| self.vcs_response = self.clone(repo_url=self.repo_url) | ||
| base_path = Path(self.vcs_response.dest_dir) / "advisories" | ||
| readme_path = base_path / "README" | ||
| files = [path for path in base_path.glob("*") if path != readme_path] | ||
| for file in files: | ||
| with open(file, "r") as f: | ||
| advisory = parse_advisory_data(f.read(), str(file.relative_to(base_path))) | ||
| if advisory: | ||
| yield advisory | ||
| finally: | ||
| if self.vcs_response: | ||
| self.vcs_response.delete() | ||
|
|
||
|
|
||
| def parse_advisory_data(glibc_advisory, file_name) -> AdvisoryData: | ||
| """ | ||
| Parses the provided GLIBC advisory data from the specified file and returns a structured representation containing the essential information. | ||
|
|
||
| Args: | ||
| glibc_advisory (str): The raw GLIBC advisory data to be parsed. | ||
| file_name (str): The name of the file containing the advisory data. | ||
|
|
||
| Returns: | ||
| AdvisoryData: A dictionary-like object encapsulating the parsed advisory data. | ||
|
|
||
| Sample Advisory: | ||
| printf: incorrect output for integers with thousands separator and width field | ||
|
|
||
| When the printf family of functions is called with a format specifier | ||
| that uses an <apostrophe> (enable grouping) and a minimum width | ||
| specifier, the resulting output could be larger than reasonably expected | ||
| by a caller that computed a tight bound on the buffer size. The | ||
| resulting larger than expected output could result in a buffer overflow | ||
| in the printf family of functions. | ||
|
|
||
| CVE-Id: CVE-2023-25139 | ||
| Public-Date: 2023-02-02 | ||
| Vulnerable-Commit: e88b9f0e5cc50cab57a299dc7efe1a4eb385161d (2.37) | ||
| Fix-Commit: c980549cc6a1c03c23cc2fe3e7b0fe626a0364b0 (2.38) | ||
| Fix-Commit: 07b9521fc6369d000216b96562ff7c0ed32a16c4 (2.37-4) | ||
|
|
||
| """ | ||
| content = glibc_advisory.split("\n") | ||
| if content: | ||
| subject = content[0] | ||
| line_counter = 2 | ||
| description = "" | ||
| date = "" | ||
| cve_id = "" | ||
| vulnerable_commits = [] | ||
| fix_commits = [] | ||
| for line in content[line_counter:]: | ||
| if not line.strip(): | ||
| break | ||
| description += line.strip() + " " | ||
| line_counter += 1 | ||
| description = description.strip() | ||
| for line in content[line_counter + 1 :]: | ||
| if not line.strip(): | ||
| break | ||
| tag, content = list(x.strip() for x in line.split(":")) | ||
| match tag: | ||
| case "CVE-Id": | ||
| cve_id = content | ||
| case "Public-Date": | ||
| date = content | ||
| case "Vulnerable-Commit": | ||
| commit, release = content.split("(") | ||
| release = release.strip(")").strip() | ||
| commit = commit.strip() | ||
| vulnerable_commits.append((commit, release)) | ||
| case "Fix-Commit": | ||
| commit, release = content.split("(") | ||
| release = release.strip(")").strip() | ||
| commit = commit.strip() | ||
| fix_commits.append((commit, release)) | ||
|
|
||
| advisory_dict = { | ||
| "aliases": [cve_id], | ||
| "affected_packages": "", | ||
| "date_published": datetime.strptime(date, "%Y-%m-%d"), | ||
| "summary": description, | ||
| "references": [ | ||
| Reference( | ||
| url="https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=advisories/" + file_name | ||
| ) | ||
| ], | ||
| "url": "https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=advisories/" + file_name, | ||
| } | ||
|
|
||
| purl = PackageURL(type="gnu", name="glibc") | ||
| min_affected_version: Optional[SemverVersion, str] = "" | ||
| max_affected_version: Optional[SemverVersion, str] = "" | ||
| for _, release in vulnerable_commits: | ||
| if min_affected_version == "" or max_affected_version == "": | ||
| min_affected_version = SemverVersion(sanitize_version(release)) | ||
| max_affected_version = SemverVersion(sanitize_version(release)) | ||
| else: | ||
| min_affected_version = ( | ||
| SemverVersion(sanitize_version(release)) | ||
| if SemverVersion(sanitize_version(release)) < min_affected_version | ||
| else min_affected_version | ||
| ) | ||
| max_affected_version = ( | ||
| SemverVersion(sanitize_version(release)) | ||
| if SemverVersion(sanitize_version(release)) > max_affected_version | ||
| else max_affected_version | ||
| ) | ||
| _, min_fixed_version = min(fix_commits, key=lambda x: SemverVersion(sanitize_version(x[1]))) | ||
| min_fixed_version = SemverVersion(sanitize_version(min_fixed_version)) | ||
| affected_version_range = None | ||
| if max_affected_version == "" and min_affected_version == "": | ||
| affected_version_range = None | ||
| elif max_affected_version == min_affected_version: | ||
| affected_version_range = VersionRange.from_string(f"vers:gnu/{str(max_affected_version)}") | ||
| else: | ||
| affected_version_range = VersionRange.from_string( | ||
| f"vers:gnu/<={str(max_affected_version)}|>={min_affected_version}" | ||
| ) | ||
| affected_packages = AffectedPackage( | ||
| package=purl, | ||
| affected_version_range=affected_version_range, | ||
| fixed_version=min_fixed_version, | ||
| ) | ||
| advisory_dict["affected_packages"] = [affected_packages] | ||
| resolved_advisory = to_advisory(advisory_dict) | ||
| return resolved_advisory | ||
|
|
||
|
|
||
| def sanitize_version(version: str): | ||
harsh098 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """ | ||
| Returns the version in Semver Format from Glibc Advisory | ||
|
|
||
| Args: | ||
| version (str): Version string from advisory | ||
|
|
||
| Returns: | ||
| str: Version string in Semver Format | ||
|
|
||
| >>> sanitize_version('2.45-12') | ||
| '2.45.12' | ||
| """ | ||
| return version.replace("-", ".") | ||
|
|
||
|
|
||
| def to_advisory(advisory_data: Dict[str, Any]) -> AdvisoryData: | ||
| """ | ||
| Returns the AdvisoryData object for a given dictionary containing advisory info | ||
|
|
||
| Args: | ||
| advisory_data: Dict[str, Any]: contains all fields to be passed to the constructor of AdvisoryData | ||
|
|
||
| Returns: | ||
| AdvisoryData: converted object into AdvisoryData format | ||
|
|
||
| """ | ||
| return AdvisoryData(**advisory_data) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| # | ||
| # Copyright (c) nexB Inc. and others. All rights reserved. | ||
| # VulnerableCode is a trademark of nexB Inc. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # See http://www.apache.org/licenses/LICENSE-2.0 for the license text. | ||
| # See https://github.com/nexB/vulnerablecode for support or download. | ||
| # See https://aboutcode.org for more information about nexB OSS projects. | ||
| # | ||
| import os | ||
harsh098 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| from datetime import datetime | ||
| from textwrap import dedent | ||
| from unittest import TestCase | ||
|
|
||
| from packageurl import PackageURL | ||
| from univers.version_range import VersionRange | ||
| from univers.versions import SemverVersion | ||
|
|
||
| from vulnerabilities.importer import AdvisoryData | ||
| from vulnerabilities.importer import AffectedPackage | ||
| from vulnerabilities.importer import Reference | ||
| from vulnerabilities.importers.glibc import parse_advisory_data | ||
|
|
||
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | ||
| TEST_DATA = os.path.join(BASE_DIR, "test_data/glibc") | ||
|
|
||
|
|
||
| class TestGlibcImporter(TestCase): | ||
| def test_parse_advisory_data_1(self): | ||
| test_data = parse_advisory_data( | ||
| dedent( | ||
| """syslog: Heap buffer overflow in __vsyslog_internal | ||
|
|
||
| __vsyslog_internal did not handle a case where printing a SYSLOG_HEADER | ||
| containing a long program name failed to update the required buffer | ||
| size, leading to the allocation and overflow of a too-small buffer on | ||
| the heap. | ||
|
|
||
| CVE-Id: CVE-2023-6246 | ||
| Public-Date: 2024-01-30 | ||
| Vulnerable-Commit: 52a5be0df411ef3ff45c10c7c308cb92993d15b1 (2.37) | ||
| Fix-Commit: 6bd0e4efcc78f3c0115e5ea9739a1642807450da (2.39) | ||
| Fix-Commit: d1a83b6767f68b3cb5b4b4ea2617254acd040c82 (2.36-126) | ||
| Fix-Commit: 23514c72b780f3da097ecf33a793b7ba9c2070d2 (2.38-42) | ||
| Fix-Commit: 97a4292aa4a2642e251472b878d0ec4c46a0e59a (2.37-57) | ||
| Vulnerable-Commit: b0e7888d1fa2dbd2d9e1645ec8c796abf78880b9 (2.36-16) | ||
| """ | ||
| ), | ||
| "GLIBC-SA-2023-0001", | ||
| ) | ||
|
|
||
| expected_output = AdvisoryData( | ||
| **{ | ||
| "aliases": ["CVE-2023-6246"], | ||
| "affected_packages": [ | ||
| AffectedPackage( | ||
| package=PackageURL(type="gnu", name="glibc"), | ||
| affected_version_range=VersionRange.from_string( | ||
| f'vers:gnu/>={str(SemverVersion(string="2.36.16"))}|<={SemverVersion(string="2.37")}' | ||
| ), | ||
| fixed_version=SemverVersion(string="2.36.126"), | ||
| ) | ||
| ], | ||
| "date_published": datetime(2024, 1, 30, 0, 0), | ||
| "summary": "__vsyslog_internal did not handle a case where printing a SYSLOG_HEADER containing a long program name failed to update the required buffer size, leading to the allocation and overflow of a too-small buffer on the heap.", | ||
| "references": [ | ||
| Reference( | ||
| reference_id="", | ||
| url="https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=advisories/GLIBC-SA-2023-0001", | ||
| severities=[], | ||
| ) | ||
| ], | ||
| "url": "https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=advisories/GLIBC-SA-2023-0001", | ||
| } | ||
| ) | ||
|
|
||
| assert expected_output == test_data | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.