From ea00dae73e4a4e4fdbb754a323e1cf4922c851f1 Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Mon, 16 Jun 2025 12:53:35 +0100 Subject: [PATCH 1/4] feat(queries): Add Grafana queries --- .../GrafanaExternalSnapshotsEnabled.md | 69 ++++++++++++ .../GrafanaExternalSnapshotsEnabled.ql | 31 ++++++ .../GrafanaExcessiveEditorPermissions.md | 74 +++++++++++++ .../GrafanaExcessiveEditorPermissions.ql | 31 ++++++ .../GrafanaExcessiveViewerPermissions.md | 74 +++++++++++++ .../GrafanaExcessiveViewerPermissions.ql | 31 ++++++ .../GrafanaSmtpSslVerificationDisabled.md | 92 ++++++++++++++++ .../GrafanaSmtpSslVerificationDisabled.ql | 33 ++++++ .../security/CWE-306/GrafanaApiKeyEnabled.md | 69 ++++++++++++ .../security/CWE-306/GrafanaApiKeyEnabled.ql | 27 +++++ .../CWE-319/GrafanaInsecureStartTLSPolicy.md | 91 ++++++++++++++++ .../CWE-319/GrafanaInsecureStartTLSPolicy.ql | 38 +++++++ .../security/CWE-352/GrafanaCsrfDisabled.md | 70 ++++++++++++ .../security/CWE-352/GrafanaCsrfDisabled.ql | 31 ++++++ .../CWE-798/HardcodedSmtpCredentials.md | 103 ++++++++++++++++++ .../CWE-798/HardcodedSmtpCredentials.ql | 35 ++++++ .../GrafanaMissingZoneRedundancy.md | 63 +++++++++++ .../GrafanaMissingZoneRedundancy.ql | 29 +++++ 18 files changed, 991 insertions(+) create mode 100644 ql/src/security/CWE-200/GrafanaExternalSnapshotsEnabled.md create mode 100644 ql/src/security/CWE-200/GrafanaExternalSnapshotsEnabled.ql create mode 100644 ql/src/security/CWE-272/GrafanaExcessiveEditorPermissions.md create mode 100644 ql/src/security/CWE-272/GrafanaExcessiveEditorPermissions.ql create mode 100644 ql/src/security/CWE-272/GrafanaExcessiveViewerPermissions.md create mode 100644 ql/src/security/CWE-272/GrafanaExcessiveViewerPermissions.ql create mode 100644 ql/src/security/CWE-295/GrafanaSmtpSslVerificationDisabled.md create mode 100644 ql/src/security/CWE-295/GrafanaSmtpSslVerificationDisabled.ql create mode 100644 ql/src/security/CWE-306/GrafanaApiKeyEnabled.md create mode 100644 ql/src/security/CWE-306/GrafanaApiKeyEnabled.ql create mode 100644 ql/src/security/CWE-319/GrafanaInsecureStartTLSPolicy.md create mode 100644 ql/src/security/CWE-319/GrafanaInsecureStartTLSPolicy.ql create mode 100644 ql/src/security/CWE-352/GrafanaCsrfDisabled.md create mode 100644 ql/src/security/CWE-352/GrafanaCsrfDisabled.ql create mode 100644 ql/src/security/CWE-798/HardcodedSmtpCredentials.md create mode 100644 ql/src/security/CWE-798/HardcodedSmtpCredentials.ql create mode 100644 ql/src/security/Dashboards/GrafanaMissingZoneRedundancy.md create mode 100644 ql/src/security/Dashboards/GrafanaMissingZoneRedundancy.ql diff --git a/ql/src/security/CWE-200/GrafanaExternalSnapshotsEnabled.md b/ql/src/security/CWE-200/GrafanaExternalSnapshotsEnabled.md new file mode 100644 index 0000000..59264ac --- /dev/null +++ b/ql/src/security/CWE-200/GrafanaExternalSnapshotsEnabled.md @@ -0,0 +1,69 @@ +# External snapshots enabled in Grafana + +This query identifies Microsoft.Dashboard/grafana resources that have external snapshots enabled, which could potentially lead to data leakage. + +## Description + +Grafana allows users to create and share snapshots of dashboards. When the `externalEnabled` property in the snapshots configuration is set to `true`, users can publish these snapshots to an external, public snapshot server. This means that dashboard data, which may include sensitive metrics or information, could be shared outside of your organization. + +External snapshots are stored on a public server provided by Grafana Labs, and anyone with the link can view the snapshot. This creates a risk of sensitive data exposure if users inadvertently share snapshots containing confidential information. + +## Recommendation + +Unless external snapshots are specifically required for your use case, disable external snapshots by setting the `externalEnabled` property to `false`. This ensures that snapshots can only be shared internally within your Grafana instance, reducing the risk of accidental data leakage. + +## Example of vulnerable code + +```bicep +resource vulnerableGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-external-snapshots' + location: 'eastus' + properties: { + grafanaConfigurations: { + snapshots: { + externalEnabled: true // Vulnerable: External snapshots are enabled + } + } + } + sku: { + name: 'Standard' + } +} +``` + +## Example of secure code + +```bicep +resource secureGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-internal-snapshots' + location: 'eastus' + properties: { + grafanaConfigurations: { + snapshots: { + externalEnabled: false // Secure: External snapshots are disabled + } + } + } + sku: { + name: 'Standard' + } +} + +// Alternative: omit the snapshots configuration block entirely to use default settings +resource secureGrafanaAlt 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-default-snapshots' + location: 'eastus' + properties: { + // No explicit snapshots configuration, using defaults + } + sku: { + name: 'Standard' + } +} +``` + +## References + +* [Grafana snapshot documentation](https://grafana.com/docs/grafana/latest/dashboards/share-dashboards-panels/#publish-a-snapshot) +* [Azure Managed Grafana documentation](https://learn.microsoft.com/en-us/azure/managed-grafana/) +* [CWE-200: Exposure of Sensitive Information to an Unauthorized Actor](https://cwe.mitre.org/data/definitions/200.html) diff --git a/ql/src/security/CWE-200/GrafanaExternalSnapshotsEnabled.ql b/ql/src/security/CWE-200/GrafanaExternalSnapshotsEnabled.ql new file mode 100644 index 0000000..a7dd677 --- /dev/null +++ b/ql/src/security/CWE-200/GrafanaExternalSnapshotsEnabled.ql @@ -0,0 +1,31 @@ +/** + * @name External snapshots enabled in Grafana + * @description External snapshots in Grafana allow sharing dashboard data with external services, + * which could potentially lead to data leakage. + * @kind problem + * @problem.severity warning + * @security-severity 4.0 + * @precision high + * @id bicep/grafana-external-snapshots-enabled + * @tags security + * bicep + * azure + * CWE-200 + */ + +import bicep +import codeql.bicep.frameworks.Microsoft.Dashboards + +from Dashboards::GrafanaResource grafana, + Dashboards::GrafanaProperties::Properties props, + Dashboards::GrafanaProperties::GrafanaConfigurations configs, + Dashboards::GrafanaProperties::Snapshots snapshots +where + props = grafana.getProperties() and + configs = props.getGrafanaConfigurations() and + snapshots = configs.getSnapshots() and + snapshots.hasExternalEnabled() and + snapshots.externalEnabled() = true +select snapshots, + "External snapshots are enabled in Grafana configuration, which could lead to " + + "unintended sharing of dashboard data with external services." diff --git a/ql/src/security/CWE-272/GrafanaExcessiveEditorPermissions.md b/ql/src/security/CWE-272/GrafanaExcessiveEditorPermissions.md new file mode 100644 index 0000000..bbc221f --- /dev/null +++ b/ql/src/security/CWE-272/GrafanaExcessiveEditorPermissions.md @@ -0,0 +1,74 @@ +# Excessive permissions for Grafana editors + +This query identifies Microsoft.Dashboard/grafana resources that grant administrative capabilities to editor users, which reduces the effectiveness of access control and can lead to privilege escalation. + +## Description + +Azure Managed Grafana supports different user roles with varying levels of permissions. The `editorsCanAdmin` property in the users configuration determines whether users with the editor role can administrate dashboards, folders, and teams they create. When set to `true`, editors gain administrative capabilities that go beyond their standard role, potentially violating the principle of least privilege. + +This configuration can lead to unintended privilege escalation, where editors gain more control over the Grafana instance than intended. It could result in unauthorized access to sensitive data, changes to important dashboards, or modifications to team permissions. + +## Recommendation + +Follow the principle of least privilege by setting the `editorsCanAdmin` property to `false` or omitting it (the default is `false`). If certain users need administrative capabilities, consider granting them the admin role instead of elevating all editors' permissions. + +## Example of vulnerable code + +```bicep +resource vulnerableGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-excessive-editor-perms' + location: 'eastus' + properties: { + grafanaConfigurations: { + users: { + editorsCanAdmin: true // Vulnerable: Editors have admin capabilities + } + } + } + sku: { + name: 'Standard' + } +} +``` + +## Example of secure code + +```bicep +resource secureGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-proper-editor-perms' + location: 'eastus' + properties: { + grafanaConfigurations: { + users: { + editorsCanAdmin: false // Secure: Editors do not have admin capabilities + } + } + } + sku: { + name: 'Standard' + } +} + +// Alternative: omit the editorsCanAdmin property to use default (false) +resource secureGrafanaAlt 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-default-editor-perms' + location: 'eastus' + properties: { + grafanaConfigurations: { + users: { + // editorsCanAdmin property omitted (defaults to false) + } + } + } + sku: { + name: 'Standard' + } +} +``` + +## References + +* [Grafana user permissions documentation](https://grafana.com/docs/grafana/latest/administration/user-management/user-roles/) +* [Azure Managed Grafana documentation](https://learn.microsoft.com/en-us/azure/managed-grafana/) +* [CWE-272: Least Privilege Violation](https://cwe.mitre.org/data/definitions/272.html) +* [Principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege) diff --git a/ql/src/security/CWE-272/GrafanaExcessiveEditorPermissions.ql b/ql/src/security/CWE-272/GrafanaExcessiveEditorPermissions.ql new file mode 100644 index 0000000..e630e90 --- /dev/null +++ b/ql/src/security/CWE-272/GrafanaExcessiveEditorPermissions.ql @@ -0,0 +1,31 @@ +/** + * @name Excessive permissions for Grafana editors + * @description Granting admin permissions to editors reduces the effectiveness of access control + * and can lead to privilege escalation. + * @kind problem + * @problem.severity warning + * @security-severity 5.0 + * @precision high + * @id bicep/grafana-excessive-editor-permissions + * @tags security + * bicep + * azure + * CWE-272 + */ + +import bicep +import codeql.bicep.frameworks.Microsoft.Dashboards + +from Dashboards::GrafanaResource grafana, + Dashboards::GrafanaProperties::Properties props, + Dashboards::GrafanaProperties::GrafanaConfigurations configs, + Dashboards::GrafanaProperties::Users users +where + props = grafana.getProperties() and + configs = props.getGrafanaConfigurations() and + users = configs.getUsers() and + users.hasEditorsCanAdmin() and + users.editorsCanAdmin() = true +select users, + "Excessive permissions granted to Grafana editors (editorsCanAdmin=true). " + + "This allows editors to administrate dashboards, folders and teams they create." diff --git a/ql/src/security/CWE-272/GrafanaExcessiveViewerPermissions.md b/ql/src/security/CWE-272/GrafanaExcessiveViewerPermissions.md new file mode 100644 index 0000000..c3cd443 --- /dev/null +++ b/ql/src/security/CWE-272/GrafanaExcessiveViewerPermissions.md @@ -0,0 +1,74 @@ +# Excessive permissions for Grafana viewers + +This query identifies Microsoft.Dashboard/grafana resources that grant edit capabilities to viewer users, which reduces the effectiveness of access control and can lead to unauthorized changes to dashboards. + +## Description + +Azure Managed Grafana supports different user roles with varying levels of permissions. The `viewersCanEdit` property in the users configuration determines whether users with the viewer role can make temporary edits to dashboards they have access to. When set to `true`, viewers gain more capabilities than they typically should have based on the principle of least privilege. + +While these edits are temporary and cannot be saved permanently, it still represents a weakening of the role-based access control model and could lead to confusion, accidental changes, or potential misuse of the dashboard data. + +## Recommendation + +Follow the principle of least privilege by setting the `viewersCanEdit` property to `false` or omitting it (the default is `false`). If certain users need to make edits to dashboards, consider granting them the editor role instead of giving all viewers edit capabilities. + +## Example of vulnerable code + +```bicep +resource vulnerableGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-excessive-viewer-perms' + location: 'eastus' + properties: { + grafanaConfigurations: { + users: { + viewersCanEdit: true // Vulnerable: Viewers can edit dashboards + } + } + } + sku: { + name: 'Standard' + } +} +``` + +## Example of secure code + +```bicep +resource secureGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-proper-viewer-perms' + location: 'eastus' + properties: { + grafanaConfigurations: { + users: { + viewersCanEdit: false // Secure: Viewers cannot edit dashboards + } + } + } + sku: { + name: 'Standard' + } +} + +// Alternative: omit the viewersCanEdit property to use default (false) +resource secureGrafanaAlt 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-default-viewer-perms' + location: 'eastus' + properties: { + grafanaConfigurations: { + users: { + // viewersCanEdit property omitted (defaults to false) + } + } + } + sku: { + name: 'Standard' + } +} +``` + +## References + +* [Grafana user permissions documentation](https://grafana.com/docs/grafana/latest/administration/user-management/user-roles/) +* [Azure Managed Grafana documentation](https://learn.microsoft.com/en-us/azure/managed-grafana/) +* [CWE-272: Least Privilege Violation](https://cwe.mitre.org/data/definitions/272.html) +* [Principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege) diff --git a/ql/src/security/CWE-272/GrafanaExcessiveViewerPermissions.ql b/ql/src/security/CWE-272/GrafanaExcessiveViewerPermissions.ql new file mode 100644 index 0000000..95d5034 --- /dev/null +++ b/ql/src/security/CWE-272/GrafanaExcessiveViewerPermissions.ql @@ -0,0 +1,31 @@ +/** + * @name Excessive permissions for Grafana viewers + * @description Granting edit permissions to viewers reduces the effectiveness of access control + * and can lead to unauthorized changes to dashboards. + * @kind problem + * @problem.severity warning + * @security-severity 4.0 + * @precision high + * @id bicep/grafana-excessive-viewer-permissions + * @tags security + * bicep + * azure + * CWE-272 + */ + +import bicep +import codeql.bicep.frameworks.Microsoft.Dashboards + +from Dashboards::GrafanaResource grafana, + Dashboards::GrafanaProperties::Properties props, + Dashboards::GrafanaProperties::GrafanaConfigurations configs, + Dashboards::GrafanaProperties::Users users +where + props = grafana.getProperties() and + configs = props.getGrafanaConfigurations() and + users = configs.getUsers() and + users.hasViewersCanEdit() and + users.viewersCanEdit() = true +select users, + "Excessive permissions granted to Grafana viewers (viewersCanEdit=true). " + + "This allows viewers to make temporary edits to dashboards they have access to." diff --git a/ql/src/security/CWE-295/GrafanaSmtpSslVerificationDisabled.md b/ql/src/security/CWE-295/GrafanaSmtpSslVerificationDisabled.md new file mode 100644 index 0000000..c030de8 --- /dev/null +++ b/ql/src/security/CWE-295/GrafanaSmtpSslVerificationDisabled.md @@ -0,0 +1,92 @@ +# Grafana SMTP SSL verification disabled + +This query identifies Microsoft.Dashboard/grafana resources that have SSL verification disabled in their SMTP configuration, which can lead to man-in-the-middle attacks and compromise of email communications. + +## Description + +Azure Managed Grafana can be configured to send email notifications using SMTP. The `skipVerify` property in the SMTP configuration controls whether the SSL/TLS certificate of the SMTP server is verified. When set to `true`, the certificate validation is disabled, which means the Grafana instance will not verify the identity of the SMTP server. This creates a security vulnerability where an attacker could potentially intercept email communications through a man-in-the-middle attack. + +## Recommendation + +Always set the `skipVerify` property to `false` (or omit it, as `false` is the default) in your Grafana SMTP configuration. This ensures that the SSL/TLS certificate of the SMTP server is properly validated, protecting against man-in-the-middle attacks. + +## Example of vulnerable code + +```bicep +resource vulnerableGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-insecure-smtp' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: 'password123' + fromAddress: 'grafana@example.com' + fromName: 'Grafana Alerts' + skipVerify: true // Vulnerable: SSL verification is disabled + startTLSPolicy: 'MandatoryStartTLS' + } + } + } + sku: { + name: 'Standard' + } +} +``` + +## Example of secure code + +```bicep +resource secureGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-secure-smtp' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: 'password123' + fromAddress: 'grafana@example.com' + fromName: 'Grafana Alerts' + skipVerify: false // Secure: SSL verification is enabled + startTLSPolicy: 'MandatoryStartTLS' + } + } + } + sku: { + name: 'Standard' + } +} + +// Alternative: omit skipVerify as false is the default +resource secureGrafanaAlt 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-secure-smtp-alt' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: 'password123' + fromAddress: 'grafana@example.com' + fromName: 'Grafana Alerts' + // skipVerify property is omitted (defaults to false) + startTLSPolicy: 'MandatoryStartTLS' + } + } + } + sku: { + name: 'Standard' + } +} +``` + +## References + +* [Grafana SMTP configuration documentation](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#smtp) +* [Azure Managed Grafana documentation](https://learn.microsoft.com/en-us/azure/managed-grafana/) +* [CWE-295: Improper Certificate Validation](https://cwe.mitre.org/data/definitions/295.html) diff --git a/ql/src/security/CWE-295/GrafanaSmtpSslVerificationDisabled.ql b/ql/src/security/CWE-295/GrafanaSmtpSslVerificationDisabled.ql new file mode 100644 index 0000000..75974c2 --- /dev/null +++ b/ql/src/security/CWE-295/GrafanaSmtpSslVerificationDisabled.ql @@ -0,0 +1,33 @@ +/** + * @name Grafana SMTP SSL verification disabled + * @description Disabling SSL verification in Grafana SMTP configuration can lead to man-in-the-middle + * attacks and compromise of email communications. + * @kind problem + * @problem.severity warning + * @security-severity 5.5 + * @precision high + * @id bicep/grafana-smtp-ssl-verification-disabled + * @tags security + * bicep + * azure + * CWE-295 + */ + +import bicep +import codeql.bicep.frameworks.Microsoft.Dashboards + +from Dashboards::GrafanaResource grafana, + Dashboards::GrafanaProperties::Properties props, + Dashboards::GrafanaProperties::GrafanaConfigurations configs, + Dashboards::GrafanaProperties::Smtp smtp +where + props = grafana.getProperties() and + configs = props.getGrafanaConfigurations() and + smtp = configs.getSmtp() and + smtp.hasEnabled() and + smtp.enabled() = true and + smtp.hasSkipVerify() and + smtp.skipVerify() = true +select smtp.getSkipVerify(), + "Grafana SMTP configuration has SSL verification disabled (skipVerify=true), " + + "which can lead to man-in-the-middle attacks." diff --git a/ql/src/security/CWE-306/GrafanaApiKeyEnabled.md b/ql/src/security/CWE-306/GrafanaApiKeyEnabled.md new file mode 100644 index 0000000..553ffa6 --- /dev/null +++ b/ql/src/security/CWE-306/GrafanaApiKeyEnabled.md @@ -0,0 +1,69 @@ +# Grafana API key enabled + +This query identifies Microsoft.Dashboard/grafana resources that have the API key feature enabled, which can increase the attack surface and lead to unauthorized access if keys are not properly managed. + +## Description + +Azure Managed Grafana allows users to create API keys that can be used to authenticate API requests. When the `apiKey` property is set to `'Enabled'`, users can generate these keys through the Grafana UI. + +While API keys can be useful for automation and integration scenarios, they also increase the security risk. API keys can have broad permissions and, if compromised, could allow attackers to access or modify dashboards, query data sources, or perform administrative actions. Additionally, API keys often don't have the same level of auditing and monitoring as user accounts. + +## Recommendation + +Unless API keys are specifically required for your use case, disable them by setting the `apiKey` property to `'Disabled'`. If you do need to use API keys: + +1. Grant the minimum required permissions to each key +2. Regularly rotate keys +3. Use shorter expiration times +4. Implement monitoring for API key usage + +Consider using service principals or OAuth2 for service-to-service authentication instead of API keys when possible. + +## Example of vulnerable code + +```bicep +resource vulnerableGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-api-keys-enabled' + location: 'eastus' + properties: { + apiKey: 'Enabled' // Vulnerable: API keys are enabled + } + sku: { + name: 'Standard' + } +} +``` + +## Example of secure code + +```bicep +resource secureGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-api-keys-disabled' + location: 'eastus' + properties: { + apiKey: 'Disabled' // Secure: API keys are disabled + } + sku: { + name: 'Standard' + } +} + +// Alternative: omit the apiKey property if it defaults to 'Disabled' +resource secureGrafanaAlt 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-default-api-keys' + location: 'eastus' + properties: { + // apiKey property omitted + } + sku: { + name: 'Standard' + } +} +``` + +## References + +* [Grafana API keys documentation](https://grafana.com/docs/grafana/latest/administration/api-keys/) +* [Azure Managed Grafana documentation](https://learn.microsoft.com/en-us/azure/managed-grafana/) +* [CWE-306: Missing Authentication for Critical Function](https://cwe.mitre.org/data/definitions/306.html) +* [OWASP API Security Top 10: Broken Authentication](https://owasp.org/www-project-api-security/) diff --git a/ql/src/security/CWE-306/GrafanaApiKeyEnabled.ql b/ql/src/security/CWE-306/GrafanaApiKeyEnabled.ql new file mode 100644 index 0000000..3748d7a --- /dev/null +++ b/ql/src/security/CWE-306/GrafanaApiKeyEnabled.ql @@ -0,0 +1,27 @@ +/** + * @name Grafana API key enabled + * @description Enabling API keys in Grafana can increase the attack surface and lead to + * unauthorized access if keys are not properly managed. + * @kind problem + * @problem.severity warning + * @security-severity 4.5 + * @precision high + * @id bicep/grafana-api-key-enabled + * @tags security + * bicep + * azure + * CWE-306 + */ + +import bicep +import codeql.bicep.frameworks.Microsoft.Dashboards + +from Dashboards::GrafanaResource grafana, + Dashboards::GrafanaProperties::Properties props +where + props = grafana.getProperties() and + props.hasApiKey() and + props.apiKey() = "Enabled" +select props.getApiKey(), + "Grafana API key feature is enabled, which can increase the attack surface. " + + "Consider disabling API keys if not strictly necessary." diff --git a/ql/src/security/CWE-319/GrafanaInsecureStartTLSPolicy.md b/ql/src/security/CWE-319/GrafanaInsecureStartTLSPolicy.md new file mode 100644 index 0000000..f30b5e3 --- /dev/null +++ b/ql/src/security/CWE-319/GrafanaInsecureStartTLSPolicy.md @@ -0,0 +1,91 @@ +# Insecure StartTLS policy in Grafana SMTP configuration + +This query identifies Microsoft.Dashboard/grafana resources that use insecure StartTLS policies (`'NoStartTLS'` or `'OpportunisticStartTLS'`) in their SMTP configuration, which can lead to email communications being sent unencrypted. + +## Description + +Azure Managed Grafana can be configured to send email notifications using SMTP. The `startTLSPolicy` property in the SMTP configuration determines how the TLS encryption is handled when communicating with the SMTP server. There are three possible values: + +- `'MandatoryStartTLS'`: Always uses TLS encryption; fails if the server doesn't support TLS +- `'OpportunisticStartTLS'`: Uses TLS encryption if the server supports it, falls back to unencrypted if not +- `'NoStartTLS'`: Never uses TLS encryption, always communicates in plain text + +When set to `'NoStartTLS'` or `'OpportunisticStartTLS'`, there's a risk that email communications, which may contain sensitive information such as alert details, dashboard links, or system information, could be transmitted in plain text. This makes them susceptible to interception and eavesdropping. + +## Recommendation + +Always use the most secure StartTLS policy, `'MandatoryStartTLS'`, to ensure that all SMTP communications are encrypted. This ensures that if the SMTP server doesn't support TLS, the connection will fail rather than falling back to an insecure connection. + +If you encounter issues with an SMTP server that doesn't support TLS, consider switching to a more secure email service provider rather than weakening your security posture. + +## Example of vulnerable code + +```bicep +resource vulnerableGrafanaNoTLS 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-no-tls' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: 'password123' + startTLSPolicy: 'NoStartTLS' // Vulnerable: No TLS encryption used + } + } + } + sku: { + name: 'Standard' + } +} + +resource vulnerableGrafanaOpportunisticTLS 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-opportunistic-tls' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: 'password123' + startTLSPolicy: 'OpportunisticStartTLS' // Vulnerable: May fall back to unencrypted connection + } + } + } + sku: { + name: 'Standard' + } +} +``` + +## Example of secure code + +```bicep +resource secureGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-mandatory-tls' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: 'password123' + startTLSPolicy: 'MandatoryStartTLS' // Secure: Always uses TLS encryption + } + } + } + sku: { + name: 'Standard' + } +} +``` + +## References + +* [Grafana SMTP configuration documentation](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#smtp) +* [Azure Managed Grafana documentation](https://learn.microsoft.com/en-us/azure/managed-grafana/) +* [CWE-319: Cleartext Transmission of Sensitive Information](https://cwe.mitre.org/data/definitions/319.html) +* [StartTLS explained](https://en.wikipedia.org/wiki/Opportunistic_TLS) diff --git a/ql/src/security/CWE-319/GrafanaInsecureStartTLSPolicy.ql b/ql/src/security/CWE-319/GrafanaInsecureStartTLSPolicy.ql new file mode 100644 index 0000000..95e1039 --- /dev/null +++ b/ql/src/security/CWE-319/GrafanaInsecureStartTLSPolicy.ql @@ -0,0 +1,38 @@ +/** + * @name Insecure StartTLS policy in Grafana SMTP configuration + * @description Using 'NoStartTLS' or 'OpportunisticStartTLS' in Grafana SMTP configuration + * can lead to email communications being sent unencrypted. + * @kind problem + * @problem.severity warning + * @security-severity 5.5 + * @precision high + * @id bicep/grafana-insecure-starttls-policy + * @tags security + * bicep + * azure + * CWE-319 + */ + +import bicep +import codeql.bicep.frameworks.Microsoft.Dashboards + +from Dashboards::GrafanaResource grafana, + Dashboards::GrafanaProperties::Properties props, + Dashboards::GrafanaProperties::GrafanaConfigurations configs, + Dashboards::GrafanaProperties::Smtp smtp, + StringLiteral startTLSPolicy +where + props = grafana.getProperties() and + configs = props.getGrafanaConfigurations() and + smtp = configs.getSmtp() and + smtp.hasEnabled() and + smtp.enabled() = true and + smtp.hasStartTLSPolicy() and + startTLSPolicy = smtp.getStartTLSPolicy() and + ( + startTLSPolicy.getValue() = "NoStartTLS" or + startTLSPolicy.getValue() = "OpportunisticStartTLS" + ) +select startTLSPolicy, + "Insecure StartTLS policy '" + startTLSPolicy.getValue() + "' configured in Grafana SMTP settings. " + + "This may allow email communications to be sent unencrypted. Use 'MandatoryStartTLS' instead." diff --git a/ql/src/security/CWE-352/GrafanaCsrfDisabled.md b/ql/src/security/CWE-352/GrafanaCsrfDisabled.md new file mode 100644 index 0000000..138e30b --- /dev/null +++ b/ql/src/security/CWE-352/GrafanaCsrfDisabled.md @@ -0,0 +1,70 @@ +# CSRF protection disabled in Grafana + +This query identifies Microsoft.Dashboard/grafana resources that have Cross-Site Request Forgery (CSRF) protection explicitly disabled, which can lead to CSRF attacks. + +## Description + +Cross-Site Request Forgery (CSRF) is a type of attack that tricks a user's browser into executing unwanted actions on a web application in which the user is authenticated. The `csrfAlwaysCheck` property in Grafana's security configuration determines whether CSRF checks are always performed, even if the login cookie is not present in a request. + +When `csrfAlwaysCheck` is set to `false`, CSRF checks may be skipped in certain scenarios, potentially leaving the Grafana instance vulnerable to CSRF attacks. This could allow an attacker to perform unauthorized actions on behalf of authenticated users. + +## Recommendation + +Always enable CSRF protection by either setting the `csrfAlwaysCheck` property to `true` or by omitting it (the default value is `false`, but Grafana still performs CSRF checks in most cases). If you're experiencing issues with legitimate requests being blocked, investigate the specific issue rather than disabling CSRF protection entirely. + +## Example of vulnerable code + +```bicep +resource vulnerableGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-csrf-disabled' + location: 'eastus' + properties: { + grafanaConfigurations: { + security: { + csrfAlwaysCheck: false // Vulnerable: CSRF protection explicitly disabled + } + } + } + sku: { + name: 'Standard' + } +} +``` + +## Example of secure code + +```bicep +resource secureGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-csrf-enabled' + location: 'eastus' + properties: { + grafanaConfigurations: { + security: { + csrfAlwaysCheck: true // Secure: CSRF protection always enabled + } + } + } + sku: { + name: 'Standard' + } +} + +// Alternative: omit the security block entirely to use defaults +resource secureGrafanaAlt 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-default-security' + location: 'eastus' + properties: { + // No explicit security configuration, using Grafana defaults + } + sku: { + name: 'Standard' + } +} +``` + +## References + +* [Grafana security configuration documentation](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/) +* [Azure Managed Grafana documentation](https://learn.microsoft.com/en-us/azure/managed-grafana/) +* [CWE-352: Cross-Site Request Forgery (CSRF)](https://cwe.mitre.org/data/definitions/352.html) +* [OWASP Cross-Site Request Forgery Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html) diff --git a/ql/src/security/CWE-352/GrafanaCsrfDisabled.ql b/ql/src/security/CWE-352/GrafanaCsrfDisabled.ql new file mode 100644 index 0000000..ffc26d0 --- /dev/null +++ b/ql/src/security/CWE-352/GrafanaCsrfDisabled.ql @@ -0,0 +1,31 @@ +/** + * @name CSRF protection disabled in Grafana + * @description CSRF protection is disabled in Grafana configuration, which can lead to + * cross-site request forgery attacks. + * @kind problem + * @problem.severity warning + * @security-severity 6.5 + * @precision high + * @id bicep/grafana-csrf-disabled + * @tags security + * bicep + * azure + * CWE-352 + */ + +import bicep +import codeql.bicep.frameworks.Microsoft.Dashboards + +from Dashboards::GrafanaResource grafana, + Dashboards::GrafanaProperties::Properties props, + Dashboards::GrafanaProperties::GrafanaConfigurations configs, + Dashboards::GrafanaProperties::Security security +where + props = grafana.getProperties() and + configs = props.getGrafanaConfigurations() and + security = configs.getSecurity() and + security.hasCsrfAlwaysCheck() and + security.csrfAlwaysCheck() = false +select security, + "CSRF protection (csrfAlwaysCheck) is explicitly disabled in Grafana configuration, " + + "which can lead to cross-site request forgery attacks." diff --git a/ql/src/security/CWE-798/HardcodedSmtpCredentials.md b/ql/src/security/CWE-798/HardcodedSmtpCredentials.md new file mode 100644 index 0000000..22bce72 --- /dev/null +++ b/ql/src/security/CWE-798/HardcodedSmtpCredentials.md @@ -0,0 +1,103 @@ +# Hardcoded SMTP credentials in Grafana configuration + +This query identifies Microsoft.Dashboard/grafana resources that have hardcoded SMTP credentials in their configuration, which could lead to credential exposure and unauthorized access. + +## Description + +Azure Managed Grafana can be configured to send email notifications using SMTP. When SMTP credentials are hardcoded in Bicep templates, they could be exposed to unauthorized individuals through source code repositories, logs, or other means. This is especially problematic when the templates are stored in version control systems. + +Hardcoded credentials create a security risk since they cannot be easily rotated or managed securely. If the credentials are compromised, an attacker could gain access to the SMTP server and potentially use it to send malicious emails. + +## Recommendation + +Instead of hardcoding SMTP credentials directly in Bicep templates, use Azure Key Vault references or parameters with secure string types. This approach allows credentials to be stored securely in Azure Key Vault and referenced in your deployments without exposing them in the template files. + +## Example of vulnerable code + +```bicep +resource vulnerableGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-insecure-credentials' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: 'SuperSecretPassword123!' // Vulnerable: Hardcoded credentials + fromAddress: 'grafana@example.com' + fromName: 'Grafana Alerts' + } + } + } + sku: { + name: 'Standard' + } +} +``` + +## Example of secure code + +```bicep +@description('The name of the key vault containing the SMTP password') +param keyVaultName string + +@description('The name of the secret containing the SMTP password') +param smtpPasswordSecretName string + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName + scope: resourceGroup() +} + +resource secureGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-secure-credentials' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: '@Microsoft.KeyVault(SecretUri=${keyVault.properties.vaultUri}secrets/${smtpPasswordSecretName})' // Secure: Using Key Vault reference + fromAddress: 'grafana@example.com' + fromName: 'Grafana Alerts' + } + } + } + sku: { + name: 'Standard' + } +} + +// Alternative: using a parameter with secureString +@description('SMTP password for Grafana') +@secure() +param smtpPassword string + +resource secureGrafanaAlt 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-secure-credentials-alt' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: smtpPassword // Secure: Using secure parameter + fromAddress: 'grafana@example.com' + fromName: 'Grafana Alerts' + } + } + } + sku: { + name: 'Standard' + } +} +``` + +## References + +* [Azure Key Vault integration with ARM templates](https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/key-vault-parameter) +* [Azure Managed Grafana documentation](https://learn.microsoft.com/en-us/azure/managed-grafana/) +* [CWE-798: Use of Hard-coded Credentials](https://cwe.mitre.org/data/definitions/798.html) diff --git a/ql/src/security/CWE-798/HardcodedSmtpCredentials.ql b/ql/src/security/CWE-798/HardcodedSmtpCredentials.ql new file mode 100644 index 0000000..a2bab44 --- /dev/null +++ b/ql/src/security/CWE-798/HardcodedSmtpCredentials.ql @@ -0,0 +1,35 @@ +/** + * @name Hardcoded SMTP credentials in Grafana configuration + * @description Hardcoded credentials in Grafana SMTP configuration can lead to credential + * exposure. Use Azure Key Vault references instead. + * @kind problem + * @problem.severity warning + * @security-severity 8.0 + * @precision high + * @id bicep/hardcoded-smtp-credentials + * @tags security + * bicep + * azure + * CWE-798 + */ + +import bicep +import codeql.bicep.frameworks.Microsoft.Dashboards + +from Dashboards::GrafanaResource grafana, + Dashboards::GrafanaProperties::Properties props, + Dashboards::GrafanaProperties::GrafanaConfigurations configs, + Dashboards::GrafanaProperties::Smtp smtp, + StringLiteral password +where + props = grafana.getProperties() and + configs = props.getGrafanaConfigurations() and + smtp = configs.getSmtp() and + smtp.hasEnabled() and + smtp.enabled() = true and + smtp.hasPassword() and + password = smtp.getPassword() and + // Exclude passwords that appear to be using key vault references + not password.getValue().matches("@Microsoft.KeyVault(%") +select password, + "Hardcoded SMTP password in Grafana configuration. Use Azure Key Vault references instead." diff --git a/ql/src/security/Dashboards/GrafanaMissingZoneRedundancy.md b/ql/src/security/Dashboards/GrafanaMissingZoneRedundancy.md new file mode 100644 index 0000000..0499a8a --- /dev/null +++ b/ql/src/security/Dashboards/GrafanaMissingZoneRedundancy.md @@ -0,0 +1,63 @@ +# Missing zone redundancy in Grafana + +This query identifies Microsoft.Dashboard/grafana resources that do not have zone redundancy enabled, which may reduce availability in case of zone failures. + +## Description + +Azure Managed Grafana instances can be deployed with or without zone redundancy. Zone redundancy provides higher availability by distributing resources across multiple availability zones in a region. When the `zoneRedundancy` property is set to `'Disabled'` or not specified, the Grafana instance is deployed in a single availability zone. + +Without zone redundancy, if there is an outage in the availability zone where your Grafana instance is deployed, your monitoring capabilities could be completely lost. This could be particularly problematic during incidents when monitoring is most needed. + +## Recommendation + +For production environments and any scenarios where high availability is important, enable zone redundancy by setting the `zoneRedundancy` property to `'Enabled'`. This ensures that your Grafana instance can withstand zone failures within a region. + +Note that enabling zone redundancy may increase the cost of your Grafana deployment, so evaluate the trade-off between availability and cost based on your specific requirements. + +## Example of vulnerable code + +```bicep +resource vulnerableGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-single-zone' + location: 'eastus' + properties: { + zoneRedundancy: 'Disabled' // Vulnerable: Zone redundancy is disabled + } + sku: { + name: 'Standard' + } +} + +// Also vulnerable if zone redundancy is not specified +resource implicitVulnerableGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-single-zone-implicit' + location: 'eastus' + properties: { + // zoneRedundancy not specified, defaults to 'Disabled' + } + sku: { + name: 'Standard' + } +} +``` + +## Example of secure code + +```bicep +resource secureGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'grafana-multi-zone' + location: 'eastus' + properties: { + zoneRedundancy: 'Enabled' // Secure: Zone redundancy is enabled + } + sku: { + name: 'Standard' + } +} +``` + +## References + +* [Azure availability zones overview](https://learn.microsoft.com/en-us/azure/availability-zones/az-overview) +* [Azure Managed Grafana documentation](https://learn.microsoft.com/en-us/azure/managed-grafana/) +* [High availability in cloud applications](https://learn.microsoft.com/en-us/azure/architecture/framework/resiliency/overview) diff --git a/ql/src/security/Dashboards/GrafanaMissingZoneRedundancy.ql b/ql/src/security/Dashboards/GrafanaMissingZoneRedundancy.ql new file mode 100644 index 0000000..b0a8b9f --- /dev/null +++ b/ql/src/security/Dashboards/GrafanaMissingZoneRedundancy.ql @@ -0,0 +1,29 @@ +/** + * @name Missing zone redundancy in Grafana + * @description Grafana instances without zone redundancy enabled may have reduced availability in + * case of zone failures. + * @kind problem + * @problem.severity recommendation + * @security-severity 3.0 + * @precision high + * @id bicep/grafana-missing-zone-redundancy + * @tags security + * bicep + * azure + * availability + */ + +import bicep +import codeql.bicep.frameworks.Microsoft.Dashboards + +from Dashboards::GrafanaResource grafana, + Dashboards::GrafanaProperties::Properties props +where + props = grafana.getProperties() and + ( + not props.hasZoneRedundancy() or + props.zoneRedundancy() = "Disabled" + ) +select grafana, + "Grafana instance is configured without zone redundancy, which may reduce availability " + + "in case of zone failures." From 09c3c5b6aa742021c5738212ab2622bc5f679cfe Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Mon, 16 Jun 2025 12:56:54 +0100 Subject: [PATCH 2/4] feat(tests): Add Grafana query tests --- .../GrafanaExternalSnapshotsEnabled.expected | 1 + .../GrafanaExternalSnapshotsEnabled.qlref | 1 + .../GrafanaExternalSnapshotsEnabled/app.bicep | 51 ++++++++++ ...GrafanaExcessiveEditorPermissions.expected | 2 + .../GrafanaExcessiveEditorPermissions.qlref | 1 + .../app.bicep | 71 ++++++++++++++ ...GrafanaExcessiveViewerPermissions.expected | 1 + .../GrafanaExcessiveViewerPermissions.qlref | 1 + .../app.bicep | 53 ++++++++++ ...rafanaSmtpSslVerificationDisabled.expected | 1 + .../GrafanaSmtpSslVerificationDisabled.qlref | 1 + .../app.bicep | 92 ++++++++++++++++++ .../GrafanaApiKeyEnabled.expected | 1 + .../GrafanaApiKeyEnabled.qlref | 1 + .../CWE-306/GrafanaApiKeyEnabled/app.bicep | 41 ++++++++ .../GrafanaInsecureStartTLSPolicy.expected | 2 + .../GrafanaInsecureStartTLSPolicy.qlref | 1 + .../GrafanaInsecureStartTLSPolicy/app.bicep | 97 +++++++++++++++++++ .../HardcodedSmtpCredentials.expected | 1 + .../HardcodedSmtpCredentials.qlref | 1 + .../HardcodedSmtpCredentials/app.bicep | 85 ++++++++++++++++ .../GrafanaMissingZoneRedundancy.expected | 1 + .../GrafanaMissingZoneRedundancy.qlref | 1 + .../GrafanaMissingZoneRedundancy/app.bicep | 41 ++++++++ 24 files changed, 549 insertions(+) create mode 100644 ql/test/queries-tests/security/CWE-200/GrafanaExternalSnapshotsEnabled/GrafanaExternalSnapshotsEnabled.expected create mode 100644 ql/test/queries-tests/security/CWE-200/GrafanaExternalSnapshotsEnabled/GrafanaExternalSnapshotsEnabled.qlref create mode 100644 ql/test/queries-tests/security/CWE-200/GrafanaExternalSnapshotsEnabled/app.bicep create mode 100644 ql/test/queries-tests/security/CWE-272/GrafanaExcessiveEditorPermissions/GrafanaExcessiveEditorPermissions.expected create mode 100644 ql/test/queries-tests/security/CWE-272/GrafanaExcessiveEditorPermissions/GrafanaExcessiveEditorPermissions.qlref create mode 100644 ql/test/queries-tests/security/CWE-272/GrafanaExcessiveEditorPermissions/app.bicep create mode 100644 ql/test/queries-tests/security/CWE-272/GrafanaExcessiveViewerPermissions/GrafanaExcessiveViewerPermissions.expected create mode 100644 ql/test/queries-tests/security/CWE-272/GrafanaExcessiveViewerPermissions/GrafanaExcessiveViewerPermissions.qlref create mode 100644 ql/test/queries-tests/security/CWE-272/GrafanaExcessiveViewerPermissions/app.bicep create mode 100644 ql/test/queries-tests/security/CWE-295/GrafanaSmtpSslVerificationDisabled/GrafanaSmtpSslVerificationDisabled.expected create mode 100644 ql/test/queries-tests/security/CWE-295/GrafanaSmtpSslVerificationDisabled/GrafanaSmtpSslVerificationDisabled.qlref create mode 100644 ql/test/queries-tests/security/CWE-295/GrafanaSmtpSslVerificationDisabled/app.bicep create mode 100644 ql/test/queries-tests/security/CWE-306/GrafanaApiKeyEnabled/GrafanaApiKeyEnabled.expected create mode 100644 ql/test/queries-tests/security/CWE-306/GrafanaApiKeyEnabled/GrafanaApiKeyEnabled.qlref create mode 100644 ql/test/queries-tests/security/CWE-306/GrafanaApiKeyEnabled/app.bicep create mode 100644 ql/test/queries-tests/security/CWE-319/GrafanaInsecureStartTLSPolicy/GrafanaInsecureStartTLSPolicy.expected create mode 100644 ql/test/queries-tests/security/CWE-319/GrafanaInsecureStartTLSPolicy/GrafanaInsecureStartTLSPolicy.qlref create mode 100644 ql/test/queries-tests/security/CWE-319/GrafanaInsecureStartTLSPolicy/app.bicep create mode 100644 ql/test/queries-tests/security/CWE-798/HardcodedSmtpCredentials/HardcodedSmtpCredentials.expected create mode 100644 ql/test/queries-tests/security/CWE-798/HardcodedSmtpCredentials/HardcodedSmtpCredentials.qlref create mode 100644 ql/test/queries-tests/security/CWE-798/HardcodedSmtpCredentials/app.bicep create mode 100644 ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/GrafanaMissingZoneRedundancy.expected create mode 100644 ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/GrafanaMissingZoneRedundancy.qlref create mode 100644 ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/app.bicep diff --git a/ql/test/queries-tests/security/CWE-200/GrafanaExternalSnapshotsEnabled/GrafanaExternalSnapshotsEnabled.expected b/ql/test/queries-tests/security/CWE-200/GrafanaExternalSnapshotsEnabled/GrafanaExternalSnapshotsEnabled.expected new file mode 100644 index 0000000..333ce9b --- /dev/null +++ b/ql/test/queries-tests/security/CWE-200/GrafanaExternalSnapshotsEnabled/GrafanaExternalSnapshotsEnabled.expected @@ -0,0 +1 @@ +| app.bicep:11:18:13:7 | Snapshots | External snapshots are enabled in Grafana configuration, which could lead to unintended sharing of dashboard data with external services. | diff --git a/ql/test/queries-tests/security/CWE-200/GrafanaExternalSnapshotsEnabled/GrafanaExternalSnapshotsEnabled.qlref b/ql/test/queries-tests/security/CWE-200/GrafanaExternalSnapshotsEnabled/GrafanaExternalSnapshotsEnabled.qlref new file mode 100644 index 0000000..178a61a --- /dev/null +++ b/ql/test/queries-tests/security/CWE-200/GrafanaExternalSnapshotsEnabled/GrafanaExternalSnapshotsEnabled.qlref @@ -0,0 +1 @@ +security/CWE-200/GrafanaExternalSnapshotsEnabled.ql diff --git a/ql/test/queries-tests/security/CWE-200/GrafanaExternalSnapshotsEnabled/app.bicep b/ql/test/queries-tests/security/CWE-200/GrafanaExternalSnapshotsEnabled/app.bicep new file mode 100644 index 0000000..12a1c75 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-200/GrafanaExternalSnapshotsEnabled/app.bicep @@ -0,0 +1,51 @@ +// Test file for GrafanaExternalSnapshotsEnabled.ql +// Contains examples of secure and insecure configurations + +// TEST CASE: Insecure - Grafana with external snapshots enabled +// This should be detected by the query +resource insecureGrafanaSnapshots 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'insecure-grafana-snapshots' + location: 'eastus' + properties: { + grafanaConfigurations: { + snapshots: { + externalEnabled: true // ALERT: External snapshots are enabled + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with external snapshots disabled +// This should NOT be detected by the query +resource secureGrafanaSnapshots 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'secure-grafana-snapshots' + location: 'eastus' + properties: { + grafanaConfigurations: { + snapshots: { + externalEnabled: false // Secure: External snapshots are disabled + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with default snapshot settings (property omitted) +// This should NOT be detected by the query (assuming default is false) +resource defaultGrafanaSnapshots 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'default-grafana-snapshots' + location: 'eastus' + properties: { + grafanaConfigurations: { + // snapshots property omitted + } + } + sku: { + name: 'Standard' + } +} diff --git a/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveEditorPermissions/GrafanaExcessiveEditorPermissions.expected b/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveEditorPermissions/GrafanaExcessiveEditorPermissions.expected new file mode 100644 index 0000000..45cc8b8 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveEditorPermissions/GrafanaExcessiveEditorPermissions.expected @@ -0,0 +1,2 @@ +| app.bicep:11:14:13:7 | Users | Excessive permissions granted to Grafana editors (editorsCanAdmin=true). This allows editors to administrate dashboards, folders and teams they create. | +| app.bicep:62:14:65:7 | Users | Excessive permissions granted to Grafana editors (editorsCanAdmin=true). This allows editors to administrate dashboards, folders and teams they create. | diff --git a/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveEditorPermissions/GrafanaExcessiveEditorPermissions.qlref b/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveEditorPermissions/GrafanaExcessiveEditorPermissions.qlref new file mode 100644 index 0000000..fddba63 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveEditorPermissions/GrafanaExcessiveEditorPermissions.qlref @@ -0,0 +1 @@ +security/CWE-272/GrafanaExcessiveEditorPermissions.ql diff --git a/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveEditorPermissions/app.bicep b/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveEditorPermissions/app.bicep new file mode 100644 index 0000000..142f715 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveEditorPermissions/app.bicep @@ -0,0 +1,71 @@ +// Test file for GrafanaExcessiveEditorPermissions.ql +// Contains examples of secure and insecure configurations + +// TEST CASE: Insecure - Grafana with excessive editor permissions +// This should be detected by the query +resource insecureGrafanaEditors 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'insecure-grafana-editors' + location: 'eastus' + properties: { + grafanaConfigurations: { + users: { + editorsCanAdmin: true // ALERT: Excessive permissions for editors + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with proper editor permissions (explicitly set to false) +// This should NOT be detected by the query +resource secureGrafanaEditors 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'secure-grafana-editors' + location: 'eastus' + properties: { + grafanaConfigurations: { + users: { + editorsCanAdmin: false // Secure: Editors cannot administrate + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with default editor permissions (property omitted) +// This should NOT be detected by the query (assuming default is false) +resource defaultGrafanaEditors 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'default-grafana-editors' + location: 'eastus' + properties: { + grafanaConfigurations: { + users: { + // editorsCanAdmin property is omitted, should default to false + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Complex - Grafana with both viewer and editor permission settings +// The editorsCanAdmin=true should be detected by the query +resource complexGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'complex-grafana-permissions' + location: 'eastus' + properties: { + grafanaConfigurations: { + users: { + editorsCanAdmin: true // ALERT: Excessive permissions for editors + viewersCanEdit: false // This is secure, but the resource should still be flagged + } + } + } + sku: { + name: 'Standard' + } +} diff --git a/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveViewerPermissions/GrafanaExcessiveViewerPermissions.expected b/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveViewerPermissions/GrafanaExcessiveViewerPermissions.expected new file mode 100644 index 0000000..932e555 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveViewerPermissions/GrafanaExcessiveViewerPermissions.expected @@ -0,0 +1 @@ +| app.bicep:11:14:13:7 | Users | Excessive permissions granted to Grafana viewers (viewersCanEdit=true). This allows viewers to make temporary edits to dashboards they have access to. | diff --git a/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveViewerPermissions/GrafanaExcessiveViewerPermissions.qlref b/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveViewerPermissions/GrafanaExcessiveViewerPermissions.qlref new file mode 100644 index 0000000..73ce427 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveViewerPermissions/GrafanaExcessiveViewerPermissions.qlref @@ -0,0 +1 @@ +security/CWE-272/GrafanaExcessiveViewerPermissions.ql diff --git a/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveViewerPermissions/app.bicep b/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveViewerPermissions/app.bicep new file mode 100644 index 0000000..42a0ca9 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-272/GrafanaExcessiveViewerPermissions/app.bicep @@ -0,0 +1,53 @@ +// Test file for GrafanaExcessiveViewerPermissions.ql +// Contains examples of secure and insecure configurations + +// TEST CASE: Insecure - Grafana with excessive viewer permissions +// This should be detected by the query +resource insecureGrafanaViewers 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'insecure-grafana-viewers' + location: 'eastus' + properties: { + grafanaConfigurations: { + users: { + viewersCanEdit: true // ALERT: Excessive permissions for viewers + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with proper viewer permissions (explicitly set to false) +// This should NOT be detected by the query +resource secureGrafanaViewers 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'secure-grafana-viewers' + location: 'eastus' + properties: { + grafanaConfigurations: { + users: { + viewersCanEdit: false // Secure: Viewers cannot edit dashboards + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with default viewer permissions (property omitted) +// This should NOT be detected by the query (assuming default is false) +resource defaultGrafanaViewers 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'default-grafana-viewers' + location: 'eastus' + properties: { + grafanaConfigurations: { + users: { + // viewersCanEdit property is omitted, should default to false + } + } + } + sku: { + name: 'Standard' + } +} diff --git a/ql/test/queries-tests/security/CWE-295/GrafanaSmtpSslVerificationDisabled/GrafanaSmtpSslVerificationDisabled.expected b/ql/test/queries-tests/security/CWE-295/GrafanaSmtpSslVerificationDisabled/GrafanaSmtpSslVerificationDisabled.expected new file mode 100644 index 0000000..4ce1632 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-295/GrafanaSmtpSslVerificationDisabled/GrafanaSmtpSslVerificationDisabled.expected @@ -0,0 +1 @@ +| app.bicep:18:21:18:24 | true | Grafana SMTP configuration has SSL verification disabled (skipVerify=true), which can lead to man-in-the-middle attacks. | diff --git a/ql/test/queries-tests/security/CWE-295/GrafanaSmtpSslVerificationDisabled/GrafanaSmtpSslVerificationDisabled.qlref b/ql/test/queries-tests/security/CWE-295/GrafanaSmtpSslVerificationDisabled/GrafanaSmtpSslVerificationDisabled.qlref new file mode 100644 index 0000000..36cdc5a --- /dev/null +++ b/ql/test/queries-tests/security/CWE-295/GrafanaSmtpSslVerificationDisabled/GrafanaSmtpSslVerificationDisabled.qlref @@ -0,0 +1 @@ +security/CWE-295/GrafanaSmtpSslVerificationDisabled.ql diff --git a/ql/test/queries-tests/security/CWE-295/GrafanaSmtpSslVerificationDisabled/app.bicep b/ql/test/queries-tests/security/CWE-295/GrafanaSmtpSslVerificationDisabled/app.bicep new file mode 100644 index 0000000..ba7e042 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-295/GrafanaSmtpSslVerificationDisabled/app.bicep @@ -0,0 +1,92 @@ +// Test file for GrafanaSmtpSslVerificationDisabled.ql +// Contains examples of secure and insecure configurations + +// TEST CASE: Insecure - Grafana with SMTP SSL verification disabled +// This should be detected by the query +resource insecureGrafanaSmtp 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'insecure-grafana-smtp' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: 'password123' + fromAddress: 'grafana@example.com' + fromName: 'Grafana Alerts' + skipVerify: true // ALERT: SSL verification is disabled + startTLSPolicy: 'MandatoryStartTLS' + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with SMTP SSL verification enabled +// This should NOT be detected by the query +resource secureGrafanaSmtp 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'secure-grafana-smtp' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: 'password123' + fromAddress: 'grafana@example.com' + fromName: 'Grafana Alerts' + skipVerify: false // Secure: SSL verification is enabled + startTLSPolicy: 'MandatoryStartTLS' + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with SMTP but no skipVerify setting (should default to false) +// This should NOT be detected by the query +resource defaultSecureGrafanaSmtp 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'default-grafana-smtp' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: 'password123' + fromAddress: 'grafana@example.com' + fromName: 'Grafana Alerts' + // skipVerify not set, defaults to false + startTLSPolicy: 'MandatoryStartTLS' + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Edge case - Grafana with SMTP disabled +// This should NOT be detected by the query even though skipVerify is true +resource disabledSmtpGrafana 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'disabled-smtp-grafana' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: false + skipVerify: true // This shouldn't trigger the alert because SMTP is disabled + } + } + } + sku: { + name: 'Standard' + } +} diff --git a/ql/test/queries-tests/security/CWE-306/GrafanaApiKeyEnabled/GrafanaApiKeyEnabled.expected b/ql/test/queries-tests/security/CWE-306/GrafanaApiKeyEnabled/GrafanaApiKeyEnabled.expected new file mode 100644 index 0000000..fd35fbd --- /dev/null +++ b/ql/test/queries-tests/security/CWE-306/GrafanaApiKeyEnabled/GrafanaApiKeyEnabled.expected @@ -0,0 +1 @@ +| app.bicep:10:13:10:21 | String | Grafana API key feature is enabled, which can increase the attack surface. Consider disabling API keys if not strictly necessary. | diff --git a/ql/test/queries-tests/security/CWE-306/GrafanaApiKeyEnabled/GrafanaApiKeyEnabled.qlref b/ql/test/queries-tests/security/CWE-306/GrafanaApiKeyEnabled/GrafanaApiKeyEnabled.qlref new file mode 100644 index 0000000..e8911a1 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-306/GrafanaApiKeyEnabled/GrafanaApiKeyEnabled.qlref @@ -0,0 +1 @@ +security/CWE-306/GrafanaApiKeyEnabled.ql diff --git a/ql/test/queries-tests/security/CWE-306/GrafanaApiKeyEnabled/app.bicep b/ql/test/queries-tests/security/CWE-306/GrafanaApiKeyEnabled/app.bicep new file mode 100644 index 0000000..3283d1c --- /dev/null +++ b/ql/test/queries-tests/security/CWE-306/GrafanaApiKeyEnabled/app.bicep @@ -0,0 +1,41 @@ +// Test file for GrafanaApiKeyEnabled.ql +// Contains examples of secure and insecure configurations + +// TEST CASE: Insecure - Grafana with API key feature enabled +// This should be detected by the query +resource insecureGrafanaApiKey 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'insecure-grafana-apikey' + location: 'eastus' + properties: { + apiKey: 'Enabled' // ALERT: API key feature is enabled + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with API key feature disabled +// This should NOT be detected by the query +resource secureGrafanaApiKey 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'secure-grafana-apikey' + location: 'westeurope' + properties: { + apiKey: 'Disabled' // Secure: API key feature is disabled + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana without explicit API key setting +// This should NOT be detected by the query (assuming default is secure) +resource defaultGrafanaApiKey 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'default-grafana-apikey' + location: 'centralus' + properties: { + // No explicit API key setting + } + sku: { + name: 'Standard' + } +} diff --git a/ql/test/queries-tests/security/CWE-319/GrafanaInsecureStartTLSPolicy/GrafanaInsecureStartTLSPolicy.expected b/ql/test/queries-tests/security/CWE-319/GrafanaInsecureStartTLSPolicy/GrafanaInsecureStartTLSPolicy.expected new file mode 100644 index 0000000..1e7cb2c --- /dev/null +++ b/ql/test/queries-tests/security/CWE-319/GrafanaInsecureStartTLSPolicy/GrafanaInsecureStartTLSPolicy.expected @@ -0,0 +1,2 @@ +| app.bicep:13:25:13:36 | String | Insecure StartTLS policy 'NoStartTLS' configured in Grafana SMTP settings. This may allow email communications to be sent unencrypted. Use 'MandatoryStartTLS' instead. | +| app.bicep:33:25:33:47 | String | Insecure StartTLS policy 'OpportunisticStartTLS' configured in Grafana SMTP settings. This may allow email communications to be sent unencrypted. Use 'MandatoryStartTLS' instead. | diff --git a/ql/test/queries-tests/security/CWE-319/GrafanaInsecureStartTLSPolicy/GrafanaInsecureStartTLSPolicy.qlref b/ql/test/queries-tests/security/CWE-319/GrafanaInsecureStartTLSPolicy/GrafanaInsecureStartTLSPolicy.qlref new file mode 100644 index 0000000..3a3a57f --- /dev/null +++ b/ql/test/queries-tests/security/CWE-319/GrafanaInsecureStartTLSPolicy/GrafanaInsecureStartTLSPolicy.qlref @@ -0,0 +1 @@ +security/CWE-319/GrafanaInsecureStartTLSPolicy.ql diff --git a/ql/test/queries-tests/security/CWE-319/GrafanaInsecureStartTLSPolicy/app.bicep b/ql/test/queries-tests/security/CWE-319/GrafanaInsecureStartTLSPolicy/app.bicep new file mode 100644 index 0000000..08b80e9 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-319/GrafanaInsecureStartTLSPolicy/app.bicep @@ -0,0 +1,97 @@ +// Test file for GrafanaInsecureStartTLSPolicy.ql +// Contains examples of secure and insecure configurations + +// TEST CASE: Insecure - Grafana with NoStartTLS policy +// This should be detected by the query +resource insecureGrafanaNoStartTLS 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'insecure-grafana-no-starttls' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + startTLSPolicy: 'NoStartTLS' // ALERT: No StartTLS policy (insecure) + host: 'smtp.example.com' + port: 25 + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Insecure - Grafana with OpportunisticStartTLS policy +// This should be detected by the query +resource insecureGrafanaOpportunisticStartTLS 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'insecure-grafana-opportunistic-starttls' + location: 'westeurope' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + startTLSPolicy: 'OpportunisticStartTLS' // ALERT: Opportunistic StartTLS policy (insecure) + host: 'smtp.example.com' + port: 587 + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with MandatoryStartTLS policy +// This should NOT be detected by the query +resource secureGrafanaMandatoryStartTLS 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'secure-grafana-mandatory-starttls' + location: 'centralus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + startTLSPolicy: 'MandatoryStartTLS' // Secure: Mandatory StartTLS policy + host: 'smtp.example.com' + port: 587 + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with SMTP disabled +// This should NOT be detected by the query +resource secureGrafanaSmtpDisabled 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'secure-grafana-smtp-disabled' + location: 'westus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: false + startTLSPolicy: 'NoStartTLS' // Not a security issue because SMTP is disabled + host: 'smtp.example.com' + port: 25 + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with no SMTP configuration +// This should NOT be detected by the query +resource secureGrafanaNoSmtp 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'secure-grafana-no-smtp' + location: 'eastus2' + properties: { + grafanaConfigurations: { + // No SMTP configuration + } + } + sku: { + name: 'Standard' + } +} diff --git a/ql/test/queries-tests/security/CWE-798/HardcodedSmtpCredentials/HardcodedSmtpCredentials.expected b/ql/test/queries-tests/security/CWE-798/HardcodedSmtpCredentials/HardcodedSmtpCredentials.expected new file mode 100644 index 0000000..b57d1fb --- /dev/null +++ b/ql/test/queries-tests/security/CWE-798/HardcodedSmtpCredentials/HardcodedSmtpCredentials.expected @@ -0,0 +1 @@ +| app.bicep:26:19:26:40 | String | Hardcoded SMTP password in Grafana configuration. Use Azure Key Vault references instead. | diff --git a/ql/test/queries-tests/security/CWE-798/HardcodedSmtpCredentials/HardcodedSmtpCredentials.qlref b/ql/test/queries-tests/security/CWE-798/HardcodedSmtpCredentials/HardcodedSmtpCredentials.qlref new file mode 100644 index 0000000..4c523ce --- /dev/null +++ b/ql/test/queries-tests/security/CWE-798/HardcodedSmtpCredentials/HardcodedSmtpCredentials.qlref @@ -0,0 +1 @@ +security/CWE-798/HardcodedSmtpCredentials.ql diff --git a/ql/test/queries-tests/security/CWE-798/HardcodedSmtpCredentials/app.bicep b/ql/test/queries-tests/security/CWE-798/HardcodedSmtpCredentials/app.bicep new file mode 100644 index 0000000..485d27c --- /dev/null +++ b/ql/test/queries-tests/security/CWE-798/HardcodedSmtpCredentials/app.bicep @@ -0,0 +1,85 @@ +// Test file for HardcodedSmtpCredentials.ql +// Contains examples of secure and insecure configurations + +// Parameters for secure implementation +@description('The name of the key vault containing the SMTP password') +param keyVaultName string = 'myKeyVault' + +@description('The name of the secret containing the SMTP password') +param smtpPasswordSecretName string = 'smtpPassword' + +@description('SMTP password for Grafana') +@secure() +param smtpPasswordParam string + +// TEST CASE: Insecure - Grafana with hardcoded SMTP credentials +// This should be detected by the query +resource insecureGrafanaCredentials 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'insecure-grafana-credentials' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: 'SuperS3cr3tP@ssw0rd!' // ALERT: Hardcoded credentials + fromAddress: 'grafana@example.com' + fromName: 'Grafana Alerts' + } + } + } + sku: { + name: 'Standard' + } +} + +// Mock Key Vault resource for reference +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName + scope: resourceGroup() +} + +// TEST CASE: Secure - Grafana with Key Vault reference for SMTP credentials +// This should NOT be detected by the query +resource secureGrafanaWithKeyVault 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'secure-grafana-keyvault' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: '@Microsoft.KeyVault(SecretUri=${keyVault.properties.vaultUri}secrets/${smtpPasswordSecretName})' // Secure: Using Key Vault reference + fromAddress: 'grafana@example.com' + fromName: 'Grafana Alerts' + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana using parameter with secureString type +// This should NOT be detected by the query +resource secureGrafanaWithParam 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'secure-grafana-param' + location: 'eastus' + properties: { + grafanaConfigurations: { + smtp: { + enabled: true + host: 'smtp.example.com:587' + user: 'grafanauser' + password: smtpPasswordParam // Secure: Using secure parameter + fromAddress: 'grafana@example.com' + fromName: 'Grafana Alerts' + } + } + } + sku: { + name: 'Standard' + } +} diff --git a/ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/GrafanaMissingZoneRedundancy.expected b/ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/GrafanaMissingZoneRedundancy.expected new file mode 100644 index 0000000..a811bbf --- /dev/null +++ b/ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/GrafanaMissingZoneRedundancy.expected @@ -0,0 +1 @@ +| app.bicep:6:1:15:1 | GrafanaResource | Grafana instance is configured without zone redundancy, which may reduce availability in case of zone failures. | diff --git a/ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/GrafanaMissingZoneRedundancy.qlref b/ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/GrafanaMissingZoneRedundancy.qlref new file mode 100644 index 0000000..fb19258 --- /dev/null +++ b/ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/GrafanaMissingZoneRedundancy.qlref @@ -0,0 +1 @@ +security/Dashboards/GrafanaMissingZoneRedundancy.ql diff --git a/ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/app.bicep b/ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/app.bicep new file mode 100644 index 0000000..a5eb2a6 --- /dev/null +++ b/ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/app.bicep @@ -0,0 +1,41 @@ +// Test file for GrafanaMissingZoneRedundancy.ql +// Contains examples of secure and insecure configurations + +// TEST CASE: Insecure - Grafana with zone redundancy explicitly disabled +// This should be detected by the query +resource insecureGrafanaNoZoneRedundancy1 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'insecure-grafana-no-zone-redundancy-1' + location: 'eastus' + properties: { + zoneRedundancy: 'Disabled' // ALERT: Zone redundancy explicitly disabled + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Insecure - Grafana without zone redundancy setting +// This should be detected by the query (missing setting) +resource insecureGrafanaNoZoneRedundancy2 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'insecure-grafana-no-zone-redundancy-2' + location: 'westeurope' + properties: { + // No zone redundancy setting + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with zone redundancy enabled +// This should NOT be detected by the query +resource secureGrafanaZoneRedundancy 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'secure-grafana-zone-redundancy' + location: 'centralus' + properties: { + zoneRedundancy: 'Enabled' // Secure: Zone redundancy is enabled + } + sku: { + name: 'Standard' + } +} From 2ec31bf01ff6ad13b90504f697058c84b3a889f0 Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Mon, 16 Jun 2025 12:59:22 +0100 Subject: [PATCH 3/4] feat(tests): Add CSRF query tests --- .../GrafanaCsrfDisabled.expected | 1 + .../GrafanaCsrfDisabled.qlref | 1 + .../CWE-352/GrafanaCsrfDisabled/app.bicep | 52 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 ql/test/queries-tests/security/CWE-352/GrafanaCsrfDisabled/GrafanaCsrfDisabled.expected create mode 100644 ql/test/queries-tests/security/CWE-352/GrafanaCsrfDisabled/GrafanaCsrfDisabled.qlref create mode 100644 ql/test/queries-tests/security/CWE-352/GrafanaCsrfDisabled/app.bicep diff --git a/ql/test/queries-tests/security/CWE-352/GrafanaCsrfDisabled/GrafanaCsrfDisabled.expected b/ql/test/queries-tests/security/CWE-352/GrafanaCsrfDisabled/GrafanaCsrfDisabled.expected new file mode 100644 index 0000000..3cc7544 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-352/GrafanaCsrfDisabled/GrafanaCsrfDisabled.expected @@ -0,0 +1 @@ +| app.bicep:12:26:12:30 | false | CSRF protection (csrfAlwaysCheck) is explicitly disabled in Grafana configuration, which can lead to cross-site request forgery attacks. | diff --git a/ql/test/queries-tests/security/CWE-352/GrafanaCsrfDisabled/GrafanaCsrfDisabled.qlref b/ql/test/queries-tests/security/CWE-352/GrafanaCsrfDisabled/GrafanaCsrfDisabled.qlref new file mode 100644 index 0000000..8970eda --- /dev/null +++ b/ql/test/queries-tests/security/CWE-352/GrafanaCsrfDisabled/GrafanaCsrfDisabled.qlref @@ -0,0 +1 @@ +security/CWE-352/GrafanaCsrfDisabled.ql diff --git a/ql/test/queries-tests/security/CWE-352/GrafanaCsrfDisabled/app.bicep b/ql/test/queries-tests/security/CWE-352/GrafanaCsrfDisabled/app.bicep new file mode 100644 index 0000000..4b61e29 --- /dev/null +++ b/ql/test/queries-tests/security/CWE-352/GrafanaCsrfDisabled/app.bicep @@ -0,0 +1,52 @@ +// Test file for GrafanaCsrfDisabled.ql +// Contains examples of secure and insecure configurations + +// TEST CASE: Insecure - Grafana with CSRF protection explicitly disabled +// This should be detected by the query +resource insecureGrafanaCsrf 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'insecure-grafana-csrf' + location: 'eastus' + properties: { + grafanaConfigurations: { + security: { + csrfAlwaysCheck: false // ALERT: CSRF protection explicitly disabled + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with CSRF protection explicitly enabled +// This should NOT be detected by the query +resource secureGrafanaCsrf 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'secure-grafana-csrf' + location: 'eastus' + properties: { + grafanaConfigurations: { + security: { + csrfAlwaysCheck: true // Secure: CSRF protection always enabled + } + } + } + sku: { + name: 'Standard' + } +} + +// TEST CASE: Secure - Grafana with no explicit security configuration (defaults) +// This should NOT be detected by the query +resource defaultGrafanaCsrf 'Microsoft.Dashboard/grafana@2024-11-01-preview' = { + name: 'default-grafana-security' + location: 'eastus' + properties: { + // No explicit security configuration - using Grafana defaults + grafanaConfigurations: { + // Security block omitted + } + } + sku: { + name: 'Standard' + } +} From 228ddec710b96afcd87f1bef8acaf0e4d1c96d2a Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Mon, 16 Jun 2025 13:26:21 +0100 Subject: [PATCH 4/4] fix: Update select statements in Grafana security queries for clarity --- ql/src/security/CWE-352/GrafanaCsrfDisabled.ql | 2 +- ql/src/security/Dashboards/GrafanaMissingZoneRedundancy.ql | 2 +- .../GrafanaMissingZoneRedundancy.expected | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ql/src/security/CWE-352/GrafanaCsrfDisabled.ql b/ql/src/security/CWE-352/GrafanaCsrfDisabled.ql index ffc26d0..ab5eceb 100644 --- a/ql/src/security/CWE-352/GrafanaCsrfDisabled.ql +++ b/ql/src/security/CWE-352/GrafanaCsrfDisabled.ql @@ -26,6 +26,6 @@ where security = configs.getSecurity() and security.hasCsrfAlwaysCheck() and security.csrfAlwaysCheck() = false -select security, +select security.getCsrfAlwaysCheck(), "CSRF protection (csrfAlwaysCheck) is explicitly disabled in Grafana configuration, " + "which can lead to cross-site request forgery attacks." diff --git a/ql/src/security/Dashboards/GrafanaMissingZoneRedundancy.ql b/ql/src/security/Dashboards/GrafanaMissingZoneRedundancy.ql index b0a8b9f..26ad8cf 100644 --- a/ql/src/security/Dashboards/GrafanaMissingZoneRedundancy.ql +++ b/ql/src/security/Dashboards/GrafanaMissingZoneRedundancy.ql @@ -24,6 +24,6 @@ where not props.hasZoneRedundancy() or props.zoneRedundancy() = "Disabled" ) -select grafana, +select props.getZoneRedundancy(), "Grafana instance is configured without zone redundancy, which may reduce availability " + "in case of zone failures." diff --git a/ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/GrafanaMissingZoneRedundancy.expected b/ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/GrafanaMissingZoneRedundancy.expected index a811bbf..fa84831 100644 --- a/ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/GrafanaMissingZoneRedundancy.expected +++ b/ql/test/queries-tests/security/Dashboards/GrafanaMissingZoneRedundancy/GrafanaMissingZoneRedundancy.expected @@ -1 +1 @@ -| app.bicep:6:1:15:1 | GrafanaResource | Grafana instance is configured without zone redundancy, which may reduce availability in case of zone failures. | +| app.bicep:10:21:10:30 | String | Grafana instance is configured without zone redundancy, which may reduce availability in case of zone failures. |