← All rules
mcp-scan rule
Suspicious execution pattern
suspicious-executionhighWhat mcp-scan saw
The AST scanner joined the command with its arguments and matched either a shell name (bash, sh, zsh, fish, ksh, csh) directly followed by -c, or the standalone word eval or exec anywhere in the joined string.
Why it fires
Running the server through a shell means the arguments are re-parsed by that shell, which puts quoting, globbing, and substitution back in play after the client already handed them over safely. It is also the layer where an injected argument becomes an executed command, which is why it is graded above a plain configuration smell.
When this is a false positive
- \bexec\b matches inside a hyphenated package name. A server invoked as mcp-exec-server or command-exec fires with no shell involved.
- docker exec and kubectl exec are ordinary ways to reach a containerized MCP server, and both contain the bare word exec.
- A path argument with a segment named exec or eval, for example /opt/tools/exec/run.js.
- The shell test requires the shell name immediately followed by -c, so a plain sh wrapper without -c does not fire. A clean report here is not proof the server avoids a shell.
How to fix it
- Read the joined command in the finding and identify which of the two shapes matched.
- For a real shell -c invocation, replace it with a direct command plus an args array. The client passes args to the process without a shell, which is the safer path.
- If you need a shell for setup, move that setup into a script file and invoke the script directly rather than passing it inline.
- For a docker exec or kubectl exec wrapper, keep it and note the dismissal. The rule is matching the word, not the risk, in that case.
- To suppress, add 'suspicious-execution' to suppressRules in .mcp-scan.json.