Is Workstation for Discord safe?
Workstation for Discord has no local Discord client; every button loads a third-party site, winfy.net, to handle the Discord session.
Every button in the popup ("Enter", "Full screen") opens https://www.winfy.net/runwine/index_ext.php or run_discord.php rather than connecting to discord.com directly. The popup's Content-Security-Policy permits scripts and resources from any origin, so whatever winfy.net serves runs with broad latitude inside the extension. Any Discord login and subsequent messages or files the user sends pass through winfy.net's servers instead of a direct browser connection to Discord.
Who publishes itWinfy - 10 other listings from the same operator, 1 of them carrying a finding
Winfy - 10 other listings from the same operator, 1 of them carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
10 other listings published from this account, 79k+ users between them. 1 of them carries a finding.
Shared hosts - 1 hostname
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.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Discord login and messages route through winfy.net, not discord.com
Code analysis shows the extension has no direct connection to discord.com.
It loads a winfy.net page for every Discord action, so any login and any message or file you send happens inside content winfy.net serves.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Static analysis
You use the extension's only path to Discord: click Enter, then log in and message inside the loaded page.
The extension's store listing promises Discord messaging with no mention of a third-party relay.
Your login and any messages or files pass through content served by winfy.net rather than a direct connection to discord.com.
winfy.net sits on-path for the entire session by construction, whether acting as a proxy or a remote client.
- Discord loginusername + password entered in the winfy.net-served frame
Any username, password, or QR handshake you complete happens inside content winfy.net serves, not a direct connection to Discord.
- Session/auth stateDiscord session cookie or auth token
Whatever session token your login produces is established inside winfy.net's origin rather than the extension's own code.
- Message and file contentchat text, uploaded images/files
Text messages, DMs, and file uploads you send through the loaded page pass through winfy.net.
Every 'enter Discord' path resolves to winfy.net, never discord.com
Same three lines, annotated
js/index.js// Every click path that "enters Discord" resolves to one of these two// lines. Both are on the winfy.net origin. Neither discord.com nor// discordapp.com appears anywhere else in the extension's code.// Path 1 and 2 (Enter button, sidemenu Enter): load winfy.net in an iframeiframe.src = 'https://www.winfy.net/runwine/index_ext.php' + '?os=winfy-secure-anonymous-discord-22.04.3-desktop-amd64&home=init';// Path 3 (sidemenu Full screen): open the same winfy.net origin in a new tabwindow.open('https://www.winfy.net/run_discord.php');- www.winfy.net
Serves 100% of the extension's Discord functionality (login flow, messaging UI) in place of a direct connection to discord.com; sits on-path for the full session.
Scans an unpacked copy of the extension for any reference to discord.com/discordapp.com, and separately lists every https URL the code actually contains.
- bash
- grep
#!/usr/bin/env bash# find_discord_endpoints.sh# Scans an unpacked copy of the extension for references to discord.com /# discordapp.com, and separately lists every https:// URL the extension's# own code actually references.## Usage: ./find_discord_endpoints.sh <path-to-unpacked-extension>set -euo pipefailDIR="${1:?Usage: find_discord_endpoints.sh <path-to-unpacked-extension>}"echo "== References to discord.com / discordapp.com =="grep -rniE "discord\.com|discordapp\.com" "$DIR" --include="*.js" --include="*.html" || echo "(none found)"echoecho "== All https:// URLs referenced in the extension's own code =="grep -rhoE "https://[a-zA-Z0-9._-]+" "$DIR" --include="*.js" --include="*.html" | sort -u- 1Unpack the extension's .crx with any CRX unpacker.
- 2Run './find_discord_endpoints.sh <unpacked-dir>'.
- 3Confirm no discord.com/discordapp.com match and the only URL is winfy.net.
Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.
Popup has no Discord client, every button loads a remote winfy.net page
Code analysis shows the popup contains no Discord client code.
Every click path (Enter, sidemenu Enter, Full screen) instead loads a page from winfy.net into an iframe or new tab, under a policy that lets that page run any script it wants.
- Severity
- Low unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Static analysis
You click Enter in the popup, sidemenu Enter, or sidemenu Full screen to start using Discord.
All three buttons run the same iframe/tab-loading code.
The extension loads a winfy.net page into the popup or a new tab instead of running any Discord client code of its own.
No Discord SDK, WebSocket client, or messaging code exists anywhere in the 461-line bundle.
The Enter button handler that replaces the popup with a remote page
Same handler with the control flow annotated
js/index.js $(document).on('click', '.view_a', function (e) { e.preventDefault(); exitMenu(); // Sets up the confirm dialog's button labels. let locale = { OK: 'OK', CONFIRM: 'OK', CANCEL: 'Cancel' }; bootbox.addLocale('custom', locale); // Every "Enter" click funnels through this confirm dialog first. bootbox.confirm('Confirm to enter in Discord', function(result) { if (result == true) { // Hide the extension's own placeholder pane... document.getElementById('preframex').style.display = "none"; document.getElementById('winfyout').style.display = "block"; // ...and replace it with a brand-new iframe whose entire // content comes from winfy.net, not from Discord or from // any code shipped in this extension. var iframe = document.createElement('iframe'); iframe.id = "winfyin"; iframe.width = "100%"; iframe.height = "850px"; iframe.src = 'https://www.winfy.net/runwine/index_ext.php' + '?os=winfy-secure-anonymous-discord-22.04.3-desktop-amd64&home=init'; // Swap the placeholder element for the live iframe. img = document.getElementById('winfyin'); img.parentNode.insertBefore(iframe, img); img.parentNode.removeChild(img); } }); });- Script sourcesscript-src 'self' http://* 'unsafe-inline' 'unsafe-eval'
Scripts loaded from winfy.net (or any origin) may run inline and use eval, with the same privileges as the extension's own popup code.
- Resource sourcesdefault-src *
The popup will fetch and render resources (images, media, frames) from any origin, not just winfy.net.
- Iframe destinationhttps://www.winfy.net/runwine/index_ext.php?os=winfy-secure-anonymous-discord-22.04.3-desktop-amd64&home=init
The single address that supplies everything you see after clicking Enter.
Reads the popup's CSP meta tag and the iframe target from an unpacked copy of the extension, then makes a live request to that target so you can see who actually serves the content.
- Node.js 18+ (built-in fetch)
#!/usr/bin/env node// verify_winfy_iframe.js// Reads the popup's CSP meta tag and the iframe target it loads, then makes// a live request to that target so you can see who actually serves it.//// Usage: node verify_winfy_iframe.js <path-to-unpacked-extension>const fs = require('fs');const path = require('path');const extDir = process.argv[2];if (!extDir) { console.error('Usage: node verify_winfy_iframe.js <path-to-unpacked-extension>'); process.exit(1);}const startHtml = fs.readFileSync(path.join(extDir, 'start.html'), 'utf8');const cspMatch = startHtml.match(/Content-Security-Policy["']?\s*content=["']([^"']+)["']/i);console.log('Popup CSP meta tag:');console.log(' ' + (cspMatch ? cspMatch[1] : '(not found)'));const indexJs = fs.readFileSync(path.join(extDir, 'js', 'index.js'), 'utf8');const srcMatch = indexJs.match(/iframe\.src\s*=\s*['"]([^'"]+)['"]/);if (!srcMatch) { console.error('No iframe.src assignment found in js/index.js'); process.exit(1);}console.log('\nIframe target found in the extension code:');console.log(' ' + srcMatch[1]);fetch(srcMatch[1]).then(async (res) => { console.log('\nLive request to that target:'); console.log(' HTTP status: ' + res.status); for (const [k, v] of res.headers.entries()) { console.log(' ' + k + ': ' + v); }}).catch((err) => { console.error('Request failed: ' + err.message);});- 1Unpack the extension's .crx with any CRX unpacker.
- 2Run 'node verify_winfy_iframe.js <unpacked-dir>'.
- 3Confirm the printed target is on winfy.net, not discord.com.
Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.
What it can do
Permissions this extension asks for, as declared in version 1.1.4. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Store data in your browser
storage
Where it sends data
Destinations our analysis observed Workstation for Discord contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.
- www.winfy.net
Workstation for Discord sends data to www.winfy.net. 6 other extensions we have analyzed send data here.