From 4f74899d931c53bd75bdcf08e9e96b6eaeac1ec5 Mon Sep 17 00:00:00 2001 From: Eric Conklin Date: Fri, 5 Jun 2026 15:55:50 -0500 Subject: [PATCH] Add finding collection context --- iamscope/cli.py | 4 + iamscope/output/findings_json.py | 123 +++++++++- iamscope/reasoner/replay.py | 3 + .../fixture_a_validated_one_admin.json | 2 +- ...xture_b_validated_two_admins_critical.json | 2 +- .../fixture_c_hyperedge_inconclusive.json | 2 +- .../fixture_a_validated_two_hop.json | 2 +- .../fixture_b_blocked_by_scp_first_hop.json | 2 +- .../fixture_c_hyperedge_inconclusive.json | 2 +- .../fixture_a_critical_naked_wildcard.json | 2 +- .../fixture_b_broad_naked_blocked_scp.json | 2 +- .../fixture_c_narrow_naked_weak.json | 2 +- .../fixture_e_scp_partial_inconclusive.json | 2 +- .../fixture_f_oidc_broad_no_sub.json | 2 +- ...ixture_g_unsupported_scp_inconclusive.json | 2 +- .../fixture_h_multi_statement_dedup.json | 2 +- .../fixture_z_same_org_downgrade.json | 2 +- .../fixture_a_validated_critical.json | 2 +- .../fixture_b_wildcard_inconclusive.json | 2 +- .../fixture_c_blocked_by_scp.json | 2 +- .../fixture_a_validated_admin.json | 2 +- .../fixture_c_blocked_by_scp.json | 2 +- .../fixture_d_blocked_by_boundary.json | 2 +- .../fixture_e_inconclusive_partial_scp.json | 2 +- .../fixture_f_hyperedge_inconclusive.json | 2 +- .../fixture_g_passrole_scoped_to_ec2.json | 2 +- .../fixture_a_validated_admin.json | 2 +- .../fixture_c_blocked_by_scp.json | 2 +- ...xture_d_blocked_by_boundary_post_bnd1.json | 2 +- .../fixture_e_inconclusive_partial_scp.json | 2 +- .../fixture_f_hyperedge_inconclusive.json | 2 +- .../fixture_g_passedtoservice_ec2.json | 2 +- .../fixture_a_validated_critical.json | 2 +- .../fixture_b_wildcard_inconclusive.json | 2 +- .../fixture_c_blocked_by_scp.json | 2 +- .../fixture_a_validated_non_admin.json | 2 +- .../fixture_b_blocked_by_scp.json | 2 +- .../fixture_c_wildcard_inconclusive.json | 2 +- ...ixture_d_kms_blocks_precondition_only.json | 2 +- ...fixture_e_kms_conditions_inconclusive.json | 2 +- tests/test_findings_json.py | 210 ++++++++++++++++++ tests/test_replay_findings.py | 44 +++- 42 files changed, 418 insertions(+), 40 deletions(-) diff --git a/iamscope/cli.py b/iamscope/cli.py index 4861d64..5bec8a0 100644 --- a/iamscope/cli.py +++ b/iamscope/cli.py @@ -1424,6 +1424,10 @@ def _run_reasoners_and_emit( time.gmtime(), ), reasoning_duration_seconds=reasoning_duration, + collection_context_source={ + "collection_failures": [failure.to_dict() for failure in result.collection_failures], + "policy_parse_failures": [failure.to_dict() for failure in result.policy_parse_failures], + }, ) return ( diff --git a/iamscope/output/findings_json.py b/iamscope/output/findings_json.py index 2a8f50e..5e71af7 100644 --- a/iamscope/output/findings_json.py +++ b/iamscope/output/findings_json.py @@ -42,6 +42,8 @@ from __future__ import annotations +import json +import re from typing import Any from iamscope.constants import ID_ALGORITHM, JSON_TRAILING_NEWLINE @@ -95,6 +97,7 @@ def emit_findings( reasoning_timestamp: str = "", reasoning_duration_seconds: float = 0.0, source_tool_version: str = DEFAULT_SOURCE_TOOL_VERSION, + collection_context_source: dict[str, Any] | None = None, ) -> tuple[bytes, str]: """Emit findings.json as canonical bytes with deterministic hash. @@ -125,6 +128,10 @@ def emit_findings( source_tool_version: Override for the iamscope version string written to top-level `source_tool_version` and `metadata.collector_version`. + collection_context_source: Optional scenario/PipelineResult metadata + carrying `collection_failures` and `policy_parse_failures`. + The emitter maps these records onto each finding without + changing verdict semantics. Returns: Tuple of (findings_json_bytes, canonical_hash_hex). The bytes @@ -154,7 +161,7 @@ def emit_findings( # property compute on each Finding, populating the cache. Subsequent # calls to `f.finding_id` (e.g., for the verdict breakdown) hit the # cache. - finding_dicts = [_finding_to_dict(f, reasoners_used) for f in sorted_findings] + finding_dicts = [_finding_to_dict(f, reasoners_used, collection_context_source) for f in sorted_findings] # Verdict breakdown — always populated with all four enum values, # zero entries for verdicts not present. This makes the field shape @@ -242,6 +249,7 @@ def _compute_verdict_breakdown(findings: list[Finding]) -> dict[str, int]: def _finding_to_dict( finding: Finding, reasoners_used: dict[str, dict[str, str]], + collection_context_source: dict[str, Any] | None = None, ) -> dict[str, Any]: """Convert a Finding to its §3.6 JSON dict shape. @@ -264,6 +272,7 @@ def _finding_to_dict( return { "assumptions": [_assumption_to_dict(a) for a in finding.assumptions], "blockers_observed": [_blocker_to_dict(b) for b in finding.blockers_observed], + "collection_context": _collection_context_for_finding(finding, collection_context_source), "evidence": _evidence_to_dict(finding.evidence), "finding_id": finding.finding_id, "finding_key": finding.finding_key, @@ -281,6 +290,118 @@ def _finding_to_dict( } +def _collection_context_for_finding( + finding: Finding, + collection_context_source: dict[str, Any] | None, +) -> dict[str, Any]: + """Build deterministic per-finding partial-collection context. + + Reasoners should not know about collection coverage. The emitter has + enough presentation context to attach scenario-level collection and + parse failures to each finding by source/target account or exact + source/target ARN while preserving verdict semantics. + """ + source = collection_context_source or {} + collection_failures = _normalise_failure_records(source.get("collection_failures", [])) + policy_parse_failures = _normalise_failure_records(source.get("policy_parse_failures", [])) + finding_accounts = sorted( + { + account + for account in ( + _extract_aws_account_id(finding.source.provider_id), + _extract_aws_account_id(finding.target.provider_id), + ) + if account + } + ) + finding_provider_ids = {finding.source.provider_id, finding.target.provider_id} + + related_collection_failures = [ + failure for failure in collection_failures if str(failure.get("account_id", "")) in finding_accounts + ] + related_policy_parse_failures: list[dict[str, Any]] = [] + account_level_policy_match = False + for failure in policy_parse_failures: + source_arn = str(failure.get("source_arn", "")) + failure_account = _extract_aws_account_id(source_arn) or _extract_aws_account_id( + str(failure.get("policy_arn", "")) + ) + if source_arn in finding_provider_ids: + related_policy_parse_failures.append(failure) + elif failure_account and failure_account in finding_accounts: + related_policy_parse_failures.append(failure) + account_level_policy_match = True + + related_collection_failures = _sort_failure_records(related_collection_failures) + related_policy_parse_failures = _sort_failure_records(related_policy_parse_failures) + + coverage_notes: list[str] = [] + if collection_failures and not related_collection_failures: + coverage_notes.append("collection was partial; no direct account match found for this finding") + if policy_parse_failures and not related_policy_parse_failures: + coverage_notes.append( + "policy parsing was partial; no direct source/target account match found for this finding" + ) + if account_level_policy_match: + coverage_notes.append("policy parse failure related by account, not exact source/target ARN") + + affected_accounts = sorted( + { + str(failure.get("account_id", "")) + for failure in related_collection_failures + if str(failure.get("account_id", "")) + } + | { + account + for failure in related_policy_parse_failures + for account in ( + _extract_aws_account_id(str(failure.get("source_arn", ""))), + _extract_aws_account_id(str(failure.get("policy_arn", ""))), + ) + if account + } + ) + + has_collection_failures = bool(collection_failures) + has_policy_parse_failures = bool(policy_parse_failures) + return { + "affected_accounts": affected_accounts, + "coverage_notes": sorted(set(coverage_notes)), + "graph_collection_complete": not (has_collection_failures or has_policy_parse_failures), + "has_collection_failures": has_collection_failures, + "has_policy_parse_failures": has_policy_parse_failures, + "related_collection_failures": related_collection_failures, + "related_policy_parse_failures": related_policy_parse_failures, + } + + +def _normalise_failure_records(value: Any) -> list[dict[str, Any]]: + if not isinstance(value, list): + return [] + records: list[dict[str, Any]] = [] + for item in value: + if isinstance(item, dict): + records.append({str(key): _safe_context_value(val) for key, val in item.items()}) + return _sort_failure_records(records) + + +def _sort_failure_records(records: list[dict[str, Any]]) -> list[dict[str, Any]]: + return sorted(records, key=lambda record: json.dumps(record, sort_keys=True, separators=(",", ":"))) + + +def _safe_context_value(value: Any) -> Any: + if isinstance(value, str): + return value.replace("\r", " ").replace("\n", " ") + if value is None or isinstance(value, (bool, int, float)): + return value + return str(value).replace("\r", " ").replace("\n", " ") + + +def _extract_aws_account_id(provider_id: str) -> str: + match = re.match(r"^arn:[^:]*:[^:]*::([0-9]{12}):", provider_id) + return match.group(1) if match else "" + + def _check_to_dict(check: Check) -> dict[str, Any]: """Convert a Check to its §3.6 dict shape. diff --git a/iamscope/reasoner/replay.py b/iamscope/reasoner/replay.py index 6fbd50f..cfd5a12 100644 --- a/iamscope/reasoner/replay.py +++ b/iamscope/reasoner/replay.py @@ -84,6 +84,8 @@ def run_reasoners_on_frozen_artifacts( binding_metadata_path=binding_metadata_path, probe_overlay_path=probe_overlay_path, ) + scenario = json.loads(Path(scenario_path).read_bytes()) + scenario_metadata = dict(scenario.get("metadata", {})) registry = Registry() for instance in reasoner_instances: registry.register(instance) @@ -117,6 +119,7 @@ def run_reasoners_on_frozen_artifacts( reasoners_skipped=reasoners_skipped or None, reasoning_timestamp=reasoning_timestamp, reasoning_duration_seconds=duration, + collection_context_source=scenario_metadata, ) return ReplayResult( findings=tuple(findings), 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 daf994e..70e6399 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":[],"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\u003111111: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\u003111111: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\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111","222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222","333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033333","444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034444","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222":["arn:aws:iam::111111\u003111111:role/DevOps",0,"trust arn:aws:iam::111111\u003111111:user/Alice"],"333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033333":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034444":["arn:aws:iam::111111\u003111111:role/Admin",0,"trust arn:aws:iam::111111\u003111111: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\u003111111: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::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/Admin","region":"-"},"title":"Validated admin reachability: arn:aws:iam::111111\u003111111:user/Alice can reach 1 admin role","verdict":"validated"},{"assumptions":[],"blockers_observed":[],"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\u003111111: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\u003111111: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\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033333","444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034444","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033333":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034444":["arn:aws:iam::111111\u003111111:role/Admin",0,"trust arn:aws:iam::111111\u003111111: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\u003111111: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::111111\u003111111:role/DevOps","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/Admin","region":"-"},"title":"Validated admin reachability: arn:aws:iam::111111\u003111111:role/DevOps can reach 1 admin role","verdict":"validated"}],"metadata":{"canonical_hash":"367d916fb472cf907286ddc413a71e79065a259a86d18d61d1278f89bc28ccc0","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::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 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 475cd32..15f499d 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":[],"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\u003111111: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\u003111111:role/Admin","arn:aws:iam::111111\u003111111: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\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111","222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222","333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033333","444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034444","555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u0035555","666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u0036666","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222":["arn:aws:iam::111111\u003111111:role/DevOps",0,"trust arn:aws:iam::111111\u003111111:user/Alice"],"333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033333":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034444":["arn:aws:iam::111111\u003111111:role/Admin",0,"trust arn:aws:iam::111111\u003111111:role/DevOps"],"555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u0035555":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u0036666":["arn:aws:iam::111111\u003111111:role/Prod",0,"trust arn:aws:iam::111111\u003111111: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\u003111111:role/Admin, arn:aws:iam::111111\u003111111: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::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/Admin","region":"-"},"title":"Validated admin reachability: arn:aws:iam::111111\u003111111:user/Alice can reach 2 admin roles","verdict":"validated"},{"assumptions":[],"blockers_observed":[],"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\u003111111: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\u003111111:role/Admin","arn:aws:iam::111111\u003111111: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\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033333","444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034444","555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u0035555","666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u0036666","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033333":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034444":["arn:aws:iam::111111\u003111111:role/Admin",0,"trust arn:aws:iam::111111\u003111111:role/DevOps"],"555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u0035555":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u0036666":["arn:aws:iam::111111\u003111111:role/Prod",0,"trust arn:aws:iam::111111\u003111111: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\u003111111:role/Admin, arn:aws:iam::111111\u003111111: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::111111\u003111111:role/DevOps","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/Admin","region":"-"},"title":"Validated admin reachability: arn:aws:iam::111111\u003111111:role/DevOps can reach 2 admin roles","verdict":"validated"}],"metadata":{"canonical_hash":"9b66f9904802fa785268d95c184f266f0f62b91e83e449fcef9f35d41d791b09","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::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 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 e16f8ba..3f5ac9d 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":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["2bcdb06db98d673422\u003250506\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::111111\u003111111: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\u003111111: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":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111","222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222":["arn:aws:iam::111111\u003111111:role/Admin",0,"trust arn:aws:iam::111111\u003111111: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\u003250506\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\u003111111: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":["2bcdb06db98d673422\u003250506\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","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":["2bcdb06db98d673422\u003250506\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\u003111111:role/DevOps","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/Admin","region":"-"},"title":"Inconclusive admin reachability: arn:aws:iam::111111\u003111111:role/DevOps can reach 1 admin role","verdict":"inconclusive"},{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","2bcdb06db98d673422\u003250506\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","c46be192ccc976f8b3486ebf2b9fa9f0e0084fcc7f82ab39cd53923ba462130d"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_source_has_assumerole_permissions","inputs":["arn:aws:iam::111111\u003111111: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\u003111111: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":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111","222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222":["arn:aws:iam::111111\u003111111:role/Admin",0,"trust arn:aws:iam::111111\u003111111: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\u003250506\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\u003111111: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","2bcdb06db98d673422\u003250506\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","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","2bcdb06db98d673422\u003250506\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\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/Admin","region":"-"},"title":"Inconclusive admin reachability: arn:aws:iam::111111\u003111111:user/Alice can reach 1 admin role","verdict":"inconclusive"}],"metadata":{"canonical_hash":"d26e6013ab94cdd417bf81f2f80377693fa85b91b13d6907408943bb156afcec","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":["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 diff --git a/tests/fixtures/expected_output/findings/assume_role_chain/fixture_a_validated_two_hop.json b/tests/fixtures/expected_output/findings/assume_role_chain/fixture_a_validated_two_hop.json index 6b80216..187107a 100644 --- a/tests/fixtures/expected_output/findings/assume_role_chain/fixture_a_validated_two_hop.json +++ b/tests/fixtures/expected_output/findings/assume_role_chain/fixture_a_validated_two_hop.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"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_chain_length_at_least_two","inputs":["2"],"reason":"chain length is 2 hops","result":"PASS","step":1},{"action":"check_endpoint_is_admin_equivalent","inputs":["arn:aws:iam::111111\u003111111:role/Admin"],"reason":"endpoint has * or iam:*","result":"PASS","step":2},{"action":"check_all_hops_have_valid_trust_and_permission_edges","inputs":["2"],"reason":"2/2 hops have both edges","result":"PASS","step":3},{"action":"check_no_scp_blocks_any_hop","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"hop 1: no SCP bindings observed on this hop; hop 2: no SCP bindings observed on this hop","result":"PASS","step":4},{"action":"check_no_boundary_blocks_any_hop","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"hop 1: no permission boundary bindings on this hop; hop 2: no permission boundary bindings on this hop","result":"PASS","step":5},{"action":"check_no_identity_deny_blocks_any_hop","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"hop 1: no identity policy Deny bindings observed on sts:AssumeRole witness edge; hop 2: no identity policy Deny bindings observed on sts:AssumeRole witness edge","result":"PASS","step":6},{"action":"check_no_hop_traverses_hyperedge","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"clean witnesses on 2/2 hops","result":"PASS","step":7}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111","222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222","333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033333","444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034444","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222":["arn:aws:iam::111111\u003111111:role/DevOps",0,"trust arn:aws:iam::111111\u003111111:user/Alice"],"333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033333":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034444":["arn:aws:iam::111111\u003111111:role/Admin",0,"trust arn:aws:iam::111111\u003111111:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"3abe234142c094b65b777798388c8f6d82f86d742d9ae3fac3af6a5db4959461","finding_key":"733bb08440d7ae4cc1cb9dd20384b58110588e68dde029e9e2caf5d97e2558b0","pattern_id":"assume_role_chain","pattern_title":"Multi-Hop AssumeRole Privilege Escalation","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; 2-hop chain to admin-equivalent endpoint","required_checks":[{"description":"Chain length must be >= 2 hops (single-hop chains are covered by cross_account_trust)","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"chain_length_at_least_two","reason":"chain length is 2 hops","state":"pass"},{"description":"Chain endpoint role has admin-equivalent permissions (* or iam:*)","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"endpoint_is_admin_equivalent","reason":"endpoint role has * or iam:* permission edge","state":"pass"},{"description":"Every hop in the chain has BOTH a valid sts:AssumeRole permission edge AND an admitting trust edge on the next role","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"all_hops_have_valid_trust_and_permission_edges","reason":"all 2 hops verified","state":"pass"},{"description":"No SCP blocks sts:AssumeRole on any hop in the chain with complete confidence","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_scp_blocks_any_hop","reason":"hop 1: no SCP bindings observed on this hop; hop 2: no SCP bindings observed on this hop","state":"pass"},{"description":"No permission boundary blocks sts:AssumeRole on any hop in the chain","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_boundary_blocks_any_hop","reason":"hop 1: no permission boundary bindings on this hop; hop 2: no permission boundary bindings on this hop","state":"pass"},{"description":"No identity-policy Deny blocks sts:AssumeRole on any hop in the chain","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_identity_deny_blocks_any_hop","reason":"hop 1: no identity policy Deny bindings observed on sts:AssumeRole witness edge; hop 2: no identity policy Deny bindings observed on sts:AssumeRole witness edge","state":"pass"},{"description":"No hop's permission edge is a wildcard hyperedge or wildcard-resource grant","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_hop_traverses_hyperedge","reason":"all 2 hops have clean witness edges","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/Admin","region":"-"},"title":"Validated 2-hop AssumeRole chain from arn:aws:iam::111111\u003111111:user/Alice to arn:aws:iam::111111\u003111111:role/Admin","verdict":"validated"}],"metadata":{"canonical_hash":"7cf6b9dbafeecee8eed06595f3dbbcc8a676cead16c091b8c19241d1359ce950","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["assume_role_chain"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"assume_role_chain":"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_chain_length_at_least_two","inputs":["2"],"reason":"chain length is 2 hops","result":"PASS","step":1},{"action":"check_endpoint_is_admin_equivalent","inputs":["arn:aws:iam::11111\u003111111\u0031:role/Admin"],"reason":"endpoint has * or iam:*","result":"PASS","step":2},{"action":"check_all_hops_have_valid_trust_and_permission_edges","inputs":["2"],"reason":"2/2 hops have both edges","result":"PASS","step":3},{"action":"check_no_scp_blocks_any_hop","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"hop 1: no SCP bindings observed on this hop; hop 2: no SCP bindings observed on this hop","result":"PASS","step":4},{"action":"check_no_boundary_blocks_any_hop","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"hop 1: no permission boundary bindings on this hop; hop 2: no permission boundary bindings on this hop","result":"PASS","step":5},{"action":"check_no_identity_deny_blocks_any_hop","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"hop 1: no identity policy Deny bindings observed on sts:AssumeRole witness edge; hop 2: no identity policy Deny bindings observed on sts:AssumeRole witness edge","result":"PASS","step":6},{"action":"check_no_hop_traverses_hyperedge","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"clean witnesses on 2/2 hops","result":"PASS","step":7}],"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":"3abe234142c094b65b777798388c8f6d82f86d742d9ae3fac3af6a5db4959461","finding_key":"733bb08440d7ae4cc1cb9dd20384b58110588e68dde029e9e2caf5d97e2558b0","pattern_id":"assume_role_chain","pattern_title":"Multi-Hop AssumeRole Privilege Escalation","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; 2-hop chain to admin-equivalent endpoint","required_checks":[{"description":"Chain length must be >= 2 hops (single-hop chains are covered by cross_account_trust)","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"chain_length_at_least_two","reason":"chain length is 2 hops","state":"pass"},{"description":"Chain endpoint role has admin-equivalent permissions (* or iam:*)","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"endpoint_is_admin_equivalent","reason":"endpoint role has * or iam:* permission edge","state":"pass"},{"description":"Every hop in the chain has BOTH a valid sts:AssumeRole permission edge AND an admitting trust edge on the next role","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"all_hops_have_valid_trust_and_permission_edges","reason":"all 2 hops verified","state":"pass"},{"description":"No SCP blocks sts:AssumeRole on any hop in the chain with complete confidence","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_scp_blocks_any_hop","reason":"hop 1: no SCP bindings observed on this hop; hop 2: no SCP bindings observed on this hop","state":"pass"},{"description":"No permission boundary blocks sts:AssumeRole on any hop in the chain","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_boundary_blocks_any_hop","reason":"hop 1: no permission boundary bindings on this hop; hop 2: no permission boundary bindings on this hop","state":"pass"},{"description":"No identity-policy Deny blocks sts:AssumeRole on any hop in the chain","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_identity_deny_blocks_any_hop","reason":"hop 1: no identity policy Deny bindings observed on sts:AssumeRole witness edge; hop 2: no identity policy Deny bindings observed on sts:AssumeRole witness edge","state":"pass"},{"description":"No hop's permission edge is a wildcard hyperedge or wildcard-resource grant","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_hop_traverses_hyperedge","reason":"all 2 hops have clean witness edges","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 2-hop AssumeRole chain from arn:aws:iam::11111\u003111111\u0031:user/Alice to arn:aws:iam::11111\u003111111\u0031:role/Admin","verdict":"validated"}],"metadata":{"canonical_hash":"7e78d203701d71c507bbe49bbca537c849f6a2ad14eb41204378d588ced435b6","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["assume_role_chain"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"assume_role_chain":"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/assume_role_chain/fixture_b_blocked_by_scp_first_hop.json b/tests/fixtures/expected_output/findings/assume_role_chain/fixture_b_blocked_by_scp_first_hop.json index a54ed79..e1f68da 100644 --- a/tests/fixtures/expected_output/findings/assume_role_chain/fixture_b_blocked_by_scp_first_hop.json +++ b/tests/fixtures/expected_output/findings/assume_role_chain/fixture_b_blocked_by_scp_first_hop.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2","edge_id":"acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","kind":"scp","reason":"SCP 58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2 denies sts:AssumeRole"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2"],"edge_constraint_refs":["acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863:58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2"],"edge_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_chain_length_at_least_two","inputs":["2"],"reason":"chain length is 2 hops","result":"PASS","step":1},{"action":"check_endpoint_is_admin_equivalent","inputs":["arn:aws:iam::111111\u003111111:role/Admin"],"reason":"endpoint has * or iam:*","result":"PASS","step":2},{"action":"check_all_hops_have_valid_trust_and_permission_edges","inputs":["2"],"reason":"2/2 hops have both edges","result":"PASS","step":3},{"action":"check_no_scp_blocks_any_hop","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"hop 1: SCP 58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2 blocks (complete); hop 2: no SCP bindings observed on this hop","result":"FAIL","step":4},{"action":"check_no_boundary_blocks_any_hop","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"hop 1: no permission boundary bindings on this hop; hop 2: no permission boundary bindings on this hop","result":"PASS","step":5},{"action":"check_no_identity_deny_blocks_any_hop","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"hop 1: no identity policy Deny bindings observed on sts:AssumeRole witness edge; hop 2: no identity policy Deny bindings observed on sts:AssumeRole witness edge","result":"PASS","step":6},{"action":"check_no_hop_traverses_hyperedge","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"clean witnesses on 2/2 hops","result":"PASS","step":7}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111","222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222","333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033333","444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034444","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222":["arn:aws:iam::111111\u003111111:role/DevOps",0,"trust arn:aws:iam::111111\u003111111:user/Alice"],"333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033333":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034444":["arn:aws:iam::111111\u003111111:role/Admin",0,"trust arn:aws:iam::111111\u003111111:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"a16b269e0b60a010e9f6bc448f41cb18edea914494\u003624321\u0038d8d5afd7f2b103b","finding_key":"733bb08440d7ae4cc1cb9dd20384b58110588e68dde029e9e2caf5d97e2558b0","pattern_id":"assume_role_chain","pattern_title":"Multi-Hop AssumeRole Privilege Escalation","pattern_version":"1.0.0","reasoner_exit_reason":"SCP blocks at least one hop in the 2-hop chain","required_checks":[{"description":"Chain length must be >= 2 hops (single-hop chains are covered by cross_account_trust)","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"chain_length_at_least_two","reason":"chain length is 2 hops","state":"pass"},{"description":"Chain endpoint role has admin-equivalent permissions (* or iam:*)","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"endpoint_is_admin_equivalent","reason":"endpoint role has * or iam:* permission edge","state":"pass"},{"description":"Every hop in the chain has BOTH a valid sts:AssumeRole permission edge AND an admitting trust edge on the next role","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"all_hops_have_valid_trust_and_permission_edges","reason":"all 2 hops verified","state":"pass"},{"description":"No SCP blocks sts:AssumeRole on any hop in the chain with complete confidence","evidence_refs":["58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2"],"name":"no_scp_blocks_any_hop","reason":"hop 1: SCP 58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2 blocks (complete); hop 2: no SCP bindings observed on this hop","state":"fail"},{"description":"No permission boundary blocks sts:AssumeRole on any hop in the chain","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_boundary_blocks_any_hop","reason":"hop 1: no permission boundary bindings on this hop; hop 2: no permission boundary bindings on this hop","state":"pass"},{"description":"No identity-policy Deny blocks sts:AssumeRole on any hop in the chain","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_identity_deny_blocks_any_hop","reason":"hop 1: no identity policy Deny bindings observed on sts:AssumeRole witness edge; hop 2: no identity policy Deny bindings observed on sts:AssumeRole witness edge","state":"pass"},{"description":"No hop's permission edge is a wildcard hyperedge or wildcard-resource grant","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_hop_traverses_hyperedge","reason":"all 2 hops have clean witness edges","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"info","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/Admin","region":"-"},"title":"Blocked 2-hop AssumeRole chain from arn:aws:iam::111111\u003111111:user/Alice to arn:aws:iam::111111\u003111111:role/Admin","verdict":"blocked"}],"metadata":{"canonical_hash":"922462e7f09eb1d964f28a744eebcf72547efaa47ea86b42b397441ff4f86b77","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["assume_role_chain"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"assume_role_chain":"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":[{"constraint_id":"58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2","edge_id":"acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863","kind":"scp","reason":"SCP 58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2 denies sts:AssumeRole"}],"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":["58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2"],"edge_constraint_refs":["acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863:58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2"],"edge_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_chain_length_at_least_two","inputs":["2"],"reason":"chain length is 2 hops","result":"PASS","step":1},{"action":"check_endpoint_is_admin_equivalent","inputs":["arn:aws:iam::11111\u003111111\u0031:role/Admin"],"reason":"endpoint has * or iam:*","result":"PASS","step":2},{"action":"check_all_hops_have_valid_trust_and_permission_edges","inputs":["2"],"reason":"2/2 hops have both edges","result":"PASS","step":3},{"action":"check_no_scp_blocks_any_hop","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"hop 1: SCP 58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2 blocks (complete); hop 2: no SCP bindings observed on this hop","result":"FAIL","step":4},{"action":"check_no_boundary_blocks_any_hop","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"hop 1: no permission boundary bindings on this hop; hop 2: no permission boundary bindings on this hop","result":"PASS","step":5},{"action":"check_no_identity_deny_blocks_any_hop","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"hop 1: no identity policy Deny bindings observed on sts:AssumeRole witness edge; hop 2: no identity policy Deny bindings observed on sts:AssumeRole witness edge","result":"PASS","step":6},{"action":"check_no_hop_traverses_hyperedge","inputs":["4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"reason":"clean witnesses on 2/2 hops","result":"PASS","step":7}],"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":"a16b269e0b60a010e9f6bc448f41cb18edea91449\u003462432\u00318d8d5afd7f2b103b","finding_key":"733bb08440d7ae4cc1cb9dd20384b58110588e68dde029e9e2caf5d97e2558b0","pattern_id":"assume_role_chain","pattern_title":"Multi-Hop AssumeRole Privilege Escalation","pattern_version":"1.0.0","reasoner_exit_reason":"SCP blocks at least one hop in the 2-hop chain","required_checks":[{"description":"Chain length must be >= 2 hops (single-hop chains are covered by cross_account_trust)","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"chain_length_at_least_two","reason":"chain length is 2 hops","state":"pass"},{"description":"Chain endpoint role has admin-equivalent permissions (* or iam:*)","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"endpoint_is_admin_equivalent","reason":"endpoint role has * or iam:* permission edge","state":"pass"},{"description":"Every hop in the chain has BOTH a valid sts:AssumeRole permission edge AND an admitting trust edge on the next role","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"all_hops_have_valid_trust_and_permission_edges","reason":"all 2 hops verified","state":"pass"},{"description":"No SCP blocks sts:AssumeRole on any hop in the chain with complete confidence","evidence_refs":["58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2"],"name":"no_scp_blocks_any_hop","reason":"hop 1: SCP 58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2 blocks (complete); hop 2: no SCP bindings observed on this hop","state":"fail"},{"description":"No permission boundary blocks sts:AssumeRole on any hop in the chain","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_boundary_blocks_any_hop","reason":"hop 1: no permission boundary bindings on this hop; hop 2: no permission boundary bindings on this hop","state":"pass"},{"description":"No identity-policy Deny blocks sts:AssumeRole on any hop in the chain","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_identity_deny_blocks_any_hop","reason":"hop 1: no identity policy Deny bindings observed on sts:AssumeRole witness edge; hop 2: no identity policy Deny bindings observed on sts:AssumeRole witness edge","state":"pass"},{"description":"No hop's permission edge is a wildcard hyperedge or wildcard-resource grant","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15","4dacdf98eb213de156622608d00877ec79918d905f124a65ec5096b573f86fd4","53685a59c1a6e1d81799b46a6b87457df71322c4d62ff8847db6ef6dde4bb8c5","acf154ef0eaefcae8048c5f211b937a0ef738e35dae7482977d58d20a6d81863"],"name":"no_hop_traverses_hyperedge","reason":"all 2 hops have clean witness edges","state":"pass"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"info","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":"Blocked 2-hop AssumeRole chain from arn:aws:iam::11111\u003111111\u0031:user/Alice to arn:aws:iam::11111\u003111111\u0031:role/Admin","verdict":"blocked"}],"metadata":{"canonical_hash":"bb0d9d1b5395fb55985a64238e3ddea9f57af4cebd4258b0c59a5e7439a773fd","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["assume_role_chain"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"assume_role_chain":"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/assume_role_chain/fixture_c_hyperedge_inconclusive.json b/tests/fixtures/expected_output/findings/assume_role_chain/fixture_c_hyperedge_inconclusive.json index bbda6a6..1c5201d 100644 --- a/tests/fixtures/expected_output/findings/assume_role_chain/fixture_c_hyperedge_inconclusive.json +++ b/tests/fixtures/expected_output/findings/assume_role_chain/fixture_c_hyperedge_inconclusive.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d673422\u003250506\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_chain_length_at_least_two","inputs":["2"],"reason":"chain length is 2 hops","result":"PASS","step":1},{"action":"check_endpoint_is_admin_equivalent","inputs":["arn:aws:iam::111111\u003111111:role/Admin"],"reason":"endpoint has * or iam:*","result":"PASS","step":2},{"action":"check_all_hops_have_valid_trust_and_permission_edges","inputs":["2"],"reason":"2/2 hops have both edges","result":"PASS","step":3},{"action":"check_no_scp_blocks_any_hop","inputs":["1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e"],"reason":"hop 1: no SCP bindings observed on this hop; hop 2: no SCP bindings observed on this hop","result":"PASS","step":4},{"action":"check_no_boundary_blocks_any_hop","inputs":["1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e"],"reason":"hop 1: no permission boundary bindings on this hop; hop 2: no permission boundary bindings on this hop","result":"PASS","step":5},{"action":"check_no_identity_deny_blocks_any_hop","inputs":["1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e"],"reason":"hop 1: no identity policy Deny bindings observed on sts:AssumeRole witness edge; hop 2: no identity policy Deny bindings observed on sts:AssumeRole witness edge","result":"PASS","step":6},{"action":"check_no_hop_traverses_hyperedge","inputs":["1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e"],"reason":"clean witnesses on 1/2 hops","result":"UNKNOWN","step":7}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111","222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032222":["arn:aws:iam::111111\u003111111:role/Admin",0,"trust arn:aws:iam::111111\u003111111:role/DevOps"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"e4436075acb883bd551ab62ce75072e234f08b9dcf4b028259af5f639896c616","finding_key":"733bb08440d7ae4cc1cb9dd20384b58110588e68dde029e9e2caf5d97e2558b0","pattern_id":"assume_role_chain","pattern_title":"Multi-Hop AssumeRole Privilege Escalation","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: no_hop_traverses_hyperedge","required_checks":[{"description":"Chain length must be >= 2 hops (single-hop chains are covered by cross_account_trust)","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d673422\u003250506\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"chain_length_at_least_two","reason":"chain length is 2 hops","state":"pass"},{"description":"Chain endpoint role has admin-equivalent permissions (* or iam:*)","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"endpoint_is_admin_equivalent","reason":"endpoint role has * or iam:* permission edge","state":"pass"},{"description":"Every hop in the chain has BOTH a valid sts:AssumeRole permission edge AND an admitting trust edge on the next role","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d673422\u003250506\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"all_hops_have_valid_trust_and_permission_edges","reason":"all 2 hops verified","state":"pass"},{"description":"No SCP blocks sts:AssumeRole on any hop in the chain with complete confidence","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d673422\u003250506\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"no_scp_blocks_any_hop","reason":"hop 1: no SCP bindings observed on this hop; hop 2: no SCP bindings observed on this hop","state":"pass"},{"description":"No permission boundary blocks sts:AssumeRole on any hop in the chain","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d673422\u003250506\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"no_boundary_blocks_any_hop","reason":"hop 1: no permission boundary bindings on this hop; hop 2: no permission boundary bindings on this hop","state":"pass"},{"description":"No identity-policy Deny blocks sts:AssumeRole on any hop in the chain","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d673422\u003250506\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"no_identity_deny_blocks_any_hop","reason":"hop 1: no identity policy Deny bindings observed on sts:AssumeRole witness edge; hop 2: no identity policy Deny bindings observed on sts:AssumeRole witness edge","state":"pass"},{"description":"No hop's permission edge is a wildcard hyperedge or wildcard-resource grant","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d673422\u003250506\u0032c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"no_hop_traverses_hyperedge","reason":"at least one hop traverses an ambiguous edge","state":"unknown"}],"scenario_hash":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/Admin","region":"-"},"title":"Inconclusive 2-hop AssumeRole chain from arn:aws:iam::111111\u003111111:user/Alice to arn:aws:iam::111111\u003111111:role/Admin","verdict":"inconclusive"}],"metadata":{"canonical_hash":"6d3cdd59ca22d73b88719104e229ffb03265d441ee67151c4283d2e70aaebb3c","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["assume_role_chain"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"assume_role_chain":"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","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"node_refs":["5919bc18efcf7869009c55705dd4ebf56bd85b44b25df3337d18f20c5dce0a11","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","d9b72ca4a0c90f41abb056f4c4c3dda4d866575e4f4a496effb34abf9799cdf6"],"reasoning_trace":[{"action":"check_chain_length_at_least_two","inputs":["2"],"reason":"chain length is 2 hops","result":"PASS","step":1},{"action":"check_endpoint_is_admin_equivalent","inputs":["arn:aws:iam::11111\u003111111\u0031:role/Admin"],"reason":"endpoint has * or iam:*","result":"PASS","step":2},{"action":"check_all_hops_have_valid_trust_and_permission_edges","inputs":["2"],"reason":"2/2 hops have both edges","result":"PASS","step":3},{"action":"check_no_scp_blocks_any_hop","inputs":["1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e"],"reason":"hop 1: no SCP bindings observed on this hop; hop 2: no SCP bindings observed on this hop","result":"PASS","step":4},{"action":"check_no_boundary_blocks_any_hop","inputs":["1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e"],"reason":"hop 1: no permission boundary bindings on this hop; hop 2: no permission boundary bindings on this hop","result":"PASS","step":5},{"action":"check_no_identity_deny_blocks_any_hop","inputs":["1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e"],"reason":"hop 1: no identity policy Deny bindings observed on sts:AssumeRole witness edge; hop 2: no identity policy Deny bindings observed on sts:AssumeRole witness edge","result":"PASS","step":6},{"action":"check_no_hop_traverses_hyperedge","inputs":["1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e"],"reason":"clean witnesses on 1/2 hops","result":"UNKNOWN","step":7}],"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":"e4436075acb883bd551ab62ce75072e234f08b9dcf4b028259af5f639896c616","finding_key":"733bb08440d7ae4cc1cb9dd20384b58110588e68dde029e9e2caf5d97e2558b0","pattern_id":"assume_role_chain","pattern_title":"Multi-Hop AssumeRole Privilege Escalation","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: no_hop_traverses_hyperedge","required_checks":[{"description":"Chain length must be >= 2 hops (single-hop chains are covered by cross_account_trust)","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"chain_length_at_least_two","reason":"chain length is 2 hops","state":"pass"},{"description":"Chain endpoint role has admin-equivalent permissions (* or iam:*)","evidence_refs":["3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"endpoint_is_admin_equivalent","reason":"endpoint role has * or iam:* permission edge","state":"pass"},{"description":"Every hop in the chain has BOTH a valid sts:AssumeRole permission edge AND an admitting trust edge on the next role","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"all_hops_have_valid_trust_and_permission_edges","reason":"all 2 hops verified","state":"pass"},{"description":"No SCP blocks sts:AssumeRole on any hop in the chain with complete confidence","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"no_scp_blocks_any_hop","reason":"hop 1: no SCP bindings observed on this hop; hop 2: no SCP bindings observed on this hop","state":"pass"},{"description":"No permission boundary blocks sts:AssumeRole on any hop in the chain","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"no_boundary_blocks_any_hop","reason":"hop 1: no permission boundary bindings on this hop; hop 2: no permission boundary bindings on this hop","state":"pass"},{"description":"No identity-policy Deny blocks sts:AssumeRole on any hop in the chain","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"no_identity_deny_blocks_any_hop","reason":"hop 1: no identity policy Deny bindings observed on sts:AssumeRole witness edge; hop 2: no identity policy Deny bindings observed on sts:AssumeRole witness edge","state":"pass"},{"description":"No hop's permission edge is a wildcard hyperedge or wildcard-resource grant","evidence_refs":["0677b241547070a9ff92eb0dd6ce04d63dd9242c6f23843aeea28aea200d58e8","1c72f981829eac965e45a4f6f6b2239bd103a176eaa97f7d2acd08798e819db0","2bcdb06db98d67342\u003225050\u00362c1f81ebfb7d8403418e52554654e5829ea6071a","347dcc859ad4d4b564da2218bdcdcdd3b68600a45eaa98fb1251844961d1869e","3a18f19b8cea9fd5fa2ca7adf9af948d35c8d866c854c06b443b0b677eb8bd15"],"name":"no_hop_traverses_hyperedge","reason":"at least one hop traverses an ambiguous edge","state":"unknown"}],"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 2-hop AssumeRole chain from arn:aws:iam::11111\u003111111\u0031:user/Alice to arn:aws:iam::11111\u003111111\u0031:role/Admin","verdict":"inconclusive"}],"metadata":{"canonical_hash":"09d70c979fec16f1595699a7fe56fa91020941c528e50573834f4780db3c185a","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["assume_role_chain"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"assume_role_chain":"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/cross_account_trust/fixture_a_critical_naked_wildcard.json b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_a_critical_naked_wildcard.json index 77f7974..c5f6c43 100644 --- a/tests/fixtures/expected_output/findings/cross_account_trust/fixture_a_critical_naked_wildcard.json +++ b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_a_critical_naked_wildcard.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"node_refs":["101dfe7ea2f60bd30f5f0f5b512235f80c8292ad05fcf2e71de606c98b9894e3","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["CRITICAL_NAKED"],"reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam:::*"],"reason":"source principal arn:aws:iam:::* resolved to node AccountPrincipalSet","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam:::*"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"reason":"no SCP bindings observed on this edge","result":"PASS","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["CRITICAL_NAKED"],"reason":"CRITICAL_NAKED with no conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::111111\u003111111:role/ProdAdmin"],"reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["critical","validated"],"reason":"all checks PASS; classification CRITICAL_NAKED; truly external source","result":"VALIDATED","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::111111\u003111111:role/ProdAdmin",0,"trust policy for arn:aws:iam::111111\u003111111:role/ProdAdmin"]}},"finding_id":"54d69d14e0b6999d7f9a2787ba2343373eadd2236067732f3c5171975f2f34d2","finding_key":"7efbf636317225dc4f4a73ee9c0aefe763f8fec0d42843676fc988adfcfc6a71","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; classification CRITICAL_NAKED; truly external source","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"name":"naked_trust_is_risky","reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam:::* resolved to node AccountPrincipalSet","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"name":"no_scp_blocks_sts_assumerole","reason":"no SCP bindings observed on this edge","state":"pass"},{"description":"naked_trust value agrees with condition features","evidence_refs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"name":"trust_conditions_confirm_classification","reason":"CRITICAL_NAKED with no conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"critical","source":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam:::*","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdAdmin","region":"-"},"title":"Validated CRITICAL_NAKED external cross-account trust grant","verdict":"validated"}],"metadata":{"canonical_hash":"8b3679937f2c1a6a9e887985a40647320868d854338ec6b5bab54cc36d022cad","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"node_refs":["101dfe7ea2f60bd30f5f0f5b512235f80c8292ad05fcf2e71de606c98b9894e3","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["CRITICAL_NAKED"],"reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam:::*"],"reason":"source principal arn:aws:iam:::* resolved to node AccountPrincipalSet","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam:::*"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"reason":"no SCP bindings observed on this edge","result":"PASS","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["CRITICAL_NAKED"],"reason":"CRITICAL_NAKED with no conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["critical","validated"],"reason":"all checks PASS; classification CRITICAL_NAKED; truly external source","result":"VALIDATED","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin",0,"trust policy for arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"]}},"finding_id":"54d69d14e0b6999d7f9a2787ba2343373eadd2236067732f3c5171975f2f34d2","finding_key":"7efbf636317225dc4f4a73ee9c0aefe763f8fec0d42843676fc988adfcfc6a71","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; classification CRITICAL_NAKED; truly external source","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"name":"naked_trust_is_risky","reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam:::* resolved to node AccountPrincipalSet","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"name":"no_scp_blocks_sts_assumerole","reason":"no SCP bindings observed on this edge","state":"pass"},{"description":"naked_trust value agrees with condition features","evidence_refs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"name":"trust_conditions_confirm_classification","reason":"CRITICAL_NAKED with no conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["757944cd7915c5fecaa1db357573572abed05d77bf59065d231aae223b87caeb"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"critical","source":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam:::*","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin","region":"-"},"title":"Validated CRITICAL_NAKED external cross-account trust grant","verdict":"validated"}],"metadata":{"canonical_hash":"668c055edfd9828e2993a33b405f1eab467bfeef36387dd9c4a68e448953dfce","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/cross_account_trust/fixture_b_broad_naked_blocked_scp.json b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_b_broad_naked_blocked_scp.json index 75fe16e..ea9b576 100644 --- a/tests/fixtures/expected_output/findings/cross_account_trust/fixture_b_broad_naked_blocked_scp.json +++ b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_b_broad_naked_blocked_scp.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"cf50a880bb88876a04d3aa31ae577629\u003067485\u0030d334ff02010c4296682a43516","edge_id":"22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751","kind":"scp","reason":"SCP DenyAssumeRoleProd at OU ou-prod denies sts:AssumeRole"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["cf50a880bb88876a04d3aa31ae577629\u003067485\u0030d334ff02010c4296682a43516"],"edge_constraint_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751|cf50a880bb88876a04d3aa31ae577629\u003067485\u0030d334ff02010c4296682a43516"],"edge_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["BROAD_NAKED"],"reason":"naked_trust=BROAD_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::999999\u003999999:root"],"reason":"source principal arn:aws:iam::999999\u003999999:root resolved to node AccountPrincipalSet","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::999999\u003999999:root"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["cf50a880bb88876a04d3aa31ae577629\u003067485\u0030d334ff02010c4296682a43516"],"reason":"1 SCP binding(s) likely_blocking with governance_confidence=complete","result":"FAIL","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["BROAD_NAKED"],"reason":"BROAD_NAKED with no strong conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::111111\u003111111:role/ProdAdmin"],"reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["blocked","info"],"reason":"SCP binding blocks sts:AssumeRole with complete governance confidence","result":"BLOCKED","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::111111\u003111111:role/ProdAdmin",0,"trust policy for arn:aws:iam::111111\u003111111:role/ProdAdmin"]}},"finding_id":"db892bb19fa17c497600faafed1ac84c6d4b211ea2bb766e78445c66f4e0d643","finding_key":"d6b595ec5ff0b1daae2d6d51ea4794852f4dbc4aabbe13d40fa948ccab235cde","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"SCP binding blocks sts:AssumeRole with complete governance confidence","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"name":"naked_trust_is_risky","reason":"naked_trust=BROAD_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::999999\u003999999:root resolved to node AccountPrincipalSet","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["cf50a880bb88876a04d3aa31ae577629\u003067485\u0030d334ff02010c4296682a43516"],"name":"no_scp_blocks_sts_assumerole","reason":"1 SCP binding(s) likely_blocking with governance_confidence=complete","state":"fail"},{"description":"naked_trust value agrees with condition features","evidence_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"name":"trust_conditions_confirm_classification","reason":"BROAD_NAKED with no strong conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"info","source":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::999999\u003999999:root","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdAdmin","region":"-"},"title":"Blocked external cross-account trust grant (BROAD_NAKED)","verdict":"blocked"}],"metadata":{"canonical_hash":"f9563c28810f3162e419e79ef823f63ca2cdc6d65c16352859ddb5ea07211d92","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"cf50a880bb88876a04d3aa31ae57762\u003906748\u00350d334ff02010c4296682a43516","edge_id":"22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751","kind":"scp","reason":"SCP DenyAssumeRoleProd at OU ou-prod denies sts:AssumeRole"}],"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":["cf50a880bb88876a04d3aa31ae57762\u003906748\u00350d334ff02010c4296682a43516"],"edge_constraint_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751|cf50a880bb88876a04d3aa31ae57762\u003906748\u00350d334ff02010c4296682a43516"],"edge_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["BROAD_NAKED"],"reason":"naked_trust=BROAD_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::99999\u003999999\u0039:root"],"reason":"source principal arn:aws:iam::99999\u003999999\u0039:root resolved to node AccountPrincipalSet","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::99999\u003999999\u0039:root"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["cf50a880bb88876a04d3aa31ae57762\u003906748\u00350d334ff02010c4296682a43516"],"reason":"1 SCP binding(s) likely_blocking with governance_confidence=complete","result":"FAIL","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["BROAD_NAKED"],"reason":"BROAD_NAKED with no strong conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["blocked","info"],"reason":"SCP binding blocks sts:AssumeRole with complete governance confidence","result":"BLOCKED","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin",0,"trust policy for arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"]}},"finding_id":"db892bb19fa17c497600faafed1ac84c6d4b211ea2bb766e78445c66f4e0d643","finding_key":"d6b595ec5ff0b1daae2d6d51ea4794852f4dbc4aabbe13d40fa948ccab235cde","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"SCP binding blocks sts:AssumeRole with complete governance confidence","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"name":"naked_trust_is_risky","reason":"naked_trust=BROAD_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::99999\u003999999\u0039:root resolved to node AccountPrincipalSet","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["cf50a880bb88876a04d3aa31ae57762\u003906748\u00350d334ff02010c4296682a43516"],"name":"no_scp_blocks_sts_assumerole","reason":"1 SCP binding(s) likely_blocking with governance_confidence=complete","state":"fail"},{"description":"naked_trust value agrees with condition features","evidence_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"name":"trust_conditions_confirm_classification","reason":"BROAD_NAKED with no strong conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["22009347165f0b24c87cdb8709cb6df423db0fee031dac55954a1c15115df751"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"info","source":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::99999\u003999999\u0039:root","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin","region":"-"},"title":"Blocked external cross-account trust grant (BROAD_NAKED)","verdict":"blocked"}],"metadata":{"canonical_hash":"ab0d9f6edde930372f05c44af5732272776e579b2f48a116d90d06e1f6d67461","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/cross_account_trust/fixture_c_narrow_naked_weak.json b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_c_narrow_naked_weak.json index 1719bb7..554764b 100644 --- a/tests/fixtures/expected_output/findings/cross_account_trust/fixture_c_narrow_naked_weak.json +++ b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_c_narrow_naked_weak.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"node_refs":["de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122","f1b864c65e3e289ee0d312df6b671ba518a82e72894c25235b425e51f86d7c00"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["NARROW_NAKED"],"reason":"naked_trust=NARROW_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::999999\u003999999:role/Specific"],"reason":"source principal arn:aws:iam::999999\u003999999:role/Specific resolved to node IAMRole","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::999999\u003999999:role/Specific"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"reason":"no SCP bindings observed on this edge","result":"PASS","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["NARROW_NAKED"],"reason":"NARROW_NAKED with weak/no conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::111111\u003111111:role/ProdAdmin"],"reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["medium","validated"],"reason":"all checks PASS; classification NARROW_NAKED; truly external source","result":"VALIDATED","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::111111\u003111111:role/ProdAdmin",0,"trust policy for arn:aws:iam::111111\u003111111:role/ProdAdmin"]}},"finding_id":"e008d668f74f4b909376c105969\u003979181f7c0097bbe453c861b1fe58ea828be0","finding_key":"c16a6b8c3fa9801386d93adfd8a8c319abfa0d47c5634f3b6a054fbab3842ea7","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; classification NARROW_NAKED; truly external source","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"name":"naked_trust_is_risky","reason":"naked_trust=NARROW_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::999999\u003999999:role/Specific resolved to node IAMRole","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"name":"no_scp_blocks_sts_assumerole","reason":"no SCP bindings observed on this edge","state":"pass"},{"description":"naked_trust value agrees with condition features","evidence_refs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"name":"trust_conditions_confirm_classification","reason":"NARROW_NAKED with weak/no conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"medium","source":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::999999\u003999999:role/Specific","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdAdmin","region":"-"},"title":"Validated NARROW_NAKED external cross-account trust grant","verdict":"validated"}],"metadata":{"canonical_hash":"510d42c596ae1d547fff78819b3056139fbf7b2a4f1aca549b15452ba9fa4161","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"node_refs":["de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122","f1b864c65e3e289ee0d312df6b671ba518a82e72894c25235b425e51f86d7c00"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["NARROW_NAKED"],"reason":"naked_trust=NARROW_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::99999\u003999999\u0039:role/Specific"],"reason":"source principal arn:aws:iam::99999\u003999999\u0039:role/Specific resolved to node IAMRole","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::99999\u003999999\u0039:role/Specific"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"reason":"no SCP bindings observed on this edge","result":"PASS","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["NARROW_NAKED"],"reason":"NARROW_NAKED with weak/no conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["medium","validated"],"reason":"all checks PASS; classification NARROW_NAKED; truly external source","result":"VALIDATED","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin",0,"trust policy for arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"]}},"finding_id":"e008d668f74f4b909376c10596\u003997918\u0031f7c0097bbe453c861b1fe58ea828be0","finding_key":"c16a6b8c3fa9801386d93adfd8a8c319abfa0d47c5634f3b6a054fbab3842ea7","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; classification NARROW_NAKED; truly external source","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"name":"naked_trust_is_risky","reason":"naked_trust=NARROW_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::99999\u003999999\u0039:role/Specific resolved to node IAMRole","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"name":"no_scp_blocks_sts_assumerole","reason":"no SCP bindings observed on this edge","state":"pass"},{"description":"naked_trust value agrees with condition features","evidence_refs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"name":"trust_conditions_confirm_classification","reason":"NARROW_NAKED with weak/no conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["bceb891e5c9c783d24012fcc1658d4ea59f51692b546c7038b2d1e06e1e6f631"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"medium","source":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::99999\u003999999\u0039:role/Specific","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin","region":"-"},"title":"Validated NARROW_NAKED external cross-account trust grant","verdict":"validated"}],"metadata":{"canonical_hash":"0e4566709f022eec6e7d6f858c7ef26fbd99ee2758ec3d2a4009137d028fc7d1","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/cross_account_trust/fixture_e_scp_partial_inconclusive.json b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_e_scp_partial_inconclusive.json index 308a08c..ff8605e 100644 --- a/tests/fixtures/expected_output/findings/cross_account_trust/fixture_e_scp_partial_inconclusive.json +++ b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_e_scp_partial_inconclusive.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":["d5efb7277849deacae17c738f0675c5a400b01ae850a82e98b70e1552dd5e6b9"],"edge_constraint_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0|d5efb7277849deacae17c738f0675c5a400b01ae850a82e98b70e1552dd5e6b9"],"edge_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["CRITICAL_NAKED"],"reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::999999\u003999999:root"],"reason":"source principal arn:aws:iam::999999\u003999999:root resolved to node AccountPrincipalSet","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::999999\u003999999:root"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["d5efb7277849deacae17c738f0675c5a400b01ae850a82e98b70e1552dd5e6b9"],"reason":"1 SCP binding(s) with governance_confidence \u2208 partial/needs_review \u2014 cannot confirm whether the action is blocked","result":"UNKNOWN","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["CRITICAL_NAKED"],"reason":"CRITICAL_NAKED with no conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::111111\u003111111:role/ProdAdmin"],"reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["high","inconclusive"],"reason":"check(s) UNKNOWN: no_scp_blocks_sts_assumerole","result":"INCONCLUSIVE","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::111111\u003111111:role/ProdAdmin",0,"trust policy for arn:aws:iam::111111\u003111111:role/ProdAdmin"]}},"finding_id":"d9c6203c97f29383e017fb4a7902c0015edf3cd40071b139818b5bac2578dcca","finding_key":"d6b595ec5ff0b1daae2d6d51ea4794852f4dbc4aabbe13d40fa948ccab235cde","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: no_scp_blocks_sts_assumerole","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"naked_trust_is_risky","reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::999999\u003999999:root resolved to node AccountPrincipalSet","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["d5efb7277849deacae17c738f0675c5a400b01ae850a82e98b70e1552dd5e6b9"],"name":"no_scp_blocks_sts_assumerole","reason":"1 SCP binding(s) with governance_confidence \u2208 partial/needs_review \u2014 cannot confirm whether the action is blocked","state":"unknown"},{"description":"naked_trust value agrees with condition features","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"trust_conditions_confirm_classification","reason":"CRITICAL_NAKED with no conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::999999\u003999999:root","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdAdmin","region":"-"},"title":"Inconclusive external cross-account trust evaluation (CRITICAL_NAKED)","verdict":"inconclusive"}],"metadata":{"canonical_hash":"7283f6fe3fa59b5ca13fb75d2204ec27c9e4e466cfbc72d73305a738e6659268","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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":["d5efb7277849deacae17c738f0675c5a400b01ae850a82e98b70e1552dd5e6b9"],"edge_constraint_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0|d5efb7277849deacae17c738f0675c5a400b01ae850a82e98b70e1552dd5e6b9"],"edge_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["CRITICAL_NAKED"],"reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::99999\u003999999\u0039:root"],"reason":"source principal arn:aws:iam::99999\u003999999\u0039:root resolved to node AccountPrincipalSet","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::99999\u003999999\u0039:root"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["d5efb7277849deacae17c738f0675c5a400b01ae850a82e98b70e1552dd5e6b9"],"reason":"1 SCP binding(s) with governance_confidence \u2208 partial/needs_review \u2014 cannot confirm whether the action is blocked","result":"UNKNOWN","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["CRITICAL_NAKED"],"reason":"CRITICAL_NAKED with no conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["high","inconclusive"],"reason":"check(s) UNKNOWN: no_scp_blocks_sts_assumerole","result":"INCONCLUSIVE","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin",0,"trust policy for arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"]}},"finding_id":"d9c6203c97f29383e017fb4a7902c0015edf3cd40071b139818b5bac2578dcca","finding_key":"d6b595ec5ff0b1daae2d6d51ea4794852f4dbc4aabbe13d40fa948ccab235cde","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: no_scp_blocks_sts_assumerole","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"naked_trust_is_risky","reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::99999\u003999999\u0039:root resolved to node AccountPrincipalSet","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["d5efb7277849deacae17c738f0675c5a400b01ae850a82e98b70e1552dd5e6b9"],"name":"no_scp_blocks_sts_assumerole","reason":"1 SCP binding(s) with governance_confidence \u2208 partial/needs_review \u2014 cannot confirm whether the action is blocked","state":"unknown"},{"description":"naked_trust value agrees with condition features","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"trust_conditions_confirm_classification","reason":"CRITICAL_NAKED with no conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::99999\u003999999\u0039:root","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin","region":"-"},"title":"Inconclusive external cross-account trust evaluation (CRITICAL_NAKED)","verdict":"inconclusive"}],"metadata":{"canonical_hash":"a15e2a9c6752231fd9412386ee8615bc6e4723b09eaf189ad8f17f5057384bd4","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/cross_account_trust/fixture_f_oidc_broad_no_sub.json b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_f_oidc_broad_no_sub.json index ecc04a1..31bf1a2 100644 --- a/tests/fixtures/expected_output/findings/cross_account_trust/fixture_f_oidc_broad_no_sub.json +++ b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_f_oidc_broad_no_sub.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"node_refs":["951c922d1c9940139d76ca22d35589e88a333646\u003028813\u0033ad31defddf50de78b","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["BROAD_NAKED"],"reason":"naked_trust=BROAD_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::111111\u003111111:oidc-provider/token.actions.githubusercontent.com"],"reason":"source principal arn:aws:iam::111111\u003111111:oidc-provider/token.actions.githubusercontent.com resolved to node OIDCProvider","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::111111\u003111111:oidc-provider/token.actions.githubusercontent.com"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"reason":"no SCP bindings observed on this edge","result":"PASS","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["BROAD_NAKED"],"reason":"BROAD_NAKED with no strong conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::111111\u003111111:role/ProdAdmin"],"reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["high","validated"],"reason":"all checks PASS; classification BROAD_NAKED; truly external source","result":"VALIDATED","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::111111\u003111111:role/ProdAdmin",0,"trust policy for arn:aws:iam::111111\u003111111:role/ProdAdmin"]}},"finding_id":"94c5577a418f85cfd68952c330940a2a5e740f51c7651eae3b778cb3f0bca129","finding_key":"9258cbb6a40024c9ad7ddcf26e10b49bdfa36349b2928a5e991d5d4040ade21d","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; classification BROAD_NAKED; truly external source","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"name":"naked_trust_is_risky","reason":"naked_trust=BROAD_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::111111\u003111111:oidc-provider/token.actions.githubusercontent.com resolved to node OIDCProvider","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"name":"no_scp_blocks_sts_assumerole","reason":"no SCP bindings observed on this edge","state":"pass"},{"description":"naked_trust value agrees with condition features","evidence_refs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"name":"trust_conditions_confirm_classification","reason":"BROAD_NAKED with no strong conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"OIDCProvider","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:oidc-provider/token.actions.githubusercontent.com","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdAdmin","region":"-"},"title":"Validated BROAD_NAKED external cross-account trust grant","verdict":"validated"}],"metadata":{"canonical_hash":"af4a720753d023758361f39978cb575bd9c068f56454f5de7e2e05c24005bd84","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"node_refs":["951c922d1c9940139d76ca22d35589e88a33364\u003602881\u00333ad31defddf50de78b","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["BROAD_NAKED"],"reason":"naked_trust=BROAD_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::11111\u003111111\u0031:oidc-provider/token.actions.githubusercontent.com"],"reason":"source principal arn:aws:iam::11111\u003111111\u0031:oidc-provider/token.actions.githubusercontent.com resolved to node OIDCProvider","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::11111\u003111111\u0031:oidc-provider/token.actions.githubusercontent.com"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"reason":"no SCP bindings observed on this edge","result":"PASS","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["BROAD_NAKED"],"reason":"BROAD_NAKED with no strong conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["high","validated"],"reason":"all checks PASS; classification BROAD_NAKED; truly external source","result":"VALIDATED","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin",0,"trust policy for arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"]}},"finding_id":"94c5577a418f85cfd68952c330940a2a5e740f51c7651eae3b778cb3f0bca129","finding_key":"9258cbb6a40024c9ad7ddcf26e10b49bdfa36349b2928a5e991d5d4040ade21d","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; classification BROAD_NAKED; truly external source","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"name":"naked_trust_is_risky","reason":"naked_trust=BROAD_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::11111\u003111111\u0031:oidc-provider/token.actions.githubusercontent.com resolved to node OIDCProvider","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"name":"no_scp_blocks_sts_assumerole","reason":"no SCP bindings observed on this edge","state":"pass"},{"description":"naked_trust value agrees with condition features","evidence_refs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"name":"trust_conditions_confirm_classification","reason":"BROAD_NAKED with no strong conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["db8433e7605773d07733ab702fce530df5ed6a47e968a687783b70a7f0020d3d"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"OIDCProvider","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:oidc-provider/token.actions.githubusercontent.com","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin","region":"-"},"title":"Validated BROAD_NAKED external cross-account trust grant","verdict":"validated"}],"metadata":{"canonical_hash":"ee3e897b9f769bf567eafc5fbb9657e0c9533e1c22afba565bc71839eeb41683","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/cross_account_trust/fixture_g_unsupported_scp_inconclusive.json b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_g_unsupported_scp_inconclusive.json index 75a179c..274af5f 100644 --- a/tests/fixtures/expected_output/findings/cross_account_trust/fixture_g_unsupported_scp_inconclusive.json +++ b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_g_unsupported_scp_inconclusive.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":["ff41779feaf5a6a5e23f817c865d24e009ecaac99e50f09a911bbacc612451e4"],"edge_constraint_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0|ff41779feaf5a6a5e23f817c865d24e009ecaac99e50f09a911bbacc612451e4"],"edge_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["CRITICAL_NAKED"],"reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::999999\u003999999:root"],"reason":"source principal arn:aws:iam::999999\u003999999:root resolved to node AccountPrincipalSet","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::999999\u003999999:root"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["ff41779feaf5a6a5e23f817c865d24e009ecaac99e50f09a911bbacc612451e4"],"reason":"1 SCP binding(s) with governance_confidence \u2208 partial/needs_review \u2014 cannot confirm whether the action is blocked","result":"UNKNOWN","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["CRITICAL_NAKED"],"reason":"CRITICAL_NAKED with no conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::111111\u003111111:role/ProdAdmin"],"reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["high","inconclusive"],"reason":"check(s) UNKNOWN: no_scp_blocks_sts_assumerole","result":"INCONCLUSIVE","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::111111\u003111111:role/ProdAdmin",0,"trust policy for arn:aws:iam::111111\u003111111:role/ProdAdmin"]}},"finding_id":"b6b5ef0548ab9e1ef9ab5aee00343561b46d130fb027a968f9ba9653422ecdda","finding_key":"d6b595ec5ff0b1daae2d6d51ea4794852f4dbc4aabbe13d40fa948ccab235cde","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: no_scp_blocks_sts_assumerole","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"naked_trust_is_risky","reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::999999\u003999999:root resolved to node AccountPrincipalSet","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["ff41779feaf5a6a5e23f817c865d24e009ecaac99e50f09a911bbacc612451e4"],"name":"no_scp_blocks_sts_assumerole","reason":"1 SCP binding(s) with governance_confidence \u2208 partial/needs_review \u2014 cannot confirm whether the action is blocked","state":"unknown"},{"description":"naked_trust value agrees with condition features","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"trust_conditions_confirm_classification","reason":"CRITICAL_NAKED with no conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::999999\u003999999:root","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdAdmin","region":"-"},"title":"Inconclusive external cross-account trust evaluation (CRITICAL_NAKED)","verdict":"inconclusive"}],"metadata":{"canonical_hash":"f6bceedf3fc144ea10286a0694f68f6b875935acdd97d19d6fd13b1f729ef59e","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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":["ff41779feaf5a6a5e23f817c865d24e009ecaac99e50f09a911bbacc612451e4"],"edge_constraint_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0|ff41779feaf5a6a5e23f817c865d24e009ecaac99e50f09a911bbacc612451e4"],"edge_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["CRITICAL_NAKED"],"reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::99999\u003999999\u0039:root"],"reason":"source principal arn:aws:iam::99999\u003999999\u0039:root resolved to node AccountPrincipalSet","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::99999\u003999999\u0039:root"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["ff41779feaf5a6a5e23f817c865d24e009ecaac99e50f09a911bbacc612451e4"],"reason":"1 SCP binding(s) with governance_confidence \u2208 partial/needs_review \u2014 cannot confirm whether the action is blocked","result":"UNKNOWN","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["CRITICAL_NAKED"],"reason":"CRITICAL_NAKED with no conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["high","inconclusive"],"reason":"check(s) UNKNOWN: no_scp_blocks_sts_assumerole","result":"INCONCLUSIVE","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin",0,"trust policy for arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"]}},"finding_id":"b6b5ef0548ab9e1ef9ab5aee00343561b46d130fb027a968f9ba9653422ecdda","finding_key":"d6b595ec5ff0b1daae2d6d51ea4794852f4dbc4aabbe13d40fa948ccab235cde","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: no_scp_blocks_sts_assumerole","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"naked_trust_is_risky","reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::99999\u003999999\u0039:root resolved to node AccountPrincipalSet","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["ff41779feaf5a6a5e23f817c865d24e009ecaac99e50f09a911bbacc612451e4"],"name":"no_scp_blocks_sts_assumerole","reason":"1 SCP binding(s) with governance_confidence \u2208 partial/needs_review \u2014 cannot confirm whether the action is blocked","state":"unknown"},{"description":"naked_trust value agrees with condition features","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"trust_conditions_confirm_classification","reason":"CRITICAL_NAKED with no conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::99999\u003999999\u0039:root","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin","region":"-"},"title":"Inconclusive external cross-account trust evaluation (CRITICAL_NAKED)","verdict":"inconclusive"}],"metadata":{"canonical_hash":"78297f2a6978f798012f15559d9c2d4fa41a376413f31fc4ffb22df5bb448efd","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/cross_account_trust/fixture_h_multi_statement_dedup.json b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_h_multi_statement_dedup.json index 9fdd80b..6e7d311 100644 --- a/tests/fixtures/expected_output/findings/cross_account_trust/fixture_h_multi_statement_dedup.json +++ b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_h_multi_statement_dedup.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["BROAD_NAKED"],"reason":"naked_trust=BROAD_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::999999\u003999999:root"],"reason":"source principal arn:aws:iam::999999\u003999999:root resolved to node AccountPrincipalSet","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::999999\u003999999:root"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"reason":"no SCP bindings observed on this edge","result":"PASS","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["BROAD_NAKED"],"reason":"BROAD_NAKED with no strong conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::111111\u003111111:role/ProdAdmin"],"reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["high","validated"],"reason":"all checks PASS; classification BROAD_NAKED; truly external source","result":"VALIDATED","step":8}],"statement_digests":["fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"],"statement_sources":{"fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210":["arn:aws:iam::111111\u003111111:role/ProdAdmin",1,"trust policy for arn:aws:iam::111111\u003111111:role/ProdAdmin"]}},"finding_id":"24a7b7d1ebf0e335271f6b0ffc2ffd3e7e53ae3ca88cce61cc472d8cd52710ec","finding_key":"d6b595ec5ff0b1daae2d6d51ea4794852f4dbc4aabbe13d40fa948ccab235cde","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; classification BROAD_NAKED; truly external source","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"name":"naked_trust_is_risky","reason":"naked_trust=BROAD_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::999999\u003999999:root resolved to node AccountPrincipalSet","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"name":"no_scp_blocks_sts_assumerole","reason":"no SCP bindings observed on this edge","state":"pass"},{"description":"naked_trust value agrees with condition features","evidence_refs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"name":"trust_conditions_confirm_classification","reason":"BROAD_NAKED with no strong conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::999999\u003999999:root","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdAdmin","region":"-"},"title":"Validated BROAD_NAKED external cross-account trust grant","verdict":"validated"}],"metadata":{"canonical_hash":"5752b4359da7f2b6b038d1fe264610aa72641e1072ddd4d09d0947ce139546d2","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["BROAD_NAKED"],"reason":"naked_trust=BROAD_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::99999\u003999999\u0039:root"],"reason":"source principal arn:aws:iam::99999\u003999999\u0039:root resolved to node AccountPrincipalSet","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::99999\u003999999\u0039:root"],"reason":"source node properties.org_member resolved to external","result":"EXTERNAL","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"reason":"no SCP bindings observed on this edge","result":"PASS","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["BROAD_NAKED"],"reason":"BROAD_NAKED with no strong conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["high","validated"],"reason":"all checks PASS; classification BROAD_NAKED; truly external source","result":"VALIDATED","step":8}],"statement_digests":["fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"],"statement_sources":{"fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin",1,"trust policy for arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"]}},"finding_id":"24a7b7d1ebf0e335271f6b0ffc2ffd3e7e53ae3ca88cce61cc472d8cd52710ec","finding_key":"d6b595ec5ff0b1daae2d6d51ea4794852f4dbc4aabbe13d40fa948ccab235cde","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; classification BROAD_NAKED; truly external source","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"name":"naked_trust_is_risky","reason":"naked_trust=BROAD_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::99999\u003999999\u0039:root resolved to node AccountPrincipalSet","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"name":"no_scp_blocks_sts_assumerole","reason":"no SCP bindings observed on this edge","state":"pass"},{"description":"naked_trust value agrees with condition features","evidence_refs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"name":"trust_conditions_confirm_classification","reason":"BROAD_NAKED with no strong conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["fa21ada7b2fdc44e8abb733160ec9dcdb9a959c0e7cbb43a3eb851e17ab07290"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::99999\u003999999\u0039:root","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin","region":"-"},"title":"Validated BROAD_NAKED external cross-account trust grant","verdict":"validated"}],"metadata":{"canonical_hash":"b7df724666fef25539b401f76996\u003416871\u0039eb481bc837767782c1d471b326d01","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/cross_account_trust/fixture_z_same_org_downgrade.json b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_z_same_org_downgrade.json index 82a43b0..8469ffc 100644 --- a/tests/fixtures/expected_output/findings/cross_account_trust/fixture_z_same_org_downgrade.json +++ b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_z_same_org_downgrade.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["CRITICAL_NAKED"],"reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::999999\u003999999:root"],"reason":"source principal arn:aws:iam::999999\u003999999:root resolved to node AccountPrincipalSet","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::999999\u003999999:root"],"reason":"source node properties.org_member resolved to same_org","result":"SAME_ORG","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"reason":"no SCP bindings observed on this edge","result":"PASS","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["CRITICAL_NAKED"],"reason":"CRITICAL_NAKED with no conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::111111\u003111111:role/ProdAdmin"],"reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["high","validated"],"reason":"all checks PASS; classification CRITICAL_NAKED; same-org cross-account \u2192 severity downgraded critical\u2192high","result":"VALIDATED","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::111111\u003111111:role/ProdAdmin",0,"trust policy for arn:aws:iam::111111\u003111111:role/ProdAdmin"]}},"finding_id":"4b87261a2a4802ae7b09e5b4fa018555d82b3a823eea12b2bcbfa684fb6fa2b2","finding_key":"d6b595ec5ff0b1daae2d6d51ea4794852f4dbc4aabbe13d40fa948ccab235cde","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; classification CRITICAL_NAKED; same-org cross-account \u2192 severity downgraded critical\u2192high","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"naked_trust_is_risky","reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::999999\u003999999:root resolved to node AccountPrincipalSet","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"no_scp_blocks_sts_assumerole","reason":"no SCP bindings observed on this edge","state":"pass"},{"description":"naked_trust value agrees with condition features","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"trust_conditions_confirm_classification","reason":"CRITICAL_NAKED with no conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::111111\u003111111:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::999999\u003999999:root","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdAdmin","region":"-"},"title":"Validated CRITICAL_NAKED same-org cross-account trust grant","verdict":"validated"}],"metadata":{"canonical_hash":"2b31836ce38aed57ac80074401eda75f3bb397ac889962dbe12672fe22b5f04b","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","de3bcf066594e5eb1c022ec2178c0317b091c7c4cbe63f32e10eff5a8acb0122"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"reason":"features.cross_account is True","result":"PASS","step":1},{"action":"check_naked_trust_is_risky","inputs":["CRITICAL_NAKED"],"reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","result":"PASS","step":2},{"action":"check_source_principal_resolvable","inputs":["arn:aws:iam::99999\u003999999\u0039:root"],"reason":"source principal arn:aws:iam::99999\u003999999\u0039:root resolved to node AccountPrincipalSet","result":"PASS","step":3},{"action":"evaluate_source_org_membership","inputs":["arn:aws:iam::99999\u003999999\u0039:root"],"reason":"source node properties.org_member resolved to same_org","result":"SAME_ORG","step":4},{"action":"check_no_scp_blocks_sts_assumerole","inputs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"reason":"no SCP bindings observed on this edge","result":"PASS","step":5},{"action":"check_trust_conditions_confirm_classification","inputs":["CRITICAL_NAKED"],"reason":"CRITICAL_NAKED with no conditions \u2014 consistent","result":"PASS","step":6},{"action":"check_target_role_exists_in_graph","inputs":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","result":"PASS","step":7},{"action":"emit_verdict","inputs":["high","validated"],"reason":"all checks PASS; classification CRITICAL_NAKED; same-org cross-account \u2192 severity downgraded critical\u2192high","result":"VALIDATED","step":8}],"statement_digests":["deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"],"statement_sources":{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin",0,"trust policy for arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin"]}},"finding_id":"4b87261a2a4802ae7b09e5b4fa018555d82b3a823eea12b2bcbfa684fb6fa2b2","finding_key":"d6b595ec5ff0b1daae2d6d51ea4794852f4dbc4aabbe13d40fa948ccab235cde","pattern_id":"cross_account_trust","pattern_title":"Cross-account trust without strong constraints","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; classification CRITICAL_NAKED; same-org cross-account \u2192 severity downgraded critical\u2192high","required_checks":[{"description":"Trust edge crosses an account boundary","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"naked_trust_is_risky","reason":"naked_trust=CRITICAL_NAKED \u2208 risky set","state":"pass"},{"description":"Source principal node exists in the fact graph","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"source_principal_resolvable","reason":"source principal arn:aws:iam::99999\u003999999\u0039:root resolved to node AccountPrincipalSet","state":"pass"},{"description":"No SCP blocks the trust action with full confidence","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"no_scp_blocks_sts_assumerole","reason":"no SCP bindings observed on this edge","state":"pass"},{"description":"naked_trust value agrees with condition features","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"trust_conditions_confirm_classification","reason":"CRITICAL_NAKED with no conditions \u2014 consistent","state":"pass"},{"description":"Target role node exists in the fact graph","evidence_refs":["17b606a0d40ec959d11a7e5267e6b53153ff9f69f287477a9e83aa3f897769d0"],"name":"target_role_exists_in_graph","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin resolved to node IAMRole","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::99999\u003999999\u0039:root","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:role/ProdAdmin","region":"-"},"title":"Validated CRITICAL_NAKED same-org cross-account trust grant","verdict":"validated"}],"metadata":{"canonical_hash":"9467ca034cd56c7ef9b5622f12ea47908ca4ceb2e4b5b010acb7eeee7d33ee78","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["cross_account_trust"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"cross_account_trust":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/iam_group_membership_escalation/fixture_a_validated_critical.json b/tests/fixtures/expected_output/findings/iam_group_membership_escalation/fixture_a_validated_critical.json index d27e4bd..c2b9756 100644 --- a/tests/fixtures/expected_output/findings/iam_group_membership_escalation/fixture_a_validated_critical.json +++ b/tests/fixtures/expected_output/findings/iam_group_membership_escalation/fixture_a_validated_critical.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937","c6fe0a6f4174c7214ba22aa074ff968d9ba079313c08eaedfeb58fea88b786e4"],"node_refs":["253084f77ea308a894c0ba30c6a54da0149a162eea04ae95ca55562b58e3eb6e","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_add_user_to_group_permission","inputs":["arn:aws:iam::111111\u003111111:user/Alice"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_witness_edge_is_clean","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"witness edge resolves to specific target group","result":"PASS","step":2},{"action":"check_no_scp_blocks_add_user_to_group","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_add_user_to_group","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_add_user_to_group","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"no identity policy Deny bindings observed on iam:AddUserToGroup witness edge","result":"PASS","step":5},{"action":"check_target_group_is_admin_equivalent","inputs":["arn:aws:iam::111111\u003111111:group/Admins"],"reason":"target group has admin-equivalent permissions (witness edge c6fe0a6f4174\u2026)","result":"PASS","step":6}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/GroupMgmt",0,"iam:AddUserToGroup grant"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"1a12f7eef478e4e3feb13b6db3182a8a4af6c40f7c08f06d3d616233dcc17df3","finding_key":"cb4b1f1f57c1c9086b7be1400289a5bbfc49fec6947d4b7a7713a0aaf074a3ee","pattern_id":"iam_group_membership_escalation","pattern_title":"IAM Group Membership Escalation","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; user can add themselves to admin-equivalent group and inherit its permissions","required_checks":[{"description":"User has a permission edge for iam:AddUserToGroup (enumeration invariant)","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"source_has_add_user_to_group_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge for iam:AddUserToGroup resolves to a specific target group (clean witness proves the edge's target)","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"witness_edge_is_clean","reason":"witness edge resolves to specific target group","state":"pass"},{"description":"No SCP blocks iam:AddUserToGroup on this edge with complete governance confidence","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"no_scp_blocks_add_user_to_group","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks iam:AddUserToGroup on this edge","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"no_boundary_blocks_add_user_to_group","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks iam:AddUserToGroup on this edge","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"no_identity_deny_blocks_add_user_to_group","reason":"no identity policy Deny bindings observed on iam:AddUserToGroup witness edge","state":"pass"},{"description":"Target group has permissions equivalent to administrator access via the shared two-tier admin detection (explicit iam:* or wildcard expansion across \u22653 prefixes)","evidence_refs":["c6fe0a6f4174c7214ba22aa074ff968d9ba079313c08eaedfeb58fea88b786e4"],"name":"target_group_is_admin_equivalent","reason":"target group has admin-equivalent permissions (witness edge c6fe0a6f4174\u2026)","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"critical","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMGroup","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:group/Admins","region":"-"},"title":"Validated group membership escalation: arn:aws:iam::111111\u003111111:user/Alice can call iam:AddUserToGroup on admin-equivalent group arn:aws:iam::111111\u003111111:group/Admins","verdict":"validated"}],"metadata":{"canonical_hash":"645cb791001d3f44fe3bd839e21e56d147f2f46841081d9ce60993ecb36d4f94","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["iam_group_membership_escalation"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"iam_group_membership_escalation":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937","c6fe0a6f4174c7214ba22aa074ff968d9ba079313c08eaedfeb58fea88b786e4"],"node_refs":["253084f77ea308a894c0ba30c6a54da0149a162eea04ae95ca55562b58e3eb6e","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_add_user_to_group_permission","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_witness_edge_is_clean","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"witness edge resolves to specific target group","result":"PASS","step":2},{"action":"check_no_scp_blocks_add_user_to_group","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_add_user_to_group","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_add_user_to_group","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"no identity policy Deny bindings observed on iam:AddUserToGroup witness edge","result":"PASS","step":5},{"action":"check_target_group_is_admin_equivalent","inputs":["arn:aws:iam::11111\u003111111\u0031:group/Admins"],"reason":"target group has admin-equivalent permissions (witness edge c6fe0a6f4174\u2026)","result":"PASS","step":6}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/GroupMgmt",0,"iam:AddUserToGroup grant"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"1a12f7eef478e4e3feb13b6db3182a8a4af6c40f7c08f06d3d616233dcc17df3","finding_key":"cb4b1f1f57c1c9086b7be1400289a5bbfc49fec6947d4b7a7713a0aaf074a3ee","pattern_id":"iam_group_membership_escalation","pattern_title":"IAM Group Membership Escalation","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; user can add themselves to admin-equivalent group and inherit its permissions","required_checks":[{"description":"User has a permission edge for iam:AddUserToGroup (enumeration invariant)","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"source_has_add_user_to_group_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge for iam:AddUserToGroup resolves to a specific target group (clean witness proves the edge's target)","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"witness_edge_is_clean","reason":"witness edge resolves to specific target group","state":"pass"},{"description":"No SCP blocks iam:AddUserToGroup on this edge with complete governance confidence","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"no_scp_blocks_add_user_to_group","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks iam:AddUserToGroup on this edge","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"no_boundary_blocks_add_user_to_group","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks iam:AddUserToGroup on this edge","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"no_identity_deny_blocks_add_user_to_group","reason":"no identity policy Deny bindings observed on iam:AddUserToGroup witness edge","state":"pass"},{"description":"Target group has permissions equivalent to administrator access via the shared two-tier admin detection (explicit iam:* or wildcard expansion across \u22653 prefixes)","evidence_refs":["c6fe0a6f4174c7214ba22aa074ff968d9ba079313c08eaedfeb58fea88b786e4"],"name":"target_group_is_admin_equivalent","reason":"target group has admin-equivalent permissions (witness edge c6fe0a6f4174\u2026)","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"critical","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"IAMGroup","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:group/Admins","region":"-"},"title":"Validated group membership escalation: arn:aws:iam::11111\u003111111\u0031:user/Alice can call iam:AddUserToGroup on admin-equivalent group arn:aws:iam::11111\u003111111\u0031:group/Admins","verdict":"validated"}],"metadata":{"canonical_hash":"b740bde7e0fc79a5cfd781cc54778\u003130272\u00392bd5a0c0fd54cb153a41f5183d79","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["iam_group_membership_escalation"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"iam_group_membership_escalation":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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/iam_group_membership_escalation/fixture_b_wildcard_inconclusive.json b/tests/fixtures/expected_output/findings/iam_group_membership_escalation/fixture_b_wildcard_inconclusive.json index 68a668e..a38aac3 100644 --- a/tests/fixtures/expected_output/findings/iam_group_membership_escalation/fixture_b_wildcard_inconclusive.json +++ b/tests/fixtures/expected_output/findings/iam_group_membership_escalation/fixture_b_wildcard_inconclusive.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["c6fe0a6f4174c7214ba22aa074ff968d9ba079313c08eaedfeb58fea88b786e4","e869651\u003752093\u00370e4af442abcebcb356903\u003376587\u0034b2c4052b4a893c8135f375"],"node_refs":["253084f77ea308a894c0ba30c6a54da0149a162eea04ae95ca55562b58e3eb6e","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_add_user_to_group_permission","inputs":["arn:aws:iam::111111\u003111111:user/Alice"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_witness_edge_is_clean","inputs":["e869651\u003752093\u00370e4af442abcebcb356903\u003376587\u0034b2c4052b4a893c8135f375"],"reason":"witness edge is wildcard-expansion hyperedge or wildcard-resource (target group iterated from all groups)","result":"UNKNOWN","step":2},{"action":"check_no_scp_blocks_add_user_to_group","inputs":["e869651\u003752093\u00370e4af442abcebcb356903\u003376587\u0034b2c4052b4a893c8135f375"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_add_user_to_group","inputs":["e869651\u003752093\u00370e4af442abcebcb356903\u003376587\u0034b2c4052b4a893c8135f375"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_add_user_to_group","inputs":["e869651\u003752093\u00370e4af442abcebcb356903\u003376587\u0034b2c4052b4a893c8135f375"],"reason":"no identity policy Deny bindings observed on iam:AddUserToGroup witness edge","result":"PASS","step":5},{"action":"check_target_group_is_admin_equivalent","inputs":["arn:aws:iam::111111\u003111111:group/Admins"],"reason":"target group has admin-equivalent permissions (witness edge c6fe0a6f4174\u2026)","result":"PASS","step":6}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/GroupMgmt",0,"iam:AddUserToGroup grant"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"0f5f593014ffee009224df7d3c192c44307f4fa205afbf650f43d381a88840b5","finding_key":"cb4b1f1f57c1c9086b7be1400289a5bbfc49fec6947d4b7a7713a0aaf074a3ee","pattern_id":"iam_group_membership_escalation","pattern_title":"IAM Group Membership Escalation","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: witness_edge_is_clean","required_checks":[{"description":"User has a permission edge for iam:AddUserToGroup (enumeration invariant)","evidence_refs":["e869651\u003752093\u00370e4af442abcebcb356903\u003376587\u0034b2c4052b4a893c8135f375"],"name":"source_has_add_user_to_group_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge for iam:AddUserToGroup resolves to a specific target group (clean witness proves the edge's target)","evidence_refs":["e869651\u003752093\u00370e4af442abcebcb356903\u003376587\u0034b2c4052b4a893c8135f375"],"name":"witness_edge_is_clean","reason":"witness edge is wildcard-expansion hyperedge or wildcard-resource (target group iterated from all groups)","state":"unknown"},{"description":"No SCP blocks iam:AddUserToGroup on this edge with complete governance confidence","evidence_refs":["e869651\u003752093\u00370e4af442abcebcb356903\u003376587\u0034b2c4052b4a893c8135f375"],"name":"no_scp_blocks_add_user_to_group","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks iam:AddUserToGroup on this edge","evidence_refs":["e869651\u003752093\u00370e4af442abcebcb356903\u003376587\u0034b2c4052b4a893c8135f375"],"name":"no_boundary_blocks_add_user_to_group","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks iam:AddUserToGroup on this edge","evidence_refs":["e869651\u003752093\u00370e4af442abcebcb356903\u003376587\u0034b2c4052b4a893c8135f375"],"name":"no_identity_deny_blocks_add_user_to_group","reason":"no identity policy Deny bindings observed on iam:AddUserToGroup witness edge","state":"pass"},{"description":"Target group has permissions equivalent to administrator access via the shared two-tier admin detection (explicit iam:* or wildcard expansion across \u22653 prefixes)","evidence_refs":["c6fe0a6f4174c7214ba22aa074ff968d9ba079313c08eaedfeb58fea88b786e4"],"name":"target_group_is_admin_equivalent","reason":"target group has admin-equivalent permissions (witness edge c6fe0a6f4174\u2026)","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMGroup","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:group/Admins","region":"-"},"title":"Inconclusive group membership escalation: arn:aws:iam::111111\u003111111:user/Alice can call iam:AddUserToGroup on admin-equivalent group arn:aws:iam::111111\u003111111:group/Admins","verdict":"inconclusive"}],"metadata":{"canonical_hash":"f9475c298f4b87692f57847f37f92ae4fe2a1c9b5139886a0355e5ff9c5bd4ac","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["iam_group_membership_escalation"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"iam_group_membership_escalation":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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":["c6fe0a6f4174c7214ba22aa074ff968d9ba079313c08eaedfeb58fea88b786e4","e86965\u003175209\u003370e4af442abcebcb35690\u003337658\u00374b2c4052b4a893c8135f375"],"node_refs":["253084f77ea308a894c0ba30c6a54da0149a162eea04ae95ca55562b58e3eb6e","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_add_user_to_group_permission","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_witness_edge_is_clean","inputs":["e86965\u003175209\u003370e4af442abcebcb35690\u003337658\u00374b2c4052b4a893c8135f375"],"reason":"witness edge is wildcard-expansion hyperedge or wildcard-resource (target group iterated from all groups)","result":"UNKNOWN","step":2},{"action":"check_no_scp_blocks_add_user_to_group","inputs":["e86965\u003175209\u003370e4af442abcebcb35690\u003337658\u00374b2c4052b4a893c8135f375"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_add_user_to_group","inputs":["e86965\u003175209\u003370e4af442abcebcb35690\u003337658\u00374b2c4052b4a893c8135f375"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_add_user_to_group","inputs":["e86965\u003175209\u003370e4af442abcebcb35690\u003337658\u00374b2c4052b4a893c8135f375"],"reason":"no identity policy Deny bindings observed on iam:AddUserToGroup witness edge","result":"PASS","step":5},{"action":"check_target_group_is_admin_equivalent","inputs":["arn:aws:iam::11111\u003111111\u0031:group/Admins"],"reason":"target group has admin-equivalent permissions (witness edge c6fe0a6f4174\u2026)","result":"PASS","step":6}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/GroupMgmt",0,"iam:AddUserToGroup grant"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"0f5f593014ffee009224df7d3c192c44307f4fa205afbf650f43d381a88840b5","finding_key":"cb4b1f1f57c1c9086b7be1400289a5bbfc49fec6947d4b7a7713a0aaf074a3ee","pattern_id":"iam_group_membership_escalation","pattern_title":"IAM Group Membership Escalation","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: witness_edge_is_clean","required_checks":[{"description":"User has a permission edge for iam:AddUserToGroup (enumeration invariant)","evidence_refs":["e86965\u003175209\u003370e4af442abcebcb35690\u003337658\u00374b2c4052b4a893c8135f375"],"name":"source_has_add_user_to_group_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge for iam:AddUserToGroup resolves to a specific target group (clean witness proves the edge's target)","evidence_refs":["e86965\u003175209\u003370e4af442abcebcb35690\u003337658\u00374b2c4052b4a893c8135f375"],"name":"witness_edge_is_clean","reason":"witness edge is wildcard-expansion hyperedge or wildcard-resource (target group iterated from all groups)","state":"unknown"},{"description":"No SCP blocks iam:AddUserToGroup on this edge with complete governance confidence","evidence_refs":["e86965\u003175209\u003370e4af442abcebcb35690\u003337658\u00374b2c4052b4a893c8135f375"],"name":"no_scp_blocks_add_user_to_group","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks iam:AddUserToGroup on this edge","evidence_refs":["e86965\u003175209\u003370e4af442abcebcb35690\u003337658\u00374b2c4052b4a893c8135f375"],"name":"no_boundary_blocks_add_user_to_group","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks iam:AddUserToGroup on this edge","evidence_refs":["e86965\u003175209\u003370e4af442abcebcb35690\u003337658\u00374b2c4052b4a893c8135f375"],"name":"no_identity_deny_blocks_add_user_to_group","reason":"no identity policy Deny bindings observed on iam:AddUserToGroup witness edge","state":"pass"},{"description":"Target group has permissions equivalent to administrator access via the shared two-tier admin detection (explicit iam:* or wildcard expansion across \u22653 prefixes)","evidence_refs":["c6fe0a6f4174c7214ba22aa074ff968d9ba079313c08eaedfeb58fea88b786e4"],"name":"target_group_is_admin_equivalent","reason":"target group has admin-equivalent permissions (witness edge c6fe0a6f4174\u2026)","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"IAMGroup","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:group/Admins","region":"-"},"title":"Inconclusive group membership escalation: arn:aws:iam::11111\u003111111\u0031:user/Alice can call iam:AddUserToGroup on admin-equivalent group arn:aws:iam::11111\u003111111\u0031:group/Admins","verdict":"inconclusive"}],"metadata":{"canonical_hash":"1f99018\u003949267\u0038a660db8ad45e3db80232fcf5479385f65fc2d0884b8158075b","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["iam_group_membership_escalation"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"iam_group_membership_escalation":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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/iam_group_membership_escalation/fixture_c_blocked_by_scp.json b/tests/fixtures/expected_output/findings/iam_group_membership_escalation/fixture_c_blocked_by_scp.json index cbaca8b..e703a11 100644 --- a/tests/fixtures/expected_output/findings/iam_group_membership_escalation/fixture_c_blocked_by_scp.json +++ b/tests/fixtures/expected_output/findings/iam_group_membership_escalation/fixture_c_blocked_by_scp.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b","edge_id":"51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937","kind":"scp","reason":"constraint 54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b affects AddUserToGroup"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b"],"edge_constraint_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937:54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b"],"edge_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937","c6fe0a6f4174c7214ba22aa074ff968d9ba079313c08eaedfeb58fea88b786e4"],"node_refs":["253084f77ea308a894c0ba30c6a54da0149a162eea04ae95ca55562b58e3eb6e","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_add_user_to_group_permission","inputs":["arn:aws:iam::111111\u003111111:user/Alice"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_witness_edge_is_clean","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"witness edge resolves to specific target group","result":"PASS","step":2},{"action":"check_no_scp_blocks_add_user_to_group","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"SCP 54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b blocks (complete)","result":"FAIL","step":3},{"action":"check_no_boundary_blocks_add_user_to_group","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_add_user_to_group","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"no identity policy Deny bindings observed on iam:AddUserToGroup witness edge","result":"PASS","step":5},{"action":"check_target_group_is_admin_equivalent","inputs":["arn:aws:iam::111111\u003111111:group/Admins"],"reason":"target group has admin-equivalent permissions (witness edge c6fe0a6f4174\u2026)","result":"PASS","step":6}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/GroupMgmt",0,"iam:AddUserToGroup grant"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"a6e1d5d82243df1a351b5de1360dfa5aff2c262f4462c40fce30b739f2345c2b","finding_key":"cb4b1f1f57c1c9086b7be1400289a5bbfc49fec6947d4b7a7713a0aaf074a3ee","pattern_id":"iam_group_membership_escalation","pattern_title":"IAM Group Membership Escalation","pattern_version":"1.0.0","reasoner_exit_reason":"SCP blocks iam:AddUserToGroup","required_checks":[{"description":"User has a permission edge for iam:AddUserToGroup (enumeration invariant)","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"source_has_add_user_to_group_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge for iam:AddUserToGroup resolves to a specific target group (clean witness proves the edge's target)","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"witness_edge_is_clean","reason":"witness edge resolves to specific target group","state":"pass"},{"description":"No SCP blocks iam:AddUserToGroup on this edge with complete governance confidence","evidence_refs":["54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b"],"name":"no_scp_blocks_add_user_to_group","reason":"SCP 54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b blocks (complete)","state":"fail"},{"description":"No permission boundary blocks iam:AddUserToGroup on this edge","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"no_boundary_blocks_add_user_to_group","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks iam:AddUserToGroup on this edge","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"no_identity_deny_blocks_add_user_to_group","reason":"no identity policy Deny bindings observed on iam:AddUserToGroup witness edge","state":"pass"},{"description":"Target group has permissions equivalent to administrator access via the shared two-tier admin detection (explicit iam:* or wildcard expansion across \u22653 prefixes)","evidence_refs":["c6fe0a6f4174c7214ba22aa074ff968d9ba079313c08eaedfeb58fea88b786e4"],"name":"target_group_is_admin_equivalent","reason":"target group has admin-equivalent permissions (witness edge c6fe0a6f4174\u2026)","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"info","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMGroup","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:group/Admins","region":"-"},"title":"Blocked group membership escalation: arn:aws:iam::111111\u003111111:user/Alice can call iam:AddUserToGroup on admin-equivalent group arn:aws:iam::111111\u003111111:group/Admins","verdict":"blocked"}],"metadata":{"canonical_hash":"e04e8a2076936c0cdf542d186bb701781739d56521d7519b9e914f1f59e93890","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["iam_group_membership_escalation"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"iam_group_membership_escalation":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b","edge_id":"51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937","kind":"scp","reason":"constraint 54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b affects AddUserToGroup"}],"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":["54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b"],"edge_constraint_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937:54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b"],"edge_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937","c6fe0a6f4174c7214ba22aa074ff968d9ba079313c08eaedfeb58fea88b786e4"],"node_refs":["253084f77ea308a894c0ba30c6a54da0149a162eea04ae95ca55562b58e3eb6e","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_add_user_to_group_permission","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_witness_edge_is_clean","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"witness edge resolves to specific target group","result":"PASS","step":2},{"action":"check_no_scp_blocks_add_user_to_group","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"SCP 54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b blocks (complete)","result":"FAIL","step":3},{"action":"check_no_boundary_blocks_add_user_to_group","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_add_user_to_group","inputs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"reason":"no identity policy Deny bindings observed on iam:AddUserToGroup witness edge","result":"PASS","step":5},{"action":"check_target_group_is_admin_equivalent","inputs":["arn:aws:iam::11111\u003111111\u0031:group/Admins"],"reason":"target group has admin-equivalent permissions (witness edge c6fe0a6f4174\u2026)","result":"PASS","step":6}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/GroupMgmt",0,"iam:AddUserToGroup grant"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"a6e1d5d82243df1a351b5de1360dfa5aff2c262f4462c40fce30b739f2345c2b","finding_key":"cb4b1f1f57c1c9086b7be1400289a5bbfc49fec6947d4b7a7713a0aaf074a3ee","pattern_id":"iam_group_membership_escalation","pattern_title":"IAM Group Membership Escalation","pattern_version":"1.0.0","reasoner_exit_reason":"SCP blocks iam:AddUserToGroup","required_checks":[{"description":"User has a permission edge for iam:AddUserToGroup (enumeration invariant)","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"source_has_add_user_to_group_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge for iam:AddUserToGroup resolves to a specific target group (clean witness proves the edge's target)","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"witness_edge_is_clean","reason":"witness edge resolves to specific target group","state":"pass"},{"description":"No SCP blocks iam:AddUserToGroup on this edge with complete governance confidence","evidence_refs":["54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b"],"name":"no_scp_blocks_add_user_to_group","reason":"SCP 54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b blocks (complete)","state":"fail"},{"description":"No permission boundary blocks iam:AddUserToGroup on this edge","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"no_boundary_blocks_add_user_to_group","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks iam:AddUserToGroup on this edge","evidence_refs":["51112b253cfcdd67eb9fd61af1afdcce4cfd0f8aad99c125584b23f76ad51937"],"name":"no_identity_deny_blocks_add_user_to_group","reason":"no identity policy Deny bindings observed on iam:AddUserToGroup witness edge","state":"pass"},{"description":"Target group has permissions equivalent to administrator access via the shared two-tier admin detection (explicit iam:* or wildcard expansion across \u22653 prefixes)","evidence_refs":["c6fe0a6f4174c7214ba22aa074ff968d9ba079313c08eaedfeb58fea88b786e4"],"name":"target_group_is_admin_equivalent","reason":"target group has admin-equivalent permissions (witness edge c6fe0a6f4174\u2026)","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"info","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"IAMGroup","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:group/Admins","region":"-"},"title":"Blocked group membership escalation: arn:aws:iam::11111\u003111111\u0031:user/Alice can call iam:AddUserToGroup on admin-equivalent group arn:aws:iam::11111\u003111111\u0031:group/Admins","verdict":"blocked"}],"metadata":{"canonical_hash":"27e68afaa8ef70f03e75ef6234057b1f6408657cfc3b4ea9d46b2a420c69f61e","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["iam_group_membership_escalation"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"iam_group_membership_escalation":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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/passrole_ecs/fixture_a_validated_admin.json b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_a_validated_admin.json index 50c5991..4be1d29 100644 --- a/tests/fixtures/expected_output/findings/passrole_ecs/fixture_a_validated_admin.json +++ b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_a_validated_admin.json @@ -1 +1 @@ -{"findings":[{"assumptions":[{"detail":"no session policy restricts ecs:RegisterTaskDefinition, ecs:RunTask, or iam:PassRole; session policies are not visible to IAMScope collectors at collection time","kind":"session_policy"}],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_ecs_create_and_run_permissions","inputs":["arn:aws:iam::111111\u003111111:user/Alice","ecs:RegisterTaskDefinition","ecs:RunTask"],"reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","arn:aws:iam::111111\u003111111:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_ecs_tasks_service","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","ecs-tasks.amazonaws.com"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_passrole_condition_scoped_to_ecs_or_absent","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":8},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":9},{"action":"emit_verdict","inputs":["critical","validated"],"reason":"all 8 checks PASS; target role has admin-equivalent permissions","result":"VALIDATED","step":10}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"ecs:RegisterTaskDefinition grant"],"aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab":["arn:aws:iam::111111\u003111111:policy/AlicePerms",1,"ecs:RunTask grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::111111\u003111111:policy/AlicePerms",2,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::111111\u003111111:role/AdminRole",0,"trust ecs-tasks.amazonaws.com"]}},"finding_id":"9699fa9cb924b0167979e7e3b92c29a926b287f2892db400dffa745fee0e7a33","finding_key":"ac47f984d3bf0c02ece335e258879418afe0da72784dad505261fc49fe257c8a","pattern_id":"passrole_ecs","pattern_title":"ECS Task RunTask + PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"all 8 checks PASS; target role has admin-equivalent permissions","required_checks":[{"description":"Source principal has ecs:RegisterTaskDefinition AND ecs:RunTask","evidence_refs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"source_has_ecs_create_and_run_permissions","reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts ECS tasks","evidence_refs":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_scp_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows both ecs:RegisterTaskDefinition and ecs:RunTask","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_boundary_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to ECS tasks","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"passrole_condition_scoped_to_ecs_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"critical","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/AdminRole","region":"-"},"title":"arn:aws:iam::111111\u003111111:user/Alice can assume admin-equivalent role arn:aws:iam::111111\u003111111:role/AdminRole via ECS PassRole chain","verdict":"validated"}],"metadata":{"canonical_hash":"0fc5b497d3f84bdc76f4e4fe470d0e0e5ee98fae3a7ef0c661bc386f0cab890f","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_ecs"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"passrole_ecs":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[{"detail":"no session policy restricts ecs:RegisterTaskDefinition, ecs:RunTask, or iam:PassRole; session policies are not visible to IAMScope collectors at collection time","kind":"session_policy"}],"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":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_ecs_create_and_run_permissions","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","ecs:RegisterTaskDefinition","ecs:RunTask"],"reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","arn:aws:iam::11111\u003111111\u0031:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_ecs_tasks_service","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","ecs-tasks.amazonaws.com"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_passrole_condition_scoped_to_ecs_or_absent","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":8},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":9},{"action":"emit_verdict","inputs":["critical","validated"],"reason":"all 8 checks PASS; target role has admin-equivalent permissions","result":"VALIDATED","step":10}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"ecs:RegisterTaskDefinition grant"],"aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",1,"ecs:RunTask grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",2,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole",0,"trust ecs-tasks.amazonaws.com"]}},"finding_id":"9699fa9cb924b0167979e7e3b92c29a926b287f2892db400dffa745fee0e7a33","finding_key":"ac47f984d3bf0c02ece335e258879418afe0da72784dad505261fc49fe257c8a","pattern_id":"passrole_ecs","pattern_title":"ECS Task RunTask + PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"all 8 checks PASS; target role has admin-equivalent permissions","required_checks":[{"description":"Source principal has ecs:RegisterTaskDefinition AND ecs:RunTask","evidence_refs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"source_has_ecs_create_and_run_permissions","reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts ECS tasks","evidence_refs":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_scp_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows both ecs:RegisterTaskDefinition and ecs:RunTask","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_boundary_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to ECS tasks","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"passrole_condition_scoped_to_ecs_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/AdminRole","region":"-"},"title":"arn:aws:iam::11111\u003111111\u0031:user/Alice can assume admin-equivalent role arn:aws:iam::11111\u003111111\u0031:role/AdminRole via ECS PassRole chain","verdict":"validated"}],"metadata":{"canonical_hash":"5d86fec2a7f0abee27694c187c5c36ab46eb414d16f2a0c9e1324766aba318b6","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_ecs"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"passrole_ecs":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/passrole_ecs/fixture_c_blocked_by_scp.json b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_c_blocked_by_scp.json index c3bd4aa..e4a7f92 100644 --- a/tests/fixtures/expected_output/findings/passrole_ecs/fixture_c_blocked_by_scp.json +++ b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_c_blocked_by_scp.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"6f1183562bba96f4b1029a8cdafd687fda3fce37693dd59985541fda1075d7cc","edge_id":"370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","kind":"scp","reason":"SCP DenyEcsRegister at OU ou-prod denies ecs:RegisterTaskDefinition"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["6f1183562bba96f4b1029a8cdafd687fda3fce37693dd59985541fda1075d7cc"],"edge_constraint_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a|6f1183562bba96f4b1029a8cdafd687fda3fce37693dd59985541fda1075d7cc"],"edge_refs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_ecs_create_and_run_permissions","inputs":["arn:aws:iam::111111\u003111111:user/Alice","ecs:RegisterTaskDefinition","ecs:RunTask"],"reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","arn:aws:iam::111111\u003111111:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_ecs_tasks_service","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","ecs-tasks.amazonaws.com"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: 1 SCP binding(s) likely_blocking with governance_confidence=complete on ecs:RegisterTaskDefinition; RunTask: no SCP bindings observed on ecs:RunTask witness edge","result":"FAIL","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_passrole_condition_scoped_to_ecs_or_absent","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":8},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":9},{"action":"emit_verdict","inputs":["blocked","info"],"reason":"SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with complete confidence","result":"BLOCKED","step":10}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"ecs:RegisterTaskDefinition grant"],"aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab":["arn:aws:iam::111111\u003111111:policy/AlicePerms",1,"ecs:RunTask grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::111111\u003111111:policy/AlicePerms",2,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::111111\u003111111:role/AdminRole",0,"trust ecs-tasks.amazonaws.com"]}},"finding_id":"7a70de83f53ac96ba0e1869fde352a21f8ca821a33b093321c04ffd7036a26e3","finding_key":"ac47f984d3bf0c02ece335e258879418afe0da72784dad505261fc49fe257c8a","pattern_id":"passrole_ecs","pattern_title":"ECS Task RunTask + PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with complete confidence","required_checks":[{"description":"Source principal has ecs:RegisterTaskDefinition AND ecs:RunTask","evidence_refs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"source_has_ecs_create_and_run_permissions","reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts ECS tasks","evidence_refs":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["6f1183562bba96f4b1029a8cdafd687fda3fce37693dd59985541fda1075d7cc"],"name":"no_scp_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: 1 SCP binding(s) likely_blocking with governance_confidence=complete on ecs:RegisterTaskDefinition; RunTask: no SCP bindings observed on ecs:RunTask witness edge","state":"fail"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows both ecs:RegisterTaskDefinition and ecs:RunTask","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_boundary_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to ECS tasks","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"passrole_condition_scoped_to_ecs_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"info","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/AdminRole","region":"-"},"title":"Blocked ECS PassRole chain from arn:aws:iam::111111\u003111111:user/Alice to arn:aws:iam::111111\u003111111:role/AdminRole","verdict":"blocked"}],"metadata":{"canonical_hash":"d57a60d114564887f6b29c38b3521d86f4c46e6930bc1e40207b65dd88702893","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_ecs"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_ecs":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"6f1183562bba96f4b1029a8cdafd687fda3fce37693dd59985541fda1075d7cc","edge_id":"370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","kind":"scp","reason":"SCP DenyEcsRegister at OU ou-prod denies ecs:RegisterTaskDefinition"}],"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":["6f1183562bba96f4b1029a8cdafd687fda3fce37693dd59985541fda1075d7cc"],"edge_constraint_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a|6f1183562bba96f4b1029a8cdafd687fda3fce37693dd59985541fda1075d7cc"],"edge_refs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_ecs_create_and_run_permissions","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","ecs:RegisterTaskDefinition","ecs:RunTask"],"reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","arn:aws:iam::11111\u003111111\u0031:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_ecs_tasks_service","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","ecs-tasks.amazonaws.com"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: 1 SCP binding(s) likely_blocking with governance_confidence=complete on ecs:RegisterTaskDefinition; RunTask: no SCP bindings observed on ecs:RunTask witness edge","result":"FAIL","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_passrole_condition_scoped_to_ecs_or_absent","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":8},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":9},{"action":"emit_verdict","inputs":["blocked","info"],"reason":"SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with complete confidence","result":"BLOCKED","step":10}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"ecs:RegisterTaskDefinition grant"],"aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",1,"ecs:RunTask grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",2,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole",0,"trust ecs-tasks.amazonaws.com"]}},"finding_id":"7a70de83f53ac96ba0e1869fde352a21f8ca821a33b093321c04ffd7036a26e3","finding_key":"ac47f984d3bf0c02ece335e258879418afe0da72784dad505261fc49fe257c8a","pattern_id":"passrole_ecs","pattern_title":"ECS Task RunTask + PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with complete confidence","required_checks":[{"description":"Source principal has ecs:RegisterTaskDefinition AND ecs:RunTask","evidence_refs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"source_has_ecs_create_and_run_permissions","reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts ECS tasks","evidence_refs":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["6f1183562bba96f4b1029a8cdafd687fda3fce37693dd59985541fda1075d7cc"],"name":"no_scp_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: 1 SCP binding(s) likely_blocking with governance_confidence=complete on ecs:RegisterTaskDefinition; RunTask: no SCP bindings observed on ecs:RunTask witness edge","state":"fail"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows both ecs:RegisterTaskDefinition and ecs:RunTask","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_boundary_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to ECS tasks","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"passrole_condition_scoped_to_ecs_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"info","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/AdminRole","region":"-"},"title":"Blocked ECS PassRole chain from arn:aws:iam::11111\u003111111\u0031:user/Alice to arn:aws:iam::11111\u003111111\u0031:role/AdminRole","verdict":"blocked"}],"metadata":{"canonical_hash":"67a2d5ba9ea22e172bfa93148f8b28201397b68f4f770c725f6eb907e7bd32cf","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_ecs"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_ecs":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/passrole_ecs/fixture_d_blocked_by_boundary.json b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_d_blocked_by_boundary.json index 551750b..9ce0f7e 100644 --- a/tests/fixtures/expected_output/findings/passrole_ecs/fixture_d_blocked_by_boundary.json +++ b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_d_blocked_by_boundary.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"ce4f30a750a2653fe026e35cf86526de3dcdd218a48ae64747dcbdf1dfc79519","edge_id":"370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","kind":"boundary","reason":"permission boundary allowed_actions={s3:*, dynamodb:*} does not include ecs:RegisterTaskDefinition (post-BND-1)"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["ce4f30a750a2653fe026e35cf86526de3dcdd218a48ae64747dcbdf1dfc79519"],"edge_constraint_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a|ce4f30a750a2653fe026e35cf86526de3dcdd218a48ae64747dcbdf1dfc79519"],"edge_refs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_ecs_create_and_run_permissions","inputs":["arn:aws:iam::111111\u003111111:user/Alice","ecs:RegisterTaskDefinition","ecs:RunTask"],"reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","arn:aws:iam::111111\u003111111:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_ecs_tasks_service","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","ecs-tasks.amazonaws.com"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: 1 permission boundary binding(s) likely_blocking with governance_confidence=complete on ecs:RegisterTaskDefinition; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","result":"FAIL","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_passrole_condition_scoped_to_ecs_or_absent","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":8},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":9},{"action":"emit_verdict","inputs":["blocked","info"],"reason":"permission boundary blocks ecs:RegisterTaskDefinition or ecs:RunTask (post-BND-1)","result":"BLOCKED","step":10}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"ecs:RegisterTaskDefinition grant"],"aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab":["arn:aws:iam::111111\u003111111:policy/AlicePerms",1,"ecs:RunTask grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::111111\u003111111:policy/AlicePerms",2,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::111111\u003111111:role/AdminRole",0,"trust ecs-tasks.amazonaws.com"]}},"finding_id":"844f6ff83c7173d4fede5ee55127f2c8852604269b73eddf0dc29f7ab125d4da","finding_key":"ac47f984d3bf0c02ece335e258879418afe0da72784dad505261fc49fe257c8a","pattern_id":"passrole_ecs","pattern_title":"ECS Task RunTask + PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"permission boundary blocks ecs:RegisterTaskDefinition or ecs:RunTask (post-BND-1)","required_checks":[{"description":"Source principal has ecs:RegisterTaskDefinition AND ecs:RunTask","evidence_refs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"source_has_ecs_create_and_run_permissions","reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts ECS tasks","evidence_refs":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_scp_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows both ecs:RegisterTaskDefinition and ecs:RunTask","evidence_refs":["ce4f30a750a2653fe026e35cf86526de3dcdd218a48ae64747dcbdf1dfc79519"],"name":"no_boundary_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: 1 permission boundary binding(s) likely_blocking with governance_confidence=complete on ecs:RegisterTaskDefinition; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","state":"fail"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to ECS tasks","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"passrole_condition_scoped_to_ecs_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"info","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/AdminRole","region":"-"},"title":"Blocked ECS PassRole chain from arn:aws:iam::111111\u003111111:user/Alice to arn:aws:iam::111111\u003111111:role/AdminRole","verdict":"blocked"}],"metadata":{"canonical_hash":"ad1bd48a433a83c710ab2082ae7d86e639cecc08095eae821f3e534c1cbb3f6f","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_ecs"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_ecs":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"ce4f30a750a2653fe026e35cf86526de3dcdd218a48ae64747dcbdf1dfc79519","edge_id":"370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","kind":"boundary","reason":"permission boundary allowed_actions={s3:*, dynamodb:*} does not include ecs:RegisterTaskDefinition (post-BND-1)"}],"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":["ce4f30a750a2653fe026e35cf86526de3dcdd218a48ae64747dcbdf1dfc79519"],"edge_constraint_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a|ce4f30a750a2653fe026e35cf86526de3dcdd218a48ae64747dcbdf1dfc79519"],"edge_refs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_ecs_create_and_run_permissions","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","ecs:RegisterTaskDefinition","ecs:RunTask"],"reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","arn:aws:iam::11111\u003111111\u0031:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_ecs_tasks_service","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","ecs-tasks.amazonaws.com"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: 1 permission boundary binding(s) likely_blocking with governance_confidence=complete on ecs:RegisterTaskDefinition; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","result":"FAIL","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_passrole_condition_scoped_to_ecs_or_absent","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":8},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":9},{"action":"emit_verdict","inputs":["blocked","info"],"reason":"permission boundary blocks ecs:RegisterTaskDefinition or ecs:RunTask (post-BND-1)","result":"BLOCKED","step":10}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"ecs:RegisterTaskDefinition grant"],"aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",1,"ecs:RunTask grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",2,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole",0,"trust ecs-tasks.amazonaws.com"]}},"finding_id":"844f6ff83c7173d4fede5ee55127f2c8852604269b73eddf0dc29f7ab125d4da","finding_key":"ac47f984d3bf0c02ece335e258879418afe0da72784dad505261fc49fe257c8a","pattern_id":"passrole_ecs","pattern_title":"ECS Task RunTask + PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"permission boundary blocks ecs:RegisterTaskDefinition or ecs:RunTask (post-BND-1)","required_checks":[{"description":"Source principal has ecs:RegisterTaskDefinition AND ecs:RunTask","evidence_refs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"source_has_ecs_create_and_run_permissions","reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts ECS tasks","evidence_refs":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_scp_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows both ecs:RegisterTaskDefinition and ecs:RunTask","evidence_refs":["ce4f30a750a2653fe026e35cf86526de3dcdd218a48ae64747dcbdf1dfc79519"],"name":"no_boundary_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: 1 permission boundary binding(s) likely_blocking with governance_confidence=complete on ecs:RegisterTaskDefinition; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","state":"fail"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to ECS tasks","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"passrole_condition_scoped_to_ecs_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"info","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/AdminRole","region":"-"},"title":"Blocked ECS PassRole chain from arn:aws:iam::11111\u003111111\u0031:user/Alice to arn:aws:iam::11111\u003111111\u0031:role/AdminRole","verdict":"blocked"}],"metadata":{"canonical_hash":"217bc19947efb32a26f6ac29b9884796b4c338418a17987b42042acab9e33350","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_ecs"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_ecs":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/passrole_ecs/fixture_e_inconclusive_partial_scp.json b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_e_inconclusive_partial_scp.json index 7a334a3..3a7d72b 100644 --- a/tests/fixtures/expected_output/findings/passrole_ecs/fixture_e_inconclusive_partial_scp.json +++ b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_e_inconclusive_partial_scp.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":["fe4f9a59334201dbedb60fcda04e68e83a77cd134bba8f154578ac7d61c9c663"],"edge_constraint_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a|fe4f9a59334201dbedb60fcda04e68e83a77cd134bba8f154578ac7d61c9c663"],"edge_refs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_ecs_create_and_run_permissions","inputs":["arn:aws:iam::111111\u003111111:user/Alice","ecs:RegisterTaskDefinition","ecs:RunTask"],"reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","arn:aws:iam::111111\u003111111:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_ecs_tasks_service","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","ecs-tasks.amazonaws.com"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: 1 SCP binding(s) with governance_confidence \u2208 partial/needs_review on ecs:RegisterTaskDefinition \u2014 cannot confirm; RunTask: no SCP bindings observed on ecs:RunTask witness edge","result":"UNKNOWN","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_passrole_condition_scoped_to_ecs_or_absent","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":8},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":9},{"action":"emit_verdict","inputs":["high","inconclusive"],"reason":"check(s) UNKNOWN: no_scp_blocks_ecs_create_or_run","result":"INCONCLUSIVE","step":10}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"ecs:RegisterTaskDefinition grant"],"aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab":["arn:aws:iam::111111\u003111111:policy/AlicePerms",1,"ecs:RunTask grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::111111\u003111111:policy/AlicePerms",2,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::111111\u003111111:role/AdminRole",0,"trust ecs-tasks.amazonaws.com"]}},"finding_id":"480b773291849e2d52828ef5d3378e294e7e29d8936dbe09ad605c9d7a0e83ce","finding_key":"ac47f984d3bf0c02ece335e258879418afe0da72784dad505261fc49fe257c8a","pattern_id":"passrole_ecs","pattern_title":"ECS Task RunTask + PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: no_scp_blocks_ecs_create_or_run","required_checks":[{"description":"Source principal has ecs:RegisterTaskDefinition AND ecs:RunTask","evidence_refs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"source_has_ecs_create_and_run_permissions","reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts ECS tasks","evidence_refs":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["fe4f9a59334201dbedb60fcda04e68e83a77cd134bba8f154578ac7d61c9c663"],"name":"no_scp_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: 1 SCP binding(s) with governance_confidence \u2208 partial/needs_review on ecs:RegisterTaskDefinition \u2014 cannot confirm; RunTask: no SCP bindings observed on ecs:RunTask witness edge","state":"unknown"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows both ecs:RegisterTaskDefinition and ecs:RunTask","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_boundary_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to ECS tasks","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"passrole_condition_scoped_to_ecs_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/AdminRole","region":"-"},"title":"Inconclusive ECS PassRole chain from arn:aws:iam::111111\u003111111:user/Alice to arn:aws:iam::111111\u003111111:role/AdminRole","verdict":"inconclusive"}],"metadata":{"canonical_hash":"7cad4756c2aa96bfdee0c50779dab69606e3b40b2981a33800073a3809a13c2d","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_ecs"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_ecs":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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":["fe4f9a59334201dbedb60fcda04e68e83a77cd134bba8f154578ac7d61c9c663"],"edge_constraint_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a|fe4f9a59334201dbedb60fcda04e68e83a77cd134bba8f154578ac7d61c9c663"],"edge_refs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_ecs_create_and_run_permissions","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","ecs:RegisterTaskDefinition","ecs:RunTask"],"reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","arn:aws:iam::11111\u003111111\u0031:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_ecs_tasks_service","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","ecs-tasks.amazonaws.com"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: 1 SCP binding(s) with governance_confidence \u2208 partial/needs_review on ecs:RegisterTaskDefinition \u2014 cannot confirm; RunTask: no SCP bindings observed on ecs:RunTask witness edge","result":"UNKNOWN","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_passrole_condition_scoped_to_ecs_or_absent","inputs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":8},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":9},{"action":"emit_verdict","inputs":["high","inconclusive"],"reason":"check(s) UNKNOWN: no_scp_blocks_ecs_create_or_run","result":"INCONCLUSIVE","step":10}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"ecs:RegisterTaskDefinition grant"],"aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",1,"ecs:RunTask grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",2,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole",0,"trust ecs-tasks.amazonaws.com"]}},"finding_id":"480b773291849e2d52828ef5d3378e294e7e29d8936dbe09ad605c9d7a0e83ce","finding_key":"ac47f984d3bf0c02ece335e258879418afe0da72784dad505261fc49fe257c8a","pattern_id":"passrole_ecs","pattern_title":"ECS Task RunTask + PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: no_scp_blocks_ecs_create_or_run","required_checks":[{"description":"Source principal has ecs:RegisterTaskDefinition AND ecs:RunTask","evidence_refs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"source_has_ecs_create_and_run_permissions","reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts ECS tasks","evidence_refs":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["fe4f9a59334201dbedb60fcda04e68e83a77cd134bba8f154578ac7d61c9c663"],"name":"no_scp_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: 1 SCP binding(s) with governance_confidence \u2208 partial/needs_review on ecs:RegisterTaskDefinition \u2014 cannot confirm; RunTask: no SCP bindings observed on ecs:RunTask witness edge","state":"unknown"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows both ecs:RegisterTaskDefinition and ecs:RunTask","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_boundary_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to ECS tasks","evidence_refs":["938686ce59c3eaef7afd1bf26d371438c7f16c0544ccd87182c4a198fa499b02"],"name":"passrole_condition_scoped_to_ecs_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/AdminRole","region":"-"},"title":"Inconclusive ECS PassRole chain from arn:aws:iam::11111\u003111111\u0031:user/Alice to arn:aws:iam::11111\u003111111\u0031:role/AdminRole","verdict":"inconclusive"}],"metadata":{"canonical_hash":"42212c7b343bf8118e173d4acca66c1bb49451236cebe9481945d691c0298850","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_ecs"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_ecs":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/passrole_ecs/fixture_f_hyperedge_inconclusive.json b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_f_hyperedge_inconclusive.json index 50a6c1a..dd16c7c 100644 --- a/tests/fixtures/expected_output/findings/passrole_ecs/fixture_f_hyperedge_inconclusive.json +++ b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_f_hyperedge_inconclusive.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","74721221dc0da397f706d3b3bdeced586a8346734ca53022b1fbed3660e7dac7","972cd814ace681360f8a66116b1726ebb99e03a86418a8f36c94ef57dcaacabb","cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_ecs_create_and_run_permissions","inputs":["arn:aws:iam::111111\u003111111:user/Alice","ecs:RegisterTaskDefinition","ecs:RunTask"],"reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","arn:aws:iam::111111\u003111111:user/Alice","iam:PassRole"],"reason":"matching iam:PassRole edge has ambiguity flag (hyperedge, wildcard resource, or conditions)","result":"UNKNOWN","step":2},{"action":"check_target_trusts_ecs_tasks_service","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","ecs-tasks.amazonaws.com"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["74721221dc0da397f706d3b3bdeced586a8346734ca53022b1fbed3660e7dac7","972cd814ace681360f8a66116b1726ebb99e03a86418a8f36c94ef57dcaacabb"],"reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["74721221dc0da397f706d3b3bdeced586a8346734ca53022b1fbed3660e7dac7","972cd814ace681360f8a66116b1726ebb99e03a86418a8f36c94ef57dcaacabb"],"reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_passrole_condition_scoped_to_ecs_or_absent","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":8},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":9},{"action":"emit_verdict","inputs":["high","inconclusive"],"reason":"check(s) UNKNOWN: source_has_passrole_to_target","result":"INCONCLUSIVE","step":10}],"statement_digests":["cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe","deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","feedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedface"],"statement_sources":{"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::111111\u003111111:role/AdminRole",0,"trust ecs-tasks.amazonaws.com"],"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"ecs:RegisterTaskDefinition grant"],"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee":["arn:aws:iam::111111\u003111111:policy/AlicePerms",3,"ecs:RunTask grant"],"feedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedface":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"wildcard PassRole"]}},"finding_id":"e132989c5df3b22260633dd60b109d88c361cb0478c1c80eab9e3993c4a2cc9e","finding_key":"ac47f984d3bf0c02ece335e258879418afe0da72784dad505261fc49fe257c8a","pattern_id":"passrole_ecs","pattern_title":"ECS Task RunTask + PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: source_has_passrole_to_target","required_checks":[{"description":"Source principal has ecs:RegisterTaskDefinition AND ecs:RunTask","evidence_refs":["74721221dc0da397f706d3b3bdeced586a8346734ca53022b1fbed3660e7dac7","972cd814ace681360f8a66116b1726ebb99e03a86418a8f36c94ef57dcaacabb"],"name":"source_has_ecs_create_and_run_permissions","reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"source_has_passrole_to_target","reason":"matching iam:PassRole edge has ambiguity flag (hyperedge, wildcard resource, or conditions)","state":"unknown"},{"description":"Target role's trust policy trusts ECS tasks","evidence_refs":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["972cd814ace681360f8a66116b1726ebb99e03a86418a8f36c94ef57dcaacabb"],"name":"no_scp_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows both ecs:RegisterTaskDefinition and ecs:RunTask","evidence_refs":["972cd814ace681360f8a66116b1726ebb99e03a86418a8f36c94ef57dcaacabb"],"name":"no_boundary_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to ECS tasks","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"passrole_condition_scoped_to_ecs_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/AdminRole","region":"-"},"title":"Inconclusive ECS PassRole chain from arn:aws:iam::111111\u003111111:user/Alice to arn:aws:iam::111111\u003111111:role/AdminRole","verdict":"inconclusive"}],"metadata":{"canonical_hash":"6be99650fd9a5713e184c01a76eac3b68362c5c4bf9821def2974f0a337732d0","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_ecs"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_ecs":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","74721221dc0da397f706d3b3bdeced586a8346734ca53022b1fbed3660e7dac7","972cd814ace681360f8a66116b1726ebb99e03a86418a8f36c94ef57dcaacabb","cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_ecs_create_and_run_permissions","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","ecs:RegisterTaskDefinition","ecs:RunTask"],"reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","arn:aws:iam::11111\u003111111\u0031:user/Alice","iam:PassRole"],"reason":"matching iam:PassRole edge has ambiguity flag (hyperedge, wildcard resource, or conditions)","result":"UNKNOWN","step":2},{"action":"check_target_trusts_ecs_tasks_service","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","ecs-tasks.amazonaws.com"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["74721221dc0da397f706d3b3bdeced586a8346734ca53022b1fbed3660e7dac7","972cd814ace681360f8a66116b1726ebb99e03a86418a8f36c94ef57dcaacabb"],"reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["74721221dc0da397f706d3b3bdeced586a8346734ca53022b1fbed3660e7dac7","972cd814ace681360f8a66116b1726ebb99e03a86418a8f36c94ef57dcaacabb"],"reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_passrole_condition_scoped_to_ecs_or_absent","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":8},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":9},{"action":"emit_verdict","inputs":["high","inconclusive"],"reason":"check(s) UNKNOWN: source_has_passrole_to_target","result":"INCONCLUSIVE","step":10}],"statement_digests":["cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe","deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","feedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedface"],"statement_sources":{"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole",0,"trust ecs-tasks.amazonaws.com"],"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"ecs:RegisterTaskDefinition grant"],"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",3,"ecs:RunTask grant"],"feedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedface":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"wildcard PassRole"]}},"finding_id":"e132989c5df3b22260633dd60b109d88c361cb0478c1c80eab9e3993c4a2cc9e","finding_key":"ac47f984d3bf0c02ece335e258879418afe0da72784dad505261fc49fe257c8a","pattern_id":"passrole_ecs","pattern_title":"ECS Task RunTask + PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: source_has_passrole_to_target","required_checks":[{"description":"Source principal has ecs:RegisterTaskDefinition AND ecs:RunTask","evidence_refs":["74721221dc0da397f706d3b3bdeced586a8346734ca53022b1fbed3660e7dac7","972cd814ace681360f8a66116b1726ebb99e03a86418a8f36c94ef57dcaacabb"],"name":"source_has_ecs_create_and_run_permissions","reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"source_has_passrole_to_target","reason":"matching iam:PassRole edge has ambiguity flag (hyperedge, wildcard resource, or conditions)","state":"unknown"},{"description":"Target role's trust policy trusts ECS tasks","evidence_refs":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["972cd814ace681360f8a66116b1726ebb99e03a86418a8f36c94ef57dcaacabb"],"name":"no_scp_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows both ecs:RegisterTaskDefinition and ecs:RunTask","evidence_refs":["972cd814ace681360f8a66116b1726ebb99e03a86418a8f36c94ef57dcaacabb"],"name":"no_boundary_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to ECS tasks","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"passrole_condition_scoped_to_ecs_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/AdminRole","region":"-"},"title":"Inconclusive ECS PassRole chain from arn:aws:iam::11111\u003111111\u0031:user/Alice to arn:aws:iam::11111\u003111111\u0031:role/AdminRole","verdict":"inconclusive"}],"metadata":{"canonical_hash":"a532e723bdea8861da2b92e637211dca1b115b85406d86147e2b92ca0cc26260","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_ecs"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_ecs":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/passrole_ecs/fixture_g_passrole_scoped_to_ec2.json b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_g_passrole_scoped_to_ec2.json index 1109548..0a5add1 100644 --- a/tests/fixtures/expected_output/findings/passrole_ecs/fixture_g_passrole_scoped_to_ec2.json +++ b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_g_passrole_scoped_to_ec2.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":null,"edge_id":"aafda5c022092\u003152387\u003947473a209c68ea01b07a5c12b5c8c2d2e9f677b16a42","kind":"passed_to_service","reason":"iam:PassedToService scoped to ['ec2.amazonaws.com'] (not ECS) \u2014 PassRole cannot pass to an ECS task role"}],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","aafda5c022092\u003152387\u003947473a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_ecs_create_and_run_permissions","inputs":["arn:aws:iam::111111\u003111111:user/Alice","ecs:RegisterTaskDefinition","ecs:RunTask"],"reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","arn:aws:iam::111111\u003111111:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_ecs_tasks_service","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","ecs-tasks.amazonaws.com"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["aafda5c022092\u003152387\u003947473a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["aafda5c022092\u003152387\u003947473a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_passrole_condition_scoped_to_ecs_or_absent","inputs":["aafda5c022092\u003152387\u003947473a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"reason":"iam:PassedToService scoped to ['ec2.amazonaws.com'] (not ECS) \u2014 PassRole cannot pass to an ECS task role","result":"FAIL","step":8},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":9},{"action":"emit_verdict","inputs":["medium","precondition_only"],"reason":"iam:PassedToService scoped away from ECS \u2014 chain not exploitable","result":"PRECONDITION_ONLY","step":10}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"ecs:RegisterTaskDefinition grant"],"aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab":["arn:aws:iam::111111\u003111111:policy/AlicePerms",1,"ecs:RunTask grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::111111\u003111111:policy/AlicePerms",2,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::111111\u003111111:role/AdminRole",0,"trust ecs-tasks.amazonaws.com"]}},"finding_id":"a3423828f4eabad49e1d0c4f0bb24ec052a71ff4430edd7316d2a79d3307f3cd","finding_key":"ac47f984d3bf0c02ece335e258879418afe0da72784dad505261fc49fe257c8a","pattern_id":"passrole_ecs","pattern_title":"ECS Task RunTask + PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"iam:PassedToService scoped away from ECS \u2014 chain not exploitable","required_checks":[{"description":"Source principal has ecs:RegisterTaskDefinition AND ecs:RunTask","evidence_refs":["13f461eaa598def467762d7ab543d519390\u003609103dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"source_has_ecs_create_and_run_permissions","reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["aafda5c022092\u003152387\u003947473a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts ECS tasks","evidence_refs":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_scp_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["aafda5c022092\u003152387\u003947473a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows both ecs:RegisterTaskDefinition and ecs:RunTask","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_boundary_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["aafda5c022092\u003152387\u003947473a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to ECS tasks","evidence_refs":["aafda5c022092\u003152387\u003947473a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"name":"passrole_condition_scoped_to_ecs_or_absent","reason":"iam:PassedToService scoped to ['ec2.amazonaws.com'] (not ECS) \u2014 PassRole cannot pass to an ECS task role","state":"fail"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"medium","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/AdminRole","region":"-"},"title":"Precondition-only ECS PassRole chain from arn:aws:iam::111111\u003111111:user/Alice to arn:aws:iam::111111\u003111111:role/AdminRole","verdict":"precondition_only"}],"metadata":{"canonical_hash":"1f1cb47c1b929224af23edf421ccdc0fe9cee99a7cc7c104f4c74ecd01ed0056","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_ecs"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":1,"validated":0}},"reasoner_versions":{"passrole_ecs":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":null,"edge_id":"aafda5c02209\u003215238\u003794747\u0033a209c68ea01b07a5c12b5c8c2d2e9f677b16a42","kind":"passed_to_service","reason":"iam:PassedToService scoped to ['ec2.amazonaws.com'] (not ECS) \u2014 PassRole cannot pass to an ECS task role"}],"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":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a","aafda5c02209\u003215238\u003794747\u0033a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_ecs_create_and_run_permissions","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","ecs:RegisterTaskDefinition","ecs:RunTask"],"reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","arn:aws:iam::11111\u003111111\u0031:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_ecs_tasks_service","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","ecs-tasks.amazonaws.com"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["aafda5c02209\u003215238\u003794747\u0033a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["aafda5c02209\u003215238\u003794747\u0033a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_passrole_condition_scoped_to_ecs_or_absent","inputs":["aafda5c02209\u003215238\u003794747\u0033a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"reason":"iam:PassedToService scoped to ['ec2.amazonaws.com'] (not ECS) \u2014 PassRole cannot pass to an ECS task role","result":"FAIL","step":8},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":9},{"action":"emit_verdict","inputs":["medium","precondition_only"],"reason":"iam:PassedToService scoped away from ECS \u2014 chain not exploitable","result":"PRECONDITION_ONLY","step":10}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"ecs:RegisterTaskDefinition grant"],"aaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaabaaaaaaab":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",1,"ecs:RunTask grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",2,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole",0,"trust ecs-tasks.amazonaws.com"]}},"finding_id":"a3423828f4eabad49e1d0c4f0bb24ec052a71ff4430edd7316d2a79d3307f3cd","finding_key":"ac47f984d3bf0c02ece335e258879418afe0da72784dad505261fc49fe257c8a","pattern_id":"passrole_ecs","pattern_title":"ECS Task RunTask + PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"iam:PassedToService scoped away from ECS \u2014 chain not exploitable","required_checks":[{"description":"Source principal has ecs:RegisterTaskDefinition AND ecs:RunTask","evidence_refs":["13f461eaa598def467762d7ab543d51939\u003060910\u0033dfdd4672962803f4d0bf91c","370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"source_has_ecs_create_and_run_permissions","reason":"both ecs:RegisterTaskDefinition and ecs:RunTask have explicit non-conditioned permission edges","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["aafda5c02209\u003215238\u003794747\u0033a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts ECS tasks","evidence_refs":["304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge 304d21b44cf3537c0b071ed88ccf10be2fe5ae9bb1c87b5bded46f5e7c2c4d8b","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_scp_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no SCP bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no SCP bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["aafda5c02209\u003215238\u003794747\u0033a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows both ecs:RegisterTaskDefinition and ecs:RunTask","evidence_refs":["370796c36f7520c5c0096e3e8d302c7775ee96fd929ea4e2822034819b2a233a"],"name":"no_boundary_blocks_ecs_create_or_run","reason":"RegisterTaskDefinition: no permission boundary bindings observed on ecs:RegisterTaskDefinition witness edge; RunTask: no permission boundary bindings observed on ecs:RunTask witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["aafda5c02209\u003215238\u003794747\u0033a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to ECS tasks","evidence_refs":["aafda5c02209\u003215238\u003794747\u0033a209c68ea01b07a5c12b5c8c2d2e9f677b16a42"],"name":"passrole_condition_scoped_to_ecs_or_absent","reason":"iam:PassedToService scoped to ['ec2.amazonaws.com'] (not ECS) \u2014 PassRole cannot pass to an ECS task role","state":"fail"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"medium","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/AdminRole","region":"-"},"title":"Precondition-only ECS PassRole chain from arn:aws:iam::11111\u003111111\u0031:user/Alice to arn:aws:iam::11111\u003111111\u0031:role/AdminRole","verdict":"precondition_only"}],"metadata":{"canonical_hash":"c0727486bdeb3f476b36b1cc1c446e6a4c80876b8126d878e44160c4fc364065","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_ecs"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":1,"validated":0}},"reasoner_versions":{"passrole_ecs":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/passrole_lambda/fixture_a_validated_admin.json b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_a_validated_admin.json index 599bd55..4951ae1 100644 --- a/tests/fixtures/expected_output/findings/passrole_lambda/fixture_a_validated_admin.json +++ b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_a_validated_admin.json @@ -1 +1 @@ -{"findings":[{"assumptions":[{"detail":"no session policy restricts lambda:CreateFunction or iam:PassRole; session policies are not visible to IAMScope collectors at collection time","kind":"session_policy"}],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499","aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_lambda_create_function","inputs":["arn:aws:iam::111111\u003111111:user/Alice","lambda:CreateFunction"],"reason":"explicit non-conditioned permission edge for lambda:CreateFunction","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","arn:aws:iam::111111\u003111111:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_lambda_service","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","lambda.amazonaws.com"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no SCP bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_no_identity_deny_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","result":"PASS","step":9},{"action":"check_passrole_condition_scoped_to_lambda_or_absent","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":10},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":11},{"action":"emit_verdict","inputs":["critical","validated"],"reason":"all checks PASS; target role has admin-equivalent permissions","result":"VALIDATED","step":12}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"lambda:CreateFunction grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::111111\u003111111:policy/AlicePerms",1,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::111111\u003111111:role/AdminRole",0,"trust lambda"]}},"finding_id":"bbe31cbb32c6d8760a7b6e6608495db9e6993e40c13aec39341577244e31c0bd","finding_key":"c488211a0effa85c5882db3d307bb6421384a01661f31370daa8aa18a97c84d3","pattern_id":"passrole_lambda","pattern_title":"Lambda PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; target role has admin-equivalent permissions","required_checks":[{"description":"Source principal has lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"source_has_lambda_create_function","reason":"explicit non-conditioned permission edge for lambda:CreateFunction","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts Lambda","evidence_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_scp_blocks_lambda_create_function","reason":"no SCP bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_boundary_blocks_lambda_create_function","reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"No identity-policy Deny blocks lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_identity_deny_blocks_lambda_create_function","reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No identity-policy Deny blocks iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_identity_deny_blocks_passrole","reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to Lambda","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"passrole_condition_scoped_to_lambda_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"critical","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/AdminRole","region":"-"},"title":"arn:aws:iam::111111\u003111111:user/Alice can assume admin-equivalent role arn:aws:iam::111111\u003111111:role/AdminRole via Lambda PassRole chain","verdict":"validated"}],"metadata":{"canonical_hash":"343f4e9fb9fdcf5b202613b1df0cab8f117205708afb9d2d13474e060e2fab8b","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_lambda"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"passrole_lambda":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[{"detail":"no session policy restricts lambda:CreateFunction or iam:PassRole; session policies are not visible to IAMScope collectors at collection time","kind":"session_policy"}],"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":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499","aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_lambda_create_function","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","lambda:CreateFunction"],"reason":"explicit non-conditioned permission edge for lambda:CreateFunction","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","arn:aws:iam::11111\u003111111\u0031:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_lambda_service","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","lambda.amazonaws.com"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no SCP bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_no_identity_deny_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","result":"PASS","step":9},{"action":"check_passrole_condition_scoped_to_lambda_or_absent","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":10},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":11},{"action":"emit_verdict","inputs":["critical","validated"],"reason":"all checks PASS; target role has admin-equivalent permissions","result":"VALIDATED","step":12}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"lambda:CreateFunction grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",1,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole",0,"trust lambda"]}},"finding_id":"bbe31cbb32c6d8760a7b6e6608495db9e6993e40c13aec39341577244e31c0bd","finding_key":"c488211a0effa85c5882db3d307bb6421384a01661f31370daa8aa18a97c84d3","pattern_id":"passrole_lambda","pattern_title":"Lambda PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; target role has admin-equivalent permissions","required_checks":[{"description":"Source principal has lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"source_has_lambda_create_function","reason":"explicit non-conditioned permission edge for lambda:CreateFunction","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts Lambda","evidence_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_scp_blocks_lambda_create_function","reason":"no SCP bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_boundary_blocks_lambda_create_function","reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"No identity-policy Deny blocks lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_identity_deny_blocks_lambda_create_function","reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No identity-policy Deny blocks iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_identity_deny_blocks_passrole","reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to Lambda","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"passrole_condition_scoped_to_lambda_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/AdminRole","region":"-"},"title":"arn:aws:iam::11111\u003111111\u0031:user/Alice can assume admin-equivalent role arn:aws:iam::11111\u003111111\u0031:role/AdminRole via Lambda PassRole chain","verdict":"validated"}],"metadata":{"canonical_hash":"667a69667a569d776eff8bad17ba8587dff475a88fce2b50422f700dc7a57d5c","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_lambda"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"passrole_lambda":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/passrole_lambda/fixture_c_blocked_by_scp.json b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_c_blocked_by_scp.json index d10300c..a382bd5 100644 --- a/tests/fixtures/expected_output/findings/passrole_lambda/fixture_c_blocked_by_scp.json +++ b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_c_blocked_by_scp.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"b8be6a39dd4b8ff39656668b709a83d2854492fc9cf7d0243fc9df2f74701cf5","edge_id":"aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef","kind":"scp","reason":"SCP DenyLambdaCreate at OU ou-prod denies lambda:CreateFunction"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["b8be6a39dd4b8ff39656668b709a83d2854492fc9cf7d0243fc9df2f74701cf5"],"edge_constraint_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef|b8be6a39dd4b8ff39656668b709a83d2854492fc9cf7d0243fc9df2f74701cf5"],"edge_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499","aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_lambda_create_function","inputs":["arn:aws:iam::111111\u003111111:user/Alice","lambda:CreateFunction"],"reason":"explicit non-conditioned permission edge for lambda:CreateFunction","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","arn:aws:iam::111111\u003111111:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_lambda_service","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","lambda.amazonaws.com"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"1 SCP binding(s) likely_blocking with governance_confidence=complete on lambda:CreateFunction","result":"FAIL","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_no_identity_deny_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","result":"PASS","step":9},{"action":"check_passrole_condition_scoped_to_lambda_or_absent","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":10},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":11},{"action":"emit_verdict","inputs":["blocked","info"],"reason":"SCP blocks lambda:CreateFunction with complete confidence","result":"BLOCKED","step":12}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"lambda:CreateFunction grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::111111\u003111111:policy/AlicePerms",1,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::111111\u003111111:role/AdminRole",0,"trust lambda"]}},"finding_id":"c2ddac95196359eeaef510e3d4d7beb9512d8e62b12db60fc7abf20152bb1595","finding_key":"c488211a0effa85c5882db3d307bb6421384a01661f31370daa8aa18a97c84d3","pattern_id":"passrole_lambda","pattern_title":"Lambda PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"SCP blocks lambda:CreateFunction with complete confidence","required_checks":[{"description":"Source principal has lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"source_has_lambda_create_function","reason":"explicit non-conditioned permission edge for lambda:CreateFunction","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts Lambda","evidence_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["b8be6a39dd4b8ff39656668b709a83d2854492fc9cf7d0243fc9df2f74701cf5"],"name":"no_scp_blocks_lambda_create_function","reason":"1 SCP binding(s) likely_blocking with governance_confidence=complete on lambda:CreateFunction","state":"fail"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_boundary_blocks_lambda_create_function","reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"No identity-policy Deny blocks lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_identity_deny_blocks_lambda_create_function","reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No identity-policy Deny blocks iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_identity_deny_blocks_passrole","reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to Lambda","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"passrole_condition_scoped_to_lambda_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"info","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/AdminRole","region":"-"},"title":"Blocked Lambda PassRole chain from arn:aws:iam::111111\u003111111:user/Alice to arn:aws:iam::111111\u003111111:role/AdminRole","verdict":"blocked"}],"metadata":{"canonical_hash":"e29b23f728cd4e84d023bb12c869a520b5dbab876d64f2283dbfa80e8db37784","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_lambda"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_lambda":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"b8be6a39dd4b8ff39656668b709a83d2854492fc9cf7d0243fc9df2f74701cf5","edge_id":"aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef","kind":"scp","reason":"SCP DenyLambdaCreate at OU ou-prod denies lambda:CreateFunction"}],"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":["b8be6a39dd4b8ff39656668b709a83d2854492fc9cf7d0243fc9df2f74701cf5"],"edge_constraint_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef|b8be6a39dd4b8ff39656668b709a83d2854492fc9cf7d0243fc9df2f74701cf5"],"edge_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499","aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_lambda_create_function","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","lambda:CreateFunction"],"reason":"explicit non-conditioned permission edge for lambda:CreateFunction","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","arn:aws:iam::11111\u003111111\u0031:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_lambda_service","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","lambda.amazonaws.com"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"1 SCP binding(s) likely_blocking with governance_confidence=complete on lambda:CreateFunction","result":"FAIL","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_no_identity_deny_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","result":"PASS","step":9},{"action":"check_passrole_condition_scoped_to_lambda_or_absent","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":10},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":11},{"action":"emit_verdict","inputs":["blocked","info"],"reason":"SCP blocks lambda:CreateFunction with complete confidence","result":"BLOCKED","step":12}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"lambda:CreateFunction grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",1,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole",0,"trust lambda"]}},"finding_id":"c2ddac95196359eeaef510e3d4d7beb9512d8e62b12db60fc7abf20152bb1595","finding_key":"c488211a0effa85c5882db3d307bb6421384a01661f31370daa8aa18a97c84d3","pattern_id":"passrole_lambda","pattern_title":"Lambda PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"SCP blocks lambda:CreateFunction with complete confidence","required_checks":[{"description":"Source principal has lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"source_has_lambda_create_function","reason":"explicit non-conditioned permission edge for lambda:CreateFunction","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts Lambda","evidence_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["b8be6a39dd4b8ff39656668b709a83d2854492fc9cf7d0243fc9df2f74701cf5"],"name":"no_scp_blocks_lambda_create_function","reason":"1 SCP binding(s) likely_blocking with governance_confidence=complete on lambda:CreateFunction","state":"fail"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_boundary_blocks_lambda_create_function","reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"No identity-policy Deny blocks lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_identity_deny_blocks_lambda_create_function","reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No identity-policy Deny blocks iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_identity_deny_blocks_passrole","reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to Lambda","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"passrole_condition_scoped_to_lambda_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"info","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/AdminRole","region":"-"},"title":"Blocked Lambda PassRole chain from arn:aws:iam::11111\u003111111\u0031:user/Alice to arn:aws:iam::11111\u003111111\u0031:role/AdminRole","verdict":"blocked"}],"metadata":{"canonical_hash":"2d1024c2c22a59a04e09250db22d1f466f1451fa09a4a4c12c04e7fa5a5c7e21","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_lambda"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_lambda":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/passrole_lambda/fixture_d_blocked_by_boundary_post_bnd1.json b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_d_blocked_by_boundary_post_bnd1.json index bb90bba..8bbb8f6 100644 --- a/tests/fixtures/expected_output/findings/passrole_lambda/fixture_d_blocked_by_boundary_post_bnd1.json +++ b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_d_blocked_by_boundary_post_bnd1.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"b364ac9edf52f48c1a9919828b1cb92a76552feef31828701aae82499204d674","edge_id":"aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef","kind":"boundary","reason":"permission boundary allowed_actions={s3:*, dynamodb:*} does not include lambda:CreateFunction (post-BND-1)"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["b364ac9edf52f48c1a9919828b1cb92a76552feef31828701aae82499204d674"],"edge_constraint_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef|b364ac9edf52f48c1a9919828b1cb92a76552feef31828701aae82499204d674"],"edge_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499","aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_lambda_create_function","inputs":["arn:aws:iam::111111\u003111111:user/Alice","lambda:CreateFunction"],"reason":"explicit non-conditioned permission edge for lambda:CreateFunction","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","arn:aws:iam::111111\u003111111:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_lambda_service","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","lambda.amazonaws.com"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no SCP bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"1 permission boundary binding(s) likely_blocking with governance_confidence=complete on lambda:CreateFunction","result":"FAIL","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_no_identity_deny_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","result":"PASS","step":9},{"action":"check_passrole_condition_scoped_to_lambda_or_absent","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":10},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":11},{"action":"emit_verdict","inputs":["blocked","info"],"reason":"permission boundary blocks lambda:CreateFunction (post-BND-1)","result":"BLOCKED","step":12}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"lambda:CreateFunction grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::111111\u003111111:policy/AlicePerms",1,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::111111\u003111111:role/AdminRole",0,"trust lambda"]}},"finding_id":"a852c54c1122d12714bb97c048da7f7d81676e461b4e0bb75f6b575752d21e02","finding_key":"c488211a0effa85c5882db3d307bb6421384a01661f31370daa8aa18a97c84d3","pattern_id":"passrole_lambda","pattern_title":"Lambda PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"permission boundary blocks lambda:CreateFunction (post-BND-1)","required_checks":[{"description":"Source principal has lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"source_has_lambda_create_function","reason":"explicit non-conditioned permission edge for lambda:CreateFunction","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts Lambda","evidence_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_scp_blocks_lambda_create_function","reason":"no SCP bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows lambda:CreateFunction","evidence_refs":["b364ac9edf52f48c1a9919828b1cb92a76552feef31828701aae82499204d674"],"name":"no_boundary_blocks_lambda_create_function","reason":"1 permission boundary binding(s) likely_blocking with governance_confidence=complete on lambda:CreateFunction","state":"fail"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"No identity-policy Deny blocks lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_identity_deny_blocks_lambda_create_function","reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No identity-policy Deny blocks iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_identity_deny_blocks_passrole","reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to Lambda","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"passrole_condition_scoped_to_lambda_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"info","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/AdminRole","region":"-"},"title":"Blocked Lambda PassRole chain from arn:aws:iam::111111\u003111111:user/Alice to arn:aws:iam::111111\u003111111:role/AdminRole","verdict":"blocked"}],"metadata":{"canonical_hash":"ab812e9aa40f4ed96a759abb0b0bb578f8cd7e63e177639\u003639097\u00345aeb03c13e","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_lambda"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_lambda":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"b364ac9edf52f48c1a9919828b1cb92a76552feef31828701aae82499204d674","edge_id":"aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef","kind":"boundary","reason":"permission boundary allowed_actions={s3:*, dynamodb:*} does not include lambda:CreateFunction (post-BND-1)"}],"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":["b364ac9edf52f48c1a9919828b1cb92a76552feef31828701aae82499204d674"],"edge_constraint_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef|b364ac9edf52f48c1a9919828b1cb92a76552feef31828701aae82499204d674"],"edge_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499","aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_lambda_create_function","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","lambda:CreateFunction"],"reason":"explicit non-conditioned permission edge for lambda:CreateFunction","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","arn:aws:iam::11111\u003111111\u0031:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_lambda_service","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","lambda.amazonaws.com"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no SCP bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"1 permission boundary binding(s) likely_blocking with governance_confidence=complete on lambda:CreateFunction","result":"FAIL","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_no_identity_deny_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","result":"PASS","step":9},{"action":"check_passrole_condition_scoped_to_lambda_or_absent","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":10},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":11},{"action":"emit_verdict","inputs":["blocked","info"],"reason":"permission boundary blocks lambda:CreateFunction (post-BND-1)","result":"BLOCKED","step":12}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"lambda:CreateFunction grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",1,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole",0,"trust lambda"]}},"finding_id":"a852c54c1122d12714bb97c048da7f7d81676e461b4e0bb75f6b575752d21e02","finding_key":"c488211a0effa85c5882db3d307bb6421384a01661f31370daa8aa18a97c84d3","pattern_id":"passrole_lambda","pattern_title":"Lambda PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"permission boundary blocks lambda:CreateFunction (post-BND-1)","required_checks":[{"description":"Source principal has lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"source_has_lambda_create_function","reason":"explicit non-conditioned permission edge for lambda:CreateFunction","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts Lambda","evidence_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_scp_blocks_lambda_create_function","reason":"no SCP bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows lambda:CreateFunction","evidence_refs":["b364ac9edf52f48c1a9919828b1cb92a76552feef31828701aae82499204d674"],"name":"no_boundary_blocks_lambda_create_function","reason":"1 permission boundary binding(s) likely_blocking with governance_confidence=complete on lambda:CreateFunction","state":"fail"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"No identity-policy Deny blocks lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_identity_deny_blocks_lambda_create_function","reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No identity-policy Deny blocks iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_identity_deny_blocks_passrole","reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to Lambda","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"passrole_condition_scoped_to_lambda_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"info","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/AdminRole","region":"-"},"title":"Blocked Lambda PassRole chain from arn:aws:iam::11111\u003111111\u0031:user/Alice to arn:aws:iam::11111\u003111111\u0031:role/AdminRole","verdict":"blocked"}],"metadata":{"canonical_hash":"60c1bd09875f83c96ec61b45126ef94d27a3704e14e31ea91e9650472dc40915","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_lambda"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_lambda":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/passrole_lambda/fixture_e_inconclusive_partial_scp.json b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_e_inconclusive_partial_scp.json index 61d7d7e..0fab1c5 100644 --- a/tests/fixtures/expected_output/findings/passrole_lambda/fixture_e_inconclusive_partial_scp.json +++ b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_e_inconclusive_partial_scp.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":["6ec4df77d19c38ae8d5cd296db1aacdbee1e49c188c85fd237e09452c570d096"],"edge_constraint_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef|6ec4df77d19c38ae8d5cd296db1aacdbee1e49c188c85fd237e09452c570d096"],"edge_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499","aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_lambda_create_function","inputs":["arn:aws:iam::111111\u003111111:user/Alice","lambda:CreateFunction"],"reason":"explicit non-conditioned permission edge for lambda:CreateFunction","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","arn:aws:iam::111111\u003111111:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_lambda_service","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","lambda.amazonaws.com"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"1 SCP binding(s) with governance_confidence \u2208 partial/needs_review on lambda:CreateFunction \u2014 cannot confirm","result":"UNKNOWN","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_no_identity_deny_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","result":"PASS","step":9},{"action":"check_passrole_condition_scoped_to_lambda_or_absent","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":10},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":11},{"action":"emit_verdict","inputs":["high","inconclusive"],"reason":"check(s) UNKNOWN: no_scp_blocks_lambda_create_function","result":"INCONCLUSIVE","step":12}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"lambda:CreateFunction grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::111111\u003111111:policy/AlicePerms",1,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::111111\u003111111:role/AdminRole",0,"trust lambda"]}},"finding_id":"7face760f827d533d5b9e05bee0013a98a24e2c07f9158429ed098766bed5ec3","finding_key":"c488211a0effa85c5882db3d307bb6421384a01661f31370daa8aa18a97c84d3","pattern_id":"passrole_lambda","pattern_title":"Lambda PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: no_scp_blocks_lambda_create_function","required_checks":[{"description":"Source principal has lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"source_has_lambda_create_function","reason":"explicit non-conditioned permission edge for lambda:CreateFunction","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts Lambda","evidence_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["6ec4df77d19c38ae8d5cd296db1aacdbee1e49c188c85fd237e09452c570d096"],"name":"no_scp_blocks_lambda_create_function","reason":"1 SCP binding(s) with governance_confidence \u2208 partial/needs_review on lambda:CreateFunction \u2014 cannot confirm","state":"unknown"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_boundary_blocks_lambda_create_function","reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"No identity-policy Deny blocks lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_identity_deny_blocks_lambda_create_function","reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No identity-policy Deny blocks iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_identity_deny_blocks_passrole","reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to Lambda","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"passrole_condition_scoped_to_lambda_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/AdminRole","region":"-"},"title":"Inconclusive Lambda PassRole chain from arn:aws:iam::111111\u003111111:user/Alice to arn:aws:iam::111111\u003111111:role/AdminRole","verdict":"inconclusive"}],"metadata":{"canonical_hash":"e3e690b6a1dd812e776fdfef8d1fce26f7461ec1c1d48523d2ff3f1711fa5a42","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_lambda"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_lambda":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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":["6ec4df77d19c38ae8d5cd296db1aacdbee1e49c188c85fd237e09452c570d096"],"edge_constraint_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef|6ec4df77d19c38ae8d5cd296db1aacdbee1e49c188c85fd237e09452c570d096"],"edge_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499","aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_lambda_create_function","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","lambda:CreateFunction"],"reason":"explicit non-conditioned permission edge for lambda:CreateFunction","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","arn:aws:iam::11111\u003111111\u0031:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_lambda_service","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","lambda.amazonaws.com"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"1 SCP binding(s) with governance_confidence \u2208 partial/needs_review on lambda:CreateFunction \u2014 cannot confirm","result":"UNKNOWN","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_no_identity_deny_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","result":"PASS","step":9},{"action":"check_passrole_condition_scoped_to_lambda_or_absent","inputs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":10},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":11},{"action":"emit_verdict","inputs":["high","inconclusive"],"reason":"check(s) UNKNOWN: no_scp_blocks_lambda_create_function","result":"INCONCLUSIVE","step":12}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"lambda:CreateFunction grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",1,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole",0,"trust lambda"]}},"finding_id":"7face760f827d533d5b9e05bee0013a98a24e2c07f9158429ed098766bed5ec3","finding_key":"c488211a0effa85c5882db3d307bb6421384a01661f31370daa8aa18a97c84d3","pattern_id":"passrole_lambda","pattern_title":"Lambda PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: no_scp_blocks_lambda_create_function","required_checks":[{"description":"Source principal has lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"source_has_lambda_create_function","reason":"explicit non-conditioned permission edge for lambda:CreateFunction","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts Lambda","evidence_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["6ec4df77d19c38ae8d5cd296db1aacdbee1e49c188c85fd237e09452c570d096"],"name":"no_scp_blocks_lambda_create_function","reason":"1 SCP binding(s) with governance_confidence \u2208 partial/needs_review on lambda:CreateFunction \u2014 cannot confirm","state":"unknown"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_boundary_blocks_lambda_create_function","reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"No identity-policy Deny blocks lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_identity_deny_blocks_lambda_create_function","reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No identity-policy Deny blocks iam:PassRole","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"no_identity_deny_blocks_passrole","reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to Lambda","evidence_refs":["889ebd36b4bc7f73d6b590cef0703a9ebf57dbfef3882b5036cde29e5d80e499"],"name":"passrole_condition_scoped_to_lambda_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/AdminRole","region":"-"},"title":"Inconclusive Lambda PassRole chain from arn:aws:iam::11111\u003111111\u0031:user/Alice to arn:aws:iam::11111\u003111111\u0031:role/AdminRole","verdict":"inconclusive"}],"metadata":{"canonical_hash":"c9079aa675953886e30c9a4f2f2a18fc4d9db4ac51ca254a405161f4f18b5b6f","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_lambda"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_lambda":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/passrole_lambda/fixture_f_hyperedge_inconclusive.json b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_f_hyperedge_inconclusive.json index 71383a5..84d55fd 100644 --- a/tests/fixtures/expected_output/findings/passrole_lambda/fixture_f_hyperedge_inconclusive.json +++ b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_f_hyperedge_inconclusive.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7","cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_lambda_create_function","inputs":["arn:aws:iam::111111\u003111111:user/Alice","lambda:CreateFunction"],"reason":"explicit non-conditioned permission edge for lambda:CreateFunction","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","arn:aws:iam::111111\u003111111:user/Alice","iam:PassRole"],"reason":"matching iam:PassRole edge has ambiguity flag (hyperedge, wildcard resource, or conditions)","result":"UNKNOWN","step":2},{"action":"check_target_trusts_lambda_service","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","lambda.amazonaws.com"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"reason":"no SCP bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_no_identity_deny_blocks_lambda_create_function","inputs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","result":"PASS","step":9},{"action":"check_passrole_condition_scoped_to_lambda_or_absent","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":10},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":11},{"action":"emit_verdict","inputs":["high","inconclusive"],"reason":"check(s) UNKNOWN: source_has_passrole_to_target","result":"INCONCLUSIVE","step":12}],"statement_digests":["cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe","deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","feedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedface"],"statement_sources":{"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::111111\u003111111:role/AdminRole",0,"trust lambda"],"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"lambda:CreateFunction grant"],"feedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedface":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"wildcard PassRole"]}},"finding_id":"f5e9bb86c98302c12186c1915332fa0285c4b564169e3802623db4fe379659cf","finding_key":"c488211a0effa85c5882db3d307bb6421384a01661f31370daa8aa18a97c84d3","pattern_id":"passrole_lambda","pattern_title":"Lambda PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: source_has_passrole_to_target","required_checks":[{"description":"Source principal has lambda:CreateFunction","evidence_refs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"name":"source_has_lambda_create_function","reason":"explicit non-conditioned permission edge for lambda:CreateFunction","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"source_has_passrole_to_target","reason":"matching iam:PassRole edge has ambiguity flag (hyperedge, wildcard resource, or conditions)","state":"unknown"},{"description":"Target role's trust policy trusts Lambda","evidence_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"name":"no_scp_blocks_lambda_create_function","reason":"no SCP bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows lambda:CreateFunction","evidence_refs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"name":"no_boundary_blocks_lambda_create_function","reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"No identity-policy Deny blocks lambda:CreateFunction","evidence_refs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"name":"no_identity_deny_blocks_lambda_create_function","reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No identity-policy Deny blocks iam:PassRole","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"no_identity_deny_blocks_passrole","reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to Lambda","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"passrole_condition_scoped_to_lambda_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/AdminRole","region":"-"},"title":"Inconclusive Lambda PassRole chain from arn:aws:iam::111111\u003111111:user/Alice to arn:aws:iam::111111\u003111111:role/AdminRole","verdict":"inconclusive"}],"metadata":{"canonical_hash":"fe2caa4b32c120dd6c7e6a52099338f85810ecd38d6bad38e70826dd2960467d","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_lambda"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_lambda":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7","cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_lambda_create_function","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","lambda:CreateFunction"],"reason":"explicit non-conditioned permission edge for lambda:CreateFunction","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","arn:aws:iam::11111\u003111111\u0031:user/Alice","iam:PassRole"],"reason":"matching iam:PassRole edge has ambiguity flag (hyperedge, wildcard resource, or conditions)","result":"UNKNOWN","step":2},{"action":"check_target_trusts_lambda_service","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","lambda.amazonaws.com"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"reason":"no SCP bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_no_identity_deny_blocks_lambda_create_function","inputs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","result":"PASS","step":9},{"action":"check_passrole_condition_scoped_to_lambda_or_absent","inputs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"reason":"no condition block on PassRole statement (unrestricted)","result":"PASS","step":10},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":11},{"action":"emit_verdict","inputs":["high","inconclusive"],"reason":"check(s) UNKNOWN: source_has_passrole_to_target","result":"INCONCLUSIVE","step":12}],"statement_digests":["cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe","deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","feedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedface"],"statement_sources":{"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole",0,"trust lambda"],"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"lambda:CreateFunction grant"],"feedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedfacefeedface":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"wildcard PassRole"]}},"finding_id":"f5e9bb86c98302c12186c1915332fa0285c4b564169e3802623db4fe379659cf","finding_key":"c488211a0effa85c5882db3d307bb6421384a01661f31370daa8aa18a97c84d3","pattern_id":"passrole_lambda","pattern_title":"Lambda PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: source_has_passrole_to_target","required_checks":[{"description":"Source principal has lambda:CreateFunction","evidence_refs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"name":"source_has_lambda_create_function","reason":"explicit non-conditioned permission edge for lambda:CreateFunction","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"source_has_passrole_to_target","reason":"matching iam:PassRole edge has ambiguity flag (hyperedge, wildcard resource, or conditions)","state":"unknown"},{"description":"Target role's trust policy trusts Lambda","evidence_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"name":"no_scp_blocks_lambda_create_function","reason":"no SCP bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows lambda:CreateFunction","evidence_refs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"name":"no_boundary_blocks_lambda_create_function","reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"No identity-policy Deny blocks lambda:CreateFunction","evidence_refs":["a26d32ca466ae507ea141dd418f8d205b9ea732057a8b14eba46ba886ff395d7"],"name":"no_identity_deny_blocks_lambda_create_function","reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No identity-policy Deny blocks iam:PassRole","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"no_identity_deny_blocks_passrole","reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to Lambda","evidence_refs":["cedd59c4da164dea1973f1c9dbe1dbe9fdece6cdb4b1f312a22fadd7b0ca7544"],"name":"passrole_condition_scoped_to_lambda_or_absent","reason":"no condition block on PassRole statement (unrestricted)","state":"pass"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/AdminRole","region":"-"},"title":"Inconclusive Lambda PassRole chain from arn:aws:iam::11111\u003111111\u0031:user/Alice to arn:aws:iam::11111\u003111111\u0031:role/AdminRole","verdict":"inconclusive"}],"metadata":{"canonical_hash":"610f82e8e6ba6fa0a9a9488eff8df89b5797fb2e6b2e97f23a7910eeb5a5efa3","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_lambda"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"passrole_lambda":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/passrole_lambda/fixture_g_passedtoservice_ec2.json b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_g_passedtoservice_ec2.json index 8c949ff..ed956f3 100644 --- a/tests/fixtures/expected_output/findings/passrole_lambda/fixture_g_passedtoservice_ec2.json +++ b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_g_passedtoservice_ec2.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":null,"edge_id":"b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3","kind":"passed_to_service","reason":"iam:PassedToService scoped to ['ec2.amazonaws.com'] (not Lambda) \u2014 PassRole cannot pass to a Lambda execution role"}],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef","b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_lambda_create_function","inputs":["arn:aws:iam::111111\u003111111:user/Alice","lambda:CreateFunction"],"reason":"explicit non-conditioned permission edge for lambda:CreateFunction","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","arn:aws:iam::111111\u003111111:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_lambda_service","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole","lambda.amazonaws.com"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no SCP bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_no_identity_deny_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","result":"PASS","step":9},{"action":"check_passrole_condition_scoped_to_lambda_or_absent","inputs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"reason":"iam:PassedToService scoped to ['ec2.amazonaws.com'] (not Lambda) \u2014 PassRole cannot pass to a Lambda execution role","result":"FAIL","step":10},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::111111\u003111111:role/AdminRole"],"reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":11},{"action":"emit_verdict","inputs":["medium","precondition_only"],"reason":"iam:PassedToService scoped away from Lambda \u2014 chain not exploitable","result":"PRECONDITION_ONLY","step":12}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::111111\u003111111:policy/AlicePerms",0,"lambda:CreateFunction grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::111111\u003111111:policy/AlicePerms",1,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::111111\u003111111:role/AdminRole",0,"trust lambda"]}},"finding_id":"96df0ce644885d2a670fdee6e782899de3ae7a9a714e01fae760825aa8ccbc1b","finding_key":"c488211a0effa85c5882db3d307bb6421384a01661f31370daa8aa18a97c84d3","pattern_id":"passrole_lambda","pattern_title":"Lambda PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"iam:PassedToService scoped away from Lambda \u2014 chain not exploitable","required_checks":[{"description":"Source principal has lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"source_has_lambda_create_function","reason":"explicit non-conditioned permission edge for lambda:CreateFunction","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts Lambda","evidence_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_scp_blocks_lambda_create_function","reason":"no SCP bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_boundary_blocks_lambda_create_function","reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"No identity-policy Deny blocks lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_identity_deny_blocks_lambda_create_function","reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No identity-policy Deny blocks iam:PassRole","evidence_refs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"name":"no_identity_deny_blocks_passrole","reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to Lambda","evidence_refs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"name":"passrole_condition_scoped_to_lambda_or_absent","reason":"iam:PassedToService scoped to ['ec2.amazonaws.com'] (not Lambda) \u2014 PassRole cannot pass to a Lambda execution role","state":"fail"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"medium","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/AdminRole","region":"-"},"title":"Precondition-only Lambda PassRole chain from arn:aws:iam::111111\u003111111:user/Alice to arn:aws:iam::111111\u003111111:role/AdminRole","verdict":"precondition_only"}],"metadata":{"canonical_hash":"90e8667b7ffd8f25d19aa0314fb417d19a700ac155d857cf407a314b1f873f74","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_lambda"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":1,"validated":0}},"reasoner_versions":{"passrole_lambda":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":null,"edge_id":"b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3","kind":"passed_to_service","reason":"iam:PassedToService scoped to ['ec2.amazonaws.com'] (not Lambda) \u2014 PassRole cannot pass to a Lambda execution role"}],"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":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef","b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"node_refs":["0f67956c87de01e03ed0e64646e0005ad87a926674f7c985e84ea696843e1863","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_source_has_lambda_create_function","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","lambda:CreateFunction"],"reason":"explicit non-conditioned permission edge for lambda:CreateFunction","result":"PASS","step":1},{"action":"check_source_has_passrole_to_target","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","arn:aws:iam::11111\u003111111\u0031:user/Alice","iam:PassRole"],"reason":"explicit non-conditioned permission edge for iam:PassRole","result":"PASS","step":2},{"action":"check_target_trusts_lambda_service","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole","lambda.amazonaws.com"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no SCP bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"reason":"no permission boundary bindings observed on iam:PassRole witness edge","result":"PASS","step":7},{"action":"check_no_identity_deny_blocks_lambda_create_function","inputs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","result":"PASS","step":9},{"action":"check_passrole_condition_scoped_to_lambda_or_absent","inputs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"reason":"iam:PassedToService scoped to ['ec2.amazonaws.com'] (not Lambda) \u2014 PassRole cannot pass to a Lambda execution role","result":"FAIL","step":10},{"action":"evaluate_admin_equivalence","inputs":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole"],"reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole has wildcard permission edges","result":"ADMIN_EQUIVALENT","step":11},{"action":"emit_verdict","inputs":["medium","precondition_only"],"reason":"iam:PassedToService scoped away from Lambda \u2014 chain not exploitable","result":"PRECONDITION_ONLY","step":12}],"statement_digests":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"],"statement_sources":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",0,"lambda:CreateFunction grant"],"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb":["arn:aws:iam::11111\u003111111\u0031:policy/AlicePerms",1,"iam:PassRole grant"],"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe":["arn:aws:iam::11111\u003111111\u0031:role/AdminRole",0,"trust lambda"]}},"finding_id":"96df0ce644885d2a670fdee6e782899de3ae7a9a714e01fae760825aa8ccbc1b","finding_key":"c488211a0effa85c5882db3d307bb6421384a01661f31370daa8aa18a97c84d3","pattern_id":"passrole_lambda","pattern_title":"Lambda PassRole Privilege Chain","pattern_version":"1.0.0","reasoner_exit_reason":"iam:PassedToService scoped away from Lambda \u2014 chain not exploitable","required_checks":[{"description":"Source principal has lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"source_has_lambda_create_function","reason":"explicit non-conditioned permission edge for lambda:CreateFunction","state":"pass"},{"description":"Source can PassRole to the target role","evidence_refs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"name":"source_has_passrole_to_target","reason":"explicit non-conditioned permission edge for iam:PassRole","state":"pass"},{"description":"Target role's trust policy trusts Lambda","evidence_refs":["2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::11111\u003111111\u0031:role/AdminRole trusts lambda.amazonaws.com via trust edge 2629c8f7396d4fb8c81877a269a7826cb75a3fc84ab0898d839656d5ab36fce5","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_scp_blocks_lambda_create_function","reason":"no SCP bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No SCP blocks iam:PassRole with full confidence","evidence_refs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"name":"no_scp_blocks_passrole","reason":"no SCP bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"Permission boundary on source allows lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_boundary_blocks_lambda_create_function","reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"Permission boundary on source allows iam:PassRole","evidence_refs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"name":"no_boundary_blocks_passrole","reason":"no permission boundary bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"No identity-policy Deny blocks lambda:CreateFunction","evidence_refs":["aa79e3205e582c0a6fe97ec720e2164f6c69585e6f73983fcd4442ccf4d3d8ef"],"name":"no_identity_deny_blocks_lambda_create_function","reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","state":"pass"},{"description":"No identity-policy Deny blocks iam:PassRole","evidence_refs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"name":"no_identity_deny_blocks_passrole","reason":"no identity policy Deny bindings observed on iam:PassRole witness edge","state":"pass"},{"description":"iam:PassRole condition iam:PassedToService is absent or scoped to Lambda","evidence_refs":["b4ab15e83fe030976622e3d756b98aacd464a9692741db1e0e1df5683a9e27e3"],"name":"passrole_condition_scoped_to_lambda_or_absent","reason":"iam:PassedToService scoped to ['ec2.amazonaws.com'] (not Lambda) \u2014 PassRole cannot pass to a Lambda execution role","state":"fail"}],"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","severity":"medium","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/AdminRole","region":"-"},"title":"Precondition-only Lambda PassRole chain from arn:aws:iam::11111\u003111111\u0031:user/Alice to arn:aws:iam::11111\u003111111\u0031:role/AdminRole","verdict":"precondition_only"}],"metadata":{"canonical_hash":"0fe542645032d37187a9d7b5e93e8a8214370d115e48c07f8d6a99a8ee51b5a9","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["passrole_lambda"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":1,"validated":0}},"reasoner_versions":{"passrole_lambda":"1.0.0"},"scenario_hash":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","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/s3_bucket_takeover/fixture_a_validated_critical.json b/tests/fixtures/expected_output/findings/s3_bucket_takeover/fixture_a_validated_critical.json index 1aac4db..2ba4a49 100644 --- a/tests/fixtures/expected_output/findings/s3_bucket_takeover/fixture_a_validated_critical.json +++ b/tests/fixtures/expected_output/findings/s3_bucket_takeover/fixture_a_validated_critical.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"node_refs":["3698aa56e2b6f8b7e5769b63328e45d96642bcba2cd6bc9de5e4b259586b9de6","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_principal_has_put_bucket_policy_permission","inputs":["arn:aws:iam::111111\u003111111:user/Alice"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_witness_edge_is_clean","inputs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"reason":"witness edge resolves to specific target bucket","result":"PASS","step":2},{"action":"check_target_bucket_collected","inputs":["arn:aws:s3:::corp-secrets"],"reason":"target bucket was directly collected","result":"PASS","step":3},{"action":"check_no_scp_blocks_put_bucket_policy","inputs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"reason":"no SCP bindings observed","result":"PASS","step":4},{"action":"check_no_boundary_blocks_put_bucket_policy","inputs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"reason":"no permission boundary bindings observed","result":"PASS","step":5},{"action":"check_principal_is_actionable","inputs":["arn:aws:iam::111111\u003111111:user/Alice"],"reason":"principal is an attacker-controllable user or role","result":"PASS","step":6}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/S3Mgmt",0,"s3:PutBucketPolicy grant"]}},"finding_id":"c2380b206ace316fc44d72498aa7d90f15a20696d9acd3442834e49ccfcdf555","finding_key":"9da3b827bc084540c2988c18f15602875cf18cae8dc2f3ae6a49681e444c526b","pattern_id":"s3_bucket_takeover","pattern_title":"S3 Bucket Takeover","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; principal can rewrite bucket policy and take full control of bucket contents","required_checks":[{"description":"Principal has a permission edge for s3:PutBucketPolicy (enumeration invariant)","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"principal_has_put_bucket_policy_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge for s3:PutBucketPolicy resolves to a specific target bucket (clean witness proves the edge's target)","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"witness_edge_is_clean","reason":"witness edge resolves to specific target bucket","state":"pass"},{"description":"Target S3 bucket was directly collected, not only materialized from a dangling policy reference","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"target_bucket_collected","reason":"target bucket was directly collected","state":"pass"},{"description":"No SCP blocks s3:PutBucketPolicy on this edge with complete governance confidence","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"no_scp_blocks_put_bucket_policy","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks s3:PutBucketPolicy on this edge","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"no_boundary_blocks_put_bucket_policy","reason":"no permission boundary bindings observed","state":"pass"},{"description":"Principal is an attacker-controllable user or role (not a service principal or root)","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"principal_is_actionable","reason":"principal is an attacker-controllable user or role","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"critical","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"S3Bucket","provider":"aws","provider_id":"arn:aws:s3:::corp-secrets","region":"-"},"title":"Validated S3 bucket takeover: arn:aws:iam::111111\u003111111:user/Alice can call s3:PutBucketPolicy on arn:aws:s3:::corp-secrets","verdict":"validated"}],"metadata":{"canonical_hash":"8ac0bb667fa986d96ec9bf5e9ea273cf27c018d6239e409c0f40343a0e6a4729","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["s3_bucket_takeover"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"s3_bucket_takeover":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"node_refs":["3698aa56e2b6f8b7e5769b63328e45d96642bcba2cd6bc9de5e4b259586b9de6","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_principal_has_put_bucket_policy_permission","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_witness_edge_is_clean","inputs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"reason":"witness edge resolves to specific target bucket","result":"PASS","step":2},{"action":"check_target_bucket_collected","inputs":["arn:aws:s3:::corp-secrets"],"reason":"target bucket was directly collected","result":"PASS","step":3},{"action":"check_no_scp_blocks_put_bucket_policy","inputs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"reason":"no SCP bindings observed","result":"PASS","step":4},{"action":"check_no_boundary_blocks_put_bucket_policy","inputs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"reason":"no permission boundary bindings observed","result":"PASS","step":5},{"action":"check_principal_is_actionable","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice"],"reason":"principal is an attacker-controllable user or role","result":"PASS","step":6}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/S3Mgmt",0,"s3:PutBucketPolicy grant"]}},"finding_id":"c2380b206ace316fc44d72498aa7d90f15a20696d9acd3442834e49ccfcdf555","finding_key":"9da3b827bc084540c2988c18f15602875cf18cae8dc2f3ae6a49681e444c526b","pattern_id":"s3_bucket_takeover","pattern_title":"S3 Bucket Takeover","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; principal can rewrite bucket policy and take full control of bucket contents","required_checks":[{"description":"Principal has a permission edge for s3:PutBucketPolicy (enumeration invariant)","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"principal_has_put_bucket_policy_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge for s3:PutBucketPolicy resolves to a specific target bucket (clean witness proves the edge's target)","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"witness_edge_is_clean","reason":"witness edge resolves to specific target bucket","state":"pass"},{"description":"Target S3 bucket was directly collected, not only materialized from a dangling policy reference","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"target_bucket_collected","reason":"target bucket was directly collected","state":"pass"},{"description":"No SCP blocks s3:PutBucketPolicy on this edge with complete governance confidence","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"no_scp_blocks_put_bucket_policy","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks s3:PutBucketPolicy on this edge","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"no_boundary_blocks_put_bucket_policy","reason":"no permission boundary bindings observed","state":"pass"},{"description":"Principal is an attacker-controllable user or role (not a service principal or root)","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"principal_is_actionable","reason":"principal is an attacker-controllable user or role","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"critical","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"S3Bucket","provider":"aws","provider_id":"arn:aws:s3:::corp-secrets","region":"-"},"title":"Validated S3 bucket takeover: arn:aws:iam::11111\u003111111\u0031:user/Alice can call s3:PutBucketPolicy on arn:aws:s3:::corp-secrets","verdict":"validated"}],"metadata":{"canonical_hash":"8ef0a38b6deb5bcb1754dc0de37abad7eae58456bb01c403dab0b6c977ca563e","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["s3_bucket_takeover"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"s3_bucket_takeover":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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/s3_bucket_takeover/fixture_b_wildcard_inconclusive.json b/tests/fixtures/expected_output/findings/s3_bucket_takeover/fixture_b_wildcard_inconclusive.json index d6b6a48..76bc670 100644 --- a/tests/fixtures/expected_output/findings/s3_bucket_takeover/fixture_b_wildcard_inconclusive.json +++ b/tests/fixtures/expected_output/findings/s3_bucket_takeover/fixture_b_wildcard_inconclusive.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["6cb6162f97b1adea099844\u003382804\u0031949a9e086d2eb507a09c31830a841d08053"],"node_refs":["3698aa56e2b6f8b7e5769b63328e45d96642bcba2cd6bc9de5e4b259586b9de6","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_principal_has_put_bucket_policy_permission","inputs":["arn:aws:iam::111111\u003111111:user/Alice"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_witness_edge_is_clean","inputs":["6cb6162f97b1adea099844\u003382804\u0031949a9e086d2eb507a09c31830a841d08053"],"reason":"witness edge is wildcard-expansion hyperedge or wildcard-resource (target bucket iterated from all buckets)","result":"UNKNOWN","step":2},{"action":"check_target_bucket_collected","inputs":["arn:aws:s3:::corp-secrets"],"reason":"target bucket was directly collected","result":"PASS","step":3},{"action":"check_no_scp_blocks_put_bucket_policy","inputs":["6cb6162f97b1adea099844\u003382804\u0031949a9e086d2eb507a09c31830a841d08053"],"reason":"no SCP bindings observed","result":"PASS","step":4},{"action":"check_no_boundary_blocks_put_bucket_policy","inputs":["6cb6162f97b1adea099844\u003382804\u0031949a9e086d2eb507a09c31830a841d08053"],"reason":"no permission boundary bindings observed","result":"PASS","step":5},{"action":"check_principal_is_actionable","inputs":["arn:aws:iam::111111\u003111111:user/Alice"],"reason":"principal is an attacker-controllable user or role","result":"PASS","step":6}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/S3Mgmt",0,"s3:PutBucketPolicy grant"]}},"finding_id":"62f8acf2884aaaddbc140e453a6082d4b46817bce03bd95411366acf7169315f","finding_key":"9da3b827bc084540c2988c18f15602875cf18cae8dc2f3ae6a49681e444c526b","pattern_id":"s3_bucket_takeover","pattern_title":"S3 Bucket Takeover","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: witness_edge_is_clean","required_checks":[{"description":"Principal has a permission edge for s3:PutBucketPolicy (enumeration invariant)","evidence_refs":["6cb6162f97b1adea099844\u003382804\u0031949a9e086d2eb507a09c31830a841d08053"],"name":"principal_has_put_bucket_policy_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge for s3:PutBucketPolicy resolves to a specific target bucket (clean witness proves the edge's target)","evidence_refs":["6cb6162f97b1adea099844\u003382804\u0031949a9e086d2eb507a09c31830a841d08053"],"name":"witness_edge_is_clean","reason":"witness edge is wildcard-expansion hyperedge or wildcard-resource (target bucket iterated from all buckets)","state":"unknown"},{"description":"Target S3 bucket was directly collected, not only materialized from a dangling policy reference","evidence_refs":["6cb6162f97b1adea099844\u003382804\u0031949a9e086d2eb507a09c31830a841d08053"],"name":"target_bucket_collected","reason":"target bucket was directly collected","state":"pass"},{"description":"No SCP blocks s3:PutBucketPolicy on this edge with complete governance confidence","evidence_refs":["6cb6162f97b1adea099844\u003382804\u0031949a9e086d2eb507a09c31830a841d08053"],"name":"no_scp_blocks_put_bucket_policy","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks s3:PutBucketPolicy on this edge","evidence_refs":["6cb6162f97b1adea099844\u003382804\u0031949a9e086d2eb507a09c31830a841d08053"],"name":"no_boundary_blocks_put_bucket_policy","reason":"no permission boundary bindings observed","state":"pass"},{"description":"Principal is an attacker-controllable user or role (not a service principal or root)","evidence_refs":["6cb6162f97b1adea099844\u003382804\u0031949a9e086d2eb507a09c31830a841d08053"],"name":"principal_is_actionable","reason":"principal is an attacker-controllable user or role","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"S3Bucket","provider":"aws","provider_id":"arn:aws:s3:::corp-secrets","region":"-"},"title":"Inconclusive S3 bucket takeover: arn:aws:iam::111111\u003111111:user/Alice can call s3:PutBucketPolicy on arn:aws:s3:::corp-secrets","verdict":"inconclusive"}],"metadata":{"canonical_hash":"e652850d89b8cfad4f7f8fe98499337da8da7d1d6eb63e60cfd1c86c843cd878","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["s3_bucket_takeover"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"s3_bucket_takeover":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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":["6cb6162f97b1adea09984\u003438280\u00341949a9e086d2eb507a09c31830a841d08053"],"node_refs":["3698aa56e2b6f8b7e5769b63328e45d96642bcba2cd6bc9de5e4b259586b9de6","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_principal_has_put_bucket_policy_permission","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_witness_edge_is_clean","inputs":["6cb6162f97b1adea09984\u003438280\u00341949a9e086d2eb507a09c31830a841d08053"],"reason":"witness edge is wildcard-expansion hyperedge or wildcard-resource (target bucket iterated from all buckets)","result":"UNKNOWN","step":2},{"action":"check_target_bucket_collected","inputs":["arn:aws:s3:::corp-secrets"],"reason":"target bucket was directly collected","result":"PASS","step":3},{"action":"check_no_scp_blocks_put_bucket_policy","inputs":["6cb6162f97b1adea09984\u003438280\u00341949a9e086d2eb507a09c31830a841d08053"],"reason":"no SCP bindings observed","result":"PASS","step":4},{"action":"check_no_boundary_blocks_put_bucket_policy","inputs":["6cb6162f97b1adea09984\u003438280\u00341949a9e086d2eb507a09c31830a841d08053"],"reason":"no permission boundary bindings observed","result":"PASS","step":5},{"action":"check_principal_is_actionable","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice"],"reason":"principal is an attacker-controllable user or role","result":"PASS","step":6}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/S3Mgmt",0,"s3:PutBucketPolicy grant"]}},"finding_id":"62f8acf2884aaaddbc140e453a6082d4b46817bce03bd95411366acf7169315f","finding_key":"9da3b827bc084540c2988c18f15602875cf18cae8dc2f3ae6a49681e444c526b","pattern_id":"s3_bucket_takeover","pattern_title":"S3 Bucket Takeover","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: witness_edge_is_clean","required_checks":[{"description":"Principal has a permission edge for s3:PutBucketPolicy (enumeration invariant)","evidence_refs":["6cb6162f97b1adea09984\u003438280\u00341949a9e086d2eb507a09c31830a841d08053"],"name":"principal_has_put_bucket_policy_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge for s3:PutBucketPolicy resolves to a specific target bucket (clean witness proves the edge's target)","evidence_refs":["6cb6162f97b1adea09984\u003438280\u00341949a9e086d2eb507a09c31830a841d08053"],"name":"witness_edge_is_clean","reason":"witness edge is wildcard-expansion hyperedge or wildcard-resource (target bucket iterated from all buckets)","state":"unknown"},{"description":"Target S3 bucket was directly collected, not only materialized from a dangling policy reference","evidence_refs":["6cb6162f97b1adea09984\u003438280\u00341949a9e086d2eb507a09c31830a841d08053"],"name":"target_bucket_collected","reason":"target bucket was directly collected","state":"pass"},{"description":"No SCP blocks s3:PutBucketPolicy on this edge with complete governance confidence","evidence_refs":["6cb6162f97b1adea09984\u003438280\u00341949a9e086d2eb507a09c31830a841d08053"],"name":"no_scp_blocks_put_bucket_policy","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks s3:PutBucketPolicy on this edge","evidence_refs":["6cb6162f97b1adea09984\u003438280\u00341949a9e086d2eb507a09c31830a841d08053"],"name":"no_boundary_blocks_put_bucket_policy","reason":"no permission boundary bindings observed","state":"pass"},{"description":"Principal is an attacker-controllable user or role (not a service principal or root)","evidence_refs":["6cb6162f97b1adea09984\u003438280\u00341949a9e086d2eb507a09c31830a841d08053"],"name":"principal_is_actionable","reason":"principal is an attacker-controllable user or role","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"S3Bucket","provider":"aws","provider_id":"arn:aws:s3:::corp-secrets","region":"-"},"title":"Inconclusive S3 bucket takeover: arn:aws:iam::11111\u003111111\u0031:user/Alice can call s3:PutBucketPolicy on arn:aws:s3:::corp-secrets","verdict":"inconclusive"}],"metadata":{"canonical_hash":"cc3a8547fa0c345dd79a4c7aefd58f8d9e2d1cfa69538506a8691ba12a95231a","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["s3_bucket_takeover"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"s3_bucket_takeover":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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/s3_bucket_takeover/fixture_c_blocked_by_scp.json b/tests/fixtures/expected_output/findings/s3_bucket_takeover/fixture_c_blocked_by_scp.json index a89a61c..211bd76 100644 --- a/tests/fixtures/expected_output/findings/s3_bucket_takeover/fixture_c_blocked_by_scp.json +++ b/tests/fixtures/expected_output/findings/s3_bucket_takeover/fixture_c_blocked_by_scp.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5","edge_id":"d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0","kind":"scp","reason":"constraint 5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5 affects PutBucketPolicy"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5"],"edge_constraint_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0:5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5"],"edge_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"node_refs":["3698aa56e2b6f8b7e5769b63328e45d96642bcba2cd6bc9de5e4b259586b9de6","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_principal_has_put_bucket_policy_permission","inputs":["arn:aws:iam::111111\u003111111:user/Alice"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_witness_edge_is_clean","inputs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"reason":"witness edge resolves to specific target bucket","result":"PASS","step":2},{"action":"check_target_bucket_collected","inputs":["arn:aws:s3:::corp-secrets"],"reason":"target bucket was directly collected","result":"PASS","step":3},{"action":"check_no_scp_blocks_put_bucket_policy","inputs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"reason":"SCP 5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5 blocks (complete)","result":"FAIL","step":4},{"action":"check_no_boundary_blocks_put_bucket_policy","inputs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"reason":"no permission boundary bindings observed","result":"PASS","step":5},{"action":"check_principal_is_actionable","inputs":["arn:aws:iam::111111\u003111111:user/Alice"],"reason":"principal is an attacker-controllable user or role","result":"PASS","step":6}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/S3Mgmt",0,"s3:PutBucketPolicy grant"]}},"finding_id":"24507eee41c8f26aa8c2fcdf33ca5a57e39992d244a3e0015f807c29a92ef685","finding_key":"9da3b827bc084540c2988c18f15602875cf18cae8dc2f3ae6a49681e444c526b","pattern_id":"s3_bucket_takeover","pattern_title":"S3 Bucket Takeover","pattern_version":"1.0.0","reasoner_exit_reason":"SCP blocks s3:PutBucketPolicy","required_checks":[{"description":"Principal has a permission edge for s3:PutBucketPolicy (enumeration invariant)","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"principal_has_put_bucket_policy_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge for s3:PutBucketPolicy resolves to a specific target bucket (clean witness proves the edge's target)","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"witness_edge_is_clean","reason":"witness edge resolves to specific target bucket","state":"pass"},{"description":"Target S3 bucket was directly collected, not only materialized from a dangling policy reference","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"target_bucket_collected","reason":"target bucket was directly collected","state":"pass"},{"description":"No SCP blocks s3:PutBucketPolicy on this edge with complete governance confidence","evidence_refs":["5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5"],"name":"no_scp_blocks_put_bucket_policy","reason":"SCP 5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5 blocks (complete)","state":"fail"},{"description":"No permission boundary blocks s3:PutBucketPolicy on this edge","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"no_boundary_blocks_put_bucket_policy","reason":"no permission boundary bindings observed","state":"pass"},{"description":"Principal is an attacker-controllable user or role (not a service principal or root)","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"principal_is_actionable","reason":"principal is an attacker-controllable user or role","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"info","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"S3Bucket","provider":"aws","provider_id":"arn:aws:s3:::corp-secrets","region":"-"},"title":"Blocked S3 bucket takeover: arn:aws:iam::111111\u003111111:user/Alice can call s3:PutBucketPolicy on arn:aws:s3:::corp-secrets","verdict":"blocked"}],"metadata":{"canonical_hash":"e3b90525b2982e0682d2ab2a19c6cff61753aadaede79e65368c8395328b22e1","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["s3_bucket_takeover"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"s3_bucket_takeover":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5","edge_id":"d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0","kind":"scp","reason":"constraint 5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5 affects PutBucketPolicy"}],"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":["5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5"],"edge_constraint_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0:5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5"],"edge_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"node_refs":["3698aa56e2b6f8b7e5769b63328e45d96642bcba2cd6bc9de5e4b259586b9de6","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578"],"reasoning_trace":[{"action":"check_principal_has_put_bucket_policy_permission","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_witness_edge_is_clean","inputs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"reason":"witness edge resolves to specific target bucket","result":"PASS","step":2},{"action":"check_target_bucket_collected","inputs":["arn:aws:s3:::corp-secrets"],"reason":"target bucket was directly collected","result":"PASS","step":3},{"action":"check_no_scp_blocks_put_bucket_policy","inputs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"reason":"SCP 5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5 blocks (complete)","result":"FAIL","step":4},{"action":"check_no_boundary_blocks_put_bucket_policy","inputs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"reason":"no permission boundary bindings observed","result":"PASS","step":5},{"action":"check_principal_is_actionable","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice"],"reason":"principal is an attacker-controllable user or role","result":"PASS","step":6}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/S3Mgmt",0,"s3:PutBucketPolicy grant"]}},"finding_id":"24507eee41c8f26aa8c2fcdf33ca5a57e39992d244a3e0015f807c29a92ef685","finding_key":"9da3b827bc084540c2988c18f15602875cf18cae8dc2f3ae6a49681e444c526b","pattern_id":"s3_bucket_takeover","pattern_title":"S3 Bucket Takeover","pattern_version":"1.0.0","reasoner_exit_reason":"SCP blocks s3:PutBucketPolicy","required_checks":[{"description":"Principal has a permission edge for s3:PutBucketPolicy (enumeration invariant)","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"principal_has_put_bucket_policy_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge for s3:PutBucketPolicy resolves to a specific target bucket (clean witness proves the edge's target)","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"witness_edge_is_clean","reason":"witness edge resolves to specific target bucket","state":"pass"},{"description":"Target S3 bucket was directly collected, not only materialized from a dangling policy reference","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"target_bucket_collected","reason":"target bucket was directly collected","state":"pass"},{"description":"No SCP blocks s3:PutBucketPolicy on this edge with complete governance confidence","evidence_refs":["5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5"],"name":"no_scp_blocks_put_bucket_policy","reason":"SCP 5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5 blocks (complete)","state":"fail"},{"description":"No permission boundary blocks s3:PutBucketPolicy on this edge","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"no_boundary_blocks_put_bucket_policy","reason":"no permission boundary bindings observed","state":"pass"},{"description":"Principal is an attacker-controllable user or role (not a service principal or root)","evidence_refs":["d44a9ba41fdbe4f56c3e294680e6e9628ae5ae78654e3cbc93ff766fbfad5fe0"],"name":"principal_is_actionable","reason":"principal is an attacker-controllable user or role","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"info","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"S3Bucket","provider":"aws","provider_id":"arn:aws:s3:::corp-secrets","region":"-"},"title":"Blocked S3 bucket takeover: arn:aws:iam::11111\u003111111\u0031:user/Alice can call s3:PutBucketPolicy on arn:aws:s3:::corp-secrets","verdict":"blocked"}],"metadata":{"canonical_hash":"2281f371384b8b040553fe36564af5835019c95adaaf69b96944c2b6b763d7b3","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["s3_bucket_takeover"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"s3_bucket_takeover":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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/secrets_blast_radius/fixture_a_validated_non_admin.json b/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_a_validated_non_admin.json index d9cffa7..36a3f47 100644 --- a/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_a_validated_non_admin.json +++ b/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_a_validated_non_admin.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"node_refs":["a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa"],"reasoning_trace":[{"action":"check_principal_has_get_secret_value_permission","inputs":["arn:aws:iam::111111\u003111111:user/Alice","arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_permission_edge_targets_clean_witness","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"clean","result":"PASS","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","result":"PASS","step":5},{"action":"check_kms_key_policy_allows_decrypt","inputs":["arn:aws:iam::111111\u003111111:user/Alice","arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123"],"reason":"secret uses AWS-managed default KMS key (delegates to IAM)","result":"PASS","step":6}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"ffd3334c8b29cb8381d16082d53e46d476b5dc94d44d6ae3cf4d841d2ec8ea32","finding_key":"6e4ca4d9637018114d5f7d5096e9848ae9517e2b3592e0536e4a6c8f507c2c50","pattern_id":"secrets_blast_radius","pattern_title":"Secrets Manager Blast Radius","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; principal is non-admin","required_checks":[{"description":"Principal has a permission edge for secretsmanager:GetSecretValue targeting this secret","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"principal_has_get_secret_value_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge is not a wildcard-resource grant or hyperedge dst (clean witness proves the edge's target)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"permission_edge_targets_clean_witness","reason":"clean witness edge","state":"pass"},{"description":"No SCP blocks secretsmanager:GetSecretValue on this edge with complete governance confidence","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_scp_blocks_get_secret_value","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_boundary_blocks_get_secret_value","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_identity_deny_blocks_get_secret_value","reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","state":"pass"},{"description":"KMS key policy on the secret's encryption key allows kms:Decrypt for the candidate principal (or the secret uses the AWS-managed default key which delegates to IAM)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"kms_key_policy_allows_decrypt_for_principal","reason":"secret uses AWS-managed default KMS key (delegates to IAM)","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"SecretsManagerSecret","provider":"aws","provider_id":"arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123","region":"-"},"title":"Validated secret read: arn:aws:iam::111111\u003111111:user/Alice can call secretsmanager:GetSecretValue on arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123","verdict":"validated"}],"metadata":{"canonical_hash":"405523de861bf2217d49f75a9ca15262c6b186c02bacf3312804aa923df4ea03","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["secrets_blast_radius"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"secrets_blast_radius":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"node_refs":["a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa"],"reasoning_trace":[{"action":"check_principal_has_get_secret_value_permission","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_permission_edge_targets_clean_witness","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"clean","result":"PASS","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","result":"PASS","step":5},{"action":"check_kms_key_policy_allows_decrypt","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123"],"reason":"secret uses AWS-managed default KMS key (delegates to IAM)","result":"PASS","step":6}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"ffd3334c8b29cb8381d16082d53e46d476b5dc94d44d6ae3cf4d841d2ec8ea32","finding_key":"6e4ca4d9637018114d5f7d5096e9848ae9517e2b3592e0536e4a6c8f507c2c50","pattern_id":"secrets_blast_radius","pattern_title":"Secrets Manager Blast Radius","pattern_version":"1.0.0","reasoner_exit_reason":"all checks PASS; principal is non-admin","required_checks":[{"description":"Principal has a permission edge for secretsmanager:GetSecretValue targeting this secret","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"principal_has_get_secret_value_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge is not a wildcard-resource grant or hyperedge dst (clean witness proves the edge's target)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"permission_edge_targets_clean_witness","reason":"clean witness edge","state":"pass"},{"description":"No SCP blocks secretsmanager:GetSecretValue on this edge with complete governance confidence","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_scp_blocks_get_secret_value","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_boundary_blocks_get_secret_value","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_identity_deny_blocks_get_secret_value","reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","state":"pass"},{"description":"KMS key policy on the secret's encryption key allows kms:Decrypt for the candidate principal (or the secret uses the AWS-managed default key which delegates to IAM)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"kms_key_policy_allows_decrypt_for_principal","reason":"secret uses AWS-managed default KMS key (delegates to IAM)","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"high","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"SecretsManagerSecret","provider":"aws","provider_id":"arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123","region":"-"},"title":"Validated secret read: arn:aws:iam::11111\u003111111\u0031:user/Alice can call secretsmanager:GetSecretValue on arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123","verdict":"validated"}],"metadata":{"canonical_hash":"bbc9c11e6e0dbc7bad758fffbabdbf2fe5c1bbecb79e99bc03a0896b103fbf15","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["secrets_blast_radius"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":0,"validated":1}},"reasoner_versions":{"secrets_blast_radius":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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/secrets_blast_radius/fixture_b_blocked_by_scp.json b/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_b_blocked_by_scp.json index 1e5eb2e..ab9a4c9 100644 --- a/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_b_blocked_by_scp.json +++ b/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_b_blocked_by_scp.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"773590\u003869181a5093a333af3daa5927c5ce66a69a47d51488fc619529\u003319759b","edge_id":"5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7","kind":"scp","reason":"constraint 773590\u003869181a5093a333af3daa5927c5ce66a69a47d51488fc619529\u003319759b affects GetSecretValue"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["773590\u003869181a5093a333af3daa5927c5ce66a69a47d51488fc619529\u003319759b"],"edge_constraint_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7:773590\u003869181a5093a333af3daa5927c5ce66a69a47d51488fc619529\u003319759b"],"edge_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"node_refs":["a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa"],"reasoning_trace":[{"action":"check_principal_has_get_secret_value_permission","inputs":["arn:aws:iam::111111\u003111111:user/Alice","arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_permission_edge_targets_clean_witness","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"clean","result":"PASS","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"SCP 773590\u003869181a5093a333af3daa5927c5ce66a69a47d51488fc619529\u003319759b blocks (complete)","result":"FAIL","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","result":"PASS","step":5},{"action":"check_kms_key_policy_allows_decrypt","inputs":["arn:aws:iam::111111\u003111111:user/Alice","arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123"],"reason":"secret uses AWS-managed default KMS key (delegates to IAM)","result":"PASS","step":6}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"23bebd1b82e1f6c279446054f2f41ab1a25121cb135ff4833b9399645921d02b","finding_key":"6e4ca4d9637018114d5f7d5096e9848ae9517e2b3592e0536e4a6c8f507c2c50","pattern_id":"secrets_blast_radius","pattern_title":"Secrets Manager Blast Radius","pattern_version":"1.0.0","reasoner_exit_reason":"SCP blocks secretsmanager:GetSecretValue","required_checks":[{"description":"Principal has a permission edge for secretsmanager:GetSecretValue targeting this secret","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"principal_has_get_secret_value_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge is not a wildcard-resource grant or hyperedge dst (clean witness proves the edge's target)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"permission_edge_targets_clean_witness","reason":"clean witness edge","state":"pass"},{"description":"No SCP blocks secretsmanager:GetSecretValue on this edge with complete governance confidence","evidence_refs":["773590\u003869181a5093a333af3daa5927c5ce66a69a47d51488fc619529\u003319759b"],"name":"no_scp_blocks_get_secret_value","reason":"SCP 773590\u003869181a5093a333af3daa5927c5ce66a69a47d51488fc619529\u003319759b blocks (complete)","state":"fail"},{"description":"No permission boundary blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_boundary_blocks_get_secret_value","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_identity_deny_blocks_get_secret_value","reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","state":"pass"},{"description":"KMS key policy on the secret's encryption key allows kms:Decrypt for the candidate principal (or the secret uses the AWS-managed default key which delegates to IAM)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"kms_key_policy_allows_decrypt_for_principal","reason":"secret uses AWS-managed default KMS key (delegates to IAM)","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"info","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"SecretsManagerSecret","provider":"aws","provider_id":"arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123","region":"-"},"title":"Blocked secret read: arn:aws:iam::111111\u003111111:user/Alice can call secretsmanager:GetSecretValue on arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123","verdict":"blocked"}],"metadata":{"canonical_hash":"2df2ed9ce66e40d06aed58b1b4ec330bbaeb64d4823f772ee15ba965e557f858","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["secrets_blast_radius"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"secrets_blast_radius":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"77359\u003086918\u0031a5093a333af3daa5927c5ce66a69a47d51488fc61952\u003931975\u0039b","edge_id":"5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7","kind":"scp","reason":"constraint 77359\u003086918\u0031a5093a333af3daa5927c5ce66a69a47d51488fc61952\u003931975\u0039b affects GetSecretValue"}],"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":["77359\u003086918\u0031a5093a333af3daa5927c5ce66a69a47d51488fc61952\u003931975\u0039b"],"edge_constraint_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7:77359\u003086918\u0031a5093a333af3daa5927c5ce66a69a47d51488fc61952\u003931975\u0039b"],"edge_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"node_refs":["a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa"],"reasoning_trace":[{"action":"check_principal_has_get_secret_value_permission","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_permission_edge_targets_clean_witness","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"clean","result":"PASS","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"SCP 77359\u003086918\u0031a5093a333af3daa5927c5ce66a69a47d51488fc61952\u003931975\u0039b blocks (complete)","result":"FAIL","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","result":"PASS","step":5},{"action":"check_kms_key_policy_allows_decrypt","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123"],"reason":"secret uses AWS-managed default KMS key (delegates to IAM)","result":"PASS","step":6}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"23bebd1b82e1f6c279446054f2f41ab1a25121cb135ff4833b9399645921d02b","finding_key":"6e4ca4d9637018114d5f7d5096e9848ae9517e2b3592e0536e4a6c8f507c2c50","pattern_id":"secrets_blast_radius","pattern_title":"Secrets Manager Blast Radius","pattern_version":"1.0.0","reasoner_exit_reason":"SCP blocks secretsmanager:GetSecretValue","required_checks":[{"description":"Principal has a permission edge for secretsmanager:GetSecretValue targeting this secret","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"principal_has_get_secret_value_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge is not a wildcard-resource grant or hyperedge dst (clean witness proves the edge's target)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"permission_edge_targets_clean_witness","reason":"clean witness edge","state":"pass"},{"description":"No SCP blocks secretsmanager:GetSecretValue on this edge with complete governance confidence","evidence_refs":["77359\u003086918\u0031a5093a333af3daa5927c5ce66a69a47d51488fc61952\u003931975\u0039b"],"name":"no_scp_blocks_get_secret_value","reason":"SCP 77359\u003086918\u0031a5093a333af3daa5927c5ce66a69a47d51488fc61952\u003931975\u0039b blocks (complete)","state":"fail"},{"description":"No permission boundary blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_boundary_blocks_get_secret_value","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_identity_deny_blocks_get_secret_value","reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","state":"pass"},{"description":"KMS key policy on the secret's encryption key allows kms:Decrypt for the candidate principal (or the secret uses the AWS-managed default key which delegates to IAM)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"kms_key_policy_allows_decrypt_for_principal","reason":"secret uses AWS-managed default KMS key (delegates to IAM)","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"info","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"SecretsManagerSecret","provider":"aws","provider_id":"arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123","region":"-"},"title":"Blocked secret read: arn:aws:iam::11111\u003111111\u0031:user/Alice can call secretsmanager:GetSecretValue on arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123","verdict":"blocked"}],"metadata":{"canonical_hash":"1da2fcb08af824eeb909549850f83bd965d8a24b6db746d12300197369a96a5d","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["secrets_blast_radius"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":1,"inconclusive":0,"precondition_only":0,"validated":0}},"reasoner_versions":{"secrets_blast_radius":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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/secrets_blast_radius/fixture_c_wildcard_inconclusive.json b/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_c_wildcard_inconclusive.json index 1024c2d..042f09b 100644 --- a/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_c_wildcard_inconclusive.json +++ b/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_c_wildcard_inconclusive.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"node_refs":["a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa"],"reasoning_trace":[{"action":"check_principal_has_get_secret_value_permission","inputs":["arn:aws:iam::111111\u003111111:user/Alice","arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_permission_edge_targets_clean_witness","inputs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"reason":"ambiguous","result":"UNKNOWN","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","result":"PASS","step":5},{"action":"check_kms_key_policy_allows_decrypt","inputs":["arn:aws:iam::111111\u003111111:user/Alice","arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123"],"reason":"secret uses AWS-managed default KMS key (delegates to IAM)","result":"PASS","step":6}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"d7c1af9d50405f5f7d985092ae2187b4de5ae8ed57b418ca40ddfaf4759901f7","finding_key":"6e4ca4d9637018114d5f7d5096e9848ae9517e2b3592e0536e4a6c8f507c2c50","pattern_id":"secrets_blast_radius","pattern_title":"Secrets Manager Blast Radius","pattern_version":"1.0.0","reasoner_exit_reason":"permission edge has wildcard resource or hyperedge dst","required_checks":[{"description":"Principal has a permission edge for secretsmanager:GetSecretValue targeting this secret","evidence_refs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"name":"principal_has_get_secret_value_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge is not a wildcard-resource grant or hyperedge dst (clean witness proves the edge's target)","evidence_refs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"name":"permission_edge_targets_clean_witness","reason":"edge traverses wildcard/hyperedge ambiguity","state":"unknown"},{"description":"No SCP blocks secretsmanager:GetSecretValue on this edge with complete governance confidence","evidence_refs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"name":"no_scp_blocks_get_secret_value","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"name":"no_boundary_blocks_get_secret_value","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"name":"no_identity_deny_blocks_get_secret_value","reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","state":"pass"},{"description":"KMS key policy on the secret's encryption key allows kms:Decrypt for the candidate principal (or the secret uses the AWS-managed default key which delegates to IAM)","evidence_refs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"name":"kms_key_policy_allows_decrypt_for_principal","reason":"secret uses AWS-managed default KMS key (delegates to IAM)","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"medium","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"SecretsManagerSecret","provider":"aws","provider_id":"arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123","region":"-"},"title":"Inconclusive secret read: arn:aws:iam::111111\u003111111:user/Alice can call secretsmanager:GetSecretValue on arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123","verdict":"inconclusive"}],"metadata":{"canonical_hash":"48c22c9454efaff9418fe4031fee2df172a6a139eb9b74562d30b6ea48eac510","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["secrets_blast_radius"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"secrets_blast_radius":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"node_refs":["a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa"],"reasoning_trace":[{"action":"check_principal_has_get_secret_value_permission","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_permission_edge_targets_clean_witness","inputs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"reason":"ambiguous","result":"UNKNOWN","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","result":"PASS","step":5},{"action":"check_kms_key_policy_allows_decrypt","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123"],"reason":"secret uses AWS-managed default KMS key (delegates to IAM)","result":"PASS","step":6}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"d7c1af9d50405f5f7d985092ae2187b4de5ae8ed57b418ca40ddfaf4759901f7","finding_key":"6e4ca4d9637018114d5f7d5096e9848ae9517e2b3592e0536e4a6c8f507c2c50","pattern_id":"secrets_blast_radius","pattern_title":"Secrets Manager Blast Radius","pattern_version":"1.0.0","reasoner_exit_reason":"permission edge has wildcard resource or hyperedge dst","required_checks":[{"description":"Principal has a permission edge for secretsmanager:GetSecretValue targeting this secret","evidence_refs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"name":"principal_has_get_secret_value_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge is not a wildcard-resource grant or hyperedge dst (clean witness proves the edge's target)","evidence_refs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"name":"permission_edge_targets_clean_witness","reason":"edge traverses wildcard/hyperedge ambiguity","state":"unknown"},{"description":"No SCP blocks secretsmanager:GetSecretValue on this edge with complete governance confidence","evidence_refs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"name":"no_scp_blocks_get_secret_value","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"name":"no_boundary_blocks_get_secret_value","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"name":"no_identity_deny_blocks_get_secret_value","reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","state":"pass"},{"description":"KMS key policy on the secret's encryption key allows kms:Decrypt for the candidate principal (or the secret uses the AWS-managed default key which delegates to IAM)","evidence_refs":["b1f992a5c025486be64e396a19663ffa4f28e2948fa3486bc1d944a562b13eed"],"name":"kms_key_policy_allows_decrypt_for_principal","reason":"secret uses AWS-managed default KMS key (delegates to IAM)","state":"pass"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"medium","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"SecretsManagerSecret","provider":"aws","provider_id":"arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123","region":"-"},"title":"Inconclusive secret read: arn:aws:iam::11111\u003111111\u0031:user/Alice can call secretsmanager:GetSecretValue on arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123","verdict":"inconclusive"}],"metadata":{"canonical_hash":"c931f843e00322c81624a38506f86295a363e27503395efb95e660db33f264cd","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["secrets_blast_radius"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"secrets_blast_radius":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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/secrets_blast_radius/fixture_d_kms_blocks_precondition_only.json b/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_d_kms_blocks_precondition_only.json index 7f4a513..01b3e28 100644 --- a/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_d_kms_blocks_precondition_only.json +++ b/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_d_kms_blocks_precondition_only.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"951e7e5545c007f8abad41c65dd6da49c324e78203835e2ace0ebd84ee700281","edge_id":"5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7","kind":"kms_key_policy","reason":"no KMS policy Allow statement covers principal"}],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"node_refs":["951e7e5545c007f8abad41c65dd6da49c324e78203835e2ace0ebd84ee700281","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa"],"reasoning_trace":[{"action":"check_principal_has_get_secret_value_permission","inputs":["arn:aws:iam::111111\u003111111:user/Alice","arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_permission_edge_targets_clean_witness","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"clean","result":"PASS","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","result":"PASS","step":5},{"action":"check_kms_key_policy_allows_decrypt","inputs":["arn:aws:iam::111111\u003111111:user/Alice","arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123"],"reason":"no KMS policy Allow statement covers principal","result":"FAIL","step":6}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"de1eaf4e0212269b5372636b9281bc38457d5d6687f01ce7daa675784fc6c9f4","finding_key":"6e4ca4d9637018114d5f7d5096e9848ae9517e2b3592e0536e4a6c8f507c2c50","pattern_id":"secrets_blast_radius","pattern_title":"Secrets Manager Blast Radius","pattern_version":"1.0.0","reasoner_exit_reason":"KMS key policy does not allow kms:Decrypt for principal","required_checks":[{"description":"Principal has a permission edge for secretsmanager:GetSecretValue targeting this secret","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"principal_has_get_secret_value_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge is not a wildcard-resource grant or hyperedge dst (clean witness proves the edge's target)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"permission_edge_targets_clean_witness","reason":"clean witness edge","state":"pass"},{"description":"No SCP blocks secretsmanager:GetSecretValue on this edge with complete governance confidence","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_scp_blocks_get_secret_value","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_boundary_blocks_get_secret_value","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_identity_deny_blocks_get_secret_value","reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","state":"pass"},{"description":"KMS key policy on the secret's encryption key allows kms:Decrypt for the candidate principal (or the secret uses the AWS-managed default key which delegates to IAM)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"kms_key_policy_allows_decrypt_for_principal","reason":"no KMS policy Allow statement covers principal","state":"fail"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"medium","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"SecretsManagerSecret","provider":"aws","provider_id":"arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123","region":"-"},"title":"Precondition-only secret read: arn:aws:iam::111111\u003111111:user/Alice can call secretsmanager:GetSecretValue on arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123","verdict":"precondition_only"}],"metadata":{"canonical_hash":"886202cb26e0bed7e6c251aac084dd41def3e87c0e47473f3dfa63902b2665ee","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["secrets_blast_radius"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":1,"validated":0}},"reasoner_versions":{"secrets_blast_radius":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file +{"findings":[{"assumptions":[],"blockers_observed":[{"constraint_id":"951e7e5545c007f8abad41c65dd6da49c324e78203835e2ace0ebd84ee700281","edge_id":"5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7","kind":"kms_key_policy","reason":"no KMS policy Allow statement covers principal"}],"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":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"node_refs":["951e7e5545c007f8abad41c65dd6da49c324e78203835e2ace0ebd84ee700281","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa"],"reasoning_trace":[{"action":"check_principal_has_get_secret_value_permission","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_permission_edge_targets_clean_witness","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"clean","result":"PASS","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","result":"PASS","step":5},{"action":"check_kms_key_policy_allows_decrypt","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123"],"reason":"no KMS policy Allow statement covers principal","result":"FAIL","step":6}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"de1eaf4e0212269b5372636b9281bc38457d5d6687f01ce7daa675784fc6c9f4","finding_key":"6e4ca4d9637018114d5f7d5096e9848ae9517e2b3592e0536e4a6c8f507c2c50","pattern_id":"secrets_blast_radius","pattern_title":"Secrets Manager Blast Radius","pattern_version":"1.0.0","reasoner_exit_reason":"KMS key policy does not allow kms:Decrypt for principal","required_checks":[{"description":"Principal has a permission edge for secretsmanager:GetSecretValue targeting this secret","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"principal_has_get_secret_value_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge is not a wildcard-resource grant or hyperedge dst (clean witness proves the edge's target)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"permission_edge_targets_clean_witness","reason":"clean witness edge","state":"pass"},{"description":"No SCP blocks secretsmanager:GetSecretValue on this edge with complete governance confidence","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_scp_blocks_get_secret_value","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_boundary_blocks_get_secret_value","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_identity_deny_blocks_get_secret_value","reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","state":"pass"},{"description":"KMS key policy on the secret's encryption key allows kms:Decrypt for the candidate principal (or the secret uses the AWS-managed default key which delegates to IAM)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"kms_key_policy_allows_decrypt_for_principal","reason":"no KMS policy Allow statement covers principal","state":"fail"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"medium","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"SecretsManagerSecret","provider":"aws","provider_id":"arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123","region":"-"},"title":"Precondition-only secret read: arn:aws:iam::11111\u003111111\u0031:user/Alice can call secretsmanager:GetSecretValue on arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123","verdict":"precondition_only"}],"metadata":{"canonical_hash":"911745399a00cdbe4ba1a1c4180b8b5e0a9a6cee8b2ffff8a725ab48e77ba481","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["secrets_blast_radius"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":0,"precondition_only":1,"validated":0}},"reasoner_versions":{"secrets_blast_radius":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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/secrets_blast_radius/fixture_e_kms_conditions_inconclusive.json b/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_e_kms_conditions_inconclusive.json index a2a8ec6..a008e45 100644 --- a/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_e_kms_conditions_inconclusive.json +++ b/tests/fixtures/expected_output/findings/secrets_blast_radius/fixture_e_kms_conditions_inconclusive.json @@ -1 +1 @@ -{"findings":[{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"node_refs":["951e7e5545c007f8abad41c65dd6da49c324e78203835e2ace0ebd84ee700281","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa"],"reasoning_trace":[{"action":"check_principal_has_get_secret_value_permission","inputs":["arn:aws:iam::111111\u003111111:user/Alice","arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_permission_edge_targets_clean_witness","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"clean","result":"PASS","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","result":"PASS","step":5},{"action":"check_kms_key_policy_allows_decrypt","inputs":["arn:aws:iam::111111\u003111111:user/Alice","arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123"],"reason":"matching Allow statement has Condition block","result":"UNKNOWN","step":6}],"statement_digests":["111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111"],"statement_sources":{"111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031111":["arn:aws:iam::111111\u003111111:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"629695da7a32fc8fa6607ce88192c51300b7dc7802aa84c75f73c1ade61298d2","finding_key":"6e4ca4d9637018114d5f7d5096e9848ae9517e2b3592e0536e4a6c8f507c2c50","pattern_id":"secrets_blast_radius","pattern_title":"Secrets Manager Blast Radius","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: kms_key_policy_allows_decrypt_for_principal","required_checks":[{"description":"Principal has a permission edge for secretsmanager:GetSecretValue targeting this secret","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"principal_has_get_secret_value_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge is not a wildcard-resource grant or hyperedge dst (clean witness proves the edge's target)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"permission_edge_targets_clean_witness","reason":"clean witness edge","state":"pass"},{"description":"No SCP blocks secretsmanager:GetSecretValue on this edge with complete governance confidence","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_scp_blocks_get_secret_value","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_boundary_blocks_get_secret_value","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_identity_deny_blocks_get_secret_value","reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","state":"pass"},{"description":"KMS key policy on the secret's encryption key allows kms:Decrypt for the candidate principal (or the secret uses the AWS-managed default key which delegates to IAM)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"kms_key_policy_allows_decrypt_for_principal","reason":"matching Allow statement has Condition block","state":"unknown"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"medium","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},"target":{"node_type":"SecretsManagerSecret","provider":"aws","provider_id":"arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123","region":"-"},"title":"Inconclusive secret read: arn:aws:iam::111111\u003111111:user/Alice can call secretsmanager:GetSecretValue on arn:aws:secretsmanager:us-east-1:111111\u003111111:secret:prod/db-password-abc123","verdict":"inconclusive"}],"metadata":{"canonical_hash":"f1997c38ec5e4ba7edf06256076a3c950e6a32324d2b3d5298ef2031d63adec3","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["secrets_blast_radius"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"secrets_blast_radius":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","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":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"node_refs":["951e7e5545c007f8abad41c65dd6da49c324e78203835e2ace0ebd84ee700281","a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa"],"reasoning_trace":[{"action":"check_principal_has_get_secret_value_permission","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123"],"reason":"permission edge witnessed","result":"PASS","step":1},{"action":"check_permission_edge_targets_clean_witness","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"clean","result":"PASS","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","result":"PASS","step":5},{"action":"check_kms_key_policy_allows_decrypt","inputs":["arn:aws:iam::11111\u003111111\u0031:user/Alice","arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123"],"reason":"matching Allow statement has Condition block","result":"UNKNOWN","step":6}],"statement_digests":["11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111"],"statement_sources":{"11111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u00311111":["arn:aws:iam::11111\u003111111\u0031:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"629695da7a32fc8fa6607ce88192c51300b7dc7802aa84c75f73c1ade61298d2","finding_key":"6e4ca4d9637018114d5f7d5096e9848ae9517e2b3592e0536e4a6c8f507c2c50","pattern_id":"secrets_blast_radius","pattern_title":"Secrets Manager Blast Radius","pattern_version":"1.0.0","reasoner_exit_reason":"check(s) UNKNOWN: kms_key_policy_allows_decrypt_for_principal","required_checks":[{"description":"Principal has a permission edge for secretsmanager:GetSecretValue targeting this secret","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"principal_has_get_secret_value_permission","reason":"permission edge witnessed","state":"pass"},{"description":"Permission edge is not a wildcard-resource grant or hyperedge dst (clean witness proves the edge's target)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"permission_edge_targets_clean_witness","reason":"clean witness edge","state":"pass"},{"description":"No SCP blocks secretsmanager:GetSecretValue on this edge with complete governance confidence","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_scp_blocks_get_secret_value","reason":"no SCP bindings observed","state":"pass"},{"description":"No permission boundary blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_boundary_blocks_get_secret_value","reason":"no permission boundary bindings observed","state":"pass"},{"description":"No identity-policy Deny blocks secretsmanager:GetSecretValue on this edge","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"no_identity_deny_blocks_get_secret_value","reason":"no identity policy Deny bindings observed on secretsmanager:GetSecretValue witness edge","state":"pass"},{"description":"KMS key policy on the secret's encryption key allows kms:Decrypt for the candidate principal (or the secret uses the AWS-managed default key which delegates to IAM)","evidence_refs":["5149bde679d7427c20a7e6c46067c5eea79c1447f18564d3348a3badca1e6ff7"],"name":"kms_key_policy_allows_decrypt_for_principal","reason":"matching Allow statement has Condition block","state":"unknown"}],"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","severity":"medium","source":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::11111\u003111111\u0031:user/Alice","region":"-"},"target":{"node_type":"SecretsManagerSecret","provider":"aws","provider_id":"arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123","region":"-"},"title":"Inconclusive secret read: arn:aws:iam::11111\u003111111\u0031:user/Alice can call secretsmanager:GetSecretValue on arn:aws:secretsmanager:us-east-1:11111\u003111111\u0031:secret:prod/db-password-abc123","verdict":"inconclusive"}],"metadata":{"canonical_hash":"d54c6a12d1e9e4535bbac715fa2c80e25816e06152106fe779c0cf4d7e811784","collector":"iamscope","collector_version":"0.2.0","findings_count":1,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","reasoners_run":["secrets_blast_radius"],"reasoners_skipped":{},"reasoning_duration_seconds":0.0,"reasoning_timestamp":"2026-01-01T00:00:00Z","verdict_breakdown":{"blocked":0,"inconclusive":1,"precondition_only":0,"validated":0}},"reasoner_versions":{"secrets_blast_radius":"1.0.0"},"scenario_hash":"ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss","schema_version":"1.0","source_tool":"iamscope","source_tool_version":"0.2.0"} \ No newline at end of file diff --git a/tests/test_findings_json.py b/tests/test_findings_json.py index f8f21d4..e561099 100644 --- a/tests/test_findings_json.py +++ b/tests/test_findings_json.py @@ -351,6 +351,7 @@ def test_finding_fields(self) -> None: assert set(finding.keys()) == { "assumptions", "blockers_observed", + "collection_context", "evidence", "finding_id", "finding_key", @@ -374,6 +375,28 @@ def test_finding_fields(self) -> None: assert finding["verdict"] == "validated" assert finding["severity"] == SEVERITY_CRITICAL + def test_finding_collection_context_fields(self) -> None: + d = self._emit() + context = d["findings"][0]["collection_context"] + assert set(context.keys()) == { + "affected_accounts", + "coverage_notes", + "graph_collection_complete", + "has_collection_failures", + "has_policy_parse_failures", + "related_collection_failures", + "related_policy_parse_failures", + } + assert 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": [], + } + def test_evidence_fields(self) -> None: d = self._emit() evidence = d["findings"][0]["evidence"] @@ -397,6 +420,193 @@ def test_reasoning_trace_step_order_preserved(self) -> None: assert steps == list(range(1, len(steps) + 1)) +# --------------------------------------------------------------------------- +# Collection context +# --------------------------------------------------------------------------- + + +class TestCollectionContext: + """Per-finding collection context makes partial graph state visible.""" + + def _finding( + self, + *, + source_arn: str | None = None, + target_arn: str | None = None, + verdict: Verdict = Verdict.VALIDATED, + ) -> Finding: + return _direct_finding( + source_arn=source_arn or f"arn:aws:iam::{_TARGET_ACCOUNT}:user/Alice", + target_arn=target_arn or f"arn:aws:iam::{_TARGET_ACCOUNT}:role/Target", + verdict=verdict, + ) + + def _emit( + self, + *, + collection_context_source: dict[str, Any] | None = None, + finding: Finding | None = None, + ) -> dict[str, Any]: + b, _ = emit_findings( + [finding or self._finding()], + scenario_hash="deadbeef" * 8, + reasoners_used=_direct_reasoners_used(), + collection_context_source=collection_context_source, + ) + return json.loads(b) + + def _collection_failure(self, account_id: str, *, message: str = "collector failed") -> dict[str, str]: + return { + "account_id": account_id, + "collector": "lambda", + "error_class": "ClientError", + "error_message": message, + "region": "us-east-1", + } + + def _parse_failure(self, source_arn: str, *, message: str = "policy parse failed") -> dict[str, str]: + return { + "error_class": "JSONDecodeError", + "error_message": message, + "failure_kind": "json_decode_error", + "parser": "permission_policy", + "policy_arn": "", + "policy_name": "BrokenInline", + "policy_source": "inline", + "source_arn": source_arn, + } + + def test_happy_path_emits_complete_context(self) -> None: + context = self._emit()["findings"][0]["collection_context"] + + assert context["graph_collection_complete"] is True + assert context["has_collection_failures"] is False + assert context["has_policy_parse_failures"] is False + assert context["affected_accounts"] == [] + assert context["related_collection_failures"] == [] + assert context["related_policy_parse_failures"] == [] + assert context["coverage_notes"] == [] + + def test_global_partial_graph_adds_note_without_unrelated_failure(self) -> None: + context = self._emit( + collection_context_source={ + "collection_failures": [self._collection_failure(_EXTERNAL_ACCOUNT)], + "policy_parse_failures": [], + } + )["findings"][0]["collection_context"] + + assert context["graph_collection_complete"] is False + assert context["has_collection_failures"] is True + assert context["related_collection_failures"] == [] + assert context["coverage_notes"] == ["collection was partial; no direct account match found for this finding"] + + def test_source_account_collection_failure_is_related(self) -> None: + failure = self._collection_failure(_TARGET_ACCOUNT) + context = self._emit( + collection_context_source={ + "collection_failures": [failure], + "policy_parse_failures": [], + } + )["findings"][0]["collection_context"] + + assert context["graph_collection_complete"] is False + assert context["affected_accounts"] == [_TARGET_ACCOUNT] + assert context["related_collection_failures"] == [failure] + assert context["coverage_notes"] == [] + + def test_target_account_collection_failure_is_related(self) -> None: + source = f"arn:aws:iam::{_EXTERNAL_ACCOUNT}:user/ExternalAlice" + target = f"arn:aws:iam::{_TARGET_ACCOUNT}:role/Target" + failure = self._collection_failure(_TARGET_ACCOUNT) + context = self._emit( + finding=self._finding(source_arn=source, target_arn=target), + collection_context_source={ + "collection_failures": [failure], + "policy_parse_failures": [], + }, + )["findings"][0]["collection_context"] + + assert context["affected_accounts"] == [_TARGET_ACCOUNT] + assert context["related_collection_failures"] == [failure] + + def test_policy_parse_failure_exact_source_is_related(self) -> None: + source = f"arn:aws:iam::{_TARGET_ACCOUNT}:user/Alice" + failure = self._parse_failure(source) + context = self._emit( + finding=self._finding(source_arn=source), + collection_context_source={ + "collection_failures": [], + "policy_parse_failures": [failure], + }, + )["findings"][0]["collection_context"] + + assert context["graph_collection_complete"] is False + assert context["has_policy_parse_failures"] is True + assert context["affected_accounts"] == [_TARGET_ACCOUNT] + assert context["related_policy_parse_failures"] == [failure] + assert context["coverage_notes"] == [] + + def test_policy_parse_failure_same_account_not_exact_source_is_noted(self) -> None: + failure = self._parse_failure(f"arn:aws:iam::{_TARGET_ACCOUNT}:role/OtherRole") + context = self._emit( + collection_context_source={ + "collection_failures": [], + "policy_parse_failures": [failure], + } + )["findings"][0]["collection_context"] + + assert context["related_policy_parse_failures"] == [failure] + assert context["coverage_notes"] == ["policy parse failure related by account, not exact source/target ARN"] + + def test_collection_context_is_deterministic(self) -> None: + context_source = { + "collection_failures": [self._collection_failure(_TARGET_ACCOUNT)], + "policy_parse_failures": [self._parse_failure(f"arn:aws:iam::{_TARGET_ACCOUNT}:role/OtherRole")], + } + + b1, h1 = emit_findings( + [self._finding()], + scenario_hash="deadbeef" * 8, + reasoners_used=_direct_reasoners_used(), + collection_context_source=context_source, + ) + b2, h2 = emit_findings( + [self._finding()], + scenario_hash="deadbeef" * 8, + reasoners_used=_direct_reasoners_used(), + collection_context_source=context_source, + ) + + assert b1 == b2 + assert h1 == h2 + + def test_validated_finding_remains_validated_with_partial_context(self) -> None: + context = self._emit( + finding=self._finding(verdict=Verdict.VALIDATED), + collection_context_source={ + "collection_failures": [self._collection_failure(_TARGET_ACCOUNT)], + "policy_parse_failures": [], + }, + )["findings"][0] + + assert context["verdict"] == "validated" + assert context["collection_context"]["graph_collection_complete"] is False + + def test_context_sanitizes_multiline_error_messages(self) -> None: + context = self._emit( + collection_context_source={ + "collection_failures": [ + self._collection_failure(_TARGET_ACCOUNT, message="line one\nTraceback details") + ], + "policy_parse_failures": [], + } + )["findings"][0]["collection_context"] + + message = context["related_collection_failures"][0]["error_message"] + assert "\n" not in message + assert message == "line one Traceback details" + + # --------------------------------------------------------------------------- # Canonical JSON properties # --------------------------------------------------------------------------- diff --git a/tests/test_replay_findings.py b/tests/test_replay_findings.py index 9534355..aea74e0 100644 --- a/tests/test_replay_findings.py +++ b/tests/test_replay_findings.py @@ -119,7 +119,12 @@ def _admin_grant(role: Node) -> Edge: ) -def _write_frozen_chain(tmp_path: Path) -> tuple[Path, Path, str, str]: +def _write_frozen_chain( + tmp_path: Path, + *, + collection_failures: list[dict] | None = None, + policy_parse_failures: list[dict] | None = None, +) -> tuple[Path, Path, str, str]: alice = _node(_ALICE, NODE_TYPE_IAM_USER) devops = _node(_DEVOPS, NODE_TYPE_IAM_ROLE) admin = _node(_ADMIN, NODE_TYPE_IAM_ROLE) @@ -133,7 +138,11 @@ def _write_frozen_chain(tmp_path: Path) -> tuple[Path, Path, str, str]: edges=[perm_1, trust_1, perm_2, trust_2, admin_grant], constraints=[], edge_constraints=[], - metadata=ScenarioMetadata(collection_timestamp="2026-01-01T00:00:00Z"), + metadata=ScenarioMetadata( + collection_timestamp="2026-01-01T00:00:00Z", + collection_failures=collection_failures or [], + policy_parse_failures=policy_parse_failures or [], + ), ) scenario_path = tmp_path / "scenario.json" scenario_path.write_bytes(scenario_bytes) @@ -202,6 +211,37 @@ def test_replay_overlay_mutates_finding_on_frozen_scenario(tmp_path: Path) -> No assert "p-runtime-deny" in changed.evidence.constraint_refs +def test_replay_attaches_collection_context_from_scenario_metadata(tmp_path: Path) -> None: + scenario_path, binding_path, _, _ = _write_frozen_chain( + tmp_path, + collection_failures=[ + { + "account_id": _ACCOUNT, + "collector": "lambda", + "error_class": "ClientError", + "error_message": "collection partial", + "region": "us-east-1", + } + ], + ) + + replay_result = run_reasoners_on_frozen_artifacts( + scenario_path=scenario_path, + binding_metadata_path=binding_path, + probe_overlay_path=None, + reasoner_instances=(AssumeRoleChainReasoner(),), + reasoning_timestamp="2026-01-01T00:00:00Z", + ) + findings_json = json.loads(replay_result.findings_bytes) + context = findings_json["findings"][0]["collection_context"] + + assert replay_result.findings[0].verdict.value == "validated" + assert context["graph_collection_complete"] is False + assert context["has_collection_failures"] is True + assert context["affected_accounts"] == [_ACCOUNT] + assert context["related_collection_failures"][0]["account_id"] == _ACCOUNT + + def test_replay_cli_writes_findings_with_overlay(tmp_path: Path) -> None: from iamscope.cli import main