Skip to content

feat(llc): Sanitize X-Stream-Client to prevent customers overriding internal values#2790

Open
VelikovPetar wants to merge 3 commits into
masterfrom
feature/sanitize_x_stream_header_client
Open

feat(llc): Sanitize X-Stream-Client to prevent customers overriding internal values#2790
VelikovPetar wants to merge 3 commits into
masterfrom
feature/sanitize_x_stream_header_client

Conversation

@VelikovPetar

@VelikovPetar VelikovPetar commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Submit a pull request

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

Sanitizes the X-Stream-Client header so SDK consumers can't spoof the identifying fields:

  • SystemEnvironmentManager.updateEnvironment now passes the incoming SystemEnvironment through a _sanitize step that locks sdkName, sdkVersion, and osName to internal
    defaults while letting appName, appVersion, osVersion, and deviceModel pass through.
  • sdkIdentifier is gated by a new _SdkIdentifier extension type with a precedence ranking — only a dart → flutter promotion is accepted; demotions and unknown values fall
    back to the current identifier.
  • The constructor now routes a user-supplied environment through the same updateEnvironment sanitization path instead of trusting it wholesale.
  • WebSocket query-string assembly reorders the map so X-Stream-Client is appended after queryParameters, preventing callers from overwriting it.

Summary by CodeRabbit

  • Bug Fixes
    • System environment values are now normalized before being used, helping ensure consistent client metadata and headers.
    • Only supported SDK identifier changes are applied; unsupported or backward changes are ignored.
    • WebSocket connections now prioritize the client’s own identifier when query parameters overlap.

VelikovPetar and others added 3 commits June 25, 2026 12:27
Replace the inline string comparison in `_sanitize` with a private
`_SdkIdentifier` extension type that owns the known identifier constants
and exposes a `precedence` getter. The promotion rule becomes a single
precedence comparison and unknown identifiers cleanly fall back to the
current value via the lowest precedence.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

SystemEnvironmentManager gains a _sanitize method and _SdkIdentifier extension type to enforce that sdkName, sdkVersion, and osName always hold internal defaults, while sdkIdentifier only allows a one-way dartflutter promotion. App-level fields pass through unchanged. The WebSocket _buildUri fixes query-parameter insertion order, and docs/tests are updated accordingly.

Changes

SystemEnvironment Sanitization

Layer / File(s) Summary
Sanitization logic and _SdkIdentifier precedence
packages/stream_chat/lib/src/core/http/system_environment_manager.dart
Constructor initializes with fixed defaults and delegates to updateEnvironment; updateEnvironment calls new _sanitize helper that forces sdkName, sdkVersion, osName to internal values and restricts sdkIdentifier to one-way dartflutter promotion via _SdkIdentifier extension type.
Tests, docs, WebSocket fix, and changelog
packages/stream_chat/test/src/core/http/system_environment_manager_test.dart, packages/stream_chat/lib/src/client/client.dart, packages/stream_chat/lib/src/ws/websocket.dart, packages/stream_chat/CHANGELOG.md
Tests replaced to cover sanitization on construction and updateEnvironment including promotion/demotion rules; updateSystemEnvironment doc comment updated with field-by-field spec; _buildUri spreads queryParameters before X-Stream-Client so the latter wins on collision; changelog entry added.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Suggested reviewers

  • xsahil03x

Poem

🐇 A rabbit guards the SDK fields with care,
No flutter demoted, no dart sneaking there,
_sanitize hops in, sets defaults just right,
App names pass through — everything's tight.
The query params ordered, the changelog in bloom,
Clean environment headers fill every room! 🌸

🚥 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 describes the main change: sanitizing X-Stream-Client so callers cannot override internal values.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/sanitize_x_stream_header_client

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.

@VelikovPetar VelikovPetar marked this pull request as ready for review June 30, 2026 07:22

@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 `@packages/stream_chat/lib/src/client/client.dart`:
- Around line 169-193: The `SystemEnvironment` docs in
`Client.updateSystemEnvironment` are inconsistent with
`SystemEnvironmentManager._sanitize`: `sdkIdentifier` is not fully immutable
because `dart` may be promoted to `flutter`. Update the documentation around
`updateSystemEnvironment` and the example to reflect the actual sanitizer
behavior, and make sure the `sdkIdentifier` bullet explains the allowed
normalization instead of claiming it is always preserved as-is.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9fdb2daa-7743-4e04-a285-c7262ab608ab

📥 Commits

Reviewing files that changed from the base of the PR and between eb0a0ec and 81f6d7e.

📒 Files selected for processing (5)
  • packages/stream_chat/CHANGELOG.md
  • packages/stream_chat/lib/src/client/client.dart
  • packages/stream_chat/lib/src/core/http/system_environment_manager.dart
  • packages/stream_chat/lib/src/ws/websocket.dart
  • packages/stream_chat/test/src/core/http/system_environment_manager_test.dart

