Is CSFloat Market Checker safe?

High risk

CSFloat is high risk. CSFloat Market Checker sends every visited Steam profile's SteamID64 to reverse.watch, a third party apart from CSFloat, no consent or disclosure. It reads the ID from page globals or the URL and fires a GET, e.g. /v1/users/{steamid64}.…

CSFloat Inc.v5.17.0Chrome Web Store
75Risk
Who publishes it

CSFloat Inc. - no other listings under this identity, 7 shared hostnames

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
CSFloat Inc.
Declared legal entity
CSFloat Inc.
Registered address
1111B S Governors Ave #6886, Dover, DE 19904-6903, US
Registered contact
CSFloat Inc.

Shared hosts - 7 hostnames

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

gateway.floatdb.com
Also called by 1 other listing: CSFloat Market Checker
skincraft.gg
Also called by 1 other listing: CSFloat Market Checker
avatars.cloudflare.steamstatic.com
Also called by 2 other listings
blog.csfloat.com
Also called by 2 other listings
notary.csfloat.com
Also called by 2 other listings, including CSFloat Market Checker
reverse.watch
Also called by 2 other listings, including CSFloat Market Checker
kushagra.dev
Also called by 3 other listings, including Fast IPTV Player - HLS M3U M3U8 EPG & VOD Streaming

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Steam Profile IDs Sent to reverse.watch on Every Profile Visit

CSFloat Market Checker sends every visited Steam profile's SteamID64 to reverse.watch, a third party apart from CSFloat, no consent or disclosure.

It reads the ID from page globals or the URL and fires a GET, e.g. /v1/users/{steamid64}.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You open any Steam user profile page while CSFloat Market Checker is active.

The extension did this

The extension reads the SteamID64 from the page and sends it to reverse.watch without prompting you.

The request fires on every profile load, for every Steam account you view, whether or not that account has any reversal history.

Data transmitted to reverse.watch on each profile visit
  • Viewed account's Steam ID
    76561197968052866

    A numeric ID that uniquely identifies the Steam account viewed. Sent to reverse.watch in the URL, letting it log lookups by session.

Captured request
GEThttps://reverse.watch/api/v1/users/76561197968052866

No response body captured, the reverse.watch host was unreachable from the sandboxed analysis network. The disclosure itself does not depend on server reachability: the outbound URL was already fully formed and dispatched from the extension with the viewed profile's SteamID64 embedded in the path, so the identifier had already left the browser before any response could arrive.

The code that does this

ReversalStatus component fires on every Steam profile page load

Readable version

Readable equivalent — getSteamId() and connectedCallback()

src/lib/page_scripts/profile.js (deobfuscated)
getSteamId() {    if (defined(typeof g_rgProfileData) && g_rgProfileData) {        return g_rgProfileData.steamid;    }    const match = window.location.pathname.match(/^\/profiles\/(\d+)/);    if (match) {        return match[1];    }}async connectedCallback() {    super.connectedCallback();    try {        const steamId = this.getSteamId();        if (!steamId) {            console.error('failed to get steam id');            return;        }        this.reversalStatus = await gReversalFetcher.fetch({ steam_id64: steamId });    } catch (e) {        console.error('failed to fetch reversal status', e);    }}

Readable equivalent — background bridge handler

src/lib/page_scripts/profile.js (deobfuscated)
const FetchReversalStatus = new SimpleHandler(    RequestType.FETCH_REVERSAL_STATUS,    async (req) => {        const resp = await fetch(`${environment.reverse_watch_base_api_url}/v1/users/${req.steam_id64}`);        const data = await resp.json();        if (!resp.ok) {            throw Error(data.message ?? 'unknown error');        }        return data;    });
External service receiving Steam profile identifiers
    • reverse.watch

    Receives the SteamID64 of every profile visited. Operated separately from CSFloat; a 'powered by reverse.watch' attribution appears on a reversal. Not disclosed in the listing.

Steam Trade Offer Data Sent to csfloat.com Every 3 Minutes

