🥒 CCC.ObjStor Test: ccc-test-bucket-nodal-time-474015-p5

Test Parameters

ServiceTypeobject-storage
ProviderServiceTypestorage.googleapis.com/Bucket
CatalogTypesCCC.ObjStor
TagFilter@object-storage, @PerService
UIDprojects/nodal-time-474015-p5/buckets/ccc-test-bucket-nodal-time-474015-p5
ResourceNameccc-test-bucket-nodal-time-474015-p5
ReportFileccc-test-bucket-nodal-time-474015-p5-service
ReportTitleccc-test-bucket-nodal-time-474015-p5
Instance
{
  "ID": "main-gcp",
  "Properties": {
    "Provider": "gcp",
    "Region": "us-central1",
    "AzureResourceGroup": "",
    "AzureSubscriptionID": "",
    "GcpProjectId": "nodal-time-474015-p5"
  },
  "Services": [
    {
      "Type": "object-storage",
      "Properties": {
        "object-storage-retention-period-seconds": 172800
      }
    },
    {
      "Type": "logging",
      "Properties": {
        "gcp-log-bucket-name": "cfi-test-log-bucket"
      }
    }
  ],
  "Rules": {
    "permitted-project-ids": "",
    "permitted-regions": [
      "us-central1"
    ]
  }
}
GcpLogBucketNamecfi-test-log-bucket
GcpProjectIdnodal-time-474015-p5
ObjectStorageRetentionPeriodSeconds172800
PermittedRegions
[
  "us-central1"
]
Providergcp
Regionus-central1

Summary

Generated: 2026-04-01 11:02:53

Total Run Time: 1m4s

Features: 41

Scenarios: 81 (✅ 34 | ❌ 47)

