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:
parent
e9e6ddadf5
commit
c22d833830
6 changed files with 72 additions and 2 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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...))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue