Skip to content

Validate region URLs before failing over to them - #1052

Merged
davidliu merged 2 commits into
mainfrom
security/validate-region-urls
Oct 30, 2026
Merged

davidliu merged 2 commits into
mainfrom
security/validate-region-urls

Conversation

@xianshijing-lk

Copy link
Copy Markdown
Contributor

Android port of livekit/rust-sdks#1601 and livekit/client-sdk-swift#1182.

What

isLKCloud() is checked against the server URL the application supplied, to decide whether region discovery applies at all. The URLs that came back were then used as they arrived — RegionInfo holds whatever string the response carried, and getNextBestRegionUrl() hands it straight to the connect path.

Those URLs are not just connected to, they are authenticated to: fetchRegionSettings sends the participant token as a bearer header, and Room calls connectionWarmer.fetch(regionUrl) on one before a connection attempt even starts. An entry should stand on its own merits rather than inherit trust from the configured server URL, and it should not be able to move that exchange onto cleartext.

How

  • New isTrustedRegionUrl: the URL must parse, name a .livekit.cloud / .livekit.run host, and use wss or https.
  • Applied in applyRegionSettings, which both ingestion paths now go through — the /settings/regions fetch and setServerReportedRegions, which takes the regions the server reports over signaling. Both were unvalidated before.
  • Rejected entries are logged rather than dropped silently.

Note on the testing override

Unlike isLKCloud(), the new check has no testing override. regionUrlProviderTesting exists so a mock server URL passes the cloud check; it must not also make the SDK accept an untrusted region. No release build can clear the new check.

The host match is case-insensitive, since URI.getHost() preserves whatever case the response used.

Tests

Six new tests: the predicate against encrypted cloud URLs, cleartext schemes, foreign hosts, suffix near-misses (not-livekit.cloud, livekit.cloud.example.com) and malformed input — plus one per ingestion path. Both ingestion tests verified to fail without the filter.

RegionUrlProviderTest passes. spotlessCheck clean, and detekt finding count is unchanged from the baseline on main (90).

Heads-up, unrelated to this PR

:livekit-android-test:testDebugUnitTest has 84 pre-existing failures on main, all in DataStreamsV2ReceiveTest. I confirmed the count is identical with and without this change, so they are not caused here — but main is currently red.

🤖 Generated with Claude Code

@changeset-bot

changeset-bot Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a7cd3ad

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
client-sdk-android Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

devin-ai-integration[bot]

This comment was marked as resolved.

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Diffuse output:

OLD: diffuse-source-file
NEW: livekit-android-sdk-release.aar

 AAR      │ old      │ new      │ diff     
──────────┼──────────┼──────────┼──────────
      jar │  3.1 MiB │  3.1 MiB │ +2.2 KiB 
 manifest │  1.5 KiB │  1.5 KiB │      0 B 
 lint-jar │ 12.7 KiB │ 12.7 KiB │      0 B 
    other │  2.4 KiB │  2.4 KiB │      0 B 
──────────┼──────────┼──────────┼──────────
    total │  3.1 MiB │  3.1 MiB │ +2.2 KiB 

 JAR     │ old   │ new   │ diff       
─────────┼───────┼───────┼────────────
 classes │  1771 │  1771 │  0 (+0 -0) 
 methods │ 22472 │ 22474 │ +2 (+2 -0) 
  fields │  5910 │  5910 │  0 (+0 -0)
AAR
 size    │ diff     │ path          
─────────┼──────────┼───────────────
 3.1 MiB │ +2.2 KiB │ ∆ classes.jar 
─────────┼──────────┼───────────────
 3.1 MiB │ +2.2 KiB │ (total)
JAR
METHODS:

   old   │ new   │ diff       
  ───────┼───────┼────────────
   22472 │ 22474 │ +2 (+2 -0) 
  
  + io.livekit.android.room.RegionUrlProvider applyRegionSettings(RegionSettings) → RegionSettings
  + io.livekit.android.room.RegionUrlProviderKt isTrustedRegionUrl(String) → boolean

`isLKCloud()` is checked against the server URL the application supplied,
to decide whether region discovery applies at all. The URLs that came
back were then used as they arrived: `RegionInfo` holds whatever string
the response carried, and `getNextBestRegionUrl()` hands it straight to
the connect path.

Those URLs are not just connected to, they are authenticated to --
`fetchRegionSettings` sends the participant token as a bearer header, and
`Room` calls `connectionWarmer.fetch(regionUrl)` on one before a
connection attempt even starts. An entry should stand on its own merits
rather than inherit trust from the configured server URL, and it should
not be able to move that exchange onto cleartext.

Add `isTrustedRegionUrl`: the URL must parse, name a `.livekit.cloud` or
`.livekit.run` host, and use `wss` or `https`.

Apply it in `applyRegionSettings`, which both ingestion paths now go
through -- the `/settings/regions` fetch and `setServerReportedRegions`,
which takes the regions the server reports over signaling. Both were
unvalidated before. Rejected entries are logged rather than dropped
silently.

Unlike `isLKCloud()`, this check has no testing override. The existing
`regionUrlProviderTesting` flag exists so a mock server URL passes the
cloud check; it must not also make the SDK accept an untrusted region,
and no release build can clear the new check.

The host match is case-insensitive, since `URI.getHost()` preserves the
case the response used.

Six tests: the predicate against encrypted cloud URLs, cleartext schemes,
foreign hosts, suffix near-misses and malformed input, plus one test per
ingestion path. Both ingestion tests fail without the filter.
@davidliu
davidliu force-pushed the security/validate-region-urls branch from 9a9aff7 to 44e518f Compare October 30, 2026 09:51
@davidliu
davidliu merged commit cc34346 into main Oct 30, 2026
5 checks passed
@davidliu
davidliu deleted the security/validate-region-urls branch October 30, 2026 10:27
@davidliu davidliu mentioned this pull request Oct 30, 2026
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.

2 participants