Browse Source

Hardening of custom plugin loading by ignore non-Python files (#853)

pull/863/head
Chris Caron 2 years ago committed by GitHub
parent
commit
6144a79513
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      apprise/utils.py
  2. 26
      test/test_apprise_utils.py

16
apprise/utils.py

@ -63,7 +63,13 @@ def import_module(path, name):
except Exception as e: except Exception as e:
# module isn't loadable # module isn't loadable
del sys.modules[name] try:
del sys.modules[name]
except KeyError:
# nothing to clean up
pass
module = None module = None
logger.debug( logger.debug(
@ -200,6 +206,9 @@ UUID4_RE = re.compile(
r'[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}', r'[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}',
re.IGNORECASE) re.IGNORECASE)
# Validate if we're a loadable Python file or not
VALID_PYTHON_FILE_RE = re.compile(r'.+\.py(o|c)?$', re.IGNORECASE)
# validate_regex() utilizes this mapping to track and re-use pre-complied # validate_regex() utilizes this mapping to track and re-use pre-complied
# regular expressions # regular expressions
REGEX_VALIDATE_LOOKUP = {} REGEX_VALIDATE_LOOKUP = {}
@ -1565,6 +1574,11 @@ def module_detection(paths, cache=True):
# Since our plugin name can conflict (as a module) with another # Since our plugin name can conflict (as a module) with another
# we want to generate random strings to avoid steping on # we want to generate random strings to avoid steping on
# another's namespace # another's namespace
if not (path and VALID_PYTHON_FILE_RE.match(path)):
# Ignore file/module type
logger.trace('Plugin Scan: Skipping %s', path)
return None
module_name = hashlib.sha1(path.encode('utf-8')).hexdigest() module_name = hashlib.sha1(path.encode('utf-8')).hexdigest()
module_pyname = "{prefix}.{name}".format( module_pyname = "{prefix}.{name}".format(
prefix='apprise.custom.module', name=module_name) prefix='apprise.custom.module', name=module_name)

26
test/test_apprise_utils.py

@ -2015,6 +2015,21 @@ def test_parse_list():
]) ])
def test_import_module(tmpdir):
"""utils: imort_module testing
"""
# Prepare ourselves a file to work with
bad_file_base = tmpdir.mkdir('a')
bad_file = bad_file_base.join('README.md')
bad_file.write(cleandoc("""
I'm a README file, not a Python one.
I can't be loaded
"""))
assert utils.import_module(str(bad_file), 'invalidfile1') is None
assert utils.import_module(str(bad_file_base), 'invalidfile2') is None
def test_module_detection(tmpdir): def test_module_detection(tmpdir):
"""utils: test_module_detection() testing """utils: test_module_detection() testing
""" """
@ -2041,13 +2056,20 @@ def test_module_detection(tmpdir):
pass pass
""")) """))
notify_ignore = notify_hook_a_base.join('README.md')
notify_ignore.write(cleandoc("""
We're not a .py file, so this file gets gracefully skipped
"""))
# Not previously loaded # Not previously loaded
assert 'clihook' not in common.NOTIFY_SCHEMA_MAP assert 'clihook' not in common.NOTIFY_SCHEMA_MAP
# load entry by string # load entry by string
utils.module_detection(str(notify_hook_a)) utils.module_detection(str(notify_hook_a))
utils.module_detection(str(notify_ignore))
utils.module_detection(str(notify_hook_a_base))
assert len(utils.PATHS_PREVIOUSLY_SCANNED) == 1 assert len(utils.PATHS_PREVIOUSLY_SCANNED) == 3
assert len(common.NOTIFY_CUSTOM_MODULE_MAP) == 1 assert len(common.NOTIFY_CUSTOM_MODULE_MAP) == 1
# Now loaded # Now loaded
@ -2057,7 +2079,7 @@ def test_module_detection(tmpdir):
utils.module_detection([str(notify_hook_a)]) utils.module_detection([str(notify_hook_a)])
# No changes to our path # No changes to our path
assert len(utils.PATHS_PREVIOUSLY_SCANNED) == 1 assert len(utils.PATHS_PREVIOUSLY_SCANNED) == 3
assert len(common.NOTIFY_CUSTOM_MODULE_MAP) == 1 assert len(common.NOTIFY_CUSTOM_MODULE_MAP) == 1
# Reset our variables for the next test # Reset our variables for the next test

Loading…
Cancel
Save