diff --git a/pkg/iac-providers/docker/v1/load-dir.go b/pkg/iac-providers/docker/v1/load-dir.go index 17bb3503a..29865b118 100644 --- a/pkg/iac-providers/docker/v1/load-dir.go +++ b/pkg/iac-providers/docker/v1/load-dir.go @@ -60,18 +60,36 @@ func (dc *DockerV1) LoadIacDir(absRootDir string, nonRecursive bool) (output.All if err != nil { zap.S().Debug("error while getting the relative path for", zap.String("IAC file", file), zap.Error(err)) } + skipRules := utils.GetSkipRules(comments) + + dockerCommand := []string{} + for j := 0; j < len(data); j++ { + dockerCommand = append(dockerCommand, data[j].Cmd) + config := output.ResourceConfig{ + Name: *files[i], + Type: data[j].Cmd, + Line: data[j].Line, + ID: data[j].Cmd + "." + GetresourceIdforDockerfile(file, data[j].Value), + Source: sourcePath, + Config: data[j].Value, + SkipRules: skipRules, + MinSeverity: minSeverity, + MaxSeverity: maxSeverity, + } + allResourcesConfig[data[j].Cmd] = append(allResourcesConfig[data[j].Cmd], config) + + } config := output.ResourceConfig{ Name: *files[i], Type: resourceTypeDockerfile, Line: 1, - ID: dockerDirectory + "." + GetresourceIdforDockerfile(file), + ID: dockerDirectory + "." + GetresourceIdforDockerfile(file, ""), Source: sourcePath, - Config: data, - SkipRules: utils.GetSkipRules(comments), + Config: dockerCommand, + SkipRules: skipRules, MinSeverity: minSeverity, MaxSeverity: maxSeverity, } - allResourcesConfig[dockerDirectory] = append(allResourcesConfig[dockerDirectory], config) } } @@ -81,9 +99,9 @@ func (dc *DockerV1) LoadIacDir(absRootDir string, nonRecursive bool) (output.All } // GetresourceIdforDockerfile Generates hash of the string to be used as the reference id for docker file -func GetresourceIdforDockerfile(filepath string) (referenceID string) { +func GetresourceIdforDockerfile(filepath string, value string) (referenceID string) { hasher := md5.New() - hasher.Write([]byte(filepath)) + hasher.Write([]byte(filepath + value)) referenceID = strings.ToLower(hex.EncodeToString(hasher.Sum(nil))) return } diff --git a/pkg/iac-providers/docker/v1/load-file.go b/pkg/iac-providers/docker/v1/load-file.go index 25a67d443..ddc2893b5 100644 --- a/pkg/iac-providers/docker/v1/load-file.go +++ b/pkg/iac-providers/docker/v1/load-file.go @@ -36,14 +36,33 @@ func (dc *DockerV1) LoadIacFile(absFilePath string) (allResourcesConfig output.A return allResourcesConfig, errors.New(errMsg) } minSeverity, maxSeverity := utils.GetMinMaxSeverity(comments) + skipRules := utils.GetSkipRules(comments) + + dockerCommand := []string{} + for i := 0; i < len(data); i++ { + dockerCommand = append(dockerCommand, data[i].Cmd) + config := output.ResourceConfig{ + Name: filepath.Base(absFilePath), + Type: data[i].Cmd, + Line: data[i].Line, + ID: data[i].Cmd + "." + GetresourceIdforDockerfile(absFilePath, data[i].Value), + Source: filepath.Base(absFilePath), + Config: data[i].Value, + SkipRules: skipRules, + MinSeverity: minSeverity, + MaxSeverity: maxSeverity, + } + allResourcesConfig[data[i].Cmd] = append(allResourcesConfig[data[i].Cmd], config) + + } config := output.ResourceConfig{ Name: filepath.Base(absFilePath), Type: resourceTypeDockerfile, Line: 1, - ID: dockerDirectory + "." + GetresourceIdforDockerfile(absFilePath), + ID: dockerDirectory + "." + GetresourceIdforDockerfile(absFilePath, ""), Source: filepath.Base(absFilePath), - Config: data, - SkipRules: utils.GetSkipRules(comments), + Config: dockerCommand, + SkipRules: skipRules, MinSeverity: minSeverity, MaxSeverity: maxSeverity, } diff --git a/pkg/iac-providers/docker/v1/parser.go b/pkg/iac-providers/docker/v1/parser.go index 244928064..8947eba23 100644 --- a/pkg/iac-providers/docker/v1/parser.go +++ b/pkg/iac-providers/docker/v1/parser.go @@ -21,32 +21,16 @@ import ( "io/ioutil" "strings" - "github.com/moby/buildkit/frontend/dockerfile/command" "github.com/moby/buildkit/frontend/dockerfile/instructions" "github.com/moby/buildkit/frontend/dockerfile/parser" "go.uber.org/zap" ) -// DockerConfig holds configuration of dockerfile -type DockerConfig struct { - Args []string `json:"args"` - Cmd []string `json:"cmd"` - From []string `json:"from"` - Labels []string `json:"labels"` - Run []string `json:"run"` - Expose []string `json:"expose"` - Env []string `json:"env"` - Add []string `json:"add"` - Copy []string `json:"copy"` - Entrypoint []string `json:"entrypoint"` - Volume []string `json:"volume"` - User []string `json:"user"` - WorkDir []string `json:"work_dir"` - Onbuild []string `json:"onBuild"` - Maintainer []string `json:"maintainer"` - HealthCheck []string `json:"healthCheck"` - Shell []string `json:"shell"` - StopSignal []string `json:"stopSignal"` +// ResourceConfig holds information about individual docker instructions +type ResourceConfig struct { + Cmd string `json:"cmd"` + Value string `json:"value"` + Line int `json:"line"` } const ( @@ -62,26 +46,26 @@ func (dc *DockerV1) ValidateInstruction(node *parser.Node) error { } // Parse parses the given dockerfile and gives docker config. -func (dc *DockerV1) Parse(filepath string) (DockerConfig, string, error) { - dockerConfig := DockerConfig{} +func (dc *DockerV1) Parse(filepath string) ([]ResourceConfig, string, error) { + config := []ResourceConfig{} data, err := ioutil.ReadFile(filepath) comments := "" if err != nil { zap.S().Error("error loading docker file", filepath, zap.Error(err)) - return DockerConfig{}, "", err + return []ResourceConfig{}, "", err } r := bytes.NewReader(data) res, err := parser.Parse(r) if err != nil { zap.S().Errorf("error while parsing iac file", filepath, zap.Error(err)) - return DockerConfig{}, "", err + return []ResourceConfig{}, "", err } for _, child := range res.AST.Children { values := []string{} err = dc.ValidateInstruction(child) if err != nil { - return DockerConfig{}, "", err + return []ResourceConfig{}, "", err } for _, comment := range child.PrevComment { @@ -92,46 +76,12 @@ func (dc *DockerV1) Parse(filepath string) (DockerConfig, string, error) { values = append(values, i.Value) } value := strings.Join(values, stringJoinCharacter) - switch child.Value { - case command.Arg: - dockerConfig.Args = append(dockerConfig.Args, value) - case command.Cmd: - dockerConfig.Cmd = append(dockerConfig.Cmd, value) - case command.From: - dockerConfig.From = append(dockerConfig.From, value) - case command.Label: - dockerConfig.Labels = append(dockerConfig.Labels, value) - case command.Run: - dockerConfig.Run = append(dockerConfig.Run, value) - case command.Expose: - dockerConfig.Expose = append(dockerConfig.Expose, value) - case command.Env: - dockerConfig.Env = append(dockerConfig.Env, value) - case command.Add: - dockerConfig.Add = append(dockerConfig.Add, value) - case command.Copy: - dockerConfig.Copy = append(dockerConfig.Copy, value) - case command.Entrypoint: - dockerConfig.Entrypoint = append(dockerConfig.Entrypoint, value) - case command.Volume: - dockerConfig.Volume = append(dockerConfig.Volume, value) - case command.User: - dockerConfig.User = append(dockerConfig.User, value) - case command.Workdir: - dockerConfig.WorkDir = append(dockerConfig.WorkDir, value) - case command.Onbuild: - dockerConfig.Onbuild = append(dockerConfig.Onbuild, value) - case command.Healthcheck: - dockerConfig.HealthCheck = append(dockerConfig.HealthCheck, value) - case command.Maintainer: - dockerConfig.Maintainer = append(dockerConfig.Maintainer, value) - case command.Shell: - dockerConfig.Shell = append(dockerConfig.Shell, value) - case command.StopSignal: - dockerConfig.StopSignal = append(dockerConfig.StopSignal, value) - default: - zap.S().Errorf("Unknow command %s", child.Value, nil) + tempConfig := ResourceConfig{ + Cmd: child.Value, + Value: value, + Line: child.StartLine, } + config = append(config, tempConfig) } - return dockerConfig, comments, nil + return config, comments, nil } diff --git a/pkg/iac-providers/docker/v1/parser_test.go b/pkg/iac-providers/docker/v1/parser_test.go index ad68c0101..7f1be902d 100644 --- a/pkg/iac-providers/docker/v1/parser_test.go +++ b/pkg/iac-providers/docker/v1/parser_test.go @@ -29,7 +29,7 @@ func TestParse(t *testing.T) { name string filePath string dockerv1 DockerV1 - want DockerConfig + want []ResourceConfig wantErr error }{ { @@ -37,12 +37,13 @@ func TestParse(t *testing.T) { filePath: filepath.Join(fileTestDataDir, "dockerfile-testparse-function"), dockerv1: DockerV1{}, wantErr: nil, - want: DockerConfig{Args: []string{"name=defaultValue"}, Cmd: []string{"server"}, From: []string{"runatlantis/atlantis:v0.16.1"}, Labels: []string{"key \"value\""}, Run: []string{"mkdir -p /etc/atlantis/ && chmod +x /usr/local/bin/*.sh && /usr/local/bin/setup.sh", "terrascan init"}, Expose: []string{"9090"}, Env: []string{"DEFAULT_TERRASCAN_VERSION 1.5.1", "PLANFILE tfplan"}, Add: []string{"setup.sh terrascan.sh launch-atlantis.sh entrypoint.sh /usr/local/bin/"}, Copy: []string{"terrascan-workflow.yaml /etc/atlantis/workflow.yaml"}, Entrypoint: []string{"/bin/bash entrypoint.sh"}, Volume: []string{"/temp"}, User: []string{"atlantis"}, WorkDir: []string{"test"}, Onbuild: []string{""}, Maintainer: []string{"accurics"}, HealthCheck: []string{"CMD executable"}, Shell: []string{"cd"}, StopSignal: []string{"1"}}, + want: []ResourceConfig{{Cmd: "from", Value: "runatlantis/atlantis:v0.16.1", Line: 1}, {Cmd: "maintainer", Value: "accurics", Line: 2}, {Cmd: "label", Value: "key \"value\"", Line: 3}, {Cmd: "workdir", Value: "test", Line: 4}, {Cmd: "env", Value: "DEFAULT_TERRASCAN_VERSION 1.5.1", Line: 5}, {Cmd: "env", Value: "PLANFILE tfplan", Line: 6}, {Cmd: "add", Value: "setup.sh terrascan.sh launch-atlantis.sh entrypoint.sh /usr/local/bin/", Line: 7}, {Cmd: "run", Value: "mkdir -p /etc/atlantis/ && chmod +x /usr/local/bin/*.sh && /usr/local/bin/setup.sh", Line: 8}, {Cmd: "copy", Value: "terrascan-workflow.yaml /etc/atlantis/workflow.yaml", Line: 11}, {Cmd: "user", Value: "atlantis", Line: 13}, {Cmd: "arg", Value: "name=defaultValue", Line: 14}, {Cmd: "run", Value: "terrascan init", Line: 15}, {Cmd: "volume", Value: "/temp", Line: 16}, {Cmd: "healthcheck", Value: "CMD executable", Line: 17}, {Cmd: "entrypoint", Value: "/bin/bash entrypoint.sh", Line: 18}, {Cmd: "shell", Value: "cd", Line: 19}, {Cmd: "onbuild", Value: "", Line: 20}, {Cmd: "expose", Value: "9090", Line: 21}, {Cmd: "stopsignal", Value: "1", Line: 22}, {Cmd: "cmd", Value: "server", Line: 23}}, }, { name: "invalid docker file path", filePath: filepath.Join(fileTestDataDir, "dockerfile-testparse-function1"), dockerv1: DockerV1{}, + want: []ResourceConfig{}, wantErr: fmt.Errorf("open %s: no such file or directory", filepath.Join(fileTestDataDir, "dockerfile-testparse-function1")), }, } diff --git a/pkg/iac-providers/docker/v1/testdata/file-test-data/dockerfile-testparse-function b/pkg/iac-providers/docker/v1/testdata/file-test-data/dockerfile-testparse-function index 8f1c2ced1..9ba4f9468 100644 --- a/pkg/iac-providers/docker/v1/testdata/file-test-data/dockerfile-testparse-function +++ b/pkg/iac-providers/docker/v1/testdata/file-test-data/dockerfile-testparse-function @@ -9,6 +9,7 @@ RUN mkdir -p /etc/atlantis/ && \ chmod +x /usr/local/bin/*.sh && \ /usr/local/bin/setup.sh Copy terrascan-workflow.yaml /etc/atlantis/workflow.yaml +# run as non root user USER atlantis ARG name=defaultValue RUN terrascan init