Skip to content

Commit 34f865d

Browse files
authored
feat(config): support JSON files (fanal#159)
* feat(config): support JSON files * feat(json): add Version() * fix Type() and add test
1 parent 30fc5b9 commit 34f865d

File tree

7 files changed

+242
-0
lines changed

7 files changed

+242
-0
lines changed

analyzer/config/const.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ package config
33
const (
44
YAML = "yaml"
55
TOML = "toml"
6+
JSON = "json"
67
)

analyzer/config/json/json.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package json
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
7+
"github.com/open-policy-agent/conftest/parser/json"
8+
"golang.org/x/xerrors"
9+
10+
"github.com/aquasecurity/fanal/analyzer"
11+
"github.com/aquasecurity/fanal/analyzer/config"
12+
"github.com/aquasecurity/fanal/types"
13+
)
14+
15+
func init() {
16+
analyzer.RegisterAnalyzer(&jsonConfigAnalyzer{
17+
parser: &json.Parser{},
18+
})
19+
}
20+
21+
const version = 1
22+
23+
var requiredExts = []string{".json"}
24+
25+
type jsonConfigAnalyzer struct {
26+
parser *json.Parser
27+
}
28+
29+
func (a jsonConfigAnalyzer) Analyze(target analyzer.AnalysisTarget) (*analyzer.AnalysisResult, error) {
30+
var parsed interface{}
31+
if err := a.parser.Unmarshal(target.Content, &parsed); err != nil {
32+
return nil, xerrors.Errorf("unable to parse JSON (%s): %w", target.FilePath, err)
33+
}
34+
return &analyzer.AnalysisResult{
35+
Configs: []types.Config{{
36+
Type: config.JSON,
37+
FilePath: target.FilePath,
38+
Content: parsed,
39+
}},
40+
}, nil
41+
}
42+
43+
func (a jsonConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool {
44+
ext := filepath.Ext(filePath)
45+
for _, required := range requiredExts {
46+
if ext == required {
47+
return true
48+
}
49+
}
50+
return false
51+
}
52+
53+
func (a jsonConfigAnalyzer) Type() analyzer.Type {
54+
return analyzer.TypeJSON
55+
}
56+
57+
func (a jsonConfigAnalyzer) Version() int {
58+
return version
59+
}

analyzer/config/json/json_test.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package json
2+
3+
import (
4+
"io/ioutil"
5+
"testing"
6+
7+
"github.com/open-policy-agent/conftest/parser/json"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/aquasecurity/fanal/analyzer"
12+
"github.com/aquasecurity/fanal/analyzer/config"
13+
"github.com/aquasecurity/fanal/types"
14+
)
15+
16+
func Test_jsonConfigAnalyzer_Analyze(t *testing.T) {
17+
tests := []struct {
18+
name string
19+
inputFile string
20+
want *analyzer.AnalysisResult
21+
wantErr string
22+
}{
23+
{
24+
name: "happy path",
25+
inputFile: "testdata/deployment.json",
26+
want: &analyzer.AnalysisResult{
27+
Configs: []types.Config{
28+
{
29+
Type: config.JSON,
30+
FilePath: "testdata/deployment.json",
31+
Content: map[string]interface{}{
32+
"apiVersion": "apps/v1",
33+
"kind": "Deployment",
34+
"metadata": map[string]interface{}{
35+
"name": "hello-kubernetes",
36+
},
37+
"spec": map[string]interface{}{
38+
"replicas": float64(3),
39+
},
40+
},
41+
},
42+
},
43+
},
44+
},
45+
{
46+
name: "happy path: json array",
47+
inputFile: "testdata/array.json",
48+
want: &analyzer.AnalysisResult{
49+
Configs: []types.Config{
50+
{
51+
Type: config.JSON,
52+
FilePath: "testdata/array.json",
53+
Content: []interface{}{
54+
map[string]interface{}{
55+
"apiVersion": "apps/v1",
56+
"kind": "Deployment",
57+
"metadata": map[string]interface{}{
58+
"name": "hello-kubernetes",
59+
},
60+
"spec": map[string]interface{}{
61+
"replicas": float64(3),
62+
},
63+
},
64+
map[string]interface{}{
65+
"apiVersion": "apps/v2",
66+
"kind": "Deployment",
67+
"metadata": map[string]interface{}{
68+
"name": "hello-kubernetes",
69+
},
70+
"spec": map[string]interface{}{
71+
"replicas": float64(3),
72+
},
73+
},
74+
},
75+
},
76+
},
77+
},
78+
},
79+
{
80+
name: "broken JSON",
81+
inputFile: "testdata/broken.json",
82+
wantErr: "unmarshal json",
83+
},
84+
}
85+
for _, tt := range tests {
86+
t.Run(tt.name, func(t *testing.T) {
87+
b, err := ioutil.ReadFile(tt.inputFile)
88+
require.NoError(t, err)
89+
90+
a := jsonConfigAnalyzer{
91+
parser: &json.Parser{},
92+
}
93+
94+
got, err := a.Analyze(analyzer.AnalysisTarget{
95+
FilePath: tt.inputFile,
96+
Content: b,
97+
})
98+
99+
if tt.wantErr != "" {
100+
require.NotNil(t, err)
101+
assert.Contains(t, err.Error(), tt.wantErr)
102+
return
103+
}
104+
assert.NoError(t, err)
105+
assert.Equal(t, tt.want, got)
106+
})
107+
}
108+
}
109+
110+
func Test_jsonConfigAnalyzer_Required(t *testing.T) {
111+
tests := []struct {
112+
name string
113+
filePath string
114+
want bool
115+
}{
116+
{
117+
name: "json",
118+
filePath: "deployment.json",
119+
want: true,
120+
},
121+
{
122+
name: "yaml",
123+
filePath: "deployment.yaml",
124+
want: false,
125+
},
126+
}
127+
for _, tt := range tests {
128+
t.Run(tt.name, func(t *testing.T) {
129+
a := jsonConfigAnalyzer{
130+
parser: &json.Parser{},
131+
}
132+
133+
got := a.Required(tt.filePath, nil)
134+
assert.Equal(t, tt.want, got)
135+
})
136+
}
137+
}
138+
139+
func Test_jsonConfigAnalyzer_Type(t *testing.T) {
140+
want := analyzer.TypeJSON
141+
a := jsonConfigAnalyzer{
142+
parser: &json.Parser{},
143+
}
144+
got := a.Type()
145+
assert.Equal(t, want, got)
146+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[
2+
{
3+
"apiVersion": "apps/v1",
4+
"kind": "Deployment",
5+
"metadata": {
6+
"name": "hello-kubernetes"
7+
},
8+
"spec": {
9+
"replicas": 3
10+
}
11+
},
12+
{
13+
"apiVersion": "apps/v2",
14+
"kind": "Deployment",
15+
"metadata": {
16+
"name": "hello-kubernetes"
17+
},
18+
"spec": {
19+
"replicas": 3
20+
}
21+
}
22+
]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"apiVersion": "apps/v1",
3+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"apiVersion": "apps/v1",
3+
"kind": "Deployment",
4+
"metadata": {
5+
"name": "hello-kubernetes"
6+
},
7+
"spec": {
8+
"replicas": 3
9+
}
10+
}

analyzer/const.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,5 @@ const (
3737
// Structured Config
3838
TypeYaml = Type("yaml")
3939
TypeTOML = Type("toml")
40+
TypeJSON = Type("json")
4041
)

0 commit comments

Comments
 (0)