If you have read anything about AWS Security Hub written before December 2025, part of it now describes a different product. A new Security Hub reached general availability on 2 December 2025, and the original service is now called AWS Security Hub CSPM. The two sit side by side. Security Hub CSPM runs posture checks (the controls and standards most people mean when they say "Security Hub") and still aggregates findings. The new Security Hub takes findings from Amazon GuardDuty, Amazon Inspector, Amazon Macie and Security Hub CSPM, normalises them into the Open Cybersecurity Schema Framework (OCSF), correlates them per resource into exposure findings, and routes the result to people and automation.
This article covers how a finding travels across accounts and Regions, how to query and triage it with the V2 APIs, and how to route it to a fix without duplicates. API names and field paths were checked against the AWS documentation on 3 October 2026.
The architecture
Two products named Security Hub
The original service did two jobs. The first was posture management: run checks such as whether a bucket blocks public access or CloudTrail is on, grouped into standards like AWS Foundational Security Best Practices, the CIS AWS Foundations Benchmark, NIST SP 800-53 and PCI DSS. Most checks are service-managed AWS Config rules. The second was aggregation: accept findings from other services and partners in the AWS Security Finding Format (ASFF).
The split gives each job its own product. Security Hub CSPM keeps the controls, the standards, ASFF, and the original APIs and events (GetFindings, BatchUpdateFindings, and the Security Hub Findings - Imported EventBridge event). The new Security Hub is the prioritisation layer rebuilt on OCSF, with its own V2 APIs, automation rules and EventBridge event (Findings Imported V2), and it treats CSPM as one source among several. Separate event streams mean that if you enable the new one without reviewing old EventBridge rules, a finding can page you twice.
| Question | Security Hub CSPM | Security Hub (new) |
|---|---|---|
| Main job | Posture checks against standards | Correlate and prioritise findings from many sources |
| Finding schema | ASFF | OCSF |
| Read / update APIs | GetFindings, BatchUpdateFindings | GetFindingsV2, BatchUpdateFindingsV2 |
| EventBridge detail-type | Security Hub Findings - Imported | Findings Imported V2 |
| Unique output | Control pass/fail and security score per standard | Exposure findings with a potential attack path |
How a finding becomes an exposure
Follow one signal through the system. Inspector scans an EC2 instance and finds a critical CVE with a known exploit. GuardDuty separately sees that instance talking to a known command-and-control address. Security Hub CSPM's control evaluation reports that the instance's security group allows inbound traffic from 0.0.0.0/0. Alone, each finding is a medium worry that gets ignored.
Security Hub ingests all three and maps them onto OCSF. Every finding then has the same shape: cloud.account.uid for the account, resources.uid and resources.type for the resource, severity_id and status_id as integers, finding_info.title and finding_info.uid for what was found, and metadata.product.name for the producer. Because the resource identifier is now common, the correlation stage can join signals per resource and raise an exposure finding: reachable from the internet, exploitable, and already showing hostile traffic. The finding's detail page has a Potential attack path tab that draws how an attacker could get from the internet to the resource and onward. AWS's launch example: an S3 bucket with versioning, Object Lock and MFA delete all disabled is labelled potential data destruction.
The integers follow OCSF. Status: 1 New, 2 In Progress, 3 Suppressed, 4 Resolved. Severity: 1 Informational, 2 Low, 3 Medium, 4 High, 5 Critical, 6 Fatal. The update API and automation rules take integers, not labels.
Accounts, Regions and coverage
In AWS Organizations, the management account designates a delegated administrator, normally a dedicated security tooling account, which sees findings for every member. GetFindingsV2 accepts a Scopes parameter only from the delegated administrator, so it can narrow a query to the whole organisation or to specific organisational units. Any other caller gets AccessDeniedException.
Regions are the second axis. With Region aggregation you choose a home Region and link others to it, so findings from linked Regions appear in the home Region. Automation rules follow the same model. With aggregation on, you can create rules only in the home Region, and they apply to every linked Region unless the rule's criteria exclude one. Any Region you do not link needs its own rules, so a forgotten new Region never reaches your rules or tickets.
CSPM controls evaluate resources through AWS Config, so every account and Region needs the recorder running. Without it a control neither passes nor fails; it is silent, and the dashboard looks clean. CSPM's central configuration lets the delegated administrator push policies that choose standards and controls per OU. See Organizations and SCPs for the account structure this assumes, and AWS Config for the recorder that posture checks depend on.
Querying findings with GetFindingsV2
The read path is GetFindingsV2. Its Filters parameter is a small boolean tree. A list of CompositeFilters is joined by CompositeOperator (AND or OR). Each composite holds typed lists, such as StringFilters, NumberFilters, DateFilters, BooleanFilters, MapFilters and IpFilters, joined by its own Operator. Each entry names an OCSF FieldName and a Filter. You can use up to 10 composites, with up to 20 filters of each type inside one composite. MaxResults runs from 1 to 100, and you page with NextToken. The response is Findings (raw OCSF JSON) plus the next token. The same filter grammar is used for automation rule criteria, so a query you debug here can be pasted into a rule.
import boto3
sh = boto3.client("securityhub", region_name="eu-west-1") # your home Region
def open_high_findings(account_id):
"""Yield New/In Progress findings of severity High or worse for one account."""
filters = {
"CompositeOperator": "AND",
"CompositeFilters": [{
"Operator": "AND",
"StringFilters": [
{"FieldName": "cloud.account.uid",
"Filter": {"Value": account_id, "Comparison": "EQUALS"}},
],
"NumberFilters": [
{"FieldName": "severity_id", "Filter": {"Gte": 4}}, # 4 High, 5 Critical, 6 Fatal
{"FieldName": "status_id", "Filter": {"Lte": 2}}, # 1 New, 2 In Progress
],
}],
}
kwargs = {"Filters": filters, "MaxResults": 100}
while True:
page = sh.get_findings_v2(**kwargs)
for finding in page["Findings"]:
yield finding
if not page.get("NextToken"):
return
kwargs["NextToken"] = page["NextToken"]
for f in open_high_findings("111122223333"):
print(f["severity_id"], f["finding_info"]["title"], f["resources"][0]["uid"])Always page to the end; a report built from the first 100 findings looks plausible and is wrong. Read optional OCSF objects defensively, since producers fill different ones. IAM uses the old action names: GetFindingsV2 is authorised by securityhub:GetFindings, and BatchUpdateFindingsV2 by securityhub:BatchUpdateFindings.
Triage with automation rules
Automation rules (CreateAutomationRuleV2) rewrite findings as they arrive. Criteria use the same OCSF filter grammar, and actions can set exactly three fields: Comment, SeverityId and StatusId. A rule can also open a ticket in Jira Cloud or ServiceNow ITSM.
Three details matter. Rules evaluate provider-supplied findings created or updated after the rule exists; they do not rewrite history. Rules are not triggered by your BatchUpdateFindingsV2 edits, and when both touch a field the last write wins, so a producer re-sending a finding can let a suppression rule override an analyst's In Progress. And the lowest RuleOrder applies first, so later rules win conflicts.
# Automation rule criteria use the GetFindingsV2 filter grammar. Lowest RuleOrder runs first;
# when two rules set the same field, the one applied last wins.
rule_1 = { # RuleOrder 1: sandbox OU accounts are informational, never paged
"criteria": {"cloud.account.uid": ["444455556666", "777788889999"]},
"actions": {"SeverityId": 1, "Comment": "sandbox account: informational by policy SEC-12"},
}
rule_2 = { # RuleOrder 2: exploitable internet-facing CVEs are critical everywhere
"criteria": {"vulnerabilities.is_exploit_available": True,
"vulnerabilities.cve.cvss.base_score": {"Gte": 9.0}},
"actions": {"SeverityId": 5, "Comment": "exploit available, CVSS >= 9"},
}
# A sandbox finding with an exploitable 9.8 CVE matches both. Rule 2 applies last, so it ends at 5.The sketch shows intent, not the literal request body; build that from the API reference. Put broad downgrades early and narrow escalations late, so a genuine critical in a sandbox still pages someone. AWS documents that rules stop working when an account joins an organisation with a delegated administrator, the administrator changes, or an unlinked Region becomes linked, so re-check rules after those events.
Routing with EventBridge without loops
Every new finding and every update reaches EventBridge as a Findings Imported V2 event carrying a single finding. That includes updates you make with BatchUpdateFindingsV2. Two cautions apply. The user guide's sample filter mixes ASFF-style keys (ProductArn, Severity.Label) with a statement that filters must follow OCSF, so test any detail filter against a captured event. Because your own updates generate events, a handler that updates its finding is invoked again; filtering on status_id == 1 breaks the loop. Match broadly and filter in code:
{
"source": ["aws.securityhub"],
"detail-type": ["Findings Imported V2"]
}import boto3
sh = boto3.client("securityhub")
def handler(event, context):
found = event["detail"]["findings"] # docs: one finding per event
for f in found if isinstance(found, list) else [found]:
if f.get("severity_id", 0) < 5 or f.get("status_id") != 1:
continue # only brand-new Critical/Fatal
ticket = open_ticket(f) # your ITSM client; must be idempotent
sh.batch_update_findings_v2(
MetadataUids=[f["metadata"]["uid"]],
StatusId=2, # In Progress
Comment=f"ticket {ticket} opened by triage-router",
)Status 2 records ownership and makes the handler ignore the re-emitted event. The ticket call must still be idempotent on metadata.uid, because EventBridge delivers at least once. BatchUpdateFindingsV2 accepts either MetadataUids or FindingIdentifiers (account, finding uid, product uid), never both, with up to 100 per call. Check UnprocessedFindings in the response instead of assuming success. To stop a broad automation role from quietly suppressing findings, IAM supports the condition key securityhub:OCSFSyntaxPath/StatusId (and the same for SeverityId and Comment). Deny the Suppressed value to every principal except the security team. For the EventBridge mechanics, see Amazon EventBridge.
Worked example: three medium findings, one real exposure
A payments team runs an internet-facing EC2 API host in account 111122223333; eu-west-1 is the home Region. Inspector reports a CVSS 9.8 CVE with vulnerabilities.is_exploit_available true. CSPM's security-group control has failed on the same instance for weeks and been ignored.
- Both findings arrive with the same
resources.uid. Correlation raises an exposure finding whose attack path shows internet, then security group, then instance, then the instance profile's role. - Rule 2 above lifts the Inspector finding to
SeverityId5. Rule 1 does not match because the account is not a sandbox. - A
Findings Imported V2event fires. The router Lambda sees severity 5 and status 1, opens one ticket in the payments queue, and sets status 2 with the ticket number in the comment. - The on-call engineer reads the attack path, not three separate alerts. They narrow the security group to the load balancer, then patch through Systems Manager.
- On the next scans, the producers update their findings to resolved, the exposure clears, and the trends view shows open findings falling over time.
Alone, the bad security group was one line among thousands; joined to an exploitable CVE on the same resource, it became step one of an attack path. See GuardDuty for what those detections look like.
Failure modes
- Double paging. Old CSPM EventBridge rules and new V2 rules both fire. Retire the CSPM rules the V2 rules replace.
- Silent coverage gaps. Config not recording, or a Region not linked: green because nothing is evaluated. Alarm on missing data per account and Region.
- Suppression that hides attacks. A broad rule sets status 3 on a whole product or account. Scope suppressions narrowly, put the reason in
Comment, and review them every quarter. - Analyst edits undone by a rule firing on a provider update; keep status-changing rules narrow and audit them.
- Partial batch updates. Ignoring
UnprocessedFindingsleaves tickets and findings disagreeing. Retry and log.
Trade-offs
For compliance reporting against named standards, CSPM is the tool. If you run GuardDuty and Inspector and the problem is "which ten things do we fix this week?", the new Security Hub's correlation is what you pay for. Most organisations run both, with CSPM as a source. AWS describes the new pricing as resource-based; check the current pricing page, not old CSPM estimates.
Neither replaces a SIEM: Security Hub tracks state (what is wrong now), not months of raw events. And automate fixes carefully. Blocking a public bucket is usually safe; closing a security group on a production API can cause a worse outage than the exposure. Automate enrichment and tickets everywhere, changes only where rollback is clear. The broader baseline is in a cloud security baseline.
What to do next
- Pick a delegated administrator account (not the management account) and a home Region. Link every Region you use, including the ones you think you do not use.
- Confirm the AWS Config recorder is on in every account and Region, and alarm when it is not.
- Enable Security Hub CSPM with one standard (Foundational Security Best Practices is the usual start) through central configuration, then enable the new Security Hub with GuardDuty and Inspector as sources.
- List existing EventBridge rules on the CSPM detail-type and decide, rule by rule, whether a
Findings Imported V2rule replaces it. - Write two or three automation rules (downgrade sandboxes early, escalate exploitable CVEs late) and test each with
GetFindingsV2using the same filter first. - Deploy an idempotent router that opens one ticket per new critical finding and sets status 2.
- Restrict who can set
StatusId3 with thesecurityhub:OCSFSyntaxPathcondition key. - Review exposure findings weekly and track time to resolve, not finding counts.