Validate L7Rule value and cookie name

Adds validations in L7 rule and session cookie APIs in order to prevent
authenticated and authorized users to inject code into HAProxy
configuration. CR and LF (\r and \n) are no longer allowed in L7
rule keys and values. The session persistence cookie names must follow
the rules described in
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie.

Story: 2008994
Task: 44859
Change-Id: Ic370e9edc3fb5548e9cf0d66b85df66e01c41e79
(cherry picked from commit 3cf866dbc0)
(cherry picked from commit 133ec4763d)
This commit is contained in:
Tom Weininger 2022-03-25 14:49:38 +01:00
parent d668e1f205
commit c30aa9df22
5 changed files with 63 additions and 9 deletions

View File

@ -69,8 +69,11 @@ class L7RulePOST(BaseL7Type):
compare_type = wtypes.wsattr(
wtypes.Enum(str, *constants.SUPPORTED_L7RULE_COMPARE_TYPES),
mandatory=True)
key = wtypes.wsattr(wtypes.StringType(max_length=255))
value = wtypes.wsattr(wtypes.StringType(max_length=255), mandatory=True)
key = wtypes.wsattr(wtypes.StringType(max_length=255,
pattern=r'^[^\r\n]*$'))
value = wtypes.wsattr(wtypes.StringType(max_length=255,
pattern=r'^[^\r\n]*$'),
mandatory=True)
invert = wtypes.wsattr(bool, default=False)
admin_state_up = wtypes.wsattr(bool, default=True)
# TODO(johnsom) Remove after deprecation (R series)
@ -90,8 +93,10 @@ class L7RulePUT(BaseL7Type):
compare_type = wtypes.wsattr(
wtypes.Enum(str,
*constants.SUPPORTED_L7RULE_COMPARE_TYPES))
key = wtypes.wsattr(wtypes.StringType(max_length=255))
value = wtypes.wsattr(wtypes.StringType(max_length=255))
key = wtypes.wsattr(wtypes.StringType(max_length=255,
pattern=r'^[^\r\n]*$'))
value = wtypes.wsattr(wtypes.StringType(max_length=255,
pattern=r'^[^\r\n]*$'))
invert = wtypes.wsattr(bool)
admin_state_up = wtypes.wsattr(bool)
tags = wtypes.wsattr(wtypes.ArrayType(wtypes.StringType(max_length=255)))

View File

@ -33,8 +33,11 @@ class SessionPersistencePOST(types.BaseType):
"""Defines mandatory and optional attributes of a POST request."""
type = wtypes.wsattr(wtypes.Enum(str, *constants.SUPPORTED_SP_TYPES),
mandatory=True)
cookie_name = wtypes.wsattr(wtypes.StringType(max_length=255),
default=None)
# pattern of invalid characters is based on
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
cookie_name = wtypes.wsattr(wtypes.StringType(
max_length=255, pattern=r'^[^\s,;\\]+$'),
default=None)
persistence_timeout = wtypes.wsattr(wtypes.IntegerType(), default=None)
persistence_granularity = wtypes.wsattr(types.IPAddressType(),
default=None)
@ -43,8 +46,11 @@ class SessionPersistencePOST(types.BaseType):
class SessionPersistencePUT(types.BaseType):
"""Defines attributes that are acceptable of a PUT request."""
type = wtypes.wsattr(wtypes.Enum(str, *constants.SUPPORTED_SP_TYPES))
cookie_name = wtypes.wsattr(wtypes.StringType(max_length=255),
default=None)
# pattern of invalid characters is based on
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
cookie_name = wtypes.wsattr(wtypes.StringType(
max_length=255, pattern=r'^[^\s,;\\]+$'),
default=None)
persistence_timeout = wtypes.wsattr(wtypes.IntegerType(), default=None)
persistence_granularity = wtypes.wsattr(types.IPAddressType(),
default=None)

View File

@ -67,12 +67,26 @@ class TestL7RulePOST(base.BaseTypesTest):
body)
def test_invalid_value(self):
body = {"type": "notvalid",
body = {"type": constants.L7RULE_TYPE_PATH,
"compare_type": constants.L7RULE_COMPARE_TYPE_STARTS_WITH,
"value": 123}
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)
def test_invalid_value_whitespace(self):
body = {"type": constants.L7RULE_TYPE_PATH,
"compare_type": constants.L7RULE_COMPARE_TYPE_STARTS_WITH,
"value": "12\n3"}
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)
def test_invalid_key_whitespace(self):
body = {"type": constants.L7RULE_TYPE_PATH,
"compare_type": constants.L7RULE_COMPARE_TYPE_STARTS_WITH,
"key": "12\n3"}
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)
def test_invalid_invert(self):
body = {"type": constants.L7RULE_TYPE_PATH,
"compare_type": constants.L7RULE_COMPARE_TYPE_STARTS_WITH,
@ -139,6 +153,16 @@ class TestL7RulePUT(base.BaseTypesTest):
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)
def test_invalid_value_linefeed(self):
body = {"value": "12\n3"}
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)
def test_invalid_key_linefeed(self):
body = {"key": "12\n3"}
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)
def test_invalid_invert(self):
body = {"invert": "notvalid"}
self.assertRaises(ValueError, wsme_json.fromjson, self._type,

View File

@ -210,7 +210,17 @@ class TestSessionPersistencePOST(base.BaseTypesTest, TestSessionPersistence):
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)
def test_invalid_app_cookie_name(self):
body = {"cookie_name": "cookie,monster"}
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)
class TestSessionPersistencePUT(base.BaseTypesTest, TestSessionPersistence):
_type = pool_type.SessionPersistencePUT
def test_invalid_app_cookie_name(self):
body = {"cookie_name": "cookie\nmonster"}
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)

View File

@ -0,0 +1,9 @@
---
fixes:
- |
Fixed validations in L7 rule and session cookie APIs in order to prevent
authenticated and authorized users to inject code into HAProxy
configuration. CR and LF (\r and \n) are no longer allowed in L7 rule
keys and values. The session persistence cookie names must follow the rules
described in
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie.