GCS buckets without a retention policy
What does ZopNight detect here?
A GCS retention policy blocks object deletion and overwrite until each object reaches a configured age: the WORM guarantee auditors recognize. ZopNight's RC-1231 flags buckets whose metadata lacks retention_policy_set=true at medium severity, a $0 compliance finding that matters most for logs, evidence, and financial records.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-1231 |
| Category | compliance |
| Severity | medium |
| Metric | none — pure configuration read |
| Source | bucket_retention.go |
Where it applies
Retention is a WORM guarantee, not a backup setting
A retention policy makes every object in the bucket immutable until it reaches a minimum age: no principal (not an editor, not a compromised service account, not you) can delete or replace an object younger than the retention period. That write-once-read-many property is what distinguishes it from versioning (which keeps old generations but lets privileged principals purge them) and from backups (which are copies, not guarantees about the original). For audit logs, security evidence, and records covered by financial-retention regulation, immutability of the primary copy is frequently the control the auditor is actually asking about.
The lock is the part auditors care about
An unlocked retention policy can be shortened or removed by anyone with sufficient permission, which means it protects against accidents but not against a privileged attacker or a rushed administrator. Locking the policy (Bucket Lock) removes that escape hatch permanently: the retention period can then only ever be increased. Locking is irreversible short of deleting the bucket after everything ages out, so choose the period carefully: a locked 7-year policy on the wrong bucket is a 7-year storage commitment. ZopNight flags the absence of any policy; whether to lock is a judgment call the finding leaves to you.
Not every bucket should have one
Retention makes object overwrite illegal, so buckets with mutable workloads (sync targets, frequently rewritten state files, scratch data) will break under a retention policy. Resolve findings on those buckets by classifying them, not by blindly applying a policy. The rule is a prompt to sort buckets into “records” and “working data”, then protect the first class.
Query the state, then set a period
gcloud storage buckets describe gs://BUCKET --format="json(retention_policy)"null reproduces the finding. To apply, for example, a 90-day period:
gcloud storage buckets update gs://BUCKET --retention-period=90dSignal shape: true-only metadata
The discoverer writes retention_policy_set=true only when a retentionPolicy exists on
the bucket. It never writes false. The rule therefore fires on the key’s absence, gated on
the storage_class anchor the same enricher always stamps, so a resource that was never
enriched as a GCS bucket abstains instead of firing. Detection is entirely read-only
(storage.buckets.get); applying or locking a policy is always a human action.