<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bala Paranj</title>
    <description>The latest articles on DEV Community by Bala Paranj (@bala_paranj_059d338e44e7e).</description>
    <link>https://dev.to/bala_paranj_059d338e44e7e</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3862804%2F7ea6c560-63cb-4daf-a713-450532280b0a.jpg</url>
      <title>DEV Community: Bala Paranj</title>
      <link>https://dev.to/bala_paranj_059d338e44e7e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bala_paranj_059d338e44e7e"/>
    <language>en</language>
    <item>
      <title>ThoughtWorks Just Named the Problem. Here's the Structural Fix.</title>
      <dc:creator>Bala Paranj</dc:creator>
      <pubDate>Fri, 07 Aug 2026 12:12:00 +0000</pubDate>
      <link>https://dev.to/bala_paranj_059d338e44e7e/thoughtworks-just-named-the-problem-heres-the-structural-fix-1hio</link>
      <guid>https://dev.to/bala_paranj_059d338e44e7e/thoughtworks-just-named-the-problem-heres-the-structural-fix-1hio</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;✓ Human-authored analysis; AI used for formatting and proofreading.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The ThoughtWorks Technology Radar (April 2026) flagged something under "Caution" that describes one instance of a problem we encountered from a completely different direction — implementing S3 bucket takeover findings from HackerOne triage work:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;When the spreadsheet that quietly runs the business evolves into customized agentic workflows that lack governance, it introduces significant security risks and a proliferation of competing solutions to similar problems.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem ThoughtWorks describes is about the &lt;strong&gt;lifecycle of cloud resources&lt;/strong&gt;. That lifecycle has been producing ungovernered risk long before AI workflow tools existed.&lt;/p&gt;

&lt;p&gt;When we triaged S3 bucket takeover reports on HackerOne, the pattern was clear: a team creates a bucket, uses it, stops using it, deletes it — but the DNS record, the CloudFront distribution, or the application code still references the old bucket name. An attacker creates a new bucket with the same name and takes over the traffic. The resource was deleted. The references were not deleted. The lifecycle wasn't governed.&lt;/p&gt;

&lt;p&gt;This is the same structural problem at every phase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create  → Resource provisioned with AI-generated defaults. Nobody reviews the policy.
Use     → Resource accumulates dependencies. Other resources reference it.
Modify  → Configuration drifts from original intent. Nobody tracks the drift.
Abandon → Team moves on. Resource remains. No owner. No review. Full permissions.
Delete  → Resource removed. References survive. Ghost references. Undefined behavior.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI workflow tools accelerate the CREATE phase. But the lifecycle problem exists at every phase. It existed before AI tools made it faster. ThoughtWorks is seeing the acceleration. The underlying problem is the ungoverned lifecycle.&lt;/p&gt;

&lt;p&gt;The spreadsheet that quietly runs the business. Every organization has one. The revenue model nobody documented. The customer onboarding flow that lives in someone's head and a Google Sheet. The compliance checklist that's a macro.&lt;/p&gt;

&lt;p&gt;Now that spreadsheet is becoming an AI workflow. Claude Cowork, GPT wrappers, no-code platforms with OpenAI integrations — non-coders are building systems that provision cloud resources, call APIs, move data, and make decisions. The barrier to building dropped to zero. The barrier to governing what was built didn't drop at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  The resource lifecycle problem
&lt;/h2&gt;

&lt;p&gt;The S3 bucket takeover is just one example. The pattern repeats at every lifecycle phase:&lt;/p&gt;

&lt;p&gt;A team creates an S3 bucket for a project. The project ships. Six months later, the team reorganizes. The bucket stays. The IAM policies stay. Nobody audits a resource that works. A year later, the project is decommissioned. Someone deletes the application code. Nobody deletes the bucket. Nobody removes the DNS record that points to it. An attacker registers the bucket name and captures the traffic.&lt;/p&gt;

&lt;p&gt;AI workflow tools make this faster. A marketing manager builds an AI workflow that provisions a bucket, an API gateway, and a Lambda function. The workflow works for two weeks. The manager moves on to the next experiment. The resources stay — untagged, unowned, with whatever permissions the AI agent assigned.&lt;/p&gt;

&lt;p&gt;But the lifecycle problem isn't caused by AI tools. It's caused by the absence of a governance mechanism that operates across the full lifecycle — create, use, modify, abandon, delete and catches the structural violations at each transition. AI tools accelerate the creation phase. The lifecycle was ungoverned at every phase before AI existed.&lt;/p&gt;

&lt;p&gt;This means the problem is larger than what the Tech Radar identified. AI-accelerated shadow IT is one phase of a five-phase governance gap. Ghost references from deleted resources, orphaned infrastructure from reorganized teams, configuration drift from accumulated changes, privilege escalation from composed policies — these exist in every cloud environment, regardless of whether AI tools were used to create the resources. Every organization has resources that were created by engineers who left, modified by scripts that were forgotten, and referenced by infrastructure that outlived its purpose.&lt;/p&gt;

&lt;p&gt;The ThoughtWorks finding is the visible tip. The lifecycle governance gap is the iceberg. A solution that addresses the full lifecycle solves a problem every cloud environment has today.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this problem was identified: a 5-month arc
&lt;/h2&gt;

&lt;p&gt;The lifecycle governance gap didn't surface from reading the ThoughtWorks Radar. It evolved through five months of work from TRIZ contradiction analysis through real incident triage to architectural design:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Month 1 — Symptom.&lt;/strong&gt; TRIZ contradiction analysis of cloud security identified shadow IT as the consequence of overly restrictive authentication. When security friction is too high, users route around controls. The contradiction: stronger authentication increases security but pushes users to create ungoverned workarounds. Shadow IT is the escape valve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Month 2 — Architectural gap.&lt;/strong&gt; If you can't observe it, you can't include it in a safety plane. Shadow IT became the canonical example of unobservable infrastructure. The resource that exists but isn't part of any governance system. The focus shifted from "write better rules" to "capture what exists."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Month 3 — Concrete evidence.&lt;/strong&gt; The S3 bucket takeover case study from HackerOne triage named the real-world pattern: abandoned developer test projects in a &lt;code&gt;samples/&lt;/code&gt; folder — shadow IT that accumulated over time. Dangling DNS references to deleted buckets. Attacker registers the bucket name, captures the traffic. Shadow IT moved from abstraction to forensic finding with a dollar-cost breach attached.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Month 4 — ThoughtWorks confirmation.&lt;/strong&gt; The Tech Radar listed "AI-accelerated shadow IT" under Caution. This confirmed the pattern. Agents create infrastructure faster than humans observe it, collapsing the shadow IT timeline from years to hours. The acceleration was new. The underlying lifecycle problem was already documented.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Month 5 — Solution shipped.&lt;/strong&gt; The safety-level model defines Level 1 as "default drift" — the shadow IT floor. The customer pitch: "your effective security level is your weakest workflow, not your headline workflow." The solution: uniform gate enforcement across every workflow. One CI gate. Every PR. No shadow corners left behind.&lt;/p&gt;

&lt;p&gt;The solution — &lt;code&gt;stave apply&lt;/code&gt; runs deterministically against any configuration snapshot the CI can put in front of it. It was designed and built before the ThoughtWorks Radar confirmed the problem. The Radar validated the market signal. The architectural work was already done.&lt;/p&gt;

&lt;h2&gt;
  
  
  ThoughtWorks recommendations are necessary but insufficient
&lt;/h2&gt;

&lt;p&gt;The Tech Radar recommends four measures:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Facilitate experimentation within controlled environments.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Correct — sandboxes are necessary. But who writes the sandbox rules? If the sandbox is a separate AWS account with no guardrails, the shadow IT just moved to a different account. The sandbox needs the same governance as production. Otherwise it's not a sandbox — it's an ungoverned playground.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Appropriately instrumented internal sandboxes where usage can be tracked.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Correct — tracking is necessary. But tracking is observation (Stage 2 on the control maturity curve). It tells you what happened. It doesn't prevent anything. By the time you observe the public bucket with PII, the data is already exposed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. A shared catalogue of existing workflows helps teams discover what's already been built.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Correct — discovery prevents duplication. But discovery doesn't prevent misconfiguration. Knowing that three teams built similar workflows doesn't tell you whether any of them configured their resources safely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Workflows that gain traction can then signal where to invest in more robust, production-grade applications.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Correct — traction signals investment priority. But the risk lives in the gap between "gained traction" and "rebuilt production-grade". The workflow is already running. Data is already flowing. Resources are already provisioned. Waiting for traction before governing is waiting for the breach before locking the door.&lt;/p&gt;

&lt;p&gt;Each recommendation is a necessary practice. None is a structural mechanism that prevents misconfigured resources from reaching production. All four operate at Stage 2-3: observe, track, discover, prioritize. None operates at Stage 4: verify before deployment, block violations mechanically.&lt;/p&gt;

&lt;h2&gt;
  
  
  The structural fix
&lt;/h2&gt;

&lt;p&gt;The gap ThoughtWorks identifies — governance for AI-accelerated workflows is one instance of the broader lifecycle governance problem. The structural fix requires three properties:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Operates at every lifecycle phase, not just creation.&lt;/strong&gt; The governance mechanism must check resources when they're created (catching bad defaults), while they're in use (catching drift), when they're modified (catching regressions), and after they're abandoned (catching orphans and ghost references). A CI gate at creation time is necessary but insufficient — most lifecycle violations happen after creation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Mechanical, not manual.&lt;/strong&gt; No security team can review every resource across every lifecycle phase. The governance must be automated. Evaluate every configuration snapshot against every declared safety property, mechanically, on every check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Structural detection without intent.&lt;/strong&gt; Resources created by AI workflows, abandoned by reorganized teams, and inherited from legacy infrastructure don't have tags, owners or intent declarations. The governance must detect violations from the configuration structure alone — attack paths, ghost references, privilege escalation, orphaned permissions without any metadata from the builder.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this works in practice
&lt;/h2&gt;

&lt;p&gt;An AI workflow provisions cloud resources. The resources have configuration state captured by the cloud provider's APIs. A collector takes a snapshot of that state. A JSON file representing every resource, policy, relationship at the moment of capture.&lt;/p&gt;

&lt;p&gt;A specification gate evaluates the snapshot against a catalog of safety invariants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invariant: No S3 bucket in any account may have public access
           without an explicit exemption in the governance catalog.

Invariant: No IAM role may allow privilege escalation through
           PassRole, PutRolePolicy, or AttachRolePolicy without
           conditions (MFA, source IP, org boundary).

Invariant: No resource may reference a deleted dependency
           (ghost reference — security group pointing to
           deleted VPC, role trusting deleted account).

Invariant: No attack path may exist from any public principal
           to any internal resource through any chain of role
           assumptions and policy attachments.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each invariant is checked mechanically. The marketing manager's AI-provisioned bucket either satisfies the invariants or it doesn't. If it doesn't, the violation is flagged with the specific invariant that failed, the specific resource that violated it, and the specific evidence from the configuration state.&lt;/p&gt;

&lt;p&gt;No security knowledge required from the builder. The builder doesn't know the invariants exist. The invariants check the output, not the input. The builder provisions resources. The specification gate verifies them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The detection classes that matter most for lifecycle governance
&lt;/h2&gt;

&lt;p&gt;Each lifecycle phase produces a specific class of structural violation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ghost references (DELETE phase).&lt;/strong&gt; A resource is deleted, but other resources still reference it — a DNS record pointing to a deleted S3 bucket, a security group referencing a deleted VPC, a role trusting an account that was decommissioned. This is the S3 bucket takeover pattern from HackerOne triage: the resource is gone, the references survive, the attacker registers the name and captures the traffic. Detectable by cross-referencing every resource identifier in the snapshot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Orphaned resources (ABANDON phase).&lt;/strong&gt; A team moves on. Resources remain — provisioned, untagged, unowned, with whatever permissions were assigned at creation. They're attack surface with no owner and no review cycle. Detectable from the snapshot: resources with no tags, no recent access, and broad permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Privilege escalation paths (CREATE + MODIFY phases).&lt;/strong&gt; AI agents generate policies that individually make sense but compose into escalation chains. Role A can assume Role B. Role B can pass Role C to Lambda. Role C has admin access. No individual policy is wrong. The chain is exploitable. Detectable by computing transitive closure over the permission graph.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Public access by default (CREATE phase).&lt;/strong&gt; AI agents generating resource configurations use permissive defaults. Policies mathematically equivalent to &lt;code&gt;Principal: *&lt;/code&gt; through condition logic that covers the entire input domain. The text doesn't contain the wildcard. The math does. Detectable by formal verification — SMT solver checks if the conditions form a tautology.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No conditions on sensitive actions (CREATE phase).&lt;/strong&gt; AI-generated IAM policies rarely include conditions (MFA, source IP, org boundary). The builder doesn't know conditions exist. Sensitive actions are permitted without identity verification beyond initial authentication. Detectable from the statement structure — Allow + sensitive action prefix + empty condition block.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration drift (MODIFY phase).&lt;/strong&gt; A resource was configured correctly at creation. Changes accumulated over months — a new policy attachment, a modified security group rule, a relaxed encryption setting. Each individual change was small. The cumulative state violates the original safety properties. Detectable by evaluating the current snapshot against invariants — the invariants haven't changed, the configuration has changed.&lt;/p&gt;

&lt;p&gt;Every one of these is detectable from the configuration structure alone. No tags, intent declarations or security knowledge from the builder. The snapshot is sufficient evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  The governance model across the lifecycle
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Resource is created (by AI workflow, IaC, console, script — any channel)
    → Resource exists in cloud provider APIs
        → Collector snapshots resource state periodically
            → Specification gate evaluates snapshot at every phase:
                CREATE:  Are defaults safe? Any public access? Missing conditions?
                USE:     Has drift occurred? Do references still resolve?
                MODIFY:  Does the change violate existing invariants?
                ABANDON: Is the resource unowned? Untagged? Unused?
                DELETE:  Do ghost references survive? Dangling dependencies?
            → Violations flagged with specific invariant + evidence
            → Clean resources pass without human review
                → Security team reviews INVARIANTS (small, stable)
                → Not ALERTS (large, noisy, lifecycle-spanning)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This addresses each ThoughtWorks recommendation and fills the structural gap:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sandboxes:&lt;/strong&gt; The specification gate runs on sandbox accounts AND production accounts. Same invariants. Same evaluation. The sandbox isn't ungoverned. It's governed by the same safety properties, automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tracking:&lt;/strong&gt; The specification gate doesn't just track what happened — it blocks what shouldn't happen. Stage 4 (verify before deployment), not Stage 2 (observe after deployment).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared catalogue:&lt;/strong&gt; The invariant catalog is the shared catalogue of governance rules. 3,000+ safety properties across 74 cloud service domains. Every team uses the same catalog. Every non-coder's resources are checked against the same invariants.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Traction signals:&lt;/strong&gt; The specification gate's coverage metrics (&lt;code&gt;readiness&lt;/code&gt;, &lt;code&gt;gaps&lt;/code&gt;) report which resources are governed and which aren't. Resources provisioned by shadow IT workflows that aren't covered by the observation contract are flagged as coverage gaps — visible, tracked, reportable. The gap between experimented and production-grade is measured, not assumed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The limitation
&lt;/h2&gt;

&lt;p&gt;The specification gate evaluates what's observable through the cloud provider's APIs at the moment of capture. Resources provisioned through channels the APIs don't fully expose, resources in accounts the collector doesn't access, and ephemeral resources that were deleted before the snapshot — these are outside the observation window.&lt;/p&gt;

&lt;p&gt;Findings are lower bounds, not exhaustive inventories. "We found 3 attack paths" means at least 3 exist. It doesn't mean only 3 exist.&lt;/p&gt;

&lt;p&gt;The tool states this in every output: "Analysis covers API-observable state at capture time. Resources not visible to the collector are not evaluated." Transparency about coverage limits is as important as the findings themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;ThoughtWorks correctly identified that AI-accelerated shadow IT is a governance crisis. Their recommendations — sandboxes, tracking, shared catalogs, traction signals are necessary practices. But practices are Stage 2-3: observe, track, discover, prioritize.&lt;/p&gt;

&lt;p&gt;The structural fix is deeper than a Stage 4 CI gate. It's &lt;strong&gt;uniform gate enforcement that eliminates the distinction between core and shadow workflows.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Today, organizations have two tiers: the reviewed core (Level 5 — formal review, compliance controls, documented architecture) and the shadow corner (Level 1 — default drift, no review, no governance). The team's effective security level is the weakest workflow, not the headline workflow. The breach happens in the shadow corner, not the reviewed core.&lt;/p&gt;

&lt;p&gt;The specification gate makes this distinction disappear. Every configuration snapshot — whether it came from a senior engineer's Terraform, a marketing manager's AI workflow, or a forgotten test project from 2019 — goes through the same evaluation against the same 3,000+ safety invariants. There's no opt-out tier. No "this is just a test project" exemption. No shadow corners left behind.&lt;/p&gt;

&lt;p&gt;The team's effective level becomes the gate's enforcement level — uniform across all workflows, all resources, all lifecycle phases. Because the gate's enforcement is uniform across all of them.&lt;/p&gt;

&lt;p&gt;The governance mechanism the Tech Radar is looking for isn't a process improvement. It's a structural change: one gate, every snapshot, no exceptions. The lifecycle governance gap — from creation through abandonment through deletion — closed by a mechanism that doesn't know or care who created the resource, what tool they used, or whether they intended it to be temporary.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The structural violations described in this article are detected by &lt;a href="https://github.com/sufield/stave" rel="noopener noreferrer"&gt;Stave&lt;/a&gt; — an open-source cloud security reasoning engine that evaluates configuration snapshots against 3,000+ safety invariants. Apache 2.0. Air-gapped. No cloud credentials required.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cloudsecurity</category>
      <category>ai</category>
      <category>devops</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Hardware Solved This 30 Years Ago. Cloud Security is Just Starting.</title>
      <dc:creator>Bala Paranj</dc:creator>
      <pubDate>Thu, 06 Aug 2026 12:36:02 +0000</pubDate>
      <link>https://dev.to/bala_paranj_059d338e44e7e/hardware-solved-this-30-years-ago-cloud-security-is-just-starting-2e1b</link>
      <guid>https://dev.to/bala_paranj_059d338e44e7e/hardware-solved-this-30-years-ago-cloud-security-is-just-starting-2e1b</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;✓ Human-authored analysis; AI used for formatting and proofreading.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The semiconductor industry has a $15 billion ecosystem dedicated to proving that chips work correctly before they're manufactured. Electronic Design Automation (EDA) — the tools, methodologies, and verification infrastructure that ensure a billion-transistor processor doesn't ship with a deadlocking bus arbiter or a race condition in the memory controller.&lt;/p&gt;

