Exemple #1
0
def yaml_file_to_advisory(yaml_path):
    impacted_packages = []
    resolved_packages = []
    references = []

    data = load_yaml(yaml_path)
    vuln_id = data["vulnerability_id"]
    summary = "\n".join([note["text"] for note in data["notes"]])

    for entry in data.get("artifacts", []):
        package = PackageURL.from_string(entry["id"])
        if entry["affected"]:
            impacted_packages.append(package)
        else:
            resolved_packages.append(package)

    for fix in data.get("fixes", []):
        for commit in fix["commits"]:
            references.append(
                Reference(url=f"{commit['repository']}/{commit['id']}"))

    return Advisory(
        vulnerability_id=vuln_id,
        summary=summary,
        affected_packages=nearest_patched_package(impacted_packages,
                                                  resolved_packages),
        references=references,
    )
Exemple #2
0
    def to_advisories(xml_response: str) -> Set[Advisory]:
        advisories = []
        pkg_name = "openssl"
        pkg_type = "generic"
        root = ET.fromstring(xml_response)
        for element in root:
            if element.tag == "issue":
                cve_id = ""
                summary = ""
                safe_pkg_versions = []
                vuln_pkg_versions = []
                ref_urls = []
                for info in element:

                    if info.tag == "cve":
                        if info.attrib.get("name"):
                            cve_id = "CVE-" + info.attrib.get("name")

                        else:
                            continue

                    if info.tag == "affects":
                        # Vulnerable package versions
                        vuln_pkg_versions.append(info.attrib.get("version"))

                    if info.tag == "fixed":
                        # Fixed package versions
                        safe_pkg_versions.append(info.attrib.get("version"))

                        if info:
                            commit_hash = info[0].attrib["hash"]
                            ref_urls.append(
                                Reference(
                                    url=
                                    "https://github.com/openssl/openssl/commit/"
                                    + commit_hash))
                    if info.tag == "description":
                        # Description
                        summary = re.sub(r"\s+", " ", info.text).strip()

                safe_purls = [
                    PackageURL(name=pkg_name, type=pkg_type, version=version)
                    for version in safe_pkg_versions
                ]
                vuln_purls = [
                    PackageURL(name=pkg_name, type=pkg_type, version=version)
                    for version in vuln_pkg_versions
                ]

                advisory = Advisory(
                    vulnerability_id=cve_id,
                    summary=summary,
                    affected_packages=nearest_patched_package(
                        vuln_purls, safe_purls),
                    references=ref_urls,
                )
                advisories.append(advisory)

        return advisories