Steps: 663 (✅ 485 | ❌ 47 | ⏭️ 124 | ❓ 7)

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"35µ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}"6s
Then "{result}" is true28µs
📎 Attachments:
policy-result-object-storage-tls-policy.json
View JSON (3626 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN01/AR01/object-storage-tls-policy/gcp.yaml",
  "name": "GCP Cloud Storage HTTPS 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": 8,
  "validity_commentary": "This check verifies that GCP Cloud Storage is accessed over HTTPS. Strengths: - GCS API explicitly requires TLS for all operations on the public endpoint - Google manages the TLS termination and certificate lifecycle - Organization-level policy 'constraints/storage.requireHttpsTrafficOnly'\n  can be used for universal enforcement\nLimitations: - GCP does not expose a per-bucket 'minimum TLS version' field - TLS 1.3 is supported but cannot be set as the exclusive minimum yet\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=json\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "{\n  \"acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"creation_time\": \"2026-04-01T10:54:57+0000\",\n  \"default_acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1775040897017591422,\n  \"labels\": {\n    \"ccc_compliance_test\": \"1775040908\"\n  },\n  \"location\": \"US-CENTRAL1\",\n  \"location_type\": \"region\",\n  \"metageneration\": 2,\n  \"name\": \"ccc-test-bucket-nodal-time-474015-p5\",\n  \"public_access_prevention\": \"inherited\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-04-01T10:54:57.224000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://ccc-test-bucket-nodal-time-474015-p5/\",\n  \"uniform_bucket_level_access\": false,\n  \"update_time\": \"2026-04-01T10:55:09+0000\"\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.name",
      "expected_values": [],
      "validation_rule": ".+",
      "description": "Confirms the bucket exists. GCP Cloud Storage implicitly requires TLS for all storage API operations. Per-bucket TLS configuration is not exposed in GCP, as it is managed at the platform level.\n",
      "actual_value": "[ccc-test-bucket-nodal-time-474015-p5]",
      "passed": true
    }
  ]
}
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}"725ms
Then "{result}" is true27µs
📎 Attachments:
policy-result-object-storage-unencrypted-policy.json
View JSON (3537 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN01/AR03/object-storage-unencrypted-policy/gcp.yaml",
  "name": "GCP Cloud 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": 8,
  "validity_commentary": "This check verifies that GCP Cloud Storage is accessed over HTTPS. Strengths: - GCS API implicitly requires TLS for all operations on the public endpoint - Google manages the TLS termination; plaintext is not accepted Limitations: - GCP does not expose a per-bucket HTTPS-only flag; enforcement is platform-level - Organization policy 'constraints/storage.requireHttpsTrafficOnly' can provide\n  additional enforcement\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=json\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "{\n  \"acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"creation_time\": \"2026-04-01T10:54:57+0000\",\n  \"default_acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1775040897017591422,\n  \"labels\": {\n    \"ccc_compliance_test\": \"1775040908\"\n  },\n  \"location\": \"US-CENTRAL1\",\n  \"location_type\": \"region\",\n  \"metageneration\": 2,\n  \"name\": \"ccc-test-bucket-nodal-time-474015-p5\",\n  \"public_access_prevention\": \"inherited\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-04-01T10:54:57.224000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://ccc-test-bucket-nodal-time-474015-p5/\",\n  \"uniform_bucket_level_access\": false,\n  \"update_time\": \"2026-04-01T10:55:09+0000\"\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.name",
      "expected_values": [],
      "validation_rule": ".+",
      "description": "Confirms the bucket exists. GCP Cloud Storage implicitly requires TLS for all storage API operations. Unencrypted HTTP is not supported at the GCS endpoint level.\n",
      "actual_value": "[ccc-test-bucket-nodal-time-474015-p5]",
      "passed": true
    }
  ]
}
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 required21µ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"39µs
Given I call "{api}" with "GetServiceAPI" using argument "object-storage"86µs
And I refer to "{result}" as "storage"22µs
And "{result}" is not an error34µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-encryption-check={Timestamp}.txt", and "encryption test data"164ms
Then "{result}" is not an error21µs
And I refer to "{result}" as "uploadResult"15µs
And "{uploadResult.Encryption}" is not null18µs
And "{uploadResult.EncryptionAlgorithm}" is "AES256"20µs
And I attach "{uploadResult}" to the test output as "Upload Result with Encryption Details"54µs
📎 Attachments:
Upload Result with Encryption Details
View JSON (273 bytes)
{"ID":"test-encryption-check=1775041380149.txt","BucketID":"ccc-test-bucket-nodal-time-474015-p5","Name":"test-encryption-check=1775041380149.txt","Size":20,"Data":["encryption test data"],"Encryption":"Google","EncryptionAlgorithm":"AES256","VersionID":"1775041380248859"}
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"42µ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}"736ms
Then "{result}" is true49µs
📎 Attachments:
policy-result-object-storage-encryption.json
View JSON (3899 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN02/AR01/object-storage-encryption/gcp.yaml",
  "name": "GCP Cloud Storage Bucket 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 GCP Cloud Storage bucket encryption configuration. GCP encrypts all data at rest by default using AES-256. Strengths: - All GCS data is encrypted by default - Supports Google-managed, CMEK, and CSEK keys - Validates CMEK configuration when specified Limitations: - Does not validate KMS key configuration details - Does not verify key rotation policies\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=json\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "{\n  \"acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"creation_time\": \"2026-04-01T10:54:57+0000\",\n  \"default_acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1775040897017591422,\n  \"labels\": {\n    \"ccc_compliance_test\": \"1775040908\"\n  },\n  \"location\": \"US-CENTRAL1\",\n  \"location_type\": \"region\",\n  \"metageneration\": 2,\n  \"name\": \"ccc-test-bucket-nodal-time-474015-p5\",\n  \"public_access_prevention\": \"inherited\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-04-01T10:54:57.224000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://ccc-test-bucket-nodal-time-474015-p5/\",\n  \"uniform_bucket_level_access\": false,\n  \"update_time\": \"2026-04-01T10:55:09+0000\"\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.name",
      "expected_values": [],
      "validation_rule": ".+",
      "description": "Confirms the bucket exists. GCP Cloud Storage encrypts all data at rest by default using AES-256. No explicit encryption configuration is required.\n",
      "actual_value": "[ccc-test-bucket-nodal-time-474015-p5]",
      "passed": true
    },
    {
      "jsonpath": "$.default_storage_class",
      "expected_values": [
        "STANDARD",
        "NEARLINE",
        "COLDLINE",
        "ARCHIVE"
      ],
      "validation_rule": "^(STANDARD|NEARLINE|COLDLINE|ARCHIVE)$",
      "description": "Confirms the bucket has a valid storage class. All storage classes use the same encryption (AES-256 with Google-managed or CMEK keys).\n",
      "actual_value": "[STANDARD]",
      "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"31µ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}"760ms
policy check failed: GCP Cloud Storage Soft Delete and Versioning Configuration:
Then "{result}" is true23µs
📎 Attachments:
policy-result-object-storage-delete-protection.json
View JSON (4175 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN03/AR01/object-storage-delete-protection/gcp.yaml",
  "name": "GCP Cloud Storage 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": "GCP does not have a direct equivalent to S3 MFA Delete at the storage level. MFA in GCP is enforced at the IAM/Identity Platform level. This check validates soft delete and versioning as compensating controls. Strengths: - Soft delete allows recovery of deleted objects - Object versioning preserves all object versions - GCP Identity Platform supports MFA for all operations Limitations: - This is NOT a direct MFA-for-delete check - MFA enforcement is at the identity layer, not storage layer\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=json\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "{\n  \"acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"creation_time\": \"2026-04-01T10:54:57+0000\",\n  \"default_acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1775040897017591422,\n  \"labels\": {\n    \"ccc_compliance_test\": \"1775040908\"\n  },\n  \"location\": \"US-CENTRAL1\",\n  \"location_type\": \"region\",\n  \"metageneration\": 2,\n  \"name\": \"ccc-test-bucket-nodal-time-474015-p5\",\n  \"public_access_prevention\": \"inherited\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-04-01T10:54:57.224000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://ccc-test-bucket-nodal-time-474015-p5/\",\n  \"uniform_bucket_level_access\": false,\n  \"update_time\": \"2026-04-01T10:55:09+0000\"\n}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.softDeletePolicy.retentionDurationSeconds",
      "expected_values": [],
      "validation_rule": "^[0-9]+$",
      "description": "Verifies that soft delete is configured with a retention duration. This allows recovery of deleted objects within the retention period.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.softDeletePolicy.retentionDurationSeconds: unknown key softDeletePolicy"
    },
    {
      "jsonpath": "$.versioning.enabled",
      "expected_values": [],
      "validation_rule": "^true$",
      "description": "Verifies that object versioning is enabled. Versioning preserves all previous versions of objects, enabling recovery from modifications.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.versioning.enabled: unknown key versioning"
    }
  ]
}
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"36µs
Then no-op required21µ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"32µs
Then no-op required24µ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"30µs
Then no-op required21µ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"34µs
Then no-op required23µ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}"886ms
Then "{result}" is true32µs
📎 Attachments:
policy-result-admin-logging.json
View JSON (3918 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN04/AR01/admin-logging/gcp.yaml",
  "name": "Cloud Audit Logs Admin Activity 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": "This query validates that Cloud Audit Logs Admin Activity logging is enabled. Admin Activity logs are always enabled by default in GCP and cannot be disabled. Strengths: - Admin Activity logs are enabled by default and cannot be disabled - Logs include principal, timestamp, method, and status - Stored in Cloud Logging separate from the bucket Limitations: - Does not validate log retention period - Does not verify log sink configuration for long-term storage\n",
  "query_template": "gcloud logging sinks list \\\n  --project=${GcpProjectId} \\\n  --format=json\n",
  "query_executed": "gcloud logging sinks list \\\n  --project=nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "[\n  {\n    \"destination\": \"logging.googleapis.com/projects/nodal-time-474015-p5/locations/global/buckets/_Required\",\n    \"filter\": \"LOG_ID(\\\"cloudaudit.googleapis.com/activity\\\") OR LOG_ID(\\\"externalaudit.googleapis.com/activity\\\") OR LOG_ID(\\\"cloudaudit.googleapis.com/system_event\\\") OR LOG_ID(\\\"externalaudit.googleapis.com/system_event\\\") OR LOG_ID(\\\"cloudaudit.googleapis.com/access_transparency\\\") OR LOG_ID(\\\"externalaudit.googleapis.com/access_transparency\\\")\",\n    \"name\": \"_Required\",\n    \"resourceName\": \"projects/nodal-time-474015-p5/sinks/_Required\"\n  },\n  {\n    \"destination\": \"logging.googleapis.com/projects/nodal-time-474015-p5/locations/global/buckets/_Default\",\n    \"filter\": \"NOT LOG_ID(\\\"cloudaudit.googleapis.com/activity\\\") AND NOT LOG_ID(\\\"externalaudit.googleapis.com/activity\\\") AND NOT LOG_ID(\\\"cloudaudit.googleapis.com/system_event\\\") AND NOT LOG_ID(\\\"externalaudit.googleapis.com/system_event\\\") AND NOT LOG_ID(\\\"cloudaudit.googleapis.com/access_transparency\\\") AND NOT LOG_ID(\\\"externalaudit.googleapis.com/access_transparency\\\")\",\n    \"name\": \"_Default\",\n    \"resourceName\": \"projects/nodal-time-474015-p5/sinks/_Default\"\n  }\n]\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$",
      "expected_values": [],
      "validation_rule": ".*",
      "description": "Admin Activity audit logs are enabled by default in GCP and cannot be disabled. This check verifies the logging infrastructure is accessible. Admin Activity logs capture resource creation, deletion, IAM changes, and configuration modifications with full identity and timestamp.\n",
      "actual_value": "[map[destination:logging.googleapis.com/projects/nodal-time-474015-p5/locations/global/buckets/_Required filter:LOG_ID(\"cloudaudit.googleapis.com/activity\") OR LOG_ID(\"externalaudit.googleapis.com/activity\") OR LOG_ID(\"cloudaudit.googleapis.com/system_event\") OR LOG_ID(\"externalaudit.googleapis.com/system_event\") OR LOG_ID(\"cloudaudit.googleapis.com/access_transparency\") OR LOG_ID(\"externalaudit.googleapis.com/access_transparency\") name:_Required resourceName:projects/nodal-time-474015-p5/sinks/_Required] map[destination:logging.googleapis.com/projects/nodal-time-474015-p5/locations/global/buckets/_Default filter:NOT LOG_ID(\"cloudaudit.googleapis.com/activity\") AND NOT LOG_ID(\"externalaudit.googleapis.com/activity\") AND NOT LOG_ID(\"cloudaudit.googleapis.com/system_event\") AND NOT LOG_ID(\"externalaudit.googleapis.com/system_event\") AND NOT LOG_ID(\"cloudaudit.googleapis.com/access_transparency\") AND NOT LOG_ID(\"externalaudit.googleapis.com/access_transparency\") name:_Default resourceName:projects/nodal-time-474015-p5/sinks/_Default]]",
      "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"42µs
And I call "{api}" with "GetServiceAPI" using argument "{ServiceType}"55µs
And I refer to "{result}" as "theService"25µs
Given I call "{api}" with "GetServiceAPI" using argument "logging"402µs
And I refer to "{result}" as "loggingService"24µs
When I call "{theService}" with "UpdateResourcePolicy"337ms
Then "{result}" is not an error33µs
And I attach "{result}" to the test output as "Policy Update Result"39µs
And we wait for a period of "10000" ms10s
When I call "{loggingService}" with "QueryAdminLogs" using arguments "{ResourceName}" and "{20}"125µs
Then "{result}" is not an error30µs
And I refer to "{result}" as "adminLogs"23µs
And I attach "{adminLogs}" to the test output as "Admin Activity Logs"70µs
Then "{adminLogs}" is an array of objects with at least the following contents42µs
result
Succeeded
expected row not found: map[result:Succeeded]
📎 Attachments:
Policy Update Result
View JSON (4 bytes)
null
Admin Activity Logs
View JSON (2 bytes)
[]
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"33µ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}"874ms
policy check failed: Cloud Audit Logs Data Write Configuration:
Then "{result}" is true25µs
📎 Attachments:
policy-result-data-write-logging.json
View JSON (3413 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN04/AR02/data-write-logging/gcp.yaml",
  "name": "Cloud Audit Logs Data 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 Data Write audit logs are enabled. Data Write logs capture resource creation, deletion, and modification. Strengths: - Captures object-level write operations - Logs include principal, timestamp, method, and status - Stored in Cloud Logging separate from the bucket Limitations: - Data Write logs must be explicitly enabled (not default) - High-volume buckets may incur significant logging costs - Requires IAM configuration at project or organization level\n",
  "query_template": "gcloud projects get-iam-policy ${GcpProjectId} \\\n  --format=json\n",
  "query_executed": "gcloud projects get-iam-policy nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "{\n  \"bindings\": [\n    {\n      \"members\": [\n        \"serviceAccount:service-784623368087@compute-system.iam.gserviceaccount.com\"\n      ],\n      \"role\": \"roles/compute.serviceAgent\"\n    },\n    {\n      \"members\": [\n        \"principalSet://iam.googleapis.com/projects/784623368087/locations/global/workloadIdentityPools/finos-oidc-pool/attribute.repository/finos-labs/ccc-cfi-compliance\",\n        \"serviceAccount:784623368087-compute@developer.gserviceaccount.com\",\n        \"serviceAccount:784623368087@cloudservices.gserviceaccount.com\",\n        \"serviceAccount:gha-deployer@nodal-time-474015-p5.iam.gserviceaccount.com\"\n      ],\n      \"role\": \"roles/editor\"\n    },\n    {\n      \"members\": [\n        \"serviceAccount:gha-deployer@nodal-time-474015-p5.iam.gserviceaccount.com\"\n      ],\n      \"role\": \"roles/iam.serviceAccountAdmin\"\n    },\n    {\n      \"members\": [\n        \"serviceAccount:service-784623368087@gcp-sa-notebooks.iam.gserviceaccount.com\"\n      ],\n      \"role\": \"roles/notebooks.serviceAgent\"\n    },\n    {\n      \"members\": [\n        \"user:finos.ccc@gmail.com\"\n      ],\n      \"role\": \"roles/owner\"\n    },\n    {\n      \"members\": [\n        \"serviceAccount:gha-deployer@nodal-time-474015-p5.iam.gserviceaccount.com\"\n      ],\n      \"role\": \"roles/resourcemanager.projectIamAdmin\"\n    },\n    {\n      \"members\": [\n        \"serviceAccount:gha-deployer@nodal-time-474015-p5.iam.gserviceaccount.com\"\n      ],\n      \"role\": \"roles/serviceusage.serviceUsageAdmin\"\n    }\n  ],\n  \"etag\": \"BwZMLLsOAwI=\",\n  \"version\": 1\n}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.auditConfigs[*].auditLogConfigs[?(@.logType=='DATA_WRITE')]",
      "expected_values": [],
      "validation_rule": ".*DATA_WRITE.*",
      "description": "Validates that Data Write audit logging is enabled. DATA_WRITE logs capture resource creation, deletion, update, and copy operations with full identity and timestamp.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.auditConfigs[*].auditLogConfigs[?(@.logType=='DATA_WRITE')]: parsing error: $.auditConfigs[*].auditLogConfigs[?(@.logType=='DATA_WRITE')]\t:1:48 - 1:60 could not parse string: invalid syntax"
    }
  ]
}
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}"711ms
policy check failed: Cloud Audit Logs Data Read Configuration:
Then "{result}" is true26µs
📎 Attachments:
policy-result-data-read-logging.json
View JSON (3396 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN04/AR03/data-read-logging/gcp.yaml",
  "name": "Cloud Audit Logs Data 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 Data Read audit logs are enabled. Data Read logs capture resource download and metadata read operations. Strengths: - Captures object-level read operations - Logs include principal, timestamp, method, and status - Stored in Cloud Logging separate from the bucket Limitations: - Data Read logs must be explicitly enabled (not default) - High-volume buckets may incur very significant logging costs - Requires IAM configuration at project or organization level\n",
  "query_template": "gcloud projects get-iam-policy ${GcpProjectId} \\\n  --format=json\n",
  "query_executed": "gcloud projects get-iam-policy nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "{\n  \"bindings\": [\n    {\n      \"members\": [\n        \"serviceAccount:service-784623368087@compute-system.iam.gserviceaccount.com\"\n      ],\n      \"role\": \"roles/compute.serviceAgent\"\n    },\n    {\n      \"members\": [\n        \"principalSet://iam.googleapis.com/projects/784623368087/locations/global/workloadIdentityPools/finos-oidc-pool/attribute.repository/finos-labs/ccc-cfi-compliance\",\n        \"serviceAccount:784623368087-compute@developer.gserviceaccount.com\",\n        \"serviceAccount:784623368087@cloudservices.gserviceaccount.com\",\n        \"serviceAccount:gha-deployer@nodal-time-474015-p5.iam.gserviceaccount.com\"\n      ],\n      \"role\": \"roles/editor\"\n    },\n    {\n      \"members\": [\n        \"serviceAccount:gha-deployer@nodal-time-474015-p5.iam.gserviceaccount.com\"\n      ],\n      \"role\": \"roles/iam.serviceAccountAdmin\"\n    },\n    {\n      \"members\": [\n        \"serviceAccount:service-784623368087@gcp-sa-notebooks.iam.gserviceaccount.com\"\n      ],\n      \"role\": \"roles/notebooks.serviceAgent\"\n    },\n    {\n      \"members\": [\n        \"user:finos.ccc@gmail.com\"\n      ],\n      \"role\": \"roles/owner\"\n    },\n    {\n      \"members\": [\n        \"serviceAccount:gha-deployer@nodal-time-474015-p5.iam.gserviceaccount.com\"\n      ],\n      \"role\": \"roles/resourcemanager.projectIamAdmin\"\n    },\n    {\n      \"members\": [\n        \"serviceAccount:gha-deployer@nodal-time-474015-p5.iam.gserviceaccount.com\"\n      ],\n      \"role\": \"roles/serviceusage.serviceUsageAdmin\"\n    }\n  ],\n  \"etag\": \"BwZMLLsOAwI=\",\n  \"version\": 1\n}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.auditConfigs[*].auditLogConfigs[?(@.logType=='DATA_READ')]",
      "expected_values": [],
      "validation_rule": ".*DATA_READ.*",
      "description": "Validates that Data Read audit logging is enabled. DATA_READ logs capture resource download, metadata read, and list operations with full identity and timestamp.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.auditConfigs[*].auditLogConfigs[?(@.logType=='DATA_READ')]: parsing error: $.auditConfigs[*].auditLogConfigs[?(@.logType=='DATA_READ')]\t:1:48 - 1:59 could not parse string: invalid syntax"
    }
  ]
}
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"51µs
Given I call "{api}" with "GetServiceAPI" using argument "object-storage"43µs
And I refer to "{result}" as "storage"14µs
Given I call "{api}" with "GetServiceAPI" using argument "logging"22µs
And I refer to "{result}" as "loggingService"14µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-read-logging-object={Timestamp}.txt", and "test data for read logging verification"130ms
Then "{result}" is not an error25µs
And I refer to "{result}" as "createResult"18µs
When I call "{storage}" with "ReadObject" using arguments "{ResourceName}" and "test-read-logging-object={Timestamp}.txt"124ms
Then "{result}" is not an error22µs
And I refer to "{result}" as "readResult"18µs
And I attach "{readResult}" to the test output as "Object Read Result"34µs
And we wait for a period of "10000" ms10s
When I call "{loggingService}" with "QueryDataReadLogs" using arguments "{ResourceName}" and "{20}"39µs
Then "{result}" is not an error17µs
And I refer to "{result}" as "readLogs"19µs
And I attach "{readLogs}" to the test output as "Data Read Logs"42µs
Then "{readLogs}" is an array of objects with at least the following contents40µs
result
Succeeded
expected row not found: map[result:Succeeded]
📎 Attachments:
Object Read Result
View JSON (270 bytes)
{"ID":"test-read-logging-object=1775041394639.txt","BucketID":"ccc-test-bucket-nodal-time-474015-p5","Name":"test-read-logging-object=1775041394639.txt","Size":39,"Data":["test data for read logging verification"],"Encryption":"","EncryptionAlgorithm":"","VersionID":""}
Data Read Logs
View JSON (2 bytes)
[]
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"40µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"38µs
And I refer to "{result}" as "storage"16µs
And I call "{api}" with "GetServiceAPI" using argument "iam"29µs
And I refer to "{result}" as "iamService"32µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-write-access", "{UID}", and "none"2s
And I refer to "{result}" as "testUserNoAccess"23µs
And I attach "{result}" to the test output as "no-access-user-identity.json"38µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"54µs
And "{result}" is not an error22µ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"20µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-cn05-unauthorized-modify={Timestamp}.txt", and "unauthorized data"18µs
Then "{result}" is an error39µs
And I attach "{result}" to the test output as "no-access-create-error.txt"14µs
📎 Attachments:
no-access-user-identity.json
View Content (250 bytes)
failed to create service account key for test-user-no-write-access: rpc error: code = NotFound desc = Service account projects/nodal-time-474015-p5/serviceAccounts/test-user-no-write-access@nodal-time-474015-p5.iam.gserviceaccount.com does not exist.
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"44µ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"33µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write-access", "{UID}", and "write"1s
And I refer to "{result}" as "testUserWrite"24µs
And I attach "{result}" to the test output as "write-user-identity.json"39µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"52µs
And "{result}" is not an error34µ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"15µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-cn05-authorized-modify={Timestamp}.txt", and "authorized data"19µs
Then "{result}" is not an error17µs
And I attach "{result}" to the test output as "write-create-object-result.json"17µs
📎 Attachments:
write-user-identity.json
View Content (244 bytes)
failed to create service account key for test-user-write-access: rpc error: code = NotFound desc = Service account projects/nodal-time-474015-p5/serviceAccounts/test-user-write-access@nodal-time-474015-p5.iam.gserviceaccount.com does not exist.
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"44µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"31µs
And I refer to "{result}" as "storage"23µs
And I call "{api}" with "GetServiceAPI" using argument "iam"22µs
And I refer to "{result}" as "iamService"14µ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}"734ms
policy check failed: GCP Cloud Storage Public Access Prevention:
Then "{result}" is true33µs
📎 Attachments:
policy-result-object-storage-block-public-write-access.json
View JSON (2240 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/gcp.yaml",
  "name": "GCP Cloud Storage Public Access Prevention",
  "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. Public access prevention ensures the bucket is not world-writable.\n",
  "validity_score": 8,
  "validity_commentary": "This query validates that GCP Cloud Storage has public access prevention enabled. When set to 'enforced', the bucket cannot be made public via IAM or ACLs, reducing the risk of unauthorized data modification. Strengths: - Prevents allUsers and allAuthenticatedUsers access when enforced - Cannot be overridden by IAM policy or ACL changes - Aligns with Google security best practices Limitations: - Does not validate IAM policies for principle of least privilege - Behavioral testing (CN05-AR01) verifies unauthorized modification is blocked at runtime - 'inherited' uses org-level policy; org policy should restrict public access\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=\"json(iamConfiguration.publicAccessPrevention)\"\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=\"json(iamConfiguration.publicAccessPrevention)\"\n",
  "query_output": "null\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.iamConfiguration.publicAccessPrevention",
      "expected_values": [],
      "validation_rule": "^(enforced|inherited)$",
      "description": "Validates that public access prevention is configured. 'enforced' blocks public access at the bucket level. 'inherited' uses the organization policy; the org should have restrictedPublicAccess or similar. When unspecified, the bucket could be made public via IAM bindings.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.iamConfiguration.publicAccessPrevention: unsupported value type \u003cnil\u003e for select, expected map[string]interface{} or []interface{}"
    }
  ]
}
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"35µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"36µs
And I refer to "{result}" as "storage"22µs
And I call "{api}" with "GetServiceAPI" using argument "iam"16µs
And I refer to "{result}" as "iamService"12µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-admin-access", "{UID}", and "none"1s
And I refer to "{result}" as "testUserNoAccess"21µs
And I attach "{result}" to the test output as "no-admin-user-identity.json"34µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"46µs
And "{result}" is not an error31µ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 "CreateBucket" using argument "test-cn05-unauthorized-admin-container"29µs
Then "{result}" is an error22µs
And I attach "{result}" to the test output as "no-admin-create-bucket-error.txt"26µs
📎 Attachments:
no-admin-user-identity.json
View Content (250 bytes)
failed to create service account key for test-user-no-admin-access: rpc error: code = NotFound desc = Service account projects/nodal-time-474015-p5/serviceAccounts/test-user-no-admin-access@nodal-time-474015-p5.iam.gserviceaccount.com does not exist.
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"36µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"34µ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"21µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read-only-admin", "{UID}", and "read"1s
And I refer to "{result}" as "testUserRead"22µs
And I attach "{result}" to the test output as "read-only-admin-user-identity.json"41µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{false}"43µs
And "{result}" is not an error21µ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"23µs
When I call "{userStorage}" with "CreateBucket" using argument "test-cn05-read-only-create-container"16µs
Then "{result}" is an error19µs
And I attach "{result}" to the test output as "read-only-create-bucket-error.txt"15µs
📎 Attachments:
read-only-admin-user-identity.json
View Content (250 bytes)
failed to create service account key for test-user-read-only-admin: rpc error: code = NotFound desc = Service account projects/nodal-time-474015-p5/serviceAccounts/test-user-read-only-admin@nodal-time-474015-p5.iam.gserviceaccount.com does not exist.
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"37µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"41µs
And I refer to "{result}" as "storage"19µs
And I call "{api}" with "GetServiceAPI" using argument "iam"18µs
And I refer to "{result}" as "iamService"12µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-admin-access", "{UID}", and "admin"870ms
And I refer to "{result}" as "testUserAdmin"20µs
And I attach "{result}" to the test output as "admin-user-identity.json"45µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserAdmin}", and "{true}"47µs
And "{result}" is not an error31µ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 "CreateBucket" using argument "test-cn05-authorized-admin-container"12µs
Then "{result}" is not an error13µs
And I attach "{result}" to the test output as "admin-create-bucket-result.json"23µs
And I call "{storage}" with "DeleteBucket" using argument "test-cn05-authorized-admin-container"31µs
📎 Attachments:
admin-user-identity.json
View Content (244 bytes)
failed to create service account key for test-user-admin-access: rpc error: code = NotFound desc = Service account projects/nodal-time-474015-p5/serviceAccounts/test-user-admin-access@nodal-time-474015-p5.iam.gserviceaccount.com does not exist.
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"40µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"35µs
And I refer to "{result}" as "storage"21µs
And I call "{api}" with "GetServiceAPI" using argument "iam"29µs
And I refer to "{result}" as "iamService"13µs
Then no-op required11µ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"24µ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}"854ms
Then "{result}" is true33µs
📎 Attachments:
policy-result-object-storage-cross-tenant-block.json
View JSON (2756 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/gcp.yaml",
  "name": "GCP Cloud Storage Cross-Tenant Access 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": 6,
  "validity_commentary": "Cross-tenant (cross-project) access in GCP Storage is granted via IAM policy bindings with principals from other projects (e.g. serviceAccount:...@other-project.iam.gserviceaccount.com). This query checks the bucket IAM policy and validates cross-project members are in permitted-project-ids. Strengths: - Directly queries bucket IAM policy - Identifies serviceAccount members from other projects - Uses permitted-project-ids for explicit allowlist Limitations: - user: and group: members from outside the project not analyzed - Organization-level bindings are not checked\n",
  "query_template": "BINDINGS=$(gcloud storage buckets get-iam-policy gs://${ResourceName} --project=${GcpProjectId} --format=\"json(bindings)\" 2\u003e/dev/null || echo '{\"bindings\":[]}')\necho \"$BINDINGS\" | jq --arg p \"${GcpProjectId}\" '{\n  crossProjectIds: [\n    .bindings[]?.members[]? |\n    select(startswith(\"serviceAccount:\")) |\n    capture(\"serviceAccount:(?\u003csa\u003e[^@]+)@(?\u003cproj\u003e[^.]+)\\\\.iam\\\\.gserviceaccount\\\\.com\") |\n    select(.proj != $p) | .proj\n  ] | unique\n}' 2\u003e/dev/null || echo '{\"crossProjectIds\": []}'\n",
  "query_executed": "BINDINGS=$(gcloud storage buckets get-iam-policy gs://ccc-test-bucket-nodal-time-474015-p5 --project=nodal-time-474015-p5 --format=\"json(bindings)\" 2\u003e/dev/null || echo '{\"bindings\":[]}')\necho \"$BINDINGS\" | jq --arg p \"nodal-time-474015-p5\" '{\n  crossProjectIds: [\n    .bindings[]?.members[]? |\n    select(startswith(\"serviceAccount:\")) |\n    capture(\"serviceAccount:(?\u003csa\u003e[^@]+)@(?\u003cproj\u003e[^.]+)\\\\.iam\\\\.gserviceaccount\\\\.com\") |\n    select(.proj != $p) | .proj\n  ] | unique\n}' 2\u003e/dev/null || echo '{\"crossProjectIds\": []}'\n",
  "query_output": "{\n  \"crossProjectIds\": []\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.crossProjectIds",
      "expected_values": [],
      "validation_rule": "",
      "description": "Validates that cross-project service account members are in permitted-project-ids. Empty permitted = no cross-project allowed.\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"34µ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}"746ms
