Repository: aquasecurity/tfsec
Pull request #1206 (2021-12-26 12:34:45)

fix tf v1.1.0 init module sources
This PR fixes the issue with the terraform v1.1.0+ module sources
by changing matching on module source to matching on module name with module key in the json metadata file.
module source is since tf 1.1.0 no longer always equal to the source in the metadata json file(.terraform\modules\modules.json) ,for instance terraform/hashicorp auto resolves to registry.terraform.com, googleapis.com to gcs etc..
see issue #1188 


liamg: Thanks for this, we should have a release out in roughly a week.

Patch:
diff --git a/internal/app/tfsec/parser/load_module.go b/internal/app/tfsec/parser/load_module.go
index d993890ca..c7d6bb538 100644
--- a/internal/app/tfsec/parser/load_module.go
+++ b/internal/app/tfsec/parser/load_module.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
+	"regexp"
 	"strings"
 
 	"github.com/aquasecurity/defsec/metrics"
@@ -30,6 +31,25 @@ type ModuleDefinition struct {
 	Modules    []block.Module
 }
 
+// getModuleKeyName constructs the module keyname from the block label and the modulename
+func (e *Evaluator) getModuleKeyName(name string) (keyName string) {
+	// regular expression for removing count and or for_each indexes
+	indexRegExp := regexp.MustCompile(`\[.+?\]`)
+
+	if e.moduleName == "root" {
+		return indexRegExp.ReplaceAllString(name, "")
+	}
+
+	modules := strings.Split(e.moduleName, ":")
+	for i := range modules {
+		keyName += strings.TrimPrefix(modules[i], "module.")
+		if i != len(modules)-1 {
+			keyName += "."
+		}
+	}
+	return indexRegExp.ReplaceAllString(keyName+"."+name, "")
+}
+
 // LoadModules reads all module blocks and loads the underlying modules, adding blocks to e.moduleBlocks
 func (e *Evaluator) loadModules(stopOnHCLError bool) []*ModuleDefinition {
 
@@ -107,8 +127,11 @@ func (e *Evaluator) loadModule(b block.Block, stopOnHCLError bool) (*ModuleDefin
 
 	if e.moduleMetadata != nil {
 		// if we have module metadata we can parse all the modules as they'll be cached locally!
+
+		name := e.getModuleKeyName(b.Label())
+
 		for _, module := range e.moduleMetadata.Modules {
-			if module.Source == source {
+			if module.Key == name {
 				modulePath = filepath.Clean(filepath.Join(e.projectRootPath, module.Dir))
 				break
 			}


Test patch:
diff --git a/internal/app/tfsec/test/module_test.go b/internal/app/tfsec/test/module_test.go
index 088ec955c..eae5ca45c 100644
--- a/internal/app/tfsec/test/module_test.go
+++ b/internal/app/tfsec/test/module_test.go
@@ -268,11 +268,23 @@ func Test_ProblemInInitialisedModule(t *testing.T) {
 
 	require.NoError(t, fs.WriteTextFile("project/main.tf", `
 module "something" {
-  	source = "/nowhere"
+  	source = "../modules/somewhere"
+	bad = false
+}
+`))
+	require.NoError(t, fs.WriteTextFile("modules/somewhere/main.tf", `
+module "something_nested" {
+	count = 1
+  	source = "github.com/some/module.git"
 	bad = true
 }
+
+variable "bad" {
+	default = false
+}
+
 `))
-	require.NoError(t, fs.WriteTextFile("project/.terraform/modules/a/main.tf", `
+	require.NoError(t, fs.WriteTextFile("project/.terraform/modules/something.something_nested/main.tf", `
 variable "bad" {
 	default = false
 }
@@ -281,7 +293,7 @@ resource "problem" "uhoh" {
 }
 `))
 	require.NoError(t, fs.WriteTextFile("project/.terraform/modules/modules.json", `
-	{"Modules":[{"Key":"something","Source":"/nowhere","Version":"2.35.0","Dir":".terraform/modules/a"}]}
+	{"Modules":[{"Key":"something","Source":"../modules/somewhere","Version":"2.35.0","Dir":"../modules/somewhere"},{"Key":"something.something_nested","Source":"git::https://github.com/some/module.git","Version":"2.35.0","Dir":".terraform/modules/something.something_nested"}]}
 `))
 
 	blocks, err := parser.New(fs.RealPath("project/"), parser.OptionStopOnHCLError()).ParseDirectory()
@@ -318,7 +330,7 @@ resource "problem" "uhoh" {
 }
 `))
 	require.NoError(t, fs.WriteTextFile("project/.terraform/modules/modules.json", `
-	{"Modules":[{"Key":"something","Source":"/nowhere","Version":"2.35.0","Dir":".terraform/modules/a"}]}
+	{"Modules":[{"Key":"something","Source":"/nowhere","Version":"2.35.0","Dir":".terraform/modules/a"},{"Key":"something2","Source":"/nowhere","Version":"2.35.0","Dir":".terraform/modules/a"}]}
 `))
 
 	blocks, err := parser.New(fs.RealPath("project/"), parser.OptionStopOnHCLError()).ParseDirectory()