Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding convenience function to get default settings #392

Merged
merged 2 commits into from
Jan 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ If you want to **only** run a specific plugin, you can do:
```bash
$ detect-secrets scan --list-all-plugins | \
grep -v 'BasicAuthDetector' | \
sed "s#^#--disable-plugin #g | \
sed "s#^#--disable-plugin #g" | \
xargs detect-secrets scan test_data
```

Expand All @@ -129,6 +129,66 @@ ratio.
$ detect-secrets audit .secrets.baseline
```

### Usage in Other Python Scripts

**Basic Use:**

```python
from detect_secrets import SecretsCollection
from detect_secrets.settings import default_settings

secrets = SecretsCollection()
with default_settings():
secrets.scan_file('test_data/config.ini')


import json
print(json.dumps(secrets.json(), indent=2))
```

**More Advanced Configuration:**

```python
from detect_secrets import SecretsCollection
from detect_secrets.settings import transient_settings

secrets = SecretsCollection()
with transient_settings({
# Only run scans with only these plugins.
# This format is the same as the one that is saved in the generated baseline.
'plugins_used': [
# Example of configuring a built-in plugin
{
'name': 'Base64HighEntropyString',
'limit': 5.0,
},

# Example of using a custom plugin
{
'name': 'HippoDetector',
'path': 'file:///Users/aaronloo/Documents/github/detect-secrets/testing/plugins.py',
},
],

# We can also specify whichever additional filters we want.
# This is an example of using the function `is_identified_by_ML_model` within the
# local file `./private-filters/example.py`.
'filters_used': [
{
'path': 'file://private-filters/example.py::is_identified_by_ML_model',
},
]
}) as settings:
# If we want to make any further adjustments to the created settings object (e.g.
# disabling default filters), we can do so as such.
settings.disable_filters(
'detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign',
'detect_secrets.filters.heuristic.is_likely_id_string',
)

secrets.scan_file('test_data/config.ini')
```

## Installation

```bash
Expand Down
1 change: 1 addition & 0 deletions detect_secrets/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .core.secrets_collection import SecretsCollection # noqa: F401
8 changes: 4 additions & 4 deletions detect_secrets/core/scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def scan_line(line: str) -> Generator[PotentialSecret, None, None]:

def scan_file(filename: str) -> Generator[PotentialSecret, None, None]:
if not get_plugins(): # pragma: no cover
log.warning('No plugins to scan with!')
log.error('No plugins to scan with!')
return

if _is_filtered_out(required_filter_parameters=['filename'], filename=filename):
Expand Down Expand Up @@ -158,7 +158,7 @@ def scan_diff(diff: str) -> Generator[PotentialSecret, None, None]:
:raises: ImportError
"""
if not get_plugins(): # pragma: no cover
log.warning('No plugins to scan with!')
log.error('No plugins to scan with!')
return

for filename, lines in _get_lines_from_diff(diff):
Expand All @@ -175,7 +175,7 @@ def scan_for_allowlisted_secrets_in_file(filename: str) -> Generator[PotentialSe
This scans specifically for these lines, and ignores everything else.
"""
if not get_plugins(): # pragma: no cover
log.warning('No plugins to scan with!')
log.error('No plugins to scan with!')
return

if _is_filtered_out(
Expand All @@ -197,7 +197,7 @@ def scan_for_allowlisted_secrets_in_file(filename: str) -> Generator[PotentialSe

def scan_for_allowlisted_secrets_in_diff(diff: str) -> Generator[PotentialSecret, None, None]:
if not get_plugins(): # pragma: no cover
log.warning('No plugins to scan with!')
log.error('No plugins to scan with!')
return

for filename, lines in _get_lines_from_diff(diff):
Expand Down
13 changes: 7 additions & 6 deletions detect_secrets/core/usage/common.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import argparse
import os

from ...settings import default_settings
from ...settings import get_settings
from ..plugins.util import get_mapping_from_secret_type_to_class


def valid_path(path: str) -> str:
Expand All @@ -24,9 +24,10 @@ def initialize_plugin_settings(args: argparse.Namespace) -> None:
if get_settings().plugins:
return

# TODO: This should take cli args (e.g. --base64-limit)
# We initialize the `settings` variable here, but we can't save it to the global object
# yet, since the contextmanager will revert those changes. As such, we quit the context
# first, then set it to the global namespace.
with default_settings() as settings:
pass

get_settings().configure_plugins([
{'name': plugin_type.__name__}
for plugin_type in get_mapping_from_secret_type_to_class().values()
])
get_settings().set(settings)
18 changes: 18 additions & 0 deletions detect_secrets/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ def configure_settings_from_baseline(baseline: Dict[str, Any], filename: str = '
return settings


@contextmanager
def default_settings() -> Generator['Settings', None, None]:
"""Convenience function to enable all plugins and default filters."""
from .core.plugins.util import get_mapping_from_secret_type_to_class

with transient_settings({
'plugins_used': [
{'name': plugin_type.__name__}
for plugin_type in get_mapping_from_secret_type_to_class().values()
],
}) as settings:
yield settings


@contextmanager
def transient_settings(config: Dict[str, Any]) -> Generator['Settings', None, None]:
"""Allows the customizability of non-global settings per invocation."""
Expand Down Expand Up @@ -91,6 +105,10 @@ def clear(self) -> None:
}
}

def set(self, other: 'Settings') -> None:
self.plugins = other.plugins
self.filters = other.filters

def configure_plugins(self, config: List[Dict[str, Any]]) -> 'Settings':
"""
:param config: e.g.
Expand Down
9 changes: 9 additions & 0 deletions tests/core/usage/scan_usage_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import pytest

from detect_secrets.core import plugins
from detect_secrets.core.plugins.util import get_mapping_from_secret_type_to_class
from detect_secrets.core.usage import ParserBuilder
from detect_secrets.settings import get_settings
Expand Down Expand Up @@ -31,3 +32,11 @@ def test_force_use_all_plugins(parser):
parser.parse_args(['scan', '--force-use-all-plugins', '--baseline', f.name])

assert len(get_settings().plugins) == len(get_mapping_from_secret_type_to_class())


def test_default_plugins_initialized(parser):
parser.parse_args(['scan', '--hex-limit', '2'])

assert len(get_settings().plugins) == len(get_mapping_from_secret_type_to_class())
assert plugins.initialize.from_plugin_classname('HexHighEntropyString').entropy_limit == 2
assert plugins.initialize.from_plugin_classname('Base64HighEntropyString').entropy_limit == 4.5