From 697089a1640e6a93a1f927d21453ce1080658fb4 Mon Sep 17 00:00:00 2001 From: Eric Conklin Date: Sat, 6 Jun 2026 01:11:00 -0500 Subject: [PATCH] Calibrate AdministratorAccess admin reachability --- iamscope/reasoner/admin_reachability.py | 62 +++++++- .../fixture_a_validated_one_admin.json | 2 +- ...xture_b_validated_two_admins_critical.json | 2 +- .../fixture_c_hyperedge_inconclusive.json | 2 +- .../test_identity_deny_pipeline.py | 8 +- tests/test_admin_reachability_reasoner.py | 146 +++++++++++++++++- 6 files changed, 213 insertions(+), 9 deletions(-) diff --git a/iamscope/reasoner/admin_reachability.py b/iamscope/reasoner/admin_reachability.py index 881500e..e39836a 100644 --- a/iamscope/reasoner/admin_reachability.py +++ b/iamscope/reasoner/admin_reachability.py @@ -66,6 +66,7 @@ _ASSUMEROLE_ACTION: str = "sts:AssumeRole" _MAX_DEPTH: int = 4 +_AWS_MANAGED_ADMINISTRATOR_ACCESS_ARN: str = "arn:aws:iam::aws:policy/AdministratorAccess" class AdminReachabilityReasoner: @@ -159,13 +160,16 @@ def _compute_reachability( current_node, ) if admin_witness is not None: + admin_witness_is_clean = self._is_clean_admin_witness(admin_witness, current_node) reachable_admins.add(current_arn) self._append_unique_edge(admin_witness_edges, admin_witness) - if path_is_clean: + if path_is_clean and admin_witness_is_clean: clean_reachable_admins.add(current_arn) self._append_unique_edge(clean_admin_witness_edges, admin_witness) self._append_unique_edges(clean_proof_walk_edges, path_edges) else: + if not admin_witness_is_clean: + any_hyperedge_traversed = True ambiguous_reachable_admins.add(current_arn) # Stop walking deeper if depth limit hit. @@ -277,6 +281,33 @@ def _is_ambiguous_edge(self, edge: Edge) -> bool: return _is_unknown_witness(edge) + def _is_clean_admin_witness(self, edge: Edge, target_role: Node) -> bool: + """Return True when an admin witness is clean enough for VALIDATED. + + Arbitrary wildcard admin-like policies remain conservative. + The one supported clean wildcard admin witness is the exact AWS + managed AdministratorAccess policy, represented as an Allow over + resource "*" with no conditions, attached to the role being + classified as admin-equivalent. + """ + if not edge.edge_type.endswith("_permission"): + return False + if edge.src.provider_id != target_role.provider_id: + return False + if not _is_admin_equivalent_wildcard_permission_edge(edge): + return False + if edge.features.get("effect") != "Allow": + return False + if edge.features.get("resource_pattern") != "*": + return False + if edge.features.get("has_conditions") is True: + return False + if edge.features.get("raw_conditions") not in ({}, None): + return False + if edge.features.get("parse_status") not in (None, "", "complete"): + return False + return _policy_arn_from_edge(edge) == _AWS_MANAGED_ADMINISTRATOR_ACCESS_ARN + def _append_unique_edge(self, edges: list[Edge], edge: Edge) -> None: """Append edge once, preserving first-seen deterministic order.""" if edge.edge_id not in {existing.edge_id for existing in edges}: @@ -524,13 +555,13 @@ def _build_finding( elif has_clean_admin_path: check_3_reason = "all BFS paths use clean witness edges" else: - check_3_reason = "BFS walk traversed at least one wildcard/hyperedge edge" + check_3_reason = "reachable admin path or admin witness uses wildcard/hyperedge evidence" check_results.append( Check( name="at_least_one_reachable_chain_uses_clean_witnesses", description=( "At least one BFS path from source to a reachable admin " - "traverses only non-wildcard, non-hyperedge edges" + "uses only clean AssumeRole and admin-equivalence witnesses" ), state=check_3_state, evidence_refs=tuple(edge_refs), @@ -554,7 +585,7 @@ def _build_finding( reason=( "clean path with ambiguous alternate walk" if has_clean_admin_path and any_hyperedge_traversed - else ("clean walk" if has_clean_admin_path else "ambiguity in walk") + else ("clean walk" if has_clean_admin_path else "ambiguity in walk or admin witness") ), ) ) @@ -780,3 +811,26 @@ def _absorb_digests( int(ref.get("statement_index", 0)), ref.get("summary", ""), ) + + +def _is_admin_equivalent_wildcard_permission_edge(edge: Edge) -> bool: + action = edge.edge_type[: -len("_permission")] + if action in ("*", "iam:*"): + return True + action_matched_via = edge.features.get("action_matched_via") + return action_matched_via in ("wildcard_star", "wildcard_iam") + + +def _policy_arn_from_edge(edge: Edge) -> str: + policy_arn = edge.features.get("policy_arn") + if isinstance(policy_arn, str) and policy_arn: + return policy_arn + allow_controls = edge.features.get("allow_controls") + if isinstance(allow_controls, list): + for control in allow_controls: + if not isinstance(control, dict): + continue + control_policy_arn = control.get("policy_arn") + if isinstance(control_policy_arn, str) and control_policy_arn: + return control_policy_arn + return "" diff --git a/tests/fixtures/expected_output/findings/admin_reachability/fixture_a_validated_one_admin.json b/tests/fixtures/expected_output/findings/admin_reachability/fixture_a_validated_one_admin.json index 70e6399..8a537ae 100644 --- a/tests/fixtures/expected_output/findings/admin_reachability/fixture_a_validated_one_admin.json +++ b/tests/fixtures/expected_output/findings/admin_reachability/fixture_a_validated_one_admin.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"collection_context":{"affected_accounts":[],"coverage_notes":[],"graph_collection_complete":true,"has_collection_failures":false,"has_policy_parse_failures":false,"related_collection_failures":[],"related_policy_parse_failures":[]},"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice"],"reason":"source has assumerole edges in walk","result":"PASS","step":1},{"action":"check_reaches_at_least_one_admin","inputs":["arn:aws:iam::11111\u003111111\u0031:role/Admin"],"reason":"1 admin endpoints reachable","result":"PASS","step":2},{"action":"check_at_least_one_reachable_chain_uses_clean_witnesses","inputs":["False"],"reason":"clean walk","result":"PASS","step":3},{"action":"check_walk_terminated_within_depth_limit","inputs":["4"],"reason":"terminated naturally","result":"PASS","step":4}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111","22222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u00322222","33333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u00333333","44444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u00344444","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"22222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u00322222":["arn:aws:iam::11111\u003111111\u0031:role/DevOps",0,"trust arn:aws:iam::11111\u003111111\u0031:user/Alice"],"33333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u00333333":["arn:aws:iam::11111\u003111111\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"44444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u00344444":["arn:aws:iam::11111\u003111111\u0031:role/Admin",0,"trust arn:aws:iam::11111\u003111111\u0031:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"5d731f72f752ac6c933bfdb1c5029fdb486881e8b3f731478b07c18798ab4f78","finding_key":"1265c7a65c1559c5d4c7296be2a73c50f595ddca927e9bc2c835a0c32ab10c8e","pattern_id":"admin_reachability","pattern_title":"Admin Reachability via AssumeRole Chains","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; reaches 1 admin-equivalent role(s)","required_checks":[{"description":"Source principal has at least one sts:AssumeRole permission edge","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"source_has_assumerole_permissions","reason":"source has 1 sts:AssumeRole permission edges","state":"pass"},{"description":"BFS walk reaches at least one admin-equivalent role within depth limit","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"reaches_at_least_one_admin","reason":"reachable admins: 1 (arn:aws:iam::11111\u003111111\u0031:role/Admin)","state":"pass"},{"description":"At least one BFS path from source to a reachable admin traverses only non-wildcard, non-hyperedge edges","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"at_least_one_reachable_chain_uses_clean_witnesses","reason":"all BFS paths use clean witness edges","state":"pass"},{"description":"BFS walk terminated naturally within 4 hops (reported admin set is complete)","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"walk_terminated_within_depth_limit","reason":"walk terminated naturally within 4 hops","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/Admin","region":"-"},"title":"Validated admin reachability: arn:aws:iam::11111\u003111111\u0031:user/Alice can reach 1 admin role","verdict":"validated"},{"assumptions":[],"blockers_observed":[],"collection_context":{"affected_accounts":[],"coverage_notes":[],"graph_collection_complete":true,"has_collection_failures":false,"has_policy_parse_failures":false,"related_collection_failures":[],"related_policy_parse_failures":[]},"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::11111\u003111111\u0031:role/DevOps"],"reason":"source has assumerole edges in walk","result":"PASS","step":1},{"action":"check_reaches_at_least_one_admin","inputs":["arn:aws:iam::11111\u003111111\u0031:role/Admin"],"reason":"1 admin endpoints reachable","result":"PASS","step":2},{"action":"check_at_least_one_reachable_chain_uses_clean_witnesses","inputs":["False"],"reason":"clean walk","result":"PASS","step":3},{"action":"check_walk_terminated_within_depth_limit","inputs":["4"],"reason":"terminated naturally","result":"PASS","step":4}],"statement_digests":["33333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u00333333","44444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u00344444","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"33333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u00333333":["arn:aws:iam::11111\u003111111\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"44444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u00344444":["arn:aws:iam::11111\u003111111\u0031:role/Admin",0,"trust arn:aws:iam::11111\u003111111\u0031:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"b8050c047852ba77c0308890373db1bd9492fd00add431c2368b1d087fe6fcfb","finding_key":"9e5c110dd4c2e611ce7384c6e8d179838ec03e700e2cd34e3a36a0a1cfe6cbfe","pattern_id":"admin_reachability","pattern_title":"Admin Reachability via AssumeRole Chains","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; reaches 1 admin-equivalent role(s)","required_checks":[{"description":"Source principal has at least one sts:AssumeRole permission edge","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5"],"name":"source_has_assumerole_permissions","reason":"source has 1 sts:AssumeRole permission edges","state":"pass"},{"description":"BFS walk reaches at least one admin-equivalent role within depth limit","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"reaches_at_least_one_admin","reason":"reachable admins: 1 (arn:aws:iam::11111\u003111111\u0031:role/Admin)","state":"pass"},{"description":"At least one BFS path from source to a reachable admin traverses only non-wildcard, non-hyperedge edges","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5"],"name":"at_least_one_reachable_chain_uses_clean_witnesses","reason":"all BFS paths use clean witness edges","state":"pass"},{"description":"BFS walk terminated naturally within 4 hops (reported admin set is complete)","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5"],"name":"walk_terminated_within_depth_limit","reason":"walk terminated naturally within 4 hops","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"high","source":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/DevOps","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/Admin","region":"-"},"title":"Validated admin reachability: arn:aws:iam::11111\u003111111\u0031:role/DevOps can reach 1 admin role","verdict":"validated"}],"metadata":{"canonical_hash":"e952ec0e86e878f7a7e4083b44fd26ae8ab0ec52fc5889b9a6b65e97607940d2","collector":"iamscope","collector_version":"0.2.0","findings_count":2,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["admin_reachability"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":2}},"reasoner_versions":{"admin_reachability":"1.0.0"},"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[],"collection_context":{"affected_accounts":[],"coverage_notes":[],"graph_collection_complete":true,"has_collection_failures":false,"has_policy_parse_failures":false,"related_collection_failures":[],"related_policy_parse_failures":[]},"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:user/Alice"],"reason":"source has assumerole edges in walk","result":"PASS","step":1},{"action":"check_reaches_at_least_one_admin","inputs":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin"],"reason":"1 admin endpoints reachable","result":"PASS","step":2},{"action":"check_at_least_one_reachable_chain_uses_clean_witnesses","inputs":["False"],"reason":"clean walk","result":"PASS","step":3},{"action":"check_walk_terminated_within_depth_limit","inputs":["4"],"reason":"terminated naturally","result":"PASS","step":4}],"statement_digests":["111111\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031","222222\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032","333333\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033","444444\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps",0,"trust arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:user/Alice"],"333333\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin",0,"trust arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"5d731f72f752ac6c933bfdb1c5029fdb486881e8b3f731478b07c18798ab4f78","finding_key":"1265c7a65c1559c5d4c7296be2a73c50f595ddca927e9bc2c835a0c32ab10c8e","pattern_id":"admin_reachability","pattern_title":"Admin Reachability via AssumeRole Chains","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; reaches 1 admin-equivalent role(s)","required_checks":[{"description":"Source principal has at least one sts:AssumeRole permission edge","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"source_has_assumerole_permissions","reason":"source has 1 sts:AssumeRole permission edges","state":"pass"},{"description":"BFS walk reaches at least one admin-equivalent role within depth limit","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"reaches_at_least_one_admin","reason":"reachable admins: 1 (arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin)","state":"pass"},{"description":"At least one BFS path from source to a reachable admin uses only clean AssumeRole and admin-equivalence witnesses","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"at_least_one_reachable_chain_uses_clean_witnesses","reason":"all BFS paths use clean witness edges","state":"pass"},{"description":"BFS walk terminated naturally within 4 hops (reported admin set is complete)","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"walk_terminated_within_depth_limit","reason":"walk terminated naturally within 4 hops","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin","region":"-"},"title":"Validated admin reachability: arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:user/Alice can reach 1 admin role","verdict":"validated"},{"assumptions":[],"blockers_observed":[],"collection_context":{"affected_accounts":[],"coverage_notes":[],"graph_collection_complete":true,"has_collection_failures":false,"has_policy_parse_failures":false,"related_collection_failures":[],"related_policy_parse_failures":[]},"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps"],"reason":"source has assumerole edges in walk","result":"PASS","step":1},{"action":"check_reaches_at_least_one_admin","inputs":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin"],"reason":"1 admin endpoints reachable","result":"PASS","step":2},{"action":"check_at_least_one_reachable_chain_uses_clean_witnesses","inputs":["False"],"reason":"clean walk","result":"PASS","step":3},{"action":"check_walk_terminated_within_depth_limit","inputs":["4"],"reason":"terminated naturally","result":"PASS","step":4}],"statement_digests":["333333\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033","444444\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"333333\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin",0,"trust arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"b8050c047852ba77c0308890373db1bd9492fd00add431c2368b1d087fe6fcfb","finding_key":"9e5c110dd4c2e611ce7384c6e8d179838ec03e700e2cd34e3a36a0a1cfe6cbfe","pattern_id":"admin_reachability","pattern_title":"Admin Reachability via AssumeRole Chains","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; reaches 1 admin-equivalent role(s)","required_checks":[{"description":"Source principal has at least one sts:AssumeRole permission edge","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5"],"name":"source_has_assumerole_permissions","reason":"source has 1 sts:AssumeRole permission edges","state":"pass"},{"description":"BFS walk reaches at least one admin-equivalent role within depth limit","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"reaches_at_least_one_admin","reason":"reachable admins: 1 (arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin)","state":"pass"},{"description":"At least one BFS path from source to a reachable admin uses only clean AssumeRole and admin-equivalence witnesses","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5"],"name":"at_least_one_reachable_chain_uses_clean_witnesses","reason":"all BFS paths use clean witness edges","state":"pass"},{"description":"BFS walk terminated naturally within 4 hops (reported admin set is complete)","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5"],"name":"walk_terminated_within_depth_limit","reason":"walk terminated naturally within 4 hops","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"high","source":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin","region":"-"},"title":"Validated admin reachability: arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps can reach 1 admin role","verdict":"validated"}],"metadata":{"canonical_hash":"ec168ade5c73f75d23e9e9392a8922970c477ef8a66cd5f69b159fb5bfca5631","collector":"iamscope","collector_version":"0.2.0","findings_count":2,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["admin_reachability"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":2}},"reasoner_versions":{"admin_reachability":"1.0.0"},"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file diff --git a/tests/fixtures/expected_output/findings/admin_reachability/fixture_b_validated_two_admins_critical.json b/tests/fixtures/expected_output/findings/admin_reachability/fixture_b_validated_two_admins_critical.json index 15f499d..eb6a435 100644 --- a/tests/fixtures/expected_output/findings/admin_reachability/fixture_b_validated_two_admins_critical.json +++ b/tests/fixtures/expected_output/findings/admin_reachability/fixture_b_validated_two_admins_critical.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"collection_context":{"affected_accounts":[],"coverage_notes":[],"graph_collection_complete":true,"has_collection_failures":false,"has_policy_parse_failures":false,"related_collection_failures":[],"related_policy_parse_failures":[]},"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","907608f2f3d9e8cd6f5cf870633bc11dfd5c10ba9658b8dc59b5645ec829bb69","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice"],"reason":"source has assumerole edges in walk","result":"PASS","step":1},{"action":"check_reaches_at_least_one_admin","inputs":["arn:aws:iam::11111\u003111111\u0031:role/Admin","arn:aws:iam::11111\u003111111\u0031:role/Prod"],"reason":"2 admin endpoints reachable","result":"PASS","step":2},{"action":"check_at_least_one_reachable_chain_uses_clean_witnesses","inputs":["False"],"reason":"clean walk","result":"PASS","step":3},{"action":"check_walk_terminated_within_depth_limit","inputs":["4"],"reason":"terminated naturally","result":"PASS","step":4}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111","22222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u00322222","33333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u00333333","44444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u00344444","55555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u00355555","66666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u00366666","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"22222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u00322222":["arn:aws:iam::11111\u003111111\u0031:role/DevOps",0,"trust arn:aws:iam::11111\u003111111\u0031:user/Alice"],"33333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u00333333":["arn:aws:iam::11111\u003111111\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"44444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u00344444":["arn:aws:iam::11111\u003111111\u0031:role/Admin",0,"trust arn:aws:iam::11111\u003111111\u0031:role/DevOps"],"55555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u00355555":["arn:aws:iam::11111\u003111111\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"66666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u00366666":["arn:aws:iam::11111\u003111111\u0031:role/Prod",0,"trust arn:aws:iam::11111\u003111111\u0031:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"18245f90ee4ec7db1d6d95314f4efcb0c9a650bf45d9c19e0f9e1aedfb56a7ee","finding_key":"1265c7a65c1559c5d4c7296be2a73c50f595ddca927e9bc2c835a0c32ab10c8e","pattern_id":"admin_reachability","pattern_title":"Admin Reachability via AssumeRole Chains","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; reaches 2 admin-equivalent role(s)","required_checks":[{"description":"Source principal has at least one sts:AssumeRole permission edge","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"source_has_assumerole_permissions","reason":"source has 1 sts:AssumeRole permission edges","state":"pass"},{"description":"BFS walk reaches at least one admin-equivalent role within depth limit","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"reaches_at_least_one_admin","reason":"reachable admins: 2 (arn:aws:iam::11111\u003111111\u0031:role/Admin, arn:aws:iam::11111\u003111111\u0031:role/Prod)","state":"pass"},{"description":"At least one BFS path from source to a reachable admin traverses only non-wildcard, non-hyperedge edges","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"at_least_one_reachable_chain_uses_clean_witnesses","reason":"all BFS paths use clean witness edges","state":"pass"},{"description":"BFS walk terminated naturally within 4 hops (reported admin set is complete)","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"walk_terminated_within_depth_limit","reason":"walk terminated naturally within 4 hops","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"critical","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/Admin","region":"-"},"title":"Validated admin reachability: arn:aws:iam::11111\u003111111\u0031:user/Alice can reach 2 admin roles","verdict":"validated"},{"assumptions":[],"blockers_observed":[],"collection_context":{"affected_accounts":[],"coverage_notes":[],"graph_collection_complete":true,"has_collection_failures":false,"has_policy_parse_failures":false,"related_collection_failures":[],"related_policy_parse_failures":[]},"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","907608f2f3d9e8cd6f5cf870633bc11dfd5c10ba9658b8dc59b5645ec829bb69","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::11111\u003111111\u0031:role/DevOps"],"reason":"source has assumerole edges in walk","result":"PASS","step":1},{"action":"check_reaches_at_least_one_admin","inputs":["arn:aws:iam::11111\u003111111\u0031:role/Admin","arn:aws:iam::11111\u003111111\u0031:role/Prod"],"reason":"2 admin endpoints reachable","result":"PASS","step":2},{"action":"check_at_least_one_reachable_chain_uses_clean_witnesses","inputs":["False"],"reason":"clean walk","result":"PASS","step":3},{"action":"check_walk_terminated_within_depth_limit","inputs":["4"],"reason":"terminated naturally","result":"PASS","step":4}],"statement_digests":["33333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u00333333","44444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u00344444","55555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u00355555","66666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u00366666","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"33333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u00333333":["arn:aws:iam::11111\u003111111\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"44444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u00344444":["arn:aws:iam::11111\u003111111\u0031:role/Admin",0,"trust arn:aws:iam::11111\u003111111\u0031:role/DevOps"],"55555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u00355555":["arn:aws:iam::11111\u003111111\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"66666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u00366666":["arn:aws:iam::11111\u003111111\u0031:role/Prod",0,"trust arn:aws:iam::11111\u003111111\u0031:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"d308ca728c7e9ef86b4ee52573d6ce1c58d71880bd144cdd9a67732f56d5da93","finding_key":"9e5c110dd4c2e611ce7384c6e8d179838ec03e700e2cd34e3a36a0a1cfe6cbfe","pattern_id":"admin_reachability","pattern_title":"Admin Reachability via AssumeRole Chains","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; reaches 2 admin-equivalent role(s)","required_checks":[{"description":"Source principal has at least one sts:AssumeRole permission edge","evidence_refs":["16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"source_has_assumerole_permissions","reason":"source has 2 sts:AssumeRole permission edges","state":"pass"},{"description":"BFS walk reaches at least one admin-equivalent role within depth limit","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"reaches_at_least_one_admin","reason":"reachable admins: 2 (arn:aws:iam::11111\u003111111\u0031:role/Admin, arn:aws:iam::11111\u003111111\u0031:role/Prod)","state":"pass"},{"description":"At least one BFS path from source to a reachable admin traverses only non-wildcard, non-hyperedge edges","evidence_refs":["16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"at_least_one_reachable_chain_uses_clean_witnesses","reason":"all BFS paths use clean witness edges","state":"pass"},{"description":"BFS walk terminated naturally within 4 hops (reported admin set is complete)","evidence_refs":["16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"walk_terminated_within_depth_limit","reason":"walk terminated naturally within 4 hops","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"critical","source":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/DevOps","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/Admin","region":"-"},"title":"Validated admin reachability: arn:aws:iam::11111\u003111111\u0031:role/DevOps can reach 2 admin roles","verdict":"validated"}],"metadata":{"canonical_hash":"c4753c994eec31b7cfb4864a954057ebf44d63a83c18b613eecd6b2ef6c9efb9","collector":"iamscope","collector_version":"0.2.0","findings_count":2,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["admin_reachability"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":2}},"reasoner_versions":{"admin_reachability":"1.0.0"},"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[],"collection_context":{"affected_accounts":[],"coverage_notes":[],"graph_collection_complete":true,"has_collection_failures":false,"has_policy_parse_failures":false,"related_collection_failures":[],"related_policy_parse_failures":[]},"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","907608f2f3d9e8cd6f5cf870633bc11dfd5c10ba9658b8dc59b5645ec829bb69","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:user/Alice"],"reason":"source has assumerole edges in walk","result":"PASS","step":1},{"action":"check_reaches_at_least_one_admin","inputs":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin","arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Prod"],"reason":"2 admin endpoints reachable","result":"PASS","step":2},{"action":"check_at_least_one_reachable_chain_uses_clean_witnesses","inputs":["False"],"reason":"clean walk","result":"PASS","step":3},{"action":"check_walk_terminated_within_depth_limit","inputs":["4"],"reason":"terminated naturally","result":"PASS","step":4}],"statement_digests":["111111\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031","222222\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032","333333\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033","444444\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034","555555\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035","666666\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps",0,"trust arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:user/Alice"],"333333\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin",0,"trust arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps"],"555555\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"666666\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Prod",0,"trust arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"18245f90ee4ec7db1d6d95314f4efcb0c9a650bf45d9c19e0f9e1aedfb56a7ee","finding_key":"1265c7a65c1559c5d4c7296be2a73c50f595ddca927e9bc2c835a0c32ab10c8e","pattern_id":"admin_reachability","pattern_title":"Admin Reachability via AssumeRole Chains","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; reaches 2 admin-equivalent role(s)","required_checks":[{"description":"Source principal has at least one sts:AssumeRole permission edge","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"source_has_assumerole_permissions","reason":"source has 1 sts:AssumeRole permission edges","state":"pass"},{"description":"BFS walk reaches at least one admin-equivalent role within depth limit","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"reaches_at_least_one_admin","reason":"reachable admins: 2 (arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin, arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Prod)","state":"pass"},{"description":"At least one BFS path from source to a reachable admin uses only clean AssumeRole and admin-equivalence witnesses","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"at_least_one_reachable_chain_uses_clean_witnesses","reason":"all BFS paths use clean witness edges","state":"pass"},{"description":"BFS walk terminated naturally within 4 hops (reported admin set is complete)","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"walk_terminated_within_depth_limit","reason":"walk terminated naturally within 4 hops","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"critical","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin","region":"-"},"title":"Validated admin reachability: arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:user/Alice can reach 2 admin roles","verdict":"validated"},{"assumptions":[],"blockers_observed":[],"collection_context":{"affected_accounts":[],"coverage_notes":[],"graph_collection_complete":true,"has_collection_failures":false,"has_policy_parse_failures":false,"related_collection_failures":[],"related_policy_parse_failures":[]},"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","907608f2f3d9e8cd6f5cf870633bc11dfd5c10ba9658b8dc59b5645ec829bb69","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps"],"reason":"source has assumerole edges in walk","result":"PASS","step":1},{"action":"check_reaches_at_least_one_admin","inputs":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin","arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Prod"],"reason":"2 admin endpoints reachable","result":"PASS","step":2},{"action":"check_at_least_one_reachable_chain_uses_clean_witnesses","inputs":["False"],"reason":"clean walk","result":"PASS","step":3},{"action":"check_walk_terminated_within_depth_limit","inputs":["4"],"reason":"terminated naturally","result":"PASS","step":4}],"statement_digests":["333333\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033","444444\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034","555555\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035","666666\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"333333\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033\u0033":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034\u0034":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin",0,"trust arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps"],"555555\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035\u0035":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"666666\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036\u0036":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Prod",0,"trust arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"d308ca728c7e9ef86b4ee52573d6ce1c58d71880bd144cdd9a67732f56d5da93","finding_key":"9e5c110dd4c2e611ce7384c6e8d179838ec03e700e2cd34e3a36a0a1cfe6cbfe","pattern_id":"admin_reachability","pattern_title":"Admin Reachability via AssumeRole Chains","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; reaches 2 admin-equivalent role(s)","required_checks":[{"description":"Source principal has at least one sts:AssumeRole permission edge","evidence_refs":["16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"source_has_assumerole_permissions","reason":"source has 2 sts:AssumeRole permission edges","state":"pass"},{"description":"BFS walk reaches at least one admin-equivalent role within depth limit","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"reaches_at_least_one_admin","reason":"reachable admins: 2 (arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin, arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Prod)","state":"pass"},{"description":"At least one BFS path from source to a reachable admin uses only clean AssumeRole and admin-equivalence witnesses","evidence_refs":["16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"at_least_one_reachable_chain_uses_clean_witnesses","reason":"all BFS paths use clean witness edges","state":"pass"},{"description":"BFS walk terminated naturally within 4 hops (reported admin set is complete)","evidence_refs":["16d53badfad1db50fe6a420b82caa78f1484d74d75ee9018f7d3a43ecde9ec91","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4d83ad0fe7e169f839b86f890b92e7344b0705677cbcfdcaafd946a921d3ce0c","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","bf50b937a81eaa0432c21b4d97f8ccd0e04502134bbd4bdda3352f8b39178610"],"name":"walk_terminated_within_depth_limit","reason":"walk terminated naturally within 4 hops","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"critical","source":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin","region":"-"},"title":"Validated admin reachability: arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps can reach 2 admin roles","verdict":"validated"}],"metadata":{"canonical_hash":"dbc7eb313d99c323ab9fc5bd6df2404779e3b390a63d6cd1e13ad7a9849fdc4e","collector":"iamscope","collector_version":"0.2.0","findings_count":2,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["admin_reachability"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":2}},"reasoner_versions":{"admin_reachability":"1.0.0"},"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file diff --git a/tests/fixtures/expected_output/findings/admin_reachability/fixture_c_hyperedge_inconclusive.json b/tests/fixtures/expected_output/findings/admin_reachability/fixture_c_hyperedge_inconclusive.json index 3f5ac9d..195153a 100644 --- a/tests/fixtures/expected_output/findings/admin_reachability/fixture_c_hyperedge_inconclusive.json +++ b/tests/fixtures/expected_output/findings/admin_reachability/fixture_c_hyperedge_inconclusive.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"collection_context":{"affected_accounts":[],"coverage_notes":[],"graph_collection_complete":true,"has_collection_failures":false,"has_policy_parse_failures":false,"related_collection_failures":[],"related_policy_parse_failures":[]},"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::11111\u003111111\u0031:role/DevOps"],"reason":"source has assumerole edges in walk","result":"PASS","step":1},{"action":"check_reaches_at_least_one_admin","inputs":["arn:aws:iam::11111\u003111111\u0031:role/Admin"],"reason":"1 admin endpoints reachable","result":"PASS","step":2},{"action":"check_at_least_one_reachable_chain_uses_clean_witnesses","inputs":["True"],"reason":"ambiguity in walk","result":"UNKNOWN","step":3},{"action":"check_walk_terminated_within_depth_limit","inputs":["4"],"reason":"terminated naturally","result":"PASS","step":4}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111","22222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u00322222","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"22222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u00322222":["arn:aws:iam::11111\u003111111\u0031:role/Admin",0,"trust arn:aws:iam::11111\u003111111\u0031:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"6fa1be58ccb5096282ae7563c1db01ce8403e1a877e14d0ebd7315fdc632e25d","finding_key":"9e5c110dd4c2e611ce7384c6e8d179838ec03e700e2cd34e3a36a0a1cfe6cbfe","pattern_id":"admin_reachability","pattern_title":"Admin Reachability via AssumeRole Chains","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: at_least_one_reachable_chain_uses_clean_witnesses; reaches 1 admin(s)","required_checks":[{"description":"Source principal has at least one sts:AssumeRole permission edge","evidence_refs":["2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"name":"source_has_assumerole_permissions","reason":"source has 1 sts:AssumeRole permission edges","state":"pass"},{"description":"BFS walk reaches at least one admin-equivalent role within depth limit","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"reaches_at_least_one_admin","reason":"reachable admins: 1 (arn:aws:iam::11111\u003111111\u0031:role/Admin)","state":"pass"},{"description":"At least one BFS path from source to a reachable admin traverses only non-wildcard, non-hyperedge edges","evidence_refs":["2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"name":"at_least_one_reachable_chain_uses_clean_witnesses","reason":"BFS walk traversed at least one wildcard/hyperedge edge","state":"unknown"},{"description":"BFS walk terminated naturally within 4 hops (reported admin set is complete)","evidence_refs":["2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"name":"walk_terminated_within_depth_limit","reason":"walk terminated naturally within 4 hops","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"high","source":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/DevOps","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/Admin","region":"-"},"title":"Inconclusive admin reachability: arn:aws:iam::11111\u003111111\u0031:role/DevOps can reach 1 admin role","verdict":"inconclusive"},{"assumptions":[],"blockers_observed":[],"collection_context":{"affected_accounts":[],"coverage_notes":[],"graph_collection_complete":true,"has_collection_failures":false,"has_policy_parse_failures":false,"related_collection_failures":[],"related_policy_parse_failures":[]},"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice"],"reason":"source has assumerole edges in walk","result":"PASS","step":1},{"action":"check_reaches_at_least_one_admin","inputs":["arn:aws:iam::11111\u003111111\u0031:role/Admin"],"reason":"1 admin endpoints reachable","result":"PASS","step":2},{"action":"check_at_least_one_reachable_chain_uses_clean_witnesses","inputs":["True"],"reason":"ambiguity in walk","result":"UNKNOWN","step":3},{"action":"check_walk_terminated_within_depth_limit","inputs":["4"],"reason":"terminated naturally","result":"PASS","step":4}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111","22222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u00322222","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"22222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u00322222":["arn:aws:iam::11111\u003111111\u0031:role/Admin",0,"trust arn:aws:iam::11111\u003111111\u0031:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"780df69260c10d6b50e067b384a4887da064ea5ba958425d91f7975ce24a4940","finding_key":"1265c7a65c1559c5d4c7296be2a73c50f595ddca927e9bc2c835a0c32ab10c8e","pattern_id":"admin_reachability","pattern_title":"Admin Reachability via AssumeRole Chains","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: at_least_one_reachable_chain_uses_clean_witnesses; reaches 1 admin(s)","required_checks":[{"description":"Source principal has at least one sts:AssumeRole permission edge","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"name":"source_has_assumerole_permissions","reason":"source has 1 sts:AssumeRole permission edges","state":"pass"},{"description":"BFS walk reaches at least one admin-equivalent role within depth limit","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"reaches_at_least_one_admin","reason":"reachable admins: 1 (arn:aws:iam::11111\u003111111\u0031:role/Admin)","state":"pass"},{"description":"At least one BFS path from source to a reachable admin traverses only non-wildcard, non-hyperedge edges","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"name":"at_least_one_reachable_chain_uses_clean_witnesses","reason":"BFS walk traversed at least one wildcard/hyperedge edge","state":"unknown"},{"description":"BFS walk terminated naturally within 4 hops (reported admin set is complete)","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"name":"walk_terminated_within_depth_limit","reason":"walk terminated naturally within 4 hops","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/Admin","region":"-"},"title":"Inconclusive admin reachability: arn:aws:iam::11111\u003111111\u0031:user/Alice can reach 1 admin role","verdict":"inconclusive"}],"metadata":{"canonical_hash":"fcd434ecd0cb5e92c063f586bc41cf134825197f42fb14fa8ba4f9a2871e53cc","collector":"iamscope","collector_version":"0.2.0","findings_count":2,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["admin_reachability"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":2,"precondition_only":0,"validated":0}},"reasoner_versions":{"admin_reachability":"1.0.0"},"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[],"collection_context":{"affected_accounts":[],"coverage_notes":[],"graph_collection_complete":true,"has_collection_failures":false,"has_policy_parse_failures":false,"related_collection_failures":[],"related_policy_parse_failures":[]},"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["2bcdb06db98d673422\u0032\u0035\u0030\u0035\u0030\u0036\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps"],"reason":"source has assumerole edges in walk","result":"PASS","step":1},{"action":"check_reaches_at_least_one_admin","inputs":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin"],"reason":"1 admin endpoints reachable","result":"PASS","step":2},{"action":"check_at_least_one_reachable_chain_uses_clean_witnesses","inputs":["True"],"reason":"ambiguity in walk or admin witness","result":"UNKNOWN","step":3},{"action":"check_walk_terminated_within_depth_limit","inputs":["4"],"reason":"terminated naturally","result":"PASS","step":4}],"statement_digests":["111111\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031","222222\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin",0,"trust arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"6fa1be58ccb5096282ae7563c1db01ce8403e1a877e14d0ebd7315fdc632e25d","finding_key":"9e5c110dd4c2e611ce7384c6e8d179838ec03e700e2cd34e3a36a0a1cfe6cbfe","pattern_id":"admin_reachability","pattern_title":"Admin Reachability via AssumeRole Chains","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: at_least_one_reachable_chain_uses_clean_witnesses; reaches 1 admin(s)","required_checks":[{"description":"Source principal has at least one sts:AssumeRole permission edge","evidence_refs":["2bcdb06db98d673422\u0032\u0035\u0030\u0035\u0030\u0036\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"name":"source_has_assumerole_permissions","reason":"source has 1 sts:AssumeRole permission edges","state":"pass"},{"description":"BFS walk reaches at least one admin-equivalent role within depth limit","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"reaches_at_least_one_admin","reason":"reachable admins: 1 (arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin)","state":"pass"},{"description":"At least one BFS path from source to a reachable admin uses only clean AssumeRole and admin-equivalence witnesses","evidence_refs":["2bcdb06db98d673422\u0032\u0035\u0030\u0035\u0030\u0036\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"name":"at_least_one_reachable_chain_uses_clean_witnesses","reason":"reachable admin path or admin witness uses wildcard/hyperedge evidence","state":"unknown"},{"description":"BFS walk terminated naturally within 4 hops (reported admin set is complete)","evidence_refs":["2bcdb06db98d673422\u0032\u0035\u0030\u0035\u0030\u0036\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"name":"walk_terminated_within_depth_limit","reason":"walk terminated naturally within 4 hops","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"high","source":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin","region":"-"},"title":"Inconclusive admin reachability: arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps can reach 1 admin role","verdict":"inconclusive"},{"assumptions":[],"blockers_observed":[],"collection_context":{"affected_accounts":[],"coverage_notes":[],"graph_collection_complete":true,"has_collection_failures":false,"has_policy_parse_failures":false,"related_collection_failures":[],"related_policy_parse_failures":[]},"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","2bcdb06db98d673422\u0032\u0035\u0030\u0035\u0030\u0036\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:user/Alice"],"reason":"source has assumerole edges in walk","result":"PASS","step":1},{"action":"check_reaches_at_least_one_admin","inputs":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin"],"reason":"1 admin endpoints reachable","result":"PASS","step":2},{"action":"check_at_least_one_reachable_chain_uses_clean_witnesses","inputs":["True"],"reason":"ambiguity in walk or admin witness","result":"UNKNOWN","step":3},{"action":"check_walk_terminated_within_depth_limit","inputs":["4"],"reason":"terminated naturally","result":"PASS","step":4}],"statement_digests":["111111\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031","222222\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031\u0031":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032\u0032":["arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin",0,"trust arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"780df69260c10d6b50e067b384a4887da064ea5ba958425d91f7975ce24a4940","finding_key":"1265c7a65c1559c5d4c7296be2a73c50f595ddca927e9bc2c835a0c32ab10c8e","pattern_id":"admin_reachability","pattern_title":"Admin Reachability via AssumeRole Chains","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: at_least_one_reachable_chain_uses_clean_witnesses; reaches 1 admin(s)","required_checks":[{"description":"Source principal has at least one sts:AssumeRole permission edge","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","2bcdb06db98d673422\u0032\u0035\u0030\u0035\u0030\u0036\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"name":"source_has_assumerole_permissions","reason":"source has 1 sts:AssumeRole permission edges","state":"pass"},{"description":"BFS walk reaches at least one admin-equivalent role within depth limit","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"reaches_at_least_one_admin","reason":"reachable admins: 1 (arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin)","state":"pass"},{"description":"At least one BFS path from source to a reachable admin uses only clean AssumeRole and admin-equivalence witnesses","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","2bcdb06db98d673422\u0032\u0035\u0030\u0035\u0030\u0036\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"name":"at_least_one_reachable_chain_uses_clean_witnesses","reason":"reachable admin path or admin witness uses wildcard/hyperedge evidence","state":"unknown"},{"description":"BFS walk terminated naturally within 4 hops (reported admin set is complete)","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","2bcdb06db98d673422\u0032\u0035\u0030\u0035\u0030\u0036\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"name":"walk_terminated_within_depth_limit","reason":"walk terminated naturally within 4 hops","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:role/Admin","region":"-"},"title":"Inconclusive admin reachability: arn:aws:iam::111111\u0031\u0031\u0031\u0031\u0031\u0031:user/Alice can reach 1 admin role","verdict":"inconclusive"}],"metadata":{"canonical_hash":"bc74cc696399e4ee1f40f97d8e9add938bcee1628789afcd5f2421a614a70342","collector":"iamscope","collector_version":"0.2.0","findings_count":2,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["admin_reachability"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":2,"precondition_only":0,"validated":0}},"reasoner_versions":{"admin_reachability":"1.0.0"},"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file diff --git a/tests/integration/test_identity_deny_pipeline.py b/tests/integration/test_identity_deny_pipeline.py index b57b9ac..1aec1ff 100644 --- a/tests/integration/test_identity_deny_pipeline.py +++ b/tests/integration/test_identity_deny_pipeline.py @@ -115,14 +115,18 @@ def _permission_results( principal_type: str, policy_doc: dict[str, Any], policy_name: str, + *, + policy_source: str = "inline", + policy_arn: str = "", ) -> list[PermissionParseResult]: return parse_permission_policy( policy_doc, source_arn=principal_arn, source_node_type=principal_type, source_account_id=_ACCOUNT, - policy_source="inline", + policy_source=policy_source, policy_name=policy_name, + policy_arn=policy_arn, ) @@ -158,6 +162,8 @@ def _build_case( NODE_TYPE_IAM_ROLE, _allow_doc("*", "*"), "AdminAccess", + policy_source="managed", + policy_arn="arn:aws:iam::aws:policy/AdministratorAccess", ) ) diff --git a/tests/test_admin_reachability_reasoner.py b/tests/test_admin_reachability_reasoner.py index eb84030..842e0d0 100644 --- a/tests/test_admin_reachability_reasoner.py +++ b/tests/test_admin_reachability_reasoner.py @@ -17,13 +17,17 @@ from __future__ import annotations +from iamscope.collector.passrole import build_permission_edges from iamscope.constants import ( CONSTRAINT_TYPE_PERMISSION_BOUNDARY, CONSTRAINT_TYPE_SCP, + NODE_TYPE_IAM_ROLE, PROVIDER_AWS, REGION_GLOBAL, ) -from iamscope.models import Constraint, EdgeConstraint +from iamscope.controls.expansion import ExpansionController +from iamscope.models import Constraint, Edge, EdgeConstraint, Node +from iamscope.parser.permission_policy import parse_permission_policy from iamscope.reasoner import AdminReachabilityReasoner, FactGraph from tests.test_assume_role_chain_reasoner import ( # noqa: I001 _ADMIN_ARN, @@ -43,6 +47,8 @@ _wildcard_trust_edge, ) +_AWS_MANAGED_ADMINISTRATOR_ACCESS_ARN = "arn:aws:iam::aws:policy/AdministratorAccess" + # --------------------------------------------------------------------------- # Preconditions # --------------------------------------------------------------------------- @@ -302,6 +308,144 @@ def test_unrelated_trust_still_produces_no_reachability(self) -> None: assert not any(f.source.provider_id == _ALICE_ARN for f in findings) +class TestAdministratorAccessCalibration: + def _admin_edges_from_policy( + self, + *, + role_arn: str, + policy_arn: str, + conditions: dict | None = None, + ) -> tuple[tuple[Edge, ...], tuple[Node, ...]]: + policy: dict = { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "*", + "Resource": "*", + } + ], + } + if conditions is not None: + policy["Statement"][0]["Condition"] = conditions + parse_results = parse_permission_policy( + policy, + source_arn=role_arn, + source_node_type=NODE_TYPE_IAM_ROLE, + source_account_id="111111\u003111111", + policy_source="managed", + policy_name="AdministratorAccess", + policy_arn=policy_arn, + ) + edges, hyperedge_nodes = build_permission_edges( + parse_results, + ExpansionController(global_mode="warn"), + known_role_arns=[role_arn], + ) + return (tuple(edges), tuple(hyperedge_nodes)) + + def _single_hop_to_admin_with_policy( + self, + *, + source_arn: str = _ALICE_ARN, + target_arn: str = _ADMIN_ARN, + policy_arn: str = _AWS_MANAGED_ADMINISTRATOR_ACCESS_ARN, + conditions: dict | None = None, + wildcard_assume_role_permission: bool = False, + ) -> FactGraph: + source = _user(source_arn) if ":user/" in source_arn else _role(source_arn) + target = _role(target_arn) + assume_perm = _assume_perm_edge( + src_arn=source_arn, + dst_arn=target_arn, + is_wildcard_resource=wildcard_assume_role_permission, + ) + trust = _trust_edge(principal_arn=source_arn, target_arn=target_arn) + admin_edges, hyperedge_nodes = self._admin_edges_from_policy( + role_arn=target_arn, + policy_arn=policy_arn, + conditions=conditions, + ) + return _make_facts( + nodes=(source, target, *hyperedge_nodes), + edges=(assume_perm, trust, *admin_edges), + ) + + def _finding_for_source(self, facts: FactGraph, source_arn: str) -> object: + return next(f for f in AdminReachabilityReasoner().run(facts) if f.source.provider_id == source_arn) + + def _clean_witness_check_state(self, finding: object) -> str: + check = next( + c for c in finding.required_checks if c.name == "at_least_one_reachable_chain_uses_clean_witnesses" + ) + return check.state.value + + def test_aws_managed_administratoraccess_is_clean_admin_witness(self) -> None: + facts = self._single_hop_to_admin_with_policy(policy_arn=_AWS_MANAGED_ADMINISTRATOR_ACCESS_ARN) + + finding = self._finding_for_source(facts, _ALICE_ARN) + + assert finding.verdict.value == "validated" + assert self._clean_witness_check_state(finding) == "pass" + + def test_custom_wildcard_admin_policy_remains_conservative(self) -> None: + facts = self._single_hop_to_admin_with_policy( + policy_arn="arn:aws:iam::111111\u003111111:policy/CustomAdminPolicy" + ) + + finding = self._finding_for_source(facts, _ALICE_ARN) + + assert finding.verdict.value == "inconclusive" + assert self._clean_witness_check_state(finding) == "unknown" + + def test_spoofed_administratoraccess_policy_arn_remains_conservative(self) -> None: + facts = self._single_hop_to_admin_with_policy( + policy_arn="arn:aws:iam::111111\u003111111:policy/MyAdministratorAccess" + ) + + finding = self._finding_for_source(facts, _ALICE_ARN) + + assert finding.verdict.value == "inconclusive" + assert self._clean_witness_check_state(finding) == "unknown" + + def test_conditioned_administratoraccess_witness_remains_conservative(self) -> None: + facts = self._single_hop_to_admin_with_policy( + policy_arn=_AWS_MANAGED_ADMINISTRATOR_ACCESS_ARN, + conditions={"StringEquals": {"aws:PrincipalTag/admin-review": "approved"}}, + ) + + finding = self._finding_for_source(facts, _ALICE_ARN) + + assert finding.verdict.value == "inconclusive" + assert self._clean_witness_check_state(finding) == "unknown" + + def test_ambiguous_assumerole_path_to_aws_managed_administratoraccess_stays_inconclusive(self) -> None: + facts = self._single_hop_to_admin_with_policy( + policy_arn=_AWS_MANAGED_ADMINISTRATOR_ACCESS_ARN, + wildcard_assume_role_permission=True, + ) + + finding = self._finding_for_source(facts, _ALICE_ARN) + + assert finding.verdict.value == "inconclusive" + assert self._clean_witness_check_state(finding) == "unknown" + + def test_real_pilot_shape_prodapprole_to_proddbadminrole_validates(self) -> None: + source_arn = "arn:aws:iam::111111\u003111111:role/ProdAppRole" + target_arn = "arn:aws:iam::111111\u003111111:role/ProdDBAdminRole" + facts = self._single_hop_to_admin_with_policy( + source_arn=source_arn, + target_arn=target_arn, + policy_arn=_AWS_MANAGED_ADMINISTRATOR_ACCESS_ARN, + ) + + finding = self._finding_for_source(facts, source_arn) + + assert finding.target.provider_id == target_arn + assert finding.verdict.value == "validated" + assert self._clean_witness_check_state(finding) == "pass" + + class TestPermissionBoundaryReachability: def _boundary(self) -> Constraint: return Constraint(