diff --git a/DEVELOPMENT.md b/DEVELOPMENT.rst similarity index 100% rename from DEVELOPMENT.md rename to DEVELOPMENT.rst diff --git a/README.rst b/README.rst index 1591c2e..5e196f1 100644 --- a/README.rst +++ b/README.rst @@ -21,7 +21,7 @@ import and configure the library with your Castle API secret. .. code:: python - from castle.configuration import configuration, WHITELISTED + from castle.configuration import configuration, DEFAULT_ALLOWLIST # Same as setting it through Castle.api_secret configuration.api_secret = ':YOUR-API-SECRET' @@ -32,16 +32,17 @@ import and configure the library with your Castle API secret. # Castle::RequestError is raised when timing out in milliseconds (default: 500 milliseconds) configuration.request_timeout = 1000 - # Whitelisted and Blacklisted headers are case insensitive and allow to use _ and - as a separator, http prefixes are removed + # Allowlisted and Denylisted headers are case insensitive + # and allow to use _ and - as a separator, http prefixes are removed # By default all headers are passed, but some are automatically scrubbed. - # If you need to apply a whitelist, we recommend using the minimum set of - # standard headers that we've exposed in the `WHITELISTED` constant. - # Whitelisted headers - configuration.whitelisted = WHITELISTED + ['X_HEADER'] + # If you need to apply an allowlist, we recommend using the minimum set of + # standard headers that we've exposed in the `DEFAULT_ALLOWLIST` constant. + # Allowlisted headers + configuration.allowlisted = DEFAULT_ALLOWLIST + ['X_HEADER'] - # Blacklisted headers take advantage over whitelisted elements. Note that + # Denylisted headers take advantage over allowlisted elements. Note that # some headers are always scrubbed, for security reasons. - configuration.blacklisted = ['HTTP-X-header'] + configuration.denylisted = ['HTTP-X-header'] # Castle needs the original IP of the client, not the IP of your proxy or load balancer. # The SDK will only trust the proxy chain as defined in the configuration. diff --git a/castle/configuration.py b/castle/configuration.py index b857c1c..6875ec4 100644 --- a/castle/configuration.py +++ b/castle/configuration.py @@ -1,7 +1,7 @@ from castle.exceptions import ConfigurationError from castle.headers_formatter import HeadersFormatter -DEFAULT_WHITELIST = [ +DEFAULT_ALLOWLIST = [ "Accept", "Accept-Charset", "Accept-Datetime", @@ -11,11 +11,15 @@ "Connection", "Content-Length", "Content-Type", - "Cookie", + "Dnt", "Host", "Origin", "Pragma", "Referer", + "Sec-Fetch-Dest", + "Sec-Fetch-Mode", + "Sec-Fetch-Site", + "Sec-Fetch-User", "TE", "Upgrade-Insecure-Requests", "User-Agent", @@ -44,8 +48,8 @@ def __init__(self): self.host = HOST self.port = PORT self.url_prefix = URL_PREFIX - self.whitelisted = [] - self.blacklisted = [] + self.allowlisted = [] + self.denylisted = [] self.request_timeout = REQUEST_TIMEOUT self.failover_strategy = FAILOVER_STRATEGY self.ip_headers = [] @@ -89,26 +93,26 @@ def url_prefix(self, value): self.__url_prefix = value @property - def whitelisted(self): - return self.__whitelisted + def allowlisted(self): + return self.__allowlisted - @whitelisted.setter - def whitelisted(self, value): + @allowlisted.setter + def allowlisted(self, value): if value: - self.__whitelisted = [HeadersFormatter.call(v) for v in value] + self.__allowlisted = [HeadersFormatter.call(v) for v in value] else: - self.__whitelisted = [] + self.__allowlisted = [] @property - def blacklisted(self): - return self.__blacklisted + def denylisted(self): + return self.__denylisted - @blacklisted.setter - def blacklisted(self, value): + @denylisted.setter + def denylisted(self, value): if value: - self.__blacklisted = [HeadersFormatter.call(v) for v in value] + self.__denylisted = [HeadersFormatter.call(v) for v in value] else: - self.__blacklisted = [] + self.__denylisted = [] @property def request_timeout(self): @@ -173,5 +177,6 @@ def trusted_proxy_depth(self, value): else: raise ConfigurationError + # pylint: disable=invalid-name configuration = Configuration() diff --git a/castle/extractors/headers.py b/castle/extractors/headers.py index d42b712..fce20fc 100644 --- a/castle/extractors/headers.py +++ b/castle/extractors/headers.py @@ -1,13 +1,13 @@ from castle.configuration import configuration -ALWAYS_BLACKLISTED = ['Cookie', 'Authorization'] -ALWAYS_WHITELISTED = ['User-Agent'] +ALWAYS_DENYLISTED = ['Cookie', 'Authorization'] +ALWAYS_ALLOWLISTED = ['User-Agent'] class ExtractorsHeaders(object): def __init__(self, headers): self.headers = headers - self.no_whitelist = len(configuration.whitelisted) == 0 + self.no_whitelist = len(configuration.allowlisted) == 0 def call(self): result = dict() @@ -18,13 +18,13 @@ def call(self): return result def _header_value(self, name, value): - if name in ALWAYS_BLACKLISTED: + if name in ALWAYS_DENYLISTED: return True - if name in ALWAYS_WHITELISTED: + if name in ALWAYS_ALLOWLISTED: return value - if name in configuration.blacklisted: + if name in configuration.denylisted: return True - if self.no_whitelist or (name in configuration.whitelisted): + if self.no_whitelist or (name in configuration.allowlisted): return value return True diff --git a/castle/extractors/ip.py b/castle/extractors/ip.py index d2af4f6..a0416c8 100644 --- a/castle/extractors/ip.py +++ b/castle/extractors/ip.py @@ -7,6 +7,7 @@ # list of header which are used with proxy depth setting DEPTH_RELATED = ['X-Forwarded-For'] + class ExtractorsIp(object): def __init__(self, headers): self.headers = headers diff --git a/castle/test/configuration_test.py b/castle/test/configuration_test.py index a2aedf1..12bfb92 100644 --- a/castle/test/configuration_test.py +++ b/castle/test/configuration_test.py @@ -10,8 +10,8 @@ def test_default_values(self): self.assertEqual(config.host, 'api.castle.io') self.assertEqual(config.port, 443) self.assertEqual(config.url_prefix, '/v1') - self.assertEqual(config.whitelisted, []) - self.assertEqual(config.blacklisted, []) + self.assertEqual(config.allowlisted, []) + self.assertEqual(config.denylisted, []) self.assertEqual(config.request_timeout, 500) self.assertEqual(config.failover_strategy, 'allow') self.assertEqual(config.ip_headers, []) @@ -37,35 +37,35 @@ def test_url_prefix_setter(self): config.url_prefix = '/v2' self.assertEqual(config.url_prefix, '/v2') - def test_whitelisted_setter_list(self): + def test_allowlisted_setter_list(self): config = Configuration() - config.whitelisted = ['test'] - self.assertEqual(config.whitelisted, ['Test']) + config.allowlisted = ['test'] + self.assertEqual(config.allowlisted, ['Test']) - def test_whitelisted_setter_none(self): + def test_allowlisted_setter_none(self): config = Configuration() - config.whitelisted = None - self.assertEqual(config.whitelisted, []) + config.allowlisted = None + self.assertEqual(config.allowlisted, []) - def test_whitelisted_setter_empty(self): + def test_allowlisted_setter_empty(self): config = Configuration() - config.whitelisted = '' - self.assertEqual(config.whitelisted, []) + config.allowlisted = '' + self.assertEqual(config.allowlisted, []) - def test_blacklisted_setter_list(self): + def test_denylisted_setter_list(self): config = Configuration() - config.blacklisted = ['test'] - self.assertEqual(config.blacklisted, ['Test']) + config.denylisted = ['test'] + self.assertEqual(config.denylisted, ['Test']) - def test_blacklisted_setter_none(self): + def test_denylisted_setter_none(self): config = Configuration() - config.blacklisted = None - self.assertEqual(config.blacklisted, []) + config.denylisted = None + self.assertEqual(config.denylisted, []) - def test_blacklisted_setter_empty(self): + def test_denylisted_setter_empty(self): config = Configuration() - config.blacklisted = '' - self.assertEqual(config.blacklisted, []) + config.denylisted = '' + self.assertEqual(config.denylisted, []) def test_request_timeout_setter(self): config = Configuration() diff --git a/castle/test/extractors/headers_test.py b/castle/test/extractors/headers_test.py index 14ab93f..64fc7af 100644 --- a/castle/test/extractors/headers_test.py +++ b/castle/test/extractors/headers_test.py @@ -1,5 +1,5 @@ from castle.test import unittest -from castle.configuration import configuration +from castle.configuration import configuration, DEFAULT_ALLOWLIST from castle.extractors.headers import ExtractorsHeaders @@ -18,8 +18,8 @@ def formatted_headers(): class ExtractorsHeadersTestCase(unittest.TestCase): def tearDown(self): - configuration.whitelisted = [] - configuration.blacklisted = [] + configuration.allowlisted = [] + configuration.denylisted = [] def test_extract_headers(self): self.assertEqual(ExtractorsHeaders(formatted_headers()).call(), @@ -32,8 +32,8 @@ def test_extract_headers(self): 'X-Forwarded-For': '1.2.3.4' }) - def test_whitelisted_headers(self): - configuration.whitelisted = ['Accept', 'OK'] + def test_allowlisted_headers(self): + configuration.allowlisted = ['Accept', 'OK'] self.assertEqual( ExtractorsHeaders(formatted_headers()).call(), {'Accept': 'application/json', @@ -45,10 +45,23 @@ def test_whitelisted_headers(self): 'X-Forwarded-For': True } ) -# - def test_restricted_blacklisted_headers(self): - configuration.blacklisted = ['User-Agent'] + def test_only_default_allowlisted_headers(self): + configuration.allowlisted = DEFAULT_ALLOWLIST + self.assertEqual( + ExtractorsHeaders(formatted_headers()).call(), + {'Accept': 'application/json', + 'Authorization': True, + 'Cookie': True, + 'Ok': True, + 'Content-Length': '0', + 'User-Agent': 'Mozilla 1234', + 'X-Forwarded-For': True + } + ) + + def test_restricted_denylisted_headers(self): + configuration.denylisted = ['User-Agent'] self.assertEqual( ExtractorsHeaders(formatted_headers()).call(), {'Accept': 'application/json', @@ -61,8 +74,8 @@ def test_restricted_blacklisted_headers(self): } ) - def test_blacklisted_headers(self): - configuration.blacklisted = ['Accept'] + def test_denylisted_headers(self): + configuration.denylisted = ['Accept'] self.assertEqual( ExtractorsHeaders(formatted_headers()).call(), {'Accept': True, @@ -74,11 +87,10 @@ def test_blacklisted_headers(self): 'X-Forwarded-For': '1.2.3.4' } ) -# - def test_blacklisted_and_whitelisted_headers(self): - configuration.blacklisted = ['Accept'] - configuration.whitelisted = ['Accept'] + def test_denylisted_and_allowlisted_headers(self): + configuration.denylisted = ['Accept'] + configuration.allowlisted = ['Accept'] self.assertEqual( ExtractorsHeaders(formatted_headers()).call()['Accept'], True )