Skip to content

NetworkEdge

Section 5 · From docs/INVENTORY_REFERENCE.md on GitHub

cloudg.schema.models.NetworkEdge, a Pydantic v2 model (extra="allow"). A directed edge between two assets.

5.1 Fields

Field Type Default Meaning
id str new UUID4 Edge ID, regenerated each run.
source_id str required Source endpoint. For edges produced by the inventory linker and mapper this is always an asset id. See 5.3 for the exception.
target_id str required Target endpoint, same rules.
edge_type EdgeType required Coarse class; see section 6.
relationship str \| None None Fine-grained relation, a RelationType name from the ontology (TRIGGERED_BY, RUNS_ON, CROSS_ACCOUNT_TRUST, ...). None when the edge class says it all. See the vocabulary.
properties dict[str, Any] {} Relation-specific detail copied from the declaration (empty values dropped), plus linker annotations (below).
description str \| None None Human-readable explanation ("event source mapping", "arn:aws:iam::999999999999:root can assume this role").
ports list[int] [] Network edges only. Capped at 100 ports per range, so a wide range lists only its first 100; use port_range.
port_range str \| None None Network edges only, in the provider's notation: see 5.3. "443", "80-443", "0-65535", "22,3389,8000-8100", "*".
protocol str \| None None Network edges only, with the provider's casing: TCP, UDP, ICMP, ALL from AWS and GCP, Tcp, Udp, Icmp, * from Azure.
cidr str \| None None Network edges only: "0.0.0.0/0".
direction str "ingress" ingress or egress for network rule edges. Relationship edges keep the default "ingress", which carries no meaning for them.

5.2 properties keys added by the linker and mapper

Key Added by Meaning
external_reference linker The edge points at an external account placeholder; this is the identifier that was referenced (the specific role ARN or bucket in the unmapped account).
hierarchy add_account_hierarchy true on account → top-level resource CONTAINS edges. These edges are excluded from unlinked_assets, cross_account_edges and dependency walks.

Every other key comes from the collector's declaration. Common ones: cross_account (true when the grantee lives in another account), conditions (the IAM condition block of a trust statement), external_id_required, actions, access_policies (EKS access entries), role, privileged, principal_type, assignment_id (Azure role assignments), events (S3 notification events), batch_size, state, network_rule, status, key_uri, via.

5.3 Collector network edges

The AWS collector's own collect_edges() adds two kinds of edges before linking:

  • SECURITY_GROUP_RULE, one per rule and IPv4 CIDR (IpRanges; IPv6 ranges and rules that name another security group are not turned into edges), between the CIDR string and the security group asset's id: ingress edges go cidr → <sg asset id>, egress edges <sg asset id> → cidr. The native group ID stays in the asset's metadata.group_id and arn. port_range is "from-to", or a single port when both are equal; protocol -1 (all traffic) is written as protocol ALL with "0-65535", other protocols upper-case. For ICMP the two numbers are the ICMP type and code, so an echo-request rule reads "8--1". ports is filled on ingress edges only; cidr and direction on both.
  • CONTAINS from a VPC to every asset whose metadata.vpc_id names it (subnets, instances, security groups, load balancers and so on), between asset UUIDs, with no relationship and the description "VPC <vpc_id> contains <name>".

The Azure collector's collect_edges() adds SECURITY_GROUP_RULE edges for NSG inbound allow rules, from the rule's source address prefix (a CIDR, a service tag such as Internet, *, or an application security group's resource ID when it was collected) to the NSG asset, and CONTAINS / VPC_CONTAINS_SUBNET edges from each VNet to its subnets. port_range is the rule's destination port ranges joined with commas ("22,3389,8000-8100") or "*" for every port, protocol is Azure's own spelling (Tcp, Udp, Icmp, *), and description is "NSG rule <name>".

The GCP collector adds one edge per internet ingress entry from 0.0.0.0/0 to the exposed asset: INTERNET_EXPOSED for exposure found through IAM, external IPs or load balancers, and SECURITY_GROUP_RULE for firewall rules. Every one of them has relationship: "INTERNET_REACHABLE" and the way the exposure was found as its description. port_range is the firewall's ports list joined with commas, or null when the rule lists no ports (which for TCP, UDP and SCTP means every port), and protocol is upper-case, ALL for all.

Every consumer in cloudg (the graph builder, the reachability analysis, the ontology) reads these strings through cloudg.graph.ports.parse_port_ranges() and edge_port_ranges(), which turn all three notations into numeric ranges and treat ICMP and other port-less protocols as opening no ports.

The security group / NSG side of a rule edge is always an asset id; the other side is deliberately external. Consumers that join edges to assets must therefore expect a few endpoints that are not asset IDs: CIDRs, Azure service tags and *, and the resource ID of an application security group that was not collected. The graph exports turn them into nodes with is_external: true (section 15), and DependencyGraph ignores edges whose endpoints are not assets.

{
  "id": "8cbfec40-e9a8-4546-bf1f-2f47d542b8b3",
  "source_id": "0.0.0.0/0",
  "target_id": "6f0d2c8e-3b1a-4f57-9c2e-8a41d7e5b903",
  "edge_type": "SECURITY_GROUP_RULE",
  "ports": [443],
  "port_range": "443",
  "protocol": "TCP",
  "cidr": "0.0.0.0/0",
  "direction": "ingress",
  "description": null,
  "relationship": null,
  "properties": {}
}

5.4 Examples of linked edges

Cross-account trust, from the external vendor account to a role (the role's trust policy requires an external ID):

{
  "id": "d9c70eec-a4c4-4144-ab2f-27f67e00b6c5",
  "source_id": "277ef061-8a0c-4e1e-9451-a50aa57198f8",
  "target_id": "1bd88609-88f3-46d7-868e-ac65ece6b65a",
  "edge_type": "IAM_TRUST",
  "ports": [], "port_range": null, "protocol": null, "cidr": null,
  "direction": "ingress",
  "description": "arn:aws:iam::999999999999:root can assume this role",
  "relationship": "CROSS_ACCOUNT_TRUST",
  "properties": {
    "conditions": {"StringEquals": {"sts:ExternalId": "vendor-123"}},
    "external_id_required": true,
    "external_reference": "arn:aws:iam::999999999999:root"
  }
}

The external ID value shown is the one in the trust policy of the test fixture. Trust policy conditions are copied as written, so treat properties.conditions with the same care as the policy itself.

S3 event notification invoking a function (arn:aws:s3:::orders-uploads → …:function:orders-worker):

{
  "source_id": "2028991c-a3b7-4c55-afde-01ccac602552",
  "target_id": "60cac611-8a80-469e-bca6-ee3cf1cdc35b",
  "edge_type": "INVOKES",
  "description": "event notification",
  "relationship": "INVOKES",
  "properties": {"events": ["s3:ObjectCreated:*"]}
}

Account hierarchy:

{
  "source_id": "95628cf7-805e-4b30-989c-189c6da80ef6",
  "target_id": "8d7d700b-7f64-4821-97b2-45577d1faf7a",
  "edge_type": "CONTAINS",
  "description": "account 123456789012 contains FullAWSAccess",
  "relationship": "ACCOUNT_CONTAINS_REGION",
  "properties": {"hierarchy": true}
}