detect-secrets icon indicating copy to clipboard operation
detect-secrets copied to clipboard

audit fails with custom plugin

Open steamraven opened this issue 2 years ago • 8 comments

When running detect-secrets audit against a baseline created with a custom filter, I get a python error

@ detect-secrets  audit  .secrets.baseline
Traceback (most recent call last):
  File "/home/matthewh/.local/lib/python3.10/site-packages/detect_secrets/core/plugins/initialize.py", line 19, in from_secret_type
    plugin_type = get_mapping_from_secret_type_to_class()[secret_type]
KeyError: 'IP Addresses'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/matthewh/.local/bin/detect-secrets", line 8, in <module>
    sys.exit(main())
  File "/home/matthewh/.local/lib/python3.10/site-packages/detect_secrets/main.py", line 32, in main
    handle_audit_action(args)
  File "/home/matthewh/.local/lib/python3.10/site-packages/detect_secrets/main.py", line 152, in handle_audit_action
    audit.audit_baseline(args.filename[0])
  File "/home/matthewh/.local/lib/python3.10/site-packages/detect_secrets/audit/audit.py", line 25, in audit_baseline
    if _classify_secrets(get_secret_iterator(secrets)):
  File "/home/matthewh/.local/lib/python3.10/site-packages/detect_secrets/audit/audit.py", line 40, in _classify_secrets
    secret.secret_value = get_raw_secret_from_file(secret)
  File "/home/matthewh/.local/lib/python3.10/site-packages/detect_secrets/audit/common.py", line 59, in get_raw_secret_from_file
    for item in get_raw_secrets_from_file(secret, line_getter_factory):
  File "/home/matthewh/.local/lib/python3.10/site-packages/detect_secrets/audit/common.py", line 78, in get_raw_secrets_from_file
    plugin = cast(BasePlugin, plugins.initialize.from_secret_type(secret.type))
  File "/home/matthewh/.local/lib/python3.10/site-packages/detect_secrets/core/plugins/initialize.py", line 21, in from_secret_type
    raise TypeError
TypeError
Return 1
$ detect-secrets  --version
1.3.0

Relevant sections of .secrets-baseline

{
  "version": "1.3.0",
  "plugins_used": [
...
    {
      "name": "IPAddressDetector",
      "path": "file:///home/matthewh/no-snap/mvfiler_body/ip_detector.py"
    },
...
  ],
  "results": {
...
    "OPTIONS.txt": [
      {
        "type": "IP Addresses",
        "filename": "OPTIONS.txt",
        "hashed_secret": ...,
        "is_verified": false,
        "line_number": 20
      }
    ],
...
  },
  "generated_at": "2022-09-09T18:36:27Z"
}

This appears to be a problem with the lru_cache on "get_mapping_from_secret_type_to_class" in detect_secrets.core.plugins.util. Removing the lru_cache allows this to work, but slowly

steamraven avatar Sep 09 '22 20:09 steamraven

Hi @steamraven, thank you for bringing up this issue you encountered. Since it seems to be related to a custom plugin you created, could you provide us with more info about it? This would help us debug and understand what the underlying problem is.

lorenzodb1 avatar Sep 16 '22 17:09 lorenzodb1

I have replicated with a minimal plugin:

minimal.py:

from detect_secrets.plugins.base import BasePlugin

class Nothing(BasePlugin):
    secret_type = 'Nothing'
    def analyze_string(self, string: str) -> Generator[str, None, None]:
        yield string

With just this file in a directory: $ detect-secrets scan --all-files --no-verify --plugin minimal.py | tee .baseline

Then $ detect-secrets audit .baseline --report

steamraven avatar Sep 19 '22 16:09 steamraven

Also, with my more useful plugin. This does not seem to be plugin specific, but a race condition in the code to search for plugins themselves.

ip_detector.py:

import re
from detect_secrets.plugins.base import RegexBasedDetector

class IPAddressDetector(RegexBasedDetector):
    secret_type = 'IP Addresses'
    denylist = [
        re.compile(
            r'(?:[0-9]{1,3}\.){3}[0-9]{1,3}'
        )
    ]

steamraven avatar Sep 19 '22 16:09 steamraven

@steamraven Hello. I do not think this is a race condition - however I do think there is a bug related to some recent changes. Can you tell me if in your baseline file that you are auditing has any of the following secret types:

  • AWS Access Key
  • Cloudant Credentials
  • IBM COS HMAC Credentials
  • SoftLayer Credentials

jpdakran avatar Sep 22 '22 15:09 jpdakran

That pr does not fix it.

I generated a baseline using the minimal.py plugin on a folder containing just the plugin itself. The only secrets are "Secret Keyword" and "Nothing".

Specifically, the plugins secret type IS in the baseline. It appears audit will fail with ANY external plugin secret type.

As I said above, commenting out the lru_cache at https://github.com/Yelp/detect-secrets/blob/57cc31909cb61a6a457a1b709bd432ff355cae3b/detect_secrets/core/plugins/util.py#L23

resolves the issue. Unfortunately, it slows down secret detection a lot.

I can get you more detailed tracing but it appears that get_mapping_from_secret_type_to_class is called during parameter setup with a generic settings object that has no plugins. Because the lru_cache, this get_mapping_from_secret_type_to_class does not return any external plugins

If the lru_cache is removed, it is eventually called with a settings object that has the external plugin

steamraven avatar Sep 22 '22 19:09 steamraven

Here is my baseline:

https://gist.github.com/steamraven/8005a93f539ebb9fa25ece4ac9d7a878

steamraven avatar Sep 22 '22 19:09 steamraven

main.parse_args calls ParserBuilder.add_console_use_arguments add_console_use_arguments adds baseline.parse_args to self._post_processors

main.parse_args calls ParserBuilder.parse_args ParserBuilder.parse_args** calls all the self._post_processors, which includes baseline.parse_args`

-- Note that baseline.add_baseline_option is called on the "scan" subparser, but the baseline.parse_args is added to the parent, so there is no way to add a "--baseline" option to the audit invocation as far as I can tell

baseline.parse_args does not see a "--baseline" option and calls common.initialize_plugin_settings common.initialize_plugin_settings calls settings.default_settings settings.default_settings calls core.plugins.util.get_mapping_from_secret_type_to_class within a settings.transient_settings

It appears there is a cache_bust in settings.transient_settings that should clear some caches. Maybe it needs to clear the lru_cache

edited for clarity

steamraven avatar Sep 22 '22 20:09 steamraven

Encountered the same issue. I used my customized plugins to generate a baseline file,

detect-secrets scan ./ \
  --all-files \
  --baseline .detect-secrets/.secrets.baseline \
  --exclude-files '^\.git(/.*)?$' \
  --exclude-files '^\.detect-secrets(/.*)?$' \
  -p .detect-secrets/plugins/absolute_filepath.py \
  -p .detect-secrets/plugins/aws_sensitive_info.py \
  -p .detect-secrets/plugins/email_address.py \
  -p .detect-secrets/plugins/ip_address.py

But the newly generated baseline file can not be used for audit due to KeyError: throw by python

If I don't use the -p argument but directly put my plugins into the plugins folder of detect-secrets, then there is no such issue.

perryzjc avatar May 12 '23 00:05 perryzjc