fix: hide masked values in log files (#1011)

this commit adds support for the `::add-mask::` command, which was
implemented as a stub before.

it does not cover debug output that appears when you run act in
verbose mode

Co-authored-by: Björn Brauer <bjoern.brauer@new-work.se>
Co-authored-by: Markus Wolf <markus.wolf@new-work.se>

Co-authored-by: Björn Brauer <bjoern.brauer@new-work.se>
Co-authored-by: Markus Wolf <markus.wolf@new-work.se>
This commit is contained in:
Robert Kowalski 2022-03-02 09:29:34 +01:00 committed by GitHub
parent e9e6ddadf5
commit c22d833830
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 72 additions and 2 deletions

View file

@ -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

View file

@ -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)
}

View file

@ -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) {

View file

@ -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 {

View file

@ -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...))

View file

@ -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