gcp-logging-delete-log-bucket

Delete a custom or _Default log bucket (logging.buckets.delete), permanently destroying all retained log history stored in it.

derived gcp emits CanDelete

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} HasPermission {'bucket': None}

where

node_type(?bucket) == LoggingService ?bucket.provider_type == 'google.logging.v2.LogBucket' effective_action(?principal, ?bucket) includes 'logging.buckets.delete' ?bucket is NOT _Required (system-immutable) ?bucket.locked == false — a locked bucket cannot be deleted

emit

source typeIdentity
target typeLoggingService
source?principal
target?bucket
permissionslogging.buckets.delete
conditionsiam_permission service_state
state logicACTIVE if the principal holds EFFECTIVE logging.buckets.delete on the target bucket AND the bucket is not locked (locked: false) AND the bucket is not _Required; CONDITIONAL(service_state) if bucket lock status is unknown; BLOCKED if the bucket is locked (locked: true), is _Required, or an IAM deny / org policy denies logging.buckets.delete at the effective scope.

Narrative

{principal.name} can delete log bucket {bucket.name} (logging.buckets.delete), permanently destroying all retained log entries stored in it - eliminating forensic evidence of prior API activity across the project's retained log history.

Raw rule rules/derived/gcp/logging.yaml

id: gcp-logging-delete-log-bucket
emits: CanDelete
description: Delete a custom or _Default log bucket (logging.buckets.delete), permanently destroying all
  retained log history stored in it.
match:
- - principal: null
  - HasPermission
  - bucket: null
where:
- node_type(?bucket) == LoggingService
- ?bucket.provider_type == 'google.logging.v2.LogBucket'
- effective_action(?principal, ?bucket) includes 'logging.buckets.delete'
- ?bucket is NOT _Required (system-immutable)
- "?bucket.locked == false \u2014 a locked bucket cannot be deleted"
emit:
  source_type: Identity
  target_type: LoggingService
  source: ?principal
  target: ?bucket
  permissions:
  - logging.buckets.delete
  conditions:
  - iam_permission
  - service_state
  state_logic: 'ACTIVE if the principal holds EFFECTIVE logging.buckets.delete on the target bucket AND
    the bucket is not locked (locked: false) AND the bucket is not _Required; CONDITIONAL(service_state)
    if bucket lock status is unknown; BLOCKED if the bucket is locked (locked: true), is _Required, or
    an IAM deny / org policy denies logging.buckets.delete at the effective scope.'
  confidence: 0.93
  derived_from:
  - HasPermission(?principal, logging.buckets.delete, ?bucket)
  false_positive_note: "_Required bucket is always BLOCKED \u2014 the API enforces immutability server-side\
    \ regardless of IAM grants. A bucket with locked: true cannot be deleted \u2014 BLOCKED. Deleting\
    \ a log bucket removes ALL stored log entries in it permanently (no soft delete or recycle bin); high-impact\
    \ destructive action. Note: deleting the _Default bucket is distinct from deleting the _Default sink\
    \ \u2014 bucket deletion removes stored entries while sink deletion stops future delivery. An attacker\
    \ wanting total coverage needs both. This is evidence destruction, not privilege escalation; CanDelete\
    \ is non-walkable by default (schema traversal: false) \u2014 surfaced for impact analysis."
  narrative: "{principal.name} can delete log bucket {bucket.name} (logging.buckets.delete), permanently\
    \ destroying all retained log entries stored in it \u2014 eliminating forensic evidence of prior API\
    \ activity across the project's retained log history."
move · open · esc close