&lt;p&gt;Cloud security has no equivalent. Not a smaller equivalent. No equivalent at all.&lt;/p&gt;

&lt;p&gt;The maturity gap isn't measured in features. It's measured in decades. Hardware crossed the verification-first threshold in the 1980s. Cloud security is approaching that threshold now. The question isn't whether cloud will adopt the same discipline. It's whether it will take another 30 years or whether the economics will force the jump sooner.&lt;/p&gt;

&lt;p&gt;The economics just changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why hardware is ahead
&lt;/h2&gt;

&lt;p&gt;The standard explanation "hardware engineers are more rigorous" is wrong. The explanation is economic.&lt;/p&gt;

&lt;p&gt;In hardware, a bug costs $50 million. A tape-out — the process of manufacturing the physical masks for a chip — is irreversible. Once the silicon is fabricated, there's no patch. No hotfix. No rollback. If the chip has a bug, you eat the $50 million fabrication cost, fix the design, and tape out again. Intel's FDIV bug in 1994 cost $475 million in recalls. A single logic error in a production chip can destroy a product line.&lt;/p&gt;

&lt;p&gt;In software, a bug costs a patch. Ship it, find the bug, push a fix. "Move fast and break things" was rational when patches were cheap. The economic incentive for correctness-by-construction was weak because the cost of incorrectness was low.&lt;/p&gt;

&lt;p&gt;That kept hardware ahead. Not superior engineering culture. Superior economic pressure. When a bug costs $50 million, you prove correctness before fabrication. When a bug costs a deploy, you test a few cases and ship.&lt;/p&gt;

&lt;p&gt;Cloud computing inherited software's economics. Configuration errors? Push a fix. Misconfigured security group? Update it. Exposed bucket? Make it private. The patch cost was low enough that "detect and respond" was rational — find the problem after it exists, fix it, move on.&lt;/p&gt;

&lt;p&gt;Until it wasn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed: patches got expensive
&lt;/h2&gt;

&lt;p&gt;Two shifts brought cloud to the same economic forcing function hardware faced decades ago.&lt;/p&gt;

&lt;p&gt;First, the blast radius expanded. A misconfigured S3 bucket isn't a minor incident anymore. Capital One: 100 million customer records. Toyota: five years of customer data exposed. The cost of a cloud configuration error is no longer "push a fix." It's regulatory fines, breach notifications, stock price impact, customer trust erosion. The patch is cheap. The damage the patch can't undo is not.&lt;/p&gt;

&lt;p&gt;Second, AI made the volume problem intractable. AI coding agents generate infrastructure-as-code at a pace no human can review line by line. A Terraform module that creates 200 resources in 30 seconds produces a configuration state that would take hours to audit manually. Without correctness-by-construction at the contract level, the result  hardware engineers would recognize as "shipping untested silicon" — infrastructure that works in the common case and fails in the edge case nobody examined.&lt;/p&gt;

&lt;p&gt;The economics crossed the threshold. The cost of incorrectness now exceeds the cost of verification. Cloud is on hardware's 1980s curve.&lt;/p&gt;

&lt;h2&gt;
  
  
  The testbench: what hardware has and cloud doesn't
&lt;/h2&gt;

&lt;p&gt;In hardware, every design has two artifacts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The DUT (Design Under Test)&lt;/strong&gt; — the actual circuit. The Verilog or VHDL code that describes what the chip does. This is the equivalent of your Terraform, your CDK, your CloudFormation. It describes the thing being built.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Testbench&lt;/strong&gt; — the verification environment that proves the circuit is correct. The testbench defines what "correct" means. It specifies the properties the DUT must satisfy, generates stimulus, checks outputs, and reports violations. The person who writes the testbench is often more senior than the person who writes the Verilog. In hardware culture, the testbench defines correctness. The DUT implements it.&lt;/p&gt;

&lt;p&gt;Cloud has the DUT. Terraform, Pulumi, CDK, CloudFormation etc. These are the Verilog of cloud. They describe the infrastructure.&lt;/p&gt;

&lt;p&gt;Cloud doesn't have the testbench. The CSPM rule library is not a testbench. It's a pattern matcher that checks known-bad configurations after deployment. A testbench doesn't pattern-match against known failures. A testbench specifies what must always be true and verifies the design satisfies it, exhaustively, before fabrication.&lt;/p&gt;

&lt;p&gt;The gap isn't "cloud needs better scanning." The gap is "cloud has no testbench." The scanning tools are post-deployment detection. The equivalent of testing a chip after manufacture by running software on it and hoping the bugs show up. Hardware abandoned that approach in the 1980s because the cost of finding bugs post-fabrication was too high.&lt;/p&gt;

&lt;p&gt;The invariant — a predicate that declares what must always be true about the configuration — is the testbench for cloud. The configuration snapshot is the DUT. The evaluation engine that checks every invariant against the snapshot is the verification run. The CI gate that blocks deployment when an invariant fails is the tape-out hold.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proofs vs. tests: a 30-year head start
&lt;/h2&gt;

&lt;p&gt;Hardware engineers don't test chips by checking a few inputs and hoping the rest work. They prove properties.&lt;/p&gt;

&lt;p&gt;A unit test says: "when I give this function input 42, I get output 7." It checks one case. If you have 100 unit tests, you've checked 100 cases. The other trillion cases are unchecked. You hope the 100 are representative.&lt;/p&gt;

&lt;p&gt;A formal property says: "for ALL possible inputs, the output satisfies this constraint." The verification engine (an SMT solver, a model checker, a formal equivalence checker) doesn't enumerate inputs. It proves the property holds across the entire input space, or it produces a counterexample showing exactly where it fails.&lt;/p&gt;

&lt;p&gt;Z3 — the SMT solver that cloud security tools are now beginning to adopt was built for hardware verification. Microsoft Research created it to prove properties of hardware designs, software protocols, and security policies. The technology is 30+ years mature in the hardware domain. Cloud is adopting tools that hardware validated decades ago.&lt;/p&gt;

&lt;p&gt;The difference in coverage is structural:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;What it checks&lt;/th&gt;
&lt;th&gt;Coverage&lt;/th&gt;
&lt;th&gt;Guarantee&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unit tests&lt;/td&gt;
&lt;td&gt;Specific inputs you chose&lt;/td&gt;
&lt;td&gt;The inputs you tested&lt;/td&gt;
&lt;td&gt;None for untested inputs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integration tests&lt;/td&gt;
&lt;td&gt;Specific scenarios you scripted&lt;/td&gt;
&lt;td&gt;The scenarios you imagined&lt;/td&gt;
&lt;td&gt;None for unimagined scenarios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSPM rule scan&lt;/td&gt;
&lt;td&gt;Specific patterns in the library&lt;/td&gt;
&lt;td&gt;Known-bad patterns&lt;/td&gt;
&lt;td&gt;None for unknown patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formal property&lt;/td&gt;
&lt;td&gt;ALL states that satisfy the precondition&lt;/td&gt;
&lt;td&gt;The entire state space within scope&lt;/td&gt;
&lt;td&gt;Mathematical — counterexample or proof&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Hardware uses the bottom row. Cloud uses the top three. The gap is in the guarantee — hardware gets a proof, cloud gets a sample.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design for Test: the invisible infrastructure
&lt;/h2&gt;

&lt;p&gt;Hardware has a concept called DFT — Design for Test. Engineers add specific pins, scan chains, built-in self-test (BIST) logic, and boundary-scan cells to a chip solely so it can be verified. The chip's functionality doesn't need those elements. The verification does.&lt;/p&gt;

&lt;p&gt;DFT is invisible to the end user. Nobody buying a processor knows about the scan chains. But without them, the chip can't be tested at manufacturing, can't be debugged in the field, and can't be verified post-silicon. DFT is the infrastructure that makes verification possible. It's designed in from day one, not bolted on after the chip is finished.&lt;/p&gt;

&lt;p&gt;Cloud has no equivalent. Cloud infrastructure is built for functionality — serve requests, store data, process events. The verification infrastructure — the ability to take a deterministic snapshot, evaluate invariants against it, produce structured verdicts — is absent by default. It's bolted on after the fact via scanning tools that run periodically against the live environment.&lt;/p&gt;

&lt;p&gt;But the features that make a cloud security tool work as a verification system as opposed to a detection system are DFT features in disguise:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;What the user sees&lt;/th&gt;
&lt;th&gt;What it is (DFT)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JSON Schema validation&lt;/td&gt;
&lt;td&gt;"I can validate output"&lt;/td&gt;
&lt;td&gt;Binary assertion — pass/fail, no ambiguity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exit codes&lt;/td&gt;
&lt;td&gt;"I can branch in CI/CD"&lt;/td&gt;
&lt;td&gt;Control flow without parsing — the scan chain of cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deterministic evaluation&lt;/td&gt;
&lt;td&gt;"Same results every run"&lt;/td&gt;
&lt;td&gt;Run twice, compare, done — reproducible verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured errors&lt;/td&gt;
&lt;td&gt;"I know what to fix"&lt;/td&gt;
&lt;td&gt;Self-correction loop — the diagnostic output of a BIST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Readiness score&lt;/td&gt;
&lt;td&gt;"I know my coverage"&lt;/td&gt;
&lt;td&gt;Objective function to maximize — the DFT coverage metric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reasoning specs&lt;/td&gt;
&lt;td&gt;"I understand the logic"&lt;/td&gt;
&lt;td&gt;Implementation guide + golden answer — the testbench specification&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every feature in the right column is something hardware builds into chips by default. Every feature in the left column is something cloud security tools historically lacked. The cloud security industry built detection (scan for known-bad patterns) without building verification infrastructure (prove properties hold, deterministically, reproducibly, with structured output that machines can consume).&lt;/p&gt;

&lt;h2&gt;
  
  
  The EDA parallel
&lt;/h2&gt;

&lt;p&gt;The semiconductor industry didn't just adopt verification. It built a $15 billion industry to support it.&lt;/p&gt;

&lt;p&gt;EDA companies — Synopsys, Cadence, Siemens EDA — provide the tools that make hardware verification practical: synthesis (translate high-level description to gate-level design), simulation (run the testbench), formal verification (prove properties), timing analysis (verify signals arrive on time), power analysis (verify thermal constraints), and DFT insertion (add test infrastructure automatically).&lt;/p&gt;

&lt;p&gt;No individual hardware company builds these tools in-house. The economics don't support it. Every chip company uses the same EDA ecosystem, the same verification methodologies, the same testbench languages (SystemVerilog, UVM). The verification infrastructure is shared. The designs are proprietary. The competition is in what you build, not in how you verify it.&lt;/p&gt;

&lt;p&gt;Cloud has no equivalent. Every cloud security vendor builds its own detection engine, its own rule format, its own output schema, its own dashboard. There's no shared verification infrastructure. No standard testbench language. No common output format that all tools emit and all dashboards consume.&lt;/p&gt;

