Merge upstream mattermost/release-11.7 (through 11.7.7) — 56 commits #5

Merged
mohlec merged 57 commits from merge/upstream-2026-06-30 into slash/main 2026-06-30 20:22:09 +00:00
Owner

Brings slash/main current with upstream release-11.7 (tip 8092c1a86, through 11.7.7).

Merge: clean, zero conflicts. Slash customizations verified intact: branding gate passes, footer/title/PWA name, Phase 5a unread-first default, Phase 3 license-gate removals.

Security/hardening (~8): incoming-webhook user validation, post-action verification, file-removal hardening, channel role validation, OAuth token issuance/cleanup, thread-membership cleanup, S3 >5GiB fix, group_constrained restriction.

Plugin bumps: Playbooks 2.9.2, Jira 4.7.1, GitLab 1.12.3, agents 2.0.4.

Build infra: runner Go upgraded 1.25.9 -> 1.25.11 (old at /usr/local/go.bak-1.25.9).

CI: server binary success; webapp dist built + verified (title=Slash Chat). 230 files, +9813/-968.

After merge: echo 8092c1a86 | sudo tee /opt/slash-upstream-watch/merged-base.sha

Brings slash/main current with upstream release-11.7 (tip 8092c1a86, through 11.7.7). Merge: clean, zero conflicts. Slash customizations verified intact: branding gate passes, footer/title/PWA name, Phase 5a unread-first default, Phase 3 license-gate removals. Security/hardening (~8): incoming-webhook user validation, post-action verification, file-removal hardening, channel role validation, OAuth token issuance/cleanup, thread-membership cleanup, S3 >5GiB fix, group_constrained restriction. Plugin bumps: Playbooks 2.9.2, Jira 4.7.1, GitLab 1.12.3, agents 2.0.4. Build infra: runner Go upgraded 1.25.9 -> 1.25.11 (old at /usr/local/go.bak-1.25.9). CI: server binary success; webapp dist built + verified (title=Slash Chat). 230 files, +9813/-968. After merge: echo 8092c1a86 | sudo tee /opt/slash-upstream-watch/merged-base.sha
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
* disable flaky autotranslation tests

