diff --git a/cmd/drone-build/run.go b/cmd/drone-build/run.go index 153060272..ca179363a 100644 --- a/cmd/drone-build/run.go +++ b/cmd/drone-build/run.go @@ -13,14 +13,15 @@ import ( type Context struct { // Links *common.Link - Clone *common.Clone `json:"clone"` - Repo *common.Repo `json:"repo"` - Commit *common.Commit `json:"commit"` - Build *common.Build `json:"build"` - Keys *common.Keypair `json:"keys"` - Netrc *common.Netrc `json:"netrc"` - Yaml []byte `json:"yaml"` - Env []string `json:"env"` + Clone *common.Clone `json:"clone"` + Repo *common.Repo `json:"repo"` + Commit *common.Commit `json:"commit"` + Build *common.Build `json:"build"` + Keys *common.Keypair `json:"keys"` + Netrc *common.Netrc `json:"netrc"` + Yaml []byte `json:"yaml"` + Env []string `json:"environment"` + Plugins []string `json:"plugins"` Conf *common.Config `json:"-"` infos []*dockerclient.ContainerInfo @@ -29,12 +30,19 @@ type Context struct { func setup(c *Context) error { var err error - var opts = parser.DefaultOpts + var opts = parser.Opts{ + Network: true, + Privileged: true, + Volumes: true, + Whitelist: c.Plugins, + } // if repository is trusted the build may specify // custom volumes, networking and run in trusted mode. if c.Repo.Trusted { - opts = &parser.Opts{Network: true, Privileged: true, Volumes: true} + opts.Network = true + opts.Privileged = true + opts.Volumes = true } // inject the matrix parameters into the yaml diff --git a/pkg/queue/worker.go b/pkg/queue/worker.go index affbb4adc..611e77fa9 100644 --- a/pkg/queue/worker.go +++ b/pkg/queue/worker.go @@ -9,13 +9,14 @@ import ( // Work represents an item for work to be // processed by a worker. type Work struct { - User *common.User `json:"user"` - Repo *common.Repo `json:"repo"` - Commit *common.Commit `json:"commit"` - Keys *common.Keypair `json:"keypair"` - Netrc *common.Netrc `json:"netrc"` - Yaml []byte `json:"yaml"` - Env []string `json:"env"` + User *common.User `json:"user"` + Repo *common.Repo `json:"repo"` + Commit *common.Commit `json:"commit"` + Keys *common.Keypair `json:"keypair"` + Netrc *common.Netrc `json:"netrc"` + Yaml []byte `json:"yaml"` + Env []string `json:"environment"` + Plugins []string `json:"plugins"` } // represents a worker that has connected diff --git a/pkg/runner/builtin/runner.go b/pkg/runner/builtin/runner.go index 2493808ac..ca8a551e6 100644 --- a/pkg/runner/builtin/runner.go +++ b/pkg/runner/builtin/runner.go @@ -112,13 +112,14 @@ func (r *Runner) Run(w *queue.Work) error { } work := &work{ - Repo: w.Repo, - Commit: w.Commit, - Keys: w.Keys, - Netrc: w.Netrc, - Yaml: w.Yaml, - Build: task, - Env: w.Env, + Repo: w.Repo, + Commit: w.Commit, + Keys: w.Keys, + Netrc: w.Netrc, + Yaml: w.Yaml, + Build: task, + Env: w.Env, + Plugins: w.Plugins, } in, err := json.Marshal(work) if err != nil { diff --git a/pkg/runner/builtin/worker.go b/pkg/runner/builtin/worker.go index 27539bce3..19031175f 100644 --- a/pkg/runner/builtin/worker.go +++ b/pkg/runner/builtin/worker.go @@ -52,13 +52,14 @@ var ( ) type work struct { - Repo *common.Repo `json:"repo"` - Commit *common.Commit `json:"commit"` - Build *common.Build `json:"build"` - Keys *common.Keypair `json:"keys"` - Netrc *common.Netrc `json:"netrc"` - Yaml []byte `json:"yaml"` - Env []string `json:"env"` + Repo *common.Repo `json:"repo"` + Commit *common.Commit `json:"commit"` + Build *common.Build `json:"build"` + Keys *common.Keypair `json:"keys"` + Netrc *common.Netrc `json:"netrc"` + Yaml []byte `json:"yaml"` + Env []string `json:"environment"` + Plugins []string `json:"plugins"` } type worker struct { diff --git a/pkg/yaml/lint.go b/pkg/yaml/lint.go index ef515f518..07b779365 100644 --- a/pkg/yaml/lint.go +++ b/pkg/yaml/lint.go @@ -2,6 +2,7 @@ package parser import ( "fmt" + "path/filepath" "strings" common "github.com/drone/drone/pkg/types" @@ -103,3 +104,42 @@ func expectTrustedNotify(c *common.Config) error { } return nil } + +func LintPlugins(c *common.Config, opts *Opts) error { + if len(opts.Whitelist) == 0 { + return nil + } + + var images []string + images = append(images, c.Setup.Image) + images = append(images, c.Clone.Image) + c.Clone.Image = imageName(c.Clone.Image) + for _, step := range c.Publish { + images = append(images, step.Image) + } + for _, step := range c.Deploy { + images = append(images, step.Image) + } + for _, step := range c.Notify { + images = append(images, step.Image) + } + + for _, image := range images { + match := false + for _, pattern := range opts.Whitelist { + if pattern == image { + match = true + break + } + ok, err := filepath.Match(pattern, image) + if ok && err == nil { + match = true + break + } + } + if !match { + return fmt.Errorf("Cannot use un-trusted image %s", image) + } + } + return nil +} diff --git a/pkg/yaml/lint_test.go b/pkg/yaml/lint_test.go index 450426445..e224c66f5 100644 --- a/pkg/yaml/lint_test.go +++ b/pkg/yaml/lint_test.go @@ -90,3 +90,58 @@ func Test_Linter(t *testing.T) { }) } + +func Test_LintPlugins(t *testing.T) { + + g := goblin.Goblin(t) + g.Describe("Plugin Linter", func() { + + g.It("Should fail un-trusted plugin", func() { + c := &common.Config{ + Setup: &common.Step{Image: "foo/baz"}, + Clone: &common.Step{Image: "foo/bar"}, + Notify: map[string]*common.Step{}, + Deploy: map[string]*common.Step{}, + Publish: map[string]*common.Step{}, + } + o := &Opts{Whitelist: []string{"plugins/*"}} + g.Assert(LintPlugins(c, o) != nil).IsTrue() + }) + + g.It("Should pass when empty whitelist", func() { + c := &common.Config{ + Setup: &common.Step{Image: "foo/baz"}, + Clone: &common.Step{Image: "foo/bar"}, + Notify: map[string]*common.Step{}, + Deploy: map[string]*common.Step{}, + Publish: map[string]*common.Step{}, + } + o := &Opts{Whitelist: []string{}} + g.Assert(LintPlugins(c, o) == nil).IsTrue() + }) + + g.It("Should pass wildcard", func() { + c := &common.Config{ + Setup: &common.Step{Image: "plugins/drone-setup"}, + Clone: &common.Step{Image: "plugins/drone-build"}, + Notify: map[string]*common.Step{}, + Deploy: map[string]*common.Step{}, + Publish: map[string]*common.Step{}, + } + o := &Opts{Whitelist: []string{"plugins/*"}} + g.Assert(LintPlugins(c, o) == nil).IsTrue() + }) + + g.It("Should pass itemized", func() { + c := &common.Config{ + Setup: &common.Step{Image: "plugins/drone-setup"}, + Clone: &common.Step{Image: "plugins/drone-build"}, + Notify: map[string]*common.Step{}, + Deploy: map[string]*common.Step{}, + Publish: map[string]*common.Step{}, + } + o := &Opts{Whitelist: []string{"plugins/drone-setup", "plugins/drone-build"}} + g.Assert(LintPlugins(c, o) == nil).IsTrue() + }) + }) +} diff --git a/pkg/yaml/parse.go b/pkg/yaml/parse.go index 6a6f420d7..02a2b3c7e 100644 --- a/pkg/yaml/parse.go +++ b/pkg/yaml/parse.go @@ -14,12 +14,14 @@ type Opts struct { Volumes bool Network bool Privileged bool + Whitelist []string } var DefaultOpts = &Opts{ Volumes: false, Network: false, Privileged: false, + Whitelist: []string{"plugins/*"}, } // Parse parses a build matrix and returns