🥒 CCC.ObjStor Test: ccc-test-container-20260410t121838z

Test Parameters

ServiceTypeobject-storage
ProviderServiceTypeMicrosoft.Storage/storageAccounts
CatalogTypesCCC.ObjStor
TagFilter@object-storage, @PerService, ~@NEGATIVE, ~@OPT_IN
UID/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z
ResourceNameccc-test-container-20260410t121838z
ReportFileccc-test-container-20260410t121838z-service
ReportTitleccc-test-container-20260410t121838z
Instance
{
  "ID": "main-azure",
  "Properties": {
    "Provider": "azure",
    "Region": "eastus",
    "AzureResourceGroup": "cfi_test_20260410t121838z",
    "AzureSubscriptionID": "c1cedd8e-bf91-4d7d-a4cc-45700402a2a1",
    "GcpProjectId": ""
  },
  "Services": [
    {
      "Type": "object-storage",
      "Properties": {
        "azure-storage-account": "stgcfi20260410t121838z",
        "default-container": "ccc-test-container-20260410t121838z",
        "object-storage-retention-period-days": 2
      }
    },
    {
      "Type": "logging",
      "Properties": {}
    }
  ],
  "Rules": {
    "permitted-destination-storage-accounts": [],
    "permitted-regions": [
      "eastus"
    ],
    "replication-locations": [
      "eastus",
      "westus"
    ]
  }
}
AzureResourceGroupcfi_test_20260410t121838z
AzureStorageAccountstgcfi20260410t121838z
AzureSubscriptionIDc1cedd8e-bf91-4d7d-a4cc-45700402a2a1
DefaultContainerccc-test-container-20260410t121838z
ObjectStorageRetentionPeriodDays2
PermittedDestinationStorageAccounts
[]
PermittedRegions
[
  "eastus"
]
Providerazure
Regioneastus
ReplicationLocations
[
  "eastus",
  "westus"
]
ResourceGroupcfi_test_20260410t121838z
SubscriptionIdc1cedd8e-bf91-4d7d-a4cc-45700402a2a1

Summary

Generated: 2026-04-10 12:24:57

Total Run Time: 34m3s

Features: 41

Scenarios: 82 (✅ 63 | ❌ 19)

Steps: 669 (✅ 601 | ❌ 19 | ⏭️ 43 | ❓ 6)