* mark tests as fixme for autotranslation and demo plugin hook toggle
Automatic Merge
* MM-68339: Add XML struct tags and multi-remote registration for shared channels plugin API  (#36126)

* Add XML struct tags and multi-remote registration for shared channels plugin API

  Phase 1: Add xml struct tags to model types used in SyncMsg (Post, User,
  Reaction, Status, PostAcknowledgement, FileInfo, SyncResponse,
  MembershipChangeMsg). Add custom MarshalXML/UnmarshalXML for SyncMsg
  (Users map, MentionTransforms map), StringMap, and StringInterface.
  Exclude Post.Metadata, PrevStatus, and server-internal FileInfo fields
  from XML. JSON serialization is unaffected.

  Phase 2: Lift the one-remote-per-plugin constraint so plugins can
  register multiple remotes with different SiteURLs. Add SiteURL field to
  RegisterPluginOpts (defaults to "plugin_<PluginID>" for backward
  compatibility). Add GetAllByPluginID and GetBySiteURL store methods.
  Rewrite registration to dedup by SiteURL instead of PluginID. Add
  UnregisterPluginRemoteForSharedChannels for single-remote removal with
  plugin ownership validation. Validate SiteURL is non-empty in
  RemoteCluster.IsValid. Simplify IsPlugin() to check PluginID only.

(cherry picked from commit 81d4fe3793)

* MM-68339: slugify RemoteCluster.Name in plugin registration (#36309)

The new TestRegisterPluginForSharedChannels tests added in #36126 broke
  master CI because RegisterPluginForSharedChannels assigned opts.Displayname
  directly to RemoteCluster.Name, which IsValid validates against the slug
  regex ^[a-zA-Z0-9.\-_]+$. Display names with spaces (e.g. "legacy plugin")
  fail validation. The tests didn't run in the PR's final CI shard and the
  issue surfaced post-merge.

  Add CleanRemoteName to the public model, mirroring CleanTeamName and
  CleanUsername: lowercase, replace spaces and other disallowed characters
  with hyphens, trim, truncate to RemoteNameMaxLength, fall back to NewId
  when the result is empty. Use it in RegisterPluginForSharedChannels so
  Name is always slug-valid while DisplayName keeps the human-readable label.
  This also lets real plugins register with display names containing spaces.

(cherry picked from commit fdaea9dec3)

* MM-68622: start inter-cluster services before plugin activation (#36366)

* MM-68622: start inter-cluster services before plugin activation

  Move startInterClusterServices from the end of Server.Start() to the
  beginning, before Channels().Start() initializes plugins. This lets
  plugins call shared channels APIs (ShareChannel, InviteRemoteToChannel,
  UninviteRemoteFromChannel, UnshareChannel, UpdateSharedChannel,
  CheckCanInviteToSharedChannel) during OnActivate instead of failing with
  "Shared Channels Service is disabled".

  Side-effect analysis:

  * Plugin API gating: getSharedChannelsService in
    channels/app/shared_channel.go:32 only requires the service to be
    non-nil. The plugin-facing wrappers all pass ensureIsActive=false, so
    Active() is bypassed. Once SetSharedChannelService runs, calls succeed
    on both leader and follower nodes. This is the fix path.

  * Multi-node leader timing: the enterprise cluster impl in
    enterprise/cluster/cluster.go:70 initializes currentLeader="", so
    IsLeader() returns false before StartInterNodeCommunication runs. The
    immediate onClusterLeaderChange in scs.Start at
    platform/services/sharedchannel/service.go:151 therefore takes the
    pause path, which is a no-op since the service was never active. When
    memberlist.Create fires NotifyJoin for the local node,
    addPotentialLeader runs and InvokeClusterLeaderChangedListeners drives
    the registered listener to resume() the sync loop on the elected
    leader. End state matches the prior ordering.

  * Single-node: IsLeader() returns true unconditionally per
    channels/app/platform/cluster.go:33, so SharedChannelSyncHandler is
    active during plugin OnActivate. Events emitted by plugins during
    activation (posts to shared channels, DM creation) now flow through
    sync where they were previously dropped. This is intended correctness,
    not a regression.

  * Transport and handlers: api4 remote-cluster routes are registered
    before Server.Start, so HTTP handlers exist when rcs.Start runs early.
    rcs and scs do not send cluster-broadcast messages during Start; they
    only register topic listeners on the rcs transport, which is
    independent of cluster gossip. registerClusterHandlers ordering is
    unaffected.

  * Config: scs reads only ConnectedWorkspacesSettings and the License at
    construction, both stable from the initial config load. ReloadConfig
    at server.go:912 has no bearing on inter-cluster service init.

  Errors from startInterClusterServices remain logged and non-fatal,
  matching prior behavior.

(cherry picked from commit 846791aa65)

* MM-68536: Show actual remote names in system console channel list (#36298)

* MM-68536: Show actual remote names in system console channel list

  The system console "Channels" list rendered SharedChannelIndicator
  without a remoteNames prop, so every shared channel showed the generic
  "Shared with trusted organizations" fallback even after PR 35908.

  Add a small connected wrapper that selects remote names via
  getRemoteNamesForChannel and dispatches fetchChannelRemotes on mount,
  mirroring the pattern used by the LHS sidebar. The shared_channel_remote_updated
  websocket event added in PR 35908 already refreshes the same Redux slice,
  so the system console list now stays in sync automatically.

(cherry picked from commit 022acb74c5)

* [MM-68697] Preserve sender file ID in plugin-relayed shared channel attachments (#36468)

Set ReqFileId on the UploadSession created in
  ReceiveSharedChannelAttachmentSyncMsg so the attachment is persisted
  under the sender's file ID. Without this, the receiving server stored
  the bytes under a freshly generated ID while the synced post's FileIds
  still referenced the sender's ID, leaving the attachment invisible in
  the UI even though the file and FileInfo row existed on disk.

  Mirrors the existing cluster-to-cluster path in
  platform/services/sharedchannel/attachment.go.

  Also tightens TestPluginAPIReceiveSharedChannelAttachmentSyncMsg to
  pass a sender-side fi.Id and assert the saved FileInfo keeps it. The
  prior assertion only checked that some ID was assigned, which the
  buggy code also satisfied.

(cherry picked from commit 97f0ad7c3b)

* MM-68705 - Order-tolerant Shared Channel plugin API's for receiving attachments (#36486)

* MM-68705 - Make ReceiveSharedChannelAttachmentSyncMsg order tolerant

  Allow plugin remotes to invoke ReceiveSharedChannelAttachmentSyncMsg
  and ReceiveSharedChannelSyncMsg in either order without losing the
  post/file binding, and make repeat deliveries of the same file id
  idempotent.

  Two localised changes inside ReceiveSharedChannelAttachmentSyncMsg:

  - Idempotency check before CreateUploadSession. If a FileInfo with
    the sender's id already exists for the same channel and creator,
    return it instead of inserting a duplicate (which would violate
    the FileInfo PK and force the caller to retry indefinitely after
    any transient ack failure). A mismatched channel or creator on the
    same id is rejected.

  - Lazy bind to post after UploadData. When the matching post is
    already present (post-then-file ordering), CreatePost will have
    stripped the unmatched file id from Post.FileIds; AttachToPost
    plus Post.Overwrite restore the binding. When the post is not
    yet present (file-then-post ordering), the FileInfo is left
    unbound so the eventual post arrival's CreatePost path binds it.
    The post is fetched first because AttachToPost is a blind UPDATE
    that does not validate post existence, so calling it before the
    post exists would orphan the FileInfo.

  No other paths change. Cluster (non-plugin) shared-channel attachments,
  ReceiveSharedChannelProfileImageSyncMsg, and UI uploads are unaffected.

  New tests in shared_channel_test.go cover both orderings, repeated
  receive success and rejection on channel/creator mismatch, and the
  empty-PostId no-op.

(cherry picked from commit 5504435231)

* MM-68708 - Fix TestCreatePost shared DM/GM subtests when env pins feature flag (#36488)

The two shared DM/GM subtests in TestCreatePost rely on
  FeatureFlags.EnableSharedChannelsDMs being false, but the config store
  reapplies MM_FEATUREFLAGS_* env overrides on every Set, so an
  UpdateConfig pin gets clobbered. Use t.Setenv to force the flag false
  for the duration of each subtest, and drop the parent's
  mainHelper.Parallel(t) so Go's "no Setenv under a parallel ancestor"
  rule is satisfied. Sibling subtests still parallelize via their own
  mainHelper.Parallel(t) calls.

(cherry picked from commit 69f30c21e9)

* MM-68838: Ping a restored plugin remote immediately on re-register (#36592)

* MM-68838: ping restored plugin remote immediately on re-register

  RegisterPluginForSharedChannels' restore branch updated the row but did
  not call PingNow, leaving the restored remote offline until the next
  pingLoop tick (up to PingFreq, default 1 minute). The new-connection
  branch already calls PingNow; the restore branch now mirrors it so
  sync attempts immediately after a plugin restart no longer fail with
  "offline remote cluster".

* MM-68838: gob-encode error returns in apiRPCServer.ReceiveSharedChannelAttachmentSyncMsg

  The apiRPCServer wrapper for ReceiveSharedChannelAttachmentSyncMsg
  assigned the hook's error return directly to the gob-encoded response
  struct. When the framework's App.ReceiveSharedChannelAttachmentSyncMsg
  returned an error wrapped with %w (*fmt.wrapError, an unexported type),
  gob refused to encode it and the RPC server broke the connection with
  "type not registered for interface: fmt.wrapError".

  Every subsequent plugin/server RPC call then returned the zero-value
  response struct, causing plugins that dereferenced the nil returns to
  crash.

  Apply the existing encodableError() helper so the returned error
  becomes a gob-safe ErrorString, matching every other apiRPCServer
  method in this file.

(cherry picked from commit 5566604e03)
Co-authored-by: David Krauser <david@krauser.org>
Replace bild imaging library with boxes-ltd/imaging (#36261) (#36606)
Some checks failed
e2e-tests-ci-template.yml / Replace bild imaging library with boxes-ltd/imaging (#36261) (#36606) (push) Failing after 0s
e2e-tests-cypress-template.yml / Replace bild imaging library with boxes-ltd/imaging (#36261) (#36606) (push) Failing after 0s
e2e-tests-cypress.yml / Replace bild imaging library with boxes-ltd/imaging (#36261) (#36606) (push) Failing after 0s
e2e-tests-playwright-template.yml / Replace bild imaging library with boxes-ltd/imaging (#36261) (#36606) (push) Failing after 0s
e2e-tests-playwright.yml / Replace bild imaging library with boxes-ltd/imaging (#36261) (#36606) (push) Failing after 0s
de117ab1cc
* Add benchmarks for image transformation functions

Covers GeneratePreview, GenerateThumbnail, GenerateMiniPreviewImage,
FillCenter, and MakeImageUpright — all functions that delegate to the
external imaging library. Baseline before/after the library swap.

* Replace anthonynsimon/bild with boxes-ltd/imaging

Reverts the imaging library swap from PR #29657 (disintegration/imaging →
anthonynsimon/bild) and replaces it with github.com/boxes-ltd/imaging, the
maintained fork of disintegration/imaging with the same API.

- emoji.go: restore direct use of imaging.Fit with Lanczos filter
- orientation.go: restore FlipH/FlipV/Rotate90/180/270/Transpose/Transverse
- preview.go: restore imaging.Resize calls with Lanczos filter
- utils.go: remove bild-based Resize/Fit/CropCenter helpers; restore the
  original FillCenter using imaging.Fill
- Remove tests and test images added for the bild-specific helpers
- Regenerate orientation test expected images (2,3,6,7,8) and GIF thumbnail
  to match boxes-ltd/imaging output

https://claude.ai/code/session_012f5wLSCRQrQeRraPj282sT

* gofmt imaging package files

* Add tests and fixtures for the boxes-ltd/imaging-backed functions

Cover MakeImageUpright across all 8 EXIF orientations, GeneratePreview,
GenerateMiniPreviewImage, and FillCenter against fixtures regenerated
under the new library.

* Contain boxes-ltd/imaging behind the local imaging wrapper

Add a Fit wrapper alongside FillCenter and route emoji.go through the
local package so boxes-ltd/imaging is only imported from
channels/app/imaging.

* Add TestFit to cover the local Fit wrapper

Dimensional checks for the Fit wrapper used by emoji.go, mirroring
TestGenerateThumbnail. Pixel correctness is covered by the upstream
boxes-ltd/imaging tests; this guards against wrapper-level mistakes
(transposed args, wrong filter).

* Address CodeRabbit nits in TestFillCenter and TestFit

Decode a fresh source image per TestFillCenter subtest so cases stay
isolated even if the wrapper ever mutates input. Rename the TestFit
"smaller than bounds (clone)" case to "no resize when smaller than
bounds" since the assertion only checks dimensions, not clone semantics.

* Revert per-subtest decode in TestFillCenter

imaging.Fill never mutates its input — it always returns a fresh
*image.NRGBA — so re-decoding the source for every subtest was
unnecessary work. Decode once, share across subtests.

* Compare decoded pixels instead of raw PNG/JPEG bytes in tests

image/png isn't byte-stable across Go versions, so comparing
re-encoded byte streams against checked-in fixtures is brittle to
toolchain bumps and re-encoding. Decode both sides and compare RGBA
pixels via a shared requireSameImage helper. Fixtures stay on disk
unchanged.

Covers TestFillCenter, TestGeneratePreview, TestGenerateMiniPreviewImage,
and TestMakeImageUpright. TestFillImageTransparency is left alone since
its byte-comparison pattern predates this branch.

* Report total diff rate when requireSameImage fails

Walk every pixel before failing instead of stopping at the first
mismatch. The failure message now includes how many pixels differ, the
percentage of the image that's off, and the first divergence.

---------



(cherry picked from commit bbbfc019a9)

Co-authored-by: Jesse Hallam <jesse.hallam@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Automatic Merge
* Add TestDesanitizeRemovesAllFakeSettings to catch future omissions

Walks every string field in the config after a Sanitize+desanitize
round-trip and fails if any still holds FakeSetting. This catches the
case where a field is added to Sanitize without a corresponding
desanitize entry.

* Fix ElasticsearchSettings.ClientKey being incorrectly masked as a secret

ClientKey is a file path, not a secret value. Masking it caused the
asterisk string to be persisted to the database on config writes, which
broke TLS client auth on restart.

* Fix desanitize missing entries for fields added in 504fb96fdd98

504fb96fdd98 masked five fields in Sanitize without adding the
corresponding desanitize entries, meaning a config save through the
API would permanently overwrite those fields with FakeSetting:

- FileSettings.ExportAmazonS3SecretAccessKey
- ServiceSettings.GoogleDeveloperKey
- ServiceSettings.GiphySdkKey
- CacheSettings.RedisPassword
- AutoTranslationSettings.LibreTranslate.APIKey

* fixup! Add TestDesanitizeRemovesAllFakeSettings to catch future omissions

* fixup! Fix desanitize missing entries for fields added in 504fb96fdd98

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
(cherry picked from commit b60ba8b6b4)

Co-authored-by: Jesse Hallam <jesse.hallam@gmail.com>
Automatic Merge
Automatic Merge
Bump golang.org/x/image to v0.40.0 (#36734)
Some checks failed
e2e-tests-ci-template.yml / Bump golang.org/x/image to v0.40.0 (#36734) (push) Failing after 0s
e2e-tests-cypress-template.yml / Bump golang.org/x/image to v0.40.0 (#36734) (push) Failing after 0s
e2e-tests-cypress.yml / Bump golang.org/x/image to v0.40.0 (#36734) (push) Failing after 0s
e2e-tests-playwright-template.yml / Bump golang.org/x/image to v0.40.0 (#36734) (push) Failing after 0s
e2e-tests-playwright.yml / Bump golang.org/x/image to v0.40.0 (#36734) (push) Failing after 0s
70c68d0cf6
* Bump golang.org/x/image to v0.40.0

Co-authored-by: Cursor <cursoragent@cursor.com>

* make modules-tidy

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
MM-68845: Tighten authorization on /share-channel autocomplete (#36662) (#36794)
Some checks failed
e2e-tests-ci-template.yml / MM-68845: Tighten authorization on /share-channel autocomplete (#36662) (#36794) (push) Failing after 0s
e2e-tests-cypress-template.yml / MM-68845: Tighten authorization on /share-channel autocomplete (#36662) (#36794) (push) Failing after 0s
e2e-tests-cypress.yml / MM-68845: Tighten authorization on /share-channel autocomplete (#36662) (#36794) (push) Failing after 0s
e2e-tests-playwright-template.yml / MM-68845: Tighten authorization on /share-channel autocomplete (#36662) (#36794) (push) Failing after 0s
e2e-tests-playwright.yml / MM-68845: Tighten authorization on /share-channel autocomplete (#36662) (#36794) (push) Failing after 0s
0375e85204
Automatic Merge
This undoes the accidental cherry-pick of 0777de11d5 from master on release-11.7.

Co-authored-by: Cursor <cursoragent@cursor.com>
Automatic Merge
Automatic Merge
Automatic Merge
This reverts commit 80284ce88c.
Automatic Merge
Bump the default prepackaged Playbooks plugin from 2.9.0 to 2.9.1 (non-FIPS).
FIPS prepackaged plugin package list is unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
* MM-68830: Preserve unknown permissions during migrations on downgrade

A server that was upgraded to a newer release (which introduced new
permissions and wrote them into roles) and then downgraded fails fatally
at startup: the permissions migration re-saves every role, and
Role.Save() rejects any permission the older binary does not recognize,
making the downgrade unrecoverable.

Add RoleStore.SavePreservingUnknownPermissions, used only by
doPermissionsMigration, which tolerates and preserves permissions this
build does not recognize (logging a warning) instead of rejecting the
role. The regular Save() — and therefore the role API path — stays
strict, so unknown permissions cannot be introduced through user input.

Unrecognized permissions are kept on disk so they are not lost on a
later re-upgrade.

* MM-68830: assert save forwarding in role cache tests

Address review feedback: assert the underlying store's Save and
SavePreservingUnknownPermissions are actually invoked (the cache
invalidation defer fires regardless of forwarding), and check the
returned errors.

* MM-68830: address review feedback

- Shorten log message in validateForSave
- Rename validationRole -> roleCopy for clarity
- Trim doc comments to describe behavior only
- List all unknown permissions in IsValidWithoutId error
- Assert specific error type in storetest

* MM-68830: add Role.Clone and use it in validateForSave

* MM-68830: add tests for Role.Clone

* MM-68830: fix scheme id deep copy assertion in Role.Clone test
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
Co-authored-by: unified-ci-app[bot] <121569378+unified-ci-app[bot]@users.noreply.github.com>
* Tighten thread membership cleanup on team membership changes (#36764)

* Tighten thread membership cleanup on team membership changes

Ensure ThreadMembership rows are cleaned up when a user is removed
from or leaves a team, add a defense-in-depth filter on the thread
retrieval path so memberships referencing channels the user is no
longer a member of are excluded, and add a one-time migration to
clean up stale records on existing deployments.

https://mattermost.atlassian.net/browse/MM-69008

* Centralize per-channel membership removal and tighten tests

Extract the combined channel-member and thread-membership removal into
a shared helper used by both removeUserFromChannel and LeaveTeam, so
future code paths cannot revoke channel access without dropping the
dependent thread state. The channel-leave event is now logged after
the combined removal completes.

Also drop verbose test header comments and rename a test to a
behavior-focused name.

* Backfill channel members in thread storetest setups

The new ChannelMembers predicate on thread read queries filters out
ThreadMembership rows whose user has no ChannelMembers row for the
thread's channel. Several existing storetest setups bypassed the
normal write path and inserted threads/memberships without channel
members. Add the missing channel-member rows so the test data matches
the real-world invariant.

* Retrigger enterprise CI

Pick up enterprise merge e6953d4 (master into
MM-69008-thread-membership-team-leave) in the combined Enterprise
CI/tests lane, which pins the enterprise SHA at mattermost-side
dispatch time.

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* Retrigger CI

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es>
* Add a new ClusterReliableFallbackLength metric

This metric tracks the length of the cluster messages that are meant to
be sent via UDP but that result in a UDP datagram larger than the
maximum length allowed.

* make mocks

* Use 8 exponential buckets: from 32KiB to 4MiB
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
Automatic Merge
mohlec merged commit 1ef060a12c into slash/main 2026-06-30 20:22:09 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
slash/slash-server!5
No description provided.