Exemple #3
0
    def _parse(self, pkg_name: str, records: Mapping[str,
                                                     Any]) -> List[Advisory]:
        advisories = []

        for cve_id, record in records.items():
            impacted_purls, resolved_purls = [], []
            if not cve_id.startswith("CVE"):
                continue

            # vulnerabilities starting with something else may not be public yet
            # see for instance https://web.archive.org/web/20201215213725/https://security-tracker.debian.org/tracker/TEMP-0000000-A2EB44  # nopep8
            # TODO: this would need to be revisited though to ensure we are not missing out on anything  # nopep8

            for release_name, release_record in record["releases"].items():
                if not release_record.get("repositories",
                                          {}).get(release_name):
                    continue

                purl = PackageURL(
                    name=pkg_name,
                    type="deb",
                    namespace="debian",
                    version=release_record["repositories"][release_name],
                    qualifiers={"distro": release_name},
                )

                if release_record.get("status", "") == "resolved":
                    resolved_purls.append(purl)
                else:
                    impacted_purls.append(purl)

                if "fixed_version" in release_record:
                    resolved_purls.append(
                        PackageURL(
                            name=pkg_name,
                            type="deb",
                            namespace="debian",
                            version=release_record["fixed_version"],
                            qualifiers={"distro": release_name},
                        ))

            references = []
            debianbug = record.get("debianbug")
            if debianbug:
                bug_url = f"https://bugs.debian.org/cgi-bin/bugreport.cgi?bug={debianbug}"
                references.append(
                    Reference(url=bug_url, reference_id=debianbug))
            # print(nearest_patched_package(impacted_purls, resolved_purls))
            advisories.append(
                Advisory(
                    vulnerability_id=cve_id,
                    affected_packages=nearest_patched_package(
                        impacted_purls, resolved_purls),
                    summary=record.get("description", ""),
                    references=references,
                ))

        return advisories
    def to_advisories(self, apache_tomcat_advisory_html):
        advisories = []
        page_soup = BeautifulSoup(apache_tomcat_advisory_html, features="lxml")
        pageh3s = page_soup.find_all("h3")
        vuln_headings = [
            i for i in pageh3s if "Fixed in Apache Tomcat" in i.text
        ]
        for data in vuln_headings:
            fixed_version = data.text.split(
                "Fixed in Apache Tomcat")[-1].strip()
            details_div = data.find_next_sibling()

            for anchor_tag in details_div.find_all("a"):
                if "cve.mitre.org" not in anchor_tag["href"]:
                    continue

                cve_id = re.search(r"CVE-\d*-\d*", anchor_tag.text).group()
                references = []
                affected_packages = []
                paragraph = anchor_tag.find_parent()

                while paragraph and "Affects:" not in paragraph.text:
                    for ref in paragraph.find_all("a"):
                        references.append(Reference(url=ref["href"]))

                    paragraph = paragraph.find_next_sibling()

                if not paragraph:
                    # At the end of details_div
                    continue

                for version_range in parse_version_ranges(paragraph.text):
                    affected_packages.extend([
                        PackageURL(type="maven",
                                   namespace="apache",
                                   name="tomcat",
                                   version=version) for version in
                        self.version_api.get("org.apache.tomcat:tomcat")
                        if MavenVersion(version) in version_range
                    ])

                fixed_package = [
                    PackageURL(type="maven",
                               namespace="apache",
                               name="tomcat",
                               version=fixed_version)
                ]

                advisories.append(
                    Advisory(
                        summary="",
                        affected_packages=nearest_patched_package(
                            affected_packages, fixed_package),
                        vulnerability_id=cve_id,
                        references=references,
                    ))

        return advisories
Exemple #5
0
    def _parse(self, record) -> List[Advisory]:
        advisories = []

        for cve_id in record["issues"]:
            affected_packages = []
            for name in record["packages"]:
                impacted_purls, resolved_purls = [], []
                impacted_purls.append(
                    PackageURL(
                        name=name,
                        type="pacman",
                        namespace="archlinux",
                        version=record["affected"],
                    )
                )

                if record["fixed"]:
                    resolved_purls.append(
                        PackageURL(
                            name=name,
                            type="pacman",
                            namespace="archlinux",
                            version=record["fixed"],
                        )
                    )
                affected_packages.extend(nearest_patched_package(impacted_purls, resolved_purls))

            references = []
            references.append(
                Reference(
                    reference_id=record["name"],
                    url="https://security.archlinux.org/{}".format(record["name"]),
                    severities=[
                        VulnerabilitySeverity(
                            system=scoring_systems["avgs"], value=record["severity"]
                        )
                    ],
                )
            )

            for ref in record["advisories"]:
                references.append(
                    Reference(
                        reference_id=ref,
                        url="https://security.archlinux.org/{}".format(ref),
                    )
                )

            advisories.append(
                Advisory(
                    vulnerability_id=cve_id,
                    summary="",
                    affected_packages=affected_packages,
                    references=references,
                )
            )

        return advisories
    def process_file(self, path) -> List[Advisory]:
        record = load_yaml(path)
        package_name = record.get("gem")
        if not package_name:
            return

        if "cve" in record:
            cve_id = "CVE-{}".format(record["cve"])
        else:
            return

        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)
        safe_versions, affected_versions = self.categorize_versions(
            all_vers, safe_version_ranges)

        impacted_purls = [
            PackageURL(
                name=package_name,
                type="gem",
                version=version,
            ) for version in affected_versions
        ]

        resolved_purls = [
            PackageURL(
                name=package_name,
                type="gem",
                version=version,
            ) for version in safe_versions
        ]

        references = []
        if record.get("url"):
            references.append(Reference(url=record.get("url")))

        return Advisory(
            summary=record.get("description", ""),
            affected_packages=nearest_patched_package(impacted_purls,
                                                      resolved_purls),
            references=references,
            vulnerability_id=cve_id,
        )
    def to_advisories(self, data):
        advisories = []
        soup = BeautifulSoup(data, features="lxml")
        vuln_list = soup.select("li p")

        # Example value of `vuln_list` :
        # ['Excessive CPU usage in HTTP/2 with small window updates',
        #  <br/>,
        #  'Severity: medium',
        #  <br/>,
        #  <a href="http://mailman.nginx.org/pipermail/nginx-announce/2019/000249.html">Advisory</a>,  # nopep8
        #  <br/>,
        #  <a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9511">CVE-2019-9511</a>,
        #  <br/>,
        #  'Not vulnerable: 1.17.3+, 1.16.1+',
        #  <br/>,
        #  'Vulnerable: 1.9.5-1.17.2']

        for vuln_info in vuln_list:
            references = []
            for index, child in enumerate(vuln_info.children):
                if index == 0:
                    # type of this child is bs4.element.NavigableString.
                    # Hence cast it into standard string
                    summary = str(child)
                    continue

                #  hasattr(child, "attrs") == False for bs4.element.NavigableString
                if hasattr(child, "attrs") and child.attrs.get("href"):
                    link = child.attrs["href"]
                    references.append(Reference(url=link))
                    if "cve.mitre.org" in link:
                        cve_id = child.text
                    continue

                if "Not vulnerable" in child:
                    fixed_packages = self.extract_fixed_pkgs(child)
                    continue

                if "Vulnerable" in child:
                    vulnerable_packages = self.extract_vuln_pkgs(child)
                    continue

            advisories.append(
                Advisory(
                    vulnerability_id=cve_id,
                    summary=summary,
                    affected_packages=nearest_patched_package(vulnerable_packages, fixed_packages),
                )
            )

        return advisories
    def to_advisory(self, advisory_page):
        advisories = []
        advisory_page = BeautifulSoup(advisory_page, features="lxml")
        cve_section_beginnings = advisory_page.find_all("h2")
        for cve_section_beginning in cve_section_beginnings:
            cve_id = cve_section_beginning.text.split("\n")[0]
            cve_description_paragraph = cve_section_beginning.find_next_sibling(
                "p")
            cve_data_table = cve_section_beginning.find_next_sibling("table")
            cve_data_table_rows = cve_data_table.find_all("tr")
            affected_versions_row = cve_data_table_rows[0]
            fixed_versions_row = cve_data_table_rows[1]
            affected_version_ranges = to_version_ranges(
                affected_versions_row.find_all("td")[1].text)
            fixed_version_ranges = to_version_ranges(
                fixed_versions_row.find_all("td")[1].text)

            fixed_packages = [
                PackageURL(type="apache", name="kafka", version=version)
                for version in self.version_api.get("apache/kafka") if any([
                    MavenVersion(version) in version_range
                    for version_range in fixed_version_ranges
                ])
            ]

            affected_packages = [
                PackageURL(type="apache", name="kafka", version=version)
                for version in self.version_api.get("apache/kafka") if any([
                    MavenVersion(version) in version_range
                    for version_range in affected_version_ranges
                ])
            ]

            advisories.append(
                Advisory(
                    vulnerability_id=cve_id,
                    summary=cve_description_paragraph.text,
                    affected_packages=nearest_patched_package(
                        affected_packages, fixed_packages),
                    references=[
                        Reference(url=ASF_PAGE_URL),
                        Reference(
                            url=
                            f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve_id}",
                            reference_id=cve_id,
                        ),
                    ],
                ))
        return advisories
