Skip to content

CLI prints the API token (and other secrets) in full under --format json/pretty and --debug, regardless of --show-sensitive #94

Open

Description

@noamloewenstern

Summary

After setting an instance token via context add, I don't want the CLI to ever print that token back out unless I explicitly ask for it. This matters a lot for using the CLI from scripts/AI agents: any command's output can end up in a log file, a transcript, or an agent's context window, and today there's no safe way to guarantee the token (or other secrets) won't be in there.

Fields like the instance token are already tagged sensitive:"true" in the model structs and are correctly hidden by the default table output unless --show-sensitive/-s is passed. That protection only exists in the table formatter, though.

Bug

internal/output/json.go and internal/output/pretty.go marshal structs directly with encoding/json.Encoder, which has no concept of the sensitive struct tag at all. So fetching context/instance details with the json or pretty output format prints the real token in plaintext, even without --show-sensitive. The same is true for every other sensitive:"true" field across the codebase — SSH private/public keys (the private-key commands), webhook secrets, OAuth client secrets, database/service environment variable values, server IP/user, etc. A few individual commands (cloud-token, s3, cloudinit) have their own hand-rolled redaction for their one specific field, but most commands have none, so the underlying model's real value goes straight to stdout.

Separately, debug mode logs full request/response bodies to stderr and only masks the literal JSON key named "token" — every other sensitive field above is printed there too, independent of the output format or show-sensitive flag entirely.

Impact

This already leaked a live Coolify API token (and, separately, private key material) into an AI coding agent's transcript, just from running an ordinary structured-output command for inspection.

Expected behavior

Any field marked sensitive:"true" should be hidden (********) under every output format — table, json, and pretty — unless --show-sensitive/-s is explicitly passed, exactly like table output already behaves. Debug logging should never print real secret values regardless of flags, since it's a diagnostic trace, not a deliberate request to reveal a value.

Fix

I've written and verified a fix (reflection-based redaction shared across all three formatters, plus closing the same gap in the debug logger) and will open a PR against main shortly.

Activity

  1. noamloewenstern commented on Sep 17, 2026

    @noamloewenstern
    Author

    One addition to the report above, found while hardening the fix in #95.

    models.Database carries ten database credentials — postgres_password, mysql_root_password, mysql_password, mariadb_root_password, mariadb_password, mongo_initdb_root_password, redis_password, keydb_password, clickhouse_admin_password, dragonfly_password — that are not tagged sensitive:"true" at all. They carry table:"-", which keeps them out of the table columns, but that tag only controls column selection, not redaction. So they leak through both paths described in this issue, and they would still have leaked after a fix that only taught the json/pretty formatters to honour the existing tags:

    $ coolify database get <uuid> --format json
    {"uuid":"...","name":"db","postgres_password":"<real password>","redis_password":"<real password>"}
    

    Same under --debug, since the debug logger redacts by JSON key name and these names weren't in its set either.

    #95 now tags all ten and adds their names to the debug key set. It also adds a test that parses internal/models and internal/config and fails if any sensitive:"true" field's JSON name is missing from that set — so the next tag added to a model can't silently keep being logged in full, which is the class of gap that let these ten sit untagged in the first place.

    Two adjacent exposure paths I noticed while tracing this, both predating the formatter bug and both out of scope for #95 — happy to open separate issues if either is worth tracking:

    • internal/api/client.go: on a non-2xx response whose body has no message/error key, the raw response body is surfaced to the user verbatim, with no redaction and independent of --debug/--show-sensitive. A validation error echoing the submitted payload would print whatever secret was in it.
    • internal/service/deployment.go: deployment logs are returned as a raw JSON string that never passes through internal/output, so nothing in them is redacted.
  2. noamloewenstern commented on Sep 29, 2026

    @noamloewenstern
    Author

    Any update?

  3. JKamsker commented on Oct 12, 2026

    @JKamsker

    Would also like to see a fix for this - my agents constantly reveal the api keys

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions