Skip to content

fix: mask sensitive input values in printed commands and report stage build failures - #8

Merged
shayanhabibi merged 1 commit into
masterfrom
fix/sensitive-values
Oct 19, 2026
Merged

shayanhabibi merged 1 commit into
masterfrom
fix/sensitive-values

Conversation

@shayanhabibi

Copy link
Copy Markdown
Owner

Problems

A. A sensitive input's value was printed in plain text. Input.sensitive only affected --schema. A value read from a marked input and interpolated through cmd $"...", the string run, or Cmd.ofList was an ordinary argument, so the run log's command line, --explain (text and JSON) and a failed command's message all showed it. Only Cmd.secretOption/runSensitive masked it.

B. A keyless publish failed with a bare NullReferenceException. A stage body is built eagerly, so apiKey.Value in the else branch of a when' (dryPublish || apiKey.IsSome) stage ran while the command read its pipelines, before any condition. Reproduced before the fix: the exception escaped pipeline.Read in the command action and reached System.CommandLine's default handler. It printed Unhandled exception: System.NullReferenceException ... with a stack trace to the error writer and exited 1. This happened identically for a plain run, --explain and --json. --json wrote no result, and nothing named the command or stage.

Changes

  • Cmd.Redaction (in Partas.Build.Cmd, netstandard2.0-safe) is a process-wide set of values to mask. Cmd.toLogString masks every registered value as a whole argument and inside one (--api-key=***), longest value first. A null, empty or whitespace value is never registered.
  • ActionInput.GetValue registers each value it reads from a symbol marked Input.sensitive: a string, each string in an option, voption or collection, and the invariant text of any other non-bool value. The process still receives the value unmasked. --explain also redacts its step labels.
  • Construction module: each StageBuilder.Delay runs the body under Construction.inStage, which records the stage name on the exception's Data and rethrows it unchanged (same type, message and stack). Input reads record the input name the same way.
  • The command action catches an exception thrown while reading its pipelines. It prints an error annotation (Command (x) failed while building its pipelines in stage (publish/push). NullReferenceException: ...) and exits 1 (Failed). It records a pipeline run whose report is addressed by the stages being built and carries the exception as a raised cause, so RunResult.Failures, --json and --report carry it. Under --explain it prints the same message and exits 1 without a run result.
  • Docs: when', failIfIgnored and whenSome (shared builtRegardless xmldoc fragment) now say the body is built whatever the condition says, and point at whenSome. The guides cover the same points and the new Input.sensitive behaviour.

Stage bodies stay eager. Making them lazy would change when every stage is built, and when its inputs are read, across the builders. Reporting the failure gives the user the stage name and the fix (whenSome) without that change.

Tests

New Expecto tests: Cmd redaction (netstandard suite), sensitive input registration for string, option, list and int values (plus blank and unmarked values), --explain text and JSON masking across cmd, the string run and Cmd.ofList, a real run whose log masks the key while the child process receives it, the stage-path record on a raised exception, and the keyless publish under a plain run, --json and --explain, plus its dry branch.

dotnet run --project Build.fsproj -- test is green: 431 passed in Partas.Build.Tests (1 ignored), 65 in ExternalAnnotations.Tests (6 ignored), 73 in Partas.ExternalAnnotations.Tests (2 ignored), 6 in Cmd.NetStandard.Tests, and 11 + 11 compiler probe (Debug and Release). dotnet build src/Partas.Build -c Release is clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DgoUpTaqPJBUQ7p2oFznr2

… build failures

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DgoUpTaqPJBUQ7p2oFznr2
@shayanhabibi
shayanhabibi merged commit e8e9eb0 into master Oct 19, 2026
5 checks passed
@shayanhabibi
shayanhabibi deleted the fix/sensitive-values branch October 19, 2026 12:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant