diff --git a/files.go b/files.go index ce3cdfd12..a92a14091 100644 --- a/files.go +++ b/files.go @@ -95,7 +95,7 @@ func (s *SourceInlineDir) validate() error { for k, d := range s.Dirs { if strings.ContainsRune(k, os.PathSeparator) { - errs = append(errs, errors.Errorf("dir name %q must not contain path separator", k)) + errs = append(errs, errors.Wrapf(sourceNamePathSeparatorError, "dir %q", k)) } if err := d.validate(); err != nil { @@ -105,7 +105,7 @@ func (s *SourceInlineDir) validate() error { for k, f := range s.Files { if strings.ContainsRune(k, os.PathSeparator) { - errs = append(errs, errors.Errorf("file name %q must not contain path separator", k)) + errs = append(errs, errors.Wrapf(sourceNamePathSeparatorError, "file %q", k)) } if err := f.validate(); err != nil { errs = append(errs, errors.Wrapf(err, "file %q", k)) diff --git a/load.go b/load.go index 07572df6b..f74257590 100644 --- a/load.go +++ b/load.go @@ -3,6 +3,7 @@ package dalec import ( goerrors "errors" "fmt" + "os" "path" "strings" @@ -370,14 +371,17 @@ func (s *Spec) FillDefaults() { func (s Spec) Validate() error { for name, src := range s.Sources { + if strings.ContainsRune(name, os.PathSeparator) { + return &InvalidSourceError{Name: name, Err: sourceNamePathSeparatorError} + } if err := src.validate(); err != nil { - return fmt.Errorf("error validating source ref %q: %w", name, err) + return &InvalidSourceError{Name: name, Err: fmt.Errorf("error validating source ref %q: %w", name, err)} } if src.DockerImage != nil && src.DockerImage.Cmd != nil { for p, cfg := range src.DockerImage.Cmd.CacheDirs { if _, err := sharingMode(cfg.Mode); err != nil { - return errors.Wrapf(err, "invalid sharing mode for source %q with cache mount at path %q", name, p) + return &InvalidSourceError{Name: name, Err: errors.Wrapf(err, "invalid sharing mode for source %q with cache mount at path %q", name, p)} } } } diff --git a/load_test.go b/load_test.go index 97ed240a6..d4334b4d3 100644 --- a/load_test.go +++ b/load_test.go @@ -2,6 +2,7 @@ package dalec import ( "encoding/json" + "errors" "fmt" "os" "reflect" @@ -443,3 +444,33 @@ sources: }) } } + +func TestSourceNameWithPathSeparator(t *testing.T) { + spec := &Spec{ + Sources: map[string]Source{ + "forbidden/name": { + Inline: &SourceInline{ + File: &SourceInlineFile{}, + }, + }, + }, + } + + err := spec.Validate() + if err == nil { + t.Fatal("expected error, but received none") + } + + var expected *InvalidSourceError + if !errors.As(err, &expected) { + t.Fatalf("expected %T, got %T", expected, err) + } + + if expected.Name != "forbidden/name" { + t.Error("expected error to contain source name") + } + + if !errors.Is(err, sourceNamePathSeparatorError) { + t.Errorf("expected error to be sourceNamePathSeparatorError, got: %v", err) + } +} diff --git a/source.go b/source.go index dc38fdcf7..b8d3d4fe2 100644 --- a/source.go +++ b/source.go @@ -11,8 +11,25 @@ import ( "github.com/moby/buildkit/frontend/dockerui" "github.com/moby/buildkit/identity" "github.com/moby/buildkit/util/gitutil" + "github.com/pkg/errors" ) +// InvalidSourceError is an error type returned when a source is invalid. +type InvalidSourceError struct { + Name string + Err error +} + +func (s *InvalidSourceError) Error() string { + return fmt.Sprintf("invalid source %s: %v", s.Name, s.Err) +} + +func (s *InvalidSourceError) Unwrap() error { + return s.Err +} + +var sourceNamePathSeparatorError = errors.New("source name must not container path separator") + type LLBGetter func(sOpts SourceOpts, opts ...llb.ConstraintsOpt) (llb.State, error) type ForwarderFunc func(llb.State, *SourceBuild) (llb.State, error)