Skip to content

Redact secret parameter values from environment variables sent over the backchannel - #18089

Merged
Mitch Denny (mitchdenny) merged 4 commits into
microsoft:mainfrom
shauryalowkeygotaura:fix/describe-secret-env-values
Jun 18, 2026
Merged

Mitch Denny (mitchdenny) merged 4 commits into
microsoft:mainfrom
shauryalowkeygotaura:fix/describe-secret-env-values

Conversation

@shauryalowkeygotaura

Copy link
Copy Markdown
Contributor

Description

Fixes #17616

aspire describe --format json (and other CLI surfaces fed by the auxiliary backchannel, including the MCP resource tools) emitted the plaintext value of a ParameterResource marked secret: true whenever that parameter was consumed by another resource via WithEnvironment(name, secretParam). The parameter resource's own Value property is already redacted at the producer (IsSensitive → null), but the resolved value flowing into dependent resources' environment dictionaries was not.

AuxiliaryBackchannelRpcTarget now collects the resolved values of secret parameters in the application model and redacts (nulls) any environment variable value that matches one of them, before snapshots leave the AppHost. This means every backchannel consumer is covered, not just describe.

Notes on the approach:

  • The collection peeks at already-resolved values only (WaitForValueTcs.Task.IsCompletedSuccessfully); it never triggers parameter resolution, so it cannot block on an interaction prompt. If a secret value hasn't been resolved yet, it also cannot have flowed into anyone's environment.
  • Redaction to null is consistent with the existing handling of sensitive resource properties and SecretText command arguments.
  • Known limitation (called out in the issue): values that embed a secret (e.g. a connection string built from a ReferenceExpression) are not caught by exact-value matching. That likely needs provenance tracking on EnvironmentVariableSnapshot and could be a follow-up.

Testing

  • Added GetResourceSnapshotsAsync_RedactsSecretParameterValuesInEnvironmentVariables to AuxiliaryBackchannelRpcTargetTests, modeled on the existing GetResourceSnapshotsAsync_MapsSnapshotData test: secret parameter value is redacted, a non-secret parameter's value and unrelated values pass through unchanged.
  • Aspire.Hosting builds clean locally with the change. I wasn't able to run the full Aspire.Hosting.Tests project in my environment (unrelated test-asset build issue in TestProject.IntegrationServiceA), so relying on CI for the test run.

🤖 Generated with Claude Code

…he backchannel

aspire describe --format json emitted the plaintext value of secret
parameters whenever the parameter was consumed by another resource via
WithEnvironment. The parameter resource's own Value property is already
redacted (IsSensitive), but the resolved value flowing into dependent
resources' environment dictionaries was not.

The auxiliary backchannel RPC target now collects the resolved values of
secret parameters (peeking at already-resolved values only, never
triggering resolution) and redacts matching environment variable values
before snapshots leave the AppHost.

Fixes microsoft#17616

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 10, 2026 •

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 18089

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 18089"

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR adds redaction of secret parameter values from environment variables when resource snapshots are sent through the backchannel RPC, preventing secrets from leaking through clients like aspire describe --format json.

Changes:

  • Adds GetResolvedSecretParameterValues() to collect resolved secret parameter values from the application model.
  • Modifies environment variable mapping in snapshot building to null out values that match any resolved secret parameter value.
  • Adds a test verifying that secret parameter values are redacted while non-secret values remain visible.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs Adds value-based redaction of secret parameters in environment variable snapshots
tests/Aspire.Hosting.Tests/Backchannel/AuxiliaryBackchannelRpcTargetTests.cs Adds test for secret parameter value redaction

Comment thread src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs Outdated
Comment thread src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs Outdated
Comment thread tests/Aspire.Hosting.Tests/Backchannel/AuxiliaryBackchannelRpcTargetTests.cs Outdated
@davidfowl

Copy link
Copy Markdown
Collaborator

Generated with Claude Code

Shaurya (@shauryalowkeygotaura) Take a look at the coding agents section in the contributing guide https://github.com/microsoft/aspire/blob/main/docs/contributing.md#coding-agents.

The skills defined here https://github.com/microsoft/aspire/tree/main/.agents/skills (I know claude doesn't read the .agents folder). We typically will run the code-review skill and pr-testing skill.

Did you verify this change works e2e with a manual test?

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mitchdenny

Copy link
Copy Markdown
Member

PR Testing Report

PR Information

Artifact Version Verification

  • Dogfood CLI artifact: N/A — this is a fork PR (shauryalowkeygotaura/aspire) whose CI run is in action_required and was never approved, so no cli-native-archives-* artifact was published. The dogfood installer fails with no valid artifacts found to download.
  • What was tested instead: The PR source at head. The in-repo src/Aspire.Hosting (which contains the production change) was built and run via a source-built Aspire CLI (13.5.0-dev). Playground AppHosts reference Aspire.Hosting by project, so the running AppHost exercises the exact PR code over the real backchannel.
  • Status: ✅ Verified against PR source at head commit

Changes Analyzed

Files Changed

  • src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs — Modified (production)
  • tests/Aspire.Hosting.Tests/Backchannel/AuxiliaryBackchannelRpcTargetTests.cs — Modified (test)

Change Categories

  • Hosting changes detected (backchannel resource-snapshot redaction; the surface aspire describe reads)
  • CLI changes
  • Dashboard changes
  • Template changes
  • Client/Component changes
  • CI infrastructure changes
  • VS Code extension changes
  • Test changes

What the change does

When building ResourceSnapshot.EnvironmentVariables, any env-var value that exactly matches the resolved value of a secret parameter is replaced with null before the snapshot is sent over the backchannel. Resolution is read-only (it never triggers parameter resolution), and non-secret parameter values are untouched.

Test Scenarios Executed

A minimal docker-free AppHost was created (an Executable resource with a secret parameter, a non-secret parameter, and a literal value, all surfaced as environment variables) and run via aspire run. aspire describe then connected over the backchannel.

var secret = builder.AddParameter("dbpassword", "s3cr3t-value-xyz", secret: true);
var region = builder.AddParameter("region", "westus2", secret: false);
builder.AddExecutable("worker", "bash", ".", "-c", "sleep 600")
       .WithEnvironment("DB_PASSWORD", secret)
       .WithEnvironment("REGION", region)
       .WithEnvironment("PLAIN_VAR", "plain-value");

Scenario 1: Secret redaction happy path (aspire describe --format json)

Objective: A secret parameter's value referenced as an env var is redacted in describe JSON output.
Coverage Type: Happy path
Status: ✅ Passed

aspire describe worker --format json returned:

"environment": {
  "DB_PASSWORD": null,
  "PLAIN_VAR": "plain-value",
  "REGION": "westus2",
  "SSL_CERT_DIR": "/var/folders/.../certs"
}

Observations: DB_PASSWORD (secret param value s3cr3t-value-xyz) was redacted to null. Exit code 0, valid JSON on stdout.


Scenario 2: No over-redaction of non-secret values

Objective: Confirm non-secret parameter values and unrelated literal env vars are NOT redacted.
Coverage Type: Unhappy-path / boundary (over-redaction)
Status: ✅ Passed

Observations: REGION (non-secret parameter region = westus2) and PLAIN_VAR (literal plain-value) were both preserved verbatim. Only the value matching the secret parameter was nulled — no over-redaction.


Scenario 3: Human-readable describe still works

Objective: Ensure the table output path is unaffected.
Coverage Type: Regression
Status: ✅ Passed

┌────────────┬────────────┬─────────┬─────────┬──────┐
│ Name       │ Type       │ State   │ Health  │ URLs │
├────────────┼────────────┼─────────┼─────────┼──────┤
│ dbpassword │ Parameter  │ Running │ Healthy │ -    │
│ region     │ Parameter  │ Running │ Healthy │ -    │
│ worker     │ Executable │ Running │ Healthy │ -    │
└────────────┴────────────┴─────────┴─────────┴──────┘

Scenario 4: Unit/integration coverage of the exact backchannel method

Objective: Exercise AuxiliaryBackchannelRpcTarget.GetResourceSnapshotsAsync() (the server method behind describe) directly.
Coverage Type: Regression unit/integration
Status: ✅ Passed (2/2)

Ran the focused hosting tests after resolving the merge conflict:

  • GetResourceSnapshotsAsync_RedactsSecretParameterValuesInEnvironmentVariables ✅
  • WaitForResourceAsync_ReturnsFailureWhenResourceHasErrorStateStyle ✅ (the test that was adjacent to the merge conflict)

Summary

Scenario Status Notes
1. Secret redaction happy path (JSON) ✅ Passed DB_PASSWORD → null
2. No over-redaction of non-secret values ✅ Passed REGION, PLAIN_VAR preserved
3. Human-readable describe ✅ Passed Table renders correctly
4. Backchannel method unit/integration tests ✅ Passed 2/2

Overall Result

✅ PR VERIFIED

The redaction works end-to-end through aspire describe --format json: secret parameter values referenced as environment variables are redacted, while non-secret parameter values and unrelated env vars are preserved (no over-redaction).

Notes / Recommendations

  • The dogfood CLI artifact is unavailable because this fork PR's CI is unapproved (action_required). A maintainer should approve the workflow so standard dogfood artifacts get published for future testing; this run validated the PR source at head instead.
  • Merge conflict in AuxiliaryBackchannelRpcTargetTests.cs (a new test landed adjacent to another newly added test on main) was resolved by keeping both tests; the production change had no conflict.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 18, 2026 04:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 2

Comment thread src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs Outdated
Comment thread src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs Outdated
…ing behavior

- Compute resolved secret values once per batch in GetResourceSnapshotsAsync,
  but keep per-event recomputation in the streaming WatchResourceSnapshotsAsync
  so secrets resolved mid-stream are still redacted (no filter bypass).
- Reword GetResolvedSecretParameterValues doc to 'never blocks waiting for
  interactive parameter resolution' (the else branch reads ValueInternal).
- Document the value-based matching limitations on RedactIfSecretValue.
- Add tests proving an unresolved secret does not block the snapshot call, that
  a secret resolved after a watch starts is still redacted, and that a non-secret
  value coincidentally equal to a secret value is redacted (expected behavior).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mitchdenny
Mitch Denny (mitchdenny) merged commit bacfc5e into microsoft:main Jun 18, 2026
672 of 677 checks passed
@github-actions github-actions Bot added this to the 13.5 milestone Jun 18, 2026
@mitchdenny

Copy link
Copy Markdown
Member

Thanks for the contribution Shaurya (@shauryalowkeygotaura)

@github-actions github-actions Bot locked and limited conversation to collaborators Jul 18, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] aspire describe --format json exposes plaintext values of secret parameters in dependent resources' env vars

4 participants