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

Test Parameters

ServiceTypeobject-storage
ProviderServiceTypestorage.googleapis.com/Bucket
CatalogTypesCCC.ObjStor
TagFilter@object-storage, @PerService
UIDprojects/nodal-time-474015-p5/buckets/nodal-time-474015-p5-bucket
ResourceNamenodal-time-474015-p5-bucket
ReportFilenodal-time-474015-p5-bucket-service
ReportTitlenodal-time-474015-p5-bucket
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-03-24 12:32:06

Total Run Time: 1m8s

Features: 41

Scenarios: 81 (✅ 34 | ❌ 47)

Steps: 663 (✅ 476 | ❌ 47 | ⏭️ 133 | ❓ 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"47µ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}"11s
Then "{result}" is true31µs
📎 Attachments:
policy-result-object-storage-tls-policy.json
View JSON (3863 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://nodal-time-474015-p5-bucket \\\n  --format=json\n",
  "query_output": "{\n  \"autoclass\": {\n    \"enabled\": true,\n    \"terminalStorageClass\": \"NEARLINE\",\n    \"terminalStorageClassUpdateTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"toggleTime\": \"2026-03-24T05:23:37.462000+00:00\"\n  },\n  \"autoclass_enabled_time\": \"2026-03-24T05:23:37+0000\",\n  \"cors_config\": [\n    {\n      \"maxAgeSeconds\": 3600,\n      \"method\": [\n        \"GET\",\n        \"HEAD\",\n        \"PUT\",\n        \"POST\",\n        \"DELETE\"\n      ],\n      \"origin\": [\n        \"http://image-store.com\"\n      ],\n      \"responseHeader\": [\n        \"*\"\n      ]\n    }\n  ],\n  \"creation_time\": \"2026-03-24T05:23:37+0000\",\n  \"data_locations\": [\n    \"US-EAST4\",\n    \"US-WEST1\"\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1774329817196675678,\n  \"labels\": {\n    \"auto_cleanup\": \"true\",\n    \"ccc_compliance_test\": \"1774352791\",\n    \"cfi_target_id\": \"local-test\",\n    \"environment\": \"cfi-test\",\n    \"github_run_id\": \"local\",\n    \"github_workflow\": \"cfi-build\",\n    \"goog-terraform-provisioned\": \"true\",\n    \"managed_by\": \"terraform\",\n    \"project\": \"ccc-cfi-compliance\"\n  },\n  \"lifecycle_config\": {\n    \"rule\": [\n      {\n        \"action\": {\n          \"type\": \"Delete\"\n        },\n        \"condition\": {\n          \"age\": 365,\n          \"matchesPrefix\": [\n            \"nodal-time-474015-p5\"\n          ]\n        }\n      }\n    ]\n  },\n  \"location\": \"US\",\n  \"location_type\": \"dual-region\",\n  \"metageneration\": 6,\n  \"name\": \"nodal-time-474015-p5-bucket\",\n  \"public_access_prevention\": \"inherited\",\n  \"retention_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionPeriod\": \"2\"\n  },\n  \"rpo\": \"DEFAULT\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://nodal-time-474015-p5-bucket/\",\n  \"uniform_bucket_level_access\": true,\n  \"update_time\": \"2026-03-24T11:46:31+0000\",\n  \"versioning_enabled\": true,\n  \"website_config\": {\n    \"mainPageSuffix\": \"index.html\",\n    \"notFoundPage\": \"404.html\"\n  }\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": "[nodal-time-474015-p5-bucket]",
      "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}"846ms
Then "{result}" is true30µs
📎 Attachments:
policy-result-object-storage-unencrypted-policy.json
View JSON (3774 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://nodal-time-474015-p5-bucket \\\n  --format=json\n",
  "query_output": "{\n  \"autoclass\": {\n    \"enabled\": true,\n    \"terminalStorageClass\": \"NEARLINE\",\n    \"terminalStorageClassUpdateTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"toggleTime\": \"2026-03-24T05:23:37.462000+00:00\"\n  },\n  \"autoclass_enabled_time\": \"2026-03-24T05:23:37+0000\",\n  \"cors_config\": [\n    {\n      \"maxAgeSeconds\": 3600,\n      \"method\": [\n        \"GET\",\n        \"HEAD\",\n        \"PUT\",\n        \"POST\",\n        \"DELETE\"\n      ],\n      \"origin\": [\n        \"http://image-store.com\"\n      ],\n      \"responseHeader\": [\n        \"*\"\n      ]\n    }\n  ],\n  \"creation_time\": \"2026-03-24T05:23:37+0000\",\n  \"data_locations\": [\n    \"US-EAST4\",\n    \"US-WEST1\"\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1774329817196675678,\n  \"labels\": {\n    \"auto_cleanup\": \"true\",\n    \"ccc_compliance_test\": \"1774352791\",\n    \"cfi_target_id\": \"local-test\",\n    \"environment\": \"cfi-test\",\n    \"github_run_id\": \"local\",\n    \"github_workflow\": \"cfi-build\",\n    \"goog-terraform-provisioned\": \"true\",\n    \"managed_by\": \"terraform\",\n    \"project\": \"ccc-cfi-compliance\"\n  },\n  \"lifecycle_config\": {\n    \"rule\": [\n      {\n        \"action\": {\n          \"type\": \"Delete\"\n        },\n        \"condition\": {\n          \"age\": 365,\n          \"matchesPrefix\": [\n            \"nodal-time-474015-p5\"\n          ]\n        }\n      }\n    ]\n  },\n  \"location\": \"US\",\n  \"location_type\": \"dual-region\",\n  \"metageneration\": 6,\n  \"name\": \"nodal-time-474015-p5-bucket\",\n  \"public_access_prevention\": \"inherited\",\n  \"retention_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionPeriod\": \"2\"\n  },\n  \"rpo\": \"DEFAULT\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://nodal-time-474015-p5-bucket/\",\n  \"uniform_bucket_level_access\": true,\n  \"update_time\": \"2026-03-24T11:46:31+0000\",\n  \"versioning_enabled\": true,\n  \"website_config\": {\n    \"mainPageSuffix\": \"index.html\",\n    \"notFoundPage\": \"404.html\"\n  }\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": "[nodal-time-474015-p5-bucket]",
      "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 required40µ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"42µs
Given I call "{api}" with "GetServiceAPI" using argument "object-storage"212µs
And I refer to "{result}" as "storage"72µs
And "{result}" is not an error74µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-encryption-check={Timestamp}.txt", and "encryption test data"167ms
Then "{result}" is not an error25µs
And I refer to "{result}" as "uploadResult"15µs
And "{uploadResult.Encryption}" is not null17µs
And "{uploadResult.EncryptionAlgorithm}" is "AES256"22µs
And I attach "{uploadResult}" to the test output as "Upload Result with Encryption Details"63µs
📎 Attachments:
Upload Result with Encryption Details
View JSON (264 bytes)
{"ID":"test-encryption-check=1774355538340.txt","BucketID":"nodal-time-474015-p5-bucket","Name":"test-encryption-check=1774355538340.txt","Size":20,"Data":["encryption test data"],"Encryption":"Google","EncryptionAlgorithm":"AES256","VersionID":"1774355538402230"}
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"51µ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}"838ms
Then "{result}" is true32µs
📎 Attachments:
policy-result-object-storage-encryption.json
View JSON (4136 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://nodal-time-474015-p5-bucket \\\n  --format=json\n",
  "query_output": "{\n  \"autoclass\": {\n    \"enabled\": true,\n    \"terminalStorageClass\": \"NEARLINE\",\n    \"terminalStorageClassUpdateTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"toggleTime\": \"2026-03-24T05:23:37.462000+00:00\"\n  },\n  \"autoclass_enabled_time\": \"2026-03-24T05:23:37+0000\",\n  \"cors_config\": [\n    {\n      \"maxAgeSeconds\": 3600,\n      \"method\": [\n        \"GET\",\n        \"HEAD\",\n        \"PUT\",\n        \"POST\",\n        \"DELETE\"\n      ],\n      \"origin\": [\n        \"http://image-store.com\"\n      ],\n      \"responseHeader\": [\n        \"*\"\n      ]\n    }\n  ],\n  \"creation_time\": \"2026-03-24T05:23:37+0000\",\n  \"data_locations\": [\n    \"US-EAST4\",\n    \"US-WEST1\"\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1774329817196675678,\n  \"labels\": {\n    \"auto_cleanup\": \"true\",\n    \"ccc_compliance_test\": \"1774352791\",\n    \"cfi_target_id\": \"local-test\",\n    \"environment\": \"cfi-test\",\n    \"github_run_id\": \"local\",\n    \"github_workflow\": \"cfi-build\",\n    \"goog-terraform-provisioned\": \"true\",\n    \"managed_by\": \"terraform\",\n    \"project\": \"ccc-cfi-compliance\"\n  },\n  \"lifecycle_config\": {\n    \"rule\": [\n      {\n        \"action\": {\n          \"type\": \"Delete\"\n        },\n        \"condition\": {\n          \"age\": 365,\n          \"matchesPrefix\": [\n            \"nodal-time-474015-p5\"\n          ]\n        }\n      }\n    ]\n  },\n  \"location\": \"US\",\n  \"location_type\": \"dual-region\",\n  \"metageneration\": 6,\n  \"name\": \"nodal-time-474015-p5-bucket\",\n  \"public_access_prevention\": \"inherited\",\n  \"retention_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionPeriod\": \"2\"\n  },\n  \"rpo\": \"DEFAULT\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://nodal-time-474015-p5-bucket/\",\n  \"uniform_bucket_level_access\": true,\n  \"update_time\": \"2026-03-24T11:46:31+0000\",\n  \"versioning_enabled\": true,\n  \"website_config\": {\n    \"mainPageSuffix\": \"index.html\",\n    \"notFoundPage\": \"404.html\"\n  }\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": "[nodal-time-474015-p5-bucket]",
      "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"38µ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}"846ms
policy check failed: GCP Cloud Storage Soft Delete and Versioning Configuration:
Then "{result}" is true22µs
📎 Attachments:
policy-result-object-storage-delete-protection.json
View JSON (4421 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://nodal-time-474015-p5-bucket \\\n  --format=json\n",
  "query_output": "{\n  \"autoclass\": {\n    \"enabled\": true,\n    \"terminalStorageClass\": \"NEARLINE\",\n    \"terminalStorageClassUpdateTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"toggleTime\": \"2026-03-24T05:23:37.462000+00:00\"\n  },\n  \"autoclass_enabled_time\": \"2026-03-24T05:23:37+0000\",\n  \"cors_config\": [\n    {\n      \"maxAgeSeconds\": 3600,\n      \"method\": [\n        \"GET\",\n        \"HEAD\",\n        \"PUT\",\n        \"POST\",\n        \"DELETE\"\n      ],\n      \"origin\": [\n        \"http://image-store.com\"\n      ],\n      \"responseHeader\": [\n        \"*\"\n      ]\n    }\n  ],\n  \"creation_time\": \"2026-03-24T05:23:37+0000\",\n  \"data_locations\": [\n    \"US-EAST4\",\n    \"US-WEST1\"\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1774329817196675678,\n  \"labels\": {\n    \"auto_cleanup\": \"true\",\n    \"ccc_compliance_test\": \"1774352791\",\n    \"cfi_target_id\": \"local-test\",\n    \"environment\": \"cfi-test\",\n    \"github_run_id\": \"local\",\n    \"github_workflow\": \"cfi-build\",\n    \"goog-terraform-provisioned\": \"true\",\n    \"managed_by\": \"terraform\",\n    \"project\": \"ccc-cfi-compliance\"\n  },\n  \"lifecycle_config\": {\n    \"rule\": [\n      {\n        \"action\": {\n          \"type\": \"Delete\"\n        },\n        \"condition\": {\n          \"age\": 365,\n          \"matchesPrefix\": [\n            \"nodal-time-474015-p5\"\n          ]\n        }\n      }\n    ]\n  },\n  \"location\": \"US\",\n  \"location_type\": \"dual-region\",\n  \"metageneration\": 6,\n  \"name\": \"nodal-time-474015-p5-bucket\",\n  \"public_access_prevention\": \"inherited\",\n  \"retention_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionPeriod\": \"2\"\n  },\n  \"rpo\": \"DEFAULT\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://nodal-time-474015-p5-bucket/\",\n  \"uniform_bucket_level_access\": true,\n  \"update_time\": \"2026-03-24T11:46:31+0000\",\n  \"versioning_enabled\": true,\n  \"website_config\": {\n    \"mainPageSuffix\": \"index.html\",\n    \"notFoundPage\": \"404.html\"\n  }\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"46µs
Then no-op required24µ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"35µ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"28µs
Then no-op required20µ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"33µs
Then no-op required21µ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}"1s
Then "{result}" is true33µ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"39µs
And I call "{api}" with "GetServiceAPI" using argument "{ServiceType}"40µs
And I refer to "{result}" as "theService"20µs
Given I call "{api}" with "GetServiceAPI" using argument "logging"483µs
And I refer to "{result}" as "loggingService"18µs
When I call "{theService}" with "UpdateResourcePolicy"477ms
Then "{result}" is not an error35µs
And I attach "{result}" to the test output as "Policy Update Result"41µs
And we wait for a period of "10000" ms10s
When I call "{loggingService}" with "QueryAdminLogs" using arguments "{ResourceName}" and "{20}"124µs
Then "{result}" is not an error18µs
And I refer to "{result}" as "adminLogs"15µs
And I attach "{adminLogs}" to the test output as "Admin Activity Logs"60µs
Then "{adminLogs}" is an array of objects with at least the following contents29µ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"27µ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}"1s
policy check failed: Cloud Audit Logs Data Write Configuration:
Then "{result}" is true23µ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"33µ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}"706ms
policy check failed: Cloud Audit Logs Data Read Configuration:
Then "{result}" is true23µ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"44µs
Given I call "{api}" with "GetServiceAPI" using argument "object-storage"37µs
And I refer to "{result}" as "storage"44µs
Given I call "{api}" with "GetServiceAPI" using argument "logging"26µs
And I refer to "{result}" as "loggingService"40µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-read-logging-object={Timestamp}.txt", and "test data for read logging verification"174ms
Then "{result}" is not an error26µ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"73ms
Then "{result}" is not an error26µs
And I refer to "{result}" as "readResult"23µs
And I attach "{readResult}" to the test output as "Object Read Result"47µs
And we wait for a period of "10000" ms10s
When I call "{loggingService}" with "QueryDataReadLogs" using arguments "{ResourceName}" and "{20}"44µs
Then "{result}" is not an error27µs
And I refer to "{result}" as "readLogs"20µs
And I attach "{readLogs}" to the test output as "Data Read Logs"46µs
Then "{readLogs}" is an array of objects with at least the following contents38µs
result
Succeeded
expected row not found: map[result:Succeeded]
📎 Attachments:
Object Read Result
View JSON (261 bytes)
{"ID":"test-read-logging-object=1774355553669.txt","BucketID":"nodal-time-474015-p5-bucket","Name":"test-read-logging-object=1774355553669.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"28µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"33µs
And I refer to "{result}" as "storage"20µs
And I call "{api}" with "GetServiceAPI" using argument "iam"25µs
And I refer to "{result}" as "iamService"21µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-write-access", "{UID}", and "none"1s
And I refer to "{result}" as "testUserNoAccess"30µs
And I attach "{result}" to the test output as "no-access-user-identity.json"80µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"303µs
And "{result}" is not an error29µs
And I refer to "{result}" as "userStorage"13µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-cn05-unauthorized-modify={Timestamp}.txt", and "unauthorized data"196ms
Then "{result}" is an error22µs
And I attach "{result}" to the test output as "no-access-create-error.txt"30µs
📎 Attachments:
no-access-user-identity.json
View JSON (3062 bytes)
{"UserName":"test-user-no-write-access","Provider":"gcp","Credentials":{"client_email":"test-user-no-write-access@nodal-time-474015-p5.iam.gserviceaccount.com","email":"test-user-no-write-access@nodal-time-474015-p5.iam.gserviceaccount.com","private_key_id":"7bfa0700b85efc62d5bda4f7de1d48dd6c17d226","project_id":"nodal-time-474015-p5","service_account_key":"{\n  \"type\": \"service_account\",\n  \"project_id\": \"nodal-time-474015-p5\",\n  \"private_key_id\": \"7bfa0700b85efc62d5bda4f7de1d48dd6c17d226\",\n  \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDNTFxKYDestgoS\\nlb/lTCy9cawOmIy9SnJXwsQQvZoqi58LdtFqQkI3MMRqWGBoPDOo1WWtz8EqI8Uu\\nrW6zLTB1d1Nzf4g7PoUV/z2hpn7mucUWMXuVRL0xHi+anQ8WgXwUhtHjjlqP2RL9\\nQUmAmSxR0Ey9ol6rnDAP+63RSEs9CkwwHUpXaX4VL2juxPiZhkKPMrwkJcpfJx76\\nIACPAdaYqxEkdmjYj6nB0d4KbwJ7I6I9VcfwQWYCw0brTjT8lY9kqNtcmqYVpyS+\\nOqrduOTFHzgFxyXmKG06ubFXqEsMEPpuZpntGxg2WKgttmeOyMMd5kw6tU6V5xBI\\nYPTQEWwfAgMBAAECgf8WjuBl0IIUtdkGrQVtrKCAQ3PPHUljJHpr8V5OBtND4wCl\\nFyuWQ84C0LMQPsIzNqMhMTUlpnpClCbiXbD27D3daAsKhLHNhNz+WaBwyKaH2Z9q\\nzCGUESQ0A9X+W2AY5NWcT1oMmMvo5gkFdZ2PwX3WF0Fz4L2ZQ6fxXAbelJyAaO7U\\nFZ4iu6k/J92c/8XiU/ODFJrygqoqtK+B1rMuqzw2C3i2lBaU0MHpmcbThMkCuYMg\\nmJaQ6fw2nWiAJKhz643rtv+4LS94kDnNcH1HElvOFoHefrRVzldccZOcKEcXk8gl\\nEj5hH1IkcU4GV3xpKjf3tyqgIq7sxlEmPbppbIkCgYEA74tWr3VBeA2kTt/dBJRe\\nOeEG2Kewom5gUWld0yEU8PakH9rLjhx6NjD35JKsTADPjr2q8+BI0/gA8UAnzpL9\\ngtxzVvEMPBfB4nwUCSE1dkMKIecw/NxjIJJvUXZBmv86SIEi2W0R0thSHBe2/HOs\\nF6c1br2UlQpdMe+xtAWa5XkCgYEA22bEOtbf4yi5xgpoD7YKBGgcU9ACClnwbOmF\\nz98xZcxO7cKOKFJyxL74x5K79/A2Cbj+e0mW3GKqI75mhAsCqgpkw2AEPayW9HAB\\nQg6064A1orqgcgqvzpdfVXLn9mrZ3WjJke3KRXx91+KcOUgVwwgPM7UHVLgTjHT5\\ngqSs8FcCgYBJyvXoVUnWzvUH60St7Xrdf6nZQkdSsofvbHMf4DgQRxqmSYOdgr5P\\nRWSIQHrdav6FrX4lZLlquyKnLylUJMn97yWE6frn5DWxLKILcfSDOwG7EfpwWwAa\\nf6HVSYXKVsfAoC95JXVxK3yX9OJFAOlJ3BPEypgkFa5B/OIUYAmomQKBgQDDAaJk\\nUdwJQYdtOI6peDmLFSYocHd/z9TcWOswicaUHnYA6a4EtNOBf+QLLqaXhw/gsGaw\\nGgZWt/NVdcQUlNy3WimZZdwfHSnG9U6I8h1oPDpv+sTk2A7O5sg7YLg/01T1cE/I\\nvrvL6wzHN4l3eFSvVph8cuijKkFl1Ap5TfjQXQKBgQDvF98RiJkiOfGL+2aBtOEs\\nNBW7qJBgONPhsngi9n88gwCASeE4jc1oYfQxQj0iLDYLRVdvNhgoS82hA0kvr5wn\\nuDIjlYs7J+1UWdQWS/CO+LVvBgxo00IuPxZXIYUV/Gww8+Be1Pofda7jnU0t75aQ\\nJNdvXeXJcSXPi8oL67YONA==\\n-----END PRIVATE KEY-----\\n\",\n  \"client_email\": \"test-user-no-write-access@nodal-time-474015-p5.iam.gserviceaccount.com\",\n  \"client_id\": \"111215898024931591049\",\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-write-access%40nodal-time-474015-p5.iam.gserviceaccount.com\",\n  \"universe_domain\": \"googleapis.com\"\n}\n","unique_id":"111215898024931591049"},"Policy":"{\"user\": \"test-user-no-write-access\", \"service\": \"projects/nodal-time-474015-p5/buckets/nodal-time-474015-p5-bucket\", \"level\": \"none\", \"role\": \"\"}"}
no-access-create-error.txt
View Content (344 bytes)
failed to close writer for object test-cn05-unauthorized-modify=1774355563924.txt: googleapi: Error 403: test-user-no-write-access@nodal-time-474015-p5.iam.gserviceaccount.com does not have storage.objects.create access to the Google Cloud Storage object. Permission 'storage.objects.create' denied on resource (or it may not exist)., forbidden
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"33µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"39µs
And I refer to "{result}" as "storage"23µs
And I call "{api}" with "GetServiceAPI" using argument "iam"28µs
And I refer to "{result}" as "iamService"21µ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"35µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"53µs
And "{result}" is not an error36µ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"22µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-cn05-authorized-modify={Timestamp}.txt", and "authorized data"31µs
Then "{result}" is not an error18µ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"29µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"38µs
And I refer to "{result}" as "storage"25µs
And I call "{api}" with "GetServiceAPI" using argument "iam"26µs
And I refer to "{result}" as "iamService"20µs
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}"867ms
policy check failed: GCP Cloud Storage Public Access Prevention:
Then "{result}" is true25µs
📎 Attachments:
policy-result-object-storage-block-public-write-access.json
View JSON (2231 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://nodal-time-474015-p5-bucket \\\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"43µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"40µs
And I refer to "{result}" as "storage"23µs
And I call "{api}" with "GetServiceAPI" using argument "iam"27µs
And I refer to "{result}" as "iamService"21µ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"26µs
And I attach "{result}" to the test output as "no-admin-user-identity.json"32µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"54µs
And "{result}" is not an error29µ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"23µs
Then "{result}" is an error24µs
And I attach "{result}" to the test output as "no-admin-create-bucket-error.txt"23µ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"32µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"38µs
And I refer to "{result}" as "storage"21µs
And I call "{api}" with "GetServiceAPI" using argument "iam"26µs
And I refer to "{result}" as "iamService"19µ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"33µs
And I attach "{result}" to the test output as "read-only-admin-user-identity.json"66µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{false}"46µ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"20µs
When I call "{userStorage}" with "CreateBucket" using argument "test-cn05-read-only-create-container"24µs
Then "{result}" is an error16µ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"29µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"32µs
And I refer to "{result}" as "storage"20µs
And I call "{api}" with "GetServiceAPI" using argument "iam"27µs
And I refer to "{result}" as "iamService"21µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-admin-access", "{UID}", and "admin"870ms
And I refer to "{result}" as "testUserAdmin"34µs
And I attach "{result}" to the test output as "admin-user-identity.json"66µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserAdmin}", and "{true}"29ms
And "{result}" is not an error33µs
expected {result} to not be an error, but got: credentials not ready: credentials not ready for GCS access: Get "https://storage.googleapis.com/storage/v1/b?alt=json&pageToken=&prefix=&prettyPrint=false&project=nodal-time-474015-p5&projection=full&returnPartialSuccess=false": auth: cannot fetch token: 400 Response: {"error":"invalid_grant","error_description":"Invalid grant: account not found"}
And I refer to "{result}" as "userStorage"14µs
When I call "{userStorage}" with "CreateBucket" using argument "test-cn05-authorized-admin-container"15µs
Then "{result}" is not an error15µs
And I attach "{result}" to the test output as "admin-create-bucket-result.json"14µs
And I call "{storage}" with "DeleteBucket" using argument "test-cn05-authorized-admin-container"14µs
📎 Attachments:
admin-user-identity.json
View JSON (3056 bytes)
{"UserName":"test-user-admin-access","Provider":"gcp","Credentials":{"client_email":"test-user-admin-access@nodal-time-474015-p5.iam.gserviceaccount.com","email":"test-user-admin-access@nodal-time-474015-p5.iam.gserviceaccount.com","private_key_id":"59ed375c192a04c34341c6580b6229772901880e","project_id":"nodal-time-474015-p5","service_account_key":"{\n  \"type\": \"service_account\",\n  \"project_id\": \"nodal-time-474015-p5\",\n  \"private_key_id\": \"59ed375c192a04c34341c6580b6229772901880e\",\n  \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDZ9oy8sHitL4+d\\njItvsovFtWvt4J01AzhXr9MQ0FJFLaFKcUBfXBT2N+jP7VbcT7SdNaGVZp3r5MQQ\\n2sLwzQKeCyQ3206srOAuTEcov18hUC3xDjGm3PQL4DCRq1U/ZUJ5Z3uMhH3jqR4g\\n9e4oAJLzM9YLxsFUHRByxdGcWfbUUIVeGPOpD4E72oHW0ufhzsdl9Pe58S4CJ/jN\\nlzxyOSkC6lDTCcSRKNzxgn5jdSroLhVCbG5Wfzljc5FqlVHwbzQY7qNz9vzXNtxI\\nIISE2UID7QGOQ3OUzP6GdUvsykMUiZhnawo1gOSQ30JTg4GqGhZTfo5HF9/MHkGA\\nqWMHIIxjAgMBAAECggEAK5wNV5l1Y61O7kZXBa+zEIk2gX895QPya3T15XjQ5R92\\nlHbpWssJNKRGFAgYJ3dZG1VGMaa49SAt7HNgqp5SJj3zbquV562lIw5G9IuppHgu\\n4Pqmi4zM1W1zOkoOAeOoM7M1QI2nxHurWAvsrRtES52092E6gDJafqQ+xqonXR4q\\nsVVgkqf7VdRzYVDhc+Hmiq9yJPSiodHLZYXlB21i2441tkpDspn8psEylY3gnfyL\\n04zSf688aIa08ZRJHVsICqDSHcZJBZEVDHfVwxRx8Oj3D9eZ/EB0CxcXsUGXDjgc\\nXMIDFvbo+TfDnTkBtLks2aJUX5Kr4Aa88WRaOqdX9QKBgQDupEH/LYyOyC7P4gTQ\\nZumkhCft7fR37lTc+a/VKLrsoiFTpKDpADpHxNftrCwanz55vLjEoqw+jKABN97S\\n/LofxH/MnWrEmust7T5nm8yZO9qzD/Dyb3k1z3erU8v69zohW1RkuPCZAYUxtsUJ\\nVUw23zUJ7nNXJctwQDAn6ZRJrQKBgQDp0Tyu64uTme5OAtFWTROc8zxLoaoxrkF9\\nUjlfCryn/e/iUTDf7Ca3mWUQt0bBqsEqQ/G6CJmH0mvxkzPcgVEwrzMhy8/SwYpe\\nSetzSwh7HAYV5iTwuYLAYKLjE94mYcGMVIrGOkqoFCKhsMrKaahO+bJO9igBTNiD\\nkN1uoy8QTwKBgQC/AI0qFmTXqvV8wWQlARmj6Xqj/r6adxFJRDctnsntt96AudVo\\naBw9cRl6S0GJlUdF3HzuNV0zOw6dqwAXUtwNrRK0zJG2BmCRg79c4eAiDTv/8ZRi\\nFkKnhQQ9BnbOya2nIRgs/bDkZckbfzO5QqIbkQ88gbOjXsazc0yXxWZAiQKBgGKz\\nactpMR1YoGkb9WRZ2wqo/u3qS573MTwGxqMhNCwyHN5LabdMx7c/MJOS2nKMc0g1\\nMEhYsv7Ly7dJbV4K1MwZWRtOHueXvaI50AN2TBkz5jm+Uh+wH8q3wGrdCyzmnT5w\\nvbgaCUPUZB4fNCun2F2mOCfu70pFNyyqrV6z0f0vAoGAGj6wsGwsrzy3i614ruwD\\nkJf3aYeY8DSYfsh/srsu7KaXzFj7uQA6G/0lvr2SbVdUaEnKGpcHR603bh/+R2y5\\nJpwu/77tcc/nbKqzePQA4iMMkvpofkuIyNYUjY/sOUMYEAmgOv6i1TUG9/cwljP+\\nY5WrtvVba5jyuIee1AEND9I=\\n-----END PRIVATE KEY-----\\n\",\n  \"client_email\": \"test-user-admin-access@nodal-time-474015-p5.iam.gserviceaccount.com\",\n  \"client_id\": \"104480796220368458958\",\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-admin-access%40nodal-time-474015-p5.iam.gserviceaccount.com\",\n  \"universe_domain\": \"googleapis.com\"\n}\n","unique_id":"104480796220368458958"},"Policy":"{\"user\": \"test-user-admin-access\", \"service\": \"projects/nodal-time-474015-p5/buckets/nodal-time-474015-p5-bucket\", \"level\": \"admin\", \"role\": \"roles/owner\"}"}
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"33µ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"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"37µs
When I attempt policy check "object-storage-cross-tenant-block" for control "CCC.Core.CN05" assessment requirement "AR03" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true32µs
📎 Attachments:
policy-result-object-storage-cross-tenant-block.json
View JSON (2747 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://nodal-time-474015-p5-bucket --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"29µ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}"832ms
policy check failed: GCP Cloud Storage Block External Unauthorized Data Requests:
Then "{result}" is true22µs
📎 Attachments:
policy-result-object-storage-block-public-read.json
View JSON (2053 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://nodal-time-474015-p5-bucket \\\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"43µs
Then no-op required21µ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"50µs
And I refer to "{result}" as "storage"23µs
And I call "{api}" with "GetServiceAPI" using argument "iam"26µs
And I refer to "{result}" as "iamService"19µs
Then no-op required20µ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"31µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"29µs
And I refer to "{result}" as "storage"20µs
And I call "{api}" with "GetServiceAPI" using argument "iam"23µs
And I refer to "{result}" as "iamService"19µs
Then no-op required18µ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"29µ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}"852ms
Then "{result}" is true31µs
📎 Attachments:
policy-result-object-storage-region.json
View JSON (3939 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://nodal-time-474015-p5-bucket \\\n  --format=json\n",
  "query_output": "{\n  \"autoclass\": {\n    \"enabled\": true,\n    \"terminalStorageClass\": \"NEARLINE\",\n    \"terminalStorageClassUpdateTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"toggleTime\": \"2026-03-24T05:23:37.462000+00:00\"\n  },\n  \"autoclass_enabled_time\": \"2026-03-24T05:23:37+0000\",\n  \"cors_config\": [\n    {\n      \"maxAgeSeconds\": 3600,\n      \"method\": [\n        \"GET\",\n        \"HEAD\",\n        \"PUT\",\n        \"POST\",\n        \"DELETE\"\n      ],\n      \"origin\": [\n        \"http://image-store.com\"\n      ],\n      \"responseHeader\": [\n        \"*\"\n      ]\n    }\n  ],\n  \"creation_time\": \"2026-03-24T05:23:37+0000\",\n  \"data_locations\": [\n    \"US-EAST4\",\n    \"US-WEST1\"\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1774329817196675678,\n  \"labels\": {\n    \"auto_cleanup\": \"true\",\n    \"ccc_compliance_test\": \"1774355541\",\n    \"cfi_target_id\": \"local-test\",\n    \"environment\": \"cfi-test\",\n    \"github_run_id\": \"local\",\n    \"github_workflow\": \"cfi-build\",\n    \"goog-terraform-provisioned\": \"true\",\n    \"managed_by\": \"terraform\",\n    \"project\": \"ccc-cfi-compliance\"\n  },\n  \"lifecycle_config\": {\n    \"rule\": [\n      {\n        \"action\": {\n          \"type\": \"Delete\"\n        },\n        \"condition\": {\n          \"age\": 365,\n          \"matchesPrefix\": [\n            \"nodal-time-474015-p5\"\n          ]\n        }\n      }\n    ]\n  },\n  \"location\": \"US\",\n  \"location_type\": \"dual-region\",\n  \"metageneration\": 7,\n  \"name\": \"nodal-time-474015-p5-bucket\",\n  \"public_access_prevention\": \"inherited\",\n  \"retention_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionPeriod\": \"2\"\n  },\n  \"rpo\": \"DEFAULT\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://nodal-time-474015-p5-bucket/\",\n  \"uniform_bucket_level_access\": true,\n  \"update_time\": \"2026-03-24T12:32:21+0000\",\n  \"versioning_enabled\": true,\n  \"website_config\": {\n    \"mainPageSuffix\": \"index.html\",\n    \"notFoundPage\": \"404.html\"\n  }\n}\n",
  "passed": true,
  "rule_results": [
    {
      "jsonpath": "$.location",
      "expected_values": [
        "EU",
        "EUROPE-WEST1",
        "US",
        "US-EAST1",
        "US-WEST1",
        "US-CENTRAL1"
      ],
      "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]",
      "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"38µs
Then no-op required27µs
Scenario: Child resource region compliance @PerService @CCC.Core @CCC.Core.CN06 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy @NotTestable @object-storage
Given a cloud api for "{Instance}" in "api"44µs
Then no-op required22µ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"28µ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}"665ms
policy check failed: GCP Enumeration Monitoring Policy Check:
Then "{result}" is true39µ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"48µs
Then no-op required23µ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"54µ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}"832ms
policy check failed: GCP Cloud Storage Enumeration Logging Policy Check:
Then "{result}" is true21µs
📎 Attachments:
policy-result-enumeration-logging-policy.json
View JSON (3278 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://nodal-time-474015-p5-bucket \\\n  --format=json\n",
  "query_output": "{\n  \"autoclass\": {\n    \"enabled\": true,\n    \"terminalStorageClass\": \"NEARLINE\",\n    \"terminalStorageClassUpdateTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"toggleTime\": \"2026-03-24T05:23:37.462000+00:00\"\n  },\n  \"autoclass_enabled_time\": \"2026-03-24T05:23:37+0000\",\n  \"cors_config\": [\n    {\n      \"maxAgeSeconds\": 3600,\n      \"method\": [\n        \"GET\",\n        \"HEAD\",\n        \"PUT\",\n        \"POST\",\n        \"DELETE\"\n      ],\n      \"origin\": [\n        \"http://image-store.com\"\n      ],\n      \"responseHeader\": [\n        \"*\"\n      ]\n    }\n  ],\n  \"creation_time\": \"2026-03-24T05:23:37+0000\",\n  \"data_locations\": [\n    \"US-EAST4\",\n    \"US-WEST1\"\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1774329817196675678,\n  \"labels\": {\n    \"auto_cleanup\": \"true\",\n    \"ccc_compliance_test\": \"1774355541\",\n    \"cfi_target_id\": \"local-test\",\n    \"environment\": \"cfi-test\",\n    \"github_run_id\": \"local\",\n    \"github_workflow\": \"cfi-build\",\n    \"goog-terraform-provisioned\": \"true\",\n    \"managed_by\": \"terraform\",\n    \"project\": \"ccc-cfi-compliance\"\n  },\n  \"lifecycle_config\": {\n    \"rule\": [\n      {\n        \"action\": {\n          \"type\": \"Delete\"\n        },\n        \"condition\": {\n          \"age\": 365,\n          \"matchesPrefix\": [\n            \"nodal-time-474015-p5\"\n          ]\n        }\n      }\n    ]\n  },\n  \"location\": \"US\",\n  \"location_type\": \"dual-region\",\n  \"metageneration\": 7,\n  \"name\": \"nodal-time-474015-p5-bucket\",\n  \"public_access_prevention\": \"inherited\",\n  \"retention_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionPeriod\": \"2\"\n  },\n  \"rpo\": \"DEFAULT\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://nodal-time-474015-p5-bucket/\",\n  \"uniform_bucket_level_access\": true,\n  \"update_time\": \"2026-03-24T12:32:21+0000\",\n  \"versioning_enabled\": true,\n  \"website_config\": {\n    \"mainPageSuffix\": \"index.html\",\n    \"notFoundPage\": \"404.html\"\n  }\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"42µs
Then no-op required27µ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"37µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"56µs
And I refer to "{result}" as "storage"24µ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}"839ms
policy check failed: GCP Cloud Storage Multi-Region/Dual-Region Configuration:
Then "{result}" is true34µs
📎 Attachments:
policy-result-object-storage-replication.json
View JSON (4248 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://nodal-time-474015-p5-bucket \\\n  --format=json\n",
  "query_output": "{\n  \"autoclass\": {\n    \"enabled\": true,\n    \"terminalStorageClass\": \"NEARLINE\",\n    \"terminalStorageClassUpdateTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"toggleTime\": \"2026-03-24T05:23:37.462000+00:00\"\n  },\n  \"autoclass_enabled_time\": \"2026-03-24T05:23:37+0000\",\n  \"cors_config\": [\n    {\n      \"maxAgeSeconds\": 3600,\n      \"method\": [\n        \"GET\",\n        \"HEAD\",\n        \"PUT\",\n        \"POST\",\n        \"DELETE\"\n      ],\n      \"origin\": [\n        \"http://image-store.com\"\n      ],\n      \"responseHeader\": [\n        \"*\"\n      ]\n    }\n  ],\n  \"creation_time\": \"2026-03-24T05:23:37+0000\",\n  \"data_locations\": [\n    \"US-EAST4\",\n    \"US-WEST1\"\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1774329817196675678,\n  \"labels\": {\n    \"auto_cleanup\": \"true\",\n    \"ccc_compliance_test\": \"1774355541\",\n    \"cfi_target_id\": \"local-test\",\n    \"environment\": \"cfi-test\",\n    \"github_run_id\": \"local\",\n    \"github_workflow\": \"cfi-build\",\n    \"goog-terraform-provisioned\": \"true\",\n    \"managed_by\": \"terraform\",\n    \"project\": \"ccc-cfi-compliance\"\n  },\n  \"lifecycle_config\": {\n    \"rule\": [\n      {\n        \"action\": {\n          \"type\": \"Delete\"\n        },\n        \"condition\": {\n          \"age\": 365,\n          \"matchesPrefix\": [\n            \"nodal-time-474015-p5\"\n          ]\n        }\n      }\n    ]\n  },\n  \"location\": \"US\",\n  \"location_type\": \"dual-region\",\n  \"metageneration\": 7,\n  \"name\": \"nodal-time-474015-p5-bucket\",\n  \"public_access_prevention\": \"inherited\",\n  \"retention_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionPeriod\": \"2\"\n  },\n  \"rpo\": \"DEFAULT\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://nodal-time-474015-p5-bucket/\",\n  \"uniform_bucket_level_access\": true,\n  \"update_time\": \"2026-03-24T12:32:21+0000\",\n  \"versioning_enabled\": true,\n  \"website_config\": {\n    \"mainPageSuffix\": \"index.html\",\n    \"notFoundPage\": \"404.html\"\n  }\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]",
      "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"37µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"36µs
And I refer to "{result}" as "storage"17µs
When I call "{storage}" with "GetReplicationStatus" using argument "{ResourceName}"64µs
And I refer to "{result}" as "replicationStatus"16µs
And I refer to "{replicationStatus.Locations}" as "locations"28µs
And I attach "{replicationStatus}" to the test output as "Replication Status"28µs
Then "{locations}" is an array of objects with length "2"32µs
field {locations} is not an array
And "{PermittedRegions}" is an array of objects with at least the following contents23µs
value
{locations[0]}
And "{PermittedRegions}" is an array of objects with at least the following contents28µ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"37µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"33µs
And I refer to "{result}" as "storage"25µ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}"872ms
policy check failed: GCP Cloud Storage Replication Status Visibility:
Then "{result}" is true26µs
📎 Attachments:
policy-result-object-storage-replication-status.json
View JSON (4160 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://nodal-time-474015-p5-bucket \\\n  --format=json\n",
  "query_output": "{\n  \"autoclass\": {\n    \"enabled\": true,\n    \"terminalStorageClass\": \"NEARLINE\",\n    \"terminalStorageClassUpdateTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"toggleTime\": \"2026-03-24T05:23:37.462000+00:00\"\n  },\n  \"autoclass_enabled_time\": \"2026-03-24T05:23:37+0000\",\n  \"cors_config\": [\n    {\n      \"maxAgeSeconds\": 3600,\n      \"method\": [\n        \"GET\",\n        \"HEAD\",\n        \"PUT\",\n        \"POST\",\n        \"DELETE\"\n      ],\n      \"origin\": [\n        \"http://image-store.com\"\n      ],\n      \"responseHeader\": [\n        \"*\"\n      ]\n    }\n  ],\n  \"creation_time\": \"2026-03-24T05:23:37+0000\",\n  \"data_locations\": [\n    \"US-EAST4\",\n    \"US-WEST1\"\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1774329817196675678,\n  \"labels\": {\n    \"auto_cleanup\": \"true\",\n    \"ccc_compliance_test\": \"1774355541\",\n    \"cfi_target_id\": \"local-test\",\n    \"environment\": \"cfi-test\",\n    \"github_run_id\": \"local\",\n    \"github_workflow\": \"cfi-build\",\n    \"goog-terraform-provisioned\": \"true\",\n    \"managed_by\": \"terraform\",\n    \"project\": \"ccc-cfi-compliance\"\n  },\n  \"lifecycle_config\": {\n    \"rule\": [\n      {\n        \"action\": {\n          \"type\": \"Delete\"\n        },\n        \"condition\": {\n          \"age\": 365,\n          \"matchesPrefix\": [\n            \"nodal-time-474015-p5\"\n          ]\n        }\n      }\n    ]\n  },\n  \"location\": \"US\",\n  \"location_type\": \"dual-region\",\n  \"metageneration\": 7,\n  \"name\": \"nodal-time-474015-p5-bucket\",\n  \"public_access_prevention\": \"inherited\",\n  \"retention_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionPeriod\": \"2\"\n  },\n  \"rpo\": \"DEFAULT\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://nodal-time-474015-p5-bucket/\",\n  \"uniform_bucket_level_access\": true,\n  \"update_time\": \"2026-03-24T12:32:21+0000\",\n  \"versioning_enabled\": true,\n  \"website_config\": {\n    \"mainPageSuffix\": \"index.html\",\n    \"notFoundPage\": \"404.html\"\n  }\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]",
      "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"289µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"142µs
And I refer to "{result}" as "storage"41µs
When I call "{storage}" with "GetReplicationStatus" using argument "{ResourceName}"54µs
And I refer to "{result}" as "replicationStatus"42µs
And I attach "{replicationStatus}" to the test output as "Replication Status"57µs
And I refer to "{replicationStatus.Locations}" as "locations"57µs
Then "{locations}" is an array of objects with at least the following contents46µ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"72µ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}"859ms
policy check failed: GCP Cloud Storage Access Logging Configuration:
Then "{result}" is true25µs
📎 Attachments:
policy-result-object-storage-access-logging.json
View JSON (4317 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://nodal-time-474015-p5-bucket \\\n  --format=json\n",
  "query_output": "{\n  \"autoclass\": {\n    \"enabled\": true,\n    \"terminalStorageClass\": \"NEARLINE\",\n    \"terminalStorageClassUpdateTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"toggleTime\": \"2026-03-24T05:23:37.462000+00:00\"\n  },\n  \"autoclass_enabled_time\": \"2026-03-24T05:23:37+0000\",\n  \"cors_config\": [\n    {\n      \"maxAgeSeconds\": 3600,\n      \"method\": [\n        \"GET\",\n        \"HEAD\",\n        \"PUT\",\n        \"POST\",\n        \"DELETE\"\n      ],\n      \"origin\": [\n        \"http://image-store.com\"\n      ],\n      \"responseHeader\": [\n        \"*\"\n      ]\n    }\n  ],\n  \"creation_time\": \"2026-03-24T05:23:37+0000\",\n  \"data_locations\": [\n    \"US-EAST4\",\n    \"US-WEST1\"\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1774329817196675678,\n  \"labels\": {\n    \"auto_cleanup\": \"true\",\n    \"ccc_compliance_test\": \"1774355541\",\n    \"cfi_target_id\": \"local-test\",\n    \"environment\": \"cfi-test\",\n    \"github_run_id\": \"local\",\n    \"github_workflow\": \"cfi-build\",\n    \"goog-terraform-provisioned\": \"true\",\n    \"managed_by\": \"terraform\",\n    \"project\": \"ccc-cfi-compliance\"\n  },\n  \"lifecycle_config\": {\n    \"rule\": [\n      {\n        \"action\": {\n          \"type\": \"Delete\"\n        },\n        \"condition\": {\n          \"age\": 365,\n          \"matchesPrefix\": [\n            \"nodal-time-474015-p5\"\n          ]\n        }\n      }\n    ]\n  },\n  \"location\": \"US\",\n  \"location_type\": \"dual-region\",\n  \"metageneration\": 7,\n  \"name\": \"nodal-time-474015-p5-bucket\",\n  \"public_access_prevention\": \"inherited\",\n  \"retention_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionPeriod\": \"2\"\n  },\n  \"rpo\": \"DEFAULT\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://nodal-time-474015-p5-bucket/\",\n  \"uniform_bucket_level_access\": true,\n  \"update_time\": \"2026-03-24T12:32:21+0000\",\n  \"versioning_enabled\": true,\n  \"website_config\": {\n    \"mainPageSuffix\": \"index.html\",\n    \"notFoundPage\": \"404.html\"\n  }\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 required29µ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"28µs
Then no-op required27µ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"29µ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}"856ms
policy check failed: GCP Cloud Storage Replication Destination Validation:
Then "{result}" is true28µs
📎 Attachments:
policy-result-object-storage-replication-destination.json
View JSON (4385 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://nodal-time-474015-p5-bucket \\\n  --format=json\n",
  "query_output": "{\n  \"autoclass\": {\n    \"enabled\": true,\n    \"terminalStorageClass\": \"NEARLINE\",\n    \"terminalStorageClassUpdateTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"toggleTime\": \"2026-03-24T05:23:37.462000+00:00\"\n  },\n  \"autoclass_enabled_time\": \"2026-03-24T05:23:37+0000\",\n  \"cors_config\": [\n    {\n      \"maxAgeSeconds\": 3600,\n      \"method\": [\n        \"GET\",\n        \"HEAD\",\n        \"PUT\",\n        \"POST\",\n        \"DELETE\"\n      ],\n      \"origin\": [\n        \"http://image-store.com\"\n      ],\n      \"responseHeader\": [\n        \"*\"\n      ]\n    }\n  ],\n  \"creation_time\": \"2026-03-24T05:23:37+0000\",\n  \"data_locations\": [\n    \"US-EAST4\",\n    \"US-WEST1\"\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1774329817196675678,\n  \"labels\": {\n    \"auto_cleanup\": \"true\",\n    \"ccc_compliance_test\": \"1774355541\",\n    \"cfi_target_id\": \"local-test\",\n    \"environment\": \"cfi-test\",\n    \"github_run_id\": \"local\",\n    \"github_workflow\": \"cfi-build\",\n    \"goog-terraform-provisioned\": \"true\",\n    \"managed_by\": \"terraform\",\n    \"project\": \"ccc-cfi-compliance\"\n  },\n  \"lifecycle_config\": {\n    \"rule\": [\n      {\n        \"action\": {\n          \"type\": \"Delete\"\n        },\n        \"condition\": {\n          \"age\": 365,\n          \"matchesPrefix\": [\n            \"nodal-time-474015-p5\"\n          ]\n        }\n      }\n    ]\n  },\n  \"location\": \"US\",\n  \"location_type\": \"dual-region\",\n  \"metageneration\": 7,\n  \"name\": \"nodal-time-474015-p5-bucket\",\n  \"public_access_prevention\": \"inherited\",\n  \"retention_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionPeriod\": \"2\"\n  },\n  \"rpo\": \"DEFAULT\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://nodal-time-474015-p5-bucket/\",\n  \"uniform_bucket_level_access\": true,\n  \"update_time\": \"2026-03-24T12:32:21+0000\",\n  \"versioning_enabled\": true,\n  \"website_config\": {\n    \"mainPageSuffix\": \"index.html\",\n    \"notFoundPage\": \"404.html\"\n  }\n}\n",
  "passed": false,
  "rule_results": [
    {
      "jsonpath": "$.location",
      "expected_values": [
        "US",
        "US-EAST1",
        "US-WEST1",
        "EU",
        "EUROPE-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]",
      "passed": true
    },
    {
      "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"47µs
Then no-op required27µ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"72µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"60µs
And I refer to "{result}" as "storage"37µs
And I call "{api}" with "GetServiceAPI" using argument "iam"85µs
And I refer to "{result}" as "iamService"57µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-access", "{UID}", and "none"181ms
And I refer to "{result}" as "testUserNoAccess"25µs
And I attach "{result}" to the test output as "no-access-user-identity.json"35µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"72µ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"22µs
When I call "{userStorage}" with "ListObjects" using argument "{ResourceName}"26µs
Then "{result}" is an error25µs
And I attach "{result}" to the test output as "no-access-list-error.txt"27µs
📎 Attachments:
no-access-user-identity.json
View Content (232 bytes)
failed to create service account test-user-no-access: 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 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"37µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"36µs
And I refer to "{result}" as "storage"25µs
And I call "{api}" with "GetServiceAPI" using argument "iam"31µs
And I refer to "{result}" as "iamService"24µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"171ms
And I refer to "{result}" as "testUserRead"29µs
And I attach "{result}" to the test output as "read-user-identity.json"36µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{true}"48µ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 attach "{result}" to the test output as "read-storage-service.json"26µs
And I refer to "{result}" as "userStorage"49µs
When I call "{userStorage}" with "ListObjects" using argument "{ResourceName}"24µs
Then "{result}" is not an error25µs
And I attach "{result}" to the test output as "read-list-objects-result.json"26µ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 bucket access control @PerService @object-storage @CCC.ObjStor @tlp-amber @tlp-red @CCC.ObjStor.CN01 @Policy
Given a cloud api for "{Instance}" in "api"39µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"50µs
And I refer to "{result}" as "storage"24µs
And I call "{api}" with "GetServiceAPI" using argument "iam"33µs
And I refer to "{result}" as "iamService"24µ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}"852ms
policy check failed: GCP Cloud Storage Block Public Access Check:
Then "{result}" is true23µs
📎 Attachments:
policy-result-no-public-access.json
View JSON (1688 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://nodal-time-474015-p5-bucket \\\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"48µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"46µs
And I refer to "{result}" as "storage"23µs
And I call "{api}" with "GetServiceAPI" using argument "iam"27µs
And I refer to "{result}" as "iamService"22µs
And I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "test content"163ms
And "{result}" is not an error49µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-access", "{UID}", and "none"127ms
And I refer to "{result}" as "testUserNoAccess"27µs
And I attach "{result}" to the test output as "no-access-user-identity.json"47µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"50µs
And "{result}" is not an error33µ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 "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"30µs
Then "{result}" is an error26µs
And I attach "{result}" to the test output as "no-access-read-object-error.txt"26µs
📎 Attachments:
no-access-user-identity.json
View Content (232 bytes)
failed to create service account test-user-no-access: 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 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"44µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"36µs
And I refer to "{result}" as "storage"23µs
And I call "{api}" with "GetServiceAPI" using argument "iam"30µ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"182ms
And "{result}" is not an error49µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"126ms
And I refer to "{result}" as "testUserRead"28µs
And I attach "{result}" to the test output as "read-user-identity.json"38µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{true}"54µs
And "{result}" is not an error36µ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"27µs
And I refer to "{result}" as "userStorage"13µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"18µs
Then "{result}" is not an error15µs
And I attach "{result}" to the test output as "read-read-object-result.json"16µ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: 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"28µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"54µs
And I refer to "{result}" as "storage"25µs
And I call "{api}" with "GetServiceAPI" using argument "iam"29µs
And I refer to "{result}" as "iamService"22µs
And I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "test content"189ms
And "{result}" is not an error27µ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}"974ms
Then "{result}" is true34µs
📎 Attachments:
policy-result-object-storage-no-public-principals.json
View JSON (1938 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://nodal-time-474015-p5-bucket --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\": 5}\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": "[5]",
      "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"46µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"37µs
And I refer to "{result}" as "storage"30µs
And I call "{api}" with "GetServiceAPI" using argument "iam"20µ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"128ms
And I refer to "{result}" as "testUserNoAccess"27µs
And I attach "{result}" to the test output as "no-access-user-identity.json"35µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"52µs
And "{result}" is not an error35µ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"27µs
When I call "{userStorage}" with "CreateBucket" using argument "test-bucket-no-access"31µs
Then "{result}" is an error29µs
And I attach "{result}" to the test output as "no-access-create-bucket-error.txt"25µs
📎 Attachments:
no-access-user-identity.json
View Content (232 bytes)
failed to create service account test-user-no-access: 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 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"49µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"41µs
And I refer to "{result}" as "storage"16µs
And I call "{api}" with "GetServiceAPI" using argument "iam"19µs
And I refer to "{result}" as "iamService"25µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"157ms
And I refer to "{result}" as "testUserWrite"25µs
And I attach "{result}" to the test output as "write-user-identity.json"48µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"51µs
And "{result}" is not an error53µ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"27µs
And I refer to "{result}" as "userStorage"22µs
When I call "{userStorage}" with "CreateBucket" using argument "test-bucket-write"26µs
Then "{result}" is not an error25µs
And I attach "{result}" to the test output as "write-create-bucket-result.json"25µs
And I call "{storage}" with "DeleteBucket" using argument "{result.ID}"15µ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"37µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"34µs
And I refer to "{result}" as "storage"25µs
And I call "{api}" with "GetServiceAPI" using argument "iam"29µs
And I refer to "{result}" as "iamService"23µs
When I attempt policy check "object-storage-no-public-principals" for control "CCC.ObjStor.CN01" assessment requirement "AR03" for service "{ServiceType}" on resource "{ResourceName}" and provider "{Provider}"1s
Then "{result}" is true36µs
📎 Attachments:
policy-result-object-storage-no-public-principals.json
View JSON (1938 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://nodal-time-474015-p5-bucket --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\": 5}\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": "[5]",
      "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"32µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"51µs
And I refer to "{result}" as "storage"24µs
And "{result}" is not an error29µs
And I call "{api}" with "GetServiceAPI" using argument "iam"28µs
And I refer to "{result}" as "iamService"16µs
And "{result}" is not an error30µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"175ms
And I refer to "{result}" as "testUserRead"30µs
And I attach "{result}" to the test output as "read-user-identity.json"45µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserRead}", and "{true}"50µ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"14µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-write-object={Timestamp}.txt", and "test content"32µs
Then "{result}" is an error18µs
And I attach "{result}" to the test output as "read-create-object-error.txt"19µ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: 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"45µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"50µs
And I refer to "{result}" as "storage"27µs
And "{result}" is not an error20µs
And I call "{api}" with "GetServiceAPI" using argument "iam"21µs
And I refer to "{result}" as "iamService"22µs
And "{result}" is not an error21µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"160ms
And I refer to "{result}" as "testUserWrite"48µs
And I attach "{result}" to the test output as "write-user-identity.json"34µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"55µs
And "{result}" is not an error39µ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"24µs
And I refer to "{result}" as "userStorage"26µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-write-object={Timestamp}.txt", and "test content"33µs
Then "{result}" is not an error26µs
And I attach "{result}" to the test output as "write-create-object-result.json"28µ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"35µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"36µs
And I refer to "{result}" as "storage"23µs
And "{result}" is not an error26µs
And I call "{api}" with "GetServiceAPI" using argument "iam"28µs
And I refer to "{result}" as "iamService"21µs
And "{result}" is not an error25µ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}"964ms
Then "{result}" is true30µs
📎 Attachments:
policy-result-object-storage-no-public-principals.json
View JSON (1938 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://nodal-time-474015-p5-bucket --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\": 5}\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": "[5]",
      "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"34µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"41µs
And I refer to "{result}" as "storage"21µs
And I call "{api}" with "GetServiceAPI" using argument "iam"27µs
And I refer to "{result}" as "iamService"20µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "test data"157ms
Then "{result}" is not an error27µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"158ms
And I refer to "{result}" as "testUserRead"36µ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}"52µ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"22µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"32µs
Then "{result}" is not an error28µs
When I call "{storage}" with "SetObjectPermission" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "none"33µs
Then "{result}" is an error27µs
And I attach "{result}" to the test output as "set-object-permission-error.txt"17µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"21µ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"41µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"39µs
And I refer to "{result}" as "storage"27µs
And I call "{api}" with "GetServiceAPI" using argument "iam"31µs
And I refer to "{result}" as "iamService"41µ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}"850ms
policy check failed: GCP Cloud Storage Uniform Bucket-Level Access Check:
Then "{result}" is true29µs
📎 Attachments:
policy-result-uniform-bucket-level-access.json
View JSON (1589 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://nodal-time-474015-p5-bucket \\\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"54µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"34µs
And I refer to "{result}" as "storage"15µs
And I call "{api}" with "GetServiceAPI" using argument "iam"32µs
And I refer to "{result}" as "iamService"14µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "test data"178ms
Then "{result}" is not an error32µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-no-access", "{UID}", and "none"142ms
And I refer to "{result}" as "testUserNoAccess"28µs
And I attach "{result}" to the test output as "no-access-user-identity.json"35µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserNoAccess}", and "{false}"57µs
And "{result}" is not an error35µ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"24µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"26µs
Then "{result}" is an error18µs
When I call "{storage}" with "SetObjectPermission" using arguments "{ResourceName}", "test-object={Timestamp}.txt", and "read"18µs
Then "{result}" is an error14µs
And I attach "{result}" to the test output as "set-object-permission-error.txt"17µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "test-object={Timestamp}.txt"18µs
Then "{result}" is an error16µs
📎 Attachments:
no-access-user-identity.json
View Content (232 bytes)
failed to create service account test-user-no-access: 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: 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"37µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"28µs
And I refer to "{result}" as "storage"14µs
And I call "{api}" with "GetServiceAPI" using argument "iam"30µs
And I refer to "{result}" as "iamService"14µs
Then no-op required15µ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"29µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"34µs
And I refer to "{result}" as "storage"21µs
When I call "{storage}" with "CreateBucket" using argument "ccc-test-soft-delete"660ms
Then "{result}" is not an error39µs
And I refer to "{result}" as "testBucket"26µs
And I attach "{result}" to the test output as "created-bucket.json"57µs
When I call "{storage}" with "DeleteBucket" using argument "ccc-test-soft-delete"256ms
Then "{result}" is not an error163µs
When I call "{storage}" with "ListDeletedBuckets"255µs
Then "{result}" is not an error59µ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"68µs
And "{result}" should have length greater than "0"50µs
When I call "{storage}" with "RestoreBucket" using argument "ccc-test-soft-delete"40µs
Then "{result}" is not an error42µs
When I call "{storage}" with "ListBuckets"40µs
Then "{result}" is not an error43µs
And I attach "{result}" to the test output as "restored-buckets.json"45µs
When I call "{storage}" with "DeleteBucket" using argument "ccc-test-soft-delete"40µs
Then "{result}" is not an error42µ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"51µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"33µs
And I refer to "{result}" as "storage"21µ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}"868ms
policy check failed: GCP Cloud Storage Soft Delete Check:
Then "{result}" is true32µs
📎 Attachments:
policy-result-bucket-soft-delete.json
View JSON (1761 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://nodal-time-474015-p5-bucket \\\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"38µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"44µs
And I refer to "{result}" as "storage"29µs
When I call "{storage}" with "GetBucketRetentionDurationDays" using argument "{ResourceName}"20ms
Then "{result}" is not an error26µs
And I refer to "{result}" as "originalRetention"19µs
And I attach "{result}" to the test output as "original-retention-days.txt"30µs
And "{result}" should be greater than "0"36µs
expected {result} (0) to be greater than 0
When I call "{storage}" with "SetBucketRetentionDurationDays" using arguments "{ResourceName}" and "1"31µs
Then "{result}" is an error29µs
And I attach "{result}" to the test output as "set-retention-error.txt"29µs
When I call "{storage}" with "GetBucketRetentionDurationDays" using argument "{ResourceName}"19µs
Then "{result}" is not an error18µs
And "{result}" should equal "{originalRetention}"21µ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"31µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"40µs
And I refer to "{result}" as "storage"25µ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}"867ms
policy check failed: GCP Cloud Storage Retention Policy Lock Check:
Then "{result}" is true31µs
📎 Attachments:
policy-result-bucket-retention-lock.json
View JSON (1712 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://nodal-time-474015-p5-bucket \\\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"50µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"35µs
And I refer to "{result}" as "storage"24µs
And I call "{api}" with "GetServiceAPI" using argument "iam"20µs
And I refer to "{result}" as "iamService"18µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"151ms
And I refer to "{result}" as "testUserWrite"27µs
And I attach "{result}" to the test output as "write-user-identity.json"36µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"50µ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 refer to "{result}" as "userStorage"25µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "test-retention-object={Timestamp}.txt", and "protected data"20µs
And I attach "{result}" to the test output as "uploaded-object.json"16µs
And I call "{userStorage}" with "GetObjectRetentionDurationDays" using arguments "{ResourceName}" and "test-retention-object={Timestamp}.txt"22µs
Then "{result}" should be greater than "1"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 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"32µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"36µs
And I refer to "{result}" as "storage"26µs
And I call "{api}" with "GetServiceAPI" using argument "iam"28µ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"163ms
Then "{result}" is not an error29µs
When I call "{storage}" with "DeleteObject" using arguments "{ResourceName}" and "immediate-delete-test={Timestamp}.txt"161ms
Then "{result}" is an error37µs
expected {result} to be an error, got
And I attach "{result}" to the test output as "immediate-delete-error.txt"28µs
And "{result}" should contain "retention"18µ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"44µs
And I refer to "{result}" as "storage"18µs
And I call "{api}" with "GetServiceAPI" using argument "iam"19µs
And I refer to "{result}" as "iamService"15µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "retention-period-test={Timestamp}.txt", and "compliance data"163ms
And I call "{storage}" with "GetObjectRetentionDurationDays" using arguments "{ResourceName}" and "retention-period-test={Timestamp}.txt"44ms
Then "{result}" should be greater than "1"54µs
expected {result} (0) to be greater than 1
And I attach "{result}" to the test output as "retention-period-days.json"26µ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"41µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"45µs
And I refer to "{result}" as "storage"27µs
And I call "{api}" with "GetServiceAPI" using argument "iam"32µs
And I refer to "{result}" as "iamService"25µ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}"849ms
policy check failed: GCP Cloud Storage Default Object Retention Check:
Then "{result}" is true28µs
📎 Attachments:
policy-result-object-default-retention.json
View JSON (1854 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://nodal-time-474015-p5-bucket \\\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"50µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"45µs
And I refer to "{result}" as "storage"30µs
And I call "{api}" with "GetServiceAPI" using argument "iam"33µs
And I refer to "{result}" as "iamService"21µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"152ms
And I refer to "{result}" as "testUserWrite"25µs
And I attach "{result}" to the test output as "write-user-identity.json"34µs
And I call "{api}" with "GetServiceAPIWithIdentity" using arguments "object-storage", "{testUserWrite}", and "{true}"58µ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"22µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "protected-object={Timestamp}.txt", and "immutable data"33µs
Then "{result}" is not an error27µs
And I attach "{result}" to the test output as "protected-object.json"31µs
When I call "{userStorage}" with "DeleteObject" using arguments "{ResourceName}" and "protected-object={Timestamp}.txt"32µs
Then "{result}" is an error17µs
And I attach "{result}" to the test output as "delete-protected-error.txt"17µs
And "{result}" should contain one of "retention, locked, immutable, protected"23µ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"56µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"39µs
And I refer to "{result}" as "storage"24µs
And I call "{api}" with "GetServiceAPI" using argument "iam"30µs
And I refer to "{result}" as "iamService"25µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "admin-protected-object={Timestamp}.txt", and "compliance data"169ms
Then "{result}" is not an error29µs
When I call "{storage}" with "DeleteObject" using arguments "{ResourceName}" and "admin-protected-object={Timestamp}.txt"167ms
Then "{result}" is an error35µs
expected {result} to be an error, got
And I attach "{result}" to the test output as "admin-delete-protected-error.txt"18µs
And "{result}" should contain "retention"21µ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"46µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"39µs
And I refer to "{result}" as "storage"24µs
And I call "{api}" with "GetServiceAPI" using argument "iam"20µs
And I refer to "{result}" as "iamService"21µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-write", "{UID}", and "write"131ms
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 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"20µ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"17µs
When I call "{userStorage}" with "CreateObject" using arguments "{ResourceName}", "modify-test-object={Timestamp}.txt", and "modified content"22µs
Then "{result}" is an error18µs
And I attach "{result}" to the test output as "modify-protected-error.txt"16µs
And "{result}" should contain one of "retention, locked, immutable, protected, exists"22µ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"35µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"44µs
And I refer to "{result}" as "storage"22µs
And I call "{api}" with "GetServiceAPI" using argument "iam"28µs
And I refer to "{result}" as "iamService"23µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "readable-protected-object={Timestamp}.txt", and "readable data"168ms
Then "{result}" is not an error32µs
Given I call "{iamService}" with "ProvisionUserWithAccess" using arguments "test-user-read", "{UID}", and "read"134ms
And I refer to "{result}" as "testUserRead"28µs
And I attach "{result}" to the test output as "read-user-identity.json"35µ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 refer to "{result}" as "userStorage"26µs
When I call "{userStorage}" with "ReadObject" using arguments "{ResourceName}" and "readable-protected-object={Timestamp}.txt"31µs
Then "{result}" is not an error25µs
And I refer to "{result}" as "readResult"20µs
And I attach "{result}" to the test output as "read-protected-object.json"26µs
And "{readResult.Name}" is "readable-protected-object={Timestamp}.txt"22µ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"36µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"37µs
And I refer to "{result}" as "storage"22µs
And I call "{api}" with "GetServiceAPI" using argument "iam"32µs
And I refer to "{result}" as "iamService"23µ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}"848ms
policy check failed: GCP Cloud Storage Object Retention Enforcement Check:
Then "{result}" is true30µs
📎 Attachments:
policy-result-object-retention-enforcement.json
View JSON (2253 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://nodal-time-474015-p5-bucket \\\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"55µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"36µs
And I refer to "{result}" as "storage"13µs
When I call "{storage}" with "IsBucketVersioningEnabled" using argument "{ResourceName}"22µs
Then "{result}" is true16µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "versioned-object.txt", and "test content"159ms
And I refer to "{result}" as "createdObject"29µs
Then "{createdObject.VersionID}" contains "20"39µs
expected {createdObject.VersionID} to contain '20', but got '1774355591353508'
And I attach "{result}" to the test output as "versioned-object.json"17µs
Scenario: Objects are stored with unique version identifiers @PerService @object-storage @CCC.ObjStor @CCC.ObjStor.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy
Given a cloud api for "{Instance}" in "api"46µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"28µ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}"849ms
policy check failed: GCP Cloud Storage Bucket Versioning Configuration:
Then "{result}" is true32µs
📎 Attachments:
policy-result-object-storage-versioning.json
View JSON (3828 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://nodal-time-474015-p5-bucket \\\n  --format=json\n",
  "query_output": "{\n  \"autoclass\": {\n    \"enabled\": true,\n    \"terminalStorageClass\": \"NEARLINE\",\n    \"terminalStorageClassUpdateTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"toggleTime\": \"2026-03-24T05:23:37.462000+00:00\"\n  },\n  \"autoclass_enabled_time\": \"2026-03-24T05:23:37+0000\",\n  \"cors_config\": [\n    {\n      \"maxAgeSeconds\": 3600,\n      \"method\": [\n        \"GET\",\n        \"HEAD\",\n        \"PUT\",\n        \"POST\",\n        \"DELETE\"\n      ],\n      \"origin\": [\n        \"http://image-store.com\"\n      ],\n      \"responseHeader\": [\n        \"*\"\n      ]\n    }\n  ],\n  \"creation_time\": \"2026-03-24T05:23:37+0000\",\n  \"data_locations\": [\n    \"US-EAST4\",\n    \"US-WEST1\"\n  ],\n  \"default_storage_class\": \"STANDARD\",\n  \"generation\": 1774329817196675678,\n  \"labels\": {\n    \"auto_cleanup\": \"true\",\n    \"ccc_compliance_test\": \"1774355541\",\n    \"cfi_target_id\": \"local-test\",\n    \"environment\": \"cfi-test\",\n    \"github_run_id\": \"local\",\n    \"github_workflow\": \"cfi-build\",\n    \"goog-terraform-provisioned\": \"true\",\n    \"managed_by\": \"terraform\",\n    \"project\": \"ccc-cfi-compliance\"\n  },\n  \"lifecycle_config\": {\n    \"rule\": [\n      {\n        \"action\": {\n          \"type\": \"Delete\"\n        },\n        \"condition\": {\n          \"age\": 365,\n          \"matchesPrefix\": [\n            \"nodal-time-474015-p5\"\n          ]\n        }\n      }\n    ]\n  },\n  \"location\": \"US\",\n  \"location_type\": \"dual-region\",\n  \"metageneration\": 7,\n  \"name\": \"nodal-time-474015-p5-bucket\",\n  \"public_access_prevention\": \"inherited\",\n  \"retention_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionPeriod\": \"2\"\n  },\n  \"rpo\": \"DEFAULT\",\n  \"soft_delete_policy\": {\n    \"effectiveTime\": \"2026-03-24T05:23:37.462000+00:00\",\n    \"retentionDurationSeconds\": \"604800\"\n  },\n  \"storage_url\": \"gs://nodal-time-474015-p5-bucket/\",\n  \"uniform_bucket_level_access\": true,\n  \"update_time\": \"2026-03-24T12:32:21+0000\",\n  \"versioning_enabled\": true,\n  \"website_config\": {\n    \"mainPageSuffix\": \"index.html\",\n    \"notFoundPage\": \"404.html\"\n  }\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"52µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"50µs
And I refer to "{result}" as "storage"25µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "original content"154ms
And I refer to "{result.VersionID}" as "version1"34µs
And I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "modified content"252ms
And I refer to "{result.VersionID}" as "version2"39µs
Then "{version1}" is not equal to "{version2}"34µ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"50µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"44µs
And I refer to "{result}" as "storage"27µs
Then no-op required20µ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"44µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"32µs
And I refer to "{result}" as "storage"25µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "original content"165ms
And I refer to "{result.VersionID}" as "version1"44µs
And I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "modified content"246ms
And I refer to "{result.VersionID}" as "version2"34µs
And I call "{storage}" with "ReadObjectAtVersion" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "{version1}"61ms
And I attach "{result}" to the test output as "original-content.json"67µs
Then "{result.Data}" contains "original content"29µs
When I call "{storage}" with "ReadObjectAtVersion" using arguments "{ResourceName}", "version-test-object={Timestamp}.txt", and "{version2}"56ms
Then "{result.Data}" contains "modified content"48µs
And I attach "{result}" to the test output as "modified-content.json"61µs
📎 Attachments:
original-content.json
View JSON (228 bytes)
{"ID":"version-test-object=1774355592711.txt","BucketID":"nodal-time-474015-p5-bucket","Name":"version-test-object=1774355592711.txt","Size":16,"Data":["original content"],"Encryption":"","EncryptionAlgorithm":"","VersionID":""}
modified-content.json
View JSON (228 bytes)
{"ID":"version-test-object=1774355592711.txt","BucketID":"nodal-time-474015-p5-bucket","Name":"version-test-object=1774355592711.txt","Size":16,"Data":["modified content"],"Encryption":"","EncryptionAlgorithm":"","VersionID":""}
Scenario: Previous object versions can be recovered @PerService @object-storage @CCC.ObjStor @CCC.ObjStor.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Policy
Given a cloud api for "{Instance}" in "api"49µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"39µs
And I refer to "{result}" as "storage"30µs
Then no-op required21µ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"48µs
And I refer to "{result}" as "storage"16µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "recover-deleted-object={Timestamp}.txt", and "data to retain"157ms
And I refer to "{result.VersionID}" as "retainedVersionId"30µs
When I call "{storage}" with "DeleteObject" using arguments "{ResourceName}" and "recover-deleted-object={Timestamp}.txt"165ms
When I call "{storage}" with "ReadObjectAtVersion" using arguments "{ResourceName}", "recover-deleted-object={Timestamp}.txt", and "{retainedVersionId}"62ms
Then "{result.Data}" contains "data to retain"39µs
And I attach "{result}" to the test output as "recovered-deleted-version.json"50µs
📎 Attachments:
recovered-deleted-version.json
View JSON (232 bytes)
{"ID":"recover-deleted-object=1774355593242.txt","BucketID":"nodal-time-474015-p5-bucket","Name":"recover-deleted-object=1774355593242.txt","Size":14,"Data":["data to retain"],"Encryption":"","EncryptionAlgorithm":"","VersionID":""}
Scenario: Deleted object version remains in version list @PerService @object-storage @CCC.ObjStor @CCC.ObjStor.CN05 @tlp-clear @tlp-green @tlp-amber @tlp-red @Behavioural
Given a cloud api for "{Instance}" in "api"43µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"42µs
And I refer to "{result}" as "storage"29µs
When I call "{storage}" with "CreateObject" using arguments "{ResourceName}", "list-deleted-versions-object={Timestamp}.txt", and "versioned data"161ms
And I refer to "{result.VersionID}" as "listedVersionId"30µs
When I call "{storage}" with "DeleteObject" using arguments "{ResourceName}" and "list-deleted-versions-object={Timestamp}.txt"164ms
When I call "{storage}" with "ListObjectVersions" using arguments "{ResourceName}" and "list-deleted-versions-object={Timestamp}.txt"40µs
And "{result}" is an array of objects with at least the following contents36µ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"20µ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"44µs
And I call "{api}" with "GetServiceAPI" using argument "object-storage"43µs
And I refer to "{result}" as "storage"26µs
Then no-op required29µs