Skip to content

Commit 99b2db3

Browse files
authoredNov 8, 2024··
fix(misconf): handle null properties in CloudFormation templates (#7813)
Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io>
1 parent ab32297 commit 99b2db3

File tree

3 files changed

+77
-15
lines changed

3 files changed

+77
-15
lines changed
 

‎pkg/iac/scanners/cloudformation/parser/file_context.go

+10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package parser
22

33
import (
4+
"github.com/samber/lo"
5+
46
"github.com/aquasecurity/trivy/pkg/iac/ignore"
57
iacTypes "github.com/aquasecurity/trivy/pkg/iac/types"
68
)
@@ -71,3 +73,11 @@ func (t *FileContext) missingParameterValues() []string {
7173
}
7274
return missing
7375
}
76+
77+
func (t *FileContext) stripNullProperties() {
78+
for _, resource := range t.Resources {
79+
resource.Inner.Properties = lo.OmitBy(resource.Inner.Properties, func(k string, v *Property) bool {
80+
return v.IsNil()
81+
})
82+
}
83+
}

‎pkg/iac/scanners/cloudformation/parser/parser.go

+18-15
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"io"
88
"io/fs"
9+
"path"
910
"path/filepath"
1011
"strings"
1112

@@ -83,7 +84,7 @@ func (p *Parser) ParseFS(ctx context.Context, fsys fs.FS, dir string) (FileConte
8384
return contexts, nil
8485
}
8586

86-
func (p *Parser) ParseFile(ctx context.Context, fsys fs.FS, path string) (fctx *FileContext, err error) {
87+
func (p *Parser) ParseFile(ctx context.Context, fsys fs.FS, filePath string) (fctx *FileContext, err error) {
8788
defer func() {
8889
if e := recover(); e != nil {
8990
err = fmt.Errorf("panic during parse: %s", e)
@@ -105,15 +106,15 @@ func (p *Parser) ParseFile(ctx context.Context, fsys fs.FS, path string) (fctx *
105106
}
106107

107108
sourceFmt := YamlSourceFormat
108-
if strings.HasSuffix(strings.ToLower(path), ".json") {
109+
if path.Ext(filePath) == ".json" {
109110
sourceFmt = JsonSourceFormat
110111
}
111112

112-
f, err := fsys.Open(filepath.ToSlash(path))
113+
f, err := fsys.Open(filePath)
113114
if err != nil {
114115
return nil, err
115116
}
116-
defer func() { _ = f.Close() }()
117+
defer f.Close()
117118

118119
content, err := io.ReadAll(f)
119120
if err != nil {
@@ -123,42 +124,44 @@ func (p *Parser) ParseFile(ctx context.Context, fsys fs.FS, path string) (fctx *
123124
lines := strings.Split(string(content), "\n")
124125

125126
fctx = &FileContext{
126-
filepath: path,
127+
filepath: filePath,
127128
lines: lines,
128129
SourceFormat: sourceFmt,
129130
}
130131

131132
switch sourceFmt {
132133
case YamlSourceFormat:
133134
if err := yaml.Unmarshal(content, fctx); err != nil {
134-
return nil, NewErrInvalidContent(path, err)
135+
return nil, NewErrInvalidContent(filePath, err)
135136
}
136-
fctx.Ignores = ignore.Parse(string(content), path, "")
137+
fctx.Ignores = ignore.Parse(string(content), filePath, "")
137138
case JsonSourceFormat:
138139
if err := jfather.Unmarshal(content, fctx); err != nil {
139-
return nil, NewErrInvalidContent(path, err)
140+
return nil, NewErrInvalidContent(filePath, err)
140141
}
141142
}
142143

144+
fctx.stripNullProperties()
145+
143146
fctx.overrideParameters(p.overridedParameters)
144147

145148
if params := fctx.missingParameterValues(); len(params) > 0 {
146-
p.logger.Warn("Missing parameter values", log.FilePath(path), log.String("parameters", strings.Join(params, ", ")))
149+
p.logger.Warn("Missing parameter values", log.FilePath(filePath), log.String("parameters", strings.Join(params, ", ")))
147150
}
148151

149152
fctx.lines = lines
150153
fctx.SourceFormat = sourceFmt
151-
fctx.filepath = path
154+
fctx.filepath = filePath
152155

153-
p.logger.Debug("Context loaded from source", log.FilePath(path))
156+
p.logger.Debug("Context loaded from source", log.FilePath(filePath))
154157

155158
// the context must be set to conditions before resources
156159
for _, c := range fctx.Conditions {
157160
c.setContext(fctx)
158161
}
159162

160163
for name, r := range fctx.Resources {
161-
r.configureResource(name, fsys, path, fctx)
164+
r.configureResource(name, fsys, filePath, fctx)
162165
}
163166

164167
return fctx, nil
@@ -190,10 +193,10 @@ func (p *Parser) parseParams() error {
190193
return nil
191194
}
192195

193-
func (p *Parser) parseParametersFile(path string) (Parameters, error) {
194-
f, err := p.configsFS.Open(path)
196+
func (p *Parser) parseParametersFile(filePath string) (Parameters, error) {
197+
f, err := p.configsFS.Open(filePath)
195198
if err != nil {
196-
return nil, fmt.Errorf("parameters file %q open error: %w", path, err)
199+
return nil, fmt.Errorf("parameters file %q open error: %w", filePath, err)
197200
}
198201

199202
var parameters Parameters

‎pkg/iac/scanners/cloudformation/parser/parser_test.go

+49
Original file line numberDiff line numberDiff line change
@@ -440,3 +440,52 @@ Conditions:
440440
require.NoError(t, err)
441441
require.Len(t, files, 1)
442442
}
443+
444+
func Test_TemplateWithNullProperty(t *testing.T) {
445+
src := `AWSTemplateFormatVersion: "2010-09-09"
446+
Resources:
447+
TestBucket:
448+
Type: "AWS::S3::Bucket"
449+
Properties:
450+
BucketName:`
451+
452+
fsys := testutil.CreateFS(t, map[string]string{
453+
"main.yaml": src,
454+
})
455+
456+
files, err := New().ParseFS(context.TODO(), fsys, ".")
457+
require.NoError(t, err)
458+
require.Len(t, files, 1)
459+
460+
file := files[0]
461+
462+
res := file.GetResourceByLogicalID("TestBucket")
463+
464+
assert.True(t, res.GetProperty("BucketName").IsNil())
465+
}
466+
467+
func Test_TemplateWithNullNestedProperty(t *testing.T) {
468+
src := `AWSTemplateFormatVersion: "2010-09-09"
469+
Description: "BAD"
470+
Resources:
471+
TestBucket:
472+
Type: "AWS::S3::Bucket"
473+
Properties:
474+
BucketName: test
475+
PublicAccessBlockConfiguration:
476+
BlockPublicAcls: null`
477+
478+
fsys := testutil.CreateFS(t, map[string]string{
479+
"main.yaml": src,
480+
})
481+
482+
files, err := New().ParseFS(context.TODO(), fsys, ".")
483+
require.NoError(t, err)
484+
require.Len(t, files, 1)
485+
486+
file := files[0]
487+
488+
res := file.GetResourceByLogicalID("TestBucket")
489+
490+
assert.True(t, res.GetProperty("PublicAccessBlockConfiguration.BlockPublicAcls").IsNil())
491+
}

0 commit comments

Comments
 (0)
Please sign in to comment.