Most teams understand the idea of code-to-cloud security — a vulnerability only matters as much as where it runs and what it can reach. Far fewer have a clear picture of how a platform actually makes that connection. What does “correlating code with cloud” mean mechanically? What data has to exist? How does a finding in a Python file on line 42 get linked to an internet-facing workload and an IAM role three systems away?
This article is the engineering-level answer. It walks through the asset graph that makes correlation possible, what each layer contributes, how the repository-to-runtime join is actually formed, how the graph is traversed to produce an attack path, and how severity is computed from that path. If you have read what is code to cloud security for the concept, this is the companion piece on the machinery underneath it.
Why correlation needs a graph, not a database join
The intuitive assumption is that correlation is a database lookup: take a code finding, look up the workload it maps to, done. In practice it is nothing like a single lookup, because the relationship between a line of code and a running cloud resource is indirect and multi-hop. A commit becomes an artifact, which becomes an image, which is deployed as a workload, which assumes a role, which has permissions on data. Each arrow is a separate relationship owned by a separate system (your SCM, your CI/CD, your registry, your orchestrator, your cloud IAM).
Relational tables model this badly. Every “which resources can this one reach” question becomes a chain of joins whose depth you do not know in advance, and the interesting questions — is there any path from the internet to sensitive data that passes through this finding? — are inherently recursive. A graph models it naturally: resources are nodes, relationships are typed edges, and questions about reachability become traversals. This is why the foundation of correlation is a cloud security graph rather than a conventional data warehouse.
So the first mental model to adopt: correlation is not “join finding to workload.” It is “place every entity — code and cloud — as nodes on one graph with typed edges, then walk the graph.”
The layers that feed the graph
A correlation graph is only as good as the entities and relationships you put into it. Five layers contribute, and each answers a different question about risk.
1. The code layer — what is wrong, and where it lives
This is what a standard code scanner produces, but modelled as graph nodes rather than a flat report:
- Repositories, branches, and commits — the identity of the code.
- SAST findings — vulnerability-class issues in first-party code (injection sinks, weak crypto, missing auth checks), each attached to a file, a line, and a commit.
- SCA findings — vulnerable direct and transitive dependencies, ideally with reachability, matched against a vulnerability database. See software composition analysis.
- Secrets — hardcoded credentials and keys, including those buried in git history.
- IaC findings — misconfigurations in Terraform, CloudFormation, and Kubernetes manifests, detected before they are provisioned. See infrastructure as code security.
- SBOM — the per-repository bill of materials that enumerates components.
On its own, this layer is the “left side” of the problem — rich detail about what is wrong, zero information about whether it runs or matters.
2. The build and artifact layer — the bridge
This is the layer most code scanners never model, and it is the single most important bridge in the whole system. It records:
- Which pipeline run built which artifact from which commit.
- Which container image (by digest) that artifact became.
- Which registry the image was pushed to.
Without this layer there is no honest way to connect a repository to a running workload, because the thing running in production is an image, not a repo. The build layer is what lets you say “this image digest was built from this commit” — the hinge the entire join swings on.
3. The cloud and workload layer — where it runs
This is the “right side”: the live cloud estate, discovered continuously across providers.
- Workloads — ECS tasks, EKS pods, EC2 instances, Lambda functions, and the images they run (by digest — which is how they connect back to layer 2).
- Network exposure — load balancers, security groups, public IPs, and ingress paths that determine whether a workload is reachable from the internet.
- Posture — misconfigurations on the services around the workload, from CSPM.
This layer answers is it deployed and is it exposed.
4. The identity layer — what it can reach
A workload is only as dangerous as the permissions it carries.
- IAM roles, policies, and trust relationships — what the workload is allowed to do.
- Effective permissions — the resolved reality after policies, boundaries, and trust are combined, from CIEM.
This layer answers if an attacker lands here, what can they touch next — the blast-radius question.
5. The data layer — what is at stake
Finally, the thing worth protecting:
- Data stores — databases, buckets, and the sensitivity of what they hold (PII, secrets, regulated data).
- Access edges — which identities can read or write them.
This layer answers is there anything behind this path worth an attacker’s effort. A path that terminates at an empty scratch bucket is not the same as one that ends at an unencrypted store of customer records.
How the join is actually formed
Here is the mechanical heart of correlation — the specific edges that connect the five layers into one walkable graph:
CODE LAYER BRIDGE CLOUD LAYER
────────── ────── ───────────
commit ──built_by──▶ pipeline_run ──produces──▶ image(digest)
│
run_as (digest match)
▼
workload
│ │
exposed_via assumes
│ │
▼ ▼
load_balancer IAM role
(public?) │
can_access
▼
data store
(sensitive?)
The three joins that matter most, and are hardest to get right:
- commit → image. Formed from build metadata (layer 2). This is the join that turns “a finding in a repo” into “a finding in a specific deployable artifact.” If your tooling cannot observe the build, this join is guesswork.
- image → workload. Formed by matching the image digest a workload is actually running against the digest produced by the build. Matching on digest rather than tag is what makes this trustworthy — tags are mutable, digests are not.
- workload → identity → data. Formed from live cloud discovery and IAM resolution (layers 3–5). This is what converts “it runs” into “it runs with these permissions over this data.”
When all three joins exist for a given service, a code finding is no longer an isolated row — it is a node with a path to the real world. The honest caveat worth stating plainly: this chain is only complete when the code, image, and runtime layers are actually connected for that service. It is not automatic for every repository the instant you connect it; a repo whose artifacts you cannot yet observe running will still be scanned, but its findings will lack the runtime half of the context until the deployment is linked.
Traversing the graph: from nodes to an attack path
Once the entities and edges exist, correlation becomes a traversal problem. The question “which findings are exploitable” is answered by walking the graph looking for paths that satisfy all four risk conditions at once: reachable code, deployed artifact, exposed workload, valuable target.
Conceptually, the traversal starts from two directions and meets in the middle:
- From the outside in: start at internet-exposed nodes (public load balancers, open security groups) and walk inward along edges to see what they lead to.
- From the crown jewels out: start at sensitive data stores and walk backward through the identities and workloads that can reach them.
Where an outside-in path and a crown-jewels-out path intersect at a workload that is running an image built from a commit with a reachable finding, you have a complete attack path: public endpoint → vulnerable workload → over-permissive role → sensitive data. That specific chain — not any single node on it — is the unit the platform surfaces. The deeper treatment of how these paths are scored and ranked is in what is attack path analysis.
This is also why a graph beats a report: the same traversal engine answers questions a static list never could. Which internet-facing workloads run code with a KEV-listed CVE and can reach PII? is one query over the graph, not a manual cross-reference of five dashboards.
How severity is computed from the path
The payoff of all this structure is that severity stops being a static label copied from a rule and becomes a value computed from the path. The inputs that shape it:
- Rule class — the inherent nature of the finding (an injection sink is inherently more serious than a style issue). This is where a standalone scanner stops.
- Exploit likelihood — EPSS probability and CISA KEV membership, so confirmed-exploited and likely-to-be-exploited issues rise. See what is EPSS and the CISA KEV catalog.
- Reachability — whether the vulnerable code is actually invoked, which demotes the large volume of present-but-unreachable dependency findings.
- Exposure — whether the workload running the code is reachable from the internet.
- Blast radius — what the workload’s identity can reach if compromised.
- Data sensitivity — whether the path terminates at something regulated or valuable.
A finding that scores high on rule class but has no deployed artifact, no exposure, and no sensitive target behind it is correctly demoted. A finding that scores “medium” by rule class but sits on a complete internet-to-PII path is correctly escalated. This is the mechanism behind contextual severity for code: a code or IaC finding borrows its real-world weight from the runtime it becomes, because the finding itself has no runtime context of its own.
The practical outcome is a single prioritised queue ordered by exploitability in your environment — shared across AppSec and cloud teams — rather than two separate backlogs each sorted by a number that ignored your topology.
Why this is hard to bolt on afterward
A recurring question is whether you can get the same result by exporting findings from a code scanner and findings from a cloud tool and reconciling them later. In practice this fails for a structural reason: the join depends on the build and digest metadata and on live, typed relationships that a flat CSV export does not carry. Once a finding has been flattened into a report row, the edges that make it correlatable — which image it became, which workload runs that digest, which role that workload assumes — are gone. You cannot reconstruct a graph traversal from two lists of findings that were never on the same graph.
That is why genuine code-to-cloud correlation requires code and cloud to be ingested onto one asset graph from the start, with one rule engine evaluating across them. It is an architectural property, not a feature you add after the fact.
How Cloudanix implements this
Cloudanix is a CNAPP+ built on exactly this model: a single Cartography-style asset graph spanning 300+ resource types with typed relationships, ingesting the code layer and the cloud layer onto the same graph rather than keeping them in separate products.
- Code layer: Cloudanix Code Security contributes SAST, SCA, secrets, repo-threat, and SBOM findings, with dependency vulnerabilities matched against a 28-feed vulnerability database enriched with KEV and EPSS.
- Cloud, identity, and data layers: CSPM, CIEM, workload inventory, and data context are discovered continuously and placed on the same graph.
- The join and traversal: repo → image (by digest) → workload → role → data is resolved on the graph when those layers are connected, and the traversal engine surfaces complete attack paths rather than isolated findings.
- Contextual severity: each code finding is scored from the real path via contextual severity, not from rule class alone.
To see it on your own estate, the fastest path is to connect a repository and a cloud account and watch the digest-level join resolve a real path end to end: book a free assessment.