Plugins are scanners which attempt to find a secret in a given arbitrary line. All plugins must
extend from detect_secrets.plugins.base.BasePlugin, though for the most part, extending
detect_secrets.plugins.base.RegexBasedDetector should suffice for well-defined secrets.
A list of configured plugins can be found in the baseline produced:
$ detect-secrets scan test_data
{
"generated_at": "2020-11-13T19:05:03Z",
"version": "1.0.0",
"plugins_used": [
{
"name": "AWSKeyDetector"
},
{
"name": "Base64HighEntropyString",
"limit": 4.5
}
],
"filters_used": [...],
"results": {...}
}
This name field refers to the classname of the plugin used. An optional path field may be
found if the plugin in question is a locally imported plugin (rather than the plugins that ship
with this tool). Finally, any other values will be used to initialize the class in question (e.g.
Base64HighEntropyString will be initialized with the keyword parameter limit set to 4.5).
Plugins are configured through the global Settings object. If you are running this as a command
line tool, all plugins will be used by default. However, if you are running this scan as part of
another script, you will need to configure the plugins yourself.
from detect_secrets.core import baseline
from detect_secrets.settings import transient_settings
config = {
'plugins_used': [
{
'name': 'AWSKeyDetector',
},
],
}
with transient_settings(config):
secrets = baseline.create('.')TODO: Make it easier to "add all plugins" as a non-CLI user.
If you want to disable plugins, you can use the --disable-plugin flag.
If you want to disable all plugins, except for selected ones, you can do something like the following:
$ detect-secrets scan --list-all-plugins | \
grep -v 'BasicAuthDetector' | \
sed "s#^#--disable-plugin #g | \
xargs detect-secrets scan test_dataSince version 0.14.0, detect-secrets allows you to import external files as plugins, so that
you are not restricted to using plugins that have been merged upstream (or maintain your own fork).
This is also especially helpful if you're building your own plugin, and want to test it out before
contributing back to the community.
To do this, you can use the --plugin flag in detect-secrets scan. For example:
$ detect-secrets scan --plugin testing/plugins.py test_dataThis will be reflected in the baseline as such:
{
...
"plugins_used": [
{
"name": "AWSKeyDetector"
},
{
"name": "HippoDetector",
"path": "file:///Users/aaronloo/Documents/github/detect-secrets/testing/plugins.py"
}
]
...
}Custom plugins will still need to extend detect_secrets.plugins.base.BasePlugin, and adhere to
the interface for any other plugin (see Writing Your Own Plugin). If
there is something wrong with your plugin initialization, it will raise a TypeError.
⚠️ WARNING⚠️ By its very nature, the use of custom plugins essentially executes arbitrary code (by importing an arbitrary file). Therefore, caution should be exercised when doing so. For a deeper dive into the security assumptions made through this feature, check out
detect_secrets.util.importlib.import_file_as_module.
If you're looking to implement a regex-based secret scan, check out
detect_secrets.plugins.basic_auth.BasicAuthDetector for a clean example of adding a
RegexBasedDetector to your suite of plugins.
If you want something a little more advanced, with in-built secret verification, check out
detect_secrets.plugins.stripe.StripeDetector.
The general workflow for adding a new plugin is as such:
-
Write your tests
Before you write your plugin, you should know what it intends to do: what it should catch, and arguably more importantly, what it should avoid. Formalize these examples in tests!
For a basic example, see
tests/plugins/basic_auth_test.py. -
Write your plugin
All plugins MUST inherit from
detect_secrets.plugins.base.BasePlugin. See that class' docstrings for more detailed information.Depending on the complexity of your plugin, you may be able to inherit from
detect_secrets.plugins.base.RegexBasedDetectorinstead. This is useful if you want to merely customize a new regex rule. Check outdetect_secrets/plugins/basic_auth.pyfor a good example of this.Be sure to write comments about why your particular regex was crafted as it is!
-
Update documentation
Be sure to add your changes to the
README.mdandCHANGELOG.mdso that it will be easier for maintainers to bump the version and for other downstream consumers to get the latest information about plugins available.
Secret Verifiability was a concept introduced since version 0.12.4, and provides the ability
to decrease false positives by making an API call to a server to check whether a PotentialSecret
is indeed real. This can be extremely useful for specific secrets (e.g. RegexBasedDetector
subclasses) since we are able to determine which server to contact.
You are able to configure your verification settings via the --no-verify or --only-verified
flags in detect-secrets scan. By default, detect-secrets will attempt to verify all secrets,
and display both unverified and verified true secrets.
This is implemented as the built-in filter
detect_secrets.filters.common.is_ignored_due_to_verification_policies, which invokes each
plugin's verify function. Furthermore, it leverages the dependency injection system, so verify
functions MUST declare a dependency on some combination of the following variables:
| Variable Name | Type | Description |
|---|---|---|
secret |
string | The raw secret value. |
context |
detect_secrets.util.code_snippet.CodeSnippet |
Lines of code surrounding secret. |
The context is especially useful since research has shown (source: Section V-D) that there's an 80% chance of finding a multi-factor secret (e.g. username + password) within five lines of context, before and after a secret.
For more information on how to verify secrets, check out: https://github.com/streaak/keyhacks.
- There should be a total of three modified files in a minimal new plugin: the plugin file, it's corresponding test, and an updated CHANGELOG.