From 17df36bcbc1c0a48f54452aa659d2cd197003a4c Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 23 Jun 2021 01:34:15 -0500 Subject: [PATCH 01/18] Validate new m.room.power_levels events Signed-off-by: Aaron Raimist --- synapse/events/validator.py | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/synapse/events/validator.py b/synapse/events/validator.py index fa6987d7cbac..9f0ac4566688 100644 --- a/synapse/events/validator.py +++ b/synapse/events/validator.py @@ -12,8 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json from typing import Union +import jsonschema +from jsonschema import validate + from synapse.api.constants import MAX_ALIAS_LENGTH, EventTypes, Membership from synapse.api.errors import Codes, SynapseError from synapse.api.room_versions import EventFormatVersions @@ -87,6 +91,41 @@ def validate_new(self, event: EventBase, config: HomeServerConfig): 400, "Can't create an ACL event that denies the local server" ) + if event.type == EventTypes.PowerLevels: + powerLevelsSchema = { + "type": "object", + "properties": { + "ban": {"type": "integer"}, + "events": { + "type": "object", + "patternProperties": {".*": {"type": "integer"}}, + }, + "events_default": {"type": "integer"}, + "invite": {"type": "integer"}, + "kick": {"type": "integer"}, + "notifications": { + "type": "object", + "patternProperties": {".*": {"type": "integer"}}, + }, + "redact": {"type": "integer"}, + "state_default": {"type": "integer"}, + "users": { + "type": "object", + "patternProperties": {".*": {"type": "integer"}}, + }, + "users_default": {"type": "integer"}, + }, + } + + try: + validate(instance=event.content, schema=powerLevelsSchema) + except jsonschema.ValidationError as e: + raise SynapseError( + code=400, + msg="'%s' not a int type" % (e.path[-1],), + errcode=Codes.BAD_JSON, + ) + def _validate_retention(self, event: EventBase): """Checks that an event that defines the retention policy for a room respects the format enforced by the spec. From 7abd3883de0ee53422042ecd55488c362456b012 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 23 Jun 2021 01:38:36 -0500 Subject: [PATCH 02/18] Add changelog Signed-off-by: Aaron Raimist --- changelog.d/10232.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/10232.bugfix diff --git a/changelog.d/10232.bugfix b/changelog.d/10232.bugfix new file mode 100644 index 000000000000..7be72271e018 --- /dev/null +++ b/changelog.d/10232.bugfix @@ -0,0 +1 @@ +Validate new `m.room.power_levels` events. Contributed by @aaronraimist. \ No newline at end of file From 517a4796b69cbbb3be12a3b4073cadbae3471a52 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 23 Jun 2021 01:43:36 -0500 Subject: [PATCH 03/18] lint Signed-off-by: Aaron Raimist --- synapse/events/validator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/synapse/events/validator.py b/synapse/events/validator.py index 9f0ac4566688..7b4f889049a7 100644 --- a/synapse/events/validator.py +++ b/synapse/events/validator.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json from typing import Union import jsonschema From ace5cd2a6847f691b4ad34d79027ab6e1e90ad9f Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 23 Jun 2021 04:43:27 -0500 Subject: [PATCH 04/18] Allow frozendict so tests pass Signed-off-by: Aaron Raimist --- synapse/events/validator.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/synapse/events/validator.py b/synapse/events/validator.py index 7b4f889049a7..9ed0e3d27f01 100644 --- a/synapse/events/validator.py +++ b/synapse/events/validator.py @@ -14,8 +14,8 @@ from typing import Union +import frozendict import jsonschema -from jsonschema import validate from synapse.api.constants import MAX_ALIAS_LENGTH, EventTypes, Membership from synapse.api.errors import Codes, SynapseError @@ -116,8 +116,20 @@ def validate_new(self, event: EventBase, config: HomeServerConfig): }, } + validator = jsonschema.validators.validator_for(powerLevelsSchema) + + type_checker = validator.TYPE_CHECKER.redefine("object", self._is_object) + + CustomValidator = jsonschema.validators.extend( + validator, type_checker=type_checker + ) + try: - validate(instance=event.content, schema=powerLevelsSchema) + jsonschema.validate( + instance=event.content, + schema=powerLevelsSchema, + cls=CustomValidator, + ) except jsonschema.ValidationError as e: raise SynapseError( code=400, @@ -223,3 +235,8 @@ def _ensure_strings(self, d, keys): def _ensure_state_event(self, event): if not event.is_state(): raise SynapseError(400, "'%s' must be state events" % (event.type,)) + + def _is_object(self, checker, instance): + if isinstance(instance, (dict, frozendict.frozendict)): + return True + return False From 2461642df8c1434fc94885fc645418a7b80b0006 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Fri, 25 Jun 2021 00:10:27 -0500 Subject: [PATCH 05/18] Switch to jsonschema <3.0.0 style type redefinition Signed-off-by: Aaron Raimist --- synapse/events/validator.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/synapse/events/validator.py b/synapse/events/validator.py index 9ed0e3d27f01..8f0dea9890d1 100644 --- a/synapse/events/validator.py +++ b/synapse/events/validator.py @@ -116,19 +116,11 @@ def validate_new(self, event: EventBase, config: HomeServerConfig): }, } - validator = jsonschema.validators.validator_for(powerLevelsSchema) - - type_checker = validator.TYPE_CHECKER.redefine("object", self._is_object) - - CustomValidator = jsonschema.validators.extend( - validator, type_checker=type_checker - ) - try: jsonschema.validate( instance=event.content, schema=powerLevelsSchema, - cls=CustomValidator, + types={"object": (dict, frozendict.frozendict)}, ) except jsonschema.ValidationError as e: raise SynapseError( @@ -235,8 +227,3 @@ def _ensure_strings(self, d, keys): def _ensure_state_event(self, event): if not event.is_state(): raise SynapseError(400, "'%s' must be state events" % (event.type,)) - - def _is_object(self, checker, instance): - if isinstance(instance, (dict, frozendict.frozendict)): - return True - return False From 602da3ae8ad523849e2f6afb8b2169489a2c005c Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Fri, 25 Jun 2021 00:11:11 -0500 Subject: [PATCH 06/18] Also validate min/max integer size (#8378) Signed-off-by: Aaron Raimist --- synapse/events/validator.py | 41 ++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/synapse/events/validator.py b/synapse/events/validator.py index 8f0dea9890d1..a502e9ec9add 100644 --- a/synapse/events/validator.py +++ b/synapse/events/validator.py @@ -94,26 +94,29 @@ def validate_new(self, event: EventBase, config: HomeServerConfig): powerLevelsSchema = { "type": "object", "properties": { - "ban": {"type": "integer"}, - "events": { - "type": "object", - "patternProperties": {".*": {"type": "integer"}}, - }, - "events_default": {"type": "integer"}, - "invite": {"type": "integer"}, - "kick": {"type": "integer"}, - "notifications": { - "type": "object", - "patternProperties": {".*": {"type": "integer"}}, + "ban": {"$ref": "#/definitions/int"}, + "events": {"$ref": "#/definitions/objectOfInts"}, + "events_default": {"$ref": "#/definitions/int"}, + "invite": {"$ref": "#/definitions/int"}, + "kick": {"$ref": "#/definitions/int"}, + "notifications": {"$ref": "#/definitions/objectOfInts"}, + "redact": {"$ref": "#/definitions/int"}, + "state_default": {"$ref": "#/definitions/int"}, + "users": {"$ref": "#/definitions/objectOfInts"}, + "users_default": {"$ref": "#/definitions/int"} + }, + + "definitions": { + "int": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 }, - "redact": {"type": "integer"}, - "state_default": {"type": "integer"}, - "users": { + "objectOfInts": { "type": "object", - "patternProperties": {".*": {"type": "integer"}}, - }, - "users_default": {"type": "integer"}, - }, + "additionalProperties": {"$ref": "#/definitions/int"} + } + } } try: @@ -125,7 +128,7 @@ def validate_new(self, event: EventBase, config: HomeServerConfig): except jsonschema.ValidationError as e: raise SynapseError( code=400, - msg="'%s' not a int type" % (e.path[-1],), + msg=e.message, errcode=Codes.BAD_JSON, ) From 30f24509ded15e26c1542015a9aadb92ab5d05fb Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Fri, 25 Jun 2021 00:13:30 -0500 Subject: [PATCH 07/18] lint Signed-off-by: Aaron Raimist --- synapse/events/validator.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/synapse/events/validator.py b/synapse/events/validator.py index a502e9ec9add..d212f2b512db 100644 --- a/synapse/events/validator.py +++ b/synapse/events/validator.py @@ -103,20 +103,19 @@ def validate_new(self, event: EventBase, config: HomeServerConfig): "redact": {"$ref": "#/definitions/int"}, "state_default": {"$ref": "#/definitions/int"}, "users": {"$ref": "#/definitions/objectOfInts"}, - "users_default": {"$ref": "#/definitions/int"} + "users_default": {"$ref": "#/definitions/int"}, }, - "definitions": { "int": { "type": "integer", "minimum": -9007199254740991, - "maximum": 9007199254740991 + "maximum": 9007199254740991, }, "objectOfInts": { "type": "object", - "additionalProperties": {"$ref": "#/definitions/int"} - } - } + "additionalProperties": {"$ref": "#/definitions/int"}, + }, + }, } try: From 50bc393c50e56cbcb4f36e04c2e9298ccb92bea1 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Fri, 25 Jun 2021 00:20:08 -0500 Subject: [PATCH 08/18] lint Signed-off-by: Aaron Raimist --- synapse/events/validator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synapse/events/validator.py b/synapse/events/validator.py index d212f2b512db..001441d2f3c9 100644 --- a/synapse/events/validator.py +++ b/synapse/events/validator.py @@ -127,7 +127,7 @@ def validate_new(self, event: EventBase, config: HomeServerConfig): except jsonschema.ValidationError as e: raise SynapseError( code=400, - msg=e.message, + msg=e.message, # noqa: B306, jsonschema.ValidationError.message is a valid attribute errcode=Codes.BAD_JSON, ) From cfe8b5d683a1503d37cc1c8865ce20e4a9904e73 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Sat, 26 Jun 2021 20:30:21 -0500 Subject: [PATCH 09/18] Revert "Switch to jsonschema <3.0.0 style type redefinition" This reverts commit 2461642df8c1434fc94885fc645418a7b80b0006. --- synapse/events/validator.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/synapse/events/validator.py b/synapse/events/validator.py index 001441d2f3c9..6269cde4bd00 100644 --- a/synapse/events/validator.py +++ b/synapse/events/validator.py @@ -118,11 +118,19 @@ def validate_new(self, event: EventBase, config: HomeServerConfig): }, } + validator = jsonschema.validators.validator_for(powerLevelsSchema) + + type_checker = validator.TYPE_CHECKER.redefine("object", self._is_object) + + CustomValidator = jsonschema.validators.extend( + validator, type_checker=type_checker + ) + try: jsonschema.validate( instance=event.content, schema=powerLevelsSchema, - types={"object": (dict, frozendict.frozendict)}, + cls=CustomValidator, ) except jsonschema.ValidationError as e: raise SynapseError( @@ -229,3 +237,8 @@ def _ensure_strings(self, d, keys): def _ensure_state_event(self, event): if not event.is_state(): raise SynapseError(400, "'%s' must be state events" % (event.type,)) + + def _is_object(self, checker, instance): + if isinstance(instance, (dict, frozendict.frozendict)): + return True + return False From 645a62637014f1a03ee23749457ad77dd48315de Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Sat, 26 Jun 2021 20:33:29 -0500 Subject: [PATCH 10/18] Bump minimum jsonschema version to 3.0.0 Signed-off-by: Aaron Raimist --- synapse/python_dependencies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synapse/python_dependencies.py b/synapse/python_dependencies.py index 271c17c22615..6837c8a19a1a 100644 --- a/synapse/python_dependencies.py +++ b/synapse/python_dependencies.py @@ -48,7 +48,7 @@ # [1] https://pip.pypa.io/en/stable/reference/pip_install/#requirement-specifiers. REQUIREMENTS = [ - "jsonschema>=2.5.1", + "jsonschema>=3.0.0", "frozendict>=1", "unpaddedbase64>=1.1.0", "canonicaljson>=1.4.0", From dfcfaf088649d56332e5f36a4d2ae2c3875d7049 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 25 Aug 2021 03:19:10 -0500 Subject: [PATCH 11/18] Mention why jsonschema 3.0.0 is required Signed-off-by: Aaron Raimist --- synapse/python_dependencies.py | 1 + 1 file changed, 1 insertion(+) diff --git a/synapse/python_dependencies.py b/synapse/python_dependencies.py index 83a3a8bd1f88..154e5b7028e9 100644 --- a/synapse/python_dependencies.py +++ b/synapse/python_dependencies.py @@ -48,6 +48,7 @@ # [1] https://pip.pypa.io/en/stable/reference/pip_install/#requirement-specifiers. REQUIREMENTS = [ + # we use the TYPE_CHECKER.redefine method added in jsonschema 3.0.0 "jsonschema>=3.0.0", "frozendict>=1", "unpaddedbase64>=1.1.0", From c7317e6c3ddf7c3df78fb0a09e541bf2ed6a6685 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 25 Aug 2021 03:19:59 -0500 Subject: [PATCH 12/18] Create constants for minimum and maximum safe integer values Signed-off-by: Aaron Raimist --- synapse/events/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/synapse/events/utils.py b/synapse/events/utils.py index b6da2f60af99..738a151cefd1 100644 --- a/synapse/events/utils.py +++ b/synapse/events/utils.py @@ -32,6 +32,9 @@ # the literal fields "foo\" and "bar" but will instead be treated as "foo\\.bar" SPLIT_FIELD_REGEX = re.compile(r"(? EventBase: """Returns a pruned version of the given event, which removes all keys we @@ -505,7 +508,7 @@ def validate_canonicaljson(value: Any): * NaN, Infinity, -Infinity """ if isinstance(value, int): - if value <= -(2 ** 53) or 2 ** 53 <= value: + if value < CANONICALJSON_MIN_INT or CANONICALJSON_MAX_INT < value: raise SynapseError(400, "JSON integer out of range", Codes.BAD_JSON) elif isinstance(value, float): From 90b7126647795885fb4a8a4b1e6d9d34f55c776c Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 25 Aug 2021 03:21:16 -0500 Subject: [PATCH 13/18] Create power level validator when server starts rather than every time a PL event is sent Signed-off-by: Aaron Raimist --- synapse/events/validator.py | 96 ++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/synapse/events/validator.py b/synapse/events/validator.py index 6269cde4bd00..422d995a5e31 100644 --- a/synapse/events/validator.py +++ b/synapse/events/validator.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - +import collections.abc from typing import Union import frozendict @@ -23,7 +23,11 @@ from synapse.config.homeserver import HomeServerConfig from synapse.events import EventBase from synapse.events.builder import EventBuilder -from synapse.events.utils import validate_canonicaljson +from synapse.events.utils import ( + CANONICALJSON_MAX_INT, + CANONICALJSON_MIN_INT, + validate_canonicaljson, +) from synapse.federation.federation_server import server_matches_acl_event from synapse.types import EventID, RoomID, UserID @@ -91,46 +95,11 @@ def validate_new(self, event: EventBase, config: HomeServerConfig): ) if event.type == EventTypes.PowerLevels: - powerLevelsSchema = { - "type": "object", - "properties": { - "ban": {"$ref": "#/definitions/int"}, - "events": {"$ref": "#/definitions/objectOfInts"}, - "events_default": {"$ref": "#/definitions/int"}, - "invite": {"$ref": "#/definitions/int"}, - "kick": {"$ref": "#/definitions/int"}, - "notifications": {"$ref": "#/definitions/objectOfInts"}, - "redact": {"$ref": "#/definitions/int"}, - "state_default": {"$ref": "#/definitions/int"}, - "users": {"$ref": "#/definitions/objectOfInts"}, - "users_default": {"$ref": "#/definitions/int"}, - }, - "definitions": { - "int": { - "type": "integer", - "minimum": -9007199254740991, - "maximum": 9007199254740991, - }, - "objectOfInts": { - "type": "object", - "additionalProperties": {"$ref": "#/definitions/int"}, - }, - }, - } - - validator = jsonschema.validators.validator_for(powerLevelsSchema) - - type_checker = validator.TYPE_CHECKER.redefine("object", self._is_object) - - CustomValidator = jsonschema.validators.extend( - validator, type_checker=type_checker - ) - try: jsonschema.validate( instance=event.content, - schema=powerLevelsSchema, - cls=CustomValidator, + schema=plSchema, + cls=plValidator, ) except jsonschema.ValidationError as e: raise SynapseError( @@ -238,7 +207,48 @@ def _ensure_state_event(self, event): if not event.is_state(): raise SynapseError(400, "'%s' must be state events" % (event.type,)) - def _is_object(self, checker, instance): - if isinstance(instance, (dict, frozendict.frozendict)): - return True - return False + +def _create_power_level_validator(): + powerLevelsSchema = { + "type": "object", + "properties": { + "ban": {"$ref": "#/definitions/int"}, + "events": {"$ref": "#/definitions/objectOfInts"}, + "events_default": {"$ref": "#/definitions/int"}, + "invite": {"$ref": "#/definitions/int"}, + "kick": {"$ref": "#/definitions/int"}, + "notifications": {"$ref": "#/definitions/objectOfInts"}, + "redact": {"$ref": "#/definitions/int"}, + "state_default": {"$ref": "#/definitions/int"}, + "users": {"$ref": "#/definitions/objectOfInts"}, + "users_default": {"$ref": "#/definitions/int"}, + }, + "definitions": { + "int": { + "type": "integer", + "minimum": CANONICALJSON_MIN_INT, + "maximum": CANONICALJSON_MAX_INT, + }, + "objectOfInts": { + "type": "object", + "additionalProperties": {"$ref": "#/definitions/int"}, + }, + }, + } + + validator = jsonschema.validators.validator_for(powerLevelsSchema) + + # by default jsonschema does not consider a frozendict to be an object so + # we need to use a custom type checker + # https://python-jsonschema.readthedocs.io/en/stable/validate/?highlight=object#validating-with-additional-types + type_checker = validator.TYPE_CHECKER.redefine( + "object", lambda checker, thing: isinstance(thing, collections.abc.Mapping) + ) + + return ( + powerLevelsSchema, + jsonschema.validators.extend(validator, type_checker=type_checker), + ) + + +plSchema, plValidator = _create_power_level_validator() From 41a5ae2459b44b0c66b485c606ec3c5eca260286 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 25 Aug 2021 03:21:28 -0500 Subject: [PATCH 14/18] Add tests Signed-off-by: Aaron Raimist --- tests/rest/client/test_power_levels.py | 78 ++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/tests/rest/client/test_power_levels.py b/tests/rest/client/test_power_levels.py index 91d0762cb0ab..c0de4c93a806 100644 --- a/tests/rest/client/test_power_levels.py +++ b/tests/rest/client/test_power_levels.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from synapse.api.errors import Codes +from synapse.events.utils import CANONICALJSON_MAX_INT, CANONICALJSON_MIN_INT from synapse.rest import admin from synapse.rest.client import login, room, sync @@ -203,3 +205,79 @@ def test_admins_can_tombstone_room(self): tok=self.admin_access_token, expect_code=200, # expect success ) + + def test_cannot_set_string_power_levels(self): + room_power_levels = self.helper.get_state( + self.room_id, + "m.room.power_levels", + tok=self.admin_access_token, + ) + + # Update existing power levels with user at PL "0" + room_power_levels["users"].update({self.user_user_id: "0"}) + + body = self.helper.send_state( + self.room_id, + "m.room.power_levels", + room_power_levels, + tok=self.admin_access_token, + expect_code=400, # expect failure + ) + + self.assertEqual( + body["errcode"], + Codes.BAD_JSON, + body, + ) + + def test_cannot_set_unsafe_large_power_levels(self): + room_power_levels = self.helper.get_state( + self.room_id, + "m.room.power_levels", + tok=self.admin_access_token, + ) + + # Update existing power levels with user at PL above the max safe integer + room_power_levels["users"].update( + {self.user_user_id: CANONICALJSON_MAX_INT + 1} + ) + + body = self.helper.send_state( + self.room_id, + "m.room.power_levels", + room_power_levels, + tok=self.admin_access_token, + expect_code=400, # expect failure + ) + + self.assertEqual( + body["errcode"], + Codes.BAD_JSON, + body, + ) + + def test_cannot_set_unsafe_small_power_levels(self): + room_power_levels = self.helper.get_state( + self.room_id, + "m.room.power_levels", + tok=self.admin_access_token, + ) + + # Update existing power levels with user at PL below the minimum safe integer + room_power_levels["users"].update( + {self.user_user_id: CANONICALJSON_MIN_INT - 1} + ) + + body = self.helper.send_state( + self.room_id, + "m.room.power_levels", + room_power_levels, + tok=self.admin_access_token, + expect_code=400, # expect failure + ) + + self.assertEqual( + body["errcode"], + Codes.BAD_JSON, + body, + ) From 3e6ee3a606a3e85c345c35a0d35c404a18c3e31b Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 25 Aug 2021 03:27:56 -0500 Subject: [PATCH 15/18] lint Signed-off-by: Aaron Raimist --- synapse/events/validator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/synapse/events/validator.py b/synapse/events/validator.py index 422d995a5e31..7b2d7f031f57 100644 --- a/synapse/events/validator.py +++ b/synapse/events/validator.py @@ -14,7 +14,6 @@ import collections.abc from typing import Union -import frozendict import jsonschema from synapse.api.constants import MAX_ALIAS_LENGTH, EventTypes, Membership From 0222cb8e282f97016e5c85a1927c85eabee65f80 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 25 Aug 2021 07:04:04 -0500 Subject: [PATCH 16/18] Provide a some more context in error message when possible Signed-off-by: Aaron Raimist --- synapse/events/validator.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/synapse/events/validator.py b/synapse/events/validator.py index 7b2d7f031f57..d9028293ee35 100644 --- a/synapse/events/validator.py +++ b/synapse/events/validator.py @@ -101,9 +101,16 @@ def validate_new(self, event: EventBase, config: HomeServerConfig): cls=plValidator, ) except jsonschema.ValidationError as e: + if e.path: + # example: "users_default": '0' is not of type 'integer' + message = '"' + e.path[-1] + '": ' + e.message + else: + # example: '0' is not of type 'integer' + message = e.message + raise SynapseError( code=400, - msg=e.message, # noqa: B306, jsonschema.ValidationError.message is a valid attribute + msg=message, # noqa: B306, jsonschema.ValidationError.message is a valid attribute errcode=Codes.BAD_JSON, ) From 8465207531d764994d9e72d22224a37ab90673ea Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 25 Aug 2021 07:29:17 -0500 Subject: [PATCH 17/18] Move comment to the right place Signed-off-by: Aaron Raimist --- synapse/events/validator.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/synapse/events/validator.py b/synapse/events/validator.py index d9028293ee35..6cbada62c726 100644 --- a/synapse/events/validator.py +++ b/synapse/events/validator.py @@ -103,14 +103,16 @@ def validate_new(self, event: EventBase, config: HomeServerConfig): except jsonschema.ValidationError as e: if e.path: # example: "users_default": '0' is not of type 'integer' - message = '"' + e.path[-1] + '": ' + e.message + message = '"' + e.path[-1] + '": ' + e.message # noqa: B306 + # jsonschema.ValidationError.message is a valid attribute else: # example: '0' is not of type 'integer' - message = e.message + message = e.message # noqa: B306 + # jsonschema.ValidationError.message is a valid attribute raise SynapseError( code=400, - msg=message, # noqa: B306, jsonschema.ValidationError.message is a valid attribute + msg=message, errcode=Codes.BAD_JSON, ) From ad0a4ad5b0062289476a0f4c603f1d43adbd9c66 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Wed, 25 Aug 2021 07:29:34 -0500 Subject: [PATCH 18/18] Simplify _create_power_level_validator() Signed-off-by: Aaron Raimist --- synapse/events/validator.py | 62 ++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/synapse/events/validator.py b/synapse/events/validator.py index 6cbada62c726..33954b4f6217 100644 --- a/synapse/events/validator.py +++ b/synapse/events/validator.py @@ -97,7 +97,7 @@ def validate_new(self, event: EventBase, config: HomeServerConfig): try: jsonschema.validate( instance=event.content, - schema=plSchema, + schema=POWER_LEVELS_SCHEMA, cls=plValidator, ) except jsonschema.ValidationError as e: @@ -216,35 +216,36 @@ def _ensure_state_event(self, event): raise SynapseError(400, "'%s' must be state events" % (event.type,)) -def _create_power_level_validator(): - powerLevelsSchema = { - "type": "object", - "properties": { - "ban": {"$ref": "#/definitions/int"}, - "events": {"$ref": "#/definitions/objectOfInts"}, - "events_default": {"$ref": "#/definitions/int"}, - "invite": {"$ref": "#/definitions/int"}, - "kick": {"$ref": "#/definitions/int"}, - "notifications": {"$ref": "#/definitions/objectOfInts"}, - "redact": {"$ref": "#/definitions/int"}, - "state_default": {"$ref": "#/definitions/int"}, - "users": {"$ref": "#/definitions/objectOfInts"}, - "users_default": {"$ref": "#/definitions/int"}, +POWER_LEVELS_SCHEMA = { + "type": "object", + "properties": { + "ban": {"$ref": "#/definitions/int"}, + "events": {"$ref": "#/definitions/objectOfInts"}, + "events_default": {"$ref": "#/definitions/int"}, + "invite": {"$ref": "#/definitions/int"}, + "kick": {"$ref": "#/definitions/int"}, + "notifications": {"$ref": "#/definitions/objectOfInts"}, + "redact": {"$ref": "#/definitions/int"}, + "state_default": {"$ref": "#/definitions/int"}, + "users": {"$ref": "#/definitions/objectOfInts"}, + "users_default": {"$ref": "#/definitions/int"}, + }, + "definitions": { + "int": { + "type": "integer", + "minimum": CANONICALJSON_MIN_INT, + "maximum": CANONICALJSON_MAX_INT, }, - "definitions": { - "int": { - "type": "integer", - "minimum": CANONICALJSON_MIN_INT, - "maximum": CANONICALJSON_MAX_INT, - }, - "objectOfInts": { - "type": "object", - "additionalProperties": {"$ref": "#/definitions/int"}, - }, + "objectOfInts": { + "type": "object", + "additionalProperties": {"$ref": "#/definitions/int"}, }, - } + }, +} + - validator = jsonschema.validators.validator_for(powerLevelsSchema) +def _create_power_level_validator(): + validator = jsonschema.validators.validator_for(POWER_LEVELS_SCHEMA) # by default jsonschema does not consider a frozendict to be an object so # we need to use a custom type checker @@ -253,10 +254,7 @@ def _create_power_level_validator(): "object", lambda checker, thing: isinstance(thing, collections.abc.Mapping) ) - return ( - powerLevelsSchema, - jsonschema.validators.extend(validator, type_checker=type_checker), - ) + return jsonschema.validators.extend(validator, type_checker=type_checker) -plSchema, plValidator = _create_power_level_validator() +plValidator = _create_power_level_validator()