fix(fusion_helpdesk): close IDOR + reliability gaps from code review
- P0 IDOR: escape LIKE wildcards in build_scope_domain + normalise emails (email_normalize) on both write and scope sides, so a self-set email of '%' can no longer match every ticket in a deployment (+2 regression tests). - Convert XML-RPC ProtocolError (central 502/503/429) to _RemoteError instead of a raw 500. - _resolve_author: log + post as service account on failure instead of silently impersonating the ticket customer. - _mark_seen now best-effort (runs after the remote post) so a bookkeeping failure can't report a successful reply as failed (no duplicate replies). - Attachment loop catches network errors + reports failed count (no duplicate-ticket trap); dialog surfaces failures. - console.error in JS catches for diagnosability. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,20 @@ filter and the unread maths without a live central Odoo to talk to.
|
||||
_NO_MATCH = '__none__'
|
||||
|
||||
|
||||
def escape_like(value):
|
||||
"""Escape SQL LIKE/ILIKE wildcards so a user-supplied value can never
|
||||
widen an `=ilike` match to other rows.
|
||||
|
||||
`res.users.email` is self-writeable and unvalidated, so without this a
|
||||
user could set their email to ``%`` and have ``partner_email =ilike '%'``
|
||||
match EVERY ticket in their deployment (a cross-user IDOR). Escaping the
|
||||
backslash first, then ``%`` and ``_``, makes those characters match
|
||||
literally. Real emails containing ``_`` (e.g. ``john_doe@x.com``) keep
|
||||
working — the underscore is matched as a literal, which is what we want.
|
||||
"""
|
||||
return (value or '').replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')
|
||||
|
||||
|
||||
def build_ticket_vals(kind, subject, body_html, team_id, client_label,
|
||||
reporter_name, reporter_email, company_name):
|
||||
"""Construct the `helpdesk.ticket` create vals for a forwarded report.
|
||||
@@ -53,7 +67,8 @@ def build_scope_domain(label, email, is_admin):
|
||||
"""
|
||||
domain = [('x_fc_client_label', '=', label or _NO_MATCH)]
|
||||
if not is_admin:
|
||||
domain.append(('partner_email', '=ilike', email or _NO_MATCH))
|
||||
safe_email = escape_like(email)
|
||||
domain.append(('partner_email', '=ilike', safe_email or _NO_MATCH))
|
||||
return domain
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user