mirror of
https://github.com/zulip/zulip.git
synced 2025-11-18 12:54:58 +00:00
widgets: Validate todo data on the backend.
This commit is contained in:
@@ -499,6 +499,37 @@ def validate_poll_data(poll_data: object, is_widget_author: bool) -> None:
|
|||||||
raise ValidationError(f"Unknown type for poll data: {poll_data['type']}")
|
raise ValidationError(f"Unknown type for poll data: {poll_data['type']}")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_todo_data(todo_data: object) -> None:
|
||||||
|
check_dict([("type", check_string)])("todo data", todo_data)
|
||||||
|
|
||||||
|
assert isinstance(todo_data, dict)
|
||||||
|
|
||||||
|
if todo_data["type"] == "new_task":
|
||||||
|
checker = check_dict_only(
|
||||||
|
[
|
||||||
|
("type", check_string),
|
||||||
|
("key", check_int),
|
||||||
|
("task", check_string),
|
||||||
|
("desc", check_string),
|
||||||
|
("completed", check_bool),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
checker("todo data", todo_data)
|
||||||
|
return
|
||||||
|
|
||||||
|
if todo_data["type"] == "strike":
|
||||||
|
checker = check_dict_only(
|
||||||
|
[
|
||||||
|
("type", check_string),
|
||||||
|
("key", check_string),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
checker("todo data", todo_data)
|
||||||
|
return
|
||||||
|
|
||||||
|
raise ValidationError(f"Unknown type for todo data: {todo_data['type']}")
|
||||||
|
|
||||||
|
|
||||||
# Converter functions for use with has_request_variables
|
# Converter functions for use with has_request_variables
|
||||||
def to_non_negative_int(s: str, max_int_size: int = 2 ** 32 - 1) -> int:
|
def to_non_negative_int(s: str, max_int_size: int = 2 ** 32 - 1) -> int:
|
||||||
x = int(s)
|
x = int(s)
|
||||||
|
|||||||
@@ -312,6 +312,54 @@ class WidgetContentTestCase(ZulipTestCase):
|
|||||||
assert_success(dict(type="new_option", idx=7, option="maybe"))
|
assert_success(dict(type="new_option", idx=7, option="maybe"))
|
||||||
assert_success(dict(type="question", question="what's for dinner?"))
|
assert_success(dict(type="question", question="what's for dinner?"))
|
||||||
|
|
||||||
|
def test_todo_type_validation(self) -> None:
|
||||||
|
sender = self.example_user("cordelia")
|
||||||
|
stream_name = "Verona"
|
||||||
|
content = "/todo"
|
||||||
|
|
||||||
|
payload = dict(
|
||||||
|
type="stream",
|
||||||
|
to=stream_name,
|
||||||
|
client="test suite",
|
||||||
|
topic="whatever",
|
||||||
|
content=content,
|
||||||
|
)
|
||||||
|
result = self.api_post(sender, "/api/v1/messages", payload)
|
||||||
|
self.assert_json_success(result)
|
||||||
|
|
||||||
|
message = self.get_last_message()
|
||||||
|
|
||||||
|
def post_submessage(content: str) -> HttpResponse:
|
||||||
|
payload = dict(
|
||||||
|
message_id=message.id,
|
||||||
|
msg_type="widget",
|
||||||
|
content=content,
|
||||||
|
)
|
||||||
|
return self.api_post(sender, "/api/v1/submessage", payload)
|
||||||
|
|
||||||
|
def assert_error(content: str, error: str) -> None:
|
||||||
|
result = post_submessage(content)
|
||||||
|
self.assert_json_error_contains(result, error)
|
||||||
|
|
||||||
|
assert_error('{"type": "bogus"}', "Unknown type for todo data: bogus")
|
||||||
|
|
||||||
|
assert_error('{"type": "new_task"}', "key is missing")
|
||||||
|
assert_error(
|
||||||
|
'{"type": "new_task", "key": 7, "task": 7, "desc": "", "completed": false}',
|
||||||
|
'data["task"] is not a string',
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_error('{"type": "strike"}', "key is missing")
|
||||||
|
assert_error('{"type": "strike", "key": 999}', 'data["key"] is not a string')
|
||||||
|
|
||||||
|
def assert_success(data: Dict[str, object]) -> None:
|
||||||
|
content = orjson.dumps(data).decode()
|
||||||
|
result = post_submessage(content)
|
||||||
|
self.assert_json_success(result)
|
||||||
|
|
||||||
|
assert_success(dict(type="new_task", key=7, task="eat", desc="", completed=False))
|
||||||
|
assert_success(dict(type="strike", key="5,9"))
|
||||||
|
|
||||||
def test_get_widget_type(self) -> None:
|
def test_get_widget_type(self) -> None:
|
||||||
sender = self.example_user("cordelia")
|
sender = self.example_user("cordelia")
|
||||||
stream_name = "Verona"
|
stream_name = "Verona"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from zerver.lib.actions import do_add_submessage, verify_submessage_sender
|
|||||||
from zerver.lib.exceptions import JsonableError
|
from zerver.lib.exceptions import JsonableError
|
||||||
from zerver.lib.message import access_message
|
from zerver.lib.message import access_message
|
||||||
from zerver.lib.response import json_error, json_success
|
from zerver.lib.response import json_error, json_success
|
||||||
from zerver.lib.validator import check_int, validate_poll_data
|
from zerver.lib.validator import check_int, validate_poll_data, validate_todo_data
|
||||||
from zerver.lib.widget import get_widget_type
|
from zerver.lib.widget import get_widget_type
|
||||||
from zerver.models import UserProfile
|
from zerver.models import UserProfile
|
||||||
|
|
||||||
@@ -47,6 +47,12 @@ def process_submessage(
|
|||||||
except ValidationError as error:
|
except ValidationError as error:
|
||||||
raise JsonableError(error.message)
|
raise JsonableError(error.message)
|
||||||
|
|
||||||
|
if widget_type == "todo":
|
||||||
|
try:
|
||||||
|
validate_todo_data(todo_data=widget_data)
|
||||||
|
except ValidationError as error:
|
||||||
|
raise JsonableError(error.message)
|
||||||
|
|
||||||
do_add_submessage(
|
do_add_submessage(
|
||||||
realm=user_profile.realm,
|
realm=user_profile.realm,
|
||||||
sender_id=user_profile.id,
|
sender_id=user_profile.id,
|
||||||
|
|||||||
Reference in New Issue
Block a user