β Human-authored analysis; AI used for formatting and proofreading.
A scanner reports: "IAM role StorageAdmin has s3:DeleteBucket permission on the CloudTrail destination bucket prod-logs."
Is this a problem?
It depends on what else is true. That single finding β one role, one permission, one bucket β means three different things depending on the configuration surrounding it:
Scenario A: The role has the permission. An SCP blocks cross-account writes to the bucket. The bucket exists and is owned by your account. CloudTrail is delivering logs successfully.
The finding is TRUE. The role really does have that permission. But the attack path is blocked. The SCP prevents the critical next step. This finding is reachable: it exists in the configuration, but every attempt to exploit it hits a wall.
Scenario B: The role has the permission. No SCP blocks cross-account writes. The bucket exists and is owned by your account. CloudTrail is delivering logs.
The finding is TRUE, and now only one precondition prevents the attack: the bucket still exists. If someone deletes it (accidentally, operationally, or maliciously), an attacker can register the same name in their own account and silently capture your CloudTrail logs. This finding is one away: one configuration change makes it exploitable.
Scenario C: The role has the permission. No SCP blocks cross-account writes. The bucket name referenced by CloudTrail does not appear in your account's inventory.
Every precondition is present. The identity can delete the bucket. No organizational policy prevents cross-account writes. The bucket may already be claimable. This finding is exploitable: the attack path is open.
Same finding and severity. Three different priorities.
A scanner checks: "does this role have s3:DeleteBucket on this bucket?" That's one resource, one permission. The scanner has no way to know:
These facts live on other resources β SCPs, CloudTrail trails, S3 inventories, organizational policies. The scanner evaluated one node. The exploitability determination requires evaluating edges β the relationships between nodes.
This is why the false positive rate is 80-99%. The scanner is incomplete about the graph. A finding without graph context is a finding without an exploitability determination. The only option is to put it in a queue and hope a human investigates.
Stave evaluates configuration snapshots against a catalog of controls. Individual controls produce findings like the scanner. But Stave also evaluates compound chains: multi-hop attack paths where every precondition must be present for the path to be open.
After the chain evaluation, every finding gets one of three labels:
β
EXPLOITABLE β this finding participates in a compound chain
where every precondition is present.
The attack path is open.
β ONE AWAY β this finding participates in a chain that
would fire if one currently-absent precondition
appeared. One configuration change makes it
exploitable.
REACHABLE β this finding is true but not connected to a
complete or near-complete attack path.
The labels change how a defender reads the finding.
An EXPLOITABLE finding goes to the top of the queue regardless of its severity rating. A MEDIUM finding in an exploitable chain is more urgent than a CRITICAL finding that's reachable only.
A ONE AWAY finding tells the defender what to protect. The missing precondition is named: "SCP data perimeter blocks cross-account writes β if this SCP is removed, the chain becomes exploitable." The defender's action is not "fix the role permission" β it's "protect the SCP." Different action, different team, different urgency.
A REACHABLE finding is a true misconfiguration with no connected attack path. It belongs in the backlog, not the sprint.
A compound chain is a Datalog rule with multiple preconditions. Here's the bucket-hijacking telemetry chain, simplified:
Chain: CHAIN.BUCKET_HIJACK.TELEMETRY
Preconditions:
1. Identity has s3:DeleteBucket on the target bucket
2. Target bucket is a CloudTrail destination
3. No SCP data perimeter blocks cross-account writes
Fires when: all three preconditions are TRUE
Result: every finding from these preconditions is β
EXPLOITABLE
If the chain fires, every participating finding β the permission finding, the CloudTrail destination finding is marked EXPLOITABLE. The β marker appears in the output:
FINDINGS:
CRITICAL β
EXPLOITABLE CHAIN.BUCKET_HIJACK.TELEMETRY.001
Identity "StorageAdmin" has s3:DeleteBucket on "prod-logs"
which is the CloudTrail destination for trail "prod-trail".
No SCP data perimeter blocks cross-account writes.
Chain preconditions:
β has_permission(StorageAdmin, s3:DeleteBucket, prod-logs)
β is_stream_destination(prod-logs, prod-trail, cloudtrail)
β has_data_perimeter_scp β ABSENT (not protecting)
If the chain doesn't fire because one precondition is missing:
HIGH β ONE AWAY CTL.IAM.POLICY.S3DELETE.001
Role "StorageAdmin" has s3:DeleteBucket on "prod-logs".
Chain: CHAIN.BUCKET_HIJACK.TELEMETRY β 1 precondition missing
β has_permission(StorageAdmin, s3:DeleteBucket, prod-logs)
β is_stream_destination(prod-logs, prod-trail, cloudtrail)
β has_data_perimeter_scp β PRESENT (protecting)
β If the data perimeter SCP is removed, this becomes EXPLOITABLE
The finding names the specific precondition protecting you. That precondition is not a finding to fix, it's a control to preserve.
The traditional triage question is: "does this finding matter?" That question requires investigation β reading the finding, checking the environment, understanding the context, deciding.
The exploitability classification answers the question before it's asked. EXPLOITABLE β fix now. ONE AWAY β protect the precondition. REACHABLE β backlog. No investigation or judgment call on individual findings. The graph did the triage.
Scanners produce a flat list sorted by severity. CRITICAL at the top, LOW at the bottom. Defenders work down the list.
The problem: severity is a property of the finding's potential impact, not its exploitability. A CRITICAL finding (potential impact: total account compromise) that's REACHABLE (no connected attack path) is less urgent than a MEDIUM finding (potential impact: data exposure) that's EXPLOITABLE (every precondition present).
After the classification, defenders sort by exploitability first, severity second:
1. EXPLOITABLE findings (any severity) β the attack path is open
2. ONE AWAY findings (any severity) β protect the precondition
3. REACHABLE findings sorted by severity β backlog
This reordering routinely promotes findings from the bottom of the severity list and demotes findings from the top. A scanner's CRITICAL finding may be demoted to REACHABLE (no connected chain). A scanner's MEDIUM finding may be promoted to EXPLOITABLE (every precondition present). The graph determines the priority, not the label.
A defender fixes an EXPLOITABLE finding. How do they know it worked?
Re-capture the snapshot. Re-run the evaluation. The classification changes:
Before: β
EXPLOITABLE (all preconditions present)
After: β ONE AWAY (the fixed precondition is now absent)
or
REACHABLE (the fix broke the chain entirely)
The diff shows the status change. The defender doesn't need to trust that the fix worked. The classification proves it. The chain that fired before no longer fires. The exit code changes. The CI gate passes.
Most security monitoring is reactive: wait for something to go wrong, then respond. The ONE AWAY classification is proactive: it tells the defender what's ABOUT to go wrong if one thing changes.
"Your data perimeter SCP is the only thing preventing a CloudTrail hijack chain. If that SCP is modified, removed, or has its conditions broadened, five findings immediately become EXPLOITABLE."
The defender's action: put an alert on the SCP. Monitor it for changes. Treat it as a critical control. Because it's the gate protecting five other findings from becoming exploitable.
This is the finding no scanner produces: "you're safe now, but you're one configuration change from not being safe, and here's the specific change that would do it."
A scanner evaluates one resource at a time. It can tell you the node property: "this role has this permission." It cannot tell you the edge property: "this permission, combined with this CloudTrail dependency, combined with this SCP absence, creates an open attack path."
The exploitability classification requires:
Every scanner does step 1. Steps 2 and 3 require the graph. The graph is the configuration snapshot. The actual state of every resource and every relationship between them, captured at a point in time, evaluated deterministically.
Same snapshot, catalog, findings and classifications. Every time.
Stave classifies every finding as exploitable, one-away, or reachable from a configuration snapshot. The compound chains fire when every precondition is present. The one-away analysis names the missing precondition. The fix verification confirms the classification changed. No credentials, agent or AI interpreting signals. stave apply --observations ./your-snapshot/