Skip to content

[MM-69253] Restrict call leave to the view that created it and the widget#3884

Open
devinbinnie wants to merge 1 commit into
masterfrom
MM-69253
Open

[MM-69253] Restrict call leave to the view that created it and the widget#3884
devinbinnie wants to merge 1 commit into
masterfrom
MM-69253

Conversation

@devinbinnie

@devinbinnie devinbinnie commented Jul 6, 2026

Copy link
Copy Markdown
Member

Summary

The leaveCall desktop API did not validate the sender, so a server view could affect a call owned by a different server. This PR adds sender validation so the request is only honored when it comes from the server that owns the call.

Ticket Link

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

Release Note

Fixed an issue where other views could end an active call

Change Impact: 🟠 Medium

Regression Risk: This changes sender validation in the call-leave IPC flow, which is a user-facing call-control path and introduces authorization-like checks. The affected code is localized to the Calls widget window, and the new tests cover the main allowed/denied cases, but there is still risk around edge cases where sender identity or window state differs.

QA Recommendation: Recommend targeted manual QA for leaving calls from the widget and from the owning view, plus a quick negative check that other views cannot end the call.
Generated by CodeRabbitAI

@devinbinnie devinbinnie requested a review from edgarbellot July 6, 2026 17:50
@devinbinnie devinbinnie added the 3: Security Review Review requested from Security Team label Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The handleCallsLeave IPC handler now validates the event sender before closing the calls widget window, allowing closure only when the sender is the widget itself or matches the current main view's webContentsId. Corresponding unit tests were added, and e2e tests were updated to use desktopAPI.leaveCall() and poll for call-start outcomes.

Changes

Calls leave sender validation

Layer / File(s) Summary
Sender validation in handleCallsLeave handler
src/app/callsWidgetWindow.ts, src/app/callsWidgetWindow.test.js
handleCallsLeave accepts the IpcMainEvent and closes the widget only if the sender is the calls widget or matches mainView.webContentsId; new tests verify both allowed and disallowed sender cases.
E2E test updates for leave call and start outcome
e2e/specs/calls/calls_functionality.test.ts
closeCallsWidget now leaves calls via desktopAPI.leaveCall() in-widget instead of emitting CALLS_LEAVE_CALL; a new pollCallStartOutcome helper polls for either a Calls widget window or a relevant ephemeral post after starting a call.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Renderer
  participant IpcMain
  participant CallsWidgetWindow

  Renderer->>IpcMain: emit calls-leave-call event
  IpcMain->>CallsWidgetWindow: handleCallsLeave(event)
  alt sender is widget or mainView
    CallsWidgetWindow->>CallsWidgetWindow: close()
  else sender mismatch
    CallsWidgetWindow-->>IpcMain: log and return
  end
Loading

Possibly related PRs

  • mattermost/desktop#3862: Adds e2e Calls functionality tests that close the widget by emitting the CALLS_LEAVE_CALL IPC event, targeting the same leave-call/IPC path modified here.
🚥 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 matches the main change: restricting call-leave actions to the originating view and the widget.
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-69253

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

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
e2e/specs/calls/calls_functionality.test.ts (1)

28-36: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Loose substring match risks false positives.

text.toLowerCase().includes('call') will match any unrelated post containing "call" as a substring (e.g. "recall", "called"), which could cause pollCallStartOutcome to falsely report a 'post' outcome and mask a real failure in the security fix this test suite validates.

🔧 Suggested tighter match
             const text = last.querySelector('.post-message__text')?.textContent ?? '';
-            return text.toLowerCase().includes('call');
+            return /\bcall\b/i.test(text);
🤖 Prompt for 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.

In `@e2e/specs/calls/calls_functionality.test.ts` around lines 28 - 36, The post
detection in the calls functionality test is too broad because the
serverWin.evaluate check uses a loose substring match on the post text, which
can falsely treat unrelated posts as call-related. Tighten the matcher in the
newPostMentionsCall logic by checking for a more specific call-related phrase or
token boundary instead of any occurrence of "call", and keep the existing post
selection/idBefore guard so pollCallStartOutcome only returns 'post' for an
actual relevant call post.
🧹 Nitpick comments (1)
src/app/callsWidgetWindow.test.js (1)

366-397: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test case for the popout sender.

isCallsWidget also matches this.popOut?.webContents.id, but no test exercises handleCallsLeave with a popout sender id (only the main widget win id is covered). Consider adding a case that sets callsWidgetWindow.popOut = {webContents: {id: 'popoutID'}} and asserts close is called.

✅ Suggested additional test
         it('should close when the sender is the main view of the call', () => {
             callsWidgetWindow.handleCallsLeave({sender: {id: 'mainViewID'}});
             expect(callsWidgetWindow.close).toHaveBeenCalled();
         });
+
+        it('should close when the sender is the calls widget popout', () => {
+            callsWidgetWindow.popOut = {webContents: {id: 'popoutID'}};
+            callsWidgetWindow.handleCallsLeave({sender: {id: 'popoutID'}});
+            expect(callsWidgetWindow.close).toHaveBeenCalled();
+        });
     });
🤖 Prompt for 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.

In `@src/app/callsWidgetWindow.test.js` around lines 366 - 397, `handleCallsLeave`
is missing coverage for the popout sender path in `CallsWidgetWindow`. Add a
test in the `describe('handleCallsLeave')` block that sets
`callsWidgetWindow.popOut` with a `webContents.id` value, then calls
`handleCallsLeave` with a matching sender id and asserts `close` is invoked,
alongside the existing `win` and `mainView` cases.
🤖 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.

Outside diff comments:
In `@e2e/specs/calls/calls_functionality.test.ts`:
- Around line 28-36: The post detection in the calls functionality test is too
broad because the serverWin.evaluate check uses a loose substring match on the
post text, which can falsely treat unrelated posts as call-related. Tighten the
matcher in the newPostMentionsCall logic by checking for a more specific
call-related phrase or token boundary instead of any occurrence of "call", and
keep the existing post selection/idBefore guard so pollCallStartOutcome only
returns 'post' for an actual relevant call post.

---

Nitpick comments:
In `@src/app/callsWidgetWindow.test.js`:
- Around line 366-397: `handleCallsLeave` is missing coverage for the popout
sender path in `CallsWidgetWindow`. Add a test in the
`describe('handleCallsLeave')` block that sets `callsWidgetWindow.popOut` with a
`webContents.id` value, then calls `handleCallsLeave` with a matching sender id
and asserts `close` is invoked, alongside the existing `win` and `mainView`
cases.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2cc23e19-2bde-4cde-94a9-3fa059430755

📥 Commits

Reviewing files that changed from the base of the PR and between 70d3f50 and 93e046f.

📒 Files selected for processing (3)
  • e2e/specs/calls/calls_functionality.test.ts
  • src/app/callsWidgetWindow.test.js
  • src/app/callsWidgetWindow.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3: Security Review Review requested from Security Team release-note

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants