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:
gsinghpal
2026-05-27 03:53:37 -04:00
parent 6ce49573ef
commit fb345be375
5 changed files with 112 additions and 25 deletions

View File

@@ -46,6 +46,7 @@ export class FusionHelpdeskDialog extends Component {
ticketId: null,
ticketUrl: "",
attached: 0,
failed: 0,
// ---- My Tickets ----
isAdmin: false,
scope: "mine", // 'mine' | 'all'
@@ -110,6 +111,7 @@ export class FusionHelpdeskDialog extends Component {
this.state.isAdmin = !!res.is_admin;
}
} catch (err) {
console.error("fusion_helpdesk: my_tickets failed", err);
this.state.listError = (err && err.message) || _t("Network error.");
} finally {
this.state.loadingList = false;
@@ -143,6 +145,7 @@ export class FusionHelpdeskDialog extends Component {
row.has_unread = false;
}
} catch (err) {
console.error("fusion_helpdesk: open ticket failed", err);
this.state.threadError = (err && err.message) || _t("Network error.");
} finally {
this.state.loadingThread = false;
@@ -173,6 +176,7 @@ export class FusionHelpdeskDialog extends Component {
this.notification.add(_t("Reply sent."), { type: "success" });
}
} catch (err) {
console.error("fusion_helpdesk: send reply failed", err);
this.state.threadError = (err && err.message) || _t("Network error.");
} finally {
this.state.sendingReply = false;
@@ -358,12 +362,14 @@ export class FusionHelpdeskDialog extends Component {
this.state.ticketId = res.ticket_id;
this.state.ticketUrl = res.ticket_url;
this.state.attached = res.attached || 0;
this.state.failed = res.failed || 0;
this.state.subject = "";
this.state.description = "";
this.state.errorCode = "";
this.state.attachments = [];
}
} catch (err) {
console.error("fusion_helpdesk: submit failed", err);
this.state.error = (err && err.message) || _t("Network error.");
} finally {
this.state.submitting = false;

View File

@@ -110,6 +110,11 @@
created<t t-if="state.attached"> with <t t-esc="state.attached"/> attachment(s)</t>.
You'll get replies by email, and can follow up under <b>My Tickets</b>.
</div>
<div t-if="state.success and state.failed" class="alert alert-warning mt-2">
<i class="fa fa-exclamation-triangle me-1"/>
<t t-esc="state.failed"/> attachment(s) could not be uploaded.
Open the ticket from <b>My Tickets</b> and add them there.
</div>
</div>
<!-- ===== LIST ===== -->