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":
|
case "error":
|
||||||
logger.Infof(" \U00002757 %s", line)
|
logger.Infof(" \U00002757 %s", line)
|
||||||
case "add-mask":
|
case "add-mask":
|
||||||
|
rc.AddMask(arg)
|
||||||
logger.Infof(" \U00002699 %s", "***")
|
logger.Infof(" \U00002699 %s", "***")
|
||||||
case "stop-commands":
|
case "stop-commands":
|
||||||
resumeCommand = arg
|
resumeCommand = arg
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package runner
|
package runner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus/hooks/test"
|
"github.com/sirupsen/logrus/hooks/test"
|
||||||
|
@ -113,5 +116,55 @@ func TestAddmask(t *testing.T) {
|
||||||
handler := rc.commandHandler(loggerCtx)
|
handler := rc.commandHandler(loggerCtx)
|
||||||
handler("::add-mask::my-secret-value\n")
|
handler("::add-mask::my-secret-value\n")
|
||||||
|
|
||||||
|
a.Equal(" \U00002699 ***", hook.LastEntry().Message)
|
||||||
a.NotEqual(" \U00002699 *my-secret-value", 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
|
// 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()
|
mux.Lock()
|
||||||
defer mux.Unlock()
|
defer mux.Unlock()
|
||||||
formatter := new(stepLogFormatter)
|
formatter := new(stepLogFormatter)
|
||||||
formatter.color = colors[nextColor%len(colors)]
|
formatter.color = colors[nextColor%len(colors)]
|
||||||
formatter.secrets = secrets
|
formatter.secrets = secrets
|
||||||
formatter.insecureSecrets = insecureSecrets
|
formatter.insecureSecrets = insecureSecrets
|
||||||
|
formatter.masks = masks
|
||||||
|
|
||||||
nextColor++
|
nextColor++
|
||||||
|
|
||||||
logger := logrus.New()
|
logger := logrus.New()
|
||||||
|
@ -66,6 +68,7 @@ type stepLogFormatter struct {
|
||||||
color int
|
color int
|
||||||
secrets map[string]string
|
secrets map[string]string
|
||||||
insecureSecrets bool
|
insecureSecrets bool
|
||||||
|
masks *[]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *stepLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
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, "***")
|
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) {
|
if f.isColored(entry) {
|
||||||
|
|
|
@ -46,6 +46,11 @@ type RunContext struct {
|
||||||
Composite *model.Action
|
Composite *model.Action
|
||||||
Inputs map[string]interface{}
|
Inputs map[string]interface{}
|
||||||
Parent *RunContext
|
Parent *RunContext
|
||||||
|
Masks []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *RunContext) AddMask(mask string) {
|
||||||
|
rc.Masks = append(rc.Masks, mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rc *RunContext) Clone() *RunContext {
|
func (rc *RunContext) Clone() *RunContext {
|
||||||
|
|
|
@ -164,7 +164,7 @@ func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor {
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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...))
|
pipeline = append(pipeline, common.NewParallelExecutor(maxParallel, stageExecutor...))
|
||||||
|
|
|
@ -671,6 +671,8 @@ func (sc *StepContext) execAsComposite(ctx context.Context, step *model.Step, _
|
||||||
"name": outputName,
|
"name": outputName,
|
||||||
}, eval.Interpolate(output.Value))
|
}, eval.Interpolate(output.Value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backup.Masks = append(backup.Masks, compositerc.Masks...)
|
||||||
// Test if evaluated parent env was altered by this composite step
|
// Test if evaluated parent env was altered by this composite step
|
||||||
// Known Issues:
|
// Known Issues:
|
||||||
// - you try to set an env variable to the same value as a scoped step env, will be discared
|
// - you try to set an env variable to the same value as a scoped step env, will be discared
|
||||||
|
|
Loading…
Add table
Reference in a new issue