Exemple #9
0
    def updated_advisories(self) -> Set[Advisory]:
        advisories = []

        for package_name in self._api_response:
            if package_name == "$meta" or package_name == "cumin":
                # This is the first entry in the data feed. It contains metadata of the feed.
                # Skip it. The 'cumin' entry is wrong
                continue

            try:
                validate_schema(self._api_response[package_name])

            except Exception as e:
                logger.error(e)
                continue

            all_package_versions = self.versions.get(package_name)
            if not len(all_package_versions):
                # PyPi does not have data about this package, we skip these
                continue

            for advisory in self._api_response[package_name]:
                if advisory["cve"]:
                    # Check on advisory["cve"] instead of using `get` because it can have null value
                    cve_ids = re.findall(r"CVE-\d+-\d+", advisory["cve"])
                else:
                    continue

                impacted_purls, resolved_purls = categorize_versions(
                    package_name, all_package_versions, advisory["specs"]
                )

                reference = [Reference(reference_id=advisory["id"])]

                for cve_id in cve_ids:
                    advisories.append(
                        Advisory(
                            vulnerability_id=cve_id,
                            summary=advisory["advisory"],
                            references=reference,
                            affected_packages=nearest_patched_package(
                                impacted_purls, resolved_purls
                            ),
                        )
                    )

        return advisories
Exemple #10
0
    def process_file(self, path):
        yaml_file = load_yaml(path)
        pkg_name = yaml_file["package"]
        safe_pkg_versions = []
        vuln_pkg_versions = []
        if not yaml_file.get("patched_versions"):
            yaml_file["patched_versions"] = []

        if not yaml_file.get("unaffected_versions"):
            yaml_file["unaffected_versions"] = []

        safe_pkg_versions, vuln_pkg_versions = self.get_versions_for_pkg_from_range_list(
            yaml_file["patched_versions"] + yaml_file["unaffected_versions"],
            pkg_name,
        )

        if yaml_file.get("cve"):
            cve_id = "CVE-" + yaml_file["cve"]
        else:
            cve_id = ""

        safe_purls = []
        vuln_purls = []

        safe_purls = [
            PackageURL(name=pkg_name, type="hex", version=version) for version in safe_pkg_versions
        ]

        vuln_purls = [
            PackageURL(name=pkg_name, type="hex", version=version) for version in vuln_pkg_versions
        ]

        references = [
            Reference(
                reference_id=yaml_file["id"],
            ),
            Reference(
                url=yaml_file["link"],
            ),
        ]

        return Advisory(
            summary=yaml_file["description"],
            affected_packages=nearest_patched_package(vuln_purls, safe_purls),
            vulnerability_id=cve_id,
            references=references,
        )
Exemple #11
0
    def process_file(self, file):
        xml_data = {}
        xml_root = ET.parse(file).getroot()
        glsa = "GLSA-" + xml_root.attrib["id"]
        vuln_reference = [
            Reference(
                reference_id=glsa,
                url="https://security.gentoo.org/glsa/{}".format(
                    xml_root.attrib["id"]),
            )
        ]

        for child in xml_root:
            if child.tag == "references":
                xml_data["cves"] = self.cves_from_reference(child)

            if child.tag == "synopsis":
                xml_data["description"] = child.text

            if child.tag == "affected":
                (
                    xml_data["affected_purls"],
                    xml_data["unaffected_purls"],
                ) = self.affected_and_safe_purls(child)
                xml_data["unaffected_purls"] = list(
                    xml_data["unaffected_purls"])
                xml_data["affected_purls"] = list(xml_data["affected_purls"])

        advisory_list = []
        # It is very inefficient, to create new Advisory for each CVE
        # this way, but there seems no alternative.
        for cve in xml_data["cves"]:
            advisory = Advisory(
                vulnerability_id=cve,
                summary=xml_data["description"],
                affected_packages=nearest_patched_package(
                    xml_data["affected_purls"], xml_data["unaffected_purls"]),
                references=vuln_reference,
            )
            advisory_list.append(advisory)
        return advisory_list
