For ten years, Google instructed developers to treat API keys for Maps or Firebase as public "project identifiers." Unlike Stripe or AWS, which split credentials into publishable and secret keys, Google used a single string. They relied on secondary restrictions to keep these keys safe in public JavaScript.

The rollout of the Gemini API broke this model. As researchers at Truffle Security pointed out, by allowing these legacy "public" keys to authenticate Generative Language requests, Google updated the Generative Language API to require secret bearer tokens. But because developers spent years treating them as public billing IDs, the change now exposes accounts to unauthorized LLM usage.

By default, new Google API keys are "Unrestricted," meaning they can access any enabled API in a project. If you have an old project with a 5-year-old Maps key hardcoded in a public repo, and you decide to turn on the generativelanguage.googleapis.com API to test Gemini, you have just retroactively granted LLM access to that old, public key.

Navigating the GCP console UI manually to find restriction statuses across dozens of keys takes too long when you need to answer a simple question: "Am I exposed?"

Apikeyscan audits these credentials in seconds. It identifies unrestricted keys so you can rotate them before they are exploited. The tool outputs standard terminal tables for human triage and deterministic JSON for CI/CD pipelines.

Terminal output displaying a table of API keys with columns for Display Name, Created date, API Restrictions, and masked API Key. Entries include Generative Language API Keys and auto-created Firebase keys.

  • Operational speed: Scans 60+ keys in under 6 seconds (averaging ~8.5 seconds per 100 keys). Outputs deterministic JSON for CI/CD or color-coded tables for human review.
  • Cost: Uses the Google Cloud API Keys API (free of charge).

You can find the source code and installation instructions on GitHub: https://github.com/ghchinoy/apikeyscan.

Balancing JSON mode with human readability

1.Building for Machine Readability

Every command in apikeyscan has a global --json flag. When active, the tool suppresses conversational output and outputs deterministic JSON. This allows security teams to script apikeyscan into a daily cron job, a CI/CD pipeline, or an agent like Gemini CLI.

2. Handling ANSI String Alignment

For human readability, I used lipgloss to apply semantic colors: red for danger (unrestricted keys), muted gray for metadata. apikeyscan sorts unrestricted keys to the top of the list so they cannot be missed.

Mixing rich terminal styling with tabular output breaks standard formatting. Because lipgloss wraps strings in invisible ANSI escape codes, standard fmt.Printf padding breaks.

You must pad the raw string before applying the color to maintain alignment:

// Correct way to pad strings with Lipgloss styles
paddedVal1 := fmt.Sprintf("%-35s", val1)
paddedVal2 := fmt.Sprintf("%-12s", val2)
fmt.Printf("%s | %s\n", StyleWarn.Render(paddedVal1), StyleMuted.Render(paddedVal2))

3. Parsing Google Cloud Protobufs

Working with the Google Cloud Go SDK requires handling generated Protocol Buffers. API key restrictions are often nested inside oneof fields. To determine if an API key is unrestricted, you must carefully check for nil pointers on these generated structs:

func isUnrestrictedAPI(k *apikeyspb.Key) bool {
        return k.Restrictions == nil || len(k.Restrictions.ApiTargets) == 0
}

func getRestrictionsText(k *apikeyspb.Key) string {
        if k.Restrictions == nil {
                return "Unrestricted (ALL APIs)"
        }
        apiTargets := k.Restrictions.ApiTargets
        if len(apiTargets) == 0 {
                return "Unrestricted (ALL APIs)*"
        }
        if len(apiTargets) == 1 {
                svc := apiTargets[0].Service
                if len(svc) > 28 {
                        svc = svc[:25] + "..."
                }
                return svc
        }
        return fmt.Sprintf("Restricted (%d APIs)", len(apiTargets))
}

This type checking prevents panics on misconfigured keys and accurately summarizes key access.