Skip to content

fix(llc): sanitize SystemEnvironment updates against the SDK-owned baseline - #163

Open
VelikovPetar wants to merge 2 commits into
mainfrom
petarvelikov/flu-727-sanitize-x-stream-client-in-stream_core-to-prevent-customers
Open

fix(llc): sanitize SystemEnvironment updates against the SDK-owned baseline#163
VelikovPetar wants to merge 2 commits into
mainfrom
petarvelikov/flu-727-sanitize-x-stream-client-in-stream_core-to-prevent-customers

Conversation

@VelikovPetar

Copy link
Copy Markdown
Contributor

Submit a pull request

Linear: FLU-727
Github Issue: #

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

Ports the X-Stream-Client sanitization from FLU-565 / GetStream/stream-chat-flutter#2790 into stream_core.

Why

SystemEnvironmentManager.updateEnvironment assigned a consumer-supplied SystemEnvironment wholesale, and HeadersInterceptor re-reads the value on every request. stream_feeds exposes that path publicly as StreamFeedsClient.updateSystemEnvironment, so any integrator could rewrite the fields Stream uses to identify the SDK — sdkName, sdkIdentifier, sdkVersion and osName.

Those fields drive SDK-usage analytics and support triage. An integrator reporting stream-chat-android v99.0.0 from a Dart Feeds app silently corrupts both, and spoofed traffic is indistinguishable from real traffic after the fact.

Chat fixed this in its own copy of the class. stream_core is a generalized fork of Chat's pre-fix version — SystemEnvironment is field-for-field identical and xStreamClientHeader is character-identical — so the same hole is here, and every SDK built on core inherits it.

What

Core can't lock these to internal constants the way Chat does, because sdkName and sdkVersion differ per product (stream-chat, stream-feeds, stream-video). Instead the environment passed to the constructor is treated as the SDK-owned baseline — only the product SDK constructs the manager; consumers only ever reach updateEnvironment — and every later update is sanitized against it:

Field Behavior
sdkName, sdkVersion, osName Keep their constructor values; custom values ignored
sdkIdentifier Only the dartflutter promotion is accepted
appName, appVersion, osVersion, deviceModel Pass through as before

The SDK-owned values are snapshotted as scalars at construction rather than held as a SystemEnvironment reference, since the type isn't final and a subtype could otherwise return a different value on every getter read.

The promotion path has to stay. The package that promotes is never the package that constructs the manager: stream_chat_flutter_core promotes from initState on a client the app already built, so updateEnvironment is its only channel, and a future stream_feeds_flutter would be identical. Locking sdkIdentifier outright would silently regress every Flutter app to -dart-.

Scope of impact

stream_feeds is the only consumer of the manager today — Video doesn't depend on stream_core, and stream_core_flutter depends on it but never uses it. Nothing inside this repo constructs it except HeadersInterceptor.

Non-breaking: no signature changes, so ^0.4.0 admits it. It is a runtime behavior change to a public method, so flagging the judgment call here rather than burying it — happy to move the entry under a breaking heading if reviewers disagree.

Testing

22 new tests in packages/stream_core/test/api/, covering each sanitization branch plus the promotion ratchet, unrecognized identifiers, and baseline drift. Also ports Chat's 9 header-format tests, since xStreamClientHeader is public API of this package and had zero coverage.

Verified beyond green tests:

  • Mutation-checked. Reverting updateEnvironment to the old wholesale assignment fails 6 tests; removing only the unrecognized-identifier guard fails exactly the 1 test written for it.
  • Live HTTP check against stream_feeds pointed at this branch, observing the header a local server actually received:
    baseline        : stream-feeds-dart-v0.5.1|os=macos
    enrich + spoof  : stream-feeds-dart-v0.5.1|app=DemoApp|app_version=2.3.4|os=macos 15.1|device_model=MacBookPro18,3
    
    The second request passed sdkName: 'stream-chat-android', sdkIdentifier: 'android', sdkVersion: '99.9.9', osName: 'spoofed-os' — none of it reached the wire, while the legitimate app/device enrichment did.
  • Full stream_core suite (364) and full stream_feeds suite (404) pass against this branch.

Screenshots / Videos

N/A — no UI surface.

@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 43bec522-531f-4afd-8fa0-7e5cf919d389


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.

@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.64%. Comparing base (f4d6732) to head (fad0df2).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #163      +/-   ##
==========================================
+ Coverage   60.32%   60.64%   +0.31%     
==========================================
  Files         192      192              
  Lines        7834     7857      +23     
==========================================
+ Hits         4726     4765      +39     
+ Misses       3108     3092      -16     

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

@VelikovPetar
VelikovPetar marked this pull request as ready for review August 24, 2026 16:09
@VelikovPetar
VelikovPetar requested a review from a team as a code owner August 24, 2026 16: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.

2 participants