Skip to content

Commit

Permalink
🔭 [Keyword Plugin] Accuracy improvements
Browse files Browse the repository at this point in the history
- Add FP check for '/etc/' in secret
- Add FP heuristic for secrets that look like directories
- Add <secret> FP heuristic for .example files
- Add more to FALSE_POSITIVES dict
- Make all non-quotes required filetypes skip secrets starting with a $
- Make quotes required for Terraform files
  • Loading branch information
KevinHock committed Aug 23, 2019
1 parent 2f2ccb0 commit a53d12e
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 17 deletions.
22 changes: 13 additions & 9 deletions detect_secrets/plugins/common/filetype.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@

class FileType(Enum):
CLS = 0
GO = 1
JAVA = 2
JAVASCRIPT = 3
PHP = 4
OBJECTIVE_C = 5
PYTHON = 6
SWIFT = 7
YAML = 8
OTHER = 9
EXAMPLE = 1
GO = 2
JAVA = 3
JAVASCRIPT = 4
PHP = 5
OBJECTIVE_C = 6
PYTHON = 7
SWIFT = 8
TERRAFORM = 9
YAML = 10
OTHER = 11


EXTENSION_TO_FILETYPE = {
'.cls': FileType.CLS,
'.example': FileType.EXAMPLE,
'.eyaml': FileType.YAML,
'.go': FileType.GO,
'.java': FileType.JAVA,
Expand All @@ -26,6 +29,7 @@ class FileType(Enum):
'.py': FileType.PYTHON,
'.pyi': FileType.PYTHON,
'.swift': FileType.SWIFT,
'.tf': FileType.TERRAFORM,
'.yaml': FileType.YAML,
'.yml': FileType.YAML,
}
Expand Down
41 changes: 33 additions & 8 deletions detect_secrets/plugins/keyword.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@
"'this",
'(nsstring',
'-default}',
'/etc/passwd:ro',
'::',
'<%=',
'<?php',
'<a',
'<aws_secret_access_key>',
'<input',
Expand All @@ -80,43 +81,60 @@
"\\k.*'",
'`cat',
'`grep',
'`sudo',
'account_password',
'api_key',
'disable',
'dummy_secret',
'dummy_value',
'false',
'false):',
'false,',
'false;',
'login_password',
'none',
'none,',
'none}',
'not',
'not_real_key',
'null',
'null,',
'null.*"',
"null.*'",
'null;',
'pass',
'pass)',
'password',
'password)',
'password))',
'password,',
'password},',
'prompt',
'redacted',
'secret',
'some_key',
'str',
'str_to_sign',
'string',
'string)',
'string,',
'string;',
'string?',
'string?)',
'string}',
'string}}',
'test',
'test-access-key',
'thisisnottherealsecret',
'todo',
'true',
'true):',
'true,',
'true;',
'undef',
'undef,',
'{',
'{{',
}
# Includes ], ', " as closing
CLOSING = r'[]\'"]{0,2}'
Expand Down Expand Up @@ -224,6 +242,7 @@
FileType.JAVASCRIPT,
FileType.PYTHON,
FileType.SWIFT,
FileType.TERRAFORM,
}


Expand Down Expand Up @@ -294,20 +313,26 @@ def secret_generator(self, string, filetype):

def probably_false_positive(lowered_secret, filetype):
if (
'fake' in lowered_secret
or 'forgot' in lowered_secret
or lowered_secret in FALSE_POSITIVES
any(
false_positive in lowered_secret
for false_positive in (
'/etc/',
'fake',
'forgot',
)
) or lowered_secret in FALSE_POSITIVES
or lowered_secret.count('/') >= 3
# For e.g. "secret": "{secret}"
or (
lowered_secret[0] == '{'
and lowered_secret[-1] == '}'
) or (
filetype == FileType.PHP
filetype not in QUOTES_REQUIRED_FILETYPES
and lowered_secret[0] == '$'
) or (
filetype == FileType.YAML
and lowered_secret.startswith('{{')
and lowered_secret.endswith('}}')
filetype == FileType.EXAMPLE
and lowered_secret[0] == '<'
and lowered_secret[-1] == '>'
)
):
return True
Expand Down
19 changes: 19 additions & 0 deletions tests/plugins/keyword_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@
'private_key "";', # Nothing in the quotes
'private_key \'"no spaces\';', # Has whitespace in the secret
'private_key "fake";', # 'fake' in the secret
'private_key "some/dir/aint/a/secret";', # 3 or more /
'private_key "${FOO}";', # Starts with ${ and ends with }
'private_key "hopenobodyfindsthisone\';', # Double-quote does not match single-quote
'private_key \'hopenobodyfindsthisone";', # Single-quote does not match double-quote
],
Expand Down Expand Up @@ -360,3 +362,20 @@ def test_analyze_yaml_negatives(self, file_content, file_extension):
'mock_filename{}'.format(file_extension),
)
assert len(output) == 0

@pytest.mark.parametrize(
'file_content',
STANDARD_POSITIVES,
)
def test_analyze_example_negatives(self, file_content):
logic = KeywordDetector()

# Make it start with `<`, (and end with `>`) so it hits our false-positive check
f = mock_file_object(
file_content.replace('m{', '<').replace('}', '>'),
)
output = logic.analyze(
f,
'mock_filename.example',
)
assert len(output) == 0

0 comments on commit a53d12e

Please sign in to comment.