Exclude shared notify list and redact title in notifyWatchers() for non-public tickets (#71)
notifyWatchers() only redacted the comment/activity preview for non-public tickets — the shared MATRIX_NOTIFY_USERS list was still merged into notify_users unconditionally, and the ticket title was never redacted at all. A status-change/comment notification for a confidential ticket with watchers still broadcast that ticket's title to the shared list, even though the function's own docblock intended to protect non-public tickets from it. For non-public tickets, the shared list is now excluded entirely (only actual watchers are notified) and the title is redacted via the same redactedTitle() helper added for #46. Verified against real MariaDB with a real watcher row: for a confidential ticket, the captured webhook payload has only the watcher's Matrix ID (no shared list) and a redacted title; for the same ticket made public, the shared list is included and the title passes through unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015nCxwFFsy8ouMWzn56rPVP
This commit is contained in:
@@ -189,11 +189,12 @@ class NotificationHelper
|
|||||||
* @param array $extraData Merged into the payload (old_status/new_status, author, etc.)
|
* @param array $extraData Merged into the payload (old_status/new_status, author, etc.)
|
||||||
* @param int|null $excludeUserId Don't notify the actor themselves
|
* @param int|null $excludeUserId Don't notify the actor themselves
|
||||||
* @param string $visibility Ticket visibility: 'public', 'internal', or
|
* @param string $visibility Ticket visibility: 'public', 'internal', or
|
||||||
* 'confidential'. notify_users includes the
|
* 'confidential'. The shared notify list may
|
||||||
* shared list, which may contain users without
|
* contain users without access to non-public
|
||||||
* access to non-public tickets, so any comment
|
* tickets, so for those tickets it's excluded
|
||||||
* body preview in $extraData is redacted for
|
* entirely (only actual watchers are notified)
|
||||||
* non-public tickets.
|
* and both the title and any comment/body
|
||||||
|
* preview in $extraData are redacted.
|
||||||
*/
|
*/
|
||||||
public static function notifyWatchers(\mysqli $conn, $ticketId, string $ticketTitle, string $event, array $extraData = [], ?int $excludeUserId = null, string $visibility = 'public'): void
|
public static function notifyWatchers(\mysqli $conn, $ticketId, string $ticketTitle, string $event, array $extraData = [], ?int $excludeUserId = null, string $visibility = 'public'): void
|
||||||
{
|
{
|
||||||
@@ -253,13 +254,17 @@ class NotificationHelper
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the global notify list duplicates and build payload
|
// The shared notify list may include users without access to
|
||||||
$allNotify = array_unique(array_merge($matrixIds, self::notifyUsers()));
|
// non-public tickets, so only mix it in for public tickets — for
|
||||||
|
// internal/confidential tickets, notify actual watchers only.
|
||||||
|
$allNotify = $visibility === 'public'
|
||||||
|
? array_unique(array_merge($matrixIds, self::notifyUsers()))
|
||||||
|
: $matrixIds;
|
||||||
|
|
||||||
$payload = array_merge($extraData, [
|
$payload = array_merge($extraData, [
|
||||||
'event' => $event,
|
'event' => $event,
|
||||||
'ticket_id' => $ticketId,
|
'ticket_id' => $ticketId,
|
||||||
'title' => $ticketTitle,
|
'title' => self::redactedTitle($ticketTitle, $visibility),
|
||||||
'url' => UrlHelper::ticketUrl($ticketId),
|
'url' => UrlHelper::ticketUrl($ticketId),
|
||||||
'notify_users' => array_values($allNotify),
|
'notify_users' => array_values($allNotify),
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user