@SITEBOON/CLAUDE-CODE-UI · CVE-2026-31862 · AI agents & MCP
FIX → 1.24.0
The @siteboon/claude-code-ui web app passes user-supplied Git parameters (file, branch, commit message, commit hash) into shell command strings, so an authenticated user can chain shell commands. An attacker can run arbitrary OS commands as the Node.js process user, which means full server compromise.
first public Mar 9, 2026 · 2 sources
Check it with your coding agent
It checks your repository and tells you if you’re affected, with the fix.
Any team running @siteboon/claude-code-ui at version 1.23.0 or earlier is exposed, since the Git endpoints pass request parameters straight into a shell. Exposure requires an account on the app, so instances open only to trusted users are at lower risk but still should be upgraded. Installs at 1.24.0 or later are not affected.
Not affected if no-user-accounts-exposed, version-1.24.0-or-later
Who cares claude-code, cwe-77, cwe-78, git, node, self-hosted
npm ls @siteboon/claude-code-ui and confirm it is above 1.23.0./api/git/* endpoints to users who can log in.server/routes/git.js for execAsync( calls that interpolate request parameters.Multiple Git-related API endpoints use execAsync() with string interpolation of user-controlled parameters (file, branch, message, commit), allowing authenticated attackers to execute arbitrary OS commands.
The claudecodeui application provides Git integration through various API endpoints. These endpoints accept user-controlled parameters such as file paths, branch names, commit messages, and commit hashes, which are directly interpolated into shell command strings passed to execAsync().
The application attempts to escape double quotes in some parameters, but this protection is trivially bypassable using other shell metacharacters such as:
Command substitution: $(command) or `command` Command chaining: ;, &&, || Newlines and other control characters
GET /api/git/diff - file parameter
GET /api/git/status - file parameter
POST /api/git/commit - files array and message parameter
POST /api/git/checkout - branch parameter
POST /api/git/create-branch - branch parameter
GET /api/git/commits - commit hash parameter
GET /api/git/commit-diff - commit parameter
Are you affected?
Tell InfraStack what you run. It checks this one, and every next one, against your stack.
Ask from here. InfraStack checks the affected versions against your stack and answers in your inbox.
InfraStack checks the affected versions against your stack and answers in Slack, with the evidence and the fix.
Upgrade to 1.24.0
@siteboon/claude-code-ui: move to 1.24.0 or later.File: server/routes/git.js
// Line 205 - git status with file parameter
const { stdout: statusOutput } = await execAsync(
`git status --porcelain "${file}"`, // INJECTION via file
{ cwd: projectPath }
);
// Lines 375-379 - git commit with files array and message
for (const file of files) {
await execAsync(`git add "${file}"`, { cwd: projectPath }); // INJECTION via files[]
}
const { stdout } = await execAsync(
`git commit -m "${message.replace(/"/g, '\\"')}"`, // INJECTION via message (bypass with $())
{ cwd: projectPath }
);
// Lines 541-543 - git show with commit parameter (no quotes!)
const { stdout } = await execAsync(
`git show ${commit}`, // INJECTION via commit
{ cwd: projectPath }
);
Commit: siteboon/claudecodeui@55567f4
All vulnerable execAsync() calls have been replaced with the existing spawnAsync() helper (which uses child_process.spawn with shell: false). Arguments are passed as an array directly to the OS — shell metacharacters in user input are inert.
Endpoints patched in server/routes/git.js:
GET /api/git/diff — file (4 calls)GET /api/git/file-with-diff — file (3 calls)POST /api/git/commit — files[], messagePOST /api/git/checkout — branchPOST /api/git/create-branch — branchGET /api/git/commits — commit.hashGET /api/git/commit-diff — commitPOST /api/git/generate-commit-message — filePOST /api/git/discard — file (3 calls)POST /api/git/delete-untracked — filePOST /api/git/publish — branchA strict allowlist regex (/^[0-9a-f]{4,64}$/i) was also added to validate the commit parameter in /api/git/commit-diff before it reaches the git process.
// BEFORE — shell interprets the string, injection possible
const { stdout } = await execAsync(`git show ${commit}`, { cwd: projectPath });
// AFTER — no shell, args passed directly to the process
const { stdout } = await spawnAsync('git', ['show', commit], { cwd: projectPath });
This page as .md · .json · CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
GitHub Advisory Database, CC-BY 4.0 · NVD data is provided by NIST and is public domain. Posts quoted belong to their authors.