diff --git a/avd_docs/azure/appservice/AVD-AZU-0001/docs.md b/avd_docs/azure/appservice/AVD-AZU-0001/docs.md index 0cb11986..739a7e04 100644 --- a/avd_docs/azure/appservice/AVD-AZU-0001/docs.md +++ b/avd_docs/azure/appservice/AVD-AZU-0001/docs.md @@ -1,8 +1,9 @@ The TLS mutual authentication technique in enterprise environments ensures the authenticity of clients to the server. If incoming client certificates are enabled only an authenticated client with valid certificates can access the app. + ### Impact -Mutual TLS is not being used + {{ remediationActions }} diff --git a/avd_docs/azure/appservice/AVD-AZU-0002/docs.md b/avd_docs/azure/appservice/AVD-AZU-0002/docs.md index d01e5f75..bb5e7178 100644 --- a/avd_docs/azure/appservice/AVD-AZU-0002/docs.md +++ b/avd_docs/azure/appservice/AVD-AZU-0002/docs.md @@ -1,8 +1,9 @@ Registering the identity used by an App with AD allows it to interact with other services without using username and password + ### Impact -Interaction between services can't easily be achieved without username/password + {{ remediationActions }} diff --git a/avd_docs/azure/appservice/AVD-AZU-0003/docs.md b/avd_docs/azure/appservice/AVD-AZU-0003/docs.md index 3efdfc1e..15e68e03 100644 --- a/avd_docs/azure/appservice/AVD-AZU-0003/docs.md +++ b/avd_docs/azure/appservice/AVD-AZU-0003/docs.md @@ -1,8 +1,9 @@ Enabling authentication ensures that all communications in the application are authenticated. The auth_settings block needs to be filled out with the appropriate auth backend settings + ### Impact -Anonymous HTTP requests will be accepted + {{ remediationActions }} diff --git a/avd_docs/azure/appservice/AVD-AZU-0004/docs.md b/avd_docs/azure/appservice/AVD-AZU-0004/docs.md index 5d44abb4..e2c00bd5 100644 --- a/avd_docs/azure/appservice/AVD-AZU-0004/docs.md +++ b/avd_docs/azure/appservice/AVD-AZU-0004/docs.md @@ -1,8 +1,9 @@ By default, clients can connect to function endpoints by using both HTTP or HTTPS. You should redirect HTTP to HTTPs because HTTPS uses the SSL/TLS protocol to provide a secure connection, which is both encrypted and authenticated. + ### Impact -Anyone can access the Function App using HTTP. + {{ remediationActions }} diff --git a/avd_docs/azure/appservice/AVD-AZU-0005/docs.md b/avd_docs/azure/appservice/AVD-AZU-0005/docs.md index 2713cb2e..bb886370 100644 --- a/avd_docs/azure/appservice/AVD-AZU-0005/docs.md +++ b/avd_docs/azure/appservice/AVD-AZU-0005/docs.md @@ -1,8 +1,9 @@ Use the latest version of HTTP to ensure you are benefiting from security fixes + ### Impact -Outdated versions of HTTP has security vulnerabilities + {{ remediationActions }} diff --git a/avd_docs/azure/appservice/AVD-AZU-0006/docs.md b/avd_docs/azure/appservice/AVD-AZU-0006/docs.md index d2cc5385..4f9c364e 100644 --- a/avd_docs/azure/appservice/AVD-AZU-0006/docs.md +++ b/avd_docs/azure/appservice/AVD-AZU-0006/docs.md @@ -1,8 +1,9 @@ Use a more recent TLS/SSL policy for the App Service + ### Impact -The minimum TLS version for apps should be TLS1_2 + {{ remediationActions }} diff --git a/avd_docs/azure/authorization/AVD-AZU-0030/docs.md b/avd_docs/azure/authorization/AVD-AZU-0030/docs.md index cb06855e..75969765 100644 --- a/avd_docs/azure/authorization/AVD-AZU-0030/docs.md +++ b/avd_docs/azure/authorization/AVD-AZU-0030/docs.md @@ -1,8 +1,9 @@ The permissions granted to a role should be kept to the minimum required to be able to do the task. Wildcard permissions must not be used. + ### Impact -Open permissions for subscriptions could result in an easily compromisable account + {{ remediationActions }} diff --git a/avd_docs/azure/container/AVD-AZU-0040/docs.md b/avd_docs/azure/container/AVD-AZU-0040/docs.md index 8e6e32d3..dc75d62d 100644 --- a/avd_docs/azure/container/AVD-AZU-0040/docs.md +++ b/avd_docs/azure/container/AVD-AZU-0040/docs.md @@ -1,8 +1,9 @@ Ensure AKS logging to Azure Monitoring is configured for containers to monitor the performance of workloads. + ### Impact -Logging provides valuable information about access and usage + {{ remediationActions }} diff --git a/avd_docs/azure/container/AVD-AZU-0041/docs.md b/avd_docs/azure/container/AVD-AZU-0041/docs.md index 01da3e2a..b60225bb 100644 --- a/avd_docs/azure/container/AVD-AZU-0041/docs.md +++ b/avd_docs/azure/container/AVD-AZU-0041/docs.md @@ -1,8 +1,9 @@ The API server is the central way to interact with and manage a cluster. To improve cluster security and minimize attacks, the API server should only be accessible from a limited set of IP address ranges. + ### Impact -Any IP can interact with the API server + {{ remediationActions }} diff --git a/avd_docs/azure/container/AVD-AZU-0042/docs.md b/avd_docs/azure/container/AVD-AZU-0042/docs.md index 92d7cca0..0c4ec0e4 100644 --- a/avd_docs/azure/container/AVD-AZU-0042/docs.md +++ b/avd_docs/azure/container/AVD-AZU-0042/docs.md @@ -1,8 +1,9 @@ Using Kubernetes role-based access control (RBAC), you can grant users, groups, and service accounts access to only the resources they need. + ### Impact -No role based access control is in place for the AKS cluster + {{ remediationActions }} diff --git a/avd_docs/azure/container/AVD-AZU-0043/docs.md b/avd_docs/azure/container/AVD-AZU-0043/docs.md index 25936a7a..43804537 100644 --- a/avd_docs/azure/container/AVD-AZU-0043/docs.md +++ b/avd_docs/azure/container/AVD-AZU-0043/docs.md @@ -1,8 +1,9 @@ The Kubernetes object type NetworkPolicy should be defined to have opportunity allow or block traffic to pods, as in a Kubernetes cluster configured with default settings, all pods can discover and communicate with each other without any restrictions. + ### Impact -No network policy is protecting the AKS cluster + {{ remediationActions }} diff --git a/checks/cloud/azure/appservice/account_identity_registered.go b/checks/cloud/azure/appservice/account_identity_registered.go index 694b305e..8990278e 100755 --- a/checks/cloud/azure/appservice/account_identity_registered.go +++ b/checks/cloud/azure/appservice/account_identity_registered.go @@ -25,7 +25,8 @@ var CheckAccountIdentityRegistered = rules.Register( Links: terraformAccountIdentityRegisteredLinks, RemediationMarkdown: terraformAccountIdentityRegisteredRemediationMarkdown, }, - Severity: severity.Low, + Severity: severity.Low, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, service := range s.Azure.AppService.Services { diff --git a/checks/cloud/azure/appservice/account_identity_registered.rego b/checks/cloud/azure/appservice/account_identity_registered.rego new file mode 100644 index 00000000..9bcd9634 --- /dev/null +++ b/checks/cloud/azure/appservice/account_identity_registered.rego @@ -0,0 +1,41 @@ +# METADATA +# title: Web App has registration with AD enabled +# description: | +# Registering the identity used by an App with AD allows it to interact with other services without using username and password +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-AZU-0002 +# avd_id: AVD-AZU-0002 +# provider: azure +# service: appservice +# severity: LOW +# short_code: account-identity-registered +# recommended_action: Register the app identity with AD +# input: +# selector: +# - type: cloud +# subtypes: +# - service: appservice +# provider: azure +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service#identity +# good_examples: checks/cloud/azure/appservice/account_identity_registered.tf.go +# bad_examples: checks/cloud/azure/appservice/account_identity_registered.tf.go +package builtin.azure.appservice.azure0002 + +import rego.v1 + +deny contains res if { + some service in input.azure.appservice.services + isManaged(service) + not has_identity_type(service) + res := result.new( + "App service does not have an identity type.", + object.get(service, ["identity", "type"], service), + ) +} + +has_identity_type(service) := service.identity.type.value != "" diff --git a/checks/cloud/azure/appservice/account_identity_registered_test.go b/checks/cloud/azure/appservice/account_identity_registered_test.go deleted file mode 100644 index be2f316b..00000000 --- a/checks/cloud/azure/appservice/account_identity_registered_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package appservice - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/appservice" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckAccountIdentityRegistered(t *testing.T) { - tests := []struct { - name string - input appservice.AppService - expected bool - }{ - { - name: "App service identity not registered", - input: appservice.AppService{ - Services: []appservice.Service{ - { - Metadata: trivyTypes.NewTestMetadata(), - Identity: struct{ Type trivyTypes.StringValue }{ - Type: trivyTypes.String("", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "App service identity registered", - input: appservice.AppService{ - Services: []appservice.Service{ - { - Metadata: trivyTypes.NewTestMetadata(), - Identity: struct{ Type trivyTypes.StringValue }{ - Type: trivyTypes.String("UserAssigned", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Azure.AppService = test.input - results := CheckAccountIdentityRegistered.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckAccountIdentityRegistered.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/azure/appservice/account_identity_registered_test.rego b/checks/cloud/azure/appservice/account_identity_registered_test.rego new file mode 100644 index 00000000..c408ead1 --- /dev/null +++ b/checks/cloud/azure/appservice/account_identity_registered_test.rego @@ -0,0 +1,24 @@ +package builtin.azure.appservice.azure0002_test + +import rego.v1 + +import data.builtin.azure.appservice.azure0002 as check +import data.lib.test + +test_deny_identity_not_registerd if { + inp := {"azure": {"appservice": {"services": [{"identity": {"type": {"value": ""}}}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_identity_type_is_not_specified if { + inp := {"azure": {"appservice": {"services": [{"identity": {}}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_identity_registerd if { + inp := {"azure": {"appservice": {"services": [{"identity": {"type": {"value": "UserAssigned"}}}]}}} + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/azure/appservice/authentication_enabled.go b/checks/cloud/azure/appservice/authentication_enabled.go index f12c068e..d9f93fd0 100755 --- a/checks/cloud/azure/appservice/authentication_enabled.go +++ b/checks/cloud/azure/appservice/authentication_enabled.go @@ -25,7 +25,8 @@ var CheckAuthenticationEnabled = rules.Register( Links: terraformAuthenticationEnabledLinks, RemediationMarkdown: terraformAuthenticationEnabledRemediationMarkdown, }, - Severity: severity.Medium, + Severity: severity.Medium, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, service := range s.Azure.AppService.Services { diff --git a/checks/cloud/azure/appservice/authentication_enabled.rego b/checks/cloud/azure/appservice/authentication_enabled.rego new file mode 100644 index 00000000..7241a63d --- /dev/null +++ b/checks/cloud/azure/appservice/authentication_enabled.rego @@ -0,0 +1,39 @@ +# METADATA +# title: App Service authentication is activated +# description: | +# Enabling authentication ensures that all communications in the application are authenticated. The auth_settings block needs to be filled out with the appropriate auth backend settings +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-AZU-0003 +# avd_id: AVD-AZU-0003 +# provider: azure +# service: appservice +# severity: MEDIUM +# short_code: authentication-enabled +# recommended_action: Enable authentication to prevent anonymous request being accepted +# input: +# selector: +# - type: cloud +# subtypes: +# - service: appservice +# provider: azure +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service#enabled +# good_examples: checks/cloud/azure/appservice/authentication_enabled.tf.go +# bad_examples: checks/cloud/azure/appservice/authentication_enabled.tf.go +package builtin.azure.appservice.azure0003 + +import rego.v1 + +deny contains res if { + some service in input.azure.appservice.services + isManaged(service) + not service.authentication.enabled.value + res := result.new( + "App service does not have authentication enabled.", + object.get(service, ["authentication", "enabled"], service), + ) +} diff --git a/checks/cloud/azure/appservice/authentication_enabled_test.go b/checks/cloud/azure/appservice/authentication_enabled_test.go deleted file mode 100644 index 67d6abac..00000000 --- a/checks/cloud/azure/appservice/authentication_enabled_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package appservice - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/appservice" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckAuthenticationEnabled(t *testing.T) { - tests := []struct { - name string - input appservice.AppService - expected bool - }{ - { - name: "App service authentication disabled", - input: appservice.AppService{ - Services: []appservice.Service{ - { - Metadata: trivyTypes.NewTestMetadata(), - Authentication: struct{ Enabled trivyTypes.BoolValue }{ - Enabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "App service authentication enabled", - input: appservice.AppService{ - Services: []appservice.Service{ - { - Metadata: trivyTypes.NewTestMetadata(), - Authentication: struct{ Enabled trivyTypes.BoolValue }{ - Enabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Azure.AppService = test.input - results := CheckAuthenticationEnabled.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckAuthenticationEnabled.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/azure/appservice/authentication_enabled_test.rego b/checks/cloud/azure/appservice/authentication_enabled_test.rego new file mode 100644 index 00000000..b26f7a35 --- /dev/null +++ b/checks/cloud/azure/appservice/authentication_enabled_test.rego @@ -0,0 +1,24 @@ +package builtin.azure.appservice.azure0003_test + +import rego.v1 + +import data.builtin.azure.appservice.azure0003 as check +import data.lib.test + +test_deny_authentication_disabled if { + inp := {"azure": {"appservice": {"services": [{"authentication": {"enabled": {"value": false}}}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_authentication_is_not_specified if { + inp := {"azure": {"appservice": {"services": [{}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_authentication_enabled if { + inp := {"azure": {"appservice": {"services": [{"authentication": {"enabled": {"value": true}}}]}}} + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/azure/appservice/enable_http2.go b/checks/cloud/azure/appservice/enable_http2.go index 63fad13a..0dad9ab7 100755 --- a/checks/cloud/azure/appservice/enable_http2.go +++ b/checks/cloud/azure/appservice/enable_http2.go @@ -25,7 +25,8 @@ var CheckEnableHttp2 = rules.Register( Links: terraformEnableHttp2Links, RemediationMarkdown: terraformEnableHttp2RemediationMarkdown, }, - Severity: severity.Low, + Severity: severity.Low, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, service := range s.Azure.AppService.Services { diff --git a/checks/cloud/azure/appservice/enable_http2.rego b/checks/cloud/azure/appservice/enable_http2.rego new file mode 100644 index 00000000..ee0c91c8 --- /dev/null +++ b/checks/cloud/azure/appservice/enable_http2.rego @@ -0,0 +1,39 @@ +# METADATA +# title: Web App uses the latest HTTP version +# description: | +# Use the latest version of HTTP to ensure you are benefiting from security fixes +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-AZU-0005 +# avd_id: AVD-AZU-0005 +# provider: azure +# service: appservice +# severity: LOW +# short_code: enable-http2 +# recommended_action: Use the latest version of HTTP +# input: +# selector: +# - type: cloud +# subtypes: +# - service: appservice +# provider: azure +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service#http2_enabled +# good_examples: checks/cloud/azure/appservice/enable_http2.tf.go +# bad_examples: checks/cloud/azure/appservice/enable_http2.tf.go +package builtin.azure.appservice.azure0005 + +import rego.v1 + +deny contains res if { + some service in input.azure.appservice.services + isManaged(service) + not service.site.enablehttp2.value + res := result.new( + "App service does not have HTTP/2 enabled.", + object.get(service, ["site", "enablehttp2"], service), + ) +} diff --git a/checks/cloud/azure/appservice/enable_http2_test.go b/checks/cloud/azure/appservice/enable_http2_test.go deleted file mode 100644 index da78ab2b..00000000 --- a/checks/cloud/azure/appservice/enable_http2_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package appservice - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/appservice" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnableHttp2(t *testing.T) { - tests := []struct { - name string - input appservice.AppService - expected bool - }{ - { - name: "HTTP2 disabled", - input: appservice.AppService{ - Services: []appservice.Service{ - { - Metadata: trivyTypes.NewTestMetadata(), - Site: struct { - EnableHTTP2 trivyTypes.BoolValue - MinimumTLSVersion trivyTypes.StringValue - }{ - EnableHTTP2: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "HTTP2 enabled", - input: appservice.AppService{ - Services: []appservice.Service{ - { - Metadata: trivyTypes.NewTestMetadata(), - Site: struct { - EnableHTTP2 trivyTypes.BoolValue - MinimumTLSVersion trivyTypes.StringValue - }{ - EnableHTTP2: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Azure.AppService = test.input - results := CheckEnableHttp2.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnableHttp2.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/azure/appservice/enable_http2_test.rego b/checks/cloud/azure/appservice/enable_http2_test.rego new file mode 100644 index 00000000..2885a41e --- /dev/null +++ b/checks/cloud/azure/appservice/enable_http2_test.rego @@ -0,0 +1,18 @@ +package builtin.azure.appservice.azure0005_test + +import rego.v1 + +import data.builtin.azure.appservice.azure0005 as check +import data.lib.test + +test_deny_http2_disabled if { + inp := {"azure": {"appservice": {"services": [{"site": {"enablehttp2": {"value": false}}}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_http2_enabled if { + inp := {"azure": {"appservice": {"services": [{"site": {"enablehttp2": {"value": true}}}]}}} + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/azure/appservice/enforce_https.go b/checks/cloud/azure/appservice/enforce_https.go index fa375a4f..c7bf10be 100755 --- a/checks/cloud/azure/appservice/enforce_https.go +++ b/checks/cloud/azure/appservice/enforce_https.go @@ -28,7 +28,8 @@ var CheckEnforceHttps = rules.Register( Links: terraformEnforceHttpsLinks, RemediationMarkdown: terraformEnforceHttpsRemediationMarkdown, }, - Severity: severity.Critical, + Severity: severity.Critical, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, functionApp := range s.Azure.AppService.FunctionApps { diff --git a/checks/cloud/azure/appservice/enforce_https.rego b/checks/cloud/azure/appservice/enforce_https.rego new file mode 100644 index 00000000..55c4da05 --- /dev/null +++ b/checks/cloud/azure/appservice/enforce_https.rego @@ -0,0 +1,42 @@ +# METADATA +# title: Ensure the Function App can only be accessed via HTTPS. The default is false. +# description: | +# By default, clients can connect to function endpoints by using both HTTP or HTTPS. You should redirect HTTP to HTTPs because HTTPS uses the SSL/TLS protocol to provide a secure connection, which is both encrypted and authenticated. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.microsoft.com/en-us/azure/app-service/configure-ssl-bindings#enforce-https +# - https://docs.microsoft.com/en-us/azure/azure-functions/security-concepts +# custom: +# id: AVD-AZU-0004 +# avd_id: AVD-AZU-0004 +# provider: azure +# service: appservice +# severity: CRITICAL +# short_code: enforce-https +# recommended_action: You can redirect all HTTP requests to the HTTPS port. +# input: +# selector: +# - type: cloud +# subtypes: +# - service: appservice +# provider: azure +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/function_app#https_only +# good_examples: checks/cloud/azure/appservice/enforce_https.tf.go +# bad_examples: checks/cloud/azure/appservice/enforce_https.tf.go +package builtin.azure.appservice.azure0004 + +import rego.v1 + +deny contains res if { + some app in input.azure.appservice.functionapps + isManaged(app) + not app.httpsonly.value + res := result.new( + "Function app does not have HTTPS enforced.", + object.get(app, "httpsonly", app), + ) +} diff --git a/checks/cloud/azure/appservice/enforce_https_test.go b/checks/cloud/azure/appservice/enforce_https_test.go deleted file mode 100644 index ae2194c1..00000000 --- a/checks/cloud/azure/appservice/enforce_https_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package appservice - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/appservice" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnforceHttps(t *testing.T) { - tests := []struct { - name string - input appservice.AppService - expected bool - }{ - { - name: "Function app doesn't enforce HTTPS", - input: appservice.AppService{ - FunctionApps: []appservice.FunctionApp{ - { - Metadata: trivyTypes.NewTestMetadata(), - HTTPSOnly: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: true, - }, - { - name: "Function app enforces HTTPS", - input: appservice.AppService{ - FunctionApps: []appservice.FunctionApp{ - { - Metadata: trivyTypes.NewTestMetadata(), - HTTPSOnly: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Azure.AppService = test.input - results := CheckEnforceHttps.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnforceHttps.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/azure/appservice/enforce_https_test.rego b/checks/cloud/azure/appservice/enforce_https_test.rego new file mode 100644 index 00000000..be2124a1 --- /dev/null +++ b/checks/cloud/azure/appservice/enforce_https_test.rego @@ -0,0 +1,24 @@ +package builtin.azure.appservice.azure0004_test + +import rego.v1 + +import data.builtin.azure.appservice.azure0004 as check +import data.lib.test + +test_deny_app_does_not_enforce_https if { + inp := {"azure": {"appservice": {"functionapps": [{"httpsonly": {"value": false}}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_httpsonly_is_not_specified if { + inp := {"azure": {"appservice": {"functionapps": [{}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_app_enforces_https if { + inp := {"azure": {"appservice": {"functionapps": [{"httpsonly": {"value": true}}]}}} + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/azure/appservice/require_client_cert.go b/checks/cloud/azure/appservice/require_client_cert.go index bf4a548e..2ea41f02 100755 --- a/checks/cloud/azure/appservice/require_client_cert.go +++ b/checks/cloud/azure/appservice/require_client_cert.go @@ -25,7 +25,8 @@ var CheckRequireClientCert = rules.Register( Links: terraformRequireClientCertLinks, RemediationMarkdown: terraformRequireClientCertRemediationMarkdown, }, - Severity: severity.Low, + Severity: severity.Low, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, service := range s.Azure.AppService.Services { diff --git a/checks/cloud/azure/appservice/require_client_cert.rego b/checks/cloud/azure/appservice/require_client_cert.rego new file mode 100644 index 00000000..8e4a72eb --- /dev/null +++ b/checks/cloud/azure/appservice/require_client_cert.rego @@ -0,0 +1,39 @@ +# METADATA +# title: Web App accepts incoming client certificate +# description: | +# The TLS mutual authentication technique in enterprise environments ensures the authenticity of clients to the server. If incoming client certificates are enabled only an authenticated client with valid certificates can access the app. +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-AZU-0001 +# avd_id: AVD-AZU-0001 +# provider: azure +# service: appservice +# severity: LOW +# short_code: require-client-cert +# recommended_action: Enable incoming certificates for clients +# input: +# selector: +# - type: cloud +# subtypes: +# - service: appservice +# provider: azure +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service#client_cert_enabled +# good_examples: checks/cloud/azure/appservice/require_client_cert.tf.go +# bad_examples: checks/cloud/azure/appservice/require_client_cert.tf.go +package builtin.azure.appservice.azure0001 + +import rego.v1 + +deny contains res if { + some service in input.azure.appservice.services + isManaged(service) + not service.enableclientcert.value + res := result.new( + "App service does not have client certificates enabled.", + object.get(service, "enableclientcert", service), + ) +} diff --git a/checks/cloud/azure/appservice/require_client_cert_test.go b/checks/cloud/azure/appservice/require_client_cert_test.go deleted file mode 100644 index 8734f50b..00000000 --- a/checks/cloud/azure/appservice/require_client_cert_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package appservice - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/appservice" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckRequireClientCert(t *testing.T) { - tests := []struct { - name string - input appservice.AppService - expected bool - }{ - { - name: "App service client certificate disabled", - input: appservice.AppService{ - Services: []appservice.Service{ - { - Metadata: trivyTypes.NewTestMetadata(), - EnableClientCert: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: true, - }, - { - name: "App service client certificate enabled", - input: appservice.AppService{ - Services: []appservice.Service{ - { - Metadata: trivyTypes.NewTestMetadata(), - EnableClientCert: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Azure.AppService = test.input - results := CheckRequireClientCert.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckRequireClientCert.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/azure/appservice/require_client_cert_test.rego b/checks/cloud/azure/appservice/require_client_cert_test.rego new file mode 100644 index 00000000..fb349783 --- /dev/null +++ b/checks/cloud/azure/appservice/require_client_cert_test.rego @@ -0,0 +1,24 @@ +package builtin.azure.appservice.azure0001_test + +import rego.v1 + +import data.builtin.azure.appservice.azure0001 as check +import data.lib.test + +test_deny_service_client_cert_disabled if { + inp := {"azure": {"appservice": {"services": [{"enableclientcert": {"value": false}}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_service_client_cert_not_specified if { + inp := {"azure": {"appservice": {"services": [{}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_service_client_cert_enabled if { + inp := {"azure": {"appservice": {"services": [{"enableclientcert": {"value": true}}]}}} + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/azure/appservice/use_secure_tls_policy.go b/checks/cloud/azure/appservice/use_secure_tls_policy.go index 0756a9a8..c9d9b117 100755 --- a/checks/cloud/azure/appservice/use_secure_tls_policy.go +++ b/checks/cloud/azure/appservice/use_secure_tls_policy.go @@ -25,7 +25,8 @@ var CheckUseSecureTlsPolicy = rules.Register( Links: terraformUseSecureTlsPolicyLinks, RemediationMarkdown: terraformUseSecureTlsPolicyRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, service := range s.Azure.AppService.Services { diff --git a/checks/cloud/azure/appservice/use_secure_tls_policy.rego b/checks/cloud/azure/appservice/use_secure_tls_policy.rego new file mode 100644 index 00000000..f76469ad --- /dev/null +++ b/checks/cloud/azure/appservice/use_secure_tls_policy.rego @@ -0,0 +1,43 @@ +# METADATA +# title: Web App uses latest TLS version +# description: | +# Use a more recent TLS/SSL policy for the App Service +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-AZU-0006 +# avd_id: AVD-AZU-0006 +# provider: azure +# service: appservice +# severity: HIGH +# short_code: use-secure-tls-policy +# recommended_action: The TLS version being outdated and has known vulnerabilities +# input: +# selector: +# - type: cloud +# subtypes: +# - service: appservice +# provider: azure +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service#min_tls_version +# good_examples: checks/cloud/azure/appservice/use_secure_tls_policy.tf.go +# bad_examples: checks/cloud/azure/appservice/use_secure_tls_policy.tf.go +package builtin.azure.appservice.azure0006 + +import rego.v1 + +recommended_tls_version := "1.2" + +deny contains res if { + some service in input.azure.appservice.services + isManaged(service) + not is_recommended_tls_version(service) + res := result.new( + "App service does not require a secure TLS version.", + object.get(service, ["site", "minimumtlsversion"], service), + ) +} + +is_recommended_tls_version(service) := service.site.minimumtlsversion.value == recommended_tls_version diff --git a/checks/cloud/azure/appservice/use_secure_tls_policy_test.go b/checks/cloud/azure/appservice/use_secure_tls_policy_test.go deleted file mode 100644 index 75a1b2ed..00000000 --- a/checks/cloud/azure/appservice/use_secure_tls_policy_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package appservice - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/appservice" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckUseSecureTlsPolicy(t *testing.T) { - tests := []struct { - name string - input appservice.AppService - expected bool - }{ - { - name: "Minimum TLS version TLS1_0", - input: appservice.AppService{ - Services: []appservice.Service{ - { - Metadata: trivyTypes.NewTestMetadata(), - Site: struct { - EnableHTTP2 trivyTypes.BoolValue - MinimumTLSVersion trivyTypes.StringValue - }{ - EnableHTTP2: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - MinimumTLSVersion: trivyTypes.String("1.0", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "Minimum TLS version TLS1_2", - input: appservice.AppService{ - Services: []appservice.Service{ - { - Metadata: trivyTypes.NewTestMetadata(), - Site: struct { - EnableHTTP2 trivyTypes.BoolValue - MinimumTLSVersion trivyTypes.StringValue - }{ - EnableHTTP2: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - MinimumTLSVersion: trivyTypes.String("1.2", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Azure.AppService = test.input - results := CheckUseSecureTlsPolicy.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckUseSecureTlsPolicy.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/azure/appservice/use_secure_tls_policy_test.rego b/checks/cloud/azure/appservice/use_secure_tls_policy_test.rego new file mode 100644 index 00000000..3ff1592f --- /dev/null +++ b/checks/cloud/azure/appservice/use_secure_tls_policy_test.rego @@ -0,0 +1,24 @@ +package builtin.azure.appservice.azure0006_test + +import rego.v1 + +import data.builtin.azure.appservice.azure0006 as check +import data.lib.test + +test_deny_minimum_tls_version_is_tls1_0 if { + inp := {"azure": {"appservice": {"services": [{"site": {"minimumtlsversion": {"value": "1.0"}}}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_minimum_tls_version_not_specified if { + inp := {"azure": {"appservice": {"services": [{}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_minimum_tls_version_is_tls1_2 if { + inp := {"azure": {"appservice": {"services": [{"site": {"minimumtlsversion": {"value": check.recommended_tls_version}}}]}}} + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/azure/authorization/limit_role_actions.go b/checks/cloud/azure/authorization/limit_role_actions.go index 24195987..74bc7462 100755 --- a/checks/cloud/azure/authorization/limit_role_actions.go +++ b/checks/cloud/azure/authorization/limit_role_actions.go @@ -25,7 +25,8 @@ var CheckLimitRoleActions = rules.Register( Links: terraformLimitRoleActionsLinks, RemediationMarkdown: terraformLimitRoleActionsRemediationMarkdown, }, - Severity: severity.Medium, + Severity: severity.Medium, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, roleDef := range s.Azure.Authorization.RoleDefinitions { diff --git a/checks/cloud/azure/authorization/limit_role_actions.rego b/checks/cloud/azure/authorization/limit_role_actions.rego new file mode 100644 index 00000000..07913ef2 --- /dev/null +++ b/checks/cloud/azure/authorization/limit_role_actions.rego @@ -0,0 +1,41 @@ +# METADATA +# title: Roles limited to the required actions +# description: | +# The permissions granted to a role should be kept to the minimum required to be able to do the task. Wildcard permissions must not be used. +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-AZU-0030 +# avd_id: AVD-AZU-0030 +# provider: azure +# service: authorization +# severity: MEDIUM +# short_code: limit-role-actions +# recommended_action: Use targeted permissions for roles +# input: +# selector: +# - type: cloud +# subtypes: +# - service: authorization +# provider: azure +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_definition#actions +# good_examples: checks/cloud/azure/authorization/limit_role_actions.tf.go +# bad_examples: checks/cloud/azure/authorization/limit_role_actions.tf.go +package builtin.azure.authorization.azure0030 + +import rego.v1 + +deny contains res if { + some roledef in input.azure.authorization.roledefinitions + some action in roledef.permissions[_].actions + contains(action.value, "*") + some scope in roledef.assignablescopes + scope.value == "/" + res := result.new( + "Role definition uses wildcard action with all scopes.", + action, + ) +} diff --git a/checks/cloud/azure/authorization/limit_role_actions_test.rego b/checks/cloud/azure/authorization/limit_role_actions_test.rego new file mode 100644 index 00000000..b4b7c292 --- /dev/null +++ b/checks/cloud/azure/authorization/limit_role_actions_test.rego @@ -0,0 +1,35 @@ +package builtin.azure.authorization.azure0030_test + +import rego.v1 + +import data.builtin.azure.authorization.azure0030 as check +import data.lib.test + +test_deny_wildcard_action_with_all_scopes if { + inp := build_input({ + "permissions": [{"actions": [{"value": "*"}]}], + "assignablescopes": [{"value": "/"}], + }) + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_wildcard_action_with_specific_scope if { + inp := build_input({ + "permissions": [{"actions": [{"value": "*"}]}], + "assignablescopes": [{"value": "/subscriptions/0b1f6471-1bf0-4dda-aec3-111122223333"}], + }) + res := check.deny with input as inp + res == set() +} + +test_allow_non_wildcard_action_with_all_scopes if { + inp := build_input({ + "permissions": [{"actions": [{"value": "Microsoft.Resources/subscriptions/resourceGroups/read"}]}], + "assignablescopes": [{"value": "/"}], + }) + res := check.deny with input as inp + res == set() +} + +build_input(roledef) := {"azure": {"authorization": {"roledefinitions": [roledef]}}} diff --git a/checks/cloud/azure/container/configured_network_policy.go b/checks/cloud/azure/container/configured_network_policy.go index 6858a8ee..f108f668 100755 --- a/checks/cloud/azure/container/configured_network_policy.go +++ b/checks/cloud/azure/container/configured_network_policy.go @@ -27,7 +27,8 @@ var CheckConfiguredNetworkPolicy = rules.Register( Links: terraformConfiguredNetworkPolicyLinks, RemediationMarkdown: terraformConfiguredNetworkPolicyRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, cluster := range s.Azure.Container.KubernetesClusters { diff --git a/checks/cloud/azure/container/configured_network_policy.rego b/checks/cloud/azure/container/configured_network_policy.rego new file mode 100644 index 00000000..a96386bb --- /dev/null +++ b/checks/cloud/azure/container/configured_network_policy.rego @@ -0,0 +1,42 @@ +# METADATA +# title: Ensure AKS cluster has Network Policy configured +# description: | +# The Kubernetes object type NetworkPolicy should be defined to have opportunity allow or block traffic to pods, as in a Kubernetes cluster configured with default settings, all pods can discover and communicate with each other without any restrictions. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://kubernetes.io/docs/concepts/services-networking/network-policies +# custom: +# id: AVD-AZU-0043 +# avd_id: AVD-AZU-0043 +# provider: azure +# service: container +# severity: HIGH +# short_code: configured-network-policy +# recommended_action: Configure a network policy +# input: +# selector: +# - type: cloud +# subtypes: +# - service: container +# provider: azure +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster#network_policy +# good_examples: checks/cloud/azure/container/configured_network_policy.tf.go +# bad_examples: checks/cloud/azure/container/configured_network_policy.tf.go +package builtin.azure.container.azure0043 + +import rego.v1 + +deny contains res if { + some cluster in input.azure.container.kubernetesclusters + not has_network_policy(cluster) + res := result.new( + "Kubernetes cluster does not have a network policy set.", + object.get(cluster, ["networkprofile", "networkpolicy"], cluster), + ) +} + +has_network_policy(cluster) := cluster.networkprofile.networkpolicy.value != "" diff --git a/checks/cloud/azure/container/configured_network_policy_test.go b/checks/cloud/azure/container/configured_network_policy_test.go deleted file mode 100644 index f68953dd..00000000 --- a/checks/cloud/azure/container/configured_network_policy_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package container - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/container" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckConfiguredNetworkPolicy(t *testing.T) { - tests := []struct { - name string - input container.Container - expected bool - }{ - { - name: "Cluster missing network policy configuration", - input: container.Container{ - KubernetesClusters: []container.KubernetesCluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - NetworkProfile: container.NetworkProfile{ - Metadata: trivyTypes.NewTestMetadata(), - NetworkPolicy: trivyTypes.String("", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "Cluster with network policy configured", - input: container.Container{ - KubernetesClusters: []container.KubernetesCluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - NetworkProfile: container.NetworkProfile{ - Metadata: trivyTypes.NewTestMetadata(), - NetworkPolicy: trivyTypes.String("calico", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Azure.Container = test.input - results := CheckConfiguredNetworkPolicy.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckConfiguredNetworkPolicy.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/azure/container/configured_network_policy_test.rego b/checks/cloud/azure/container/configured_network_policy_test.rego new file mode 100644 index 00000000..ccf5d2b6 --- /dev/null +++ b/checks/cloud/azure/container/configured_network_policy_test.rego @@ -0,0 +1,24 @@ +package builtin.azure.container.azure0043_test + +import rego.v1 + +import data.builtin.azure.container.azure0043 as check +import data.lib.test + +test_deny_cluster_without_network_policy if { + inp := {"azure": {"container": {"kubernetesclusters": [{"networkprofile": {"networkpolicy": {"value": ""}}}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_cluster_with_network_policy_is_not_specified if { + inp := {"azure": {"container": {"kubernetesclusters": [{}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_cluster_with_network_policy if { + inp := {"azure": {"container": {"kubernetesclusters": [{"networkprofile": {"networkpolicy": {"value": "calico"}}}]}}} + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/azure/container/limit_authorized_ips.go b/checks/cloud/azure/container/limit_authorized_ips.go index 58d74922..b9540c2f 100755 --- a/checks/cloud/azure/container/limit_authorized_ips.go +++ b/checks/cloud/azure/container/limit_authorized_ips.go @@ -27,7 +27,8 @@ var CheckLimitAuthorizedIps = rules.Register( Links: terraformLimitAuthorizedIpsLinks, RemediationMarkdown: terraformLimitAuthorizedIpsRemediationMarkdown, }, - Severity: severity.Critical, + Severity: severity.Critical, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, cluster := range s.Azure.Container.KubernetesClusters { diff --git a/checks/cloud/azure/container/limit_authorized_ips.rego b/checks/cloud/azure/container/limit_authorized_ips.rego new file mode 100644 index 00000000..23602b58 --- /dev/null +++ b/checks/cloud/azure/container/limit_authorized_ips.rego @@ -0,0 +1,46 @@ +# METADATA +# title: Ensure AKS has an API Server Authorized IP Ranges enabled +# description: | +# The API server is the central way to interact with and manage a cluster. To improve cluster security and minimize attacks, the API server should only be accessible from a limited set of IP address ranges. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.microsoft.com/en-us/azure/aks/api-server-authorized-ip-ranges +# custom: +# id: AVD-AZU-0041 +# avd_id: AVD-AZU-0041 +# provider: azure +# service: container +# severity: CRITICAL +# short_code: limit-authorized-ips +# recommended_action: Limit the access to the API server to a limited IP range +# input: +# selector: +# - type: cloud +# subtypes: +# - service: container +# provider: azure +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster#authorized_ip_ranges +# good_examples: checks/cloud/azure/container/limit_authorized_ips.tf.go +# bad_examples: checks/cloud/azure/container/limit_authorized_ips.tf.go +package builtin.azure.container.azure0041 + +import rego.v1 + +deny contains res if { + some cluster in input.azure.container.kubernetesclusters + isManaged(cluster) + not is_private_cluster(cluster) + not is_limit_ip_ranges(cluster) + res := result.new( + "Cluster does not limit API access to specific IP addresses.", + object.get(cluster, "apiserverauthorizedipranges", cluster), + ) +} + +is_limit_ip_ranges(cluster) := count(cluster.apiserverauthorizedipranges) > 0 + +is_private_cluster(cluster) := cluster.enableprivatecluster.value diff --git a/checks/cloud/azure/container/limit_authorized_ips_test.go b/checks/cloud/azure/container/limit_authorized_ips_test.go deleted file mode 100644 index 8c9dfebf..00000000 --- a/checks/cloud/azure/container/limit_authorized_ips_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package container - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/container" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckLimitAuthorizedIps(t *testing.T) { - tests := []struct { - name string - input container.Container - expected bool - }{ - { - name: "API server authorized IP ranges undefined", - input: container.Container{ - KubernetesClusters: []container.KubernetesCluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - EnablePrivateCluster: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - APIServerAuthorizedIPRanges: []trivyTypes.StringValue{}, - }, - }, - }, - expected: true, - }, - { - name: "API server authorized IP ranges defined", - input: container.Container{ - KubernetesClusters: []container.KubernetesCluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - EnablePrivateCluster: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - APIServerAuthorizedIPRanges: []trivyTypes.StringValue{ - trivyTypes.String("1.2.3.4/32", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Azure.Container = test.input - results := CheckLimitAuthorizedIps.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckLimitAuthorizedIps.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/azure/container/limit_authorized_ips_test.rego b/checks/cloud/azure/container/limit_authorized_ips_test.rego new file mode 100644 index 00000000..9a85dde3 --- /dev/null +++ b/checks/cloud/azure/container/limit_authorized_ips_test.rego @@ -0,0 +1,24 @@ +package builtin.azure.container.azure0041_test + +import rego.v1 + +import data.builtin.azure.container.azure0041 as check +import data.lib.test + +test_deny_authorized_ip_ranges_undefined if { + inp := {"azure": {"container": {"kubernetesclusters": [{}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_authorized_ip_ranges_defined if { + inp := {"azure": {"container": {"kubernetesclusters": [{"apiserverauthorizedipranges": [{"value": "1.2.3.4/32"}]}]}}} + res := check.deny with input as inp + res == set() +} + +test_allow_authorized_ip_ranges_undefined_for_private_cluster if { + inp := {"azure": {"container": {"kubernetesclusters": [{"enableprivatecluster": {"value": true}}]}}} + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/azure/container/logging.go b/checks/cloud/azure/container/logging.go index 6f54a9de..e1115055 100755 --- a/checks/cloud/azure/container/logging.go +++ b/checks/cloud/azure/container/logging.go @@ -27,7 +27,8 @@ var CheckLogging = rules.Register( Links: terraformLoggingLinks, RemediationMarkdown: terraformLoggingRemediationMarkdown, }, - Severity: severity.Medium, + Severity: severity.Medium, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, cluster := range s.Azure.Container.KubernetesClusters { diff --git a/checks/cloud/azure/container/logging.rego b/checks/cloud/azure/container/logging.rego new file mode 100644 index 00000000..3d99a6be --- /dev/null +++ b/checks/cloud/azure/container/logging.rego @@ -0,0 +1,41 @@ +# METADATA +# title: Ensure AKS logging to Azure Monitoring is Configured +# description: | +# Ensure AKS logging to Azure Monitoring is configured for containers to monitor the performance of workloads. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.microsoft.com/en-us/azure/azure-monitor/insights/container-insights-onboard +# custom: +# id: AVD-AZU-0040 +# avd_id: AVD-AZU-0040 +# provider: azure +# service: container +# severity: MEDIUM +# short_code: logging +# recommended_action: Enable logging for AKS +# input: +# selector: +# - type: cloud +# subtypes: +# - service: container +# provider: azure +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster#oms_agent +# good_examples: checks/cloud/azure/container/logging.tf.go +# bad_examples: checks/cloud/azure/container/logging.tf.go +package builtin.azure.container.azure0040 + +import rego.v1 + +deny contains res if { + some cluster in input.azure.container.kubernetesclusters + isManaged(cluster) + not cluster.addonprofile.omsagent.enabled.value + res := result.new( + "Cluster does not have logging enabled via OMS Agent.", + object.get(cluster, ["addonprofile", "omsagent", "enabled"], cluster), + ) +} diff --git a/checks/cloud/azure/container/logging_test.go b/checks/cloud/azure/container/logging_test.go deleted file mode 100644 index e1a17ccf..00000000 --- a/checks/cloud/azure/container/logging_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package container - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/container" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckLogging(t *testing.T) { - tests := []struct { - name string - input container.Container - expected bool - }{ - { - name: "Logging via OMS agent disabled", - input: container.Container{ - KubernetesClusters: []container.KubernetesCluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - AddonProfile: container.AddonProfile{ - Metadata: trivyTypes.NewTestMetadata(), - OMSAgent: container.OMSAgent{ - Metadata: trivyTypes.NewTestMetadata(), - Enabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "Logging via OMS agent enabled", - input: container.Container{ - KubernetesClusters: []container.KubernetesCluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - AddonProfile: container.AddonProfile{ - Metadata: trivyTypes.NewTestMetadata(), - OMSAgent: container.OMSAgent{ - Metadata: trivyTypes.NewTestMetadata(), - Enabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Azure.Container = test.input - results := CheckLogging.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckLogging.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/azure/container/logging_test.rego b/checks/cloud/azure/container/logging_test.rego new file mode 100644 index 00000000..9c2f8acd --- /dev/null +++ b/checks/cloud/azure/container/logging_test.rego @@ -0,0 +1,24 @@ +package builtin.azure.container.azure0040_test + +import rego.v1 + +import data.builtin.azure.container.azure0040 as check +import data.lib.test + +test_deny_logging_via_oms_agent_disabled if { + inp := {"azure": {"container": {"kubernetesclusters": [{"addonprofile": {"omsagent": {"enabled": {"value": false}}}}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_logging_via_oms_agent_is_not_specified if { + inp := {"azure": {"container": {"kubernetesclusters": [{}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_logging_via_oms_agent_enabled if { + inp := {"azure": {"container": {"kubernetesclusters": [{"addonprofile": {"omsagent": {"enabled": {"value": true}}}}]}}} + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/azure/container/use_rbac_permissions.go b/checks/cloud/azure/container/use_rbac_permissions.go index c6bec07d..7e429fd4 100755 --- a/checks/cloud/azure/container/use_rbac_permissions.go +++ b/checks/cloud/azure/container/use_rbac_permissions.go @@ -27,7 +27,8 @@ var CheckUseRbacPermissions = rules.Register( Links: terraformUseRbacPermissionsLinks, RemediationMarkdown: terraformUseRbacPermissionsRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, cluster := range s.Azure.Container.KubernetesClusters { diff --git a/checks/cloud/azure/container/use_rbac_permissions.rego b/checks/cloud/azure/container/use_rbac_permissions.rego new file mode 100644 index 00000000..0e7b4dbd --- /dev/null +++ b/checks/cloud/azure/container/use_rbac_permissions.rego @@ -0,0 +1,41 @@ +# METADATA +# title: Ensure RBAC is enabled on AKS clusters +# description: | +# Using Kubernetes role-based access control (RBAC), you can grant users, groups, and service accounts access to only the resources they need. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.microsoft.com/en-us/azure/aks/concepts-identity +# custom: +# id: AVD-AZU-0042 +# avd_id: AVD-AZU-0042 +# provider: azure +# service: container +# severity: HIGH +# short_code: use-rbac-permissions +# recommended_action: Enable RBAC +# input: +# selector: +# - type: cloud +# subtypes: +# - service: container +# provider: azure +# terraform: +# links: +# - https://www.terraform.io/docs/providers/azurerm/r/kubernetes_cluster.html#role_based_access_control +# good_examples: checks/cloud/azure/container/use_rbac_permissions.tf.go +# bad_examples: checks/cloud/azure/container/use_rbac_permissions.tf.go +package builtin.azure.container.azure0042 + +import rego.v1 + +deny contains res if { + some cluster in input.azure.container.kubernetesclusters + isManaged(cluster) + not cluster.rolebasedaccesscontrol.enabled.value + res := result.new( + "RBAC is not enabled on cluster", + object.get(cluster, ["rolebasedaccesscontrol", "enabled"], cluster), + ) +} diff --git a/checks/cloud/azure/container/use_rbac_permissions_test.go b/checks/cloud/azure/container/use_rbac_permissions_test.go deleted file mode 100644 index 166c9517..00000000 --- a/checks/cloud/azure/container/use_rbac_permissions_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package container - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/azure/container" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckUseRbacPermissions(t *testing.T) { - tests := []struct { - name string - input container.Container - expected bool - }{ - { - name: "Role based access control disabled", - input: container.Container{ - KubernetesClusters: []container.KubernetesCluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - RoleBasedAccessControl: container.RoleBasedAccessControl{ - Metadata: trivyTypes.NewTestMetadata(), - Enabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "Role based access control enabled", - input: container.Container{ - KubernetesClusters: []container.KubernetesCluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - RoleBasedAccessControl: container.RoleBasedAccessControl{ - Metadata: trivyTypes.NewTestMetadata(), - Enabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Azure.Container = test.input - results := CheckUseRbacPermissions.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckUseRbacPermissions.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/azure/container/use_rbac_permissions_test.rego b/checks/cloud/azure/container/use_rbac_permissions_test.rego new file mode 100644 index 00000000..6f1a3277 --- /dev/null +++ b/checks/cloud/azure/container/use_rbac_permissions_test.rego @@ -0,0 +1,24 @@ +package builtin.azure.container.azure0042_test + +import rego.v1 + +import data.builtin.azure.container.azure0042 as check +import data.lib.test + +test_deny_rbac_disabled if { + inp := {"azure": {"container": {"kubernetesclusters": [{"rolebasedaccesscontrol": {"enabled": {"value": false}}}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_rbac_is_not_specified if { + inp := {"azure": {"container": {"kubernetesclusters": [{}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_rbac_enabled if { + inp := {"azure": {"container": {"kubernetesclusters": [{"rolebasedaccesscontrol": {"enabled": {"value": true}}}]}}} + res := check.deny with input as inp + res == set() +} diff --git a/checks/docker/missing_apk_no_cache.rego b/checks/docker/missing_apk_no_cache.rego index 0f3841b2..3d994dfe 100644 --- a/checks/docker/missing_apk_no_cache.rego +++ b/checks/docker/missing_apk_no_cache.rego @@ -36,7 +36,6 @@ get_apk[output] { deny[res] { output := get_apk[_] msg := sprintf("'--no-cache' is missed: %s", [output.arg]) - print(msg) res := result.new(msg, output.cmd) } diff --git a/test/rego/azure_appservice_test.go b/test/rego/azure_appservice_test.go new file mode 100644 index 00000000..f423b0f3 --- /dev/null +++ b/test/rego/azure_appservice_test.go @@ -0,0 +1,197 @@ +package test + +import ( + "github.com/aquasecurity/trivy/pkg/iac/providers/azure" + "github.com/aquasecurity/trivy/pkg/iac/providers/azure/appservice" + "github.com/aquasecurity/trivy/pkg/iac/state" + trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" +) + +var azureAppServiceTestCases = testCases{ + "AVD-AZU-0002": { + { + name: "App service identity not registered", + input: state.State{Azure: azure.Azure{AppService: appservice.AppService{ + Services: []appservice.Service{ + { + Metadata: trivyTypes.NewTestMetadata(), + Identity: struct{ Type trivyTypes.StringValue }{ + Type: trivyTypes.String("", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "App service identity registered", + input: state.State{Azure: azure.Azure{AppService: appservice.AppService{ + Services: []appservice.Service{ + { + Metadata: trivyTypes.NewTestMetadata(), + Identity: struct{ Type trivyTypes.StringValue }{ + Type: trivyTypes.String("UserAssigned", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AZU-0003": { + { + name: "App service authentication disabled", + input: state.State{Azure: azure.Azure{AppService: appservice.AppService{ + Services: []appservice.Service{ + { + Metadata: trivyTypes.NewTestMetadata(), + Authentication: struct{ Enabled trivyTypes.BoolValue }{ + Enabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "App service authentication enabled", + input: state.State{Azure: azure.Azure{AppService: appservice.AppService{ + Services: []appservice.Service{ + { + Metadata: trivyTypes.NewTestMetadata(), + Authentication: struct{ Enabled trivyTypes.BoolValue }{ + Enabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AZU-0005": { + { + name: "HTTP2 disabled", + input: state.State{Azure: azure.Azure{AppService: appservice.AppService{ + Services: []appservice.Service{ + { + Metadata: trivyTypes.NewTestMetadata(), + Site: struct { + EnableHTTP2 trivyTypes.BoolValue + MinimumTLSVersion trivyTypes.StringValue + }{ + EnableHTTP2: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "HTTP2 enabled", + input: state.State{Azure: azure.Azure{AppService: appservice.AppService{ + Services: []appservice.Service{ + { + Metadata: trivyTypes.NewTestMetadata(), + Site: struct { + EnableHTTP2 trivyTypes.BoolValue + MinimumTLSVersion trivyTypes.StringValue + }{ + EnableHTTP2: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AZU-0004": { + { + name: "Function app doesn't enforce HTTPS", + input: state.State{Azure: azure.Azure{AppService: appservice.AppService{ + FunctionApps: []appservice.FunctionApp{ + { + Metadata: trivyTypes.NewTestMetadata(), + HTTPSOnly: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: true, + }, + { + name: "Function app enforces HTTPS", + input: state.State{Azure: azure.Azure{AppService: appservice.AppService{ + FunctionApps: []appservice.FunctionApp{ + { + Metadata: trivyTypes.NewTestMetadata(), + HTTPSOnly: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AZU-0001": { + { + name: "App service client certificate disabled", + input: state.State{Azure: azure.Azure{AppService: appservice.AppService{ + Services: []appservice.Service{ + { + Metadata: trivyTypes.NewTestMetadata(), + EnableClientCert: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: true, + }, + { + name: "App service client certificate enabled", + input: state.State{Azure: azure.Azure{AppService: appservice.AppService{ + Services: []appservice.Service{ + { + Metadata: trivyTypes.NewTestMetadata(), + EnableClientCert: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AZU-0006": { + { + name: "Minimum TLS version TLS1_0", + input: state.State{Azure: azure.Azure{AppService: appservice.AppService{ + Services: []appservice.Service{ + { + Metadata: trivyTypes.NewTestMetadata(), + Site: struct { + EnableHTTP2 trivyTypes.BoolValue + MinimumTLSVersion trivyTypes.StringValue + }{ + EnableHTTP2: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + MinimumTLSVersion: trivyTypes.String("1.0", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Minimum TLS version TLS1_2", + input: state.State{Azure: azure.Azure{AppService: appservice.AppService{ + Services: []appservice.Service{ + { + Metadata: trivyTypes.NewTestMetadata(), + Site: struct { + EnableHTTP2 trivyTypes.BoolValue + MinimumTLSVersion trivyTypes.StringValue + }{ + EnableHTTP2: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + MinimumTLSVersion: trivyTypes.String("1.2", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, +} diff --git a/checks/cloud/azure/authorization/limit_role_actions_test.go b/test/rego/azure_authorization_test.go similarity index 58% rename from checks/cloud/azure/authorization/limit_role_actions_test.go rename to test/rego/azure_authorization_test.go index 15f93b88..ba0a3cda 100644 --- a/checks/cloud/azure/authorization/limit_role_actions_test.go +++ b/test/rego/azure_authorization_test.go @@ -1,27 +1,17 @@ -package authorization +package test import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - + "github.com/aquasecurity/trivy/pkg/iac/providers/azure" "github.com/aquasecurity/trivy/pkg/iac/providers/azure/authorization" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" + "github.com/aquasecurity/trivy/pkg/iac/state" + trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) -func TestCheckLimitRoleActions(t *testing.T) { - tests := []struct { - name string - input authorization.Authorization - expected bool - }{ +var azureAuthorizationTestCases = testCases{ + "AVD-AZU-0030": { { name: "Wildcard action with all scopes", - input: authorization.Authorization{ + input: state.State{Azure: azure.Azure{Authorization: authorization.Authorization{ RoleDefinitions: []authorization.RoleDefinition{ { Metadata: trivyTypes.NewTestMetadata(), @@ -38,12 +28,12 @@ func TestCheckLimitRoleActions(t *testing.T) { }, }, }, - }, + }}}, expected: true, }, { name: "Wildcard action with specific scope", - input: authorization.Authorization{ + input: state.State{Azure: azure.Azure{Authorization: authorization.Authorization{ RoleDefinitions: []authorization.RoleDefinition{ { Metadata: trivyTypes.NewTestMetadata(), @@ -60,26 +50,8 @@ func TestCheckLimitRoleActions(t *testing.T) { }, }, }, - }, + }}}, expected: false, }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Azure.Authorization = test.input - results := CheckLimitRoleActions.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckLimitRoleActions.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } + }, } diff --git a/test/rego/azure_container_test.go b/test/rego/azure_container_test.go new file mode 100644 index 00000000..8714be20 --- /dev/null +++ b/test/rego/azure_container_test.go @@ -0,0 +1,143 @@ +package test + +import ( + "github.com/aquasecurity/trivy/pkg/iac/providers/azure" + "github.com/aquasecurity/trivy/pkg/iac/providers/azure/container" + "github.com/aquasecurity/trivy/pkg/iac/state" + trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" +) + +var azureContainerTestCases = testCases{ + "AVD-AZU-0043": { + { + name: "Cluster missing network policy configuration", + input: state.State{Azure: azure.Azure{Container: container.Container{ + KubernetesClusters: []container.KubernetesCluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + NetworkProfile: container.NetworkProfile{ + Metadata: trivyTypes.NewTestMetadata(), + NetworkPolicy: trivyTypes.String("", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Cluster with network policy configured", + input: state.State{Azure: azure.Azure{Container: container.Container{ + KubernetesClusters: []container.KubernetesCluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + NetworkProfile: container.NetworkProfile{ + Metadata: trivyTypes.NewTestMetadata(), + NetworkPolicy: trivyTypes.String("calico", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AZU-0041": { + { + name: "API server authorized IP ranges undefined", + input: state.State{Azure: azure.Azure{Container: container.Container{ + KubernetesClusters: []container.KubernetesCluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + EnablePrivateCluster: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + APIServerAuthorizedIPRanges: []trivyTypes.StringValue{}, + }, + }, + }}}, + expected: true, + }, + { + name: "API server authorized IP ranges defined", + input: state.State{Azure: azure.Azure{Container: container.Container{ + KubernetesClusters: []container.KubernetesCluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + EnablePrivateCluster: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + APIServerAuthorizedIPRanges: []trivyTypes.StringValue{ + trivyTypes.String("1.2.3.4/32", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AZU-0040": { + { + name: "Logging via OMS agent disabled", + input: state.State{Azure: azure.Azure{Container: container.Container{ + KubernetesClusters: []container.KubernetesCluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + AddonProfile: container.AddonProfile{ + Metadata: trivyTypes.NewTestMetadata(), + OMSAgent: container.OMSAgent{ + Metadata: trivyTypes.NewTestMetadata(), + Enabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Logging via OMS agent enabled", + input: state.State{Azure: azure.Azure{Container: container.Container{ + KubernetesClusters: []container.KubernetesCluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + AddonProfile: container.AddonProfile{ + Metadata: trivyTypes.NewTestMetadata(), + OMSAgent: container.OMSAgent{ + Metadata: trivyTypes.NewTestMetadata(), + Enabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AZU-0042": { + { + name: "Role based access control disabled", + input: state.State{Azure: azure.Azure{Container: container.Container{ + KubernetesClusters: []container.KubernetesCluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + RoleBasedAccessControl: container.RoleBasedAccessControl{ + Metadata: trivyTypes.NewTestMetadata(), + Enabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Role based access control enabled", + input: state.State{Azure: azure.Azure{Container: container.Container{ + KubernetesClusters: []container.KubernetesCluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + RoleBasedAccessControl: container.RoleBasedAccessControl{ + Metadata: trivyTypes.NewTestMetadata(), + Enabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, +} diff --git a/test/rego/rego_checks_test.go b/test/rego/rego_checks_test.go index c6111b82..94f03cee 100644 --- a/test/rego/rego_checks_test.go +++ b/test/rego/rego_checks_test.go @@ -54,6 +54,10 @@ func TestRegoChecks(t *testing.T) { awsDocumentDBTestCases, awsDynamodbTestCases, + azureAppServiceTestCases, + azureAuthorizationTestCases, + azureContainerTestCases, + googleDnsTestCases, googleKmsTestCases, googleBigQueryTestCases,