Skip to content

Commit 58819c5

Browse files
yagreutnikpivkin
andauthored
feat(misconf): Update SecurityCenter schema (#9674)
Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io> Co-authored-by: nikpivkin <nikita.pivkin@smartforce.io>
1 parent 2690ac9 commit 58819c5

File tree

6 files changed

+195
-15
lines changed

6 files changed

+195
-15
lines changed

pkg/iac/adapters/arm/securitycenter/adapt.go

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package securitycenter
33
import (
44
"github.com/aquasecurity/trivy/pkg/iac/providers/azure/securitycenter"
55
"github.com/aquasecurity/trivy/pkg/iac/scanners/azure"
6+
iacTypes "github.com/aquasecurity/trivy/pkg/iac/types"
67
)
78

89
func Adapt(deployment azure.Deployment) securitycenter.SecurityCenter {
@@ -12,7 +13,8 @@ func Adapt(deployment azure.Deployment) securitycenter.SecurityCenter {
1213
}
1314
}
1415

15-
func adaptContacts(deployment azure.Deployment) (contacts []securitycenter.Contact) {
16+
func adaptContacts(deployment azure.Deployment) []securitycenter.Contact {
17+
var contacts []securitycenter.Contact
1618
for _, resource := range deployment.GetResourcesByType("Microsoft.Security/securityContacts") {
1719
contacts = append(contacts, adaptContact(resource))
1820
}
@@ -21,16 +23,59 @@ func adaptContacts(deployment azure.Deployment) (contacts []securitycenter.Conta
2123
}
2224

2325
func adaptContact(resource azure.Resource) securitycenter.Contact {
26+
alertsToAdminsState := resource.Properties.GetMapValue("notificationsByRole").GetMapValue("state").AsStringValue("", resource.Metadata)
27+
isEnabledValue := resource.Properties.GetMapValue("isEnabled").AsBoolValue(false, resource.Metadata)
28+
29+
enableAlertNotifications, minimalSeverity := extractNotificationSettings(resource, isEnabledValue)
30+
2431
return securitycenter.Contact{
25-
Metadata: resource.Metadata,
26-
// TODO: The email field does not exist
27-
// https://learn.microsoft.com/en-us/azure/templates/microsoft.security/securitycontacts?pivots=deployment-language-arm-template#securitycontactproperties-1
28-
EnableAlertNotifications: resource.Properties.GetMapValue("email").AsBoolValue(false, resource.Metadata),
32+
Metadata: resource.Metadata,
33+
EnableAlertNotifications: enableAlertNotifications,
34+
EnableAlertsToAdmins: iacTypes.Bool(alertsToAdminsState.EqualTo("On"), resource.Metadata),
35+
Email: resource.Properties.GetMapValue("emails").AsStringValue("", resource.Metadata),
2936
Phone: resource.Properties.GetMapValue("phone").AsStringValue("", resource.Metadata),
37+
IsEnabled: isEnabledValue,
38+
MinimalSeverity: minimalSeverity,
39+
}
40+
}
41+
42+
func extractNotificationSettings(resource azure.Resource, isEnabled iacTypes.BoolValue) (iacTypes.BoolValue, iacTypes.StringValue) {
43+
notificationsSources := resource.Properties.GetMapValue("notificationsSources")
44+
if !notificationsSources.IsNull() {
45+
return extractFromNotificationsSources(notificationsSources, isEnabled, resource)
46+
}
47+
48+
enableAlertNotifications := resource.Properties.GetMapValue("alertNotifications").AsBoolValue(false, resource.Metadata)
49+
minimalSeverity := iacTypes.StringDefault("", resource.Metadata)
50+
return enableAlertNotifications, minimalSeverity
51+
}
52+
53+
func extractFromNotificationsSources(notificationsSources azure.Value, isEnabled iacTypes.BoolValue, resource azure.Resource) (iacTypes.BoolValue, iacTypes.StringValue) {
54+
minimalSeverity := iacTypes.StringDefault("", resource.Metadata)
55+
56+
for _, source := range notificationsSources.AsList() {
57+
sourceMap := source.AsMap()
58+
if sourceMap == nil {
59+
continue
60+
}
61+
62+
sourceType, hasSourceType := sourceMap["sourceType"]
63+
if !hasSourceType || !sourceType.AsStringValue("", resource.Metadata).EqualTo("Alert") {
64+
continue
65+
}
66+
67+
if minimalSeverityVal, hasMinimalSeverity := sourceMap["minimalSeverity"]; hasMinimalSeverity {
68+
minimalSeverity = minimalSeverityVal.AsStringValue("", resource.Metadata)
69+
}
70+
break
3071
}
72+
73+
enableAlertNotifications := iacTypes.Bool(isEnabled.IsTrue() && !minimalSeverity.IsEmpty(), resource.Metadata)
74+
return enableAlertNotifications, minimalSeverity
3175
}
3276

33-
func adaptSubscriptions(deployment azure.Deployment) (subscriptions []securitycenter.SubscriptionPricing) {
77+
func adaptSubscriptions(deployment azure.Deployment) []securitycenter.SubscriptionPricing {
78+
var subscriptions []securitycenter.SubscriptionPricing
3479
for _, resource := range deployment.GetResourcesByType("Microsoft.Security/pricings") {
3580
subscriptions = append(subscriptions, adaptSubscription(resource))
3681
}

pkg/iac/adapters/arm/securitycenter/adapt_test.go

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,18 @@ func TestAdapt(t *testing.T) {
3636
},
3737
},
3838
{
39-
name: "complete",
39+
name: "complete - legacy format",
4040
source: `{
4141
"resources": [
4242
{
4343
"type": "Microsoft.Security/securityContacts",
4444
"properties": {
45-
"phone": "buz"
45+
"emails": "security@example.com",
46+
"phone": "buz",
47+
"alertNotifications": true,
48+
"notificationsByRole": {
49+
"state": "On"
50+
}
4651
}
4752
},
4853
{
@@ -55,13 +60,93 @@ func TestAdapt(t *testing.T) {
5560
}`,
5661
expected: securitycenter.SecurityCenter{
5762
Contacts: []securitycenter.Contact{{
58-
Phone: types.StringTest("buz"),
63+
Email: types.StringTest("security@example.com"),
64+
Phone: types.StringTest("buz"),
65+
EnableAlertNotifications: types.BoolTest(true),
66+
EnableAlertsToAdmins: types.BoolTest(true),
5967
}},
6068
Subscriptions: []securitycenter.SubscriptionPricing{{
6169
Tier: types.StringTest("Standard"),
6270
}},
6371
},
6472
},
73+
{
74+
name: "complete - new format",
75+
source: `{
76+
"resources": [
77+
{
78+
"type": "Microsoft.Security/securityContacts",
79+
"properties": {
80+
"emails": "security@example.com",
81+
"phone": "+1-555-555-5555",
82+
"isEnabled": true,
83+
"notificationsSources": [
84+
{
85+
"sourceType": "Alert",
86+
"minimalSeverity": "High"
87+
}
88+
],
89+
"notificationsByRole": {
90+
"state": "On"
91+
}
92+
}
93+
},
94+
{
95+
"type": "Microsoft.Security/pricings",
96+
"properties": {
97+
"pricingTier": "Standard"
98+
}
99+
}
100+
]
101+
}`,
102+
expected: securitycenter.SecurityCenter{
103+
Contacts: []securitycenter.Contact{{
104+
Email: types.StringTest("security@example.com"),
105+
Phone: types.StringTest("+1-555-555-5555"),
106+
EnableAlertNotifications: types.BoolTest(true),
107+
EnableAlertsToAdmins: types.BoolTest(true),
108+
IsEnabled: types.BoolTest(true),
109+
MinimalSeverity: types.StringTest("High"),
110+
}},
111+
Subscriptions: []securitycenter.SubscriptionPricing{{
112+
Tier: types.StringTest("Standard"),
113+
}},
114+
},
115+
},
116+
{
117+
name: "new format - disabled",
118+
source: `{
119+
"resources": [
120+
{
121+
"type": "Microsoft.Security/securityContacts",
122+
"properties": {
123+
"emails": "security@example.com",
124+
"phone": "+1-555-555-5555",
125+
"isEnabled": false,
126+
"notificationsSources": [
127+
{
128+
"sourceType": "Alert",
129+
"minimalSeverity": "Medium"
130+
}
131+
],
132+
"notificationsByRole": {
133+
"state": "Off"
134+
}
135+
}
136+
}
137+
]
138+
}`,
139+
expected: securitycenter.SecurityCenter{
140+
Contacts: []securitycenter.Contact{{
141+
Email: types.StringTest("security@example.com"),
142+
Phone: types.StringTest("+1-555-555-5555"),
143+
EnableAlertNotifications: types.BoolTest(false),
144+
EnableAlertsToAdmins: types.BoolTest(false),
145+
IsEnabled: types.BoolTest(false),
146+
MinimalSeverity: types.StringTest("Medium"),
147+
}},
148+
},
149+
},
65150
}
66151

67152
for _, tt := range tests {

pkg/iac/adapters/terraform/azure/securitycenter/adapt.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package securitycenter
33
import (
44
"github.com/aquasecurity/trivy/pkg/iac/providers/azure/securitycenter"
55
"github.com/aquasecurity/trivy/pkg/iac/terraform"
6+
iacTypes "github.com/aquasecurity/trivy/pkg/iac/types"
67
)
78

89
func Adapt(modules terraform.Modules) securitycenter.SecurityCenter {
@@ -38,13 +39,24 @@ func adaptContact(resource *terraform.Block) securitycenter.Contact {
3839
enableAlertNotifAttr := resource.GetAttribute("alert_notifications")
3940
enableAlertNotifVal := enableAlertNotifAttr.AsBoolValueOrDefault(false, resource)
4041

42+
// TODO: add support for the new format https://github.com/hashicorp/terraform-provider-azurerm/issues/30797
43+
alertsToAdminsAttr := resource.GetAttribute("alerts_to_admins")
44+
alertsToAdminsVal := alertsToAdminsAttr.AsBoolValueOrDefault(false, resource)
45+
46+
emailAttr := resource.GetAttribute("email")
47+
emailVal := emailAttr.AsStringValueOrDefault("", resource)
48+
4149
phoneAttr := resource.GetAttribute("phone")
4250
phoneVal := phoneAttr.AsStringValueOrDefault("", resource)
4351

4452
return securitycenter.Contact{
4553
Metadata: resource.GetMetadata(),
4654
EnableAlertNotifications: enableAlertNotifVal,
55+
EnableAlertsToAdmins: alertsToAdminsVal,
56+
Email: emailVal,
4757
Phone: phoneVal,
58+
IsEnabled: iacTypes.BoolValue{}, // Not supported in Terraform provider yet
59+
MinimalSeverity: iacTypes.StringValue{}, // Not supported in Terraform provider yet
4860
}
4961
}
5062

pkg/iac/adapters/terraform/azure/securitycenter/adapt_test.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,20 @@ func Test_adaptContact(t *testing.T) {
2222
name: "defined",
2323
terraform: `
2424
resource "azurerm_security_center_contact" "example" {
25+
email = "contact@example.com"
2526
phone = "+1-555-555-5555"
2627
alert_notifications = true
28+
alerts_to_admins = true
2729
}
2830
`,
2931
expected: securitycenter.Contact{
3032
Metadata: iacTypes.NewTestMetadata(),
3133
EnableAlertNotifications: iacTypes.Bool(true, iacTypes.NewTestMetadata()),
34+
EnableAlertsToAdmins: iacTypes.Bool(true, iacTypes.NewTestMetadata()),
35+
Email: iacTypes.String("contact@example.com", iacTypes.NewTestMetadata()),
3236
Phone: iacTypes.String("+1-555-555-5555", iacTypes.NewTestMetadata()),
37+
IsEnabled: iacTypes.BoolValue{},
38+
MinimalSeverity: iacTypes.StringValue{},
3339
},
3440
},
3541
{
@@ -41,7 +47,11 @@ func Test_adaptContact(t *testing.T) {
4147
expected: securitycenter.Contact{
4248
Metadata: iacTypes.NewTestMetadata(),
4349
EnableAlertNotifications: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
50+
EnableAlertsToAdmins: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
51+
Email: iacTypes.String("", iacTypes.NewTestMetadata()),
4452
Phone: iacTypes.String("", iacTypes.NewTestMetadata()),
53+
IsEnabled: iacTypes.BoolValue{},
54+
MinimalSeverity: iacTypes.StringValue{},
4555
},
4656
},
4757
}
@@ -107,8 +117,10 @@ func Test_adaptSubscription(t *testing.T) {
107117
func TestLines(t *testing.T) {
108118
src := `
109119
resource "azurerm_security_center_contact" "example" {
120+
email = "contact@example.com"
110121
phone = "+1-555-555-5555"
111122
alert_notifications = true
123+
alerts_to_admins = true
112124
}
113125
114126
resource "azurerm_security_center_subscription_pricing" "example" {
@@ -124,12 +136,18 @@ func TestLines(t *testing.T) {
124136
contact := adapted.Contacts[0]
125137
sub := adapted.Subscriptions[0]
126138

127-
assert.Equal(t, 3, contact.Phone.GetMetadata().Range().GetStartLine())
128-
assert.Equal(t, 3, contact.Phone.GetMetadata().Range().GetEndLine())
139+
assert.Equal(t, 3, contact.Email.GetMetadata().Range().GetStartLine())
140+
assert.Equal(t, 3, contact.Email.GetMetadata().Range().GetEndLine())
129141

130-
assert.Equal(t, 4, contact.EnableAlertNotifications.GetMetadata().Range().GetStartLine())
131-
assert.Equal(t, 4, contact.EnableAlertNotifications.GetMetadata().Range().GetEndLine())
142+
assert.Equal(t, 4, contact.Phone.GetMetadata().Range().GetStartLine())
143+
assert.Equal(t, 4, contact.Phone.GetMetadata().Range().GetEndLine())
132144

133-
assert.Equal(t, 8, sub.Tier.GetMetadata().Range().GetStartLine())
134-
assert.Equal(t, 8, sub.Tier.GetMetadata().Range().GetEndLine())
145+
assert.Equal(t, 5, contact.EnableAlertNotifications.GetMetadata().Range().GetStartLine())
146+
assert.Equal(t, 5, contact.EnableAlertNotifications.GetMetadata().Range().GetEndLine())
147+
148+
assert.Equal(t, 6, contact.EnableAlertsToAdmins.GetMetadata().Range().GetStartLine())
149+
assert.Equal(t, 6, contact.EnableAlertsToAdmins.GetMetadata().Range().GetEndLine())
150+
151+
assert.Equal(t, 10, sub.Tier.GetMetadata().Range().GetStartLine())
152+
assert.Equal(t, 10, sub.Tier.GetMetadata().Range().GetEndLine())
135153
}

pkg/iac/providers/azure/securitycenter/securitycenter.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ type SecurityCenter struct {
1212
type Contact struct {
1313
Metadata iacTypes.Metadata
1414
EnableAlertNotifications iacTypes.BoolValue
15+
EnableAlertsToAdmins iacTypes.BoolValue
16+
Email iacTypes.StringValue
1517
Phone iacTypes.StringValue
18+
IsEnabled iacTypes.BoolValue
19+
MinimalSeverity iacTypes.StringValue
1620
}
1721

1822
const (

pkg/iac/rego/schemas/cloud.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5407,10 +5407,26 @@
54075407
"type": "object",
54085408
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata"
54095409
},
5410+
"email": {
5411+
"type": "object",
5412+
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue"
5413+
},
54105414
"enablealertnotifications": {
54115415
"type": "object",
54125416
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue"
54135417
},
5418+
"enablealertstoadmins": {
5419+
"type": "object",
5420+
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue"
5421+
},
5422+
"isenabled": {
5423+
"type": "object",
5424+
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue"
5425+
},
5426+
"minimalseverity": {
5427+
"type": "object",
5428+
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue"
5429+
},
54145430
"phone": {
54155431
"type": "object",
54165432
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue"

0 commit comments

Comments
 (0)