Exemple #12
0
    def process_file(self, file) -> List[Advisory]:

        record = load_json(file)
        advisories = []
        package_name = record["module_name"].strip()
        all_versions = self.versions.get(package_name)
        aff_range = record.get("vulnerable_versions")
        if not aff_range:
            aff_range = ""
        fixed_range = record.get("patched_versions")
        if not fixed_range:
            fixed_range = ""

        if aff_range == "*" or fixed_range == "*":
            return []

        impacted_versions, resolved_versions = categorize_versions(
            all_versions, aff_range, fixed_range
        )

        impacted_purls = _versions_to_purls(package_name, impacted_versions)
        resolved_purls = _versions_to_purls(package_name, resolved_versions)
        vuln_reference = [
            Reference(
                url=NPM_URL.format(f'/-/npm/v1/advisories/{record["id"]}'),
                reference_id=record["id"],
            )
        ]

        for cve_id in record.get("cves") or [""]:
            advisories.append(
                Advisory(
                    summary=record.get("overview", ""),
                    vulnerability_id=cve_id,
                    affected_packages=nearest_patched_package(impacted_purls, resolved_purls),
                    references=vuln_reference,
                )
            )
        return advisories
    def _load_advisory(self, path: str) -> Optional[Advisory]:
        record = get_advisory_data(path)
        advisory = record.get("advisory", {})
        crate_name = advisory["package"]
        references = []
        if advisory.get("url"):
            references.append(Reference(url=advisory["url"]))

        all_versions = self.crates_api.get(crate_name)

        # FIXME: Avoid wildcard version ranges for now.
        # See https://github.com/RustSec/advisory-db/discussions/831
        affected_ranges = [
            VersionSpecifier.from_scheme_version_spec_string("semver", r)
            for r in chain.from_iterable(
                record.get("affected", {}).get("functions", {}).values())
            if r != "*"
        ]

        unaffected_ranges = [
            VersionSpecifier.from_scheme_version_spec_string("semver", r)
            for r in record.get("versions", {}).get("unaffected", [])
            if r != "*"
        ]
        resolved_ranges = [
            VersionSpecifier.from_scheme_version_spec_string("semver", r)
            for r in record.get("versions", {}).get("patched", []) if r != "*"
        ]

        unaffected, affected = categorize_versions(all_versions,
                                                   unaffected_ranges,
                                                   affected_ranges,
                                                   resolved_ranges)

        impacted_purls = [
            PackageURL(type="cargo", name=crate_name, version=v)
            for v in affected
        ]
        resolved_purls = [
            PackageURL(type="cargo", name=crate_name, version=v)
            for v in unaffected
        ]

        cve_id = None
        if "aliases" in advisory:
            for alias in advisory["aliases"]:
                if alias.startswith("CVE-"):
                    cve_id = alias
                    break

        references.append(
            Reference(
                reference_id=advisory["id"],
                url="https://rustsec.org/advisories/{}.html".format(
                    advisory["id"]),
            ))

        return Advisory(
            summary=advisory.get("description", ""),
            affected_packages=nearest_patched_package(impacted_purls,
                                                      resolved_purls),
            vulnerability_id=cve_id,
            references=references,
        )
