Ejemplo n.º 1
0
    def _check_package(
            self, package: Union[Dict[str, Any], List[Dict[str,
                                                           Any]]]) -> bool:
        """
        Calls the analysis API endpoint with a package to see if a package is safe.

        :param package: a package string with an optional version specified
        :return: bool
        """
        vuln_results: DependencySet
        if isinstance(package, dict):
            if any(spec in package.get("version", "")
                   for spec in INVALID_SPECIFIERS):
                raise OchronaImportException(
                    f"An invalid specifier was found in {package.get('version')}, either specify the package without a version or pin to a single version using `name==version`."
                )
            parts = package.get("version", "").split(EQUALS_SPECIFIER)
            if len(parts) == 1:
                package["version"] = self._get_most_recent_version(
                    package=package.get("version", ""))
            vuln_results = resolve(dependencies=[package], logger=self._logger)
        elif isinstance(package, list):
            vuln_results = resolve(dependencies=package, logger=self._logger)
        if len(vuln_results.confirmed_vulnerabilities) > 0:
            self._logger.info(
                f"A full list of packages that would be installed, included dependencies: {', '.join(vuln_results.flat_list)}"
            )
            self._logger.error(
                f"""Vulerabilities found related to import:\n{''.join([self._format_vulnerability(v) for v in vuln_results.confirmed_vulnerabilities])}"""
            )
            return False
        self._logger.info(
            f"A full list of packages to be installed, included dependencies: {', '.join(vuln_results.flat_list)}"
        )
        return True
Ejemplo n.º 2
0
 def test_resolve(self):
     res = e.resolve(logger=MockLogger())
     assert isinstance(res, DependencySet)
     assert res.dependencies == []
     assert res.flat_list == []
     assert res.confirmed_vulnerabilities == []
     assert res.policy_violations == []
Ejemplo n.º 3
0
 def test_resolve_no_vulns(self):
     res = e.resolve(dependencies=[{
         "version": "fake=9.9.9"
     }],
                     logger=MockLogger())
     assert isinstance(res, DependencySet)
     assert len(res.dependencies) == 1
     assert res.flat_list == ["fake=9.9.9"]
     assert res.confirmed_vulnerabilities == []
     assert res.policy_violations == []
Ejemplo n.º 4
0
 def test_resolve_vuln_found(self):
     res = e.resolve(dependencies=[{
         "version": "requests==2.19.0"
     }],
                     logger=MockLogger())
     assert isinstance(res, DependencySet)
     assert len(res.dependencies) == 1
     assert res.flat_list == ["requests==2.19.0"]
     assert len(res.confirmed_vulnerabilities) == 1
     assert res.confirmed_vulnerabilities[0].cve_id == "CVE-2018-18074"
     assert res.policy_violations == []
Ejemplo n.º 5
0
 def test_resolve_policy_violation(self):
     res = e.resolve(dependencies=[{
         "version": "fake=9.9.9"
     }],
                     policies=["name IN requests,click,pytest"],
                     logger=MockLogger())
     assert isinstance(res, DependencySet)
     assert len(res.dependencies) == 1
     assert res.flat_list == ["fake=9.9.9"]
     assert res.confirmed_vulnerabilities == []
     assert len(res.policy_violations) == 1
     assert res.policy_violations[
         0].message == "Policy violated by fake=9.9.9"
Ejemplo n.º 6
0
 def test_resolve_policy_violation_legacy(self):
     res = e.resolve(dependencies=[{
         "version": "fake=9.9.9"
     }],
                     policies=[{
                         "policy_type": "package_name",
                         "allow_list": "urllib3"
                     }],
                     logger=MockLogger())
     assert isinstance(res, DependencySet)
     assert len(res.dependencies) == 1
     assert res.flat_list == ["fake=9.9.9"]
     assert res.confirmed_vulnerabilities == []
     assert len(res.policy_violations) == 1
     assert res.policy_violations[
         0].message == "'fake' not in list of allowed packages. (from fake=9.9.9)"
