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 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_load_and_output(): with open('.secrets.baseline') as f: data = json.loads(f.read()) secrets = baseline.load(data, filename='.secrets.baseline') output = baseline.format_for_output(secrets) for item in [data, output]: item.pop('generated_at') # We perform string matching because we want to ensure stable sorts. assert json.dumps(output) == json.dumps(data) # We need to make sure that default values carry through, for future backwards compatibility. for plugin in output['plugins_used']: if plugin['name'] == 'Base64HighEntropyString': assert plugin['limit'] == 4.5 break
def test_fails_if_no_line_numbers_found(printer): with transient_settings({ 'plugins_used': [ { 'name': 'Base64HighEntropyString' }, ], }): secrets = SecretsCollection() secrets.scan_file('test_data/config.env') # Remove line numbers secrets = baseline.load( baseline.format_for_output(secrets, is_slim_mode=True)) with mock.patch('detect_secrets.audit.io.clear_screen') as m: run_logic(secrets) assert not m.called assert 'No line numbers found in baseline' in printer.message
def test_restores_line_numbers(): with tempfile.NamedTemporaryFile('w+') as f: with redirect_stdout(f): main_module.main(['scan', '--slim', 'test_data/config.env']) f.seek(0) main_module.main([ 'scan', '--slim', 'test_data/config.md', 'test_data/config.env', '--baseline', f.name, ]) f.seek(0) secrets = baseline.load(baseline.load_from_file(f.name)) # Make sure both old and new files exist assert secrets.files == {'test_data/config.env', 'test_data/config.md'} # Make sure they both have line numbers assert list(secrets['test_data/config.env'])[0].line_number assert list(secrets['test_data/config.md'])[0].line_number
def run_logic( secrets: SecretsCollection, input: Optional[str] = None, ) -> SecretsCollection: """ :param input: if provided, will automatically quit at the end of input string. otherwise, will assert that no user input is requested. """ with tempfile.NamedTemporaryFile() as f: baseline.save_to_file(secrets, f.name) f.seek(0) with mock.patch('detect_secrets.audit.io.input') as m: if input is not None: m.side_effect = list(input) + ['q'] main(['audit', f.name]) if input is None: assert not m.called return baseline.load(baseline.load_from_file(f.name), f.name)