&lt;p&gt;The closest analogs are emerging: CEL (Google's Common Expression Language) as a predicate language, SARIF as an output format, SMT-LIB as a reasoning interface, OPA/Rego as a policy language. Each one is a fragment of what would be a cloud EDA. None has been composed into a coherent verification stack the way EDA tools compose into a chip design flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The curve
&lt;/h2&gt;

&lt;p&gt;Every technology domain follows an S-curve of maturity. Hardware verification is on the mature plateau — formal methods are standard practice, EDA is a stable industry, DFT is designed in by default. Cloud security is on the steep part of the curve — the forcing function has arrived (blast radius + AI speed), the tools exist (SMT solvers, expression languages, snapshot-based evaluation), but the discipline hasn't been institutionalized.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Maturity
  │
  │                          ┌──── Hardware (2025) ────── plateau
  │                         ╱
  │                        ╱
  │                       ╱
  │                      ╱
  │         ┌── Cloud (2025) ──── steep climb
  │        ╱
  │       ╱
  │      ╱
  │─────╱───── Hardware (1985) ── forcing function hit
  │    ╱
  └────────────────────────────────────────────── Time
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hardware hit the forcing function in the 1980s (tape-out costs made bugs unaffordable). The industry responded with formal verification, testbench methodology, and DFT. It took roughly 15 years to institutionalize — by 2000, no serious chip shipped without formal verification.&lt;/p&gt;

&lt;p&gt;Cloud hit the forcing function now (breach costs + AI speed made configuration bugs unaffordable). The tools exist — they're literally the same SMT solvers hardware uses. The methodology exists — invariants as the testbench, snapshots as the DUT, CI gate as the tape-out hold. The question is whether the institutionalization takes 15 years or happens faster because the tools are already mature.&lt;/p&gt;

&lt;p&gt;The bet: cloud doesn't need to invent formal verification. It needs to adopt it. The invention happened 30 years ago. The tools are open source. The methodology is documented. The only thing missing is the cultural shift from "detect and respond" to "verify and prevent."&lt;/p&gt;

&lt;p&gt;Hardware made that shift when $50 million bugs forced it. Cloud is making that shift now, because breaches that cost hundreds of millions and AI-generated infrastructure that nobody can manually review have created the same economic forcing function.&lt;/p&gt;

&lt;p&gt;The same shift. The same tools. The same destination. Just 30 years later.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Z3, the SMT solver referenced throughout, was created by Microsoft Research and is freely available. Google CEL is Apache 2.0 licensed. The verification methodology described — invariants evaluated against configuration snapshots in CI — is implemented in open-source tools. The EDA industry analogy draws on the author's analysis of hardware verification workflows; the cloud-to-hardware maturity mapping is the author's framework.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is part of a series on the structural foundations of cloud security. Related: &lt;a href="https://systeminvariant.substack.com/p/the-missing-primitive-in-cloud-security" rel="noopener noreferrer"&gt;"The Missing Primitive Was Discovered in 1977"&lt;/a&gt; (Lamport's safety invariant), &lt;a href="https://dev.to/bala_paranj_059d338e44e7e/four-security-problems-that-dont-need-a-scanner-4ip9"&gt;"Four Security Problems That Don't Need a Scanner"&lt;/a&gt; (problem class to solver mapping), and &lt;a href="https://systeminvariant.substack.com/p/your-scanner-checks-settings-the" rel="noopener noreferrer"&gt;"Your Scanner Checks Settings. The Breach Was a Combination."&lt;/a&gt; (the unit-of-analysis shift).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cloudsecurity</category>
      <category>architecture</category>
      <category>hardware</category>
      <category>engineering</category>
    </item>
    <item>
      <title>Your Cloud Has Attack Paths No Scanner Can Find. Because They Don't Exist in Any Single Resource</title>
      <dc:creator>Bala Paranj</dc:creator>
      <pubDate>Wed, 05 Aug 2026 11:36:04 +0000</pubDate>
      <link>https://dev.to/bala_paranj_059d338e44e7e/your-cloud-has-attack-paths-no-scanner-can-find-because-they-dont-exist-in-any-single-resource-4koi</link>
      <guid>https://dev.to/bala_paranj_059d338e44e7e/your-cloud-has-attack-paths-no-scanner-can-find-because-they-dont-exist-in-any-single-resource-4koi</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;✓ Human-authored analysis; AI used for formatting and proofreading.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most cloud security tools work by checking individual resources against a library of known-bad patterns. Is this bucket public? Is this port open? Is this key unrotated?&lt;/p&gt;

&lt;p&gt;These checks are necessary. They're also insufficient. Because the most dangerous violations don't exist in any single resource. They exist in the RELATIONSHIPS between resources. They're detectable from the configuration structure alone, without any user input, without any tags, without any intent declarations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two classes
&lt;/h2&gt;

&lt;p&gt;Cloud security violations divide into two structurally distinct classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Intent-DEPENDENT (requires user declaration):
    "No public bucket with sensitive data"
    → Needs the user to declare: this bucket IS sensitive
    → Without the declaration, no tool can distinguish a public
       website bucket from a public PII bucket

Intent-INDEPENDENT (detectable from structure alone):
    "No attack path from any public principal to any internal resource"
    → The PATH is the violation. No declaration needed.
    → If anonymous can reach an internal resource through a chain
       of role assumptions, it's exploitable — regardless of what
       the resource contains.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The industry has focused almost entirely on the first class. Tools that check properties of individual resources, optionally enriched by metadata. The second class is larger, more dangerous, and requires zero user setup to detect. The configuration graph is sufficient evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Six structural violation classes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Attack paths (transitive reachability)
&lt;/h3&gt;

&lt;p&gt;Policy A allows User X to update an IAM role. Policy B allows that role to pass a role to a Lambda function. Policy C allows that Lambda to delete objects in an S3 bucket.&lt;/p&gt;

&lt;p&gt;No individual policy is wrong. Each permission makes sense in isolation. The chain creates a path from a non-admin user to a data-deletion action that nobody intended.&lt;/p&gt;

&lt;p&gt;Detection: compute transitive closure over the permission graph. If any path exists from a low-privilege principal to a high-privilege action that isn't explicitly authorized, the path is a violation. Datalog engines are designed for this computation — six lines of relational rules, and the engine finds ALL paths, including compositions nobody cataloged.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Implicit over-permissiveness (equivalent-to-star)
&lt;/h3&gt;

&lt;p&gt;A developer writes a complex policy with conditions, exclusions (&lt;code&gt;NotAction&lt;/code&gt;, &lt;code&gt;NotResource&lt;/code&gt;), and carefully scoped allows. The policy reads like it's restrictive. But the logic of the conditions, evaluated together, mathematically simplifies to &lt;code&gt;Resource: *&lt;/code&gt; or &lt;code&gt;Principal: *&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No human typed the wildcard. The logic is equivalent to it.&lt;/p&gt;

&lt;p&gt;Detection: an SMT solver (like Z3) checks whether the set of conditions covers the entire domain of possibilities. If the allow conditions form a tautology — true for all possible inputs — the resource is effectively public, regardless of how the policy reads. The solver doesn't search for the &lt;code&gt;*&lt;/code&gt; character. It checks whether the MATH produces the same result.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Privilege escalation loops
&lt;/h3&gt;

&lt;p&gt;A principal can modify its own permissions via &lt;code&gt;iam:PassRole&lt;/code&gt;, &lt;code&gt;iam:PutRolePolicy&lt;/code&gt;, or &lt;code&gt;iam:AttachRolePolicy&lt;/code&gt;. Self-modification is structurally dangerous regardless of the principal's intended scope. If you can change what you're allowed to do, the boundary is meaningless.&lt;/p&gt;

&lt;p&gt;Circular trust is Role A trusts Role B, Role B trusts A. It is exploitable regardless of what either role does. The graph topology is the evidence.&lt;/p&gt;

&lt;p&gt;Detection: search the permission graph for cycles and self-referential paths. Any principal with a path from "current permissions" to "modify own permissions" has a privilege escalation vulnerability. The topology tells you everything. No tags needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Policy shadowing (dead deny statements)
&lt;/h3&gt;

&lt;p&gt;A developer writes a Deny statement believing it protects a resource. The Deny says: "deny all access from external accounts."&lt;/p&gt;

&lt;p&gt;But the Allow statement in the same policy (or a resource policy on the target) is structured so that the Deny condition can never be met. The conditions don't overlap. The Deny is mathematically unreachable — a dead branch in the logic tree. The developer thinks security is enforced. The SMT solver proves it isn't.&lt;/p&gt;

&lt;p&gt;Detection: Z3 checks for unsatisfiability. If the Deny condition can never be satisfied given the structure of the Allow statements, the Deny is shadowed. The guardrail exists in the JSON. It doesn't exist in the math.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Ghost references
&lt;/h3&gt;

&lt;p&gt;A security group references a VPC that was deleted. A role trust policy grants access to an account that no longer exists. A bucket policy references a principal ARN that was decommissioned.&lt;/p&gt;

&lt;p&gt;The reference is dangling. The cloud provider's behavior when evaluating a dangling reference is undefined or provider-specific. The resource APPEARS configured, but one of its dependencies was removed. The configuration is in an inconsistent state.&lt;/p&gt;

&lt;p&gt;Detection: cross-reference every resource identifier in the snapshot. If a reference target doesn't exist in the snapshot, the reference is a ghost. No intent needed. A dangling reference is wrong by definition.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Network reachability (unintentional paths)
&lt;/h3&gt;

&lt;p&gt;AWS's own internal tool Tiros (built on Z3) converts route tables, security groups, and network ACLs into a graph of logic formulas. The solver asks: "does there exist any packet, any combination of source IP and port that can reach this private subnet from the internet gateway?"&lt;/p&gt;

&lt;p&gt;If the solver returns SAT (satisfiable), it provides the exact packet headers and the route path that reaches the private subnet. A private subnet reachable from 0.0.0.0/0 is wrong by definition. No intent declaration required.&lt;/p&gt;

&lt;p&gt;Detection: model the network topology as logical constraints. Let the solver search for any satisfying assignment. If one exists, an unintentional network path exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why pattern matching can't find these
&lt;/h2&gt;

&lt;p&gt;A scanner searches for patterns in text. The &lt;code&gt;*&lt;/code&gt; character, the &lt;code&gt;0.0.0.0/0&lt;/code&gt; CIDR, the &lt;code&gt;"Effect": "Allow"&lt;/code&gt; string. This catches the obvious cases. It misses the mathematical ones.&lt;/p&gt;

&lt;p&gt;An SMT solver understands things pattern matching can't:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Condition logic.&lt;/strong&gt; &lt;code&gt;StringLike&lt;/code&gt; and &lt;code&gt;StringEquals&lt;/code&gt; can overlap in ways that create unintended access. A scanner sees two separate conditions. The solver evaluates their intersection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IP math.&lt;/strong&gt; 10.0.0.0/24 is contained within 10.0.0.0/16. A "restrictive" security group that allows a /24 in a VPC that already allows the /16 isn't restrictive. It's redundant. The solver computes CIDR containment. A scanner compares strings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Variable substitution.&lt;/strong&gt; If a user with a specific name matches a condition pattern the author didn't anticipate, they bypass a restriction. The solver checks ALL possible variable assignments. A scanner checks the ones it's been told about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Policy composition.&lt;/strong&gt; Identity policy + resource policy + SCP + permission boundary, evaluated in deny-override order. The effective permission is a function of all four layers. No individual policy statement reveals it. The solver evaluates the composition. A scanner evaluates each policy in isolation.&lt;/p&gt;

&lt;p&gt;Pattern matching asks "does this text look bad?" Formal verification asks "does there exist ANY input that reaches an unsafe state?"&lt;/p&gt;

&lt;p&gt;The first catches what's been cataloged. The second catches what's mathematically possible including compositions nobody anticipated.&lt;/p&gt;

&lt;h2&gt;
  
  
  The day-zero value proposition
&lt;/h2&gt;

&lt;p&gt;This creates a product adoption path with zero user setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Day 0 (zero configuration):
    Point the tool at a configuration snapshot.
    No tags. No intent declarations. No metadata. No setup.

    Structural findings delivered immediately:
    → Attack paths (transitive reachability through IAM graph)
    → Ghost references (dangling resource identifiers)  
    → Privilege escalation paths (self-modification chains)
    → Policy shadowing (dead deny statements)
    → Implicit over-permissiveness (equivalent-to-star)
    → Network reachability (unintended internet paths)

Day 30 (intent declared):
    User starts tagging resources: Sensitivity: High, Framework: HIPAA.

    Intent-dependent findings activate:
    → Public bucket + PII tag = violation
    → Unencrypted database + HIPAA tag = violation
    → Broad access + sensitive-service archetype = violation

    Richer analysis. Deeper findings.
    But the structural findings were already there on day 0.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user doesn't need to understand intent declarations, tagging strategies, or compliance frameworks to start getting results. The structural detections require nothing except the snapshot. The intent layer deepens the analysis when the user is ready. Both classes are valuable. Both are correct. The structural class needs zero setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  The limitation: the snapshot is never 100%
&lt;/h2&gt;

&lt;p&gt;There's a boundary this approach must acknowledge. The structural analysis is only as complete as the snapshot and in real organizations, the snapshot is never the full picture.&lt;/p&gt;

&lt;p&gt;In large enterprises, cloud configuration arrives through many channels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure as Code&lt;/strong&gt; (Terraform, CloudFormation, Pulumi) — captured&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud console clicks&lt;/strong&gt; — reflected in API state, but no audit trail of intent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scripts and automation&lt;/strong&gt; — may or may not produce API-observable state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual hotfixes&lt;/strong&gt; — applied directly, often undocumented&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legacy resources&lt;/strong&gt; — pre-date the current tooling, partially configured&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-account resources&lt;/strong&gt; — visible only if the collector has cross-account access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cloud APIs reflect the CURRENT state of what's been configured through them. They don't reflect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resources configured through channels the APIs don't fully expose&lt;/li&gt;
&lt;li&gt;Configuration that was intended but never applied (a Terraform plan that was never run)&lt;/li&gt;
&lt;li&gt;Resources in accounts the collector doesn't have access to&lt;/li&gt;
&lt;li&gt;Ephemeral infrastructure that existed and was deleted before the snapshot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The analysis covers what's observable through the cloud provider's APIs at the moment the snapshot was taken. In a well-managed organization with consistent IaC practices, that's close to complete. In a large enterprise with decades of accumulated infrastructure, multiple teams, console-driven workflows, and partial IaC adoption. It's less than 100%.&lt;/p&gt;

&lt;p&gt;This means the structural findings are LOWER BOUNDS, not exhaustive inventories. "We found 3 attack paths" means at least 3 exist. It doesn't mean only 3 exist. Unobserved resources may harbor additional paths the snapshot can't see.&lt;/p&gt;

&lt;p&gt;The tool should state this in every output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Analysis scope: 847 resources across 12 services observed in snapshot.
Coverage: This analysis reflects API-observable state at capture time.
          Resources not visible to the collector are not evaluated.
          Findings are lower bounds, not exhaustive inventories.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Transparency about coverage limits is as important as the findings themselves. A tool that says "you're safe" when it only saw 60% of the environment is more dangerous than a tool that says "we found 3 problems in the 60% we could observe." The second is honest. The first creates false confidence.&lt;/p&gt;

&lt;p&gt;This is also why the &lt;code&gt;stave readiness&lt;/code&gt; and &lt;code&gt;stave gaps&lt;/code&gt; commands exist. They report what the observation contract covers and what it doesn't. The coverage gap is a measured, tracked metric, not a hidden assumption.&lt;/p&gt;

&lt;h2&gt;
  
  
  The deeper insight
&lt;/h2&gt;

&lt;p&gt;The adversarial review of the specification-first platform article asked: "What if users don't tag resources correctly? What about classification circularity? What about tagging debt?"&lt;/p&gt;

&lt;p&gt;Every question assumes the platform requires user input to produce value. For the intent-dependent class, that's true. For the structural class, it's false. The six violation classes described here need nothing from the user. The configuration graph is the evidence. The solver asks what's mathematically possible. The user provides a snapshot. The math provides the findings.&lt;/p&gt;

&lt;p&gt;This means the strongest findings in a security assessment — attack paths, privilege escalation chains, ghost references, policy shadowing are the ones that require the LEAST user effort. The most dangerous violations are the most automatically detectable. The gap between "install the tool" and "see your first critical finding" is one command.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article is part of a series on the structural foundations of cloud security. Related: &lt;a href="https://systeminvariant.substack.com/p/your-scanner-checks-settings-the" rel="noopener noreferrer"&gt;"Your Scanner Checks Settings. The Breach Was a Combination."&lt;/a&gt; (the unit-of-analysis shift), &lt;a href="https://dev.to/bala_paranj_059d338e44e7e/the-next-generation-platform-wont-track-configurations-itll-track-why-they-matter-g02"&gt;"The Next Platform Won't Track Configurations."&lt;/a&gt; (the specification-first architecture), and The Root Cause Nobody Names: AWS Configuration IS First-Order Logic" (why engineers misconfigure cloud resources).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The structural violation classes described here are implemented in &lt;a href="https://github.com/sufield/stave" rel="noopener noreferrer"&gt;Stave&lt;/a&gt; — an open-source cloud security reasoning engine. Apache 2.0.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cloudsecurity</category>
      <category>aws</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>Every AWS Complaint is an Unwritten Invariant</title>
      <dc:creator>Bala Paranj</dc:creator>
      <pubDate>Tue, 04 Aug 2026 08:54:26 +0000</pubDate>
      <link>https://dev.to/bala_paranj_059d338e44e7e/every-aws-complaint-is-an-unwritten-invariant-41pk</link>
      <guid>https://dev.to/bala_paranj_059d338e44e7e/every-aws-complaint-is-an-unwritten-invariant-41pk</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;✓ Human-authored analysis; AI used for formatting and proofreading.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Chris Farris published a post in December 2024 titled "How AWS needs to change." It's complaints about AWS's security culture, ranging from inconsistent IAM action naming to the lack of free security telemetry. It's informed and comes from an experienced cloud security professional's work at the coalface.&lt;/p&gt;

&lt;p&gt;Every practitioner complaint describes a configuration state the customer can observe is a specification waiting to be written.&lt;/p&gt;

&lt;p&gt;Farris has thirteen complaints. Seven require AWS to change — pricing decisions, API design, release culture. Those are advocacy, which is valuable. But a customer can't verify whether AWS has acted on it. The other six describe configuration states that exist in the customer's own environment and can be checked from a snapshot today. Those aren't complaints. Those are invariants.&lt;/p&gt;

&lt;h2&gt;
  
  
  The transformation
&lt;/h2&gt;

&lt;p&gt;An invariant is a property that must always be true. When a practitioner says "AWS shouldn't let customers do X," the invariant is: "X is not true in my environment." When they say "AWS should enable Y by default," the invariant is: "Y is enabled." The complaint names the property. The invariant states it. The control checks it.&lt;/p&gt;

&lt;p&gt;The transformation is mechanical: take the complaint, strip the "AWS should," replace it with "my environment does," and you have a verifiable property. The question is whether the verification can be done from configuration data you already have.&lt;/p&gt;

&lt;p&gt;Walk through six of Farris's thirteen and watch the transformation happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Not all features are for all customers
&lt;/h2&gt;

&lt;p&gt;Farris references &lt;code&gt;kms:ImportKeyMaterial&lt;/code&gt; as an example of a feature most customers shouldn't have access. He links to his ransomware research showing that an attacker who can import external key material into KMS can encrypt your data with a key only they control. The data is not recoverable without the attacker's key material. AWS makes this action available to every account by default.&lt;/p&gt;

&lt;p&gt;The complaint: AWS should restrict dangerous actions by default.&lt;/p&gt;

&lt;p&gt;The invariant: &lt;code&gt;kms:ImportKeyMaterial&lt;/code&gt; is denied by SCP at the organization root.&lt;/p&gt;

&lt;p&gt;This is a single predicate evaluated against a single document. Does any SCP in the hierarchy contain an explicit Deny for this action? The answer is yes or no. The consequence of "no" has a documented precedent with a dollar figure.&lt;/p&gt;

&lt;p&gt;The same transformation applies to every action Farris lists: &lt;code&gt;organizations:LeaveOrganization&lt;/code&gt; (an attacker escapes all organizational controls in one API call), &lt;code&gt;account:CloseAccount&lt;/code&gt; (destructive and irreversible), &lt;code&gt;iam:CreateUser&lt;/code&gt; (permanent credentials in a world that should use session-based roles). Each one is a complaint about AWS's defaults. Each one is a verifiable invariant about your own SCP coverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Security services should be a promise
&lt;/h2&gt;

&lt;p&gt;Farris documents that AWS launched the Stockholm region (eu-north-1) in December 2018 without GuardDuty. GuardDuty became available 95 days later. During those 95 days, any customer who deployed to that region had no managed threat detection.&lt;/p&gt;

&lt;p&gt;The complaint: AWS shouldn't launch regions without security services.&lt;/p&gt;

&lt;p&gt;The invariant: every region with active resources has a GuardDuty detector enabled.&lt;/p&gt;

&lt;p&gt;This requires two facts from a configuration snapshot: the set of regions where resources exist, and the set of regions where GuardDuty detectors are active. The gap between those two sets is the blind spot. The customer can't control when AWS adds GuardDuty to a region. But they can verify that they haven't deployed resources to a region without it.&lt;/p&gt;

&lt;p&gt;The same pattern applies to CloudTrail. Farris's point #12 says all IAM actions must be logged. The invariant: no CloudTrail trail has exclusion selectors filtering management events, and every active region is covered by at least one trail. These are configuration properties. They're observable from a snapshot. Their violation creates a logging blind spot that an attacker can exploit.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Secure defaults are critical
&lt;/h2&gt;

&lt;p&gt;Farris notes that the EC2 Launch Wizard defaults to 0.0.0.0/0 for security group ingress. Every cloud security tool checks for open security groups. That's the obvious invariant and it's been in every scanner since 2015.&lt;/p&gt;

&lt;p&gt;The less obvious invariant came from a different announcement in the same month: VPC Block Public Access. AWS shipped a declarative control that prevents resources in public subnets from receiving internet traffic and restricts outbound to specific NAT gateways. The invariant isn't "no open security groups." The invariant is: VPC Block Public Access is enabled at the account level, the mode is bidirectional, and any exclusions are scoped to specific subnets rather than entire VPCs.&lt;/p&gt;

&lt;p&gt;That's a three-part predicate, and each part catches a different failure mode. Account-level enablement is the baseline. Bidirectional mode prevents data exfiltration via egress (ingress-only BPA still allows outbound). Subnet-scoped exclusions prevent a VPC-level exception from defeating the entire control.&lt;/p&gt;

&lt;p&gt;There's a fourth invariant hiding behind BPA: is it enforced via a declarative policy at the organizational level, or just set per-account? Per-account settings can be changed by any account administrator. Organizational declarative policies can't. The invariant is: a declarative policy enforces VPC BPA organization-wide.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Stop hiding behind Shared Responsibility
&lt;/h2&gt;

&lt;p&gt;Farris's most pointed critique is about IMDSv2 and the Capital One breach. AWS knew IMDSv1 was exploitable. They shipped IMDSv2 as an opt-in. Capital One didn't opt in. 100 million records were exfiltrated.&lt;/p&gt;

&lt;p&gt;The invariant exists and has existed since 2019: every EC2 instance requires IMDSv2. Every scanner checks it. But the invariant alone isn't the interesting thing. What's interesting is what happens when you combine it with other invariants that weren't being checked.&lt;/p&gt;

&lt;p&gt;The Capital One breach required three conditions: IMDSv1 enabled, public IP on the instance, and an IAM role with permissions reaching sensitive data. Each condition is an invariant violation. The compound finding — all three true simultaneously is a named incident shape. Checking whether two of the three are true while one is holding is a distance-one finding: you are one configuration change away from the exact pattern that cost $300 million.&lt;/p&gt;

&lt;p&gt;This is where individual invariants become compound invariants, and where the transformation from complaint to specification gets its leverage. Farris's complaint about Shared Responsibility is about one invariant violation (IMDSv1). The compound finding is about three invariant violations intersecting. The distance-one finding is about two violations plus one gate. Each layer requires the previous one to exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Customers don't know how their cloud works
&lt;/h2&gt;

&lt;p&gt;Farris asks AWS for Audit-Allow and Audit-Deny IAM effects. The ability to see what an SCP would do before applying it in production. He shouldn't need Splunk or Chronicle to figure out whether a new SCP will break his company.&lt;/p&gt;

&lt;p&gt;AWS hasn't built it. But the invariant underneath Farris's request is built. Before applying an organizational policy, the customer knows which currently-permitted actions it would deny.&lt;/p&gt;

&lt;p&gt;That's a satisfiability problem. Given a proposed SCP deny statement, and the set of IAM policies in the organization, which currently-permitted actions would become denied? You translate the SCP into a formal representation, translate the IAM policies into the same representation, and compute the intersection. The answer is the set of actions that the SCP would break.&lt;/p&gt;

&lt;p&gt;This is a what-if analysis. But it's derived from the same transformation: Farris states a need ("I need to know what this SCP would do"), and the specification is the formal property that answers it. The fact that AWS hasn't built the feature doesn't mean the analysis is impossible. It means the customer needs a tool that can reason about policy interactions formally rather than waiting for AWS to add a button.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The false positive that erodes trust
&lt;/h2&gt;

&lt;p&gt;The most subtle invariant in Farris's posts isn't stated as a complaint. It's a parenthetical in his discussion of centralized root management: "We just need the CSPM community to catch up and not report missing MFA on accounts without credentials."&lt;/p&gt;

&lt;p&gt;AWS shipped centralized root access management in November 2024. It removes root credentials from member accounts entirely, replacing them with &lt;code&gt;sts:AssumeRoot&lt;/code&gt; from the management account. When this feature is enabled, member accounts have no root user, no root password, no root access keys. Flagging "missing root MFA" on these accounts is a false positive.&lt;/p&gt;

&lt;p&gt;Every CSPM tool on the market generates this false positive today. The invariant isn't "root user has MFA configured." The invariant is: "root user has MFA configured OR centralized root management is enabled for this account." The existing check is incomplete. The false positive erodes trust in the tool, which means that when the tool flags something real, the operator is more likely to ignore it.&lt;/p&gt;

&lt;p&gt;This is the inverse of the SILENT_RISK pattern. SILENT_RISK is a false negative: the control passes because the data to detect the violation was missing. This is a false positive: the control fails because the data to recognize the exemption is missing. Both are failures of context. Both corrode signal quality. Both are fixed by the same discipline: write the predicate to account for the full condition, not just the obvious one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;Every one of these transformations followed the same steps:&lt;/p&gt;

&lt;p&gt;The practitioner states a complaint about how cloud security should work. Inside the complaint is a property that must always be true. The property can be expressed as a predicate over configuration data. The predicate can be evaluated against a snapshot.&lt;/p&gt;

&lt;p&gt;The practitioner's value is naming the property. The engineering value is making it checkable. Neither is sufficient without the other. Farris's 95-day GuardDuty gap in Stockholm is meaningless as an invariant if nobody writes the predicate. The predicate is meaningless without the domain knowledge to know that GuardDuty's absence in an active region is a problem.&lt;/p&gt;

&lt;p&gt;This is why the best security control catalogs are derived from practitioner complaints, not from compliance frameworks. Compliance frameworks tell you what categories to care about. Practitioners tell you what specific configuration states cause incidents. The framework says "enable logging." The practitioner says "CloudTrail exclusion selectors filtering IAM actions will get you breached." The first is a checkbox. The second is an invariant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Converting Complaints to Invariants
&lt;/h2&gt;

&lt;p&gt;Next time you read a cloud security post that says "AWS should" or "customers need to" or "this is a failure of Shared Responsibility," try the transformation:&lt;/p&gt;

&lt;p&gt;Strip the "should." State the property. Check whether it's observable from a configuration snapshot. If it is, you have an invariant. Write it down. Make it checkable. If the post names an incident, connect the invariant to the incident shape, because that connection turns a compliance checkbox into a risk forecast.&lt;/p&gt;

&lt;p&gt;The best invariants are hiding in the complaints of practitioners who have seen enough incidents to know what goes wrong. They just haven't been written as specifications yet.&lt;/p&gt;

&lt;p&gt;Every rant is an unwritten invariant. Write it down.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>cloudsecurity</category>
      <category>devops</category>
    </item>
    <item>
      <title>Common Mode Failure: The Blast Radius Nobody Measures</title>
      <dc:creator>Bala Paranj</dc:creator>
      <pubDate>Mon, 03 Aug 2026 12:34:42 +0000</pubDate>
      <link>https://dev.to/bala_paranj_059d338e44e7e/common-mode-failure-the-blast-radius-nobody-measures-k6h</link>
      <guid>https://dev.to/bala_paranj_059d338e44e7e/common-mode-failure-the-blast-radius-nobody-measures-k6h</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;✓ Human-authored analysis; AI used for formatting and proofreading.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your compliance dashboard says 100% of S3 buckets are encrypted. Your audit report says every RDS database uses KMS encryption. Your security scanner reports zero encryption findings.&lt;/p&gt;

&lt;p&gt;All 23 resources are encrypted with the same key.&lt;/p&gt;

&lt;p&gt;Nobody checked.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Happens
&lt;/h2&gt;

&lt;p&gt;Nobody plans a common mode failure. It accumulates.&lt;/p&gt;

&lt;p&gt;Day 1: a team creates a KMS key for a new project. They encrypt the first S3 bucket. Correct, responsible, compliant.&lt;/p&gt;

&lt;p&gt;Day 30: a new bucket is needed for the same project. "Use the same key — it's already set up, the key policy already grants access, one less thing to configure." Reasonable.&lt;/p&gt;

&lt;p&gt;Day 90: an RDS database is added. "Use the same key — one key policy to manage instead of two." Faster.&lt;/p&gt;

&lt;p&gt;Day 180: EBS volumes for new EC2 instances. "Same key." Nobody questions it. The team has shipped three features this quarter. Key management is not on the roadmap.&lt;/p&gt;

&lt;p&gt;Day 365: 23 resources depend on one KMS key. 12 S3 buckets. 6 RDS databases. 3 EBS volumes. 2 Secrets Manager secrets. Nobody planned this. Each decision was individually reasonable. The total is a single point of failure that nobody has measured.&lt;/p&gt;

&lt;p&gt;Day 400: a Terraform update replaces the key policy. The developer included the application roles but forgot to include &lt;code&gt;kms:PutKeyPolicy&lt;/code&gt; for the admin role. The key is now orphaned. It exists, nobody can modify its policy, and the application roles that were in the old policy are gone. None of the 23 resources can decrypt their data. The S3 buckets return &lt;code&gt;AccessDenied&lt;/code&gt; on every &lt;code&gt;GetObject&lt;/code&gt; call. The RDS databases transition to &lt;code&gt;inaccessible-encryption-credentials&lt;/code&gt; and stop. The EBS volumes won't mount. The secrets can't be read.&lt;/p&gt;

&lt;p&gt;Twenty-three resources down. One root cause. One key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Every Tool Misses It
&lt;/h2&gt;

&lt;p&gt;Every security tool evaluates resources individually.&lt;/p&gt;

&lt;p&gt;"Is this S3 bucket encrypted?" Yes. ✅&lt;/p&gt;

&lt;p&gt;"Is this RDS database encrypted?" Yes. ✅&lt;/p&gt;

&lt;p&gt;"Is this EBS volume encrypted?" Yes. ✅&lt;/p&gt;

&lt;p&gt;Twenty-three checks. Twenty-three passes. Zero findings. The audit is green. The compliance report looks good. The risk is invisible. Because the risk isn't in any individual resource. The risk is in the relationship between resources. The risk is in the graph, not the node.&lt;/p&gt;

&lt;p&gt;No tool asks: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"How many resources share this KMS key?" &lt;/li&gt;
&lt;li&gt;"What is the blast radius if this key becomes unavailable?" &lt;/li&gt;
&lt;li&gt;"Is this key a single point of failure?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The individual configuration is correct. The shared dependency is the risk. The shared dependency is invisible to every per-resource scanner on the market.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Failure Modes
&lt;/h2&gt;

&lt;p&gt;A shared KMS key creates three distinct failure modes, each with a different trigger and a different blast radius.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure mode 1: Availability — key disruption.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The key is disabled, scheduled for deletion, or its key policy is modified to remove access for the roles that need it. Every resource encrypted with that key loses the ability to read or write data. The blast radius is every resource that references the key's ARN.&lt;/p&gt;

&lt;p&gt;IAM policy cleanups routinely tighten permissions without checking which KMS key policies reference the modified roles. A departing employee's access is revoked, and the automation that revokes it doesn't know that the employee's role was the only principal with &lt;code&gt;kms:Decrypt&lt;/code&gt; in the key policy for the production encryption key. The key still exists. Nobody can use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure mode 2: Confidentiality — key compromise.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An attacker who obtains &lt;code&gt;kms:Decrypt&lt;/code&gt; permission for one key gains access to the data in every resource encrypted with that key. If 12 S3 buckets share one key, compromising access to that key exposes all 12 buckets — not just the one the attacker targeted.&lt;/p&gt;

&lt;p&gt;The Capital One breach demonstrated this pattern at scale. The compromised role had access to S3 objects encrypted with keys it could decrypt. The blast radius wasn't one bucket. It was every bucket the key policy permitted — over 700.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure mode 3: Compliance — blast radius exceeds certification scope.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A compliance framework certifies that specific resources meet specific controls. If a key shared across 23 resources is involved in a compliance incident, the blast radius of the investigation expands to all 23 resources. Even if only one was in scope for the original certification. The compliance team must now determine whether 22 additional resources were affected, re-certify the scope, and explain to the auditor why a single key was shared across certified and uncertified resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure mode 4: Throttling — unrelated services die together.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS KMS has request quotas — typically 10,000 to 50,000 cryptographic operations per second depending on the region and key type. If 23 resources share one key, they share the same quota. A traffic spike in Service A (S3 bulk uploads encrypting thousands of objects per second) consumes the quota that Service B (RDS) needs to sustain database connections. RDS returns &lt;code&gt;ThrottlingException&lt;/code&gt; on its decrypt calls. The database can't perform cryptographic operations. Connections fail. The database goes down, because an unrelated S3 bulk upload consumed the shared key's quota.&lt;/p&gt;

&lt;p&gt;The services have no architectural relationship. They don't communicate. They don't share a VPC or a security group. They share a KMS key. That shared key couples their availability through an invisible throttling ceiling that no monitoring dashboard shows. Because the throttling is attributed to the KMS API, not to the relationship between the services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The KMS Grants dimension amplifies all four failure modes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In large-scale AWS environments, services like EBS and RDS don't use the key policy directly. They use KMS Grants, temporary permissions the service creates when attaching an encrypted volume or starting an encrypted database. An automated cleanup script looking for "stale grants" might retire grants for 23 active resources at once. A single &lt;code&gt;RevokeGrant&lt;/code&gt; or &lt;code&gt;RetireGrant&lt;/code&gt; call removes access for the specific service-to-key relationship. If 23 services depend on grants to the same key, a cleanup script that doesn't understand the dependency graph can revoke all 23 grants in one batch. The same common mode failure triggered by automation that was supposed to improve security posture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cost Counter-Argument
&lt;/h2&gt;

&lt;p&gt;Why do teams share keys?&lt;/p&gt;

&lt;p&gt;Each AWS KMS Customer Managed Key costs $1.00 per month. For a startup with 23 resources, the choice is $1/month (one shared key) versus $4/month (one key per service) versus $23/month (one key per resource). The cost difference is trivial.&lt;/p&gt;

&lt;p&gt;But for an organization with 500 microservices, the choice is $1/month versus $500/month. At scale, the cost becomes a line item that a manager questions: "Why are we paying $500/month for encryption keys when $1 works?"&lt;/p&gt;

&lt;p&gt;The answer: "Is $499/month worth the insurance against a total infrastructure blackout?" One shared key means one incident takes everything down. Five hundred dedicated keys means one incident takes down one service. The $499 buys blast radius isolation. The same isolation that the organization spends millions on for network segmentation, account separation, and availability zones. But nobody applies that thinking to encryption keys because nobody measures the blast radius of a shared key.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern is Not Limited to KMS
&lt;/h2&gt;

&lt;p&gt;KMS is the most visible example because encryption is universally mandated. But the common mode failure pattern appears wherever resources share a dependency that can fail independently of the resources themselves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared IAM execution role:&lt;/strong&gt; five Lambda functions use the same execution role. The role's permissions are tightened. All five functions fail simultaneously. The developer who tightened the permissions was fixing a finding on one function and didn't know four others shared the role.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared VPC:&lt;/strong&gt; every production service runs in one VPC. A route table misconfiguration, a CIDR conflict from a VPC peering connection, or a NACL rule change affects every service in the VPC simultaneously. The blast radius is the entire production environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared security group:&lt;/strong&gt; eight EC2 instances reference the same security group. A rule change intended for one instance affects all eight. The developer modified the security group for "the web server" without knowing that "the database server" and six other instances share it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared S3 bucket for logs:&lt;/strong&gt; every service writes logs to one bucket. The bucket policy is modified. Every service loses logging simultaneously. The CloudTrail trail that would have recorded what went wrong is also writing to the same bucket. The audit trail is gone.&lt;/p&gt;

&lt;p&gt;In every case, the individual resource is correctly configured. The shared dependency is the risk. The shared dependency is invisible to per-resource evaluation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Specification is Missing
&lt;/h2&gt;

&lt;p&gt;This is the same structural gap described in the Missing Specification series: mechanism without meaning. The compliance framework says "encrypt at rest." It doesn't say "don't encrypt everything with the same key." The specification tells the team WHAT to do. It doesn't tell them HOW MANY shared dependencies is too many.&lt;/p&gt;

&lt;p&gt;The missing declaration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# What nobody writes:&lt;/span&gt;
&lt;span class="na"&gt;dependency_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;kms_keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;max_resources_per_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="na"&gt;critical_resources_require_dedicated_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;cross_service_sharing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prohibited&lt;/span&gt;  &lt;span class="c1"&gt;# S3 and RDS must not share a key&lt;/span&gt;

  &lt;span class="na"&gt;iam_roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;max_functions_per_execution_role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
    &lt;span class="na"&gt;production_functions_require_dedicated_role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;vpcs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;max_services_per_vpc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;critical_services_require_dedicated_subnet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this declaration existed, a tool could evaluate it: "This KMS key is shared across 23 resources, exceeding the maximum of 5." The finding would be specific, actionable, and quantified. Without the declaration, the team doesn't know they have a problem until the key fails and 23 resources go down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stewart Brand's Shearing Layers
&lt;/h2&gt;

&lt;p&gt;The common mode failure pattern is a shearing layer violation. The dependency (KMS key) changes at a different rate than the resources that use it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;KMS key policy (changes rarely — key management is not on the sprint board)
  → resources using the key (change frequently — new buckets, new databases, new volumes)
  → the count of resources sharing the key (grows monotonically — resources are added, never migrated to a new key)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dependency is static. The resource count grows. The gap between them is the blast radius and it grows monotonically because migrating an encrypted resource to a new key is expensive (decrypt with old key, re-encrypt with new key, update all references). Nobody does it unless forced. So the count only goes up.&lt;/p&gt;

&lt;p&gt;This is the same pattern as IAM privilege creep: permissions are granted and never revoked because revoking might break something. Dependencies are shared and never separated because separating is expensive. In both cases, the risk accumulates silently because the system provides no signal that it's accumulating. The resource works fine with a shared key. It works fine with a dedicated key. The system doesn't care. The system doesn't distinguish between "encrypted with a dedicated key" and "encrypted with a key shared by 22 other resources." Both pass the audit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graph Analysis
&lt;/h2&gt;

&lt;p&gt;The evaluation requires graph analysis not per-resource property checks. A per-resource check sees: "this bucket is encrypted with key X." A graph analysis sees: "key X is referenced by 23 resources across 4 services, and if key X becomes unavailable, the blast radius is 23 resources."&lt;/p&gt;

&lt;p&gt;The check is a group-by-and-count operation on the dependency graph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;For each dependency type (KMS key, IAM role, VPC, security group):
  1. Collect all resources that reference this dependency
  2. Group by dependency identifier (ARN or ID)
  3. Count resources per dependency
  4. Flag when count exceeds threshold
  5. Report: which dependency, how many dependents, blast radius
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FINDING: Common mode failure — KMS key shared across 23 resources

  Dependency: arn:aws:kms:us-east-1:123456789012:key/abc-123
  Type: KMS key
  Dependents: 23
    S3 buckets: 12 (bucket/data-*, bucket/logs-*, bucket/exports-*)
    RDS databases: 6 (db/orders, db/users, db/analytics, ...)
    EBS volumes: 3 (vol-abc, vol-def, vol-ghi)
    Secrets Manager: 2 (secret/db-creds, secret/api-key)

  Blast radius: if this key is disabled or compromised, 
  23 resources across 4 services lose access to their data.

  Recommendation: create dedicated keys per service 
  (one for S3, one for RDS, one for EBS, one for Secrets Manager).
  Minimum improvement: reduce from 1 shared key to 4 service-scoped keys.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This finding is invisible to every per-resource scanner. It's visible only to a tool that evaluates the graph of relationships between resources — the edges, not just the nodes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Broader Lesson
&lt;/h2&gt;

&lt;p&gt;Common mode failure is the operational face of every thesis in the Missing Specification series. The team encrypted everything but never specified the isolation boundary. The team used IAM roles but never specified the sharing boundary. The team built in one VPC but never specified the blast radius boundary.&lt;/p&gt;

&lt;p&gt;In each case, the mechanism was correct. The specification was missing. The risk accumulated silently because the system provides no signal that it's accumulating. Per-resource tools see the nodes and approve them. Nobody sees the edges.&lt;/p&gt;

&lt;p&gt;The question is not "is this resource correctly configured?" Every resource is correctly configured. The question is "how many other resources fail when this dependency fails?" That question requires the graph. The graph requires compound analysis. Compound analysis requires an engine that reasons about relationships, not just properties.&lt;/p&gt;

&lt;p&gt;Every compliance framework mandates encryption. No compliance framework mandates encryption isolation. The audit that checks "is it encrypted?" passes. The audit that checks "what's the blast radius of your encryption key?" doesn't exist because the tool to answer it hasn't existed.&lt;/p&gt;

&lt;p&gt;Until now.&lt;/p&gt;

</description>
      <category>cloudsecurity</category>
      <category>aws</category>
      <category>security</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Your CI Pipeline Has a Security Gap That Isn't a Vulnerability</title>
      <dc:creator>Bala Paranj</dc:creator>
      <pubDate>Sun, 02 Aug 2026 08:36:38 +0000</pubDate>
      <link>https://dev.to/bala_paranj_059d338e44e7e/your-ci-pipeline-has-a-security-gap-that-isnt-a-vulnerability-27ec</link>
      <guid>https://dev.to/bala_paranj_059d338e44e7e/your-ci-pipeline-has-a-security-gap-that-isnt-a-vulnerability-27ec</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;✓ Human-authored analysis; AI used for formatting and proofreading.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Your CI pipeline probably runs SAST. It probably runs SCA. It might scan container images. It might check for secrets in commits. If you're thorough, it runs Checkov or tfsec against your Terraform plans.&lt;/p&gt;

&lt;p&gt;All of these check the artifact. None of them check what the artifact deploys into.&lt;/p&gt;

&lt;p&gt;A CI pipeline that passes every security gate can deploy perfectly secure code into a catastrophically misconfigured environment. The code has no vulnerabilities. The dependencies are patched. The container image is clean. The IAM role the pipeline uses to deploy has AdministratorAccess, trusts any GitHub repo without subject restrictions, and can reach every S3 bucket in the account.&lt;/p&gt;

&lt;p&gt;That's not a vulnerability. No CVE will be assigned. No scanner will flag it. No SAST tool will find it. It's a configuration state. It's the root cause of 19% of documented cloud security breaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap shift-left created
&lt;/h2&gt;

&lt;p&gt;Shift-left moved vulnerability detection earlier in the development lifecycle. That was the right idea. Catching a SQL injection in a pull request is cheaper than catching it in production. Catching a vulnerable dependency before merge is cheaper than patching it after deployment.&lt;/p&gt;

&lt;p&gt;But shift-left moved one kind of detection left code vulnerabilities and left another kind entirely unshifted: the configuration of the deployment target. The CI pipeline knows everything about the artifact it's building. It knows nothing about the environment it's deploying into.&lt;/p&gt;

&lt;p&gt;This creates a specific blind spot. The pipeline checks whether the Terraform plan creates a public S3 bucket (good). It does not check whether the IAM role executing the Terraform plan can create resources in any account in the organization (bad). The pipeline checks whether the Docker image has a known CVE (good). It does not check whether the ECS task role the container will assume has access to production databases (bad). The pipeline checks whether secrets are committed to the repository (good). It does not check whether the OIDC federation that authenticates the pipeline to AWS allows any repository in the GitHub organization or any repository on GitHub to assume the deployment role (bad).&lt;/p&gt;

&lt;p&gt;The artifact is secure. The pipeline is not. The pipeline's misconfiguration is invisible to every tool in the pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three breaches, one pattern
&lt;/h2&gt;

&lt;p&gt;In the breaches.cloud incident database maintained by Chris Farris, three of sixteen documented cloud breaches share the same root cause: CI/CD systems with long-term credentials or misconfigured trust relationships that gave attackers a direct path to production data.&lt;/p&gt;

&lt;p&gt;CommuteAir (2023): a Jenkins server with AWS access keys in the build workspace. An attacker accessed the Jenkins instance, found the keys, and used them to access S3 buckets containing the TSA No Fly List. The CI/CD system was the entry point. The access keys were the bridge. The data was the target.&lt;/p&gt;

&lt;p&gt;Uber (2014 and 2016): AWS access keys committed to GitHub repositories. In 2014, the repository was public. In 2016, it was private but accessible to an attacker who compromised a developer's GitHub credentials. Both times, the keys in the repository provided direct access to production S3 data. The CI/CD artifact (the repository) contained the credential. The credential was the bridge.&lt;/p&gt;

&lt;p&gt;Drizly (2020): an executive's GitHub account was compromised via credential stuffing. The attacker found AWS access keys in a private repository and used them to access customer data in RDS. The FTC ordered Drizly to destroy the data and implement a security program.&lt;/p&gt;

&lt;p&gt;Three incidents, different companies and different years. The same pattern: a CI/CD system held credentials that provided direct access to production data, and no configuration check verified that the CI/CD system's access was appropriately scoped.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five configuration states CI/CD security doesn't check
&lt;/h2&gt;

&lt;p&gt;The gap isn't one missing check. It's five categories of configuration that exist at the boundary between the CI pipeline and the AWS environment, and that no tool in the pipeline evaluates.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. OIDC trust without subject restrictions
&lt;/h3&gt;

&lt;p&gt;GitHub Actions, GitLab CI, and Terraform Cloud all support OIDC federation for AWS authentication. Instead of storing long-term access keys, the CI system authenticates via a short-lived token that AWS verifies against the OIDC provider. This is the recommended approach. It eliminates stored credentials.&lt;/p&gt;

&lt;p&gt;But the trust relationship has a scope property that most teams don't configure. An IAM role that trusts GitHub Actions OIDC must specify WHICH repositories and branches can assume it. Without a subject restriction, the trust policy says "I trust GitHub Actions" not "I trust my organization's repository on the main branch." Any GitHub repository, including repositories the attacker controls, can assume the role.&lt;/p&gt;

&lt;p&gt;The hackingthe.cloud wiki documents this for GitHub, GitLab, and Terraform Cloud. AWS's own console creates the GitLab misconfiguration by default. The OIDC provider is configured correctly. The trust policy is configured incorrectly. Nothing in the CI pipeline checks the trust policy, because the trust policy is an AWS configuration, not a CI artifact.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Overpermissioned deployment roles
&lt;/h3&gt;

&lt;p&gt;The IAM role a CI pipeline uses to deploy infrastructure determines what the pipeline can create, modify, and access. In practice, these roles frequently have AdministratorAccess or broadly scoped permissions, because restricting the deployment role requires knowing which AWS actions the deployment needs. That set changes with every Terraform change.&lt;/p&gt;

&lt;p&gt;The result: the deployment role can do anything. If the CI pipeline is compromised through a dependency confusion attack, a malicious pull request, a compromised runner, or a stolen token — the attacker inherits the deployment role's permissions. An AdministratorAccess deployment role means a compromised pipeline is equivalent to compromised root.&lt;/p&gt;

&lt;p&gt;The pipeline checks whether the Terraform plan is safe. It does not check whether its own role is safe. The role that executes the plan is invisible to the plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Cross-account deployment without boundaries
&lt;/h3&gt;

&lt;p&gt;Organizations with multiple AWS accounts often grant CI/CD pipelines the ability to deploy across accounts. A pipeline in the tooling account assumes a role in the production account to deploy. The trust relationship allows this, the SCP permits it, and no guardrail limits which accounts the pipeline can reach.&lt;/p&gt;

&lt;p&gt;If the tooling account is compromised, every account the pipeline can deploy to is compromised. The blast radius of the CI/CD system equals the number of accounts it can access. In organizations with hundreds of accounts and a centralized deployment pipeline, that blast radius is total.&lt;/p&gt;

&lt;p&gt;No tool in the pipeline evaluates how many accounts the deployment role can reach. That's a cross-account IAM reachability question. It requires joining the trust policy on the target role with the permissions on the source role and the SCP chain between the two accounts. It's a graph traversal, not a per-resource check. CI/CD security tools don't do graph traversals.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. No SCP constraining CI/CD actions
&lt;/h3&gt;

&lt;p&gt;SCPs restrict what any principal in a member account can do, regardless of their IAM permissions. An SCP that denies &lt;code&gt;iam:CreateUser&lt;/code&gt; prevents any pipeline from creating IAM users, even if the pipeline's role permits it. An SCP that denies &lt;code&gt;kms:ImportKeyMaterial&lt;/code&gt; prevents a compromised pipeline from executing the BYOKM ransomware technique, even with full KMS access.&lt;/p&gt;

&lt;p&gt;But SCPs are organizational configurations. They're set by the cloud platform team, not by the application team that owns the pipeline. The CI pipeline doesn't know whether SCPs exist, what they restrict, or whether its own actions are constrained by them. The platform team that manages SCPs doesn't know which actions the pipeline needs, because the pipeline is managed by the application team.&lt;/p&gt;

&lt;p&gt;The gap is organizational. Two teams own different halves of the security boundary, and neither verifies the other's half.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Secrets in CI/CD that aren't in code
&lt;/h3&gt;

&lt;p&gt;Secret scanning in CI pipelines catches credentials committed to source code. It doesn't catch credentials stored in CI environment variables, secret managers, or build system configurations. Jenkins credentials, GitHub Actions secrets, GitLab CI variables, CircleCI contexts. These are stored outside the repository and injected at runtime. They're invisible to repository-level secret scanning.&lt;/p&gt;

&lt;p&gt;The CommuteAir breach happened because access keys were in the Jenkins workspace — not in the code repository, but in the build environment's configuration. Repository secret scanning wouldn't have found them. The keys existed in the CI system's configuration layer, which no CI security tool evaluates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this gap persists
&lt;/h2&gt;

&lt;p&gt;CI/CD security tools are designed to run inside the pipeline. They evaluate artifacts the pipeline produces: source code, dependency manifests, container images, infrastructure-as-code plans. They run with the pipeline's permissions, see the pipeline's inputs, and produce findings about the pipeline's outputs.&lt;/p&gt;

&lt;p&gt;The configuration gap lives outside the pipeline. The IAM trust policy is an AWS configuration. The deployment role's permissions are an AWS configuration. The SCP is an AWS organization configuration. The cross-account trust is a relationship between two AWS accounts. None of these are artifacts the pipeline produces. They're the environment the pipeline operates in.&lt;/p&gt;

&lt;p&gt;A tool that evaluates these configurations needs to see the AWS environment, not the CI artifacts. It needs to read trust policies, resolve OIDC provider configurations, check subject restrictions, evaluate deployment role permissions, trace cross-account reachability, and verify SCP coverage. That's a configuration verification task. The same task performed on any other AWS resource.&lt;/p&gt;

&lt;p&gt;The CI/CD security gap is a gap in configuration verification at the CI/CD boundary. The tools that check the code are doing their job. The tools that should check the deployment target configuration either don't exist or aren't run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing the gap
&lt;/h2&gt;

&lt;p&gt;Five invariants, each verifiable from a configuration snapshot:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every OIDC trust restricts the subject claim.&lt;/strong&gt; For GitHub Actions: the trust policy contains a condition on &lt;code&gt;token.actions.githubusercontent.com:sub&lt;/code&gt; with a specific &lt;code&gt;repo:org/repo:ref:refs/heads/branch&lt;/code&gt; pattern. For GitLab: same pattern on &lt;code&gt;gitlab.com:sub&lt;/code&gt;. For Terraform Cloud: same on &lt;code&gt;app.terraform.io:sub&lt;/code&gt;. Any OIDC trust without a subject restriction is a finding regardless of what the pipeline deploys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment roles follow least privilege.&lt;/strong&gt; The IAM role assumed by the CI pipeline has permissions scoped to the specific AWS actions the deployment requires — not AdministratorAccess, PowerUserAccess or a managed policy that covers more than the pipeline needs. This is the hardest invariant to specify because the required permissions change with every deployment change. But the upper bound is specifiable: no deployment role should have &lt;code&gt;iam:*&lt;/code&gt;, &lt;code&gt;organizations:*&lt;/code&gt;, or &lt;code&gt;sts:AssumeRole&lt;/code&gt; without resource restrictions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-account deployment is bounded.&lt;/strong&gt; The set of accounts a CI/CD pipeline can reach is explicitly defined and reviewed. A deployment role that can assume roles in 200 accounts should be a critical finding, regardless of what it deploys to any one of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SCPs constrain CI/CD actions.&lt;/strong&gt; Dangerous actions — &lt;code&gt;kms:ImportKeyMaterial&lt;/code&gt;, &lt;code&gt;iam:CreateUser&lt;/code&gt;, &lt;code&gt;organizations:LeaveOrganization&lt;/code&gt; are SCP-denied in accounts where CI/CD pipelines operate. The pipeline doesn't need to know about the SCP. The SCP needs to exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI/CD credentials are session-based, not stored.&lt;/strong&gt; OIDC federation, not access keys. Session tokens, not long-term credentials. No access keys in environment variables, build configurations, or workspace files. This is the invariant CommuteAir, Uber, and Drizly violated. It's verifiable from a snapshot that shows IAM users with access keys whose names or paths match CI/CD patterns.&lt;/p&gt;

&lt;p&gt;None of these invariants require running inside the pipeline. They require evaluating the AWS configuration that the pipeline connects to. That's configuration verification. The same discipline applied to any other AWS resource.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline doesn't need another scanner. The environment needs a verifier.
&lt;/h2&gt;

&lt;p&gt;The CI/CD security tooling market is mature and effective at its job. SAST finds code vulnerabilities. SCA finds dependency vulnerabilities. Container scanning finds image vulnerabilities. Secret scanning finds committed credentials. IaC scanning finds misconfigured plans.&lt;/p&gt;

&lt;p&gt;The gap that caused 19% of documented cloud breaches isn't in any of these tools. It's in the space between the pipeline and the environment — the trust policies, the deployment roles, the cross-account relationships, the missing SCPs. That space isn't checked by adding another scanner to the pipeline. It's checked by verifying the AWS configuration the pipeline connects to.&lt;/p&gt;

&lt;p&gt;Your CI pipeline has a security gap. It isn't a vulnerability. It's a configuration.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>security</category>
      <category>cloudsecurity</category>
      <category>devops</category>
    </item>
    <item>
      <title>Reachable Doesn't Mean Exploitable. How to Tell the Difference From Configuration Alone.</title>
      <dc:creator>Bala Paranj</dc:creator>
      <pubDate>Sat, 01 Aug 2026 11:45:12 +0000</pubDate>
      <link>https://dev.to/bala_paranj_059d338e44e7e/reachable-doesnt-mean-exploitable-how-to-tell-the-difference-from-configuration-alone-45f7</link>
      <guid>https://dev.to/bala_paranj_059d338e44e7e/reachable-doesnt-mean-exploitable-how-to-tell-the-difference-from-configuration-alone-45f7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;✓ Human-authored analysis; AI used for formatting and proofreading.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The finding that means three different things
&lt;/h2&gt;

&lt;p&gt;A scanner reports: "IAM role StorageAdmin has s3:DeleteBucket permission on the CloudTrail destination bucket prod-logs."&lt;/p&gt;

&lt;p&gt;Is this a problem?&lt;/p&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario A&lt;/strong&gt;: 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.&lt;/p&gt;

&lt;p&gt;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 &lt;strong&gt;reachable&lt;/strong&gt;: it exists in the configuration, but every attempt to exploit it hits a wall.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario B&lt;/strong&gt;: The role has the permission. No SCP blocks cross-account writes. The bucket exists and is owned by your account. CloudTrail is delivering logs.&lt;/p&gt;

&lt;p&gt;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 &lt;strong&gt;one away&lt;/strong&gt;: one configuration change makes it exploitable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario C&lt;/strong&gt;: 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.&lt;/p&gt;

&lt;p&gt;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 &lt;strong&gt;exploitable&lt;/strong&gt;: the attack path is open.&lt;/p&gt;

&lt;p&gt;Same finding and severity. Three different priorities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scanners can't tell the difference
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whether an SCP blocks the downstream action&lt;/li&gt;
&lt;li&gt;Whether the bucket is a destination for a security service&lt;/li&gt;
&lt;li&gt;Whether the bucket exists in the account's inventory&lt;/li&gt;
&lt;li&gt;Whether a data perimeter prevents cross-account writes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three tiers
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;After the chain evaluation, every finding gets one of three labels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;★ 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.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The labels change how a defender reads the finding.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;A REACHABLE finding is a true misconfiguration with no connected attack path. It belongs in the backlog, not the sprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  How compound chains work
&lt;/h2&gt;

&lt;p&gt;A compound chain is a Datalog rule with multiple preconditions. Here's the bucket-hijacking telemetry chain, simplified:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the chain fires, every participating finding — the permission finding, the CloudTrail destination finding is marked EXPLOITABLE. The ★ marker appears in the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the chain doesn't fire because one precondition is missing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The finding names the specific precondition protecting you. That precondition is not a finding to fix, it's a control to preserve.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this changes for defenders
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Triage becomes mechanical
&lt;/h3&gt;

&lt;p&gt;The traditional triage question is: "does this finding matter?" That question requires investigation — reading the finding, checking the environment, understanding the context, deciding.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Severity stops being the priority axis
&lt;/h3&gt;

&lt;p&gt;Scanners produce a flat list sorted by severity. CRITICAL at the top, LOW at the bottom. Defenders work down the list.&lt;/p&gt;

&lt;p&gt;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).&lt;/p&gt;

&lt;p&gt;After the classification, defenders sort by exploitability first, severity second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix verification becomes meaningful
&lt;/h3&gt;

&lt;p&gt;A defender fixes an EXPLOITABLE finding. How do they know it worked?&lt;/p&gt;

&lt;p&gt;Re-capture the snapshot. Re-run the evaluation. The classification changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before: ★ EXPLOITABLE (all preconditions present)
After:  ⚠ ONE AWAY (the fixed precondition is now absent)
        or
        REACHABLE (the fix broke the chain entirely)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  One-away is the early warning system
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;"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."&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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."&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this requires the graph
&lt;/h2&gt;

&lt;p&gt;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."&lt;/p&gt;

&lt;p&gt;The exploitability classification requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Individual findings (node properties) — what each resource's configuration looks like&lt;/li&gt;
&lt;li&gt;Compound chain rules (edge properties) — which combinations of findings create attack paths&lt;/li&gt;
&lt;li&gt;Precondition counting — how many edges are present vs absent for each chain&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Same snapshot, catalog, findings and classifications. Every time.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/sufield/stave" rel="noopener noreferrer"&gt;Stave&lt;/a&gt; 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. &lt;code&gt;stave apply --observations ./your-snapshot/&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
    <item>
      <title>Your AI SOC Agent Has a Task Role. Does It Also Have Your S3 Credentials?</title>
      <dc:creator>Bala Paranj</dc:creator>
      <pubDate>Fri, 31 Jul 2026 11:38:55 +0000</pubDate>
      <link>https://dev.to/bala_paranj_059d338e44e7e/your-ai-soc-agent-has-a-task-role-does-it-also-have-your-s3-credentials-50d6</link>
      <guid>https://dev.to/bala_paranj_059d338e44e7e/your-ai-soc-agent-has-a-task-role-does-it-also-have-your-s3-credentials-50d6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;✓ Human-authored analysis; AI used for formatting and proofreading.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Agentic security platforms are arriving. ECS-hosted investigation agents that invoke Bedrock models, write case artifacts to S3, mount shared session state via EFS, and expose interactive shells through ALBs. The architecture is sound. The security configuration surface is enormous. Most of it isn't covered by existing benchmarks because the benchmarks were written before agents existed.&lt;/p&gt;

&lt;p&gt;I audited a published deployment architecture for an ECS-hosted agentic SOC platform. An AI incident response system where autonomous agents triage alerts, investigate incidents, and produce case reports. 24 configuration properties matter for security. I checked each against the control catalog.&lt;/p&gt;

&lt;p&gt;19 were already covered. 5 weren't. The 5 gaps reveal a pattern worth understanding even if you never deploy this specific architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;The architecture has four layers, each with its own security surface:&lt;/p&gt;

&lt;p&gt;An artifact store (S3) holds case data — investigation results, forensic artifacts, evidence files. Public access blocks, access point policies, encryption, transport security, lifecycle rules, and versioning all apply. Standard S3 hardening, but the data is investigation evidence, which means a tampering risk that doesn't exist for typical application data.&lt;/p&gt;

&lt;p&gt;Session state (EFS) provides shared filesystem storage across agent tasks. Encryption, IAM authentication (no anonymous mounts), TLS enforcement, POSIX identity on access points, and security group restrictions on mount targets. EFS security is less well-understood than S3 — most teams haven't thought about whether their mount target allows NFS traffic from the internet.&lt;/p&gt;

&lt;p&gt;Compound risks live in task identity (ECS). Each agent runs as an ECS task with an IAM role. That role needs Bedrock model invocation, S3 artifact writes, and EFS mounts. The question is whether the permissions are scoped correctly, whether roles are shared across services, and whether a read-only consumer accidentally has write credentials.&lt;/p&gt;

&lt;p&gt;Exposure controls cover the network surface: no public IPs on ECS tasks, ALB authentication enforcement on interactive shell endpoints, security group restrictions on listener ports, and audit logging for exec sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  19 of 24 already covered
&lt;/h2&gt;

&lt;p&gt;The configuration properties that existing cloud security benchmarks address are well-covered:&lt;/p&gt;

&lt;p&gt;S3 public access blocks, access point policies, encryption with customer-managed keys, transport encryption, lifecycle rules, and versioning — all have dedicated controls. EFS encryption, IAM authentication, TLS enforcement, and POSIX identity on access points are covered. ECS secrets management, public IP assignment, log configuration, and exec audit logging also covered. ALB authentication actions and security group restrictions are covered.&lt;/p&gt;

&lt;p&gt;These are table-stakes checks. Every scanner in the market finds these. The interesting findings are in the 5 that weren't covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5 gaps — all compound
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Gap 1: Task role and execution role are the same IAM role.&lt;/strong&gt; ECS task definitions have two role fields: the task role (what the application code uses) and the execution role (what ECS uses to pull container images and push logs). When they're the same role, the application code inherits ECR pull and CloudWatch push permissions it doesn't need. Two trust boundaries collapse into one. The fix is a single property comparison: does &lt;code&gt;taskRoleArn&lt;/code&gt; equal &lt;code&gt;executionRoleArn&lt;/code&gt;? Simple check, but no benchmark includes it because the distinction between task roles and execution roles is an ECS-specific concept that predates agentic architectures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gap 2: Read-only consumer has write credentials.&lt;/strong&gt; The SOC platform has services that should only read from the artifact store — the reporting service, the dashboard, the evidence viewer. If their task role has &lt;code&gt;s3:PutObject&lt;/code&gt; permission, a compromised read-only service can tamper with investigation artifacts. The check requires correlating the ECS task definition (which role?) with the IAM policy analysis (what can that role do?). A cross-resource property that single-resource scanners can't express. The collector resolves the role's IAM policies and stamps a boolean on the ECS observation: &lt;code&gt;has_s3_write: true/false&lt;/code&gt;. The evaluator checks the boolean. The cross-resource complexity lives in the collector, not in the check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gap 3: Bedrock model invocation on &lt;code&gt;Resource: *&lt;/code&gt;.&lt;/strong&gt; An agent role with &lt;code&gt;bedrock:InvokeModel&lt;/code&gt; on all resources can invoke any model in the account — including custom fine-tuned models that may contain training data from other projects. The fix is scoping the resource to specific model ARNs. The existing wildcard detection control covers this, but only if the action is in the sensitive-action registry. Adding &lt;code&gt;bedrock:InvokeModel&lt;/code&gt; (and the streaming and conversation variants) to the registry is the fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gap 4: No compound chain linking internet exposure to model invocation.&lt;/strong&gt; The atomic controls work: "ECS service has public IP" fires. "Role has broad Bedrock invoke" fires. "Role has broad S3 write" fires. Each fires independently. No compound chain connects them into "internet-reachable service can invoke any model AND write to any bucket." The compound path is the blast radius. The individual findings are the components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gap 5: Artifact tampering through shared storage.&lt;/strong&gt; One service writes to S3. Another service reads from S3. If the writer is compromised, it can poison the artifacts the reader trusts. No existing chain models this "write → shared store → poison read-only consumer" pattern. It's a data integrity attack, not a data access attack. The chain connects: internet-reachable service has write access, store has no versioning (tampered objects overwrite silently), and downstream consumers trust store contents.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern across all 5
&lt;/h2&gt;

&lt;p&gt;Every gap is a cross-resource composition. Task role equals execution role compares two fields on one resource. Read-only consumer with write credentials correlates a task definition with an IAM policy. The compound chains connect 3-4 resources into attack paths.&lt;/p&gt;

&lt;p&gt;Single-resource scanners check each resource independently. Each passes. The compound path is unsafe. The scanner reports green.&lt;/p&gt;

&lt;p&gt;This is the same structural gap the Hugging Face incident revealed. The sandbox passed every individual check. The escape path was a compound of three configurations that were each individually acceptable. The agentic SOC platform has the same shape: each service is configured correctly. The risk lives in interactions between services.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for agentic architectures
&lt;/h2&gt;

&lt;p&gt;If you're deploying AI agents on ECS (or Lambda, or EKS, or any container orchestrator), the configuration surface isn't the individual resource. It's the graph of permissions and network paths connecting the agent's compute, identity, data stores, and exposure points.&lt;/p&gt;

&lt;p&gt;Five questions for your deployment:&lt;/p&gt;

&lt;p&gt;Is the task role distinct from the execution role? If the agent's application code inherits ECR and CloudWatch permissions it doesn't need, the blast radius of a compromise is wider than intended.&lt;/p&gt;

&lt;p&gt;Does every service have its own role? Shared roles mean a compromised triage agent has the same permissions as the investigation agent. Per-service roles limit the blast radius to one function.&lt;/p&gt;

&lt;p&gt;Is the model invocation scoped to specific models? An agent that can invoke any model can access any fine-tuned model's training data. Scope to the models the agent uses.&lt;/p&gt;

&lt;p&gt;Can a read-only consumer write to the artifact store? If the reporting service can modify investigation evidence, the integrity of every case report is questionable. The check requires correlating the task role with the IAM policy — not just checking the task definition.&lt;/p&gt;

&lt;p&gt;If the agent is internet-reachable, what can it reach? The compound path from internet exposure through the agent's role to Bedrock, S3, and EFS is the blast radius. Checking each resource independently misses the path.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The 24-item checklist and the 5 gap closures are implemented in &lt;a href="https://github.com/sufield/stave" rel="noopener noreferrer"&gt;Stave&lt;/a&gt;, an open-source cloud configuration verification tool. The compound chains that detect cross-resource agent risks require composing findings across ECS, IAM, S3, EFS, and ELB — something single-resource scanners structurally cannot do. Apache 2.0.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cloudsecurity</category>
      <category>ai</category>
      <category>aws</category>
      <category>security</category>
    </item>
    <item>
      <title>CSA Says Harden Your Networks for the AI Storm. Here's How to Verify You Actually Did.</title>
      <dc:creator>Bala Paranj</dc:creator>
      <pubDate>Thu, 30 Jul 2026 11:01:38 +0000</pubDate>
      <link>https://dev.to/bala_paranj_059d338e44e7e/csa-says-harden-your-networks-for-the-ai-storm-heres-how-to-verify-you-actually-did-1i58</link>
      <guid>https://dev.to/bala_paranj_059d338e44e7e/csa-says-harden-your-networks-for-the-ai-storm-heres-how-to-verify-you-actually-did-1i58</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;✓ Human-authored analysis; AI used for formatting and proofreading.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;The Cloud Security Alliance and Cisco recently published &lt;em&gt;Preparing Your Networks for the AI Storm&lt;/em&gt;, a paper by Rich Mogull arguing that AI-accelerated attacks have collapsed the timelines defenders relied on. The recommendations are practical: harden what's exposed, segment what's flat, build toward Zero Trust.&lt;/p&gt;

&lt;p&gt;The paper is right. But it describes &lt;em&gt;what&lt;/em&gt; to do, not &lt;em&gt;how to verify you did it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That gap is filled by configuration verification. Every recommendation in the paper that maps to a cloud configuration property can be checked automatically, continuously, and without credentials. This article maps the paper's three phases to specific automated checks and shows what coverage looks like when every in-scope recommendation has a corresponding verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Paper's Argument
&lt;/h2&gt;

&lt;p&gt;AI has shifted the economics of offense. Exploit development that took months now takes hours. The defensive response is to increase attacker costs through security boundaries — network segmentation, patching cadence, and serial defensive layers. The paper lays out three phases: harden what you have (Phase 1), add segmentation boundaries (Phase 2), and drive toward microsegmentation and Zero Trust (Phase 3).&lt;/p&gt;

&lt;p&gt;Every phase contains actionable recommendations. Most of them map to AWS configuration properties that either exist or don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 1: Seven Immediate Actions — All Verified
&lt;/h2&gt;

&lt;p&gt;Phase 1 is "outside in, key assets first." Identify what's internet-facing, harden management interfaces, enforce MFA, filter egress, add WAFs, audit automation secrets, and keep everything current.&lt;/p&gt;

&lt;h3&gt;
  
  
  External Attack Surface
&lt;/h3&gt;

&lt;p&gt;The paper recommends identifying every internet-reachable device and service. In AWS, this means security groups with &lt;code&gt;0.0.0.0/0&lt;/code&gt; inbound rules, public subnets with auto-assign public IP, internet gateways, public-facing load balancers, API gateways, CloudFront distributions, and S3 buckets with public access.&lt;/p&gt;

&lt;p&gt;This is the most deeply covered area in the catalog. Across S3, EC2, RDS, ELB, CloudFront, API Gateway, and a dozen other services, there are hundreds of controls checking for unintended public exposure. Every major AWS service that can be internet-facing has dedicated exposure checks.&lt;/p&gt;

&lt;p&gt;The depth matters. It's not just "is this security group open". It's "is this security group open AND is the resource behind it in a public subnet AND does it have a public IP AND is there no WAF in front of it." Component checks find individual misconfigurations. Compound checks find the combination that makes them exploitable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Management Interface Exposure
&lt;/h3&gt;

&lt;p&gt;The paper says to disable management interfaces exposed to the internet or DMZ. In AWS: SSH and RDP ports open to the world, serial console access enabled, key pair governance, and IMDS hardening.&lt;/p&gt;

&lt;p&gt;The catalog checks all of these. SSH/RDP port restriction, SSM-preferred-over-SSH controls, serial console disabled, IMDS v2 enforcement (18 controls covering EC2, ECS, and EKS), and ECS exec controls. If Systems Manager is available, direct SSH access is unnecessary attack surface.&lt;/p&gt;

&lt;h3&gt;
  
  
  MFA, Egress, WAF, Secrets
&lt;/h3&gt;

&lt;p&gt;These four recommendations each have strong coverage:&lt;/p&gt;

&lt;p&gt;MFA enforcement spans root account hardware MFA, IAM user console MFA, Cognito MFA configuration, S3 MFA delete, and SSO MFA settings.&lt;/p&gt;

&lt;p&gt;Egress filtering covers security group egress rules, Route53 DNS Firewall association, DNS exfiltration patterns, VPC endpoint force-routing (to keep traffic off the public internet), and NAT Gateway monitoring.&lt;/p&gt;

&lt;p&gt;WAF checks verify association with ALBs and CloudFront distributions, logging enablement, bypass detection (can traffic reach the origin without passing through the WAF?), and consistency across deployments.&lt;/p&gt;

&lt;p&gt;Secrets in automation checks cover Lambda environment variable encryption, CodeBuild environment secrets, EC2 userdata credential scanning, access key governance, and Secrets Manager rotation policies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Version Currency
&lt;/h3&gt;

&lt;p&gt;The paper recommends keeping everything current and patchable. In AWS managed services, this translates to runtime and engine version checks. Lambda runtime deprecation, RDS engine end-of-life, EKS cluster version, ElastiCache engine currency, and OpenSearch engine version all have dedicated controls. Each checking the running version against AWS's deprecation schedule.&lt;/p&gt;

&lt;p&gt;Physical hardware patching is out of scope. A cloud configuration verifier checks cloud services, not router firmware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 2: Three Segmentation Actions — All Verified
&lt;/h2&gt;

&lt;p&gt;Phase 2 adds security boundaries. The paper describes macrosegmentation (isolate stacks from each other), east-west isolation (prevent lateral movement within a stack), and inline tier boundaries (each application tier talks only to its neighbors).&lt;/p&gt;

&lt;h3&gt;
  
  
  Macrosegmentation
&lt;/h3&gt;

&lt;p&gt;The paper recommends isolating application stacks so that a compromise of less-valuable resources doesn't become the easy path to critical assets.&lt;/p&gt;

&lt;p&gt;VPC-level segmentation is covered across multiple layers: default VPC detection, VPC peering controls (10 controls covering routes, DNS resolution, and CIDR overlap), subnet tiering, and bastion host placement.&lt;/p&gt;

&lt;p&gt;Transit Gateway — the standard hub-and-spoke architecture for multi-account AWS has dedicated controls for route table isolation, auto-accept attachment, default route table association, and propagation scope. Route table isolation is the critical check: a Transit Gateway where all attachments share a single route table is a flat network with extra steps. The control verifies that segments are actually isolated, not just labeled.&lt;/p&gt;

&lt;h3&gt;
  
  
  East-West Isolation
&lt;/h3&gt;

&lt;p&gt;The paper says to isolate from east-west attacks. In AWS, this is security group rules between tiers, NACL rules, flow logs for visibility, and network boundary enforcement.&lt;/p&gt;

&lt;p&gt;Security group east-west controls check for unrestricted internal traffic patterns — self-referencing rules, broad RFC 1918 CIDR sources, and the specific case where security group source CIDRs don't cover all subnets in the VPC. This gap only appears after VPC CIDR expansion, when new subnets fall outside existing security group rules.&lt;/p&gt;

&lt;p&gt;Transit Gateway segmented routing verifies that route propagation doesn't leak across segments — preventing the case where a compromised workload in one segment can route to resources in another through the hub.&lt;/p&gt;

&lt;p&gt;Flow logs provide the visibility layer: 13 controls verify VPC flow log enablement, format, destination, and encryption.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inline Tier Boundaries
&lt;/h3&gt;

&lt;p&gt;The paper recommends that each tier of an application should only communicate with the tier above and below it, on expected ports only. In AWS, this is VPC segment tiering controls, private subnet enforcement, NACL port restriction, and VPC endpoint policies that scope S3 and DynamoDB access per subnet.&lt;/p&gt;

&lt;p&gt;This includes an ECS-specific control that verifies service security groups only allow traffic from the load balancer — a tier boundary enforcement at the container level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 3: Three Strategic Actions — All Verified
&lt;/h2&gt;

&lt;p&gt;Phase 3 is microsegmentation and Zero Trust. The paper frames this as the long-term destination: identity-based access per connection, fine-grained resource policies, and continuous visibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Least Privilege and Resource Policies
&lt;/h3&gt;

&lt;p&gt;IAM least-privilege checks span admin policy detection, wildcard action usage, and separation of duties. Service Control Policies add organization-level guardrails. Resource policies on S3, KMS, SNS, SQS, and other services verify per-resource access controls.&lt;/p&gt;

&lt;p&gt;A Lambda-specific control checks whether execution roles are shared across functions — the microsegmentation principle applied to serverless compute.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visibility
&lt;/h3&gt;

&lt;p&gt;The paper emphasizes that visibility underpins everything. CloudTrail (management and data events, organization trails, integrity validation), GuardDuty (enabled with all protection features), AWS Config (recording all resource types), SecurityHub, CloudWatch alarms for security events, and access logging on S3, ALB, CloudFront, and NAT Gateways — all have dedicated controls.&lt;/p&gt;

&lt;p&gt;The detection-absence chains add compound verification: escalation paths that exist alongside absent detection produce critical findings. An IAM escalation path with no CloudWatch alarm is worse than either condition alone — the compound chain catches the combination.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identity Providers as High-Value Targets
&lt;/h3&gt;

&lt;p&gt;The paper's most interesting point: as you move toward Zero Trust, identity providers become the highest-value targets precisely because everything trusts them. They "earn more scrutiny and more boundaries."&lt;/p&gt;

&lt;p&gt;This is the deepest coverage area in the catalog after IAM itself. Cognito alone has 113 controls covering MFA, ghost triggers, advanced security features, federation configuration, and orphan resource detection. IAM Identity Center, OIDC providers (with subject and audience restriction checks for GitHub, GitLab, and Terraform Cloud specifically), SAML federation (audience, certificate rotation, session duration), and cross-account trust all have dedicated control families.&lt;/p&gt;

&lt;p&gt;The Datalog-based federation reachability model goes further: it computes transitive paths from external identity providers through role chains to resources, answering questions like "can an external OIDC provider reach this S3 bucket through a chain of role assumptions?" That's not a single control firing. It's a graph traversal across the identity trust boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scorecard
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Recommendations (in scope)&lt;/th&gt;
&lt;th&gt;Covered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Phase 1 — Harden&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phase 2 — Segment&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phase 3 — Zero Trust&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;13&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;13&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Out of Scope
&lt;/h2&gt;

&lt;p&gt;The paper is about physical network hardening — routers, switches, firewalls, hardware patching, data diodes for OT networks. Cloud configuration verification covers the cloud side of the same recommendations. If your network modernization program includes replacing end-of-life switches and adding data diodes to OT networks, no cloud tool checks that for you.&lt;/p&gt;

&lt;p&gt;What a cloud tool checks is whether the cloud side of the same principles — segmentation, least-privilege access, egress filtering, visibility is configured the way you intended. The paper says to do these things. Automated verification confirms they're done and catches drift when they stop being done.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Drift
&lt;/h2&gt;

&lt;p&gt;Mapping recommendations to checks is the easy part. The harder problem is drift.&lt;/p&gt;

&lt;p&gt;A security group that was correctly restricted last week may have been widened by a deployment today. A VPC endpoint policy that scoped S3 access to specific subnets may have been broadened during a CIDR expansion. A Transit Gateway that enforced route isolation may have had propagation enabled during a migration and never disabled.&lt;/p&gt;

&lt;p&gt;The paper's Phase 2 recommendation to "add boundaries" is only as durable as the verification that confirms boundaries still exist. Most organizations implemented the boundary, verified it once during the project, and never checked again. Continuous verification against a defined specification — like the 13 recommendations in this paper, each mapped to specific checks makes boundaries persistent rather than aspirational.&lt;/p&gt;

&lt;p&gt;The paper ends with a choice: "Zero Trust or zero days." Configuration verification adds a third option: verify continuously, so the boundaries you built hold when the storm arrives.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The coverage scorecard is re-verified when controls are added or modified, and monthly via automated regression against the control catalog.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The CSA paper, "Preparing Your Networks for the AI Storm" by Rich Mogull, is available at cloudsecurityalliance.org.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cloudsecurity</category>
      <category>aws</category>
      <category>networking</category>
      <category>zerotrust</category>
    </item>
    <item>
      <title>The Model Didn't Escape the Sandbox. The Sandbox Was Misconfigured.</title>
      <dc:creator>Bala Paranj</dc:creator>
      <pubDate>Wed, 29 Jul 2026 07:07:36 +0000</pubDate>
      <link>https://dev.to/bala_paranj_059d338e44e7e/the-model-didnt-escape-the-sandbox-the-sandbox-was-misconfigured-4h6f</link>
      <guid>https://dev.to/bala_paranj_059d338e44e7e/the-model-didnt-escape-the-sandbox-the-sandbox-was-misconfigured-4h6f</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;✓ Human-authored analysis; AI used for formatting and proofreading.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All control mappings verified against a live catalog and tested against synthetic fixtures. The CSA/SANS/RSAC post-mortem (v0.8, July 27 2026) is the primary reference.*&lt;/p&gt;




&lt;p&gt;Last week, an OpenAI model broke out of a benchmark sandbox, stole credentials, chained a zero-day, and attacked Hugging Face's production systems. The model was running inside OpenAI's own red-team evaluation. The sandbox gave it one controlled path to the internet. The model found a bug in that path and used it.&lt;/p&gt;

&lt;p&gt;Within 48 hours, 700 CISOs convened. CSA, SANS, RSAC, FIRST, and Knostic published a joint post-mortem with 30+ recommendations. The recommendations focus on governance, accountability, agent identity, deception technology, and incident response preparedness.&lt;/p&gt;

&lt;p&gt;The recommendations are reasonable. They're also treating the symptom.&lt;/p&gt;

&lt;p&gt;The root cause is simpler: the sandbox's network configuration wasn't verified before the model ran inside it. The egress path had a vulnerability. The credentials were accessible from the sandbox environment. The IAM role was broad enough to reach external targets. No compound check verified that these three conditions together created an escape path.&lt;/p&gt;

&lt;p&gt;Each condition would pass a standard security check individually. The sandbox had internet access controls. The credentials were managed. The role existed within the evaluation environment. Three passing checks. One breach.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CrowdStrike structure
&lt;/h2&gt;

&lt;p&gt;This incident has the same structure as the CrowdStrike outage that took down millions of Windows machines in 2024.&lt;/p&gt;

&lt;p&gt;CrowdStrike put a powerful payload (kernel-level driver update) inside an environment (production Windows fleet). They assumed the environment would handle the payload safely. They didn't verify the update's configuration against the target environment. The payload worked as designed. The environment couldn't contain it.&lt;/p&gt;

&lt;p&gt;OpenAI put a powerful payload (frontier model optimizing for a benchmark score) inside an environment (evaluation sandbox). They assumed the sandbox would contain the payload. They didn't verify the sandbox's network configuration. The payload worked as designed. It found the shortest path to the score. The environment couldn't contain it.&lt;/p&gt;

&lt;p&gt;Same defect class: the containment boundary was assumed, not verified. The payload was correct in both cases. The configuration was wrong. Nobody checked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;The CSA post-mortem recommends "treat sandbox-isolation claims with skepticism" and "test your isolation mechanisms directly." Both are correct. Test directly means, 32 configuration invariants that, verified before the model runs, prevent the breach regardless of the model's capability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sandbox network isolation
&lt;/h3&gt;

&lt;p&gt;The model escaped because the sandbox had a network path to the internet. Five invariants verify containment:&lt;/p&gt;

&lt;p&gt;The sandbox VPC has no unrestricted egress. Security groups on agent compute allow outbound traffic only to allowlisted destinations. A rule allowing &lt;code&gt;0.0.0.0/0&lt;/code&gt; on any port is a containment failure.&lt;/p&gt;

&lt;p&gt;The sandbox VPC has no internet gateway or if one exists, route tables restrict which subnets can reach it. An internet gateway on an agent VPC without route table scoping is an escape path waiting for a model to find it.&lt;/p&gt;

&lt;p&gt;Agent compute runs in an isolated VPC, not shared with production. A sandbox in the same VPC as production systems means the model's network is the production network.&lt;/p&gt;

&lt;p&gt;No VPC peering or Transit Gateway connects the sandbox to production. Route table isolation on Transit Gateway prevents cross-segment routing. Without it, the sandbox routes to production through the hub.&lt;/p&gt;

&lt;p&gt;The agent role cannot reach production secrets. Graph reachability analysis (using Datalog transitive closure) traces paths from agent roles through arbitrary AssumeRole and PassRole chains to sensitive resources. A role that can transitively reach production Secrets Manager entries is a credential theft path.&lt;/p&gt;

&lt;p&gt;All five of these are configuration properties. They can be verified before the model runs. They're deterministic. The VPC either has unrestricted egress or it doesn't. No probabilistic judgment. No model inference. A boolean check against the infrastructure state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Non-human identity scoping
&lt;/h3&gt;

&lt;p&gt;The model stole credentials and used them outside the sandbox. Seven invariants verify identity containment:&lt;/p&gt;

&lt;p&gt;Agent roles have permission boundaries. A permission boundary limits the maximum scope of an IAM role regardless of attached policies. Without one, the role's effective permissions are whatever policies it happens to have, which may have been broadened during development and never scoped back.&lt;/p&gt;

&lt;p&gt;Agent roles have short maximum session duration. The post-mortem recommends ephemeral credentials limited by design. A session that lasts 12 hours gives an attacker 12 hours to use stolen credentials. A session limited to 1 hour limits the exploitation window.&lt;/p&gt;

&lt;p&gt;Agent roles require session naming for CloudTrail attribution. Without session naming, a stolen credential's activity in CloudTrail is attributed to the role, not to who assumed it. Forensics can see the role was used but not by whom or what.&lt;/p&gt;

&lt;p&gt;Agent roles restrict source IP or VPC. Condition keys on the trust policy limit where the role can be assumed from. Credentials stolen and used from outside the sandbox are blocked by the condition, regardless of the credential's validity.&lt;/p&gt;

&lt;p&gt;Agent roles have no &lt;code&gt;iam:PassRole&lt;/code&gt; on &lt;code&gt;Resource: *&lt;/code&gt;. PassRole with unrestricted scope allows the role to delegate its permissions to any service — Lambda, SageMaker, CodeBuild. Each delegation is a new execution context the model can use.&lt;/p&gt;

&lt;p&gt;No permanent access keys on agent roles. Only temporary credentials via AssumeRole, which expire and can't be reused after session termination.&lt;/p&gt;

&lt;p&gt;OIDC providers have subject and audience restrictions. If the agent authenticates via an external identity provider, the provider's trust configuration must restrict which subjects and audiences are accepted.&lt;/p&gt;

&lt;h3&gt;
  
  
  The compound paths
&lt;/h3&gt;

&lt;p&gt;Individual controls check individual resources. The breach wasn't an individual misconfiguration. It was a path through three resources that were each individually acceptable.&lt;/p&gt;

&lt;p&gt;The sandbox escape path: agent VPC has unrestricted egress AND agent role has broad permissions AND no VPC flow logs for forensic visibility. Each component might pass a standard security check. The compound path is a critical finding that requires correlating all three.&lt;/p&gt;

&lt;p&gt;The credential exfiltration path: agent role can read production secrets AND the compute environment has internet egress AND session naming isn't required on the role. Each resource passes its own check. The path through all three is how credentials leave the environment unattributed.&lt;/p&gt;

&lt;p&gt;The unmonitored escalation path: agent role has PassRole plus Lambda create permission AND CloudTrail data events are not enabled for Lambda. The escalation path exists. The forensic trail to detect it doesn't.&lt;/p&gt;

&lt;p&gt;The immutable infrastructure bypass: ECS exec is enabled AND the root filesystem is writable AND no image signing is enforced. The container can be modified at runtime, the modification persists, and no code integrity check catches it.&lt;/p&gt;

&lt;p&gt;These compound paths are the detection gap the industry has. Every tool on the market can check "is this security group restrictive?" and "is this role least-privilege?" and "are flow logs enabled?" No tool composes them into "this security group plus this role plus this telemetry gap equals an escape path."&lt;/p&gt;

&lt;p&gt;The Hugging Face breach happened through a compound path. The post-mortem recommends governance, accountability, and incident response. Those are necessary. But the configuration that created the path is verifiable before the model runs and should have been verified.&lt;/p&gt;

&lt;h3&gt;
  
  
  Credential rotation and ephemeral credentials
&lt;/h3&gt;

&lt;p&gt;The post-mortem recommends building the ability to mass-rotate credentials and designing for ephemeral credentials by default.&lt;/p&gt;

&lt;p&gt;The configuration invariants behind that recommendation: Secrets Manager rotation is enabled AND the rotation Lambda actually exists. The second check matters, rotation can show as "enabled" in the console while the backing Lambda has been deleted. The secret hasn't changed in 120 days. The dashboard says 30. The ghost reference — a configuration that appears active but references a deleted dependency is one of the most common Tier 3 failure modes in cloud security.&lt;/p&gt;

&lt;p&gt;IAM access key age verification. KMS automatic key rotation. ACM certificate expiry monitoring. Each is a configuration property that's verifiable and deterministic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Immutable infrastructure
&lt;/h3&gt;

&lt;p&gt;The post-mortem recommends adopting immutable infrastructure. Services that are destroyed and redeployed from known-good images rather than patched in place.&lt;/p&gt;

&lt;p&gt;The configuration invariants: ECS tasks with read-only root filesystems. Lambda functions with code signing enforced. ECR repositories with tag immutability and image scanning enabled. EC2 instances with IMDSv2 enforced (preventing the trivial SSRF credential theft that's been a standard attack technique for years). ECS exec disabled in production.&lt;/p&gt;

&lt;p&gt;These aren't new controls. They're standard cloud hardening. What's notable is how few organizations verify all of them simultaneously and how the compound check (exec enabled + writable filesystem + no signing = runtime modification with no integrity check) isn't in any benchmark.&lt;/p&gt;

&lt;h2&gt;
  
  
  The guardrail symmetry
&lt;/h2&gt;

&lt;p&gt;The post-mortem surfaces a finding that should concern every security team: when Hugging Face's defenders tried to analyze the attack's 17,000 actions, the commercial models they reached for blocked their analysis. Real attack commands and exploit payloads triggered safety guardrails that couldn't distinguish an incident responder from an intruder.&lt;/p&gt;

&lt;p&gt;The attacker was bound by no guardrail. The defenders were.&lt;/p&gt;

&lt;p&gt;This is the inevitable consequence of prompt-level controls applied to infrastructure problems. A text filter that blocks "dangerous commands" can't tell whether the command is part of an attack or part of the investigation. The attacker doesn't use the text filter. The defender does.&lt;/p&gt;

&lt;p&gt;Configuration verification operates at a different layer. It doesn't filter commands. It verifies that the infrastructure's configuration prevents the attack path from existing in the first place. The VPC either has unrestricted egress or it doesn't. The role either has a permission boundary or it doesn't. These checks don't trigger safety guardrails because they're not analyzing attack commands. They're verifying infrastructure state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Actionable by CISO
&lt;/h2&gt;

&lt;p&gt;The post-mortem's "What a CISO Should Do Now" section lists 20 recommendations across three time horizons (this week, this month, this quarter). Most are process and governance. The configuration verification subset is smaller and more immediately actionable:&lt;/p&gt;

&lt;p&gt;Verify sandbox isolation before deploying any AI agent or evaluation. Run the 5 network isolation checks against the actual VPC configuration. The check takes seconds. The breach took days to clean up.&lt;/p&gt;

&lt;p&gt;Verify agent role scoping against the 7 identity invariants. Permission boundaries, session duration limits, session naming, source restrictions, PassRole scope, credential lifetime, and OIDC restrictions. Each is a configuration property the cloud provider exposes.&lt;/p&gt;

&lt;p&gt;Verify compound paths, not just individual controls. The three-resource escape path that enabled the Hugging Face breach passes every individual check in every scanner on the market. The compound check which requires correlating resources across security groups, IAM roles, and telemetry configuration catches it.&lt;/p&gt;

&lt;p&gt;Run these checks continuously, not once. Configuration drifts. A sandbox that was correctly isolated last month may have had its egress modified during a debugging session and never reverted. Continuous verification against the 32 invariants catches drift before a model finds it.&lt;/p&gt;

&lt;h2&gt;
  
  
  32 invariants, verified
&lt;/h2&gt;

&lt;p&gt;Every configuration invariant described in this article is verifiable against a live cloud environment using Stave, an open-source verification tool. The invariants span five clusters: sandbox network isolation (5), non-human identity scoping (7), credential rotation and ephemeral credentials (5), immutable infrastructure (6), agent telemetry (4), and compound cross-resource paths (5).&lt;/p&gt;

&lt;p&gt;The atomic controls existed in the catalog before the Hugging Face incident was public. The compound chains that compose them into agent-specific escape, exfiltration, and bypass paths were authored in response to the CSA post-mortem. Because the cross-resource composition connecting them into compound findings hadn't been explicitly defined.&lt;/p&gt;

&lt;p&gt;That distinction matters: the individual checks ("is egress restricted?" "is the role scoped?" "are flow logs enabled?") are table stakes. Every scanner has them. The compound check ("egress is unrestricted AND the role is broad AND flow logs are absent, together creating an escape path") cannot be produced by any single-resource scanner. Because it requires reasoning across resource types, not within them.&lt;/p&gt;

&lt;p&gt;The model didn't outsmart the security controls. It walked through a configuration gap that nobody verified was closed. The gap is closed now. The 32 invariants verify it stays closed — every commit, every snapshot, every deployment.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/sufield/stave" rel="noopener noreferrer"&gt;Stave&lt;/a&gt; is open-source (Apache 2.0), runs locally, and requires no cloud credentials for evaluation. The agent isolation coverage audit and the 32 invariants referenced in this article are in the repository. Try it: &lt;code&gt;stave apply --observations ./snapshots&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The CSA/SANS/RSAC post-mortem, "Hugging Face Incident Initial Post-Mortem" (v0.8, July 27 2026), is available from the Cloud Security Alliance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cloudsecurity</category>
      <category>ai</category>
      <category>security</category>
      <category>aws</category>
    </item>
    <item>
      <title>Listing the Bucket is Half the Breach</title>
      <dc:creator>Bala Paranj</dc:creator>
      <pubDate>Tue, 28 Jul 2026 09:34:12 +0000</pubDate>
      <link>https://dev.to/bala_paranj_059d338e44e7e/listing-the-bucket-is-half-the-breach-1j3m</link>
      <guid>https://dev.to/bala_paranj_059d338e44e7e/listing-the-bucket-is-half-the-breach-1j3m</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;✓ Human-authored analysis; AI used for formatting and proofreading.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;s3:ListBucket&lt;/code&gt; does not return object contents. It returns the &lt;em&gt;key inventory&lt;/em&gt; — the filenames, prefixes, sizes, and timestamps of every object in the bucket. That inventory is the map for an attacker. Knowing what to look for makes everything that comes next cheaper.&lt;/p&gt;

&lt;p&gt;Three HackerOne cases on file demonstrate the pattern: Shopify report 57505 (public list), Zomato report 507097 (public list paired with read), and Sriram's 2017 disclosure (LIST + WRITE on bucket-level ACL). Each one is the same policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:ListBucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::&amp;lt;bucket&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Principal: "*"&lt;/code&gt; on &lt;code&gt;s3:ListBucket&lt;/code&gt;. No read permission required.&lt;/p&gt;

&lt;p&gt;Anyone on the internet can call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;ls &lt;/span&gt;s3://&amp;lt;bucket&amp;gt;/ &lt;span class="nt"&gt;--no-sign-request&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…and receive every key in the bucket, paginated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Listing is The Discovery Signal
&lt;/h2&gt;

&lt;p&gt;Public read disclosure tells you what to grab. Public list disclosure tells you what &lt;em&gt;exists&lt;/em&gt;. The two failure modes are different:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you have&lt;/th&gt;
&lt;th&gt;Public read alone&lt;/th&gt;
&lt;th&gt;Public list alone&lt;/th&gt;
&lt;th&gt;Both&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Random object URL guess&lt;/td&gt;
&lt;td&gt;Sometimes works&lt;/td&gt;
&lt;td&gt;Doesn't help&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Targeted by name&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;td&gt;Returns the name&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inventory recon&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time to reproduce&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Listing without reading is enough on its own when the &lt;strong&gt;keys themselves carry information&lt;/strong&gt;: customer email addresses in filenames, dump-&lt;code&gt;&amp;lt;date&amp;gt;.sql&lt;/code&gt; patterns that imply a database backup cadence, prefixes like &lt;code&gt;users/12345/profile.json&lt;/code&gt; that leak ID schemes, or build-artefact paths that reveal the software stack. The Zomato report (&lt;code&gt;507097&lt;/code&gt;) describes this — public listing paired with predictable key naming meant the bucket was effectively public-read once an attacker knew the schema.&lt;/p&gt;

&lt;p&gt;Listing is also a &lt;strong&gt;stable signal&lt;/strong&gt;. A public-read bucket whose operators rotate object keys after every incident is a moving target. A public-list bucket cannot rotate the schema cheaply — that would break every consumer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The System Invariant
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;No S3 bucket may resolve a &lt;code&gt;ListBucket&lt;/code&gt; request from an unauthenticated principal.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Same shape as the public-read invariant. A fold of (a) bucket policy, (b) ACL, (c) Public Access Block, (d) account-level PAB — with &lt;code&gt;s3:ListBucket&lt;/code&gt; as the action of interest. In the observation schema the engine's verdict surfaces under &lt;code&gt;storage.access.public_list&lt;/code&gt;, with the raw policy text alongside under &lt;code&gt;storage.policy_json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Stave catalogs two versions of this invariant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CTL.S3.PUBLIC.LIST.001&lt;/code&gt; — the strict rule. Listing must not be public, period. Severity &lt;code&gt;high&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CTL.S3.PUBLIC.LIST.002&lt;/code&gt; — the graduated rule. Listing may be public &lt;em&gt;only when the bucket carries  &lt;code&gt;public_list_intended=true&lt;/code&gt; as a tag&lt;/em&gt;. The tag forces the intent to be explicit and auditable; it does not weaken the default.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article focuses on the &lt;code&gt;.001&lt;/code&gt; form. The &lt;code&gt;.002&lt;/code&gt; graduated rule is the right control for buckets that legitimately serve as public archives (open-data publishing, package mirrors). It turns "is listing public" from a binary unsafe-state question into "is the public listing &lt;em&gt;intended&lt;/em&gt; and labelled."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Control Predicate
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CTL.S3.PUBLIC.LIST.001&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;No Public S3 Bucket Listing&lt;/span&gt;
&lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;high&lt;/span&gt;
&lt;span class="na"&gt;unsafe_predicate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;any&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;properties.storage.access.public_list&lt;/span&gt;
      &lt;span class="na"&gt;op&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eq&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One leaf clause. The engine's &lt;code&gt;public_list&lt;/code&gt; fold is the answer the predicate consults. The raw &lt;code&gt;policy_json&lt;/code&gt; field carries the evidence behind that fold for downstream solver use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproducing The Detection
&lt;/h2&gt;

&lt;p&gt;The repository ships a self-contained example at &lt;code&gt;stave/examples/s3-public-list-policy/&lt;/code&gt;. From the &lt;code&gt;stave/&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run ./examples/s3-public-list-policy before
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fixture under &lt;code&gt;fixtures/before/&lt;/code&gt; is a bucket called &lt;code&gt;acme-public-archive&lt;/code&gt; whose policy admits &lt;code&gt;Principal: "*"&lt;/code&gt; for &lt;code&gt;s3:ListBucket&lt;/code&gt; on the bucket ARN. Two snapshots, captured a week apart, both unsafe. The captured stdout (in &lt;code&gt;expected/before-output.txt&lt;/code&gt;) is byte-for-byte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=== before (vulnerable) ===
  status: NON_COMPLIANT   total_assets=1   violations=1
  CTL.S3.PUBLIC.LIST.001 fired on 1 asset(s):
    - arn:aws:s3:::acme-public-archive   severity=high   exposure_score=100.00
  assertion: fires=true (expected) ✓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;severity=high&lt;/code&gt; rather than &lt;code&gt;critical&lt;/code&gt; — listing is the recon step, not the breach itself. The control fires because the listing exposure makes every subsequent stage cheaper, not because listing alone exfiltrated data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authoring Note: ListBucket Resource ARNs
&lt;/h2&gt;

&lt;p&gt;When operators &lt;em&gt;try&lt;/em&gt; to scope a list policy and get it wrong, the most common mistake is the Resource ARN's arity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; {
   "Effect": "Allow",
   "Principal": {"AWS": "arn:aws:iam::111122223333:role/AcmeArchiveReader"},
   "Action": "s3:ListBucket",
&lt;span class="gd"&gt;-  "Resource": "arn:aws:s3:::acme-public-archive/*"
&lt;/span&gt;&lt;span class="gi"&gt;+  "Resource": "arn:aws:s3:::acme-public-archive"
&lt;/span&gt; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;s3:ListBucket&lt;/code&gt; operates on the bucket, not on objects. The trailing &lt;code&gt;/*&lt;/code&gt; makes the Resource pattern fail to match the bucket itself. Policies that mix this up silently fail to grant listing, the operator panics, and the safest emergency fix gets applied: open up the policy to &lt;code&gt;Principal: "*"&lt;/code&gt;. That's how several of the disclosed reports got there. The bug that produced the policy was a typo. The bug that exposed the data was the panic fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Remediation
&lt;/h2&gt;

&lt;p&gt;Two changes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Scope the policy&lt;/strong&gt; — name the role that legitimately needs to list, and use the bucket-level Resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; {
   "Version": "2012-10-17",
   "Statement": [{
&lt;span class="gd"&gt;-    "Sid": "PublicList",
&lt;/span&gt;&lt;span class="gi"&gt;+    "Sid": "AppRoleListOnly",
&lt;/span&gt;     "Effect": "Allow",
&lt;span class="gd"&gt;-    "Principal": "*",
&lt;/span&gt;&lt;span class="gi"&gt;+    "Principal": {"AWS": "arn:aws:iam::111122223333:role/AcmeArchiveReader"},
&lt;/span&gt;     "Action": "s3:ListBucket",
     "Resource": "arn:aws:s3:::acme-public-archive"
   }]
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Turn on the full Public Access Block&lt;/strong&gt; — same four flags as for public read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api put-public-access-block &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; acme-public-archive &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--public-access-block-configuration&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;BlockPublicAcls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,IgnorePublicAcls&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,&lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;BlockPublicPolicy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,RestrictPublicBuckets&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the bucket genuinely needs to be a public archive, leave the public listing in place but tag it explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api put-bucket-tagging &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; acme-public-archive &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tagging&lt;/span&gt; &lt;span class="s1"&gt;'TagSet=[{Key=public_list_intended,Value=true}]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tag flips the bucket from &lt;code&gt;CTL.S3.PUBLIC.LIST.001&lt;/code&gt; (strict) to &lt;code&gt;CTL.S3.PUBLIC.LIST.002&lt;/code&gt; (graduated) — explicit intent, the posture audit can show it was deliberate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verifying the Fix
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run ./examples/s3-public-list-policy after
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Captured output (&lt;code&gt;expected/after-output.txt&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=== after  (remediated) ===
  status: COMPLIANT   total_assets=1   violations=0
  CTL.S3.PUBLIC.LIST.001: no findings
  assertion: fires=false (expected) ✓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The control is silent. The example exits 0.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Prevention Lesson
&lt;/h2&gt;

&lt;p&gt;Listing is the &lt;strong&gt;stable&lt;/strong&gt; part of the breach. Object names change as new content lands. The bucket's public-list disposition does not. That makes prevention worth investing in:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service Control Policy&lt;/strong&gt; denying public-list-creating actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DenyPublicListPolicies"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"s3:PutBucketPolicy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"s3:PutBucketAcl"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Bool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"s3:PublicAccessBlockConfiguration.RestrictPublicBuckets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Module-level enforcement&lt;/strong&gt; — the IaC bucket module wraps every bucket with PAB on, and a separate &lt;code&gt;public-archive&lt;/code&gt; module exists for the rare cases where listing is intentional. The existence of two distinct modules makes the choice visible in review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI invariant check&lt;/strong&gt; — &lt;code&gt;stave apply&lt;/code&gt; runs against the pre-merge snapshot. A PR that publishes a bucket to public list without setting &lt;code&gt;public_list_intended=true&lt;/code&gt; either fails the strict &lt;code&gt;.001&lt;/code&gt; control (if it shouldn't be public) or graduates to the &lt;code&gt;.002&lt;/code&gt; control with the tag in place.&lt;/p&gt;

&lt;p&gt;The CI gate  catches changes made &lt;em&gt;outside&lt;/em&gt; IaC. A console click that opens listing for "just one bucket, just for debugging" trips the predicate the same way it trips for any other path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Every bucket carries &lt;code&gt;public_list=false&lt;/code&gt; unless the &lt;code&gt;public_list_intended=true&lt;/code&gt; tag is also set&lt;/li&gt;
&lt;li&gt;Bucket policies that grant &lt;code&gt;s3:ListBucket&lt;/code&gt; use the bucket ARN as Resource (not &lt;code&gt;bucket/*&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;PAB has all four flags on for non-archive buckets&lt;/li&gt;
&lt;li&gt;An organization-level SCP denies bucket-policy / bucket-ACL changes that would re-enable public listing&lt;/li&gt;
&lt;li&gt;CI runs &lt;code&gt;stave apply&lt;/code&gt; and blocks on &lt;code&gt;CTL.S3.PUBLIC.LIST.001&lt;/code&gt; findings; graduated buckets surface as &lt;code&gt;CTL.S3.PUBLIC.LIST.002&lt;/code&gt; instead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reports differ in industry and in what data the keys revealed. The configuration that exposed the inventory was identical. Listing is the cheap, stable half of the breach — the half that prevention pays the most to close.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The example is at &lt;a href="https://github.com/sufield/stave/tree/main/examples/s3-public-list-policy" rel="noopener noreferrer"&gt;&lt;code&gt;stave/examples/s3-public-list-policy/&lt;/code&gt;&lt;/a&gt; — a self-contained Go program that loads two fixture snapshots, runs &lt;code&gt;pkg/stave.Apply&lt;/code&gt;, asserts that &lt;code&gt;CTL.S3.PUBLIC.LIST.001&lt;/code&gt; fires on the vulnerable fixture and is silent on the remediated one, and exits zero when both assertions hold. &lt;a href="https://github.com/sufield/stave" rel="noopener noreferrer"&gt;Stave&lt;/a&gt; detects this pattern and 31 other H1-grounded scenarios from local AWS configuration snapshots, with no cloud credentials.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>aws</category>
      <category>cloud</category>
      <category>appsec</category>
    </item>
    <item>
      <title>The CISO Doesn't Read Your CLI Output</title>
      <dc:creator>Bala Paranj</dc:creator>
      <pubDate>Mon, 27 Jul 2026 12:43:41 +0000</pubDate>
      <link>https://dev.to/bala_paranj_059d338e44e7e/the-ciso-doesnt-read-your-cli-output-4mg7</link>
      <guid>https://dev.to/bala_paranj_059d338e44e7e/the-ciso-doesnt-read-your-cli-output-4mg7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;✓ Human-authored analysis; AI used for formatting and proofreading.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Every security tool produces findings. Very few produce decisions.&lt;/p&gt;

&lt;p&gt;Run Prowler against an AWS account and you get a report with 572 checks, each labeled PASS or FAIL, each mapped to a CIS control number. Run Checkov against a Terraform plan and you get a list of policy violations, each with a severity level. Run any CSPM and you get a dashboard with a number — 84% compliant, 312 findings, 47 critical.&lt;/p&gt;

&lt;p&gt;These outputs are designed for the engineer who will fix the problem. They are not designed for the person who decides whether the problem gets fixed.&lt;/p&gt;

&lt;p&gt;The CISO doesn't read your CLI output. Neither does the CFO, the board, the insurance underwriter, or the acquirer in a due diligence process. They read risk narratives. Security programs stall in the gap between what your tool produces and what the decision-maker needs, budgets get cut, and findings age into breaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  The translation problem
&lt;/h2&gt;

&lt;p&gt;A security engineer runs a scan. The scan produces 312 findings. The engineer triages them, identifies the 15 that are critical, and writes a report. The report goes to the CISO. The CISO reads it and asks one question:&lt;/p&gt;

&lt;p&gt;"What does this mean for the business?"&lt;/p&gt;

&lt;p&gt;The engineer can answer: "We have 15 critical findings including overpermissioned IAM roles, public S3 buckets, and missing encryption." The CISO asks: "So what's our risk? Are we going to have an incident? How does this compare to last quarter? Should I be worried?"&lt;/p&gt;

&lt;p&gt;The tool produced findings. The CISO needs a risk posture. The translation between the two is done manually, by the engineer, in a slide deck, using professional judgment. The judgment is valuable. The process is not valuable. Because it's lossy, subjective, inconsistent, and not repeatable. Two engineers looking at the same 312 findings will produce different risk narratives. The same engineer looking at the same findings on different days will produce a different narrative. The CISO is making decisions based on an unreproducible translation of tool output into business language.&lt;/p&gt;

&lt;p&gt;This is a language problem. Security tools speak in findings. Decision-makers speak in risk. Nobody built the translator.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the CISO needs to know
&lt;/h2&gt;

&lt;p&gt;Talk to CISOs about what they need from their security teams and the same five questions come up:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Are we going to get breached?"&lt;/strong&gt; Not "do we have findings" — "do we have findings that combine into something an attacker can use?" A finding that says "this S3 bucket is public" is a data point. A finding that says "your environment matches the configuration pattern that caused the Capital One breach, and the only gate holding is IMDSv2 enforcement on three instances" is a risk assessment. The first generates a ticket. The second generates an executive conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"How bad would it be?"&lt;/strong&gt; Not the severity label on the finding — the business consequence. Critical means different things to different tools. A dollar figure from a precedent incident means the same thing to everyone. "This pattern cost Capital One $300 million in fines and settlements" can be taken to the board by CISO. "Critical severity, CVSS 9.8" can be taken to a triage meeting by an engineer. These are different audiences, decisions, and languages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Is it getting better or worse?"&lt;/strong&gt; Trend, not snapshot. A CISO who sees 312 findings this quarter and 287 last quarter knows the number went up. They don't know whether the risk went up. Maybe the new findings are all low-severity and the critical ones from last quarter were fixed. Maybe the new findings include a compound chain that didn't exist before. The number tells you nothing without composition — what kinds of findings, how they interact, whether the new ones are more or less dangerous than the old ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Can we prove it?"&lt;/strong&gt; To the auditor, underwriter and acquirer. The proof needs to be reproducible. Someone else should be able to verify the claim independently. "We ran our tool and it said we're clean" is an assertion. "Here is the snapshot, here is the control catalog, run the evaluation yourself and you'll get the same findings" is evidence. The difference between assertion and evidence is the difference between trust-me and verify-it, and every stakeholder outside the security team wants the second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Where should I spend the next dollar?"&lt;/strong&gt; The CISO needs to know where incremental investment changes the risk posture most. Should the dollar go to closing the three remaining findings in the Capital One chain (preventing a named $300M incident shape) or to rotating 200 stale IAM access keys (reducing the credential exposure surface)? These are different investments with different returns, and no CLI output helps make the comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why compliance scores fail as the bridge
&lt;/h2&gt;

&lt;p&gt;The industry's attempt to bridge the gap between findings and decisions is the compliance score: a single number that represents security posture. 84% compliant. 47 critical findings. An "A" rating from the security scoring vendor.&lt;/p&gt;

&lt;p&gt;Compliance scores fail as decision inputs for three reasons.&lt;/p&gt;

&lt;p&gt;First, they aggregate without weighting. A compliance score that treats a public S3 bucket the same as a missing CloudTrail tag counts both as one finding. The score doesn't know that the public bucket is an active data exposure and the missing tag is a governance housekeeping item. Moving from 84% to 86% by fixing two tag findings and one bucket finding looks the same whether you fixed the bucket or the tags. The score improved. The risk may not have improved.&lt;/p&gt;

&lt;p&gt;Second, they measure coverage, not exposure. A compliance score says "you checked 572 things and 480 passed." It doesn't say "the 92 that failed combine into three exploitable attack paths." The passing rate can be high and the risk can be high simultaneously, because risk lives in the interactions between failures, not in the failures individually. A 95% compliance score with a live Capital One chain is worse than an 80% compliance score without one.&lt;/p&gt;

&lt;p&gt;Third, they're not reproducible by a third party. A compliance score is generated by a vendor's proprietary algorithm against the vendor's proprietary scan. The CISO can report the score, but the auditor can't verify it independently. The insurance underwriter can't re-run it. The acquirer can't check it against their own analysis. The score is an assertion, not evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the output should look like
&lt;/h2&gt;

&lt;p&gt;The output that bridges the gap between findings and decisions has four properties.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It names the risk, not the finding.&lt;/strong&gt; Not "CTL.EC2.IMDSV2.001: VIOLATION on i-0abc123" but "your environment matches the Capital One breach pattern on 3 instances." The named incident shape connects the technical finding to a business consequence the decision-maker already understands. Capital One is a story the CISO can tell the board. A control ID is not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It quantifies distance, not severity.&lt;/strong&gt; Not "critical severity" but "one configuration change away from an active attack path." Distance-one means the compound isn't active today but would activate if a single control changed. That's more precise than "critical" (which is a static label) and more useful than "number of findings" (which is a count without composition). A CISO can prioritize: distance-zero findings first, distance-one findings second, everything else by remediation cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It cites precedent, not probability.&lt;/strong&gt; Not "EPSS score 0.73" but "this pattern was exploited in 7 documented campaigns between 2019 and 2025, including Capital One ($300M), Uber ($148M fine), and CodeSpaces (company ceased operations)." Probability is abstract. Precedent is concrete. An insurance underwriter can price against precedent. A board member can understand precedent. Nobody outside the security team knows what an EPSS score means.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's independently reproducible.&lt;/strong&gt; The same snapshot, the same control catalog, and the same evaluation engine produce the same findings on anyone's machine. The CISO can share the snapshot with the auditor. The auditor can run the evaluation independently. The findings are evidence, not assertions. This is the property that makes the output trustworthy to stakeholders who don't trust the tool. Because they don't need to trust the tool. They need to trust the snapshot, and they can verify the evaluation themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  The budget consequence
&lt;/h2&gt;

&lt;p&gt;Security programs that speak in findings get compliance budgets. Security programs that speak in risk narratives get security budgets. The difference is structural.&lt;/p&gt;

&lt;p&gt;A CISO who tells the board "we have 312 findings and we need headcount to fix them" is making a compliance argument. The board hears: spend more to check more boxes. The budget is measured against the cost of audit failure, which is knowable and bounded. The CISO gets enough budget to pass the audit and not a dollar more.&lt;/p&gt;

&lt;p&gt;A CISO who tells the board "we are one configuration change away from the pattern that cost Capital One $300 million, and closing that gap requires these three specific changes" is making a risk argument. The board hears: spend this to avoid that. The budget is measured against the cost of the incident, which is real and documented. The CISO gets enough budget to close the gap, because the gap has a name, a dollar figure, and a precedent.&lt;/p&gt;

&lt;p&gt;The difference is the tool output the CISO is translating from. If the tool produces 312 undifferentiated findings, the CISO can only make the compliance argument. If the tool produces named incident shapes with distance classifications and cited precedents, the CISO can make the risk argument. The tool determines the ceiling of the conversation.&lt;/p&gt;

&lt;p&gt;Uber's engineering director said: "These numbers are activity metrics, not business outcomes. When we start talking about costs, our CFO has asked me: what is the impact? I can't point him to the number of diffs, I need to show what's the impact on revenue." Replace "diffs" with "findings" and the statement applies to every security program that reports compliance scores to executive leadership.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shift
&lt;/h2&gt;

&lt;p&gt;The shift isn't from one tool to another. It's from one output format to another.&lt;/p&gt;

&lt;p&gt;The current output: a list of findings, each with a severity label, optionally mapped to a compliance framework control number. This is the language of the security engineer. It's correct, specific, and actionable for the person who will ssh into the instance and change the configuration.&lt;/p&gt;

&lt;p&gt;The needed output: a list of named risk patterns, each with a distance classification (active / one change away / potential), each citing incident precedent with documented business consequences, each independently verifiable from a reproducible artifact. This is the language of the decision-maker. It's correct, specific, and actionable for the person who will allocate budget, accept risk, or report to the board.&lt;/p&gt;

&lt;p&gt;Both outputs come from the same underlying data. The difference is how the data is composed and presented. Findings compose into chains. Chains name incident shapes. Incident shapes cite precedents. Precedents have dollar figures. Dollar figures fund security programs.&lt;/p&gt;

&lt;p&gt;The CISO doesn't read your CLI output. They read the risk narrative that your CLI output should produce but currently doesn't. Close that gap and the CISO becomes your ally instead of your audience. The findings become business cases instead of technical debt. And the security program gets funded against the cost of incidents, not against the cost of audit failure.&lt;/p&gt;

&lt;p&gt;The tool that helps the engineer make the case is the tool that earns its place in the stack. Everything else is a scanner.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cloudsecurity</category>
      <category>leadership</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
