Skip to content

Commit

Permalink
feat(terraform): check cognitive services restrict outbound network (#…
Browse files Browse the repository at this point in the history
…6919)

* added check for cognitive services restrict outbound network

* changed to None

* Update checkov/terraform/checks/resource/azure/CognitiveServicesRestrictOutboundNetwork.py

Co-authored-by: Taylor <[email protected]>

* update name to reflect openai specific rule

* remove unused import

* python3.8 compat

---------

Co-authored-by: Taylor <[email protected]>
  • Loading branch information
tjwald and tsmithv11 authored Dec 23, 2024
1 parent 3d39c27 commit 70e5baa
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import Any, List, Dict

from checkov.common.models.enums import CheckCategories, CheckResult
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck


class OpenAICognitiveServicesRestrictOutboundNetwork(BaseResourceCheck):
def __init__(self):
name = "Ensure that Azure Cognitive Services account hosted with OpenAI is configured with data loss prevention"
id = "CKV_AZURE_247"
supported_resources = ('azurerm_cognitive_account', )
categories = (CheckCategories.NETWORKING, )
super().__init__(
name=name,
id=id,
categories=categories,
supported_resources=supported_resources,
)

def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult:
if conf.get("kind", [""])[0].lower() != 'openai':
return CheckResult.PASSED

outbound_network_access_restricted = conf.get('outbound_network_access_restricted', [None])[0]
fqdns = conf.get('fqdns', [[]])[0]
if not outbound_network_access_restricted or not fqdns:
return CheckResult.FAILED

return CheckResult.PASSED


check = OpenAICognitiveServicesRestrictOutboundNetwork()
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
resource "azurerm_cognitive_account" "pass_openai" {
name = "openai-account"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
kind = "OpenAI"
identity {
type = "a"
}
sku_name = "S0"

outbound_network_access_restricted = true
fqdns = ["openai.example.com"] # Valid FQDN should pass the check

tags = {
Acceptance = "Test"
}
}

resource "azurerm_cognitive_account" "fail_openai_missing_fqdns" {
name = "openai-account-missing-fqdns"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
kind = "OpenAI"
identity {
type = "a"
}
sku_name = "S0"

outbound_network_access_restricted = true
fqdns = [] # Empty list of FQDNs should trigger failure

tags = {
Acceptance = "Test"
}
}

resource "azurerm_cognitive_account" "fail_openai_missing_outbound_network_access" {
name = "openai-account-missing-outbound-network-access"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
kind = "OpenAI"
identity {
type = "a"
}
sku_name = "S0"

# Missing outbound_network_access_restricted field should trigger failure
fqdns = ["openai.example.com"]

tags = {
Acceptance = "Test"
}
}

resource "azurerm_cognitive_account" "pass_non_openai" {
name = "non-openai-account"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
kind = "TextAnalytics" # Non-OpenAI kind should automatically pass the check
identity {
type = "a"
}
sku_name = "S0"

outbound_network_access_restricted = false
fqdns = [] # Doesn't matter since kind is not OpenAI

tags = {
Acceptance = "Test"
}
}

resource "azurerm_cognitive_account" "fail_openai_missing_fqdns_and_outbound_network_access" {
name = "openai-account-missing-both"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
kind = "OpenAI"
identity {
type = "a"
}
sku_name = "S0"

# Missing outbound access should trigger failure
# Empty FQDNs list should trigger failure

tags = {
Acceptance = "Test"
}
}

resource "azurerm_cognitive_account" "pass_openai_multiple_fqdns" {
name = "openai-account-multiple-fqdns"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
kind = "OpenAI"
identity {
type = "a"
}
sku_name = "S0"

outbound_network_access_restricted = true
fqdns = ["openai1.example.com", "openai2.example.com", "openai3.example.com"] # Multiple FQDNs should pass

tags = {
Acceptance = "Test"
}
}

resource "azurerm_cognitive_account" "fail_openai_missing_fqdns_but_present_outbound_network_access" {
name = "openai-account-failed-missing-fqdns"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
kind = "OpenAI"
identity {
type = "a"
}
sku_name = "S0"

outbound_network_access_restricted = true # Present outbound access but missing FQDNs
fqdns = [] # Empty list of FQDNs should trigger failure

tags = {
Acceptance = "Test"
}
}

resource "azurerm_cognitive_account" "fail_openai_no_outbound_access_and_multiple_fqdns" {
name = "openai-account-failed-no-outbound-access-multiple-fqdns"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
kind = "OpenAI"
identity {
type = "a"
}
sku_name = "S0"

# Missing outbound access but multiple FQDNs present
fqdns = ["openai1.example.com", "openai2.example.com", "openai3.example.com"] # Multiple FQDNs should trigger failure due to missing outbound access

tags = {
Acceptance = "Test"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import unittest
from pathlib import Path

from checkov.runner_filter import RunnerFilter
from checkov.terraform.checks.resource.azure.OpenAICognitiveServicesRestrictOutboundNetwork import check
from checkov.terraform.runner import Runner


class TestOpenAICognitiveServicesRestrictedOutboundNetwork(unittest.TestCase):
def test(self):
test_files_dir = Path(__file__).parent / "example_OpenAICognitiveServicesRestrictOutboundNetwork"

report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id]))
summary = report.get_summary()

passing_resources = {
"azurerm_cognitive_account.pass_openai",
"azurerm_cognitive_account.pass_non_openai",
"azurerm_cognitive_account.pass_openai_multiple_fqdns",
}
failing_resources = {
"azurerm_cognitive_account.fail_openai_missing_fqdns",
"azurerm_cognitive_account.fail_openai_missing_outbound_network_access",
"azurerm_cognitive_account.fail_openai_missing_fqdns_and_outbound_network_access",
"azurerm_cognitive_account.fail_openai_missing_fqdns_but_present_outbound_network_access",
"azurerm_cognitive_account.fail_openai_no_outbound_access_and_multiple_fqdns",
}

passed_check_resources = {c.resource for c in report.passed_checks}
failed_check_resources = {c.resource for c in report.failed_checks}

self.assertEqual(summary["passed"], len(passing_resources))
self.assertEqual(summary["failed"], len(failing_resources))
self.assertEqual(summary["skipped"], 0)
self.assertEqual(summary["parsing_errors"], 0)

self.assertEqual(passing_resources, passed_check_resources)
self.assertEqual(failing_resources, failed_check_resources)


if __name__ == "__main__":
unittest.main()

0 comments on commit 70e5baa

Please sign in to comment.