def configure_plugins(): with transient_settings({ 'plugins_used': [{ 'name': 'PrivateKeyDetector' }], }): yield
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 configure_settings(): with transient_settings({ 'plugins_used': [{ 'name': 'Base64HighEntropyString', 'limit': 4.5 }], }): yield
def test_ignore_case(): with transient_settings({ 'plugins_used': [{ 'name': 'KeywordDetector', }], }): assert list( scan_line('os.environ["AWS_SECRET_ACCESS_KEY"] = "testing"'))
def configure_plugins(): with transient_settings({ 'plugins_used': [ { 'name': 'BasicAuthDetector', }, ], }): yield
def test_cli_overrides_stdin(): with transient_settings({ 'plugins_used': [ { 'name': 'AWSKeyDetector', }, ], }), mock_stdin('AKIATESTTESTTESTTEST', ), mock_printer( main_module) as printer: assert main_module.main(['scan', '--string', 'blah']) == 0 assert printer.message.strip() == 'AWSKeyDetector: False'
def test_failed_high_entropy_string(): with transient_settings({ 'plugins_used': [ { 'name': 'Base64HighEntropyString', 'limit': 5.0, }, ], }): assert scan_adhoc_string('bangbangintotheroom').splitlines() == [ 'Base64HighEntropyString: False (3.326)', ]
def test_supports_stdin(): with transient_settings({ 'plugins_used': [ { 'name': 'AWSKeyDetector', }, ], }), mock_stdin('AKIATESTTESTTESTTEST', ), mock_printer( main_module) as printer, disable_gibberish_filter(): assert main_module.main(['scan', '--string']) == 0 assert printer.message.strip( ) == 'AWSKeyDetector: True (unverified)'
def test_analyze_javascript_negatives(line): with transient_settings({ 'plugins_used': [{ 'name': 'KeywordDetector', }], }): assert list(scan_line(line)) with tempfile.NamedTemporaryFile(suffix='.js') as f: f.write(line.encode('utf-8')) f.seek(0) assert not list(scan_file(f.name))
def test_load_from_baseline(): with transient_settings({ 'filters_used': [{ 'path': 'detect_secrets.filters.gibberish.should_exclude_secret', 'model': os.path.join(filters.gibberish.__path__[0], 'rfc.model'), 'file_hash': '00b672f709e9bf51fe2e09abe247ac3b6415d645', 'limit': 3.7, }], }): assert filters.gibberish.should_exclude_secret('clearly-not-a-secret')
def test_load_from_baseline(): with transient_settings({ 'filters_used': [{ 'path': 'detect_secrets.filters.wordlist.should_exclude_secret', 'min_length': 8, 'file_name': 'test_data/word_list.txt', 'file_hash': '116598304e5b33667e651025bcfed6b9a99484c7', }], }): assert filters.wordlist.should_exclude_secret('testPass') is True
def test_basic(): with transient_settings({ 'plugins_used': [ { 'name': 'AWSKeyDetector', }, { 'name': 'PrivateKeyDetector', }, ], }): assert scan_adhoc_string('AKIATESTTESTTESTTEST').splitlines() == [ 'AWSKeyDetector : True', 'PrivateKeyDetector: False', ]
def baseline_file(): # Create our own SecretsCollection manually, so that we have fine-tuned control. first_content = textwrap.dedent(f""" url = {url_format.format(first_secret)} example = {url_format.format(random_secret)} link = {url_format.format(first_secret)} """)[1:] second_content = textwrap.dedent(f""" url = {url_format.format(second_secret)} example = {url_format.format(random_secret)} """)[1:] with create_file_with_content(first_content) as first_file, \ create_file_with_content(second_content) as second_file, \ tempfile.NamedTemporaryFile() as baseline_file, \ transient_settings({ 'plugins_used': [ {'name': 'BasicAuthDetector'}, {'name': 'JwtTokenDetector'}, ], }): secrets = SecretsCollection() secrets.scan_file(first_file) secrets.scan_file(second_file) labels = { (first_file, BasicAuthDetector.secret_type, 1): True, (first_file, BasicAuthDetector.secret_type, 2): None, (first_file, BasicAuthDetector.secret_type, 3): True, (second_file, JwtTokenDetector.secret_type, 1): True, (second_file, BasicAuthDetector.secret_type, 1): False, (second_file, BasicAuthDetector.secret_type, 2): False, } for item in secrets: _, secret = item secret.is_secret = labels[(secret.filename, secret.type, secret.line_number)] baseline.save_to_file(secrets, baseline_file.name) baseline_file.seek(0) yield baseline_file.name
def run(self, root_folder: str, external_checks_dir: Optional[List[str]] = None, files: Optional[List[str]] = None, runner_filter: RunnerFilter = RunnerFilter(), collect_skip_comments: bool = True) -> Report: current_dir = os.path.dirname(os.path.realpath(__file__)) secrets = SecretsCollection() with transient_settings({ # Only run scans with only these plugins. 'plugins_used': [{ 'name': 'AWSKeyDetector' }, { 'name': 'ArtifactoryDetector' }, { 'name': 'AzureStorageKeyDetector' }, { 'name': 'BasicAuthDetector' }, { 'name': 'CloudantDetector' }, { 'name': 'IbmCloudIamDetector' }, { 'name': 'MailchimpDetector' }, { 'name': 'PrivateKeyDetector' }, { 'name': 'SlackDetector' }, { 'name': 'SoftlayerDetector' }, { 'name': 'SquareOAuthDetector' }, { 'name': 'StripeDetector' }, { 'name': 'TwilioKeyDetector' }, { 'name': 'EntropyKeywordCombinator', 'path': f'file://{current_dir}/plugins/entropy_keyword_combinator.py', 'limit': ENTROPY_KEYWORD_LIMIT }] }) as settings: report = Report(self.check_type) # Implement non IaC files (including .terraform dir) files_to_scan = files or [] excluded_paths = ( runner_filter.excluded_paths or []) + ignored_directories + [DEFAULT_EXTERNAL_MODULES_DIR] if root_folder: for root, d_names, f_names in os.walk(root_folder): filter_ignored_paths(root, d_names, excluded_paths) filter_ignored_paths(root, f_names, excluded_paths) for file in f_names: if file not in PROHIBITED_FILES and f".{file.split('.')[-1]}" in SUPPORTED_FILE_EXTENSIONS: files_to_scan.append(os.path.join(root, file)) logging.info( f'Secrets scanning will scan {len(files_to_scan)} files') settings.disable_filters( *['detect_secrets.filters.heuristic.is_indirect_reference']) Runner._scan_files(files_to_scan, secrets) for _, secret in iter(secrets): check_id = SECRET_TYPE_TO_ID.get(secret.type) bc_check_id = bc_integration.ckv_to_bc_id_mapping.get( check_id) if bc_integration.ckv_to_bc_id_mapping else None if not check_id: continue if runner_filter.checks and not runner_filter.should_run_check( check_id, bc_check_id): continue result: _CheckResult = {'result': CheckResult.FAILED} line_text = linecache.getline(secret.filename, secret.line_number) if line_text != "" and len(line_text.split( )) > 0 and line_text.split()[0] == 'git_commit': continue result = self.search_for_suppression( check_id=check_id, bc_check_id=bc_check_id, secret=secret, runner_filter=runner_filter, ) or result report.add_resource(f'{secret.filename}:{secret.secret_hash}') report.add_record( Record(check_id=check_id, bc_check_id=bc_check_id, check_name=secret.type, check_result=result, code_block=[(secret.line_number, line_text)], file_path= f'/{os.path.relpath(secret.filename, root_folder)}', file_line_range=[ secret.line_number, secret.line_number + 1 ], resource=secret.secret_hash, check_class=None, evaluations=None, file_abs_path=os.path.abspath(secret.filename))) return report