Skip to content

Commit

Permalink
refs resolution (tenable#1445)
Browse files Browse the repository at this point in the history
* refs resolution

* ensure parameter variable references are accounted

* add comment for exported variables

* fix err string

* code fmt validation fix
  • Loading branch information
gaurav-gogia authored Nov 4, 2022
1 parent 7365ecf commit 0f53f05
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 4 deletions.
4 changes: 2 additions & 2 deletions pkg/iac-providers/cft/v1/load-file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestLoadIacFile(t *testing.T) {

testErrString1 := fmt.Sprintf("unsupported extension for file %s", testFile)
testErrString2 := "unable to read file nonexistent.txt"
testErrString3 := "invalid YAML template: yaml: line 27: did not find expected alphabetic or numeric character"
testErrString3 := "yaml: line 28: did not find expected alphabetic or numeric character"

validFileConfig := make(map[string][]output.ResourceConfig, 2)
validFileConfig["aws_s3_bucket_policy"] = []output.ResourceConfig{{
Expand Down Expand Up @@ -88,7 +88,7 @@ func TestLoadIacFile(t *testing.T) {
filePath: "nonexistent.txt",
typeOnly: false,
}, {
wantErr: fmt.Errorf("error while resolving intrinsic functions, error %w", fmt.Errorf(testErrString3)),
wantErr: fmt.Errorf("error while unmarshalling yaml, error %w", fmt.Errorf(testErrString3)),
want: output.AllResourceConfigs{},
cftv1: CFTV1{},
name: "invalid file",
Expand Down
73 changes: 71 additions & 2 deletions pkg/iac-providers/cft/v1/sanitize-cft-template.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,27 @@ import (
"github.com/awslabs/goformation/v7/cloudformation/policies"
"github.com/awslabs/goformation/v7/intrinsics"
"go.uber.org/zap"
"gopkg.in/yaml.v3"
)

// PARAMETERS is a constant to fetch Parameters from CFT
const PARAMETERS = "Parameters"

// RESOURCES is a constant to fetch Resources from CFT
const RESOURCES = "Resources"

func (a *CFTV1) sanitizeCftTemplate(data []byte, isYAML bool) (map[string]interface{}, error) {
var (
intrinsified []byte
err error
)

if isYAML {
data, err = removeRefAnchors(data)
if err != nil {
return nil, err
}

// Process all AWS CloudFormation intrinsic functions (e.g. Fn::Join)
intrinsified, err = intrinsics.ProcessYAML(data, nil)
if err != nil {
Expand All @@ -57,7 +69,7 @@ func (a *CFTV1) sanitizeCftTemplate(data []byte, isYAML bool) (map[string]interf
}

// sanitize Parameters
params, ok := templateFileMap["Parameters"]
params, ok := templateFileMap[PARAMETERS]
if ok {
pMap, ok := params.(map[string]interface{})
if ok {
Expand All @@ -69,7 +81,7 @@ func (a *CFTV1) sanitizeCftTemplate(data []byte, isYAML bool) (map[string]interf
}

// sanitize resources
r, ok := templateFileMap["Resources"]
r, ok := templateFileMap[RESOURCES]
if ok {
rMap, ok := r.(map[string]interface{})
if ok {
Expand All @@ -86,6 +98,63 @@ func (a *CFTV1) sanitizeCftTemplate(data []byte, isYAML bool) (map[string]interf
return templateFileMap, nil
}

func removeRefAnchors(data []byte) ([]byte, error) {
const REF = "!ref"
strdata := string(data)
words := strings.Split(strdata, " ")

var cfnmap map[any]any
err := yaml.Unmarshal(data, &cfnmap)
if err != nil {
return nil, fmt.Errorf("error while unmarshalling yaml, error %w", err)
}

cfnJSONMap := anyMapToStringMap(cfnmap)
paramsMap, paramsOk := cfnJSONMap[PARAMETERS].(map[string]any)

for i := range words {
current := strings.ToLower(words[i])
if len(words) == i+1 {
break
}

if strings.Contains(current, REF) {
next := strings.TrimSpace(words[i+1])
nextLower := strings.ToLower(words[i+1])

if strings.Contains(nextLower, "aws::") {
continue
}

if paramsOk {
if _, ok := paramsMap[next]; ok {
continue
}
}
}

if strings.Contains(current, REF) {
words[i] = strings.Replace(current, REF, "", 1)
}
}

strdata = strings.Join(words, " ")
return []byte(strdata), nil
}

func anyMapToStringMap(cfnmap map[any]any) map[string]any {
res := map[string]any{}
for k, v := range cfnmap {
switch v2 := v.(type) {
case map[any]any:
res[fmt.Sprint(k)] = anyMapToStringMap(v2)
default:
res[fmt.Sprint(k)] = v
}
}
return res
}

func inspectAndSanitizeParameters(p interface{}) {
paramMap, ok := p.(map[string]interface{})
if !ok {
Expand Down

0 comments on commit 0f53f05

Please sign in to comment.