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 2f94726
Show file tree
Hide file tree
Showing 21 changed files with 266 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
163 changes: 113 additions & 50 deletions pkg/iac-providers/terraform/commons/extract-container-images.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,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 +61,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 +224,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 +237,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 2f94726

Please sign in to comment.