mirror of
https://github.com/pre-commit/pre-commit-hooks.git
synced 2026-04-05 19:46:54 +00:00
Add check for permitted characters in pathnames
Add a new checker that checks for allowed characters in pathnames. Includes arguments for overriding the default allowed list and extending it. Default allow list is alphanumeric plus `.-_`.
This commit is contained in:
parent
69b4df5589
commit
9bb6ebc34d
5 changed files with 109 additions and 0 deletions
|
|
@ -45,6 +45,12 @@
|
||||||
entry: check-json
|
entry: check-json
|
||||||
language: python
|
language: python
|
||||||
types: [json]
|
types: [json]
|
||||||
|
- id: check-permitted-path-characters
|
||||||
|
name: check for permitted path characters
|
||||||
|
description: checks pathnames for allowed characters
|
||||||
|
entry: check-permitted-path-characters
|
||||||
|
language: python
|
||||||
|
args: ['--']
|
||||||
- id: check-shebang-scripts-are-executable
|
- id: check-shebang-scripts-are-executable
|
||||||
name: check that scripts with shebangs are executable
|
name: check that scripts with shebangs are executable
|
||||||
description: ensures that (non-binary) files with a shebang are executable.
|
description: ensures that (non-binary) files with a shebang are executable.
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,9 @@ Attempts to load all json files to verify syntax.
|
||||||
#### `check-merge-conflict`
|
#### `check-merge-conflict`
|
||||||
Check for files that contain merge conflict strings.
|
Check for files that contain merge conflict strings.
|
||||||
|
|
||||||
|
#### `check-permitted-path-characters`
|
||||||
|
Check for allowed characters in pathnames.
|
||||||
|
|
||||||
#### `check-shebang-scripts-are-executable`
|
#### `check-shebang-scripts-are-executable`
|
||||||
Checks that scripts with shebangs are executable.
|
Checks that scripts with shebangs are executable.
|
||||||
|
|
||||||
|
|
|
||||||
49
pre_commit_hooks/check_permitted_path_characters.py
Normal file
49
pre_commit_hooks/check_permitted_path_characters.py
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import string
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
DEFAULT_ALLOWLIST = string.ascii_letters + string.digits + '-_./'
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: Sequence[str] | None = None) -> int:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('filenames', nargs='*')
|
||||||
|
parser.add_argument(
|
||||||
|
'--allowlist',
|
||||||
|
default=DEFAULT_ALLOWLIST,
|
||||||
|
help=(
|
||||||
|
'Override the default allowlist of permitted characters. The'
|
||||||
|
' default is %(default)s'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--extra-allowlist',
|
||||||
|
default='',
|
||||||
|
help='Extend the default allowlist with these characters.',
|
||||||
|
)
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
|
allowlist = set(args.allowlist + args.extra_allowlist)
|
||||||
|
|
||||||
|
retcode = 0
|
||||||
|
for filename in args.filenames:
|
||||||
|
# check the entire path, not just the filename, to catch directories
|
||||||
|
# with invalid characters
|
||||||
|
file_chars = set(filename)
|
||||||
|
if not file_chars.issubset(allowlist):
|
||||||
|
# sorted and stringified for readability
|
||||||
|
pretty_allowlist = ''.join(sorted(allowlist))
|
||||||
|
pretty_banlist = repr(''.join(sorted(file_chars - allowlist)))
|
||||||
|
print(
|
||||||
|
f'"{filename}" contains characters not in the allowlist:'
|
||||||
|
f' "{pretty_banlist}". Allowlist is: "{pretty_allowlist}".',
|
||||||
|
)
|
||||||
|
retcode = 1
|
||||||
|
|
||||||
|
return retcode
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
raise SystemExit(main())
|
||||||
|
|
@ -43,6 +43,7 @@ console_scripts =
|
||||||
check-executables-have-shebangs = pre_commit_hooks.check_executables_have_shebangs:main
|
check-executables-have-shebangs = pre_commit_hooks.check_executables_have_shebangs:main
|
||||||
check-json = pre_commit_hooks.check_json:main
|
check-json = pre_commit_hooks.check_json:main
|
||||||
check-merge-conflict = pre_commit_hooks.check_merge_conflict:main
|
check-merge-conflict = pre_commit_hooks.check_merge_conflict:main
|
||||||
|
check-permitted-path-characters = pre_commit_hooks.check_permitted_path_characters:main
|
||||||
check-shebang-scripts-are-executable = pre_commit_hooks.check_shebang_scripts_are_executable:main
|
check-shebang-scripts-are-executable = pre_commit_hooks.check_shebang_scripts_are_executable:main
|
||||||
check-symlinks = pre_commit_hooks.check_symlinks:main
|
check-symlinks = pre_commit_hooks.check_symlinks:main
|
||||||
check-toml = pre_commit_hooks.check_toml:main
|
check-toml = pre_commit_hooks.check_toml:main
|
||||||
|
|
|
||||||
50
tests/check_permitted_path_characters_test.py
Normal file
50
tests/check_permitted_path_characters_test.py
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pre_commit_hooks.check_permitted_path_characters import main
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_all_pass():
|
||||||
|
ret = main(
|
||||||
|
[
|
||||||
|
'/some/path/foo_test.py',
|
||||||
|
'./relative/path/bar_test.py',
|
||||||
|
'filename-only.py',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert ret == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_default_chars():
|
||||||
|
# use '--' for separating pathnames from args, so pathnames with leading
|
||||||
|
# '-' are not interpreted as flags
|
||||||
|
ret = main(
|
||||||
|
[
|
||||||
|
'--',
|
||||||
|
'-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_',
|
||||||
|
'abcdefghijklmnopqrstuvwxyz',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert ret == 0
|
||||||
|
|
||||||
|
ret = main(['+'])
|
||||||
|
assert ret == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_invalid_dir():
|
||||||
|
ret = main(['--', '/some+funky%%dir/pathname'])
|
||||||
|
assert ret == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_allowlist():
|
||||||
|
ret = main(['--allowlist', 'abc', 'invalid.py'])
|
||||||
|
assert ret == 1
|
||||||
|
ret = main(['--allowlist', 'abc', 'cba'])
|
||||||
|
assert ret == 0
|
||||||
|
# a pathological case
|
||||||
|
ret = main(['--allowlist', '\b\x01\t/.', '\b.\x01/\t'])
|
||||||
|
assert ret == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_extra_allowlist():
|
||||||
|
ret = main(['--extra-allowlist', '+', 'valid+'])
|
||||||
|
assert ret == 0
|
||||||
Loading…
Add table
Add a link
Reference in a new issue