Ejemplo n.º 7
0
    def test_cyclonedx_xml(self):
        res = e.resolve(dependencies=[{
            "version":
            "requests==2.22.0",
            "hashes": [
                "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
                "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
            ]
        }],
                        logger=MockLogger())
        cyclone = CycloneDX(dependency_set=res)
        cyclone.xml(f"{dir_path}/test_data/output")

        tree = ET.parse(f"{dir_path}/test_data/output/bom.xml")
        root = tree.getroot()
        assert root.attrib.get("version") == "1"
        assert root[1][0][0].text == "requests"
        assert root[1][0][3][
            0].text == "11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4"
        assert root[1][0][4][0][0].text == "Apache-2.0"
        os.remove(f"{dir_path}/test_data/output/bom.xml")
Ejemplo n.º 8
0
    def test_cyclonedx_json(self):
        res = e.resolve(dependencies=[{
            "version":
            "requests==2.22.0",
            "hashes": [
                "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
                "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
            ]
        }],
                        logger=MockLogger())
        cyclone = CycloneDX(dependency_set=res)
        cyclone.json(f"{dir_path}/test_data/output")

        with open(f"{dir_path}/test_data/output/bom.json") as f:
            j = json.load(f)
            assert j.get("bomFormat") == "CycloneDX"
            assert len(j.get("components")) == 1
            assert j.get("components")[0].get("name") == "requests"
            assert len(j.get("components")[0].get("hashes")) == 2
            assert j.get("components")[0].get("licenses")[0].get(
                "license").get("id") == "Apache-2.0"
        os.remove(f"{dir_path}/test_data/output/bom.json")
Ejemplo n.º 9
0
def run(
    direct: Optional[str],
    dir: Optional[str],
    exclude_dir: Optional[str],
    file: Optional[str],
    debug: bool,
    silent: bool,
    report_type: Optional[str],
    output: Optional[str],
    exit: bool,
    ignore: Optional[str],
    include_dev: bool,
    install: Optional[str],
    sbom: bool,
    sbom_format: Optional[str],
):
    config = OchronaConfig(
        dir=dir,
        exclude_dir=exclude_dir,
        file=file,
        debug=debug,
        silent=silent,
        report_type=report_type,
        report_location=output,
        exit=exit,
        ignore=ignore,
        include_dev=include_dev,
        sbom=sbom,
        sbom_format=sbom_format,
    )
    log = OchronaLogger(config=config)
    if install:
        # Install mode
        try:
            importer = SafeImport(logger=log)
            importer.install(package=install)
        except OchronaException as ex:
            OchronaLogger.static_error(ex)
            sys.exit(1)
    else:
        # Regular operational check
        reporter = OchronaReporter(log, config)
        if not config.silent:
            log.header()

        direct = direct if direct != "" else None
        try:
            if direct is None:
                files = rfind_all_dependencies_files(log, config.dir,
                                                     config.exclude_dir,
                                                     config.file)
            else:
                files = []
        except OchronaFileException as ex:
            OchronaLogger.static_error(ex)
            sys.exit(1)

        try:
            results = []
            for file_ in files:
                payload = parse_to_payload(log, file_, config)
                if payload.get("dependencies") != []:
                    results.append(resolve(**payload))
                else:
                    # can't leave empty otherwise result counts are off
                    results.append(DependencySet())
                # Generate SBOM
                if config.sbom and config.report_location:
                    generate_sbom(
                        dependencies=payload.get("dependencies", []),
                        location=config.report_location,
                        format=config.sbom_format,
                    )
            if direct is not None:
                # use piped input directly and treat as PEP-508 format
                payload = parse_direct_to_payload(log, direct, config)
                if payload.get("dependencies") != []:
                    results.append(resolve(**payload))
                else:
                    # can't leave empty otherwise result counts are off
                    results.append(DependencySet())
                # Generate SBOM
                if config.sbom and config.report_location:
                    generate_sbom(
                        dependencies=payload.get("dependencies", []),
                        location=config.report_location,
                        format=config.sbom_format,
                    )
            if results == []:
                log.warn(f"No dependencies found in {files}")
            reporter.report_collector(files, results)
        except OchronaException as ex:
            OchronaLogger.static_error(ex)
            sys.exit(1)