message: Bundle message stripping, validation, and truncation.

We always want to do these at the same time.  Previously, message
editing did too much stripping (fixes #16837) and failed to check for
NUL bytes.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2020-12-18 17:36:50 -08:00
parent 37c8505435
commit a054f57af6
6 changed files with 17 additions and 22 deletions

View File

@ -100,8 +100,8 @@ from zerver.lib.message import (
MessageDict, MessageDict,
access_message, access_message,
get_last_message_id, get_last_message_id,
normalize_body,
render_markdown, render_markdown,
truncate_body,
truncate_topic, truncate_topic,
update_first_visible_message_id, update_first_visible_message_id,
wildcard_mention_allowed, wildcard_mention_allowed,
@ -2333,13 +2333,7 @@ def check_message(sender: UserProfile, client: Client, addressee: Addressee,
""" """
stream = None stream = None
message_content = message_content_raw.rstrip() message_content = normalize_body(message_content_raw)
if len(message_content) == 0:
raise JsonableError(_("Message must not be empty"))
if '\x00' in message_content:
raise JsonableError(_("Message must not contain null bytes"))
message_content = truncate_body(message_content)
if realm is None: if realm is None:
realm = sender.realm realm = sender.realm

View File

@ -21,7 +21,7 @@ from zerver.lib.email_mirror_helpers import (
) )
from zerver.lib.email_notifications import convert_html_to_markdown from zerver.lib.email_notifications import convert_html_to_markdown
from zerver.lib.exceptions import RateLimited from zerver.lib.exceptions import RateLimited
from zerver.lib.message import truncate_body, truncate_topic from zerver.lib.message import normalize_body, truncate_topic
from zerver.lib.queue import queue_json_publish from zerver.lib.queue import queue_json_publish
from zerver.lib.rate_limiter import RateLimitedObject from zerver.lib.rate_limiter import RateLimitedObject
from zerver.lib.send_email import FromAddress from zerver.lib.send_email import FromAddress
@ -161,8 +161,7 @@ def construct_zulip_body(message: EmailMessage, realm: Realm, show_sender: bool=
if not body.endswith('\n'): if not body.endswith('\n'):
body += '\n' body += '\n'
body += extract_and_upload_attachments(message, realm) body += extract_and_upload_attachments(message, realm)
body = body.strip() if not body.rstrip():
if not body:
body = '(No email body)' body = '(No email body)'
if show_sender: if show_sender:
@ -182,7 +181,7 @@ def send_zulip(sender: UserProfile, stream: Stream, topic: str, content: str) ->
sender, sender,
stream, stream,
truncate_topic(topic), truncate_topic(topic),
truncate_body(content), normalize_body(content),
email_gateway=True) email_gateway=True)
def get_message_part_by_type(message: EmailMessage, content_type: str) -> Optional[str]: def get_message_part_by_type(message: EmailMessage, content_type: str) -> Optional[str]:

View File

@ -89,7 +89,12 @@ def truncate_content(content: str, max_length: int, truncation_message: str) ->
content = content[:max_length - len(truncation_message)] + truncation_message content = content[:max_length - len(truncation_message)] + truncation_message
return content return content
def truncate_body(body: str) -> str: def normalize_body(body: str) -> str:
body = body.rstrip()
if len(body) == 0:
raise JsonableError(_("Message must not be empty"))
if '\x00' in body:
raise JsonableError(_("Message must not contain null bytes"))
return truncate_content(body, MAX_MESSAGE_LENGTH, "\n[message truncated]") return truncate_content(body, MAX_MESSAGE_LENGTH, "\n[message truncated]")
def truncate_topic(topic: str) -> str: def truncate_topic(topic: str) -> str:

View File

@ -264,7 +264,7 @@ class DraftCreationTests(ZulipTestCase):
}] }]
self.create_and_check_drafts_for_error( self.create_and_check_drafts_for_error(
draft_dicts, draft_dicts,
"Content must not contain null bytes" "Message must not contain null bytes"
) )
draft_dicts = [{ draft_dicts = [{

View File

@ -8,7 +8,7 @@ from django.utils.translation import ugettext as _
from zerver.lib.actions import recipient_for_user_profiles from zerver.lib.actions import recipient_for_user_profiles
from zerver.lib.addressee import get_user_profiles_by_ids from zerver.lib.addressee import get_user_profiles_by_ids
from zerver.lib.exceptions import JsonableError from zerver.lib.exceptions import JsonableError
from zerver.lib.message import truncate_body, truncate_topic from zerver.lib.message import normalize_body, truncate_topic
from zerver.lib.request import REQ, has_request_variables from zerver.lib.request import REQ, has_request_variables
from zerver.lib.response import json_error, json_success from zerver.lib.response import json_error, json_success
from zerver.lib.streams import access_stream_by_id from zerver.lib.streams import access_stream_by_id
@ -48,9 +48,7 @@ def further_validated_draft_dict(draft_dict: Dict[str, Any],
validated" draft dict. It will have a slightly different set of keys the values validated" draft dict. It will have a slightly different set of keys the values
for which can be used to directly create a Draft object. """ for which can be used to directly create a Draft object. """
content = truncate_body(draft_dict["content"]) content = normalize_body(draft_dict["content"])
if "\x00" in content:
raise JsonableError(_("Content must not contain null bytes"))
timestamp = draft_dict.get("timestamp", time.time()) timestamp = draft_dict.get("timestamp", time.time())
timestamp = round(timestamp, 6) timestamp = round(timestamp, 6)

View File

@ -17,7 +17,7 @@ from zerver.lib.actions import (
from zerver.lib.exceptions import JsonableError from zerver.lib.exceptions import JsonableError
from zerver.lib.html_diff import highlight_html_differences from zerver.lib.html_diff import highlight_html_differences
from zerver.lib.markdown import MentionData from zerver.lib.markdown import MentionData
from zerver.lib.message import access_message, truncate_body from zerver.lib.message import access_message, normalize_body
from zerver.lib.queue import queue_json_publish from zerver.lib.queue import queue_json_publish
from zerver.lib.response import json_error, json_success from zerver.lib.response import json_error, json_success
from zerver.lib.streams import get_stream_by_id from zerver.lib.streams import get_stream_by_id
@ -161,10 +161,9 @@ def update_message_backend(request: HttpRequest, user_profile: UserMessage,
mention_user_ids: Set[int] = set() mention_user_ids: Set[int] = set()
mention_data: Optional[MentionData] = None mention_data: Optional[MentionData] = None
if content is not None: if content is not None:
content = content.strip() if content.rstrip() == "":
if content == "":
content = "(deleted)" content = "(deleted)"
content = truncate_body(content) content = normalize_body(content)
mention_data = MentionData( mention_data = MentionData(
realm_id=user_profile.realm.id, realm_id=user_profile.realm.id,