mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-12 15:50:15 -08:00
refactor(misconf): switch to x/json (#8719)
Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io>
This commit is contained in:
2
go.mod
2
go.mod
@@ -20,7 +20,7 @@ require (
|
||||
github.com/aquasecurity/go-pep440-version v0.0.1
|
||||
github.com/aquasecurity/go-version v0.0.1
|
||||
github.com/aquasecurity/iamgo v0.0.10
|
||||
github.com/aquasecurity/jfather v0.0.8
|
||||
github.com/aquasecurity/jfather v0.0.8 // indirect
|
||||
github.com/aquasecurity/table v1.8.0
|
||||
github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8
|
||||
github.com/aquasecurity/tml v0.6.1
|
||||
|
||||
@@ -42,7 +42,6 @@ func (t *FileContext) GetResourcesByType(names ...string) []*Resource {
|
||||
for _, r := range t.Resources {
|
||||
for _, name := range names {
|
||||
if name == r.Type() {
|
||||
//
|
||||
resources = append(resources, r)
|
||||
}
|
||||
}
|
||||
@@ -56,6 +55,7 @@ func (t *FileContext) Metadata() iacTypes.Metadata {
|
||||
return iacTypes.NewMetadata(rng, NewCFReference("Template", rng).String())
|
||||
}
|
||||
|
||||
// TODO: use map[string]string
|
||||
func (t *FileContext) overrideParameters(params map[string]any) {
|
||||
for key := range t.Parameters {
|
||||
if val, ok := params[key]; ok {
|
||||
@@ -76,7 +76,7 @@ func (t *FileContext) missingParameterValues() []string {
|
||||
|
||||
func (t *FileContext) stripNullProperties() {
|
||||
for _, resource := range t.Resources {
|
||||
resource.Inner.Properties = lo.OmitBy(resource.Inner.Properties, func(k string, v *Property) bool {
|
||||
resource.properties = lo.OmitBy(resource.properties, func(k string, v *Property) bool {
|
||||
return v.IsNil()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,35 +7,24 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
)
|
||||
|
||||
func Test_resolve_and_value(t *testing.T) {
|
||||
|
||||
property1 := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -43,49 +32,33 @@ func Test_resolve_and_value(t *testing.T) {
|
||||
}
|
||||
|
||||
property2 := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
andProperty := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::And": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
property1,
|
||||
property2,
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::And": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
property1,
|
||||
property2,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -100,29 +73,19 @@ func Test_resolve_and_value(t *testing.T) {
|
||||
func Test_resolve_and_value_not_the_same(t *testing.T) {
|
||||
|
||||
property1 := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -130,49 +93,33 @@ func Test_resolve_and_value_not_the_same(t *testing.T) {
|
||||
}
|
||||
|
||||
property2 := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
andProperty := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::And": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
property1,
|
||||
property2,
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::And": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
property1,
|
||||
property2,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,24 +7,17 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
)
|
||||
|
||||
func Test_resolve_base64_value(t *testing.T) {
|
||||
|
||||
property := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Base64": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "HelloWorld",
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Base64": {
|
||||
Type: cftypes.String,
|
||||
Value: "HelloWorld",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -13,10 +13,8 @@ func Test_cidr_generator(t *testing.T) {
|
||||
ctx: nil,
|
||||
name: "cidr",
|
||||
comment: "",
|
||||
Inner: PropertyInner{
|
||||
Type: "",
|
||||
Value: nil,
|
||||
},
|
||||
Type: "",
|
||||
Value: nil,
|
||||
}
|
||||
|
||||
ranges, err := calculateCidrs("10.1.0.0/16", 4, 4, original)
|
||||
@@ -40,10 +38,8 @@ func Test_cidr_generator_8_bits(t *testing.T) {
|
||||
ctx: nil,
|
||||
name: "cidr",
|
||||
comment: "",
|
||||
Inner: PropertyInner{
|
||||
Type: "",
|
||||
Value: nil,
|
||||
},
|
||||
Type: "",
|
||||
Value: nil,
|
||||
}
|
||||
|
||||
ranges, err := calculateCidrs("10.1.0.0/16", 4, 8, original)
|
||||
|
||||
@@ -14,78 +14,56 @@ func Test_resolve_condition_value(t *testing.T) {
|
||||
fctx := new(FileContext)
|
||||
fctx.Conditions = map[string]Property{
|
||||
"SomeCondition": {
|
||||
ctx: fctx,
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
ctx: fctx,
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "some val",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "some val",
|
||||
},
|
||||
},
|
||||
},
|
||||
ctx: fctx,
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
ctx: fctx,
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "some val",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "some val",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"EnableVersioning": {
|
||||
ctx: fctx,
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Condition": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "SomeCondition",
|
||||
},
|
||||
},
|
||||
ctx: fctx,
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Condition": {
|
||||
Type: cftypes.String,
|
||||
Value: "SomeCondition",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
property := &Property{
|
||||
ctx: fctx,
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::If": {
|
||||
ctx: fctx,
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "EnableVersioning",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "Enabled",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "Suspended",
|
||||
},
|
||||
},
|
||||
},
|
||||
ctx: fctx,
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::If": {
|
||||
ctx: fctx,
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "EnableVersioning",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "Enabled",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "Suspended",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,35 +7,24 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
)
|
||||
|
||||
func Test_resolve_equals_value(t *testing.T) {
|
||||
|
||||
property := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -51,29 +40,19 @@ func Test_resolve_equals_value(t *testing.T) {
|
||||
func Test_resolve_equals_value_to_false(t *testing.T) {
|
||||
|
||||
property := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -89,29 +68,19 @@ func Test_resolve_equals_value_to_false(t *testing.T) {
|
||||
func Test_resolve_equals_value_to_true_when_boolean(t *testing.T) {
|
||||
|
||||
property := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Bool,
|
||||
Value: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Bool,
|
||||
Value: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.Bool,
|
||||
Value: true,
|
||||
},
|
||||
{
|
||||
Type: cftypes.Bool,
|
||||
Value: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -127,43 +96,31 @@ func Test_resolve_equals_value_when_one_is_a_reference(t *testing.T) {
|
||||
|
||||
property := &Property{
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "staging",
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "staging",
|
||||
},
|
||||
{
|
||||
ctx: &FileContext{
|
||||
Parameters: map[string]*Parameter{
|
||||
"Environment": {
|
||||
inner: parameterInner{
|
||||
Type: "string",
|
||||
Default: "staging",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ctx: &FileContext{
|
||||
filepath: "",
|
||||
Parameters: map[string]*Parameter{
|
||||
"Environment": {
|
||||
inner: parameterInner{
|
||||
Type: "string",
|
||||
Default: "staging",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Ref": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "Environment",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Ref": {
|
||||
Type: cftypes.String,
|
||||
Value: "Environment",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -42,5 +42,5 @@ func ResolveGetAtt(property *Property) (resolved *Property, success bool) {
|
||||
return property.deriveResolved(cftypes.String, referencedResource.ID()), true
|
||||
}
|
||||
|
||||
return property.deriveResolved(referencedProperty.Type(), referencedProperty.RawValue()), true
|
||||
return property.deriveResolved(referencedProperty.Type, referencedProperty.RawValue()), true
|
||||
}
|
||||
|
||||
@@ -7,41 +7,28 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
)
|
||||
|
||||
func Test_resolve_if_value(t *testing.T) {
|
||||
|
||||
property := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::If": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Bool,
|
||||
Value: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::If": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.Bool,
|
||||
Value: true,
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,52 +7,35 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
)
|
||||
|
||||
func Test_resolve_join_value(t *testing.T) {
|
||||
|
||||
property := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Join": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Join": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "::",
|
||||
},
|
||||
{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "::",
|
||||
},
|
||||
Type: cftypes.String,
|
||||
Value: "s3",
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "s3",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "part1",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "part2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.String,
|
||||
Value: "part1",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "part2",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -70,7 +53,6 @@ func Test_resolve_join_value_with_reference(t *testing.T) {
|
||||
|
||||
property := &Property{
|
||||
ctx: &FileContext{
|
||||
filepath: "",
|
||||
Parameters: map[string]*Parameter{
|
||||
"Environment": {
|
||||
inner: parameterInner{
|
||||
@@ -81,62 +63,44 @@ func Test_resolve_join_value_with_reference(t *testing.T) {
|
||||
},
|
||||
},
|
||||
name: "EnvironmentBucket",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Join": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Join": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "::",
|
||||
},
|
||||
{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "::",
|
||||
},
|
||||
Type: cftypes.String,
|
||||
Value: "s3",
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "s3",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "part1",
|
||||
},
|
||||
},
|
||||
{
|
||||
ctx: &FileContext{
|
||||
filepath: "",
|
||||
Parameters: map[string]*Parameter{
|
||||
"Environment": {
|
||||
inner: parameterInner{
|
||||
Type: "string",
|
||||
Default: "staging",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Ref": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "Environment",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.String,
|
||||
Value: "part1",
|
||||
},
|
||||
{
|
||||
ctx: &FileContext{
|
||||
Parameters: map[string]*Parameter{
|
||||
"Environment": {
|
||||
inner: parameterInner{
|
||||
Type: "string",
|
||||
Default: "staging",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Ref": {
|
||||
Type: cftypes.String,
|
||||
Value: "Environment",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -10,26 +10,18 @@ import (
|
||||
|
||||
func Test_ResolveLength_WhenPropIsArray(t *testing.T) {
|
||||
prop := &Property{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Length": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Int,
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "IntParameter",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Length": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.Int,
|
||||
Value: 1,
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "IntParameter",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -53,37 +45,25 @@ func Test_ResolveLength_WhenPropIsIntrinsicFunction(t *testing.T) {
|
||||
},
|
||||
}
|
||||
prop := &Property{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Length": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Split": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "|",
|
||||
},
|
||||
},
|
||||
{
|
||||
ctx: fctx,
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Ref": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "SomeParameter",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Length": {
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Split": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "|",
|
||||
},
|
||||
{
|
||||
ctx: fctx,
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Ref": {
|
||||
Type: cftypes.String,
|
||||
Value: "SomeParameter",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,34 +7,23 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
)
|
||||
|
||||
func Test_resolve_not_value(t *testing.T) {
|
||||
property1 := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -42,19 +31,13 @@ func Test_resolve_not_value(t *testing.T) {
|
||||
}
|
||||
|
||||
notProperty := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Not": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
property1,
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Not": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
property1,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -68,29 +51,19 @@ func Test_resolve_not_value(t *testing.T) {
|
||||
|
||||
func Test_resolve_not_value_when_true(t *testing.T) {
|
||||
property1 := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -98,19 +71,13 @@ func Test_resolve_not_value_when_true(t *testing.T) {
|
||||
}
|
||||
|
||||
notProperty := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Not": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
property1,
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Not": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
property1,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,34 +7,23 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
)
|
||||
|
||||
func Test_resolve_or_value(t *testing.T) {
|
||||
property1 := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -42,49 +31,33 @@ func Test_resolve_or_value(t *testing.T) {
|
||||
}
|
||||
|
||||
property2 := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
orProperty := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Or": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
property1,
|
||||
property2,
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Or": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
property1,
|
||||
property2,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -98,29 +71,19 @@ func Test_resolve_or_value(t *testing.T) {
|
||||
|
||||
func Test_resolve_or_value_when_neither_true(t *testing.T) {
|
||||
property1 := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -128,49 +91,33 @@ func Test_resolve_or_value_when_neither_true(t *testing.T) {
|
||||
}
|
||||
|
||||
property2 := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "bar",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
orProperty := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Or": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
property1,
|
||||
property2,
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Or": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
property1,
|
||||
property2,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,7 +14,6 @@ func Test_resolve_referenced_value(t *testing.T) {
|
||||
|
||||
property := &Property{
|
||||
ctx: &FileContext{
|
||||
filepath: "",
|
||||
Parameters: map[string]*Parameter{
|
||||
"BucketName": {
|
||||
inner: parameterInner{
|
||||
@@ -25,16 +24,11 @@ func Test_resolve_referenced_value(t *testing.T) {
|
||||
},
|
||||
},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Ref": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "BucketName",
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Ref": {
|
||||
Type: cftypes.String,
|
||||
Value: "BucketName",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -48,15 +42,10 @@ func Test_resolve_referenced_value(t *testing.T) {
|
||||
func Test_property_value_correct_when_not_reference(t *testing.T) {
|
||||
|
||||
property := &Property{
|
||||
ctx: &FileContext{
|
||||
filepath: "",
|
||||
},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "someBucketName",
|
||||
},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Type: cftypes.String,
|
||||
Value: "someBucketName",
|
||||
}
|
||||
|
||||
// should fail when trying to resolve function that is not in fact a function
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -18,29 +17,19 @@ import (
|
||||
func Test_resolve_split_value(t *testing.T) {
|
||||
|
||||
property := &Property{
|
||||
ctx: &FileContext{},
|
||||
name: "BucketName",
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Split": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "::",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "s3::bucket::to::split",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Split": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "::",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "s3::bucket::to::split",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -37,7 +37,7 @@ func resolveMapSub(refValue, original *Property) (*Property, bool) {
|
||||
|
||||
for k, v := range components {
|
||||
replacement := "[failed to resolve]"
|
||||
switch v.Type() {
|
||||
switch v.Type {
|
||||
case cftypes.Map:
|
||||
resolved, _ := ResolveIntrinsicFunc(v)
|
||||
replacement = resolved.AsString()
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-json-experiment/json"
|
||||
"github.com/go-json-experiment/json/jsontext"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/aquasecurity/jfather"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes"
|
||||
)
|
||||
|
||||
type Parameter struct {
|
||||
// TODO: remove inner
|
||||
inner parameterInner
|
||||
}
|
||||
|
||||
@@ -27,27 +29,34 @@ func (p *Parameter) UnmarshalYAML(node *yaml.Node) error {
|
||||
return node.Decode(&p.inner)
|
||||
}
|
||||
|
||||
func (p *Parameter) UnmarshalJSONWithMetadata(node jfather.Node) error {
|
||||
func (p *Parameter) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
||||
|
||||
var inner parameterInner
|
||||
|
||||
if err := node.Decode(&inner); err != nil {
|
||||
if err := json.UnmarshalDecode(dec, &inner,
|
||||
json.WithUnmarshalers(json.UnmarshalFromFunc(unmarshalIntFirst)),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// jfather parses Number without fraction as int64
|
||||
// https://github.com/liamg/jfather/blob/4ef05d70c05af167226d3333a4ec7d8ac3c9c281/parse_number.go#L33-L42
|
||||
switch v := inner.Default.(type) {
|
||||
case int64:
|
||||
inner.Default = int(v)
|
||||
default:
|
||||
inner.Default = v
|
||||
}
|
||||
|
||||
p.inner = inner
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalIntFirst(dec *jsontext.Decoder, v *any) error {
|
||||
if dec.PeekKind() == '0' {
|
||||
if jval, err := dec.ReadValue(); err != nil {
|
||||
return err
|
||||
} else if v1, err := strconv.ParseInt(string(jval), 10, 64); err == nil {
|
||||
*v = int(v1)
|
||||
} else if v1, err := strconv.ParseFloat(string(jval), 64); err == nil {
|
||||
*v = v1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return json.SkipFunc
|
||||
}
|
||||
|
||||
func (p *Parameter) Type() cftypes.CfType {
|
||||
switch p.inner.Type {
|
||||
case "Boolean":
|
||||
@@ -83,35 +92,34 @@ func (p *Parameter) UpdateDefault(inVal any) {
|
||||
type Parameters map[string]any
|
||||
|
||||
func (p *Parameters) Merge(other Parameters) {
|
||||
for k, v := range other {
|
||||
(*p)[k] = v
|
||||
}
|
||||
maps.Copy((*p), other)
|
||||
}
|
||||
|
||||
func (p *Parameters) UnmarshalJSON(data []byte) error {
|
||||
func (p *Parameters) UnmarshalJSONFrom(d *jsontext.Decoder) error {
|
||||
(*p) = make(Parameters)
|
||||
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case data[0] == '{' && data[len(data)-1] == '}': // object
|
||||
switch d.PeekKind() {
|
||||
case '{':
|
||||
// CodePipeline like format
|
||||
var params struct {
|
||||
Params map[string]any `json:"Parameters"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||
if err := json.UnmarshalDecode(d, ¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
(*p) = params.Params
|
||||
case data[0] == '[' && data[len(data)-1] == ']': // array
|
||||
case '[':
|
||||
// Original format
|
||||
var params []string
|
||||
|
||||
if err := json.Unmarshal(data, ¶ms); err == nil {
|
||||
jval, err := d.ReadValue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(jval, ¶ms); err == nil {
|
||||
for _, param := range params {
|
||||
parts := strings.Split(param, "=")
|
||||
if len(parts) != 2 {
|
||||
@@ -128,9 +136,7 @@ func (p *Parameters) UnmarshalJSON(data []byte) error {
|
||||
ParameterValue string `json:"ParameterValue"`
|
||||
}
|
||||
|
||||
d := json.NewDecoder(bytes.NewReader(data))
|
||||
d.DisallowUnknownFields()
|
||||
if err := d.Decode(&cfparams); err != nil {
|
||||
if err := json.Unmarshal(jval, &cfparams, json.RejectUnknownMembers(true)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -143,3 +149,11 @@ func (p *Parameters) UnmarshalJSON(data []byte) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseParameters(r io.Reader) (Parameters, error) {
|
||||
var parameters Parameters
|
||||
if err := json.UnmarshalRead(r, ¶meters); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parameters, nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -75,9 +75,7 @@ func TestParameters_UnmarshalJSON(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var params Parameters
|
||||
|
||||
err := json.Unmarshal([]byte(tt.source), ¶ms)
|
||||
params, err := ParseParameters(strings.NewReader(tt.source))
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
|
||||
@@ -2,7 +2,6 @@ package parser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
@@ -13,9 +12,9 @@ import (
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/aquasecurity/jfather"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/ignore"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xjson "github.com/aquasecurity/trivy/pkg/x/json"
|
||||
)
|
||||
|
||||
type Parser struct {
|
||||
@@ -136,7 +135,7 @@ func (p *Parser) ParseFile(ctx context.Context, fsys fs.FS, filePath string) (fc
|
||||
}
|
||||
fctx.Ignores = ignore.Parse(string(content), filePath, "")
|
||||
case JsonSourceFormat:
|
||||
if err := jfather.Unmarshal(content, fctx); err != nil {
|
||||
if err := xjson.Unmarshal(content, fctx); err != nil {
|
||||
return nil, NewErrInvalidContent(filePath, err)
|
||||
}
|
||||
}
|
||||
@@ -176,11 +175,18 @@ func (p *Parser) parseParams() error {
|
||||
|
||||
var errs error
|
||||
for _, path := range p.parameterFiles {
|
||||
if parameters, err := p.parseParametersFile(path); err != nil {
|
||||
f, err := p.configsFS.Open(path)
|
||||
if err != nil {
|
||||
errs = multierror.Append(errs, fmt.Errorf("open file: %w", err))
|
||||
continue
|
||||
}
|
||||
|
||||
if parameters, err := ParseParameters(f); err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
} else {
|
||||
params.Merge(parameters)
|
||||
}
|
||||
_ = f.Close()
|
||||
}
|
||||
|
||||
if errs != nil {
|
||||
@@ -192,16 +198,3 @@ func (p *Parser) parseParams() error {
|
||||
p.overridedParameters = params
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseParametersFile(filePath string) (Parameters, error) {
|
||||
f, err := p.configsFS.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parameters file %q open error: %w", filePath, err)
|
||||
}
|
||||
|
||||
var parameters Parameters
|
||||
if err := json.NewDecoder(f).Decode(¶meters); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parameters, nil
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-json-experiment/json"
|
||||
"github.com/go-json-experiment/json/jsontext"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/aquasecurity/jfather"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes"
|
||||
iacTypes "github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
xjson "github.com/aquasecurity/trivy/pkg/x/json"
|
||||
)
|
||||
|
||||
type EqualityOptions = int
|
||||
@@ -20,28 +23,25 @@ const (
|
||||
)
|
||||
|
||||
type Property struct {
|
||||
xjson.Location
|
||||
ctx *FileContext
|
||||
Type cftypes.CfType
|
||||
Value any `json:"Value" yaml:"Value"`
|
||||
name string
|
||||
comment string
|
||||
rng iacTypes.Range
|
||||
parentRange iacTypes.Range
|
||||
Inner PropertyInner
|
||||
logicalId string
|
||||
unresolved bool
|
||||
}
|
||||
|
||||
type PropertyInner struct {
|
||||
Type cftypes.CfType
|
||||
Value any `json:"Value" yaml:"Value"`
|
||||
}
|
||||
|
||||
func (p *Property) Comment() string {
|
||||
return p.comment
|
||||
}
|
||||
|
||||
func (p *Property) setName(name string) {
|
||||
p.name = name
|
||||
if p.Type() == cftypes.Map {
|
||||
if p.Type == cftypes.Map {
|
||||
for n, subProp := range p.AsMap() {
|
||||
if subProp == nil {
|
||||
continue
|
||||
@@ -71,10 +71,10 @@ func (p *Property) setContext(ctx *FileContext) {
|
||||
}
|
||||
|
||||
func (p *Property) setFileAndParentRange(target fs.FS, filepath string, parentRange iacTypes.Range) {
|
||||
p.rng = iacTypes.NewRange(filepath, p.rng.GetStartLine(), p.rng.GetEndLine(), p.rng.GetSourcePrefix(), target)
|
||||
p.rng = iacTypes.NewRange(filepath, p.StartLine, p.EndLine, p.rng.GetSourcePrefix(), target)
|
||||
p.parentRange = parentRange
|
||||
|
||||
switch p.Type() {
|
||||
switch p.Type {
|
||||
case cftypes.Map:
|
||||
for _, subProp := range p.AsMap() {
|
||||
if subProp == nil {
|
||||
@@ -93,27 +93,71 @@ func (p *Property) setFileAndParentRange(target fs.FS, filepath string, parentRa
|
||||
}
|
||||
|
||||
func (p *Property) UnmarshalYAML(node *yaml.Node) error {
|
||||
p.rng = iacTypes.NewRange("", node.Line, calculateEndLine(node), "", nil)
|
||||
|
||||
p.StartLine = node.Line
|
||||
p.EndLine = calculateEndLine(node)
|
||||
p.comment = node.LineComment
|
||||
return setPropertyValueFromYaml(node, &p.Inner)
|
||||
if err := setPropertyValueFromYaml(node, p); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Property) UnmarshalJSONWithMetadata(node jfather.Node) error {
|
||||
p.rng = iacTypes.NewRange("", node.Range().Start.Line, node.Range().End.Line, "", nil)
|
||||
return setPropertyValueFromJson(node, &p.Inner)
|
||||
func (p *Property) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
||||
var valPtr any
|
||||
var nodeType cftypes.CfType
|
||||
|
||||
switch k := dec.PeekKind(); k {
|
||||
case 't', 'f':
|
||||
valPtr = new(bool)
|
||||
nodeType = cftypes.Bool
|
||||
case '"':
|
||||
valPtr = new(string)
|
||||
nodeType = cftypes.String
|
||||
case '0':
|
||||
return p.parseNumericValue(dec)
|
||||
case '[', 'n':
|
||||
valPtr = new([]*Property)
|
||||
nodeType = cftypes.List
|
||||
case '{':
|
||||
valPtr = new(map[string]*Property)
|
||||
nodeType = cftypes.Map
|
||||
case 0:
|
||||
return dec.SkipValue()
|
||||
default:
|
||||
return fmt.Errorf("unexpected token kind %q at %d", k.String(), dec.InputOffset())
|
||||
}
|
||||
|
||||
if err := json.UnmarshalDecode(dec, valPtr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Value = reflect.ValueOf(valPtr).Elem().Interface()
|
||||
p.Type = nodeType
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Property) Type() cftypes.CfType {
|
||||
return p.Inner.Type
|
||||
}
|
||||
func (p *Property) parseNumericValue(dec *jsontext.Decoder) error {
|
||||
raw, err := dec.ReadValue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
strVal := string(raw)
|
||||
|
||||
func (p *Property) Range() iacTypes.Range {
|
||||
return p.rng
|
||||
if v, err := strconv.ParseInt(strVal, 10, 64); err == nil {
|
||||
p.Value = int(v)
|
||||
p.Type = cftypes.Int
|
||||
return nil
|
||||
}
|
||||
if v, err := strconv.ParseFloat(strVal, 64); err == nil {
|
||||
p.Value = v
|
||||
p.Type = cftypes.Float64
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("invalid numeric value: %q", strVal)
|
||||
}
|
||||
|
||||
func (p *Property) Metadata() iacTypes.Metadata {
|
||||
return iacTypes.NewMetadata(p.Range(), p.name).
|
||||
return iacTypes.NewMetadata(p.rng, p.name).
|
||||
WithParent(iacTypes.NewMetadata(p.parentRange, p.logicalId))
|
||||
}
|
||||
|
||||
@@ -121,7 +165,7 @@ func (p *Property) isFunction() bool {
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
if p.Type() == cftypes.Map {
|
||||
if p.Type == cftypes.Map {
|
||||
for n := range p.AsMap() {
|
||||
return IsIntrinsic(n)
|
||||
}
|
||||
@@ -130,7 +174,7 @@ func (p *Property) isFunction() bool {
|
||||
}
|
||||
|
||||
func (p *Property) RawValue() any {
|
||||
return p.Inner.Value
|
||||
return p.Value
|
||||
}
|
||||
|
||||
func (p *Property) AsRawStrings() ([]string, error) {
|
||||
@@ -264,16 +308,15 @@ func (p *Property) GetProperty(path string) *Property {
|
||||
|
||||
func (p *Property) deriveResolved(propType cftypes.CfType, propValue any) *Property {
|
||||
return &Property{
|
||||
Location: p.Location,
|
||||
Value: propValue,
|
||||
Type: propType,
|
||||
ctx: p.ctx,
|
||||
name: p.name,
|
||||
comment: p.comment,
|
||||
rng: p.rng,
|
||||
parentRange: p.parentRange,
|
||||
logicalId: p.logicalId,
|
||||
Inner: PropertyInner{
|
||||
Type: propType,
|
||||
Value: propValue,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +360,7 @@ func (p *Property) inferBool(prop *Property, defaultValue bool) iacTypes.BoolVal
|
||||
|
||||
func (p *Property) String() string {
|
||||
r := ""
|
||||
switch p.Type() {
|
||||
switch p.Type {
|
||||
case cftypes.String:
|
||||
r = p.AsString()
|
||||
case cftypes.Int:
|
||||
@@ -326,7 +369,7 @@ func (p *Property) String() string {
|
||||
return r
|
||||
}
|
||||
|
||||
func (p *Property) SetLogicalResource(id string) {
|
||||
func (p *Property) setLogicalResource(id string) {
|
||||
p.logicalId = id
|
||||
|
||||
if p.isFunction() {
|
||||
@@ -338,13 +381,13 @@ func (p *Property) SetLogicalResource(id string) {
|
||||
if subProp == nil {
|
||||
continue
|
||||
}
|
||||
subProp.SetLogicalResource(id)
|
||||
subProp.setLogicalResource(id)
|
||||
}
|
||||
}
|
||||
|
||||
if p.IsList() {
|
||||
for _, subProp := range p.AsList() {
|
||||
subProp.SetLogicalResource(id)
|
||||
subProp.setLogicalResource(id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,9 +459,9 @@ func convert(input any) any {
|
||||
}
|
||||
|
||||
func (p *Property) inferType() {
|
||||
typ := cftypes.TypeFromGoValue(p.Inner.Value)
|
||||
typ := cftypes.TypeFromGoValue(p.Value)
|
||||
if typ == cftypes.Unknown {
|
||||
return
|
||||
}
|
||||
p.Inner.Type = typ
|
||||
p.Type = typ
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func (p *Property) IsConvertableTo(conversionType cftypes.CfType) bool {
|
||||
}
|
||||
|
||||
func (p *Property) isConvertableToString() bool {
|
||||
switch p.Type() {
|
||||
switch p.Type {
|
||||
case cftypes.Map:
|
||||
return false
|
||||
case cftypes.List:
|
||||
@@ -40,7 +40,7 @@ func (p *Property) isConvertableToString() bool {
|
||||
}
|
||||
|
||||
func (p *Property) isConvertableToBool() bool {
|
||||
switch p.Type() {
|
||||
switch p.Type {
|
||||
case cftypes.String:
|
||||
return p.EqualTo("true", IgnoreCase) || p.EqualTo("false", IgnoreCase) ||
|
||||
p.EqualTo("1", IgnoreCase) || p.EqualTo("0", IgnoreCase)
|
||||
@@ -54,7 +54,7 @@ func (p *Property) isConvertableToBool() bool {
|
||||
}
|
||||
|
||||
func (p *Property) isConvertableToInt() bool {
|
||||
switch p.Type() {
|
||||
switch p.Type {
|
||||
case cftypes.String:
|
||||
if _, err := strconv.Atoi(p.AsString()); err == nil {
|
||||
return true
|
||||
@@ -70,15 +70,15 @@ func (p *Property) ConvertTo(conversionType cftypes.CfType) *Property {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.Type() == conversionType {
|
||||
if p.Type == conversionType {
|
||||
return p
|
||||
}
|
||||
|
||||
if !p.IsConvertableTo(conversionType) {
|
||||
log.Debug("Failed to convert property",
|
||||
log.String("from", string(p.Type())),
|
||||
log.String("from", string(p.Type)),
|
||||
log.String("to", string(conversionType)),
|
||||
log.Any("range", p.Range().String()),
|
||||
log.Any("range", p.rng.String()),
|
||||
)
|
||||
return p
|
||||
}
|
||||
@@ -94,7 +94,7 @@ func (p *Property) ConvertTo(conversionType cftypes.CfType) *Property {
|
||||
}
|
||||
|
||||
func (p *Property) convertToString() *Property {
|
||||
switch p.Type() {
|
||||
switch p.Type {
|
||||
case cftypes.Int:
|
||||
return p.deriveResolved(cftypes.String, strconv.Itoa(p.AsInt()))
|
||||
case cftypes.Bool:
|
||||
@@ -110,7 +110,7 @@ func (p *Property) convertToString() *Property {
|
||||
}
|
||||
|
||||
func (p *Property) convertToBool() *Property {
|
||||
switch p.Type() {
|
||||
switch p.Type {
|
||||
case cftypes.String:
|
||||
if p.EqualTo("true", IgnoreCase) || p.EqualTo("1") {
|
||||
return p.deriveResolved(cftypes.Bool, true)
|
||||
@@ -130,8 +130,7 @@ func (p *Property) convertToBool() *Property {
|
||||
}
|
||||
|
||||
func (p *Property) convertToInt() *Property {
|
||||
//
|
||||
switch p.Type() {
|
||||
switch p.Type {
|
||||
case cftypes.String:
|
||||
if val, err := strconv.Atoi(p.AsString()); err == nil {
|
||||
return p.deriveResolved(cftypes.Int, val)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func (p *Property) IsNil() bool {
|
||||
return p == nil || p.Inner.Value == nil
|
||||
return p == nil || p.Value == nil
|
||||
}
|
||||
|
||||
func (p *Property) IsNotNil() bool {
|
||||
@@ -25,7 +25,7 @@ func (p *Property) Is(t cftypes.CfType) bool {
|
||||
return prop.Is(t)
|
||||
}
|
||||
}
|
||||
return p.Inner.Type == t
|
||||
return p.Type == t
|
||||
}
|
||||
|
||||
func (p *Property) IsString() bool {
|
||||
@@ -48,7 +48,7 @@ func (p *Property) IsMap() bool {
|
||||
if p.IsNil() || p.IsUnresolved() {
|
||||
return false
|
||||
}
|
||||
return p.Inner.Type == cftypes.Map
|
||||
return p.Type == cftypes.Map
|
||||
}
|
||||
|
||||
func (p *Property) IsNotMap() bool {
|
||||
@@ -89,7 +89,7 @@ func (p *Property) AsString() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
return p.Inner.Value.(string)
|
||||
return p.Value.(string)
|
||||
}
|
||||
|
||||
func (p *Property) AsStringValue() iacTypes.StringValue {
|
||||
@@ -113,7 +113,7 @@ func (p *Property) AsInt() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
return p.Inner.Value.(int)
|
||||
return p.Value.(int)
|
||||
}
|
||||
|
||||
func (p *Property) AsIntValue() iacTypes.IntValue {
|
||||
@@ -133,7 +133,7 @@ func (p *Property) AsBool() bool {
|
||||
if !p.IsBool() {
|
||||
return false
|
||||
}
|
||||
return p.Inner.Value.(bool)
|
||||
return p.Value.(bool)
|
||||
}
|
||||
|
||||
func (p *Property) AsBoolValue() iacTypes.BoolValue {
|
||||
@@ -144,7 +144,7 @@ func (p *Property) AsBoolValue() iacTypes.BoolValue {
|
||||
}
|
||||
|
||||
func (p *Property) AsMap() map[string]*Property {
|
||||
val, ok := p.Inner.Value.(map[string]*Property)
|
||||
val, ok := p.Value.(map[string]*Property)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
@@ -159,7 +159,7 @@ func (p *Property) AsList() []*Property {
|
||||
return []*Property{}
|
||||
}
|
||||
|
||||
if list, ok := p.Inner.Value.([]*Property); ok {
|
||||
if list, ok := p.Value.([]*Property); ok {
|
||||
return list
|
||||
}
|
||||
return nil
|
||||
@@ -183,23 +183,23 @@ func (p *Property) EqualTo(checkValue any, equalityOptions ...EqualityOptions) b
|
||||
return false
|
||||
}
|
||||
|
||||
if p.Inner.Type == cftypes.String || p.IsString() {
|
||||
if p.Type == cftypes.String || p.IsString() {
|
||||
if ignoreCase {
|
||||
return strings.EqualFold(p.AsString(), checkerVal)
|
||||
}
|
||||
return p.AsString() == checkerVal
|
||||
} else if p.Inner.Type == cftypes.Int || p.IsInt() {
|
||||
} else if p.Type == cftypes.Int || p.IsInt() {
|
||||
if val, err := strconv.Atoi(checkerVal); err == nil {
|
||||
return p.AsInt() == val
|
||||
}
|
||||
}
|
||||
return false
|
||||
case bool:
|
||||
if p.Inner.Type == cftypes.Bool || p.IsBool() {
|
||||
if p.Type == cftypes.Bool || p.IsBool() {
|
||||
return p.AsBool() == checkerVal
|
||||
}
|
||||
case int:
|
||||
if p.Inner.Type == cftypes.Int || p.IsInt() {
|
||||
if p.Type == cftypes.Int || p.IsInt() {
|
||||
return p.AsInt() == checkerVal
|
||||
}
|
||||
}
|
||||
@@ -225,7 +225,7 @@ func (p *Property) IsEmpty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
switch p.Inner.Type {
|
||||
switch p.Type {
|
||||
case cftypes.String:
|
||||
return p.AsString() == ""
|
||||
case cftypes.List, cftypes.Map:
|
||||
@@ -240,7 +240,7 @@ func (p *Property) Contains(checkVal any) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
switch p.Type() {
|
||||
switch p.Type {
|
||||
case cftypes.List:
|
||||
for _, p := range p.AsList() {
|
||||
if p.EqualTo(checkVal) {
|
||||
|
||||
@@ -9,15 +9,6 @@ import (
|
||||
"github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
)
|
||||
|
||||
func newProp(inner PropertyInner) *Property {
|
||||
return &Property{
|
||||
name: "test_prop",
|
||||
ctx: &FileContext{},
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Inner: inner,
|
||||
}
|
||||
}
|
||||
|
||||
func Test_EqualTo(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -34,155 +25,136 @@ func Test_EqualTo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "compare strings",
|
||||
property: newProp(PropertyInner{
|
||||
property: &Property{
|
||||
name: "test_prop",
|
||||
ctx: &FileContext{},
|
||||
rng: types.NewRange("testfile", 1, 1, "", nil),
|
||||
Type: cftypes.String,
|
||||
Value: "is str",
|
||||
}),
|
||||
},
|
||||
checkValue: "is str",
|
||||
isEqual: true,
|
||||
},
|
||||
{
|
||||
name: "compare strings ignoring case",
|
||||
property: newProp(PropertyInner{
|
||||
property: &Property{
|
||||
Type: cftypes.String,
|
||||
Value: "is str",
|
||||
}),
|
||||
},
|
||||
opts: []EqualityOptions{IgnoreCase},
|
||||
checkValue: "Is StR",
|
||||
isEqual: true,
|
||||
},
|
||||
{
|
||||
name: "strings ate not equal",
|
||||
property: newProp(PropertyInner{
|
||||
property: &Property{
|
||||
Type: cftypes.String,
|
||||
Value: "some value",
|
||||
}),
|
||||
},
|
||||
checkValue: "some other value",
|
||||
isEqual: false,
|
||||
},
|
||||
{
|
||||
name: "compare prop with a int represented by a string",
|
||||
property: newProp(PropertyInner{
|
||||
property: &Property{
|
||||
Type: cftypes.Int,
|
||||
Value: 147,
|
||||
}),
|
||||
},
|
||||
checkValue: "147",
|
||||
isEqual: true,
|
||||
},
|
||||
{
|
||||
name: "compare ints",
|
||||
property: newProp(PropertyInner{
|
||||
property: &Property{
|
||||
Type: cftypes.Int,
|
||||
Value: 701,
|
||||
}),
|
||||
},
|
||||
checkValue: 701,
|
||||
isEqual: true,
|
||||
},
|
||||
{
|
||||
name: "compare bools",
|
||||
property: newProp(PropertyInner{
|
||||
property: &Property{
|
||||
Type: cftypes.Bool,
|
||||
Value: true,
|
||||
}),
|
||||
},
|
||||
checkValue: true,
|
||||
isEqual: true,
|
||||
},
|
||||
{
|
||||
name: "prop is string fn",
|
||||
property: newProp(PropertyInner{
|
||||
property: &Property{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::If": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Bool,
|
||||
Value: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "bad",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "some value",
|
||||
},
|
||||
},
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.Bool,
|
||||
Value: false,
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "bad",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "some value",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
checkValue: "some value",
|
||||
isEqual: true,
|
||||
},
|
||||
{
|
||||
name: "prop is int fn",
|
||||
property: newProp(PropertyInner{
|
||||
property: &Property{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::If": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Bool,
|
||||
Value: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Int,
|
||||
Value: 121,
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Int,
|
||||
Value: -1,
|
||||
},
|
||||
},
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.Bool,
|
||||
Value: true,
|
||||
},
|
||||
{
|
||||
Type: cftypes.Int,
|
||||
Value: 121,
|
||||
},
|
||||
{
|
||||
Type: cftypes.Int,
|
||||
Value: -1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
checkValue: 121,
|
||||
isEqual: true,
|
||||
},
|
||||
{
|
||||
name: "prop is bool fn",
|
||||
property: newProp(PropertyInner{
|
||||
property: &Property{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::Equals": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
{
|
||||
Type: cftypes.String,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
checkValue: true,
|
||||
isEqual: true,
|
||||
},
|
||||
|
||||
@@ -16,16 +16,12 @@ var pseudoParameters = map[string]pseudoParameter{
|
||||
t: cftypes.List,
|
||||
val: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "notification::arn::1",
|
||||
},
|
||||
Type: cftypes.String,
|
||||
Value: "notification::arn::1",
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "notification::arn::2",
|
||||
},
|
||||
Type: cftypes.String,
|
||||
Value: "notification::arn::2",
|
||||
},
|
||||
},
|
||||
raw: []string{"notification::arn::1", "notification::arn::2"},
|
||||
|
||||
@@ -4,23 +4,22 @@ import (
|
||||
"io/fs"
|
||||
"strings"
|
||||
|
||||
"github.com/go-json-experiment/json"
|
||||
"github.com/go-json-experiment/json/jsontext"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/aquasecurity/jfather"
|
||||
iacTypes "github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
xjson "github.com/aquasecurity/trivy/pkg/x/json"
|
||||
)
|
||||
|
||||
type Resource struct {
|
||||
ctx *FileContext
|
||||
rng iacTypes.Range
|
||||
id string
|
||||
comment string
|
||||
Inner ResourceInner
|
||||
}
|
||||
|
||||
type ResourceInner struct {
|
||||
Type string `json:"Type" yaml:"Type"`
|
||||
Properties map[string]*Property `json:"Properties" yaml:"Properties"`
|
||||
xjson.Location
|
||||
typ string
|
||||
properties map[string]*Property
|
||||
ctx *FileContext
|
||||
rng iacTypes.Range
|
||||
id string
|
||||
comment string
|
||||
}
|
||||
|
||||
func (r *Resource) configureResource(id string, target fs.FS, filepath string, ctx *FileContext) {
|
||||
@@ -32,15 +31,15 @@ func (r *Resource) configureResource(id string, target fs.FS, filepath string, c
|
||||
func (r *Resource) setId(id string) {
|
||||
r.id = id
|
||||
|
||||
for n, p := range r.properties() {
|
||||
for n, p := range r.properties {
|
||||
p.setName(n)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resource) setFile(target fs.FS, filepath string) {
|
||||
r.rng = iacTypes.NewRange(filepath, r.rng.GetStartLine(), r.rng.GetEndLine(), r.rng.GetSourcePrefix(), target)
|
||||
r.rng = iacTypes.NewRange(filepath, r.StartLine, r.EndLine, r.rng.GetSourcePrefix(), target)
|
||||
|
||||
for _, p := range r.Inner.Properties {
|
||||
for _, p := range r.properties {
|
||||
p.setFileAndParentRange(target, filepath, r.rng)
|
||||
}
|
||||
}
|
||||
@@ -48,21 +47,39 @@ func (r *Resource) setFile(target fs.FS, filepath string) {
|
||||
func (r *Resource) setContext(ctx *FileContext) {
|
||||
r.ctx = ctx
|
||||
|
||||
for _, p := range r.Inner.Properties {
|
||||
p.SetLogicalResource(r.id)
|
||||
for _, p := range r.properties {
|
||||
p.setLogicalResource(r.id)
|
||||
p.setContext(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resource) UnmarshalYAML(value *yaml.Node) error {
|
||||
r.rng = iacTypes.NewRange("", value.Line-1, calculateEndLine(value), "", nil)
|
||||
r.comment = value.LineComment
|
||||
return value.Decode(&r.Inner)
|
||||
type resourceInner struct {
|
||||
Type string `json:"Type" yaml:"Type"`
|
||||
Properties map[string]*Property `json:"Properties" yaml:"Properties"`
|
||||
}
|
||||
|
||||
func (r *Resource) UnmarshalJSONWithMetadata(node jfather.Node) error {
|
||||
r.rng = iacTypes.NewRange("", node.Range().Start.Line, node.Range().End.Line, "", nil)
|
||||
return node.Decode(&r.Inner)
|
||||
func (r *Resource) UnmarshalYAML(node *yaml.Node) error {
|
||||
r.StartLine = node.Line - 1
|
||||
r.EndLine = calculateEndLine(node)
|
||||
r.comment = node.LineComment
|
||||
|
||||
var i resourceInner
|
||||
if err := node.Decode(&i); err != nil {
|
||||
return err
|
||||
}
|
||||
r.typ = i.Type
|
||||
r.properties = i.Properties
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resource) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
||||
var i resourceInner
|
||||
if err := json.UnmarshalDecode(dec, &i); err != nil {
|
||||
return err
|
||||
}
|
||||
r.typ = i.Type
|
||||
r.properties = i.Properties
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resource) ID() string {
|
||||
@@ -70,7 +87,7 @@ func (r *Resource) ID() string {
|
||||
}
|
||||
|
||||
func (r *Resource) Type() string {
|
||||
return r.Inner.Type
|
||||
return r.typ
|
||||
}
|
||||
|
||||
func (r *Resource) Range() iacTypes.Range {
|
||||
@@ -85,10 +102,6 @@ func (r *Resource) Metadata() iacTypes.Metadata {
|
||||
return iacTypes.NewMetadata(r.Range(), NewCFReference(r.id, r.rng).String())
|
||||
}
|
||||
|
||||
func (r *Resource) properties() map[string]*Property {
|
||||
return r.Inner.Properties
|
||||
}
|
||||
|
||||
func (r *Resource) IsNil() bool {
|
||||
return r.id == ""
|
||||
}
|
||||
@@ -100,7 +113,7 @@ func (r *Resource) GetProperty(path string) *Property {
|
||||
first := pathParts[0]
|
||||
property := &Property{}
|
||||
|
||||
if p, exists := r.properties()[first]; exists {
|
||||
if p, exists := r.properties[first]; exists {
|
||||
property = p
|
||||
}
|
||||
|
||||
|
||||
@@ -10,55 +10,37 @@ import (
|
||||
|
||||
func Test_GetProperty_PropIsFunction(t *testing.T) {
|
||||
resource := Resource{
|
||||
Inner: ResourceInner{
|
||||
Type: "AWS::S3::Bucket",
|
||||
Properties: map[string]*Property{
|
||||
"BucketName": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "mybucket",
|
||||
},
|
||||
},
|
||||
"VersioningConfiguration": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::If": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Bool,
|
||||
Value: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Status": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "Enabled",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Status": {
|
||||
Inner: PropertyInner{
|
||||
Type: cftypes.String,
|
||||
Value: "Suspended",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
typ: "AWS::S3::Bucket",
|
||||
properties: map[string]*Property{
|
||||
"BucketName": {
|
||||
Type: cftypes.String,
|
||||
Value: "mybucket",
|
||||
},
|
||||
"VersioningConfiguration": {
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Fn::If": {
|
||||
Type: cftypes.List,
|
||||
Value: []*Property{
|
||||
{
|
||||
Type: cftypes.Bool,
|
||||
Value: false,
|
||||
},
|
||||
{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Status": {
|
||||
Type: cftypes.String,
|
||||
Value: "Enabled",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: cftypes.Map,
|
||||
Value: map[string]*Property{
|
||||
"Status": {
|
||||
Type: cftypes.String,
|
||||
Value: "Suspended",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -5,58 +5,11 @@ import (
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/aquasecurity/jfather"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/cftypes"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes/parser"
|
||||
)
|
||||
|
||||
func setPropertyValueFromJson(node jfather.Node, propertyData *PropertyInner) error {
|
||||
|
||||
switch node.Kind() {
|
||||
case jfather.KindNumber:
|
||||
var val any
|
||||
if err := node.Decode(&val); err != nil {
|
||||
return err
|
||||
}
|
||||
switch v := val.(type) {
|
||||
case float64:
|
||||
propertyData.Type = cftypes.Float64
|
||||
propertyData.Value = v
|
||||
case int64:
|
||||
propertyData.Type = cftypes.Int
|
||||
propertyData.Value = int(v)
|
||||
}
|
||||
return nil
|
||||
case jfather.KindBoolean:
|
||||
propertyData.Type = cftypes.Bool
|
||||
return node.Decode(&propertyData.Value)
|
||||
case jfather.KindString:
|
||||
propertyData.Type = cftypes.String
|
||||
return node.Decode(&propertyData.Value)
|
||||
case jfather.KindObject:
|
||||
var childData map[string]*Property
|
||||
if err := node.Decode(&childData); err != nil {
|
||||
return err
|
||||
}
|
||||
propertyData.Type = cftypes.Map
|
||||
propertyData.Value = childData
|
||||
return nil
|
||||
case jfather.KindArray:
|
||||
var childData []*Property
|
||||
if err := node.Decode(&childData); err != nil {
|
||||
return err
|
||||
}
|
||||
propertyData.Type = cftypes.List
|
||||
propertyData.Value = childData
|
||||
return nil
|
||||
default:
|
||||
propertyData.Type = cftypes.String
|
||||
return node.Decode(&propertyData.Value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func setPropertyValueFromYaml(node *yaml.Node, propertyData *PropertyInner) error {
|
||||
func setPropertyValueFromYaml(node *yaml.Node, propertyData *Property) error {
|
||||
if IsIntrinsicFunc(node) {
|
||||
var newContent []*yaml.Node
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-json-experiment/json"
|
||||
"github.com/go-json-experiment/json/jsontext"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
xjson "github.com/aquasecurity/trivy/pkg/x/json"
|
||||
)
|
||||
|
||||
type Manifest struct {
|
||||
@@ -17,6 +13,16 @@ type Manifest struct {
|
||||
Content *ManifestNode
|
||||
}
|
||||
|
||||
func NewManifest(path string, root *ManifestNode) *Manifest {
|
||||
root.Walk(func(n *ManifestNode) {
|
||||
n.Path = path
|
||||
})
|
||||
return &Manifest{
|
||||
Path: path,
|
||||
Content: root,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manifest) UnmarshalYAML(value *yaml.Node) error {
|
||||
|
||||
switch value.Tag {
|
||||
@@ -39,66 +45,11 @@ func (m *Manifest) ToRego() any {
|
||||
}
|
||||
|
||||
func ManifestFromJSON(path string, data []byte) (*Manifest, error) {
|
||||
root := &ManifestNode{
|
||||
Path: path,
|
||||
}
|
||||
var root = &ManifestNode{}
|
||||
|
||||
if err := json.Unmarshal(data, root, json.WithUnmarshalers(
|
||||
json.UnmarshalFromFunc(func(dec *jsontext.Decoder, node *ManifestNode) error {
|
||||
startOffset := dec.InputOffset()
|
||||
if err := unmarshalManifestNode(dec, node); err != nil {
|
||||
return err
|
||||
}
|
||||
endOffset := dec.InputOffset()
|
||||
node.StartLine = 1 + countLines(data, int(startOffset))
|
||||
node.EndLine = 1 + countLines(data, int(endOffset))
|
||||
node.Path = path
|
||||
return nil
|
||||
})),
|
||||
); err != nil && !errors.Is(err, io.EOF) {
|
||||
if err := xjson.Unmarshal(data, root); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Manifest{
|
||||
Path: path,
|
||||
Content: root,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func unmarshalManifestNode(dec *jsontext.Decoder, node *ManifestNode) error {
|
||||
var valPtr any
|
||||
var nodeType TagType
|
||||
switch k := dec.PeekKind(); k {
|
||||
case 't', 'f':
|
||||
valPtr = new(bool)
|
||||
nodeType = TagBool
|
||||
case '"':
|
||||
nodeType = TagStr
|
||||
valPtr = new(string)
|
||||
case '0':
|
||||
nodeType = TagInt
|
||||
valPtr = new(uint64)
|
||||
case '[', 'n':
|
||||
valPtr = new([]*ManifestNode)
|
||||
nodeType = TagSlice
|
||||
case '{':
|
||||
valPtr = new(map[string]*ManifestNode)
|
||||
nodeType = TagMap
|
||||
case 0:
|
||||
return dec.SkipValue()
|
||||
default:
|
||||
return fmt.Errorf("unexpected token kind %q at %d", k.String(), dec.InputOffset())
|
||||
}
|
||||
|
||||
if err := json.UnmarshalDecode(dec, valPtr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
node.Value = reflect.ValueOf(valPtr).Elem().Interface()
|
||||
node.Type = nodeType
|
||||
return nil
|
||||
}
|
||||
|
||||
func countLines(data []byte, offset int) int {
|
||||
return bytes.Count(data[:offset], []byte("\n"))
|
||||
return NewManifest(path, root), nil
|
||||
}
|
||||
|
||||
@@ -3,12 +3,16 @@ package parser
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-json-experiment/json"
|
||||
"github.com/go-json-experiment/json/jsontext"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xjson "github.com/aquasecurity/trivy/pkg/x/json"
|
||||
)
|
||||
|
||||
type TagType string
|
||||
@@ -26,42 +30,37 @@ const (
|
||||
)
|
||||
|
||||
type ManifestNode struct {
|
||||
StartLine int
|
||||
EndLine int
|
||||
Offset int
|
||||
Value any
|
||||
Type TagType
|
||||
Path string
|
||||
xjson.Location
|
||||
Offset int
|
||||
Value any
|
||||
Type TagType
|
||||
Path string
|
||||
}
|
||||
|
||||
func (r *ManifestNode) ToRego() any {
|
||||
if r == nil {
|
||||
func (n *ManifestNode) ToRego() any {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
switch r.Type {
|
||||
switch n.Type {
|
||||
case TagBool, TagInt, TagFloat, TagString, TagStr, TagBinary:
|
||||
return r.Value
|
||||
return n.Value
|
||||
case TagTimestamp:
|
||||
t, ok := r.Value.(time.Time)
|
||||
t, ok := n.Value.(time.Time)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return t.Format(time.RFC3339)
|
||||
case TagSlice:
|
||||
var output []any
|
||||
for _, node := range r.Value.([]*ManifestNode) {
|
||||
for _, node := range n.Value.([]*ManifestNode) {
|
||||
output = append(output, node.ToRego())
|
||||
}
|
||||
return output
|
||||
case TagMap:
|
||||
output := make(map[string]any)
|
||||
output["__defsec_metadata"] = map[string]any{
|
||||
"startline": r.StartLine,
|
||||
"endline": r.EndLine,
|
||||
"filepath": r.Path,
|
||||
"offset": r.Offset,
|
||||
output := map[string]any{
|
||||
"__defsec_metadata": n.metadata(),
|
||||
}
|
||||
for key, node := range r.Value.(map[string]*ManifestNode) {
|
||||
for key, node := range n.Value.(map[string]*ManifestNode) {
|
||||
output[key] = node.ToRego()
|
||||
}
|
||||
return output
|
||||
@@ -69,64 +68,73 @@ func (r *ManifestNode) ToRego() any {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ManifestNode) UnmarshalYAML(node *yaml.Node) error {
|
||||
r.StartLine = node.Line
|
||||
r.EndLine = node.Line
|
||||
r.Type = TagType(node.Tag)
|
||||
func (n *ManifestNode) metadata() map[string]any {
|
||||
return map[string]any{
|
||||
"startline": n.StartLine,
|
||||
"endline": n.EndLine,
|
||||
"filepath": n.Path,
|
||||
"offset": n.Offset,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *ManifestNode) UnmarshalYAML(node *yaml.Node) error {
|
||||
n.StartLine = node.Line
|
||||
n.EndLine = node.Line
|
||||
n.Type = TagType(node.Tag)
|
||||
|
||||
switch TagType(node.Tag) {
|
||||
case TagString, TagStr:
|
||||
r.Value = node.Value
|
||||
n.Value = node.Value
|
||||
case TagInt:
|
||||
val, err := strconv.Atoi(node.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse int: %w", err)
|
||||
}
|
||||
r.Value = val
|
||||
n.Value = val
|
||||
case TagFloat:
|
||||
val, err := strconv.ParseFloat(node.Value, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse float: %w", err)
|
||||
}
|
||||
r.Value = val
|
||||
n.Value = val
|
||||
case TagBool:
|
||||
val, err := strconv.ParseBool(node.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse bool: %w", err)
|
||||
}
|
||||
r.Value = val
|
||||
n.Value = val
|
||||
case TagTimestamp:
|
||||
var val time.Time
|
||||
if err := node.Decode(&val); err != nil {
|
||||
return fmt.Errorf("failed to decode timestamp: %w", err)
|
||||
}
|
||||
r.Value = val
|
||||
n.Value = val
|
||||
case TagBinary:
|
||||
val, err := base64.StdEncoding.DecodeString(node.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode binary data: %w", err)
|
||||
}
|
||||
r.Value = val
|
||||
n.Value = val
|
||||
case TagMap:
|
||||
return r.handleMapTag(node)
|
||||
return n.handleMapTag(node)
|
||||
case TagSlice:
|
||||
return r.handleSliceTag(node)
|
||||
return n.handleSliceTag(node)
|
||||
default:
|
||||
log.WithPrefix("k8s").Debug("Skipping unsupported node tag",
|
||||
log.String("tag", node.Tag),
|
||||
log.FilePath(r.Path),
|
||||
log.FilePath(n.Path),
|
||||
log.Int("line", node.Line),
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ManifestNode) handleSliceTag(node *yaml.Node) error {
|
||||
func (n *ManifestNode) handleSliceTag(node *yaml.Node) error {
|
||||
var nodes []*ManifestNode
|
||||
maxLine := node.Line
|
||||
for _, contentNode := range node.Content {
|
||||
newNode := new(ManifestNode)
|
||||
newNode.Path = r.Path
|
||||
newNode.Path = n.Path
|
||||
if err := contentNode.Decode(newNode); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -135,8 +143,8 @@ func (r *ManifestNode) handleSliceTag(node *yaml.Node) error {
|
||||
}
|
||||
nodes = append(nodes, newNode)
|
||||
}
|
||||
r.EndLine = maxLine
|
||||
r.Value = nodes
|
||||
n.EndLine = maxLine
|
||||
n.Value = nodes
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -163,3 +171,51 @@ func (r *ManifestNode) handleMapTag(node *yaml.Node) error {
|
||||
r.Value = output
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *ManifestNode) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
||||
var valPtr any
|
||||
var nodeType TagType
|
||||
switch k := dec.PeekKind(); k {
|
||||
case 't', 'f':
|
||||
valPtr = new(bool)
|
||||
nodeType = TagBool
|
||||
case '"':
|
||||
nodeType = TagStr
|
||||
valPtr = new(string)
|
||||
case '0':
|
||||
nodeType = TagInt
|
||||
valPtr = new(uint64)
|
||||
case '[', 'n':
|
||||
valPtr = new([]*ManifestNode)
|
||||
nodeType = TagSlice
|
||||
case '{':
|
||||
valPtr = new(map[string]*ManifestNode)
|
||||
nodeType = TagMap
|
||||
case 0:
|
||||
return dec.SkipValue()
|
||||
default:
|
||||
return fmt.Errorf("unexpected token kind %q at %d", k.String(), dec.InputOffset())
|
||||
}
|
||||
|
||||
if err := json.UnmarshalDecode(dec, valPtr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.Value = reflect.ValueOf(valPtr).Elem().Interface()
|
||||
n.Type = nodeType
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *ManifestNode) Walk(f func(n *ManifestNode)) {
|
||||
f(n)
|
||||
switch n.Type {
|
||||
case TagSlice:
|
||||
for _, node := range n.Value.([]*ManifestNode) {
|
||||
node.Walk(f)
|
||||
}
|
||||
case TagMap:
|
||||
for _, node := range n.Value.(map[string]*ManifestNode) {
|
||||
node.Walk(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ func TestJsonManifestToRego(t *testing.T) {
|
||||
"__defsec_metadata": map[string]any{
|
||||
"filepath": filePath,
|
||||
"offset": 0,
|
||||
"startline": 8,
|
||||
"startline": 9,
|
||||
"endline": 17,
|
||||
},
|
||||
"command": []any{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -20,7 +21,7 @@ func Parse(_ context.Context, r io.Reader, path string) ([]any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if strings.TrimSpace(string(contents))[0] == '{' {
|
||||
if bytes.TrimSpace(contents)[0] == '{' {
|
||||
manifest, err := ManifestFromJSON(path, contents)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user