From a853546f0f1b06b78a33ff196e80544014c998ab Mon Sep 17 00:00:00 2001 From: Chris Caron Date: Sat, 26 Sep 2020 20:14:55 -0400 Subject: [PATCH] Strict enforcing of +, -, and : prefixed kwargs in URLs (#302) --- apprise/config/ConfigBase.py | 15 +++++++++++++++ apprise/plugins/NotifyEnigma2.py | 8 ++++++-- apprise/plugins/NotifyIFTTT.py | 4 ++++ apprise/plugins/NotifyJSON.py | 8 ++++++-- apprise/plugins/NotifyNextcloud.py | 8 ++++++-- apprise/plugins/NotifyNotica.py | 8 ++++++-- apprise/plugins/NotifyXML.py | 8 ++++++-- test/test_apprise_config.py | 21 +++++++++++++++++---- test/test_config_base.py | 15 ++++++++++++--- test/test_rest_plugins.py | 4 ++++ 10 files changed, 82 insertions(+), 17 deletions(-) diff --git a/apprise/config/ConfigBase.py b/apprise/config/ConfigBase.py index 0cd0b0d6..553cf47d 100644 --- a/apprise/config/ConfigBase.py +++ b/apprise/config/ConfigBase.py @@ -41,6 +41,10 @@ from ..utils import parse_bool from ..utils import parse_urls from . import SCHEMA_MAP +# Test whether token is valid or not +VALID_TOKEN = re.compile( + r'(?P[a-z0-9][a-z0-9_]+)', re.I) + class ConfigBase(URLBase): """ @@ -878,6 +882,17 @@ class ConfigBase(URLBase): # Just use the global settings _results['tag'] = global_tags + for key in list(_results.keys()): + # Strip out any tokens we know that we can't accept and + # warn the user + match = VALID_TOKEN.match(key) + if not match: + ConfigBase.logger.warning( + 'Ignoring invalid token ({}) found in YAML ' + 'configuration entry #{}, item #{}' + .format(key, no + 1, entry)) + del _results[key] + ConfigBase.logger.trace( 'URL #{}: {} unpacked as:{}{}' .format(no + 1, url, os.linesep, os.linesep.join( diff --git a/apprise/plugins/NotifyEnigma2.py b/apprise/plugins/NotifyEnigma2.py index 1a8e97fc..7161fff2 100644 --- a/apprise/plugins/NotifyEnigma2.py +++ b/apprise/plugins/NotifyEnigma2.py @@ -338,8 +338,12 @@ class NotifyEnigma2(NotifyBase): # Add our headers that the user can potentially over-ride if they wish # to to our returned result set - results['headers'] = results['qsd-'] - results['headers'].update(results['qsd+']) + results['headers'] = results['qsd+'] + if results['qsd-']: + results['headers'].update(results['qsd-']) + NotifyBase.logger.deprecate( + "minus (-) based Enigma header tokens are being " + " removed; use the plus (+) symbol instead.") # Tidy our header entries by unquoting them results['headers'] = { diff --git a/apprise/plugins/NotifyIFTTT.py b/apprise/plugins/NotifyIFTTT.py index e6b40acd..c038a9e7 100644 --- a/apprise/plugins/NotifyIFTTT.py +++ b/apprise/plugins/NotifyIFTTT.py @@ -325,6 +325,10 @@ class NotifyIFTTT(NotifyBase): # Unquote our API Key results['webhook_id'] = NotifyIFTTT.unquote(results['webhook_id']) + # Parse our add_token and del_token arguments (if specified) + results['add_token'] = results['qsd+'] + results['del_token'] = results['qsd-'] + # Our Event results['events'] = list() if results['user']: diff --git a/apprise/plugins/NotifyJSON.py b/apprise/plugins/NotifyJSON.py index d8a55ac8..c42a6970 100644 --- a/apprise/plugins/NotifyJSON.py +++ b/apprise/plugins/NotifyJSON.py @@ -259,8 +259,12 @@ class NotifyJSON(NotifyBase): # Add our headers that the user can potentially over-ride if they wish # to to our returned result set - results['headers'] = results['qsd-'] - results['headers'].update(results['qsd+']) + results['headers'] = results['qsd+'] + if results['qsd-']: + results['headers'].update(results['qsd-']) + NotifyBase.logger.deprecate( + "minus (-) based JSON header tokens are being " + " removed; use the plus (+) symbol instead.") # Tidy our header entries by unquoting them results['headers'] = {NotifyJSON.unquote(x): NotifyJSON.unquote(y) diff --git a/apprise/plugins/NotifyNextcloud.py b/apprise/plugins/NotifyNextcloud.py index 240ed0aa..455f2343 100644 --- a/apprise/plugins/NotifyNextcloud.py +++ b/apprise/plugins/NotifyNextcloud.py @@ -287,7 +287,11 @@ class NotifyNextcloud(NotifyBase): # Add our headers that the user can potentially over-ride if they # wish to to our returned result set - results['headers'] = results['qsd-'] - results['headers'].update(results['qsd+']) + results['headers'] = results['qsd+'] + if results['qsd-']: + results['headers'].update(results['qsd-']) + NotifyBase.logger.deprecate( + "minus (-) based Nextcloud header tokens are being " + " removed; use the plus (+) symbol instead.") return results diff --git a/apprise/plugins/NotifyNotica.py b/apprise/plugins/NotifyNotica.py index 3dcc0172..2a031e38 100644 --- a/apprise/plugins/NotifyNotica.py +++ b/apprise/plugins/NotifyNotica.py @@ -365,8 +365,12 @@ class NotifyNotica(NotifyBase): # Add our headers that the user can potentially over-ride if they # wish to to our returned result set - results['headers'] = results['qsd-'] - results['headers'].update(results['qsd+']) + results['headers'] = results['qsd+'] + if results['qsd-']: + results['headers'].update(results['qsd-']) + NotifyBase.logger.deprecate( + "minus (-) based Notica header tokens are being " + " removed; use the plus (+) symbol instead.") return results diff --git a/apprise/plugins/NotifyXML.py b/apprise/plugins/NotifyXML.py index 21ddf0b6..aabfbbed 100644 --- a/apprise/plugins/NotifyXML.py +++ b/apprise/plugins/NotifyXML.py @@ -278,8 +278,12 @@ class NotifyXML(NotifyBase): # Add our headers that the user can potentially over-ride if they wish # to to our returned result set - results['headers'] = results['qsd-'] - results['headers'].update(results['qsd+']) + results['headers'] = results['qsd+'] + if results['qsd-']: + results['headers'].update(results['qsd-']) + NotifyBase.logger.deprecate( + "minus (-) based XML header tokens are being " + "removed; use the plus (+) symbol instead.") # Tidy our header entries by unquoting them results['headers'] = {NotifyXML.unquote(x): NotifyXML.unquote(y) diff --git a/test/test_apprise_config.py b/test/test_apprise_config.py index 33a1360b..02094aa3 100644 --- a/test/test_apprise_config.py +++ b/test/test_apprise_config.py @@ -83,6 +83,9 @@ def test_apprise_config(tmpdir): # Just 1 token provided causes exception sns://T1JJ3T3L2/ + + # XML + xml://localhost/?+HeaderEntry=Test&:IgnoredEntry=Ignored """) # Create ourselves a config object @@ -95,8 +98,8 @@ def test_apprise_config(tmpdir): # when there is at least one entry assert ac - # We should be able to read our 3 servers from that - assert len(ac.servers()) == 3 + # We should be able to read our 4 servers from that + assert len(ac.servers()) == 4 # Get our URL back assert isinstance(ac[0].url(), six.string_types) @@ -345,6 +348,16 @@ def test_apprise_add_config(): - mailto://usera:pass@gmail.com - gnome://: tag: taga,tagb + + - json://localhost: + +HeaderEntry1: 'a header entry' + -HeaderEntryDepricated: 'a deprecated entry' + :HeaderEntryIgnored: 'an ignored header entry' + + - xml://localhost: + +HeaderEntry1: 'a header entry' + -HeaderEntryDepricated: 'a deprecated entry' + :HeaderEntryIgnored: 'an ignored header entry' """ # Create ourselves a config object @@ -359,8 +372,8 @@ def test_apprise_add_config(): # when there is at least one entry assert ac - # We should be able to read our 2 servers from that - assert len(ac.servers()) == 2 + # We should be able to read our 4 servers from that + assert len(ac.servers()) == 4 # Now an invalid configuration file content = "invalid" diff --git a/test/test_config_base.py b/test/test_config_base.py index 1c06f7b8..5dc2e3d8 100644 --- a/test/test_config_base.py +++ b/test/test_config_base.py @@ -234,6 +234,10 @@ def test_config_base_config_parse_text(): # Valid Configuration result, config = ConfigBase.config_parse_text(""" + # A completely invalid token on json string (it gets ignored) + # but the URL is still valid + json://localhost?invalid-token=nodashes + # A comment line over top of a URL mailto://userb:pass@gmail.com @@ -256,7 +260,7 @@ def test_config_base_config_parse_text(): # We expect to parse 3 entries from the above assert isinstance(result, list) assert isinstance(config, list) - assert len(result) == 3 + assert len(result) == 4 assert len(result[0].tags) == 0 # Our last element will have 2 tags associated with it @@ -545,12 +549,17 @@ urls: - mailto://test:password@gmail.com - https://apprise.ryver.com/application/webhook/ckhrjW8w672m6HG - https://not.a.native.url/ + + # A completely invalid token on json string (it gets ignored) + # but the URL is still valid + - json://localhost?invalid-token=nodashes + """, asset=asset) - # We expect to parse 3 entries from the above + # We expect to parse 4 entries from the above # The Ryver one is in a native form and the 4th one is invalid assert isinstance(result, list) - assert len(result) == 3 + assert len(result) == 4 assert len(result[0].tags) == 0 # There were 3 include entries diff --git a/test/test_rest_plugins.py b/test/test_rest_plugins.py index af835905..0fce6dbb 100644 --- a/test/test_rest_plugins.py +++ b/test/test_rest_plugins.py @@ -1924,6 +1924,10 @@ TEST_URLS = ( ('notica://localhost:8080//%s/?+HeaderKey=HeaderValue' % ('7' * 6), { 'instance': plugins.NotifyNotica, }), + # Test Depricated Header overrides + ('notica://localhost:8080//%s/?-HeaderKey=HeaderValue' % ('7' * 6), { + 'instance': plugins.NotifyNotica, + }), ('notica://%s' % ('c' * 6), { 'instance': plugins.NotifyNotica, # force a failure