Skip to content

MM-67542: stop leaking the server subpath in Boards Desktop navigation - #244

Merged
jgheithcock merged 3 commits into
mainfrom
MM-67542-boards-desktop-subpath
Aug 11, 2026
Merged

MM-67542: stop leaking the server subpath in Boards Desktop navigation#244
jgheithcock merged 3 commits into
mainfrom
MM-67542-boards-desktop-subpath

Conversation

@jgheithcock

@jgheithcock jgheithcock commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Boards sent the Desktop App a browser-history path built from
`${frontendBaseURL}${path}`, and frontendBaseURL is
<serverSubpath>/boards — so every navigation leaked the server subpath.
The core web app sends subpath-relative paths (it strips the basename itself);
to compensate for Boards, the Desktop App added a workaround that strips the
subpath from paths Boards pushes. That workaround misfires when a team's name
collides with the subpath, which is currently blocking a Desktop App fix.

This PR makes Boards send a subpath-relative `/boards${path}` instead,
matching the core web app contract, so the Desktop App no longer has to clean
the path.

What changed

  • Send /boards${path} (subpath-relative) to the Desktop App instead of
    ${frontendBaseURL}${path}.
  • Extracted the desktop navigation history logic out of the side-effect-heavy
    index.tsx into a dedicated, unit-testable webapp/src/desktopHistory.ts
    module, and factored the repeated /boards literal into a shared
    boardsRouteBase constant used by both the send and receive sides.
  • Added webapp/src/desktopHistory.test.ts with a regression test asserting the
    subpath is never included in the path sent to Desktop (simulated
    /company/boards subpath deployment), plus receive-side and postMessage
    fallback coverage.

Browser routing is unchanged: the history basename still includes the subpath,
so in-app URLs resolve exactly as before. No behavioral change for
non-subpath deployments
— there, old and new produce byte-identical paths.

Compatibility & rollout

The only case where behavior differs is subpath deployments:

Desktop Boards Result
Old (cleans subpath) Old (leaks subpath) Works today
Old (cleans subpath) New (this PR) Works — cleaning is a no-op
New (no cleaning) Old (leaks subpath) Broken — must be avoided
New (no cleaning) New (this PR) Works (the goal)

Safe sequencing: this Boards fix ships first; the Desktop App removes its
path-cleaning once no supported/ESR builds still ship an affected Boards. This
PR intentionally does not add a runtime capability signal — Desktop can gate
hack-removal on server/plugin version if it wants to decouple from the ESR tail.

Ticket Link

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

Related Pull Requests

  • Requires a coordinated follow-up in the Desktop App to remove the subpath
    path-cleaning workaround once supported Boards versions include this fix.
    (link the Desktop PR here once it exists)

Screenshots

N/A — no visual changes.

Test Plan

  • Automated (webapp): npm run test (882 tests / 487 snapshots pass; new
    desktopHistory suite 7/7), npm run check-types (tsc) clean,
    npm run check (eslint + stylelint) clean.
  • Manual: Desktop App with a server subpath configured — deep links, team
    switching, board/card navigation all work.
  • Manual: Desktop App without a subpath — regression check, behavior
    identical to today.
  • Manual: regular browser under a subpath — navigation unaffected.

Release Note

Fixed an issue where Boards navigation in the Desktop App included the server
subpath, which could cause navigation problems on servers configured with a
subpath.

Change Impact: 🟡 Medium

Regression Risk: Navigation behavior changes across web and desktop history handling. Tests cover subpaths, fallback handling, traversal rejection, and desktop-only paths. The flow is user-facing and spans multiple modules.

QA Recommendation: Run targeted manual QA for desktop and subpath navigation. No broad manual regression pass is required.

Generated by CodeRabbitAI

Boards sent `${frontendBaseURL}${path}` (which includes the server subpath)
to the Desktop App on every navigation, unlike the core web app which sends
subpath-relative paths. The Desktop App added a workaround to strip that
subpath, and that workaround misfires for teams whose name collides with the
subpath, blocking a Desktop App fix.

Send a subpath-relative `/boards${path}` instead, matching the core web app
contract, so the Desktop App no longer needs to clean the path. Browser
routing keeps the subpath-aware history basename, so there is no change for
non-subpath deployments.

The desktop navigation history logic is extracted into a dedicated,
unit-tested `desktopHistory` module, with a regression test asserting the
subpath is never included in the path sent to the Desktop App.
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 66498004-823e-48de-a6c8-79cef11e26b8

📥 Commits

Reviewing files that changed from the base of the PR and between ef548c3 and 2eda64d.

📒 Files selected for processing (2)
  • webapp/src/desktopHistory.test.ts
  • webapp/src/desktopHistory.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • webapp/src/desktopHistory.ts
  • webapp/src/desktopHistory.test.ts

📝 Walkthrough

Walkthrough

The change adds desktop-aware history handling for the Boards route. Navigation uses the Desktop API or a same-origin postMessage fallback. Incoming routes are validated and normalized. Application initialization now uses the shared customHistory helper.

Changes

Desktop history integration

