Skip to content

Commit

Permalink
code nits: added new method for jsoncode type
Browse files Browse the repository at this point in the history
  • Loading branch information
Rchanger committed Aug 20, 2021
1 parent 5b7ed46 commit 605cf22
Show file tree
Hide file tree
Showing 21 changed files with 282 additions and 942 deletions.
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ require (
github.com/aws/aws-sdk-go-v2/service/s3 v1.11.1
github.com/aws/smithy-go v1.6.0
github.com/awslabs/goformation/v4 v4.19.1
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/ghodss/yaml v1.0.0
github.com/go-errors/errors v1.0.1
github.com/google/uuid v1.2.0
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1858,8 +1858,6 @@ golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.4 h1:cVngSRcfgyZCzys3KYOpCFa+4dqX/Oub9tAq00ttGVs=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
179 changes: 129 additions & 50 deletions pkg/iac-providers/terraform/commons/extract-container-images.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
Copyright (C) 2020 Accurics, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package commons

import (
Expand Down Expand Up @@ -29,31 +45,29 @@ const (
containerDefinitions = "container_definitions"
)

var k8sResources = make(map[string]struct{})

// all the type of resources which has container definitaions
var k8sResourcesTypes = []string{"kubernetes_deployment",
"kubernetes_pod", "kubernetes_stateful_set", "kubernetes_job",
"kubernetes_cron_job", "kubernetes_daemonset", "kubernetes_replication_controller"}

func init() {
for _, resource := range k8sResourcesTypes {
k8sResources[resource] = struct{}{}
}
var k8sResources = map[string]struct{}{
"kubernetes_deployment": {},
"kubernetes_pod": {},
"kubernetes_stateful_set": {},
"kubernetes_job": {},
"kubernetes_cron_job": {},
"kubernetes_daemonset": {},
"kubernetes_replication_controller": {},
}

// isKuberneteResource - verifies resource is k8s type and can we fetch container details from it
// isKuberneteResource - verifies resource is k8s type
func isKuberneteResource(resource *hclConfigs.Resource) bool {
_, ok := k8sResources[resource.Type]
return ok
}

//isAzureConatinerResource verifies resource is azure type and can we fetch container details from it
//isAzureConatinerResource verifies resource is azure type
func isAzureConatinerResource(resource *hclConfigs.Resource) bool {
return resource.Type == azureContainerResource
}

//isAwsConatinerResource verifies resource is aws type and can we fetch container details from it
//isAwsConatinerResource verifies resource is aws type
func isAwsConatinerResource(resource *hclConfigs.Resource) bool {
return resource.Type == awsContainerResources
}
Expand All @@ -63,65 +77,114 @@ func fetchContainersFromAzureResource(resource jsonObj) []output.ContainerNameAn
results := []output.ContainerNameAndImage{}
if v, ok := resource[container]; ok {
if containers, vok := v.([]jsonObj); vok {
for _, container := range containers {
tempContainer := output.ContainerNameAndImage{}
if image, iok := container[image]; iok {
tempContainer.Image = image.(string)
}
if name, nok := container[name]; nok {
tempContainer.Name = name.(string)
}
if tempContainer.Name == "" && tempContainer.Image == "" {
continue
}
results = append(results, tempContainer)
}
results = getContainers(containers)
}
}
return results
}

//fetchConatinersFromAwsResource extracts all the containers from aws ecs resource
func fetchContainersFromAwsResource(resource jsonObj, absRootDir string) []output.ContainerNameAndImage {
func fetchContainersFromAwsResource(resource jsonObj, hclBody *hclsyntax.Body, resourcePath string) []output.ContainerNameAndImage {
results := []output.ContainerNameAndImage{}
if v, ok := resource[containerDefinitions]; ok {
def := v.(string)
if strings.HasPrefix(def, jsonCodeSuffix) {
def = strings.TrimPrefix(def, jsonCodeSuffix)
def = strings.TrimSuffix(def, ")}")
return getContainersFromhclBody(hclBody)
} else if strings.HasPrefix(def, fileSuffix) {
file := strings.TrimPrefix(def, fileSuffix)
file = strings.TrimSuffix(file, `")}`)
dir := filepath.Dir(absRootDir)
file = filepath.Join(dir, file)
fileData, err := ioutil.ReadFile(file)
fileLocation := strings.TrimSpace(def)
fileLocation = strings.TrimPrefix(fileLocation, fileSuffix)
fileLocation = strings.TrimSuffix(fileLocation, `")}`)
dir := filepath.Dir(resourcePath)
if !filepath.IsAbs(fileLocation) {
fileLocation = filepath.Join(dir, fileLocation)
}
fileData, err := ioutil.ReadFile(fileLocation)
if err != nil {
zap.S().Errorf("error reading file: %s : %v", file, err)
zap.S().Errorf("error fetching containers from aws resource: %v", err)
return results
}
def = string(fileData)
}
containers := []jsonObj{}
err := json.Unmarshal([]byte(def), &containers)
if err != nil {
zap.S().Errorf("error unmarshaling string: %s : %v", def, err)
zap.S().Errorf("error fetching containers from aws resource: %v", err)
return results
}
for _, container := range containers {
tempContainer := output.ContainerNameAndImage{}
if image, iok := container[image]; iok {
tempContainer.Image = image.(string)
results = getContainers(containers)
}
return results
}

//getContainersFromhclBody parses the attribute and creates container object
func getContainersFromhclBody(hclBody *hclsyntax.Body) (results []output.ContainerNameAndImage) {
for _, v := range hclBody.Attributes {
if v.Name == containerDefinitions {
switch v.Expr.(type) {
case *hclsyntax.FunctionCallExpr:
funcExp := v.Expr.(*hclsyntax.FunctionCallExpr)
for _, arg := range funcExp.Args {
re, diags := arg.Value(nil)
if diags.HasErrors() {
zap.S().Errorf("error fetching containers from aws resource: %v", getErrorMessagesFromDiagnostics(diags))
return
}
if !re.CanIterateElements() {
return
}
it := re.ElementIterator()
for it.Next() {
_, val := it.Element()
containerTemp, err := ctyToMap(val)
if err != nil {
zap.S().Errorf("error fetching containers from aws resource: %v", err)
return
}
containerMap := containerTemp.(map[string]interface{})
tempContainer := output.ContainerNameAndImage{}
if image, iok := containerMap[image]; iok {
if imageName, ok := image.(string); ok {
tempContainer.Image = imageName
}
}
if name, nok := containerMap[name]; nok {
if containerName, ok := name.(string); ok {
tempContainer.Name = containerName
}
}
if tempContainer.Name == "" && tempContainer.Image == "" {
continue
}
results = append(results, tempContainer)
}
}
}
if name, nok := container[name]; nok {
tempContainer.Name = name.(string)
break
}
}
return
}

// getContainers reads and creates container config
func getContainers(containers []jsonObj) (results []output.ContainerNameAndImage) {
for _, container := range containers {
tempContainer := output.ContainerNameAndImage{}
if image, iok := container[image]; iok {
if imageName, ok := image.(string); ok {
tempContainer.Image = imageName
}
if tempContainer.Name == "" && tempContainer.Image == "" {
continue
}
if name, nok := container[name]; nok {
if containerName, ok := name.(string); ok {
tempContainer.Name = containerName
}
results = append(results, tempContainer)
}
if tempContainer.Name == "" && tempContainer.Image == "" {
continue
}
results = append(results, tempContainer)
}
return results
return
}

//extractContainerImagesFromk8sResources extracts containers from k8s resource
Expand Down Expand Up @@ -177,12 +240,10 @@ func getContainerConfigFromContainerBlock(containerBlocks []*hclsyntax.Block) (c
containerImage := output.ContainerNameAndImage{}
for _, attr := range conatainerBlock.Body.Attributes {
if attr.Name == image {
val, _ := attr.Expr.Value(nil)
containerImage.Image = val.AsString()
containerImage.Image = getValueFromCtyExpr(attr.Expr)
}
if attr.Name == name {
val, _ := attr.Expr.Value(nil)
containerImage.Name = val.AsString()
containerImage.Name = getValueFromCtyExpr(attr.Expr)
}
}
if containerImage.Image == "" && containerImage.Name == "" {
Expand All @@ -192,3 +253,21 @@ func getContainerConfigFromContainerBlock(containerBlocks []*hclsyntax.Block) (c
}
return
}

//getValueFromCtyExpr get value string from hcl expression
func getValueFromCtyExpr(expr hclsyntax.Expression) (value string) {
val, diags := expr.Value(nil)
if diags.HasErrors() {
zap.S().Errorf("error fetching containers from k8s resource: %v", getErrorMessagesFromDiagnostics(diags))
return
}
valInterface, err := ctyToStr(val)
if err != nil {
zap.S().Errorf("error fetching containers from k8s resource: %v", err)
return
}
if containerName, ok := valInterface.(string); ok {
value = containerName
}
return
}
4 changes: 0 additions & 4 deletions pkg/iac-providers/terraform/commons/load-dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,6 @@ func (t TerraformDirectoryLoader) loadDirNonRecursive() (output.AllResourceConfi
allResourcesConfig[resourceConfig.Type] = append(allResourcesConfig[resourceConfig.Type], resourceConfig)
}
}

zap.S().Infof(managedResource.Provider.ForDisplay())
zap.S().Infof(managedResource.Provider.Namespace)
zap.S().Infof(managedResource.Provider.Type)
}

// add all current's children to the queue
Expand Down
76 changes: 76 additions & 0 deletions pkg/iac-providers/terraform/commons/load-dir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,21 @@
package commons

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/accurics/terrascan/pkg/downloader"
"github.com/accurics/terrascan/pkg/iac-providers/output"
"github.com/accurics/terrascan/pkg/iac-providers/terraform/commons/test"
"github.com/accurics/terrascan/pkg/utils"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/hcl/v2"
hclConfigs "github.com/hashicorp/terraform/configs"
"github.com/spf13/afero"
"go.uber.org/zap"
)

Expand All @@ -40,6 +46,10 @@ var (
SourceAddr: testLocalSourceAddr,
CallRange: hcl.Range{Filename: testFileNamePath},
}

invalidDirErrStringTemplate = "directory '%s' has no terraform config files"
testDataDir = "testdata"
tfJSONDir = filepath.Join(testDataDir, "tfjson")
)

func TestProcessLocalSource(t *testing.T) {
Expand Down Expand Up @@ -324,3 +334,69 @@ func TestGetRemoteModuleIfPresentInTerraformSrc(t *testing.T) {
})
}
}

func TestTerraformDirectoryLoaderLoadIacDir(t *testing.T) {
var nilMultiErr *multierror.Error = nil
tests := []struct {
name string
tfConfigDir string
tfJSONFile string
options map[string]interface{}
wantErr error
}{
{
name: "directory with resources having container defined",
tfConfigDir: filepath.Join(testDataDir, "terraform-container-extraction"),
tfJSONFile: filepath.Join(tfJSONDir, "output-with-containers.json"),
wantErr: multierror.Append(
fmt.Errorf(invalidDirErrStringTemplate, filepath.Join(testDataDir, "terraform-container-extraction")),
fmt.Errorf(invalidDirErrStringTemplate, filepath.Join(testDataDir, "terraform-container-extraction/terraform-aws-provider/task-definitions")),
),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tr := TerraformDirectoryLoader{
absRootDir: tt.tfConfigDir,
remoteDownloader: downloader.NewRemoteDownloader(),
parser: hclConfigs.NewParser(afero.NewOsFs()),
terraformInitModuleCache: make(map[string]TerraformModuleManifest),
}
got, gotErr := tr.LoadIacDir()
me, ok := gotErr.(*multierror.Error)
if !ok {
t.Errorf("expected multierror.Error, got %T", gotErr)
}
if tt.wantErr == nilMultiErr {
if err := me.ErrorOrNil(); err != nil {
t.Errorf("unexpected error; gotErr: '%v', wantErr: '%v'", gotErr, tt.wantErr)
}
} else if me.Error() != tt.wantErr.Error() {
t.Errorf("unexpected error; gotErr: '%v', wantErr: '%v'", gotErr, tt.wantErr)
}

var want output.AllResourceConfigs

// Read the expected value and unmarshal into want
contents, _ := ioutil.ReadFile(tt.tfJSONFile)
if utils.IsWindowsPlatform() {
contents = utils.ReplaceWinNewLineBytes(contents)
}

err := json.Unmarshal(contents, &want)
if err != nil {
t.Errorf("unexpected error unmarshalling want: %v", err)
}

match, err := test.IdenticalAllResourceConfigs(got, want)
if err != nil {
t.Errorf("unexpected error checking result: %v", err)
}
if !match {
g, _ := json.MarshalIndent(got, "", " ")
w, _ := json.MarshalIndent(want, "", " ")
t.Errorf("got '%v', want: '%v'", string(g), string(w))
}
})
}
}
2 changes: 1 addition & 1 deletion pkg/iac-providers/terraform/commons/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func findContainers(managedResource *hclConfigs.Resource, jsonBody jsonObj, hclB
} else if isAzureConatinerResource(managedResource) {
containers = fetchContainersFromAzureResource(jsonBody)
} else if isAwsConatinerResource(managedResource) {
containers = fetchContainersFromAwsResource(jsonBody, managedResource.DeclRange.Filename)
containers = fetchContainersFromAwsResource(jsonBody, hclBody, managedResource.DeclRange.Filename)
}
return
}
Loading

0 comments on commit 605cf22

Please sign in to comment.