Exemple #14
0
    def process_response(self) -> List[Advisory]:
        adv_list = []
        for ecosystem in self.advisories:
            self.set_version_api(ecosystem)
            pkg_type = self.version_api.package_type
            for resp_page in self.advisories[ecosystem]:
                for adv in resp_page["data"]["securityVulnerabilities"][
                        "edges"]:
                    name = adv["node"]["package"]["name"]

                    if self.process_name(ecosystem, name):
                        ns, pkg_name = self.process_name(ecosystem, name)
                        aff_range = adv["node"]["vulnerableVersionRange"]
                        aff_vers, unaff_vers = self.categorize_versions(
                            self.version_api.package_type, aff_range,
                            self.version_api.get(name))
                        affected_purls = [
                            PackageURL(name=pkg_name,
                                       namespace=ns,
                                       version=version,
                                       type=pkg_type) for version in aff_vers
                        ]

                        unaffected_purls = [
                            PackageURL(name=pkg_name,
                                       namespace=ns,
                                       version=version,
                                       type=pkg_type) for version in unaff_vers
                        ]
                    else:
                        affected_purls = []
                        unaffected_purls = []

                    cve_ids = set()
                    references = self.extract_references(
                        adv["node"]["advisory"]["references"])
                    vuln_desc = adv["node"]["advisory"]["summary"]

                    for identifier in adv["node"]["advisory"]["identifiers"]:
                        # collect CVEs
                        if identifier["type"] == "CVE":
                            cve_ids.add(identifier["value"])

                        # attach the GHSA with severity score
                        if identifier["type"] == "GHSA":
                            for ref in references:
                                if ref.reference_id == identifier["value"]:
                                    ref.severities = [
                                        VulnerabilitySeverity(
                                            system=scoring_systems[
                                                "cvssv3.1_qr"],
                                            value=adv["node"]["advisory"]
                                            ["severity"],
                                        )
                                    ]
                                    # Each Node has only one GHSA, hence exit after attaching
                                    # score to this GHSA
                                    break

                    for cve_id in cve_ids:
                        adv_list.append(
                            Advisory(
                                vulnerability_id=cve_id,
                                summary=vuln_desc,
                                affected_packages=nearest_patched_package(
                                    affected_purls, unaffected_purls),
                                references=references,
                            ))
        return adv_list
    def get_data_from_xml_doc(self, xml_doc: ET.ElementTree, pkg_metadata={}) -> List[Advisory]:
        """
        The orchestration method of the OvalDataSource. This method breaks an
        OVAL xml ElementTree into a list of `Advisory`.

        Note: pkg_metadata is a mapping of Package URL data that MUST INCLUDE
        "type" key.

        Example value of pkg_metadata:
                {"type":"deb","qualifiers":{"distro":"buster"} }
        """

        all_adv = []
        oval_doc = OvalParser(self.translations, xml_doc)
        raw_data = oval_doc.get_data()
        all_pkgs = self._collect_pkgs(raw_data)
        self.set_api(all_pkgs)

        # convert definition_data to Advisory objects
        for definition_data in raw_data:
            # These fields are definition level, i.e common for all elements
            # connected/linked to an OvalDefinition
            vuln_id = definition_data["vuln_id"]
            description = definition_data["description"]
            references = [Reference(url=url) for url in definition_data["reference_urls"]]
            affected_packages = []
            for test_data in definition_data["test_data"]:
                for package_name in test_data["package_list"]:
                    if package_name and len(package_name) >= 50:
                        continue

                    affected_version_range = test_data["version_ranges"] or set()
                    version_class = version_class_by_package_type[pkg_metadata["type"]]
                    version_scheme = version_class.scheme

                    affected_version_range = VersionSpecifier.from_scheme_version_spec_string(
                        version_scheme, affected_version_range
                    )
                    all_versions = self.pkg_manager_api.get(package_name)

                    # FIXME: what is this 50 DB limit? that's too small for versions
                    # FIXME: we should not drop data this way
                    # This filter is for filtering out long versions.
                    # 50 is limit because that's what db permits atm.
                    all_versions = [version for version in all_versions if len(version) < 50]
                    if not all_versions:
                        continue

                    affected_purls = []
                    safe_purls = []
                    for version in all_versions:
                        purl = self.create_purl(
                            pkg_name=package_name,
                            pkg_version=version,
                            pkg_data=pkg_metadata,
                        )
                        if version_class(version) in affected_version_range:
                            affected_purls.append(purl)
                        else:
                            safe_purls.append(purl)

                    affected_packages.extend(
                        nearest_patched_package(affected_purls, safe_purls),
                    )

            all_adv.append(
                Advisory(
                    summary=description,
                    affected_packages=affected_packages,
                    vulnerability_id=vuln_id,
                    references=references,
                )
            )
        return all_adv
