Skip to content

Conceal stellar env output unless --reveal is used#2597

Open
fnando wants to merge 4 commits into
mainfrom
stellar-env-reveal
Open

Conceal stellar env output unless --reveal is used#2597
fnando wants to merge 4 commits into
mainfrom
stellar-env-reveal

Conversation

@fnando
Copy link
Copy Markdown
Member

@fnando fnando commented Jun 1, 2026

What

To reduce churn in env handling, stellar env now conceals secret-bearing values by default and adds a --reveal flag to print the real values when explicitly requested. Output values are also escaped: the full KEY=value listing is shell-quoted (safe for eval "$(stellar env)"), while a single named lookup prints the raw value with only control characters neutralized (safe for command substitution like RPC_URL=$(stellar env STELLAR_RPC_URL --reveal)).

Why

The allow-list that decides which variables are safe to display had its logic inverted: variables marked "safe to display" were the ones being concealed, while everything else — including values that can embed credentials — was printed in plain text. This meant stellar env could leak secrets in logs, screen shares, or shell history. This change fixes the inverted check so credential-bearing values are concealed unless the user opts in with --reveal.

Known limitations

N/A

Copilot AI review requested due to automatic review settings June 1, 2026 20:55
@github-project-automation github-project-automation Bot moved this to Backlog (Not Ready) in DevX Jun 1, 2026
@claude
Copy link
Copy Markdown

claude Bot commented Jun 1, 2026

Claude finished @fnando's task in 3m 40s —— View job


Claude PR Review

  • Gather PR context
  • Read changed files
  • Analyze logic, security, and edge cases
  • Post review feedback

Summary

Overall this is a good direction: opt-in disclosure (--reveal), shell-safe quoting of the listing output via shell_escape, and tightening RPC_URL/ARCHIVE_URL (both can carry credentials) are all real improvements. Inline comments cover specifics; the higher-level points below are worth a second look before merge.

Higher-level notes

  • The PR description's framing is slightly off. Reading the old code carefully, is_concealed() was misleadingly named — it returned true for VISIBLE keys — but the callsites used it consistently, so the behavior was correct: VISIBLE keys printed in plaintext, others were concealed. So secrets weren't being leaked in plaintext in the listing prior to this PR. The real wins of this change are: (1) the function now matches its name, (2) values are shell-escaped (this was a latent injection risk for eval "$(stellar env)"), (3) opt-in --reveal, and (4) tightening RPC_URL/ARCHIVE_URL. Worth rewording the PR description so reviewers/future readers don't go looking for a CVE-class bug that wasn't there.

  • Behavioral break for users with STELLAR_RPC_URL / STELLAR_ARCHIVE_URL in scripts. URL=$(stellar env STELLAR_RPC_URL) now silently assigns "" instead of the URL. This is the right default but should be (a) called out in release notes / CHANGELOG, and (b) ideally surfaced via a stderr hint on concealed single-name lookups (see inline comment on env/mod.rs:57).

  • No tests added. Given the security framing, this PR really should land with tests covering: concealed key without --reveal returns empty, with --reveal returns value; listing without --reveal shows # KEY=<concealed> for the non-VISIBLE set; eval "$(stellar env --reveal)" survives a value with ', $, ; in it (regression guard for the new shell-escape path). The cmd/crates/soroban-test/tests/it/config.rs file already exercises stellar env and is a natural home.

  • Lossy control-character escaping for single-name lookup. escape_control_characters turns \n into the literal 4-char sequence \x0a, so e.g. STELLAR_RPC_HEADERS=$'A: x\nB: y' round-trips into a different value when read back via stellar env STELLAR_RPC_HEADERS --reveal. Command substitution itself isn't an injection vector, so this neutralization costs correctness for arguably no safety gain. See inline comment on env/mod.rs:55.

  • reveal field on EnvVar is per-variable state for what's really a per-command flag. Works today, but it gets pulled into the derived Ord and is a duplicated source of truth. Cleaner to thread reveal: bool through is_revealed() / str() instead of storing it. See inline comment on env/mod.rs:42-49.

Nits

  • #[arg(long, default_value_t = false)] on a booldefault_value_t = false is redundant.
  • Doc string in commands/mod.rs (and the mirrored text in FULL_HELP_DOCS.md) claims the single-value output is "unescaped" but control characters are escaped — phrasing should match the implementation.

Security / correctness check (positive)

  • The full listing now uses shell_escape::escape after control-char neutralization, so eval "$(stellar env --reveal)" is safe against values containing ', ", $, ;, backticks, etc. Good.
  • The new is_concealed() correctly returns true for any key not in VISIBLE, including unknown keys that may have been picked up via the prefix scan. Good defense-in-depth posture.

Inline comments above for the specific spots.
· branch: stellar-env-reveal

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 98ea6da22b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread cmd/soroban-cli/src/commands/env/mod.rs
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adjusts stellar env to avoid leaking secret-bearing environment values by default, adding an explicit --reveal opt-in for printing real values and tightening output escaping to be safer in shell contexts.

Changes:

  • Fixes the concealment decision by inverting the allow-list check and removing additional vars from the “visible” list.
  • Adds --reveal and updates stellar env output formatting: shell-escaped KEY=value listing, and single-key output with control characters neutralized.
  • Updates CLI help text and the generated FULL_HELP_DOCS.md to document the new conceal/reveal behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
FULL_HELP_DOCS.md Documents default concealment, single-key behavior, and adds --reveal option to the help output.
cmd/soroban-cli/src/env_vars.rs Inverts concealment logic so only allow-listed vars are shown by default; removes ARCHIVE_URL/RPC_URL from visible list.
cmd/soroban-cli/src/commands/mod.rs Updates top-level command help text for stellar env to describe concealment and --reveal.
cmd/soroban-cli/src/commands/env/mod.rs Implements --reveal, adds shell escaping for listing output, and control-character escaping for single-key output.

Comment thread cmd/soroban-cli/src/env_vars.rs Outdated
Comment thread cmd/soroban-cli/src/commands/env/mod.rs
Comment thread cmd/soroban-cli/src/commands/mod.rs Outdated
Comment thread FULL_HELP_DOCS.md Outdated
Comment thread FULL_HELP_DOCS.md
Comment thread cmd/soroban-cli/src/commands/env/mod.rs
Comment thread cmd/soroban-cli/src/env_vars.rs
Comment thread cmd/soroban-cli/src/commands/env/mod.rs
Comment thread cmd/soroban-cli/src/commands/env/mod.rs
Comment thread cmd/soroban-cli/src/commands/env/mod.rs
Comment thread cmd/soroban-cli/src/commands/env/mod.rs
Comment thread cmd/soroban-cli/src/commands/mod.rs Outdated
@claude
Copy link
Copy Markdown

claude Bot commented Jun 1, 2026

Claude finished @fnando's task in 1m 35s —— View job


Claude PR Re-review (post-sync)

  • Pull latest commits and inspect diff vs origin/main
  • Verify doc-comment fixes (is_concealed, single-value "unescaped" wording, --reveal placeholder phrasing)
  • Inspect new --reveal integration tests
  • Re-check still-open feedback (silent miss on concealed lookup, reveal field on EnvVar, lossy control-char escaping, dotenv compatibility, default_value_t = false nit)
  • Post updated review

Summary of changes since the last review

Commits 11726bd and b759b55 address a subset of the earlier feedback:

Earlier concern Status
is_concealed doc comment contradicted the function body (Copilot) ✅ Fixed in env_vars.rs:48-50
"raw value is printed unescaped" wording in commands/mod.rs and FULL_HELP_DOCS.md (Copilot, claude) ✅ Fixed — now says "without shell quoting (control characters are neutralized)"
default_value_t = false redundant on bool (claude) ✅ Removed in env/mod.rs:22-23
No tests for --reveal path (Copilot, claude) ✅ Two new tests in config.rs:778-823 covering full-listing reveal and single-key reveal

Good iteration on docs and tests.

Still open from earlier feedback

A few items from the prior round don't appear to be addressed; flagging in case they were intentionally deferred vs. missed.

1. --reveal help text still claims "placeholder value"env/mod.rs:20-22

/// Whether to reveal the value of concealed env vars. By default, concealed env vars are
/// hidden behind a placeholder value.
#[arg(long)]
pub reveal: bool,

