# GCS Bucket Missing Retention Policy

> 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.

Source: https://zop.dev/integrations/gcp/recommendations/gcs-bucket-missing-retention-policy
Updated: 2026-08-19

---

## 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

```bash
gcloud storage buckets describe gs://BUCKET --format="json(retention_policy)"
```

`null` reproduces the finding. To apply, for example, a 90-day period:

```bash
gcloud storage buckets update gs://BUCKET --retention-period=90d
```

## Signal 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.
