Skip to content

Commit

Permalink
fix: add validation for module local source dir in terraform iac (#793)
Browse files Browse the repository at this point in the history
* fix: added validation for module local source dir

* modifid the code block and added unit test case
  • Loading branch information
Rchanger authored May 20, 2021
1 parent bea2473 commit 0c3c547
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 6 deletions.
20 changes: 16 additions & 4 deletions pkg/iac-providers/terraform/commons/load-dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func (t TerraformDirectoryLoader) loadDirRecursive(dirList []string) (output.All
if diags.HasErrors() {
// log a warn message in this case because there are errors in
// loading the config dir, and continue with other directories
errMessage := fmt.Sprintf("failed to build unified config. errors:\n%+v\n", diags)
errMessage := fmt.Sprintf("failed to build unified config. errors:\n%+v\n", getErrorMessagesFromDiagnostics(diags))
zap.S().Warnf(errMessage)
t.addError(errMessage, dir)
continue
Expand Down Expand Up @@ -235,7 +235,7 @@ func (t TerraformDirectoryLoader) loadDirNonRecursive() (output.AllResourceConfi
if diags.HasErrors() {
// log a warn message in this case because there are errors in
// loading the config dir, and continue with other directories
errMessage := fmt.Sprintf("failed to build unified config. errors:\n%+v\n", diags)
errMessage := fmt.Sprintf("failed to build unified config. errors:\n%+v\n", getErrorMessagesFromDiagnostics(diags))
zap.S().Warnf(errMessage)
return nil, multierror.Append(t.errIacLoadDirs, results.DirScanErr{IacType: "terraform", Directory: t.absRootDir, ErrMessage: ErrBuildTFConfigDir.Error()})
}
Expand Down Expand Up @@ -319,8 +319,11 @@ func (t TerraformDirectoryLoader) buildUnifiedConfig(rootMod *hclConfigs.Module,
func(req *hclConfigs.ModuleRequest) (*hclConfigs.Module, *version.Version, hcl.Diagnostics) {

// figure out path sub module directory, if it's remote then download it locally
var pathToModule string
var err error
var (
pathToModule string
err error
moduleDirDiags hcl.Diagnostics
)
if downloader.IsLocalSourceAddr(req.SourceAddr) {

pathToModule = t.processLocalSource(req)
Expand All @@ -344,6 +347,15 @@ func (t TerraformDirectoryLoader) buildUnifiedConfig(rootMod *hclConfigs.Module,
}
}

// verify whether the module source directory has any .tf config files
if utils.IsDirExists(pathToModule) && !t.parser.IsConfigDir(pathToModule) {
moduleDirDiags = append(moduleDirDiags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid module config directory",
Detail: fmt.Sprintf("Module directory '%s' has no terraform config files for module %s", pathToModule, req.Name),
})
return nil, nil, moduleDirDiags
}
// load sub module directory
subMod, diags := t.parser.LoadConfigDir(pathToModule)
version, _ := version.NewVersion(fmt.Sprintf("1.0.%d", versionI))
Expand Down
13 changes: 13 additions & 0 deletions pkg/iac-providers/terraform/v12/load-dir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ References to other resources during the destroy phase can cause dependency cycl
%s:34,1-29: Duplicate resource "null_resource" configuration; A null_resource resource named "b" was already declared at testdata/destroy-provisioners/main.tf:23,1-29. Resource names must be unique per type in each module.
`, destroyProvisionersDir, destroyProvisionersMainFile, destroyProvisionersMainFile, destroyProvisionersMainFile, destroyProvisionersMainFile)

errStringModuleSourceInvalid := fmt.Sprintf(`failed to build unified config. errors:
<nil>: Invalid module config directory; Module directory '%s' has no terraform config files for module cloudfront
<nil>: Invalid module config directory; Module directory '%s' has no terraform config files for module m1
`, filepath.Join(testDataDir, "invalid-module-source"), filepath.Join(testDataDir, "invalid-module-source"))

testDirPath1 := "not-there"

testDirPath2 := filepath.Join(testDataDir, "testfile")
Expand Down Expand Up @@ -139,6 +144,8 @@ References to other resources during the destroy phase can cause dependency cycl
fmt.Errorf(invalidDirErrStringTemplate, filepath.Join(testDataDir, "deep-modules", "modules")),
fmt.Errorf(invalidDirErrStringTemplate, filepath.Join(testDataDir, "deep-modules", "modules", "m4", "modules")),
fmt.Errorf(errStringDestroyProvisioners),
fmt.Errorf(invalidDirErrStringTemplate, filepath.Join(testDataDir, "invalid-module-source")),
fmt.Errorf(errStringModuleSourceInvalid),
fmt.Errorf(errStringInvalidModuleConfigs),
fmt.Errorf(errStringInvalidModuleConfigs),
fmt.Errorf(testErrorString2),
Expand All @@ -159,6 +166,12 @@ References to other resources during the destroy phase can cause dependency cycl
tfv12: TfV12{},
wantErr: multierror.Append(fmt.Errorf(testErrorString2)),
},
{
name: "invalid module source directory",
dirPath: filepath.Join(testDataDir, "invalid-module-source", "invalid_source"),
tfv12: TfV12{},
wantErr: multierror.Append(fmt.Errorf(errStringModuleSourceInvalid)),
},
}

for _, tt := range table {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
terraform {
required_version = ">= 0.12.0"
}

provider "aws" {
version = "2.58.0"
region = "us-east-1"
}


module "m1" {
source = "../"
m1projectid = "tf-test-project"
}


module "cloudfront" {
source = "../"
}
18 changes: 16 additions & 2 deletions pkg/iac-providers/terraform/v14/load-dir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,14 @@ func TestLoadIacDir(t *testing.T) {
`, filepath.Join(testDataDir, "invalid-moduleconfigs", "cloudfront", "sub-cloudfront"))

errStringDependsOnDir := fmt.Sprintf(`failed to build unified config. errors:
<nil>: Failed to read module directory; Module directory %s does not exist or cannot be read., and 1 other diagnostic(s)
`, filepath.Join(testDataDir, "depends_on", "live", "log"))
<nil>: Failed to read module directory; Module directory %s does not exist or cannot be read.
<nil>: Failed to read module directory; Module directory %s does not exist or cannot be read.
`, filepath.Join(testDataDir, "depends_on", "live", "log"), filepath.Join(testDataDir, "depends_on", "live", "security"))

errStringModuleSourceInvalid := fmt.Sprintf(`failed to build unified config. errors:
<nil>: Invalid module config directory; Module directory '%s' has no terraform config files for module cloudfront
<nil>: Invalid module config directory; Module directory '%s' has no terraform config files for module m1
`, filepath.Join(testDataDir, "invalid-module-source"), filepath.Join(testDataDir, "invalid-module-source"))

testDirPath1 := "not-there"
testDirPath2 := filepath.Join(testDataDir, "testfile")
Expand Down Expand Up @@ -119,12 +125,20 @@ func TestLoadIacDir(t *testing.T) {
fmt.Errorf(invalidDirErrStringTemplate, filepath.Join(testDataDir, "deep-modules", "modules")),
fmt.Errorf(invalidDirErrStringTemplate, filepath.Join(testDataDir, "deep-modules", "modules", "m4", "modules")),
fmt.Errorf(errStringDependsOnDir),
fmt.Errorf(invalidDirErrStringTemplate, filepath.Join(testDataDir, "invalid-module-source")),
fmt.Errorf(errStringModuleSourceInvalid),
fmt.Errorf(errStringInvalidModuleConfigs),
fmt.Errorf(errStringInvalidModuleConfigs),
fmt.Errorf(invalidDirErrStringTemplate, filepath.Join(testDataDir, "relative-moduleconfigs")),
fmt.Errorf(invalidDirErrStringTemplate, filepath.Join(testDataDir, "tfjson")),
),
},
{
name: "invalid module source directory",
dirPath: filepath.Join(testDataDir, "invalid-module-source", "invalid_source"),
tfv14: TfV14{},
wantErr: multierror.Append(fmt.Errorf(errStringModuleSourceInvalid)),
},
}

for _, tt := range table {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
terraform {
required_version = ">= 0.12.0"
}

provider "aws" {
version = "2.58.0"
region = "us-east-1"
}


module "m1" {
source = "../"
m1projectid = "tf-test-project"
}


module "cloudfront" {
source = "../"
}
10 changes: 10 additions & 0 deletions pkg/utils/dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,13 @@ func GetHomeDir() (terrascanDir string) {
func GenerateTempDir() string {
return filepath.Join(os.TempDir(), GenRandomString(6))
}

// IsDirExists checks wether the provided directory exists or not
func IsDirExists(dir string) bool {
_, err := os.Stat(dir)
if os.IsNotExist(err) {
zap.S().Debug("Directory %s does not exist.", dir)
return false
}
return true
}
36 changes: 36 additions & 0 deletions pkg/utils/dir_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package utils

import (
"os"
"path/filepath"
"testing"
)

func TestIsDirExists(t *testing.T) {
type args struct {
dir string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "directory doesnot exists",
args: args{dir: "test"},
want: false,
},
{
name: "directory exists",
args: args{dir: filepath.Join(os.Getenv("PWD"), "testdata")},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := IsDirExists(tt.args.dir); got != tt.want {
t.Errorf("IsDirExists() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit 0c3c547

Please sign in to comment.