Skip to content

Commit 7b13a60

Browse files
authored
Merge pull request #196 from Yelp/common-filters
shared false positive filters
2 parents fbf245f + 28c41bd commit 7b13a60

File tree

8 files changed

+95
-31
lines changed

8 files changed

+95
-31
lines changed

detect_secrets/plugins/artifactory.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class ArtifactoryDetector(RegexBasedDetector):
1111

1212
denylist = [
1313
# artifactory tokens begin with AKC
14-
re.compile(r'(\s|=|:|"|^)AKC\w{10,}'), # api token
14+
re.compile(r'(?:\s|=|:|"|^)AKC\w{10,}'), # api token
1515
# artifactory encrypted passwords begin with AP6
16-
re.compile(r'(\s|=|:|"|^)AP6\w{10,}'), # password
16+
re.compile(r'(?:\s|=|:|"|^)AP6\w{10,}'), # password
1717
]

detect_secrets/plugins/base.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
from abc import abstractmethod
44
from abc import abstractproperty
55

6+
from .common.constants import ALLOWLIST_REGEXES
7+
from .common.filters import is_false_positive
68
from detect_secrets.core.potential_secret import PotentialSecret
7-
from detect_secrets.plugins.common.constants import ALLOWLIST_REGEXES
89

910

1011
class BasePlugin(object):
@@ -169,4 +170,7 @@ def analyze_string_content(self, string, line_num, filename):
169170
def secret_generator(self, string, *args, **kwargs):
170171
for regex in self.denylist:
171172
for match in regex.findall(string):
173+
if is_false_positive(match):
174+
continue
175+
172176
yield match
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""
2+
Heuristic, false positive filters that are shared across all plugin types.
3+
This abstraction allows for development of later ML work, or further
4+
heuristical determinations (e.g. word filter, entropy comparator).
5+
"""
6+
import string
7+
8+
9+
def is_false_positive(secret):
10+
for func in [
11+
is_sequential_string,
12+
]:
13+
if func(secret):
14+
return True
15+
16+
return False
17+
18+
19+
def is_sequential_string(secret):
20+
"""
21+
Returns true if string is sequential.
22+
"""
23+
sequences = (
24+
(
25+
string.ascii_uppercase +
26+
string.ascii_uppercase +
27+
string.digits +
28+
string.ascii_uppercase +
29+
string.ascii_uppercase +
30+
'+/'
31+
),
32+
33+
# Capturing any number sequences
34+
'0123456789' * 2,
35+
36+
string.hexdigits.upper() + string.hexdigits.upper(),
37+
string.ascii_uppercase + '=/',
38+
)
39+
40+
uppercase = secret.upper()
41+
for sequential_string in sequences:
42+
if uppercase in sequential_string:
43+
return True
44+
45+
return False

detect_secrets/plugins/high_entropy_strings.py

+7-24
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,12 @@
1414
import yaml
1515

1616
from .base import BasePlugin
17+
from .common.filters import is_false_positive
18+
from .common.ini_file_parser import IniFileParser
19+
from .common.yaml_file_parser import YamlFileParser
1720
from detect_secrets.core.potential_secret import PotentialSecret
18-
from detect_secrets.plugins.common.ini_file_parser import IniFileParser
19-
from detect_secrets.plugins.common.yaml_file_parser import YamlFileParser
20-
21-
22-
IGNORED_SEQUENTIAL_STRINGS = (
23-
(
24-
string.ascii_uppercase +
25-
string.ascii_uppercase +
26-
string.digits +
27-
string.ascii_uppercase +
28-
string.ascii_uppercase +
29-
'+/'
30-
),
31-
string.hexdigits.upper() + string.hexdigits.upper(),
32-
string.ascii_uppercase + '=/',
33-
)
21+
22+
3423
YAML_EXTENSIONS = (
3524
'.yaml',
3625
'.yml',
@@ -97,22 +86,16 @@ def calculate_shannon_entropy(self, data):
9786

9887
return entropy
9988

100-
def _is_sequential_string(self, string):
101-
uppercased_string = string.upper()
102-
for sequential_string in IGNORED_SEQUENTIAL_STRINGS:
103-
if uppercased_string in sequential_string:
104-
return True
105-
return False
106-
10789
def analyze_string_content(self, string, line_num, filename):
10890
"""Searches string for custom pattern, and captures all high entropy strings that
10991
match self.regex, with a limit defined as self.entropy_limit.
11092
"""
11193
output = {}
11294

11395
for result in self.secret_generator(string):
114-
if self._is_sequential_string(result):
96+
if is_false_positive(result):
11597
continue
98+
11699
secret = PotentialSecret(self.secret_type, filename, result, line_num)
117100
output[secret] = secret
118101

detect_secrets/plugins/stripe.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ class StripeDetector(RegexBasedDetector):
1414

1515
denylist = (
1616
# stripe standard keys begin with sk_live and restricted with rk_live
17-
re.compile(r'(r|s)k_live_[0-9a-zA-Z]{24}'),
17+
re.compile(r'(?:r|s)k_live_[0-9a-zA-Z]{24}'),
1818
)

test_data/config.ini

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
[credentials]
2-
password = 12345678901234
2+
password = 123456789a1234
33

44
[parent]
55
[child]
66
keyA = 678912345
77
keyB = value1
88

99
[aws]
10-
aws_secret_key = 2345678901
10+
aws_secret_key = 23456789a1
1111

1212
[key with multiple values]
1313
keyA =

tests/plugins/common/filters_test.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from __future__ import absolute_import
2+
3+
import pytest
4+
5+
from detect_secrets.plugins.common import filters
6+
7+
8+
class TestIsSequentialString:
9+
# TODO: More tests should be had.
10+
11+
@pytest.mark.parametrize(
12+
'secret',
13+
(
14+
'ABCDEF',
15+
16+
# Number sequences
17+
'0123456789',
18+
'1234567890',
19+
),
20+
)
21+
def test_success(self, secret):
22+
assert filters.is_sequential_string(secret)
23+
24+
@pytest.mark.parametrize(
25+
'secret',
26+
(
27+
'BEEF1234',
28+
),
29+
)
30+
def test_failure(self, secret):
31+
assert not filters.is_sequential_string(secret)

tests/plugins/high_entropy_strings_test.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ def setup(self):
165165
'Location: test_data/config.ini:10',
166166
'Location: test_data/config.ini:15',
167167
'Location: test_data/config.ini:21',
168-
'Location: test_data/config.ini:22', ],
168+
'Location: test_data/config.ini:22',
169+
],
169170
),
170171
(
171172
'test_data/files/file_with_secrets.py',

0 commit comments

Comments
 (0)