mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Add constraint.rs module that parses Azure Policy JSON constraints into span-annotated AST nodes: - Logical combinators: allOf, anyOf, not - Leaf conditions: field/value with all 19 operators - Count blocks: field-count and value-count with where clauses Public API: parse_constraint() parses a standalone constraint from JSON. Includes YAML-driven test suite with 6 test files covering operators, fields, expressions, logical combinators, count, and parse errors.
618 lines
16 KiB
YAML
618 lines
16 KiB
YAML
# Copyright (c) Microsoft Corporation.
|
|
# Licensed under the MIT License.
|
|
|
|
# Count Expressions Test Suite
|
|
# Tests field count and value count with optional where clauses and name bindings.
|
|
|
|
cases:
|
|
# =========================================================================
|
|
# Field count — direct path (core subset, no alias resolution)
|
|
# =========================================================================
|
|
|
|
- note: field_count_direct_path_core
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "securityRules[*]"
|
|
},
|
|
"greater": 2
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
resource:
|
|
securityRules:
|
|
- { "name": "r1" }
|
|
- { "name": "r2" }
|
|
- { "name": "r3" }
|
|
want_effect: "deny"
|
|
|
|
- note: field_count_direct_path_where_core
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "securityRules[*]",
|
|
"where": {
|
|
"field": "securityRules[*].access",
|
|
"equals": "Allow"
|
|
}
|
|
},
|
|
"equals": 2
|
|
},
|
|
"then": { "effect": "audit" }
|
|
}
|
|
resource:
|
|
securityRules:
|
|
- { "access": "Allow" }
|
|
- { "access": "Deny" }
|
|
- { "access": "Allow" }
|
|
want_effect: "audit"
|
|
|
|
# =========================================================================
|
|
# Field count — basic
|
|
# =========================================================================
|
|
|
|
- note: field_count_basic
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "securityRules[*]"
|
|
},
|
|
"greater": 10
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
resource:
|
|
type: "Microsoft.Network/networkSecurityGroups"
|
|
securityRules:
|
|
- { "name": "r1" }
|
|
- { "name": "r2" }
|
|
- { "name": "r3" }
|
|
- { "name": "r4" }
|
|
- { "name": "r5" }
|
|
- { "name": "r6" }
|
|
- { "name": "r7" }
|
|
- { "name": "r8" }
|
|
- { "name": "r9" }
|
|
- { "name": "r10" }
|
|
- { "name": "r11" }
|
|
want_effect: "deny"
|
|
|
|
- note: field_count_equals_zero
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "storageProfile.dataDisks[*]"
|
|
},
|
|
"equals": 0
|
|
},
|
|
"then": { "effect": "audit" }
|
|
}
|
|
resource:
|
|
type: "Microsoft.Compute/virtualMachines"
|
|
want_effect: "audit"
|
|
|
|
# =========================================================================
|
|
# Field count — with where clause
|
|
# =========================================================================
|
|
|
|
- note: field_count_with_where
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "securityRules[*]",
|
|
"where": {
|
|
"field": "securityRules[*].access",
|
|
"equals": "Allow"
|
|
}
|
|
},
|
|
"greater": 5
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
resource:
|
|
type: "Microsoft.Network/networkSecurityGroups"
|
|
securityRules:
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
want_effect: "deny"
|
|
|
|
- note: field_count_where_allOf
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "securityRules[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{
|
|
"field": "securityRules[*].access",
|
|
"equals": "Allow"
|
|
},
|
|
{
|
|
"field": "securityRules[*].direction",
|
|
"equals": "Inbound"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"greaterOrEquals": 1
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
resource:
|
|
type: "Microsoft.Network/networkSecurityGroups"
|
|
securityRules:
|
|
- { "access": "Allow", "direction": "Inbound" }
|
|
- { "access": "Deny", "direction": "Outbound" }
|
|
want_effect: "deny"
|
|
|
|
- note: field_count_where_anyOf
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "securityRules[*]",
|
|
"where": {
|
|
"anyOf": [
|
|
{
|
|
"field": "securityRules[*].destinationPortRange",
|
|
"equals": "22"
|
|
},
|
|
{
|
|
"field": "securityRules[*].destinationPortRange",
|
|
"equals": "3389"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"notEquals": 0
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
resource:
|
|
type: "Microsoft.Network/networkSecurityGroups"
|
|
securityRules:
|
|
- { "destinationPortRange": "22" }
|
|
- { "destinationPortRange": "443" }
|
|
want_effect: "deny"
|
|
|
|
- note: field_count_where_not
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "securityRules[*]",
|
|
"where": {
|
|
"not": {
|
|
"field": "securityRules[*].access",
|
|
"equals": "Deny"
|
|
}
|
|
}
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "audit" }
|
|
}
|
|
resource:
|
|
type: "Microsoft.Network/networkSecurityGroups"
|
|
securityRules:
|
|
- { "access": "Allow" }
|
|
- { "access": "Deny" }
|
|
want_effect: "audit"
|
|
|
|
# =========================================================================
|
|
# Value count
|
|
# =========================================================================
|
|
|
|
- note: value_count_basic
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"value": ["eastus", "westus", "centralus"]
|
|
},
|
|
"equals": 3
|
|
},
|
|
"then": { "effect": "audit" }
|
|
}
|
|
resource:
|
|
type: "any"
|
|
want_effect: "audit"
|
|
|
|
- note: value_count_with_name
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"value": ["eastus", "westus", "centralus"],
|
|
"name": "location"
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "audit" }
|
|
}
|
|
resource:
|
|
type: "any"
|
|
want_effect: "audit"
|
|
|
|
- note: value_count_with_name_and_where
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"value": ["eastus", "westus", "centralus", "northeurope"],
|
|
"name": "loc",
|
|
"where": {
|
|
"value": "[current('loc')]",
|
|
"like": "*us"
|
|
}
|
|
},
|
|
"equals": 3
|
|
},
|
|
"then": { "effect": "audit" }
|
|
}
|
|
resource:
|
|
type: "any"
|
|
want_effect: "audit"
|
|
|
|
- note: value_count_expression
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"value": "[parameters('allowedLocations')]",
|
|
"name": "loc"
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "audit" }
|
|
}
|
|
parameters:
|
|
allowedLocations:
|
|
- "eastus"
|
|
- "westus"
|
|
resource:
|
|
type: "any"
|
|
want_effect: "audit"
|
|
|
|
# =========================================================================
|
|
# Count in allOf/anyOf
|
|
# =========================================================================
|
|
|
|
- note: count_in_allOf
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"allOf": [
|
|
{ "field": "type", "equals": "Microsoft.Network/networkSecurityGroups" },
|
|
{
|
|
"count": {
|
|
"field": "securityRules[*]",
|
|
"where": {
|
|
"field": "securityRules[*].access",
|
|
"equals": "Allow"
|
|
}
|
|
},
|
|
"greater": 10
|
|
}
|
|
]
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
resource:
|
|
type: "Microsoft.Network/networkSecurityGroups"
|
|
securityRules:
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
- { "access": "Allow" }
|
|
want_effect: "deny"
|
|
|
|
- note: count_in_not
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"not": {
|
|
"count": {
|
|
"field": "storageProfile.dataDisks[*]"
|
|
},
|
|
"lessOrEquals": 4
|
|
}
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
resource:
|
|
type: "Microsoft.Compute/virtualMachines"
|
|
storageProfile:
|
|
dataDisks:
|
|
- { "name": "d1" }
|
|
- { "name": "d2" }
|
|
- { "name": "d3" }
|
|
- { "name": "d4" }
|
|
- { "name": "d5" }
|
|
want_effect: "deny"
|
|
|
|
# =========================================================================
|
|
# Count with nested where containing count
|
|
# =========================================================================
|
|
|
|
- note: value_count_nested_where
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"value": "[parameters('requiredTags')]",
|
|
"name": "tag",
|
|
"where": {
|
|
"field": "[concat('tags[', current('tag'), ']')]",
|
|
"exists": true
|
|
}
|
|
},
|
|
"notEquals": "[length(parameters('requiredTags'))]"
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
parameters:
|
|
requiredTags:
|
|
- "environment"
|
|
- "costCenter"
|
|
resource:
|
|
type: "Microsoft.Compute/virtualMachines"
|
|
tags:
|
|
environment: "prod"
|
|
want_effect: "deny"
|
|
|
|
# =========================================================================
|
|
# Alias field refs inside count resolve to current loop element
|
|
# =========================================================================
|
|
|
|
- note: field_count_multiple_alias_refs_same_element
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "securityRules[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{ "field": "securityRules[*].access", "equals": "Allow" },
|
|
{ "field": "securityRules[*].direction", "equals": "Inbound" },
|
|
{ "field": "securityRules[*].protocol", "equals": "Tcp" }
|
|
]
|
|
}
|
|
},
|
|
"equals": 1
|
|
},
|
|
"then": { "effect": "audit" }
|
|
}
|
|
resource:
|
|
securityRules:
|
|
- { "access": "Allow", "direction": "Inbound", "protocol": "Tcp" }
|
|
- { "access": "Allow", "direction": "Outbound", "protocol": "Tcp" }
|
|
- { "access": "Deny", "direction": "Inbound", "protocol": "Tcp" }
|
|
want_effect: "audit"
|
|
|
|
- note: field_count_nested_field_access
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "storageProfile.dataDisks[*]",
|
|
"where": {
|
|
"field": "storageProfile.dataDisks[*].managedDisk.storageAccountType",
|
|
"notEquals": "Premium_LRS"
|
|
}
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
resource:
|
|
storageProfile:
|
|
dataDisks:
|
|
- { "name": "d1", "managedDisk": { "storageAccountType": "Premium_LRS" } }
|
|
- { "name": "d2", "managedDisk": { "storageAccountType": "Standard_LRS" } }
|
|
want_effect: "deny"
|
|
|
|
- note: field_count_where_zero_matches
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "items[*]",
|
|
"where": {
|
|
"field": "items[*].status",
|
|
"equals": "failed"
|
|
}
|
|
},
|
|
"equals": 0
|
|
},
|
|
"then": { "effect": "audit" }
|
|
}
|
|
resource:
|
|
items:
|
|
- { "status": "ok" }
|
|
- { "status": "ok" }
|
|
- { "status": "ok" }
|
|
want_effect: "audit"
|
|
|
|
# =========================================================================
|
|
# Nested count: count inside another count's where clause
|
|
# =========================================================================
|
|
|
|
- note: nested_field_and_value_count
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"value": "[parameters('requiredPorts')]",
|
|
"name": "port",
|
|
"where": {
|
|
"count": {
|
|
"field": "securityRules[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{ "field": "securityRules[*].destinationPortRange", "equals": "[current('port')]" },
|
|
{ "field": "securityRules[*].access", "equals": "Allow" }
|
|
]
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
},
|
|
"equals": "[length(parameters('requiredPorts'))]"
|
|
},
|
|
"then": { "effect": "audit" }
|
|
}
|
|
parameters:
|
|
requiredPorts:
|
|
- "443"
|
|
- "80"
|
|
resource:
|
|
securityRules:
|
|
- { "destinationPortRange": "443", "access": "Allow" }
|
|
- { "destinationPortRange": "80", "access": "Allow" }
|
|
- { "destinationPortRange": "22", "access": "Deny" }
|
|
want_effect: "audit"
|
|
|
|
- note: nested_field_and_value_count_fail
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"value": "[parameters('requiredPorts')]",
|
|
"name": "port",
|
|
"where": {
|
|
"count": {
|
|
"field": "securityRules[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{ "field": "securityRules[*].destinationPortRange", "equals": "[current('port')]" },
|
|
{ "field": "securityRules[*].access", "equals": "Allow" }
|
|
]
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
},
|
|
"equals": "[length(parameters('requiredPorts'))]"
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
parameters:
|
|
requiredPorts:
|
|
- "443"
|
|
- "80"
|
|
- "8080"
|
|
resource:
|
|
securityRules:
|
|
- { "destinationPortRange": "443", "access": "Allow" }
|
|
- { "destinationPortRange": "80", "access": "Allow" }
|
|
- { "destinationPortRange": "22", "access": "Deny" }
|
|
want_effect: ~
|
|
|
|
# =========================================================================
|
|
# current() — zero-arg form (innermost count element)
|
|
# =========================================================================
|
|
|
|
- note: current_zero_arg_value_count
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"value": ["Allow", "Allow", "Deny"],
|
|
"name": "access",
|
|
"where": {
|
|
"value": "[current()]",
|
|
"equals": "Allow"
|
|
}
|
|
},
|
|
"equals": 2
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
resource:
|
|
type: "any"
|
|
want_effect: "deny"
|
|
|
|
- note: current_zero_arg_field_count
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "items[*]",
|
|
"where": {
|
|
"value": "[current()]",
|
|
"equals": "yes"
|
|
}
|
|
},
|
|
"equals": 2
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
resource:
|
|
items: ["yes", "no", "yes"]
|
|
want_effect: "deny"
|
|
|
|
- note: current_zero_arg_with_function
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"value": ["HELLO", "WORLD"],
|
|
"name": "word",
|
|
"where": {
|
|
"value": "[startsWith(current(), 'HE')]",
|
|
"equals": true
|
|
}
|
|
},
|
|
"equals": 1
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
resource:
|
|
type: "any"
|
|
want_effect: "deny"
|
|
|
|
- note: current_zero_arg_nested_innermost
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"value": ["a", "b"],
|
|
"name": "outer",
|
|
"where": {
|
|
"count": {
|
|
"value": ["x", "y"],
|
|
"name": "inner",
|
|
"where": {
|
|
"value": "[current()]",
|
|
"equals": "x"
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "deny" }
|
|
}
|
|
resource:
|
|
type: "any"
|
|
want_effect: "deny"
|