freshdesk: Remove unsafe TicketDict class.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2022-05-10 23:15:12 -07:00 committed by Alex Vandiver
parent cce142c61a
commit 6d1b68c61b
1 changed files with 26 additions and 39 deletions

View File

@ -1,5 +1,5 @@
"""Webhooks for external integrations.""" """Webhooks for external integrations."""
from typing import Any, List from typing import List
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
@ -7,7 +7,7 @@ from zerver.decorator import authenticated_rest_api_view
from zerver.lib.email_notifications import convert_html_to_markdown from zerver.lib.email_notifications import convert_html_to_markdown
from zerver.lib.request import REQ, has_request_variables from zerver.lib.request import REQ, has_request_variables
from zerver.lib.response import json_success from zerver.lib.response import json_success
from zerver.lib.validator import WildValue, WildValueDict, check_string, to_wild_value from zerver.lib.validator import WildValue, check_string, to_wild_value
from zerver.lib.webhooks.common import check_send_webhook_message from zerver.lib.webhooks.common import check_send_webhook_message
from zerver.models import UserProfile from zerver.models import UserProfile
@ -30,19 +30,6 @@ TICKET_CREATION_TEMPLATE = """
""".strip() """.strip()
class TicketDict(WildValueDict):
"""
A helper class to turn a dictionary with ticket information into
an object where each of the keys is an attribute for easy access.
"""
def __getattr__(self, field: str) -> Any:
if "_" in field:
return self.get(field)
else:
return self.get("ticket_" + field)
def property_name(property: str, index: int) -> str: def property_name(property: str, index: int) -> str:
"""The Freshdesk API is currently pretty broken: statuses are customizable """The Freshdesk API is currently pretty broken: statuses are customizable
but the API will only tell you the number associated with the status, not but the API will only tell you the number associated with the status, not
@ -94,30 +81,30 @@ def parse_freshdesk_event(event_string: str) -> List[str]:
] ]
def format_freshdesk_note_message(ticket: TicketDict, event_info: List[str]) -> str: def format_freshdesk_note_message(ticket: WildValue, event_info: List[str]) -> str:
"""There are public (visible to customers) and private note types.""" """There are public (visible to customers) and private note types."""
note_type = event_info[1] note_type = event_info[1]
content = NOTE_TEMPLATE.format( content = NOTE_TEMPLATE.format(
name=ticket.requester_name.tame(check_string), name=ticket["requester_name"].tame(check_string),
email=ticket.requester_email.tame(check_string), email=ticket["requester_email"].tame(check_string),
note_type=note_type, note_type=note_type,
ticket_id=ticket.id.tame(check_string), ticket_id=ticket["ticket_id"].tame(check_string),
ticket_url=ticket.url.tame(check_string), ticket_url=ticket["ticket_url"].tame(check_string),
) )
return content return content
def format_freshdesk_property_change_message(ticket: TicketDict, event_info: List[str]) -> str: def format_freshdesk_property_change_message(ticket: WildValue, event_info: List[str]) -> str:
"""Freshdesk will only tell us the first event to match our webhook """Freshdesk will only tell us the first event to match our webhook
configuration, so if we change multiple properties, we only get the before configuration, so if we change multiple properties, we only get the before
and after data for the first one. and after data for the first one.
""" """
content = PROPERTY_CHANGE_TEMPLATE.format( content = PROPERTY_CHANGE_TEMPLATE.format(
name=ticket.requester_name.tame(check_string), name=ticket["requester_name"].tame(check_string),
email=ticket.requester_email.tame(check_string), email=ticket["requester_email"].tame(check_string),
ticket_id=ticket.id.tame(check_string), ticket_id=ticket["ticket_id"].tame(check_string),
ticket_url=ticket.url.tame(check_string), ticket_url=ticket["ticket_url"].tame(check_string),
property_name=event_info[0].capitalize(), property_name=event_info[0].capitalize(),
old=event_info[1], old=event_info[1],
new=event_info[2], new=event_info[2],
@ -126,18 +113,18 @@ def format_freshdesk_property_change_message(ticket: TicketDict, event_info: Lis
return content return content
def format_freshdesk_ticket_creation_message(ticket: TicketDict) -> str: def format_freshdesk_ticket_creation_message(ticket: WildValue) -> str:
"""They send us the description as HTML.""" """They send us the description as HTML."""
cleaned_description = convert_html_to_markdown(ticket.description.tame(check_string)) cleaned_description = convert_html_to_markdown(ticket["ticket_description"].tame(check_string))
content = TICKET_CREATION_TEMPLATE.format( content = TICKET_CREATION_TEMPLATE.format(
name=ticket.requester_name.tame(check_string), name=ticket["requester_name"].tame(check_string),
email=ticket.requester_email.tame(check_string), email=ticket["requester_email"].tame(check_string),
ticket_id=ticket.id.tame(check_string), ticket_id=ticket["ticket_id"].tame(check_string),
ticket_url=ticket.url.tame(check_string), ticket_url=ticket["ticket_url"].tame(check_string),
description=cleaned_description, description=cleaned_description,
type=ticket.type.tame(check_string), type=ticket["ticket_type"].tame(check_string),
priority=ticket.priority.tame(check_string), priority=ticket["ticket_priority"].tame(check_string),
status=ticket.status.tame(check_string), status=ticket["ticket_status"].tame(check_string),
) )
return content return content
@ -150,12 +137,12 @@ def api_freshdesk_webhook(
user_profile: UserProfile, user_profile: UserProfile,
payload: WildValue = REQ(argument_type="body", converter=to_wild_value), payload: WildValue = REQ(argument_type="body", converter=to_wild_value),
) -> HttpResponse: ) -> HttpResponse:
ticket_data = payload["freshdesk_webhook"] ticket = payload["freshdesk_webhook"]
ticket = TicketDict("freshdesk_webhook", ticket_data) subject = (
f"#{ticket['ticket_id'].tame(check_string)}: {ticket['ticket_subject'].tame(check_string)}"
subject = f"#{ticket.id.tame(check_string)}: {ticket.subject.tame(check_string)}" )
event_info = parse_freshdesk_event(ticket.triggered_event.tame(check_string)) event_info = parse_freshdesk_event(ticket["triggered_event"].tame(check_string))
if event_info[1] == "created": if event_info[1] == "created":
content = format_freshdesk_ticket_creation_message(ticket) content = format_freshdesk_ticket_creation_message(ticket)