serverless MCP Server vulnerable to command injection in list-projects tool
Description
The Serverless Framework is a framework for using AWS Lambda and other managed cloud services to build applications. Starting in version 4.29.0 and prior to version 4.29.3, a command injection vulnerability exists in the Serverless Framework's built-in MCP server package (@serverless/mcp). This vulnerability only affects users of the experimental MCP server feature (serverless mcp), which represents less than 0.1% of Serverless Framework users. The core Serverless Framework CLI and deployment functionality are not affected. The vulnerability is caused by the unsanitized use of input parameters within a call to child_process.exec, enabling an attacker to inject arbitrary system commands. Successful exploitation can lead to remote code execution under the server process's privileges. The server constructs and executes shell commands using unvalidated user input directly within command-line strings. This introduces the possibility of shell metacharacter injection (|, >, &&, etc.). Version 4.29.3 fixes the issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
serverlessnpm | >= 4.29.0, < 4.29.3 | 4.29.3 |
Affected products
1- Range: 1.0.0-beta.2, 1.0.0-rc.1, 1.0.0-rc.2, …
Patches
1681ca039550crefactor(mcp): validate workspaceDir and use execFile in project-finder (#13203)
2 files changed · +74 −16
.github/workflows/release-framework.yml+4 −4 modified@@ -46,7 +46,7 @@ jobs: fail-fast: false # Continue running jobs even if one or more jobs in the matrix fail matrix: group: - - latest-linux + - ubuntu-latest - latest-arm-linux - gh-windows-latest runs-on: @@ -93,7 +93,7 @@ jobs: name: "Release: Canary & Tag" needs: [ test-matrix, test-engine ] runs-on: - group: latest-linux + group: ubuntu-latest outputs: new_version: ${{ steps.tag_if_new_version.outputs.new_version }} steps: @@ -147,7 +147,7 @@ jobs: needs: release-canary if: needs.release-canary.outputs.new_version runs-on: - group: latest-linux + group: ubuntu-latest steps: - name: "Checkout Code" uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 @@ -185,7 +185,7 @@ jobs: name: "Release: NPM Installer" needs: release-stable runs-on: - group: latest-linux + group: ubuntu-latest defaults: run: working-directory: ./packages/sf-core-installer
packages/mcp/src/lib/project-finder.js+70 −12 modified@@ -1,11 +1,36 @@ import fs from 'fs' import path from 'path' import { promisify } from 'util' -import { exec } from 'child_process' +import { execFile } from 'child_process' import { enhanceProjectsWithServiceDetails } from './serverless-framework/service-details.js' -const execAsync = promisify(exec) +const execFileAsync = promisify(execFile) const readFileAsync = promisify(fs.readFile) +const statAsync = promisify(fs.stat) + +/** + * Validate that the provided path is an existing directory + * + * @param {string} dirPath - The directory path to validate + * @returns {Promise<string>} - The validated absolute path + * @throws {Error} - If the path is not a valid directory + */ +async function validateWorkspaceDir(dirPath) { + if (!dirPath || typeof dirPath !== 'string') { + throw new Error('Workspace directory must be a non-empty string') + } + + // Resolve to absolute path to prevent relative path tricks + const absolutePath = path.resolve(dirPath) + + // Verify the path exists and is a directory + const stats = await statAsync(absolutePath) + if (!stats.isDirectory()) { + throw new Error(`Path is not a directory: ${absolutePath}`) + } + + return absolutePath +} /** * Find all Serverless Framework projects in the workspace @@ -16,12 +41,23 @@ const readFileAsync = promisify(fs.readFile) */ export async function findServerlessFrameworkProjects(workspaceDir) { try { - // If no workspace directory is provided, use the current working directory - const rootDir = workspaceDir || process.cwd() + // Validate and resolve the workspace directory + const rootDir = await validateWorkspaceDir(workspaceDir || process.cwd()) // Use find command to locate all serverless.yml files, excluding node_modules and .git - const { stdout } = await execAsync( - `find "${rootDir}" -name "serverless.yml" -not -path "*/node_modules/*" -not -path "*/\.git/*"`, + const { stdout } = await execFileAsync( + 'find', + [ + rootDir, + '-name', + 'serverless.yml', + '-not', + '-path', + '*/node_modules/*', + '-not', + '-path', + '*/.git/*', + ], { maxBuffer: 10 * 1024 * 1024 }, // Increase buffer size for large workspaces ) @@ -50,18 +86,40 @@ export async function findServerlessFrameworkProjects(workspaceDir) { * @returns {Promise<string[]>} - Array of file paths */ async function findYamlFiles(workspaceDir) { - // If no workspace directory is provided, use the current working directory - const rootDir = workspaceDir || process.cwd() + // Validate and resolve the workspace directory + const rootDir = await validateWorkspaceDir(workspaceDir || process.cwd()) // Use find command to locate all yaml/yml files, excluding node_modules and .git // We'll run two separate find commands to avoid syntax issues with complex expressions - const { stdout: yamlStdout } = await execAsync( - `find "${rootDir}" -name "*.yaml" -not -path "*/node_modules/*" -not -path "*/\.git/*"`, + const { stdout: yamlStdout } = await execFileAsync( + 'find', + [ + rootDir, + '-name', + '*.yaml', + '-not', + '-path', + '*/node_modules/*', + '-not', + '-path', + '*/.git/*', + ], { maxBuffer: 5 * 1024 * 1024 }, // Increase buffer size for large workspaces ) - const { stdout: ymlStdout } = await execAsync( - `find "${rootDir}" -name "*.yml" -not -path "*/node_modules/*" -not -path "*/\.git/*"`, + const { stdout: ymlStdout } = await execFileAsync( + 'find', + [ + rootDir, + '-name', + '*.yml', + '-not', + '-path', + '*/node_modules/*', + '-not', + '-path', + '*/.git/*', + ], { maxBuffer: 5 * 1024 * 1024 }, // Increase buffer size for large workspaces )
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-rwc2-f344-q6w6ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-69256ghsaADVISORY
- github.com/serverless/serverless/blob/6213453da7df375aaf12fb3522ab8870488fc59a/packages/mcp/src/tools/list-projects.jsghsax_refsource_MISCWEB
- github.com/serverless/serverless/commit/681ca039550c7169369f98780c6301a00f2dc4c4ghsax_refsource_MISCWEB
- github.com/serverless/serverless/releases/tag/sf-core%404.29.3ghsax_refsource_MISCWEB
- github.com/serverless/serverless/security/advisories/GHSA-rwc2-f344-q6w6ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.