BoundBench

Prowler MCP Server

Cloud security posture platform; ships the Prowler MCP Server and Lighthouse AI assistant

github.com/prowler-cloud/prowler › mcp_server · 2026-10-03 · 383a9bf

Defense-in-depth score

4.7 / 10

Minimal

The server is a thin, well-typed wrapper over the Prowler API with careful error masking, but it ships every read and write tool at once, with no risk annotations, no read-only mode, no confirmation or dry-run, and almost no record of what was called. A hijacked model can delete providers, replace the mutelist (hiding findings), change user roles, and create export integrations that send scan data to a destination it names. The cloud credentials used to onboard providers also pass through the model as plain tool arguments. Safety rests almost entirely on the host's approval step and the permissions of the Prowler API key.

Key gaps (3)

  1. set_user_role can reassign any user's role, including the role of the key the server runs as, with no restriction or logging in the server. C1 · Identity & least privilege
  2. A hijacked model could combine tenant data reads with deletions, mutelist replacement and export integrations to an arbitrary bucket, with no server-side approval or egress control. C5 · Untrusted input blast radius
  3. Cloud and integration secrets are supplied as model-written tool arguments, so they pass through the model and the host's transcripts. C8 · Secrets & sensitive-data protection

Criteria

C1 Identity & least privilege

Minimal 0.23 / 1.00

The server holds no identity of its own: in stdio mode it uses one Prowler API key from the environment, and in HTTP mode it forwards whatever bearer token or API key the caller sends to the Prowler API. Every tool, read or write, uses that same credential, so authority is whatever role the key has and the server does nothing to narrow it. One tool, set_user_role, lets the agent change any user's role (including the key owner's) with no check beyond the API's own permissions. The Prowler API's own role checks are the only remaining layer.

C2 Approval gates

Minimal 0.05 / 1.00

No tool carries any risk annotation (read-only, destructive, idempotent hints), so a host cannot tell the read tools from tools that delete providers or replace the mutelist. There is no dry-run, preview, confirmation step or read-only mode. Destructive tools describe their danger only in docstrings, which a host cannot enforce. Deletions of providers (with their scans and findings), the mutelist and Jira work items cannot be undone.

C3 Tool & action scoping

Minimal 0.40 / 1.00

Arguments are typed and validated in several places: non-blank identifiers, page sizes capped at 1000, date formats and ranges, enumerations, and an HTTPS plus single-host allowlist for the one external fetch. The Prowler Hub client encodes path segments safely. Most app tools, however, join model-supplied IDs into API paths with plain f-strings, and credential and mutelist arguments are free-form dictionaries. Every tool, read and write, is enabled with no way to disable groups.

C4 Code-execution isolation

N/A · full credit 1.00 / 1.00

The server contains no code path that runs model-supplied text as code: no shell, subprocess, eval, exec or deserialization of untrusted data. Attack-path queries are chosen by ID and run by the Prowler API, not interpreted here. The only dynamic import loads tool modules from the server's own package.

C5 Untrusted input blast radius

Minimal 0.07 / 1.00

Tools return tenant data an outsider can influence: finding text, resource names and tags, scan and documentation excerpts, all passed through as plain structured fields with no provenance or untrusted marker. The server cannot see what the host does with it and offers no mode that removes the write or egress tools after such content is read. A hijacked model could therefore chain reads of attacker-influenced data into deletions, mutelist replacement, or an export integration pointing at a destination it names, with no server-side gate.

C6 Memory, context & configuration integrity

N/A · full credit 1.00 / 1.00

The server has no memory, retrieval store or auto-loaded configuration: it reads no workspace files, no dotenv file and no instruction files, and keeps no state between stateless HTTP requests. Settings come from environment variables and command-line flags only. Persisted Prowler mutelist and mute rules are stored in the tenant, and are scored as state-changing actions elsewhere.

C7 Third-party extensions

N/A · full credit 1.00 / 1.00

The server loads no third-party code at runtime: tools are discovered from its own package, it installs nothing, and the external content it fetches (Prowler Hub JSON, docs pages, check source from raw.githubusercontent.com) is returned as text and never executed. Dependencies are pinned in the lockfile and the container build uses a frozen sync with a digest-pinned base image.

C8 Secrets & sensitive-data protection

Minimal 0.17 / 1.00

The server's own key comes from an environment variable and is not logged. Error handling is careful: upstream error bodies are logged but not relayed to the model, and unhandled failures are masked. However, onboarding cloud providers and integrations requires the model to write the cloud secrets (AWS keys, Azure client secrets, GCP private keys, kubeconfigs, Jira tokens) as tool arguments, so they travel through the model and the host's transcripts. There is no redaction of logged upstream bodies, and the stored secrets are long-lived.

C9 Audit & traceability

Minimal 0.30 / 1.00

There is no per-call record of tool use. Some write tools (providers, integrations, mutelist, scan trigger) log an info line to the server's standard log, and failures are logged by a shared middleware, but roles, users, findings and resources tools log nothing, including set_user_role. Logs carry no caller identity or arguments, and nothing is tamper-evident or exported. The process log is outside the model's control.

C10 Limits & kill switch

Moderate 0.50 / 1.00

The server enforces some bounds on its own work: page size capped at 1000 (50 for events, 20 for docs search), 30 second HTTP timeouts, 60 second and Jira dispatch polling timeouts, and a two-day window on historical finding queries. It has no rate limits, concurrency limits, size cap on write inputs such as finding_ids, or explicit cancellation of in-flight work, and callers choose how close to the ceiling to run.