From d4eb9ec4d7f7f123c619d6ef6eb96521671a5e84 Mon Sep 17 00:00:00 2001 From: Max R Date: Wed, 6 Sep 2023 13:24:58 -0400 Subject: [PATCH] Add detect_secret_token --- .pre-commit-hooks.yaml | 6 ++++ README.md | 3 ++ pre_commit_hooks/detect_secret_token.py | 41 +++++++++++++++++++++++++ setup.cfg | 1 + tests/detect_secret_token_test.py | 36 ++++++++++++++++++++++ 5 files changed, 87 insertions(+) create mode 100644 pre_commit_hooks/detect_secret_token.py create mode 100644 tests/detect_secret_token_test.py diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index c0d811c..59bc48d 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -119,6 +119,12 @@ entry: detect-private-key language: python types: [text] +- id: detect-secret-token + name: detect secret token + description: detects the presence of RFC 8959 secret-token. + entry: detect-secret-token + language: python + types: [text] - id: double-quote-string-fixer name: fix double quoted strings description: replaces double quoted strings with single quoted strings. diff --git a/README.md b/README.md index 1ebba89..03c0db1 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,9 @@ The following arguments are available: #### `detect-private-key` Checks for the existence of private keys. +#### `detect-secret-token` +Checks for the existence of RFC 8959 `secret-token`. + #### `double-quote-string-fixer` This hook replaces double quoted strings with single quoted strings. diff --git a/pre_commit_hooks/detect_secret_token.py b/pre_commit_hooks/detect_secret_token.py new file mode 100644 index 0000000..659ec57 --- /dev/null +++ b/pre_commit_hooks/detect_secret_token.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +import argparse +import re +import sys +from typing import Sequence + +# secret token is defined in https://datatracker.ietf.org/doc/html/rfc8959 as: +# +# secret-token-URI = secret-token-scheme ":" token +# secret-token-scheme = "secret-token" +# token = 1*pchar +# +# pchar is defined in https://www.rfc-editor.org/rfc/rfc3986#section-3.3 as: +# +# pchar = unreserved / pct-encoded / sub-delims / ":" / "@" +SECRET_TOKEN_RE = re.compile( + 'secret-token:(' + r"[A-Za-z0-9\-._~!$&'()*+,;=:@]" # unreserved / sub-delims / ":" / "@" + '|%[0-9A-Fa-f]{2}' # pct-encoded + ')+', +) + + +def main(argv: Sequence[str] | None = None) -> int: + parser = argparse.ArgumentParser() + parser.add_argument('filenames', nargs='*', help='Filenames to check') + args = parser.parse_args(argv) + + found = False + for filename in args.filenames: + with open(filename) as f: + if SECRET_TOKEN_RE.match(f.read()): + found = True + print(f'secret-token found: {filename}', file=sys.stderr) + + return int(found) + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/setup.cfg b/setup.cfg index b72aa64..c7fbbff 100644 --- a/setup.cfg +++ b/setup.cfg @@ -49,6 +49,7 @@ console_scripts = destroyed-symlinks = pre_commit_hooks.destroyed_symlinks:main detect-aws-credentials = pre_commit_hooks.detect_aws_credentials:main detect-private-key = pre_commit_hooks.detect_private_key:main + detect-secret-token = pre_commit_hooks.detect_secret_token:main double-quote-string-fixer = pre_commit_hooks.string_fixer:main end-of-file-fixer = pre_commit_hooks.end_of_file_fixer:main file-contents-sorter = pre_commit_hooks.file_contents_sorter:main diff --git a/tests/detect_secret_token_test.py b/tests/detect_secret_token_test.py new file mode 100644 index 0000000..5be1a46 --- /dev/null +++ b/tests/detect_secret_token_test.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +import pytest + +from pre_commit_hooks.detect_secret_token import main + + +@pytest.mark.parametrize( + ('input', 'expected'), + ( + pytest.param( + 'There is no secret here', + 0, + id='no secret-token', + ), + pytest.param( + 'There is no secret here ☃', + 0, + id='no secret-token unicode', + ), + pytest.param( + 'Read about using "secret-token:" in RFC 8959', + 0, + id='has secret-token prefix only', + ), + pytest.param( + 'secret-token:E92FB7EB-D882-47A4-A265-A0B6135DC842%20foo', + 1, + id='has secret-token', + ), + ), +) +def test_main(input, expected, tmpdir): + path = tmpdir.join('file.txt') + path.write(input) + assert main([str(path)]) == expected