def test_cli_configuration_used_by_default( runner: click.testing.CliRunner, cache_dir: str, monkeypatch: MonkeyPatch, ) -> None: """Ensure that we use options set in the configuration file if not overridden by passing CLI options.""" monkeypatch.setenv("SKJOLD_CACHE_DIR", cache_dir) config = Configuration() config.use({ "report_only": True, "report_format": "json", "sources": ["gemnasium"] }) result = runner.invoke(cli, args=["config"], obj=config) assert "report_only: True" in result.stderr assert "report_format: json" in result.stderr # TODO(twu): Figure out how to do this right. input_ = make_input_stream("urllib3==1.23\nrequests==22.2.2\n", "utf-8") setattr(input_, "name", "<stdin>") result = runner.invoke( cli, args=["audit", "-"], env={"SKJOLD_CACHE_DIR": cache_dir}, input=input_, obj=config, ) assert result.exit_code == 0 json_ = json.loads(result.stdout) assert len(json_) > 0 assert json_[0]["name"] == "urllib3" assert json_[0]["source"] == "gemnasium"
def test_vulnerable_package_with_ignore_list_via_cli( runner: click.testing.CliRunner, cache_dir: str, monkeypatch: MonkeyPatch) -> None: """Ensure that using a .skjoldignore file ignores marked findings and can be set via '-i/--ignore-file'.""" monkeypatch.setenv("SKJOLD_CACHE_DIR", cache_dir) ignore_path = os.path.join(os.path.dirname(__file__), "fixtures", "formats", "ignore", "all") assert os.path.exists(ignore_path) config = Configuration() config.use({ "report_only": False, "report_format": "cli", "sources": ["pypa"] }) assert config.ignore_file == ".skjoldignore" # TODO(twu): Figure out how to do this right. input_ = make_input_stream("urllib3==1.23", "utf-8") setattr(input_, "name", "<stdin>") result = runner.invoke(cli, args=["audit", "-i", ignore_path, "-"], input=input_, obj=config) assert "Ignored 6 finding(s)!" in result.stderr assert "No vulnerable packages found!" in result.stderr assert result.exit_code == 0
def test_cli_ensure_formats_are_handled_properly( folder: str, filename: str, runner: click.testing.CliRunner, cache_dir: str, monkeypatch: MonkeyPatch, ) -> None: monkeypatch.setenv("SKJOLD_CACHE_DIR", cache_dir) config = Configuration() config.use({ "report_only": False, "report_format": "cli", "sources": ["pyup"] }) path = format_fixture_path_for(filename) result = runner.invoke( cli, args=["audit", "-r", "-o", "json", "-s", "github", path], env={"SKJOLD_CACHE_DIR": cache_dir}, ) assert not result.exception assert result.exit_code == 0 json_ = json.loads(result.stdout) assert len(json_) > 0 assert json_[0]["name"] == "urllib3" assert json_[0]["source"] == "github"
def test_cli_configuration_override_via_cli( runner: click.testing.CliRunner, cache_dir: str, monkeypatch: MonkeyPatch, ) -> None: """Ensure that overriding configured values via CLI is possible.""" monkeypatch.setenv("SKJOLD_CACHE_DIR", cache_dir) config = Configuration() config.use({ "report_only": True, "report_format": "json", "sources": ["pyup"], "cache_dir": cache_dir, }) result = runner.invoke(cli, args=["config"], obj=config) assert "report_only: True" in result.stderr assert "report_format: json" in result.stderr # TODO(twu): Figure out how to do this right. input_ = make_input_stream("urllib3==1.23\nrequests==22.2.2\n", "utf-8") setattr(input_, "name", "<stdin>") result = runner.invoke( cli, args=["audit", "-r", "-s", "github", "-o", "cli", "-"], input=input_, env={"SKJOLD_CACHE_DIR": cache_dir}, obj=config, ) assert result.exit_code == 0 assert "urllib3" in result.stdout assert "via github" in result.stdout
def cli( config: Configuration, configuration_file: click.Path, verbose: bool, ) -> None: """ Check a given Python dependency file against a set of advisory databases.""" config.verbose = verbose file_ = str(configuration_file) skip_configuration = not os.environ.get("SKJOLD_SKIP_RC", None) is None if os.path.exists(file_) and not skip_configuration: settings = get_configuration_from_toml(file_) config.use(config=settings) else: click.secho("Warning: No 'pyproject.toml' found!", err=True, fg="yellow") if config.verbose: print_configuration(config) click.secho(f"Using {config.cache_dir} as cache location", err=True) # Cache Directory # Check for cache directory and create it if necessary. if not os.path.isdir(config.cache_dir): os.makedirs(config.cache_dir, exist_ok=True) if config.verbose: click.secho( f"Cache '{config.cache_dir}' does not exist! Creating it.", err=True, ) if not os.path.isdir(config.cache_dir): raise click.ClickException( f"Unable to create cache directory '{config.cache_dir}'!" )
def test_cli_json_report_with_package_list_via_stdin( runner: click.testing.CliRunner, cache_dir: str, monkeypatch: MonkeyPatch, ) -> None: """Ensure request json output with packages via stdin results in parsable stdout.""" monkeypatch.setenv("SKJOLD_CACHE_DIR", cache_dir) config = Configuration() config.use({ "report_only": False, "report_format": "cli", "sources": ["pyup"] }) # TODO(twu): Figure out how to do this right. input_ = make_input_stream("urllib3==1.23\nrequests==22.2.2\n", "utf-8") setattr(input_, "name", "<stdin>") result = runner.invoke( cli, args=["audit", "-r", "-o", "json", "-s", "github", "-"], input=input_) assert not result.exception assert result.exit_code == 0 json_ = json.loads(result.stdout) assert len(json_) > 0 assert json_[0]["name"] == "urllib3"
def audit_( config: Configuration, report_only: bool, report_format: str, file_format: str, sources: List[str], file: TextIO, ) -> None: """ Checks a given dependency file against advisory databases. \b FILE is the path to the dependency file to audit. """ config.report_only = report_only config.report_format = report_format # Only override sources if at least once --source is passed. if len(sources) > 0: config.sources = list(set(sources)) if len(config.sources) == 0: raise click.ClickException( "Please specify or configure at least one advisory source." ) packages = extract_package_list_from(config, file, file_format) if config.verbose: click.secho("Checking ", nl=False, err=True) click.secho(f"{len(packages)}", fg="green", nl=False, err=True) click.secho(" package(s).", err=True) click.secho("Using ", nl=False, err=True) click.secho(f"{config.sources}", fg="green", nl=False, err=True) click.secho(" as source(s).", err=True) results, vulnerable = audit(config, packages) report(config, results) if len(vulnerable) > 0 and config.verbose: click.secho("", err=True) click.secho( f" Found {len(vulnerable)} vulnerable packages!", fg="red", blink=True, err=True, ) click.secho("", err=True) elif config.verbose: click.secho("", err=True) click.secho(f" No vulnerable packages found!", fg="green", err=True) # By default we want to exit with a non-zero exit-code when we encounter # any findings. if not config.report_only and len(vulnerable) > 0: sys.exit(1)
def test_cli_run_config( runner: click.testing.CliRunner, cache_dir: str, monkeypatch: MonkeyPatch ) -> None: monkeypatch.setenv("SKJOLD_CACHE_DIR", cache_dir) config = Configuration() config.use({"report_only": False, "report_format": "cli", "sources": ["pyup"]}) result = runner.invoke(cli, args=["config"]) assert result.exit_code == 0
def test_register_source() -> None: DummyAdvisorySource._name = "dummy2" register_source("dummy2", DummyAdvisorySource) assert is_registered_source("dummy2") _config = Configuration() assert "dummy2" in _config.available_sources
def test_extract_package_versions_from_with_poetry_lock( folder: str, filename: str, format_: Optional[str]) -> None: with open(format_fixture_path_for(folder, filename)) as fh: packages = list(extract_package_list_from(Configuration(), fh, format_)) assert len(packages) > 0
def test_vulnerable_package_via_cli( runner: click.testing.CliRunner, cache_dir: str, monkeypatch: MonkeyPatch ) -> None: """Ensure that passing a vulnerable package via stdin produces the expected output.""" monkeypatch.setenv("SKJOLD_CACHE_DIR", cache_dir) config = Configuration() config.use({"report_only": False, "report_format": "cli", "sources": ["pyup"]}) # TODO(twu): Figure out how to do this right. input_ = make_input_stream("urllib3==1.23\nrequests==22.2.2\n", "utf-8") setattr(input_, "name", "<stdin>") result = runner.invoke(cli, args=["audit", "-s", "github", "-"], input=input_) assert result.exception assert result.exit_code == 1 assert "via github" in result.stdout
def cli(config: Configuration, configuration_file: click.Path, verbose: bool) -> None: """ Check a given Python dependency file against a set of advisory databases.""" doc = {} if os.path.exists(str(configuration_file)): with open(str(configuration_file)) as fh: doc = tomlkit.parse(fh.read()) else: click.secho("Warning: No 'pyproject.toml' found!", err=True, fg="yellow") _config = doc.get("tool", {}).get("skjold", {}) # Configuration file config.report_only = _config.get("report_only", config.report_only) config.report_format = _config.get("report_format", config.report_format) config.cache_dir = _config.get("cache_dir", config.cache_dir) config.cache_expires = _config.get("cache_expires", config.cache_expires) config.verbose = verbose # Configure cache_dir selection: ENV > pyproject.toml > default(posix). app_home = click.get_app_dir("skjold", roaming=False, force_posix=True) default_cache_dir = os.path.join(app_home, "cache") config.cache_dir = os.environ.get( "SKJOLD_CACHE_DIR", _config.get("cache_dir", default_cache_dir)) if config.verbose: click.secho(f"Using {config.cache_dir} as cache location", err=True) # Check for cache directory and create it if necessary. if not os.path.isdir(config.cache_dir): os.makedirs(config.cache_dir, exist_ok=True) if config.verbose: click.secho( f"Cache '{config.cache_dir}' does not exist! Creating it.", err=True) if not os.path.isdir(config.cache_dir): raise click.ClickException( f"Unable to create cache directory '{config.cache_dir}'!") # Configure and validate sources. config.sources = _config.get("sources", []) if not len(config.sources): click.secho("Warning: No advisory sources configured!", err=True, fg="yellow") for source_name in config.sources: if not is_registered_source(source_name): raise click.ClickException( f"Source with name '{source_name}' does not exist!") if config.verbose: print_configuration(config)
def test_extract_dependencies_using_minimal_examples(folder: str, filename: str) -> None: with open(format_fixture_path_for(folder, filename)) as fh: packages = list(extract_package_list_from(Configuration(), fh, None)) assert len(packages) > 0