fix(run): remove new Function() execution of registry stdioFunction (CWE-94) - #798
Open
sebastionoss wants to merge 1 commit into
Open
Conversation
…CWE-94) The stdioFunction connection path executed a string received from the Smithery registry via new Function(), giving arbitrary code execution. A malicious or compromised registry entry could compromise any user running the server and exfiltrate the ServerConfig (including API keys) passed to the executed function. The stdioFunction path was already marked deprecated in favour of bundle connections. This change removes the dynamic-code path entirely and returns a clear error asking the server author to republish as a bundle connection.
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.
What's added in this PR?
This PR removes an arbitrary code execution sink in
prepareStdioConnectionwhere a JavaScript string fetched from the Smithery registry was passed tonew Function()and executed on the user's machine every time they ransmithery run <server>.Vulnerability summary
src/utils/run/prepare-stdio-connection.tsnew Function("config", \return (${bundleConnection.stdioFunction})(config)`)followed by immediate invocation with the user'sconfig` (which contains API keys / secrets).connection.stdioFunction— a string returned by the Smithery registry API for the requested server. From the local CLI's perspective this is untrusted remote input: anyone who publishes a server, or anyone who can influence a registry response, controls the string.smithery run <qualifiedName>against a server whosestdioFunctionconnection type is selected.The evaluated function is invoked with the caller's
configobject, so the attacker not only gets arbitrary JS execution in the user's Node process but also gets the user's API keys handed to them as an argument.Fix
The
stdioFunctioncase inprepareStdioConnectionno longer evaluates the string. It throws a clear error asking the server author to republish as a bundle connection:The
commandandbundleconnection paths are untouched. TheisValidStdioConnectionhelper and the now-unusedLocalStdioConnectionimport were removed since nothing else needed them.Rationale for removal rather than sandboxing:
stdioFunctionwas already marked@deprecatedin favour of bundle connections, and there is no safe way to execute an attacker-controlled JS string in-process. A vm2/isolated-vm sandbox would add a large dependency and a long track record of escape CVEs for a code path the project is already moving away from.Tests
stdioFunctionpayload that writes a file to/tmpif executed.pnpm test→ 395 passed / 395.Proof of concept
Reproduces against the pre-patch code. No network required — the malicious registry response is simulated by constructing the
Serverobject directly, exactly asprepareStdioConnectionreceives it from the registry client.The end-user command that hits this path in the wild is:
After this patch the same input throws before any evaluation happens; the automated regression test in
src/utils/run/__tests__/prepare-stdio-connection.test.tsdemonstrates that a payload attemptingfs.writeFileSyncnever executes.Security analysis / adversarial review
Before submitting we tried to disprove this:
stdioFunctionfield is server-author-supplied string data returned verbatim to every CLI user. No signing or allowlist is applied client-side.new Functionruns in the same Node realm as the CLI, with full access torequire,process, filesystem, network, and theconfigargument (secrets).smithery run <name>selects the connection type automatically viadetermineConnectionType, andstdioFunctionis picked whenever the registry entry contains one.stdioFunctionwas already@deprecatedin the codebase in favour of bundle connections; the error message directs affected server authors to the supported path.What's the issues or discussion related to this PR?
No public issue — reported directly via PR since the fix is a small, self-contained removal of a deprecated code path and the diff itself discloses the sink. Happy to adjust the approach (e.g. keep the case but gate behind an explicit opt-in flag) if the maintainers prefer, but given
stdioFunctionis already deprecated, removal seemed cleanest.Discovered by the Sebastion AI GitHub App.