[object Object]

← back to Gcp Case 72846716

GCP case 72846716 remediation kit: dry-run-first api-keys delete/restrict script

70fc3f0200734c1a53c741faa66fc390b1812efe · 2026-07-01 17:03:43 -0700 · Steve Abrams

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Files touched

Diff

commit 70fc3f0200734c1a53c741faa66fc390b1812efe
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Jul 1 17:03:43 2026 -0700

    GCP case 72846716 remediation kit: dry-run-first api-keys delete/restrict script
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---
 .gitignore   |  8 ++++++++
 README.md    | 32 ++++++++++++++++++++++++++++++
 remediate.sh | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 103 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1924158
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+node_modules/
+.env*
+tmp/
+*.log
+.DS_Store
+dist/
+build/
+.next/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..19e8396
--- /dev/null
+++ b/README.md
@@ -0,0 +1,32 @@
+# GCP security case #72846716 — Gemini project compromise remediation kit
+
+Google flagged project `gen-lang-client-…65425540` (AI Studio / Gemini API) as
+possibly compromised and is throttling it. Prime suspects: two leaked orphan
+Gemini keys (last-4 `ejMo`, `S2ic`) flagged by vp-security 2026-06-23, never
+registry-managed, values since scrubbed from disk by secret-strip.
+
+## Status (as of 2026-07-01)
+
+- Master `GEMINI_API_KEY` (…-mvA) and `GEMINI_API_KEY_WALLCO` (…k720): both
+  verified live (HTTP 200 on models list) — not throttled yet.
+- Leaked key values no longer on disk anywhere (grep sweep clean) — the only
+  live copies are on Google's side.
+- No local credential can reach the project:
+  - `info@designerwallcoverings.com` gcloud token: expired, needs interactive re-auth
+  - ADC (`~/.config/gcloud/application_default_credentials.json`): same
+  - `theagentabrams@gmail.com`: live, but cannot see any gen-lang-client project
+  - `claude-gmc` SA: scoped to dw-gmc only
+
+## To finish (one human step + one command)
+
+1. Steve, in any terminal:
+   `gcloud auth login info@designerwallcoverings.com`
+   (or `steveabramsdesigns@gmail.com` if the AI Studio keys were made under the
+   personal account — whichever login shows the gen-lang-client project)
+2. Then: `./remediate.sh` (dry-run) → review → `./remediate.sh --apply`
+
+Deletes are soft (30-day undelete). Kept keys get an API restriction limiting
+them to the Generative Language API only.
+
+After a fresh replacement key is issued, route it with the secrets skill:
+`add GEMINI_API_KEY_DW_ENRICH <key>` (pre-staged slot, auto-fans-out).
diff --git a/remediate.sh b/remediate.sh
new file mode 100755
index 0000000..dbb3a89
--- /dev/null
+++ b/remediate.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+# GCP security case #72846716 — gen-lang-client project flagged compromised (Gemini API abuse).
+# Remediation via API Keys API: delete the two leaked orphan keys (last-4 ejMo / S2ic),
+# restrict every kept key to the Generative Language API, and surface billing.
+#
+# DRY-RUN by default. Pass --apply to actually delete/restrict.
+# Deletes are soft: `gcloud services api-keys undelete` restores within 30 days.
+#
+# Prereq (the only step that needs a human): a live Google login that can see the
+# gen-lang-client project — run one of:
+#   gcloud auth login info@designerwallcoverings.com
+#   gcloud auth login steveabramsdesigns@gmail.com
+set -euo pipefail
+export PATH=/opt/homebrew/bin:$PATH
+
+APPLY=0; [ "${1:-}" = "--apply" ] && APPLY=1
+LEAK_SUFFIXES=("ejMo" "S2ic")
+
+echo "== Accounts =="
+gcloud auth list --format="value(account,status)" || true
+
+echo "== Locating gen-lang-client project =="
+PROJECT=$(gcloud projects list --filter="projectId:gen-lang-client*" --format="value(projectId)" | head -1)
+if [ -z "$PROJECT" ]; then
+  echo "FATAL: no gen-lang-client* project visible to the active account."
+  echo "Log in with the Google account that owns the Gemini/AI Studio keys, then re-run."
+  exit 1
+fi
+echo "project: $PROJECT"
+
+echo "== API keys in $PROJECT =="
+gcloud services api-keys list --project="$PROJECT" \
+  --format="table(uid,displayName,createTime,restrictions.apiTargets[].service)"
+
+echo "== Classifying keys =="
+KEYS=$(gcloud services api-keys list --project="$PROJECT" --format="value(uid)")
+for UID in $KEYS; do
+  KEYSTRING=$(gcloud services api-keys get-key-string "$UID" --project="$PROJECT" --format="value(keyString)")
+  LAST4=${KEYSTRING: -4}
+  NAME="projects/$PROJECT/locations/global/keys/$UID"
+  LEAKED=0
+  for S in "${LEAK_SUFFIXES[@]}"; do [ "$LAST4" = "$S" ] && LEAKED=1; done
+  if [ $LEAKED -eq 1 ]; then
+    echo "LEAKED key …$LAST4 ($UID) -> DELETE"
+    if [ $APPLY -eq 1 ]; then
+      gcloud services api-keys delete "$UID" --project="$PROJECT" --quiet
+      echo "  deleted (undelete window: 30 days)"
+    fi
+  else
+    echo "kept key …$LAST4 ($UID) -> restrict to generativelanguage.googleapis.com"
+    if [ $APPLY -eq 1 ]; then
+      gcloud services api-keys update "$UID" --project="$PROJECT" \
+        --api-target=service=generativelanguage.googleapis.com --quiet
+      echo "  restricted"
+    fi
+  fi
+done
+
+echo "== Billing on $PROJECT =="
+gcloud billing projects describe "$PROJECT" 2>/dev/null || \
+  echo "(billing API not enabled or no permission — check console Billing page for rogue spend)"
+
+[ $APPLY -eq 0 ] && echo "DRY-RUN complete. Re-run with --apply to execute." || echo "APPLIED."

(oldest)  ·  back to Gcp Case 72846716  ·  (newest)