The same 3-min alarm reading your Steam trade history also calls Valve's Trade Offers API for sent offers (IDs, asset IDs, state, counterparty ID64), forwarding matches to pending CSFloat trades, needing the steampowered.com permission.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

You grant the extension's optional steampowered.com permission, offered during CSFloat trade-tracking setup, and have a Steam trade offer pending against a CSFloat listing.

No further action is needed after that; the alarm runs on its own schedule from then on.

The extension did this

Every 3 min, the same alarm pinging your trade history also reads sent Steam trade offers, forwarding matches' IDs, asset IDs, and the counterparty's Steam ID64 to csfloat.com.

This happens automatically in the background with no further prompt once the permission is granted.

The code that does this

Background alarm reads sent Steam trade offers via Valve's API, POSTs matches to CSFloat every 3min.

Readable version

What a lower-footprint version would send

// Send an opaque, hashed reference to the pending CSFloat trade and// its settlement state, instead of the raw counterparty Steam ID64 and// asset IDs taken directly from Valve's Trade Offers API response.await TradeOfferStatus.handleRequest({    sent_offers: offersForCSFloat.map(minimalOfferReference),    type,}, {});
Captured request
POSThttps://csfloat.com/api/v1/trades/steam-status/offer

CSFloat's backend returned 200 OK, acknowledging receipt of the trade-offer payload.

Headers
Content-Type
application/json
Body
{  "sent_offers": [    {      "offer_id": "5127349018",      "state": 2,      "given_asset_ids": [        "20938471002"      ],      "received_asset_ids": [],      "time_created": 1751998000,      "time_updated": 1752000000,      "other_steam_id64": "76561198083722517"    }  ],  "type": 1}
What gets sent to csfloat.com for each matched trade offer
  • Steam trade offer ID
    offer_id: 5127349018

    Valve's internal identifier for this specific trade offer.

  • Item asset IDs given and received
    given_asset_ids: ["20938471002"]

    Unique identifiers for the specific in-game items (skins) the offer moves.

  • The counterparty's Steam ID64
    other_steam_id64: 76561198083722517

    Identifies exactly who the other side of the trade offer is, tied to their Steam account.

  • Offer state
    state: 2 (active)

    Whether the offer is active, accepted, declined, or expired.

Steam Web API Token Extracted From steamcommunity.com Page HTML

Each time the alarm runs, it fetches your steamcommunity.com homepage with your login cookies, then regexes your Steam Web API token and ID from the HTML, storing both locally.

Confirmed via a marker reused in a live steampowered.com call.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

The extension's background alarm runs on its normal schedule, no page visit or click from you is required.

The same alarm that drives the trade-history and trade-offer pings also drives this token refresh.

The extension did this

It fetches your own steamcommunity.com homepage using your session cookies, regexes your Steam Web API token and Steam ID out of the HTML, and stores both locally.

The stored token is then reused directly in later calls to Valve's official Steam Web API.

The code that does this

Scrapes your Steam Web API token and Steam ID from steamcommunity.com HTML, then caches them.

Readable version

What a documented alternative would look like

// Use Valve's officially supported Web API key issuance flow// (a user-generated key from steamcommunity.com/dev/apikey, entered// once by the user) instead of scraping an internal, undocumented// HTML attribute that Valve can change or remove without notice.
What's stored on your device

The extension's cached Steam Web API credential, read back and reused for 30 minutes before it re-fetches steamcommunity.com to refresh it.

Location
chrome.storage.local key 'access_token'
Contents
{  "token": "<redacted>",  "steam_id": "76561198012345678",  "updated_at": 1752000000000}
Captured request
GEThttps://api.steampowered.com/IEconService/GetInventoryItemsWithDescriptions/v1/?access_token=<redacted>&steamid=&appid=730&contextid=2

Valve's API accepted the request shape; with a real Steam session's token this returns the caller's live CS2/CS:GO inventory.

+2 more findings not shown

Updated 30 September 2026jjicbefpemnphinccgikpdaagjebbnhg