diff --git a/go.mod b/go.mod index d27af7f..551e659 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/docker/go-connections v0.4.0 github.com/go-git/go-billy/v5 v5.4.1 github.com/go-git/go-git/v5 v5.7.0 + github.com/gobwas/glob v0.2.3 github.com/imdario/mergo v0.3.15 github.com/joho/godotenv v1.5.1 github.com/julienschmidt/httprouter v1.3.0 diff --git a/go.sum b/go.sum index cc8e3bb..45fb0dc 100644 --- a/go.sum +++ b/go.sum @@ -68,6 +68,8 @@ github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw4 github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE= github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= diff --git a/pkg/container/docker_run.go b/pkg/container/docker_run.go index 376c337..99d43fe 100644 --- a/pkg/container/docker_run.go +++ b/pkg/container/docker_run.go @@ -18,6 +18,7 @@ import ( "github.com/docker/docker/api/types/network" networktypes "github.com/docker/docker/api/types/network" + "github.com/gobwas/glob" "github.com/go-git/go-billy/v5/helper/polyfill" "github.com/go-git/go-billy/v5/osfs" @@ -883,13 +884,27 @@ func (cr *containerReference) wait() common.Executor { } } +// For Gitea +// sanitizeConfig remove the invalid configurations from `config` and `hostConfig` func (cr *containerReference) sanitizeConfig(ctx context.Context, config *container.Config, hostConfig *container.HostConfig) (*container.Config, *container.HostConfig) { logger := common.Logger(ctx) if len(cr.input.ValidVolumes) > 0 { - vv := make(map[string]struct{}, len(cr.input.ValidVolumes)) - for _, volume := range cr.input.ValidVolumes { - vv[volume] = struct{}{} + globs := make([]glob.Glob, 0, len(cr.input.ValidVolumes)) + for _, v := range cr.input.ValidVolumes { + if g, err := glob.Compile(v); err != nil { + logger.Errorf("create glob from %s error: %v", v, err) + } else { + globs = append(globs, g) + } + } + isValid := func(v string) bool { + for _, g := range globs { + if g.Match(v) { + return true + } + } + return false } // sanitize binds sanitizedBinds := make([]string, 0, len(hostConfig.Binds)) @@ -904,23 +919,26 @@ func (cr *containerReference) sanitizeConfig(ctx context.Context, config *contai sanitizedBinds = append(sanitizedBinds, bind) continue } - if _, ok := vv[parsed.Source]; ok { + if isValid(parsed.Source) { sanitizedBinds = append(sanitizedBinds, bind) } else { - logger.Warnf("[%s] is not a valid volume, will be ignored", bind) + logger.Warnf("[%s] is not a valid volume, will be ignored", parsed.Source) } } hostConfig.Binds = sanitizedBinds // sanitize mounts sanitizedMounts := make([]mount.Mount, 0, len(hostConfig.Mounts)) for _, mt := range hostConfig.Mounts { - if _, ok := vv[mt.Source]; ok { + if isValid(mt.Source) { sanitizedMounts = append(sanitizedMounts, mt) } else { logger.Warnf("[%s] is not a valid volume, will be ignored", mt.Source) } } hostConfig.Mounts = sanitizedMounts + } else { + hostConfig.Binds = []string{} + hostConfig.Mounts = []mount.Mount{} } return config, hostConfig diff --git a/pkg/container/docker_run_test.go b/pkg/container/docker_run_test.go index bc3ab4b..f288cf6 100644 --- a/pkg/container/docker_run_test.go +++ b/pkg/container/docker_run_test.go @@ -9,8 +9,12 @@ import ( "testing" "time" + "github.com/nektos/act/pkg/common" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" + "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -166,3 +170,76 @@ func TestDockerExecFailure(t *testing.T) { // Type assert containerReference implements ExecutionsEnvironment var _ ExecutionsEnvironment = &containerReference{} + +func TestCheckVolumes(t *testing.T) { + testCases := []struct { + desc string + validVolumes []string + binds []string + expectedBinds []string + }{ + { + desc: "match all volumes", + validVolumes: []string{"**"}, + binds: []string{ + "shared_volume:/shared_volume", + "/home/test/data:/test_data", + "/etc/conf.d/base.json:/config/base.json", + "sql_data:/sql_data", + "/secrets/keys:/keys", + }, + expectedBinds: []string{ + "shared_volume:/shared_volume", + "/home/test/data:/test_data", + "/etc/conf.d/base.json:/config/base.json", + "sql_data:/sql_data", + "/secrets/keys:/keys", + }, + }, + { + desc: "no volumes can be matched", + validVolumes: []string{}, + binds: []string{ + "shared_volume:/shared_volume", + "/home/test/data:/test_data", + "/etc/conf.d/base.json:/config/base.json", + "sql_data:/sql_data", + "/secrets/keys:/keys", + }, + expectedBinds: []string{}, + }, + { + desc: "only allowed volumes can be matched", + validVolumes: []string{ + "shared_volume", + "/home/test/data", + "/etc/conf.d/*.json", + }, + binds: []string{ + "shared_volume:/shared_volume", + "/home/test/data:/test_data", + "/etc/conf.d/base.json:/config/base.json", + "sql_data:/sql_data", + "/secrets/keys:/keys", + }, + expectedBinds: []string{ + "shared_volume:/shared_volume", + "/home/test/data:/test_data", + "/etc/conf.d/base.json:/config/base.json", + }, + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + logger, _ := test.NewNullLogger() + ctx := common.WithLogger(context.Background(), logger) + cr := &containerReference{ + input: &NewContainerInput{ + ValidVolumes: tc.validVolumes, + }, + } + _, hostConf := cr.sanitizeConfig(ctx, &container.Config{}, &container.HostConfig{Binds: tc.binds}) + assert.Equal(t, tc.expectedBinds, hostConf.Binds) + }) + } +}