def parse_sensitivity_values(args): """ When configuring which plugins to run, the user is able to either specify a configuration file (with --config-file), or select individual values (e.g. --base64-limit). This function handles parsing the values from these various places, and returning them as a SensitivityValues namedtuple. Order Precedence: 1. Values specified in config file. 2. Values specified inline. (eg. `--hex-limit 6`) 3. Default values for CLI arguments (specified in ParserBuilder) :param args: parsed arguments from parse_args. :return: SensitivityValues """ default_plugins = {} if args.config_file: data = open_config_file(args.config_file[0]).get('default', {}) default_plugins = data.get('plugins', {}) return SensitivityValues( base64_limit=default_plugins.get('Base64HighEntropyString') or args.plugins.get('Base64HighEntropyString', {}).get('base64_limit', [])[0], hex_limit=default_plugins.get('HexHighEntropyString') or args.plugins.get('HexHighEntropyString', {}).get('hex_limit', [])[0], private_key_detector=default_plugins.get('PrivateKeyDetector') or 'PrivateKeyDetector' in args.plugins, )
def mock_tracked_repo(cls=BaseTrackedRepo, **kwargs): """Returns a mock TrackedRepo for testing""" defaults = { 'sha': 'does_not_matter', 'repo': '[email protected]:pre-commit/pre-commit-hooks.git', 'cron': '* * 4 * *', 'repo_config': RepoConfig( base_tmp_dir='foo/bar', baseline='.secrets.baseline', exclude_regex='', ), 'plugin_sensitivity': SensitivityValues( base64_limit=4.5, hex_limit=3, ) } defaults.update(kwargs) with mock.patch( 'detect_secrets_server.repos.base_tracked_repo.os.path.isdir' ) as m: m.return_value = True return cls(**defaults)
def test_initialize_repos_from_repo_yaml_no_tracked_repos(self, mock_data): mock_data.return_value = {'nothing': 'important'} assert initialize_repos_from_repo_yaml( 'will_be_mocked', SensitivityValues(), self._mock_repo_config(), ) == []
def test_initialize_plugins_failed_instantiation(self): with mock.patch( 'detect_secrets.plugins.HexHighEntropyString.__init__', side_effect=TypeError, ): output = initialize(SensitivityValues(hex_limit=3, ), ) assert len(output) == 0
def test_initialize_repos_from_repo_yaml_success(self, mock_data, mock_subprocess): def _create_mock_tracked_repo_repr(**kwargs): defaults = { 'sha': 'does_not_matter', 'repo': 'does_not_matter', } defaults.update(kwargs) return defaults mock_data.return_value = { 'tracked': [ _create_mock_tracked_repo_repr( # Test that it can also be overriden here. plugins={ 'Base64HighEntropyString': 2, }, baseline_file='is_included', ), _create_mock_tracked_repo_repr( # Test local repo is_local_repo=True, ), _create_mock_tracked_repo_repr( # Test S3 remote repo s3_backed=True, ), _create_mock_tracked_repo_repr( # Test S3 local repo is_local_repo=True, s3_backed=True, ), ] } with mock.patch.object(S3TrackedRepo, '_initialize_s3_client'): output = initialize_repos_from_repo_yaml( 'will_be_mocked', SensitivityValues( base64_limit=1, hex_limit=2, ), self._mock_repo_config(), S3Config( s3_creds_file='filename', bucket_name='bucket', prefix='prefix', )) assert isinstance(output[0], BaseTrackedRepo) assert isinstance(output[1], LocalTrackedRepo) assert isinstance(output[2], S3TrackedRepo) assert isinstance(output[3], S3LocalTrackedRepo) assert output[0].plugin_config.base64_limit == 2 assert output[0].baseline_file == 'is_included' assert output[1].plugin_config.base64_limit == 1
def test_success(self): plugins = SensitivityValues( base64_limit=4.5, hex_limit=3, ) output = initialize(plugins) assert isinstance(output[0], Base64HighEntropyString) assert output[0].entropy_limit == 4.5 assert isinstance(output[1], HexHighEntropyString) assert output[1].entropy_limit == 3
def _modify_tracked_file_contents(cls, data): """For better representation, we use namedtuples. However, these do not directly correlate to file dumps (which `save` does, using `__dict__`. Therefore, we may need to modify these values, before loading them into the class constructor. :type data: dict :param data: pretty much the layout of __dict__ :return: dict """ # Need to change plugins to type SensitivityValues data['plugin_sensitivity'] = SensitivityValues(**data['plugins']) return data
def test_aliases(self): """For better usability, we can also use aliases when initializing the SensitivityValues object. """ plugins = SensitivityValues( Base64HighEntropyString=2, # Non aliases should take precedence over aliases. HexHighEntropyString=1, hex_limit=1.5, ) output = initialize(plugins) assert isinstance(output[0], Base64HighEntropyString) assert output[0].entropy_limit == 2 assert isinstance(output[1], HexHighEntropyString) assert output[1].entropy_limit == 1.5
def test_no_sensitivity_value_necessary_plugin(self): plugins = SensitivityValues(PrivateKeyDetector=True) output = initialize(plugins) assert len(output) == 1 assert isinstance(output[0], PrivateKeyDetector)
def test_false_disables_plugin(self): output = initialize(SensitivityValues(PrivateKeyDetector=False)) assert len(output) == 0
def initialize_repos_from_repo_yaml( repo_yaml, plugin_sensitivity, repo_config, s3_config=None ): """For expected yaml file format, see `repos.yaml.sample` :type repo_yaml: string :param repo_yaml: filename of config file to read and parse :type plugin_sensitivity: SensitivityValues :type repo_config: RepoConfig :type s3_config: S3Config :return: list of TrackedRepos :raises: IOError """ data = open_config_file(repo_yaml) output = [] if data.get('tracked') is None: return output for entry in data['tracked']: sensitivity = plugin_sensitivity if entry.get('plugins'): # Merge plugin sensitivities plugin_dict = plugin_sensitivity._asdict() # Use SensitivityValues constructor to convert values entry_sensitivity = SensitivityValues(**entry['plugins']) plugin_dict.update(entry_sensitivity._asdict()) sensitivity = SensitivityValues(**plugin_dict) entry['plugin_sensitivity'] = sensitivity config = repo_config if 'baseline_file' in entry: config = RepoConfig( base_tmp_dir=repo_config.base_tmp_dir, exclude_regex=repo_config.exclude_regex, baseline=entry['baseline_file'], ) entry['repo_config'] = config if entry.get('s3_backed') and s3_config is None: CustomLogObj.getLogger().error( ( 'Unable to load s3 config for %s. Make sure to specify ' '--s3-config-file for s3_backed repos!' ), entry.get('repo'), ) continue entry['s3_config'] = s3_config # After setting up all arguments, create respective object. repo = tracked_repo_factory( entry.get('is_local_repo', False), entry.get('s3_backed', False), ) output.append(repo(**entry)) return output