Skip to content

Sensitive commands now print secrets from the child's stderr #95

Description

@nojaf

Since #94 landed, Process.StartAsync calls BeginErrorReadLine(). That is a real fix: before it, RedirectStandardError was set but the stream was never drained, so stderr was silently discarded and a child writing past the pipe buffer would hang forever (verified: a step emitting 500 KB to stderr never returns on 4ce7911, completes on master).

The side effect is that the child's stderr is now printed for the first time, and the RunSensitive* members only mask the command line, not the child's output.

Secrets leak into the log

let token = "SUPERSECRET123"

pipeline "t" {
    stage "s" {
        run (fun ctx -> ctx.RunSensitiveCommand $"sh -c \"echo Authorization: Bearer {token} >&2\"")
    }
    runIfOnlySpecified false
}

Before #94:

sh -c "echo Authorization: Bearer * >&2"

On master:

sh -c "echo Authorization: Bearer * >&2"
Authorization: Bearer SUPERSECRET123

The masked line still prints, so the log looks like masking worked. Anything that echoes credentials on stderr is now exposed: curl -v request headers, ssh -v, git with GIT_TRACE, tools that warn about a token on argv.

This affects master only. The latest version on NuGet is 1.1.18, which predates #94, so no released version is impacted.

Suggested fix: the interpolated values should be masked in the child's output, not only in the command string.

stderr is reprinted on stdout

ProcessExtensions.fs handles both streams with the same callback, which ends in Console.WriteLine. So the child's stderr is written to the parent's stdout.

Running a build as dotnet fsi build.fsx >out.txt 2>err.txt puts the child's stderr lines into out.txt and leaves err.txt empty. Any CI wrapper or problem matcher that classifies failure by "stderr is non-empty" now sees a clean run. Console.Error.WriteLine for the stderr pump would preserve the separation.


Edited to drop a third section that asked for deterministic ordering between the two streams. That cannot be done without merging them, which would undo the separation asked for above, so it is out of scope. Worth noting that splitting the streams does give up the serialisation both pumps got for free from sharing Console.Out, so a fix should take a lock to keep each line atomic.

Activity

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