Level 3 - Project Report
30 / 8 / 2026
CANOPY
Level 3 - Project
R Rohan Shalom
Overview
Canopy is an AWS-focused cloud security platform that turns a complicated cloud environment into a clear attack map. Rather than simply reporting isolated misconfigurations, it focuses on a more practical question:
If an attacker starts from the Internet, what sequence of exposed services, IAM permissions, and trust relationships could lead to a sensitive AWS asset?
This transforms security review from a checklist into a path-based risk model. Canopy pieces together AWS identities, network exposure, IAM privileges, and trust paths to determine the most likely exploitation route.
Core Idea
Canopy treats an AWS environment as a security graph. Every resource and identity becomes a node, while exposure, trust, access, and escalation become directed edges.
Example:
INTERNET
↓
Public EC2
↓
IAM Role
↓
Assumable Role / Sensitive Permission
↓
S3 Bucket / Admin Resource
This helps answer not just, "What is wrong?" but more importantly, "How can an attacker get there?"
What Canopy Scans
The platform models core AWS resources including:
- EC2 instances
- Security Groups
- IAM Users and Roles
- S3 Buckets
- Lambda functions
- RDS instances
- API Gateway
- Region-scoped AWS resources
Each resource is normalized into a common abstraction containing:
- resource type
- ARN
- name
- region
- account ID
- associated metadata
- security-relevant properties
This standardization enables consistent graph construction and comparison across heterogeneous AWS services.
Secure AWS Access Model
Canopy avoids requiring long-lived customer credentials. Instead, the customer provides an IAM role ARN, and the platform uses AWS STS AssumeRole to obtain temporary credentials. After that, it verifies the account identity using GetCallerIdentity before running the scan.
This design is important because it keeps access short-lived and reduces the risk of storing or managing permanent AWS keys.
Customer Role ARN
↓
STS AssumeRole
↓
Temporary Credentials
↓
AWS API Access
↓
Resource Discovery + IAM Evaluation
Resource Discovery and Extraction
Canopy uses separate extractors for different AWS services, such as:
- IAMExtractor
- EC2Extractor
- S3Extractor
- LambdaExtractor
- RDSExtractor
- API Gateway Extractor
These extractors run in parallel using a thread pool, which improves scan speed because AWS discovery is largely I/O-bound. The system also includes pagination logic so large AWS responses do not get truncated, and it uses safe wrappers to continue processing if one service fails.
This provides resilience and keeps the scan practical even in large environments.
IAM: The Most Important Layer
IAM is the foundation of Canopy's analysis because it determines who can do what and on which resource. The platform models:
- IAM users
- IAM roles
- permission policies
- trust policies
- role assumptions
- privilege escalation paths
Example permission policy:
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "*"
}
Trust policies define who is allowed to assume a role. This is critical because a resource might look harmless on its own, but if a role is assumable by a compromised identity, the trust chain can dramatically expand the attack surface.
DeveloperRole
↓
CAN_ASSUME
↓
AdminRole
Canopy evaluates IAM logic using a focused policy engine that understands:
- Allow / Deny
- Action / NotAction
- Resource / NotResource
- Principal
- Conditions
- Wildcards
The decision path is:
Explicit Deny?
├─ Yes → DENY
└─ No
↓
Matching Allow?
├─ Yes → ALLOW
└─ No → Implicit DENY
This is not a full AWS authorization engine; it is a purpose-built evaluator for attack analysis.

Network Exposure and Security Groups
Security Groups act as a cloud firewall. For example, opening TCP 22 to 0.0.0.0/0 indicates that SSH might be reachable from anywhere on the internet.
Canopy represents this as a path from the Internet to an EC2 resource:
INTERNET
↓
EXPOSES_PORT
↓
EC2 Instance
This is useful because public exposure is not the same as compromise. It simply signals a possible entry point that may be used in a larger attack chain.
Directed Security Graph
Canopy builds a directed graph using NetworkX. Resources and identities are nodes, and relationships become edges.
Representative edge types include:
EXPOSES_PORTATTACHED_SGHAS_ROLECAN_ASSUMECAN_ACCESSPRIVILEGE_ESCALATIONHAS_ENV_CREDS
This graph is directional because relationships matter in a specific direction. For example:
EC2 ──HAS_ROLE──> IAM Role
does not mean the inverse relationship is equally valid.
The virtual INTERNET node gives the engine a consistent external starting point, which allows a path search to begin from a realistic attacker origin.

Weighted Attack-Path Search
Canopy does not simply do a breadth-first scan. It runs a weighted graph search that resembles Dijkstra's algorithm, where each edge has a modeled attack cost.
Example weights:
EXPOSES_PORT = 0.1
HAS_ROLE = 0.2
CAN_ASSUME = 0.2
CAN_ACCESS = 0.3
PRIVILEGE_ESCALATION = 0.15
This means Canopy can prefer a path with more hops but lower total risk cost over a shorter but more difficult or less likely route.
Search constraints such as MAX_HOPS, MAX_WEIGHT, and MAX_PATHS keep results practical and bounded.
Internet
↓ 0.1
EC2
↓ 0.2
Role
↓ 0.3
S3 Bucket
This path has total modeled cost:
0.1 + 0.2 + 0.3 = 0.6
These values are heuristic and intended to support prioritization rather than reflect official AWS severity values.
Sensitive Targets and Exploitability
Not every resource is equally important. Canopy prioritizes nodes marked as sensitive or administrative, such as:
- S3 buckets containing sensitive data
- IAM roles with high privileges
- admin-level identities
- critical data stores
This ensures the engine focuses on realistic high-value endpoints rather than every minor relationship in the environment.
The risk model classifies paths using a heuristic score such as:
< 0.8 CRITICAL
< 1.5 HIGH
< 2.5 MEDIUM
>= 2.5 LOW
These thresholds are Canopy-defined and used for ranking, not for official AWS scoring.
Blast Radius and Damage Scoring
Attack-path analysis answers: “How can an attacker reach the target?”
Blast radius answers: “If a resource is compromised, what else becomes reachable?”
Canopy computes this by traversing the graph from a compromised node and identifying downstream reachable resources. It then counts both the total affected resources and the sensitivity of those resources.
Example heuristic values:
IAM Role = 10
RDS Instance = 8
IAM User = 7
S3 Bucket = 6
Lambda = 5
EC2 = 4
Security Group = 2
These values are combined into a higher-level damage estimate that is capped for practical scoring.
1 IAM Role + 1 RDS + 2 S3
= 10 + 8 + (2 × 6)
= 30

Overall Security Score
Canopy starts from a base score of 100 and subtracts points for discovered attack paths:
CRITICAL = -25
HIGH = -15
MEDIUM = -8
LOW = -3
Example:
100 - (2 × 25) - 15 = 35
This produces a single account-level risk score that helps prioritize remediation. It is a Canopy-defined aggregate score designed for actionability rather than a formal AWS rating.
AI Narration
Once the graph identifies the highest-risk path, Canopy can pass it to an AI narrator that converts a technical route into plain-language findings.
The process is:
Graph analysis
↓
Critical path identified
↓
AI narration
↓
Readable explanation for stakeholders
This keeps the underlying security logic deterministic while making the final result easier for human users to understand.

System Workflow
The overall Canopy workflow is:
Customer provides Role ARN
↓
STS AssumeRole
↓
Temporary AWS credentials
↓
Parallel resource extraction
↓
IAM policy parsing and evaluation
↓
Graph construction
↓
Weighted attack-path search
↓
Blast-radius calculation
↓
Risk scoring
↓
AI narration + dashboard output
This is a well-structured pipeline: discover, model, analyze, explain, and prioritize.
API Surface
Canopy exposes a lightweight API for scanning and status tracking:
/connect— validate AWS access and assume role/scan— start a scan and create a scan ID/scan/{scan_id}— fetch scan status and results/dashboard/{customer_id}— retrieve latest completed scan for a customer
This allows integration with customer portals, monitoring tools, and dashboards.
Architecture Flow
flowchart TD
A(["POST /scan\n{customer_id}"])
B["upsert_user(customer_id)"]
C["get_role_arn(customer_id) from DB"]
D["create_scan(scan_id, customer_id)\nstatus = 'running'"]
E["Return {scan_id, status: 'running'}\nimmediately to frontend"]
F(["Background Task: _run_scan()"])
A --> B --> C --> D --> E
D --> F
subgraph EXTRACT["① Resource Extraction — extract_all(role_arn)"]
G["STS: AssumeRole → boto3.Session"]
H["ThreadPoolExecutor (4 workers)\nRun all extractors in parallel"]
I1["IAMExtractor\n→ roles, users, policies"]
I2["EC2Extractor\n→ instances, security groups"]
I3["S3Extractor\n→ buckets (public/private)"]
I4["LambdaExtractor\n→ functions + env vars"]
I5["APIGatewayExtractor\n→ REST & HTTP APIs"]
I6["RDSExtractor\n→ DB instances"]
J["Merge all into Resource list\nupdate_resource_count(scan_id, N)"]
G --> H
H --> I1 & I2 & I3 & I4 & I5 & I6 --> J
end
subgraph POLS["② Policy Document Extraction"]
K["Collect all attached_policy ARNs\nfrom resource metadata"]
L["PolicyDocExtractor.extract_docs(arns)\nFetch full JSON policy documents from IAM"]
K --> L
end
subgraph GRAPH["③ Graph Construction — build_graph()"]
M["CanopyGraph (NetworkX DiGraph)"]
N1["Add all resources as nodes\n(id, name, type, arn, is_sensitive, is_admin, …)"]
N2["Add virtual INTERNET node\n(attacker entry point)"]
N3["_add_network_edges()\nINTERNET → internet-facing resources\nEdgeType: EXPOSES_PORT (weight 0.1)"]
N4["_add_iam_edges()\nEC2/Lambda → IAM Role (HAS_ROLE, 0.2)\nRole → Role via trust policy (CAN_ASSUME, 0.2)\nINTERNET → publicly assumable role (0.05)"]
N5["_add_data_edges()\nLambda with suspicious env vars → marked sensitive"]
N6["_add_privilege_escalation_edges()\nNon-admin role with iam:* / PassRole → admin role\nEdgeType: PRIVILEGE_ESCALATION (0.15)"]
N7["add_policy_edges()\nEvaluate IAM policy docs for CAN_ACCESS edges"]
M --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
end
subgraph ENGINE["④ Attack Path Engine — AttackPathEngine.find_all()"]
O1["Identify target nodes\n(is_sensitive OR is_admin)"]
O2["For each target: Dijkstra-like\nheap search from INTERNET node\nmax 6 hops, max weight 4.0, max 10 paths/target"]
O3["Build AttackPath objects\n(hops, score, exploitability)"]
O4["Deduplicate by target+edge_type signature\nSort by score (ascending = easier)"]
O5["Cap at top 30 paths"]
O1 --> O2 --> O3 --> O4 --> O5
end
subgraph BLAST["⑤ Blast Radius Calculation"]
P1["BlastRadiusCalculator.calculate(target_id)"]
P2["nx.descendants() — find all reachable nodes"]
P3["Score based on resource damage weights\n(IAM Role=10, RDS=8, IAM User=7,\nS3=6, Lambda=5, EC2=4, SG=2)"]
P4["Estimate recovery hours\nFlag can_delete_account if any admin reachable"]
P1 --> P2 --> P3 --> P4
end
subgraph AI["⑥ AI Narration — narrate_path()"]
Q1["Top 3 CRITICAL paths only"]
Q2["Build prompt with hops, score,\nblast radius, target name"]
Q3["Gemini 2.5 Flash\nresponse_mime_type=application/json"]
Q4["Return: headline, story, business_impact,\nfix, fix_time, attacker_difficulty,\ntime_to_exploit"]
Q1 --> Q2 --> Q3 --> Q4
end
subgraph STORE["⑦ Persist Results"]
R1["Security Score = 100 - max(risk_score of all paths)"]
R2["complete_scan() → MongoDB\n• status = complete\n• score, resource_count, node_count, edge_count\n• attack_paths[] (embedded)\n• graph_data {nodes[], links[]} (D3 format)"]
R1 --> R2
end
F --> EXTRACT --> POLS --> GRAPH --> ENGINE
ENGINE --> BLAST --> AI --> STORE
Why Canopy Stands Out
Most tools identify problems in isolation. Canopy connects them across the AWS environment to show how an attacker could move from one compromised element to another. That is the key difference.
It is not just detecting exposure. It is modeling realistic exploitation paths by combining:
- public access
- IAM trust relationships
- privilege escalation
- resource connectivity
- sensitive target prioritization
This creates a more meaningful and actionable security output.
Bottom Line
Canopy is a graph-driven AWS security analysis platform that answers the question the industry most often misses: not merely "what is wrong," but "how can an attacker move through the environment and reach something valuable?"
Its value lies in turning AWS configuration data into an actionable attack narrative, making high-risk paths visible, prioritized, and understandable.
Dashboard:


