예제 #1
0
    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
예제 #2
0
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])
예제 #3
0
    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
예제 #4
0
    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
예제 #5
0
    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
예제 #6
0
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'])
예제 #7
0
    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()
예제 #8
0
    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
예제 #9
0
 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'
예제 #10
0
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')
예제 #11
0
    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()
예제 #12
0
    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')
예제 #13
0
    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()
예제 #14
0
    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()
예제 #15
0
    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()
예제 #16
0
    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
예제 #17
0
    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']
예제 #18
0
    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()
예제 #19
0
    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
예제 #20
0
    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()
예제 #23
0
    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)
예제 #24
0
    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',
            '.',
        )
예제 #25
0
    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),
        )
예제 #26
0
    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),
        )
예제 #27
0
    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',
        )
예제 #28
0
    def test_scan_basic(self, mock_baseline_initialize):
        assert main(['--scan']) == 0

        mock_baseline_initialize.assert_called_once_with(
            Any(tuple),
            None,
            '.',
        )
예제 #29
0
    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',
            '.',
        )
예제 #30
0
    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,
            '.',
        )
예제 #31
0
    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',
        )
예제 #32
0
 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:]
예제 #33
0
 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)',
         })
예제 #34
0
    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,
        )
예제 #35
0
    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),
        )
예제 #36
0
 def test_smoke(self):
     with mock_stdin():
         assert main([]) == 0