You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
MCP servers launched with npx or uvx on Windows now load. They often took longer than the 3-second startup limit and were silently dropped, so their tools never showed up. The default is now 10 seconds
Claude through a custom Anthropic base URL (Azure AI Foundry, corporate gateways) no longer fails with a 400 error
Kimi K3 and other models that only accept certain reasoning levels no longer reject requests. The CLI picks the closest level the model supports
OpenAI-compatible providers no longer print a providerOptions key 'openai-compatible' deprecation warning on every response
If a model's response ends without a recognized finish reason, the agent asks it to continue once instead of treating the response as complete
cline config --json prints JSON again. It opened the interactive view instead, which failed outside a terminal; cline config --json mcp printed plain text
-y/--yolo is now listed in cline --help, with a warning to use it only in sandboxed environments
The default blockUnsafeOperationsPlugin in simple-git when an application permits untrusted values to reach SimpleGitOptions.config or Git inline configuration arguments such as -c <key>=<value>.
Summary
trailer.<token>.cmd is not recognized as unsafe by the default guard. Therefore, a configured inline value reaches Git without a GitPluginError.
Git documents trailer.<token>.cmd as a shell command invoked by git interpret-trailers. An application that relies on the default plugin to reject unsafe configuration can therefore execute a command supplied through an untrusted trailer-command configuration value.
Technical details
simple-git/src/lib/git-factory.ts installs commandConfigPrefixingPlugin before blockUnsafeOperationsPlugin. The prefixing plugin in simple-git/src/lib/plugins/command-config-prefixing-plugin.ts turns every SimpleGitOptions.config entry into -c <key>=<value> before the unsafe-operation plugin evaluates the final argv.
In simple-git@<!-- -->3.36.0, blockUnsafeOperationsPlugin delegates to @<!-- -->simple-git/argv-parser. packages/argv-parser/src/vulnerabilities/detect-vulnerable-config-writes.ts compares parsed configuration writes against preventUnsafeConfig. That list has no matcher for trailer.<token>.cmd, so the invocation is allowed.
Git v2.39.5's Documentation/git-interpret-trailers.txt states that trailer.<token>.cmd specifies a shell command called to generate or modify a trailer.
Preconditions
The application must use an affected simple-git version with the default unsafe-operation plugin active and must pass attacker-controlled data into instance configuration or Git command arguments that configure trailer.<token>.cmd.
The invoked Git binary must support the documented trailer-command behavior, and the application must execute git interpret-trailers with the attacker-controlled configuration in scope. The command runs with the operating-system identity and permissions of the Node.js process.
Verification
Use an isolated test environment and a harmless executable test helper that records only its invocation.
Control: Configure core.editor=<test-helper> through SimpleGitOptions.config and invoke a benign Git task. The default plugin should throw GitPluginError before spawning Git because core.editor is present in preventUnsafeConfig.
Bypass: Configure trailer.audit.cmd=<test-helper> through the same option and invoke Git with the equivalent argv shape:
A vulnerable build does not raise GitPluginError; Git invokes the test helper while processing the trailer. Confirm the helper invocation, then remove test artifacts.
Impact
An attacker who controls the stated configuration input can cause Git to execute a shell command as the Node.js application process. The impact is bounded by that process's filesystem, network, and service permissions. Applications that do not expose untrusted configuration or command arguments to simple-git are outside this threat model.
Affected versions
Commit 6b3c631eadea81f80ed10f6dec7d19a9db4d7084 introduced the default unsafe-operation plugin, and simple-git@<!-- -->3.15.0 is the first release confirmed to contain it. Its implementation only rejected protocol.allow configuration, leaving trailer-command configuration unblocked.
The latest simple-git release, 3.36.0, still lacks a trailer-command matcher. The current main branch also lacks one. No released remediation was identified.
Remediation
Default-deny configuration keys that can trigger executable behavior, or add a dedicated unsafe category that rejects trailer.<token>.cmd before spawning Git unless the application explicitly opts in.
Evaluate trailer.<token>.command alongside .cmd, because Git documents it as related command behavior. Add parser and integration tests for leading -c, configured instance prefixes, and git config write forms, asserting that no Git child process is spawned without an explicit unsafe opt-in.
Evidence
simple-git@<!-- -->3.15.0 was released on 2022-11-12 and contains the initial unsafe-operation plugin.
simple-git@<!-- -->3.36.0 was released on 2026-04-12; its parser source does not match trailer.<token>.cmd.
main retains the missing matcher in packages/argv-parser/src/vulnerabilities/detect-vulnerable-config-writes.ts.
Git v2.39.5 documents the trailer command behavior in Documentation/git-interpret-trailers.txt.
This review verified repository, release, and source artifacts through GitHub; it did not independently execute the runtime reproduction.
Improper Neutralization of Special Elements used in a Command ('Command Injection')
Affected range
<=3.36.0
Fixed version
4.0.0
CVSS Score
8.1
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS Score
0.363%
EPSS Percentile
28th percentile
Description
simple-git's blockUnsafeOperationsPlugin blocks dangerous git options (--upload-pack/--receive-pack/--exec/...) unless the consumer opts in via unsafe:{allowUnsafePack:true}. Detection (@simple-git/argv-parser detectVulnerableFlags) matches the parsed flag NAME against literal-spelling patterns: /--(upload|receive)-pack/ (requires the literal '-pack') and the string '--exec'. But git accepts unambiguous prefix abbreviations of long options, and expandToken (token-expander.ts) returns the LITERAL token as flag.name (it uses the spec only for needsNext, never to canonicalize). So git push --receive-p=<cmd> parses with flag.name='--receive-p', which /--(upload|receive)-pack/ does NOT match, yet git expands --receive-p -> --receive-pack and runs (on a local/file remote, locally). The clone side is robust (its '--u' substring rule catches every --upload* abbreviation); the push --receive-* and --exe* abbreviations have no equivalent rule and slip through.
Proof of concept (latest: simple-git 3.36.0, @simple-git/argv-parser 1.1.1, default unsafe plugin ON):
Gate vulnerabilityCheck: 'push --receive-pack=touch...' -> BLOCKED; 'push --receive-pa=touch...' and 'push --receive-p=touch...' -> BYPASS (empty vulns); '--exec=' BLOCKED, '--exe=' BYPASS.
End-to-end via simpleGit().push():
CONTROL git.push(['../bare','HEAD:refs/heads/c','--receive-pack=touch /tmp/pwned_ctrl;']) -> throws GitPluginError 'Use of --upload-pack or --receive-pack is not permitted...'; no command runs.
BYPASS git.push(['../bare','HEAD:refs/heads/e2e','--receive-p=touch /tmp/pwned_e2e;']) -> NO GitPluginError; /tmp/pwned_e2e CREATED (git executed the injected command); only a later GitError surfaces.
Direct git confirms git push ../bare --receive-p='touch X;' HEAD:refs/heads/m and --exe='touch Y;' both execute on a local/path remote.
Impact: any app relying on simple-git's default unsafe-operations protection while passing attacker-influenced options/args into a git push (local/file remote, or attacker-influenced receive-pack target) can be made to execute arbitrary commands -- the exact protection CVE-2026-28291 provided, defeated by an abbreviated spelling. Same class/impact as GHSA-jcxm-m3jx-f287, on the push path.
Remediation: canonicalize git option abbreviations before matching (resolve to the canonical long name via the per-task flag spec in expandToken), or match on the option stem/prefix down to the shortest unambiguous form for push receive-pack/exec (mirroring the clone-side '--u' approach). Also audit --template and -c/config-write detection for the same abbreviation gap.
Credit: anir0y (independent security research).
Improper Neutralization of Special Elements used in a Command ('Command Injection')
Affected range
<=3.36.0
Fixed version
4.0.0
CVSS Score
8.1
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS Score
0.460%
EPSS Percentile
38th percentile
Description
Summary
An OS command injection vulnerability in git.clone() allows any application that flows attacker-influenced data into customArgs to execute arbitrary code. simple-git 3.36.0 (current latest on npm) ships without any include.path entry in the blockUnsafeOperationsPlugin denylist. Passing -c include.path=<file> via customArgs loads any local file as a gitconfig. The loaded file can set core.sshCommand (or any otherwise-denied key), and the next remote operation in the same clone executes the attacker's command.
PR #1167 (merged to main 2026-05-10, not yet released to npm) adds preventConfigBuilder('include.path', 'allowUnsafeInclude') to the denylist. The generated regex /\s*include.path/ closes the plain spelling but does not match the conditional form includeIf.<cond>.path. The variant therefore survives the upcoming release if the regex is not tightened in the same cycle.
This sits in the same denylist class as the prior incomplete-fix chain (CVE-2022-24433, CVE-2022-24066, CVE-2022-25912, CVE-2022-25860, CVE-2026-28291, CVE-2026-28292). include and includeIf are not referenced in any published advisory, in any commit prior to PR #1167, or anywhere in the 3.36.0 source.
Details
Two sinks share the same root cause: the denylist is incomplete.
Sink A: published 3.36.0 has no include.path entry
packages/argv-parser/src/vulnerabilities/detect-vulnerable-config-writes.ts in the v3.36.0 tag contains no entry for include.path or includeIf.*.path. The argv parser recognises -c include.path=<file> and -c includeIf.<cond>.path=<file> as config writes, but detectVulnerableConfigWrites iterates a denylist that does not include either key. The plugin returns no vulnerability and the operation proceeds.
For 'include.path', the generated regex is /\s*include.path/. The . between include and path is a regex wildcard. The engine matches include plus exactly one arbitrary character plus path. Conditional include keys have the form includeIf.<condition>.path (includeIf.gitdir:.path, includeIf.onbranch:main.path, includeIf.hasconfig:r.u:**.path, etc.). The substring between include and path is if.<condition>:, always longer than one character. The 11-character match window cannot align and the test returns false.
The argv parser at packages/argv-parser/src/argv/analyse-config.ts correctly recognises both include.path=... and includeIf.gitdir:.path=... as config writes; both yield a ConfigWrite with the lowercased key. The defect is purely in the denylist regex (after PR #1167) and in the entry being absent (before PR #1167).
Exploitation chain
Attacker writes a gitconfig to any path the simple-git process can read. Realistic write primitives: file upload (avatar, attachment, CI artifact, S3-mounted bucket), shared /tmp in multi-tenant runners, log poisoning that lands [core] headers in a log path, predictable artifact paths, container volume mounts the attacker controls.
Attacker triggers git.clone() with crafted customArgs. Either the URL or the customArgs flow from attacker-influenced input. This is the documented threat model of blockUnsafeOperationsPlugin.
blockUnsafeOperationsPlugin runs parseArgv and collectWriteFlags, yielding the write. detectVulnerableConfigWrites iterates the denylist. In 3.36.0 the denylist has no entry. After PR chore(deps): update dependency updatecli/updatecli to v0.64.0 #1167 the denylist has an entry but its regex does not match includeif.gitdir:.path. Either way, no vulnerability is yielded and the plugin permits the operation.
suffixPathsPlugin moves pathspec items to the suffix. Final argv: git clone -c <payload> -- ssh://target.example/repo.git /tmp/dst.
git clone has its own -c / --config option (-c <key>=<value>, --config <key>=<value> per git clone --help), so a -c immediately after the subcommand is honoured by clone itself. Git evaluates the include (the conditional form uses an empty gitdir: pattern that matches the current gitdir), reads /tmp/attacker.cfg, registers core.sshCommand.
Git invokes ssh through the configured command. Attacker's shell payload runs in the simple-git process's context.
git clone is the unique git subcommand that honours -c after itself. git fetch -c k=v, git pull -c k=v, git push -c k=v all reject the placement (those subcommands treat -c as a global option that must precede them). Since simple-git always places the subcommand at argv[0], user-controlled -c in customArgs always lands after the subcommand. Clone is the entry point for both sinks.
Secondary chain: HOME and XDG_CONFIG_HOME not in parseEnv denylist
packages/argv-parser/src/env/parse-env.ts:5-25 lists env keys removed from the spawned-process environment when sourced from git.env(...). HOME, XDG_CONFIG_HOME, and similar config-resolution keys are absent. Calling git.env({HOME: '/tmp/fake-home'}) makes git read /tmp/fake-home/.gitconfig, which the attacker controls. Same exploit primitive, parallel surface. Should be addressed in the same fix.
PoC
Reproduction from a clean install:
mkdir /tmp/sg-poc &&cd /tmp/sg-poc
npm init -y
npm install simple-git@<!-- -->3.36.0
cat > poc.js <<'EOF'const { simpleGit } = require('simple-git');const fs = require('fs');fs.writeFileSync('/tmp/sg-attacker.cfg', `[core]\nsshCommand = "/bin/sh -c 'id > /tmp/sg-id; touch /tmp/sg-pwned'"\n`);const git = simpleGit({ baseDir: '/tmp' });(async () => { // Sink A: plain include.path works on published 3.36.0 (no denylist entry). // Swap to 'includeIf.gitdir:.path=...' to demonstrate Sink B against PR #1167. const payload = 'include.path=/tmp/sg-attacker.cfg'; try { await git.clone( 'ssh://nonexistent.example.com/repo.git', '/tmp/sg-rce-dst', ['-c', payload] ); } catch (_) { /* clone fails after sshCommand has already run */ } await new Promise(r => setTimeout(r, 500)); console.log(fs.readFileSync('/tmp/sg-id', 'utf8'));})();EOF
node poc.js
Output on simple-git 3.36.0:
uid=0(root) gid=0(root) groups=0(root)
Swapping the payload to 'includeIf.gitdir:.path=/tmp/sg-attacker.cfg' reproduces the same RCE on 3.36.0 and is the variant that will survive the PR #1167 release.
Impact
Pre-authentication remote code execution in any server that flows attacker-influenced data into customArgs of clone() or mirror(). simple-git is approximately 9.4M weekly downloads on npm. Affected consumer patterns:
CI/CD systems and custom GitHub Actions / Buildkite plugins / GitLab cache helpers
PaaS and hosting platforms that accept customer-tunable git options
Code analyzers and security scanners that clone user-supplied repos
Bot frameworks (Probot, GitOps controllers) that wrap simple-git
AI agent frameworks that auto-clone repositories for analysis
VS Code extensions, Electron tools, and dev tooling that pass options through
The chain needs one byte of attacker-writable, process-readable storage in addition to customArgs influence. In consumers where the file-write primitive is co-located with the clone trigger (single-request file upload + clone, multi-tenant CI runners with shared /tmp, agent frameworks that write per-task scratch files), this is effectively unauthenticated pre-auth RCE with AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8 Critical. The form value uses the conservative AC:H = 8.1 baseline that accounts for the separate-request case.
Distinction from prior advisories and pending fix
Reviewed the published GHSA list at steveukx/git-js/security/advisories. Two advisories are published:
GHSA-jcxm-m3jx-f287 (CVE-2026-28291, High): generic option-parsing class addressed by the 3.32.0 refactor
GHSA-r275-fr43-pm7q (CVE-2026-28292, Critical): case-insensitive protocol.allow form
Neither mentions include, includeIf, or conditional includes. The terms do not appear anywhere in source files, tests, or commits in the repository at any tagged release. PR #1167 (merged to main 2026-05-10) is the first commit anywhere in the repository to reference include.path. It addresses the plain form but its regex misses the conditional includeIf.<cond>.path spelling.
The published 3.36.0 vulnerability (Sink A) is unaddressed in any released version. The pending PR #1167 (Sink B) addresses the plain key but leaves the conditional variant open. Both should land in one release.
Suggested fix
In packages/argv-parser/src/vulnerabilities/detect-vulnerable-config-writes.ts, add the plain include.path entry and ensure conditional forms are covered:
Alternatively pre-process the key in parseAssignment to strip the if.<condition>: decoration before testing against include.path, since includeIf is semantically equivalent to include for security purposes.
Stronger, longer-term fix: invert the model. Reject any -c, --config, --config-env in customArgs unconditionally and require callers to use the typed config: option (already prefix-checked through the same plugin). Git's config namespace is open-ended; new dangerous keys land in every git release. A denylist will need new entries indefinitely.
Also extend parseEnv to drop HOME, XDG_CONFIG_HOME, and any env key that affects config-file resolution.
parseEnv (packages/argv-parser/src/env/parse-env.ts), reached via vulnerabilityCheck(tokens, env)
Vulnerability class
Security-control bypass — incomplete denylist in unsafe-editor detection
Verified against
c427fbad33f1f2b11341f1cf852eedecbb106400 (@<!-- -->simple-git/argv-parser 1.1.1), plus the published npm artifact 1.1.1; main at 98864c6 observed still unpatched
API surface
Public documented API (parseEnv(raw) / vulnerabilityCheck(tokens, env))
Affected in default configuration
Yes — reproduced with blockUnsafeOperationsPlugin under default options, with no unsafe allowances enabled
Summary
GitEnvKeys in packages/argv-parser/src/env/parse-env.ts maps only editor, git_editor and git_sequence_editor to the allowUnsafeEditor category. prepareEnv keeps an environment entry only when its lowercased name is a known GitEnvKey or starts with git, so VISUAL is discarded before collectConfigVulnerabilities ever inspects it. Git, however, falls back to VISUAL when resolving an editor, so parseEnv({ VISUAL: '/tmp/evileditor' }) reports no vulnerability while an interactive Git operation will execute that binary.
In a consuming application the shape is: environment values derived from a request or job are forwarded into the child Git environment and classified by this parser before spawn. The parser exists to classify exactly such values, and the equivalent EDITOR or GIT_EDITOR value is rejected — so the attacker gains an editor substitution that the guard is specifically designed to block.
VISUAL -> absent from GitEnvKeys; dropped by prepareEnv, lines 60-68 — no vulnerability emitted
Impact
A consuming application that allows attacker-influenced environment values can have an attacker-selected executable launched by Git during operations such as git commit --amend, bypassing the parser's default unsafe-editor protection. Execution happens as the host user running the Git child process, with the attacker's binary invoked against the repository's editor file (for example .git/COMMIT_EDITMSG, or .git/rebase-merge/git-rebase-todo for git rebase -i).
The new capability is the bypass itself: without this gap, the same attacker-supplied value under EDITOR, GIT_EDITOR or GIT_SEQUENCE_EDITOR is refused unless the consumer explicitly opts in to allowUnsafeEditor. With VISUAL, the equivalent code execution proceeds with no opt-in and no reported vulnerability. VISUAL also takes precedence over EDITOR, the variable the parser does flag.
Scoring note: no CVSS vector or score is available for this finding, and one is not asserted here. Exploitability depends on the consuming application's data flow — specifically whether attacker-influenced environment entries reach the Git child environment. Consumers that never forward untrusted environment values into Git, or that always set a higher-priority GIT_EDITOR or core.editor, are not affected.
Preconditions
The consumer forwards attacker-influenced environment entries into the environment passed to child Git (for example via simple-git's .env()), and classifies them with this parser before spawn.
The Git command opens an editor — for example commit without -m, commit --amend, or rebase -i.
No higher-priority editor setting overrides VISUAL: GIT_EDITOR, core.editor and EDITOR are absent (GIT_EDITOR and core.editor take precedence; VISUAL itself overrides EDITOR).
TERM is set to a value other than exactly dumb — Git consults VISUAL only then. TERM is neither a GitEnvKey nor git-prefixed, so an attacker who controls the environment object supplies it too and the parser reports nothing for it either.
The attacker-selected executable exists and is runnable on the host.
This requires no non-standard usage, no monkey-patching and no unusual configuration: the affected path is the documented, default-enabled guard. docs/PLUGIN-UNSAFE-ACTIONS.md ("Text editor") documents this control as covering editor environment variables that substitute an arbitrary binary, but lists only EDITOR, GIT_EDITOR and GIT_SEQUENCE_EDITOR; Git's VISUAL fallback is not mentioned, and the string visual does not appear anywhere in the repository at the verified commit. The docs do state that supplying environment values is the caller's responsibility, but they do not warn that VISUAL is outside the guard.
The feed payload's precondition list also carries entries relating to a separate GIT_CONFIG_PARAMETERS / allowUnsafeConfigEnvCount config-injection scenario. Those were not needed here: the bypass was reproduced with default options and no unsafe allowances enabled.
Propagation — prepareEnv lowercases keys and retains only known GitEnvKeys or names starting with git; visual is neither, so the entry is dropped before any analysis sees it (packages/argv-parser/src/env/parse-env.ts:60-68).
Sink — collectConfigVulnerabilities therefore never emits allowUnsafeEditor for VISUAL, and vulnerabilityCheck(tokens, env) returns an empty list (packages/argv-parser/src/env/parse-env.ts:45-54).
The three mapped keys are the safe siblings; the missing visual entry is the gap. Because prepareEnv (lines 60-68) filters on this map plus a git prefix, the omission is not merely a missing classification — the value never reaches the classifier at all.
Reproduction
Verified — reproduced dynamically, end to end, against a checkout of c427fbad33f1f2b11341f1cf852eedecbb106400 (packages/argv-parser/package.json = 1.1.1) and against the published npm artifact 1.1.1.
Observed at the parser level (vitest PoC run against the repo):
parseEnv({ EDITOR }), parseEnv({ GIT_EDITOR }) and parseEnv({ GIT_SEQUENCE_EDITOR }) each yield one allowUnsafeEditor vulnerability.
parseEnv({ VISUAL: '/tmp/poc/evileditor' }) yields [] in every casing.
vulnerabilityCheck(['commit', '--amend'], { VISUAL }) — the exact call the spawn guard makes — returns [].
The published dist/index.cjs of 1.1.1 contains zero occurrences of visual; the same holds for the repository at the verified commit, including docs/PLUGIN-UNSAFE-ACTIONS.md.
Observed at the Git level (only VISUAL set, EDITOR and GIT_EDITOR unset): git var GIT_EDITOR returned the attacker path; git commit --amend executed the attacker script (marker written, commit subject rewritten); git rebase -i executed it for the rebase-todo as well. VISUAL also took precedence over EDITOR (EDITOR=/bin/true VISUAL=evil -> evil).
Observed end to end through the real spawn path (simple-git built from this commit, default options, no unsafe allowances): the EDITOR and GIT_EDITOR variants both threw GitPluginError — "Use of ... is not permitted without enabling allowUnsafeEditor" — with no execution. The VISUAL variant was not blocked: the plugin saw an empty vulnerability list, Git spawned, and the attacker-supplied editor executed as the host user against .git/COMMIT_EDITMSG, rewriting the commit message.
Expected:parseEnv({ VISUAL: '/tmp/evileditor' }).vulnerabilities contains allowUnsafeEditor, and the spawn guard refuses the operation unless the consumer has enabled allowUnsafeEditor — the behaviour already applied to EDITOR, GIT_EDITOR and GIT_SEQUENCE_EDITOR.
Actual: no vulnerability is reported, the guard permits the spawn, and Git executes the attacker-selected binary.
Suggested remediation
Treat VISUAL as an editor source, so Git's own editor-resolution precedence is fully covered by the denylist.
Because prepareEnv filters on GitEnvKeys membership, this single entry is enough to make visual survive filtering and be classified; no change to prepareEnv or collectConfigVulnerabilities is required.
Notes:
TERM is likewise neither a GitEnvKey nor git-prefixed, and it is the variable that decides whether Git consults VISUAL at all (TERM=dumb or unset means VISUAL is ignored). An attacker who controls the environment object supplies it alongside VISUAL; whether TERM warrants its own classification is a maintainer judgement call, but it is worth considering while fixing this.
git rebase -i reaches the same sink: git_sequence_editor falls back to normal editor resolution, so the VISUAL path executes the attacker binary against the rebase-todo file as well.
docs/PLUGIN-UNSAFE-ACTIONS.md ("Text editor") should list VISUAL alongside EDITOR / GIT_EDITOR / GIT_SEQUENCE_EDITOR, since the documented scope of the control is what consumers rely on.
Already safe and needing no change: EDITOR, GIT_EDITOR and GIT_SEQUENCE_EDITOR are all correctly classified and enforced by the plugin, and the parser's handling of git-prefixed variables is unaffected.
Suggested regression test alongside test/parse-env.spec.ts (which currently covers EDITOR / GIT_EDITOR / GIT_SEQUENCE_EDITOR / PAGER but has no VISUAL case): assert that parseEnv({ VISUAL: '/tmp/evileditor' }) yields one allowUnsafeEditor vulnerability in every casing, and that vulnerabilityCheck(['commit', '--amend'], { VISUAL: '/tmp/evileditor' }) returns that vulnerability rather than []. A precedence case is worth adding too: VISUAL set together with EDITOR must still be flagged, since VISUAL wins in Git's resolution order.
undici5.29.0 (npm)
pkg:npm/undici@5.29.0
Uncaught Exception
Affected range
<6.24.0
Fixed version
6.24.0
CVSS Score
7.5
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Score
0.874%
EPSS Percentile
58th percentile
Description
Impact
The undici WebSocket client is vulnerable to a denial-of-service attack due to improper validation of the server_max_window_bits parameter in the permessage-deflate extension. When a WebSocket client connects to a server, it automatically advertises support for permessage-deflate compression. A malicious server can respond with an out-of-range server_max_window_bits value (outside zlib's valid range of 8-15). When the server subsequently sends a compressed frame, the client attempts to create a zlib InflateRaw instance with the invalid windowBits value, causing a synchronous RangeError exception that is not caught, resulting in immediate process termination.
The vulnerability exists because:
The isValidClientWindowBits() function only validates that the value contains ASCII digits, not that it falls within the valid range 8-15
The createInflateRaw() call is not wrapped in a try-catch block
The resulting exception propagates up through the call stack and crashes the Node.js process
Patches
Has the problem been patched? What versions should users upgrade to?
Workarounds
Is there a way for users to fix or remediate the vulnerability without upgrading?
Improper Handling of Highly Compressed Data (Data Amplification)
Affected range
<6.24.0
Fixed version
6.24.0
CVSS Score
7.5
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Score
1.150%
EPSS Percentile
66th percentile
Description
Description
The undici WebSocket client is vulnerable to a denial-of-service attack via unbounded memory consumption during permessage-deflate decompression. When a WebSocket connection negotiates the permessage-deflate extension, the client decompresses incoming compressed frames without enforcing any limit on the decompressed data size. A malicious WebSocket server can send a small compressed frame (a "decompression bomb") that expands to an extremely large size in memory, causing the Node.js process to exhaust available memory and crash or become unresponsive.
The vulnerability exists in the PerMessageDeflate.decompress() method, which accumulates all decompressed chunks in memory and concatenates them into a single Buffer without checking whether the total size exceeds a safe threshold.
Impact
Remote denial of service against any Node.js application using undici's WebSocket client
A single compressed WebSocket frame of ~6 MB can decompress to ~1 GB or more
Memory exhaustion occurs in native/external memory, bypassing V8 heap limits
No application-level mitigation is possible as decompression occurs before message delivery
Patches
Users should upgrade to fixed versions.
Workarounds
No workaround are possible.
Uncontrolled Resource Consumption
Affected range
<6.27.0
Fixed version
6.27.0
CVSS Score
7.5
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Score
0.789%
EPSS Percentile
55th percentile
Description
Impact
The undici WebSocket client enforces maxPayloadSize on the cumulative byte count of fragments in a message but does not enforce a limit on the number of fragments. A malicious WebSocket server can stream many small or empty continuation frames that each pass per-frame and cumulative-size validation, collectively causing unbounded memory growth in the client process. The result is memory exhaustion and a denial of service.
Affected applications are those using the undici WebSocket client (new WebSocket(...)) or the WebSocketStream API that can be induced to connect to an attacker-controlled or compromised WebSocket endpoint.
All releases starting at undici 6.17.0 are affected.
Patches
Upgrade to undici v6.27.0, v7.28.0 or v8.5.0.
Workarounds
No workaround is available. The fix must be applied through an upgrade.
Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')
Affected range
<6.24.0
Fixed version
6.24.0
CVSS Score
6.5
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L
EPSS Score
0.493%
EPSS Percentile
40th percentile
Description
Impact
Undici allows duplicate HTTP Content-Length headers when they are provided in an array with case-variant names (e.g., Content-Length and content-length). This produces malformed HTTP/1.1 requests with multiple conflicting Content-Length values on the wire.
Who is impacted:
Applications using undici.request(), undici.Client, or similar low-level APIs with headers passed as flat arrays
Applications that accept user-controlled header names without case-normalization
Potential consequences:
Denial of Service: Strict HTTP parsers (proxies, servers) will reject requests with duplicate Content-Length headers (400 Bad Request)
HTTP Request Smuggling: In deployments where an intermediary and backend interpret duplicate headers inconsistently (e.g., one uses the first value, the other uses the last), this can enable request smuggling attacks leading to ACL bypass, cache poisoning, or credential hijacking
Patches
Patched in the undici version v7.24.0 and v6.24.0. Users should upgrade to this version or later.
Workarounds
If upgrading is not immediately possible:
Validate header names: Ensure no duplicate Content-Length headers (case-insensitive) are present before passing headers to undici
Use object format: Pass headers as a plain object ({ 'content-length': '123' }) rather than an array, which naturally deduplicates by key
Sanitize user input: If headers originate from user input, normalize header names to lowercase and reject duplicates
Improper Neutralization of CRLF Sequences ('CRLF Injection')
Affected range
<6.27.0
Fixed version
6.27.0
CVSS Score
5.9
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N
EPSS Score
0.332%
EPSS Percentile
24th percentile
Description
Impact
undici's cookie parser in parseSetCookie percent-decodes cookie values via qsUnescape, turning encoded sequences like %0D%0A, %00, %3B, and %3D into their literal byte equivalents. RFC 6265 §5.4 does not specify any decoding and browsers do not decode either.
Applications that parse a Set-Cookie header and then forward the parsed value into a response header (proxies, middleware, SSR frameworks) become vulnerable to HTTP response header injection: an attacker-controlled upstream can inject arbitrary Set-Cookie, Location, or Cache-Control headers into the application's downstream response, enabling session fixation, open redirect, or cache poisoning.
Affected applications are those that use undici's cookie parsing (parseSetCookie, parseCookie, getSetCookies) and forward the parsed cookie value into a response header.
If upgrade is not immediately possible, do not forward values returned by parseSetCookie/parseCookie/getSetCookies directly into response headers; sanitize the value first to strip or reject CR, LF, NUL, ;, and = bytes.
Allocation of Resources Without Limits or Throttling
Affected range
<6.23.0
Fixed version
6.23.0
CVSS Score
5.9
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Score
0.463%
EPSS Percentile
38th percentile
Description
Impact
The fetch() API supports chained HTTP encoding algorithms for response content according to RFC 9110 (e.g., Content-Encoding: gzip, br). This is also supported by the undici decompress interceptor.
However, the number of links in the decompression chain is unbounded and the default maxHeaderSize allows a malicious server to insert thousands compression steps leading to high CPU usage and excessive memory allocation.
Patches
Upgrade to 7.18.2 or 6.23.0.
Workarounds
It is possible to apply an undici interceptor and filter long Content-Encoding sequences manually.
Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')
Affected range
<6.28.0
Fixed version
6.28.0
CVSS Score
4.8
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N
EPSS Score
0.191%
EPSS Percentile
8th percentile
Description
Impact
The setCookie function has two attribute injection paths. validateCookieDomain does not reject semicolons (validateCookiePath already does at 0x3B), so a domain value like example.com; SameSite=None lands verbatim as Domain=example.com; SameSite=None. The unparsed array's loop only checks each entry contains = and does not sanitize values, so an entry like X-Custom=val; HttpOnly lands unchanged, injecting HttpOnly without the caller setting cookie.httpOnly = true.
Applications that pass user-controlled input to these fields, typically multi-tenant or reverse-proxy servers that scope session cookies to a tenant-supplied domain, can have SameSite CSRF protections bypassed, Secure or HttpOnly forced or stripped, or the intended SameSite tier overridden.
Patches
Patched in undici v6.28.0, v7.29.0, and v8.9.0.
Workarounds
Sanitize domain values against the RFC 1034 letter-digit-hyphen set before passing to setCookie.
Do not pass user-controlled data to the unparsed field.
Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')
Affected range
<6.28.0
Fixed version
6.28.0
CVSS Score
4.8
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N
EPSS Score
0.176%
EPSS Percentile
7th percentile
Description
Impact
Undici's interceptors.retry() can deliver a response whose body length does not match the Content-Length header exposed to the application after a retry or resume of a partial response. Applications that use interceptors.retry() and forward upstream response headers and bodies downstream, for example proxy or gateway applications, may emit an invalid HTTP response with a stale Content-Length header. This can lead to downstream response desynchronization, connection hangs, or response corruption in clients or intermediaries that rely on the forwarded framing metadata.
A malicious or faulty upstream can respond to a range request with a 206 Partial Content response such as:
Content-Range: bytes 0-99/300Content-Length: 300
and then send only 99 bytes before closing the socket. interceptors.retry() can then retry with Range: bytes=99-99, receive the final byte, and deliver a 100-byte body to the application while the response headers still contain Content-Length: 300 from the first response.
The bug requires interceptors.retry() to be enabled, an upstream that returns a partial response with a mismatched framing header, and a downstream forwarder that does not remove or recalculate Content-Length.
Patches
Patched in undici v6.28.0, v7.29.0, and v8.9.0. Users should upgrade to one of these versions or later.
Workarounds
Disable interceptors.retry() for untrusted upstreams.
Remove or recalculate Content-Length before forwarding a response body assembled or transformed by Undici.
Improper Neutralization of CRLF Sequences ('CRLF Injection')
Affected range
<6.24.0
Fixed version
6.24.0
CVSS Score
4.6
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:N
EPSS Score
0.276%
EPSS Percentile
18th percentile
Description
Impact
When an application passes user-controlled input to the upgrade option of client.request(), an attacker can inject CRLF sequences (\r\n) to:
Inject arbitrary HTTP headers
Terminate the HTTP request prematurely and smuggle raw data to non-HTTP services (Redis, Memcached, Elasticsearch)
The vulnerability exists because undici writes the upgrade value directly to the socket without validating for invalid header characters:
Improper Neutralization of CRLF Sequences ('CRLF Injection')
Affected range
<6.28.0
Fixed version
6.28.0
CVSS Score
4.2
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N
EPSS Score
0.193%
EPSS Percentile
8th percentile
Description
Impact
When an application passes a duck-typed blob-like body to undici's HTTP/1.1 dispatcher (via request(), stream(), pipeline(), or dispatch()) with a .type derived from untrusted input, an attacker can inject CRLF sequences (\r\n) to append arbitrary HTTP headers and potentially smuggle a second request past the upstream.
The vulnerable branch in lib/dispatcher/client-h1.js pushes body.type directly into the outgoing headers with no validation, while every other header path in undici goes through isValidHeaderValue():
The bug requires a hand-rolled duck-typed blob object or a Blob subclass with a controlled .type. Native Blob is safe because its constructor strips CRLF from .type. fetch() is unaffected because it validates via the Headers class. Ecosystem consumers that build duck-typed blob shapes from user input include form-data-encoder, formdata-polyfill, and formdata-node.
Same defect class as CVE-2022-35948 (explicit content-type sink, fixed in undici 5.8.2) and CVE-2026-1527 (upgrade option sink, fixed in 6.24.0 / 7.24.0), both closed by adding isValidHeaderValue() on their respective sinks. This branch was missed.
Patches
Patched in undici v6.28.0, v7.29.0, and v8.9.0. Users should upgrade to one of these versions or later.
Workarounds
Set an explicit, validated content-type header on the request options (skips the vulnerable branch).
Use a native Blob (or fetch-blob) instead of a hand-rolled duck-typed object.
Reject control characters in the MIME type before assigning it to .type.
Use fetch() instead of the non-fetch APIs.
Time-of-check Time-of-use (TOCTOU) Race Condition
Affected range
<6.27.0
Fixed version
6.27.0
CVSS Score
3.7
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N
EPSS Score
0.270%
EPSS Percentile
18th percentile
Description
Impact
Undici's HTTP/1.1 client is vulnerable to response queue poisoning on reused keep-alive sockets. An attacker-controlled upstream server can inject an unsolicited HTTP/1.1 response onto an idle socket after a request completes. When the client dispatches the next request on that socket, it associates the injected response with the new request, causing responses to be delivered to the wrong requests.
This requires an attacker-controlled or compromised upstream HTTP/1.1 server and keep-alive connection reuse.
Patches
Upgrade to undici v6.27.0, v7.28.0 or v8.5.0.
Workarounds
Disable keep-alive connection reuse by setting keepAliveTimeout: 0 on the Client or Pool.
Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')
Affected range
<6.28.1
Fixed version
6.28.1
CVSS Score
3.7
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N
EPSS Score
0.239%
EPSS Percentile
14th percentile
Description
Impact
Undici's interceptors.retry() can resume a request after a partial response and append the resumed bytes to an already partially delivered body, while the application still receives the original response's status and headers. When that response carried a Content-Length, the application can receive a longer body. Applications that forward Undici's status, headers, and body downstream without recalculating framing, for example proxy or gateway applications, may emit a response whose body exceeds the forwarded Content-Length, and the excess bytes can be read as the start of a subsequent HTTP response (downstream response splitting or desynchronization).
For example, a 404 Not Found with Content-Length: 2 that sends one byte then closes can be resumed with an open-ended Range request, and the resumed 206 Partial Content bytes are appended, so the application receives more than two body bytes while still seeing Content-Length: 2. The bug requires interceptors.retry() enabled, an attacker-controlled or faulty upstream, and a downstream forwarder that does not recalculate Content-Length.
Patches
Patched in undici v6.28.1, v7.29.1, and v8.10.2. Upgrade to one of these or later.
Workarounds
Disable interceptors.retry() for untrusted upstreams, or set maxRetries: 0.
Remove or recalculate Content-Length before forwarding a response body assembled by Undici.
Permissive List of Allowed Inputs
Affected range
<6.27.0
Fixed version
6.27.0
CVSS Score
3.7
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N
EPSS Score
0.238%
EPSS Percentile
14th percentile
Description
Impact
When undici parses a Set-Cookie header, it accepts any SameSite attribute value that contains Strict, Lax, or None as a substring, rather than the case-insensitive exact match specified by RFC 6265. Non-spec values are silently mapped to one of the three standard tokens:
SameSite=NoneOfYourBusiness is parsed as None, the most permissive setting.
SameSite=StrictLax is parsed as Lax, a downgrade from Strict.
Affected applications are those that consume Set-Cookie headers from server responses (for example via undici's fetch or proxy code paths) and then forward or rely on the parsed sameSite attribute. A malicious or non-compliant server can coerce the consumer's view of a cookie's SameSite policy to a weaker value, silently degrading the SameSite enforcement the cookie is supposed to provide.
This was introduced in undici 5.15.0 when the cookies feature was added.
Patches
Upgrade to undici v6.27.0, v7.28.0 or v8.5.0.
Workarounds
After parsing a Set-Cookie header, validate that the resulting sameSite attribute is one of 'Strict', 'Lax', or 'None' (exact, case-insensitive) before forwarding or relying on it.
@fastify/busboy2.1.1 (npm)
pkg:npm/%40fastify/busboy@2.1.1
Improper Check for Unusual or Exceptional Conditions
Affected range
>=1.0.0 <3.2.1
Fixed version
3.2.1
CVSS Score
7.5
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Score
0.493%
EPSS Percentile
40th percentile
Description
Impact
Versions of @<!-- -->fastify/busboy from 1.0.0 and prior to 3.2.1 are vulnerable to a Denial of Service. The multipart header parser stores part-header names on a plain JavaScript object, so a part header named __proto__ or constructor resolves to an inherited value that is not an array, and the parser throws TypeError: this.header[h].push is not a function. Through the documented req.pipe(busboy) integration this surfaces as an error event, while direct write()/end() usage throws synchronously and can terminate the Node.js process if uncaught. The parser runs before application middleware, so any unauthenticated client that can submit multipart/form-data is affected.
Patches
Fixed in version 3.2.1.
Workarounds
Attach an error listener to the Busboy stream so the parser failure is handled rather than crashing the process, and wrap direct write()/end() calls in a try/catch. Upgrading to 3.2.1 removes the failure entirely.
Improper Neutralization of CRLF Sequences ('CRLF Injection')
Affected range
<3.2.2
Fixed version
3.2.2
CVSS Score
5.8
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:N
EPSS Score
0.326%
EPSS Percentile
24th percentile
Description
Impact
@<!-- -->fastify/busboy 3.2.1 and earlier retains a bare carriage return or line feed inside the parsed Content-Disposition parameters returned to the application. The multipart header parser ends a header line only on the two-byte \r\n sequence, so a lone CR or LF embedded in a part header is carried verbatim into the filename and field name delivered by the file and field events. An application that forwards the supplied filename or name into a CR/LF-sensitive sink cannot anticipate the embedded control character.
An attacker who uploads a file whose filename or field name contains a bare CR or LF can pollute filenames on disk when the upload is saved under its original name, inject forged lines into logs, or inject headers on a downstream hop that does not re-validate CR/LF, such as object-storage metadata or a proxied backend. Applications using @<!-- -->fastify/busboy directly, or through @<!-- -->fastify/multipart and its consumers, are affected.
Patches
Upgrade to @<!-- -->fastify/busboy 3.2.2 or later.
Workarounds
Validate or strip carriage return and line feed characters from the filename and field name before using them in any filesystem, logging, or outbound-header context.
node-forge through 1.4.0 fails to validate element count in nested DigestAlgorithm sequences during RSA PKCS#1 v1.5 signature verification. Attackers can embed garbage bytes inside the DigestAlgorithm sequence to forge valid signatures for arbitrary messages using low-exponent RSA keys. This is an incomplete fix for CVE-2026-33894.
@opentelemetry/core2.6.1 (npm)
pkg:npm/%40opentelemetry/core@2.6.1
Allocation of Resources Without Limits or Throttling
Affected range
<2.8.0
Fixed version
2.8.0
CVSS Score
5.3
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
EPSS Score
0.402%
EPSS Percentile
32nd percentile
Description
Overview
W3CBaggagePropagator.extract() in @<!-- -->opentelemetry/core does not enforce size limits when parsing inbound baggage HTTP headers. The W3C Baggage specification recommends a maximum of 8,192 bytes and 180 entries; these limits were only enforced on the outbound (inject()) path, not on the inbound (extract()) path. Parsing oversized baggage causes memory allocation proportional to the header size without any cap.
Impact
The practical availability impact for most Node.js deployments is limited. Node.js enforces a default --max-http-header-size of 16,384 bytes on the total combined size of all HTTP headers, constraining what an external attacker can deliver before the propagator is reached. Additionally, the header is already in memory (parsed by the HTTP layer) by the time it reaches the propagator - the additional allocation is the overhead of splitting into entry objects, not an unbounded read.
The risk is higher when transport-layer limits are absent - e.g., non-HTTP transports (messaging systems, custom TextMapGetter implementations) or deployments that have raised --max-http-header-size.
Remediation
Update @<!-- -->opentelemetry/core to version 2.8.0 or later. The fix enforces limits consistent with the W3C Baggage specification at the propagator level:
Maximum total baggage size: 8,192 bytes
Maximum number of entries: 180
Maximum per-entry size: 4,096 bytes
Headers that exceed these limits are truncated at the point the limit is reached.
Workarounds
Ensure header size limits are configured at the server or gateway level. The default Node.js HTTP header limit (16 KB) mitigates external attack vectors independently of this fix. For non-HTTP transports receiving baggage from untrusted sources, validate input size before passing it to the propagator.
Versions of @<!-- -->ai-sdk/provider-utils before 3.0.28, from 4.0.0 before 4.0.33, and from 5.0.0 before 5.0.1 are vulnerable to uncontrolled resource consumption. The createJsonResponseHandler, createJsonErrorResponseHandler, and createStatusCodeErrorResponseHandler functions in packages/provider-utils/src/response-handler.ts read response bodies without a shared size limit, allowing a remote attacker with low privileges to cause excessive memory consumption. The exploit has been publicly disclosed and may be utilized.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
3.0.68→3.0.69Warning
Some dependencies could not be looked up. Check the Dependency Dashboard for more information.
Release Notes
cline/cline (cline)
v3.0.69: CLI v3.0.69Compare Source
npxoruvxon Windows now load. They often took longer than the 3-second startup limit and were silently dropped, so their tools never showed up. The default is now 10 secondsproviderOptions key 'openai-compatible'deprecation warning on every responsecline config --jsonprints JSON again. It opened the interactive view instead, which failed outside a terminal;cline config --json mcpprinted plain text-y/--yolois now listed incline --help, with a warning to use it only in sandboxed environmentsFull Changelog: cline/cline@cli-v3.0.68...cli-v3.0.69
Configuration
📅 Schedule: (in timezone Europe/Berlin)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR has been generated by Mend Renovate CLI.