From c74b2fe7a42540a82e0622d7c0a8c95004068bd0 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Wed, 26 Jan 2022 10:29:17 +0100 Subject: [PATCH] bake: restrict target name This fix adds a restriction `[a-zA-Z0-9_-]+` for target name. This is pretty much the same as the container name restriction in moby. Signed-off-by: CrazyMax --- bake/bake.go | 21 +++++++++++--- bake/bake_test.go | 55 +++++++++++++++++++++++++++++++++++++ bake/compose.go | 5 ++++ bake/compose_test.go | 52 +++++++++++++++++++++++++++++++++++ bake/hcl.go | 2 +- bake/hclparser/hclparser.go | 22 +++++++++++++-- 6 files changed, 150 insertions(+), 7 deletions(-) diff --git a/bake/bake.go b/bake/bake.go index 6848559b..295264f6 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -22,8 +22,13 @@ import ( "github.com/pkg/errors" ) -var httpPrefix = regexp.MustCompile(`^https?://`) -var gitURLPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`) +var ( + httpPrefix = regexp.MustCompile(`^https?://`) + gitURLPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`) + + validTargetNameChars = `[a-zA-Z0-9_-]+` + targetNamePattern = regexp.MustCompile(`^` + validTargetNameChars + `$`) +) type File struct { Name string @@ -176,8 +181,9 @@ func ParseFiles(files []File, defaults map[string]string) (_ *Config, err error) if len(fs) > 0 { if err := hclparser.Parse(hcl.MergeFiles(fs), hclparser.Opt{ - LookupVar: os.LookupEnv, - Vars: defaults, + LookupVar: os.LookupEnv, + Vars: defaults, + ValidateLabel: validateTargetName, }, &c); err.HasErrors() { return nil, err } @@ -798,3 +804,10 @@ func parseOutputType(str string) string { } return "" } + +func validateTargetName(name string) error { + if !targetNamePattern.MatchString(name) { + return errors.Errorf("only %q are allowed", validTargetNameChars) + } + return nil +} diff --git a/bake/bake_test.go b/bake/bake_test.go index 32023d5d..955dd0d1 100644 --- a/bake/bake_test.go +++ b/bake/bake_test.go @@ -788,3 +788,58 @@ group "default" { }) } } + +func TestTargetName(t *testing.T) { + ctx := context.TODO() + cases := []struct { + target string + wantErr bool + }{ + { + target: "a", + wantErr: false, + }, + { + target: "abc", + wantErr: false, + }, + { + target: "a/b", + wantErr: true, + }, + { + target: "a.b", + wantErr: true, + }, + { + target: "_a", + wantErr: false, + }, + { + target: "a_b", + wantErr: false, + }, + { + target: "AbC", + wantErr: false, + }, + { + target: "AbC-0123", + wantErr: false, + }, + } + for _, tt := range cases { + tt := tt + t.Run(tt.target, func(t *testing.T) { + _, _, err := ReadTargets(ctx, []File{{ + Name: "docker-bake.hcl", + Data: []byte(`target "` + tt.target + `" {}`), + }}, []string{tt.target}, nil, nil) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/bake/compose.go b/bake/compose.go index 2872c72d..07f2f158 100644 --- a/bake/compose.go +++ b/bake/compose.go @@ -8,6 +8,7 @@ import ( "github.com/compose-spec/compose-go/loader" compose "github.com/compose-spec/compose-go/types" + "github.com/pkg/errors" ) func parseCompose(dt []byte) (*compose.Project, error) { @@ -59,6 +60,10 @@ func ParseCompose(dt []byte) (*Config, error) { continue } + if err = validateTargetName(s.Name); err != nil { + return nil, errors.Wrapf(err, "invalid service name %q", s.Name) + } + var contextPathP *string if s.Build.Context != "" { contextPath := s.Build.Context diff --git a/bake/compose_test.go b/bake/compose_test.go index a5389007..fffd97b0 100644 --- a/bake/compose_test.go +++ b/bake/compose_test.go @@ -314,3 +314,55 @@ func newBool(val bool) *bool { b := val return &b } + +func TestServiceName(t *testing.T) { + cases := []struct { + svc string + wantErr bool + }{ + { + svc: "a", + wantErr: false, + }, + { + svc: "abc", + wantErr: false, + }, + { + svc: "a.b", + wantErr: true, + }, + { + svc: "_a", + wantErr: false, + }, + { + svc: "a_b", + wantErr: false, + }, + { + svc: "AbC", + wantErr: false, + }, + { + svc: "AbC-0123", + wantErr: false, + }, + } + for _, tt := range cases { + tt := tt + t.Run(tt.svc, func(t *testing.T) { + _, err := ParseCompose([]byte(` +services: + ` + tt.svc + `: + build: + context: . +`)) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/bake/hcl.go b/bake/hcl.go index c0b321af..9c059cd8 100644 --- a/bake/hcl.go +++ b/bake/hcl.go @@ -3,7 +3,7 @@ package bake import ( "strings" - hcl "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclparse" "github.com/moby/buildkit/solver/errdefs" "github.com/moby/buildkit/solver/pb" diff --git a/bake/hclparser/hclparser.go b/bake/hclparser/hclparser.go index 6438c7eb..bf23dce8 100644 --- a/bake/hclparser/hclparser.go +++ b/bake/hclparser/hclparser.go @@ -16,8 +16,9 @@ import ( ) type Opt struct { - LookupVar func(string) (string, bool) - Vars map[string]string + LookupVar func(string) (string, bool) + Vars map[string]string + ValidateLabel func(string) error } type variable struct { @@ -262,6 +263,12 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics { } } + if opt.ValidateLabel == nil { + opt.ValidateLabel = func(string) error { + return nil + } + } + p := &parser{ opt: opt, @@ -446,6 +453,17 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics { continue } + if err := opt.ValidateLabel(b.Labels[0]); err != nil { + return hcl.Diagnostics{ + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid name", + Detail: err.Error(), + Subject: &b.LabelRanges[0], + }, + } + } + lblIndex := setLabel(vv, b.Labels[0]) oldValue, exists := t.values[b.Labels[0]]