Comment on lines +169 to 193
/// The passed [environment] is sanitized before being applied:
///
/// Overridable fields (passed through as-is):
/// - [SystemEnvironment.appName]
/// - [SystemEnvironment.appVersion]
/// - [SystemEnvironment.osVersion]
/// - [SystemEnvironment.deviceModel]
///
/// Immutable fields (custom values are ignored, internal defaults are
/// preserved):
/// - [SystemEnvironment.sdkName]
/// - [SystemEnvironment.sdkIdentifier]
/// - [SystemEnvironment.sdkVersion]
/// - [SystemEnvironment.osName]
///
/// Example:
/// ```dart
/// client.updateSystemEnvironment(
/// SystemEnvironment(
/// name: 'my_app',
/// version: '1.0.0',
/// sdkName: 'stream-chat',
/// sdkIdentifier: 'dart',
/// sdkVersion: StreamChatClient.packageVersion,
/// appName: 'my_app',
/// appVersion: '1.0.0',
/// ),

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the sdkIdentifier docs to match the sanitizer.

Line 180 documents sdkIdentifier as immutable, but SystemEnvironmentManager._sanitize accepts a dartflutter promotion and the new tests assert that behavior. The example also passes sdkName/sdkIdentifier/sdkVersion, which suggests those caller values matter when they are sanitized away or constrained.

Proposed doc update
-  /// Immutable fields (custom values are ignored, internal defaults are
-  /// preserved):
+  /// Internally controlled fields:
   /// - [SystemEnvironment.sdkName]
-  /// - [SystemEnvironment.sdkIdentifier]
   /// - [SystemEnvironment.sdkVersion]
   /// - [SystemEnvironment.osName]
+  ///
+  /// [SystemEnvironment.sdkIdentifier] keeps its current value unless the
+  /// update promotes it from `dart` to `flutter`.
@@
   /// client.updateSystemEnvironment(
   ///   SystemEnvironment(
-  ///     sdkName: 'stream-chat',
-  ///     sdkIdentifier: 'dart',
-  ///     sdkVersion: StreamChatClient.packageVersion,
   ///     appName: 'my_app',
   ///     appVersion: '1.0.0',
   ///   ),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// The passed [environment] is sanitized before being applied:
///
/// Overridable fields (passed through as-is):
/// - [SystemEnvironment.appName]
/// - [SystemEnvironment.appVersion]
/// - [SystemEnvironment.osVersion]
/// - [SystemEnvironment.deviceModel]
///
/// Immutable fields (custom values are ignored, internal defaults are
/// preserved):
/// - [SystemEnvironment.sdkName]
/// - [SystemEnvironment.sdkIdentifier]
/// - [SystemEnvironment.sdkVersion]
/// - [SystemEnvironment.osName]
///
/// Example:
/// ```dart
/// client.updateSystemEnvironment(
/// SystemEnvironment(
/// name: 'my_app',
/// version: '1.0.0',
/// sdkName: 'stream-chat',
/// sdkIdentifier: 'dart',
/// sdkVersion: StreamChatClient.packageVersion,
/// appName: 'my_app',
/// appVersion: '1.0.0',
/// ),
/// The passed [environment] is sanitized before being applied:
///
/// Overridable fields (passed through as-is):
/// - [SystemEnvironment.appName]
/// - [SystemEnvironment.appVersion]
/// - [SystemEnvironment.osVersion]
/// - [SystemEnvironment.deviceModel]
///
/// Internally controlled fields:
/// - [SystemEnvironment.sdkName]
/// - [SystemEnvironment.sdkVersion]
/// - [SystemEnvironment.osName]
///
/// [SystemEnvironment.sdkIdentifier] keeps its current value unless the
/// update promotes it from `dart` to `flutter`.
///
/// Example:
///
🤖 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 `@packages/stream_chat/lib/src/client/client.dart` around lines 169 - 193, The
`SystemEnvironment` docs in `Client.updateSystemEnvironment` are inconsistent
with `SystemEnvironmentManager._sanitize`: `sdkIdentifier` is not fully
immutable because `dart` may be promoted to `flutter`. Update the documentation
around `updateSystemEnvironment` and the example to reflect the actual sanitizer
behavior, and make sure the `sdkIdentifier` bullet explains the allowed
normalization instead of claiming it is always preserved as-is.

@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.73684% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 69.61%. Comparing base (eb0a0ec) to head (81f6d7e).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
packages/stream_chat/lib/src/ws/websocket.dart 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2790      +/-   ##
==========================================
+ Coverage   69.59%   69.61%   +0.01%     
==========================================
  Files         426      426              
  Lines       25676    25691      +15     
==========================================
+ Hits        17869    17884      +15     
  Misses       7807     7807              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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