Feature: CCC.Core.CN01.AR01
Scenario: Storage account enforces minimum TLS version @CCC.Core @tlp-green @tlp-amber @tlp-red @CCC.Core.CN01 @Policy @PerService @object-storage
Given a cloud api for "{Instance}" in "api"39µs
When I attempt policy check "object-storage-tls-policy" for control "CCC.Core.CN01" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"2s
policy check failed: Azure Storage Account TLS Policy Check:
Then "{result}" is true20µs
📎 Attachments:
policy-result-object-storage-tls-policy.json
View JSON (2290 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN01/AR01/object-storage-tls-policy/azure.yaml",
  "name": "Azure Storage Account TLS Policy Check",
  "service_type": "object-storage",
  "requirement_text": "When a port is exposed for non-SSH network traffic, all traffic MUST include  a TLS handshake AND be encrypted using TLS 1.3 or higher.\n",
  "validity_score": 9,
  "validity_commentary": "This query validates TLS configuration at the Azure Storage Account level. Azure enforces minimum TLS version at the account level for all blob, file, queue, and table endpoints. Note: Azure Storage currently supports TLS 1.2  as the highest minimum version - TLS 1.3 support is available but cannot  be set as the exclusive minimum yet.\n",
  "query_template": "az storage account show \\\n  --name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"{minimumTlsVersion: minimumTlsVersion, supportsHttpsTrafficOnly: supportsHttpsTrafficOnly}\" \\\n  --output json\n",
  "query_executed": "az storage account show \\\n  --name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"{minimumTlsVersion: minimumTlsVersion, supportsHttpsTrafficOnly: supportsHttpsTrafficOnly}\" \\\n  --output json\n",
  "query_output": "{\n  \"minimumTlsVersion\": \"TLS1_2\",\n  \"supportsHttpsTrafficOnly\": null\n}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.minimumTlsVersion",
      "expected_values": [
        "TLS1_3"
      ],
      "validation_rule": "^TLS1_3$",
      "description": "Verifies that the minimum TLS protocol version is set to TLS 1.3. Note: Azure Storage does not yet support TLS 1.3 as a minimum version, so this check will fail until Azure adds support. This accurately reflects the compliance gap against the CCC requirement.\n",
      "actual_value": "[TLS1_2]",
      "passed": false
    },
    {
      "jsonpath": "$.supportsHttpsTrafficOnly",
      "expected_values": [
        "true"
      ],
      "validation_rule": "^true$",
      "description": "Confirms that HTTPS-only traffic is enforced. When enabled, all HTTP  requests are rejected (not redirected).",
      "actual_value": "[null]",
      "passed": false
    }
  ]
}
Feature: CCC.Core.CN01.AR03
Scenario: Object storage policy prevents the use of unencrypted ports @tlp-green @tlp-amber @tlp-red @CCC.Core @CCC.Core.CN01 @Policy @PerService @object-storage
When I attempt policy check "object-storage-unencrypted-policy" for control "CCC.Core.CN01" assessment requirement "AR03" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
policy check failed: Azure Storage Unencrypted Traffic Block Check:
Then "{result}" is true23µs
📎 Attachments:
policy-result-object-storage-unencrypted-policy.json
View JSON (1874 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN01/AR03/object-storage-unencrypted-policy/azure.yaml",
  "name": "Azure Storage Unencrypted Traffic Block Check",
  "service_type": "object-storage",
  "requirement_text": "Unencrypted traffic MUST be blocked or redirected to secure equivalents so that no data is transmitted in plaintext.\n",
  "validity_score": 9,
  "validity_commentary": "This query validates that Azure Storage rejects HTTP traffic at the account level. When supportsHttpsTrafficOnly is true, all HTTP requests are rejected (not redirected). Strengths: - Account-level enforcement for all blob, file, queue, and table endpoints - Prevents plaintext transmission Limitations: - Does not validate TLS version (see AR01 object-storage-tls-policy) - Redirect behavior (301/302) is not validated here; Azure rejects HTTP outright\n",
  "query_template": "az storage account show \\\n  --name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"{supportsHttpsTrafficOnly: supportsHttpsTrafficOnly}\" \\\n  --output json\n",
  "query_executed": "az storage account show \\\n  --name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"{supportsHttpsTrafficOnly: supportsHttpsTrafficOnly}\" \\\n  --output json\n",
  "query_output": "{\n  \"supportsHttpsTrafficOnly\": null\n}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.supportsHttpsTrafficOnly",
      "expected_values": [
        "true"
      ],
      "validation_rule": "^true$",
      "description": "Confirms that HTTPS-only traffic is enforced. When enabled, all HTTP requests are rejected at the storage account level, preventing plaintext transmission to containers and blobs.\n",
      "actual_value": "[null]",
      "passed": false
    }
  ]
}
Feature: CCC.Core.CN01.AR08
Scenario: Storage account enforces mutual TLS @tls @tlp-amber @tlp-red @CCC.Core @CCC.Core.CN01 @Policy @NotTested @PerService @object-storage
Then no-op required31µs
Feature: CCC.Core.CN02.AR01 - Data Encryption at Rest
Scenario: Verify objects are encrypted at rest @PerService @CCC.Core @CCC.Core.CN02 @tlp-green @tlp-amber @tlp-red @Behavioural @object-storage
Given a cloud api for "{Instance}" in "api"45µs
Given I call "{api}" with "GetServiceAPI" using argument "object-storage"98µs
And I refer to "{result}" as "storage"44µs
And "{result}" is not an error36µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-encryption-check={Timestamp}.txt", and "encryption test data"410ms
Then "{result}" is not an error55µs
And I refer to "{result}" as "uploadResult"16µs
And "{uploadResult.Encryption}" is not null191µs
And "{uploadResult.EncryptionAlgorithm}" is "AES256"62µs
And I attach "{uploadResult}" to the test output as "Upload Result with Encryption Details"161µs
📎 Attachments:
Upload Result with Encryption Details
View JSON (287 bytes)
{"ID":"test-encryption-check=1775823899892.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"test-encryption-check=1775823899892.txt","Size":20,"Data":["encryption test data"],"Encryption":"Microsoft","EncryptionAlgorithm":"AES256","VersionID":"2026-04-10T12:25:00.2915221Z"}
Scenario: Object storage encryption compliance @PerService @CCC.Core @CCC.Core.CN02 @tlp-green @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"31µs
When I attempt policy check "object-storage-encryption" for control "CCC.Core.CN02" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true37µs
📎 Attachments:
policy-result-object-storage-encryption.json
View JSON (2975 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN02/AR01/object-storage-encryption/azure.yaml",
  "name": "Azure Storage Account Encryption Check",
  "service_type": "object-storage",
  "requirement_text": "When data is stored, it MUST be encrypted using the latest industry-standard  encryption methods.\n",
  "validity_score": 9,
  "validity_commentary": "This query validates Azure Storage Account encryption configuration. Azure Storage automatically encrypts all data at rest with 256-bit AES encryption. Strengths: - Directly queries the storage account's encryption configuration - Validates encryption key source (Microsoft-managed or customer-managed) - Azure enforces encryption for all data, cannot be disabled Limitations: - Does not validate Key Vault key configuration details - Does not verify customer-managed key rotation policies - Infrastructure encryption (double encryption) is checked separately\n",
  "query_template": "az storage account show \\\n  --name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"{keySource: encryption.keySource, requireInfrastructureEncryption: encryption.requireInfrastructureEncryption, services: encryption.services}\" \\\n  --output json\n",
  "query_executed": "az storage account show \\\n  --name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"{keySource: encryption.keySource, requireInfrastructureEncryption: encryption.requireInfrastructureEncryption, services: encryption.services}\" \\\n  --output json\n",
  "query_output": "{\n  \"keySource\": \"Microsoft.Storage\",\n  \"requireInfrastructureEncryption\": null,\n  \"services\": {\n    \"blob\": {\n      \"enabled\": true,\n      \"keyType\": \"Account\",\n      \"lastEnabledTime\": \"2026-04-10T12:19:49.919443+00:00\"\n    },\n    \"file\": {\n      \"enabled\": true,\n      \"keyType\": \"Account\",\n      \"lastEnabledTime\": \"2026-04-10T12:19:49.919443+00:00\"\n    },\n    \"queue\": null,\n    \"table\": null\n  }\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.keySource",
      "expected_values": [
        "Microsoft.Keyvault",
        "Microsoft.Storage"
      ],
      "validation_rule": "^(Microsoft\\.Storage|Microsoft\\.Keyvault)$",
      "description": "Verifies that encryption is configured with an approved key source. 'Microsoft.Storage' uses Microsoft-managed keys, 'Microsoft.Keyvault' uses customer-managed keys stored in Azure Key Vault.\n",
      "actual_value": "[Microsoft.Storage]",
      "passed": true
    },
    {
      "jsonpath": "$.services.blob.enabled",
      "expected_values": [
        "true"
      ],
      "validation_rule": "^true$",
      "description": "Verifies that blob service encryption is enabled. This should always be true as Azure Storage encryption cannot be disabled.\n",
      "actual_value": "[true]",
      "passed": true
    }
  ]
}
Feature: CCC.Core.CN03.AR01 - Multi-Factor Authentication for Destructive Operations
Scenario: Object storage delete protection compliance @PerService @CCC.Core @CCC.Core.CN03 @tlp-green @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"54µs
When I attempt policy check "object-storage-delete-protection" for control "CCC.Core.CN03" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true33µs
📎 Attachments:
policy-result-object-storage-delete-protection.json
View JSON (3042 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN03/AR01/object-storage-delete-protection/azure.yaml",
  "name": "Azure Storage Account Soft Delete and Versioning Configuration",
  "service_type": "object-storage",
  "requirement_text": "When an entity attempts to modify the service through a user interface, the  authentication process MUST require multiple identifying factors for authentication.\n",
  "validity_score": 5,
  "validity_commentary": "Azure does not have a direct equivalent to S3 MFA Delete at the storage level. MFA in Azure is enforced at the Azure AD/Entra ID level for all operations. This check validates soft delete and versioning as compensating controls. Strengths: - Soft delete allows recovery of deleted blobs - Versioning preserves all blob versions - Azure AD MFA applies to all authenticated operations Limitations: - This is NOT a direct MFA-for-delete check - MFA enforcement is at the identity layer, not storage layer - Soft delete only protects for the retention period - For true MFA enforcement, Azure AD Conditional Access policies should be verified\n",
  "query_template": "az storage account blob-service-properties show \\\n  --account-name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"{softDeleteEnabled: deleteRetentionPolicy.enabled, softDeleteDays: deleteRetentionPolicy.days, versioningEnabled: isVersioningEnabled}\" \\\n  --output json\n",
  "query_executed": "az storage account blob-service-properties show \\\n  --account-name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"{softDeleteEnabled: deleteRetentionPolicy.enabled, softDeleteDays: deleteRetentionPolicy.days, versioningEnabled: isVersioningEnabled}\" \\\n  --output json\n",
  "query_output": "{\n  \"softDeleteDays\": 7,\n  \"softDeleteEnabled\": true,\n  \"versioningEnabled\": true\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.softDeleteEnabled",
      "expected_values": [
        "true"
      ],
      "validation_rule": "^true$",
      "description": "Verifies that blob soft delete is enabled. This allows recovery of deleted blobs within the retention period, providing protection against accidental or malicious deletion.\n",
      "actual_value": "[true]",
      "passed": true
    },
    {
      "jsonpath": "$.softDeleteDays",
      "expected_values": [],
      "validation_rule": "^[7-9]|[1-9][0-9]+$",
      "description": "Verifies that soft delete retention is at least 7 days. Higher values provide longer recovery windows.\n",
      "actual_value": "[7]",
      "passed": true
    },
    {
      "jsonpath": "$.versioningEnabled",
      "expected_values": [
        "true"
      ],
      "validation_rule": "^true$",
      "description": "Verifies that blob versioning is enabled. Versioning preserves all previous versions of blobs, enabling recovery from modifications.\n",
      "actual_value": "[true]",
      "passed": true
    }
  ]
}
Scenario: MFA requirement for destructive operations cannot be tested automatically @PerService @CCC.Core @CCC.Core.CN03 @tlp-green @tlp-amber @tlp-red @Behavioural @object-storage @load-balancer
Given a cloud api for "{Instance}" in "api"47µs
Then no-op required23µs
Feature: CCC.Core.CN03.AR02 - API Authentication with Credentials
Scenario: API modification requires credential and trust perimeter origin @PerService @CCC.Core @CCC.Core.CN03 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy @NotTestable @object-storage @load-balancer
Given a cloud api for "{Instance}" in "api"37µs
Then no-op required20µs
Feature: CCC.Core.CN03.AR03 - MFA for UI Viewing
Scenario: UI viewing requires multi-factor authentication @PerService @CCC.Core @CCC.Core.CN03 @tlp-amber @tlp-red @Policy @NotTestable @object-storage @load-balancer
Given a cloud api for "{Instance}" in "api"37µs
Then no-op required22µs
Feature: CCC.Core.CN03.AR04 - API Authentication for Viewing
Scenario: API viewing requires credential and trust perimeter origin @PerService @CCC.Core @CCC.Core.CN03 @tlp-amber @tlp-red @Policy @NotTestable @object-storage @load-balancer
Given a cloud api for "{Instance}" in "api"44µs
Then no-op required20µs
Feature: CCC.Core.CN04.AR01 - Log Administrative Access Attempts
Scenario: Object storage admin logging compliance @PerService @CCC.Core @CCC.Core.CN04 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy @object-storage
When I attempt policy check "admin-logging" for control "CCC.Core.CN04" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"3s
Then "{result}" is true30µs
📎 Attachments:
policy-result-admin-logging.json
View JSON (6984 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN04/AR01/admin-logging/azure.yaml",
  "name": "Azure Activity Log Configuration",
  "service_type": "all",
  "requirement_text": "The service MUST log all access and changes by administrators  (identity, timestamp, action, result).\n",
  "validity_score": 9,
  "validity_commentary": "Azure Activity Log automatically captures all administrative operations for all Azure resources. Activity Log is enabled by default at the subscription level and cannot be disabled. Strengths: - Activity Log is enabled by default and cannot be disabled - Logs include caller identity, timestamp, operation, and status - Covers all resources in the subscription Limitations: - Default retention is 90 days; longer retention requires export - Does not validate export to Log Analytics or Storage Account\n",
  "query_template": "az monitor activity-log list \\\n  --subscription ${AzureSubscriptionID} \\\n  --max-events 1 \\\n  --output json\n",
  "query_executed": "az monitor activity-log list \\\n  --subscription c1cedd8e-bf91-4d7d-a4cc-45700402a2a1 \\\n  --max-events 1 \\\n  --output json\n",
  "query_output": "[\n  {\n    \"authorization\": {\n      \"action\": \"Microsoft.Authorization/roleAssignments/write\",\n      \"scope\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/providers/Microsoft.Authorization/roleAssignments/7c4e8b3b-e32f-ed57-34fd-f0c1c55d9b21\"\n    },\n    \"caller\": \"02d55131-c257-44f6-af4e-edb89ce735e7\",\n    \"category\": {\n      \"localizedValue\": \"Administrative\",\n      \"value\": \"Administrative\"\n    },\n    \"claims\": {\n      \"aio\": \"AWQAm/8bAAAACogpsYiIeVMAQcTIxnQhokQatlewJJNIRm95h1cm2vwmiSExG5OAAcW5bSHDA8Xal4siUMNcOVf3f7zFE9C5EYq/jImcZW5khJVEH6ioF4VHqCnoW7RUO8hQYMDpvnbl\",\n      \"appid\": \"fd1a537d-a7ae-4a92-bb37-8ddbef54a795\",\n      \"appidacr\": \"2\",\n      \"aud\": \"https://management.azure.com\",\n      \"exp\": \"1775827140\",\n      \"http://schemas.microsoft.com/identity/claims/identityprovider\": \"https://sts.windows.net/fa193ac0-9c06-4111-bf55-341e4db193d3/\",\n      \"http://schemas.microsoft.com/identity/claims/objectidentifier\": \"02d55131-c257-44f6-af4e-edb89ce735e7\",\n      \"http://schemas.microsoft.com/identity/claims/tenantid\": \"fa193ac0-9c06-4111-bf55-341e4db193d3\",\n      \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier\": \"02d55131-c257-44f6-af4e-edb89ce735e7\",\n      \"iat\": \"1775823240\",\n      \"idtyp\": \"app\",\n      \"iss\": \"https://sts.windows.net/fa193ac0-9c06-4111-bf55-341e4db193d3/\",\n      \"nbf\": \"1775823240\",\n      \"rh\": \"1.AXUAwDoZ-gacEUG_VTQeTbGT00ZIf3kAutdPukPawfj2MBMAAAB1AA.\",\n      \"uti\": \"nLXR4W9ULkqoLV-KctEQAA\",\n      \"ver\": \"1.0\",\n      \"wids\": \"0997a1d0-0d1d-4acb-b408-d5ca73121e90\",\n      \"xms_act_fct\": \"3 9\",\n      \"xms_ftd\": \"Xw4UxXWsPw6h6LO_95tFsotDEoBAriC7TCeXXuMQpYIBdXNzb3V0aC1kc21z\",\n      \"xms_idrel\": \"26 7\",\n      \"xms_rd\": \"0.AXgAh_8KBQgCEgF1EhQIBxIQoLZOn9BLxESh-gTgBeFSJhIUCAkSEC159G0uOGtDqn2XbOd5Be8SFAgIEhBGSH95ALrXT7pD2sH49jATEhQICxIQ49rJZZ33Cz-j8omG-mUV4hoOCAoSCjE3NzU4MjM1MTkaBwgEEgNDUDE\",\n      \"xms_sub_fct\": \"3 9\",\n      \"xms_tcdt\": \"1763979378\"\n    },\n    \"correlationId\": \"cbc2a912-dc2d-6316-3578-a9a0dd47dd19\",\n    \"description\": \"\",\n    \"eventDataId\": \"a8e856dc-6e9f-4b40-9881-af3b9762fe5d\",\n    \"eventName\": {\n      \"localizedValue\": \"End request\",\n      \"value\": \"EndRequest\"\n    },\n    \"eventTimestamp\": \"2026-04-10T12:21:55.3126997Z\",\n    \"httpRequest\": {\n      \"clientIpAddress\": \"135.232.201.248\",\n      \"clientRequestId\": \"\",\n      \"method\": \"PUT\",\n      \"uri\": \"https://management.azure.com/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/providers/Microsoft.Authorization/roleAssignments/7c4e8b3b-e32f-ed57-34fd-f0c1c55d9b21?api-version=2022-04-01\"\n    },\n    \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/providers/Microsoft.Authorization/roleAssignments/7c4e8b3b-e32f-ed57-34fd-f0c1c55d9b21/events/a8e856dc-6e9f-4b40-9881-af3b9762fe5d/ticks/639114205153126997\",\n    \"level\": \"Informational\",\n    \"operationId\": \"d8288b8a-e12d-40fa-ab80-038623a2c25a\",\n    \"operationName\": {\n      \"localizedValue\": \"Create role assignment\",\n      \"value\": \"Microsoft.Authorization/roleAssignments/write\"\n    },\n    \"properties\": {\n      \"entity\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/providers/Microsoft.Authorization/roleAssignments/7c4e8b3b-e32f-ed57-34fd-f0c1c55d9b21\",\n      \"eventCategory\": \"Administrative\",\n      \"hierarchy\": \"fa193ac0-9c06-4111-bf55-341e4db193d3/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1\",\n      \"message\": \"Microsoft.Authorization/roleAssignments/write\",\n      \"statusCode\": \"Created\"\n    },\n    \"resourceGroup\": \"cfi_test_20260410t121838z\",\n    \"resourceGroupName\": \"cfi_test_20260410t121838z\",\n    \"resourceId\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/providers/Microsoft.Authorization/roleAssignments/7c4e8b3b-e32f-ed57-34fd-f0c1c55d9b21\",\n    \"resourceProviderName\": {\n      \"localizedValue\": \"Microsoft.Authorization\",\n      \"value\": \"Microsoft.Authorization\"\n    },\n    \"resourceType\": {\n      \"localizedValue\": \"Microsoft.Authorization/roleAssignments\",\n      \"value\": \"Microsoft.Authorization/roleAssignments\"\n    },\n    \"status\": {\n      \"localizedValue\": \"Succeeded\",\n      \"value\": \"Succeeded\"\n    },\n    \"subStatus\": {\n      \"localizedValue\": \"Created (HTTP Status Code: 201)\",\n      \"value\": \"Created\"\n    },\n    \"submissionTimestamp\": \"2026-04-10T12:23:15Z\",\n    \"subscriptionId\": \"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1\",\n    \"tenantId\": \"fa193ac0-9c06-4111-bf55-341e4db193d3\"\n  }\n]\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$[0].eventTimestamp",
      "expected_values": [],
      "validation_rule": "^\\d{4}-\\d{2}-\\d{2}T.*$",
      "description": "Azure Activity Log is enabled by default for all subscriptions. This check verifies Activity Log is accessible and contains events. Activity logs include identity (caller), timestamp, operation name, and result status for all administrative actions.\n",
      "actual_value": "[2026-04-10T12:21:55.3126997Z]",
      "passed": true
    }
  ]
}
Scenario: Verify admin actions are logged with identity and timestamp @PerService @CCC.Core @CCC.Core.CN04 @tlp-clear @tlp-green @tlp-amber @tlp-red @Behavioural @object-storage
Given a cloud api for "{Instance}" in "api"40µs
And I call "{api}" with "GetServiceAPI" using argument "{ServiceType}"34µs
And I refer to "{result}" as "theService"13µs
Given I call "{api}" with "GetServiceAPI" using argument "logging"163µs
And I refer to "{result}" as "loggingService"13µs
When I call "{theService}" with "UpdateResourcePolicy"1s
Then "{result}" is not an error25µs
And I attach "{result}" to the test output as "Policy Update Result"31µs
And we wait for a period of "10000" ms10s
When I call "{loggingService}" with "QueryAdminLogs" using arguments "{ResourceName}" and "{20}"2s
Then "{result}" is not an error24µs
And I refer to "{result}" as "adminLogs"16µs
And I attach "{adminLogs}" to the test output as "Admin Activity Logs"139µs
Then "{adminLogs}" is an array of objects with at least the following contents63µs
result
Succeeded
📎 Attachments:
Policy Update Result
View JSON (4 bytes)
null
Admin Activity Logs
View JSON (13928 bytes)
[{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create/Update Storage Account","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:23:51.399829Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"'auditIfNotExists' Policy action.","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:23:51.3685717Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"'audit' Policy action.","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:23:50.8373244Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create/Update Storage Account","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:23:50.7435748Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create role assignment","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/providers/Microsoft.Authorization/roleAssignments/7c4e8b3b-e32f-ed57-34fd-f0c1c55d9b21","timestamp":"2026-04-10T12:21:55.3126997Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create role assignment","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/providers/Microsoft.Authorization/roleAssignments/7c4e8b3b-e32f-ed57-34fd-f0c1c55d9b21","timestamp":"2026-04-10T12:21:54.2502085Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create or update resource diagnostic setting","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/providers/Microsoft.Insights/diagnosticSettings/cfi-blob-diag-audit-20260410t121838z","timestamp":"2026-04-10T12:20:58.9543566Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"List Storage Account Keys","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:20:58.5714229Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"List Storage Account Keys","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:20:58.5089205Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create or update resource diagnostic setting","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/providers/Microsoft.Insights/diagnosticSettings/cfi-blob-diag-cn04-20260410t121838z","timestamp":"2026-04-10T12:20:56.6056607Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Lock blob container immutability policy","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/containers/ccc-test-container-20260410t121838z/immutabilityPolicies/default","timestamp":"2026-04-10T12:20:56.404309Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Lock blob container immutability policy","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/containers/ccc-test-container-20260410t121838z/immutabilityPolicies/default","timestamp":"2026-04-10T12:20:56.2480041Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Put blob container immutability policy","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/containers/ccc-test-container-20260410t121838z/immutabilityPolicies/default","timestamp":"2026-04-10T12:20:56.0199888Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Put blob container immutability policy","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/containers/ccc-test-container-20260410t121838z/immutabilityPolicies/default","timestamp":"2026-04-10T12:20:55.8481145Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"List Storage Account Keys","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:20:55.4659423Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"List Storage Account Keys","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:20:55.3721793Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Put blob container","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/containers/ccc-test-container-20260410t121838z","timestamp":"2026-04-10T12:20:55.0434468Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Put blob container","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/containers/ccc-test-container-20260410t121838z","timestamp":"2026-04-10T12:20:54.6215784Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create or update resource diagnostic setting","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/providers/Microsoft.Insights/diagnosticSettings/cfi-blob-diag-cn04-20260410t121838z","timestamp":"2026-04-10T12:20:54.3088079Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create or update resource diagnostic setting","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/providers/Microsoft.Insights/diagnosticSettings/cfi-blob-diag-audit-20260410t121838z","timestamp":"2026-04-10T12:20:54.1415801Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"List Storage Account Keys","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:20:52.2928358Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"List Storage Account Keys","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:20:52.2303358Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Put blob service properties","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default","timestamp":"2026-04-10T12:20:51.8111112Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Put blob service properties","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default","timestamp":"2026-04-10T12:20:51.4829896Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"List Workspace Shared Keys","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z","timestamp":"2026-04-10T12:20:33.7813255Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"List Workspace Shared Keys","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z","timestamp":"2026-04-10T12:20:33.7344493Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create Workspace","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z","timestamp":"2026-04-10T12:20:32.2455926Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create Workspace","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z","timestamp":"2026-04-10T12:20:11.64379Z","result":"Accepted"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create Workspace","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z","timestamp":"2026-04-10T12:20:11.5005771Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create Workspace","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z","timestamp":"2026-04-10T12:20:11.4406672Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create/Update Storage Account","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:20:09.7252078Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create/Update Storage Account","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:19:51.5185825Z","result":"Accepted"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"'auditIfNotExists' Policy action.","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:19:51.4873358Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create Workspace","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z","timestamp":"2026-04-10T12:19:50.8586343Z","result":"Accepted"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create Workspace","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z","timestamp":"2026-04-10T12:19:50.5773853Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"'audit' Policy action.","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:19:49.3936014Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Create/Update Storage Account","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z","timestamp":"2026-04-10T12:19:49.3623541Z","result":"Started"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Update resource group","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z","timestamp":"2026-04-10T12:19:27.4556719Z","result":"Succeeded"},{"identity":"02d55131-c257-44f6-af4e-edb89ce735e7","action":"Update resource group","resource":"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z","timestamp":"2026-04-10T12:19:24.8773856Z","result":"Started"}]
Feature: CCC.Core.CN04.AR02 - Log Data Modification Attempts
Scenario: Object storage data modification logging compliance @PerService @CCC.Core @CCC.Core.CN04 @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"42µs
When I attempt policy check "data-write-logging" for control "CCC.Core.CN04" assessment requirement "AR02" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"2s
policy check failed: Azure Diagnostic Logging Write Configuration:
Then "{result}" is true24µs
📎 Attachments:
policy-result-data-write-logging.json
View JSON (5079 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN04/AR02/data-write-logging/azure.yaml",
  "name": "Azure Diagnostic Logging Write Configuration",
  "service_type": "all",
  "requirement_text": "The service MUST log all data modification attempts  (identity, timestamp, action, result).\n",
  "validity_score": 7,
  "validity_commentary": "This query validates that Azure diagnostic settings are configured to log write operations. Strengths: - StorageWrite captures blob upload, delete, and copy operations - Logs include caller identity, timestamp, operation, and status - Separate from the storage account being logged Limitations: - Diagnostic settings must be explicitly configured (not default) - Requires destination (Log Analytics, Storage, or Event Hub) - High-volume accounts may generate significant log data\n",
  "query_template": "az monitor diagnostic-settings list \\\n  --resource \"/subscriptions/${AzureSubscriptionID}/resourceGroups/${AzureResourceGroup}/providers/Microsoft.Storage/storageAccounts/${AzureStorageAccount}/blobServices/default\" \\\n  --output json\n",
  "query_executed": "az monitor diagnostic-settings list \\\n  --resource \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default\" \\\n  --output json\n",
  "query_output": "[\n  {\n    \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-audit-20260410t121838z\",\n    \"logs\": [\n      {\n        \"categoryGroup\": \"audit\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"categoryGroup\": \"allLogs\",\n        \"enabled\": false,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"metrics\": [\n      {\n        \"category\": \"Capacity\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"Transaction\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"name\": \"cfi-blob-diag-audit-20260410t121838z\",\n    \"resourceGroup\": \"cfi_test_20260410t121838z\",\n    \"type\": \"Microsoft.Insights/diagnosticSettings\",\n    \"workspaceId\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z\"\n  },\n  {\n    \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-cn04-20260410t121838z\",\n    \"logs\": [\n      {\n        \"category\": \"StorageRead\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"StorageWrite\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"StorageDelete\",\n        \"enabled\": false,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"metrics\": [\n      {\n        \"category\": \"Capacity\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"Transaction\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"name\": \"cfi-blob-diag-cn04-20260410t121838z\",\n    \"resourceGroup\": \"cfi_test_20260410t121838z\",\n    \"type\": \"Microsoft.Insights/diagnosticSettings\",\n    \"workspaceId\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z\"\n  }\n]\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.value[*].properties.logs[?(@.category==\"StorageWrite\")].enabled",
      "expected_values": [],
      "validation_rule": "^true$",
      "description": "Validates that StorageWrite logging is enabled for the blob service. StorageWrite captures blob creation, deletion, update, and copy operations with full caller identity and timestamp. If no diagnostic settings exist, this check will fail.\n",
      "actual_value": "[]",
      "passed": false
    }
  ]
}
Feature: CCC.Core.CN04.AR03 - Log Data Read Attempts
Scenario: Data read logging compliance @PerService @CCC.Core @CCC.Core.CN04 @tlp-red @Policy @object-storage @vpc
Given a cloud api for "{Instance}" in "api"48µs
When I attempt policy check "data-read-logging" for control "CCC.Core.CN04" assessment requirement "AR03" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"2s
policy check failed: Azure Diagnostic Logging Read Configuration:
Then "{result}" is true24µs
📎 Attachments:
policy-result-data-read-logging.json
View JSON (5059 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN04/AR03/data-read-logging/azure.yaml",
  "name": "Azure Diagnostic Logging Read Configuration",
  "service_type": "all",
  "requirement_text": "The service MUST log all data read attempts  (identity, timestamp, action, result).\n",
  "validity_score": 7,
  "validity_commentary": "This query validates that Azure diagnostic settings are configured to log read operations. Strengths: - StorageRead captures blob download and list operations - Logs include caller identity, timestamp, operation, and status - Separate from the storage account being logged Limitations: - Diagnostic settings must be explicitly configured (not default) - Requires destination (Log Analytics, Storage, or Event Hub) - High-volume accounts may generate very significant log data\n",
  "query_template": "az monitor diagnostic-settings list \\\n  --resource \"/subscriptions/${AzureSubscriptionID}/resourceGroups/${AzureResourceGroup}/providers/Microsoft.Storage/storageAccounts/${AzureStorageAccount}/blobServices/default\" \\\n  --output json\n",
  "query_executed": "az monitor diagnostic-settings list \\\n  --resource \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default\" \\\n  --output json\n",
  "query_output": "[\n  {\n    \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-audit-20260410t121838z\",\n    \"logs\": [\n      {\n        \"categoryGroup\": \"audit\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"categoryGroup\": \"allLogs\",\n        \"enabled\": false,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"metrics\": [\n      {\n        \"category\": \"Capacity\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"Transaction\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"name\": \"cfi-blob-diag-audit-20260410t121838z\",\n    \"resourceGroup\": \"cfi_test_20260410t121838z\",\n    \"type\": \"Microsoft.Insights/diagnosticSettings\",\n    \"workspaceId\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z\"\n  },\n  {\n    \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-cn04-20260410t121838z\",\n    \"logs\": [\n      {\n        \"category\": \"StorageRead\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"StorageWrite\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"StorageDelete\",\n        \"enabled\": false,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"metrics\": [\n      {\n        \"category\": \"Capacity\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"Transaction\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"name\": \"cfi-blob-diag-cn04-20260410t121838z\",\n    \"resourceGroup\": \"cfi_test_20260410t121838z\",\n    \"type\": \"Microsoft.Insights/diagnosticSettings\",\n    \"workspaceId\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z\"\n  }\n]\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.value[*].properties.logs[?(@.category==\"StorageRead\")].enabled",
      "expected_values": [],
      "validation_rule": "^true$",
      "description": "Validates that StorageRead logging is enabled for the blob service. StorageRead captures blob download, metadata read, and list operations with full caller identity and timestamp. If no diagnostic settings exist, this check will fail.\n",
      "actual_value": "[]",
      "passed": false
    }
  ]
}
Scenario: Verify data read operations are logged with identity and timestamp @PerService @CCC.Core @CCC.Core.CN04 @tlp-red @Behavioural @object-storage
Given a cloud api for "{Instance}" in "api"42µs
Given I call "{api}" with "GetServiceAPI" using argument "object-storage"43µs
And I refer to "{result}" as "storage"23µs
Given I call "{api}" with "GetServiceAPI" using argument "logging"29µs
And I refer to "{result}" as "loggingService"21µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-read-logging-object={Timestamp}.txt", and "test data for read logging verification"406ms
Then "{result}" is not an error42µs
And I refer to "{result}" as "createResult"29µs
When I call "{storage}" with "ReadObject" using arguments "{ResourceName}" and "test-read-logging-object={Timestamp}.txt"404ms
Then "{result}" is not an error36µs
And I refer to "{result}" as "readResult"25µs
And I attach "{readResult}" to the test output as "Object Read Result"58µs
And we wait for a period of "10000" ms10s
When I call "{loggingService}" with "QueryDataReadLogs" using arguments "{ResourceName}" and "{20}"18s
Then "{result}" is not an error27µs
And I refer to "{result}" as "readLogs"16µs
And I attach "{readLogs}" to the test output as "Data Read Logs"40µs
Then "{readLogs}" is an array of objects with at least the following contents35µs
result
Succeeded
expected row not found: map[result:Succeeded]
📎 Attachments:
Object Read Result
View JSON (269 bytes)
{"ID":"test-read-logging-object=1775823922682.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"test-read-logging-object=1775823922682.txt","Size":39,"Data":["test data for read logging verification"],"Encryption":"","EncryptionAlgorithm":"","VersionID":""}
Data Read Logs
View JSON (4 bytes)
null
Feature: CCC.Core.CN05.AR01 - Block Unauthorized Data Modification
Scenario: Service prevents data modification by user with no access @PerService @CCC.Core @CCC.Core.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Destructive @Behavioural @object-storage
Given a cloud api for "{Instance}" in "api"43µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"45µs
And I refer to "{result}" as "storage"13µs
And I call "{api}" with "GetServiceAPI" using argument "iam"17µs
And I refer to "{result}" as "iamService"20µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-access", "{UID}", and "none"2s
And I refer to "{result}" as "testUserNoAccess"24µs
And I attach "{result}" to the test output as "no-access-user-identity.json"31µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"70µs
And "{result}" is not an error37µs
expected {result} to not be an error, but got: Error calling {api}.GetServiceAPIWithIdentity: reflect: Call using *fmt.wrapError as type *iam.Identity
And I refer to "{result}" as "userStorage"21µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-cn05-unauthorized-modify={Timestamp}.txt", and "unauthorized data"28µs
Then "{result}" is an error16µs
And I attach "{result}" to the test output as "no-access-create-error.txt"24µs
📎 Attachments:
no-access-user-identity.json
View Content (646 bytes)
failed to create service principal: graph API request failed with status 400: {"error":{"code":"Request_BadRequest","message":"The appId '57a14b3b-5283-414c-b191-0bf3b3ae4620' of the service principal does not reference a valid application object.","details":[{"code":"NoBackingApplicationObject","message":"The appId '57a14b3b-5283-414c-b191-0bf3b3ae4620' of the service principal does not reference a valid application object.","target":"appId","blockedWord":"","prefix":"","suffix":""}],"innerError":{"date":"2026-04-10T12:25:53","request-id":"5f155da1-84cc-42ec-99b2-db773ce32609","client-request-id":"5f155da1-84cc-42ec-99b2-db773ce32609"}}}
Scenario: Service allows data modification by user with write access @PerService @CCC.Core @CCC.Core.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Destructive @Behavioural @object-storage
Given a cloud api for "{Instance}" in "api"40µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"36µs
And I refer to "{result}" as "storage"23µs
And I call "{api}" with "GetServiceAPI" using argument "iam"26µs
And I refer to "{result}" as "iamService"20µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write-access", "{UID}", and "write"20s
And I refer to "{result}" as "testUserWrite"42µs
And I attach "{result}" to the test output as "write-user-identity.json"68µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"3s
And "{result}" is not an error39µs
And I refer to "{result}" as "userStorage"22µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-cn05-authorized-modify={Timestamp}.txt", and "authorized data"26ms
Then "{result}" is not an error30µs
And I attach "{result}" to the test output as "write-create-object-result.json"36µs
📎 Attachments:
write-user-identity.json
View JSON (900 bytes)
{"UserName":"test-user-write-access","Provider":"azure","Credentials":{"app_object_id":"01167efc-65cc-42cc-ba2d-85d0e5dd52c1","client_id":"813bfec6-3aa6-4682-9481-a57427d15afc","client_secret":"VsF8Q~sEXGJ1dD~eOp9NpWbEt7eaaABcoz1dOc-Q","display_name":"CCC-Test-test-user-write-access","object_id":"02a91a17-21d7-4600-9912-bee65445add9","secret_id":"415b63ba-9d18-4e6b-a3fb-33a3efc478f3","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-write-access\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"write\", \"role\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe\"}"}
write-create-object-result.json
View JSON (294 bytes)
{"ID":"test-cn05-authorized-modify=1775823953748.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"test-cn05-authorized-modify=1775823953748.txt","Size":15,"Data":["authorized data"],"Encryption":"Microsoft","EncryptionAlgorithm":"AES256","VersionID":"2026-04-10T12:26:16.7300192Z"}
Scenario: Storage is not configured for public write access @PerService @CCC.Core @CCC.Core.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"57µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"38µs
And I refer to "{result}" as "storage"21µs
And I call "{api}" with "GetServiceAPI" using argument "iam"26µs
And I refer to "{result}" as "iamService"22µs
When I attempt policy check "object-storage-block-public-write-access" for control "CCC.Core.CN05" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true39µs
📎 Attachments:
policy-result-object-storage-block-public-write-access.json
View JSON (5681 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN05/AR01/object-storage-block-public-write-access/azure.yaml",
  "name": "Azure Storage Block Blob Public Access",
  "service_type": "object-storage",
  "requirement_text": "When an attempt is made to modify data on the service or a child resource, the service MUST block requests from unauthorized entities. Disabling blob public access ensures the storage account is not world-writable.\n",
  "validity_score": 8,
  "validity_commentary": "This query validates that Azure Storage has blob public access disabled. When allowBlobPublicAccess is false, anonymous access to blobs is not permitted, reducing the risk of unauthorized data modification. Strengths: - Storage account-level setting applies to all containers - Overrides any public access settings on individual containers - Aligns with CIS and Prowler benchmarks Limitations: - Does not validate shared key or SAS token restrictions - Behavioral testing (CN05-AR01) verifies unauthorized modification is blocked at runtime - Network rules (e.g. allow from VNet only) are checked separately\n",
  "query_template": "az storage account show \\\n  --name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --output json\n",
  "query_executed": "az storage account show \\\n  --name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --output json\n",
  "query_output": "{\n  \"accessTier\": \"Hot\",\n  \"accountMigrationInProgress\": null,\n  \"allowBlobPublicAccess\": false,\n  \"allowCrossTenantReplication\": false,\n  \"allowSharedKeyAccess\": true,\n  \"allowedCopyScope\": null,\n  \"azureFilesIdentityBasedAuthentication\": null,\n  \"blobRestoreStatus\": null,\n  \"creationTime\": \"2026-04-10T12:19:49.555461+00:00\",\n  \"customDomain\": null,\n  \"defaultToOAuthAuthentication\": false,\n  \"dnsEndpointType\": \"Standard\",\n  \"dualStackEndpointPreference\": null,\n  \"enableExtendedGroups\": null,\n  \"enableHttpsTrafficOnly\": true,\n  \"enableNfsV3\": false,\n  \"encryption\": {\n    \"encryptionIdentity\": null,\n    \"keySource\": \"Microsoft.Storage\",\n    \"keyVaultProperties\": null,\n    \"requireInfrastructureEncryption\": null,\n    \"services\": {\n      \"blob\": {\n        \"enabled\": true,\n        \"keyType\": \"Account\",\n        \"lastEnabledTime\": \"2026-04-10T12:19:49.919443+00:00\"\n      },\n      \"file\": {\n        \"enabled\": true,\n        \"keyType\": \"Account\",\n        \"lastEnabledTime\": \"2026-04-10T12:19:49.919443+00:00\"\n      },\n      \"queue\": null,\n      \"table\": null\n    }\n  },\n  \"extendedLocation\": null,\n  \"failoverInProgress\": null,\n  \"geoPriorityReplicationStatus\": null,\n  \"geoReplicationStats\": null,\n  \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\",\n  \"identity\": {\n    \"principalId\": null,\n    \"tenantId\": null,\n    \"type\": \"None\",\n    \"userAssignedIdentities\": null\n  },\n  \"immutableStorageWithVersioning\": null,\n  \"isHnsEnabled\": false,\n  \"isLocalUserEnabled\": false,\n  \"isSftpEnabled\": false,\n  \"isSkuConversionBlocked\": null,\n  \"keyCreationTime\": {\n    \"key1\": \"2026-04-10T12:19:49.913275+00:00\",\n    \"key2\": \"2026-04-10T12:19:49.913275+00:00\"\n  },\n  \"keyPolicy\": null,\n  \"kind\": \"StorageV2\",\n  \"largeFileSharesState\": null,\n  \"lastGeoFailoverTime\": null,\n  \"location\": \"eastus\",\n  \"minimumTlsVersion\": \"TLS1_2\",\n  \"name\": \"stgcfi20260410t121838z\",\n  \"networkRuleSet\": {\n    \"bypass\": \"AzureServices\",\n    \"defaultAction\": \"Allow\",\n    \"ipRules\": [],\n    \"ipv6Rules\": [],\n    \"resourceAccessRules\": [],\n    \"virtualNetworkRules\": []\n  },\n  \"placement\": null,\n  \"primaryEndpoints\": {\n    \"blob\": \"https://stgcfi20260410t121838z.blob.core.windows.net/\",\n    \"dfs\": \"https://stgcfi20260410t121838z.dfs.core.windows.net/\",\n    \"file\": \"https://stgcfi20260410t121838z.file.core.windows.net/\",\n    \"internetEndpoints\": null,\n    \"ipv6Endpoints\": null,\n    \"microsoftEndpoints\": null,\n    \"queue\": \"https://stgcfi20260410t121838z.queue.core.windows.net/\",\n    \"table\": \"https://stgcfi20260410t121838z.table.core.windows.net/\",\n    \"web\": \"https://stgcfi20260410t121838z.z13.web.core.windows.net/\"\n  },\n  \"primaryLocation\": \"eastus\",\n  \"privateEndpointConnections\": [],\n  \"provisioningState\": \"Succeeded\",\n  \"publicNetworkAccess\": \"Enabled\",\n  \"resourceGroup\": \"cfi_test_20260410t121838z\",\n  \"routingPreference\": null,\n  \"sasPolicy\": null,\n  \"secondaryEndpoints\": null,\n  \"secondaryLocation\": \"westus\",\n  \"sku\": {\n    \"name\": \"Standard_GRS\",\n    \"tier\": \"Standard\"\n  },\n  \"statusOfPrimary\": \"available\",\n  \"statusOfSecondary\": \"available\",\n  \"storageAccountSkuConversionStatus\": null,\n  \"tags\": {\n    \"ccc_compliance_test\": \"1775823906\"\n  },\n  \"type\": \"Microsoft.Storage/storageAccounts\",\n  \"zones\": null\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.allowBlobPublicAccess",
      "expected_values": [
        "false"
      ],
      "validation_rule": "^false$",
      "description": "Validates that blob public access is disabled at the storage account level. When false, anonymous access is not permitted regardless of container ACLs. This is a necessary precondition for blocking unauthorized data modification.\n",
      "actual_value": "[false]",
      "passed": true
    }
  ]
}
Feature: CCC.Core.CN05.AR02 - Block Unauthorized Administrative Access
Scenario: Service prevents administrative action (creating a new bucket) by user with no access @PerService @CCC.Core @CCC.Core.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Destructive @Behavioural @object-storage
Given a cloud api for "{Instance}" in "api"42µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"40µs
And I refer to "{result}" as "storage"21µs
And I call "{api}" with "GetServiceAPI" using argument "iam"27µs
And I refer to "{result}" as "iamService"20µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-access", "{UID}", and "none"3s
And I refer to "{result}" as "testUserNoAccess"35µs
And I attach "{result}" to the test output as "no-admin-user-identity.json"52µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"181µs
And "{result}" is not an error29µs
And I refer to "{result}" as "userStorage"21µs
When I call "{userStorage}" with "CreateBucket" using argument "test-cn05-unauthorized-admin-container"196ms
Then "{result}" is an error39µs
And I attach "{result}" to the test output as "no-admin-create-bucket-error.txt"51µs
📎 Attachments:
no-admin-user-identity.json
View JSON (752 bytes)
{"UserName":"test-user-no-access","Provider":"azure","Credentials":{"app_object_id":"7d0dc390-05d9-4ad8-8713-509d4fb76bfe","client_id":"57a14b3b-5283-414c-b191-0bf3b3ae4620","client_secret":"GHc8Q~aEOFLSYXlGBm.iIoMHrTkwD4oEMUxQJcso","display_name":"CCC-Test-test-user-no-access","object_id":"020b7851-f5c3-417a-a197-6eae3dbfd3af","secret_id":"854f9164-a183-47b4-8be5-0a2f90832a8f","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-no-access\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"none\", \"role\": \"\"}"}
no-admin-create-bucket-error.txt
View Content (1253 bytes)
failed to create container: failed to create container test-cn05-unauthorized-admin-container: ClientSecretCredential authentication failed. 
POST https://login.microsoftonline.com/fa193ac0-9c06-4111-bf55-341e4db193d3/oauth2/v2.0/token
--------------------------------------------------------------------------------
RESPONSE 401: 401 Unauthorized
--------------------------------------------------------------------------------
{
  "error": "invalid_client",
  "error_description": "AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app '57a14b3b-5283-414c-b191-0bf3b3ae4620'. Trace ID: b40a62d2-879d-48e5-ba2d-b8caa4005500 Correlation ID: b27c07a7-f65c-4709-8949-45dc2fc453d9 Timestamp: 2026-04-10 12:26:20Z",
  "error_codes": [
    7000215
  ],
  "timestamp": "2026-04-10 12:26:20Z",
  "trace_id": "b40a62d2-879d-48e5-ba2d-b8caa4005500",
  "correlation_id": "b27c07a7-f65c-4709-8949-45dc2fc453d9",
  "error_uri": "https://login.microsoftonline.com/error?code=7000215"
}
--------------------------------------------------------------------------------
To troubleshoot, visit https://aka.ms/azsdk/go/identity/troubleshoot#client-secret
Scenario: Service prevents administrative action (creating a new bucket) by user with read-only access @PerService @CCC.Core @CCC.Core.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Destructive @Behavioural @object-storage
Given a cloud api for "{Instance}" in "api"41µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"38µs
And I refer to "{result}" as "storage"21µs
And I call "{api}" with "GetServiceAPI" using argument "iam"28µs
And I refer to "{result}" as "iamService"20µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read-only-admin", "{UID}", and "read"19s
And I refer to "{result}" as "testUserRead"30µs
And I attach "{result}" to the test output as "read-only-admin-user-identity.json"58µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{false}"234µs
And "{result}" is not an error37µs
And I refer to "{result}" as "userStorage"26µs
When I call "{userStorage}" with "CreateBucket" using argument "test-cn05-read-only-create-container"4m0s
Then "{result}" is an error42µs
And I attach "{result}" to the test output as "read-only-create-bucket-error.txt"43µs
📎 Attachments:
read-only-admin-user-identity.json
View JSON (908 bytes)
{"UserName":"test-user-read-only-admin","Provider":"azure","Credentials":{"app_object_id":"bab0b846-eb38-4161-80c9-faa8b722e366","client_id":"af19e659-14d8-46c6-9513-e207cd2b4ea9","client_secret":"ntV8Q~ozHdMbOy4W7fk1TCCw4k2FhJGhNK5TVcBN","display_name":"CCC-Test-test-user-read-only-admin","object_id":"aa45ba4e-089f-4c8e-9f4c-b58f1fc3c678","secret_id":"52370475-3974-49fb-a2c5-f654a05961d0","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-read-only-admin\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"read\", \"role\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1\"}"}
read-only-create-bucket-error.txt
View Content (786 bytes)
failed to create container: failed to create container test-cn05-read-only-create-container: PUT https://stgcfi20260410t121838z.blob.core.windows.net/test-cn05-read-only-create-container
--------------------------------------------------------------------------------
RESPONSE 403: 403 This request is not authorized to perform this operation.
ERROR CODE: AuthorizationFailure
--------------------------------------------------------------------------------
AuthorizationFailureThis request is not authorized to perform this operation.
RequestId:c3b83674-001e-00d9-5fe5-c8133a000000
Time:2026-04-10T12:30:40.8128528Z
--------------------------------------------------------------------------------
Scenario: Service allows administrative action (creating a new bucket) by user with admin access @PerService @CCC.Core @CCC.Core.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Behavioural @object-storage
Given a cloud api for "{Instance}" in "api"31µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"40µs
And I refer to "{result}" as "storage"25µs
And I call "{api}" with "GetServiceAPI" using argument "iam"26µs
And I refer to "{result}" as "iamService"20µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-admin-access", "{UID}", and "admin"22s
And I refer to "{result}" as "testUserAdmin"37µs
And I attach "{result}" to the test output as "admin-user-identity.json"55µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserAdmin}", and "{true}"56s
And "{result}" is not an error32µs
expected {result} to not be an error, but got: user provisioning validation failed: user permissions validation timed out after 12 attempts: credentials not ready for Azure Blob Storage access: failed to list containers: GET https://stgcfi20260410t121838z.blob.core.windows.net/ -------------------------------------------------------------------------------- RESPONSE 403: 403 This request is not authorized to perform this operation using this permission. ERROR CODE: AuthorizationPermissionMismatch -------------------------------------------------------------------------------- AuthorizationPermissionMismatchThis request is not authorized to perform this operation using this permission. RequestId:c3b8d84b-001e-00d9-09e6-c8133a000000 Time:2026-04-10T12:31:58.7596818Z --------------------------------------------------------------------------------
And I refer to "{result}" as "userStorage"12µs
When I call "{userStorage}" with "CreateBucket" using argument "test-cn05-authorized-admin-container"15µs
Then "{result}" is not an error14µs
And I attach "{result}" to the test output as "admin-create-bucket-result.json"15µs
And I call "{storage}" with "DeleteBucket" using argument "test-cn05-authorized-admin-container"16µs
📎 Attachments:
admin-user-identity.json
View JSON (900 bytes)
{"UserName":"test-user-admin-access","Provider":"azure","Credentials":{"app_object_id":"1c7f39bb-bee7-4944-bef3-a478b06ac751","client_id":"f7c9ba85-b9f4-40ed-a4d8-77dfca8cc89f","client_secret":"zOT8Q~-MBmRWmmllJ95Ox7~4VVOBcV4R.DZWabOK","display_name":"CCC-Test-test-user-admin-access","object_id":"ecbce41c-1033-44f7-9a08-3ed0c2848996","secret_id":"36c30afc-1e56-4ed0-a819-0898490bf377","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-admin-access\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"admin\", \"role\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b\"}"}
Scenario: Unauthorized administrative access is blocked @PerService @CCC.Core @CCC.Core.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"30µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"27µs
And I refer to "{result}" as "storage"35µs
And I call "{api}" with "GetServiceAPI" using argument "iam"25µs
And I refer to "{result}" as "iamService"21µs
Then no-op required16µs
Feature: CCC.Core.CN05.AR03 - Block Cross-Tenant Access
Scenario: Cross-tenant access is blocked without explicit allowlist @PerService @CCC.Core @CCC.Core.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"36µs
When I attempt policy check "object-storage-cross-tenant-block" for control "CCC.Core.CN05" assessment requirement "AR03" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true38µs
📎 Attachments:
policy-result-object-storage-cross-tenant-block.json
View JSON (3130 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN05/AR03/object-storage-cross-tenant-block/azure.yaml",
  "name": "Azure Storage Cross-Tenant Replication Block",
  "service_type": "object-storage",
  "requirement_text": "When administrative access or configuration change is attempted on the service or a child resource in a multi-tenant environment, the service MUST refuse requests across tenant boundaries unless the origin is explicitly included in a pre-approved allowlist.\n",
  "validity_score": 8,
  "validity_commentary": "This query validates Azure Storage object replication against permitted-destination-storage-accounts. When allowCrossTenantReplication is false, no cross-tenant replication is possible (pass). When true, all replication policy destinations must be in the permitted list. Strengths: - Directly queries replication policies and destination accounts - Uses provider-specific allowlist for precise control - Aligns with CIS and Prowler benchmarks Limitations: - Does not validate cross-tenant access via other mechanisms (e.g. SAS) - Destination format (name vs resource ID) must match Azure API response\n",
  "query_template": "ALLOW_XT=$(az storage account show \\\n  --name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"properties.allowCrossTenantReplication\" --output tsv 2\u003e/dev/null || echo \"false\")\nif [ \"$ALLOW_XT\" != \"true\" ]; then\n  echo '{\"destinationAccounts\": []}'\nelse\n  az rest --method get \\\n    --url \"https://management.azure.com/subscriptions/${AzureSubscriptionID}/resourceGroups/${AzureResourceGroup}/providers/Microsoft.Storage/storageAccounts/${AzureStorageAccount}/objectReplicationPolicies?api-version=2025-06-01\" \\\n    --query \"{destinationAccounts: value[].properties.destinationAccount}\" -o json 2\u003e/dev/null || echo '{\"destinationAccounts\": []}'\nfi\n",
  "query_executed": "ALLOW_XT=$(az storage account show \\\n  --name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"properties.allowCrossTenantReplication\" --output tsv 2\u003e/dev/null || echo \"false\")\nif [ \"$ALLOW_XT\" != \"true\" ]; then\n  echo '{\"destinationAccounts\": []}'\nelse\n  az rest --method get \\\n    --url \"https://management.azure.com/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/objectReplicationPolicies?api-version=2025-06-01\" \\\n    --query \"{destinationAccounts: value[].properties.destinationAccount}\" -o json 2\u003e/dev/null || echo '{\"destinationAccounts\": []}'\nfi\n",
  "query_output": "{\"destinationAccounts\": []}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.destinationAccounts",
      "expected_values": [],
      "validation_rule": "",
      "description": "Validates that when cross-tenant replication is enabled, all destination storage accounts are in permitted-destination-storage-accounts.\n",
      "actual_value": "[]",
      "passed": true
    }
  ]
}
Feature: CCC.Core.CN05.AR04 - Block Unauthorized External Data Requests
Scenario: External unauthorized data requests are blocked @PerService @CCC.Core @CCC.Core.CN05 @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"30µs
When I attempt policy check "object-storage-block-public-read" for control "CCC.Core.CN05" assessment requirement "AR04" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true35µs
📎 Attachments:
policy-result-object-storage-block-public-read.json
View JSON (2048 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN05/AR04/object-storage-block-public-read/azure.yaml",
  "name": "Azure Storage Block External Unauthorized Data Requests",
  "service_type": "object-storage",
  "requirement_text": "Data requests from outside the trust perimeter MUST be blocked so that data exfiltration is prevented. Only authorised identities may read data.\n",
  "validity_score": 8,
  "validity_commentary": "This query validates that Azure Storage has blob public access disabled. When allowBlobPublicAccess is false, anonymous read access to blobs is not permitted, blocking external unauthorized data requests. Strengths: - Storage account-level setting applies to all containers and blobs - Overrides any public access settings on individual containers - Aligns with CIS and Prowler benchmarks Limitations: - Does not validate network rules (e.g. allow from VNet only) - Does not validate shared key or SAS token restrictions - Behavioral testing verifies unauthorized read is blocked at runtime\n",
  "query_template": "az storage account show \\\n  --name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"{allowBlobPublicAccess:allowBlobPublicAccess}\" \\\n  --output json\n",
  "query_executed": "az storage account show \\\n  --name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"{allowBlobPublicAccess:allowBlobPublicAccess}\" \\\n  --output json\n",
  "query_output": "{\n  \"allowBlobPublicAccess\": false\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.allowBlobPublicAccess",
      "expected_values": [
        "false"
      ],
      "validation_rule": "^false$",
      "description": "Validates that blob public access is disabled at the storage account level. When false, anonymous read access is not permitted, blocking external unauthorized data requests to containers and blobs.\n",
      "actual_value": "[false]",
      "passed": true
    }
  ]
}
Feature: CCC.Core.CN05.AR05 - Hide Service Existence from External Requests
Scenario: External requests do not reveal service existence @PerService @CCC.Core @CCC.Core.CN05 @tlp-red @Policy @NotTested @object-storage
Given a cloud api for "{Instance}" in "api"46µs
Then no-op required26µs
Feature: CCC.Core.CN05.AR06 - Block All Unauthorized Requests
Scenario: Service prevents data read by user with no access @PerService @CCC.Core @CCC.Core.CN05 @tlp-green @tlp-amber @tlp-red @Destructive @Behavioural @Duplicate @object-storage
Given a cloud api for "{Instance}" in "api"46µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"39µs
And I refer to "{result}" as "storage"24µs
And I call "{api}" with "GetServiceAPI" using argument "iam"28µs
And I refer to "{result}" as "iamService"27µs
Then no-op required23µs
Scenario: All unauthorized requests are blocked @PerService @CCC.Core @CCC.Core.CN05 @tlp-green @tlp-amber @tlp-red @Policy @Duplicate @object-storage
Given a cloud api for "{Instance}" in "api"44µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"37µs
And I refer to "{result}" as "storage"23µs
And I call "{api}" with "GetServiceAPI" using argument "iam"30µs
And I refer to "{result}" as "iamService"25µs
Then no-op required24µs
Feature: CCC.Core.CN06.AR01 - Resource Location Compliance
Scenario: Object storage region compliance @PerService @CCC.Core @CCC.Core.CN06 @tlp-green @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"42µs
When I attempt policy check "object-storage-region" for control "CCC.Core.CN06" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true35µs
📎 Attachments:
policy-result-object-storage-region.json
View JSON (2029 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN06/AR01/object-storage-region/azure.yaml",
  "name": "Azure Storage Account Region Compliance",
  "service_type": "object-storage",
  "requirement_text": "When the service is running, its region and availability zone MUST be included  in a list of explicitly trusted or approved locations within the trust perimeter.\n",
  "validity_score": 8,
  "validity_commentary": "This query validates that Azure Storage Accounts are deployed in approved regions. Storage accounts are regional resources, making region validation straightforward. Strengths: - Directly queries storage account location - Storage account region cannot be changed after creation - Easy to validate against an approved region list Limitations: - Approved regions must be defined externally (not in this query) - Does not validate geo-redundant storage secondary regions - Blob replication destinations are checked separately (CN10)\n",
  "query_template": "az storage account show \\\n  --name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"{location: location}\" \\\n  --output json\n",
  "query_executed": "az storage account show \\\n  --name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"{location: location}\" \\\n  --output json\n",
  "query_output": "{\n  \"location\": \"eastus\"\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.location",
      "expected_values": [
        "westeurope",
        "northeurope",
        "eastus",
        "eastus2",
        "westus",
        "westus2"
      ],
      "validation_rule": "^(eastus|eastus2|westus|westus2|westeurope|northeurope)$",
      "description": "Validates that the storage account is located in an approved region. The expected_values list should be customized to match your organization's approved Azure regions.\n",
      "actual_value": "[eastus]",
      "passed": true
    }
  ]
}
Scenario: Resource region can be retrieved for compliance verification @PerService @CCC.Core @CCC.Core.CN06 @tlp-green @tlp-amber @tlp-red @Behavioural @object-storage @vpc
Given a cloud api for "{Instance}" in "api"49µs
Given I call "{api}" with "GetServiceAPI" using argument "{ServiceType}"44µs
And I refer to "{result}" as "theService"26µs
When I call "{theService}" with "GetResourceRegion" using argument "{ResourceName}"56µs
Then "{result}" is not an error36µs
expected {result} to not be an error, but got: not yet implemented
And I refer to "{result}" as "region"24µs
And I attach "{region}" to the test output as "Resource Region"28µs
Then "{PermittedRegions}" is an array of objects with at least the following contents25µs
value
{region}
Feature: CCC.Core.CN06.AR02 - Child Resource Location Compliance
Scenario: Child resource region compliance @PerService @CCC.Core @CCC.Core.CN06 @tlp-clear @tlp-green @tlp-amber @tlp-red @Behavioural @NotTestable @object-storage
Given a cloud api for "{Instance}" in "api"43µs
Then no-op required26µs
Scenario: Child resource region compliance @PerService @CCC.Core @CCC.Core.CN06 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy @NotTestable @object-storage
Given a cloud api for "{Instance}" in "api"44µs
Then no-op required27µs
Feature: CCC.Core.CN07.AR01 - Publish Enumeration Activity Events
Scenario: Enumeration activities publish events to monitored channels @PerService @CCC.Core @CCC.Core.CN07 @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"43µs
When I attempt policy check "enumeration-monitoring-policy" for control "CCC.Core.CN07" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"2s
policy check failed: Azure Storage Enumeration Monitoring Policy Check:
Then "{result}" is true20µs
📎 Attachments:
policy-result-enumeration-monitoring-policy.json
View JSON (4562 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN07/AR01/enumeration-monitoring-policy/azure.yaml",
  "name": "Azure Storage Enumeration Monitoring Policy Check",
  "service_type": "object-storage",
  "requirement_text": "Enumeration activities must be monitored and generate events that can be sent to monitored channels.\n",
  "validity_score": 9,
  "validity_commentary": "This query validates monitoring configuration at the Azure Storage blob service level.\n",
  "query_template": "az monitor diagnostic-settings list \\\n  --resource \"/subscriptions/${AzureSubscriptionID}/resourceGroups/${AzureResourceGroup}/providers/Microsoft.Storage/storageAccounts/${AzureStorageAccount}/blobServices/default\" \\\n  --output json\n",
  "query_executed": "az monitor diagnostic-settings list \\\n  --resource \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default\" \\\n  --output json\n",
  "query_output": "[\n  {\n    \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-audit-20260410t121838z\",\n    \"logs\": [\n      {\n        \"categoryGroup\": \"audit\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"categoryGroup\": \"allLogs\",\n        \"enabled\": false,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"metrics\": [\n      {\n        \"category\": \"Capacity\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"Transaction\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"name\": \"cfi-blob-diag-audit-20260410t121838z\",\n    \"resourceGroup\": \"cfi_test_20260410t121838z\",\n    \"type\": \"Microsoft.Insights/diagnosticSettings\",\n    \"workspaceId\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z\"\n  },\n  {\n    \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-cn04-20260410t121838z\",\n    \"logs\": [\n      {\n        \"category\": \"StorageRead\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"StorageWrite\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"StorageDelete\",\n        \"enabled\": false,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"metrics\": [\n      {\n        \"category\": \"Capacity\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"Transaction\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"name\": \"cfi-blob-diag-cn04-20260410t121838z\",\n    \"resourceGroup\": \"cfi_test_20260410t121838z\",\n    \"type\": \"Microsoft.Insights/diagnosticSettings\",\n    \"workspaceId\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z\"\n  }\n]\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.value[*].properties.logs[?(@.categoryGroup==\"audit\")].enabled",
      "expected_values": [],
      "validation_rule": "^true$",
      "description": "Verifies that diagnostic settings are configured to audit enumeration activities.\n",
      "actual_value": "[]",
      "passed": false
    }
  ]
}
Scenario: Enumeration event publishing cannot be tested automatically @PerService @CCC.Core @CCC.Core.CN07 @tlp-amber @tlp-red @Behavioural @NotTestable @object-storage
Given a cloud api for "{Instance}" in "api"95µs
Then no-op required43µs
Feature: CCC.Core.CN07.AR02 - Log Enumeration Activities
Scenario: Enumeration activities are logged @PerService @CCC.Core @CCC.Core.CN07 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"34µs
When I attempt policy check "enumeration-logging-policy" for control "CCC.Core.CN07" assessment requirement "AR02" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"2s
Then "{result}" is true33µs
📎 Attachments:
policy-result-enumeration-logging-policy.json
View JSON (6422 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN07/AR02/enumeration-logging-policy/azure.yaml",
  "name": "Azure Storage Enumeration Logging Policy Check",
  "service_type": "object-storage",
  "requirement_text": "Enumeration activities must be logged.\n",
  "validity_score": 9,
  "validity_commentary": "This query validates that diagnostic settings are configured for the blob service.\n",
  "query_template": "az monitor diagnostic-settings list \\\n  --resource \"/subscriptions/${AzureSubscriptionID}/resourceGroups/${AzureResourceGroup}/providers/Microsoft.Storage/storageAccounts/${AzureStorageAccount}/blobServices/default\" \\\n  --output json\n",
  "query_executed": "az monitor diagnostic-settings list \\\n  --resource \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default\" \\\n  --output json\n",
  "query_output": "[\n  {\n    \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-audit-20260410t121838z\",\n    \"logs\": [\n      {\n        \"categoryGroup\": \"audit\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"categoryGroup\": \"allLogs\",\n        \"enabled\": false,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"metrics\": [\n      {\n        \"category\": \"Capacity\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"Transaction\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"name\": \"cfi-blob-diag-audit-20260410t121838z\",\n    \"resourceGroup\": \"cfi_test_20260410t121838z\",\n    \"type\": \"Microsoft.Insights/diagnosticSettings\",\n    \"workspaceId\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z\"\n  },\n  {\n    \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-cn04-20260410t121838z\",\n    \"logs\": [\n      {\n        \"category\": \"StorageRead\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"StorageWrite\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"StorageDelete\",\n        \"enabled\": false,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"metrics\": [\n      {\n        \"category\": \"Capacity\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"Transaction\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"name\": \"cfi-blob-diag-cn04-20260410t121838z\",\n    \"resourceGroup\": \"cfi_test_20260410t121838z\",\n    \"type\": \"Microsoft.Insights/diagnosticSettings\",\n    \"workspaceId\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z\"\n  }\n]\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$",
      "expected_values": [],
      "validation_rule": "(category|categoryGroup).*enabled.*true|enabled.*true.*(category|categoryGroup)",
      "description": "Verifies that at least one log category or category group is enabled.\n",
      "actual_value": "[map[id:/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-audit-20260410t121838z logs:[map[categoryGroup:audit enabled:true retentionPolicy:map[days:0 enabled:false]] map[categoryGroup:allLogs enabled:false retentionPolicy:map[days:0 enabled:false]]] metrics:[map[category:Capacity enabled:true retentionPolicy:map[days:0 enabled:false]] map[category:Transaction enabled:true retentionPolicy:map[days:0 enabled:false]]] name:cfi-blob-diag-audit-20260410t121838z resourceGroup:cfi_test_20260410t121838z type:Microsoft.Insights/diagnosticSettings workspaceId:/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z] map[id:/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-cn04-20260410t121838z logs:[map[category:StorageRead enabled:true retentionPolicy:map[days:0 enabled:false]] map[category:StorageWrite enabled:true retentionPolicy:map[days:0 enabled:false]] map[category:StorageDelete enabled:false retentionPolicy:map[days:0 enabled:false]]] metrics:[map[category:Capacity enabled:true retentionPolicy:map[days:0 enabled:false]] map[category:Transaction enabled:true retentionPolicy:map[days:0 enabled:false]]] name:cfi-blob-diag-cn04-20260410t121838z resourceGroup:cfi_test_20260410t121838z type:Microsoft.Insights/diagnosticSettings workspaceId:/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z]]",
      "passed": true
    }
  ]
}
Scenario: Enumeration logging cannot be verified automatically @PerService @CCC.Core @CCC.Core.CN07 @tlp-clear @tlp-green @tlp-amber @tlp-red @Behavioural @NotTestable @object-storage
Given a cloud api for "{Instance}" in "api"43µs
Then no-op required30µs
Feature: CCC.Core.CN08.AR01 - Data Replication and Redundancy
Scenario: Object storage replication compliance @PerService @CCC.Core @CCC.Core.CN08 @tlp-green @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"35µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"48µs
And I refer to "{result}" as "storage"29µs
When I attempt policy check "object-storage-replication" for control "CCC.Core.CN08" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true36µs
📎 Attachments:
policy-result-object-storage-replication.json
View JSON (3075 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN08/AR01/object-storage-replication/azure.yaml",
  "name": "Azure Storage Geo-Redundant Replication Configuration",
  "service_type": "object-storage",
  "requirement_text": "When data is created or modified, the data MUST have a complete and recoverable  duplicate that is stored in a physically separate data center.\n",
  "validity_score": 8,
  "validity_commentary": "This query validates Azure Storage geo-redundant replication configuration. Azure Storage provides automatic geo-replication through GRS, GZRS, or RA-GRS. Strengths: - Replication is automatic and synchronous/asynchronous - Secondary region is automatically selected by Azure (paired region) - RA-GRS/RA-GZRS provides read access to secondary Limitations: - Does not support custom destination region (uses Azure paired regions) - Failover must be manually initiated or use account failover - Object replication for cross-region copies requires separate configuration - LRS and ZRS do not provide geographic redundancy\n",
  "query_template": "az storage account show \\\n  --name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"{sku: sku.name, primaryLocation: primaryLocation, secondaryLocation: secondaryLocation, statusOfSecondary: statusOfSecondary}\" \\\n  --output json\n",
  "query_executed": "az storage account show \\\n  --name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"{sku: sku.name, primaryLocation: primaryLocation, secondaryLocation: secondaryLocation, statusOfSecondary: statusOfSecondary}\" \\\n  --output json\n",
  "query_output": "{\n  \"primaryLocation\": \"eastus\",\n  \"secondaryLocation\": \"westus\",\n  \"sku\": \"Standard_GRS\",\n  \"statusOfSecondary\": \"available\"\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.sku",
      "expected_values": [
        "Standard_GRS",
        "Standard_GZRS",
        "Standard_RAGRS",
        "Standard_RAGZRS"
      ],
      "validation_rule": "^Standard_(GRS|GZRS|RAGRS|RAGZRS)$",
      "description": "Validates that the storage account uses a geo-redundant SKU. GRS/GZRS replicate to a secondary region. RA- variants provide read access. LRS and ZRS do not satisfy cross-region replication requirements.\n",
      "actual_value": "[Standard_GRS]",
      "passed": true
    },
    {
      "jsonpath": "$.secondaryLocation",
      "expected_values": [],
      "validation_rule": "^[a-z]+$",
      "description": "Validates that a secondary location is configured for geo-replication. This is automatically set based on Azure paired regions.\n",
      "actual_value": "[westus]",
      "passed": true
    },
    {
      "jsonpath": "$.statusOfSecondary",
      "expected_values": [
        "available"
      ],
      "validation_rule": "^available$",
      "description": "Verifies the secondary region is available and replication is healthy.\n",
      "actual_value": "[available]",
      "passed": true
    }
  ]
}
Scenario: Bucket data is replicated to physically separate locations @PerService @CCC.Core @CCC.Core.CN08 @tlp-green @tlp-amber @tlp-red @Behavioural @object-storage
Given a cloud api for "{Instance}" in "api"42µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"40µs
And I refer to "{result}" as "storage"16µs
When I call "{storage}" with "GetReplicationStatus" using argument "{ResourceName}"126ms
And I refer to "{result}" as "replicationStatus"38µs
And I refer to "{replicationStatus.Locations}" as "locations"21µs
And I attach "{replicationStatus}" to the test output as "Replication Status"96µs
Then "{locations}" is an array of objects with length "2"27µs
And "{PermittedRegions}" is an array of objects with at least the following contents255µs
value
{locations[0]}
And "{PermittedRegions}" is an array of objects with at least the following contents2ms
value
{locations[1]}
📎 Attachments:
Replication Status
View JSON (95 bytes)
{"Locations":[{"value":"eastus"},{"value":"westus"}],"Status":"Enabled","SyncStatus":"Unknown"}
Feature: CCC.Core.CN08.AR02 - Replication Status Visibility
Scenario: Object storage replication status is visible @PerService @CCC.Core @CCC.Core.CN08 @tlp-green @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"41µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"37µs
And I refer to "{result}" as "storage"19µs
When I attempt policy check "object-storage-replication-status" for control "CCC.Core.CN08" assessment requirement "AR02" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true63µs
📎 Attachments:
policy-result-object-storage-replication-status.json
View JSON (2624 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN08/AR02/object-storage-replication-status/azure.yaml",
  "name": "Azure Storage Replication Status Visibility",
  "service_type": "object-storage",
  "requirement_text": "When data is replicated into a second location, the service MUST be able to accurately represent the replication locations, replication status, and data synchronization status.\n",
  "validity_score": 8,
  "validity_commentary": "This query validates that Azure Storage geo-replication status is visible. Azure Storage provides automatic geo-replication with status reporting. Strengths: - Secondary location is visible in account properties - Replication status is automatically tracked - Last sync time available for RA-GRS/RA-GZRS Limitations: - Replication lag is not guaranteed - Detailed sync metrics require Azure Monitor\n",
  "query_template": "az storage account show \\\n  --name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"{sku: sku.name, primaryLocation: primaryLocation, secondaryLocation: secondaryLocation, statusOfPrimary: statusOfPrimary, statusOfSecondary: statusOfSecondary, lastGeoFailoverTime: geoReplicationStats.lastSyncTime}\" \\\n  --output json\n",
  "query_executed": "az storage account show \\\n  --name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"{sku: sku.name, primaryLocation: primaryLocation, secondaryLocation: secondaryLocation, statusOfPrimary: statusOfPrimary, statusOfSecondary: statusOfSecondary, lastGeoFailoverTime: geoReplicationStats.lastSyncTime}\" \\\n  --output json\n",
  "query_output": "{\n  \"lastGeoFailoverTime\": null,\n  \"primaryLocation\": \"eastus\",\n  \"secondaryLocation\": \"westus\",\n  \"sku\": \"Standard_GRS\",\n  \"statusOfPrimary\": \"available\",\n  \"statusOfSecondary\": \"available\"\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.secondaryLocation",
      "expected_values": [],
      "validation_rule": "^[a-z]+$",
      "description": "Validates that a secondary location is configured and visible, indicating geo-replication is active.\n",
      "actual_value": "[westus]",
      "passed": true
    },
    {
      "jsonpath": "$.statusOfSecondary",
      "expected_values": [
        "available"
      ],
      "validation_rule": "^(available|unavailable)$",
      "description": "Validates that the secondary region status is reported, providing visibility into replication health.\n",
      "actual_value": "[available]",
      "passed": true
    }
  ]
}
Scenario: Replication status can be retrieved for monitoring @PerService @CCC.Core @CCC.Core.CN08 @tlp-green @tlp-amber @tlp-red @Behavioural @object-storage
Given a cloud api for "{Instance}" in "api"51µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"42µs
And I refer to "{result}" as "storage"26µs
When I call "{storage}" with "GetReplicationStatus" using argument "{ResourceName}"206ms
And I refer to "{result}" as "replicationStatus"26µs
And I attach "{replicationStatus}" to the test output as "Replication Status"52µs
And I refer to "{replicationStatus.Locations}" as "locations"30µs
Then "{locations}" is an array of objects with at least the following contents84µs
value
{ReplicationLocations[0]}
{ReplicationLocations[1]}
📎 Attachments:
Replication Status
View JSON (95 bytes)
{"Locations":[{"value":"eastus"},{"value":"westus"}],"Status":"Enabled","SyncStatus":"Unknown"}
Feature: CCC.Core.CN09.AR01 - Access Logging Separation
Scenario: Object storage access logging compliance @PerService @CCC.Core @CCC.Core.CN09 @tlp-green @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"51µs
When I attempt policy check "object-storage-access-logging" for control "CCC.Core.CN09" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"2s
policy check failed: Azure Storage Account Diagnostic Logging Configuration:
Then "{result}" is true27µs
📎 Attachments:
policy-result-object-storage-access-logging.json
View JSON (7799 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN09/AR01/object-storage-access-logging/azure.yaml",
  "name": "Azure Storage Account Diagnostic Logging Configuration",
  "service_type": "object-storage",
  "requirement_text": "When the service is operational, its logs and any child resource logs MUST NOT  be accessible from the resource they record access to.\n",
  "validity_score": 7,
  "validity_commentary": "This query validates that Azure Storage blob diagnostic settings are configured. Azure Storage logs can be sent to Log Analytics, Storage Account, or Event Hub. Strengths: - Validates that diagnostic logging is enabled - Azure Monitor provides centralized logging separate from source - Supports multiple logging destinations Limitations: - Does not validate the specific logging destination - Does not verify Log Analytics workspace configuration - Classic storage analytics logging is deprecated in favor of Azure Monitor - Blob read/write/delete logs require specific category configuration\n",
  "query_template": "az monitor diagnostic-settings list \\\n  --resource \"/subscriptions/${AzureSubscriptionID}/resourceGroups/${AzureResourceGroup}/providers/Microsoft.Storage/storageAccounts/${AzureStorageAccount}/blobServices/default\" \\\n  --output json\n",
  "query_executed": "az monitor diagnostic-settings list \\\n  --resource \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default\" \\\n  --output json\n",
  "query_output": "[\n  {\n    \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-audit-20260410t121838z\",\n    \"logs\": [\n      {\n        \"categoryGroup\": \"audit\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"categoryGroup\": \"allLogs\",\n        \"enabled\": false,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"metrics\": [\n      {\n        \"category\": \"Capacity\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"Transaction\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"name\": \"cfi-blob-diag-audit-20260410t121838z\",\n    \"resourceGroup\": \"cfi_test_20260410t121838z\",\n    \"type\": \"Microsoft.Insights/diagnosticSettings\",\n    \"workspaceId\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z\"\n  },\n  {\n    \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-cn04-20260410t121838z\",\n    \"logs\": [\n      {\n        \"category\": \"StorageRead\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"StorageWrite\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"StorageDelete\",\n        \"enabled\": false,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"metrics\": [\n      {\n        \"category\": \"Capacity\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      },\n      {\n        \"category\": \"Transaction\",\n        \"enabled\": true,\n        \"retentionPolicy\": {\n          \"days\": 0,\n          \"enabled\": false\n        }\n      }\n    ],\n    \"name\": \"cfi-blob-diag-cn04-20260410t121838z\",\n    \"resourceGroup\": \"cfi_test_20260410t121838z\",\n    \"type\": \"Microsoft.Insights/diagnosticSettings\",\n    \"workspaceId\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z\"\n  }\n]\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.value[0].name",
      "expected_values": [],
      "validation_rule": "^.+$",
      "description": "Validates that at least one diagnostic setting exists for the blob service. The presence of any diagnostic setting indicates logging is configured.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.value[0].name: could not select value, invalid key: expected number but got value (string)"
    },
    {
      "jsonpath": "$",
      "expected_values": [],
      "validation_rule": "(StorageRead.*StorageWrite.*StorageDelete|StorageRead.*StorageDelete.*StorageWrite|StorageWrite.*StorageRead.*StorageDelete|StorageWrite.*StorageDelete.*StorageRead|StorageDelete.*StorageRead.*StorageWrite|StorageDelete.*StorageWrite.*StorageRead)|categoryGroup[^\"]*audit",
      "description": "Validates read/write/delete logging: either individual categories (StorageRead, StorageWrite, StorageDelete) all enabled, or categoryGroup audit (which replaces them).\n",
      "actual_value": "[map[id:/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-audit-20260410t121838z logs:[map[categoryGroup:audit enabled:true retentionPolicy:map[days:0 enabled:false]] map[categoryGroup:allLogs enabled:false retentionPolicy:map[days:0 enabled:false]]] metrics:[map[category:Capacity enabled:true retentionPolicy:map[days:0 enabled:false]] map[category:Transaction enabled:true retentionPolicy:map[days:0 enabled:false]]] name:cfi-blob-diag-audit-20260410t121838z resourceGroup:cfi_test_20260410t121838z type:Microsoft.Insights/diagnosticSettings workspaceId:/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z] map[id:/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourcegroups/cfi_test_20260410t121838z/providers/microsoft.storage/storageaccounts/stgcfi20260410t121838z/blobservices/default/providers/microsoft.insights/diagnosticSettings/cfi-blob-diag-cn04-20260410t121838z logs:[map[category:StorageRead enabled:true retentionPolicy:map[days:0 enabled:false]] map[category:StorageWrite enabled:true retentionPolicy:map[days:0 enabled:false]] map[category:StorageDelete enabled:false retentionPolicy:map[days:0 enabled:false]]] metrics:[map[category:Capacity enabled:true retentionPolicy:map[days:0 enabled:false]] map[category:Transaction enabled:true retentionPolicy:map[days:0 enabled:false]]] name:cfi-blob-diag-cn04-20260410t121838z resourceGroup:cfi_test_20260410t121838z type:Microsoft.Insights/diagnosticSettings workspaceId:/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.OperationalInsights/workspaces/cfi-storage-diag-20260410t121838z]]",
      "passed": true
    }
  ]
}
Feature: CCC.Core.CN09.AR02 - Logs Cannot Be Disabled
Scenario: Disabling logs requires disabling the resource @PerService @CCC.Core @CCC.Core.CN09 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy @NotTestable @object-storage
Given a cloud api for "{Instance}" in "api"152µs
Then no-op required53µs
Feature: CCC.Core.CN09.AR03 - Log Redirection Requires Service Halt
Scenario: Redirecting logs requires halting the resource @PerService @CCC.Core @CCC.Core.CN09 @tlp-amber @tlp-red @Policy @NotTestable @object-storage
Given a cloud api for "{Instance}" in "api"37µs
Then no-op required22µs
Feature: CCC.Core.CN10.AR01 - Replication Destination Trust
Scenario: Object storage replication destination compliance @PerService @CCC.Core @CCC.Core.CN10 @tlp-green @tlp-amber @tlp-red @Policy @object-storage
Given a cloud api for "{Instance}" in "api"37µs
When I attempt policy check "object-storage-replication-destination" for control "CCC.Core.CN10" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true37µs
📎 Attachments:
policy-result-object-storage-replication-destination.json
View JSON (2784 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN10/AR01/object-storage-replication-destination/azure.yaml",
  "name": "Azure Storage Replication Destination Region Validation",
  "service_type": "object-storage",
  "requirement_text": "When data is replicated, the service MUST ensure that replication only occurs  to destinations that are explicitly included within the defined trust perimeter.\n",
  "validity_score": 7,
  "validity_commentary": "This query validates that Azure Storage replication destinations are within approved Azure paired regions. Strengths: - Azure paired regions are pre-defined and documented - Secondary region is automatically set based on primary - Can validate against approved region pairs Limitations: - Azure does not allow custom secondary region selection for GRS - Object replication policies for custom destinations need separate checks - Cross-subscription replication requires additional trust validation\n",
  "query_template": "az storage account show \\\n  --name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"{primaryLocation: primaryLocation, secondaryLocation: secondaryLocation, sku: sku.name}\" \\\n  --output json\n",
  "query_executed": "az storage account show \\\n  --name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"{primaryLocation: primaryLocation, secondaryLocation: secondaryLocation, sku: sku.name}\" \\\n  --output json\n",
  "query_output": "{\n  \"primaryLocation\": \"eastus\",\n  \"secondaryLocation\": \"westus\",\n  \"sku\": \"Standard_GRS\"\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.secondaryLocation",
      "expected_values": [
        "northeurope",
        "westeurope",
        "westus",
        "westus2",
        "eastus",
        "eastus2"
      ],
      "validation_rule": "^(westus|westus2|eastus|eastus2|northeurope|westeurope)$",
      "description": "Validates that the secondary replication location is within the approved list of Azure regions in the trust perimeter. Azure paired regions are automatically determined but should be verified against policy.\n",
      "actual_value": "[westus]",
      "passed": true
    },
    {
      "jsonpath": "$.primaryLocation",
      "expected_values": [
        "eastus2",
        "westus",
        "westus2",
        "westeurope",
        "northeurope",
        "eastus"
      ],
      "validation_rule": "^(eastus|eastus2|westus|westus2|westeurope|northeurope)$",
      "description": "Validates the primary location is in an approved region. The secondary location is determined by Azure based on region pairing.\n",
      "actual_value": "[eastus]",
      "passed": true
    }
  ]
}
Scenario: Replication destination trust cannot be verified automatically @PerService @CCC.Core @CCC.Core.CN10 @tlp-green @tlp-amber @tlp-red @Behavioural @NotTestable @object-storage
Given a cloud api for "{Instance}" in "api"50µs
Then no-op required32µs
Feature: CCC.ObjStor.CN01.AR01
Scenario: Service prevents reading bucket with no access @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN01 @Behavioural
Given a cloud api for "{Instance}" in "api"50µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"48µs
And I refer to "{result}" as "storage"30µs
And I call "{api}" with "GetServiceAPI" using argument "iam"33µs
And I refer to "{result}" as "iamService"24µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-access", "{UID}", and "none"55µs
And I refer to "{result}" as "testUserNoAccess"22µs
And I attach "{result}" to the test output as "no-access-user-identity.json"50µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"39µs
And "{result}" is not an error29µs
And I refer to "{result}" as "userStorage"25µs
When I call "{userStorage}" with "ListObjects" using argument "{ResourceName}"4m1s
Then "{result}" is an error46µs
And I attach "{result}" to the test output as "no-access-list-error.txt"33µs
📎 Attachments:
no-access-user-identity.json
View JSON (752 bytes)
{"UserName":"test-user-no-access","Provider":"azure","Credentials":{"app_object_id":"7d0dc390-05d9-4ad8-8713-509d4fb76bfe","client_id":"57a14b3b-5283-414c-b191-0bf3b3ae4620","client_secret":"GHc8Q~aEOFLSYXlGBm.iIoMHrTkwD4oEMUxQJcso","display_name":"CCC-Test-test-user-no-access","object_id":"020b7851-f5c3-417a-a197-6eae3dbfd3af","secret_id":"854f9164-a183-47b4-8be5-0a2f90832a8f","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-no-access\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"none\", \"role\": \"\"}"}
no-access-list-error.txt
View Content (780 bytes)
failed to list blobs: GET https://stgcfi20260410t121838z.blob.core.windows.net/ccc-test-container-20260410t121838z
--------------------------------------------------------------------------------
RESPONSE 403: 403 This request is not authorized to perform this operation using this permission.
ERROR CODE: AuthorizationPermissionMismatch
--------------------------------------------------------------------------------
AuthorizationPermissionMismatchThis request is not authorized to perform this operation using this permission.
RequestId:c3bb41b7-001e-00d9-69e6-c8133a000000
Time:2026-04-10T12:36:12.2703538Z
--------------------------------------------------------------------------------
Scenario: Service allows reading bucket with read access @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN01 @Behavioural
Given a cloud api for "{Instance}" in "api"31µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"42µs
And I refer to "{result}" as "storage"25µs
And I call "{api}" with "GetServiceAPI" using argument "iam"33µs
And I refer to "{result}" as "iamService"27µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"26s
And I refer to "{result}" as "testUserRead"27µs
And I attach "{result}" to the test output as "read-user-identity.json"46µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{true}"36s
And "{result}" is not an error30µs
And I attach "{result}" to the test output as "read-storage-service.json"49µs
And I refer to "{result}" as "userStorage"29µs
When I call "{userStorage}" with "ListObjects" using argument "{ResourceName}"44ms
Then "{result}" is not an error22µs
And I attach "{result}" to the test output as "read-list-objects-result.json"58µs
📎 Attachments:
read-user-identity.json
View JSON (875 bytes)
{"UserName":"test-user-read","Provider":"azure","Credentials":{"app_object_id":"4a1ae0ee-eb57-4a90-bd62-001deccf2c13","client_id":"0a1ff458-0de3-439a-aa58-613ccac5c99e","client_secret":"EXh8Q~c_BqjDj4QU1gAlmTVfW3edwBzxrHwcZbEl","display_name":"CCC-Test-test-user-read","object_id":"3efae6ca-3b80-4888-ab2f-be2315255403","secret_id":"3019f835-ea12-4eaa-b2cf-0957b11890ed","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-read\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"read\", \"role\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1\"}"}
read-storage-service.json
View JSON (2 bytes)
{}
read-list-objects-result.json
View JSON (694 bytes)
[{"ID":"test-cn05-authorized-modify=1775823953748.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"test-cn05-authorized-modify=1775823953748.txt","Size":15,"Data":null,"Encryption":"","EncryptionAlgorithm":"","VersionID":""},{"ID":"test-encryption-check=1775823899892.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"test-encryption-check=1775823899892.txt","Size":20,"Data":null,"Encryption":"","EncryptionAlgorithm":"","VersionID":""},{"ID":"test-read-logging-object=1775823922682.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"test-read-logging-object=1775823922682.txt","Size":39,"Data":null,"Encryption":"","EncryptionAlgorithm":"","VersionID":""}]
Scenario: Test policy for bucket access control @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN01 @Policy
Given a cloud api for "{Instance}" in "api"81µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"58µs
And I refer to "{result}" as "storage"63µs
And I call "{api}" with "GetServiceAPI" using argument "iam"60µs
And I refer to "{result}" as "iamService"39µs
When I attempt policy check "no-public-access" for control "CCC.ObjStor.CN01" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true34µs
📎 Attachments:
policy-result-no-public-access.json
View JSON (1685 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN01/AR01/no-public-access/azure.yaml",
  "name": "Azure Blob Block Public Access Check",
  "service_type": "object-storage",
  "requirement_text": "The service MUST enforce access control so that only authorised identities can read, list, write, or create objects and buckets.\n",
  "validity_score": 9,
  "validity_commentary": "This query validates that public blob access is disabled at the Azure storage account level, preventing anonymous read access to containers and blobs. Strengths: - Checks account-level public access configuration - Ensures all access requires Azure AD authentication or SAS tokens Limitations: - Does not audit individual RBAC role assignments\n",
  "query_template": "az storage account show \\\n  --name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"{allowBlobPublicAccess:allowBlobPublicAccess}\" \\\n  --output json\n",
  "query_executed": "az storage account show \\\n  --name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"{allowBlobPublicAccess:allowBlobPublicAccess}\" \\\n  --output json\n",
  "query_output": "{\n  \"allowBlobPublicAccess\": false\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.allowBlobPublicAccess",
      "expected_values": [
        "false"
      ],
      "validation_rule": "^false$",
      "description": "Verifies that public blob access is disabled at the account level, requiring authenticated access to all containers and blobs.\n",
      "actual_value": "[false]",
      "passed": true
    }
  ]
}
Feature: CCC.ObjStor.CN01.AR02
Scenario: Service prevents reading object with no access @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN01 @Behavioural
Given a cloud api for "{Instance}" in "api"61µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"43µs
And I refer to "{result}" as "storage"23µs
And I call "{api}" with "GetServiceAPI" using argument "iam"29µs
And I refer to "{result}" as "iamService"26µs
And I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "test content"412ms
And "{result}" is not an error30µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-access", "{UID}", and "none"63µs
And I refer to "{result}" as "testUserNoAccess"30µs
And I attach "{result}" to the test output as "no-access-user-identity.json"64µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"41µs
And "{result}" is not an error34µs
And I refer to "{result}" as "userStorage"23µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"4m0s
Then "{result}" is an error37µs
And I attach "{result}" to the test output as "no-access-read-object-error.txt"38µs
📎 Attachments:
no-access-user-identity.json
View JSON (752 bytes)
{"UserName":"test-user-no-access","Provider":"azure","Credentials":{"app_object_id":"7d0dc390-05d9-4ad8-8713-509d4fb76bfe","client_id":"57a14b3b-5283-414c-b191-0bf3b3ae4620","client_secret":"GHc8Q~aEOFLSYXlGBm.iIoMHrTkwD4oEMUxQJcso","display_name":"CCC-Test-test-user-no-access","object_id":"020b7851-f5c3-417a-a197-6eae3dbfd3af","secret_id":"854f9164-a183-47b4-8be5-0a2f90832a8f","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-no-access\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"none\", \"role\": \"\"}"}
no-access-read-object-error.txt
View Content (843 bytes)
failed to download blob test-object=1775824635388.txt: GET https://stgcfi20260410t121838z.blob.core.windows.net/ccc-test-container-20260410t121838z/test-object=1775824635388.txt
--------------------------------------------------------------------------------
RESPONSE 403: 403 This request is not authorized to perform this operation using this permission.
ERROR CODE: AuthorizationPermissionMismatch
--------------------------------------------------------------------------------
AuthorizationPermissionMismatchThis request is not authorized to perform this operation using this permission.
RequestId:c3bd4981-001e-00d9-7de7-c8133a000000
Time:2026-04-10T12:41:15.9422111Z
--------------------------------------------------------------------------------
Scenario: Service allows reading object with read access @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN01 @Behavioural
Given a cloud api for "{Instance}" in "api"40µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"41µs
And I refer to "{result}" as "storage"15µs
And I call "{api}" with "GetServiceAPI" using argument "iam"32µs
And I refer to "{result}" as "iamService"15µs
And I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "test content"407ms
And "{result}" is not an error31µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"60µs
And I refer to "{result}" as "testUserRead"32µs
And I attach "{result}" to the test output as "read-user-identity.json"64µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{true}"40µs
And "{result}" is not an error32µs
And I attach "{result}" to the test output as "read-storage-service.json"38µs
And I refer to "{result}" as "userStorage"26µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"24ms
Then "{result}" is not an error37µs
And I attach "{result}" to the test output as "read-read-object-result.json"52µs
📎 Attachments:
read-user-identity.json
View JSON (875 bytes)
{"UserName":"test-user-read","Provider":"azure","Credentials":{"app_object_id":"4a1ae0ee-eb57-4a90-bd62-001deccf2c13","client_id":"0a1ff458-0de3-439a-aa58-613ccac5c99e","client_secret":"EXh8Q~c_BqjDj4QU1gAlmTVfW3edwBzxrHwcZbEl","display_name":"CCC-Test-test-user-read","object_id":"3efae6ca-3b80-4888-ab2f-be2315255403","secret_id":"3019f835-ea12-4eaa-b2cf-0957b11890ed","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-read\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"read\", \"role\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1\"}"}
read-storage-service.json
View JSON (2 bytes)
{}
read-read-object-result.json
View JSON (216 bytes)
{"ID":"test-object=1775824875951.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"test-object=1775824875951.txt","Size":12,"Data":["test content"],"Encryption":"","EncryptionAlgorithm":"","VersionID":""}
Scenario: All unauthorized requests are blocked @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN01 @Policy
Given a cloud api for "{Instance}" in "api"48µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"41µs
And I refer to "{result}" as "storage"26µs
And I call "{api}" with "GetServiceAPI" using argument "iam"32µs
And I refer to "{result}" as "iamService"20µs
And I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "test content"407ms
And "{result}" is not an error28µs
When I attempt policy check "object-storage-no-public-principals" for control "CCC.ObjStor.CN01" assessment requirement "AR02" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"2s
policy check failed: Azure Storage RBAC in Use:
Then "{result}" is true26µs
📎 Attachments:
policy-result-object-storage-no-public-principals.json
View JSON (2043 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN01/AR02/object-storage-no-public-principals/azure.yaml",
  "name": "Azure Storage RBAC in Use",
  "service_type": "object-storage",
  "requirement_text": "All unauthorized requests MUST be blocked. Access MUST be controlled by RBAC.\n",
  "validity_score": 7,
  "validity_commentary": "ObjStor.CN01.AR01 already validates no-public-access at the account level. This check validates that RBAC role assignments exist on the storage account, proving access control is role-based rather than anonymous or key-based. Strengths: - Confirms RBAC is configured for the storage account - Role assignments scope access to specific identities Limitations: - Does not validate least-privilege role assignments - Behavioral testing verifies unauthorized access is blocked at runtime\n",
  "query_template": "SCOPE=\"/subscriptions/${AzureSubscriptionID}/resourceGroups/${AzureResourceGroup}/providers/Microsoft.Storage/storageAccounts/${AzureStorageAccount}\"\nCOUNT=$(az role assignment list --scope \"$SCOPE\" --query \"length(@)\" --output tsv 2\u003e/dev/null || echo \"0\")\necho \"{\\\"rbacInUse\\\": $COUNT}\"\n",
  "query_executed": "SCOPE=\"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\"\nCOUNT=$(az role assignment list --scope \"$SCOPE\" --query \"length(@)\" --output tsv 2\u003e/dev/null || echo \"0\")\necho \"{\\\"rbacInUse\\\": $COUNT}\"\n",
  "query_output": "{\"rbacInUse\": 0}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.rbacInUse",
      "expected_values": [],
      "validation_rule": "^[1-9][0-9]*$",
      "description": "Validates that at least one RBAC role assignment exists on the storage account. RBAC is the required access control mechanism; no assignments indicates access may not be properly scoped.\n",
      "actual_value": "[0]",
      "passed": false
    }
  ]
}
Feature: CCC.ObjStor.CN01.AR03
Scenario: Service prevents creating bucket with no access @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN01 @Behavioural
Given a cloud api for "{Instance}" in "api"47µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"49µs
And I refer to "{result}" as "storage"25µs
And I call "{api}" with "GetServiceAPI" using argument "iam"28µs
And I refer to "{result}" as "iamService"14µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-access", "{UID}", and "none"51µs
And I refer to "{result}" as "testUserNoAccess"23µs
And I attach "{result}" to the test output as "no-access-user-identity.json"53µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"37µs
And "{result}" is not an error26µs
And I refer to "{result}" as "userStorage"20µs
When I call "{userStorage}" with "CreateBucket" using argument "test-bucket-no-access"4m0s
Then "{result}" is an error26µs
And I attach "{result}" to the test output as "no-access-create-bucket-error.txt"36µs
📎 Attachments:
no-access-user-identity.json
View JSON (752 bytes)
{"UserName":"test-user-no-access","Provider":"azure","Credentials":{"app_object_id":"7d0dc390-05d9-4ad8-8713-509d4fb76bfe","client_id":"57a14b3b-5283-414c-b191-0bf3b3ae4620","client_secret":"GHc8Q~aEOFLSYXlGBm.iIoMHrTkwD4oEMUxQJcso","display_name":"CCC-Test-test-user-no-access","object_id":"020b7851-f5c3-417a-a197-6eae3dbfd3af","secret_id":"854f9164-a183-47b4-8be5-0a2f90832a8f","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-no-access\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"none\", \"role\": \"\"}"}
no-access-create-bucket-error.txt
View Content (756 bytes)
failed to create container: failed to create container test-bucket-no-access: PUT https://stgcfi20260410t121838z.blob.core.windows.net/test-bucket-no-access
--------------------------------------------------------------------------------
RESPONSE 403: 403 This request is not authorized to perform this operation.
ERROR CODE: AuthorizationFailure
--------------------------------------------------------------------------------
AuthorizationFailureThis request is not authorized to perform this operation.
RequestId:c3bed263-001e-00d9-0ae7-c8133a000000
Time:2026-04-10T12:45:19.4255840Z
--------------------------------------------------------------------------------
Scenario: Service allows creating bucket with write access @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN01 @Behavioural
Given a cloud api for "{Instance}" in "api"40µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"39µs
And I refer to "{result}" as "storage"15µs
And I call "{api}" with "GetServiceAPI" using argument "iam"18µs
And I refer to "{result}" as "iamService"22µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"20s
And I refer to "{result}" as "testUserWrite"58µs
And I attach "{result}" to the test output as "write-user-identity.json"60µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"56s
And "{result}" is not an error46µs
expected {result} to not be an error, but got: user provisioning validation failed: user permissions validation timed out after 12 attempts: credentials not ready for Azure Blob Storage access: failed to list containers: GET https://stgcfi20260410t121838z.blob.core.windows.net/ -------------------------------------------------------------------------------- RESPONSE 403: 403 This request is not authorized to perform this operation using this permission. ERROR CODE: AuthorizationPermissionMismatch -------------------------------------------------------------------------------- AuthorizationPermissionMismatchThis request is not authorized to perform this operation using this permission. RequestId:c3bf4821-001e-00d9-5ae8-c8133a000000 Time:2026-04-10T12:46:34.9851799Z --------------------------------------------------------------------------------
And I attach "{result}" to the test output as "write-storage-service.json"28µs
And I refer to "{result}" as "userStorage"24µs
When I call "{userStorage}" with "CreateBucket" using argument "test-bucket-write"17µs
Then "{result}" is not an error18µs
And I attach "{result}" to the test output as "write-create-bucket-result.json"17µs
And I call "{storage}" with "DeleteBucket" using argument "{result.ID}"18µs
📎 Attachments:
write-user-identity.json
View JSON (879 bytes)
{"UserName":"test-user-write","Provider":"azure","Credentials":{"app_object_id":"84671853-a603-4519-b30b-9a4bec9b5988","client_id":"309f569c-b57e-4ba6-9392-00e51e1d58ce","client_secret":"oSy8Q~5houXWKkxhS2H6SSDf-gGwTUep7u25GaQu","display_name":"CCC-Test-test-user-write","object_id":"84d6c138-77cf-46a0-816d-59e7c7eaee0e","secret_id":"1a7f9bf1-0948-475f-adc6-5e6e7e7ca6a0","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-write\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"write\", \"role\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe\"}"}
Scenario: All unauthorized requests are blocked @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN01 @Policy
Given a cloud api for "{Instance}" in "api"33µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"44µs
And I refer to "{result}" as "storage"25µs
And I call "{api}" with "GetServiceAPI" using argument "iam"27µs
And I refer to "{result}" as "iamService"14µs
When I attempt policy check "object-storage-no-public-principals" for control "CCC.ObjStor.CN01" assessment requirement "AR03" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
policy check failed: Azure Storage RBAC in Use:
Then "{result}" is true27µs
📎 Attachments:
policy-result-object-storage-no-public-principals.json
View JSON (2047 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN01/AR03/object-storage-no-public-principals/azure.yaml",
  "name": "Azure Storage RBAC in Use",
  "service_type": "object-storage",
  "requirement_text": "All unauthorized requests MUST be blocked. Access MUST be controlled by RBAC.\n",
  "validity_score": 7,
  "validity_commentary": "ObjStor.CN01.AR01/AR02 already validate no-public-access at the account level. This check validates that RBAC role assignments exist on the storage account, proving access control is role-based rather than anonymous or key-based. Strengths: - Confirms RBAC is configured for the storage account - Role assignments scope access to specific identities Limitations: - Does not validate least-privilege role assignments - Behavioral testing verifies unauthorized access is blocked at runtime\n",
  "query_template": "SCOPE=\"/subscriptions/${AzureSubscriptionID}/resourceGroups/${AzureResourceGroup}/providers/Microsoft.Storage/storageAccounts/${AzureStorageAccount}\"\nCOUNT=$(az role assignment list --scope \"$SCOPE\" --query \"length(@)\" --output tsv 2\u003e/dev/null || echo \"0\")\necho \"{\\\"rbacInUse\\\": $COUNT}\"\n",
  "query_executed": "SCOPE=\"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\"\nCOUNT=$(az role assignment list --scope \"$SCOPE\" --query \"length(@)\" --output tsv 2\u003e/dev/null || echo \"0\")\necho \"{\\\"rbacInUse\\\": $COUNT}\"\n",
  "query_output": "{\"rbacInUse\": 0}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.rbacInUse",
      "expected_values": [],
      "validation_rule": "^[1-9][0-9]*$",
      "description": "Validates that at least one RBAC role assignment exists on the storage account. RBAC is the required access control mechanism; no assignments indicates access may not be properly scoped.\n",
      "actual_value": "[0]",
      "passed": false
    }
  ]
}
Feature: CCC.ObjStor.CN01.AR04
Scenario: Service prevents writing object with read-only access @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN01 @Behavioural
Given a cloud api for "{Instance}" in "api"45µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"37µs
And I refer to "{result}" as "storage"24µs
And "{result}" is not an error18µs
And I call "{api}" with "GetServiceAPI" using argument "iam"17µs
And I refer to "{result}" as "iamService"25µs
And "{result}" is not an error17µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"38µs
And I refer to "{result}" as "testUserRead"13µs
And I attach "{result}" to the test output as "read-user-identity.json"31µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{true}"35µs
And "{result}" is not an error32µs
And I refer to "{result}" as "userStorage"23µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-write-object={Timestamp}.txt", and "test content"4m1s
Then "{result}" is an error30µs
And I attach "{result}" to the test output as "read-create-object-error.txt"42µs
📎 Attachments:
read-user-identity.json
View JSON (875 bytes)
{"UserName":"test-user-read","Provider":"azure","Credentials":{"app_object_id":"4a1ae0ee-eb57-4a90-bd62-001deccf2c13","client_id":"0a1ff458-0de3-439a-aa58-613ccac5c99e","client_secret":"EXh8Q~c_BqjDj4QU1gAlmTVfW3edwBzxrHwcZbEl","display_name":"CCC-Test-test-user-read","object_id":"3efae6ca-3b80-4888-ab2f-be2315255403","secret_id":"3019f835-ea12-4eaa-b2cf-0957b11890ed","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-read\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"read\", \"role\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1\"}"}
read-create-object-error.txt
View Content (853 bytes)
failed to upload blob test-write-object=1775825196283.txt: PUT https://stgcfi20260410t121838z.blob.core.windows.net/ccc-test-container-20260410t121838z/test-write-object=1775825196283.txt
--------------------------------------------------------------------------------
RESPONSE 403: 403 This request is not authorized to perform this operation using this permission.
ERROR CODE: AuthorizationPermissionMismatch
--------------------------------------------------------------------------------
AuthorizationPermissionMismatchThis request is not authorized to perform this operation using this permission.
RequestId:c3c0b4f1-001e-00d9-3de8-c8133a000000
Time:2026-04-10T12:50:36.8552368Z
--------------------------------------------------------------------------------
Scenario: Service allows writing object with write access @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN01 @Behavioural
Given a cloud api for "{Instance}" in "api"29µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"40µs
And I refer to "{result}" as "storage"22µs
And "{result}" is not an error27µs
And I call "{api}" with "GetServiceAPI" using argument "iam"23µs
And I refer to "{result}" as "iamService"15µs
And "{result}" is not an error18µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"34µs
And I refer to "{result}" as "testUserWrite"29µs
And I attach "{result}" to the test output as "write-user-identity.json"52µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"472ms
And "{result}" is not an error33µs
And I attach "{result}" to the test output as "write-storage-service.json"48µs
And I refer to "{result}" as "userStorage"22µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-write-object={Timestamp}.txt", and "test content"26ms
Then "{result}" is not an error29µs
And I attach "{result}" to the test output as "write-create-object-result.json"48µs
📎 Attachments:
write-user-identity.json
View JSON (879 bytes)
{"UserName":"test-user-write","Provider":"azure","Credentials":{"app_object_id":"84671853-a603-4519-b30b-9a4bec9b5988","client_id":"309f569c-b57e-4ba6-9392-00e51e1d58ce","client_secret":"oSy8Q~5houXWKkxhS2H6SSDf-gGwTUep7u25GaQu","display_name":"CCC-Test-test-user-write","object_id":"84d6c138-77cf-46a0-816d-59e7c7eaee0e","secret_id":"1a7f9bf1-0948-475f-adc6-5e6e7e7ca6a0","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-write\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"write\", \"role\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe\"}"}
write-storage-service.json
View JSON (2 bytes)
{}
write-create-object-result.json
View JSON (271 bytes)
{"ID":"test-write-object=1775825436864.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"test-write-object=1775825436864.txt","Size":12,"Data":["test content"],"Encryption":"Microsoft","EncryptionAlgorithm":"AES256","VersionID":"2026-04-10T12:50:37.3524554Z"}
Scenario: All unauthorized requests are blocked @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN01 @Policy
Given a cloud api for "{Instance}" in "api"33µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"42µs
And I refer to "{result}" as "storage"25µs
And "{result}" is not an error26µs
And I call "{api}" with "GetServiceAPI" using argument "iam"18µs
And I refer to "{result}" as "iamService"16µs
And "{result}" is not an error19µs
When I attempt policy check "object-storage-no-public-principals" for control "CCC.ObjStor.CN01" assessment requirement "AR04" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
policy check failed: Azure Storage RBAC in Use:
Then "{result}" is true29µs
📎 Attachments:
policy-result-object-storage-no-public-principals.json
View JSON (2047 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN01/AR04/object-storage-no-public-principals/azure.yaml",
  "name": "Azure Storage RBAC in Use",
  "service_type": "object-storage",
  "requirement_text": "All unauthorized requests MUST be blocked. Access MUST be controlled by RBAC.\n",
  "validity_score": 7,
  "validity_commentary": "ObjStor.CN01.AR01/AR02 already validate no-public-access at the account level. This check validates that RBAC role assignments exist on the storage account, proving access control is role-based rather than anonymous or key-based. Strengths: - Confirms RBAC is configured for the storage account - Role assignments scope access to specific identities Limitations: - Does not validate least-privilege role assignments - Behavioral testing verifies unauthorized access is blocked at runtime\n",
  "query_template": "SCOPE=\"/subscriptions/${AzureSubscriptionID}/resourceGroups/${AzureResourceGroup}/providers/Microsoft.Storage/storageAccounts/${AzureStorageAccount}\"\nCOUNT=$(az role assignment list --scope \"$SCOPE\" --query \"length(@)\" --output tsv 2\u003e/dev/null || echo \"0\")\necho \"{\\\"rbacInUse\\\": $COUNT}\"\n",
  "query_executed": "SCOPE=\"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\"\nCOUNT=$(az role assignment list --scope \"$SCOPE\" --query \"length(@)\" --output tsv 2\u003e/dev/null || echo \"0\")\necho \"{\\\"rbacInUse\\\": $COUNT}\"\n",
  "query_output": "{\"rbacInUse\": 0}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.rbacInUse",
      "expected_values": [],
      "validation_rule": "^[1-9][0-9]*$",
      "description": "Validates that at least one RBAC role assignment exists on the storage account. RBAC is the required access control mechanism; no assignments indicates access may not be properly scoped.\n",
      "actual_value": "[0]",
      "passed": false
    }
  ]
}
Feature: CCC.ObjStor.CN02.AR01 - Uniform Bucket-Level Access (Consistent Allow)
Scenario: Service enforces uniform bucket-level access by rejecting object-level permissions @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN02 @Behavioural
Given a cloud api for "{Instance}" in "api"41µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"47µs
And I refer to "{result}" as "storage"24µs
And I call "{api}" with "GetServiceAPI" using argument "iam"31µs
And I refer to "{result}" as "iamService"24µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "test data"412ms
Then "{result}" is not an error39µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"60µs
And I refer to "{result}" as "testUserRead"23µs
And I attach "{result}" to the test output as "read-user-identity.json"53µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{true}"38µs
And "{result}" is not an error27µs
And I refer to "{result}" as "userStorage"23µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"23ms
Then "{result}" is not an error38µs
When I call "{storage}" with "SetObjectPermission" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "none"92µs
Then "{result}" is an error28µs
And I attach "{result}" to the test output as "set-object-permission-error.txt"40µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"23ms
Then "{result}" is not an error23µs
📎 Attachments:
read-user-identity.json
View JSON (875 bytes)
{"UserName":"test-user-read","Provider":"azure","Credentials":{"app_object_id":"4a1ae0ee-eb57-4a90-bd62-001deccf2c13","client_id":"0a1ff458-0de3-439a-aa58-613ccac5c99e","client_secret":"EXh8Q~c_BqjDj4QU1gAlmTVfW3edwBzxrHwcZbEl","display_name":"CCC-Test-test-user-read","object_id":"3efae6ca-3b80-4888-ab2f-be2315255403","secret_id":"3019f835-ea12-4eaa-b2cf-0957b11890ed","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-read\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"read\", \"role\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1\"}"}
set-object-permission-error.txt
View Content (111 bytes)
azure Blob Storage does not support object-level permissions - uniform bucket-level access is enforced via RBAC
Scenario: Test policy for uniform access @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN02 @Policy
Given a cloud api for "{Instance}" in "api"31µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"48µs
And I refer to "{result}" as "storage"15µs
And I call "{api}" with "GetServiceAPI" using argument "iam"24µs
And I refer to "{result}" as "iamService"21µs
When I attempt policy check "uniform-bucket-level-access" for control "CCC.ObjStor.CN02" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true37µs
📎 Attachments:
policy-result-uniform-bucket-level-access.json
View JSON (1525 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN02/AR01/uniform-bucket-level-access/azure.yaml",
  "name": "Azure Blob Uniform Access Check",
  "service_type": "object-storage",
  "requirement_text": "The service MUST enforce uniform bucket-level access, preventing ad-hoc object-level permissions.\n",
  "validity_score": 8,
  "validity_commentary": "This query checks if public access is disabled and if the storage account configuration supports uniform access. Azure by default uses RBAC which is uniform at the container level if implemented correctly. Strengths: - Checks for disabling of public access - Focuses on management-level configuration\n",
  "query_template": "az storage account show \\\n  --name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"{publicAccess:allowBlobPublicAccess}\" \\\n  --output json\n",
  "query_executed": "az storage account show \\\n  --name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"{publicAccess:allowBlobPublicAccess}\" \\\n  --output json\n",
  "query_output": "{\n  \"publicAccess\": false\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.publicAccess",
      "expected_values": [
        "false"
      ],
      "validation_rule": "^false$",
      "description": "Verifies that public access to blobs is disabled at the account level.\n",
      "actual_value": "[false]",
      "passed": true
    }
  ]
}
Feature: CCC.ObjStor.CN02.AR02 - Uniform Bucket-Level Access (Consistent Deny)
Scenario: Service enforces uniform bucket-level access denial @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN02 @Behavioural
Given a cloud api for "{Instance}" in "api"38µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"41µs
And I refer to "{result}" as "storage"20µs
And I call "{api}" with "GetServiceAPI" using argument "iam"27µs
And I refer to "{result}" as "iamService"26µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "test data"423ms
Then "{result}" is not an error27µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-access", "{UID}", and "none"59µs
And I refer to "{result}" as "testUserNoAccess"25µs
And I attach "{result}" to the test output as "no-access-user-identity.json"49µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"40µs
And "{result}" is not an error2ms
And I refer to "{result}" as "userStorage"198µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"4m0s
Then "{result}" is an error36µs
When I call "{storage}" with "SetObjectPermission" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "read"40µs
Then "{result}" is an error18µs
And I attach "{result}" to the test output as "set-object-permission-error.txt"28µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"4m0s
Then "{result}" is an error28µs
📎 Attachments:
no-access-user-identity.json
View JSON (752 bytes)
{"UserName":"test-user-no-access","Provider":"azure","Credentials":{"app_object_id":"7d0dc390-05d9-4ad8-8713-509d4fb76bfe","client_id":"57a14b3b-5283-414c-b191-0bf3b3ae4620","client_secret":"GHc8Q~aEOFLSYXlGBm.iIoMHrTkwD4oEMUxQJcso","display_name":"CCC-Test-test-user-no-access","object_id":"020b7851-f5c3-417a-a197-6eae3dbfd3af","secret_id":"854f9164-a183-47b4-8be5-0a2f90832a8f","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-no-access\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"none\", \"role\": \"\"}"}
set-object-permission-error.txt
View Content (111 bytes)
azure Blob Storage does not support object-level permissions - uniform bucket-level access is enforced via RBAC
Scenario: Uniform bucket-level access prevents object-level deny overrides @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN02 @Policy @Duplicate @object-storage
Given a cloud api for "{Instance}" in "api"45µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"55µs
And I refer to "{result}" as "storage"20µs
And I call "{api}" with "GetServiceAPI" using argument "iam"34µs
And I refer to "{result}" as "iamService"17µs
Then no-op required16µs
Feature: CCC.ObjStor.CN03.AR01 - Bucket Soft Delete and Recovery
Scenario: Service supports bucket soft delete and recovery @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN03 @Behavioural
Given a cloud api for "{Instance}" in "api"38µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"29µs
And I refer to "{result}" as "storage"15µs
When I call "{storage}" with "CreateBucket" using argument "ccc-test-soft-delete"450ms
Then "{result}" is not an error48µs
And I refer to "{result}" as "testBucket"32µs
And I attach "{result}" to the test output as "created-bucket.json"66µs
When I call "{storage}" with "DeleteBucket" using argument "ccc-test-soft-delete"454ms
Then "{result}" is not an error30µs
When I call "{storage}" with "ListDeletedBuckets"438ms
Then "{result}" is not an error39µs
And I attach "{result}" to the test output as "deleted-buckets.json"70µs
And "{result}" should have length greater than "0"33µs
When I call "{storage}" with "RestoreBucket" using argument "ccc-test-soft-delete"32µs
Then "{result}" is not an error26µs
When I call "{storage}" with "ListBuckets"26µs
Then "{result}" is not an error29µs
And I attach "{result}" to the test output as "restored-buckets.json"30µs
When I call "{storage}" with "DeleteBucket" using argument "ccc-test-soft-delete"26µs
Then "{result}" is not an error31µs
📎 Attachments:
created-bucket.json
View JSON (77 bytes)
{"ID":"ccc-test-soft-delete","Name":"ccc-test-soft-delete","Region":"eastus"}
deleted-buckets.json
View JSON (79 bytes)
[{"ID":"ccc-test-soft-delete","Name":"ccc-test-soft-delete","Region":"eastus"}]
Scenario: Test policy for bucket soft delete @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN03 @Policy
Given a cloud api for "{Instance}" in "api"46µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"41µs
And I refer to "{result}" as "storage"24µs
When I attempt policy check "bucket-soft-delete" for control "CCC.ObjStor.CN03" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"2s
Then "{result}" is true34µs
📎 Attachments:
policy-result-bucket-soft-delete.json
View JSON (1971 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN03/AR01/bucket-soft-delete/azure.yaml",
  "name": "Azure Blob Soft Delete Check",
  "service_type": "object-storage",
  "requirement_text": "When an object storage bucket deletion is attempted, the bucket MUST be fully recoverable for a set time-frame after deletion is requested.\n",
  "validity_score": 9,
  "validity_commentary": "Azure Blob Storage supports container soft delete and blob soft delete. This check validates that container soft delete is enabled with a retention period greater than zero days. Strengths: - Directly checks container (bucket-equivalent) soft delete configuration - Azure container soft delete is a native feature for recovery Limitations: - Does not verify the exact minimum retention duration required\n",
  "query_template": "az storage account blob-service-properties show \\\n  --account-name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"{containerDeleteRetentionPolicy: containerDeleteRetentionPolicy}\" \\\n  --output json\n",
  "query_executed": "az storage account blob-service-properties show \\\n  --account-name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"{containerDeleteRetentionPolicy: containerDeleteRetentionPolicy}\" \\\n  --output json\n",
  "query_output": "{\n  \"containerDeleteRetentionPolicy\": {\n    \"allowPermanentDelete\": null,\n    \"days\": 7,\n    \"enabled\": true\n  }\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.containerDeleteRetentionPolicy.enabled",
      "expected_values": [
        "true"
      ],
      "validation_rule": "^true$",
      "description": "Verifies that container soft delete is enabled on the storage account, allowing deleted containers to be recovered within the retention period.\n",
      "actual_value": "[true]",
      "passed": true
    }
  ]
}
Feature: CCC.ObjStor.CN03.AR02 - Immutable Bucket Retention Policy
Scenario: Service prevents modification of locked retention policy @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN03 @Behavioural
Given a cloud api for "{Instance}" in "api"43µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"46µs
And I refer to "{result}" as "storage"23µs
When I call "{storage}" with "GetBucketRetentionDurationDays" using argument "{ResourceName}"421ms
Then "{result}" is not an error28µs
And I refer to "{result}" as "originalRetention"18µs
And I attach "{result}" to the test output as "original-retention-days.txt"43µs
And "{result}" should be greater than "0"36µs
expected {result} (0) to be greater than 0
When I call "{storage}" with "SetBucketRetentionDurationDays" using arguments "{ResourceName}" and "1"26µs
Then "{result}" is an error24µs
And I attach "{result}" to the test output as "set-retention-error.txt"64µs
When I call "{storage}" with "GetBucketRetentionDurationDays" using argument "{ResourceName}"17µs
Then "{result}" is not an error14µs
And "{result}" should equal "{originalRetention}"18µs
📎 Attachments:
original-retention-days.txt
View JSON (1 bytes)
0
Scenario: Test policy for immutable bucket retention lock @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN03 @Policy
Given a cloud api for "{Instance}" in "api"43µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"43µs
And I refer to "{result}" as "storage"22µs
When I attempt policy check "bucket-retention-lock" for control "CCC.ObjStor.CN03" assessment requirement "AR02" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"2s
Then "{result}" is true37µs
📎 Attachments:
policy-result-bucket-retention-lock.json
View JSON (2830 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN03/AR02/bucket-retention-lock/azure.yaml",
  "name": "Azure Blob Immutability Policy Lock Check",
  "service_type": "object-storage",
  "requirement_text": "When an attempt is made to modify the retention policy for an object storage bucket, the service MUST prevent the policy from being modified.\n",
  "validity_score": 9,
  "validity_commentary": "This query validates that an immutability policy is set on the Azure storage container (bucket-equivalent) and that it is in a locked state. Strengths: - A locked immutability policy cannot be shortened or deleted - Directly checks the container-level immutability configuration Limitations: - Requires the container name to be known; uses ResourceName as container - Immutability must be applied at container level in Azure\n",
  "query_template": "az resource show \\\n  --ids \"/subscriptions/${AzureSubscriptionID}/resourceGroups/${AzureResourceGroup}/providers/Microsoft.Storage/storageAccounts/${AzureStorageAccount}/blobServices/default/containers/${ResourceName}/immutabilityPolicies/default\" \\\n  --output json\n",
  "query_executed": "az resource show \\\n  --ids \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/containers/ccc-test-container-20260410t121838z/immutabilityPolicies/default\" \\\n  --output json\n",
  "query_output": "{\n  \"etag\": \"\\\"8de96fb9de7adb9\\\"\",\n  \"extendedLocation\": null,\n  \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/containers/ccc-test-container-20260410t121838z/immutabilityPolicies/default\",\n  \"identity\": null,\n  \"kind\": null,\n  \"location\": null,\n  \"managedBy\": null,\n  \"name\": \"default\",\n  \"plan\": null,\n  \"properties\": {\n    \"allowProtectedAppendWrites\": false,\n    \"allowProtectedAppendWritesAll\": false,\n    \"immutabilityPeriodSinceCreationInDays\": 2,\n    \"state\": \"Locked\"\n  },\n  \"resourceGroup\": \"cfi_test_20260410t121838z\",\n  \"sku\": null,\n  \"tags\": null,\n  \"type\": \"Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies\"\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.properties.state",
      "expected_values": [
        "Locked"
      ],
      "validation_rule": "^Locked$",
      "description": "Verifies that the container immutability policy is in the Locked state, preventing any modification or deletion of the retention configuration.\n",
      "actual_value": "[Locked]",
      "passed": true
    }
  ]
}
Feature: CCC.ObjStor.CN04.AR01
Scenario: Service applies default retention policy to newly uploaded object @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN04 @Behavioural
Given a cloud api for "{Instance}" in "api"47µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"39µs
And I refer to "{result}" as "storage"22µs
And I call "{api}" with "GetServiceAPI" using argument "iam"18µs
And I refer to "{result}" as "iamService"23µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"48µs
And I refer to "{result}" as "testUserWrite"23µs
And I attach "{result}" to the test output as "write-user-identity.json"44µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"29µs
And "{result}" is not an error20µs
And I refer to "{result}" as "userStorage"23µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-retention-object={Timestamp}.txt", and "protected data"25ms
And I attach "{result}" to the test output as "uploaded-object.json"64µs
And I call "{userStorage}" with "GetObjectRetentionDurationDays" using arguments "{ResourceName}" and "test-retention-object={Timestamp}.txt"46ms
Then "{result}" should be greater than "1"42µs
expected {result} (0) to be greater than 1
📎 Attachments:
write-user-identity.json
View JSON (879 bytes)
{"UserName":"test-user-write","Provider":"azure","Credentials":{"app_object_id":"84671853-a603-4519-b30b-9a4bec9b5988","client_id":"309f569c-b57e-4ba6-9392-00e51e1d58ce","client_secret":"oSy8Q~5houXWKkxhS2H6SSDf-gGwTUep7u25GaQu","display_name":"CCC-Test-test-user-write","object_id":"84d6c138-77cf-46a0-816d-59e7c7eaee0e","secret_id":"1a7f9bf1-0948-475f-adc6-5e6e7e7ca6a0","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-write\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"write\", \"role\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe\"}"}
uploaded-object.json
View JSON (281 bytes)
{"ID":"test-retention-object=1775825926767.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"test-retention-object=1775825926767.txt","Size":14,"Data":["protected data"],"Encryption":"Microsoft","EncryptionAlgorithm":"AES256","VersionID":"2026-04-10T12:58:46.7821052Z"}
Scenario: Service enforces retention policy on newly created objects @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN04 @Behavioural
Given a cloud api for "{Instance}" in "api"52µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"46µs
And I refer to "{result}" as "storage"23µs
And I call "{api}" with "GetServiceAPI" using argument "iam"26µs
And I refer to "{result}" as "iamService"18µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "immediate-delete-test={Timestamp}.txt", and "test content"434ms
Then "{result}" is not an error40µs
When I call "{storage}" with "DeleteObject" using arguments "{ResourceName}" and "immediate-delete-test={Timestamp}.txt"438ms
Then "{result}" is an error28µs
And I attach "{result}" to the test output as "immediate-delete-error.txt"42µs
📎 Attachments:
immediate-delete-error.txt
View Content (838 bytes)
failed to delete blob immediate-delete-test=1775825926840.txt: DELETE https://stgcfi20260410t121838z.blob.core.windows.net/ccc-test-container-20260410t121838z/immediate-delete-test=1775825926840.txt
--------------------------------------------------------------------------------
RESPONSE 409: 409 This operation is not permitted as the blob is immutable due to a policy.
ERROR CODE: BlobImmutableDueToPolicy
--------------------------------------------------------------------------------
BlobImmutableDueToPolicyThis operation is not permitted as the blob is immutable due to a policy.
RequestId:c3c3ae18-001e-00d9-77e9-c8133a000000
Time:2026-04-10T12:58:47.7034433Z
--------------------------------------------------------------------------------
Scenario: Service validates retention period meets minimum requirements @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN04 @Behavioural
Given a cloud api for "{Instance}" in "api"44µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"44µs
And I refer to "{result}" as "storage"26µs
And I call "{api}" with "GetServiceAPI" using argument "iam"31µs
And I refer to "{result}" as "iamService"22µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "retention-period-test={Timestamp}.txt", and "compliance data"423ms
And I call "{storage}" with "GetObjectRetentionDurationDays" using arguments "{ResourceName}" and "retention-period-test={Timestamp}.txt"868ms
Then "{result}" should be greater than "1"40µs
expected {result} (0) to be greater than 1
And I attach "{result}" to the test output as "retention-period-days.json"22µs
Scenario: Test policy for default object retention @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN04 @Policy
Given a cloud api for "{Instance}" in "api"44µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"61µs
And I refer to "{result}" as "storage"28µs
And I call "{api}" with "GetServiceAPI" using argument "iam"30µs
And I refer to "{result}" as "iamService"23µs
When I attempt policy check "object-default-retention" for control "CCC.ObjStor.CN04" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true29µs
📎 Attachments:
policy-result-object-default-retention.json
View JSON (2472 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN04/AR01/object-default-retention/azure.yaml",
  "name": "Azure Blob Default Immutability Policy Check",
  "service_type": "object-storage",
  "requirement_text": "Objects MUST automatically receive a default retention policy upon upload, protecting critical data from premature deletion or modification.\n",
  "validity_score": 8,
  "validity_commentary": "This query validates that an immutability policy is configured on the Azure storage container with a non-zero retention interval, ensuring uploaded blobs are automatically protected. Strengths: - Container-level immutability policy applies to all blobs within it - Directly checks the immutability interval in days Limitations: - Policy may need to be set per-container in Azure - Does not verify that the policy is locked (see CN03.AR02)\n",
  "query_template": "az storage container immutability-policy show \\\n  --account-name ${AzureStorageAccount} \\\n  --container-name ${ResourceName} \\\n  --resource-group ${AzureResourceGroup} \\\n  --output json\n",
  "query_executed": "az storage container immutability-policy show \\\n  --account-name stgcfi20260410t121838z \\\n  --container-name ccc-test-container-20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --output json\n",
  "query_output": "{\n  \"allowProtectedAppendWrites\": false,\n  \"allowProtectedAppendWritesAll\": false,\n  \"etag\": \"\\\"8de96fb9de7adb9\\\"\",\n  \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/containers/ccc-test-container-20260410t121838z/immutabilityPolicies/default\",\n  \"immutabilityPeriodSinceCreationInDays\": 2,\n  \"name\": \"default\",\n  \"resourceGroup\": \"cfi_test_20260410t121838z\",\n  \"state\": \"Locked\",\n  \"type\": \"Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies\"\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.immutabilityPeriodSinceCreationInDays",
      "expected_values": [],
      "validation_rule": "^[1-9][0-9]*$",
      "description": "Verifies that the immutability period is set to a positive number of days, ensuring all blobs uploaded to the container receive retention protection.\n",
      "actual_value": "[2]",
      "passed": true
    }
  ]
}
Feature: CCC.ObjStor.CN04.AR02
Scenario: Service prevents object deletion by write user during retention period @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN04 @Behavioural
Given a cloud api for "{Instance}" in "api"43µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"36µs
And I refer to "{result}" as "storage"14µs
And I call "{api}" with "GetServiceAPI" using argument "iam"29µs
And I refer to "{result}" as "iamService"23µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"46µs
And I refer to "{result}" as "testUserWrite"22µs
And I attach "{result}" to the test output as "write-user-identity.json"42µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"38µs
And "{result}" is not an error26µs
And I refer to "{result}" as "userStorage"24µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "protected-object={Timestamp}.txt", and "immutable data"25ms
Then "{result}" is not an error28µs
And I attach "{result}" to the test output as "protected-object.json"37µs
When I call "{userStorage}" with "DeleteObject" using arguments "{ResourceName}" and "protected-object={Timestamp}.txt"23ms
Then "{result}" is an error33µs
And I attach "{result}" to the test output as "delete-protected-error.txt"42µs
And "{result}" should contain one of "retention, locked, immutable, protected"33µs
📎 Attachments:
write-user-identity.json
View JSON (879 bytes)
{"UserName":"test-user-write","Provider":"azure","Credentials":{"app_object_id":"84671853-a603-4519-b30b-9a4bec9b5988","client_id":"309f569c-b57e-4ba6-9392-00e51e1d58ce","client_secret":"oSy8Q~5houXWKkxhS2H6SSDf-gGwTUep7u25GaQu","display_name":"CCC-Test-test-user-write","object_id":"84d6c138-77cf-46a0-816d-59e7c7eaee0e","secret_id":"1a7f9bf1-0948-475f-adc6-5e6e7e7ca6a0","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-write\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"write\", \"role\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe\"}"}
protected-object.json
View JSON (271 bytes)
{"ID":"protected-object=1775825930317.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"protected-object=1775825930317.txt","Size":14,"Data":["immutable data"],"Encryption":"Microsoft","EncryptionAlgorithm":"AES256","VersionID":"2026-04-10T12:58:50.3314191Z"}
delete-protected-error.txt
View Content (828 bytes)
failed to delete blob protected-object=1775825930317.txt: DELETE https://stgcfi20260410t121838z.blob.core.windows.net/ccc-test-container-20260410t121838z/protected-object=1775825930317.txt
--------------------------------------------------------------------------------
RESPONSE 409: 409 This operation is not permitted as the blob is immutable due to a policy.
ERROR CODE: BlobImmutableDueToPolicy
--------------------------------------------------------------------------------
BlobImmutableDueToPolicyThis operation is not permitted as the blob is immutable due to a policy.
RequestId:c3c3b1b9-001e-00d9-75e9-c8133a000000
Time:2026-04-10T12:58:50.3578289Z
--------------------------------------------------------------------------------
Scenario: Service prevents object deletion by admin user during retention period @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN04 @Behavioural
Given a cloud api for "{Instance}" in "api"42µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"33µs
And I refer to "{result}" as "storage"15µs
And I call "{api}" with "GetServiceAPI" using argument "iam"28µs
And I refer to "{result}" as "iamService"14µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "admin-protected-object={Timestamp}.txt", and "compliance data"430ms
Then "{result}" is not an error36µs
When I call "{storage}" with "DeleteObject" using arguments "{ResourceName}" and "admin-protected-object={Timestamp}.txt"417ms
Then "{result}" is an error29µs
And I attach "{result}" to the test output as "admin-delete-protected-error.txt"319µs
📎 Attachments:
admin-delete-protected-error.txt
View Content (840 bytes)
failed to delete blob admin-protected-object=1775825930367.txt: DELETE https://stgcfi20260410t121838z.blob.core.windows.net/ccc-test-container-20260410t121838z/admin-protected-object=1775825930367.txt
--------------------------------------------------------------------------------
RESPONSE 409: 409 This operation is not permitted as the blob is immutable due to a policy.
ERROR CODE: BlobImmutableDueToPolicy
--------------------------------------------------------------------------------
BlobImmutableDueToPolicyThis operation is not permitted as the blob is immutable due to a policy.
RequestId:c3c3b2f3-001e-00d9-78e9-c8133a000000
Time:2026-04-10T12:58:51.2063918Z
--------------------------------------------------------------------------------
Scenario: Service prevents object modification during retention period @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN04 @Behavioural
Given a cloud api for "{Instance}" in "api"43µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"42µs
And I refer to "{result}" as "storage"21µs
And I call "{api}" with "GetServiceAPI" using argument "iam"26µs
And I refer to "{result}" as "iamService"22µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"49µs
And I refer to "{result}" as "testUserWrite"20µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"38µs
And "{result}" is not an error27µs
And I refer to "{result}" as "userStorage"23µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "modify-test-object={Timestamp}.txt", and "original content"25ms
Then "{result}" is not an error27µs
And I attach "{result}" to the test output as "original-object.json"50µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "modify-test-object={Timestamp}.txt", and "modified content"25ms
Then "{result}" is an error31µs
expected {result} to be an error, got *objstorage.Object
And I attach "{result}" to the test output as "modify-protected-error.txt"35µs
And "{result}" should contain one of "retention, locked, immutable, protected, exists"32µs
📎 Attachments:
original-object.json
View JSON (277 bytes)
{"ID":"modify-test-object=1775825931218.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"modify-test-object=1775825931218.txt","Size":16,"Data":["original content"],"Encryption":"Microsoft","EncryptionAlgorithm":"AES256","VersionID":"2026-04-10T12:58:51.2329702Z"}
Scenario: Service allows object read access during retention period @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN04 @Behavioural
Given a cloud api for "{Instance}" in "api"47µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"44µs
And I refer to "{result}" as "storage"25µs
And I call "{api}" with "GetServiceAPI" using argument "iam"32µs
And I refer to "{result}" as "iamService"16µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "readable-protected-object={Timestamp}.txt", and "readable data"428ms
Then "{result}" is not an error41µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"64µs
And I refer to "{result}" as "testUserRead"26µs
And I attach "{result}" to the test output as "read-user-identity.json"51µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{true}"46µs
And "{result}" is not an error32µs
And I refer to "{result}" as "userStorage"21µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "readable-protected-object={Timestamp}.txt"23ms
Then "{result}" is not an error27µs
And I refer to "{result}" as "readResult"18µs
And I attach "{result}" to the test output as "read-protected-object.json"41µs
And "{readResult.Name}" is "readable-protected-object={Timestamp}.txt"41µs
📎 Attachments:
read-user-identity.json
View JSON (875 bytes)
{"UserName":"test-user-read","Provider":"azure","Credentials":{"app_object_id":"4a1ae0ee-eb57-4a90-bd62-001deccf2c13","client_id":"0a1ff458-0de3-439a-aa58-613ccac5c99e","client_secret":"EXh8Q~c_BqjDj4QU1gAlmTVfW3edwBzxrHwcZbEl","display_name":"CCC-Test-test-user-read","object_id":"3efae6ca-3b80-4888-ab2f-be2315255403","secret_id":"3019f835-ea12-4eaa-b2cf-0957b11890ed","subscription_id":"c1cedd8e-bf91-4d7d-a4cc-45700402a2a1","tenant_id":"fa193ac0-9c06-4111-bf55-341e4db193d3"},"Policy":"{\"user\": \"test-user-read\", \"service\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z\", \"level\": \"read\", \"role\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1\"}"}
read-protected-object.json
View JSON (245 bytes)
{"ID":"readable-protected-object=1775825931271.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"readable-protected-object=1775825931271.txt","Size":13,"Data":["readable data"],"Encryption":"","EncryptionAlgorithm":"","VersionID":""}
Scenario: Test policy for object retention enforcement @PerService @object-storage @CCC.ObjStor @tlp-clear @tlp-green @tlp-amber @tlp-red @CCC.ObjStor.CN04 @Policy
Given a cloud api for "{Instance}" in "api"34µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"46µs
And I refer to "{result}" as "storage"28µs
And I call "{api}" with "GetServiceAPI" using argument "iam"35µs
And I refer to "{result}" as "iamService"16µs
When I attempt policy check "object-retention-enforcement" for control "CCC.ObjStor.CN04" assessment requirement "AR02" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true63µs
📎 Attachments:
policy-result-object-retention-enforcement.json
View JSON (3158 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN04/AR02/object-retention-enforcement/azure.yaml",
  "name": "Azure Blob Object Retention Enforcement Check",
  "service_type": "object-storage",
  "requirement_text": "The service MUST prevent deletion or modification of objects under active retention, maintaining data integrity and compliance requirements.\n",
  "validity_score": 9,
  "validity_commentary": "This query validates that a locked immutability policy is in place on the Azure storage container, preventing deletion or modification of blobs during the retention period. Strengths: - A Locked immutability policy cannot be shortened or removed - Directly verifies the policy state and period Limitations: - Must be configured at the container level in Azure\n",
  "query_template": "az resource show \\\n  --ids \"/subscriptions/${AzureSubscriptionID}/resourceGroups/${AzureResourceGroup}/providers/Microsoft.Storage/storageAccounts/${AzureStorageAccount}/blobServices/default/containers/${ResourceName}/immutabilityPolicies/default\" \\\n  --output json\n",
  "query_executed": "az resource show \\\n  --ids \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/containers/ccc-test-container-20260410t121838z/immutabilityPolicies/default\" \\\n  --output json\n",
  "query_output": "{\n  \"etag\": \"\\\"8de96fb9de7adb9\\\"\",\n  \"extendedLocation\": null,\n  \"id\": \"/subscriptions/c1cedd8e-bf91-4d7d-a4cc-45700402a2a1/resourceGroups/cfi_test_20260410t121838z/providers/Microsoft.Storage/storageAccounts/stgcfi20260410t121838z/blobServices/default/containers/ccc-test-container-20260410t121838z/immutabilityPolicies/default\",\n  \"identity\": null,\n  \"kind\": null,\n  \"location\": null,\n  \"managedBy\": null,\n  \"name\": \"default\",\n  \"plan\": null,\n  \"properties\": {\n    \"allowProtectedAppendWrites\": false,\n    \"allowProtectedAppendWritesAll\": false,\n    \"immutabilityPeriodSinceCreationInDays\": 2,\n    \"state\": \"Locked\"\n  },\n  \"resourceGroup\": \"cfi_test_20260410t121838z\",\n  \"sku\": null,\n  \"tags\": null,\n  \"type\": \"Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies\"\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.properties.state",
      "expected_values": [
        "Locked"
      ],
      "validation_rule": "^Locked$",
      "description": "Verifies that the immutability policy is locked, enforcing retention and preventing blob deletion or modification before the period expires.\n",
      "actual_value": "[Locked]",
      "passed": true
    },
    {
      "jsonpath": "$.properties.immutabilityPeriodSinceCreationInDays",
      "expected_values": [
        "2"
      ],
      "validation_rule": "",
      "description": "Verifies that the immutability period meets the configured minimum (days), confirming objects are actively protected from modification or deletion.\n",
      "actual_value": "[2]",
      "passed": true
    }
  ]
}
Feature: CCC.ObjStor.CN05.AR01 - Versioning with Unique Identifiers
Scenario: Service enables versioning and objects receive unique version identifiers @PerService @object-storage @CCC.ObjStor @CCC.ObjStor.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Behavioural
Given a cloud api for "{Instance}" in "api"44µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"41µs
And I refer to "{result}" as "storage"23µs
When I call "{storage}" with "IsBucketVersioningEnabled" using argument "{ResourceName}"33µs
Then "{result}" is true24µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "versioned-object.txt", and "test content"434ms
And I refer to "{result}" as "createdObject"27µs
Then "{createdObject.VersionID}" is not empty30µs
And I attach "{result}" to the test output as "versioned-object.json"17µs
Scenario: Objects are stored with unique version identifiers @PerService @object-storage @CCC.ObjStor @CCC.ObjStor.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy
Given a cloud api for "{Instance}" in "api"45µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"38µs
And I refer to "{result}" as "storage"27µs
When I attempt policy check "object-storage-versioning" for control "CCC.ObjStor.CN05" assessment requirement "AR01" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true52µs
📎 Attachments:
policy-result-object-storage-versioning.json
View JSON (2254 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN05/AR01/object-storage-versioning/azure.yaml",
  "name": "Azure Blob Versioning Configuration",
  "service_type": "object-storage",
  "requirement_text": "When an object is uploaded to the object storage bucket, the object MUST be stored with a unique identifier.\n",
  "validity_score": 9,
  "validity_commentary": "This query validates that Azure Blob versioning is enabled. When versioning is enabled, each blob version gets a unique version ID. Strengths: - Directly queries versioning configuration - Azure automatically assigns unique version IDs - Versioning integrates with soft delete for protection Limitations: - Does not validate soft delete configuration - Versioning must be enabled at the storage account level\n",
  "query_template": "az storage account blob-service-properties show \\\n  --account-name ${AzureStorageAccount} \\\n  --resource-group ${AzureResourceGroup} \\\n  --query \"{isVersioningEnabled: isVersioningEnabled, containerDeleteRetentionPolicy: containerDeleteRetentionPolicy, deleteRetentionPolicy: deleteRetentionPolicy}\" \\\n  --output json\n",
  "query_executed": "az storage account blob-service-properties show \\\n  --account-name stgcfi20260410t121838z \\\n  --resource-group cfi_test_20260410t121838z \\\n  --query \"{isVersioningEnabled: isVersioningEnabled, containerDeleteRetentionPolicy: containerDeleteRetentionPolicy, deleteRetentionPolicy: deleteRetentionPolicy}\" \\\n  --output json\n",
  "query_output": "{\n  \"containerDeleteRetentionPolicy\": {\n    \"allowPermanentDelete\": null,\n    \"days\": 7,\n    \"enabled\": true\n  },\n  \"deleteRetentionPolicy\": {\n    \"allowPermanentDelete\": false,\n    \"days\": 7,\n    \"enabled\": true\n  },\n  \"isVersioningEnabled\": true\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.isVersioningEnabled",
      "expected_values": [
        "true"
      ],
      "validation_rule": "^true$",
      "description": "Verifies that blob versioning is enabled on the storage account. When enabled, all blobs receive unique version IDs automatically.\n",
      "actual_value": "[true]",
      "passed": true
    }
  ]
}
Feature: CCC.ObjStor.CN05.AR02 - New Version ID on Modification
Scenario: Modified objects receive new version identifiers @PerService @object-storage @CCC.ObjStor @CCC.ObjStor.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Behavioural
Given a cloud api for "{Instance}" in "api"67µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"63µs
And I refer to "{result}" as "storage"747µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "original content"444ms
And I refer to "{result.VersionID}" as "version1"29µs
And I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "modified content"443ms
And I refer to "{result.VersionID}" as "version2"33µs
Then "{version1}" is not equal to "{version2}"22µs
Scenario: Modified objects receive new version identifiers @PerService @object-storage @CCC.ObjStor @CCC.ObjStor.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy @Duplicate
Given a cloud api for "{Instance}" in "api"49µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"44µs
And I refer to "{result}" as "storage"24µs
Then no-op required24µs
Feature: CCC.ObjStor.CN05.AR03 - Recovery of Previous Versions
Scenario: Modified objects receive new version identifiers @PerService @object-storage @CCC.ObjStor @CCC.ObjStor.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Behavioural
Given a cloud api for "{Instance}" in "api"43µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"38µs
And I refer to "{result}" as "storage"26µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "original content"451ms
And I refer to "{result.VersionID}" as "version1"44µs
And I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "modified content"450ms
And I refer to "{result.VersionID}" as "version2"38µs
And I call "{storage}" with "ReadObjectAtVersion" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "{version1}"441ms
And I attach "{result}" to the test output as "original-content.json"75µs
Then "{result.Data}" contains "original content"43µs
When I call "{storage}" with "ReadObjectAtVersion" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "{version2}"446ms
Then "{result.Data}" contains "modified content"68µs
And I attach "{result}" to the test output as "modified-content.json"52µs
📎 Attachments:
original-content.json
View JSON (236 bytes)
{"ID":"version-test-object=1775825935896.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"version-test-object=1775825935896.txt","Size":16,"Data":["original content"],"Encryption":"","EncryptionAlgorithm":"","VersionID":""}
modified-content.json
View JSON (236 bytes)
{"ID":"version-test-object=1775825935896.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"version-test-object=1775825935896.txt","Size":16,"Data":["modified content"],"Encryption":"","EncryptionAlgorithm":"","VersionID":""}
Scenario: Previous object versions can be recovered @PerService @object-storage @CCC.ObjStor @CCC.ObjStor.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy
Given a cloud api for "{Instance}" in "api"43µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"48µs
And I refer to "{result}" as "storage"27µs
Then no-op required26µs
Feature: CCC.ObjStor.CN05.AR04 - Retain Versions on Delete
Scenario: Deleted object data can be reloaded from previous version @PerService @object-storage @CCC.ObjStor @CCC.ObjStor.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Behavioural
Given a cloud api for "{Instance}" in "api"43µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"35µs
And I refer to "{result}" as "storage"27µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "recover-deleted-object={Timestamp}.txt", and "data to retain"441ms
And I refer to "{result.VersionID}" as "retainedVersionId"31µs
When I call "{storage}" with "DeleteObject" using arguments "{ResourceName}" and "recover-deleted-object={Timestamp}.txt"450ms
When I call "{storage}" with "ReadObjectAtVersion" using arguments "{ResourceName}", "recover-deleted-object={Timestamp}.txt", and "{retainedVersionId}"451ms
Then "{result.Data}" contains "data to retain"42µs
And I attach "{result}" to the test output as "recovered-deleted-version.json"47µs
📎 Attachments:
recovered-deleted-version.json
View JSON (240 bytes)
{"ID":"recover-deleted-object=1775825937686.txt","BucketID":"ccc-test-container-20260410t121838z","Name":"recover-deleted-object=1775825937686.txt","Size":14,"Data":["data to retain"],"Encryption":"","EncryptionAlgorithm":"","VersionID":""}
Scenario: Deleted object version remains in version list @PerService @object-storage @CCC.ObjStor @CCC.ObjStor.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Behavioural
Given a cloud api for "{Instance}" in "api"61µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"34µs
And I refer to "{result}" as "storage"25µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "list-deleted-versions-object={Timestamp}.txt", and "versioned data"474ms
And I refer to "{result.VersionID}" as "listedVersionId"39µs
When I call "{storage}" with "DeleteObject" using arguments "{ResourceName}" and "list-deleted-versions-object={Timestamp}.txt"443ms
When I call "{storage}" with "ListObjectVersions" using arguments "{ResourceName}" and "list-deleted-versions-object={Timestamp}.txt"32µs
And "{result}" is an array of objects with at least the following contents24µs
VersionIDObjectID
{listedVersionId}list-deleted-versions-object={Timestamp}.txt
field {result} is not an array
And I attach "{result}" to the test output as "versions-after-delete.json"18µs
Scenario: Object versions are retained after deletion @PerService @object-storage @CCC.ObjStor @CCC.ObjStor.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy @Duplicate
Given a cloud api for "{Instance}" in "api"35µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"44µs
And I refer to "{result}" as "storage"25µs
Then no-op required24µs