Summary
Top-level buzz --help renders the values of BUZZ_PRIVATE_KEY and BUZZ_AUTH_TAG in its help text, not just the variable names. Any agent or user who runs buzz --help with those variables set writes its full signing key and its full owner attestation to stdout — and therefore into whatever captures that output.
This is clap's default behaviour (env-var values are shown in [env: VAR=value]), so the arg definitions simply need hide_env_values = true.
Affected surface (measured, values redacted)
Running buzz --help with a managed agent's environment set:
BUZZ_PRIVATE_KEY : rendered, 63-char value (full nsec1…)
BUZZ_AUTH_TAG : rendered, 208-char value (full NIP-OA attestation JSON)
BUZZ_RELAY_URL : rendered
Scoped precisely: only the top-level help does this. buzz messages --help, buzz repos --help, and buzz messages send --help render neither value. The global options block is the leak point.
Version: CLI shipped in Buzz Desktop, /Applications/Buzz.app/Contents/MacOS/buzz, build dated 2026-07-29.
Why the two values together matter
A leaked private key alone is inert on a membership-gated relay: a plain NIP-42 kind:22242 AUTH from a managed-agent key is rejected with restricted: not a relay member, because managed-agent membership rides on the NIP-OA auth attestation tag. --help discloses both halves at once, which is what makes the output a usable identity rather than a useless secret.
Impact
Local information disclosure (CWE-532, sensitive information written to log/output). Not remotely exploitable on its own — an attacker needs read access to the captured output. Severity therefore depends entirely on where agent output lands, and in the shipped configuration that turns out to be the problem:
-
Agents are instructed to run it. The managed-agent base prompt says, verbatim: "Run buzz --help or buzz <group> --help for full usage." So the leak is not an accident a careful operator can avoid — it is the documented first step, executed on fresh sessions, unattended.
-
Harness transcripts persist it. With the Claude Code runtime, agent sessions are recorded to ~/.claude/projects/<slug>/*.jsonl. Those files are mode 0600, but every managed agent on the machine runs as the same user, so any agent can read every other agent's transcript, including the keys in them.
-
It recurs. On one deployment I inventoried four transcript files containing key material across two agents. After deleting all four and verifying zero remained, a new one appeared 25 minutes later — a different agent session had run buzz --help again. Cleanup does not converge while the flag is unset.
Worth noting in contrast: Buzz's own storage is careful. Private keys are in the macOS Keychain, absent from managed-agents.json; agent logs and retention DBs contained zero key material. The leak is entirely via help output into the layer above.
Reproduction
# with a managed agent's env set
buzz --help | grep -A2 'private-key'
# → [env: BUZZ_PRIVATE_KEY=nsec1…<full 63-char key>]
buzz --help | grep -o 'BUZZ_AUTH_TAG=.*'
# → full attestation JSON
To confirm without staining your own terminal history, redirect to a file and measure lengths rather than printing.
Suggested fix
hide_env_values = true on both args:
#[arg(long, env = "BUZZ_PRIVATE_KEY", hide_env_values = true)]
private_key: Option<String>,
#[arg(long, env = "BUZZ_AUTH_TAG", hide_env_values = true)]
auth_tag: Option<String>,
Two adjacent hardening items, lower priority but same root concern:
--private-key / --auth-tag as CLI flags put secrets in argv, visible to any same-user process via ps. Consider accepting them only via env or a file path (--private-key-file), or documenting the env var as the supported path.
- A grep for other secret-bearing args with
env = … would be worth doing at the same time; I only audited these three variables.
What I did not verify
- Whether other runtimes (codex, goose) persist agent output the same way — I confirmed the Claude Code path and found
~/.codex clean on this machine.
- Whether this has already been reported privately; the public issue tracker has nothing matching, but I can't see private reports.
Disclosure note
I tried the private vulnerability reporting channel first — GET /repos/block/buzz/private-vulnerability-reporting returns {"enabled":true}, but POST /repos/block/buzz/security-advisories/reports returns HTTP 500 with an empty body, reproducibly, including with a minimal {summary, description} payload. (A deliberately invalid severity was correctly rejected with a 422, so the request is reaching validation — the 500 is server-side.) Your private channel is advertised but not functioning; worth checking independently of this report.
Filing publicly instead, on the reasoning that this has no remote vector: anyone positioned to read the help output already has local access to the agent's environment, and therefore to the key regardless. The novel harm here is persistence into files that outlive the process and are readable across agents — which is what the fix addresses.
Summary
Top-level
buzz --helprenders the values ofBUZZ_PRIVATE_KEYandBUZZ_AUTH_TAGin its help text, not just the variable names. Any agent or user who runsbuzz --helpwith those variables set writes its full signing key and its full owner attestation to stdout — and therefore into whatever captures that output.This is clap's default behaviour (env-var values are shown in
[env: VAR=value]), so the arg definitions simply needhide_env_values = true.Affected surface (measured, values redacted)
Running
buzz --helpwith a managed agent's environment set:Scoped precisely: only the top-level help does this.
buzz messages --help,buzz repos --help, andbuzz messages send --helprender neither value. The global options block is the leak point.Version: CLI shipped in Buzz Desktop,
/Applications/Buzz.app/Contents/MacOS/buzz, build dated 2026-07-29.Why the two values together matter
A leaked private key alone is inert on a membership-gated relay: a plain NIP-42 kind:22242 AUTH from a managed-agent key is rejected with
restricted: not a relay member, because managed-agent membership rides on the NIP-OAauthattestation tag.--helpdiscloses both halves at once, which is what makes the output a usable identity rather than a useless secret.Impact
Local information disclosure (CWE-532, sensitive information written to log/output). Not remotely exploitable on its own — an attacker needs read access to the captured output. Severity therefore depends entirely on where agent output lands, and in the shipped configuration that turns out to be the problem:
Agents are instructed to run it. The managed-agent base prompt says, verbatim: "Run
buzz --helporbuzz <group> --helpfor full usage." So the leak is not an accident a careful operator can avoid — it is the documented first step, executed on fresh sessions, unattended.Harness transcripts persist it. With the Claude Code runtime, agent sessions are recorded to
~/.claude/projects/<slug>/*.jsonl. Those files are mode0600, but every managed agent on the machine runs as the same user, so any agent can read every other agent's transcript, including the keys in them.It recurs. On one deployment I inventoried four transcript files containing key material across two agents. After deleting all four and verifying zero remained, a new one appeared 25 minutes later — a different agent session had run
buzz --helpagain. Cleanup does not converge while the flag is unset.Worth noting in contrast: Buzz's own storage is careful. Private keys are in the macOS Keychain, absent from
managed-agents.json; agent logs and retention DBs contained zero key material. The leak is entirely via help output into the layer above.Reproduction
To confirm without staining your own terminal history, redirect to a file and measure lengths rather than printing.
Suggested fix
hide_env_values = trueon both args:Two adjacent hardening items, lower priority but same root concern:
--private-key/--auth-tagas CLI flags put secrets inargv, visible to any same-user process viaps. Consider accepting them only via env or a file path (--private-key-file), or documenting the env var as the supported path.env = …would be worth doing at the same time; I only audited these three variables.What I did not verify
~/.codexclean on this machine.Disclosure note
I tried the private vulnerability reporting channel first —
GET /repos/block/buzz/private-vulnerability-reportingreturns{"enabled":true}, butPOST /repos/block/buzz/security-advisories/reportsreturns HTTP 500 with an empty body, reproducibly, including with a minimal{summary, description}payload. (A deliberately invalidseveritywas correctly rejected with a 422, so the request is reaching validation — the 500 is server-side.) Your private channel is advertised but not functioning; worth checking independently of this report.Filing publicly instead, on the reasoning that this has no remote vector: anyone positioned to read the help output already has local access to the agent's environment, and therefore to the key regardless. The novel harm here is persistence into files that outlive the process and are readable across agents — which is what the fix addresses.