Skip to content

Commit 11696f8

Browse files
authored
add plugin to look for AWS key IDs (#100)
1 parent d378815 commit 11696f8

File tree

8 files changed

+108
-0
lines changed

8 files changed

+108
-0
lines changed

detect_secrets/core/usage.py

+5
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,11 @@ class PluginOptions(object):
244244
disable_flag_text='--no-keyword-scan',
245245
disable_help_text='Disables scanning for secret keywords.',
246246
),
247+
PluginDescriptor(
248+
classname='AWSKeyDetector',
249+
disable_flag_text='--no-aws-key-scan',
250+
disable_help_text='Disables scanning for AWS keys.',
251+
),
247252
]
248253

249254
def __init__(self, parser):

detect_secrets/plugins/aws.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""
2+
This plugin searches for AWS key IDs
3+
"""
4+
from __future__ import absolute_import
5+
6+
import re
7+
8+
from .base import RegexBasedDetector
9+
10+
11+
class AWSKeyDetector(RegexBasedDetector):
12+
secret_type = 'AWS key'
13+
blacklist = (
14+
re.compile(r'AKIA[0-9A-Z]{16}'),
15+
)

detect_secrets/plugins/base.py

+45
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from abc import ABCMeta
22
from abc import abstractmethod
3+
from abc import abstractproperty
34

5+
from detect_secrets.core.potential_secret import PotentialSecret
46
from detect_secrets.plugins.core.constants import WHITELIST_REGEX
57

68

@@ -83,3 +85,46 @@ def __dict__(self):
8385
return {
8486
'name': self.__class__.__name__,
8587
}
88+
89+
90+
class RegexBasedDetector(BasePlugin):
91+
"""Base class for regular-expressed based detectors.
92+
93+
To create a new regex-based detector, subclass this and set
94+
`secret_type` with a description and `blacklist`
95+
with a sequence of regular expressions, like:
96+
97+
class FooDetector(RegexBasedDetector):
98+
secret_type = "foo"
99+
blacklist = (
100+
re.compile(r'foo'),
101+
)
102+
"""
103+
__metaclass__ = ABCMeta
104+
105+
@abstractproperty
106+
def secret_type(self):
107+
return NotImplementedError
108+
109+
@abstractproperty
110+
def blacklist(self):
111+
return NotImplementedError
112+
113+
def analyze_string(self, string, line_num, filename):
114+
output = {}
115+
116+
for identifier in self.secret_generator(string):
117+
secret = PotentialSecret(
118+
self.secret_type,
119+
filename,
120+
identifier,
121+
line_num,
122+
)
123+
output[secret] = secret
124+
125+
return output
126+
127+
def secret_generator(self, string):
128+
for regex in self.blacklist:
129+
if regex.search(string):
130+
yield regex.pattern

detect_secrets/plugins/core/initialize.py

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
except ImportError:
55
from functools32 import lru_cache
66

7+
from ..aws import AWSKeyDetector # noqa: F401
78
from ..base import BasePlugin
89
from ..basic_auth import BasicAuthDetector # noqa: F401
910
from ..high_entropy_strings import Base64HighEntropyString # noqa: F401

tests/core/usage_test.py

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def test_consolidates_output_basic(self):
3333
'base64_limit': 4.5,
3434
},
3535
'PrivateKeyDetector': {},
36+
'AWSKeyDetector': {},
3637
}
3738
assert not hasattr(args, 'no_private_key_scan')
3839

tests/main_test.py

+2
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ def test_scan_string_basic(self, mock_baseline_initialize):
8787
) as printer_shim:
8888
assert main('scan --string'.split()) == 0
8989
assert printer_shim.message == textwrap.dedent("""
90+
AWSKeyDetector : False
9091
Base64HighEntropyString: False (3.459)
9192
BasicAuthDetector : False
9293
HexHighEntropyString : True (3.459)
@@ -103,6 +104,7 @@ def test_scan_string_cli_overrides_stdin(self):
103104
) as printer_shim:
104105
assert main('scan --string 012345'.split()) == 0
105106
assert printer_shim.message == textwrap.dedent("""
107+
AWSKeyDetector : False
106108
Base64HighEntropyString: False (2.585)
107109
BasicAuthDetector : False
108110
HexHighEntropyString : False (2.121)

tests/plugins/aws_key_test.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from __future__ import absolute_import
2+
from __future__ import unicode_literals
3+
4+
import pytest
5+
6+
from detect_secrets.plugins.aws import AWSKeyDetector
7+
from testing.mocks import mock_file_object
8+
9+
10+
class TestAWSKeyDetector(object):
11+
12+
@pytest.mark.parametrize(
13+
'file_content,should_flag',
14+
[
15+
(
16+
'AKIAZZZZZZZZZZZZZZZZ',
17+
True,
18+
),
19+
(
20+
'akiazzzzzzzzzzzzzzzz',
21+
False,
22+
),
23+
(
24+
'AKIAZZZ',
25+
False,
26+
),
27+
],
28+
)
29+
def test_analyze(self, file_content, should_flag):
30+
logic = AWSKeyDetector()
31+
32+
f = mock_file_object(file_content)
33+
output = logic.analyze(f, 'mock_filename')
34+
assert len(output) == (1 if should_flag else 0)
35+
for potential_secret in output:
36+
assert 'mock_filename' == potential_secret.filename

tests/pre_commit_hook_test.py

+3
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ def test_that_baseline_gets_updated(
131131
# See that we updated the plugins and version
132132
assert current_version == baseline_written['version']
133133
assert baseline_written['plugins_used'] == [
134+
{
135+
'name': 'AWSKeyDetector',
136+
},
134137
{
135138
'base64_limit': 4.5,
136139
'name': 'Base64HighEntropyString',

0 commit comments

Comments
 (0)