Fix helpers/config: timezone, comment leak, silent misconfig, cache perms
- Database.php: pin MySQL session time_zone to the configured named zone (mysql.time_zone tables now loaded on the DB) with a fixed-offset fallback, so NOW()/TIMESTAMP and PHP agree regardless of the DB server's SYSTEM tz. Best-effort, never fatals the connection. - NotificationHelper: redact comment-body previews for internal/ confidential tickets in sendCommentNotification and notifyWatchers so they are not leaked to the shared Matrix notify list (new $visibility param; callers wired in the API batch). - config.php: die with a clear error if parse_ini_file fails instead of silently falling back to insecure defaults (empty DB pass / proxies). - CacheHelper: create cache dir 0700 and cache files 0600 so other local users cannot read or poison security-relevant cached data. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -96,21 +96,31 @@ class NotificationHelper
|
||||
* @param string $commentText Plain text (first 200 chars will be sent)
|
||||
* @param string|null $authorDisplay Display name of commenter
|
||||
* @param bool $isInternal True if the comment is internal-only
|
||||
* @param string $visibility Ticket visibility: 'public', 'internal', or
|
||||
* 'confidential'. For non-public tickets the
|
||||
* comment text preview is redacted so it is
|
||||
* never leaked to the shared notify list.
|
||||
*/
|
||||
public static function sendCommentNotification($ticketId, string $ticketTitle, string $commentText, ?string $authorDisplay = null, bool $isInternal = false): void
|
||||
public static function sendCommentNotification($ticketId, string $ticketTitle, string $commentText, ?string $authorDisplay = null, bool $isInternal = false, string $visibility = 'public'): void
|
||||
{
|
||||
// Skip if this is an internal-only comment — only the assignee/admin need to know
|
||||
$notifyUsers = self::notifyUsers();
|
||||
if (empty($notifyUsers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The shared notify list may include users without access to non-public
|
||||
// tickets, so never post the comment body for internal/confidential
|
||||
// tickets — only that activity occurred.
|
||||
$preview = $visibility === 'public'
|
||||
? mb_strimwidth($commentText, 0, 200, '…')
|
||||
: null;
|
||||
|
||||
self::fire([
|
||||
'event' => 'comment_added',
|
||||
'ticket_id' => $ticketId,
|
||||
'title' => $ticketTitle,
|
||||
'author' => $authorDisplay,
|
||||
'preview' => mb_strimwidth($commentText, 0, 200, '…'),
|
||||
'preview' => $preview,
|
||||
'is_internal' => $isInternal,
|
||||
'url' => UrlHelper::ticketUrl($ticketId),
|
||||
'notify_users' => $notifyUsers,
|
||||
@@ -155,8 +165,14 @@ class NotificationHelper
|
||||
* @param string $event One of: status_changed, comment_added, assigned
|
||||
* @param array $extraData Merged into the payload (old_status/new_status, author, etc.)
|
||||
* @param int|null $excludeUserId Don't notify the actor themselves
|
||||
* @param string $visibility Ticket visibility: 'public', 'internal', or
|
||||
* 'confidential'. notify_users includes the
|
||||
* shared list, which may contain users without
|
||||
* access to non-public tickets, so any comment
|
||||
* body preview in $extraData is redacted for
|
||||
* non-public tickets.
|
||||
*/
|
||||
public static function notifyWatchers(\mysqli $conn, $ticketId, string $ticketTitle, string $event, array $extraData = [], ?int $excludeUserId = null): void
|
||||
public static function notifyWatchers(\mysqli $conn, $ticketId, string $ticketTitle, string $event, array $extraData = [], ?int $excludeUserId = null, string $visibility = 'public'): void
|
||||
{
|
||||
$webhookUrl = $GLOBALS['config']['MATRIX_WEBHOOK_URL'] ?? null;
|
||||
$domain = $GLOBALS['config']['MATRIX_DOMAIN'] ?? null;
|
||||
@@ -164,6 +180,12 @@ class NotificationHelper
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't leak comment/body content to the shared notify list for
|
||||
// non-public tickets — keep only the fact that activity occurred.
|
||||
if ($visibility !== 'public' && isset($extraData['preview'])) {
|
||||
$extraData['preview'] = null;
|
||||
}
|
||||
|
||||
// Fetch watcher usernames, excluding the actor so they don't notify
|
||||
// themselves. Notifications are best-effort: if the watchers table is
|
||||
// absent or the query fails, skip silently rather than fataling the
|
||||
|
||||
Reference in New Issue
Block a user