def test_saves_to_baseline(): # We create an empty baseline, with customized settings. # This way, we expect the engine to use the settings configured by the baseline, # but have the results replaced by the new scan. with transient_settings({ 'plugins_used': [ { 'name': 'Base64HighEntropyString', 'limit': 4.5, }, ], }): secrets = SecretsCollection() old_secrets = baseline.format_for_output(secrets) with mock_printer( main_module) as printer, tempfile.NamedTemporaryFile() as f: baseline.save_to_file(old_secrets, f.name) f.seek(0) # We also test setting the root directory through this test. main_module.main(['scan', 'test_data', '--baseline', f.name]) f.seek(0) new_secrets = json.loads(f.read()) assert not secrets.exactly_equals( baseline.load(new_secrets, f.name)) assert new_secrets['plugins_used'] == [ { 'name': 'Base64HighEntropyString', 'limit': 4.5, }, ] assert not printer.message
def run_logic(secretsA: SecretsCollection, secretsB: SecretsCollection): with tempfile.NamedTemporaryFile() as f, tempfile.NamedTemporaryFile( ) as g: baseline.save_to_file(secretsA, f.name) baseline.save_to_file(secretsB, g.name) main(['audit', '--diff', f.name, g.name])
def test_fail_on_live_mutually_inclusive_with_report(self, capsys): with pytest.raises(SystemExit): main('audit --fail-on-live fileA'.split()) captured = capsys.readouterr() assert 'argument --fail-on-live: not allowed without argument --report' in captured.err
def test_json_mutually_exclusive_with_omit_instructions(self, capsys): with pytest.raises(SystemExit): main('audit --report --json --omit-instructions fileA'.split()) captured = capsys.readouterr() assert 'argument --omit-instructions: not allowed with argument --json' in captured.err
def test_report_without_file(self, capsys): with pytest.raises(SystemExit): main('audit --report'.split()) captured = capsys.readouterr() assert 'the following arguments are required: filename' in captured.err
def test_no_divide_by_zero(secret): secrets = SecretsCollection() secrets['file'].add(secret) with tempfile.NamedTemporaryFile() as f: baseline.save_to_file(secrets, f.name) f.seek(0) main(['audit', f.name, '--stats', '--json'])
def test_json_report_prints_json_output( self, mock_print_json_report, ): with self.mock_env(), pytest.raises(SystemExit): main('audit --report --json fileA'.split()) mock_print_json_report.assert_called()
def test_json_report_with_all_fail_conditions_exits_with_non_zero_upon_failure(self): with self.mock_env(), pytest.raises(SystemExit) as context: main( 'audit --report --json --fail-on-unaudited' ' --fail-on-live --fail-on-audited-real fileA'.split(), ) assert context.type == SystemExit assert context.value.code == ReportExitCode.FAIL.value
def test_reads_baseline_from_file_with_other_ioerror(self, ): io_error = IOError() with mock_stdin(), mock.patch( 'detect_secrets.main._read_from_file', side_effect=io_error, ) as m_read: with pytest.raises(IOError): main('scan --update non_existed_baseline_file'.split()) == 0 assert m_read.call_args[0][0] == 'non_existed_baseline_file'
def test_baseline(printer) -> SecretsCollection: # We call this through the CLI so it does the plugin initialization for us. # It doesn't matter what we scan -- we just need a large enough corpus so that # we can perform our tests. main(['scan', 'test_data']) output = printer.message printer.clear() return baseline.load(json.loads(output), 'does-not-matter')
def test_report_fail_on_unaudited_only( self, mock_fail_on_unaudited, mock_fail_on_audited_real, mock_fail_on_live, ): with self.mock_env(), pytest.raises(SystemExit): main('audit --report --fail-on-unaudited fileA'.split()) mock_fail_on_unaudited.assert_called() mock_fail_on_audited_real.assert_not_called() mock_fail_on_live.assert_not_called()
def test_adheres_to_verification_policies(args, verified_result, should_be_present): with register_plugin(MockPlugin(verified_result=verified_result), ), mock_printer(main_module) as printer: main_module.main(['scan', '--string', 'fake-secret', *args]) for line in printer.message.splitlines(): plugin_name, result = [x.strip() for x in line.split(':')] if plugin_name != 'MockPlugin': continue assert should_be_present == result.startswith('True')
def test_default_report_runs_all_checks( self, mock_fail_on_unaudited, mock_fail_on_live, mock_fail_on_audited_real, ): with self.mock_env(), pytest.raises(SystemExit): main('audit --report fileA'.split()) mock_fail_on_unaudited.assert_called() mock_fail_on_live.assert_called() mock_fail_on_audited_real.assert_called()
def test_default_report_prints_table_output( self, mock_print_stats, mock_print_table_report, mock_print_summary, ): with self.mock_env(), pytest.raises(SystemExit): main('audit --report fileA'.split()) mock_print_stats.assert_called() mock_print_table_report.assert_called() mock_print_summary.assert_called()
def test_default_json_report_executes_all_conditions( self, mock_fail_on_unaudited, mock_fail_on_live, mock_fail_on_audited_real, ): with self.mock_env(), pytest.raises(SystemExit): main('audit --report --json fileA'.split()) mock_fail_on_unaudited.assert_called() mock_fail_on_live.assert_called() mock_fail_on_audited_real.assert_called()
def test_audit_display_results(self, filename, expected_output): with mock_stdin(), mock_printer(main_module, ) as printer_shim: main(['scan', filename]) baseline = printer_shim.message baseline_dict = json.loads(baseline) with mock.patch( 'detect_secrets.core.audit._get_baseline_from_file', return_value=baseline_dict, ), mock_printer(audit_module, ) as printer_shim: main(['audit', '--display-results', 'MOCKED']) assert json.loads(uncolor( printer_shim.message))['plugins'] == expected_output
def test_only_displays_result_if_actual_secret(): with mock_printer(main_module) as printer: main_module.main([ 'scan', '--only-allowlisted', '--disable-plugin', 'KeywordDetector', 'test_data/config.ini', ]) output = json.loads(printer.message) # The only allowlisted item in this is an entirely numerical string, so this # should not be detected. assert not output['results']
def test_report_with_all_fail_on_conditions_executes_all_conditions( self, mock_fail_on_unaudited, mock_fail_on_live, mock_fail_on_audited_real, ): with self.mock_env(), pytest.raises(SystemExit): main( 'audit --report --fail-on-unaudited --fail-on-live' ' --fail-on-audited-real fileA'.split(), ) mock_fail_on_unaudited.assert_called() mock_fail_on_live.assert_called() mock_fail_on_audited_real.assert_called()
def test_report_with_all_fail_on_conditions_exits_with_non_zero_upon_failure(self): modified_baseline = deepcopy(self.baseline) print(self.baseline) modified_baseline['results']['filenameA'][0]['is_secret'] = True modified_baseline['results']['filenameA'][1]['is_secret'] = None modified_baseline['results']['filenameB'][0]['is_verified'] = True with self.mock_env(baseline=modified_baseline), pytest.raises(SystemExit) as context: main( 'audit --report --fail-on-unaudited --fail-on-live' ' --fail-on-audited-real fileA'.split(), ) assert context.type == SystemExit assert context.value.code == ReportExitCode.FAIL.value
def test_reads_non_existed_baseline_from_file( self, mock_merge_baseline, mock_baseline_initialize, ): fnf_error = FileNotFoundError() fnf_error.errno = 2 with mock_stdin(), mock.patch( 'detect_secrets.main._read_from_file', side_effect=fnf_error, ) as m_read, mock.patch( 'detect_secrets.main.write_baseline_to_file', ) as m_write: assert main('scan --update non_existed_baseline_file'.split()) == 0 assert m_read.call_args[0][0] == 'non_existed_baseline_file' assert m_write.call_args[1][ 'filename'] == 'non_existed_baseline_file' assert m_write.call_args[1]['data'] == Any(dict) mock_baseline_initialize.assert_called_once_with( plugins=Any(tuple), exclude_files_regex='^non_existed_baseline_file$', exclude_lines_regex=None, path='.', should_scan_all_files=False, output_raw=False, output_verified_false=False, word_list_file=None, word_list_hash=None, ) mock_merge_baseline.assert_not_called()
def test_old_baseline_ignored_with_update_flag( self, mock_baseline_initialize, exclude_files_arg, expected_regex, ): with mock_stdin(), mock.patch( 'detect_secrets.main._read_from_file', return_value={}, ), mock.patch( # We don't want to be creating a file during test 'detect_secrets.main.write_baseline_to_file', ) as file_writer: assert main( shlex.split( 'scan --update old_baseline_file {}'.format( exclude_files_arg, ), ), ) == 0 assert ( file_writer.call_args[1]['data']['exclude']['files'] == expected_regex )
def test_scan_string_basic( self, mock_baseline_initialize, string, expected_base64_result, expected_hex_result, ): with mock_stdin( string, ), mock_printer( main_module, ) as printer_shim: assert main('scan --string'.split()) == 0 assert uncolor(printer_shim.message) == textwrap.dedent( """ AWSKeyDetector : False ArtifactoryDetector : False Base64HighEntropyString: {} BasicAuthDetector : False HexHighEntropyString : {} JwtTokenDetector : False KeywordDetector : False MailchimpDetector : False PrivateKeyDetector : False SlackDetector : False StripeDetector : False """.format( expected_base64_result, expected_hex_result, ), )[1:] mock_baseline_initialize.assert_not_called()
def test_plugin_from_old_baseline_respected_with_update_flag( self, mock_baseline_initialize, plugins_used, plugins_overwriten, plugins_wrote, ): with mock_stdin(), mock.patch( 'detect_secrets.main._read_from_file', return_value={ 'plugins_used': plugins_used, 'results': {}, 'version': VERSION, 'exclude': { 'files': '', 'lines': '', }, }, ), mock.patch( # We don't want to be creating a file during test 'detect_secrets.main.write_baseline_to_file', ) as file_writer: assert main( shlex.split( 'scan --update old_baseline_file {}'.format( plugins_overwriten, ), ), ) == 0 assert (file_writer.call_args[1]['data']['plugins_used'] == plugins_wrote)
def test_scan_with_excludes_flag(self, mock_baseline_initialize): assert main('--scan --exclude some_pattern_here'.split()) == 0 mock_baseline_initialize.assert_called_once_with( Any(tuple), 'some_pattern_here', '.', )
def test_reads_from_stdin(self, mock_merge_baseline): with mock_stdin(json.dumps({'key': 'value'})): assert main(['scan']) == 0 mock_merge_baseline.assert_called_once_with( {'key': 'value'}, Any(dict), )
def test_reads_from_stdin(self, mock_merge_baseline): with mock_stdin(json.dumps({'key': 'value'})): assert main(['--scan']) == 0 mock_merge_baseline.assert_called_once_with( {'key': 'value'}, Any(dict), )
def test_scan_with_rootdir(self, mock_baseline_initialize): assert main('--scan test_data'.split()) == 0 mock_baseline_initialize.assert_called_once_with( Any(tuple), None, 'test_data', )
def test_scan_basic(self, mock_baseline_initialize): assert main(['--scan']) == 0 mock_baseline_initialize.assert_called_once_with( Any(tuple), None, '.', )
def test_scan_with_excludes_flag(self, mock_baseline_initialize): with mock_stdin(): assert main('--scan --exclude some_pattern_here'.split()) == 0 mock_baseline_initialize.assert_called_once_with( Any(tuple), 'some_pattern_here', '.', )
def test_scan_basic(self, mock_baseline_initialize): with mock_stdin(): assert main(['--scan']) == 0 mock_baseline_initialize.assert_called_once_with( Any(tuple), None, '.', )
def test_scan_with_rootdir(self, mock_baseline_initialize): with mock_stdin(): assert main('--scan test_data'.split()) == 0 mock_baseline_initialize.assert_called_once_with( Any(tuple), None, 'test_data', )
def test_scan_string_cli_overrides_stdin(self): with mock_stdin('012345678ab', ), mock_printer( main_module, ) as printer_shim: assert main('scan --string 012345'.split()) == 0 assert printer_shim.message == textwrap.dedent(""" Base64HighEntropyString: False (2.585) BasicAuthDetector : False HexHighEntropyString : False (2.121) PrivateKeyDetector : False """)[1:]
def test_scan_string_cli_overrides_stdin(self): with mock_stdin('012345678ab', ), mock_printer( main_module, ) as printer_shim: assert main('scan --string 012345'.split()) == 0 assert uncolor(printer_shim.message) == get_plugin_report({ 'Base64HighEntropyString': 'False (2.585)', 'HexHighEntropyString': 'False (2.121)', })
def test_scan_with_all_files_flag(self, mock_baseline_initialize): with mock_stdin(): assert main('scan --all-files'.split()) == 0 mock_baseline_initialize.assert_called_once_with( Any(tuple), None, '.', True, )
def test_reads_old_baseline_from_file(self, mock_merge_baseline): with mock_stdin(), mock_open( json.dumps({'key': 'value'}), 'detect_secrets.main.open', ) as m: assert main('--scan --import old_baseline_file'.split()) == 0 assert m.call_args[0][0] == 'old_baseline_file' mock_merge_baseline.assert_called_once_with( {'key': 'value'}, Any(dict), )
def test_smoke(self): with mock_stdin(): assert main([]) == 0