# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

# E2E Test: Network/NetworkSecurityGroup_SSHAccess_Audit
# Real Azure Policy: "[Deprecated]: SSH access from the Internet should be blocked"
# Source: regolator/policyDefinitions/Network/NetworkSecurityGroup_SSHAccess_Audit.json
#
# Features exercised:
#   - Deeply nested template expressions: if(and(not(empty(...)), contains(...)))
#   - Arithmetic in templates: int(), split(), first(), last()
#   - Port range parsing: lessOrEquals/greaterOrEquals on split results
#   - count with where clause + template expression
#   - Double negation pattern: not { field notEquals "x" }
#   - Parameterized effect with defaultValue
#   - Multiple anyOf branches (destination port + source address)

aliases: test_aliases.json

policy_definition: |
  {
    "properties": {
      "displayName": "[Deprecated]: SSH access from the Internet should be blocked",
      "policyType": "BuiltIn",
      "mode": "All",
      "parameters": {
        "effect": {
          "type": "string",
          "defaultValue": "Audit",
          "allowedValues": ["Audit", "Disabled"]
        }
      },
      "policyRule": {
        "if": {
          "allOf": [
            {
              "field": "type",
              "equals": "Microsoft.Network/networkSecurityGroups/securityRules"
            },
            {
              "allOf": [
                {
                  "field": "Microsoft.Network/networkSecurityGroups/securityRules/access",
                  "equals": "Allow"
                },
                {
                  "field": "Microsoft.Network/networkSecurityGroups/securityRules/direction",
                  "equals": "Inbound"
                },
                {
                  "anyOf": [
                    {
                      "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange",
                      "equals": "*"
                    },
                    {
                      "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange",
                      "equals": "22"
                    },
                    {
                      "value": "[if(and(not(empty(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'))), contains(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'),'-')), and(lessOrEquals(int(first(split(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'), '-'))),22),greaterOrEquals(int(last(split(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'), '-'))),22)), 'false')]",
                      "equals": "true"
                    },
                    {
                      "count": {
                        "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]",
                        "where": {
                          "value": "[if(and(not(empty(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')))), contains(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')),'-')), and(lessOrEquals(int(first(split(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')), '-'))),22),greaterOrEquals(int(last(split(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')), '-'))),22)) , 'false')]",
                          "equals": "true"
                        }
                      },
                      "greater": 0
                    },
                    {
                      "not": {
                        "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]",
                        "notEquals": "*"
                      }
                    },
                    {
                      "not": {
                        "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]",
                        "notEquals": "22"
                      }
                    }
                  ]
                },
                {
                  "anyOf": [
                    {
                      "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix",
                      "equals": "*"
                    },
                    {
                      "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix",
                      "equals": "Internet"
                    },
                    {
                      "not": {
                        "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]",
                        "notEquals": "*"
                      }
                    },
                    {
                      "not": {
                        "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]",
                        "notEquals": "Internet"
                      }
                    }
                  ]
                }
              ]
            }
          ]
        },
        "then": {
          "effect": "[parameters('effect')]"
        }
      }
    }
  }

cases:
  # =========================================================================
  # Exact port 22 match
  # =========================================================================

  - note: audit_exact_port_22_from_internet
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "allow-ssh"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRange: "22"
        sourceAddressPrefix: "Internet"
    want_effect: "Audit"

  - note: audit_exact_port_22_from_wildcard
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "allow-ssh-any"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRange: "22"
        sourceAddressPrefix: "*"
    want_effect: "Audit"

  # =========================================================================
  # Wildcard port (*)
  # =========================================================================

  - note: audit_wildcard_port_from_internet
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "allow-all"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRange: "*"
        sourceAddressPrefix: "Internet"
    want_effect: "Audit"

  # =========================================================================
  # Port range containing 22
  # =========================================================================

  - note: audit_port_range_includes_22
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "allow-ssh-range"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRange: "20-25"
        sourceAddressPrefix: "*"
    want_effect: "Audit"

  - note: audit_port_range_exact_22_to_22
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "exactly-22"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRange: "22-22"
        sourceAddressPrefix: "Internet"
    want_effect: "Audit"

  - note: audit_port_range_1_to_1024
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "low-ports"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRange: "1-1024"
        sourceAddressPrefix: "*"
    want_effect: "Audit"

  # =========================================================================
  # Port range NOT containing 22
  # =========================================================================

  - note: pass_port_range_excludes_22
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "http-only"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRange: "80-443"
        destinationPortRanges: []
        sourceAddressPrefix: "Internet"
        sourceAddressPrefixes: []
    want_undefined: true

  - note: pass_port_23_only
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "port-23"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRange: "23"
        destinationPortRanges: []
        sourceAddressPrefix: "*"
        sourceAddressPrefixes: []
    want_undefined: true

  # =========================================================================
  # destinationPortRanges[*] array — double negation pattern
  # =========================================================================

  - note: audit_port_ranges_array_contains_22
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "multi-port-ssh"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRanges:
          - "80"
          - "22"
          - "443"
        sourceAddressPrefix: "Internet"
    want_effect: "Audit"

  - note: audit_port_ranges_array_contains_wildcard
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "multi-port-wildcard"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRanges:
          - "*"
        sourceAddressPrefix: "*"
    want_effect: "Audit"

  - note: audit_port_ranges_array_with_range_containing_22
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "multi-port-range"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRanges:
          - "80-443"
          - "10-30"
        sourceAddressPrefix: "Internet"
    want_effect: "Audit"

  - note: pass_port_ranges_array_no_22
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "non-ssh-ports"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRanges:
          - "80"
          - "443"
          - "8080"
        sourceAddressPrefix: "Internet"
    want_undefined: true

  # =========================================================================
  # Source address variations
  # =========================================================================

  - note: audit_source_prefixes_array_wildcard
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "src-wild-array"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRange: "22"
        sourceAddressPrefixes:
          - "*"
    want_effect: "Audit"

  - note: audit_source_prefixes_array_internet
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "src-inet-array"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRange: "22"
        sourceAddressPrefixes:
          - "Internet"
    want_effect: "Audit"

  - note: pass_source_is_private_subnet
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "private-ssh"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRange: "22"
        destinationPortRanges: []
        sourceAddressPrefix: "10.0.0.0/8"
        sourceAddressPrefixes: []
    want_undefined: true

  - note: pass_source_prefixes_all_private
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "private-array"
      properties:
        access: "Allow"
        direction: "Inbound"
        destinationPortRange: "22"
        destinationPortRanges: []
        sourceAddressPrefixes:
          - "10.0.0.0/8"
          - "172.16.0.0/12"
    want_undefined: true

  # =========================================================================
  # Non-matching access / direction
  # =========================================================================

  - note: pass_deny_rule
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "deny-ssh"
      properties:
        access: "Deny"
        direction: "Inbound"
        destinationPortRange: "22"
        sourceAddressPrefix: "*"
    want_undefined: true

  - note: pass_outbound_rule
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      name: "outbound-ssh"
      properties:
        access: "Allow"
        direction: "Outbound"
        destinationPortRange: "22"
        sourceAddressPrefix: "*"
    want_undefined: true

  # =========================================================================
  # Wrong resource type
  # =========================================================================

  - note: pass_wrong_type
    resource:
      type: "Microsoft.Storage/storageAccounts"
      name: "not-nsg"
      properties:
        access: "Allow"
    want_undefined: true
