Skip to content

Commit 6726d12

Browse files
authored
Merge pull request fanal#321 from owenrumney/owenr-add-cfsec-support
add support for cfsec
2 parents 8a6775a + 41c0dbb commit 6726d12

File tree

12 files changed

+513
-64
lines changed

12 files changed

+513
-64
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package cloudformation
2+
3+
import (
4+
"context"
5+
"os"
6+
"path/filepath"
7+
"regexp"
8+
9+
"github.com/aquasecurity/fanal/analyzer"
10+
"github.com/aquasecurity/fanal/types"
11+
)
12+
13+
const version = 1
14+
15+
var requiredExts = []string{".yaml", ".json", ".yml"}
16+
17+
var awsConfigurationRegex = regexp.MustCompile(`(?i)(?m)^\s*?["|]?AWSTemplateFormatVersion[:|"]?`)
18+
var cloudFormationMatchRegex = []*regexp.Regexp{
19+
regexp.MustCompile(`(?i)(?m)^\s*?["|]?Resources[:|"]?`),
20+
regexp.MustCompile(`(?i)(?m)^\s*?["|]?Parameters[:|"]?`),
21+
}
22+
23+
type ConfigAnalyzer struct {
24+
}
25+
26+
func NewConfigAnalyzer() ConfigAnalyzer {
27+
return ConfigAnalyzer{}
28+
}
29+
30+
// Analyze returns a results of CloudFormation file
31+
func (a ConfigAnalyzer) Analyze(_ context.Context, target analyzer.AnalysisTarget) (*analyzer.AnalysisResult, error) {
32+
33+
if looksLikeCloudFormation(target.Content) {
34+
return &analyzer.AnalysisResult{
35+
Configs: []types.Config{
36+
{
37+
Type: types.CloudFormation,
38+
FilePath: filepath.Join(target.Dir, target.FilePath),
39+
},
40+
},
41+
}, nil
42+
}
43+
return &analyzer.AnalysisResult{}, nil
44+
}
45+
46+
func (a ConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
47+
for _, extension := range requiredExts {
48+
if filepath.Ext(filePath) == extension {
49+
return true
50+
}
51+
}
52+
return false
53+
}
54+
55+
func (ConfigAnalyzer) Type() analyzer.Type {
56+
return analyzer.TypeCloudFormation
57+
}
58+
59+
func (ConfigAnalyzer) Version() int {
60+
return version
61+
}
62+
63+
func looksLikeCloudFormation(content []byte) bool {
64+
65+
if awsConfigurationRegex.MatchString(string(content)) {
66+
return true
67+
}
68+
69+
for _, regex := range cloudFormationMatchRegex {
70+
if !regex.MatchString(string(content)) {
71+
return false
72+
}
73+
}
74+
75+
return true
76+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package cloudformation
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/aquasecurity/fanal/analyzer"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestConfigAnalyzer_Required(t *testing.T) {
13+
tests := []struct {
14+
name string
15+
filePath string
16+
want bool
17+
}{
18+
{
19+
name: "CloudFormation yaml",
20+
filePath: "main.yaml",
21+
want: true,
22+
},
23+
{
24+
name: "Cloudformation JSON",
25+
filePath: "main.json",
26+
want: true,
27+
},
28+
{
29+
name: "non CloudFormation yaml",
30+
filePath: "k8s.yaml",
31+
want: true,
32+
},
33+
{
34+
name: "non CloudFormation json",
35+
filePath: "random.yaml",
36+
want: true,
37+
},
38+
}
39+
for _, tt := range tests {
40+
t.Run(tt.name, func(t *testing.T) {
41+
a := ConfigAnalyzer{}
42+
got := a.Required(tt.filePath, nil)
43+
assert.Equal(t, tt.want, got)
44+
})
45+
}
46+
}
47+
48+
func TestConfigAnalyzer_Analyzed(t *testing.T) {
49+
tests := []struct {
50+
name string
51+
content string
52+
filePath string
53+
want int
54+
}{
55+
{
56+
name: "CloudFormation yaml",
57+
content: `---
58+
Parameters:
59+
SomeParameter:
60+
Resources:
61+
SomeResource:
62+
Type: Something
63+
`,
64+
filePath: "main.yaml",
65+
want: 1,
66+
},
67+
{
68+
name: "Cloudformation JSON",
69+
content: `{
70+
"Parameters": {
71+
"SomeParameter": null
72+
},
73+
"Resources": {
74+
"SomeResource": {
75+
"Type": "Something"
76+
}
77+
}
78+
}`,
79+
filePath: "main.json",
80+
want: 1,
81+
},
82+
{
83+
name: "non CloudFormation yaml",
84+
content: `---
85+
apiVersion: apps/v1
86+
kind: Deployment
87+
metadata:
88+
name: nginx-deployment
89+
spec:
90+
selector:
91+
matchLabels:
92+
app: nginx
93+
minReadySeconds: 5
94+
template:
95+
metadata:
96+
labels:
97+
app: nginx
98+
spec:
99+
containers:
100+
- name: nginx
101+
image: nginx:1.14.2
102+
ports:
103+
- containerPort: 80
104+
`,
105+
filePath: "k8s.yaml",
106+
want: 0,
107+
},
108+
{
109+
name: "non CloudFormation json",
110+
content: `{
111+
"foo": [
112+
"baaaaa",
113+
"bar",
114+
"baa"
115+
]
116+
}
117+
`,
118+
filePath: "random.yaml",
119+
want: 0,
120+
},
121+
}
122+
for _, tt := range tests {
123+
t.Run(tt.name, func(t *testing.T) {
124+
a := ConfigAnalyzer{}
125+
got, err := a.Analyze(context.Background(), analyzer.AnalysisTarget{
126+
FilePath: tt.filePath,
127+
Content: []byte(tt.content),
128+
})
129+
require.NoError(t, err)
130+
assert.Len(t, got.Configs, tt.want)
131+
})
132+
}
133+
}