The actual output is a commented line (# KEY=<concealed>), not a placeholder assignment. Copilot flagged this on both env/mod.rs:23 and FULL_HELP_DOCS.md:1202. The doc-comment cleanup in b759b55 updated the single-value phrasing but missed the --reveal flag's own doc string. The same text is duplicated in FULL_HELP_DOCS.md:1202 and will need regenerating.

Suggested wording:

Reveal the values of concealed env vars. By default, concealed vars are emitted as a commented-out line (# KEY=<concealed>) so that eval "$(stellar env)" does not export them.

2. New env_single_is_returned_when_using_reveal test doesn't exercise control-char escaping

config.rs:811-823 uses a secret-key value (SDIY6...) that contains no control characters, so it never hits the escape_control_characters branch on env/mod.rs:55. If the intent is to lock in the new "control chars are neutralized" contract, a separate test with a value like "hello\nworld\t!" asserting stdout is hello\\x0aworld\\x09!\n would actually guard against regressions in the neutralization step. (This is also where the lossy-escaping concern from the prior review would surface.)

3. Silent miss on concealed single-name lookupenv/mod.rs:52-60

if let Some(name) = &self.name {
    if let Some(v) = vars.iter().find(|v| &v.key == name) {
        if v.is_revealed() {
            println!("{}", escape_control_characters(&v.value));
        }
    }

    return Ok(());
}

stellar env STELLAR_SECRET_KEY (without --reveal) prints nothing and exits 0 — indistinguishable from "variable is unset". A one-line stderr hint when a found variable is concealed would make the cause discoverable without changing the stdout contract:

if v.is_revealed() {
    println!("{}", escape_control_characters(&v.value));
} else {
    eprintln!("# {name} is concealed; pass --reveal to print its value");
}

Same reasoning applies to the breaking-change angle on STELLAR_RPC_URL / STELLAR_ARCHIVE_URL — users with URL=$(stellar env STELLAR_RPC_URL) in existing scripts now silently get "".

4. reveal field on EnvVar — minor design smell (still applies)

EnvVar carries reveal: bool even though every instance in a given run has the same value. It also rides along in the derived Ord, so the sort key is (key, value, source, reveal) — uniform today, but a footgun if anyone ever introduces per-variable conceal policy. Threading reveal: bool through is_revealed(&self, reveal: bool) and str(&self, reveal: bool) instead of storing it would drop both the field and reveal() setter.

Not a blocker; flagging since b759b55 would have been a natural place to clean it up.

5. Changelog / release-notes entry

Removing RPC_URL / ARCHIVE_URL from VISIBLE is a real behavior change for any script doing URL=$(stellar env STELLAR_RPC_URL). Worth a CHANGELOG entry so users discover this on upgrade rather than via a broken script.

Codex's dotenv concern

Codex flagged that shell_escape::escape on values containing ' produces POSIX shell concatenation ('a'\''b'), which eval handles but a dotenv parser does not. You replied "can't reproduce. Using single quotes work fine." Worth a quick concrete check to be sure — try:

STELLAR_NETWORK_PASSPHRASE="don't break" stellar env --reveal

If the output contains '\'', then writing stellar env --reveal > .env followed by reading that .env back via the CLI's own dotenv loader would round-trip wrong. The help text and the commands/mod.rs doc both still claim the output "can be used as .env file", so either the claim or the escaping should give. If dotenv compatibility is intended, the listing path could use a dotenv-safe escape (double-quote values, escape "/\/$/backtick) instead of shell_escape::escape. If only eval compatibility is intended, the docs should drop the ".env file" framing.

Nothing else jumped out

The new logic in env_vars.rs is correct, the tests that were added pass through the happy paths, and the doc-comment fixes are accurate. The notes above are about completeness of the previous round, not new defects.


· branch: stellar-env-reveal

@fnando fnando moved this from Backlog (Not Ready) to Needs Review in DevX Jun 1, 2026
@fnando fnando self-assigned this Jun 1, 2026
@fnando fnando requested review from leighmcculloch and mootz12 June 1, 2026 23:10
@claude
Copy link
Copy Markdown

claude Bot commented Jun 1, 2026

Claude finished @fnando's task in 58s —— View job


Claude PR Re-review (post-merge sync)

  • Inspect the new merge commit (4605178)
  • Diff against prior review state
  • Confirm earlier-feedback responses
  • Post follow-up

Summary

The synchronize that triggered this run is just a merge of origin/main into the branch (4605178). The merge brings in CI workflow bumps only (.github/workflows/docker.yml, rust.yml, test-install.yml) — unrelated to this PR's code. No new substantive changes to review since b759b55.

Author responses to prior feedback — acknowledged

Item Resolution
is_concealed doc comment ✅ Fixed (b759b55)
"unescaped" wording in commands/mod.rs + FULL_HELP_DOCS.md ✅ Fixed (b759b55)
default_value_t = false redundancy ✅ Removed (11726bd)
Tests for --reveal path ✅ Added (b759b55)
--reveal help text "placeholder value" 🟡 Author keeping as-is — <concealed> is the placeholder, line is commented (# KEY=<concealed>). Fair interpretation; reasonable to leave.
Silent miss on concealed single-key lookup 🟡 Deferred — intentional to preserve the "" stdout contract; covered by env_single_concealed_key_returns_empty. Stderr hint left as a follow-up.
reveal field on EnvVar 🟡 Acknowledged, deferred to keep diff small.
Lossy control-char escape on single-name lookup 🟡 Deliberate — values with embedded newlines aren't a supported shape for these env vars.
CHANGELOG entry N/A — repo has no CHANGELOG.
Codex dotenv concern ('\'' round-trip) ✅ Verified by author: dotenvy understands the POSIX form, round-trips correctly.
Control-char test coverage 🟡 Not added — current tests don't exercise escape_control_characters since the SDIY-prefixed secret has no control bytes. Low risk; the path is small and obvious.

Net assessment

No new issues introduced by the merge. All earlier corrections were applied, and the remaining open items have been thoughtfully triaged by the author with sound reasoning. From my side: LGTM as a focused, well-scoped change.

The biggest user-facing footgun remaining is the silent break for URL=$(stellar env STELLAR_RPC_URL) in existing scripts — worth a release-notes mention by whoever cuts the next release, even though there's no CHANGELOG in-repo. Not a blocker.

--- · branch: stellar-env-reveal

Copy link
Copy Markdown
Member

@leighmcculloch leighmcculloch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'reveal' is not overly obvious imo, maybe --reveal-secrets to be more explicit.

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

Labels

None yet

Projects

Status: Needs Review

Development

Successfully merging this pull request may close these issues.

3 participants