From 66461e75538e888dc727d7024c695f0d0aca3bef Mon Sep 17 00:00:00 2001 From: Nick Misasi Date: Fri, 17 Apr 2026 11:15:09 -0400 Subject: [PATCH 1/3] [MM-67949] Harden notification email filename rendering (#36082) * [MM-67949] Harden notification email filename rendering Escape and normalize attachment filenames before inserting them into notification email content, and add focused regression coverage for file-only and image-only notification paths. Made-with: Cursor * chore: retrigger CI checks --------- Co-authored-by: Edgar --- .../channels/app/email/notification_email.go | 22 ++-- .../channels/app/notification_email_test.go | 102 ++++++++++++++++++ 2 files changed, 115 insertions(+), 9 deletions(-) diff --git a/server/channels/app/email/notification_email.go b/server/channels/app/email/notification_email.go index d95cc452a56..8fa9d460170 100644 --- a/server/channels/app/email/notification_email.go +++ b/server/channels/app/email/notification_email.go @@ -8,7 +8,6 @@ import ( "html" "html/template" "net/url" - "path/filepath" "strings" "github.com/mattermost/mattermost/server/public/model" @@ -33,7 +32,7 @@ func (es *Service) GetMessageForNotification(post *model.Post, teamName, siteUrl return es.prepareNotificationMessageForEmail(post.Message, teamName, siteUrl) } - // extract the filenames from their paths and determine what type of files are attached + // normalize the filenames and determine what type of files are attached infos, err := es.store.FileInfo().GetForPost(post.Id, true, false, true) if err != nil { mlog.Warn("Encountered error when getting files for notification message", mlog.String("post_id", post.Id), mlog.Err(err)) @@ -42,12 +41,11 @@ func (es *Service) GetMessageForNotification(post *model.Post, teamName, siteUrl filenames := make([]string, len(infos)) onlyImages := true for i, info := range infos { - if escaped, err := url.QueryUnescape(filepath.Base(info.Name)); err != nil { - // this should never error since filepath was escaped using url.QueryEscape - filenames[i] = escaped - } else { - filenames[i] = info.Name + filename := info.Name + if unescaped, err := url.QueryUnescape(filename); err == nil { + filename = unescaped } + filenames[i] = filename onlyImages = onlyImages && info.IsImage() } @@ -55,9 +53,15 @@ func (es *Service) GetMessageForNotification(post *model.Post, teamName, siteUrl props := map[string]any{"Filenames": strings.Join(filenames, ", ")} if onlyImages { - return translateFunc("api.post.get_message_for_notification.images_sent", len(filenames), props) + return string(i18n.TranslateAsHTML(translateFunc, "api.post.get_message_for_notification.images_sent", map[string]any{ + "Count": len(filenames), + "Filenames": props["Filenames"], + })) } - return translateFunc("api.post.get_message_for_notification.files_sent", len(filenames), props) + return string(i18n.TranslateAsHTML(translateFunc, "api.post.get_message_for_notification.files_sent", map[string]any{ + "Count": len(filenames), + "Filenames": props["Filenames"], + })) } func ProcessMessageAttachments(post *model.Post, siteURL string) []*EmailMessageAttachment { diff --git a/server/channels/app/notification_email_test.go b/server/channels/app/notification_email_test.go index 841ea921373..5d399ac7438 100644 --- a/server/channels/app/notification_email_test.go +++ b/server/channels/app/notification_email_test.go @@ -6,7 +6,9 @@ package app import ( "bytes" "fmt" + "html" "html/template" + "net/url" "regexp" "testing" "time" @@ -746,6 +748,106 @@ func TestGetNotificationEmailEscapingChars(t *testing.T) { assert.NotContains(t, body, message) } +func TestGetNotificationEmailBodyFullNotificationFileOnlyPostEscapesFilenameHTML(t *testing.T) { + mainHelper.Parallel(t) + th := SetupWithStoreMock(t) + + recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true) + dangerousFilename := "owned.txt" + post := &model.Post{ + Id: "test-post-id", + Message: "", + FileIds: []string{"test-file-id"}, + } + channel := &model.Channel{ + Id: "test-channel-id", + Name: "testchannel", + DisplayName: "ChannelName", + Type: model.ChannelTypeOpen, + } + sender := buildTestUser("test-sender-id", "sender", "sender", true) + team := buildTestTeam("test-team-id", "testteam", "testteam") + + storeMock := th.App.Srv().Store().(*mocks.Store) + fileInfoStoreMock := mocks.FileInfoStore{} + fileInfoStoreMock.On("GetForPost", post.Id, true, false, true).Return([]*model.FileInfo{{ + Id: "test-file-id", + PostId: post.Id, + Name: dangerousFilename, + Extension: "txt", + MimeType: "text/plain", + }}, nil) + storeMock.On("FileInfo").Return(&fileInfoStoreMock) + + setupPreferenceMocks(th, recipient.Id, true) + th.App.Srv().EmailService.SetStore(storeMock) + + notification := buildTestPostNotification(post, channel, sender) + emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team) + body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "") + require.NoError(t, err) + + require.Contains(t, body, html.EscapeString(dangerousFilename)) + require.NotContains(t, body, dangerousFilename) +} + +func TestGetNotificationEmailBodyFullNotificationImageOnlyPostNormalizesAndEscapesFilenamesHTML(t *testing.T) { + mainHelper.Parallel(t) + th := SetupWithStoreMock(t) + + recipient := buildTestUser("test-recipient-id", "recipient", "Recipient User", true) + rawDangerousFilename := ".png" + encodedDangerousFilename := url.QueryEscape(rawDangerousFilename) + secondFilename := "photo & notes.png" + post := &model.Post{ + Id: "test-post-id", + Message: "", + FileIds: []string{"test-file-id-1", "test-file-id-2"}, + } + channel := &model.Channel{ + Id: "test-channel-id", + Name: "testchannel", + DisplayName: "ChannelName", + Type: model.ChannelTypeOpen, + } + sender := buildTestUser("test-sender-id", "sender", "sender", true) + team := buildTestTeam("test-team-id", "testteam", "testteam") + + storeMock := th.App.Srv().Store().(*mocks.Store) + fileInfoStoreMock := mocks.FileInfoStore{} + fileInfoStoreMock.On("GetForPost", post.Id, true, false, true).Return([]*model.FileInfo{ + { + Id: "test-file-id-1", + PostId: post.Id, + Name: encodedDangerousFilename, + Extension: "png", + MimeType: "image/png", + }, + { + Id: "test-file-id-2", + PostId: post.Id, + Name: secondFilename, + Extension: "png", + MimeType: "image/png", + }, + }, nil) + storeMock.On("FileInfo").Return(&fileInfoStoreMock) + + setupPreferenceMocks(th, recipient.Id, true) + th.App.Srv().EmailService.SetStore(storeMock) + + notification := buildTestPostNotification(post, channel, sender) + emailNotification := th.App.buildEmailNotification(th.Context, notification, recipient, team) + body, err := th.App.getNotificationEmailBodyFromEmailNotification(th.Context, recipient, emailNotification, post, "") + require.NoError(t, err) + + expectedFilenames := fmt.Sprintf("%s, %s", html.EscapeString(rawDangerousFilename), html.EscapeString(secondFilename)) + require.Contains(t, body, "2 images sent:") + require.Contains(t, body, expectedFilenames) + require.NotContains(t, body, rawDangerousFilename) + require.NotContains(t, body, encodedDangerousFilename) +} + func TestGetNotificationEmailBodyPublicChannelMention(t *testing.T) { mainHelper.Parallel(t) th := SetupWithStoreMock(t) From 73100fcb566e54ac24395e57093d33437429a0cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Garc=C3=ADa=20Montoro?= Date: Fri, 17 Apr 2026 17:29:37 +0200 Subject: [PATCH 2/3] Bump Boards FIPS version to v9.2.4 (#36165) --- server/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Makefile b/server/Makefile index 9fb237008c3..d45c8c0c939 100644 --- a/server/Makefile +++ b/server/Makefile @@ -179,7 +179,7 @@ PLUGIN_PACKAGES += mattermost-plugin-channel-export-v1.3.0 ifeq ($(FIPS_ENABLED),true) PLUGIN_PACKAGES = mattermost-plugin-playbooks-v2.8.0%2Bc4449ac-fips PLUGIN_PACKAGES += mattermost-plugin-agents-v1.7.2%2B866e2dd-fips - PLUGIN_PACKAGES += mattermost-plugin-boards-v9.2.2%2B4282c63-fips + PLUGIN_PACKAGES += mattermost-plugin-boards-v9.2.4%2B5855fe1-fips endif EE_PACKAGES=$(shell $(GO) list $(BUILD_ENTERPRISE_DIR)/...) From bf84301784777a6e08f9709ee882b0eac029437a Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Fri, 17 Apr 2026 15:14:20 -0400 Subject: [PATCH 3/3] MM-38308 Remove remaining support for IE and pre-Chromium Edge (#36034) * Remove logic for supporting pre-Chromium versions of Edge * Remove support for IE11 * Remove IE/Edge-specific CSS --- .../channel_layout/channel_controller.tsx | 11 +----- webapp/channels/src/images/icon_WS.png | Bin 8766 -> 0 bytes .../src/sass/components/_emoticons.scss | 4 -- .../channels/src/sass/components/_modal.scss | 12 ------ .../channels/src/sass/components/_post.scss | 21 ----------- .../channels/src/sass/layout/_navigation.scss | 13 ------- .../channels/src/sass/responsive/_mobile.scss | 35 ------------------ .../channels/src/sass/responsive/_tablet.scss | 9 ----- .../channels/src/sass/routes/_settings.scss | 4 -- .../src/utils/notification_sounds.tsx | 13 +------ webapp/channels/src/utils/notifications.ts | 9 +---- webapp/channels/src/utils/user_agent.tsx | 10 ----- webapp/channels/src/utils/utils.tsx | 7 +--- 13 files changed, 5 insertions(+), 143 deletions(-) delete mode 100644 webapp/channels/src/images/icon_WS.png delete mode 100644 webapp/channels/src/utils/user_agent.tsx diff --git a/webapp/channels/src/components/channel_layout/channel_controller.tsx b/webapp/channels/src/components/channel_layout/channel_controller.tsx index 0de96244157..a430dc7a633 100644 --- a/webapp/channels/src/components/channel_layout/channel_controller.tsx +++ b/webapp/channels/src/components/channel_layout/channel_controller.tsx @@ -21,7 +21,6 @@ import UnreadsStatusHandler from 'components/unreads_status_handler'; import Pluggable from 'plugins/pluggable'; import {Constants} from 'utils/constants'; -import {isInternetExplorer, isEdge} from 'utils/user_agent'; const ProductNoticesModal = makeAsyncComponent('ProductNoticesModal', lazy(() => import('components/product_notices_modal'))); const ResetStatusModal = makeAsyncComponent('ResetStatusModal', lazy(() => import('components/reset_status_modal'))); @@ -39,12 +38,11 @@ export default function ChannelController(props: Props) { const dispatch = useDispatch(); useEffect(() => { - const isMsBrowser = isInternetExplorer() || isEdge(); const {navigator} = window; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const platform = navigator?.userAgentData?.platform || navigator?.platform || 'unknown'; - document.body.classList.add(...getClassnamesForBody(platform, isMsBrowser)); + document.body.classList.add(...getClassnamesForBody(platform)); return () => { document.body.classList.remove(...BODY_CLASS_FOR_CHANNEL); @@ -93,7 +91,7 @@ export default function ChannelController(props: Props) { ); } -export function getClassnamesForBody(platform: Window['navigator']['platform'], isMsBrowser = false) { +export function getClassnamesForBody(platform: Window['navigator']['platform']) { const bodyClass = [...BODY_CLASS_FOR_CHANNEL]; // OS Detection @@ -103,10 +101,5 @@ export function getClassnamesForBody(platform: Window['navigator']['platform'], bodyClass.push('os--mac'); } - // IE Detection - if (isMsBrowser) { - bodyClass.push('browser--ie'); - } - return bodyClass; } diff --git a/webapp/channels/src/images/icon_WS.png b/webapp/channels/src/images/icon_WS.png deleted file mode 100644 index 9d34b88c7b2bee3fe792344603950f8c60e3a283..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8766 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 GjQ(FZ9kE*g diff --git a/webapp/channels/src/sass/components/_emoticons.scss b/webapp/channels/src/sass/components/_emoticons.scss index 22e49ca3d75..bdc20573964 100644 --- a/webapp/channels/src/sass/components/_emoticons.scss +++ b/webapp/channels/src/sass/components/_emoticons.scss @@ -100,10 +100,6 @@ @include mixins.clearfix; - .browser--ie & { - width: 325px; - } - .app__content & { margin-right: 0; } diff --git a/webapp/channels/src/sass/components/_modal.scss b/webapp/channels/src/sass/components/_modal.scss index 218fa87039b..7f4b1d965b6 100644 --- a/webapp/channels/src/sass/components/_modal.scss +++ b/webapp/channels/src/sass/components/_modal.scss @@ -4,14 +4,6 @@ @use 'sass:color'; -.browser--ie { - .modal { - .modal-dialog { - transform: translateY(0); - } - } -} - .modal-backdrop { &.in { opacity: 0.64; @@ -754,10 +746,6 @@ display: flex; flex-direction: column; - .browser--ie & { - flex: 20; - } - >div:not([data-simplebar]) { overflow: auto; min-height: 100%; diff --git a/webapp/channels/src/sass/components/_post.scss b/webapp/channels/src/sass/components/_post.scss index 08fe147268f..c6500b2a4c1 100644 --- a/webapp/channels/src/sass/components/_post.scss +++ b/webapp/channels/src/sass/components/_post.scss @@ -599,27 +599,6 @@ } } - .browser--ie & { - .post__header { - .col__reply { - .comment-icon__container { - flex: 0 1 auto; - align-items: center; - } - - > .open, - > div { - flex: 0 1 30px; - align-items: center; - - &:first-child { - flex: 0 1 25px; - } - } - } - } - } - &.post--system { .post__header { .post-menu { diff --git a/webapp/channels/src/sass/layout/_navigation.scss b/webapp/channels/src/sass/layout/_navigation.scss index 9604b7f898e..e3b06992fae 100644 --- a/webapp/channels/src/sass/layout/_navigation.scss +++ b/webapp/channels/src/sass/layout/_navigation.scss @@ -46,19 +46,6 @@ justify-content: center; } - .browser--ie & { - .navbar-default { - .navbar-brand { - overflow: visible; - padding: 1px; - - .heading { - max-width: 100px; - } - } - } - } - .navbar-default { position: absolute; min-height: 50px; diff --git a/webapp/channels/src/sass/responsive/_mobile.scss b/webapp/channels/src/sass/responsive/_mobile.scss index 7cb92cbb658..ea0089d6f0f 100644 --- a/webapp/channels/src/sass/responsive/_mobile.scss +++ b/webapp/channels/src/sass/responsive/_mobile.scss @@ -46,16 +46,6 @@ } } } - - .browser--ie & { - .navbar-default { - .dropdown-menu { - .close { - top: 70px; - } - } - } - } } .app__body { @@ -732,16 +722,6 @@ visibility: visible; } - .browser--ie & { - .post__header { - .post-menu { - .dropdown + div { - display: none; - } - } - } - } - .post-menu__item--reactions { display: none; } @@ -1394,15 +1374,6 @@ width: 100%; transform: translate3d(100%, 0, 0); - .browser--ie & { - display: none; - -webkit-transform: none !important; - -moz-transform: none !important; - -ms-transform: none !important; - -o-transform: none !important; - transform: none !important; - } - .sidebar--right__bg { display: none; } @@ -1586,12 +1557,6 @@ } @media screen and (max-width: 640px) { - body { - &.browser--ie { - min-width: 600px; - } - } - .section-min .d-flex { flex-direction: column; } diff --git a/webapp/channels/src/sass/responsive/_tablet.scss b/webapp/channels/src/sass/responsive/_tablet.scss index 8fc287b3a32..430e7beb8dd 100644 --- a/webapp/channels/src/sass/responsive/_tablet.scss +++ b/webapp/channels/src/sass/responsive/_tablet.scss @@ -431,15 +431,6 @@ -o-transform: translateX(0) !important; transform: translateX(0) !important; - .browser--ie & { - display: block; - -webkit-transform: none !important; - -moz-transform: none !important; - -ms-transform: none !important; - -o-transform: none !important; - transform: none !important; - } - .search-bar__container { z-index: 5; display: block !important; diff --git a/webapp/channels/src/sass/routes/_settings.scss b/webapp/channels/src/sass/routes/_settings.scss index 44a53703c1d..ffd92c20019 100644 --- a/webapp/channels/src/sass/routes/_settings.scss +++ b/webapp/channels/src/sass/routes/_settings.scss @@ -355,10 +355,6 @@ font: normal normal normal 14px/1 FontAwesome; pointer-events: none; text-rendering: auto; - - .browser--ie & { - display: none; - } } } diff --git a/webapp/channels/src/utils/notification_sounds.tsx b/webapp/channels/src/utils/notification_sounds.tsx index fbe6dd41e60..ac16066c748 100644 --- a/webapp/channels/src/utils/notification_sounds.tsx +++ b/webapp/channels/src/utils/notification_sounds.tsx @@ -19,7 +19,6 @@ import hello from 'sounds/hello.mp3'; import ripple from 'sounds/ripple.mp3'; import upstairs from 'sounds/upstairs.mp3'; import {DesktopSound} from 'utils/constants'; -import {isEdge} from 'utils/user_agent'; export const DesktopNotificationSounds = { DEFAULT: 'default', @@ -169,7 +168,7 @@ export function getValueOfIncomingCallSoundsSelect(soundName?: string) { let canDing = true; export function ding(name: string) { - if (hasSoundOptions() && canDing) { + if (canDing) { tryNotificationSound(name); canDing = false; setTimeout(() => { @@ -185,9 +184,6 @@ export function tryNotificationSound(name: string) { let currentRing: HTMLAudioElement | null = null; export function ring(name: string) { - if (!hasSoundOptions()) { - return; - } stopRing(); currentRing = loopNotificationRing(name); @@ -208,9 +204,6 @@ export function stopRing() { let currentTryRing: HTMLAudioElement | null = null; let currentTimer: NodeJS.Timeout; export function tryNotificationRing(name: string) { - if (!hasSoundOptions()) { - return; - } stopTryNotificationRing(); clearTimeout(currentTimer); @@ -240,10 +233,6 @@ export function loopNotificationRing(name: string) { return audio; } -export function hasSoundOptions() { - return (!isEdge()); -} - /** * This conversion is needed because User's preference for desktop sound is stored as either true or false. On the other hand, * Channel's specific desktop sound is stored as either On or Off. diff --git a/webapp/channels/src/utils/notifications.ts b/webapp/channels/src/utils/notifications.ts index 253a9081832..2cfef8a4c7a 100644 --- a/webapp/channels/src/utils/notifications.ts +++ b/webapp/channels/src/utils/notifications.ts @@ -2,8 +2,6 @@ // See LICENSE.txt for license information. import icon50 from 'images/icon50x50.png'; -import iconWS from 'images/icon_WS.png'; -import {isEdge} from 'utils/user_agent'; import type {ThunkActionFunc} from 'types/store'; @@ -45,11 +43,6 @@ export function showNotification( }, ): ThunkActionFunc void}>> { return async () => { - let icon = icon50; - if (isEdge()) { - icon = iconWS; - } - if (!isNotificationAPISupported()) { throw new Error('Notification API is not supported'); } @@ -79,7 +72,7 @@ export function showNotification( const notification = new Notification(title, { body, tag: body, - icon, + icon: icon50, requireInteraction, silent, }); diff --git a/webapp/channels/src/utils/user_agent.tsx b/webapp/channels/src/utils/user_agent.tsx deleted file mode 100644 index 69aab209236..00000000000 --- a/webapp/channels/src/utils/user_agent.tsx +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -export function isInternetExplorer(): boolean { - return window.navigator.userAgent.indexOf('Trident') !== -1; -} - -export function isEdge(): boolean { - return window.navigator.userAgent.indexOf('Edge') !== -1; -} diff --git a/webapp/channels/src/utils/utils.tsx b/webapp/channels/src/utils/utils.tsx index ff8df0126da..bcd0a5ea7ca 100644 --- a/webapp/channels/src/utils/utils.tsx +++ b/webapp/channels/src/utils/utils.tsx @@ -61,7 +61,6 @@ import DesktopApp from 'utils/desktop_api'; import {getIntl} from 'utils/i18n'; import * as Keyboard from 'utils/keyboard'; import {FOCUS_REPLY_POST, isPopoutWindow, sendToParent} from 'utils/popouts/popout_windows'; -import {isInternetExplorer, isEdge} from 'utils/user_agent'; import {joinPrivateChannelPrompt} from './channel_utils'; @@ -1192,15 +1191,11 @@ export function fillRecord(value: T, length: number): Record { // Checks if a data transfer contains files not text, folders, etc.. // Slightly modified from http://stackoverflow.com/questions/6848043/how-do-i-detect-a-file-is-being-dragged-rather-than-a-draggable-element-on-my-pa export function isFileTransfer(files: DataTransfer) { - if (isInternetExplorer() || isEdge()) { - return files.types != null && files.types.includes('Files'); - } - return files.types != null && (files.types.indexOf ? files.types.indexOf('Files') !== -1 : files.types.includes('application/x-moz-file')); } export function isUriDrop(dataTransfer: DataTransfer) { - if (isInternetExplorer() || isEdge() || isSafari()) { + if (isSafari()) { for (let i = 0; i < dataTransfer.items.length; i++) { if (dataTransfer.items[i].type === 'text/uri-list') { return true;