policy check failed: GCP Cloud Storage Block External Unauthorized Data Requests:
Then "{result}" is true21µs
📎 Attachments:
policy-result-object-storage-block-public-read.json
View JSON (2062 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/gcp.yaml",
  "name": "GCP Cloud 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 GCP Cloud Storage has public access prevention enabled. When set to 'enforced' or 'inherited', the bucket cannot grant public read access via IAM or ACLs, blocking external unauthorized data requests. Strengths: - Prevents allUsers and allAuthenticatedUsers read access when enforced - Cannot be overridden by IAM policy or ACL changes - Aligns with Google security best practices Limitations: - Does not validate network-level restrictions - 'inherited' uses org-level policy; org should restrict public access - Behavioral testing verifies unauthorized read is blocked at runtime\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=\"json(iamConfiguration.publicAccessPrevention)\"\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=\"json(iamConfiguration.publicAccessPrevention)\"\n",
  "query_output": "null\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.iamConfiguration.publicAccessPrevention",
      "expected_values": [],
      "validation_rule": "^(enforced|inherited)$",
      "description": "Validates that public access prevention is configured. When enforced or inherited, external unauthorized data requests (public read) are blocked at the bucket level.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.iamConfiguration.publicAccessPrevention: unsupported value type \u003cnil\u003e for select, expected map[string]interface{} or []interface{}"
    }
  ]
}
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"39µs
Then no-op required23µ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"31µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"37µs
And I refer to "{result}" as "storage"19µs
And I call "{api}" with "GetServiceAPI" using argument "iam"18µs
And I refer to "{result}" as "iamService"12µs
Then no-op required11µ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"30µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"24µs
And I refer to "{result}" as "storage"15µs
And I call "{api}" with "GetServiceAPI" using argument "iam"16µs
And I refer to "{result}" as "iamService"13µs
Then no-op required11µ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"34µ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}"744ms
Then "{result}" is true32µs
📎 Attachments:
policy-result-object-storage-region.json
View JSON (3702 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN06/AR01/object-storage-region/gcp.yaml",
  "name": "GCP Cloud Storage Bucket 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 GCP Cloud Storage buckets are deployed in approved locations. GCS supports regional, dual-region, and multi-region locations. Strengths: - Directly queries bucket location - Bucket location cannot be changed after creation - Easy to validate against an approved location list Limitations: - Approved locations must be defined externally - Multi-region buckets span multiple locations\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=json\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "{\n  \"acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"creation_time\": \"2026-04-01T10:54:57+0000\",\n  \"default_acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1775040897017591422,\n  \"labels\": {\n    \"ccc_compliance_test\": \"1775041382\"\n  },\n  \"location\": \"US-CENTRAL1\",\n  \"location_type\": \"region\",\n  \"metageneration\": 3,\n  \"name\": \"ccc-test-bucket-nodal-time-474015-p5\",\n  \"public_access_prevention\": \"inherited\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-04-01T10:54:57.224000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://ccc-test-bucket-nodal-time-474015-p5/\",\n  \"uniform_bucket_level_access\": false,\n  \"update_time\": \"2026-04-01T11:03:03+0000\"\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.location",
      "expected_values": [
        "US",
        "US-EAST1",
        "US-WEST1",
        "US-CENTRAL1",
        "EU",
        "EUROPE-WEST1"
      ],
      "validation_rule": "^(US|US-EAST1|US-WEST1|US-CENTRAL1|EU|EUROPE-WEST1)$",
      "description": "Validates that the bucket is located in an approved GCP location. The expected_values list should be customized to match your organization's approved locations.\n",
      "actual_value": "[US-CENTRAL1]",
      "passed": true
    }
  ]
}
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"34µs
Then no-op required25µ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"74µs
Then no-op required66µ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"45µ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}"541ms
policy check failed: GCP Enumeration Monitoring Policy Check:
Then "{result}" is true22µs
📎 Attachments:
policy-result-enumeration-monitoring-policy.json
View JSON (2567 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN07/AR01/enumeration-monitoring-policy/gcp.yaml",
  "name": "GCP 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": 8,
  "validity_commentary": "This query validates that at least one log sink exists for the project, which allows logs to be exported to monitored channels like Pub/Sub, BigQuery, or a separate Logging project.\n",
  "query_template": "gcloud logging sinks list --project ${GcpProjectId} --format=json\n",
  "query_executed": "gcloud logging sinks list --project nodal-time-474015-p5 --format=json\n",
  "query_output": "[\n  {\n    \"destination\": \"logging.googleapis.com/projects/nodal-time-474015-p5/locations/global/buckets/_Required\",\n    \"filter\": \"LOG_ID(\\\"cloudaudit.googleapis.com/activity\\\") OR LOG_ID(\\\"externalaudit.googleapis.com/activity\\\") OR LOG_ID(\\\"cloudaudit.googleapis.com/system_event\\\") OR LOG_ID(\\\"externalaudit.googleapis.com/system_event\\\") OR LOG_ID(\\\"cloudaudit.googleapis.com/access_transparency\\\") OR LOG_ID(\\\"externalaudit.googleapis.com/access_transparency\\\")\",\n    \"name\": \"_Required\",\n    \"resourceName\": \"projects/nodal-time-474015-p5/sinks/_Required\"\n  },\n  {\n    \"destination\": \"logging.googleapis.com/projects/nodal-time-474015-p5/locations/global/buckets/_Default\",\n    \"filter\": \"NOT LOG_ID(\\\"cloudaudit.googleapis.com/activity\\\") AND NOT LOG_ID(\\\"externalaudit.googleapis.com/activity\\\") AND NOT LOG_ID(\\\"cloudaudit.googleapis.com/system_event\\\") AND NOT LOG_ID(\\\"externalaudit.googleapis.com/system_event\\\") AND NOT LOG_ID(\\\"cloudaudit.googleapis.com/access_transparency\\\") AND NOT LOG_ID(\\\"externalaudit.googleapis.com/access_transparency\\\")\",\n    \"name\": \"_Default\",\n    \"resourceName\": \"projects/nodal-time-474015-p5/sinks/_Default\"\n  }\n]\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.[0].name",
      "expected_values": [],
      "validation_rule": "^.+$",
      "description": "Verifies that at least one log sink is configured. Sinks are used to export logs to monitored channels.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.[0].name: parsing error: $.[0].name\t:1:3 - 1:4 unexpected \"[\" while scanning JSON select expected Ident, \".\" or \"*\""
    }
  ]
}
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"62µs
Then no-op required28µ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"30µ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}"745ms
policy check failed: GCP Cloud Storage Enumeration Logging Policy Check:
Then "{result}" is true23µs
📎 Attachments:
policy-result-enumeration-logging-policy.json
View JSON (3032 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN07/AR02/enumeration-logging-policy/gcp.yaml",
  "name": "GCP Cloud Storage Enumeration Logging Policy Check",
  "service_type": "object-storage",
  "requirement_text": "Enumeration activities must be logged.\n",
  "validity_score": 7,
  "validity_commentary": "This query validates that bucket-level logging is enabled.\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=json\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "{\n  \"acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"creation_time\": \"2026-04-01T10:54:57+0000\",\n  \"default_acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1775040897017591422,\n  \"labels\": {\n    \"ccc_compliance_test\": \"1775041382\"\n  },\n  \"location\": \"US-CENTRAL1\",\n  \"location_type\": \"region\",\n  \"metageneration\": 3,\n  \"name\": \"ccc-test-bucket-nodal-time-474015-p5\",\n  \"public_access_prevention\": \"inherited\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-04-01T10:54:57.224000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://ccc-test-bucket-nodal-time-474015-p5/\",\n  \"uniform_bucket_level_access\": false,\n  \"update_time\": \"2026-04-01T11:03:03+0000\"\n}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.logging.logBucket",
      "expected_values": [],
      "validation_rule": "^.+$",
      "description": "Verifies that a log bucket is configured.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.logging.logBucket: unknown key logging"
    }
  ]
}
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"49µs
Then no-op required23µ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"69µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"65µs
And I refer to "{result}" as "storage"44µ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}"720ms
policy check failed: GCP Cloud Storage Multi-Region/Dual-Region Configuration:
Then "{result}" is true29µs
📎 Attachments:
policy-result-object-storage-replication.json
View JSON (4011 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN08/AR01/object-storage-replication/gcp.yaml",
  "name": "GCP Cloud Storage Multi-Region/Dual-Region 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 GCP Cloud Storage replication through multi-region or dual-region bucket configuration. GCP provides automatic geo-redundancy for multi-region and dual-region buckets. Strengths: - Replication is automatic and synchronous - Multi-region provides geographic redundancy - Turbo replication available for dual-region Limitations: - Regional buckets do not provide geo-redundancy - Cross-bucket replication requires separate configuration\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=json\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "{\n  \"acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"creation_time\": \"2026-04-01T10:54:57+0000\",\n  \"default_acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1775040897017591422,\n  \"labels\": {\n    \"ccc_compliance_test\": \"1775041382\"\n  },\n  \"location\": \"US-CENTRAL1\",\n  \"location_type\": \"region\",\n  \"metageneration\": 3,\n  \"name\": \"ccc-test-bucket-nodal-time-474015-p5\",\n  \"public_access_prevention\": \"inherited\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-04-01T10:54:57.224000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://ccc-test-bucket-nodal-time-474015-p5/\",\n  \"uniform_bucket_level_access\": false,\n  \"update_time\": \"2026-04-01T11:03:03+0000\"\n}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.locationType",
      "expected_values": [],
      "validation_rule": "^(multi-region|dual-region)$",
      "description": "Validates that the bucket uses a multi-region or dual-region location type, which provides automatic geo-redundancy. Regional buckets do not satisfy cross-region replication requirements.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.locationType: unknown key locationType"
    },
    {
      "jsonpath": "$.location",
      "expected_values": [],
      "validation_rule": "^.+$",
      "description": "Confirms the bucket has a location configured. Multi-region locations (US, EU, ASIA) span multiple data centers.\n",
      "actual_value": "[US-CENTRAL1]",
      "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"213µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"35µs
And I refer to "{result}" as "storage"20µs
When I call "{storage}" with "GetReplicationStatus" using argument "{ResourceName}"32µs
And I refer to "{result}" as "replicationStatus"25µs
And I refer to "{replicationStatus.Locations}" as "locations"25µs
And I attach "{replicationStatus}" to the test output as "Replication Status"41µs
Then "{locations}" is an array of objects with length "2"21µs
field {locations} is not an array
And "{PermittedRegions}" is an array of objects with at least the following contents14µs
value
{locations[0]}
And "{PermittedRegions}" is an array of objects with at least the following contents13µs
value
{locations[1]}
📎 Attachments:
Replication Status
View Content (19 bytes)
not yet implemented
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"40µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"35µs
And I refer to "{result}" as "storage"15µ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}"728ms
policy check failed: GCP Cloud Storage Replication Status Visibility:
Then "{result}" is true28µs
📎 Attachments:
policy-result-object-storage-replication-status.json
View JSON (3923 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN08/AR02/object-storage-replication-status/gcp.yaml",
  "name": "GCP Cloud 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": 7,
  "validity_commentary": "This query validates that GCP Cloud Storage bucket replication information is visible. GCP provides location and replication type information. Strengths: - Location type indicates replication configuration - Multi-region/dual-region locations are automatically replicated - Turbo replication provides faster sync for dual-region Limitations: - GCP does not expose real-time replication lag metrics - Object-level replication status is not available\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=json\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "{\n  \"acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"creation_time\": \"2026-04-01T10:54:57+0000\",\n  \"default_acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1775040897017591422,\n  \"labels\": {\n    \"ccc_compliance_test\": \"1775041382\"\n  },\n  \"location\": \"US-CENTRAL1\",\n  \"location_type\": \"region\",\n  \"metageneration\": 3,\n  \"name\": \"ccc-test-bucket-nodal-time-474015-p5\",\n  \"public_access_prevention\": \"inherited\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-04-01T10:54:57.224000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://ccc-test-bucket-nodal-time-474015-p5/\",\n  \"uniform_bucket_level_access\": false,\n  \"update_time\": \"2026-04-01T11:03:03+0000\"\n}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.locationType",
      "expected_values": [],
      "validation_rule": "^(multi-region|dual-region|region)$",
      "description": "Validates that the location type is visible, indicating the replication configuration.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.locationType: unknown key locationType"
    },
    {
      "jsonpath": "$.location",
      "expected_values": [],
      "validation_rule": "^.+$",
      "description": "Confirms the bucket location is visible, providing information about where data is replicated.\n",
      "actual_value": "[US-CENTRAL1]",
      "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"46µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"50µs
And I refer to "{result}" as "storage"31µs
When I call "{storage}" with "GetReplicationStatus" using argument "{ResourceName}"39µs
And I refer to "{result}" as "replicationStatus"31µs
And I attach "{replicationStatus}" to the test output as "Replication Status"30µs
And I refer to "{replicationStatus.Locations}" as "locations"45µs
Then "{locations}" is an array of objects with at least the following contents36µs
value
{ReplicationLocations[0]}
{ReplicationLocations[1]}
field {locations} is not an array
📎 Attachments:
Replication Status
View Content (19 bytes)
not yet implemented
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"39µ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}"719ms
policy check failed: GCP Cloud Storage Access Logging Configuration:
Then "{result}" is true122µs
📎 Attachments:
policy-result-object-storage-access-logging.json
View JSON (4071 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN09/AR01/object-storage-access-logging/gcp.yaml",
  "name": "GCP Cloud Storage Access 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 GCP Cloud Storage access logging is configured to write to a separate bucket. GCP also supports Cloud Audit Logs. Strengths: - Validates logging is enabled and configured - Target bucket separation ensures logs are not accessible from source - Cloud Audit Logs provide additional logging Limitations: - Does not validate Cloud Audit Logs configuration - Does not verify target bucket permissions - Legacy logging (bucket-level) is being deprecated\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=json\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "{\n  \"acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"creation_time\": \"2026-04-01T10:54:57+0000\",\n  \"default_acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1775040897017591422,\n  \"labels\": {\n    \"ccc_compliance_test\": \"1775041382\"\n  },\n  \"location\": \"US-CENTRAL1\",\n  \"location_type\": \"region\",\n  \"metageneration\": 3,\n  \"name\": \"ccc-test-bucket-nodal-time-474015-p5\",\n  \"public_access_prevention\": \"inherited\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-04-01T10:54:57.224000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://ccc-test-bucket-nodal-time-474015-p5/\",\n  \"uniform_bucket_level_access\": false,\n  \"update_time\": \"2026-04-01T11:03:03+0000\"\n}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.logging.logBucket",
      "expected_values": [],
      "validation_rule": "^(?!${ResourceName}$).+$",
      "description": "Validates that access logging is enabled and the log bucket is different from the source bucket. Logs stored in the same bucket would violate the integrity requirement.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.logging.logBucket: unknown key logging"
    },
    {
      "jsonpath": "$.logging.logObjectPrefix",
      "expected_values": [],
      "validation_rule": "^.*$",
      "description": "Checks that a log object prefix is configured. A prefix helps organize logs for easier management.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.logging.logObjectPrefix: unknown key logging"
    }
  ]
}
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"46µs
Then no-op required27µ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"39µs
Then no-op required28µ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"45µ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}"730ms
policy check failed: GCP Cloud Storage Replication Destination Validation:
Then "{result}" is true26µs
📎 Attachments:
policy-result-object-storage-replication-destination.json
View JSON (4149 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.Core/CCC.Core.CN10/AR01/object-storage-replication-destination/gcp.yaml",
  "name": "GCP Cloud Storage Replication Destination 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 GCP Cloud Storage bucket locations are within approved regions. For multi-region/dual-region buckets, GCP automatically determines replication destinations. Strengths: - Location type and location are directly queryable - Multi-region locations have known member regions - Can validate against approved location list Limitations: - GCP does not allow custom replication destination selection - Dual-region specific regions may need validation\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=json\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "{\n  \"acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"creation_time\": \"2026-04-01T10:54:57+0000\",\n  \"default_acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1775040897017591422,\n  \"labels\": {\n    \"ccc_compliance_test\": \"1775041382\"\n  },\n  \"location\": \"US-CENTRAL1\",\n  \"location_type\": \"region\",\n  \"metageneration\": 3,\n  \"name\": \"ccc-test-bucket-nodal-time-474015-p5\",\n  \"public_access_prevention\": \"inherited\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-04-01T10:54:57.224000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://ccc-test-bucket-nodal-time-474015-p5/\",\n  \"uniform_bucket_level_access\": false,\n  \"update_time\": \"2026-04-01T11:03:03+0000\"\n}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.location",
      "expected_values": [
        "EU",
        "EUROPE-WEST1",
        "US",
        "US-EAST1",
        "US-WEST1"
      ],
      "validation_rule": "^(US|US-EAST1|US-WEST1|EU|EUROPE-WEST1|NAM4|EUR4)$",
      "description": "Validates that the bucket location (and thus replication destination) is within the approved list of locations in the trust perimeter.\n",
      "actual_value": "[US-CENTRAL1]",
      "passed": false
    },
    {
      "jsonpath": "$.locationType",
      "expected_values": [],
      "validation_rule": "^(multi-region|dual-region|region)$",
      "description": "Validates the location type. Multi-region and dual-region buckets replicate to known GCP regions within that area.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.locationType: unknown key locationType"
    }
  ]
}
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"39µs
Then no-op required26µ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"39µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"41µs
And I refer to "{result}" as "storage"17µs
And I call "{api}" with "GetServiceAPI" using argument "iam"19µs
And I refer to "{result}" as "iamService"17µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-access", "{UID}", and "none"1s
And I refer to "{result}" as "testUserNoAccess"26µs
And I attach "{result}" to the test output as "no-access-user-identity.json"72µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"229µs
And "{result}" is not an error20µs
And I refer to "{result}" as "userStorage"16µs
When I call "{userStorage}" with "ListObjects" using argument "{ResourceName}"89ms
Then "{result}" is an error26µs
And I attach "{result}" to the test output as "no-access-list-error.txt"50µs
📎 Attachments:
no-access-user-identity.json
View JSON (3035 bytes)
{"UserName":"test-user-no-access","Provider":"gcp","Credentials":{"client_email":"test-user-no-access@nodal-time-474015-p5.iam.gserviceaccount.com","email":"test-user-no-access@nodal-time-474015-p5.iam.gserviceaccount.com","private_key_id":"3dcbc99aba98933bcc4aa49064be50624a6bd1cb","project_id":"nodal-time-474015-p5","service_account_key":"{\n  \"type\": \"service_account\",\n  \"project_id\": \"nodal-time-474015-p5\",\n  \"private_key_id\": \"3dcbc99aba98933bcc4aa49064be50624a6bd1cb\",\n  \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+aRWDoHDQhXBR\\ncFPov9ii1cxS4tN3t5EXTJAYVqXYyy2WxM/0OwzNQoDdQ/xBD9VfMXBuN4CvVu2p\\nkfxkXvvOCjy/EiJdjs0zC5whA7jv7ebPg5xGy0sUaJ2ItJBejkiHFGikunokLQMn\\nZTCJIZ19DX6M9hMIGGAdHGTHRTjYPeA9iR4gcNJJ/r9CRc/cf/6oa2G3FMwJc2Kk\\n7Ez9JvqWM91fOgqOep8TkD3v8EXBzKUmacQrsRINhT45acI0FJRjxo5RrPHrPjz1\\nne91Qx2w4d5eqe3chWQcXFXfW8lJoBOrpB8UprNjsNlw8JIPEZN2y9jn/eRxXwGE\\n3HaIWPgjAgMBAAECggEAAZ70Y25dhEqDT8L3u+UyMdF4thxSaSD9BIIO2F4yEDvk\\n84639HmYnM2CbYYs76lAvqXPMekAC/FFM0Q+YUsogP/RgsQW2ppvtCdwcoMZ5m6T\\nue2CMMOw3zrnjs8QSyj4t7APsGNNmeEJaolWfFX20lma9Uk8+6Morxn1E6sxSWON\\nrCsijhJs+n821/qwEry4CyxH+EwWij2za7yZYoh4fDlwa0MXc1TJfbetmJ+FYjYM\\nwa9yaxsNPVnVrzTNgcgAMV+gAjmAjusE2Fe61rcw5jkEqmH+Tmw7na7VkjkvDcm0\\nPqSoitDcau6LJ/KYy6ihXcd713YvuB/ivfup0OtQuQKBgQD4HWVLPnVI/jb0p/rU\\n8XV661YOO4LMZAgnG0FUiKwPzRk3jXfWTr6HysIgVYqOXV0K2HE9C/Fu7cmBPMBD\\nrsn0KtS6Nr3QZV5/q7EypGTEjqR9Wa1xEJO067UceWW8mnJxPdTcyUGbH0LQDXUc\\nh0790/A+KVBOzcj0gCOiQAIPqQKBgQDEdjgkAbLrwfyuBLwcohXbeh+tPxDD+wYp\\nlDZoxR91Cx+VZDkXAkaQ1dP7mcr8BghID2FUn/gCv/r0E+LwGMRIUOOcptYtNvW8\\nY3616X7xj9quy+Bo92FmXHOfg87TwJanRW2elXQ4zdTT455b2rX0S5rZZ55np+Wt\\n0Y5ifg7Y6wKBgQDAyram3QVtjntNaClzxZuvw6W+sWdVQnVDTd5ZvUlVjd0GWZrA\\nn7dU9KwbS9y5+roNb1muQdV8iLCHB5mt/edXHuDlQRwFDn97XQH3g935DY2rCT/I\\nF0Vu06iLU5lGEAzVCWKZnJip3ISh/ZL+c/VhLooDAz8vvJ9lvRvzOyFe4QKBgQCu\\nME9IhYUZCU87D5GmJYQRq+8IEYGQ8jGdjf7L1/MzOGCReWIw52CfrCcezlYuLD8N\\nfS2D9a3U6ljcLgGpdZwarjWRQE9nmXI5zVGcguF91llDYBqhlqzyMAiIJzqrfvdU\\nafdugv6gGseX8xoGQR3Q83PhTMEjELdQ7Rs8cp5BbwKBgBo6Sjt81+equZeJaaTk\\n6ckT3ihBzuMCFlYDKCFngRtdkqPOZsAIBYBFJoOw5V0Tx03yiFiLnoBZe3CMv0Ck\\nj9JDhz09hMd7mfZhwp+05LSZ4Vi/gB7CkpcuNYrQS0r/eBG4y/uoxG6IZpKQ3I2L\\nN5FxcSi9ACV8fAWFD+pBrDj0\\n-----END PRIVATE KEY-----\\n\",\n  \"client_email\": \"test-user-no-access@nodal-time-474015-p5.iam.gserviceaccount.com\",\n  \"client_id\": \"103670581071086602180\",\n  \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n  \"token_uri\": \"https://oauth2.googleapis.com/token\",\n  \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n  \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/test-user-no-access%40nodal-time-474015-p5.iam.gserviceaccount.com\",\n  \"universe_domain\": \"googleapis.com\"\n}\n","unique_id":"103670581071086602180"},"Policy":"{\"user\": \"test-user-no-access\", \"service\": \"projects/nodal-time-474015-p5/buckets/ccc-test-bucket-nodal-time-474015-p5\", \"level\": \"none\", \"role\": \"\"}"}
no-access-list-error.txt
View Content (409 bytes)
failed to list objects: Get "https://storage.googleapis.com/storage/v1/b/ccc-test-bucket-nodal-time-474015-p5/o?alt=json&delimiter=&endOffset=&includeFoldersAsPrefixes=false&includeTrailingDelimiter=false&matchGlob=&pageToken=&prefix=&prettyPrint=false&projection=full&startOffset=&versions=false": auth: cannot fetch token: 400
Response: {"error":"invalid_grant","error_description":"Invalid JWT Signature."}
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"58µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"40µs
And I refer to "{result}" as "storage"27µs
And I call "{api}" with "GetServiceAPI" using argument "iam"34µs
And I refer to "{result}" as "iamService"23µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"1s
And I refer to "{result}" as "testUserRead"37µs
And I attach "{result}" to the test output as "read-user-identity.json"39µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{true}"55µs
And "{result}" is not an error26µ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 attach "{result}" to the test output as "read-storage-service.json"19µs
And I refer to "{result}" as "userStorage"17µs
When I call "{userStorage}" with "ListObjects" using argument "{ResourceName}"19µs
Then "{result}" is not an error20µs
And I attach "{result}" to the test output as "read-list-objects-result.json"18µs
📎 Attachments:
read-user-identity.json
View Content (228 bytes)
failed to create service account key for test-user-read: rpc error: code = NotFound desc = Service account projects/nodal-time-474015-p5/serviceAccounts/test-user-read@nodal-time-474015-p5.iam.gserviceaccount.com does not exist.
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"42µ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"18µs
And I refer to "{result}" as "iamService"16µ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}"722ms
policy check failed: GCP Cloud Storage Block Public Access Check:
Then "{result}" is true31µs
📎 Attachments:
policy-result-no-public-access.json
View JSON (1697 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN01/AR01/no-public-access/gcp.yaml",
  "name": "GCP Cloud Storage 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": 10,
  "validity_commentary": "This query validates that Uniform Bucket-Level Access is enabled on the GCP bucket, which prevents unauthenticated or ACL-based public access. Strengths: - Directly verifies the IAM configuration for uniform access - When UBLA is enabled, only IAM bindings control access — no public ACLs - Consistent with GCP security best practices\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=\"json(iamConfiguration.uniformBucketLevelAccess)\"\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=\"json(iamConfiguration.uniformBucketLevelAccess)\"\n",
  "query_output": "null\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.iamConfiguration.uniformBucketLevelAccess.enabled",
      "expected_values": [],
      "validation_rule": "^true$",
      "description": "Verifies that Uniform Bucket-Level Access is enabled, preventing unauthenticated access via legacy ACLs.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.iamConfiguration.uniformBucketLevelAccess.enabled: unsupported value type \u003cnil\u003e for select, expected map[string]interface{} or []interface{}"
    }
  ]
}
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"46µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"31µs
And I refer to "{result}" as "storage"27µs
And I call "{api}" with "GetServiceAPI" using argument "iam"18µs
And I refer to "{result}" as "iamService"16µs
And I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "test content"137ms
And "{result}" is not an error32µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-access", "{UID}", and "none"49µs
And I refer to "{result}" as "testUserNoAccess"28µ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}"40µs
And "{result}" is not an error29µs
And I refer to "{result}" as "userStorage"22µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"29ms
Then "{result}" is an error29µs
And I attach "{result}" to the test output as "no-access-read-object-error.txt"37µs
📎 Attachments:
no-access-user-identity.json
View JSON (3035 bytes)
{"UserName":"test-user-no-access","Provider":"gcp","Credentials":{"client_email":"test-user-no-access@nodal-time-474015-p5.iam.gserviceaccount.com","email":"test-user-no-access@nodal-time-474015-p5.iam.gserviceaccount.com","private_key_id":"3dcbc99aba98933bcc4aa49064be50624a6bd1cb","project_id":"nodal-time-474015-p5","service_account_key":"{\n  \"type\": \"service_account\",\n  \"project_id\": \"nodal-time-474015-p5\",\n  \"private_key_id\": \"3dcbc99aba98933bcc4aa49064be50624a6bd1cb\",\n  \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+aRWDoHDQhXBR\\ncFPov9ii1cxS4tN3t5EXTJAYVqXYyy2WxM/0OwzNQoDdQ/xBD9VfMXBuN4CvVu2p\\nkfxkXvvOCjy/EiJdjs0zC5whA7jv7ebPg5xGy0sUaJ2ItJBejkiHFGikunokLQMn\\nZTCJIZ19DX6M9hMIGGAdHGTHRTjYPeA9iR4gcNJJ/r9CRc/cf/6oa2G3FMwJc2Kk\\n7Ez9JvqWM91fOgqOep8TkD3v8EXBzKUmacQrsRINhT45acI0FJRjxo5RrPHrPjz1\\nne91Qx2w4d5eqe3chWQcXFXfW8lJoBOrpB8UprNjsNlw8JIPEZN2y9jn/eRxXwGE\\n3HaIWPgjAgMBAAECggEAAZ70Y25dhEqDT8L3u+UyMdF4thxSaSD9BIIO2F4yEDvk\\n84639HmYnM2CbYYs76lAvqXPMekAC/FFM0Q+YUsogP/RgsQW2ppvtCdwcoMZ5m6T\\nue2CMMOw3zrnjs8QSyj4t7APsGNNmeEJaolWfFX20lma9Uk8+6Morxn1E6sxSWON\\nrCsijhJs+n821/qwEry4CyxH+EwWij2za7yZYoh4fDlwa0MXc1TJfbetmJ+FYjYM\\nwa9yaxsNPVnVrzTNgcgAMV+gAjmAjusE2Fe61rcw5jkEqmH+Tmw7na7VkjkvDcm0\\nPqSoitDcau6LJ/KYy6ihXcd713YvuB/ivfup0OtQuQKBgQD4HWVLPnVI/jb0p/rU\\n8XV661YOO4LMZAgnG0FUiKwPzRk3jXfWTr6HysIgVYqOXV0K2HE9C/Fu7cmBPMBD\\nrsn0KtS6Nr3QZV5/q7EypGTEjqR9Wa1xEJO067UceWW8mnJxPdTcyUGbH0LQDXUc\\nh0790/A+KVBOzcj0gCOiQAIPqQKBgQDEdjgkAbLrwfyuBLwcohXbeh+tPxDD+wYp\\nlDZoxR91Cx+VZDkXAkaQ1dP7mcr8BghID2FUn/gCv/r0E+LwGMRIUOOcptYtNvW8\\nY3616X7xj9quy+Bo92FmXHOfg87TwJanRW2elXQ4zdTT455b2rX0S5rZZ55np+Wt\\n0Y5ifg7Y6wKBgQDAyram3QVtjntNaClzxZuvw6W+sWdVQnVDTd5ZvUlVjd0GWZrA\\nn7dU9KwbS9y5+roNb1muQdV8iLCHB5mt/edXHuDlQRwFDn97XQH3g935DY2rCT/I\\nF0Vu06iLU5lGEAzVCWKZnJip3ISh/ZL+c/VhLooDAz8vvJ9lvRvzOyFe4QKBgQCu\\nME9IhYUZCU87D5GmJYQRq+8IEYGQ8jGdjf7L1/MzOGCReWIw52CfrCcezlYuLD8N\\nfS2D9a3U6ljcLgGpdZwarjWRQE9nmXI5zVGcguF91llDYBqhlqzyMAiIJzqrfvdU\\nafdugv6gGseX8xoGQR3Q83PhTMEjELdQ7Rs8cp5BbwKBgBo6Sjt81+equZeJaaTk\\n6ckT3ihBzuMCFlYDKCFngRtdkqPOZsAIBYBFJoOw5V0Tx03yiFiLnoBZe3CMv0Ck\\nj9JDhz09hMd7mfZhwp+05LSZ4Vi/gB7CkpcuNYrQS0r/eBG4y/uoxG6IZpKQ3I2L\\nN5FxcSi9ACV8fAWFD+pBrDj0\\n-----END PRIVATE KEY-----\\n\",\n  \"client_email\": \"test-user-no-access@nodal-time-474015-p5.iam.gserviceaccount.com\",\n  \"client_id\": \"103670581071086602180\",\n  \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n  \"token_uri\": \"https://oauth2.googleapis.com/token\",\n  \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n  \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/test-user-no-access%40nodal-time-474015-p5.iam.gserviceaccount.com\",\n  \"universe_domain\": \"googleapis.com\"\n}\n","unique_id":"103670581071086602180"},"Policy":"{\"user\": \"test-user-no-access\", \"service\": \"projects/nodal-time-474015-p5/buckets/ccc-test-bucket-nodal-time-474015-p5\", \"level\": \"none\", \"role\": \"\"}"}
no-access-read-object-error.txt
View Content (281 bytes)
failed to create reader for object test-object=1775041421670.txt: Get "https://storage.googleapis.com/ccc-test-bucket-nodal-time-474015-p5/test-object=1775041421670.txt": auth: cannot fetch token: 400
Response: {"error":"invalid_grant","error_description":"Invalid JWT Signature."}
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"45µ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"22µs
And I refer to "{result}" as "iamService"18µs
And I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "test content"130ms
And "{result}" is not an error32µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"834ms
And I refer to "{result}" as "testUserRead"24µs
And I attach "{result}" to the test output as "read-user-identity.json"33µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{true}"54µs
And "{result}" is not an error38µ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 attach "{result}" to the test output as "read-storage-service.json"29µs
And I refer to "{result}" as "userStorage"24µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"32µs
Then "{result}" is not an error25µs
And I attach "{result}" to the test output as "read-read-object-result.json"17µs
📎 Attachments:
read-user-identity.json
View Content (228 bytes)
failed to create service account key for test-user-read: rpc error: code = NotFound desc = Service account projects/nodal-time-474015-p5/serviceAccounts/test-user-read@nodal-time-474015-p5.iam.gserviceaccount.com does not exist.
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"42µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"38µs
And I refer to "{result}" as "storage"23µs
And I call "{api}" with "GetServiceAPI" using argument "iam"31µ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"187ms
And "{result}" is not an error37µ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}"838ms
Then "{result}" is true35µs
📎 Attachments:
policy-result-object-storage-no-public-principals.json
View JSON (1947 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/gcp.yaml",
  "name": "GCP Cloud Storage IAM in Use",
  "service_type": "object-storage",
  "requirement_text": "All unauthorized requests MUST be blocked. Access MUST be controlled by IAM.\n",
  "validity_score": 7,
  "validity_commentary": "ObjStor.CN01.AR01 already validates no-public-access. This check validates that IAM bindings exist on the bucket, proving access control is identity-based rather than public. At least one binding indicates IAM is in use. Strengths: - Confirms IAM is configured for the bucket - Bindings scope access to specific identities (user, group, service account) Limitations: - Does not validate least-privilege bindings - Inherited project-level bindings may not appear in bucket policy - Behavioral testing verifies unauthorized access is blocked at runtime\n",
  "query_template": "POLICY=$(gcloud storage buckets get-iam-policy gs://${ResourceName} --format=json 2\u003e/dev/null || echo \"{}\")\nCOUNT=$(echo \"$POLICY\" | jq -r '.bindings | length' 2\u003e/dev/null || echo \"0\")\necho \"{\\\"iamInUse\\\": $COUNT}\"\n",
  "query_executed": "POLICY=$(gcloud storage buckets get-iam-policy gs://ccc-test-bucket-nodal-time-474015-p5 --format=json 2\u003e/dev/null || echo \"{}\")\nCOUNT=$(echo \"$POLICY\" | jq -r '.bindings | length' 2\u003e/dev/null || echo \"0\")\necho \"{\\\"iamInUse\\\": $COUNT}\"\n",
  "query_output": "{\"iamInUse\": 2}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.iamInUse",
      "expected_values": [],
      "validation_rule": "^[1-9][0-9]*$",
      "description": "Validates that at least one IAM binding exists on the bucket. IAM is the required access control mechanism; no bindings indicates access may not be properly scoped.\n",
      "actual_value": "[2]",
      "passed": true
    }
  ]
}
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"33µs
And I refer to "{result}" as "storage"24µs
And I call "{api}" with "GetServiceAPI" using argument "iam"21µs
And I refer to "{result}" as "iamService"18µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-access", "{UID}", and "none"52µs
And I refer to "{result}" as "testUserNoAccess"22µs
And I attach "{result}" to the test output as "no-access-user-identity.json"67µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"41µs
And "{result}" is not an error21µs
And I refer to "{result}" as "userStorage"26µs
When I call "{userStorage}" with "CreateBucket" using argument "test-bucket-no-access"198ms
Then "{result}" is an error28µs
And I attach "{result}" to the test output as "no-access-create-bucket-error.txt"31µs
📎 Attachments:
no-access-user-identity.json
View JSON (3035 bytes)
{"UserName":"test-user-no-access","Provider":"gcp","Credentials":{"client_email":"test-user-no-access@nodal-time-474015-p5.iam.gserviceaccount.com","email":"test-user-no-access@nodal-time-474015-p5.iam.gserviceaccount.com","private_key_id":"3dcbc99aba98933bcc4aa49064be50624a6bd1cb","project_id":"nodal-time-474015-p5","service_account_key":"{\n  \"type\": \"service_account\",\n  \"project_id\": \"nodal-time-474015-p5\",\n  \"private_key_id\": \"3dcbc99aba98933bcc4aa49064be50624a6bd1cb\",\n  \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+aRWDoHDQhXBR\\ncFPov9ii1cxS4tN3t5EXTJAYVqXYyy2WxM/0OwzNQoDdQ/xBD9VfMXBuN4CvVu2p\\nkfxkXvvOCjy/EiJdjs0zC5whA7jv7ebPg5xGy0sUaJ2ItJBejkiHFGikunokLQMn\\nZTCJIZ19DX6M9hMIGGAdHGTHRTjYPeA9iR4gcNJJ/r9CRc/cf/6oa2G3FMwJc2Kk\\n7Ez9JvqWM91fOgqOep8TkD3v8EXBzKUmacQrsRINhT45acI0FJRjxo5RrPHrPjz1\\nne91Qx2w4d5eqe3chWQcXFXfW8lJoBOrpB8UprNjsNlw8JIPEZN2y9jn/eRxXwGE\\n3HaIWPgjAgMBAAECggEAAZ70Y25dhEqDT8L3u+UyMdF4thxSaSD9BIIO2F4yEDvk\\n84639HmYnM2CbYYs76lAvqXPMekAC/FFM0Q+YUsogP/RgsQW2ppvtCdwcoMZ5m6T\\nue2CMMOw3zrnjs8QSyj4t7APsGNNmeEJaolWfFX20lma9Uk8+6Morxn1E6sxSWON\\nrCsijhJs+n821/qwEry4CyxH+EwWij2za7yZYoh4fDlwa0MXc1TJfbetmJ+FYjYM\\nwa9yaxsNPVnVrzTNgcgAMV+gAjmAjusE2Fe61rcw5jkEqmH+Tmw7na7VkjkvDcm0\\nPqSoitDcau6LJ/KYy6ihXcd713YvuB/ivfup0OtQuQKBgQD4HWVLPnVI/jb0p/rU\\n8XV661YOO4LMZAgnG0FUiKwPzRk3jXfWTr6HysIgVYqOXV0K2HE9C/Fu7cmBPMBD\\nrsn0KtS6Nr3QZV5/q7EypGTEjqR9Wa1xEJO067UceWW8mnJxPdTcyUGbH0LQDXUc\\nh0790/A+KVBOzcj0gCOiQAIPqQKBgQDEdjgkAbLrwfyuBLwcohXbeh+tPxDD+wYp\\nlDZoxR91Cx+VZDkXAkaQ1dP7mcr8BghID2FUn/gCv/r0E+LwGMRIUOOcptYtNvW8\\nY3616X7xj9quy+Bo92FmXHOfg87TwJanRW2elXQ4zdTT455b2rX0S5rZZ55np+Wt\\n0Y5ifg7Y6wKBgQDAyram3QVtjntNaClzxZuvw6W+sWdVQnVDTd5ZvUlVjd0GWZrA\\nn7dU9KwbS9y5+roNb1muQdV8iLCHB5mt/edXHuDlQRwFDn97XQH3g935DY2rCT/I\\nF0Vu06iLU5lGEAzVCWKZnJip3ISh/ZL+c/VhLooDAz8vvJ9lvRvzOyFe4QKBgQCu\\nME9IhYUZCU87D5GmJYQRq+8IEYGQ8jGdjf7L1/MzOGCReWIw52CfrCcezlYuLD8N\\nfS2D9a3U6ljcLgGpdZwarjWRQE9nmXI5zVGcguF91llDYBqhlqzyMAiIJzqrfvdU\\nafdugv6gGseX8xoGQR3Q83PhTMEjELdQ7Rs8cp5BbwKBgBo6Sjt81+equZeJaaTk\\n6ckT3ihBzuMCFlYDKCFngRtdkqPOZsAIBYBFJoOw5V0Tx03yiFiLnoBZe3CMv0Ck\\nj9JDhz09hMd7mfZhwp+05LSZ4Vi/gB7CkpcuNYrQS0r/eBG4y/uoxG6IZpKQ3I2L\\nN5FxcSi9ACV8fAWFD+pBrDj0\\n-----END PRIVATE KEY-----\\n\",\n  \"client_email\": \"test-user-no-access@nodal-time-474015-p5.iam.gserviceaccount.com\",\n  \"client_id\": \"103670581071086602180\",\n  \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n  \"token_uri\": \"https://oauth2.googleapis.com/token\",\n  \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n  \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/test-user-no-access%40nodal-time-474015-p5.iam.gserviceaccount.com\",\n  \"universe_domain\": \"googleapis.com\"\n}\n","unique_id":"103670581071086602180"},"Policy":"{\"user\": \"test-user-no-access\", \"service\": \"projects/nodal-time-474015-p5/buckets/ccc-test-bucket-nodal-time-474015-p5\", \"level\": \"none\", \"role\": \"\"}"}
no-access-create-bucket-error.txt
View Content (295 bytes)
failed to create bucket test-bucket-no-access: googleapi: Error 403: test-user-no-access@nodal-time-474015-p5.iam.gserviceaccount.com does not have storage.buckets.create access to the Google Cloud project. Permission 'storage.buckets.create' denied on resource (or it may not exist)., forbidden
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"43µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"40µs
And I refer to "{result}" as "storage"26µs
And I call "{api}" with "GetServiceAPI" using argument "iam"37µs
And I refer to "{result}" as "iamService"32µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"143ms
And I refer to "{result}" as "testUserWrite"24µs
And I attach "{result}" to the test output as "write-user-identity.json"40µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"48µs
And "{result}" is not an error45µ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 attach "{result}" to the test output as "write-storage-service.json"17µs
And I refer to "{result}" as "userStorage"13µs
When I call "{userStorage}" with "CreateBucket" using argument "test-bucket-write"15µs
Then "{result}" is not an error14µs
And I attach "{result}" to the test output as "write-create-bucket-result.json"15µs
And I call "{storage}" with "DeleteBucket" using argument "{result.ID}"13µs
📎 Attachments:
write-user-identity.json
View Content (228 bytes)
failed to create service account test-user-write: rpc error: code = ResourceExhausted desc = A quota has been reached for project number 784623368087: Service accounts created per minute per project.
error details: retry in 1m0s
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"41µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"35µs
And I refer to "{result}" as "storage"16µs
And I call "{api}" with "GetServiceAPI" using argument "iam"18µ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}"748ms
Then "{result}" is true36µs
📎 Attachments:
policy-result-object-storage-no-public-principals.json
View JSON (1947 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/gcp.yaml",
  "name": "GCP Cloud Storage IAM in Use",
  "service_type": "object-storage",
  "requirement_text": "All unauthorized requests MUST be blocked. Access MUST be controlled by IAM.\n",
  "validity_score": 7,
  "validity_commentary": "ObjStor.CN01.AR01 already validates no-public-access. This check validates that IAM bindings exist on the bucket, proving access control is identity-based rather than public. At least one binding indicates IAM is in use. Strengths: - Confirms IAM is configured for the bucket - Bindings scope access to specific identities (user, group, service account) Limitations: - Does not validate least-privilege bindings - Inherited project-level bindings may not appear in bucket policy - Behavioral testing verifies unauthorized access is blocked at runtime\n",
  "query_template": "POLICY=$(gcloud storage buckets get-iam-policy gs://${ResourceName} --format=json 2\u003e/dev/null || echo \"{}\")\nCOUNT=$(echo \"$POLICY\" | jq -r '.bindings | length' 2\u003e/dev/null || echo \"0\")\necho \"{\\\"iamInUse\\\": $COUNT}\"\n",
  "query_executed": "POLICY=$(gcloud storage buckets get-iam-policy gs://ccc-test-bucket-nodal-time-474015-p5 --format=json 2\u003e/dev/null || echo \"{}\")\nCOUNT=$(echo \"$POLICY\" | jq -r '.bindings | length' 2\u003e/dev/null || echo \"0\")\necho \"{\\\"iamInUse\\\": $COUNT}\"\n",
  "query_output": "{\"iamInUse\": 2}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.iamInUse",
      "expected_values": [],
      "validation_rule": "^[1-9][0-9]*$",
      "description": "Validates that at least one IAM binding exists on the bucket. IAM is the required access control mechanism; no bindings indicates access may not be properly scoped.\n",
      "actual_value": "[2]",
      "passed": true
    }
  ]
}
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"64µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"42µs
And I refer to "{result}" as "storage"27µs
And "{result}" is not an error29µs
And I call "{api}" with "GetServiceAPI" using argument "iam"30µs
And I refer to "{result}" as "iamService"15µs
And "{result}" is not an error23µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"1s
And I refer to "{result}" as "testUserRead"25µs
And I attach "{result}" to the test output as "read-user-identity.json"39µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{true}"69µs
And "{result}" is not an error26µ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"17µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-write-object={Timestamp}.txt", and "test content"18µs
Then "{result}" is an error19µs
And I attach "{result}" to the test output as "read-create-object-error.txt"14µs
📎 Attachments:
read-user-identity.json
View Content (228 bytes)
failed to create service account key for test-user-read: rpc error: code = NotFound desc = Service account projects/nodal-time-474015-p5/serviceAccounts/test-user-read@nodal-time-474015-p5.iam.gserviceaccount.com does not exist.
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"40µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"38µs
And I refer to "{result}" as "storage"14µs
And "{result}" is not an error17µs
And I call "{api}" with "GetServiceAPI" using argument "iam"23µs
And I refer to "{result}" as "iamService"14µs
And "{result}" is not an error31µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"94ms
And I refer to "{result}" as "testUserWrite"25µs
And I attach "{result}" to the test output as "write-user-identity.json"32µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"60µs
And "{result}" is not an error30µ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 attach "{result}" to the test output as "write-storage-service.json"22µs
And I refer to "{result}" as "userStorage"13µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-write-object={Timestamp}.txt", and "test content"20µs
Then "{result}" is not an error16µs
And I attach "{result}" to the test output as "write-create-object-result.json"16µs
📎 Attachments:
write-user-identity.json
View Content (228 bytes)
failed to create service account test-user-write: rpc error: code = ResourceExhausted desc = A quota has been reached for project number 784623368087: Service accounts created per minute per project.
error details: retry in 1m0s
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"31µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"44µs
And I refer to "{result}" as "storage"26µs
And "{result}" is not an error18µs
And I call "{api}" with "GetServiceAPI" using argument "iam"19µs
And I refer to "{result}" as "iamService"20µs
And "{result}" is not an error16µ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}"818ms
Then "{result}" is true34µs
📎 Attachments:
policy-result-object-storage-no-public-principals.json
View JSON (1947 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/gcp.yaml",
  "name": "GCP Cloud Storage IAM in Use",
  "service_type": "object-storage",
  "requirement_text": "All unauthorized requests MUST be blocked. Access MUST be controlled by IAM.\n",
  "validity_score": 7,
  "validity_commentary": "ObjStor.CN01.AR01 already validates no-public-access. This check validates that IAM bindings exist on the bucket, proving access control is identity-based rather than public. At least one binding indicates IAM is in use. Strengths: - Confirms IAM is configured for the bucket - Bindings scope access to specific identities (user, group, service account) Limitations: - Does not validate least-privilege bindings - Inherited project-level bindings may not appear in bucket policy - Behavioral testing verifies unauthorized access is blocked at runtime\n",
  "query_template": "POLICY=$(gcloud storage buckets get-iam-policy gs://${ResourceName} --format=json 2\u003e/dev/null || echo \"{}\")\nCOUNT=$(echo \"$POLICY\" | jq -r '.bindings | length' 2\u003e/dev/null || echo \"0\")\necho \"{\\\"iamInUse\\\": $COUNT}\"\n",
  "query_executed": "POLICY=$(gcloud storage buckets get-iam-policy gs://ccc-test-bucket-nodal-time-474015-p5 --format=json 2\u003e/dev/null || echo \"{}\")\nCOUNT=$(echo \"$POLICY\" | jq -r '.bindings | length' 2\u003e/dev/null || echo \"0\")\necho \"{\\\"iamInUse\\\": $COUNT}\"\n",
  "query_output": "{\"iamInUse\": 2}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.iamInUse",
      "expected_values": [],
      "validation_rule": "^[1-9][0-9]*$",
      "description": "Validates that at least one IAM binding exists on the bucket. IAM is the required access control mechanism; no bindings indicates access may not be properly scoped.\n",
      "actual_value": "[2]",
      "passed": true
    }
  ]
}
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"72µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"41µs
And I refer to "{result}" as "storage"28µs
And I call "{api}" with "GetServiceAPI" using argument "iam"38µs
And I refer to "{result}" as "iamService"20µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "test data"135ms
Then "{result}" is not an error26µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"110ms
And I refer to "{result}" as "testUserRead"26µs
And I attach "{result}" to the test output as "read-user-identity.json"40µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{true}"50µs
And "{result}" is not an error41µ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"16µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"20µs
Then "{result}" is not an error19µs
When I call "{storage}" with "SetObjectPermission" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "none"19µs
Then "{result}" is an error16µs
And I attach "{result}" to the test output as "set-object-permission-error.txt"18µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"18µs
Then "{result}" is not an error19µs
📎 Attachments:
read-user-identity.json
View Content (227 bytes)
failed to create service account test-user-read: rpc error: code = ResourceExhausted desc = A quota has been reached for project number 784623368087: Service accounts created per minute per project.
error details: retry in 1m0s
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"36µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"39µs
And I refer to "{result}" as "storage"28µs
And I call "{api}" with "GetServiceAPI" using argument "iam"20µs
And I refer to "{result}" as "iamService"15µ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}"743ms
policy check failed: GCP Cloud Storage Uniform Bucket-Level Access Check:
Then "{result}" is true27µs
📎 Attachments:
policy-result-uniform-bucket-level-access.json
View JSON (1598 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN02/AR01/uniform-bucket-level-access/gcp.yaml",
  "name": "GCP Cloud Storage Uniform Bucket-Level 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": 10,
  "validity_commentary": "This query validates that Uniform Bucket-Level Access (UBLA) is enabled. UBLA ensures that only IAM policies govern access to the bucket and its objects. Strengths: - Directly queries the UBLA configuration - Guarantees no ad-hoc object ACLs can be used - Consistent with GCP security best practices\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=\"json(iamConfiguration.uniformBucketLevelAccess)\"\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=\"json(iamConfiguration.uniformBucketLevelAccess)\"\n",
  "query_output": "null\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.iamConfiguration.uniformBucketLevelAccess.enabled",
      "expected_values": [],
      "validation_rule": "^true$",
      "description": "Verifies that uniform Bucket-Level Access is enabled.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.iamConfiguration.uniformBucketLevelAccess.enabled: unsupported value type \u003cnil\u003e for select, expected map[string]interface{} or []interface{}"
    }
  ]
}
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"49µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"29µs
And I refer to "{result}" as "storage"15µs
And I call "{api}" with "GetServiceAPI" using argument "iam"17µs
And I refer to "{result}" as "iamService"18µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "test data"141ms
Then "{result}" is not an error26µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-access", "{UID}", and "none"40µs
And I refer to "{result}" as "testUserNoAccess"18µs
And I attach "{result}" to the test output as "no-access-user-identity.json"56µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"49µs
And "{result}" is not an error29µs
And I refer to "{result}" as "userStorage"23µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"170ms
Then "{result}" is an error26µs
When I call "{storage}" with "SetObjectPermission" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "read"74ms
Then "{result}" is an error27µs
expected {result} to be an error, got
And I attach "{result}" to the test output as "set-object-permission-error.txt"19µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"17µs
Then "{result}" is an error17µs
📎 Attachments:
no-access-user-identity.json
View JSON (3035 bytes)
{"UserName":"test-user-no-access","Provider":"gcp","Credentials":{"client_email":"test-user-no-access@nodal-time-474015-p5.iam.gserviceaccount.com","email":"test-user-no-access@nodal-time-474015-p5.iam.gserviceaccount.com","private_key_id":"3dcbc99aba98933bcc4aa49064be50624a6bd1cb","project_id":"nodal-time-474015-p5","service_account_key":"{\n  \"type\": \"service_account\",\n  \"project_id\": \"nodal-time-474015-p5\",\n  \"private_key_id\": \"3dcbc99aba98933bcc4aa49064be50624a6bd1cb\",\n  \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+aRWDoHDQhXBR\\ncFPov9ii1cxS4tN3t5EXTJAYVqXYyy2WxM/0OwzNQoDdQ/xBD9VfMXBuN4CvVu2p\\nkfxkXvvOCjy/EiJdjs0zC5whA7jv7ebPg5xGy0sUaJ2ItJBejkiHFGikunokLQMn\\nZTCJIZ19DX6M9hMIGGAdHGTHRTjYPeA9iR4gcNJJ/r9CRc/cf/6oa2G3FMwJc2Kk\\n7Ez9JvqWM91fOgqOep8TkD3v8EXBzKUmacQrsRINhT45acI0FJRjxo5RrPHrPjz1\\nne91Qx2w4d5eqe3chWQcXFXfW8lJoBOrpB8UprNjsNlw8JIPEZN2y9jn/eRxXwGE\\n3HaIWPgjAgMBAAECggEAAZ70Y25dhEqDT8L3u+UyMdF4thxSaSD9BIIO2F4yEDvk\\n84639HmYnM2CbYYs76lAvqXPMekAC/FFM0Q+YUsogP/RgsQW2ppvtCdwcoMZ5m6T\\nue2CMMOw3zrnjs8QSyj4t7APsGNNmeEJaolWfFX20lma9Uk8+6Morxn1E6sxSWON\\nrCsijhJs+n821/qwEry4CyxH+EwWij2za7yZYoh4fDlwa0MXc1TJfbetmJ+FYjYM\\nwa9yaxsNPVnVrzTNgcgAMV+gAjmAjusE2Fe61rcw5jkEqmH+Tmw7na7VkjkvDcm0\\nPqSoitDcau6LJ/KYy6ihXcd713YvuB/ivfup0OtQuQKBgQD4HWVLPnVI/jb0p/rU\\n8XV661YOO4LMZAgnG0FUiKwPzRk3jXfWTr6HysIgVYqOXV0K2HE9C/Fu7cmBPMBD\\nrsn0KtS6Nr3QZV5/q7EypGTEjqR9Wa1xEJO067UceWW8mnJxPdTcyUGbH0LQDXUc\\nh0790/A+KVBOzcj0gCOiQAIPqQKBgQDEdjgkAbLrwfyuBLwcohXbeh+tPxDD+wYp\\nlDZoxR91Cx+VZDkXAkaQ1dP7mcr8BghID2FUn/gCv/r0E+LwGMRIUOOcptYtNvW8\\nY3616X7xj9quy+Bo92FmXHOfg87TwJanRW2elXQ4zdTT455b2rX0S5rZZ55np+Wt\\n0Y5ifg7Y6wKBgQDAyram3QVtjntNaClzxZuvw6W+sWdVQnVDTd5ZvUlVjd0GWZrA\\nn7dU9KwbS9y5+roNb1muQdV8iLCHB5mt/edXHuDlQRwFDn97XQH3g935DY2rCT/I\\nF0Vu06iLU5lGEAzVCWKZnJip3ISh/ZL+c/VhLooDAz8vvJ9lvRvzOyFe4QKBgQCu\\nME9IhYUZCU87D5GmJYQRq+8IEYGQ8jGdjf7L1/MzOGCReWIw52CfrCcezlYuLD8N\\nfS2D9a3U6ljcLgGpdZwarjWRQE9nmXI5zVGcguF91llDYBqhlqzyMAiIJzqrfvdU\\nafdugv6gGseX8xoGQR3Q83PhTMEjELdQ7Rs8cp5BbwKBgBo6Sjt81+equZeJaaTk\\n6ckT3ihBzuMCFlYDKCFngRtdkqPOZsAIBYBFJoOw5V0Tx03yiFiLnoBZe3CMv0Ck\\nj9JDhz09hMd7mfZhwp+05LSZ4Vi/gB7CkpcuNYrQS0r/eBG4y/uoxG6IZpKQ3I2L\\nN5FxcSi9ACV8fAWFD+pBrDj0\\n-----END PRIVATE KEY-----\\n\",\n  \"client_email\": \"test-user-no-access@nodal-time-474015-p5.iam.gserviceaccount.com\",\n  \"client_id\": \"103670581071086602180\",\n  \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n  \"token_uri\": \"https://oauth2.googleapis.com/token\",\n  \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n  \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/test-user-no-access%40nodal-time-474015-p5.iam.gserviceaccount.com\",\n  \"universe_domain\": \"googleapis.com\"\n}\n","unique_id":"103670581071086602180"},"Policy":"{\"user\": \"test-user-no-access\", \"service\": \"projects/nodal-time-474015-p5/buckets/ccc-test-bucket-nodal-time-474015-p5\", \"level\": \"none\", \"role\": \"\"}"}
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"35µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"41µ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 required26µ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"69µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"64µs
And I refer to "{result}" as "storage"61µs
When I call "{storage}" with "CreateBucket" using argument "ccc-test-soft-delete"3s
Then "{result}" is not an error51µs
And I refer to "{result}" as "testBucket"33µs
And I attach "{result}" to the test output as "created-bucket.json"98µs
When I call "{storage}" with "DeleteBucket" using argument "ccc-test-soft-delete"342ms
Then "{result}" is not an error27µs
When I call "{storage}" with "ListDeletedBuckets"43µs
Then "{result}" is not an error21µs
expected {result} to not be an error, but got: GCS does not support bucket-level soft delete - bucket deletion is immediate
And I attach "{result}" to the test output as "deleted-buckets.json"23µs
And "{result}" should have length greater than "0"21µs
When I call "{storage}" with "RestoreBucket" using argument "ccc-test-soft-delete"18µs
Then "{result}" is not an error21µs
When I call "{storage}" with "ListBuckets"17µs
Then "{result}" is not an error19µs
And I attach "{result}" to the test output as "restored-buckets.json"19µs
When I call "{storage}" with "DeleteBucket" using argument "ccc-test-soft-delete"18µs
Then "{result}" is not an error21µs
📎 Attachments:
created-bucket.json
View JSON (82 bytes)
{"ID":"ccc-test-soft-delete","Name":"ccc-test-soft-delete","Region":"us-central1"}
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"37µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"40µs
And I refer to "{result}" as "storage"23µ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}"720ms
policy check failed: GCP Cloud Storage Soft Delete Check:
Then "{result}" is true33µs
📎 Attachments:
policy-result-bucket-soft-delete.json
View JSON (1770 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN03/AR01/bucket-soft-delete/gcp.yaml",
  "name": "GCP Cloud Storage 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": "GCP Cloud Storage supports bucket soft delete via the softDeletePolicy. This check validates that the soft delete retention duration is set to a non-zero value, ensuring deleted buckets and objects can be recovered. Strengths: - Directly checks the soft delete policy configuration - GCP soft delete is a native feature providing bucket-level recovery Limitations: - Does not verify the exact minimum retention duration required\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=\"json(softDeletePolicy)\"\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=\"json(softDeletePolicy)\"\n",
  "query_output": "null\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.softDeletePolicy.retentionDurationSeconds",
      "expected_values": [],
      "validation_rule": "^[1-9][0-9]*$",
      "description": "Verifies that the soft delete retention duration is set to a positive number of seconds, enabling bucket and object recovery after deletion.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.softDeletePolicy.retentionDurationSeconds: unsupported value type \u003cnil\u003e for select, expected map[string]interface{} or []interface{}"
    }
  ]
}
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"37µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"42µs
And I refer to "{result}" as "storage"24µs
When I call "{storage}" with "GetBucketRetentionDurationDays" using argument "{ResourceName}"31ms
Then "{result}" is not an error31µs
And I refer to "{result}" as "originalRetention"19µs
And I attach "{result}" to the test output as "original-retention-days.txt"38µs
And "{result}" should be greater than "0"49µs
expected {result} (0) to be greater than 0
When I call "{storage}" with "SetBucketRetentionDurationDays" using arguments "{ResourceName}" and "1"19µs
Then "{result}" is an error15µs
And I attach "{result}" to the test output as "set-retention-error.txt"23µs
When I call "{storage}" with "GetBucketRetentionDurationDays" using argument "{ResourceName}"15µs
Then "{result}" is not an error17µs
And "{result}" should equal "{originalRetention}"23µ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"47µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"38µs
And I refer to "{result}" as "storage"24µ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}"700ms
policy check failed: GCP Cloud Storage Retention Policy Lock Check:
Then "{result}" is true35µs
📎 Attachments:
policy-result-bucket-retention-lock.json
View JSON (1721 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN03/AR02/bucket-retention-lock/gcp.yaml",
  "name": "GCP Cloud Storage Retention 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": 10,
  "validity_commentary": "This query validates that a retention policy is set and locked on the GCP bucket. A locked retention policy cannot be removed or shortened, even by project owners. Strengths: - Locking is an irreversible, immutable configuration - Directly checks both the presence and locked state of the policy - Consistent with GCP compliance best practices Limitations: - Does not validate the specific minimum retention period\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=\"json(retentionPolicy)\"\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=\"json(retentionPolicy)\"\n",
  "query_output": "null\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.retentionPolicy.isLocked",
      "expected_values": [],
      "validation_rule": "^true$",
      "description": "Verifies that the retention policy is locked, preventing any modification. A locked retention policy cannot be shortened or removed.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.retentionPolicy.isLocked: unsupported value type \u003cnil\u003e for select, expected map[string]interface{} or []interface{}"
    }
  ]
}
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"41µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"40µs
And I refer to "{result}" as "storage"22µs
And I call "{api}" with "GetServiceAPI" using argument "iam"19µs
And I refer to "{result}" as "iamService"15µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"288ms
And I refer to "{result}" as "testUserWrite"27µs
And I attach "{result}" to the test output as "write-user-identity.json"30µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"50µ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"25µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-retention-object={Timestamp}.txt", and "protected data"34µs
And I attach "{result}" to the test output as "uploaded-object.json"29µs
And I call "{userStorage}" with "GetObjectRetentionDurationDays" using arguments "{ResourceName}" and "test-retention-object={Timestamp}.txt"20µs
Then "{result}" should be greater than "1"17µs
📎 Attachments:
write-user-identity.json
View Content (228 bytes)
failed to create service account test-user-write: rpc error: code = ResourceExhausted desc = A quota has been reached for project number 784623368087: Service accounts created per minute per project.
error details: retry in 1m0s
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"41µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"38µs
And I refer to "{result}" as "storage"27µs
And I call "{api}" with "GetServiceAPI" using argument "iam"38µs
And I refer to "{result}" as "iamService"22µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "immediate-delete-test={Timestamp}.txt", and "test content"148ms
Then "{result}" is not an error23µs
When I call "{storage}" with "DeleteObject" using arguments "{ResourceName}" and "immediate-delete-test={Timestamp}.txt"66ms
Then "{result}" is an error29µs
expected {result} to be an error, got
And I attach "{result}" to the test output as "immediate-delete-error.txt"19µs
And "{result}" should contain "retention"26µs
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"47µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"38µs
And I refer to "{result}" as "storage"31µs
And I call "{api}" with "GetServiceAPI" using argument "iam"26µs
And I refer to "{result}" as "iamService"20µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "retention-period-test={Timestamp}.txt", and "compliance data"125ms
And I call "{storage}" with "GetObjectRetentionDurationDays" using arguments "{ResourceName}" and "retention-period-test={Timestamp}.txt"90ms
Then "{result}" should be greater than "1"56µ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"61µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"47µs
And I refer to "{result}" as "storage"26µs
And I call "{api}" with "GetServiceAPI" using argument "iam"25µs
And I refer to "{result}" as "iamService"22µ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}"711ms
policy check failed: GCP Cloud Storage Default Object Retention Check:
Then "{result}" is true31µs
📎 Attachments:
policy-result-object-default-retention.json
View JSON (1863 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN04/AR01/object-default-retention/gcp.yaml",
  "name": "GCP Cloud Storage Default Object Retention 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": 9,
  "validity_commentary": "This query validates that a default object retention policy is configured on the GCP Cloud Storage bucket. GCP's default object retention ensures all objects created in the bucket automatically inherit the retention duration. Strengths: - Default retention is applied at bucket level, covering all new objects - Directly queries the bucket retention policy configuration Limitations: - Does not validate whether the policy is locked (see CN03.AR02) - Does not validate the specific minimum retention duration\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=\"json(retentionPolicy)\"\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=\"json(retentionPolicy)\"\n",
  "query_output": "null\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.retentionPolicy.retentionPeriod",
      "expected_values": [],
      "validation_rule": "^[1-9][0-9]*$",
      "description": "Verifies that a retention period (in seconds) is configured and greater than zero, ensuring newly created objects automatically inherit a retention policy.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.retentionPolicy.retentionPeriod: unsupported value type \u003cnil\u003e for select, expected map[string]interface{} or []interface{}"
    }
  ]
}
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"52µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"42µs
And I refer to "{result}" as "storage"18µs
And I call "{api}" with "GetServiceAPI" using argument "iam"18µs
And I refer to "{result}" as "iamService"25µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"147ms
And I refer to "{result}" as "testUserWrite"30µs
And I attach "{result}" to the test output as "write-user-identity.json"33µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"49µs
And "{result}" is not an error26µ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"16µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "protected-object={Timestamp}.txt", and "immutable data"19µs
Then "{result}" is not an error15µs
And I attach "{result}" to the test output as "protected-object.json"16µs
When I call "{userStorage}" with "DeleteObject" using arguments "{ResourceName}" and "protected-object={Timestamp}.txt"16µs
Then "{result}" is an error13µs
And I attach "{result}" to the test output as "delete-protected-error.txt"15µs
And "{result}" should contain one of "retention, locked, immutable, protected"19µs
📎 Attachments:
write-user-identity.json
View Content (228 bytes)
failed to create service account test-user-write: rpc error: code = ResourceExhausted desc = A quota has been reached for project number 784623368087: Service accounts created per minute per project.
error details: retry in 1m0s
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"34µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"37µs
And I refer to "{result}" as "storage"24µs
And I call "{api}" with "GetServiceAPI" using argument "iam"21µs
And I refer to "{result}" as "iamService"13µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "admin-protected-object={Timestamp}.txt", and "compliance data"136ms
Then "{result}" is not an error23µs
When I call "{storage}" with "DeleteObject" using arguments "{ResourceName}" and "admin-protected-object={Timestamp}.txt"60ms
Then "{result}" is an error24µs
expected {result} to be an error, got
And I attach "{result}" to the test output as "admin-delete-protected-error.txt"20µs
And "{result}" should contain "retention"18µs
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"40µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"40µs
And I refer to "{result}" as "storage"26µs
And I call "{api}" with "GetServiceAPI" using argument "iam"38µs
And I refer to "{result}" as "iamService"26µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"79ms
And I refer to "{result}" as "testUserWrite"30µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"37µs
And "{result}" is not an error26µ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"18µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "modify-test-object={Timestamp}.txt", and "original content"24µs
Then "{result}" is not an error19µs
And I attach "{result}" to the test output as "original-object.json"16µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "modify-test-object={Timestamp}.txt", and "modified content"21µs
Then "{result}" is an error15µs
And I attach "{result}" to the test output as "modify-protected-error.txt"15µs
And "{result}" should contain one of "retention, locked, immutable, protected, exists"20µs
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"34µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"33µs
And I refer to "{result}" as "storage"27µs
And I call "{api}" with "GetServiceAPI" using argument "iam"33µs
And I refer to "{result}" as "iamService"15µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "readable-protected-object={Timestamp}.txt", and "readable data"124ms
Then "{result}" is not an error26µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"118ms
And I refer to "{result}" as "testUserRead"33µs
And I attach "{result}" to the test output as "read-user-identity.json"33µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{true}"64µs
And "{result}" is not an error32µ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"16µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "readable-protected-object={Timestamp}.txt"20µs
Then "{result}" is not an error18µs
And I refer to "{result}" as "readResult"14µs
And I attach "{result}" to the test output as "read-protected-object.json"22µs
And "{readResult.Name}" is "readable-protected-object={Timestamp}.txt"21µs
📎 Attachments:
read-user-identity.json
View Content (227 bytes)
failed to create service account test-user-read: rpc error: code = ResourceExhausted desc = A quota has been reached for project number 784623368087: Service accounts created per minute per project.
error details: retry in 1m0s
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"38µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"40µs
And I refer to "{result}" as "storage"26µs
And I call "{api}" with "GetServiceAPI" using argument "iam"21µ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}"746ms
policy check failed: GCP Cloud Storage Object Retention Enforcement Check:
Then "{result}" is true36µs
📎 Attachments:
policy-result-object-retention-enforcement.json
View JSON (2262 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN04/AR02/object-retention-enforcement/gcp.yaml",
  "name": "GCP Cloud Storage 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 retention policy is configured and locked on the GCP bucket, preventing deletion or modification of any object before its retention period has elapsed. Strengths: - Locked policy cannot be shortened, removed, or overridden - Directly queries the retention policy lock status - Consistent with GCP compliance best practices Limitations: - Retention period is specified in seconds in GCP\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=\"json(retentionPolicy)\"\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=\"json(retentionPolicy)\"\n",
  "query_output": "null\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.retentionPolicy.isLocked",
      "expected_values": [],
      "validation_rule": "^true$",
      "description": "Verifies that the retention policy is locked, enforcing retention and preventing object deletion or modification before the period expires.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.retentionPolicy.isLocked: unsupported value type \u003cnil\u003e for select, expected map[string]interface{} or []interface{}"
    },
    {
      "jsonpath": "$.retentionPolicy.retentionPeriod",
      "expected_values": [],
      "validation_rule": "",
      "description": "Verifies that the retention period meets the configured minimum (seconds), confirming objects are actively protected from modification or deletion.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.retentionPolicy.retentionPeriod: unsupported value type \u003cnil\u003e for select, expected map[string]interface{} or []interface{}"
    }
  ]
}
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"51µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"58µs
And I refer to "{result}" as "storage"16µs
When I call "{storage}" with "IsBucketVersioningEnabled" using argument "{ResourceName}"36µs
Then "{result}" is true18µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "versioned-object.txt", and "test content"125ms
And I refer to "{result}" as "createdObject"21µs
Then "{createdObject.VersionID}" contains "20"25µs
expected {createdObject.VersionID} to contain '20', but got '1775041435846380'
And I attach "{result}" to the test output as "versioned-object.json"18µ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"33µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"41µs
And I refer to "{result}" as "storage"23µ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}"724ms
policy check failed: GCP Cloud Storage Bucket Versioning Configuration:
Then "{result}" is true33µs
📎 Attachments:
policy-result-object-storage-versioning.json
View JSON (3582 bytes)
{
  "policy_path": "/home/runner/work/ccc-cfi-compliance/ccc-cfi-compliance/testing/policy/CCC.ObjStor/CCC.ObjStor.CN05/AR01/object-storage-versioning/gcp.yaml",
  "name": "GCP Cloud Storage Bucket 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 GCP Cloud Storage bucket versioning is enabled. When versioning is enabled, each object version gets a unique generation number. Strengths: - Directly queries versioning configuration - GCS automatically assigns unique generation numbers - Versioning can be suspended but history is preserved Limitations: - Does not validate lifecycle rules that may delete versions - Suspended versioning stops creating new versions\n",
  "query_template": "gcloud storage buckets describe gs://${ResourceName} \\\n  --format=json\n",
  "query_executed": "gcloud storage buckets describe gs://ccc-test-bucket-nodal-time-474015-p5 \\\n  --format=json\n",
  "query_output": "{\n  \"acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"creation_time\": \"2026-04-01T10:54:57+0000\",\n  \"default_acl\": [\n    {\n      \"entity\": \"project-owners-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"owners\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-editors-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"editors\"\n      },\n      \"role\": \"OWNER\"\n    },\n    {\n      \"entity\": \"project-viewers-784623368087\",\n      \"projectTeam\": {\n        \"projectNumber\": \"784623368087\",\n        \"team\": \"viewers\"\n      },\n      \"role\": \"READER\"\n    }\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1775040897017591422,\n  \"labels\": {\n    \"ccc_compliance_test\": \"1775041382\"\n  },\n  \"location\": \"US-CENTRAL1\",\n  \"location_type\": \"region\",\n  \"metageneration\": 3,\n  \"name\": \"ccc-test-bucket-nodal-time-474015-p5\",\n  \"public_access_prevention\": \"inherited\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-04-01T10:54:57.224000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://ccc-test-bucket-nodal-time-474015-p5/\",\n  \"uniform_bucket_level_access\": false,\n  \"update_time\": \"2026-04-01T11:03:03+0000\"\n}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.versioning.enabled",
      "expected_values": [],
      "validation_rule": "^true$",
      "description": "Verifies that versioning is enabled on the bucket. When enabled, all objects receive unique generation numbers automatically.\n",
      "actual_value": "",
      "passed": false,
      "error": "JSONPath query failed $.versioning.enabled: unknown key versioning"
    }
  ]
}
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"51µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"50µs
And I refer to "{result}" as "storage"38µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "original content"124ms
And I refer to "{result.VersionID}" as "version1"30µs
And I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "modified content"134ms
And I refer to "{result.VersionID}" as "version2"26µs
Then "{version1}" is not equal to "{version2}"25µ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"18µs
Then no-op required17µ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"42µs
And I refer to "{result}" as "storage"22µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "original content"115ms
And I refer to "{result.VersionID}" as "version1"35µs
And I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "modified content"144ms
And I refer to "{result.VersionID}" as "version2"29µs
And I call "{storage}" with "ReadObjectAtVersion" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "{version1}"77ms
And I attach "{result}" to the test output as "original-content.json"34µs
Then "{result.Data}" contains "original content"57µs
expected {result.Data} to contain 'original content', but got ''
When I call "{storage}" with "ReadObjectAtVersion" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "{version2}"39µs
Then "{result.Data}" contains "modified content"36µs
And I attach "{result}" to the test output as "modified-content.json"34µs
📎 Attachments:
original-content.json
View Content (128 bytes)
failed to create reader for object version-test-object=1775041436884.txt version 1775041436944881: storage: object doesn't exist
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"46µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"45µs
And I refer to "{result}" as "storage"35µs
Then no-op required29µ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"41µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"41µs
And I refer to "{result}" as "storage"30µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "recover-deleted-object={Timestamp}.txt", and "data to retain"125ms
And I refer to "{result.VersionID}" as "retainedVersionId"53µs
When I call "{storage}" with "DeleteObject" using arguments "{ResourceName}" and "recover-deleted-object={Timestamp}.txt"59ms
When I call "{storage}" with "ReadObjectAtVersion" using arguments "{ResourceName}", "recover-deleted-object={Timestamp}.txt", and "{retainedVersionId}"117ms
Then "{result.Data}" contains "data to retain"42µs
expected {result.Data} to contain 'data to retain', but got ''
And I attach "{result}" to the test output as "recovered-deleted-version.json"21µs
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"36µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"42µs
And I refer to "{result}" as "storage"28µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "list-deleted-versions-object={Timestamp}.txt", and "versioned data"145ms
And I refer to "{result.VersionID}" as "listedVersionId"28µs
When I call "{storage}" with "DeleteObject" using arguments "{ResourceName}" and "list-deleted-versions-object={Timestamp}.txt"57ms
When I call "{storage}" with "ListObjectVersions" using arguments "{ResourceName}" and "list-deleted-versions-object={Timestamp}.txt"46µs
And "{result}" is an array of objects with at least the following contents25µ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"24µ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"52µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"45µs
And I refer to "{result}" as "storage"31µs
Then no-op required24µs