Add --exclude-values arg to detect-aws-credentials

Adds an argument to pass a regex which will exclude any values that match it to
not count as a failure.

The rationale behind this are test variables, such as those used with
LocalStack (in some cases literally `TEST`) are counted as a failure. Adding
this parameter will allow bypassing the check for those values only instead of
excluding the files from the hook running where _real_ credentials could
potentially leak.
This commit is contained in:
Justin Roberson 2023-01-12 11:44:14 -05:00
parent 507fb40267
commit 0a1e725b63
2 changed files with 46 additions and 1 deletions

View file

@ -3,6 +3,7 @@ from __future__ import annotations
import argparse import argparse
import configparser import configparser
import os import os
import re
from typing import NamedTuple from typing import NamedTuple
from typing import Sequence from typing import Sequence
@ -89,6 +90,11 @@ def check_file_for_aws_keys(
return bad_files return bad_files
def filter_keys(keys: set[str], exclude: str) -> set[str]:
pattern = re.compile(exclude)
return {key for key in keys if not pattern.match(key)}
def main(argv: Sequence[str] | None = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='+', help='Filenames to run') parser.add_argument('filenames', nargs='+', help='Filenames to run')
@ -110,6 +116,12 @@ def main(argv: Sequence[str] | None = None) -> int:
action='store_true', action='store_true',
help='Allow hook to pass when no credentials are detected.', help='Allow hook to pass when no credentials are detected.',
) )
parser.add_argument(
'--exclude-values',
dest='exclude_values',
default='^$',
help='Regular expression for secret values that should be excluded.',
)
args = parser.parse_args(argv) args = parser.parse_args(argv)
credential_files = set(args.credentials_file) credential_files = set(args.credentials_file)
@ -137,7 +149,7 @@ def main(argv: Sequence[str] | None = None) -> int:
) )
return 2 return 2
keys_b = {key.encode() for key in keys} keys_b = {key.encode() for key in filter_keys(keys, args.exclude_values)}
bad_filenames = check_file_for_aws_keys(args.filenames, keys_b) bad_filenames = check_file_for_aws_keys(args.filenames, keys_b)
if bad_filenames: if bad_filenames:
for bad_file in bad_filenames: for bad_file in bad_filenames:

View file

@ -4,6 +4,7 @@ from unittest.mock import patch
import pytest import pytest
from pre_commit_hooks.detect_aws_credentials import filter_keys
from pre_commit_hooks.detect_aws_credentials import get_aws_cred_files_from_env from pre_commit_hooks.detect_aws_credentials import get_aws_cred_files_from_env
from pre_commit_hooks.detect_aws_credentials import get_aws_secrets_from_env from pre_commit_hooks.detect_aws_credentials import get_aws_secrets_from_env
from pre_commit_hooks.detect_aws_credentials import get_aws_secrets_from_file from pre_commit_hooks.detect_aws_credentials import get_aws_secrets_from_file
@ -101,6 +102,18 @@ def test_get_aws_secrets_from_file(filename, expected_keys):
assert keys == expected_keys assert keys == expected_keys
@pytest.mark.parametrize(
('keys', 'exclude', 'expected_ret'),
(
({'foo', 'bar'}, '^$', {'foo', 'bar'}),
({'foo', 'bar', 'baz'}, '^bar$', {'foo', 'baz'}),
({'foo', 'bar', 'baz'}, '^ba', {'foo'}),
),
)
def test_filter_keys(keys, exclude, expected_ret):
assert filter_keys(keys, exclude) == expected_ret
@pytest.mark.parametrize( @pytest.mark.parametrize(
('filename', 'expected_retval'), ('filename', 'expected_retval'),
( (
@ -123,6 +136,26 @@ def test_detect_aws_credentials(filename, expected_retval):
assert ret == expected_retval assert ret == expected_retval
@pytest.mark.parametrize(
('filename', 'exclude', 'expected_retval'),
(
('aws_config_with_secret.ini', '.*rpg.*', 0),
('aws_config_with_multiple_sections.ini', '.*rpg.*', 1),
),
)
def test_detect_aws_credentials_with_exclude(
filename, exclude, expected_retval,
):
ret = main((
get_resource_path(filename),
'--exclude-values',
exclude,
'--credentials-file',
'testing/resources/aws_config_with_multiple_sections.ini',
))
assert ret == expected_retval
def test_allows_arbitrarily_encoded_files(tmpdir): def test_allows_arbitrarily_encoded_files(tmpdir):
src_ini = tmpdir.join('src.ini') src_ini = tmpdir.join('src.ini')
src_ini.write( src_ini.write(