heroku-mcp-server
- Type
- MCP server
- Scanned
- Oct 29, 2026
- Version
- 970528fc07bc
- Files read
- 85
This MCP server lets AI coding agents manage Heroku applications, databases, add-ons, pipelines, and deployments using the Heroku CLI and Platform API. Its biggest risks are that prompt-injected tool arguments can execute local commands or read local files via psql, pack and upload arbitrary local directories during deployment, or overwrite local files via AI inference output.
What it can reach
- Shell commands
- Spawns the Heroku CLI REPL (via npx or local heroku binary), runs git commands in workspace directories, and invokes local psql through heroku pg:psql.
- Files
- Reads and archives local directories for app deployment, reads local SQL files for pg_psql, and writes temporary and user-specified output files for AI inference.
- Network
- Connects to api.heroku.com, devcenter.heroku.com, S3 source upload URLs, build stream URLs, and Heroku Rendezvous TLS endpoints.
- Secrets
- Reads HEROKU_API_KEY from the environment and uses existing Heroku CLI authentication sessions.
Findings
-
HighLocal command execution and arbitrary file read via pg_psql tool arguments
The pg_psql tool passes the user-supplied 'file' and 'command' arguments to 'heroku pg:psql', which runs the local 'psql' client. An attacker using prompt injection can pass a local file path in 'file' to read files from the user's machine, or use psql backslash meta-commands (such as '\! <cmd>' or '\copy') in 'command' or 'file' to execute arbitrary shell commands locally once connected to a Heroku Postgres database.
Fix: Remove the 'file' parameter from pg_psql, disallow psql backslash meta-commands in 'command', or pass '--set=ON_ERROR_STOP=1' without allowing meta-command execution.
-
HighArbitrary local directory upload via unvalidated rootUri in deploy_to_heroku
src/tools/deploy-to-heroku.ts:289
The deploy_to_heroku tool accepts any filesystem path in 'rootUri' and archives all files in that directory into a tarball that is uploaded to Heroku. Because 'appJson' can be provided inline, 'rootUri' does not need to contain an app.json file. Through prompt injection, an attacker can point 'rootUri' at sensitive folders (such as ~/.ssh or ~/.aws) to upload and exfiltrate their contents to a Heroku app.
Fix: Restrict 'rootUri' to an explicitly configured workspace root directory and require user confirmation before packaging and uploading local directories.
-
HighArbitrary local file write via output parameter in make_ai_inference
When '@heroku/plugin-ai' is installed, the make_ai_inference tool exposes an 'output' parameter that passes '--output' to 'heroku ai:agents:call'. A prompt-injected agent can set 'output' to any path on the user's filesystem and control the model prompt, allowing it to overwrite sensitive files such as shell profiles, SSH keys, or scripts.
Fix: Remove the 'output' file path parameter from make_ai_inference and return the inference output solely through the MCP tool response.
-
MediumUnpinned runtime download and execution of heroku@latest via npx
src/repl/heroku-cli-repl.ts:126
Before checking for an installed 'heroku' CLI binary, the server checks if 'npx' exists and spawns 'npx -y heroku@latest repl'. Because npx is present in standard Node.js environments, the server downloads and executes the latest unpinned 'heroku' package from npm at runtime with HEROKU_API_KEY in its environment.
Fix: Prefer the locally installed 'heroku' CLI binary first, and if npx fallback is retained, pin to a specific verified version rather than 'heroku@latest'.
-
LowUnencoded path parameters in Heroku Platform API requests
src/services/app-service.ts:20
Service methods interpolate 'appIdentity', 'buildIdentity', and 'dynoIdentity' directly into API URL paths without URL-encoding or strict character validation. In deploy_to_heroku and deploy_one_off_dyno, the 'name' parameter is only validated for length (5 to 30 characters), allowing path traversal segments ('../') in authenticated requests to api.heroku.com.
Fix: Validate app names against Heroku's naming pattern (/^[a-z][a-z0-9-]{1,28}[a-z0-9]$/) and wrap path segments with encodeURIComponent().