Shopify has introduced Overexposed. This internal scanner locates Kubernetes Ingresses on GKE and Cloud Run services. It checks for those that are accessible from the internet without authentication. After a first version based on repository config files produced false alarms and blind spots, the team rebuilt it on GCP Cloud Asset Inventory and an anonymous HTTP probe. In just a few weeks of tuning, this reduced inconclusive Ingress verdicts by around 80%.
The original scanner checked the infrastructure repository for Ingresses. It focused on public hostnames without authentication settings and then probed them. Shopify says this failed in three ways. Teams that configured proxy authentication annotations directly triggered false positives. Admission controllers that rewrite settings at admission time, but do not run in every cluster, meant a correct-looking config could have no effect. Ingresses created by other repositories or platform tooling were never seen. The authors summarize the lesson as: the repository shows intent; the cluster shows what is enforced.
The current version follows a schedule. It queries Cloud Asset Inventory. This inventory includes Ingresses accepted by GKE after admission, along with Cloud Run services and load balancers. A second search retrieves access policies without needing cluster credentials. The inventory also surfaced Cloud Run services in projects the first scanner had never covered.
Each candidate then receives a single anonymous request. The scanner sends HEAD first, falls back to GET, follows at most three redirects and checks each one. It also tests path variations such as /ADMIN, since path rules can be case-sensitive. Results map to six verdicts: exposed, expected, protected, not found, unreachable and inconclusive. "Expected" applies when an endpoint is open but matches a reviewed, in-code allowlist entry by exact host and path. Allowlisted endpoints are still probed on every run, and an entry that turns out to be protected or gone is flagged as stale.
Shopify stresses that status codes are context-dependent. A 404 behind an Ingress means the proxy answered and the path is missing. On Cloud Run, Google's front end can return a 404 before the application runs, so the scanner classifies it as inconclusive. A 403 is also not treated as proof of protection. For Cloud Run, the scanner distinguishes ingress settings from IAM: a service is public only if allUsers holds the invoker role or the invoker check is disabled. Services behind a load balancer use a URL map. This map decides which backend handles each path. It also shows where authentication, like Identity-Aware Proxy, is required.
In early runs, roughly three in four results were inconclusive in one July run. The team grouped results by reason, added verdicts where evidence supported them, and replayed rule changes against saved scan results before shipping. Uncertain evidence stays inconclusive rather than defaulting to protected. The scanner stores verdicts, reasons and team ownership, but not response bodies or credentials.
Because the scanner makes outbound requests to arbitrary hostnames, Shopify hardened it against SSRF. It validates every IP returned by DNS, rejects private and loopback addresses, connects directly to the checked IP to prevent DNS rebinding, and repeats checks on each redirect. The authors recommend also restricting the scanner's network egress, including cloud metadata endpoints.
The team built Overexposed with AI coding agents and AI code review. To catch tests that pass while checking the wrong thing, they apply mutation testing to security guards and keep one canary target per verdict.
Shopify’s experience illustrates a split that applies to exposure scanning in general. Rule-based checks, such as Trend Micro’s Cloud Run rule, look for allUsers bindings. This is affordable and easily integrable into CI. This tool report intent, not enforcement, so it miss resources created outside the repository and settings that an admission controller later rewrites.
Inventory-based approaches, such as Cartography and the Cloud Asset Inventory queries behind Shopify’s Overexposed, start from what the cloud actually runs, which closes those blind spots. Cartography still derives an exposed_internet flag from configuration, so it can’t confirm what an outsider sees. Overexposed adds an anonymous probe, an allowlist, and evidence-backed verdicts to the inventory. It sees uncertain evidence as inconclusive, not protected.
For teams creating something similar, use inventory as the main reference. Keep config analysis as an early signal in CI. Add a probe only if inventory can’t show whether access control works.