Skip to content

Commit

Permalink
Refactor and add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sigmabaryon authored and bagulm123 committed Jun 2, 2021
1 parent 085fdbe commit 0898808
Showing 9 changed files with 122 additions and 80 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ require (
github.com/stretchr/testify v1.7.0
github.com/zclconf/go-cty v1.8.2
go.uber.org/zap v1.16.0
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b
golang.org/x/tools v0.1.2 // indirect
gopkg.in/src-d/go-git.v4 v4.13.1
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1524,6 +1524,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea h1:+WiDlPBBaO+h9vPNZi8uJ3k4BkKQB7Iow3aqwHVA5hI=
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b h1:qh4f65QIVFjq9eBURLEYWqaEXmOyqdUyiBSgaXWccWk=
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
28 changes: 1 addition & 27 deletions pkg/iac-providers/kubernetes/v1/normalize.go
Original file line number Diff line number Diff line change
@@ -29,9 +29,6 @@ import (
)

const (
terrascanSkip = "runterrascan.io/skip"
terrascanSkipRule = "rule"
terrascanSkipComment = "comment"
terrascanMaxSeverity = "runterrascan.io/maxseverity"
terrascanMinSeverity = "runterrascan.io/minseverity"
)
@@ -138,7 +135,7 @@ func (k *K8sV1) Normalize(doc *utils.IacDocument) (*output.ResourceConfig, error
}

// read and update skip rules, if present
skipRules := readSkipRulesFromAnnotations(resource.Metadata.Annotations, resourceConfig.ID)
skipRules := utils.ReadSkipRulesFromMap(resource.Metadata.Annotations, resourceConfig.ID)
if skipRules != nil {
resourceConfig.SkipRules = append(resourceConfig.SkipRules, skipRules...)
}
@@ -159,29 +156,6 @@ func (k *K8sV1) Normalize(doc *utils.IacDocument) (*output.ResourceConfig, error
return &resourceConfig, nil
}

func readSkipRulesFromAnnotations(annotations map[string]interface{}, resourceID string) []output.SkipRule {

var skipRulesFromAnnotations interface{}
var ok bool
if skipRulesFromAnnotations, ok = annotations[terrascanSkip]; !ok {
zap.S().Debugf(infileInstructionNotPresentLog, terrascanSkip, resourceID)
return nil
}

if rules, ok := skipRulesFromAnnotations.(string); ok {
skipRules := make([]output.SkipRule, 0)
err := json.Unmarshal([]byte(rules), &skipRules)
if err != nil {
zap.S().Debugf("json string %s cannot be unmarshalled to []output.SkipRules struct schema", rules)
return nil
}
return skipRules
}

zap.S().Debugf("%s must be a string containing an json array like [{rule: ruleID, comment: reason for skipping}]", terrascanSkip)
return nil
}

// readMinMaxSeverityFromAnnotations finds the min max severity values set in annotations for the resource
func readMinMaxSeverityFromAnnotations(annotations map[string]interface{}, resourceID string) (maxSeverity, minSeverity string) {
var (
22 changes: 11 additions & 11 deletions pkg/iac-providers/kubernetes/v1/normalize_test.go
Original file line number Diff line number Diff line change
@@ -124,7 +124,7 @@ func TestK8sV1ExtractResource(t *testing.T) {
Metadata: k8sMetadata{
Name: "myapp-pod",
Annotations: map[string]interface{}{
terrascanSkip: "[{\"rule\": \"accurics.kubernetes.IAM.109\", \"comment\": \"reason to skip the rule\"}]\n",
utils.TerrascanSkip: "[{\"rule\": \"accurics.kubernetes.IAM.109\", \"comment\": \"reason to skip the rule\"}]\n",
},
},
},
@@ -224,7 +224,7 @@ func TestK8sV1Normalize(t *testing.T) {
"kind": "Pod",
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
terrascanSkip: "[{\"rule\": \"accurics.kubernetes.IAM.109\", \"comment\": \"reason to skip the rule\"}]\n",
utils.TerrascanSkip: "[{\"rule\": \"accurics.kubernetes.IAM.109\", \"comment\": \"reason to skip the rule\"}]\n",
},
"name": "myapp-pod",
},
@@ -258,7 +258,7 @@ func TestK8sV1Normalize(t *testing.T) {
"kind": "CRD",
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
terrascanSkip: "[{\"rule\": \"accurics.kubernetes.IAM.109\", \"comment\": \"reason to skip the rule\"}]\n",
utils.TerrascanSkip: "[{\"rule\": \"accurics.kubernetes.IAM.109\", \"comment\": \"reason to skip the rule\"}]\n",
},
"generateName": "myapp-pod-prefix-",
},
@@ -328,7 +328,7 @@ func TestReadSkipRulesFromAnnotations(t *testing.T) {
name: "annotations with invalid terrascanSkipRules type",
args: args{
annotations: map[string]interface{}{
terrascanSkip: "test",
utils.TerrascanSkip: "test",
},
},
want: nil,
@@ -337,7 +337,7 @@ func TestReadSkipRulesFromAnnotations(t *testing.T) {
name: "annotations with invalid SkipRule object",
args: args{
annotations: map[string]interface{}{
terrascanSkip: []interface{}{1},
utils.TerrascanSkip: []interface{}{1},
},
},
want: nil,
@@ -346,7 +346,7 @@ func TestReadSkipRulesFromAnnotations(t *testing.T) {
name: "annotations with invalid terrascanSkipRules rule value",
args: args{
annotations: map[string]interface{}{
terrascanSkip: fmt.Sprintf(`{"%s":%d}`, terrascanSkipRule, 1),
utils.TerrascanSkip: fmt.Sprintf(`{"%s":%d}`, utils.TerrascanSkipRule, 1),
},
},
want: nil,
@@ -355,7 +355,7 @@ func TestReadSkipRulesFromAnnotations(t *testing.T) {
name: "annotations with one terrascanSkipRules",
args: args{
annotations: map[string]interface{}{
terrascanSkip: fmt.Sprintf(`[{"%s":"%s"}]`, terrascanSkipRule, testRuleA),
utils.TerrascanSkip: fmt.Sprintf(`[{"%s":"%s"}]`, utils.TerrascanSkipRule, testRuleA),
},
},
want: []output.SkipRule{
@@ -368,7 +368,7 @@ func TestReadSkipRulesFromAnnotations(t *testing.T) {
name: "annotations with multiple terrascanSkipRules",
args: args{
annotations: map[string]interface{}{
terrascanSkip: fmt.Sprintf(`[{"rule":"%s","comment":"%s"}, {"rule":"%s","comment":"%s"}, {"rule":"%s","comment":"%s"}]`, testRuleA, testCommentA, testRuleB, testCommentB, testRuleC, testCommentC),
utils.TerrascanSkip: fmt.Sprintf(`[{"rule":"%s","comment":"%s"}, {"rule":"%s","comment":"%s"}, {"rule":"%s","comment":"%s"}]`, testRuleA, testCommentA, testRuleB, testCommentB, testRuleC, testCommentC),
},
},
want: []output.SkipRule{
@@ -390,7 +390,7 @@ func TestReadSkipRulesFromAnnotations(t *testing.T) {
name: "annotations with invalid rule key in terrascanSkipRules",
args: args{
annotations: map[string]interface{}{
terrascanSkip: fmt.Sprintf(`[{"skip":"%s","comment":"%s"}]`, testRuleA, testCommentA),
utils.TerrascanSkip: fmt.Sprintf(`[{"skip":"%s","comment":"%s"}]`, testRuleA, testCommentA),
},
},
want: []output.SkipRule{{Comment: testCommentA}},
@@ -399,15 +399,15 @@ func TestReadSkipRulesFromAnnotations(t *testing.T) {
name: "annotations with no comment key in terrascanSkipRules",
args: args{
annotations: map[string]interface{}{
terrascanSkip: fmt.Sprintf(`[{"rule":"%s"}]`, testRuleA),
utils.TerrascanSkip: fmt.Sprintf(`[{"rule":"%s"}]`, testRuleA),
},
},
want: []output.SkipRule{testSkipRule},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := readSkipRulesFromAnnotations(tt.args.annotations, tt.args.resourceID); !reflect.DeepEqual(got, tt.want) {
if got := utils.ReadSkipRulesFromMap(tt.args.annotations, tt.args.resourceID); !reflect.DeepEqual(got, tt.want) {
t.Errorf("readSkipRulesFromAnnotations() = got %v, want %v", got, tt.want)
}
})
1 change: 1 addition & 0 deletions pkg/mapper/iac-providers/cft/config/api-gateway-stage.go
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ type MethodSettingConfig struct {
MethodSettings []Settings `json:"settings"`
}

// Settings holds configs for the MethodSetting attribute
type Settings struct {
MetricsEnabled bool `json:"metrics_enabled"`
}
36 changes: 1 addition & 35 deletions pkg/mapper/iac-providers/cft/mapper.go
Original file line number Diff line number Diff line change
@@ -17,13 +17,10 @@
package cft

import (
"encoding/json"
"errors"
"fmt"

"github.com/awslabs/goformation/v4/cloudformation/cloudfront"
"github.com/awslabs/goformation/v4/cloudformation/cloudtrail"
"go.uber.org/zap"

cf "github.com/awslabs/goformation/v4/cloudformation/cloudformation"
cnf "github.com/awslabs/goformation/v4/cloudformation/config"
@@ -68,11 +65,6 @@ import (

const errUnsupportedDoc = "unsupported document type"

const (
terrascanSkip = "terrascanSkip"
terrascanSkipRule = "rule"
)

type cftMapper struct {
}

@@ -119,7 +111,7 @@ func (m cftMapper) Map(doc *utils.IacDocument, params ...map[string]interface{})

// add skipRules if available
if resourceConfig.Metadata != nil {
skipRules := readSkipRulesFromMap(resourceConfig.Metadata, rc.ID)
skipRules := utils.ReadSkipRulesFromMap(resourceConfig.Metadata, rc.ID)
if skipRules != nil {
rc.SkipRules = append(rc.SkipRules, skipRules...)
}
@@ -152,32 +144,6 @@ func extractTemplate(doc *utils.IacDocument) (*cloudformation.Template, error) {
}
}

func readSkipRulesFromMap(skipRuleMap map[string]interface{}, resourceID string) []output.SkipRule {

var skipRulesFromMap interface{}

var ok bool
if skipRulesFromMap, ok = skipRuleMap[terrascanSkip]; !ok {
zap.S().Debugf("%s not present for resource: %s", terrascanSkip, resourceID)
return nil
}

fmt.Printf("%+v\n", skipRuleMap)

if rules, ok := skipRulesFromMap.(string); ok {
skipRules := make([]output.SkipRule, 0)
err := json.Unmarshal([]byte(rules), &skipRules)
if err != nil {
zap.S().Debugf("json string %s cannot be unmarshalled to []output.SkipRules struct schema", rules)
return nil
}
return skipRules
}

zap.S().Debugf("%s must be a string containing an json array like [{rule: ruleID, comment: reason for skipping}]", terrascanSkip)
return nil
}

func (m cftMapper) mapConfigForResource(r cloudformation.Resource) []config.AWSResourceConfig {
switch resource := r.(type) {
case *docdb.DBCluster:
4 changes: 0 additions & 4 deletions pkg/policy/opa/engine.go
Original file line number Diff line number Diff line change
@@ -350,10 +350,6 @@ func (e *Engine) Evaluate(engineInput policy.EngineInput) (policy.EngineOutput,
// Keep track of how long it takes to evaluate the policies
start := time.Now()

inputData, err := json.Marshal(engineInput.InputData)
if err == nil {
fmt.Printf("%+v\n", string(inputData))
}
// Evaluate the policy against each resource type
for k := range e.regoDataMap {
// Execute the prepared query.
62 changes: 60 additions & 2 deletions pkg/utils/skip_rules.go
Original file line number Diff line number Diff line change
@@ -17,15 +17,27 @@
package utils

import (
"encoding/json"
"regexp"
"strings"

"github.com/accurics/terrascan/pkg/iac-providers/output"
"go.uber.org/zap"
)

const (
// TerrascanSkip key used to detect rules for skipping violations
TerrascanSkip = "runterrascan.io/skip"
// TerrascanSkipRule key used to detect the rule to be skipped
TerrascanSkipRule = "rule"
// TerrascanSkipComment key used to detect comment skiupping a give rule
TerrascanSkipComment = "comment"
)

var (
skipRulesPattern = regexp.MustCompile(`(#ts:skip=[ \t]*(([A-Za-z0-9]+[.-]{1}){3,5}([\d]+)){1}([ \t]+.*){0,1})`)
skipRulesPrefix = "#ts:skip="
skipRulesPattern = regexp.MustCompile(`(#ts:skip=[ \t]*(([A-Za-z0-9]+[.-]{1}){3,5}([\d]+)){1}([ \t]+.*){0,1})`)
skipRulesPrefix = "#ts:skip="
infileInstructionNotPresentLog = "%s not present for resource: %s"
)

// GetSkipRules returns a list of rules to be skipped. The rules to be skipped
@@ -69,3 +81,49 @@ func getSkipRuleObject(s string) *output.SkipRule {
}
return &skipRule
}

// ReadSkipRulesFromMap returns a list of rules to be skipped. The rules to be skipped
// can be set in annotations for kubernetes manifests and Resource Metadata in AWS cft:
// k8s:
// metadata:
// annotations:
// runterrascan.io/skip: |
// [{"rule": "accurics.kubernetes.IAM.109", "comment": "reason to skip the rule"}]
// cft:
// Resource:
// myResource:
// Metadata:
// runterrascan.io/skip: |
// [{"rule": "AC_AWS_047", "comment": "reason to skip the rule"}]
// cft json:
// "Resource":{
// "myResource":{
// "Metadata":{
// "runterrascan.io/skip": "[{\"rule\":\"AWS.CloudFormation.Medium.0603\"}]"
// }
// }
// }
// each rule and its optional comment must be a string containing an json array like
// [{rule: ruleID, comment: reason for skipping}]
func ReadSkipRulesFromMap(skipRulesMap map[string]interface{}, resourceID string) []output.SkipRule {

var skipRulesFromMap interface{}
var ok bool
if skipRulesFromMap, ok = skipRulesMap[TerrascanSkip]; !ok {
zap.S().Debugf(infileInstructionNotPresentLog, TerrascanSkip, resourceID)
return nil
}

if rules, ok := skipRulesFromMap.(string); ok {
skipRules := make([]output.SkipRule, 0)
err := json.Unmarshal([]byte(rules), &skipRules)
if err != nil {
zap.S().Debugf("json string %s cannot be unmarshalled to []output.SkipRules struct schema", rules)
return nil
}
return skipRules
}

zap.S().Debugf("%s must be a string containing an json array like [{rule: ruleID, comment: reason for skipping}]", TerrascanSkip)
return nil
}
45 changes: 45 additions & 0 deletions pkg/utils/skip_rules_test.go
Original file line number Diff line number Diff line change
@@ -143,3 +143,48 @@ func TestGetSkipRules(t *testing.T) {
})
}
}

func TestReadSkipRulesFromMap(t *testing.T) {
testRuleAWS1 := "AWS.CloudFormation.Medium.0603"
testRuleK8s := "accurics.kubernetes.IAM.109"

table := []struct {
name string
input map[string]interface{}
expected []output.SkipRule
}{
{
name: "no rules",
input: make(map[string]interface{}),
// expected would be empty
},
{
name: "with valid aws rule",
input: map[string]interface{}{TerrascanSkip: "[{\"rule\":\"AWS.CloudFormation.Medium.0603\"}]"},
expected: []output.SkipRule{
{Rule: testRuleAWS1},
},
},
{
name: "with valid k8s rule",
input: map[string]interface{}{TerrascanSkip: "[{\"rule\":\"accurics.kubernetes.IAM.109\"}]"},
expected: []output.SkipRule{
{Rule: testRuleK8s},
},
},
{
name: "with invalid rule format",
input: map[string]interface{}{TerrascanSkip: "[{\"rule\"\"accurics.kubernetes.IAM.109\"}]"},
// expected would be empty
},
}

for _, tt := range table {
t.Run(tt.name, func(t *testing.T) {
actual := ReadSkipRulesFromMap(tt.input, "testID")
if !reflect.DeepEqual(actual, tt.expected) {
t.Errorf("rule ids got: '%v', want: '%v'", actual, tt.expected)
}
})
}
}

0 comments on commit 0898808

Please sign in to comment.