def test_repositories_added_can_be_scanned(self, mock_rootdir,
                                               repo_to_scan):
        directory = '{}/repos/{}'.format(
            mock_rootdir,
            BaseStorage.hash_filename('Yelp/detect-secrets'),
        )
        mocked_sha = 'aabbcc'

        # We don't **actually** want to clone the repo per test run.
        with mock_git_calls(
                SubprocessMock(expected_input=(
                    'git clone https://github.com/Yelp/detect-secrets {} --bare'
                ).format(directory, ), ),
                # Since there is no prior sha to retrieve
                SubprocessMock(
                    expected_input='git rev-parse HEAD',
                    mocked_output=mocked_sha,
                )):
            assert main([
                'add',
                'https://github.com/Yelp/detect-secrets',
                '--root-dir',
                mock_rootdir,
            ]) == 0

        with mock_git_calls(
                # Getting latest changes
                SubprocessMock(
                    expected_input='git rev-parse --abbrev-ref HEAD',
                    mocked_output='master',
                ),
                SubprocessMock(
                    expected_input=
                    'git fetch --quiet origin master:master --force', ),
                # Getting relevant diff
                SubprocessMock(
                    expected_input=
                    'git diff {} HEAD --name-only --diff-filter ACM'.format(
                        mocked_sha),
                    mocked_output='filenameA',
                ),
                SubprocessMock(
                    expected_input='git diff {} HEAD -- filenameA'.format(
                        mocked_sha),
                    mocked_output='',
                ),
                # Storing latest sha
                SubprocessMock(expected_input='git rev-parse HEAD', ),
        ):
            assert main([
                'scan',
                repo_to_scan,
                '--root-dir',
                mock_rootdir,
            ]) == 0
    def test_main_scan_repo_scan_success_secrets_found(self, mock_file,
                                                       mock_scan, mock_log):
        mock_file.return_value = {
            'sha': 'does_not_matter',
            'repo': 'repo_name',
            'plugins': {
                'base64_limit': 3,
            },
            'cron': '* * * * *',
            'baseline_file': '.secrets.baseline',
        }

        mock_secret_collection = SecretsCollection()
        mock_secret_collection.data['junk'] = 'data'
        mock_scan.return_value = mock_secret_collection

        with mock.patch('detect_secrets_server.usage.ExternalHook') as hook, \
                mock.patch('detect_secrets_server.repos.base_tracked_repo.BaseTrackedRepo.update') as update, \
                mock.patch('detect_secrets.core.secrets_collection.SecretsCollection.json') as secrets_json:
            assert main([
                '--scan-repo',
                'will-be-mocked',
                '--output-hook',
                'examples/standalone_hook.py',
            ]) == 0

            assert update.call_count == 0
            assert hook().alert.call_count == 1
            assert secrets_json.call_count == 1
    def test_main_initialize_success(self, mock_data, mock_save, mock_repo_url,
                                     mock_print):
        mock_save.return_value = True
        mock_repo_url.return_value = b'[email protected]:some/random-repo.git'
        mock_data.return_value = {
            'tracked': [
                {
                    'repo': '[email protected]:yelp/detect-secrets.git',
                    'sha': 'some_sha_value',
                    'cron': '1 2 3 4 5',
                },
                {
                    'repo': '/file/to/local/repo',
                    'is_local_repo': True,
                    'sha': 'some_other_value',
                    'cron': '2 3 4 5 6',
                },
            ]
        }

        assert main('--initialize --output-hook examples/standalone_hook.py'.
                    split()) == 0
        mock_print.assert_has_calls([
            mock.call('# detect-secrets scanner'),
            mock.call(
                '1 2 3 4 5    detect-secrets-server --scan-repo yelp/detect-secrets '
                '--output-hook examples/standalone_hook.py'),
            mock.call(
                '2 3 4 5 6    detect-secrets-server --scan-repo some/random-repo --local '
                '--output-hook examples/standalone_hook.py'),
        ])
        assert mock_print.call_count == 3
    def test_main_initialize_failures(self, mock_print):
        with mock.patch(
                'detect_secrets_server.__main__.initialize_repos_from_repo_yaml'
        ) as m:
            m.side_effect = IOError
            assert main(
                '--initialize --output-hook examples/standalone_hook.py'.split(
                )) == 1

        with mock.patch(
                'detect_secrets_server.__main__.initialize_repos_from_repo_yaml'
        ) as m:
            m.return_value = []
            assert main(
                '--initialize --output-hook examples/standalone_hook.py'.split(
                )) == 0
            assert mock_print.call_count == 0
    def test_main_add_repo_local(self, mock_subprocess_obj):
        mock_subprocess_obj.side_effect = mock_subprocess((
            # mock out `clone_and_pull_repo`
            SubprocessMock(
                expected_input='git clone',
                mocked_output=b"fatal: destination path 'asdf' already exists",
            ),
            SubprocessMock(
                expected_input='git rev-parse --abbrev-ref',
                mocked_output=b'master',
            ),
            SubprocessMock(
                expected_input='git fetch -q origin',
                mocked_output=b'',
            ),

            # mock out `update`
            SubprocessMock(
                expected_input='git rev-parse HEAD',
                mocked_output=b'new-sha-hash',
            )))

        m = mock.mock_open()
        with mock.patch(
                'detect_secrets_server.repos.base_tracked_repo.codecs.open',
                m):
            assert main([
                '--add-repo',
                '/file/to/local/repo',
                '--local',
                '--baseline',
                '.baseline',
            ]) == 0

        m().write.assert_called_once_with(
            json.dumps(
                {
                    'sha': 'new-sha-hash',
                    'repo': '/file/to/local/repo',
                    'plugins': {
                        'base64_limit': 4.5,
                        'hex_limit': 3,
                        'private_key_detector': True,
                    },
                    'cron': '',
                    'baseline_file': '.baseline',
                },
                indent=2))
    def test_main_scan_repo_scan_success_no_results_found(
            self, mock_file, mock_scan, mock_log, mock_subprocess_obj):
        mock_file.return_value = {
            'sha': 'does_not_matter',
            'repo': 'repo_name',
            'plugins': {
                'base64_limit': 3,
            },
            'cron': '* * * * *',
            'baseline_file': '.secrets.baseline',
        }
        mock_scan.return_value = SecretsCollection()

        mock_subprocess_obj.side_effect = mock_subprocess(
            (SubprocessMock(expected_input='git rev-parse HEAD',
                            mocked_output=b'new_sha'), ))

        m = mock.mock_open()
        with mock.patch(
                'detect_secrets_server.repos.base_tracked_repo.codecs.open',
                m):
            assert main([
                '--scan-repo',
                'will-be-mocked',
                '--output-hook',
                'examples/standalone_hook.py',
            ]) == 0

        mock_log().info.assert_called_with(
            'SCAN COMPLETE - STATUS: clean for %s',
            'repo_name',
        )

        m().write.assert_called_once_with(
            json.dumps(
                {
                    'sha': 'new_sha',
                    'repo': 'repo_name',
                    'plugins': {
                        'base64_limit': 3,
                        'hex_limit': None,
                        'private_key_detector': False,
                    },
                    'cron': '* * * * *',
                    'baseline_file': '.secrets.baseline',
                },
                indent=2))
    def test_main_scan_repo_scan_failed(self, mock_read_file, mock_scan):
        mock_read_file.return_value = {
            'sha': 'does_not_matter',
            'repo': 'repo_name',
            'plugins': {
                'base64_limit': 3,
            },
            'cron': '* * * * *',
            'baseline_file': '.secrets.baseline',
        }

        mock_scan.return_value = None
        assert main([
            '--scan-repo',
            'will-be-mocked',
            '--output-hook',
            'examples/standalone_hook.py',
        ]) == 1
    def test_actions(self, argument_string, action_executed):
        """All detailed actions tests are covered in their individual
        test cases. This just makes sure they run, for coverage.
        """
        with mock.patch(
            'detect_secrets_server.__main__.actions',
            autospec=True,
        ) as mock_actions, mock.patch(
            'detect_secrets_server.core.usage.s3.should_enable_s3_options',
            return_value=True,
        ), mock.patch(
            'detect_secrets_server.core.usage.common.storage.should_enable_s3_options',
            return_value=True,
        ):
            mock_actions.initialize.return_value = ''
            mock_actions.scan_repo.return_value = 0

            assert main(argument_string.split()) == 0
            assert getattr(mock_actions, action_executed).called
    def test_main_add_repo_s3(self, mock_subprocess_obj, mock_s3_obj):
        mock_subprocess_obj.side_effect = mock_subprocess((
            # mock out `_get_repo_name`
            SubprocessMock(
                expected_input='git remote get-url origin',
                mocked_output=b'[email protected]:yelp/detect-secrets',
            ),

            # mock out `update`
            SubprocessMock(
                expected_input='git rev-parse HEAD',
                mocked_output=b'new-sha-hash',
            )))

        mock_s3_config = {
            's3_creds_file': 'filename',
            'bucket_name': 'bucketman',
            'prefix': 'mister',
        }

        final_output = mock.mock_open()
        s3_config = mock.mock_open(read_data=json.dumps(mock_s3_config))
        with mock.patch('detect_secrets_server.repos.base_tracked_repo.codecs.open', final_output),\
                mock.patch('detect_secrets_server.__main__.codecs.open', s3_config),\
                mock.patch(
                    'detect_secrets_server.repos.s3_tracked_repo.S3TrackedRepo._initialize_s3_client'
        ):
            assert main([
                '--add-repo',
                '[email protected]:yelp/detect-secrets.git',
                '--s3-config-file',
                'will-be-mocked',
            ]) == 0

        mock_s3_obj.list_objects_v2.assert_called_once_with(
            Bucket='bucketman',
            Prefix='mister/%s.json' %
            hashlib.sha512('yelp/detect-secrets'.encode('utf-8')).hexdigest(),
        )

        assert mock_s3_obj.upload_file.call_count == 1
 def test_no_args(self):
     with pytest.raises(SystemExit):
         main([])
 def test_main_no_args(self):
     # Needed for coverage
     assert main([]) == 0
 def test_main_scan_repo_unconfigured_repo(self, mock_load_from_file):
     mock_load_from_file.return_value = None
     assert main([
         '--scan-repo', 'will-be-mocked', '--output-hook',
         'examples/standalone_hook.py'
     ]) == 1