Layer / File(s) Summary
Desktop history behavior
webapp/src/desktopHistory.ts
The module defines the /boards route base, forwards navigation through the Desktop API or postMessage, validates incoming paths, and creates basename-aware history.
Application wiring and behavior coverage
webapp/src/index.tsx, webapp/src/desktopHistory.test.ts
The entry point uses customHistory. Tests cover route normalization, path traversal rejection, message validation, navigation forwarding, subpath handling, and non-desktop behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BrowserHistory
  participant DesktopHistory
  participant DesktopAPI
  participant Window
  BrowserHistory->>DesktopHistory: push(path)
  DesktopHistory->>DesktopAPI: sendBrowserHistoryPush(path)
  DesktopHistory->>Window: postMessage(path) fallback
  Window->>DesktopHistory: receive same-origin path
  DesktopHistory->>BrowserHistory: replace normalized path
Loading

Suggested reviewers: avasconcelos114, devinbinnie

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: preventing server subpath leakage in Boards Desktop navigation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch MM-67542-boards-desktop-subpath

Comment @coderabbitai help to get the list of available commands.

@jgheithcock jgheithcock added the 2: Dev Review Requires review by a core committer label Aug 4, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@webapp/src/desktopHistory.ts`:
- Around line 47-52: Update the message listener around handleBrowserHistoryPush
to safely access event.data via optional chaining and invoke the handler only
when the extracted pathName is a string; otherwise ignore the message. Add a
fallback-message test covering a null event.data payload.
- Around line 31-37: Update handleBrowserHistoryPush to accept only the exact
boardsRouteBase path or paths beginning with boardsRouteBase followed by “/”,
rejecting routes such as `/boards-legacy/...`; remove the confirmed prefix using
slice rather than replace, and add a regression test covering the rejected
legacy route.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9b2f9fca-91a6-4e46-86be-d2aef7ffc1d4

📥 Commits

Reviewing files that changed from the base of the PR and between 9c099ba and bb75cca.

📒 Files selected for processing (3)
  • webapp/src/desktopHistory.test.ts
  • webapp/src/desktopHistory.ts
  • webapp/src/index.tsx

Comment thread webapp/src/desktopHistory.ts Outdated
Comment thread webapp/src/desktopHistory.ts Outdated
- Require a `/boards` route boundary so paths like `/boards-legacy/...` are
  not treated as boards paths, and strip the prefix with `slice` (mapping the
  bare `/boards` to `/`).
- Validate the fallback `message` payload with optional chaining and a string
  type guard before navigating, so a malformed same-origin message can't throw.
- Add regression tests for the rejected legacy prefix, the bare boards route,
  and the message handler (valid, cross-origin, null payload, non-string path).
Comment thread webapp/src/desktopHistory.ts Fixed
Comment thread webapp/src/desktopHistory.ts Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@webapp/src/desktopHistory.ts`:
- Around line 31-40: Update handleBrowserHistoryPush to canonicalize pathName
before validating the boardsRouteBase boundary, preventing dot-segment inputs
such as `/boards/../admin` from reaching history.replace. Reject paths
containing traversal segments or whose canonical form falls outside `/boards`,
while preserving valid boards-root and nested routes; add a regression test
covering this input.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0c93255d-5597-49dd-9870-fc445d7340b5

📥 Commits

Reviewing files that changed from the base of the PR and between bb75cca and ef548c3.

📒 Files selected for processing (2)
  • webapp/src/desktopHistory.test.ts
  • webapp/src/desktopHistory.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • webapp/src/desktopHistory.test.ts

Comment thread webapp/src/desktopHistory.ts
…bbit review)

Canonicalize the incoming path with the URL parser before re-checking the
`/boards` boundary, so dot-segment inputs such as `/boards/../admin` (and their
percent-encoded forms) resolve to a path outside boards and are rejected rather
than reaching `history.replace`. The query string and hash are preserved.

Adds regression tests for `/boards/../admin`, `/boards/%2e%2e/admin`, and
query/hash preservation.

@devinbinnie devinbinnie left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Just as a sanity check, have we manually tested the four cases highlighted in the PR description? I can provide a build with the cleaning removed if need be.

@jgheithcock

Copy link
Copy Markdown
Contributor Author

LGTM. Just as a sanity check, have we manually tested the four cases highlighted in the PR description? I can provide a build with the cleaning removed if need be.

@devinbinnie - I have not, that would be great, thanks.

@devinbinnie

Copy link
Copy Markdown
Member

LGTM. Just as a sanity check, have we manually tested the four cases highlighted in the PR description? I can provide a build with the cleaning removed if need be.

@devinbinnie - I have not, that would be great, thanks.

@jgheithcock Here you go: https://github.com/mattermost/desktop/actions/runs/30936995306/artifacts/8903794993

@avasconcelos114 avasconcelos114 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! nice to see this custom history separated into its own file with a dedicated test suite

@jgheithcock

Copy link
Copy Markdown
Contributor Author

Manual testing done

(With a built version of the Desktop app with the fix in):
Set up subpath to localhost:8065/boardstest

  1. Open Boards from the product switcher. → path logged should be /boards or /boards/team/ (no /boardstest).
  2. Open a board. → Navigating Boards to /boards/team//, and the board actually renders.
  3. Open a card, then a view. → paths extend to ...//, all /boards-rooted; the correct card/view opens.
  4. Switch teams. → Navigating Boards to /boards/team/; lands on that team's boards.
  5. Back/forward + reload the Boards page. → returns to the same board, no redirect to an /error?type=team_not_found page caused by Boards.
  6. Switch Boards → a channel → back to Boards. → Boards reopens on the right board.

Remove subpath (this takes a fair bit of time to clear all the caches, restart servers etc)
Tests above continue to work.

@jgheithcock
jgheithcock merged commit 8cf5a98 into main Aug 11, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2: Dev Review Requires review by a core committer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants