GHSA-VCV2-R9JH-99M5: execSync-to-execFileSync Root-Cause Fix in agentic-flow MCP Tools
Summary
The patch addresses a classic CWE-78 flaw by replacing shell-interpreted command construction with argv-based process execution across the affected MCP server entry points, and downstream consumers are updated to require agentic-flow 2.0.14. Based on the provided diffs, this is a root-cause remediation for the demonstrated injection pattern rather than a superficial input filter.
Analysis
Vulnerability
GHSA-VCV2-R9JH-99M5 describes OS command injection in agentic-flow MCP server tools. The vulnerable code assembled shell command strings with untrusted parameters such as key, value, namespace, pattern, task, agent, model, and related options, then executed those strings via Node.js execSync. Because execSync executes through a shell when given a command string, attacker-controlled content can be reinterpreted as shell metacharacters or additional commands.
The provided diffs show this pattern in multiple MCP server files, including agentic-flow/src/mcp/fastmcp/servers/claude-flow-sdk.ts, http-sse.ts, and http-streaming-updated.ts in the agentic-flow patch PR. The advisory impact is especially relevant in agent-driven workflows because malicious external content can flow into tool parameters without a human explicitly typing shell syntax.
const cmd = `npx --yes agentic-flow --agent "${agent}" --task "${task}"`;
const result = execSync(cmd, { encoding: 'utf-8' });That construction is intrinsically unsafe: quoting inside a shell string is not a reliable security boundary, and every interpolated field expands the attack surface. The issue is therefore the use of shell-string execution for parameterized tool invocations, not merely a lack of escaping for one field.
Patch
The patch replaces execSync string execution with execFileSync using explicit argv arrays and shell: false across the affected call sites. This change is visible in ruvnet/agentic-flow#170. The patched files also add an inline security comment warning against reintroducing template-string shell execution.
const NPX_EXEC_OPTS = { shell: false as const };
const args: string[] = ['--yes', 'agentic-flow', '--agent', agent, '--task', task];
if (model) args.push('--model', model);
if (provider) args.push('--provider', provider);
const result = execFileSync('npx', args, {
...NPX_EXEC_OPTS,
encoding: 'utf-8'
});This is the correct primitive for the observed bug class. With argv-based execution, user-controlled values remain opaque argument elements rather than being reparsed by a shell. The same remediation pattern is applied to memory, swarm, agent spawn, task orchestration, list, and conflict operations in the shown snippets.
The downstream dependency update in ruvnet/ruflo#2415 bumps consumers from agentic-flow ^2.0.13 to ^2.0.14 and adds a wrapper-level constraint "agentic-flow": ">=2.0.14". That is an important distribution step so patched consumers do not continue resolving the vulnerable package version.
Review
Pros
- Eliminates the root injection sink by removing shell-string execution in the affected paths shown in the patch.
- Uses the safer Node.js API shape: executable plus argv array, with
shell: falseexplicitly set. - Applies the fix consistently across multiple MCP server implementations rather than patching only one endpoint.
- Preserves functional behavior for optional flags by conditionally appending argv elements instead of concatenating shell fragments.
- Adds maintainability guidance in comments that documents the security invariant and reduces regression risk.
- Updates downstream packages to consume
agentic-flow2.0.14, including a wrapper-level minimum version constraint inruflo/package.json.
Cons
- The patch summary does not show added regression tests that specifically exercise malicious metacharacter payloads against these tool parameters.
- Invocation still depends on
npx; while argv execution removes shell injection, operational risk remains if package resolution or execution policy aroundnpxis undesirable in production. - The review is limited to the displayed call sites; any other shell-string executions elsewhere in the codebase would need separate verification.
- The fix addresses command injection, but it does not by itself constrain what legitimate subcommands may do once invoked with attacker-chosen but syntactically valid arguments.
Verdict
Root-cause.
Based on the provided diffs, the remediation directly fixes the underlying CWE-78 condition by changing the execution model from shell-interpreted strings to argv-based process spawning with shell: false. That is materially stronger than escaping or blocklisting because it removes shell parsing from the trust boundary. The dependency bumps in the ruflo update also improve rollout integrity by ensuring consumers resolve the fixed agentic-flow release. Engineers should still add negative tests for shell metacharacters and audit the broader repository for any remaining execSync or similar string-based process launches, but the shown patch is technically sound and source-aligned as a root-cause fix.
Sources: agentic-flow patch PR, ruflo dependency update PR, GitHub Security Advisory, CVE Reports summary.
Recommended Labs
Try this vulnerability pattern yourself with hands-on labs.
- Command Injection.js
Best direct match for this advisory’s root issue: unsanitized user-controlled input reaching a shell in a JavaScript/Node.js context. This hands-on defensive lab should help practice identifying shell execution sinks, removing dangerous shell interpolation, and replacing shell-based patterns with safer command invocation and strict input handling.
- BadVal.js
Strong secondary fit because the GHSA describes arbitrary OS command execution through execSync. This JavaScript lab is tagged as remote command execution and is useful for practicing defensive fixes where attacker-controlled values cross trust boundaries and trigger dangerous process execution behavior.
- Curl Eval.github
Recommended as a broader learning resource because the advisory involves AI/automation tooling processing untrusted external content before execution. This CI/CD-focused lab covers command injection patterns in automated workflows, reinforcing the same defensive principle: never pass untrusted content into shell-evaluated commands.