analyzer/config/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"sort"
66
"strings"
77

8+
"github.com/aquasecurity/fanal/analyzer/config/cloudformation"
89
"golang.org/x/xerrors"
910

1011
"github.com/aquasecurity/fanal/analyzer"
@@ -68,6 +69,7 @@ func RegisterConfigAnalyzers(filePatterns []string) error {
6869
analyzer.RegisterAnalyzer(hcl.NewConfigAnalyzer(hclRegexp))
6970
analyzer.RegisterAnalyzer(json.NewConfigAnalyzer(jsonRegexp))
7071
analyzer.RegisterAnalyzer(terraform.NewConfigAnalyzer())
72+
analyzer.RegisterAnalyzer(cloudformation.NewConfigAnalyzer())
7173
analyzer.RegisterAnalyzer(toml.NewConfigAnalyzer(tomlRegexp))
7274
analyzer.RegisterAnalyzer(yaml.NewConfigAnalyzer(yamlRegexp))
7375

analyzer/const.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,13 @@ const (
6767
// =================
6868
// Structured Config
6969
// =================
70-
TypeYaml Type = "yaml"
71-
TypeTOML Type = "toml"
72-
TypeJSON Type = "json"
73-
TypeDockerfile Type = "dockerfile"
74-
TypeHCL Type = "hcl"
75-
TypeTerraform Type = "terraform"
70+
TypeYaml Type = "yaml"
71+
TypeTOML Type = "toml"
72+
TypeJSON Type = "json"
73+
TypeDockerfile Type = "dockerfile"
74+
TypeHCL Type = "hcl"
75+
TypeTerraform Type = "terraform"
76+
TypeCloudFormation Type = "cloudFormation"
7677
)
7778

7879
var (

0 commit comments

Comments
 (0)