11from functools import lru_cache
22from attr import frozen , define
3+ import networkx
34from fix_plugin_aws .resource .base import AwsAccount , AwsResource , GraphBuilder
45
56from typing import Dict , List , Literal , Set , Optional , Tuple , Union , Pattern
67
8+ from networkx .algorithms .dag import is_directed_acyclic_graph
9+
710from fixlib .baseresources import (
811 PermissionCondition ,
912 PolicySource ,
2124from policy_sentry .querying .actions import get_action_data , get_actions_matching_arn
2225from policy_sentry .querying .all import get_all_actions
2326from policy_sentry .util .arns import ARN , get_service_from_arn
27+ from fixlib .graph import EdgeKey
2428import re
2529import logging
2630
@@ -63,13 +67,30 @@ def find_allowed_action(policy_document: PolicyDocument, service_prefix: str) ->
6367 return allowed_actions
6468
6569
70+ def find_non_service_actions (resource_arn : str ) -> Set [IamAction ]:
71+ try :
72+ splitted = resource_arn .split (":" )
73+ service_prefix = splitted [2 ]
74+ if service_prefix == "iam" :
75+ resource_type = splitted [5 ]
76+ resource = resource_type .split ("/" )[0 ]
77+ if resource == "role" :
78+ return {"sts:AssumeRole" }
79+ except Exception as e :
80+ log .info (f"Error when trying to get non-service actions for ARN { resource_arn } : { e } " )
81+ return set ()
82+
83+
6684def find_all_allowed_actions (all_involved_policies : List [PolicyDocument ], resource_arn : str ) -> Set [IamAction ]:
6785 resource_actions = set ()
6886 try :
6987 resource_actions = set (get_actions_matching_arn (resource_arn ))
7088 except Exception as e :
7189 log .debug (f"Error when trying to get actions matching ARN { resource_arn } : { e } " )
7290
91+ if additinal_actions := find_non_service_actions (resource_arn ):
92+ resource_actions .update (additinal_actions )
93+
7394 service_prefix = ""
7495 try :
7596 service_prefix = get_service_from_arn (resource_arn )
@@ -793,15 +814,12 @@ def _get_role_based_policies(self, principal: AwsIamRole) -> List[Tuple[PolicySo
793814
794815 def add_access_edges (self ) -> None :
795816
796- principal_arns = set ([p .principal .arn for p in self .principals ])
797-
798817 for node in self .builder .nodes (clazz = AwsResource , filter = lambda r : r .arn is not None ):
799818
800- if node .arn in principal_arns :
801- # do not create cycles
802- continue
803-
804819 for context in self .principals :
820+ if context .principal .arn == node .arn :
821+ # small graph cycles avoidance optimization
822+ continue
805823
806824 resource_policies : List [Tuple [PolicySource , PolicyDocument ]] = []
807825 if isinstance (node , HasResourcePolicy ):
@@ -821,3 +839,29 @@ def add_access_edges(self) -> None:
821839 reported = to_json ({"permissions" : permissions , "access" : access }, strip_nulls = True )
822840
823841 self .builder .add_edge (from_node = context .principal , edge_type = EdgeType .iam , reported = reported , node = node )
842+
843+ all_principal_arns = {p .principal .arn for p in self .principals if p .principal .arn }
844+
845+ # check that there are no cycles in the IAM edges besides the principal -> principal edges
846+ iam_edges_no_double_principal = []
847+ for edge in self .builder .graph .edges (keys = True ):
848+ if len (edge ) != 3 :
849+ continue
850+
851+ # skip non-iam edges
852+ key : EdgeKey = edge [2 ]
853+ if key .edge_type != EdgeType .iam :
854+ continue
855+
856+ # skip the principal -> principal edges
857+ if key .src .arn in all_principal_arns and key .dst .arn in all_principal_arns :
858+ continue
859+
860+ iam_edges_no_double_principal .append (edge )
861+
862+ # check for loops:
863+ subgraph = self .builder .graph .edge_subgraph (iam_edges_no_double_principal )
864+ if not is_directed_acyclic_graph (subgraph ):
865+ cycle = [edge [2 ] for edge in networkx .algorithms .cycles .find_cycle (subgraph )]
866+ desc = ", " .join (f"{ key .edge_type } : { key .src .kdname } -->{ key .dst .kdname } " for key in cycle )
867+ log .error (f"IAM graph of account { self .builder .account .arn } is not acyclic! Cycle { desc } " )
0 commit comments