diff --git a/pkg/runner/command.go b/pkg/runner/command.go index 956ee0a..3182f83 100755 --- a/pkg/runner/command.go +++ b/pkg/runner/command.go @@ -55,6 +55,7 @@ func (rc *RunContext) commandHandler(ctx context.Context) common.LineHandler { case "error": logger.Infof(" \U00002757 %s", line) case "add-mask": + rc.AddMask(arg) logger.Infof(" \U00002699 %s", "***") case "stop-commands": resumeCommand = arg diff --git a/pkg/runner/command_test.go b/pkg/runner/command_test.go index 95e05db..bd3c998 100644 --- a/pkg/runner/command_test.go +++ b/pkg/runner/command_test.go @@ -1,7 +1,10 @@ package runner import ( + "bytes" "context" + "io" + "os" "testing" "github.com/sirupsen/logrus/hooks/test" @@ -113,5 +116,55 @@ func TestAddmask(t *testing.T) { handler := rc.commandHandler(loggerCtx) handler("::add-mask::my-secret-value\n") + a.Equal(" \U00002699 ***", hook.LastEntry().Message) a.NotEqual(" \U00002699 *my-secret-value", hook.LastEntry().Message) } + +// based on https://stackoverflow.com/a/10476304 +func captureOutput(t *testing.T, f func()) string { + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + outC := make(chan string) + + go func() { + var buf bytes.Buffer + _, err := io.Copy(&buf, r) + if err != nil { + a := assert.New(t) + a.Fail("io.Copy failed") + } + outC <- buf.String() + }() + + w.Close() + os.Stdout = old + out := <-outC + + return out +} + +func TestAddmaskUsemask(t *testing.T) { + rc := new(RunContext) + rc.StepResults = make(map[string]*model.StepResult) + rc.CurrentStep = "my-step" + rc.StepResults[rc.CurrentStep] = &model.StepResult{ + Outputs: make(map[string]string), + } + + a := assert.New(t) + + re := captureOutput(t, func() { + ctx := context.Background() + ctx = WithJobLogger(ctx, "testjob", map[string]string{}, false, &rc.Masks) + + handler := rc.commandHandler(ctx) + handler("::add-mask::secret\n") + handler("::set-output:: token=secret\n") + }) + + a.Equal("[testjob] \U00002699 ***\n[testjob] \U00002699 ::set-output:: = token=***\n", re) +} diff --git a/pkg/runner/logger.go b/pkg/runner/logger.go index 8da4bf1..0ad2cd1 100644 --- a/pkg/runner/logger.go +++ b/pkg/runner/logger.go @@ -38,13 +38,15 @@ func init() { } // WithJobLogger attaches a new logger to context that is aware of steps -func WithJobLogger(ctx context.Context, jobName string, secrets map[string]string, insecureSecrets bool) context.Context { +func WithJobLogger(ctx context.Context, jobName string, secrets map[string]string, insecureSecrets bool, masks *[]string) context.Context { mux.Lock() defer mux.Unlock() formatter := new(stepLogFormatter) formatter.color = colors[nextColor%len(colors)] formatter.secrets = secrets formatter.insecureSecrets = insecureSecrets + formatter.masks = masks + nextColor++ logger := logrus.New() @@ -66,6 +68,7 @@ type stepLogFormatter struct { color int secrets map[string]string insecureSecrets bool + masks *[]string } func (f *stepLogFormatter) Format(entry *logrus.Entry) ([]byte, error) { @@ -78,6 +81,12 @@ func (f *stepLogFormatter) Format(entry *logrus.Entry) ([]byte, error) { entry.Message = strings.ReplaceAll(entry.Message, v, "***") } } + + for _, v := range *f.masks { + if v != "" { + entry.Message = strings.ReplaceAll(entry.Message, v, "***") + } + } } if f.isColored(entry) { diff --git a/pkg/runner/run_context.go b/pkg/runner/run_context.go index 2131e96..620410d 100644 --- a/pkg/runner/run_context.go +++ b/pkg/runner/run_context.go @@ -46,6 +46,11 @@ type RunContext struct { Composite *model.Action Inputs map[string]interface{} Parent *RunContext + Masks []string +} + +func (rc *RunContext) AddMask(mask string) { + rc.Masks = append(rc.Masks, mask) } func (rc *RunContext) Clone() *RunContext { diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index d7020dd..18d499b 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -164,7 +164,7 @@ func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor { } return nil - })(common.WithJobErrorContainer(WithJobLogger(ctx, jobName, rc.Config.Secrets, rc.Config.InsecureSecrets))) + })(common.WithJobErrorContainer(WithJobLogger(ctx, jobName, rc.Config.Secrets, rc.Config.InsecureSecrets, &rc.Masks))) }) } pipeline = append(pipeline, common.NewParallelExecutor(maxParallel, stageExecutor...)) diff --git a/pkg/runner/step_context.go b/pkg/runner/step_context.go index 51ce945..bdf3ead 100644 --- a/pkg/runner/step_context.go +++ b/pkg/runner/step_context.go @@ -671,6 +671,8 @@ func (sc *StepContext) execAsComposite(ctx context.Context, step *model.Step, _ "name": outputName, }, eval.Interpolate(output.Value)) } + + backup.Masks = append(backup.Masks, compositerc.Masks...) // Test if evaluated parent env was altered by this composite step // Known Issues: // - you try to set an env variable to the same value as a scoped step env, will be discared