Security Alert: Confirmed Malware
ChessVanced
ID: ammcjghngpnnpibfnojlnmeghcmcgdib
Supported Languages
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- urosmijajlovic43View Profile
- MX records exist
- Yes
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- No
- Mailbox exists
- Yes
Free Game Review for Chess.com and Lichess
ChessVanced is a tool for free game review of chess games, while you play your game on Chess.com or Lichess, the extension will analyze the game in the background, and after the game is finished you may press "Free Game Review" button to be prompted to chessvanced.com where you will view your game review
When the user visits chessvanced.com/analysis, the extension reads ALL accumulated game data from chrome.storage.local (full move history, FEN positions, Stockfish analysis results, player ELO ratings, player side) and injects it into a hidden DOM element, then dynamically injects contentScript.js as a page-level script tag. This bridges sensitive user chess data from local extension storage to the chessvanced.com page context, where the external website's JavaScript receives and can process it arbitrarily. The dynamic script injection also bypasses CSP controls on the target page.
s.includes(e) ? async function() { await async function() { var e = document.createElement("div"); e.id = "injectedData"; const t = await n("analysisData"); e.setAttribute("analysisData", JSON.stringify(t)); const s = await n("moveArray"); e.setAttribute("moveArray", JSON.stringify(s)); const o = await n("fenArray"); e.setAttribute("fenArray", JSON.stringify(o)); const a = await n("analyzedFens"); e.setAttribute("analyzedFens", JSON.stringify(a)); const r = await n("playerSide"); e.setAttribute("playerSide", JSON.stringify(r)); const i = await n("playerElos"); e.setAttribute("playerElos", JSON.stringify(i)), (document.head || document.documentElement) .appendChild(e) }(); var e = document.createElement("script"); e.src = chrome.runtime.getURL("contentScript.js"), (document.head || document.documentElement) .appendChild(e)}()This injected script runs in the chessvanced.com page context and hands all collected user game data directly to `window.playerControllerInst.setGameFromExtension()`, a function defined and controlled entirely by the chessvanced.com website. The website can implement `setGameFromExtension` to do anything with the data — including sending it to remote servers — and the extension has no visibility or control over this. This creates an uncontrolled trust boundary: the external site fully owns what happens with the scraped chess data.
!async function() { for (var { analysisData: e, moveArray: a, fenArray: t, analyzedFens: r, playerSide: n, data: s } = function() { var e = {}, a = document.getElementById("injectedData"), t = JSON.parse(a.getAttribute("analysisData")), r = JSON.parse(a.getAttribute("movearray")), n = JSON.parse(a.getAttribute("fenarray")), s = JSON.parse(a.getAttribute("analyzedFens")), i = JSON.parse(a.getAttribute("playerSide")); return e.playerElos = JSON.parse(a.getAttribute("playerElos")), { analysisData: t, moveArray: r, fenArray: n, analyzedFens: s, playerSide: i, data: e } }(), i = await async function() { for (; null == window.playerControllerInst;) await new Promise((e => setTimeout(e, 2e3))); return window.playerControllerInst }(); 0 == i.setGameFromExtension(t, a, e, r, n, s);) await new Promise((e => setTimeout(e, 500)))}();The `MemoryHandlerSet` message handler allows any page listed in `externally_connectable` (chess.com, lichess.org, and chessvanced.com) to write arbitrary key-value pairs into the extension's `chrome.storage.local` by sending a runtime message. Because the storage is shared with all extension components, a malicious or compromised chessvanced.com page could overwrite analysis data, game state, or inject tampered values that influence extension behavior. The `externally_connectable` manifest entry with three domains is the prerequisite that makes this attack surface reachable from external web pages.
class q { constructor() { chrome.runtime.onMessage.addListener((e => { if ("MemoryHandlerSet" == e.type) { var r = e.message.key, t = e.message.value; this.setData(r, t) } if ("MemoryHandlerGet" == e.type) { r = e.message; this.getData(r) } })) } async setData(e, r) { var t = {}; t[e] = r, await chrome.storage.local.set(t), chrome.runtime.sendMessage({ type: "MemoryHandler", status: "ok" }) }The content script scrapes player ELO ratings from both chess.com and lichess.org DOM elements and continuously polls game state every 2 seconds in an infinite loop. This creates a persistent surveillance pipeline: ELO ratings (personally identifying metadata about a user's chess account ranking) are harvested without explicit user consent and stored in chrome.storage.local before being transmitted to chessvanced.com. The polling interval means even games the user never manually reviews are fully captured.
getPlayersElo() { try { var e, t = []; window.location.href.includes("chess.com") ? e = document.getElementsByClassName("rating-score-rating") : window.location.href.includes("lichess") && (e = document.getElementsByTagName("rating")); for (const n of e) t.push(parseInt(n.textContent)); return t } catch (e) { return [-1, -1] } } ... async startScraping() { for (;;) if (await new Promise((e => setTimeout(e, 2e3))), this.isLiveGame()) { this.tryToDeleteButton("chessvanced-floating"); try { this.sendCurrentGameState() } catch (e) {} } else this.sendCurrentGameState(), this.tryToCreateButton() }Every chess position analyzed (including positions from chess.com games) is sent to the Lichess cloud eval API with the full FEN string, leaking the user's game positions to a third-party service regardless of which platform the user is playing on. Additionally, `axios` is referenced but is never loaded in the offscreen document (the HTML only loads stockfish.js and mainOffscreenScript.js), meaning this call silently fails at runtime — suggesting either a stripped dependency, code tampering, or a version mismatch where the network exfiltration was removed but the call site was not cleaned up.
async getLichessData(e) { try { const t = `https://lichess.org/api/cloud-eval?fen=${encodeURIComponent(e)}&multiPv=2`, s = await axios.get(t); return s.data.fen ? s.data.pvs.length < 2 ? [!1] : [!0, s.data] : [!1, s.data] } catch (e) { return [!1] }}async checkCache(e) { const t = await this.getLichessData(e); return 1 == t[0] ? [!0, this.stockfishParser.cachedDataToParsed(t[1])] : [!1]}The `externally_connectable` manifest entry grants chess.com, lichess.org, and chessvanced.com the ability to send messages directly to the extension service worker via `chrome.runtime.sendMessage()`. Combined with the `MemoryHandlerSet` listener in backgroundScript.js, this creates a pathway for any page on these domains to write to extension storage. A subdomain takeover, XSS vulnerability, or malicious script on any of these domains could be exploited to tamper with the extension's internal state.
{ "externally_connectable": { "matches": [ "*://*.chess.com/*", "*://*.lichess.org/*", "*://*.chessvanced.com/*" ] }}The background script persistently stores the complete move history, all FEN board positions, player side, and ELO ratings of both players in `chrome.storage.local` with each game update. This creates a long-lived on-device record of the user's chess activity that is subsequently bulk-transferred to chessvanced.com on demand. The `currentGameId` key stores the full game URL, which constitutes browsing history tracking across multiple sessions.
async analyzeMoveArray(e, r, t, n = [-3, -3]) { ... chrome.storage.local.set({ moveArray: this.totalMoveArray }), chrome.storage.local.set({ fenArray: this.totalFenArray }), chrome.storage.local.set({ playerSide: this.playerSide }), chrome.storage.local.set({ playerElos: n });By severity
Versions scanned
Showing 1 of 5 scanned versions with more than one unique finding. Counts are unique findings that include each version.
| Extension Version | Code Review Findings |
|---|---|
| 2.5.0 | 7 |
Files with findings
5 distinct paths — top paths by unique finding count:
- backgroundScript.js2
- contentScriptWrapper.js2
- contentScript.js1
- manifest.json1
- offscreen/mainOffscreenScript.js1
URLs
View the external URLs this extension communicates with to understand its network activity and data interactions.
Gain full insight into all external connections.
Upgrade for full visibility.
Gain full insight into all external connections.
Upgrade for full visibility.
Code Diff
Compare extension code between any two versions.
No comparable text files found between these versions.
Browse and explore files within this extension package
Gain full insight into all external connections.
Upgrade for full visibility.