Security Command Center accumulates findings faster than most teams can triage them inside the console alone, and once you have more than a handful of projects, the console view stops being where decisions get made. The real workflow, ticket creation, SLA tracking, exception handling, lives in Jira, ServiceNow, or a dedicated vulnerability management system. Getting SCC data there reliably, and keeping it in sync as findings change state, is a distinct engineering problem from running the scans themselves. This piece covers the finding schema you’re exporting, the three export mechanisms GCP offers, how to make an export survive an organization’s inevitable project and folder reshuffling, how to map SCC’s four severities onto a real SLA model, and how to write ticket resolution back into SCC without fighting the API.
SCC finding structure: sources, categories, states, and mute rules
Every finding in Security Command Center belongs to a source. Built-in sources include Security Health Analytics (misconfigurations like public buckets or open firewall rules), Web Security Scanner (XSS, outdated libraries, mixed content on App Engine and Compute Engine web apps), Event Threat Detection and Container Threat Detection (runtime signals from Cloud Logging and GKE audit data), and Rapid Vulnerability Detection (network-reachable exposures). You can also register your own custom source and push findings through the API, which matters if you want a third-party scanner or an internal script to show up in the same inventory.
Each finding carries a category (a string like OPEN_FIREWALL or PUBLIC_BUCKET_ACL), a severity (CRITICAL, HIGH, MEDIUM, LOW), a findingClass (VULNERABILITY, MISCONFIGURATION, OBSERVATION, THREAT, or SCC_ERROR), and a state of ACTIVE or INACTIVE. The state field is the one people misread most often: for scanner-owned sources, only the scanner sets it back to INACTIVE, and only on a subsequent scan that no longer detects the condition. Muting a finding does not change its state. Mute is a separate field (MUTED, UNMUTED, UNDEFINED) with its own muteInfo showing who muted it and why. You can mute a finding once, or define a dynamic mute rule with a filter expression, for example category=”MFA_NOT_ENFORCED” AND resource.project_display_name=”sandbox-*”, so future matches are muted automatically without a human clicking through the console each time. Every finding also has a compliances array listing which standards and control IDs it maps to, which is the field you want for compliance reporting rather than trying to re-derive that mapping yourself from category strings.
Export paths compared: Pub/Sub streaming, BigQuery sink, direct API pull
GCP gives you three ways to get findings out, and they are not interchangeable.
| Path | Latency | Best for | Operational cost |
|---|---|---|---|
| Pub/Sub continuous export | Near real time, one message per finding change | Event-driven ticket creation, alerting, SIEM forwarding | Low; message volume is small, but you own the subscriber and its retry logic |
| BigQuery continuous export | Near real time, appended as rows | Trend analysis, SLA reporting, joining findings with asset and IAM tables | Storage and query cost only; no subscriber to maintain |
| Direct API pull (findings.list) | Point in time, whatever you schedule | Backfill, ad hoc audits, environments where you can’t stand up a Pub/Sub subscriber | Counts against API quota; scales poorly past a few thousand findings per poll |
In practice, most teams end up running two of the three: Pub/Sub for the live ticketing pipeline, and BigQuery for the reporting and audit trail that Pub/Sub’s fire-and-forget model doesn’t give you on its own. The direct API pull is what you reach for during initial backfill, since neither Pub/Sub nor BigQuery exports replay history from before the export was created. Run a one-time findings.list with a broad filter (state=”ACTIVE”) to seed your system, then let the continuous export carry updates from that point forward.
Building a continuous export that survives project and folder changes
The most common mistake is scoping the export at the project level. A NotificationConfig or BigQueryExport created under a single project only sees findings whose resource currently sits in that project. When someone moves a project between folders, spins up a new project for a workload, or a reorg changes which project owns a given VPC, project-scoped exports silently stop covering resources they used to cover, and nobody notices until an audit finds a gap.
Create the export at the organization level instead: parent set to organizations/ORG_ID rather than projects/PROJECT_ID. An organization-scoped NotificationConfig or BigQueryExport follows the resource hierarchy automatically, so a project moving folders keeps publishing to the same topic or table without any config change on your side. If your org policy requires narrower scopes for specific teams, scope at the folder level instead of the project level, since folders are also inherited but move around far less often than individual projects.
Two more details that save an on-call page later. First, set a filter that excludes findingClass=”SCC_ERROR” from your main pipeline; scanner errors are useful for debugging the scanner, not for your ticket queue, and mixing them in pollutes your SLA metrics. Second, if you manage the export config with Terraform, keep it in the same module as your organization-level IAM bindings rather than a per-environment module, so a terraform destroy on a workload environment can never take the export config down with it.
Mapping SCC categories to your own severity and SLA model
SCC’s four severities are a starting point, not a finished prioritization model. They reflect how dangerous the underlying issue is in the abstract, not how exposed the specific resource is in your environment. A CRITICAL finding on an internal, firewalled batch-processing VM and a CRITICAL finding on an internet-facing load balancer back end deserve different clocks.
Build a small lookup table that combines severity with something that captures blast radius, such as an asset label you already maintain in Cloud Asset Inventory (internet_facing=true) or a network tag. A workable starting point:
| Severity | Internet-facing asset | Internal-only asset |
|---|---|---|
| CRITICAL | 24 hours | 72 hours |
| HIGH | 7 days | 14 days |
| MEDIUM | 30 days | 60 days |
| LOW | 90 days, best effort | Backlog, no fixed SLA |
Store this mapping as data, not as a chain of if-statements buried in a script, since the SLA thresholds are a policy decision that security leadership will want to revisit independently of the pipeline code. Platforms that automate triage, such as SITEY, take exactly this approach: the raw severity and findingClass from the source feed a scoring step that also weighs asset exposure and known exploitation before a ticket gets its priority label, rather than passing SCC’s severity straight through to the queue. If you’re building this mapping yourself rather than adopting a platform for it, the AI triage module is a reasonable reference point for what the scoring inputs typically look like.
Closing the loop: writing state back when a ticket is resolved
This is where most homegrown pipelines break down. Someone closes the Jira ticket, and the SCC finding just sits there ACTIVE, because state is scanner owned for built-in sources; you cannot flip it from your side, and you shouldn’t try. Waiting for the next scheduled scan to flip it to INACTIVE (Security Health Analytics reruns roughly daily by default, Web Security Scanner on whatever cadence you configured) is the correct behavior, not a bug, but it means your ticketing system needs its own notion of “resolved, pending verification” that isn’t just mirroring SCC’s state field.
The two write paths you do have are security marks and mute. Use updateSecurityMarks to annotate the finding with your own workflow keys, for example marks.ticket_id=PROJ-4821 and marks.resolved_by=alice, so anyone looking at the finding in the console can see it’s already tracked elsewhere without duplicating the ticket. Use mute, with a documented reason, for findings that are an accepted risk rather than a fix in progress, for example a flagged default service account on a project slated for decommission next quarter. Do not use mute as a substitute for actually closing out fixable findings; a muted CRITICAL finding on a production load balancer is the kind of thing an auditor finds and asks pointed questions about six months later.
Tools that reconcile ticket status against the original scanner, such as SITEY, generally follow this same pattern: write the external ticket ID into security marks at intake, then re-test the specific resource directly instead of trusting that a closed ticket means the underlying condition is gone, and only mark the internal record resolved once that re-test confirms it. The retest and closure phase exists specifically because a closed ticket and a fixed vulnerability are not the same fact, regardless of which scanner or ticketing system you’re running.
Cost and quota notes for high-volume organizations
BigQuery continuous export costs are dominated by storage, a few cents per GB per month for the findings table, plus whatever you spend querying it; streaming inserts from the export itself carry a generous free tier that most organizations never exceed. Pub/Sub costs scale with message volume and are usually negligible unless you’re running Event Threat Detection across a very large fleet with a noisy environment generating thousands of threat findings a day.
The place organizations actually get burned is API quota on the direct list path. Security Command Center’s read API has a default per-minute quota that is comfortable for occasional audits and backfills but not for polling as your primary ingestion method. If your architecture still depends on a scheduled findings.list poll instead of Pub/Sub or BigQuery, either move to a push-based export or request a quota increase in the console’s Quotas page before you scale past a handful of projects. It’s also worth checking whether your polling interval is shorter than it needs to be; a five-minute poll of an org with a few hundred projects can burn through a request budget that a properly filtered Pub/Sub subscription would never touch, since Pub/Sub only sends a message when something actually changes.
Finally, keep an eye on finding volume itself when Security Health Analytics or Web Security Scanner is first enabled on a large, previously unmonitored organization. The initial scan can surface a spike of findings that dwarfs the ongoing steady state, and if your BigQuery export or ticketing integration assumes a roughly constant daily volume, that first sync is a good time to pre-warn whoever owns the ticket queue on the other end. Organizing scanner coverage through a single scanner integrations layer, and mapping the resulting categories against your compliance obligations through a dedicated compliance mapping step, keeps that initial spike from turning into a permanent backlog that nobody trusts the numbers on.
About SITEY
SITEY is an autonomous vulnerability management platform. It discovers, validates, prioritizes, remediates and re-tests vulnerabilities through an eight-phase automated pipeline, unifying output from 17 integrated scanners. SITEY is self-hosted: it runs in your own infrastructure and your findings are stored there. Outbound connections are limited to licence activation and the optional services you enable, such as an AI provider, CVE enrichment and patch catalogues. Pricing is 599 USD per month or 5,999 USD for a perpetual lifetime license. See pricing or how the platform works.