← 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
A .gitignoreA README.mdA remediate.sh
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)