Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add audit baseline functionality #44

Merged
merged 11 commits into from
Jun 27, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
handling case where auditing ini/yaml files
  • Loading branch information
Aaron Loo committed Jun 26, 2018
commit bf07ab54ca1ed1b40ad1051a2fef012c0fc24109
33 changes: 23 additions & 10 deletions detect_secrets/core/audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from collections import defaultdict

from ..plugins.core import initialize
from ..plugins.high_entropy_strings import HighEntropyStringsPlugin
from .baseline import merge_results
from .color import BashColor
from .color import Color
Expand Down Expand Up @@ -147,6 +148,7 @@ def _save_baseline_to_file(filename, data): # pragma: no cover


def _secret_generator(baseline):
"""Generates secrets to audit, from the baseline"""
current_secret_index = 1
num_secrets_to_parse = sum(map(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Maybe add some comments and space it out so there isn't the ))). I initially misread it as map gets all the secrets that don't have an is_secret attribute (i.e. the ones that have not been audited) and adds that number to the number of results in the baseline.

num_secrets_to_parse = sum(
  map(
      lambda filename: len(
        list(
          filter(
              lambda secret: not hasattr(secret, 'is_secret'),
              baseline['results'][filename],
          )
        )
      ),
      baseline['results'],
  )
)

lambda filename: len(list(filter(
Expand Down Expand Up @@ -250,7 +252,7 @@ def _highlight_secret(secret_line, secret, filename, plugin_settings):
plugin_settings,
)

for raw_secret in plugin.secret_generator(secret_line):
for raw_secret in _raw_secret_generator(plugin, secret_line):
secret_obj = PotentialSecret(
plugin.secret_type,
filename,
Expand All @@ -265,15 +267,6 @@ def _highlight_secret(secret_line, secret, filename, plugin_settings):
# We only want to highlight the right one.
if secret_obj.secret_hash == secret['hashed_secret']:
break
else:
# It hits here if the secret has been moved, from the original
# line number listed in the baseline.
raise SecretNotFoundOnSpecifiedLineError(
textwrap.dedent("""
ERROR: Secret not found on specified line number!
Try recreating your baseline to fix this issue.
""")[1:-1],
)

index_of_secret = secret_line.index(raw_secret)
return '{}{}{}'.format(
Expand All @@ -286,6 +279,26 @@ def _highlight_secret(secret_line, secret, filename, plugin_settings):
)


def _raw_secret_generator(plugin, secret_line):
"""Generates raw secrets by re-scanning the line, with the specified plugin"""
for raw_secret in plugin.secret_generator(secret_line):
yield raw_secret

if issubclass(plugin.__class__, HighEntropyStringsPlugin):
with plugin.non_quoted_string_regex(strict=False):
for raw_secret in plugin.secret_generator(secret_line):
yield raw_secret

# It hits here if the secret has been moved, from the original
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be more pythonic/you wouldn't need a comment (uncle bob 💯 ), if you did for .. else (i.e. if you don't take a break you get a raise.)

# line number listed in the baseline.
raise SecretNotFoundOnSpecifiedLineError(
textwrap.dedent("""
ERROR: Secret not found on specified line number!
Try recreating your baseline to fix this issue.
""")[1:-1],
)


def _get_user_decision(prompt_secret_decision=True):
"""
:type prompt_secret_decision: bool
Expand Down
44 changes: 26 additions & 18 deletions detect_secrets/plugins/high_entropy_strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,37 @@ def secret_generator(self, string):
if entropy_value > self.entropy_limit:
yield result

@contextmanager
def non_quoted_string_regex(self, strict=True):
"""For certain file formats, strings need not necessarily follow the
normal convention of being denoted by single or double quotes. In these
cases, we modify the regex accordingly.

Public, because detect_secrets.core.audit needs to reference it.

:type strict: bool
:param strict: if True, the regex will match the entire string.
"""
old_regex = self.regex

regex_alternative = r'([{}]+)'.format(re.escape(self.charset))
if strict:
regex_alternative = r'^' + regex_alternative + r'$'

self.regex = re.compile(regex_alternative)

try:
yield
finally:
self.regex = old_regex

def _analyze_ini_file(self, file, filename):
"""
:returns: same format as super().analyze()
"""
potential_secrets = {}

with self._non_quoted_string_regex():
with self.non_quoted_string_regex():
for value, lineno in IniFileParser(file).iterator():
potential_secrets.update(self.analyze_string(
value,
Expand All @@ -131,7 +155,7 @@ def _analyze_yaml_file(self, file, filename):
potential_secrets = {}

to_search = [data]
with self._non_quoted_string_regex():
with self.non_quoted_string_regex():
while len(to_search) > 0:
item = to_search.pop()

Expand All @@ -155,22 +179,6 @@ def _analyze_yaml_file(self, file, filename):

return potential_secrets

@contextmanager
def _non_quoted_string_regex(self):
"""For certain file formats, strings need not necessarily follow the
normal convention of being denoted by single or double quotes. In these
cases, we modify the regex accordingly.
"""
old_regex = self.regex
self.regex = re.compile(
r'^([%s]+)$' % re.escape(self.charset),
)

try:
yield
finally:
self.regex = old_regex


class HexHighEntropyString(HighEntropyStringsPlugin):
"""HighEntropyStringsPlugin for hex strings"""
Expand Down