Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support comment with rule skipping for resource and scan summary modifications #466

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions pkg/cli/testdata/run-test/test_pod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ metadata:
test: someupdate
test2: someupdate3
annotations:
terrascanSkip: [accurics.kubernetes.IAM.109]
terrascanSkip:
- rule: accurics.kubernetes.IAM.109
comment: reason to skip the rule
spec:
containers:
- name: myapp-container
Expand All @@ -25,7 +27,11 @@ metadata:
test: someupdate
test2: someupdate3
annotations:
terrascanSkip: [accurics.kubernetes.IAM.3, accurics.kubernetes.OPS.461]
terrascanSkip:
- rule: accurics.kubernetes.IAM.3
comment: reason to skip the rule
- rule: accurics.kubernetes.OPS.461
comment: reason to skip the rule
spec:
template:
spec:
Expand Down
47 changes: 40 additions & 7 deletions pkg/iac-providers/kubernetes/v1/normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ import (
"gopkg.in/yaml.v3"
)

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

var (
errUnsupportedDoc = fmt.Errorf("unsupported document type")
Expand Down Expand Up @@ -146,7 +150,7 @@ func (k *K8sV1) Normalize(doc *utils.IacDocument) (*output.ResourceConfig, error
return &resourceConfig, nil
}

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

var skipRulesFromAnnotations interface{}
var ok bool
Expand All @@ -155,18 +159,47 @@ func readSkipRulesFromAnnotations(annotations map[string]interface{}, resourceID
return nil
}

skipRules := make([]string, 0)
skipRules := make([]output.SkipRule, 0)
if rules, ok := skipRulesFromAnnotations.([]interface{}); ok {
for _, rule := range rules {
if value, ok := rule.(string); ok {
skipRules = append(skipRules, value)
if value, ok := rule.(map[string]interface{}); ok {
skipRule := getSkipRuleObject(value)
if skipRule != nil {
skipRules = append(skipRules, *skipRule)
}
} else {
zap.S().Debugf("each rule in %s must be of string type", terrascanSkip)
zap.S().Debugf("each rule in %s must be of map type", terrascanSkip)
}
}
} else {
zap.S().Debugf("%s must be an array of rules to skip", terrascanSkip)
zap.S().Debugf("%s must be an array of {rule: ruleID, comment: reason for skipping}", terrascanSkip)
}

return skipRules
}

func getSkipRuleObject(m map[string]interface{}) *output.SkipRule {
var skipRule output.SkipRule
var rule, comment interface{}
var ok bool

// get rule, if rule not found return nil
if rule, ok = m[terrascanSkipRule]; ok {
if _, ok = rule.(string); ok {
skipRule.Rule = rule.(string)
} else {
return nil
}
} else {
return nil
}

// get comment
if comment, ok = m[terrascanSkipComment]; ok {
if _, ok = comment.(string); ok {
skipRule.Comment = comment.(string)
}
}

return &skipRule
}
121 changes: 106 additions & 15 deletions pkg/iac-providers/kubernetes/v1/normalize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ kind: Pod
metadata:
name: myapp-pod
annotations:
terrascanSkip: [accurics.kubernetes.IAM.109]
terrascanSkip:
- rule: accurics.kubernetes.IAM.109
comment: reason to skip the rule
spec:
containers:
- name: myapp-container
Expand All @@ -66,7 +68,9 @@ kind: CRD
metadata:
generateName: myapp-pod-prefix-
annotations:
terrascanSkip: [accurics.kubernetes.IAM.109]
terrascanSkip:
- rule: accurics.kubernetes.IAM.109
comment: reason to skip the rule
spec:
containers:
- name: myapp-container
Expand Down Expand Up @@ -121,7 +125,10 @@ func TestK8sV1ExtractResource(t *testing.T) {
Metadata: k8sMetadata{
Name: "myapp-pod",
Annotations: map[string]interface{}{
terrascanSkip: []interface{}{"accurics.kubernetes.IAM.109"},
terrascanSkip: []interface{}{map[string]interface{}{
"rule": "accurics.kubernetes.IAM.109",
"comment": "reason to skip the rule",
}},
},
},
},
Expand Down Expand Up @@ -179,6 +186,12 @@ func TestK8sV1GetNormalizedName(t *testing.T) {

func TestK8sV1Normalize(t *testing.T) {
testRule := "accurics.kubernetes.IAM.109"
testComment := "reason to skip the rule"

testSkipRule := output.SkipRule{
Rule: testRule,
Comment: testComment,
}

type args struct {
doc *utils.IacDocument
Expand Down Expand Up @@ -215,7 +228,10 @@ func TestK8sV1Normalize(t *testing.T) {
"kind": "Pod",
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
terrascanSkip: []interface{}{testRule},
terrascanSkip: []interface{}{map[string]interface{}{
"rule": testRule,
"comment": testComment,
}},
},
"name": "myapp-pod",
},
Expand All @@ -228,7 +244,7 @@ func TestK8sV1Normalize(t *testing.T) {
},
},
},
SkipRules: []string{testRule},
SkipRules: []output.SkipRule{testSkipRule},
},
},
{
Expand All @@ -249,7 +265,10 @@ func TestK8sV1Normalize(t *testing.T) {
"kind": "CRD",
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
terrascanSkip: []interface{}{testRule},
terrascanSkip: []interface{}{map[string]interface{}{
"rule": testRule,
"comment": testComment,
}},
},
"generateName": "myapp-pod-prefix-",
},
Expand All @@ -262,7 +281,7 @@ func TestK8sV1Normalize(t *testing.T) {
},
},
},
SkipRules: []string{testRule},
SkipRules: []output.SkipRule{testSkipRule},
},
},
}
Expand All @@ -284,8 +303,13 @@ func TestK8sV1Normalize(t *testing.T) {
func TestReadSkipRulesFromAnnotations(t *testing.T) {
// test data
testRuleA := "RuleA"
testCommentA := "RuleA can be skipped"
testRuleB := "RuleB"
testCommentB := "RuleB must be skipped"
testRuleC := "RuleC"
testCommentC := "RuleC skipped"

testSkipRule := output.SkipRule{Rule: testRuleA}

type args struct {
annotations map[string]interface{}
Expand All @@ -294,7 +318,7 @@ func TestReadSkipRulesFromAnnotations(t *testing.T) {
tests := []struct {
name string
args args
want []string
want []output.SkipRule
}{
{
name: "nil annotations",
Expand All @@ -317,34 +341,101 @@ func TestReadSkipRulesFromAnnotations(t *testing.T) {
terrascanSkip: "test",
},
},
want: []string{},
want: []output.SkipRule{},
},
{
name: "annotations with invalid terrascanSkipRules rule value",
name: "annotations with invalid SkipRule object",
args: args{
annotations: map[string]interface{}{
terrascanSkip: []interface{}{1},
},
},
want: []string{},
want: []output.SkipRule{},
},
{
name: "annotations with invalid terrascanSkipRules rule value",
args: args{
annotations: map[string]interface{}{
terrascanSkip: []interface{}{map[string]interface{}{
terrascanSkipRule: 1,
}},
},
},
want: []output.SkipRule{},
},
{
name: "annotations with one terrascanSkipRules",
args: args{
annotations: map[string]interface{}{
terrascanSkip: []interface{}{testRuleA},
terrascanSkip: []interface{}{map[string]interface{}{
terrascanSkipRule: testRuleA,
}},
},
},
want: []output.SkipRule{
{
Rule: testRuleA,
},
},
want: []string{testRuleA},
},
{
name: "annotations with multiple terrascanSkipRules",
args: args{
annotations: map[string]interface{}{
terrascanSkip: []interface{}{testRuleA, testRuleB, testRuleC},
terrascanSkip: []interface{}{
map[string]interface{}{
terrascanSkipRule: testRuleA,
terrascanSkipComment: testCommentA,
},
map[string]interface{}{
terrascanSkipRule: testRuleB,
terrascanSkipComment: testCommentB,
},
map[string]interface{}{
terrascanSkipRule: testRuleC,
terrascanSkipComment: testCommentC,
}},
},
},
want: []output.SkipRule{
{
Rule: testRuleA,
Comment: testCommentA,
},
{
Rule: testRuleB,
Comment: testCommentB,
},
{
Rule: testRuleC,
Comment: testCommentC,
},
},
},
{
name: "annotations with invalid rule key in terrascanSkipRules",
args: args{
annotations: map[string]interface{}{
terrascanSkip: []interface{}{
map[string]interface{}{
"skip-rule": testRuleA,
terrascanSkipComment: testCommentA,
}},
},
},
want: []output.SkipRule{},
},
{
name: "annotations with no comment key in terrascanSkipRules",
args: args{
annotations: map[string]interface{}{
terrascanSkip: []interface{}{
map[string]interface{}{
terrascanSkipRule: testRuleA,
}},
},
},
want: []string{testRuleA, testRuleB, testRuleC},
want: []output.SkipRule{testSkipRule},
},
}
for _, tt := range tests {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ metadata:
test: someupdate
test2: someupdate3
annotations:
terrascanSkip: [accurics.kubernetes.IAM.109]
terrascanSkip:
- rule: accurics.kubernetes.IAM.109
comment: reason to skip the rule
spec:
containers:
- name: myapp-container
Expand All @@ -25,7 +27,11 @@ metadata:
test: someupdate
test2: someupdate3
annotations:
terrascanSkip: [accurics.kubernetes.IAM.3, accurics.kubernetes.OPS.461]
terrascanSkip:
- rule: accurics.kubernetes.IAM.3
comment: reason to skip the rule
- rule: accurics.kubernetes.OPS.461
comment: reason to skip the rule
spec:
template:
spec:
Expand Down
8 changes: 7 additions & 1 deletion pkg/iac-providers/output/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ type ResourceConfig struct {
// SkipRules will hold the rules to be skipped for the resource.
// Each iac provider should append the rules to be skipped for a resource,
// while extracting resource from the iac files
SkipRules []string `json:"skip_rules"`
SkipRules []SkipRule `json:"skip_rules" yaml:"skip_rules"`
}

// SkipRule struct will hold the skipped rule and any comment for the skipped rule
type SkipRule struct {
Rule string `json:"rule"`
Comment string `json:"comment"`
}

// AllResourceConfigs is a list/slice of resource configs present in IaC
Expand Down
Loading