Heroku MCP
npm:@heroku/mcp-server@1.2.2
Severity breakdown
Worst finding
Tool `pg_psql` exposes a code/command execution surface
· pg_psql
`pg_psql` looks like it executes code or shell commands (Execute SQL queries: analyze, debug, modify schema, manage data). Arbitrary execution is the maximal authority a tool can hold -- it subsumes every other caveat, so it should never be exposed to an agent without a hard sandbox and an explicit, narrowly-scoped capability.
fix: Do not expose raw code/shell execution to an agent. If unavoidable, run it in a disposable sandbox with no network + no host FS, gate it behind a capframe-bind capability scoped to an allow-list of commands, and require holder-of-key proof per call.
All 35 findings
- criticalTool `pg_psql` exposes a code/command execution surface· pg_psqlexcessive agency
`pg_psql` looks like it executes code or shell commands (Execute SQL queries: analyze, debug, modify schema, manage data). Arbitrary execution is the maximal authority a tool can hold -- it subsumes every other caveat, so it should never be exposed to an agent without a hard sandbox and an explicit, narrowly-scoped capability.
fix: Do not expose raw code/shell execution to an agent. If unavoidable, run it in a disposable sandbox with no network + no host FS, gate it behind a capframe-bind capability scoped to an allow-list of commands, and require holder-of-key proof per call.
- criticalTool `deploy_one_off_dyno` exposes a code/command execution surface· deploy_one_off_dynoexcessive agency
`deploy_one_off_dyno` looks like it executes code or shell commands ( Run code/commands in Heroku one-off dyno with network and filesystem access. Requirements: - Show command output - Use app_info for buildpack detection - Support shell setup commands - Use stdout/stderr Features: - Network/filesystem access - Environment variables - File operations - Temp directory handling Usage: 1. Use Heroku runtime 2. Proper syntax/imports 3. Organized code structure 4. Package management: - Define dependencies - Minimize external deps - Prefer native modules Example package.json: ```json { "type": "module", "dependencies": { "axios": "^1.6.0" } } ``` ). Arbitrary execution is the maximal authority a tool can hold -- it subsumes every other caveat, so it should never be exposed to an agent without a hard sandbox and an explicit, narrowly-scoped capability.
fix: Do not expose raw code/shell execution to an agent. If unavoidable, run it in a disposable sandbox with no network + no host FS, gate it behind a capframe-bind capability scoped to an allow-list of commands, and require holder-of-key proof per call.
- highTool `create_app` name implies a side effect that is not declared· create_appexcessive agency
`create_app` looks like a side-effecting tool (its name contains a mutation verb), but its `side_effects` declaration is []. A policy synthesizer cannot produce safe rules for this tool because it cannot tell what it actually does.
fix: Declare the tool's true side effects explicitly. If the tool is genuinely read-only, rename it to match (e.g. `email.preview` rather than `email.send`).
- highTool `create_addon` name implies a side effect that is not declared· create_addonexcessive agency
`create_addon` looks like a side-effecting tool (its name contains a mutation verb), but its `side_effects` declaration is []. A policy synthesizer cannot produce safe rules for this tool because it cannot tell what it actually does.
fix: Declare the tool's true side effects explicitly. If the tool is genuinely read-only, rename it to match (e.g. `email.preview` rather than `email.send`).
- highTool `pipelines_create` name implies a side effect that is not declared· pipelines_createexcessive agency
`pipelines_create` looks like a side-effecting tool (its name contains a mutation verb), but its `side_effects` declaration is []. A policy synthesizer cannot produce safe rules for this tool because it cannot tell what it actually does.
fix: Declare the tool's true side effects explicitly. If the tool is genuinely read-only, rename it to match (e.g. `email.preview` rather than `email.send`).
- mediumTool `list_apps` accepts unconstrained string input· list_appsunconstrained input
The following string parameter(s) have no `maxLength` constraint: `space`, `team`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `get_app_info` accepts unconstrained string input· get_app_infounconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `create_app` accepts unconstrained string input· create_appunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `region`, `space`, `team`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `rename_app` accepts unconstrained string input· rename_appunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `newName`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `maintenance_on` accepts unconstrained string input· maintenance_onunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `maintenance_off` accepts unconstrained string input· maintenance_offunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `get_app_logs` accepts unconstrained string input· get_app_logsunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `dynoName`, `processType`, `source`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `list_addons` accepts unconstrained string input· list_addonsunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `get_addon_info` accepts unconstrained string input· get_addon_infounconstrained input
The following string parameter(s) have no `maxLength` constraint: `addon`, `app`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `get_addon_info` description mentions money but no `money` side-effect is declared· get_addon_infoexcessive agency
Description: "Get add-on details: plan, state, billing" -- this references money/payment/refund/etc., but the declared side_effects ([]) don't include `money`. A capframe-bind policy that relies on declared side_effects to scope spend caveats will under-scope this tool.
fix: Add `money` to the tool's `side_effects` declaration, or rewrite the description to clarify that no actual money moves.
- mediumTool `create_addon` accepts unconstrained string input· create_addonunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `as`, `name`, `serviceAndPlan`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `list_addon_plans` accepts unconstrained string input· list_addon_plansunconstrained input
The following string parameter(s) have no `maxLength` constraint: `service`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `pg_psql` accepts unconstrained string input· pg_psqlunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `command`, `credential`, `database`, `file`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `pg_info` accepts unconstrained string input· pg_infounconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `database`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `pg_ps` accepts unconstrained string input· pg_psunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `database`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `pg_locks` accepts unconstrained string input· pg_locksunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `database`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `pg_outliers` accepts unconstrained string input· pg_outliersunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `database`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `pg_credentials` accepts unconstrained string input· pg_credentialsunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `database`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `pg_kill` accepts unconstrained string input· pg_killunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `database`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `pg_maintenance` accepts unconstrained string input· pg_maintenanceunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `database`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `pg_backups` accepts unconstrained string input· pg_backupsunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `pg_upgrade` accepts unconstrained string input· pg_upgradeunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `confirm`, `database`, `version`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `ps_list` accepts unconstrained string input· ps_listunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `ps_scale` accepts unconstrained string input· ps_scaleunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `dyno`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `ps_restart` accepts unconstrained string input· ps_restartunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `dyno-name`, `process-type`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `pipelines_create` accepts unconstrained string input· pipelines_createunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `name`, `stage`, `team`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `pipelines_promote` accepts unconstrained string input· pipelines_promoteunconstrained input
The following string parameter(s) have no `maxLength` constraint: `app`, `to`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `pipelines_info` accepts unconstrained string input· pipelines_infounconstrained input
The following string parameter(s) have no `maxLength` constraint: `pipeline`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `deploy_to_heroku` accepts unconstrained string input· deploy_to_herokuunconstrained input
The following string parameter(s) have no `maxLength` constraint: `appJson`, `rootUri`, `spaceId`, `tarballUri`, `teamId`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
- mediumTool `deploy_one_off_dyno` accepts unconstrained string input· deploy_one_off_dynounconstrained input
The following string parameter(s) have no `maxLength` constraint: `command`, `size`. Unbounded strings let an attacker stuff arbitrary payloads through the tool, including indirect-injection content.
fix: Add a `maxLength` to each string property, or constrain with an `enum` or `pattern`. Most legitimate tool inputs fit under a few hundred bytes.
How this was scored
Source sandbox — live tools/list captured in an ephemeral Docker container (parameter schemas included → R1/R2/R4 fire). Findings are emitted by the public capframe.findings.v1 schema. Score = 100 − (10·Critical + 4·High + 2·Medium + 1·Low), clamped to [0, 100].
Disagree with a finding? Open an issue.