diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 095df9ad6..a6a74d165 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,39 @@ Release notes ============= +Version v34.0.0rc4 +------------------- + +- Drop migration for removing duplicated changelogs. + + +Version v34.0.0rc3 +------------------- + +- Add resource URL to the vulnerability and package details view in the API serializers (#1423) +- Add support for all osv ecosystems (#926) +- Add RubyImporter to git_importer test_git_importer_clone (#799) +- Remove duplicated changelogs (#1400) +- Fix Encoding Type in Fireeye Importer (#1404) +- Add license_url for GitHub Importer (#1392) +- Add support for CVSS vectors display (#1312) + + +Version v34.0.0rc2 +------------------- + +- We updated package-url models, WARNING: in next major version of + vulnerablecode i.e v35.0.0 qualifiers will be of type ``string`` and not ``dict``. +- We added changelog and dates on packages and vulnerabilities. +- We fixed table borders in Vulnerability details UI #1356 (#1358) +- We added robots.txt in views. +- We fixed import runner's process_inferences (#1360) +- We fixed debian OVAL importer (#1361) +- We added graph model diagrams #977(#1350) +- We added endpoint for purl lookup (#1359) +- We fixed swagger API docs generation (#1366) +- Fix issues https://github.com/nexB/vulnerablecode/issues/1385, https://github.com/nexB/vulnerablecode/issues/1387 + Version v34.0.0rc1 ------------------- diff --git a/requirements.txt b/requirements.txt index 042f541da..47dcc8bb9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ cffi==1.15.0 chardet==4.0.0 charset-normalizer==2.0.12 click==8.1.2 -cryptography==41.0.6 +cryptography==42.0.4 decorator==5.1.1 defusedxml==0.7.1 distro==1.7.0 @@ -35,7 +35,7 @@ executing==0.8.3 freezegun==1.2.1 frozenlist==1.3.0 gitdb==4.0.9 -GitPython==3.1.37 +GitPython==3.1.41 gunicorn==20.1.0 idna==3.3 imagesize==1.3.0 @@ -44,7 +44,7 @@ iniconfig==1.1.1 ipython==8.10.0 isort==5.10.1 jedi==0.18.1 -Jinja2==3.1.1 +Jinja2==3.1.3 jsonschema==3.2.0 license-expression==21.6.14 lxml==4.9.1 @@ -106,7 +106,7 @@ toml==0.10.2 tomli==2.0.1 traitlets==5.1.1 typing_extensions==4.1.1 -univers==30.10.0 +univers==30.11.0 urllib3==1.26.18 wcwidth==0.2.5 websocket-client==0.59.0 diff --git a/setup.cfg b/setup.cfg index 7fc65fa21..a030f0ded 100644 --- a/setup.cfg +++ b/setup.cfg @@ -71,7 +71,7 @@ install_requires = #essentials packageurl-python>=0.10.5rc1 - univers>=30.10.0 + univers>=30.11.0 license-expression>=21.6.14 # file and data formats diff --git a/vulnerabilities/api.py b/vulnerabilities/api.py index 30ca4cb9f..9c4fb26bd 100644 --- a/vulnerabilities/api.py +++ b/vulnerabilities/api.py @@ -20,6 +20,7 @@ from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response +from rest_framework.reverse import reverse from rest_framework.throttling import AnonRateThrottle from rest_framework.throttling import UserRateThrottle @@ -48,7 +49,41 @@ class Meta: fields = ["reference_url", "reference_id", "scores", "url"] -class MinimalPackageSerializer(serializers.HyperlinkedModelSerializer): +class BaseResourceSerializer(serializers.HyperlinkedModelSerializer): + """ + Base serializer containing common methods. + """ + + def get_fields(self): + fields = super().get_fields() + fields["resource_url"] = serializers.SerializerMethodField(method_name="get_resource_url") + return fields + + def get_resource_url(self, instance): + """ + Return the instance fully qualified URL including the schema and domain. + + Usage: + resource_url = serializers.SerializerMethodField() + """ + resource_url = instance.get_absolute_url() + + if request := self.context.get("request", None): + return request.build_absolute_uri(location=resource_url) + + return resource_url + + +class PurlPackageSerializer(BaseResourceSerializer): + + purl = serializers.CharField(source="package_url") + + class Meta: + model = Package + fields = ["url", "purl", "is_vulnerable"] + + +class MinimalPackageSerializer(PurlPackageSerializer): """ Used for nesting inside vulnerability focused APIs. """ @@ -72,14 +107,12 @@ def get_vulnerability(self, vuln): affected_by_vulnerabilities = serializers.SerializerMethodField("get_affected_vulnerabilities") - purl = serializers.CharField(source="package_url") - class Meta: model = Package fields = ["url", "purl", "is_vulnerable", "affected_by_vulnerabilities"] -class MinimalVulnerabilitySerializer(serializers.HyperlinkedModelSerializer): +class MinimalVulnerabilitySerializer(BaseResourceSerializer): """ Lookup vulnerabilities by aliases (such as a CVE). """ @@ -99,7 +132,7 @@ class Meta: fields = ["alias"] -class VulnSerializerRefsAndSummary(serializers.HyperlinkedModelSerializer): +class VulnSerializerRefsAndSummary(BaseResourceSerializer): """ Lookup vulnerabilities references by aliases (such as a CVE). """ @@ -110,7 +143,7 @@ def to_representation(self, instance): data["aliases"] = aliases return data - fixed_packages = MinimalPackageSerializer( + fixed_packages = PurlPackageSerializer( many=True, source="filtered_fixed_packages", read_only=True ) @@ -141,24 +174,23 @@ def to_representation(self, instance): return representation -class VulnerabilitySerializer(serializers.HyperlinkedModelSerializer): - fixed_packages = MinimalPackageSerializer( +class VulnerabilitySerializer(BaseResourceSerializer): + fixed_packages = PurlPackageSerializer( many=True, source="filtered_fixed_packages", read_only=True ) - affected_packages = MinimalPackageSerializer(many=True, read_only=True) + affected_packages = PurlPackageSerializer(many=True, read_only=True) references = VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set") aliases = AliasSerializer(many=True, source="alias") weaknesses = WeaknessSerializer(many=True) def to_representation(self, instance): - representation = super().to_representation(instance) + data = super().to_representation(instance) - # Exclude None values from the weaknesses list - weaknesses = representation.get("weaknesses", []) - representation["weaknesses"] = [weakness for weakness in weaknesses if weakness is not None] + weaknesses = data.get("weaknesses", []) + data["weaknesses"] = [weakness for weakness in weaknesses if weakness is not None] - return representation + return data class Meta: model = Vulnerability @@ -174,7 +206,7 @@ class Meta: ] -class PackageSerializer(serializers.HyperlinkedModelSerializer): +class PackageSerializer(BaseResourceSerializer): """ Lookup software package using Package URLs """ @@ -182,6 +214,7 @@ class PackageSerializer(serializers.HyperlinkedModelSerializer): def to_representation(self, instance): data = super().to_representation(instance) data["qualifiers"] = normalize_qualifiers(data["qualifiers"], encode=False) + return data next_non_vulnerable_version = serializers.SerializerMethodField("get_next_non_vulnerable") diff --git a/vulnerabilities/importers/__init__.py b/vulnerabilities/importers/__init__.py index add6967f8..cedd8902b 100644 --- a/vulnerabilities/importers/__init__.py +++ b/vulnerabilities/importers/__init__.py @@ -18,6 +18,7 @@ from vulnerabilities.importers import fireeye from vulnerabilities.importers import gentoo from vulnerabilities.importers import github +from vulnerabilities.importers import github_osv from vulnerabilities.importers import gitlab from vulnerabilities.importers import istio from vulnerabilities.importers import mozilla @@ -32,6 +33,7 @@ from vulnerabilities.importers import pysec from vulnerabilities.importers import redhat from vulnerabilities.importers import retiredotnet +from vulnerabilities.importers import ruby from vulnerabilities.importers import suse_scores from vulnerabilities.importers import ubuntu from vulnerabilities.importers import ubuntu_usn @@ -67,6 +69,8 @@ fireeye.FireyeImporter, apache_kafka.ApacheKafkaImporter, oss_fuzz.OSSFuzzImporter, + ruby.RubyImporter, + github_osv.GithubOSVImporter, ] IMPORTERS_REGISTRY = {x.qualified_name: x for x in IMPORTERS_REGISTRY} diff --git a/vulnerabilities/importers/fireeye.py b/vulnerabilities/importers/fireeye.py index 814af7248..f39ff6c45 100644 --- a/vulnerabilities/importers/fireeye.py +++ b/vulnerabilities/importers/fireeye.py @@ -44,7 +44,7 @@ def advisory_data(self) -> Iterable[AdvisoryData]: if Path(file).stem == "README": continue try: - with open(file) as f: + with open(file, encoding="utf-8-sig") as f: yield parse_advisory_data(raw_data=f.read(), file=file, base_path=base_path) except UnicodeError: logger.error(f"Invalid file {file}") diff --git a/vulnerabilities/importers/github.py b/vulnerabilities/importers/github.py index 837bb458e..f6eb724f5 100644 --- a/vulnerabilities/importers/github.py +++ b/vulnerabilities/importers/github.py @@ -92,6 +92,7 @@ class GitHubAPIImporter(Importer): spdx_license_expression = "CC-BY-4.0" importer_name = "GHSA Importer" + license_url = "https://github.com/github/advisory-database/blob/main/LICENSE.md" def advisory_data(self) -> Iterable[AdvisoryData]: for ecosystem, package_type in PACKAGE_TYPE_BY_GITHUB_ECOSYSTEM.items(): diff --git a/vulnerabilities/importers/github_osv.py b/vulnerabilities/importers/github_osv.py new file mode 100644 index 000000000..bef06a8af --- /dev/null +++ b/vulnerabilities/importers/github_osv.py @@ -0,0 +1,56 @@ +# +# 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 json +import logging +from pathlib import Path +from typing import Iterable + +from vulnerabilities.importer import AdvisoryData +from vulnerabilities.importer import Importer +from vulnerabilities.importers.osv import parse_advisory_data +from vulnerabilities.utils import get_advisory_url + +logger = logging.getLogger(__name__) + + +class GithubOSVImporter(Importer): + license_url = "https://github.com/github/advisory-database/blob/main/LICENSE.md" + spdx_license_expression = "CC-BY-4.0" + repo_url = "git+https://github.com/github/advisory-database/" + importer_name = "GithubOSV Importer" + + def advisory_data(self) -> Iterable[AdvisoryData]: + supported_ecosystems = [ + "pypi", + "npm", + "maven", + "golang", + "composer", + "hex", + "gem", + "nuget", + "cargo", + ] + try: + self.clone(repo_url=self.repo_url) + base_path = Path(self.vcs_response.dest_dir) + # filter out non-github-reviewed files and only keep the files end-with .json + advisory_dirs = base_path / "advisories/github-reviewed" + for file in advisory_dirs.glob("**/*.json"): + advisory_url = get_advisory_url( + file=file, + base_path=base_path, + url="https://github.com/github/advisory-database/blob/main/", + ) + with open(file) as f: + raw_data = json.load(f) + yield parse_advisory_data(raw_data, supported_ecosystems, advisory_url) + finally: + if self.vcs_response: + self.vcs_response.delete() diff --git a/vulnerabilities/importers/oss_fuzz.py b/vulnerabilities/importers/oss_fuzz.py index e86df5ce8..e81f06fc2 100644 --- a/vulnerabilities/importers/oss_fuzz.py +++ b/vulnerabilities/importers/oss_fuzz.py @@ -40,7 +40,7 @@ def advisory_data(self) -> Iterable[AdvisoryData]: url="https://github.com/pypa/advisory-database/blob/main/", ) yield parse_advisory_data( - yaml_data, supported_ecosystem="oss-fuzz", advisory_url=advisory_url + yaml_data, supported_ecosystems=["oss-fuzz"], advisory_url=advisory_url ) finally: if self.vcs_response: diff --git a/vulnerabilities/importers/osv.py b/vulnerabilities/importers/osv.py index 1bf3f7c36..1ee45a1be 100644 --- a/vulnerabilities/importers/osv.py +++ b/vulnerabilities/importers/osv.py @@ -13,10 +13,10 @@ from typing import Optional import dateparser +from cvss.exceptions import CVSS3MalformedError from packageurl import PackageURL from univers.version_range import RANGE_CLASS_BY_SCHEMES from univers.versions import InvalidVersion -from univers.versions import PypiVersion from univers.versions import SemverVersion from univers.versions import Version @@ -31,9 +31,21 @@ logger = logging.getLogger(__name__) +PURL_TYPE_BY_OSV_ECOSYSTEM = { + "npm": "npm", + "pypi": "pypi", + "maven": "maven", + "nuget": "nuget", + "packagist": "composer", + "rubygems": "gem", + "go": "golang", + "hex": "hex", + "cargo": "cargo", +} + def parse_advisory_data( - raw_data: dict, supported_ecosystem, advisory_url: str + raw_data: dict, supported_ecosystems, advisory_url: str ) -> Optional[AdvisoryData]: """ Return an AdvisoryData build from a ``raw_data`` mapping of OSV advisory and @@ -56,18 +68,21 @@ def parse_advisory_data( for affected_pkg in raw_data.get("affected") or []: purl = get_affected_purl(affected_pkg=affected_pkg, raw_id=raw_id) - if purl.type != supported_ecosystem: - logger.error(f"Unsupported package type: {purl!r} in OSV: {raw_id!r}") + + if not purl or purl.type not in supported_ecosystems: + logger.error(f"Unsupported package type: {affected_pkg!r} in OSV: {raw_id!r}") continue affected_version_range = get_affected_version_range( affected_pkg=affected_pkg, raw_id=raw_id, - supported_ecosystem=supported_ecosystem, + supported_ecosystem=purl.type, ) for fixed_range in affected_pkg.get("ranges") or []: - fixed_version = get_fixed_versions(fixed_range=fixed_range, raw_id=raw_id) + fixed_version = get_fixed_versions( + fixed_range=fixed_range, raw_id=raw_id, supported_ecosystem=purl.type + ) for version in fixed_version: affected_packages.append( @@ -121,14 +136,22 @@ def get_severities(raw_data) -> Iterable[VulnerabilitySeverity]: """ Yield VulnerabilitySeverity extracted from a mapping of OSV ``raw_data`` """ - for severity in raw_data.get("severity") or []: - if severity.get("type") == "CVSS_V3": - vector = severity["score"] - system = SCORING_SYSTEMS["cvssv3.1"] - score = system.compute(vector) - yield VulnerabilitySeverity(system=system, value=score, scoring_elements=vector) - else: - logger.error(f"Unsupported severity type: {severity!r} for OSV id: {raw_data['id']!r}") + try: + for severity in raw_data.get("severity") or []: + if severity.get("type") == "CVSS_V3": + vector = severity.get("score") + # remove the / from the end of the vector if / exist + valid_vector = vector[:-1] if vector and vector[-1] == "/" else vector + system = SCORING_SYSTEMS["cvssv3.1"] + score = system.compute(valid_vector) + yield VulnerabilitySeverity(system=system, value=score, scoring_elements=vector) + + else: + logger.error( + f"Unsupported severity type: {severity!r} for OSV id: {raw_data['id']!r}" + ) + except CVSS3MalformedError as e: + logger.error(f"Invalid severity {e}") ecosystem_specific = raw_data.get("ecosystem_specific") or {} severity = ecosystem_specific.get("severity") @@ -173,21 +196,31 @@ def get_affected_purl(affected_pkg, raw_id): purl = package.get("purl") if purl: try: - return PackageURL.from_string(purl) + purl = PackageURL.from_string(purl) except ValueError: logger.error( f"Invalid PackageURL: {purl!r} for OSV " f"affected_pkg {affected_pkg} and id: {raw_id}" ) - - ecosys = package.get("ecosystem") - name = package.get("name") - if ecosys and name: - return PackageURL(type=ecosys, name=name) - - logger.error( - f"No PackageURL possible: {purl!r} for affected_pkg {affected_pkg} for OSV id: {raw_id}" - ) + else: + ecosys = package.get("ecosystem") + name = package.get("name") + if ecosys and name: + ecosys = ecosys.lower() + purl_type = PURL_TYPE_BY_OSV_ECOSYSTEM.get(ecosys) + if not purl_type: + return + namespace = "" + if purl_type == "maven": + namespace, _, name = name.partition(":") + + purl = PackageURL(type=purl_type, namespace=namespace, name=name) + else: + logger.error( + f"No PackageURL possible: {purl!r} for affected_pkg {affected_pkg} for OSV id: {raw_id}" + ) + return + return PackageURL.from_string(str(purl)) def get_affected_version_range(affected_pkg, raw_id, supported_ecosystem): @@ -206,18 +239,17 @@ def get_affected_version_range(affected_pkg, raw_id, supported_ecosystem): ) -def get_fixed_versions(fixed_range, raw_id) -> List[Version]: +def get_fixed_versions(fixed_range, raw_id, supported_ecosystem) -> List[Version]: """ Return a list of unique fixed univers Versions given a ``fixed_range`` univers VersionRange and a ``raw_id``. - For example:: - - >>> get_fixed_versions(fixed_range={}, raw_id="GHSA-j3f7-7rmc-6wqj") + >>> get_fixed_versions(fixed_range={}, raw_id="GHSA-j3f7-7rmc-6wqj", supported_ecosystem="pypi",) [] >>> get_fixed_versions( - ... fixed_range={"type": "ECOSYSTEM", "events": [{"fixed": "1.7.0"}]}, - ... raw_id="GHSA-j3f7-7rmc-6wqj" + ... fixed_range={"type": "ECOSYSTEM", "events": [{"fixed": "1.7.0"}], }, + ... raw_id="GHSA-j3f7-7rmc-6wqj", + ... supported_ecosystem="pypi", ... ) [PypiVersion(string='1.7.0')] """ @@ -228,21 +260,27 @@ def get_fixed_versions(fixed_range, raw_id) -> List[Version]: fixed_range_type = fixed_range["type"] - for version in extract_fixed_versions(fixed_range): + version_range_class = RANGE_CLASS_BY_SCHEMES.get(supported_ecosystem) + version_class = version_range_class.version_class if version_range_class else None - # FIXME: ECOSYSTEM does not imply PyPI!!!! + for version in extract_fixed_versions(fixed_range): if fixed_range_type == "ECOSYSTEM": try: - fixed_versions.append(PypiVersion(version)) + if not version_class: + raise InvalidVersion( + f"Unsupported version for ecosystem: {supported_ecosystem}" + ) + fixed_versions.append(version_class(version)) except InvalidVersion: - logger.error(f"Invalid PypiVersion: {version!r} for OSV id: {raw_id!r}") + logger.error( + f"Invalid version class: {version_class} - {version!r} for OSV id: {raw_id!r}" + ) elif fixed_range_type == "SEMVER": try: fixed_versions.append(SemverVersion(version)) except InvalidVersion: logger.error(f"Invalid SemverVersion: {version!r} for OSV id: {raw_id!r}") - else: logger.error(f"Unsupported fixed version type: {version!r} for OSV id: {raw_id!r}") diff --git a/vulnerabilities/importers/pypa.py b/vulnerabilities/importers/pypa.py index 0f545be55..e0648e1c2 100644 --- a/vulnerabilities/importers/pypa.py +++ b/vulnerabilities/importers/pypa.py @@ -34,7 +34,7 @@ def advisory_data(self) -> Iterable[AdvisoryData]: for advisory_url, raw_data in fork_and_get_files(base_path=path): yield parse_advisory_data( raw_data=raw_data, - supported_ecosystem="pypi", + supported_ecosystems=["pypi"], advisory_url=advisory_url, ) finally: diff --git a/vulnerabilities/importers/pysec.py b/vulnerabilities/importers/pysec.py index b42d7cb7e..058747463 100644 --- a/vulnerabilities/importers/pysec.py +++ b/vulnerabilities/importers/pysec.py @@ -40,5 +40,5 @@ def advisory_data(self) -> Iterable[AdvisoryData]: with zip_file.open(file_name) as f: vul_info = json.load(f) yield parse_advisory_data( - raw_data=vul_info, supported_ecosystem="pypi", advisory_url=url + raw_data=vul_info, supported_ecosystems=["pypi"], advisory_url=url ) diff --git a/vulnerabilities/importers/ruby.py b/vulnerabilities/importers/ruby.py index 556e39140..6a3b5f3f1 100644 --- a/vulnerabilities/importers/ruby.py +++ b/vulnerabilities/importers/ruby.py @@ -7,132 +7,177 @@ # See https://aboutcode.org for more information about nexB OSS projects. # -import asyncio -from typing import List -from typing import Set +import logging +from pathlib import Path +from typing import Iterable from dateutil.parser import parse from packageurl import PackageURL from pytz import UTC -from univers.version_range import VersionRange -from univers.versions import SemverVersion +from univers.version_range import GemVersionRange from vulnerabilities.importer import AdvisoryData +from vulnerabilities.importer import AffectedPackage from vulnerabilities.importer import Importer from vulnerabilities.importer import Reference -from vulnerabilities.package_managers import RubyVersionAPI +from vulnerabilities.importer import VulnerabilitySeverity +from vulnerabilities.severity_systems import SCORING_SYSTEMS +from vulnerabilities.utils import build_description +from vulnerabilities.utils import get_advisory_url from vulnerabilities.utils import load_yaml -from vulnerabilities.utils import nearest_patched_package +logger = logging.getLogger(__name__) -class RubyImporter(Importer): - def __enter__(self): - super(RubyImporter, self).__enter__() - - if not getattr(self, "_added_files", None): - self._added_files, self._updated_files = self.file_changes( - recursive=True, file_ext="yml", subdir="./gems" - ) - self.pkg_manager_api = RubyVersionAPI() - self.set_api(self.collect_packages()) - - def set_api(self, packages): - asyncio.run(self.pkg_manager_api.load_api(packages)) - - def updated_advisories(self) -> Set[AdvisoryData]: - files = self._updated_files.union(self._added_files) - advisories = [] - for f in files: - processed_data = self.process_file(f) - if processed_data: - advisories.append(processed_data) - return self.batch_advisories(advisories) - - def collect_packages(self): - packages = set() - files = self._updated_files.union(self._added_files) - for f in files: - data = load_yaml(f) - if data.get("gem"): - packages.add(data["gem"]) - - return packages - - def process_file(self, path) -> List[AdvisoryData]: - record = load_yaml(path) +class RubyImporter(Importer): + license_url = "https://github.com/rubysec/ruby-advisory-db/blob/master/LICENSE.txt" + repo_url = "git+https://github.com/rubysec/ruby-advisory-db" + importer_name = "Ruby Importer" + spdx_license_expression = "LicenseRef-scancode-public-domain-disclaimer" + notice = """ + If you submit code or data to the ruby-advisory-db that is copyrighted by + yourself, upon submission you hereby agree to release it into the public + domain. + + The data imported from the ruby-advisory-db have been filtered to exclude + any non-public domain data from the data copyrighted by the Open + Source Vulnerability Database (http://osvdb.org). + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + """ + + def advisory_data(self) -> Iterable[AdvisoryData]: + try: + self.clone(self.repo_url) + base_path = Path(self.vcs_response.dest_dir) + supported_subdir = ["rubies", "gems"] + for subdir in supported_subdir: + for file_path in base_path.glob(f"{subdir}/**/*.yml"): + if file_path.name.startswith("OSVDB-"): + continue + raw_data = load_yaml(file_path) + advisory_url = get_advisory_url( + file=file_path, + base_path=base_path, + url="https://github.com/rubysec/ruby-advisory-db/blob/master/", + ) + yield parse_ruby_advisory(raw_data, subdir, advisory_url) + finally: + if self.vcs_response: + self.vcs_response.delete() + + +def parse_ruby_advisory(record, schema_type, advisory_url): + """ + Parse a ruby advisory file and return an AdvisoryData or None. + Each advisory file contains the advisory information in YAML format. + Schema: https://github.com/rubysec/ruby-advisory-db/tree/master/spec/schemas + """ + if schema_type == "gems": package_name = record.get("gem") - if not package_name: - return - if "cve" in record: - cve_id = "CVE-{}".format(record["cve"]) + if not package_name: + logger.error("Invalid package name") else: - return - - publish_time = parse(record["date"]).replace(tzinfo=UTC) - safe_version_ranges = record.get("patched_versions", []) - # this case happens when the advisory contain only 'patched_versions' field - # and it has value None(i.e it is empty :( ). - if not safe_version_ranges: - safe_version_ranges = [] - safe_version_ranges += record.get("unaffected_versions", []) - safe_version_ranges = [i for i in safe_version_ranges if i] - - if not getattr(self, "pkg_manager_api", None): - self.pkg_manager_api = RubyVersionAPI() - all_vers = self.pkg_manager_api.get(package_name, until=publish_time).valid_versions - safe_versions, affected_versions = self.categorize_versions(all_vers, safe_version_ranges) - - impacted_purls = [ - PackageURL( - name=package_name, - type="gem", - version=version, + purl = PackageURL(type="gem", name=package_name) + + return AdvisoryData( + aliases=get_aliases(record), + summary=get_summary(record), + affected_packages=get_affected_packages(record, purl), + references=get_references(record), + date_published=get_publish_time(record), + url=advisory_url, ) - for version in affected_versions - ] - - resolved_purls = [ - PackageURL( - name=package_name, - type="gem", - version=version, + + elif schema_type == "rubies": + engine = record.get("engine") # engine enum: [jruby, rbx, ruby] + if not engine: + logger.error("Invalid engine name") + else: + purl = PackageURL(type="ruby", name=engine) + return AdvisoryData( + aliases=get_aliases(record), + summary=get_summary(record), + affected_packages=get_affected_packages(record, purl), + references=get_references(record), + date_published=get_publish_time(record), + url=advisory_url, ) - for version in safe_versions - ] - references = [] - if record.get("url"): - references.append(Reference(url=record.get("url"))) - return AdvisoryData( - summary=record.get("description", ""), - affected_packages=nearest_patched_package(impacted_purls, resolved_purls), - references=references, - vulnerability_id=cve_id, +def get_affected_packages(record, purl): + """ + Return AffectedPackage objects one for each affected_version_range and invert the safe_version_ranges + ( patched_versions , unaffected_versions ) then passing the purl and the inverted safe_version_range + to the AffectedPackage object + """ + safe_version_ranges = record.get("patched_versions", []) + # this case happens when the advisory contain only 'patched_versions' field + # and it has value None(i.e it is empty :( ). + if not safe_version_ranges: + safe_version_ranges = [] + safe_version_ranges += record.get("unaffected_versions", []) + safe_version_ranges = [i for i in safe_version_ranges if i] + + affected_packages = [] + affected_version_ranges = [ + GemVersionRange.from_native(elem).invert() for elem in safe_version_ranges + ] + + for affected_version_range in affected_version_ranges: + affected_packages.append( + AffectedPackage( + package=purl, + affected_version_range=affected_version_range, + ) ) + return affected_packages + - @staticmethod - def categorize_versions(all_versions, unaffected_version_ranges): +def get_aliases(record) -> [str]: + aliases = [] + if record.get("cve"): + aliases.append("CVE-{}".format(record.get("cve"))) + if record.get("osvdb"): + aliases.append("OSV-{}".format(record.get("osvdb"))) + if record.get("ghsa"): + aliases.append("GHSA-{}".format(record.get("ghsa"))) + return aliases - for id, elem in enumerate(unaffected_version_ranges): - unaffected_version_ranges[id] = VersionRange.from_scheme_version_spec_string( - "semver", elem + +def get_references(record) -> [Reference]: + references = [] + cvss_v3 = record.get("cvss_v3") + if record.get("url"): + if not cvss_v3: + references.append(Reference(url=record.get("url"))) + else: + references.append( + Reference( + url=record.get("url"), + severities=[ + VulnerabilitySeverity(system=SCORING_SYSTEMS["cvssv3"], value=cvss_v3) + ], + ) ) + return references + + +def get_publish_time(record): + date = record.get("date") + if not date: + return + return parse(date).replace(tzinfo=UTC) + - safe_versions = [] - vulnerable_versions = [] - for i in all_versions: - vobj = SemverVersion(i) - is_vulnerable = False - for ver_rng in unaffected_version_ranges: - if vobj in ver_rng: - safe_versions.append(i) - is_vulnerable = True - break - - if not is_vulnerable: - vulnerable_versions.append(i) - - return safe_versions, vulnerable_versions +def get_summary(record): + title = record.get("title") or "" + description = record.get("description") or "" + return build_description(summary=title, description=description) diff --git a/vulnerabilities/improvers/__init__.py b/vulnerabilities/improvers/__init__.py index 9880bf9ee..8cc68b9a6 100644 --- a/vulnerabilities/improvers/__init__.py +++ b/vulnerabilities/improvers/__init__.py @@ -24,6 +24,8 @@ valid_versions.DebianOvalImprover, valid_versions.UbuntuOvalImprover, valid_versions.OSSFuzzImprover, + valid_versions.RubyImprover, + valid_versions.GithubOSVImprover, vulnerability_status.VulnerabilityStatusImprover, ] diff --git a/vulnerabilities/improvers/valid_versions.py b/vulnerabilities/improvers/valid_versions.py index cada4bbb6..d23508bea 100644 --- a/vulnerabilities/improvers/valid_versions.py +++ b/vulnerabilities/improvers/valid_versions.py @@ -32,11 +32,13 @@ from vulnerabilities.importers.debian_oval import DebianOvalImporter from vulnerabilities.importers.elixir_security import ElixirSecurityImporter from vulnerabilities.importers.github import GitHubAPIImporter +from vulnerabilities.importers.github_osv import GithubOSVImporter from vulnerabilities.importers.gitlab import GitLabAPIImporter from vulnerabilities.importers.istio import IstioImporter from vulnerabilities.importers.nginx import NginxImporter from vulnerabilities.importers.npm import NpmImporter from vulnerabilities.importers.oss_fuzz import OSSFuzzImporter +from vulnerabilities.importers.ruby import RubyImporter from vulnerabilities.importers.ubuntu import UbuntuImporter from vulnerabilities.improver import MAX_CONFIDENCE from vulnerabilities.improver import Improver @@ -460,3 +462,13 @@ class UbuntuOvalImprover(ValidVersionImprover): class OSSFuzzImprover(ValidVersionImprover): importer = OSSFuzzImporter ignorable_versions = [] + + +class RubyImprover(ValidVersionImprover): + importer = RubyImporter + ignorable_versions = [] + + +class GithubOSVImprover(ValidVersionImprover): + importer = GithubOSVImporter + ignorable_versions = [] diff --git a/vulnerabilities/migrations/0054_alter_packagechangelog_software_version_and_more.py b/vulnerabilities/migrations/0054_alter_packagechangelog_software_version_and_more.py new file mode 100644 index 000000000..9bc84b9e9 --- /dev/null +++ b/vulnerabilities/migrations/0054_alter_packagechangelog_software_version_and_more.py @@ -0,0 +1,31 @@ +# Generated by Django 4.1.13 on 2024-01-09 17:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("vulnerabilities", "0053_alter_packagechangelog_software_version_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="packagechangelog", + name="software_version", + field=models.CharField( + default="34.0.0rc2", + help_text="Version of the software at the time of change", + max_length=100, + ), + ), + migrations.AlterField( + model_name="vulnerabilitychangelog", + name="software_version", + field=models.CharField( + default="34.0.0rc2", + help_text="Version of the software at the time of change", + max_length=100, + ), + ), + ] diff --git a/vulnerabilities/migrations/0055_alter_packagechangelog_software_version_and_more.py b/vulnerabilities/migrations/0055_alter_packagechangelog_software_version_and_more.py new file mode 100644 index 000000000..7e3095160 --- /dev/null +++ b/vulnerabilities/migrations/0055_alter_packagechangelog_software_version_and_more.py @@ -0,0 +1,31 @@ +# Generated by Django 4.1.13 on 2024-03-18 08:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("vulnerabilities", "0054_alter_packagechangelog_software_version_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="packagechangelog", + name="software_version", + field=models.CharField( + default="34.0.0rc3", + help_text="Version of the software at the time of change", + max_length=100, + ), + ), + migrations.AlterField( + model_name="vulnerabilitychangelog", + name="software_version", + field=models.CharField( + default="34.0.0rc3", + help_text="Version of the software at the time of change", + max_length=100, + ), + ), + ] diff --git a/vulnerabilities/migrations/0056_alter_packagechangelog_software_version_and_more.py b/vulnerabilities/migrations/0056_alter_packagechangelog_software_version_and_more.py new file mode 100644 index 000000000..906877fa1 --- /dev/null +++ b/vulnerabilities/migrations/0056_alter_packagechangelog_software_version_and_more.py @@ -0,0 +1,31 @@ +# Generated by Django 4.1.13 on 2024-03-18 08:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("vulnerabilities", "0055_alter_packagechangelog_software_version_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="packagechangelog", + name="software_version", + field=models.CharField( + default="34.0.0rc4", + help_text="Version of the software at the time of change", + max_length=100, + ), + ), + migrations.AlterField( + model_name="vulnerabilitychangelog", + name="software_version", + field=models.CharField( + default="34.0.0rc4", + help_text="Version of the software at the time of change", + max_length=100, + ), + ), + ] diff --git a/vulnerabilities/models.py b/vulnerabilities/models.py index 2ebf47f16..a0fd63a44 100644 --- a/vulnerabilities/models.py +++ b/vulnerabilities/models.py @@ -37,6 +37,8 @@ from rest_framework.authtoken.models import Token from univers import versions from univers.version_range import RANGE_CLASS_BY_SCHEMES +from univers.version_range import AlpineLinuxVersionRange +from univers.versions import Version from vulnerabilities import utils from vulnerabilities.severity_systems import SCORING_SYSTEMS @@ -255,6 +257,18 @@ def get_absolute_url(self): """ return reverse("vulnerability_details", args=[self.vulnerability_id]) + def get_details_url(self, request): + """ + Return this Package details URL. + """ + from rest_framework.reverse import reverse + + return reverse( + "vulnerability_details", + kwargs={"vulnerability_id": self.vulnerability_id}, + request=request, + ) + def get_related_cpes(self): """ Return a list of CPE strings of this vulnerability. @@ -631,6 +645,14 @@ def get_absolute_url(self): """ return reverse("package_details", args=[self.purl]) + def get_details_url(self, request): + """ + Return this Package details URL. + """ + from rest_framework.reverse import reverse + + return reverse("package_details", kwargs={"purl": self.purl}, request=request) + def sort_by_version(self, packages): """ Return a list of `packages` sorted by version. @@ -645,7 +667,11 @@ def sort_by_version(self, packages): @property def version_class(self): - return RANGE_CLASS_BY_SCHEMES[self.type].version_class + RANGE_CLASS_BY_SCHEMES["alpine"] = AlpineLinuxVersionRange + range_class = RANGE_CLASS_BY_SCHEMES.get(self.type) + if not range_class: + return Version + return range_class.version_class @property def current_version(self): diff --git a/vulnerabilities/severity_systems.py b/vulnerabilities/severity_systems.py index de0d45f69..6260750b2 100644 --- a/vulnerabilities/severity_systems.py +++ b/vulnerabilities/severity_systems.py @@ -37,6 +37,9 @@ def compute(self, scoring_elements: str) -> str: """ return NotImplementedError + def get(self, scoring_elements: str): + return NotImplementedError + @dataclasses.dataclass(order=True) class Cvssv2ScoringSystem(ScoringSystem): @@ -49,6 +52,10 @@ def compute(self, scoring_elements: str) -> str: """ return str(CVSS2(vector=scoring_elements).base_score) + def get(self, scoring_elements: str) -> dict: + scoring_elements = scoring_elements.strip() + return CVSS2(vector=scoring_elements).as_json() + CVSSV2 = Cvssv2ScoringSystem( identifier="cvssv2", @@ -71,6 +78,10 @@ def compute(self, scoring_elements: str) -> str: """ return str(CVSS3(vector=scoring_elements).base_score) + def get(self, scoring_elements: str) -> dict: + scoring_elements = scoring_elements.strip() + return CVSS3(vector=scoring_elements).as_json() + CVSSV3 = Cvssv3ScoringSystem( identifier="cvssv3", diff --git a/vulnerabilities/templates/package_details.html b/vulnerabilities/templates/package_details.html index ed10cd122..632790304 100644 --- a/vulnerabilities/templates/package_details.html +++ b/vulnerabilities/templates/package_details.html @@ -43,7 +43,7 @@
- {% if affected_by_vulnerabilities|length == 0 %} + {% if affected_by_vulnerabilities|length != 0 %}
{% else %}
@@ -65,7 +65,7 @@
- {% if affected_by_vulnerabilities|length == 0 %} + {% if affected_by_vulnerabilities|length != 0 %}
diff --git a/vulnerabilities/templates/vulnerability_details.html b/vulnerabilities/templates/vulnerability_details.html index a7c1a4d4f..4f16c32ff 100644 --- a/vulnerabilities/templates/vulnerability_details.html +++ b/vulnerabilities/templates/vulnerability_details.html @@ -2,6 +2,7 @@ {% load humanize %} {% load widget_tweaks %} {% load static %} +{% load show_cvss %} {% block title %} VulnerableCode Vulnerability Details - {{ vulnerability.vulnerability_id }} @@ -52,6 +53,13 @@ +
  • + + + Severities vectors ({{ severity_vectors|length }}) + + +
  • @@ -309,7 +317,63 @@
  • - +
    + {% for severity_vector in severity_vectors %} + {% if severity_vector.version == '2.0' %} + Vector: {{ severity_vector.vectorString }} + + + + + + + + + + + + + + + + + + + +
    Exploitability (E)Access Vector (AV)Access Complexity (AC)Authentication (Au)Confidentiality Impact (C)Integrity Impact (I)Availability Impact (A)
    {{ severity_vector.exploitability|cvss_printer:"high,functional,unproven,proof_of_concept,not_defined" }}{{ severity_vector.accessVector|cvss_printer:"local,adjacent_network,network" }}{{ severity_vector.accessComplexity|cvss_printer:"high,medium,low" }}{{ severity_vector.authentication|cvss_printer:"multiple,single,none" }}{{ severity_vector.confidentialityImpact|cvss_printer:"none,partial,complete" }}{{ severity_vector.integrityImpact|cvss_printer:"none,partial,complete" }}{{ severity_vector.availabilityImpact|cvss_printer:"none,partial,complete" }}
    + {% elif severity_vector.version == '3.1' or severity_vector.version == '3.0'%} + Vector: {{ severity_vector.vectorString }} + + + + + + + + + + + + + + + + + + + + + +
    Attack Vector (AV)Attack Complexity (AC)Privileges Required (PR)User Interaction (UI)Scope (S)Confidentiality Impact (C)Integrity Impact (I)Availability Impact (A)
    {{ severity_vector.attackVector|cvss_printer:"network,adjacent_network,local,physical"}}{{ severity_vector.attackComplexity|cvss_printer:"low,high" }}{{ severity_vector.privilegesRequired|cvss_printer:"none,low,high" }}{{ severity_vector.userInteraction|cvss_printer:"none,required"}}{{ severity_vector.scope|cvss_printer:"unchanged,changed" }}{{ severity_vector.confidentialityImpact|cvss_printer:"high,low,none" }}{{ severity_vector.integrityImpact|cvss_printer:"high,low,none" }}{{ severity_vector.availabilityImpact|cvss_printer:"high,low,none" }}
    + {% endif %} + {% empty %} + + + There are no known CVSS vectors. + + + {% endfor %} +
    diff --git a/vulnerabilities/templatetags/__init__.py b/vulnerabilities/templatetags/__init__.py new file mode 100644 index 000000000..bdac1cd30 --- /dev/null +++ b/vulnerabilities/templatetags/__init__.py @@ -0,0 +1,8 @@ +# +# 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. +# diff --git a/vulnerabilities/templatetags/show_cvss.py b/vulnerabilities/templatetags/show_cvss.py new file mode 100644 index 000000000..52533d0f2 --- /dev/null +++ b/vulnerabilities/templatetags/show_cvss.py @@ -0,0 +1,17 @@ +from django import template +from django.utils.safestring import mark_safe + +register = template.Library() + + +@register.filter(is_safe=True) +def cvss_printer(selected_vector, vector_values): + """highlight the selected vector value and return a list of paragraphs""" + p_list = [] + selected_vector = selected_vector.lower() + for vector_value in vector_values.split(","): + if selected_vector == vector_value: + p_list.append(f"

    {selected_vector}

    ") + else: + p_list.append(f"

    {vector_value}

    ") + return mark_safe("".join(p_list)) diff --git a/vulnerabilities/tests/conftest.py b/vulnerabilities/tests/conftest.py index 8076f4b9b..f9216c742 100644 --- a/vulnerabilities/tests/conftest.py +++ b/vulnerabilities/tests/conftest.py @@ -26,7 +26,6 @@ def no_rmtree(monkeypatch): # Step 3: Migrate all the tests collect_ignore = [ "test_models.py", - "test_ruby.py", "test_rust.py", "test_suse_backports.py", "test_suse.py", diff --git a/vulnerabilities/tests/test_api.py b/vulnerabilities/tests/test_api.py index f94fe80cb..197707a01 100644 --- a/vulnerabilities/tests/test_api.py +++ b/vulnerabilities/tests/test_api.py @@ -9,6 +9,7 @@ import json import os +from collections import OrderedDict from urllib.parse import quote from django.test import TestCase @@ -223,18 +224,21 @@ def test_api_with_single_vulnerability(self): "vulnerability_id": self.vulnerability.vulnerability_id, "summary": "test", "aliases": [], + "resource_url": f"http://testserver/vulnerabilities/{self.vulnerability.vulnerability_id}", "fixed_packages": [ { "url": f"http://testserver/api/packages/{self.pkg2.id}", "purl": "pkg:deb/flask@0.1.2", "is_vulnerable": False, "affected_by_vulnerabilities": [], + "resource_url": f"http://testserver/packages/{self.pkg2.purl}", }, { "url": f"http://testserver/api/packages/{self.pkg1.id}", "purl": "pkg:pypi/flask@0.1.2", "is_vulnerable": False, "affected_by_vulnerabilities": [], + "resource_url": f"http://testserver/packages/{self.pkg1.purl}", }, ], "affected_packages": [], @@ -257,11 +261,13 @@ def test_api_with_single_vulnerability_with_filters(self): "vulnerability_id": self.vulnerability.vulnerability_id, "summary": "test", "aliases": [], + "resource_url": f"http://testserver/vulnerabilities/{self.vulnerability.vulnerability_id}", "fixed_packages": [ { "url": f"http://testserver/api/packages/{self.pkg1.id}", "purl": "pkg:pypi/flask@0.1.2", "is_vulnerable": False, + "resource_url": f"http://testserver/packages/{self.pkg1.purl}", "affected_by_vulnerabilities": [], }, ], @@ -443,49 +449,106 @@ def test_api_with_lesser_and_greater_fixed_by_packages(self): "next_non_vulnerable_version": "2.14.0-rc1", "latest_non_vulnerable_version": "2.14.0-rc1", "affected_by_vulnerabilities": [ - { - "url": f"http://testserver/api/vulnerabilities/{self.vuln_VCID_2nyb_8rwu_aaag.id}", - "vulnerability_id": "VCID-2nyb-8rwu-aaag", - "summary": "This is VCID-2nyb-8rwu-aaag", - "references": [], - "fixed_packages": [ - { - "url": f"http://testserver/api/packages/{self.package_maven_jackson_databind_2_13_2.id}", - "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2", - "is_vulnerable": True, - "affected_by_vulnerabilities": [ - {"vulnerability": "VCID-gqhw-ngh8-aaap"} + OrderedDict( + [ + ( + "url", + f"http://testserver/api/vulnerabilities/{self.vuln_VCID_2nyb_8rwu_aaag.id}", + ), + ("vulnerability_id", "VCID-2nyb-8rwu-aaag"), + ("summary", "This is VCID-2nyb-8rwu-aaag"), + ("references", []), + ( + "fixed_packages", + [ + OrderedDict( + [ + ( + "url", + f"http://testserver/api/packages/{self.package_maven_jackson_databind_2_13_2.id}", + ), + ( + "purl", + "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2", + ), + ("is_vulnerable", True), + ( + "affected_by_vulnerabilities", + [{"vulnerability": "VCID-gqhw-ngh8-aaap"}], + ), + ( + "resource_url", + "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2", + ), + ] + ) ], - } - ], - "aliases": ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"], - } + ), + ("aliases", ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"]), + ("resource_url", "http://testserver/vulnerabilities/VCID-2nyb-8rwu-aaag"), + ] + ) ], "fixing_vulnerabilities": [ - { - "url": f"http://testserver/api/vulnerabilities/{self.vuln_VCID_ftmk_wbwx_aaar.id}", - "vulnerability_id": "VCID-ftmk-wbwx-aaar", - "summary": "This is VCID-ftmk-wbwx-aaar", - "references": [], - "fixed_packages": [ - { - "url": f"http://testserver/api/packages/{self.package_maven_jackson_databind_2_12_6.id}", - "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6", - "is_vulnerable": False, - "affected_by_vulnerabilities": [], - }, - { - "url": f"http://testserver/api/packages/{self.package_maven_jackson_databind_2_13_1.id}", - "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1", - "is_vulnerable": True, - "affected_by_vulnerabilities": [ - {"vulnerability": "VCID-2nyb-8rwu-aaag"} + OrderedDict( + [ + ( + "url", + f"http://testserver/api/vulnerabilities/{self.vuln_VCID_ftmk_wbwx_aaar.id}", + ), + ("vulnerability_id", "VCID-ftmk-wbwx-aaar"), + ("summary", "This is VCID-ftmk-wbwx-aaar"), + ("references", []), + ( + "fixed_packages", + [ + OrderedDict( + [ + ( + "url", + f"http://testserver/api/packages/{self.package_maven_jackson_databind_2_12_6.id}", + ), + ( + "purl", + "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6", + ), + ("is_vulnerable", False), + ("affected_by_vulnerabilities", []), + ( + "resource_url", + "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6", + ), + ] + ), + OrderedDict( + [ + ( + "url", + f"http://testserver/api/packages/{self.package_maven_jackson_databind_2_13_1.id}", + ), + ( + "purl", + "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1", + ), + ("is_vulnerable", True), + ( + "affected_by_vulnerabilities", + [{"vulnerability": "VCID-2nyb-8rwu-aaag"}], + ), + ( + "resource_url", + "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1", + ), + ] + ), ], - }, - ], - "aliases": ["CVE-2021-46877", "GHSA-3x8x-79m2-3w2w"], - }, + ), + ("aliases", ["CVE-2021-46877", "GHSA-3x8x-79m2-3w2w"]), + ("resource_url", "http://testserver/vulnerabilities/VCID-ftmk-wbwx-aaar"), + ] + ) ], + "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1", } assert response == expected_response diff --git a/vulnerabilities/tests/test_data/fireeye/fireeye_test3.md b/vulnerabilities/tests/test_data/fireeye/fireeye_test3.md new file mode 100644 index 000000000..b825b967b --- /dev/null +++ b/vulnerabilities/tests/test_data/fireeye/fireeye_test3.md @@ -0,0 +1,31 @@ +# MNDT-2023-0017 + +The IBM Personal Communications (PCOMM) application 13.0.0 and earlier caused a user's plaintext password to be written to the `C:\Temp\pcsnp_init.log` file when re-connection was made through a remote desktop protocol. + +## Common Weakness Enumeration +CWE-312: Cleartext Storage of Sensitive Information + +## Impact +High - An attacker with low-privilege access to a host with IBM PCOMM could recover the plaintext password of another user. + +## Exploitability +Low - Exploitability varies depending on the environment in which IBM PCOMM is installed. Mandiant identified this vulnerability when conducting independent security research for a client that used Citrix to connect to shared Windows Server instances. In certain environments where remote desktop is used to connect to shared hosts with IBM PCOMM installed, the exploitability is greatly increased. + +## CVE Reference +CVE-2016-0321 - scope expanded + +## Technical Details +While conducting independent security research, Mandiant identified a plaintext Active Directory password stored within the `C:\Temp\pcsnp_init.log` file. The affected host had IBM PCOMM version 13.0.0 installed and was used by multiple users who connected with Citrix. Upon a user connecting, disconnecting, and connecting again, the user's plaintext password was stored in the `C:\Temp\pcsnp_init.log` file. + +## Discovery Credits +- Adin Drabkin, Mandiant +- Matthew Rotlevi, Mandiant + +## Disclosure Timeline +- 2023-09-26 - Issue reported to the vendor. +- 2023-11-03 - The vendor updated the security bulletin for CVE-2016-0321 to include all known affected and fixed versions. + +## References +- [IBM Security Bulletin](https://www.ibm.com/support/pages/security-bulletin-ibm-personal-communications-could-allow-remote-user-obtain-sensitive-information-including-user-passwords-allowing-unauthorized-access-cve-2016-0321) +- [IBM Personal Communications](https://www.ibm.com/support/pages/ibm-personal-communications) +- [Mitre CVE-2016-0321](https://www.cve.org/CVERecord?id=CVE-2016-0321) diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_expected_1.json b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_1.json new file mode 100644 index 000000000..2182e5db4 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_1.json @@ -0,0 +1,122 @@ +{ + "aliases": [ + "CVE-2015-8315", + "GHSA-3fx5-fwvr-xrjg" + ], + "summary": "Regular Expression Denial of Service in ms\nVersions of `ms` prior to 0.7.1 are affected by a regular expression denial of service vulnerability when extremely long version strings are parsed.\n\n## Proof of Concept\n```javascript\nvar ms = require('ms');\nvar genstr = function (len, chr) {\n var result = \"\";\n for (i=0; i<=len; i++) {\n result = result + chr;\n }\n\n return result;\n}\n\nms(genstr(process.argv[2], \"5\") + \" minutea\");\n\n```\n\n### Results\nShowing increase in execution time based on the input string.\n```\n$ time node ms.js 10000\n\nreal\t0m0.758s\nuser\t0m0.724s\nsys\t0m0.031s\n\n$ time node ms.js 20000\n\nreal\t0m2.580s\nuser\t0m2.494s\nsys\t0m0.047s\n\n$ time node ms.js 30000\n\nreal\t0m5.747s\nuser\t0m5.483s\nsys\t0m0.080s\n\n$ time node ms.js 80000\n\nreal\t0m41.022s\nuser\t0m38.894s\nsys\t0m0.529s\n```", + "affected_packages": [ + { + "package": { + "type": "npm", + "namespace": null, + "name": "ms", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "0.7.1" + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2015-8315", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/unshiftio/millisecond/", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://support.f5.com/csp/article/K46337613?utm_source=f5support&utm_medium=RSS", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://www.npmjs.com/advisories/46", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://www.openwall.com/lists/oss-security/2016/04/20/11", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://www.securityfocus.com/bid/96389", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2017-10-24T18:33:36+00:00", + "weaknesses": [400], + "url": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/github_osv_test_1.json" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_expected_2.json b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_2.json new file mode 100644 index 000000000..8a23cff27 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_2.json @@ -0,0 +1,79 @@ +{ + "aliases": [ + "CVE-2022-1036", + "GHSA-3qr6-qrqm-8v86" + ], + "summary": "Integer Overflow or Wraparound in Microweber\nIn Microweber prior to 1.2.12, a user can create an account with a password thousands of characters in length, leading to memory corruption/integer overflow. Version 1.2.2 sets maximum password length at 500 characters.", + "affected_packages": [ + + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-1036", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/microweber/microweber/commit/82be4f0b4729be870ccefdae99a04833f134aa6a", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/microweber/microweber", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://huntr.dev/bounties/db615581-d5a9-4ca5-a3e9-7a39eceaa424", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2022-03-23T00:00:23+00:00", + "weaknesses": [190], + "url": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/github_osv_test_2.json" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_expected_3.json b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_3.json new file mode 100644 index 000000000..7bfc88189 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_3.json @@ -0,0 +1,142 @@ +{ + "aliases": [ + "CVE-2020-10688", + "GHSA-29qj-rvv6-qrmv" + ], + "summary": "Cross-site scripting in RESTEasy\nA cross-site scripting (XSS) flaw was found in RESTEasy in versions before 3.11.1.Final and before 4.5.3.Final, where it did not properly handle URL encoding when the RESTEASY003870 exception occurs. An attacker could use this flaw to launch a reflected XSS attack.", + "affected_packages": [ + { + "package": { + "type": "maven", + "namespace": "org.jboss.resteasy", + "name": "resteasy-bom", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "3.11.1.Final" + }, + { + "package": { + "type": "maven", + "namespace": "org.jboss.resteasy", + "name": "resteasy-bom", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "4.5.3.Final" + }, + { + "package": { + "type": "maven", + "namespace": "org.jboss.resteasy", + "name": "resteasy-core", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "3.11.1.Final" + }, + { + "package": { + "type": "maven", + "namespace": "org.jboss.resteasy", + "name": "resteasy-core", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "4.5.3.Final" + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-10688", + "severities": [ + { + "system": "cvssv3.1", + "value": "5.4", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/quarkusio/quarkus/issues/7248", + "severities": [ + { + "system": "cvssv3.1", + "value": "5.4", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://bugzilla.redhat.com/show_bug.cgi?id=1814974", + "severities": [ + { + "system": "cvssv3.1", + "value": "5.4", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://issues.redhat.com/browse/RESTEASY-2519", + "severities": [ + { + "system": "cvssv3.1", + "value": "5.4", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://security.netapp.com/advisory/ntap-20210706-0008/", + "severities": [ + { + "system": "cvssv3.1", + "value": "5.4", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2021-06-15T16:05:22+00:00", + "weaknesses": [79], + "url": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/github_osv_test_3.json" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_expected_4.json b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_4.json new file mode 100644 index 000000000..a58d9cb0b --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_4.json @@ -0,0 +1,194 @@ +{ + "aliases": [ + "CVE-2011-0447", + "GHSA-24fg-p96v-hxh8" + ], + "summary": "Moderate severity vulnerability that affects rails\nRuby on Rails 2.1.x, 2.2.x, and 2.3.x before 2.3.11, and 3.x before 3.0.4, does not properly validate HTTP requests that contain an X-Requested-With header, which makes it easier for remote attackers to conduct cross-site request forgery (CSRF) attacks via forged (1) AJAX or (2) API requests that leverage \"combinations of browser plugins and HTTP redirects,\" a related issue to CVE-2011-0696.", + "affected_packages": [ + { + "package": { + "type": "gem", + "namespace": null, + "name": "rails", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "2.3.11" + }, + { + "package": { + "type": "gem", + "namespace": null, + "name": "rails", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "3.0.4" + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2011-0447", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/advisories/GHSA-24fg-p96v-hxh8", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://groups.google.com/group/rubyonrails-security/msg/c22ea1668c0d181c?dmode=source&output=gplain", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://lists.fedoraproject.org/pipermail/package-announce/2011-April/057650.html", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://lists.fedoraproject.org/pipermail/package-announce/2011-March/055074.html", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://lists.fedoraproject.org/pipermail/package-announce/2011-March/055088.html", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://secunia.com/advisories/43274", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://secunia.com/advisories/43666", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://www.debian.org/security/2011/dsa-2247", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://www.securityfocus.com/bid/46291", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://www.securitytracker.com/id?1025060", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://www.vupen.com/english/advisories/2011/0587", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://www.vupen.com/english/advisories/2011/0877", + "severities": [ + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2017-10-24T18:33:38+00:00", + "weaknesses": [ + 352 + ], + "url": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/github_osv_test_4.json" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_expected_5.json b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_5.json new file mode 100644 index 000000000..41718243e --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_5.json @@ -0,0 +1,224 @@ +{ + "aliases": [ + "CVE-2023-2727", + "GHSA-qc2g-gmh6-95p4" + ], + "summary": "kube-apiserver vulnerable to policy bypass\nUsers may be able to launch containers using images that are restricted by ImagePolicyWebhook when using ephemeral containers. Kubernetes clusters are only affected if the ImagePolicyWebhook admission plugin is used together with ephemeral containers.", + "affected_packages": [ + { + "package": { + "type": "golang", + "namespace": "k8s.io", + "name": "kubernetes", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "1.27.3" + }, + { + "package": { + "type": "golang", + "namespace": "k8s.io", + "name": "kubernetes", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "1.26.6" + }, + { + "package": { + "type": "golang", + "namespace": "k8s.io", + "name": "kubernetes", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "1.25.11" + }, + { + "package": { + "type": "golang", + "namespace": "k8s.io", + "name": "kubernetes", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "1.24.15" + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-2727", + "severities": [ + { + "system": "cvssv3.1", + "value": "6.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/kubernetes/kubernetes/issues/118640", + "severities": [ + { + "system": "cvssv3.1", + "value": "6.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/kubernetes/kubernetes/pull/118356", + "severities": [ + { + "system": "cvssv3.1", + "value": "6.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/kubernetes/kubernetes/pull/118471", + "severities": [ + { + "system": "cvssv3.1", + "value": "6.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/kubernetes/kubernetes/pull/118473", + "severities": [ + { + "system": "cvssv3.1", + "value": "6.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/kubernetes/kubernetes/pull/118474", + "severities": [ + { + "system": "cvssv3.1", + "value": "6.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/kubernetes/kubernetes/pull/118512", + "severities": [ + { + "system": "cvssv3.1", + "value": "6.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/kubernetes/kubernetes", + "severities": [ + { + "system": "cvssv3.1", + "value": "6.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://groups.google.com/g/kubernetes-security-announce/c/vPWYJ_L84m8", + "severities": [ + { + "system": "cvssv3.1", + "value": "6.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://www.openwall.com/lists/oss-security/2023/07/06/2", + "severities": [ + { + "system": "cvssv3.1", + "value": "6.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2023-07-03T21:30:57+00:00", + "weaknesses": [ + 20 + ], + "url": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/github_osv_test_5.json" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_expected_6.json b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_6.json new file mode 100644 index 000000000..a2aa7984c --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_6.json @@ -0,0 +1,91 @@ +{ + "aliases": [ + "GHSA-pffg-92cg-xf5c" + ], + "summary": "gnark-crypto's exponentiation in the pairing target group GT using GLV can give incorrect results\n### Impact\nWhen the exponent is bigger than `r`, the group order of the pairing target group `GT`, the exponentiation à la GLV (`ExpGLV`) can *sometimes* give incorrect results compared to normal exponentiation (`Exp`). \n\nThe issue impacts all users using `ExpGLV` for exponentiations in `GT`. This does not impact `Exp` and `ExpCyclotomic` which are sound. Also note that GLV methods in G1 and G2 are sound and _not_ impacted.\n\n### Patches\nFix has been implemented in pull request https://github.com/Consensys/gnark-crypto/pull/451 and merged in commit https://github.com/Consensys/gnark-crypto/commit/ec6be1a037f7c496d595c541a8a8d31c47bcfa3d to master branch.\n\nThe fix increased the bounds of the sub-scalars by 1. In fact, since https://github.com/Consensys/gnark-crypto/pull/213, we use a fast scalar decomposition that tradeoffs divisions (needed in the Babai rounding) by right-shifts. We precompute `b=2^m*v/d (m > log2(d))` and then at runtime compute `scalar*b/2^m` (`v` is a lattice vector and `d` the lattice determinant). This increases the bounds on sub-scalars by 1 which we check at runtime before increasing the loop size (we don't target constant-timeness). `m` is chosen to be a machine word twice big than `log2(d)` so that we rarely need to increase the loop size. Hence why the issue happens only *sometimes* if we omit to increase the bounds. This bounds increase was implemented in G1 and G2 but forgot in GT.\n\n### Workarounds\nUpdating to `v0.12.1+`. Alternatively, use `Exp` or `ExpCyclotomic` instead. We are not aware of any users using `ExpGLV` anyway.\n\n### References\n- Fix PR: https://github.com/Consensys/gnark-crypto/pull/451 \n- Fast scalar decomposition PR: https://github.com/Consensys/gnark-crypto/pull/213\n- https://eprint.iacr.org/2015/565 Sec.4.2\n\n### Acknowledgement\nThe vulnerability was reported by [Antonio Sanso](https://github.com/asanso) @ [EF](https://crypto.ethereum.org/).", + "affected_packages": [ + { + "package": { + "type": "golang", + "namespace": "github.com/consensys", + "name": "gnark-crypto", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "0.12.1" + } + ], + "references": [ + { + "reference_id": "", + "url": "https://github.com/Consensys/gnark-crypto/security/advisories/GHSA-pffg-92cg-xf5c", + "severities": [ + { + "system": "generic_textual", + "value": "LOW", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/Consensys/gnark-crypto/pull/213", + "severities": [ + { + "system": "generic_textual", + "value": "LOW", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/Consensys/gnark-crypto/pull/451", + "severities": [ + { + "system": "generic_textual", + "value": "LOW", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/Consensys/gnark-crypto/commit/ec6be1a037f7c496d595c541a8a8d31c47bcfa3d", + "severities": [ + { + "system": "generic_textual", + "value": "LOW", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://eprint.iacr.org/2015/565", + "severities": [ + { + "system": "generic_textual", + "value": "LOW", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/Consensys/gnark-crypto", + "severities": [ + { + "system": "generic_textual", + "value": "LOW", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2023-10-05T20:57:20+00:00", + "weaknesses": [], + "url": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/github_osv_test_6.json" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_expected_7.json b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_7.json new file mode 100644 index 000000000..c38362a20 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_7.json @@ -0,0 +1,150 @@ +{ + "aliases": [ + "CVE-2023-36796", + "GHSA-h7jm-g87p-5935" + ], + "summary": "# Microsoft Security Advisory CVE-2023-36796: .NET Remote Code Execution Vulnerability\n\n## Executive summary\n\nMicrosoft is releasing this security advisory to provide information about a vulnerability in .NET 7.0 and .NET 6.0. This advisory also provides guidance on what developers can do to update their applications to remove this vulnerability.\n\nA vulnerability exists in Microsoft.DiaSymReader.Native.amd64.dll when reading a corrupted PDB file which may lead to remote code execution. This issue only affects Windows systems.\n\n**Note:** The vulnerabilities [CVE-2023-36792]( https://www.cve.org/CVERecord?id=CVE-2023-36792), [CVE-2023-36793]( https://www.cve.org/CVERecord?id=CVE-2023-36793), [CVE-2023-36792]( https://www.cve.org/CVERecord?id=CVE-2023-36794), [CVE-2023-36796]( https://www.cve.org/CVERecord?id=CVE-2023-36796) are all resolved by a single patch. Get [affected software](#affected-software) to resolve all of them.\n\n## Discussion\n\nDiscussion for this issue can be found at https://github.com/dotnet/runtime/issues/91948\n\n### Mitigation factors\n\nMicrosoft has not identified any mitigating factors for this vulnerability.\n\n## Affected software\n\n* Any .NET 7.0 application running on .NET 7.0.10 or earlier.\n* Any .NET 6.0 application running on .NET 6.0.21 or earlier.\n\nIf your application uses the following package versions, ensure you update to the latest version of .NET.\n\n### .NET 7\n\nPackage name | Affected version | Patched version\n------------ | ---------------- | -------------------------\n[Microsoft.NETCore.App.Runtime.win-arm64](https://www.nuget.org/packages/Microsoft.NETCore.App.Runtime.win-arm64) | >= 7.0.0, <= 7.0.10 | 7.0.11\n[Microsoft.NETCore.App.Runtime.win-x64](https://www.nuget.org/packages/Microsoft.NETCore.App.Runtime.win-x64) | >= 7.0.0, <= 7.0.10 | 7.0.11\n[Microsoft.NETCore.App.Runtime.win-x86](https://www.nuget.org/packages/Microsoft.NETCore.App.Runtime.win-x86) | >= 7.0.0, <= 7.0.10 | 7.0.11\n\n### .NET 6\n\nPackage name | Affected version | Patched version\n------------ | ---------------- | -------------------------\n[Microsoft.NETCore.App.Runtime.win-arm64](https://www.nuget.org/packages/Microsoft.NETCore.App.Runtime.win-arm64) | >= 6.0.0, <= 6.0.21 | 6.0.22\n[Microsoft.NETCore.App.Runtime.win-x64](https://www.nuget.org/packages/Microsoft.NETCore.App.Runtime.win-x64) | >= 6.0.0, <= 6.0.21 | 6.0.22\n[Microsoft.NETCore.App.Runtime.win-x86](https://www.nuget.org/packages/Microsoft.NETCore.App.Runtime.win-x86) | >= 6.0.0, <= 6.0.21 | 6.0.22\n\n\n## Advisory FAQ\n\n### How do I know if I am affected?\n\nIf you have a runtime or SDK with a version listed, or an affected package listed in [affected software](#affected-software), you're exposed to the vulnerability.\n\n### How do I fix the issue?\n\n* To fix the issue please install the latest version of .NET 6.0 or .NET 7.0. If you have installed one or more .NET SDKs through Visual Studio, Visual Studio will prompt you to update Visual Studio, which will also update your .NET SDKs.\n* If you are using one of the affected packages, please update to the patched version listed above.\n* If you have .NET 6.0 or greater installed, you can list the versions you have installed by running the `dotnet --info` command. You will see output like the following;\n\n```\n.NET Core SDK (reflecting any global.json):\n\n Version: 6.0.300\n Commit: 8473146e7d\n\nRuntime Environment:\n\n OS Name: Windows\n OS Version: 10.0.18363\n OS Platform: Windows\n RID: win10-x64\n Base Path: C:\\Program Files\\dotnet\\sdk\\6.0.300\\\n\nHost (useful for support):\n\n Version: 6.0.5\n Commit: 8473146e7d\n\n.NET Core SDKs installed:\n\n 6.0.300 [C:\\Program Files\\dotnet\\sdk]\n\n.NET Core runtimes installed:\n\n Microsoft.AspNetCore.App 6.0.5 [C:\\Program Files\\dotnet\\shared\\Microsoft.AspNetCore.App]\n Microsoft.NETCore.App 6.0.5 [C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App]\n Microsoft.WindowsDesktop.App 6.0.5 [C:\\Program Files\\dotnet\\shared\\Microsoft.WindowsDesktop.App]\n\nTo install additional .NET Core runtimes or SDKs:\n https://aka.ms/dotnet-download\n```\n\n* If you're using .NET 7.0, you should download and install Runtime 7.0.11 or SDK 7.0.111 (for Visual Studio 2022 v17.4) from https://dotnet.microsoft.com/download/dotnet-core/7.0.\n* If you're using .NET 6.0, you should download and install Runtime 6.0.22 or SDK 6.0.317 (for Visual Studio 2022 v17.2) from https://dotnet.microsoft.com/download/dotnet-core/6.0.\n\n.NET 6.0 and and .NET 7.0 updates are also available from Microsoft Update. To access this either type \"Check for updates\" in your Windows search, or open Settings, choose Update & Security and then click Check for Updates.\n\nOnce you have installed the updated runtime or SDK, restart your apps for the update to take effect.\n\nAdditionally, if you've deployed [self-contained applications](https://docs.microsoft.com/dotnet/core/deploying/#self-contained-deployments-scd) targeting any of the impacted versions, these applications are also vulnerable and must be recompiled and redeployed.\n\n## Other Information\n\n### Reporting Security Issues\n\nIf you have found a potential security issue in .NET 6.0 or .NET 7.0, please email details to secure@microsoft.com. Reports may qualify for the Microsoft .NET Core & .NET 5 Bounty. Details of the Microsoft .NET Bounty Program including terms and conditions are at .\n\n### Support\n\nYou can ask questions about this issue on GitHub in the .NET GitHub organization. The main repos are located at https://github.com/dotnet/runtime and https://github.com/dotnet/aspnet/. The Announcements repo (https://github.com/dotnet/Announcements) will contain this bulletin as an issue and will include a link to a discussion issue. You can ask questions in the linked discussion issue.\n\n### Disclaimer\n\nThe information provided in this advisory is provided \"as is\" without warranty of any kind. Microsoft disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall Microsoft Corporation or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if Microsoft Corporation or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion or limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.\n\n### External Links\n\n[CVE-2023-36796]( https://www.cve.org/CVERecord?id=CVE-2023-36796)\n\n### Revisions\n\nV1.0 (September 12, 2023): Advisory published.\n\n_Version 1.0_\n\n_Last Updated 2023-09-12_", + "affected_packages": [ + { + "package": { + "type": "nuget", + "namespace": "", + "name": "Microsoft.NETCore.App.Runtime.win-arm64", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": null, + "fixed_version": "7.0.11" + }, + { + "package": { + "type": "nuget", + "namespace": "", + "name": "Microsoft.NETCore.App.Runtime.win-arm64", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": null, + "fixed_version": "6.0.22" + }, + { + "package": { + "type": "nuget", + "namespace": "", + "name": "Microsoft.NETCore.App.Runtime.win-x64", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": null, + "fixed_version": "7.0.11" + }, + { + "package": { + "type": "nuget", + "namespace": "", + "name": "Microsoft.NETCore.App.Runtime.win-x64", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": null, + "fixed_version": "6.0.22" + }, + { + "package": { + "type": "nuget", + "namespace": "", + "name": "Microsoft.NETCore.App.Runtime.win-x86", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": null, + "fixed_version": "6.0.22" + }, + { + "package": { + "type": "nuget", + "namespace": "", + "name": "Microsoft.NETCore.App.Runtime.win-x86", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": null, + "fixed_version": "7.0.11" + } + ], + "references": [ + { + "reference_id": "", + "url": "https://github.com/dotnet/runtime/security/advisories/GHSA-h7jm-g87p-5935", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.8", + "scoring_elements": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-36796", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.8", + "scoring_elements": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/dotnet/runtime", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.8", + "scoring_elements": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-36796", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.8", + "scoring_elements": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2023-09-12T20:05:18+00:00", + "weaknesses": [], + "url": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/github_osv_test_7.json" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_test_1.json b/vulnerabilities/tests/test_data/github_osv/github_osv_test_1.json new file mode 100644 index 000000000..02583ee61 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_test_1.json @@ -0,0 +1,71 @@ +{ + "schema_version": "1.3.0", + "id": "GHSA-3fx5-fwvr-xrjg", + "modified": "2021-09-22T20:19:52Z", + "published": "2017-10-24T18:33:36Z", + "aliases": [ + "CVE-2015-8315" + ], + "summary": "Regular Expression Denial of Service in ms", + "details": "Versions of `ms` prior to 0.7.1 are affected by a regular expression denial of service vulnerability when extremely long version strings are parsed.\n\n## Proof of Concept\n```javascript\nvar ms = require('ms');\nvar genstr = function (len, chr) {\n var result = \"\";\n for (i=0; i<=len; i++) {\n result = result + chr;\n }\n\n return result;\n}\n\nms(genstr(process.argv[2], \"5\") + \" minutea\");\n\n```\n\n### Results\nShowing increase in execution time based on the input string.\n```\n$ time node ms.js 10000\n\nreal\t0m0.758s\nuser\t0m0.724s\nsys\t0m0.031s\n\n$ time node ms.js 20000\n\nreal\t0m2.580s\nuser\t0m2.494s\nsys\t0m0.047s\n\n$ time node ms.js 30000\n\nreal\t0m5.747s\nuser\t0m5.483s\nsys\t0m0.080s\n\n$ time node ms.js 80000\n\nreal\t0m41.022s\nuser\t0m38.894s\nsys\t0m0.529s\n```\n", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + } + ], + "affected": [ + { + "package": { + "ecosystem": "npm", + "name": "ms" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.7.1" + } + ] + } + ] + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2015-8315" + }, + { + "type": "PACKAGE", + "url": "https://github.com/unshiftio/millisecond/" + }, + { + "type": "WEB", + "url": "https://support.f5.com/csp/article/K46337613?utm_source=f5support&utm_medium=RSS" + }, + { + "type": "WEB", + "url": "https://www.npmjs.com/advisories/46" + }, + { + "type": "WEB", + "url": "http://www.openwall.com/lists/oss-security/2016/04/20/11" + }, + { + "type": "WEB", + "url": "http://www.securityfocus.com/bid/96389" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-400" + ], + "severity": "HIGH", + "github_reviewed": true + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_test_2.json b/vulnerabilities/tests/test_data/github_osv/github_osv_test_2.json new file mode 100644 index 000000000..fef8553f2 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_test_2.json @@ -0,0 +1,63 @@ +{ + "schema_version": "1.3.0", + "id": "GHSA-3qr6-qrqm-8v86", + "modified": "2022-03-30T20:02:35Z", + "published": "2022-03-23T00:00:23Z", + "aliases": [ + "CVE-2022-1036" + ], + "summary": "Integer Overflow or Wraparound in Microweber", + "details": "In Microweber prior to 1.2.12, a user can create an account with a password thousands of characters in length, leading to memory corruption/integer overflow. Version 1.2.2 sets maximum password length at 500 characters.", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + } + ], + "affected": [ + { + "package": { + "ecosystem": "Packagist", + "name": "microweber/microweber" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.2.12" + } + ] + } + ] + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-1036" + }, + { + "type": "WEB", + "url": "https://github.com/microweber/microweber/commit/82be4f0b4729be870ccefdae99a04833f134aa6a" + }, + { + "type": "PACKAGE", + "url": "https://github.com/microweber/microweber" + }, + { + "type": "WEB", + "url": "https://huntr.dev/bounties/db615581-d5a9-4ca5-a3e9-7a39eceaa424" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-190" + ], + "severity": "HIGH", + "github_reviewed": true + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_test_3.json b/vulnerabilities/tests/test_data/github_osv/github_osv_test_3.json new file mode 100644 index 000000000..f8a3f0f44 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_test_3.json @@ -0,0 +1,136 @@ +{ + "schema_version": "1.3.0", + "id": "GHSA-29qj-rvv6-qrmv", + "modified": "2021-06-01T20:09:20Z", + "published": "2021-06-15T16:05:22Z", + "aliases": [ + "CVE-2020-10688" + ], + "summary": "Cross-site scripting in RESTEasy", + "details": "A cross-site scripting (XSS) flaw was found in RESTEasy in versions before 3.11.1.Final and before 4.5.3.Final, where it did not properly handle URL encoding when the RESTEASY003870 exception occurs. An attacker could use this flaw to launch a reflected XSS attack.", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N" + } + ], + "affected": [ + { + "package": { + "ecosystem": "Maven", + "name": "org.jboss.resteasy:resteasy-bom" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "3.11.1.Final" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 3.11.0.Final" + } + }, + { + "package": { + "ecosystem": "Maven", + "name": "org.jboss.resteasy:resteasy-bom" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "4.0.0" + }, + { + "fixed": "4.5.3.Final" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 4.5.2.Final" + } + }, + { + "package": { + "ecosystem": "Maven", + "name": "org.jboss.resteasy:resteasy-core" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "3.11.1.Final" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 3.11.0.Final" + } + }, + { + "package": { + "ecosystem": "Maven", + "name": "org.jboss.resteasy:resteasy-core" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "4.0.0" + }, + { + "fixed": "4.5.3.Final" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 4.5.2.Final" + } + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-10688" + }, + { + "type": "WEB", + "url": "https://github.com/quarkusio/quarkus/issues/7248" + }, + { + "type": "WEB", + "url": "https://bugzilla.redhat.com/show_bug.cgi?id=1814974" + }, + { + "type": "WEB", + "url": "https://issues.redhat.com/browse/RESTEASY-2519" + }, + { + "type": "WEB", + "url": "https://security.netapp.com/advisory/ntap-20210706-0008/" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-79" + ], + "severity": "MODERATE", + "github_reviewed": true + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_test_4.json b/vulnerabilities/tests/test_data/github_osv/github_osv_test_4.json new file mode 100644 index 000000000..9ce660a2b --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_test_4.json @@ -0,0 +1,119 @@ +{ + "schema_version": "1.3.0", + "id": "GHSA-24fg-p96v-hxh8", + "modified": "2020-06-16T20:51:22Z", + "published": "2017-10-24T18:33:38Z", + "aliases": [ + "CVE-2011-0447" + ], + "summary": "Moderate severity vulnerability that affects rails", + "details": "Ruby on Rails 2.1.x, 2.2.x, and 2.3.x before 2.3.11, and 3.x before 3.0.4, does not properly validate HTTP requests that contain an X-Requested-With header, which makes it easier for remote attackers to conduct cross-site request forgery (CSRF) attacks via forged (1) AJAX or (2) API requests that leverage \"combinations of browser plugins and HTTP redirects,\" a related issue to CVE-2011-0696.", + "severity": [ + + ], + "affected": [ + { + "package": { + "ecosystem": "RubyGems", + "name": "rails" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "2.1.0" + }, + { + "fixed": "2.3.11" + } + ] + } + ] + }, + { + "package": { + "ecosystem": "RubyGems", + "name": "rails" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "3.0.0" + }, + { + "fixed": "3.0.4" + } + ] + } + ] + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2011-0447" + }, + { + "type": "ADVISORY", + "url": "https://github.com/advisories/GHSA-24fg-p96v-hxh8" + }, + { + "type": "WEB", + "url": "http://groups.google.com/group/rubyonrails-security/msg/c22ea1668c0d181c?dmode=source&output=gplain" + }, + { + "type": "WEB", + "url": "http://lists.fedoraproject.org/pipermail/package-announce/2011-April/057650.html" + }, + { + "type": "WEB", + "url": "http://lists.fedoraproject.org/pipermail/package-announce/2011-March/055074.html" + }, + { + "type": "WEB", + "url": "http://lists.fedoraproject.org/pipermail/package-announce/2011-March/055088.html" + }, + { + "type": "WEB", + "url": "http://secunia.com/advisories/43274" + }, + { + "type": "WEB", + "url": "http://secunia.com/advisories/43666" + }, + { + "type": "WEB", + "url": "http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails" + }, + { + "type": "WEB", + "url": "http://www.debian.org/security/2011/dsa-2247" + }, + { + "type": "WEB", + "url": "http://www.securityfocus.com/bid/46291" + }, + { + "type": "WEB", + "url": "http://www.securitytracker.com/id?1025060" + }, + { + "type": "WEB", + "url": "http://www.vupen.com/english/advisories/2011/0587" + }, + { + "type": "WEB", + "url": "http://www.vupen.com/english/advisories/2011/0877" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-352" + ], + "severity": "MODERATE", + "github_reviewed": true + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_test_5.json b/vulnerabilities/tests/test_data/github_osv/github_osv_test_5.json new file mode 100644 index 000000000..f4e715047 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_test_5.json @@ -0,0 +1,146 @@ +{ + "schema_version": "1.4.0", + "id": "GHSA-qc2g-gmh6-95p4", + "modified": "2023-07-05T22:46:57Z", + "published": "2023-07-03T21:30:57Z", + "aliases": [ + "CVE-2023-2727" + ], + "summary": "kube-apiserver vulnerable to policy bypass", + "details": "Users may be able to launch containers using images that are restricted by ImagePolicyWebhook when using ephemeral containers. Kubernetes clusters are only affected if the ImagePolicyWebhook admission plugin is used together with ephemeral containers.\n\n", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N" + } + ], + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "k8s.io/kubernetes" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "1.27.0" + }, + { + "fixed": "1.27.3" + } + ] + } + ] + }, + { + "package": { + "ecosystem": "Go", + "name": "k8s.io/kubernetes" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "1.26.0" + }, + { + "fixed": "1.26.6" + } + ] + } + ] + }, + { + "package": { + "ecosystem": "Go", + "name": "k8s.io/kubernetes" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "1.25.0" + }, + { + "fixed": "1.25.11" + } + ] + } + ] + }, + { + "package": { + "ecosystem": "Go", + "name": "k8s.io/kubernetes" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.24.15" + } + ] + } + ] + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-2727" + }, + { + "type": "WEB", + "url": "https://github.com/kubernetes/kubernetes/issues/118640" + }, + { + "type": "WEB", + "url": "https://github.com/kubernetes/kubernetes/pull/118356" + }, + { + "type": "WEB", + "url": "https://github.com/kubernetes/kubernetes/pull/118471" + }, + { + "type": "WEB", + "url": "https://github.com/kubernetes/kubernetes/pull/118473" + }, + { + "type": "WEB", + "url": "https://github.com/kubernetes/kubernetes/pull/118474" + }, + { + "type": "WEB", + "url": "https://github.com/kubernetes/kubernetes/pull/118512" + }, + { + "type": "PACKAGE", + "url": "https://github.com/kubernetes/kubernetes" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/kubernetes-security-announce/c/vPWYJ_L84m8" + }, + { + "type": "WEB", + "url": "http://www.openwall.com/lists/oss-security/2023/07/06/2" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-20" + ], + "severity": "MODERATE", + "github_reviewed": true, + "github_reviewed_at": "2023-07-05T22:46:57Z", + "nvd_published_at": "2023-07-03T21:15:09Z" + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_test_6.json b/vulnerabilities/tests/test_data/github_osv/github_osv_test_6.json new file mode 100644 index 000000000..f9e97c05f --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_test_6.json @@ -0,0 +1,73 @@ +{ + "schema_version": "1.4.0", + "id": "GHSA-pffg-92cg-xf5c", + "modified": "2023-11-08T18:36:33Z", + "published": "2023-10-05T20:57:20Z", + "aliases": [ + + ], + "summary": "gnark-crypto's exponentiation in the pairing target group GT using GLV can give incorrect results", + "details": "### Impact\nWhen the exponent is bigger than `r`, the group order of the pairing target group `GT`, the exponentiation à la GLV (`ExpGLV`) can *sometimes* give incorrect results compared to normal exponentiation (`Exp`). \n\nThe issue impacts all users using `ExpGLV` for exponentiations in `GT`. This does not impact `Exp` and `ExpCyclotomic` which are sound. Also note that GLV methods in G1 and G2 are sound and _not_ impacted.\n\n### Patches\nFix has been implemented in pull request https://github.com/Consensys/gnark-crypto/pull/451 and merged in commit https://github.com/Consensys/gnark-crypto/commit/ec6be1a037f7c496d595c541a8a8d31c47bcfa3d to master branch.\n\nThe fix increased the bounds of the sub-scalars by 1. In fact, since https://github.com/Consensys/gnark-crypto/pull/213, we use a fast scalar decomposition that tradeoffs divisions (needed in the Babai rounding) by right-shifts. We precompute `b=2^m*v/d (m > log2(d))` and then at runtime compute `scalar*b/2^m` (`v` is a lattice vector and `d` the lattice determinant). This increases the bounds on sub-scalars by 1 which we check at runtime before increasing the loop size (we don't target constant-timeness). `m` is chosen to be a machine word twice big than `log2(d)` so that we rarely need to increase the loop size. Hence why the issue happens only *sometimes* if we omit to increase the bounds. This bounds increase was implemented in G1 and G2 but forgot in GT.\n\n### Workarounds\nUpdating to `v0.12.1+`. Alternatively, use `Exp` or `ExpCyclotomic` instead. We are not aware of any users using `ExpGLV` anyway.\n\n### References\n- Fix PR: https://github.com/Consensys/gnark-crypto/pull/451 \n- Fast scalar decomposition PR: https://github.com/Consensys/gnark-crypto/pull/213\n- https://eprint.iacr.org/2015/565 Sec.4.2\n\n### Acknowledgement\nThe vulnerability was reported by [Antonio Sanso](https://github.com/asanso) @ [EF](https://crypto.ethereum.org/).\n", + "severity": [ + + ], + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "github.com/consensys/gnark-crypto" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.12.1" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 0.12.0" + } + } + ], + "references": [ + { + "type": "WEB", + "url": "https://github.com/Consensys/gnark-crypto/security/advisories/GHSA-pffg-92cg-xf5c" + }, + { + "type": "WEB", + "url": "https://github.com/Consensys/gnark-crypto/pull/213" + }, + { + "type": "WEB", + "url": "https://github.com/Consensys/gnark-crypto/pull/451" + }, + { + "type": "WEB", + "url": "https://github.com/Consensys/gnark-crypto/commit/ec6be1a037f7c496d595c541a8a8d31c47bcfa3d" + }, + { + "type": "WEB", + "url": "https://eprint.iacr.org/2015/565" + }, + { + "type": "PACKAGE", + "url": "https://github.com/Consensys/gnark-crypto" + } + ], + "database_specific": { + "cwe_ids": [ + + ], + "severity": "LOW", + "github_reviewed": true, + "github_reviewed_at": "2023-10-05T20:57:20Z", + "nvd_published_at": null + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_test_7.json b/vulnerabilities/tests/test_data/github_osv/github_osv_test_7.json new file mode 100644 index 000000000..b7bac4bf0 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_test_7.json @@ -0,0 +1,178 @@ +{ + "schema_version": "1.4.0", + "id": "GHSA-h7jm-g87p-5935", + "modified": "2023-09-12T20:05:18Z", + "published": "2023-09-12T20:05:18Z", + "aliases": [ + "CVE-2023-36796" + ], + "summary": "Microsoft Security Advisory CVE-2023-36796: .NET Remote Code Execution Vulnerability", + "details": "# Microsoft Security Advisory CVE-2023-36796: .NET Remote Code Execution Vulnerability\n\n## Executive summary\n\nMicrosoft is releasing this security advisory to provide information about a vulnerability in .NET 7.0 and .NET 6.0. This advisory also provides guidance on what developers can do to update their applications to remove this vulnerability.\n\nA vulnerability exists in Microsoft.DiaSymReader.Native.amd64.dll when reading a corrupted PDB file which may lead to remote code execution. This issue only affects Windows systems.\n\n**Note:** The vulnerabilities [CVE-2023-36792]( https://www.cve.org/CVERecord?id=CVE-2023-36792), [CVE-2023-36793]( https://www.cve.org/CVERecord?id=CVE-2023-36793), [CVE-2023-36792]( https://www.cve.org/CVERecord?id=CVE-2023-36794), [CVE-2023-36796]( https://www.cve.org/CVERecord?id=CVE-2023-36796) are all resolved by a single patch. Get [affected software](#affected-software) to resolve all of them.\n\n## Discussion\n\nDiscussion for this issue can be found at https://github.com/dotnet/runtime/issues/91948\n\n### Mitigation factors\n\nMicrosoft has not identified any mitigating factors for this vulnerability.\n\n## Affected software\n\n* Any .NET 7.0 application running on .NET 7.0.10 or earlier.\n* Any .NET 6.0 application running on .NET 6.0.21 or earlier.\n\nIf your application uses the following package versions, ensure you update to the latest version of .NET.\n\n### .NET 7\n\nPackage name | Affected version | Patched version\n------------ | ---------------- | -------------------------\n[Microsoft.NETCore.App.Runtime.win-arm64](https://www.nuget.org/packages/Microsoft.NETCore.App.Runtime.win-arm64) | >= 7.0.0, <= 7.0.10 | 7.0.11\n[Microsoft.NETCore.App.Runtime.win-x64](https://www.nuget.org/packages/Microsoft.NETCore.App.Runtime.win-x64) | >= 7.0.0, <= 7.0.10 | 7.0.11\n[Microsoft.NETCore.App.Runtime.win-x86](https://www.nuget.org/packages/Microsoft.NETCore.App.Runtime.win-x86) | >= 7.0.0, <= 7.0.10 | 7.0.11\n\n### .NET 6\n\nPackage name | Affected version | Patched version\n------------ | ---------------- | -------------------------\n[Microsoft.NETCore.App.Runtime.win-arm64](https://www.nuget.org/packages/Microsoft.NETCore.App.Runtime.win-arm64) | >= 6.0.0, <= 6.0.21 | 6.0.22\n[Microsoft.NETCore.App.Runtime.win-x64](https://www.nuget.org/packages/Microsoft.NETCore.App.Runtime.win-x64) | >= 6.0.0, <= 6.0.21 | 6.0.22\n[Microsoft.NETCore.App.Runtime.win-x86](https://www.nuget.org/packages/Microsoft.NETCore.App.Runtime.win-x86) | >= 6.0.0, <= 6.0.21 | 6.0.22\n\n\n## Advisory FAQ\n\n### How do I know if I am affected?\n\nIf you have a runtime or SDK with a version listed, or an affected package listed in [affected software](#affected-software), you're exposed to the vulnerability.\n\n### How do I fix the issue?\n\n* To fix the issue please install the latest version of .NET 6.0 or .NET 7.0. If you have installed one or more .NET SDKs through Visual Studio, Visual Studio will prompt you to update Visual Studio, which will also update your .NET SDKs.\n* If you are using one of the affected packages, please update to the patched version listed above.\n* If you have .NET 6.0 or greater installed, you can list the versions you have installed by running the `dotnet --info` command. You will see output like the following;\n\n```\n.NET Core SDK (reflecting any global.json):\n\n Version: 6.0.300\n Commit: 8473146e7d\n\nRuntime Environment:\n\n OS Name: Windows\n OS Version: 10.0.18363\n OS Platform: Windows\n RID: win10-x64\n Base Path: C:\\Program Files\\dotnet\\sdk\\6.0.300\\\n\nHost (useful for support):\n\n Version: 6.0.5\n Commit: 8473146e7d\n\n.NET Core SDKs installed:\n\n 6.0.300 [C:\\Program Files\\dotnet\\sdk]\n\n.NET Core runtimes installed:\n\n Microsoft.AspNetCore.App 6.0.5 [C:\\Program Files\\dotnet\\shared\\Microsoft.AspNetCore.App]\n Microsoft.NETCore.App 6.0.5 [C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App]\n Microsoft.WindowsDesktop.App 6.0.5 [C:\\Program Files\\dotnet\\shared\\Microsoft.WindowsDesktop.App]\n\nTo install additional .NET Core runtimes or SDKs:\n https://aka.ms/dotnet-download\n```\n\n* If you're using .NET 7.0, you should download and install Runtime 7.0.11 or SDK 7.0.111 (for Visual Studio 2022 v17.4) from https://dotnet.microsoft.com/download/dotnet-core/7.0.\n* If you're using .NET 6.0, you should download and install Runtime 6.0.22 or SDK 6.0.317 (for Visual Studio 2022 v17.2) from https://dotnet.microsoft.com/download/dotnet-core/6.0.\n\n.NET 6.0 and and .NET 7.0 updates are also available from Microsoft Update. To access this either type \"Check for updates\" in your Windows search, or open Settings, choose Update & Security and then click Check for Updates.\n\nOnce you have installed the updated runtime or SDK, restart your apps for the update to take effect.\n\nAdditionally, if you've deployed [self-contained applications](https://docs.microsoft.com/dotnet/core/deploying/#self-contained-deployments-scd) targeting any of the impacted versions, these applications are also vulnerable and must be recompiled and redeployed.\n\n## Other Information\n\n### Reporting Security Issues\n\nIf you have found a potential security issue in .NET 6.0 or .NET 7.0, please email details to secure@microsoft.com. Reports may qualify for the Microsoft .NET Core & .NET 5 Bounty. Details of the Microsoft .NET Bounty Program including terms and conditions are at .\n\n### Support\n\nYou can ask questions about this issue on GitHub in the .NET GitHub organization. The main repos are located at https://github.com/dotnet/runtime and https://github.com/dotnet/aspnet/. The Announcements repo (https://github.com/dotnet/Announcements) will contain this bulletin as an issue and will include a link to a discussion issue. You can ask questions in the linked discussion issue.\n\n### Disclaimer\n\nThe information provided in this advisory is provided \"as is\" without warranty of any kind. Microsoft disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall Microsoft Corporation or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if Microsoft Corporation or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion or limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.\n\n### External Links\n\n[CVE-2023-36796]( https://www.cve.org/CVERecord?id=CVE-2023-36796)\n\n### Revisions\n\nV1.0 (September 12, 2023): Advisory published.\n\n_Version 1.0_\n\n_Last Updated 2023-09-12_", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H" + } + ], + "affected": [ + { + "package": { + "ecosystem": "NuGet", + "name": "Microsoft.NETCore.App.Runtime.win-arm64" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "7.0.0" + }, + { + "fixed": "7.0.11" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 7.0.10" + } + }, + { + "package": { + "ecosystem": "NuGet", + "name": "Microsoft.NETCore.App.Runtime.win-arm64" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "6.0.0" + }, + { + "fixed": "6.0.22" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 6.0.21" + } + }, + { + "package": { + "ecosystem": "NuGet", + "name": "Microsoft.NETCore.App.Runtime.win-x64" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "7.0.0" + }, + { + "fixed": "7.0.11" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 7.0.10" + } + }, + { + "package": { + "ecosystem": "NuGet", + "name": "Microsoft.NETCore.App.Runtime.win-x64" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "6.0.0" + }, + { + "fixed": "6.0.22" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 6.0.21" + } + }, + { + "package": { + "ecosystem": "NuGet", + "name": "Microsoft.NETCore.App.Runtime.win-x86" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "6.0.0" + }, + { + "fixed": "6.0.22" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 6.0.21" + } + }, + { + "package": { + "ecosystem": "NuGet", + "name": "Microsoft.NETCore.App.Runtime.win-x86" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "7.0.0" + }, + { + "fixed": "7.0.11" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 7.0.10" + } + } + ], + "references": [ + { + "type": "WEB", + "url": "https://github.com/dotnet/runtime/security/advisories/GHSA-h7jm-g87p-5935" + }, + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-36796" + }, + { + "type": "PACKAGE", + "url": "https://github.com/dotnet/runtime" + }, + { + "type": "WEB", + "url": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-36796" + } + ], + "database_specific": { + "cwe_ids": [ + + ], + "severity": "HIGH", + "github_reviewed": true, + "github_reviewed_at": "2023-09-12T20:05:18Z", + "nvd_published_at": "2023-09-12T17:15:15Z" + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/pypa/pypa-expected.json b/vulnerabilities/tests/test_data/pypa/pypa-expected.json index bf49770a5..1a907bd05 100644 --- a/vulnerabilities/tests/test_data/pypa/pypa-expected.json +++ b/vulnerabilities/tests/test_data/pypa/pypa-expected.json @@ -67,5 +67,5 @@ ], "date_published": "2022-01-05T00:15:00+00:00", "weaknesses": [], - "url": "https://github.com/pypa/advisory-database" + "url": "https://github.com/pypa/advisory-database/blob/main/vulns/pypa-expected.json" } \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ruby/CVE-2007-5770-expected.json b/vulnerabilities/tests/test_data/ruby/CVE-2007-5770-expected.json new file mode 100644 index 000000000..aa3b4088c --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/CVE-2007-5770-expected.json @@ -0,0 +1,42 @@ +{ + "aliases": [ + "CVE-2007-5770" + ], + "summary": "Ruby Net::HTTPS library does not validate server certificate CN\nThe (1) Net::ftptls, (2) Net::telnets, (3) Net::imap, (4) Net::pop, and (5)\nNet::smtp libraries in Ruby 1.8.5 and 1.8.6 do not verify that the\ncommonName (CN) field in a server certificate matches the domain name in a\nrequest sent over SSL, which makes it easier for remote attackers to\nintercept SSL transmissions via a man-in-the-middle attack or spoofed web\nsite, different components than CVE-2007-5162.", + "affected_packages": [ + { + "package": { + "type": "ruby", + "namespace": null, + "name": "ruby", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/<1.8.6.230|>=1.8.7", + "fixed_version": null + }, + { + "package": { + "type": "ruby", + "namespace": null, + "name": "ruby", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/<1.8.7", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "http://www.cvedetails.com/cve/CVE-2007-5770/", + "severities": [] + } + ], + "date_published": "2007-10-08T00:00:00+00:00", + "weaknesses": [], + "url": "https://github.com/rubysec/ruby-advisory-db" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ruby/CVE-2007-5770.yml b/vulnerabilities/tests/test_data/ruby/CVE-2007-5770.yml new file mode 100644 index 000000000..fcb1c372b --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/CVE-2007-5770.yml @@ -0,0 +1,17 @@ +--- +engine: ruby +cve: 2007-5770 +url: http://www.cvedetails.com/cve/CVE-2007-5770/ +title: Ruby Net::HTTPS library does not validate server certificate CN +date: 2007-10-08 +description: | + The (1) Net::ftptls, (2) Net::telnets, (3) Net::imap, (4) Net::pop, and (5) + Net::smtp libraries in Ruby 1.8.5 and 1.8.6 do not verify that the + commonName (CN) field in a server certificate matches the domain name in a + request sent over SSL, which makes it easier for remote attackers to + intercept SSL transmissions via a man-in-the-middle attack or spoofed web + site, different components than CVE-2007-5162. +cvss_v2: 4.3 +patched_versions: +- ~> 1.8.6.230 +- '>= 1.8.7' diff --git a/vulnerabilities/tests/test_data/ruby/CVE-2010-1330-expected.json b/vulnerabilities/tests/test_data/ruby/CVE-2010-1330-expected.json new file mode 100644 index 000000000..bd09931cc --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/CVE-2010-1330-expected.json @@ -0,0 +1,31 @@ +{ + "aliases": [ + "CVE-2010-1330", + "OSV-77297" + ], + "summary": "CVE-2010-1330 jruby: XSS in the regular expression engine when processing invalid UTF-8 byte sequences\nThe regular expression engine in JRuby before 1.4.1, when $KCODE is set to 'u', does not properly handle characters immediately after a UTF-8 character, which allows remote attackers to conduct cross-site scripting (XSS) attacks via a crafted string.", + "affected_packages": [ + { + "package": { + "type": "ruby", + "namespace": null, + "name": "jruby", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/<1.4.1", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "http://jruby.org/2010/04/26/jruby-1-4-1-xss-vulnerability", + "severities": [] + } + ], + "date_published": "2010-04-26T00:00:00+00:00", + "weaknesses": [], + "url": "https://github.com/rubysec/ruby-advisory-db" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ruby/CVE-2010-1330.yml b/vulnerabilities/tests/test_data/ruby/CVE-2010-1330.yml new file mode 100644 index 000000000..78e0dfdd9 --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/CVE-2010-1330.yml @@ -0,0 +1,15 @@ +--- +engine: jruby +cve: 2010-1330 +osvdb: 77297 +url: http://jruby.org/2010/04/26/jruby-1-4-1-xss-vulnerability +title: 'CVE-2010-1330 jruby: XSS in the regular expression engine when processing + invalid UTF-8 byte sequences' +date: 2010-04-26 +description: The regular expression engine in JRuby before 1.4.1, when $KCODE is set + to 'u', does not properly handle characters immediately after a UTF-8 character, + which allows remote attackers to conduct cross-site scripting (XSS) attacks via + a crafted string. +cvss_v2: 4.3 +patched_versions: +- '>= 1.4.1' diff --git a/vulnerabilities/tests/test_data/ruby/CVE-2018-11627-expected.json b/vulnerabilities/tests/test_data/ruby/CVE-2018-11627-expected.json new file mode 100644 index 000000000..3cc824fa4 --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/CVE-2018-11627-expected.json @@ -0,0 +1,36 @@ +{ + "aliases": [ + "CVE-2018-11627" + ], + "summary": "Sinatra before 2.0.2 has XSS via the 400 Bad Request page that occurs upon a params parser exception.", + "affected_packages": [ + { + "package": { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/<2.0.2", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/issues/1428", + "severities": [ + { + "system": "cvssv3", + "value": "6.1", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2018-05-31T00:00:00+00:00", + "weaknesses": [], + "url": "https://github.com/rubysec/ruby-advisory-db" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ruby/sinatra/CVE-2018-11627.yml b/vulnerabilities/tests/test_data/ruby/CVE-2018-11627.yml similarity index 100% rename from vulnerabilities/tests/test_data/ruby/sinatra/CVE-2018-11627.yml rename to vulnerabilities/tests/test_data/ruby/CVE-2018-11627.yml diff --git a/vulnerabilities/tests/test_data/ruby/CVE-2018-7212-expected.json b/vulnerabilities/tests/test_data/ruby/CVE-2018-7212-expected.json new file mode 100644 index 000000000..a8d31bd5f --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/CVE-2018-7212-expected.json @@ -0,0 +1,48 @@ +{ + "aliases": [ + "CVE-2018-7212" + ], + "summary": "sinatra ruby gem path traversal via backslash characters on Windows\nAn issue was discovered in rack-protection/lib/rack/protection/path_traversal.rb\nin Sinatra 2.x before 2.0.1 on Windows. Path traversal is possible via backslash\ncharacters.", + "affected_packages": [ + { + "package": { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/<2.0.1", + "fixed_version": null + }, + { + "package": { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/>1.0.0", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/pull/1379", + "severities": [ + { + "system": "cvssv3", + "value": "5.3", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2018-01-09T00:00:00+00:00", + "weaknesses": [], + "url": "https://github.com/rubysec/ruby-advisory-db" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ruby/sinatra/CVE-2018-7212.yml b/vulnerabilities/tests/test_data/ruby/CVE-2018-7212.yml similarity index 100% rename from vulnerabilities/tests/test_data/ruby/sinatra/CVE-2018-7212.yml rename to vulnerabilities/tests/test_data/ruby/CVE-2018-7212.yml diff --git a/vulnerabilities/tests/test_data/ruby/parse-advisory-ruby-expected.json b/vulnerabilities/tests/test_data/ruby/parse-advisory-ruby-expected.json new file mode 100644 index 000000000..75f0dd4ad --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/parse-advisory-ruby-expected.json @@ -0,0 +1,60 @@ +[ + { + "aliases": [ + "CVE-2018-7212" + ], + "summary": "sinatra ruby gem path traversal via backslash characters on Windows\nAn issue was discovered in rack-protection/lib/rack/protection/path_traversal.rb\nin Sinatra 2.x before 2.0.1 on Windows. Path traversal is possible via backslash\ncharacters.", + "affected_packages": [ + { + "package": { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/<2.0.1", + "fixed_version": null + }, + { + "package": { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/>1.0.0", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/pull/1379", + "severities": [ + { + "system": "cvssv2", + "value": "5.0", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/pull/1379", + "severities": [ + { + "system": "cvssv3", + "value": "5.3", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2018-01-09T00:00:00+00:00", + "weaknesses": [] +} +] \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ruby/ruby-improver-expected.json b/vulnerabilities/tests/test_data/ruby/ruby-improver-expected.json new file mode 100644 index 000000000..a17ae5f00 --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/ruby-improver-expected.json @@ -0,0 +1,215 @@ +[ + { + "vulnerability_id": null, + "aliases": [ + "CVE-2018-7212" + ], + "confidence": 100, + "summary": "sinatra ruby gem path traversal via backslash characters on Windows\nAn issue was discovered in rack-protection/lib/rack/protection/path_traversal.rb\nin Sinatra 2.x before 2.0.1 on Windows. Path traversal is possible via backslash\ncharacters.", + "affected_purls": [ + { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": "0.2.6", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": "1.2.7", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": "1.3.6", + "qualifiers": null, + "subpath": null + } + ], + "fixed_purl": { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": "2.2.1", + "qualifiers": null, + "subpath": null + }, + "references": [ + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/pull/1379", + "severities": [ + { + "system": "cvssv2", + "value": "5.0", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/pull/1379", + "severities": [ + { + "system": "cvssv3", + "value": "5.3", + "scoring_elements": "" + } + ] + } + ], + "weaknesses": [] + }, + { + "vulnerability_id": null, + "aliases": [ + "CVE-2018-7212" + ], + "confidence": 100, + "summary": "sinatra ruby gem path traversal via backslash characters on Windows\nAn issue was discovered in rack-protection/lib/rack/protection/path_traversal.rb\nin Sinatra 2.x before 2.0.1 on Windows. Path traversal is possible via backslash\ncharacters.", + "affected_purls": [ + { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": "1.2.7", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": "1.3.6", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": "2.2.1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": "3.0.2", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": "3.0.5", + "qualifiers": null, + "subpath": null + } + ], + "fixed_purl": null, + "references": [ + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/pull/1379", + "severities": [ + { + "system": "cvssv2", + "value": "5.0", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/pull/1379", + "severities": [ + { + "system": "cvssv3", + "value": "5.3", + "scoring_elements": "" + } + ] + } + ], + "weaknesses": [] + }, + { + "vulnerability_id": null, + "aliases": [ + "CVE-2018-7212" + ], + "confidence": 100, + "summary": "sinatra ruby gem path traversal via backslash characters on Windows\nAn issue was discovered in rack-protection/lib/rack/protection/path_traversal.rb\nin Sinatra 2.x before 2.0.1 on Windows. Path traversal is possible via backslash\ncharacters.", + "affected_purls": [], + "fixed_purl": null, + "references": [ + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/pull/1379", + "severities": [ + { + "system": "cvssv2", + "value": "5.0", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/pull/1379", + "severities": [ + { + "system": "cvssv3", + "value": "5.3", + "scoring_elements": "" + } + ] + } + ], + "weaknesses": [] + }, + { + "vulnerability_id": null, + "aliases": [ + "CVE-2018-7212" + ], + "confidence": 100, + "summary": "sinatra ruby gem path traversal via backslash characters on Windows\nAn issue was discovered in rack-protection/lib/rack/protection/path_traversal.rb\nin Sinatra 2.x before 2.0.1 on Windows. Path traversal is possible via backslash\ncharacters.", + "affected_purls": [], + "fixed_purl": null, + "references": [ + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/pull/1379", + "severities": [ + { + "system": "cvssv2", + "value": "5.0", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/pull/1379", + "severities": [ + { + "system": "cvssv3", + "value": "5.3", + "scoring_elements": "" + } + ] + } + ], + "weaknesses": [] + } +] \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125675.yml b/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125675.yml deleted file mode 100644 index 12e317da0..000000000 --- a/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125675.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -gem: sidekiq -osvdb: 125675 -url: https://github.com/mperham/sidekiq/pull/2422 -title: Sidekiq Gem for Ruby Multiple Unspecified CSRF -date: 2015-07-06 -description: Sidekiq::Web lacks CSRF protection -patched_versions: - - ">= 3.4.2" diff --git a/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125676.yml b/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125676.yml deleted file mode 100644 index 18ba94428..000000000 --- a/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125676.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -gem: sidekiq -osvdb: 125676 -url: https://github.com/mperham/sidekiq/issues/2330 -title: | - Sidekiq Gem for Ruby web/views/queue.erb CurrentMessagesInQueue Element - Reflected XSS -date: 2015-06-04 -description: XSS via queue name in Sidekiq::Web -patched_versions: - - ">= 3.4.0" -related: - osvdb: - - 125677 diff --git a/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125678.yml b/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125678.yml deleted file mode 100644 index 1566d10a7..000000000 --- a/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125678.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -gem: sidekiq -osvdb: 125678 -url: https://github.com/mperham/sidekiq/pull/2309 -title: Sidekiq Gem for Ruby web/views/queue.erb msg.display_class Element XSS -date: 2015-04-21 -description: XSS via job arguments display class in Sidekiq::Web -patched_versions: - - ">= 3.4.0" diff --git a/vulnerabilities/tests/test_data_source.py b/vulnerabilities/tests/test_data_source.py index 369ac9c23..7d0a5f707 100644 --- a/vulnerabilities/tests/test_data_source.py +++ b/vulnerabilities/tests/test_data_source.py @@ -21,12 +21,14 @@ from vulnerabilities.importers.elixir_security import ElixirSecurityImporter from vulnerabilities.importers.fireeye import FireyeImporter from vulnerabilities.importers.gentoo import GentooImporter +from vulnerabilities.importers.github_osv import GithubOSVImporter from vulnerabilities.importers.gitlab import GitLabAPIImporter from vulnerabilities.importers.istio import IstioImporter from vulnerabilities.importers.mozilla import MozillaImporter from vulnerabilities.importers.npm import NpmImporter from vulnerabilities.importers.pypa import PyPaImporter from vulnerabilities.importers.retiredotnet import RetireDotnetImporter +from vulnerabilities.importers.ruby import RubyImporter from vulnerabilities.oval_parser import OvalParser BASE_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -123,6 +125,8 @@ def test_git_importer(mock_clone): NpmImporter, RetireDotnetImporter, PyPaImporter, + RubyImporter, + GithubOSVImporter, ], ) def test_git_importer_clone(git_importer): diff --git a/vulnerabilities/tests/test_fireeye.py b/vulnerabilities/tests/test_fireeye.py index f6a5be74a..15935728c 100644 --- a/vulnerabilities/tests/test_fireeye.py +++ b/vulnerabilities/tests/test_fireeye.py @@ -172,3 +172,48 @@ def test_parse_advisory_data_2(self): result = imported_data.to_dict() util_tests.check_results_against_json(result, expected_file) + + def test_md_list_to_dict_2(self): + expected_output = { + "# MNDT-2023-0017\n": [ + "\n", + "The IBM Personal Communications (PCOMM) application 13.0.0 and earlier caused a user's plaintext password to be written to the `C:\\Temp\\pcsnp_init.log` file when re-connection was made through a remote desktop protocol.\n", + "\n", + ], + "## Common Weakness Enumeration\n": [ + "CWE-312: Cleartext Storage of Sensitive Information\n", + "\n", + ], + "## Impact\n": [ + "High - An attacker with low-privilege access to a host with IBM PCOMM could recover the plaintext password of another user.\n", + "\n", + ], + "## Exploitability\n": [ + "Low - Exploitability varies depending on the environment in which IBM PCOMM is installed. Mandiant identified this vulnerability when conducting independent security research for a client that used Citrix to connect to shared Windows Server instances. In certain environments where remote desktop is used to connect to shared hosts with IBM PCOMM installed, the exploitability is greatly increased.\n", + "\n", + ], + "## CVE Reference\n": ["CVE-2016-0321 - scope expanded\n", "\n"], + "## Technical Details\n": [ + "While conducting independent security research, Mandiant identified a plaintext Active Directory password stored within the `C:\\Temp\\pcsnp_init.log` file. The affected host had IBM PCOMM version 13.0.0 installed and was used by multiple users who connected with Citrix. Upon a user connecting, disconnecting, and connecting again, the user's plaintext password was stored in the `C:\\Temp\\pcsnp_init.log` file.\n", + "\n", + ], + "## Discovery Credits\n": [ + "- Adin Drabkin, Mandiant\n", + "- Matthew Rotlevi, Mandiant\n", + "\n", + ], + "## Disclosure Timeline\n": [ + "- 2023-09-26 - Issue reported to the vendor.\n", + "- 2023-11-03 - The vendor updated the security bulletin for CVE-2016-0321 to include all known affected and fixed versions.\n", + "\n", + ], + "## References\n": [ + "- [IBM Security Bulletin](https://www.ibm.com/support/pages/security-bulletin-ibm-personal-communications-could-allow-remote-user-obtain-sensitive-information-including-user-passwords-allowing-unauthorized-access-cve-2016-0321)\n", + "- [IBM Personal Communications](https://www.ibm.com/support/pages/ibm-personal-communications)\n", + "- [Mitre CVE-2016-0321](https://www.cve.org/CVERecord?id=CVE-2016-0321)\n", + ], + } + with open(os.path.join(TEST_DATA, "fireeye_test3.md"), encoding="utf-8-sig") as f: + md_list = f.readlines() + md_dict = md_list_to_dict(md_list) + assert md_dict == expected_output diff --git a/vulnerabilities/tests/test_get_serverity.py b/vulnerabilities/tests/test_get_serverity.py new file mode 100644 index 000000000..4ac3e646f --- /dev/null +++ b/vulnerabilities/tests/test_get_serverity.py @@ -0,0 +1,100 @@ +import pytest +from cvss.exceptions import CVSS2MalformedError +from cvss.exceptions import CVSS3MalformedError + +from vulnerabilities.severity_systems import CVSSV2 +from vulnerabilities.severity_systems import CVSSV3 +from vulnerabilities.templatetags.show_cvss import cvss_printer + + +def test_get_cvss2_vector_values(): + assert ( + CVSSV2.get("AV:N/AC:L/Au:N/C:P/I:N/A:N ") + == CVSSV2.get("AV:N/AC:L/Au:N/C:P/I:N/A:N") + == { + "accessComplexity": "LOW", + "accessVector": "NETWORK", + "authentication": "NONE", + "availabilityImpact": "NONE", + "availabilityRequirement": "NOT_DEFINED", + "baseScore": 5.0, + "collateralDamagePotential": "NOT_DEFINED", + "confidentialityImpact": "PARTIAL", + "confidentialityRequirement": "NOT_DEFINED", + "environmentalScore": 0.0, + "exploitability": "NOT_DEFINED", + "integrityImpact": "NONE", + "integrityRequirement": "NOT_DEFINED", + "remediationLevel": "NOT_DEFINED", + "reportConfidence": "NOT_DEFINED", + "targetDistribution": "NOT_DEFINED", + "temporalScore": 0.0, + "vectorString": "AV:N/AC:L/Au:N/C:P/I:N/A:N", + "version": "2.0", + } + ) + + with pytest.raises(CVSS2MalformedError): + CVSSV2.get("") + + with pytest.raises(CVSS2MalformedError): + CVSSV2.get("AV:N/AffgL/Au:N/C:P/I:N/A:N ") + + +def test_get_cvss3_vector_values(): + assert ( + CVSSV3.get("CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H ") + == CVSSV3.get("CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H") + == { + "attackComplexity": "LOW", + "attackVector": "NETWORK", + "availabilityImpact": "HIGH", + "availabilityRequirement": "NOT_DEFINED", + "baseScore": 9.1, + "baseSeverity": "CRITICAL", + "confidentialityImpact": "HIGH", + "confidentialityRequirement": "NOT_DEFINED", + "environmentalScore": 9.1, + "environmentalSeverity": "CRITICAL", + "exploitCodeMaturity": "NOT_DEFINED", + "integrityImpact": "HIGH", + "integrityRequirement": "NOT_DEFINED", + "modifiedAttackComplexity": "LOW", + "modifiedAttackVector": "NETWORK", + "modifiedAvailabilityImpact": "HIGH", + "modifiedConfidentialityImpact": "HIGH", + "modifiedIntegrityImpact": "HIGH", + "modifiedPrivilegesRequired": "HIGH", + "modifiedScope": "CHANGED", + "modifiedUserInteraction": "NONE", + "privilegesRequired": "HIGH", + "remediationLevel": "NOT_DEFINED", + "reportConfidence": "NOT_DEFINED", + "scope": "CHANGED", + "temporalScore": 9.1, + "temporalSeverity": "CRITICAL", + "userInteraction": "NONE", + "vectorString": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H", + "version": "3.1", + } + ) + + with pytest.raises(CVSS3MalformedError): + CVSSV3.get("CVSS:3.7/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H ") + + with pytest.raises(CVSS3MalformedError): + CVSSV3.get("") + + +def test_blank_cvss_printer(): + result = cvss_printer("", "") + assert result == "

    " + + +def test_cvss_printer(): + result = cvss_printer("HIGH", "high,medium,low") + assert result == ( + "

    high

    " + "

    medium

    " + "

    low

    " + ) diff --git a/vulnerabilities/tests/test_github_osv.py b/vulnerabilities/tests/test_github_osv.py new file mode 100644 index 000000000..559ba8d15 --- /dev/null +++ b/vulnerabilities/tests/test_github_osv.py @@ -0,0 +1,113 @@ +# +# 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 json +import os +from unittest import TestCase + +from vulnerabilities.importers.osv import parse_advisory_data +from vulnerabilities.tests import util_tests + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +TEST_DATA = os.path.join(BASE_DIR, "test_data/github_osv") + + +class GithubOSVImporter(TestCase): + def test_github_osv_importer1(self): + with open(os.path.join(TEST_DATA, "github_osv_test_1.json")) as f: + mock_response = json.load(f) + expected_file = os.path.join(TEST_DATA, "github_osv_expected_1.json") + imported_data = parse_advisory_data( + mock_response, + supported_ecosystems=["npm"], + advisory_url="https://github.com/github/advisory-database" + "/blob/main/advisories/github-reviewed/github_osv_test_1.json", + ) + result = imported_data.to_dict() + util_tests.check_results_against_json(result, expected_file) + + def test_github_osv_importer2(self): + with open(os.path.join(TEST_DATA, "github_osv_test_2.json")) as f: + mock_response = json.load(f) + expected_file = os.path.join(TEST_DATA, "github_osv_expected_2.json") + # if supported_ecosystems = [] : the expected affected_packages = [] + imported_data = parse_advisory_data( + mock_response, + supported_ecosystems=[], + advisory_url="https://github.com/github/advisory-database" + "/blob/main/advisories/github-reviewed/github_osv_test_2.json", + ) + result = imported_data.to_dict() + util_tests.check_results_against_json(result, expected_file) + + def test_github_osv_importer3(self): + with open(os.path.join(TEST_DATA, "github_osv_test_3.json")) as f: + mock_response = json.load(f) + expected_file = os.path.join(TEST_DATA, "github_osv_expected_3.json") + imported_data = parse_advisory_data( + mock_response, + supported_ecosystems=["maven"], + advisory_url="https://github.com/github/advisory-database" + "/blob/main/advisories/github-reviewed/github_osv_test_3.json", + ) + result = imported_data.to_dict() + util_tests.check_results_against_json(result, expected_file) + + def test_github_osv_importer4(self): + with open(os.path.join(TEST_DATA, "github_osv_test_4.json")) as f: + mock_response = json.load(f) + expected_file = os.path.join(TEST_DATA, "github_osv_expected_4.json") + imported_data = parse_advisory_data( + mock_response, + supported_ecosystems=["gem"], + advisory_url="https://github.com/github/advisory-database" + "/blob/main/advisories/github-reviewed/github_osv_test_4.json", + ) + result = imported_data.to_dict() + util_tests.check_results_against_json(result, expected_file) + + def test_github_osv_importer5(self): + # test golang + with open(os.path.join(TEST_DATA, "github_osv_test_5.json")) as f: + mock_response = json.load(f) + expected_file = os.path.join(TEST_DATA, "github_osv_expected_5.json") + imported_data = parse_advisory_data( + mock_response, + supported_ecosystems=["golang"], + advisory_url="https://github.com/github/advisory-database" + "/blob/main/advisories/github-reviewed/github_osv_test_5.json", + ) + result = imported_data.to_dict() + util_tests.check_results_against_json(result, expected_file) + + def test_github_osv_importer6(self): + # test golang + with open(os.path.join(TEST_DATA, "github_osv_test_6.json")) as f: + mock_response = json.load(f) + expected_file = os.path.join(TEST_DATA, "github_osv_expected_6.json") + imported_data = parse_advisory_data( + mock_response, + supported_ecosystems=["golang"], + advisory_url="https://github.com/github/advisory-database" + "/blob/main/advisories/github-reviewed/github_osv_test_6.json", + ) + result = imported_data.to_dict() + util_tests.check_results_against_json(result, expected_file) + + def test_github_osv_importer7(self): + with open(os.path.join(TEST_DATA, "github_osv_test_7.json")) as f: + mock_response = json.load(f) + expected_file = os.path.join(TEST_DATA, "github_osv_expected_7.json") + imported_data = parse_advisory_data( + mock_response, + supported_ecosystems=["nuget"], + advisory_url="https://github.com/github/advisory-database" + "/blob/main/advisories/github-reviewed/github_osv_test_7.json", + ) + result = imported_data.to_dict() + util_tests.check_results_against_json(result, expected_file) diff --git a/vulnerabilities/tests/test_models.py b/vulnerabilities/tests/test_models.py index ed8eb29e6..2efe45e86 100644 --- a/vulnerabilities/tests/test_models.py +++ b/vulnerabilities/tests/test_models.py @@ -407,8 +407,8 @@ def test_univers_version_class(self): pypi_package_version = RANGE_CLASS_BY_SCHEMES[pypi_package.type].version_class assert pypi_package_version == versions.PypiVersion - RANGE_CLASS_BY_SCHEMES["alpine"] = AlpineLinuxVersionRange - alpine_version = RANGE_CLASS_BY_SCHEMES["alpine"].version_class + alpine_package = models.Package.objects.create(type="alpine", name="lxml", version="0.9") + alpine_version = RANGE_CLASS_BY_SCHEMES[alpine_package.type].version_class assert alpine_version == versions.AlpineLinuxVersion def test_sort_by_version(self): diff --git a/vulnerabilities/tests/test_osv.py b/vulnerabilities/tests/test_osv.py index 3ca50cebf..5779d0589 100644 --- a/vulnerabilities/tests/test_osv.py +++ b/vulnerabilities/tests/test_osv.py @@ -13,6 +13,7 @@ from univers.version_constraint import VersionConstraint from univers.version_range import PypiVersionRange from univers.versions import PypiVersion +from univers.versions import SemverVersion from vulnerabilities.importer import Reference from vulnerabilities.importer import VulnerabilitySeverity @@ -353,12 +354,18 @@ def test_get_affected_version_range(self): assert results == expected def test_get_fixed_versions1(self): - assert get_fixed_versions(fixed_range={}, raw_id="GHSA-j3f7-7rmc-6wqj") == [] + assert ( + get_fixed_versions( + fixed_range={}, raw_id="GHSA-j3f7-7rmc-6wqj", supported_ecosystem="pypi" + ) + == [] + ) def test_get_fixed_versions2(self): results = get_fixed_versions( fixed_range={"type": "ECOSYSTEM", "events": [{"introduced": "0"}, {"fixed": "1.7.0"}]}, raw_id="GHSA-j3f7-7rmc-6wqj", + supported_ecosystem="pypi", ) assert results == [PypiVersion("1.7.0")] @@ -374,6 +381,19 @@ def test_get_fixed_versions3(self): ], }, raw_id="GHSA-j3f7-7rmc-6wqj", + supported_ecosystem="pypi", ) assert results == [PypiVersion("9.0.0"), PypiVersion("9.0.1")] + + def test_get_fixed_versions4(self): + results = get_fixed_versions( + fixed_range={ + "type": "ECOSYSTEM", + "events": [{"introduced": "0"}, {"fixed": "6.5.4"}], + }, + raw_id="GHSA-r9p9-mrjm-926w", + supported_ecosystem="npm", + ) + + assert results == [SemverVersion("6.5.4")] diff --git a/vulnerabilities/tests/test_pypa.py b/vulnerabilities/tests/test_pypa.py index ffd648fac..1a59260e6 100644 --- a/vulnerabilities/tests/test_pypa.py +++ b/vulnerabilities/tests/test_pypa.py @@ -22,9 +22,11 @@ class TestPyPaImporter(TestCase): def test_to_advisories_with_summary(self): with open(os.path.join(TEST_DATA, "pypa_test.yaml")) as f: mock_response = saneyaml.load(f) - expected_file = os.path.join(TEST_DATA, f"pypa-expected.json") + expected_file = os.path.join(TEST_DATA, "pypa-expected.json") imported_data = parse_advisory_data( - mock_response, "pypi", "https://github.com/pypa/advisory-database" + mock_response, + ["pypi"], + "https://github.com/pypa/advisory-database/blob/main/vulns/pypa-expected.json", ) result = imported_data.to_dict() util_tests.check_results_against_json(result, expected_file) diff --git a/vulnerabilities/tests/test_pysec.py b/vulnerabilities/tests/test_pysec.py index f12a6e417..dcba3a776 100644 --- a/vulnerabilities/tests/test_pysec.py +++ b/vulnerabilities/tests/test_pysec.py @@ -22,7 +22,7 @@ class TestPyPIImporter(TestCase): def test_to_advisories_with_summary(self): with open(os.path.join(TEST_DATA, "pysec-advisories_with_summary.json")) as f: mock_response = json.load(f) - results = parse_advisory_data(mock_response, "pypi", "https://test.com").to_dict() + results = parse_advisory_data(mock_response, ["pypi"], "https://test.com").to_dict() expected_file = os.path.join(TEST_DATA, "pysec-advisories_with_summary-expected.json") check_results_against_json( @@ -35,7 +35,7 @@ def test_to_advisories_without_summary(self): with open(os.path.join(TEST_DATA, "pysec-advisories_without_summary.json")) as f: mock_response = json.load(f) - results = parse_advisory_data(mock_response, "pypi", "https://test.com").to_dict() + results = parse_advisory_data(mock_response, ["pypi"], "https://test.com").to_dict() expected_file = os.path.join(TEST_DATA, "pysec-advisories_without_summary-expected.json") check_results_against_json( @@ -49,7 +49,7 @@ def test_to_advisories_with_cwe(self): mock_response = json.load(f) results = parse_advisory_data( - raw_data=mock_response, supported_ecosystem="pypi", advisory_url="https://tes.com" + raw_data=mock_response, supported_ecosystems=["pypi"], advisory_url="https://tes.com" ).to_dict() expected_file = os.path.join(TEST_DATA, "pysec-advisories_with_cwe-expected.json") diff --git a/vulnerabilities/tests/test_ruby.py b/vulnerabilities/tests/test_ruby.py index e57026fe3..0e06afe1d 100644 --- a/vulnerabilities/tests/test_ruby.py +++ b/vulnerabilities/tests/test_ruby.py @@ -6,135 +6,91 @@ # See https://github.com/nexB/vulnerablecode for support or download. # See https://aboutcode.org for more information about nexB OSS projects. # - - +import json import os -import pathlib -from unittest import TestCase from unittest.mock import patch +import pytest from packageurl import PackageURL +from univers.version_range import GemVersionRange from vulnerabilities.importer import AdvisoryData -from vulnerabilities.importer import Reference -from vulnerabilities.importers.ruby import RubyImporter -from vulnerabilities.package_managers import RubyVersionAPI -from vulnerabilities.package_managers import VersionResponse -from vulnerabilities.utils import AffectedPackage +from vulnerabilities.importer import AffectedPackage +from vulnerabilities.importers.ruby import get_affected_packages +from vulnerabilities.importers.ruby import parse_ruby_advisory +from vulnerabilities.improvers.default import DefaultImprover +from vulnerabilities.improvers.valid_versions import RubyImprover +from vulnerabilities.tests import util_tests +from vulnerabilities.tests.util_tests import check_results_against_json +from vulnerabilities.utils import load_yaml BASE_DIR = os.path.dirname(os.path.abspath(__file__)) TEST_DATA = os.path.join(BASE_DIR, "test_data", "ruby") -MOCK_ADDED_FILES = [] - -for filepath in pathlib.Path(TEST_DATA).glob("**/*.yml"): - MOCK_ADDED_FILES.append(filepath.absolute()) +@pytest.mark.parametrize( + "filename,expected_filename,schema_type", + [ + ("CVE-2018-7212.yml", "CVE-2018-7212-expected.json", "gems"), + ("CVE-2018-11627.yml", "CVE-2018-11627-expected.json", "gems"), + ("CVE-2007-5770.yml", "CVE-2007-5770-expected.json", "rubies"), + ("CVE-2010-1330.yml", "CVE-2010-1330-expected.json", "rubies"), + ], +) +def test_advisories(filename, expected_filename, schema_type): + file_path = os.path.join(TEST_DATA, filename) + mock_response = load_yaml(file_path) + results = parse_ruby_advisory( + mock_response, schema_type, "https://github.com/rubysec/ruby-advisory-db" + ).to_dict() + expected_file = os.path.join(TEST_DATA, expected_filename) + check_results_against_json(results=results, expected_file=expected_file) -class RubyImporterTest(TestCase): - @classmethod - def setUpClass(cls): - data_source_cfg = { - "repository_url": "https://github.com/rubysec/ruby-advisory-db.git", - } - cls.data_src = RubyImporter(1, config=data_source_cfg) - cls.data_src.pkg_manager_api = RubyVersionAPI() - - @patch( - "vulnerabilities.package_managers.RubyVersionAPI.get", - return_value=VersionResponse( - valid_versions={"1.0.0", "1.8.0", "2.0.3"}, newer_versions=set() - ), - ) - def test_process_file(self, mock_write): - expected_advisories = [ - Advisory( - summary="An issue was discovered in rack-protection/lib/rack/protection/path_traversal.rb\nin Sinatra 2.x before 2.0.1 on Windows. Path traversal is possible via backslash\ncharacters.\n", - vulnerability_id="CVE-2018-7212", - affected_packages=[ - AffectedPackage( - vulnerable_package=PackageURL( - type="gem", - namespace=None, - name="sinatra", - version="1.8.0", - ), - patched_package=PackageURL( - type="gem", - namespace=None, - name="sinatra", - version="2.0.3", - ), - ) - ], - references=[ - Reference( - reference_id="", - url="https://github.com/sinatra/sinatra/pull/1379", - severities=[], - ) - ], - ), - Advisory( - summary="Sinatra before 2.0.2 has XSS via the 400 Bad Request page that occurs upon a params parser exception.\n", - vulnerability_id="CVE-2018-11627", - affected_packages=[ - AffectedPackage( - vulnerable_package=PackageURL( - type="gem", - namespace=None, - name="sinatra", - version="1.0.0", - ), - patched_package=PackageURL( - type="gem", - namespace=None, - name="sinatra", - version="2.0.3", - ), - ), - AffectedPackage( - vulnerable_package=PackageURL( - type="gem", - namespace=None, - name="sinatra", - version="1.8.0", - ), - patched_package=PackageURL( - type="gem", - namespace=None, - name="sinatra", - version="2.0.3", - ), - ), - ], - references=[ - Reference( - reference_id="", - url="https://github.com/sinatra/sinatra/issues/1428", - severities=[], - ) - ], - ), - ] - found_advisories = [] - for p in MOCK_ADDED_FILES: - advisory = self.data_src.process_file(p) - if advisory: - found_advisories.append(advisory) - found_advisories = list(map(Advisory.normalized, found_advisories)) - expected_advisories = list(map(Advisory.normalized, expected_advisories)) - assert sorted(found_advisories) == sorted(expected_advisories) +@patch("vulnerabilities.improvers.valid_versions.RubyImprover.get_package_versions") +def test_ruby_improver(mock_response): + advisory_file = os.path.join(TEST_DATA, f"parse-advisory-ruby-expected.json") + with open(advisory_file) as exp: + advisories = [AdvisoryData.from_dict(adv) for adv in (json.load(exp))] + mock_response.return_value = ["0.2.6", "1.2.7", "1.3.6", "2.2.1", "3.0.2", "3.0.5"] + improvers = [RubyImprover(), DefaultImprover()] + result = [] + for improver in improvers: + for advisory in advisories: + inference = [data.to_dict() for data in improver.get_inferences(advisory)] + result.extend(inference) + expected_file = os.path.join(TEST_DATA, f"ruby-improver-expected.json") + util_tests.check_results_against_json(result, expected_file) - def test_categorize_versions(self): - all_versions = ["1.0.0", "1.2.0", "9.0.2", "0.2.3"] - safe_ver_ranges = ["==1.0.0", ">1.2.0"] - - exp_safe_vers = ["1.0.0", "9.0.2"] - exp_aff_vers = ["1.2.0", "0.2.3"] - - safe_vers, aff_vers = self.data_src.categorize_versions(all_versions, safe_ver_ranges) - assert exp_aff_vers == aff_vers - assert exp_safe_vers == safe_vers +@pytest.mark.parametrize( + "record,purl,result", + [ + ( + {"patched_versions": [">= 1.6.5.1"]}, + PackageURL(type="gem", name="jruby"), + [ + AffectedPackage( + package=PackageURL(type="gem", name="jruby"), + affected_version_range=GemVersionRange.from_string("vers:gem/<1.6.5.1"), + ) + ], + ), + ( + {"patched_versions": [">= 1.1.3"], "unaffected_versions": ["< 0.1.33"]}, + PackageURL(type="gem", name="'devise_token_auth'"), + [ + AffectedPackage( + package=PackageURL(type="gem", name="'devise_token_auth'"), + affected_version_range=GemVersionRange.from_string("vers:gem/<1.1.3"), + ), + AffectedPackage( + package=PackageURL(type="gem", name="'devise_token_auth'"), + affected_version_range=GemVersionRange.from_string("vers:gem/>=0.1.33"), + ), + ], + ), + ], +) +def test_get_affected_packages(record, purl, result): + assert get_affected_packages(record, purl) == result diff --git a/vulnerabilities/views.py b/vulnerabilities/views.py index 026ce1b09..391c165e7 100644 --- a/vulnerabilities/views.py +++ b/vulnerabilities/views.py @@ -6,9 +6,11 @@ # See https://github.com/nexB/vulnerablecode for support or download. # See https://aboutcode.org for more information about nexB OSS projects. # - +import logging from datetime import datetime +from cvss.exceptions import CVSS2MalformedError +from cvss.exceptions import CVSS3MalformedError from django.contrib import messages from django.core.exceptions import ValidationError from django.core.mail import send_mail @@ -26,6 +28,7 @@ from vulnerabilities.forms import PackageSearchForm from vulnerabilities.forms import VulnerabilitySearchForm from vulnerabilities.models import VulnerabilityStatusType +from vulnerabilities.severity_systems import SCORING_SYSTEMS from vulnerabilities.utils import get_severity_range from vulnerablecode.settings import env @@ -132,6 +135,16 @@ def get_context_data(self, **kwargs): weakness_object for weakness_object in weaknesses if weakness_object.weakness ] status = self.object.get_status_label + + severity_vectors = [] + for s in self.object.severities: + if s.scoring_elements and s.scoring_system in SCORING_SYSTEMS: + try: + vector_values = SCORING_SYSTEMS[s.scoring_system].get(s.scoring_elements) + severity_vectors.append(vector_values) + except (CVSS2MalformedError, CVSS3MalformedError, NotImplementedError): + logging.error(f"CVSSMalformedError for {s.scoring_elements}") + context.update( { "vulnerability": self.object, @@ -140,6 +153,7 @@ def get_context_data(self, **kwargs): "severity_score_range": get_severity_range( {s.value for s in self.object.severities} ), + "severity_vectors": severity_vectors, "references": self.object.references.all(), "aliases": self.object.aliases.all(), "affected_packages": self.object.affected_packages.all(), diff --git a/vulnerablecode/__init__.py b/vulnerablecode/__init__.py index b0bd1606d..e4d5b5f8d 100644 --- a/vulnerablecode/__init__.py +++ b/vulnerablecode/__init__.py @@ -12,7 +12,7 @@ import warnings from pathlib import Path -__version__ = "34.0.0rc1" +__version__ = "34.0.0rc4" def command_line(): diff --git a/vulntotal/datasources/deps.py b/vulntotal/datasources/deps.py index 33c62773d..413c0d18b 100644 --- a/vulntotal/datasources/deps.py +++ b/vulntotal/datasources/deps.py @@ -26,12 +26,21 @@ class DepsDataSource(DataSource): def fetch_json_response(self, url): response = requests.get(url) - if not response.status_code == 200 or response.text == "Not Found": + if response.status_code != 200 or response.text == "Not Found": logger.error(f"Error while fetching {url}") return return response.json() def datasource_advisory(self, purl) -> Iterable[VendorData]: + """ + Fetch and parse advisories from a given purl. + + Parameters: + purl: A string representing the package URL. + + Returns: + A list of VendorData objects containing the advisory information. + """ payload = generate_meta_payload(purl) response = self.fetch_json_response(payload) if response: @@ -58,6 +67,16 @@ def supported_ecosystem(cls): def parse_advisory(advisory, purl) -> Iterable[VendorData]: + """ + Parse an advisory into a VendorData object. + + Parameters: + advisory: A dictionary representing the advisory data. + purl: PURL for the advisory. + + Yields: + VendorData instance containing purl, aliases, affected_versions and fixed_versions. + """ package = advisory["packages"][0] affected_versions = [event["version"] for event in package["versionsAffected"]] fixed_versions = [event["version"] for event in package["versionsUnaffected"]] @@ -70,6 +89,15 @@ def parse_advisory(advisory, purl) -> Iterable[VendorData]: def parse_advisories_from_meta(advisories_metadata): + """ + Parse advisories from a given metadata. + + Parameters: + advisories_metadata: A dictionary representing the metadata of the advisories. + + Returns: + A list of dictionaries, each representing an advisory. + """ advisories = [] dependencies = advisories_metadata.get("dependencies") or [] for dependency in dependencies: @@ -84,6 +112,15 @@ def generate_advisory_payload(advisory_meta): def generate_meta_payload(purl): + """ + Generate a payload for fetching advisories metadata from a given purl. + + Parameters: + purl: A PackageURL object representing the package URL. + + Returns: + A string representing the payload for fetching advisories metadata. It should be a valid URL that contains the ecosystem, package name and package version of the dependency. + """ url_advisories_meta = "https://deps.dev/_/s/{ecosystem}/p/{package}/v/{version}/dependencies" supported_ecosystem = DepsDataSource.supported_ecosystem() if purl.type in supported_ecosystem: diff --git a/vulntotal/datasources/github.py b/vulntotal/datasources/github.py index 3311665c4..59ac65679 100644 --- a/vulntotal/datasources/github.py +++ b/vulntotal/datasources/github.py @@ -68,8 +68,8 @@ def datasource_advisory_from_cve(self, cve: str) -> Iterable[VendorData]: yield VendorData( purl=purl, aliases=sorted(list(set(advisory.get("identifiers", None)))), - affected_versions=sorted(list(set(advisory.get("firstPatchedVersion", None)))), - fixed_versions=sorted(list(set(advisory.get("vulnerableVersionRange", None)))), + affected_versions=sorted(list(set(advisory.get("vulnerableVersionRange", None)))), + fixed_versions=sorted(list(set(advisory.get("firstPatchedVersion", None)))), ) @classmethod @@ -101,7 +101,7 @@ def parse_advisory(interesting_edges, purl) -> Iterable[VendorData]: """ for edge in interesting_edges: node = edge["node"] - aliases = [aliase["value"] for aliase in get_item(node, "advisory", "identifiers")] + aliases = [alias["value"] for alias in get_item(node, "advisory", "identifiers")] affected_versions = node["vulnerableVersionRange"].strip().replace(" ", "").split(",") parsed_fixed_versions = get_item(node, "firstPatchedVersion", "identifier") fixed_versions = [parsed_fixed_versions] if parsed_fixed_versions else [] diff --git a/vulntotal/datasources/gitlab.py b/vulntotal/datasources/gitlab.py index 55aaa5b99..ae805ab00 100644 --- a/vulntotal/datasources/gitlab.py +++ b/vulntotal/datasources/gitlab.py @@ -31,6 +31,15 @@ class GitlabDataSource(DataSource): license_url = "TODO" def datasource_advisory(self, purl) -> Iterable[VendorData]: + """ + Fetches advisories for a given purl from the GitLab API. + + Parameters: + purl: A PackageURL instance representing the package to query. + + Yields: + VendorData instance containing the advisory information for the package. + """ package_slug = get_package_slug(purl) location = download_subtree(package_slug, speculative_execution=True) if not location: @@ -60,6 +69,15 @@ def supported_ecosystem(cls): def get_package_slug(purl): + """ + Constructs a package slug from a given purl. + + Parameters: + purl: A PackageURL instance representing the package to query. + + Returns: + A string representing the package slug, or None if the purl type is not supported by GitLab. + """ supported_ecosystem = GitlabDataSource.supported_ecosystem() if purl.type not in supported_ecosystem: @@ -75,6 +93,16 @@ def get_package_slug(purl): def download_subtree(package_slug: str, speculative_execution=False): + """ + Downloads and extracts a tar file from a given package slug. + + Parameters: + package_slug: A string representing the package slug to query. + speculative_execution: A boolean indicating whether to log errors or not. + + Returns: + A Path object representing the extracted location, or None if an error occurs. + """ url = f"https://gitlab.com/gitlab-org/security-products/gemnasium-db/-/archive/master/gemnasium-db-master.tar.gz?path={package_slug}" response = fetch(url) if os.path.getsize(response.location) > 0: @@ -91,6 +119,12 @@ def download_subtree(package_slug: str, speculative_execution=False): def clear_download(location): + """ + Deletes a directory and its contents. + + Parameters: + location: A Path object representing the directory to delete. + """ if location: shutil.rmtree(location) @@ -133,9 +167,9 @@ def get_casesensitive_slug(path, package_slug): } ] url = "https://gitlab.com/api/graphql" - hasnext = True + has_next = True - while hasnext: + while has_next: response = requests.post(url, json=payload).json() paginated_tree = response[0]["data"]["project"]["repository"]["paginatedTree"] @@ -149,14 +183,26 @@ def get_casesensitive_slug(path, package_slug): return get_gitlab_style_slug(slug_flatpath, package_slug) payload[0]["variables"]["nextPageCursor"] = paginated_tree["pageInfo"]["endCursor"] - hasnext = paginated_tree["pageInfo"]["hasNextPage"] + has_next = paginated_tree["pageInfo"]["hasNextPage"] def parse_interesting_advisories(location, purl, delete_download=False) -> Iterable[VendorData]: + """ + Parses advisories from YAML files in a given location that match a given version. + + Parameters: + location: A Path object representing the location of the YAML files. + purl: PURL for the advisory. + version: A string representing the version to check against the affected range. + delete_download: A boolean indicating whether to delete the downloaded files after parsing. + + Yields: + VendorData instance containing the advisory information for the package. + """ version = purl.version path = Path(location) - glob = "**/*.yml" - files = (p for p in path.glob(glob) if p.is_file()) + pattern = "**/*.yml" + files = [p for p in path.glob(pattern) if p.is_file()] for file in sorted(files): with open(file) as f: gitlab_advisory = saneyaml.load(f) diff --git a/vulntotal/datasources/oss_index.py b/vulntotal/datasources/oss_index.py index d00454304..a18e65d21 100644 --- a/vulntotal/datasources/oss_index.py +++ b/vulntotal/datasources/oss_index.py @@ -27,6 +27,14 @@ class OSSDataSource(DataSource): api_authenticated = "https://ossindex.sonatype.org/api/v3/authorized/component-report" def fetch_json_response(self, coordinates): + """Fetch JSON response from OSS Index API for a given list of coordinates. + + Parameters: + coordinates: A list of strings representing the package coordinates. + + Returns: + A dictionary containing the JSON response from the OSS Index API, or None if the response is unsuccessful or an error occurs while fetching data. + """ username = os.environ.get("OSS_USERNAME", None) token = os.environ.get("OSS_TOKEN", None) auth = None @@ -35,20 +43,21 @@ def fetch_json_response(self, coordinates): auth = (username, token) url = self.api_authenticated response = requests.post(url, auth=auth, json={"coordinates": coordinates}) - - if response.status_code == 200: + try: + response.raise_for_status() return response.json() - elif response.status_code == 401: - logger.error("Invalid credentials") - elif response.status_code == 429: - msg = ( - "Too many requests" - if auth - else "Too many requests: add OSS_USERNAME and OSS_TOKEN in .env file" - ) - logger.error(msg) - else: - logger.error(f"unknown status code: {response.status_code} while fetching: {url}") + except requests.exceptions.HTTPError as e: + if e.response.status_code == 401: + logger.error("Invalid credentials") + elif e.response.status_code == 429: + msg = ( + "Too many requests" + if auth + else "Too many requests: add OSS_USERNAME and OSS_TOKEN in .env file" + ) + logger.error(msg) + else: + logger.error(f"Unknown status code: {e.response.status_code} while fetching: {url}") def datasource_advisory(self, purl) -> Iterable[VendorData]: if purl.type not in self.supported_ecosystem(): @@ -81,6 +90,16 @@ def supported_ecosystem(cls): def parse_advisory(component, purl) -> Iterable[VendorData]: + """ + Parse component from OSS Index API and yield VendorData. + + Parameters: + component: A list containing a dictionary with component details. + purl: PURL for the advisory. + + Yields: + VendorData instance containing advisory information for the component. + """ response = component[0] vulnerabilities = response.get("vulnerabilities") or [] for vuln in vulnerabilities: diff --git a/vulntotal/datasources/osv.py b/vulntotal/datasources/osv.py index e7360cc24..4adf6322c 100644 --- a/vulntotal/datasources/osv.py +++ b/vulntotal/datasources/osv.py @@ -27,11 +27,21 @@ class OSVDataSource(DataSource): url = "https://api.osv.dev/v1/query" def fetch_advisory(self, payload): - """Fetch JSON advisory from OSV API for a given package payload""" + """ + Fetch JSON advisory from OSV API for a given package payload + + Parameters: + payload: A dictionary representing the package data to query. + + Returns: + A JSON object containing the advisory information for the package, or None if an error occurs while fetching data from the OSV API. + """ response = requests.post(self.url, data=str(payload)) - if not response.status_code == 200: - logger.error(f"Error while fetching {payload}: {response.status_code}") + try: + response.raise_for_status() + except requests.exceptions.HTTPError as e: + logger.error(f"Error while fetching {payload}: {e}") return return response.json() @@ -66,6 +76,12 @@ def supported_ecosystem(cls): def parse_advisory(response, purl) -> Iterable[VendorData]: """ Parse response from OSV API and yield VendorData + + Parameters: + response: A JSON object containing the response data from the OSV API. + + Yields: + VendorData instance containing the advisory information for the package. """ for vuln in response.get("vulns") or []: @@ -79,17 +95,17 @@ def parse_advisory(response, purl) -> Iterable[VendorData]: try: affected_versions.extend(get_item(vuln, "affected", 0, "versions") or []) - except: - pass + except (KeyError, TypeError, IndexError) as e: + logger.error(f"Error while parsing affected versions: {e}") try: - for event in get_item(vuln, "affected", 0, "ranges", 0, "events") or []: - affected_versions.append(event.get("introduced")) if event.get( - "introduced" - ) else None - fixed.append(event.get("fixed")) if event.get("fixed") else None - except: - pass + events = get_item(vuln, "affected", 0, "ranges", 0, "events") or [] + affected_versions.extend( + [event.get("introduced") for event in events if event.get("introduced")] + ) + fixed.extend([event.get("fixed") for event in events if event.get("fixed")]) + except (KeyError, TypeError, IndexError) as e: + logger.error(f"Error while parsing events: {e}") yield VendorData( purl=PackageURL(purl.type, purl.namespace, purl.name), @@ -100,7 +116,15 @@ def parse_advisory(response, purl) -> Iterable[VendorData]: def generate_payload(purl): - """Generate compatible payload for OSV API from a PURL""" + """ + Generate compatible payload for OSV API from a PURL + + Parameters: + purl: A PackageURL instance representing the package to query. + + Returns: + A dictionary containing the package data compatible with the OSV API. + """ supported_ecosystem = OSVDataSource.supported_ecosystem() payload = {} diff --git a/vulntotal/datasources/snyk.py b/vulntotal/datasources/snyk.py index 4b1a173f8..5b2418071 100644 --- a/vulntotal/datasources/snyk.py +++ b/vulntotal/datasources/snyk.py @@ -10,14 +10,16 @@ import logging from typing import Iterable from urllib.parse import quote +from urllib.parse import unquote_plus import requests from bs4 import BeautifulSoup from packageurl import PackageURL from vulntotal.validator import DataSource +from vulntotal.validator import InvalidCVEError from vulntotal.validator import VendorData -from vulntotal.vulntotal_utils import snky_constraints_satisfied +from vulntotal.vulntotal_utils import snyk_constraints_satisfied logger = logging.getLogger(__name__) @@ -27,15 +29,36 @@ class SnykDataSource(DataSource): license_url = "TODO" def fetch(self, url): + """ + Fetch the content of a given URL. + + Parameters: + url: A string representing the URL to fetch. + + Returns: + A string of HTML or a dictionary of JSON if the response is successful, + or None if the response is unsuccessful. + """ response = requests.get(url) - if not response.status_code == 200: - logger.error(f"Error while fetching {url}") + try: + response.raise_for_status() + except requests.exceptions.HTTPError as e: + logger.error(f"Error while fetching {url}: {e}") return if response.headers["content-type"] == "application/json, charset=utf-8": return response.json() return response.text def datasource_advisory(self, purl) -> Iterable[VendorData]: + """ + Fetch advisories from Snyk for a given package. + + Parameters: + purl: A PackageURL instance representing the package. + + Yields: + VendorData instance containing advisory information. + """ package_advisory_url = generate_package_advisory_url(purl) package_advisories_list = self.fetch(package_advisory_url) self._raw_dump.append(package_advisories_list) @@ -49,6 +72,38 @@ def datasource_advisory(self, purl) -> Iterable[VendorData]: if advisory_html: yield parse_html_advisory(advisory_html, snyk_id, affected, purl) + def datasource_advisory_from_cve(self, cve: str) -> Iterable[VendorData]: + """ + Fetch advisories from Snyk for a given CVE. + + Parameters: + cve : CVE ID + + Yields: + VendorData instance containing advisory information. + """ + if not cve.upper().startswith("CVE-"): + raise InvalidCVEError + + package_list = generate_payload_from_cve(cve) + response = self.fetch(package_list) + self._raw_dump = [response] + + # get list of vulnerabilities for cve id + vulns_list = parse_cve_advisory_html(response) + + # for each vulnerability get fixed version from snyk_id_url, get affected version from package_advisory_url + for snyk_id, package_advisory_url in vulns_list.items(): + package_advisories_list = self.fetch(package_advisory_url) + package_advisories = extract_html_json_advisories(package_advisories_list) + affected_versions = package_advisories[snyk_id] + advisory_payload = generate_advisory_payload(snyk_id) + advisory_html = self.fetch(advisory_payload) + self._raw_dump.append(advisory_html) + purl = generate_purl(package_advisory_url) + if advisory_html and purl: + yield parse_html_advisory(advisory_html, snyk_id, affected_versions, purl) + @classmethod def supported_ecosystem(cls): return { @@ -68,6 +123,15 @@ def supported_ecosystem(cls): def generate_package_advisory_url(purl): + """ + Generate a URL for fetching advisories from Snyk for a given package. + + Parameters: + purl: A PackageURL instance representing the package. + + Returns: + A string containing the URL or None if the package is not supported by Snyk. + """ url_package_advisories = "https://security.snyk.io/package/{ecosystem}/{package}" # Pseudo API, unfortunately gives only 30 vulnerability per package, but this is the best we have for unmanaged packages @@ -102,14 +166,78 @@ def generate_package_advisory_url(purl): ) +def generate_purl(package_advisory_url): + """ + Generates purl from Package advisory url. + + Parameters: + package_advisory_url: URL of the package on Snyk. + + Returns: + A PackageURL instance representing the package + """ + package_advisory_url = unquote_plus( + package_advisory_url.replace("https://security.snyk.io/package/", "") + ) + supported_ecosystems = {v: k for (k, v) in SnykDataSource.supported_ecosystem().items()} + + package_url_split = package_advisory_url.split("/") + pkg_type = package_url_split[0] + + pkg_name = None + namespace = None + qualifiers = {} + + if pkg_type == "maven": + pkg_name = package_url_split[1].split(":")[1] + namespace = package_url_split[1].split(":")[0] + + elif pkg_type == "composer": + pkg_name = package_url_split[-1] + namespace = package_url_split[-2] + + elif pkg_type == "golang": + pkg_name = package_url_split[-1] + namespace = "/".join(package_url_split[1:-1]) + + elif pkg_type == "npm": + # handle scoped npm packages + if "@" in package_advisory_url: + namespace = package_url_split[-2] + + pkg_name = package_url_split[-1] + + elif pkg_type == "linux": + pkg_name = package_url_split[-1] + qualifiers["distro"] = package_url_split[1] + + elif pkg_type in ("cocoapods", "hex", "nuget", "pip", "rubygems", "unmanaged"): + pkg_name = package_url_split[-1] + + if pkg_type is None or pkg_name is None: + logger.error("Invalid package advisory url, package type or name is missing") + return + + return PackageURL(type=supported_ecosystems[pkg_type], name=pkg_name, namespace=namespace) + + def extract_html_json_advisories(package_advisories): - vulnerablity = {} + """ + Extract vulnerability information from HTML or JSON advisories. + + Parameters: + package_advisories: A string of HTML or a dictionary of JSON containing advisories for a package. + + Returns: + A dictionary mapping vulnerability IDs to lists of affected versions for the package. + """ + vulnerability = {} # If advisories are json and is obtained through pseudo API if isinstance(package_advisories, dict): if package_advisories["status"] == "ok": for vuln in package_advisories["vulnerabilities"]: - vulnerablity[vuln["id"]] = vuln["semver"]["vulnerable"] + vulnerability[vuln["id"]] = vuln["semver"]["vulnerable"] else: soup = BeautifulSoup(package_advisories, "html.parser") vulns_table = soup.find("tbody", class_="vue--table__tbody") @@ -121,11 +249,23 @@ def extract_html_json_advisories(package_advisories): "span", class_="vue--chip vulnerable-versions__chip vue--chip--default" ) affected_versions = [vers.text.strip() for vers in ranges] - vulnerablity[anchor["href"].rsplit("/", 1)[-1]] = affected_versions - return vulnerablity + vulnerability[anchor["href"].rsplit("/", 1)[-1]] = affected_versions + return vulnerability def parse_html_advisory(advisory_html, snyk_id, affected, purl) -> VendorData: + """ + Parse HTML advisory from Snyk and extract vendor data. + + Parameters: + advisory_html: A string of HTML containing the advisory details. + snyk_id: A string representing the Snyk ID of the vulnerability. + affected: A list of strings representing the affected versions. + purl: PURL for the advisory. + + Returns: + A VendorData instance containing aliases, affected versions and fixed versions for the vulnerability. + """ aliases = [] fixed_versions = [] @@ -153,12 +293,41 @@ def parse_html_advisory(advisory_html, snyk_id, affected, purl) -> VendorData: ) +def parse_cve_advisory_html(cve_advisory_html): + """ + Parse CVE HTML advisory from Snyk and extract list of vulnerabilities and corresponding packages for that CVE. + + Parameters: + advisory_html: A string of HTML containing the vulnerabilities for given CVE. + + Returns: + A dictionary with each item representing a vulnerability. Key of each item is the SNYK_ID and value is the package advisory url on snyk website + """ + cve_advisory_soup = BeautifulSoup(cve_advisory_html, "html.parser") + vulns_table = cve_advisory_soup.find("tbody", class_="vue--table__tbody") + if not vulns_table: + return None + vulns_rows = vulns_table.find_all("tr", class_="vue--table__row") + vulns_list = {} + + for row in vulns_rows: + anchors = row.find_all("a", {"class": "vue--anchor"}) + if len(anchors) != 2: + continue + snyk_id = anchors[0]["href"].split("/")[1] + package_advisory_url = f"https://security.snyk.io{anchors[1]['href']}" + vulns_list[snyk_id] = package_advisory_url + + return vulns_list + + def is_purl_in_affected(version, affected): - for affected_range in affected: - if snky_constraints_satisfied(affected_range, version): - return True - return False + return any(snyk_constraints_satisfied(affected_range, version) for affected_range in affected) def generate_advisory_payload(snyk_id): return f"https://security.snyk.io/vuln/{snyk_id}" + + +def generate_payload_from_cve(cve_id): + return f"https://security.snyk.io/vuln?search={cve_id}" diff --git a/vulntotal/datasources/vulnerablecode.py b/vulntotal/datasources/vulnerablecode.py index 7fa4c2709..d0122db83 100644 --- a/vulntotal/datasources/vulnerablecode.py +++ b/vulntotal/datasources/vulnerablecode.py @@ -30,22 +30,49 @@ class VulnerableCodeDataSource(DataSource): vc_purl_search_api_path = "api/packages/bulk_search/" def fetch_post_json(self, payload): + """ + Fetches JSON data from the VulnerableCode API using a POST request with a given payload. + + Parameters: + payload: A dictionary representing the data to send in the request body. + + Returns: + A JSON object containing the response data, or None if an error occurs while fetching data from the VulnerableCode API. + """ url = urljoin(self.global_instance, self.vc_purl_search_api_path) response = fetch_vulnerablecode_query(url=url, payload=payload) - if not response.status_code == 200: + if response.status_code != 200: logger.error(f"Error while fetching {url}") return return response.json() def fetch_get_json(self, url): + """ + Fetches JSON data from a given URL using the VulnerableCode API. + + Parameters: + url: A string representing the URL to query. + + Returns: + A JSON object containing the response data, or None if an error occurs while fetching data from the URL. + """ response = fetch_vulnerablecode_query(url=url, payload=None) - if not response.status_code == 200: + if response.status_code != 200: logger.error(f"Error while fetching {url}") return return response.json() def datasource_advisory(self, purl) -> Iterable[VendorData]: - if purl.type not in self.supported_ecosystem() or not purl.version: + """ + Fetches advisories for a given purl from the VulnerableCode API. + + Parameters: + purl: A PackageURL instance representing the package to query. + + Yields: + VendorData instance containing the advisory information for the package. + """ + if purl.type not in self.supported_ecosystem() or purl.version is None: return metadata_advisories = self.fetch_post_json({"purls": [str(purl)]}) self._raw_dump.append(metadata_advisories) @@ -101,10 +128,10 @@ class VCIOTokenError(Exception): def fetch_vulnerablecode_query(url: str, payload: dict): """ Requires VCIO API key in .env file - For example:: - - VCIO_TOKEN="OJ78Os2IPfM80hqVT2ek+1QnrTKvsX1HdOMABq3pmQd" + For example: + VCIO_TOKEN='OJ78Os2IPfM80hqVT2ek+1QnrTKvsX1HdOMABq3pmQd' """ + load_dotenv() vcio_token = os.environ.get("VCIO_TOKEN", None) if not vcio_token: diff --git a/vulntotal/tests/test_data/snyk/html/4.html b/vulntotal/tests/test_data/snyk/html/4.html new file mode 100644 index 000000000..6ab4ccc0a --- /dev/null +++ b/vulntotal/tests/test_data/snyk/html/4.html @@ -0,0 +1,190 @@ + + + + Vulnerability DB | Snyk + + +

    Find out if you have vulnerabilities that put you at risk

    + Test your applications +
    Toggle filtering controls
    Expand this section

    + APPLICATION +

    Expand this section

    + OPERATING SYSTEM +

    + Report a new vulnerability +
    + VULNERABILITY + + AFFECTS + + TYPE + + PUBLISHED +
    • C
    + Cross-site Scripting (XSS) +
    + org.webjars.bowergithub.sentsin:layui + + [0,] + + Maven + + 12 Jan 2024 +
    • C
    + Cross-site Scripting (XSS) +
    + org.webjars.bowergithub.diguoyihao:layui + + [0,] + + Maven + + 12 Jan 2024 +
    • C
    + Cross-site Scripting (XSS) +
    + org.webjars.bower:layui + + [0,] + + Maven + + 12 Jan 2024 +
    • C
    + Cross-site Scripting (XSS) +
    + org.webjars.bowergithub.layui:layui + + [0,] + + Maven + + 12 Jan 2024 +
    • C
    + Cross-site Scripting (XSS) +
    + org.webjars:layui + + [,2.7.6) + + Maven + + 12 Jan 2024 +
    • C
    + Cross-site Scripting (XSS) +
    + org.webjars.npm:layui + + [,2.7.6) + + Maven + + 12 Jan 2024 +
    + + diff --git a/vulntotal/tests/test_data/snyk/html/4.html-expected.json b/vulntotal/tests/test_data/snyk/html/4.html-expected.json new file mode 100644 index 000000000..5f5f93616 --- /dev/null +++ b/vulntotal/tests/test_data/snyk/html/4.html-expected.json @@ -0,0 +1,8 @@ +{ + "SNYK-JAVA-ORGWEBJARSBOWERGITHUBSENTSIN-6146043": "https://security.snyk.io/package/maven/org.webjars.bowergithub.sentsin%3Alayui", + "SNYK-JAVA-ORGWEBJARSBOWERGITHUBDIGUOYIHAO-6146042": "https://security.snyk.io/package/maven/org.webjars.bowergithub.diguoyihao%3Alayui", + "SNYK-JAVA-ORGWEBJARSBOWER-6146041": "https://security.snyk.io/package/maven/org.webjars.bower%3Alayui", + "SNYK-JAVA-ORGWEBJARSBOWERGITHUBLAYUI-6146040": "https://security.snyk.io/package/maven/org.webjars.bowergithub.layui%3Alayui", + "SNYK-JAVA-ORGWEBJARS-6146039": "https://security.snyk.io/package/maven/org.webjars%3Alayui", + "SNYK-JAVA-ORGWEBJARSNPM-6146038": "https://security.snyk.io/package/maven/org.webjars.npm%3Alayui" +} diff --git a/vulntotal/tests/test_data/snyk/html/5.html b/vulntotal/tests/test_data/snyk/html/5.html new file mode 100644 index 000000000..e52bffcea --- /dev/null +++ b/vulntotal/tests/test_data/snyk/html/5.html @@ -0,0 +1,430 @@ + + + + Vulnerability DB | Snyk + + +

    Find out if you have vulnerabilities that put you at risk

    + Test your applications +
    Toggle filtering controls
    Expand this section

    + APPLICATION +

    Expand this section

    + OPERATING SYSTEM +

    + Report a new vulnerability +
    + VULNERABILITY + + AFFECTS + + TYPE + + PUBLISHED +
    • M
    + CVE-2023-6237 +
    + libopenssl3 + + <3.0.8-150500.5.24.1 + + sles:15.5 + + 23 Jan 2024 +
    • M
    + CVE-2023-6237 +
    + libopenssl-3-devel + + <3.0.8-150500.5.24.1 + + sles:15.5 + + 23 Jan 2024 +
    • M
    + CVE-2023-6237 +
    + openssl-3 + + <3.0.8-150500.5.24.1 + + sles:15.5 + + 23 Jan 2024 +
    • L
    + CVE-2023-6237 +
    + openssl + + <3.0.12-r3 + + alpine:3.17 + + 17 Jan 2024 +
    • L
    + CVE-2023-6237 +
    + openssl + + <3.1.4-r4 + + alpine:3.18 + + 17 Jan 2024 +
    • L
    + CVE-2023-6237 +
    + openssl + + <3.1.4-r4 + + alpine:3.19 + + 17 Jan 2024 +
    • L
    + Resource Exhaustion +
    + openssl-perl + + * + + centos:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + openssl-perl + + * + + rhel:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + openssl-devel + + * + + centos:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + openssl-devel + + * + + rhel:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + openssl + + * + + centos:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + openssl + + * + + rhel:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + openssl-libs + + * + + centos:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + openssl-libs + + * + + rhel:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + edk2-tools-doc + + * + + centos:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + edk2-tools-doc + + * + + rhel:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + edk2-tools + + * + + centos:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + edk2-tools + + * + + rhel:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + edk2-aarch64 + + * + + centos:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + edk2-aarch64 + + * + + rhel:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + edk2 + + * + + centos:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + edk2 + + * + + rhel:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + edk2-ovmf + + * + + centos:9 + + 16 Jan 2024 +
    • L
    + Resource Exhaustion +
    + edk2-ovmf + + * + + rhel:9 + + 16 Jan 2024 +
    • M
    + Uncontrolled Resource Consumption ('Resource Exhaustion') +
    + pyopenssl + + [22.0.0,] + + pip + + 16 Jan 2024 +
    • M
    + Uncontrolled Resource Consumption ('Resource Exhaustion') +
    + openssl + + >=3.0.0 + + RubyGems + + 16 Jan 2024 +
    • M
    + Uncontrolled Resource Consumption ('Resource Exhaustion') +
    + openssl-src + + >=300.0.0+3.0.0 + + Cargo + + 16 Jan 2024 +
    • M
    + Uncontrolled Resource Consumption ('Resource Exhaustion') +
    + cryptography + + [35.0.0,] + + pip + + 16 Jan 2024 +
    • M
    + Uncontrolled Resource Consumption ('Resource Exhaustion') +
    + openssl + + [3.0.0,] + + Unmanaged (C/C++) + + 16 Jan 2024 +
    • L
    + CVE-2023-6237 +
    + openssl + + * + + debian:unstable + + 16 Jan 2024 +
    + + diff --git a/vulntotal/tests/test_data/snyk/html/5.html-expected.json b/vulntotal/tests/test_data/snyk/html/5.html-expected.json new file mode 100644 index 000000000..9b2a495a2 --- /dev/null +++ b/vulntotal/tests/test_data/snyk/html/5.html-expected.json @@ -0,0 +1,31 @@ +{ + "SNYK-SLES155-LIBOPENSSL3-6184655": "https://security.snyk.io/package/linux/sles:15.5/libopenssl3", + "SNYK-SLES155-LIBOPENSSL3DEVEL-6184653": "https://security.snyk.io/package/linux/sles:15.5/libopenssl-3-devel", + "SNYK-SLES155-OPENSSL3-6184652": "https://security.snyk.io/package/linux/sles:15.5/openssl-3", + "SNYK-ALPINE317-OPENSSL-6160001": "https://security.snyk.io/package/linux/alpine:3.17/openssl", + "SNYK-ALPINE318-OPENSSL-6160000": "https://security.snyk.io/package/linux/alpine:3.18/openssl", + "SNYK-ALPINE319-OPENSSL-6159994": "https://security.snyk.io/package/linux/alpine:3.19/openssl", + "SNYK-CENTOS9-OPENSSLPERL-6157924": "https://security.snyk.io/package/linux/centos:9/openssl-perl", + "SNYK-RHEL9-OPENSSLPERL-6157922": "https://security.snyk.io/package/linux/rhel:9/openssl-perl", + "SNYK-CENTOS9-OPENSSLDEVEL-6157920": "https://security.snyk.io/package/linux/centos:9/openssl-devel", + "SNYK-RHEL9-OPENSSLDEVEL-6157919": "https://security.snyk.io/package/linux/rhel:9/openssl-devel", + "SNYK-CENTOS9-OPENSSL-6157917": "https://security.snyk.io/package/linux/centos:9/openssl", + "SNYK-RHEL9-OPENSSL-6157915": "https://security.snyk.io/package/linux/rhel:9/openssl", + "SNYK-CENTOS9-OPENSSLLIBS-6157913": "https://security.snyk.io/package/linux/centos:9/openssl-libs", + "SNYK-RHEL9-OPENSSLLIBS-6157911": "https://security.snyk.io/package/linux/rhel:9/openssl-libs", + "SNYK-CENTOS9-EDK2TOOLSDOC-6157909": "https://security.snyk.io/package/linux/centos:9/edk2-tools-doc", + "SNYK-RHEL9-EDK2TOOLSDOC-6157908": "https://security.snyk.io/package/linux/rhel:9/edk2-tools-doc", + "SNYK-CENTOS9-EDK2TOOLS-6157906": "https://security.snyk.io/package/linux/centos:9/edk2-tools", + "SNYK-RHEL9-EDK2TOOLS-6157904": "https://security.snyk.io/package/linux/rhel:9/edk2-tools", + "SNYK-CENTOS9-EDK2AARCH64-6157902": "https://security.snyk.io/package/linux/centos:9/edk2-aarch64", + "SNYK-RHEL9-EDK2AARCH64-6157900": "https://security.snyk.io/package/linux/rhel:9/edk2-aarch64", + "SNYK-CENTOS9-EDK2-6157898": "https://security.snyk.io/package/linux/centos:9/edk2", + "SNYK-RHEL9-EDK2-6157896": "https://security.snyk.io/package/linux/rhel:9/edk2", + "SNYK-CENTOS9-EDK2OVMF-6157895": "https://security.snyk.io/package/linux/centos:9/edk2-ovmf", + "SNYK-RHEL9-EDK2OVMF-6157893": "https://security.snyk.io/package/linux/rhel:9/edk2-ovmf", + "SNYK-PYTHON-PYOPENSSL-6157250": "https://security.snyk.io/package/pip/pyopenssl", + "SNYK-RUBY-OPENSSL-6157246": "https://security.snyk.io/package/rubygems/openssl", + "SNYK-RUST-OPENSSLSRC-6157249": "https://security.snyk.io/package/cargo/openssl-src", + "SNYK-PYTHON-CRYPTOGRAPHY-6157248": "https://security.snyk.io/package/pip/cryptography", + "SNYK-DEBIANUNSTABLE-OPENSSL-6157245": "https://security.snyk.io/package/linux/debian:unstable/openssl" +} diff --git a/vulntotal/tests/test_snyk.py b/vulntotal/tests/test_snyk.py index f4f221b39..871d8a27b 100644 --- a/vulntotal/tests/test_snyk.py +++ b/vulntotal/tests/test_snyk.py @@ -52,6 +52,58 @@ def test_generate_package_advisory_url(self): ] util_tests.check_results_against_expected(results, expected) + def test_generate_purl(self): + package_advisory_urls = [ + "https://security.snyk.io/package/pip/jinja2", + "https://security.snyk.io/package/maven/org.apache.tomcat%3Atomcat", + "https://security.snyk.io/package/npm/semver-regex", + "https://security.snyk.io/package/npm/@urql%2Fnext", + "https://security.snyk.io/package/npm/@lobehub%2Fchat", + "https://security.snyk.io/package/npm/meshcentral", + "https://security.snyk.io/package/composer/bolt%2Fcore", + "https://security.snyk.io/package/linux/debain:11/trafficserver", + "https://security.snyk.io/package/linux/almalinux:8/rpm-plugin-fapolicyd", + "https://security.snyk.io/package/nuget/moment.js", + "https://security.snyk.io/package/cocoapods/ffmpeg", + "https://security.snyk.io/package/hex/coherence", + "https://security.snyk.io/package/rubygems/log4j-jars", + "https://security.snyk.io/package/golang/github.com%2Fgrafana%2Fgrafana%2Fpkg%2Fservices%2Fsqlstore%2Fmigrator", + "https://security.snyk.io/package/golang/github.com%2Fanswerdev%2Fanswer%2Finternal%2Frepo%2Factivity", + "https://security.snyk.io/package/golang/go.etcd.io%2Fetcd%2Fv3%2Fauth", + "https://security.snyk.io/package/golang/gopkg.in%2Fkubernetes%2Fkubernetes.v0%2Fpkg%2Fregistry%2Fpod", + "https://security.snyk.io/package/golang/gogs.io%2Fgogs%2Finternal%2Fdb", + "https://security.snyk.io/package/golang/golang.org%2Fx%2Fcrypto%2Fssh", + ] + + results = [ + PackageURL.to_string(snyk.generate_purl(package_advisory_url)) + for package_advisory_url in package_advisory_urls + ] + + expected = [ + "pkg:pypi/jinja2", + "pkg:maven/org.apache.tomcat/tomcat", + "pkg:npm/semver-regex", + "pkg:npm/%40urql/next", + "pkg:npm/%40lobehub/chat", + "pkg:npm/meshcentral", + "pkg:composer/bolt/core", + "pkg:linux/trafficserver", + "pkg:linux/rpm-plugin-fapolicyd", + "pkg:nuget/moment.js", + "pkg:cocoapods/ffmpeg", + "pkg:hex/coherence", + "pkg:gem/log4j-jars", + "pkg:golang/github.com/grafana/grafana/pkg/services/sqlstore/migrator", + "pkg:golang/github.com/answerdev/answer/internal/repo/activity", + "pkg:golang/go.etcd.io/etcd/v3/auth", + "pkg:golang/gopkg.in/kubernetes/kubernetes.v0/pkg/registry/pod", + "pkg:golang/gogs.io/gogs/internal/db", + "pkg:golang/golang.org/x/crypto/ssh", + ] + + util_tests.check_results_against_expected(results, expected) + def test_parse_html_advisory_0(self): file = self.get_test_loc("html/0.html") with open(file) as f: @@ -91,3 +143,19 @@ def test_parse_html_advisory_3(self): ).to_dict() expected_file = f"{file}-expected.json" util_tests.check_results_against_json(result, expected_file) + + def test_parse_cve_advisory_html_0(self): + file = self.get_test_loc("html/4.html") + with open(file) as f: + page = f.read() + result = snyk.parse_cve_advisory_html(page) + expected_file = f"{file}-expected.json" + util_tests.check_results_against_json(result, expected_file) + + def test_parse_cve_advisory_html_1(self): + file = self.get_test_loc("html/5.html") + with open(file) as f: + page = f.read() + result = snyk.parse_cve_advisory_html(page) + expected_file = f"{file}-expected.json" + util_tests.check_results_against_json(result, expected_file) diff --git a/vulntotal/vulntotal_cli.py b/vulntotal/vulntotal_cli.py index 4106b0f98..c65007ba8 100755 --- a/vulntotal/vulntotal_cli.py +++ b/vulntotal/vulntotal_cli.py @@ -304,22 +304,22 @@ def prettyprint(purl, datasources, pagination, no_threading): def group_by_cve(vulnerabilities): grouped_by_cve = {} - nocve = [] - noadvisory = [] + no_cve = [] + no_advisory = [] for datasource, advisories in vulnerabilities.items(): if not advisories: - noadvisory.append([datasource.upper(), "", "", ""]) + no_advisory.append([datasource.upper(), "", "", ""]) for advisory in advisories: cve = next((x for x in advisory.aliases if x.startswith("CVE")), None) if not cve: - nocve.append(formatted_row(datasource, advisory)) + no_cve.append(formatted_row(datasource, advisory)) continue if cve not in grouped_by_cve: grouped_by_cve[cve] = [] grouped_by_cve[cve].append(formatted_row(datasource, advisory)) - grouped_by_cve["NOCVE"] = nocve - grouped_by_cve["NOADVISORY"] = noadvisory + grouped_by_cve["NOCVE"] = no_cve + grouped_by_cve["NOADVISORY"] = no_advisory return grouped_by_cve diff --git a/vulntotal/vulntotal_utils.py b/vulntotal/vulntotal_utils.py index 787ff9d92..79d866e05 100644 --- a/vulntotal/vulntotal_utils.py +++ b/vulntotal/vulntotal_utils.py @@ -80,14 +80,14 @@ def parse_constraint(constraint): return constraint[-1], constraint[:-1] -def github_constraints_satisfied(github_constrain, version): +def github_constraints_satisfied(github_constraint, version): """ Return True or False depending on whether the given version satisfies the github constraint For example: >>> assert github_constraints_satisfied(">= 7.0.0, <= 7.6.57", "7.1.1") == True >>> assert github_constraints_satisfied(">= 10.4.0, <= 10.4.1", "10.6.0") == False """ - gh_constraints = github_constrain.strip().replace(" ", "") + gh_constraints = github_constraint.strip().replace(" ", "") constraints = gh_constraints.split(",") for constraint in constraints: gh_comparator, gh_version = parse_constraint(constraint) @@ -98,15 +98,15 @@ def github_constraints_satisfied(github_constrain, version): return True -def snky_constraints_satisfied(snyk_constrain, version): +def snyk_constraints_satisfied(snyk_constraint, version): """ Return True or False depending on whether the given version satisfies the snyk constraint For example: - >>> assert snky_constraints_satisfied(">=4.0.0, <4.0.10.16", "4.0.10.15") == True - >>> assert snky_constraints_satisfied(" >=4.1.0, <4.4.15.7", "4.0.10.15") == False - >>> assert snky_constraints_satisfied("[3.0.0,3.1.25)", "3.0.2") == True + >>> assert snyk_constraints_satisfied(">=4.0.0, <4.0.10.16", "4.0.10.15") == True + >>> assert snyk_constraints_satisfied(" >=4.1.0, <4.4.15.7", "4.0.10.15") == False + >>> assert snyk_constraints_satisfied("[3.0.0,3.1.25)", "3.0.2") == True """ - snyk_constraints = snyk_constrain.strip().replace(" ", "") + snyk_constraints = snyk_constraint.strip().replace(" ", "") constraints = snyk_constraints.split(",") for constraint in constraints: snyk_comparator, snyk_version = parse_constraint(constraint) @@ -117,7 +117,7 @@ def snky_constraints_satisfied(snyk_constrain, version): return True -def gitlab_constraints_satisfied(gitlab_constrain, version): +def gitlab_constraints_satisfied(gitlab_constraint, version): """ Return True or False depending on whether the given version satisfies the gitlab constraint For example: @@ -128,7 +128,7 @@ def gitlab_constraints_satisfied(gitlab_constrain, version): >>> assert gitlab_constraints_satisfied( ">=1.5,<1.5.2", "2.2") == False """ - gitlab_constraints = gitlab_constrain.strip() + gitlab_constraints = gitlab_constraint.strip() if gitlab_constraints.startswith(("[", "(")): # transform "[7.0.0,7.0.11),[7.2.0,7.2.4)" -> [ "[7.0.0,7.0.11)", "[7.2.0,7.2.4)" ] splitted = gitlab_constraints.split(",") @@ -144,10 +144,10 @@ def gitlab_constraints_satisfied(gitlab_constrain, version): for constraint in constraints: is_constraint_satisfied = True - for subcontraint in constraint.strip().split(delimiter): - if not subcontraint: + for subconstraint in constraint.strip().split(delimiter): + if not subconstraint: continue - gitlab_comparator, gitlab_version = parse_constraint(subcontraint.strip()) + gitlab_comparator, gitlab_version = parse_constraint(subconstraint.strip()) if not gitlab_version: continue if not compare(