Skip to content

chore: back-merge 4.15.3 hotfix into master#3408

Merged
jeanfbrito merged 8 commits into
masterfrom
hotfix/4.15.3
Jul 10, 2026
Merged

chore: back-merge 4.15.3 hotfix into master#3408
jeanfbrito merged 8 commits into
masterfrom
hotfix/4.15.3

Conversation

@jeanfbrito

@jeanfbrito jeanfbrito commented Jul 10, 2026

Copy link
Copy Markdown
Member

The exception scope check introduced in #3323 requires
exceptions.uniqueId to equal server.uniqueID, but the server-signed
validation path returns before getUniqueId() ever runs and /api/info
does not include a uniqueId field, so the local uniqueID is missing on
that path and uniqueId-scoped exceptions were always disqualified.

Resolve the workspace uniqueID from the server before validating when
the payload carries a uniqueId-scoped exceptions block, persist it via
WEBVIEW_SERVER_UNIQUE_ID_UPDATED so subsequent runs (including offline
cache validation) keep working, and keep rejecting when the fetched
value does not match. Also compare exceptions.domain case-insensitively
(DNS names are case-insensitive per RFC 4343).
Audited the supported-versions subsystem end to end after the
uniqueId-scope escalation and fixed the confirmed defects:

- Wrap the cache/builtin fallback validation in a helper with try/catch
  so a malformed cached payload can no longer reject the update before
  the error state is dispatched, which left fetchState stuck at
  'loading' and suppressed the UnsupportedServer block gate.
- Catch rejections at all four fire-and-forget validation call sites.
- Fall back to the persisted workspace uniqueID when the fresh fetch
  fails so the cloud lookup is not skipped for previously-known servers.
- Switch uniqueID/version/gitCommitHash reducer cases from upsert to
  update so a late identity dispatch cannot resurrect a server deleted
  while a validation was in flight.
- Guard getExpirationMessageTranslated against payloads whose i18n
  dictionary lacks both the user language and 'en'; the missing guard
  crashed the async check and silently suppressed the expiring-
  workspace warning dialog.
- Revalidate all servers on powerMonitor 'resume' (the window 'online'
  event does not fire when waking with the same network connected).
- Use the real currentView state in SupportedVersionDialog's effect
  dependencies; the previous dependency was the imported reducer
  function, which never changes, so the dialog never re-checked on
  view switches.
- Add a 'Check again' button to the unsupported-workspace screen so
  users can re-trigger validation without restarting the app.
- Fix logRequestError printing a literal ${description}.

Also relax the exception scope check for UNKNOWN local identity: an
unfetchable workspace uniqueID (e.g. settings.public restricted by
enterprise API ACLs) no longer disqualifies a domain-matched exception;
a PROVEN uniqueID mismatch still rejects. The gate is client-side UX
enforcement rather than a security boundary, and wrongly blocking a
legitimate workspace is the worse failure mode.
Server, cloud, and cache supported-versions payloads are fetched from or
for the server being validated, so their exceptions block cannot belong
to another tenant. For these sources a domain/uniqueId scope mismatch is
now logged as a diagnostic warning instead of disqualifying the
exception. The bundled builtin payload is the only source that could
carry another deployment's exceptions and keeps the strict scope
requirement.

This removes the remaining paths where a valid, unexpired exception
could be rejected over unverifiable or drifted identity data (restricted
settings.public API, rotated workspace uniqueID, stale persisted
state).
A missing or unparseable enforcementStartDate previously produced an
Invalid Date that failed the future-date comparison and fell through to
the unsupported verdict — blocking the workspace based on incomplete
payload data. Blocking now requires a valid, past enforcement date:
uncertain data keeps the server usable until a payload with a valid
enforcement date proves enforcement is active.
…nfo contract; try both fallback sources

The desktop's ServerInfo type declared fields the server never returns
to unauthenticated callers (verified against the server source,
apps/meteor/server/api/lib/getServerInfo.ts):

- Remove the fictional uniqueId field. No server version includes it in
  /api/info; the workspace uniqueID comes from settings.public and is
  resolved on demand by withExceptionScopeUniqueId. Remove the dead
  serverInfoResult.uniqueId fallback in updateSupportedVersionsData.
- Mark build, marketplaceApiVersion, and commit optional and document
  that they only appear for authenticated view-statistics callers; the
  desktop always calls /api/info unauthenticated. The sha-exception
  path uses the persisted server.gitCommitHash pushed by the injected
  script instead.
- Rebuild the default test fixture to mirror the real unauthenticated
  wire shape (trimmed major.minor version, no uniqueId/commit/info) so
  the suite can no longer validate assumptions against fields real
  servers do not send — the gap that let the exception-scope defect
  pass CI. Switch beforeEach to jest.resetAllMocks(), fixing latent
  cross-test mock leakage the honest fixtures surfaced.

Also rework the offline fallback to try BOTH remaining sources instead
of picking one by timestamp: the bundled builtin payload never carries
tenant exceptions, so preferring a fresher builtin over a cache that
holds the tenant's exception blocked a workspace that should stay
usable. Candidates are checked in freshness order and the first source
that supports the server wins; blocking requires every available source
to fail. This keeps the stale-cache rescue from #3388 while never
losing a cached exception to it.
Asserts the unauthenticated /api/info response shape against a real
server (open.rocket.chat): trimmed major.minor version, no uniqueId,
no commit/info blocks, string minimumClientVersions, JWT-shaped
supportedVersions.signed. Skips only on genuine network failure or
SKIP_CONTRACT_TESTS=1, so contract drift between the desktop and the
server cannot hide behind mocks that agree with themselves.
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@jeanfbrito, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a07b33b6-fec6-4820-954b-49d39d245f66

📥 Commits

Reviewing files that changed from the base of the PR and between 5597070 and 885ad12.

📒 Files selected for processing (14)
  • package.json
  • src/i18n/en.i18n.json
  • src/screenSharing/screenSharePicker.tsx
  • src/servers/reducers.ts
  • src/servers/reducers/__tests__/servers.spec.ts
  • src/servers/supportedVersions/apiContract.main.spec.ts
  • src/servers/supportedVersions/main.main.spec.ts
  • src/servers/supportedVersions/main.ts
  • src/servers/supportedVersions/types.ts
  • src/ui/components/ServersView/ServerPane.tsx
  • src/ui/components/ServersView/UnsupportedServer.spec.tsx
  • src/ui/components/ServersView/UnsupportedServer.tsx
  • src/ui/components/SupportedVersionDialog/index.spec.tsx
  • src/ui/components/SupportedVersionDialog/index.tsx

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@jeanfbrito jeanfbrito merged commit da45deb into master Jul 10, 2026
8 checks passed
@jeanfbrito jeanfbrito deleted the hotfix/4.15.3 branch July 10, 2026 14:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant