@@ -43,16 +43,14 @@ var (
4343 ErrFailedStarting = errors .New ("command failed starting" )
4444 // ErrSignaled is returned by Wait() if a signal was sent to the command while running.
4545 ErrSignaled = errors .New ("command execution signaled" )
46- // ErrExecutionFailed is returned by Wait() when a command executes but returns a non-zero error
47- // code.
46+ // ErrExecutionFailed is returned by Wait() when a command executes but returns a non-zero error code.
4847 ErrExecutionFailed = errors .New ("command returned a non-zero exit code" )
4948 // ErrFailedSendingSignal may happen if sending a signal to an already terminated process.
5049 ErrFailedSendingSignal = errors .New ("failed sending signal" )
5150
5251 // ErrExecAlreadyStarted is a system error normally indicating a bogus double call to Run().
5352 ErrExecAlreadyStarted = errors .New ("command has already been started (double `Run`)" )
54- // ErrExecNotStarted is a system error normally indicating that Wait() has been called without
55- // first calling Run().
53+ // ErrExecNotStarted is a system error normally indicating that Wait() has been called without first calling Run().
5654 ErrExecNotStarted = errors .New ("command has not been started (call `Run` first)" )
5755 // ErrExecAlreadyFinished is a system error indicating a double call to Wait().
5856 ErrExecAlreadyFinished = errors .New ("command is already finished" )
@@ -75,7 +73,7 @@ type Result struct {
7573}
7674
7775type execution struct {
78- //nolint:containedctx
76+ //nolint:containedctx // Is there a way around this?
7977 context context.Context
8078 cancel context.CancelFunc
8179 command * exec.Cmd
@@ -93,10 +91,10 @@ type Command struct {
9391 WrapArgs []string
9492 Timeout time.Duration
9593
96- WorkingDir string
97- Env map [string ]string
98- // FIXME: EnvBlackList might change for a better mechanism (regexp and/or whitelist + blacklist)
94+ WorkingDir string
95+ Env map [string ]string
9996 EnvBlackList []string
97+ EnvWhiteList []string
10098
10199 writers []func () io.Reader
102100
@@ -122,6 +120,7 @@ func (gc *Command) Clone() *Command {
122120 WorkingDir : gc .WorkingDir ,
123121 Env : map [string ]string {},
124122 EnvBlackList : append ([]string (nil ), gc .EnvBlackList ... ),
123+ EnvWhiteList : append ([]string (nil ), gc .EnvWhiteList ... ),
125124
126125 writers : append ([]func () io.Reader (nil ), gc .writers ... ),
127126
@@ -137,27 +136,24 @@ func (gc *Command) Clone() *Command {
137136 return com
138137}
139138
140- // WithPTY requests that the command be executed with a pty for std streams. Parameters allow
141- // showing which streams
142- // are to be tied to the pty.
139+ // WithPTY requests that the command be executed with a pty for std streams.
140+ // Parameters allow showing which streams are to be tied to the pty.
143141// This command has no effect if Run has already been called.
144142func (gc * Command ) WithPTY (stdin , stdout , stderr bool ) {
145143 gc .ptyStdout = stdout
146144 gc .ptyStderr = stderr
147145 gc .ptyStdin = stdin
148146}
149147
150- // WithFeeder ensures that the provider function will be executed and its output fed to the command
151- // stdin. WithFeeder, like Feed, can be used multiple times, and writes will be performed
152- // sequentially, in order.
148+ // WithFeeder ensures that the provider function will be executed and its output fed to the command stdin.
149+ // WithFeeder, like Feed, can be used multiple times, and writes will be performed sequentially, in order.
153150// This command has no effect if Run has already been called.
154151func (gc * Command ) WithFeeder (writers ... func () io.Reader ) {
155152 gc .writers = append (gc .writers , writers ... )
156153}
157154
158155// Feed ensures that the provider reader will be copied on the command stdin.
159- // Feed, like WithFeeder, can be used multiple times, and writes will be performed in sequentially,
160- // in order.
156+ // Feed, like WithFeeder, can be used multiple times, and writes will be performed in sequentially, in order.
161157// This command has no effect if Run has already been called.
162158func (gc * Command ) Feed (reader io.Reader ) {
163159 gc .writers = append (gc .writers , func () io.Reader {
@@ -197,7 +193,6 @@ func (gc *Command) Run(parentCtx context.Context) error {
197193
198194 // Create a contextual command, set the logger
199195 cmd = gc .buildCommand (ctx )
200-
201196 // Get a debug-logger from the context
202197 var (
203198 log logger.Logger
@@ -338,8 +333,7 @@ func (gc *Command) wrap() error {
338333 err error
339334 )
340335
341- // XXXgolang: this is troubling. cmd.ProcessState.ExitCode() is always fine, even if
342- // cmd.ProcessState is nil.
336+ // XXXgolang: this is troubling. cmd.ProcessState.ExitCode() is always fine, even if cmd.ProcessState is nil.
343337 exitCode = cmd .ProcessState .ExitCode ()
344338
345339 if cmd .ProcessState != nil {
@@ -356,7 +350,7 @@ func (gc *Command) wrap() error {
356350 }
357351 }
358352
359- // Catch-up on the context
353+ // Catch-up on the context.
360354 switch ctx .Err () {
361355 case context .DeadlineExceeded :
362356 err = ErrTimeout
@@ -365,7 +359,7 @@ func (gc *Command) wrap() error {
365359 default :
366360 }
367361
368- // Stuff everything in Result and return err
362+ // Stuff everything in Result and return err.
369363 gc .result = & Result {
370364 ExitCode : exitCode ,
371365 Stdout : pipes .fromStdout ,
@@ -382,7 +376,7 @@ func (gc *Command) wrap() error {
382376}
383377
384378func (gc * Command ) buildCommand (ctx context.Context ) * exec.Cmd {
385- // Build arguments and binary
379+ // Build arguments and binary.
386380 args := gc .Args
387381 if gc .PrependArgs != nil {
388382 args = append (gc .PrependArgs , args ... )
@@ -399,26 +393,55 @@ func (gc *Command) buildCommand(ctx context.Context) *exec.Cmd {
399393 //nolint:gosec
400394 cmd := exec .CommandContext (ctx , binary , args ... )
401395
402- // Add dir
396+ // Add dir.
403397 cmd .Dir = gc .WorkingDir
404398
405- // Set wait delay after waits returns
399+ // Set wait delay after waits returns.
406400 cmd .WaitDelay = delayAfterWait
407401
408- // Build env
402+ // Build env.
409403 cmd .Env = []string {}
410- // TODO: replace with regexps? and/or whitelist?
404+
405+ const (
406+ star = "*"
407+ equal = "="
408+ )
409+
411410 for _ , envValue := range os .Environ () {
412411 add := true
413412
414- for _ , b := range gc .EnvBlackList {
415- if b == "*" || strings .HasPrefix (envValue , b + "=" ) {
413+ for _ , needle := range gc .EnvBlackList {
414+ if strings .HasSuffix (needle , star ) {
415+ needle = strings .TrimSuffix (needle , star )
416+ } else if needle != star && ! strings .Contains (needle , equal ) {
417+ needle += equal
418+ }
419+
420+ if needle == star || strings .HasPrefix (envValue , needle ) {
416421 add = false
417422
418423 break
419424 }
420425 }
421426
427+ if len (gc .EnvWhiteList ) > 0 {
428+ add = false
429+
430+ for _ , needle := range gc .EnvWhiteList {
431+ if strings .HasSuffix (needle , star ) {
432+ needle = strings .TrimSuffix (needle , star )
433+ } else if needle != star && ! strings .Contains (needle , equal ) {
434+ needle += equal
435+ }
436+
437+ if needle == star || strings .HasPrefix (envValue , needle ) {
438+ add = true
439+
440+ break
441+ }
442+ }
443+ }
444+
422445 if add {
423446 cmd .Env = append (cmd .Env , envValue )
424447 }
@@ -429,12 +452,12 @@ func (gc *Command) buildCommand(ctx context.Context) *exec.Cmd {
429452 cmd .Env = append (cmd .Env , k + "=" + v )
430453 }
431454
432- // Attach platform ProcAttr and get optional custom cancellation routine
455+ // Attach platform ProcAttr and get optional custom cancellation routine.
433456 if cancellation := addAttr (cmd ); cancellation != nil {
434457 cmd .Cancel = func () error {
435458 gc .exec .log .Log ("command cancelled" )
436459
437- // Call the platform dependent cancellation routine
460+ // Call the platform dependent cancellation routine.
438461 return cancellation ()
439462 }
440463 }
0 commit comments