StuMason/coolify-mcp
TypeScript
Issue Opened
Issue #209 opened: Audit list_resources include_full for other sensitive nested surfaces (env_vars, private_keys, etc.)
Follow-up to #204 (merged via #206).
#204 / #206 mask five top-level secret fields on each row returned by system({ action: 'list_resources', include_full: true }):
manual_webhook_secret_githubmanual_webhook_secret_gitlabmanual_webhook_secret_giteamanual_webhook_secret_bitbuckethttp_basic_auth_password
But the raw Coolify /api/v1/resources row carries ~95 fields, and some of them are themselves references / nested collections that may surface additional sensitive data depending on the resource type and configuration. Worth a focused audit pass.
Surfaces to look at
environment_variables[]— appears nested on application / service / database rows in some Coolify configurations. Each entry carriesvalue/real_value, which the dedicatedenv_varspipeline masks. The nested copy on/resourcesis not routed through that pipeline today, so if it's present in the payload it's a leak vector.private_keysreferences — applications point to keys by UUID. The UUIDs themselves are not secrets (you still need the API token to pull the private-key body), but if/resourcesever inlines the key body alongside the reference, that needs masking.docker_compose_raw— base64-encoded compose YAML. Not a secret on its own, but compose files frequently embed registry creds, env defaults, etc. that the user may not intend to expose.custom_labels— base64-encoded Traefik label config. Same risk profile as compose.- Database-specific fields — root password, replication password, connection strings for postgres / mysql / mariadb / mongo / redis / clickhouse / dragonfly / keydb rows. If any of these are returned on the
/resourcesrow (vs. only onGET /databases/{uuid}), they need masking.
What to do
- Hit a live Coolify v4 instance with one of each resource type (app, service, every supported database, plus a couple with
is_http_basic_auth_enabledand various webhook providers wired up). system({ action: 'list_resources', include_full: true, reveal: true })and grep every value field for anything that looks like a credential or compose blob.- For each new sensitive surface found:
- Add to
SENSITIVE_RESOURCE_FIELDSif it's a single top-level field. - Add a dedicated walker if it's nested (e.g. mirror
maskEnvVarover the embeddedenvironment_variables[]).
- Add to
- Add a test per masked field, matching the existing pattern in
coolify-client.test.ts > listResources > sensitive-field masking on include_full.
Out of scope
- Field-level renaming or other API tidy. This is masking-only.
- Anything that requires changing the Coolify-side response shape — we mask at the MCP boundary regardless.
Refs
- #204 — original webhook secret + basic-auth masking issue
- #206 — implementation PR
- #159 / #182 — the v2.9.0
env_varsmasking pattern this would mirror for nested env-var collections