Exemple #16
0
    def process_file(self, path):

        advisories = []

        data = self.get_data_from_md(path)

        releases = []
        if data.get("releases"):
            for release in data["releases"]:
                # If it is of form "All versions prior to x"
                if "All releases" in release:
                    release = release.strip()
                    release = release.split(" ")
                    releases.append("<" + release[4])
                # If it is of form "a to b"
                elif "to" in release:
                    release = release.strip()
                    release = release.split(" ")
                    lbound = ">=" + release[0]
                    ubound = "<=" + release[2]
                    releases.append(lbound + "," + ubound)
                # If it is a single release
                elif is_release(release):
                    releases.append(release)

        data["release_ranges"] = releases

        if not data.get("cves"):
            data["cves"] = [""]

        for cve_id in data["cves"]:

            if not cve_id.startswith("CVE"):
                cve_id = ""

            safe_pkg_versions = []
            vuln_pkg_versions = []

            if not data.get("release_ranges"):
                data["release_ranges"] = []

            safe_pkg_versions, vuln_pkg_versions = self.get_pkg_versions_from_ranges(
                data["release_ranges"])

            affected_packages = []

            safe_purls_golang = [
                PackageURL(type="golang", name="istio", version=version)
                for version in safe_pkg_versions
            ]

            vuln_purls_golang = [
                PackageURL(type="golang", name="istio", version=version)
                for version in vuln_pkg_versions
            ]

            affected_packages.extend(
                nearest_patched_package(vuln_purls_golang, safe_purls_golang))

            safe_purls_github = [
                PackageURL(type="github", name="istio", version=version)
                for version in safe_pkg_versions
            ]

            vuln_purls_github = [
                PackageURL(type="github", name="istio", version=version)
                for version in vuln_pkg_versions
            ]

            affected_packages.extend(
                nearest_patched_package(vuln_purls_github, safe_purls_github))

            advisories.append(
                Advisory(
                    vulnerability_id=cve_id,
                    summary=data["description"],
                    affected_packages=affected_packages,
                ))

        return advisories
    def process_file(self, path):

        advisories = []

        data = self.get_data_from_md(path)

        releases = []
        if data.get("releases"):
            for release in data["releases"]:
                # If it is of form "All releases prior to x"
                if "All releases prior" in release:
                    release = release.strip()
                    release = release.split(" ")
                    releases.append("<" + release[4])

                # Eg. 'All releases 1.5 and later'
                elif "All releases" in release and "and later" in release:
                    release = release.split()[2].strip()
                    releases.append(f">={release}")

                elif "to" in release:
                    release = release.strip()
                    release = release.split(" ")
                    lbound = ">=" + release[0]
                    ubound = "<=" + release[2]
                    releases.append(lbound + "," + ubound)
                # If it is a single release
                elif is_release(release):
                    releases.append(release)

        data["release_ranges"] = releases

        if not data.get("cves"):
            data["cves"] = [""]

        for cve_id in data["cves"]:

            if not cve_id.startswith("CVE"):
                cve_id = ""

            safe_pkg_versions = []
            vuln_pkg_versions = []

            if not data.get("release_ranges"):
                data["release_ranges"] = []

            safe_pkg_versions, vuln_pkg_versions = self.get_pkg_versions_from_ranges(
                data["release_ranges"])

            affected_packages = []

            safe_purls_golang = [
                PackageURL(type="golang", name="istio", version=version)
                for version in safe_pkg_versions
            ]

            vuln_purls_golang = [
                PackageURL(type="golang", name="istio", version=version)
                for version in vuln_pkg_versions
            ]

            affected_packages.extend(
                nearest_patched_package(vuln_purls_golang, safe_purls_golang))

            safe_purls_github = [
                PackageURL(type="github", name="istio", version=version)
                for version in safe_pkg_versions
            ]

            vuln_purls_github = [
                PackageURL(type="github", name="istio", version=version)
                for version in vuln_pkg_versions
            ]

            affected_packages.extend(
                nearest_patched_package(vuln_purls_github, safe_purls_github))

            advisories.append(
                Advisory(
                    vulnerability_id=cve_id,
                    summary=data["description"],
                    affected_packages=affected_packages,
                    references=[
                        Reference(
                            reference_id=data["title"],
                            url=
                            f"https://istio.io/latest/news/security/{data['title']}/",
                        )
                    ],
                ))

        return advisories
def to_advisory(advisory_data):
    affected_purls = []
    if advisory_data.get("affected_packages"):
        for rpm in advisory_data["affected_packages"]:
            purl = rpm_to_purl(rpm)
            if purl:
                affected_purls.append(purl)

    references = []
    bugzilla = advisory_data.get("bugzilla")
    if bugzilla:
        url = "https://bugzilla.redhat.com/show_bug.cgi?id={}".format(bugzilla)
        bugzilla_data = requests_session.get(
            f"https://bugzilla.redhat.com/rest/bug/{bugzilla}").json()
        if (bugzilla_data.get("bugs") and len(bugzilla_data["bugs"])
                and bugzilla_data["bugs"][0].get("severity")):
            bugzilla_severity_val = bugzilla_data["bugs"][0]["severity"]
            bugzilla_severity = VulnerabilitySeverity(
                system=scoring_systems["rhbs"],
                value=bugzilla_severity_val,
            )

            references.append(
                Reference(
                    severities=[bugzilla_severity],
                    url=url,
                    reference_id=bugzilla,
                ))

    for rh_adv in advisory_data["advisories"]:
        # RH provides 3 types of advisories RHSA, RHBA, RHEA. Only RHSA's contain severity score.
        # See https://access.redhat.com/articles/2130961 for more details.

        if "RHSA" in rh_adv.upper():
            rhsa_data = requests_session.get(
                f"https://access.redhat.com/hydra/rest/securitydata/cvrf/{rh_adv}.json"
            ).json()  # nopep8

            rhsa_aggregate_severities = []
            if rhsa_data.get("cvrfdoc"):
                # not all RHSA errata have a corresponding CVRF document
                value = rhsa_data["cvrfdoc"]["aggregate_severity"]
                rhsa_aggregate_severities.append(
                    VulnerabilitySeverity(
                        system=scoring_systems["rhas"],
                        value=value,
                    ))

            references.append(
                Reference(
                    severities=rhsa_aggregate_severities,
                    url="https://access.redhat.com/errata/{}".format(rh_adv),
                    reference_id=rh_adv,
                ))

        else:
            references.append(
                Reference(severities=[], url=url, reference_id=rh_adv))

    redhat_scores = []
    cvssv3_score = advisory_data.get("cvss3_score")
    if cvssv3_score:
        redhat_scores.append(
            VulnerabilitySeverity(
                system=scoring_systems["cvssv3"],
                value=cvssv3_score,
            ))

    cvssv3_vector = advisory_data.get("cvss3_scoring_vector")
    if cvssv3_vector:
        redhat_scores.append(
            VulnerabilitySeverity(
                system=scoring_systems["cvssv3_vector"],
                value=cvssv3_vector,
            ))

    references.append(
        Reference(severities=redhat_scores, url=advisory_data["resource_url"]))
    return Advisory(
        vulnerability_id=advisory_data["CVE"],
        summary=advisory_data["bugzilla_description"],
        affected_packages=nearest_patched_package(affected_purls, []),
        references=references,
    )
    def to_advisory(self, data):
        cve = data["CVE_data_meta"]["ID"]
        descriptions = data["description"]["description_data"]
        description = None
        for desc in descriptions:
            if desc["lang"] == "eng":
                description = desc.get("value")
                break

        severities = []
        impacts = data.get("impact", [])
        for impact in impacts:
            value = impact.get("other")
            if value:
                severities.append(
                    VulnerabilitySeverity(
                        system=scoring_systems["apache_httpd"],
                        value=value,
                    )
                )
                break
        reference = Reference(
            reference_id=cve,
            url=urllib.parse.urljoin(self.base_url, f"{cve}.json"),
            severities=severities,
        )

        versions_data = []
        for vendor in data["affects"]["vendor"]["vendor_data"]:
            for products in vendor["product"]["product_data"]:
                for version_data in products["version"]["version_data"]:
                    versions_data.append(version_data)

        fixed_version_ranges, affected_version_ranges = self.to_version_ranges(versions_data)

        affected_packages = []
        fixed_packages = []

        for version_range in fixed_version_ranges:
            fixed_packages.extend(
                [
                    PackageURL(type="apache", name="httpd", version=version)
                    for version in self.version_api.get("apache/httpd")
                    if MavenVersion(version) in version_range
                ]
            )

        for version_range in affected_version_ranges:
            affected_packages.extend(
                [
                    PackageURL(type="apache", name="httpd", version=version)
                    for version in self.version_api.get("apache/httpd")
                    if MavenVersion(version) in version_range
                ]
            )

        return Advisory(
            vulnerability_id=cve,
            summary=description,
            affected_packages=nearest_patched_package(affected_packages, fixed_packages),
            references=[reference],
        )