diff --git a/docs/specs/case-sensitive-arn-id-collision-design.md b/docs/specs/case-sensitive-arn-id-collision-design.md index 557d90e..42cf32b 100644 --- a/docs/specs/case-sensitive-arn-id-collision-design.md +++ b/docs/specs/case-sensitive-arn-id-collision-design.md @@ -92,11 +92,11 @@ mistakenly compare v2 and v3 IDs as if they were the same identifier family. ## Candidate Fix -Introduce a new deterministic ID algorithm version, tentatively: +Implemented in the v3 migration slice as: `sha256_null_separated_v3_case_sensitive_provider_ids` -The candidate v3 behavior should use field-aware canonicalization: +The v3 behavior uses field-aware canonicalization: - keep provider and structural type fields normalized where they are intended to be case-insensitive, such as `provider`, `node_type`, `edge_type`, and @@ -105,13 +105,19 @@ The candidate v3 behavior should use field-aware canonicalization: `provider_id`, `src_provider_id`, and `dst_provider_id`; - keep feature canonicalization deterministic and unchanged unless a separate review finds a feature-level case collision; -- review `constraint_id` separately before deciding whether it should stay on - the existing canonicalization behavior or move to a field-aware formula. +- leaves `constraint_id` on the existing canonicalization behavior pending + separate review. The code fix should avoid a broad "never lowercase anything" change. The safer boundary is to make each deterministic ID formula choose the canonicalization rule for each field it owns. +v3 is not ID-compatible with v2. Cross-version comparisons must not treat +`node_id` or `edge_id` values as the same identifier family unless a future +explicit migration/remapping layer is added. ARF-RT, probe overlays, +observation logs, and `findings_diff` consumers should gate ID comparison on +matching `id_algorithm` values. + ## Migration and Versioning Plan 1. Add focused regression tests that pin the current collision as design diff --git a/iamscope/constants.py b/iamscope/constants.py index 31f7c01..185366c 100644 --- a/iamscope/constants.py +++ b/iamscope/constants.py @@ -13,7 +13,7 @@ # --------------------------------------------------------------------------- # Deterministic ID algorithm version # --------------------------------------------------------------------------- -ID_ALGORITHM: str = "sha256_null_separated_v2" +ID_ALGORITHM: str = "sha256_null_separated_v3_case_sensitive_provider_ids" # --------------------------------------------------------------------------- # confidence_q mapping (int 0..1000) diff --git a/iamscope/identity/deterministic_ids.py b/iamscope/identity/deterministic_ids.py index 569703f..1a8e6a5 100644 --- a/iamscope/identity/deterministic_ids.py +++ b/iamscope/identity/deterministic_ids.py @@ -1,7 +1,9 @@ """Deterministic ID generation for IAMScope. -Algorithm: sha256_null_separated_v2 (bumped from v1 in v0.2.37) -- Fields are stripped and lowercased +Algorithm: sha256_null_separated_v3_case_sensitive_provider_ids +- Fields are stripped +- Structural fields are lowercased by the ID formula that owns them +- Provider-owned identity fields preserve exact case - Joined with NULL byte separator (\\x00) - SHA-256 hashed - Full hex digest (64 chars) @@ -44,22 +46,49 @@ produced the edge_ids in a given scenario. Consumers should read that field and gate any edge-id comparison on equality. -Pinned formulas (v2): -- node_id = canonical_id(provider, node_type, provider_id) -- edge_id = canonical_id(edge_type, src_provider_id, - dst_provider_id, region, - features_digest) +Pinned formulas (v3): +- node_id = hash(provider.lower(), node_type.lower(), + case-preserved provider_id) +- edge_id = hash(edge_type.lower(), + case-preserved src_provider_id, + case-preserved dst_provider_id, + region.lower(), + case-preserved features_digest) where features_digest is canonical_json_bytes(features).decode("utf-8") - constraint_id = canonical_id(provider, constraint_type, scope_type, scope_id, policy_id, statement_id) + intentionally remains on the legacy lowercase + canonicalization path pending separate review `node_id` and `constraint_id` formulas are UNCHANGED between v1 and v2 — only `edge_id` gained the features_digest field. A pure v1→v2 re-emit on the same inputs produces identical node and constraint IDs, and v2-only-different edge IDs for edges whose features participated in the v1 collision. + +### v2 → v3 migration (case-sensitive provider IDs) + +v2 used `canonical_id` for every deterministic ID formula, which +lowercased every string field before hashing. AWS IAM role and user +names are case-sensitive, so case-distinct provider IDs such as +`arn:aws:iam::000000000000:role/CaseRole` and +`arn:aws:iam::000000000000:role/caserole` collided under `node_id`. +`edge_id` had the same class of collision for source and destination +provider IDs. + +v3 makes `node_id` and `edge_id` field-aware: +- structural fields (`provider`, `node_type`, `edge_type`, `region`) + are stripped and lowercased; +- provider-owned identity fields (`provider_id`, `src_provider_id`, + `dst_provider_id`) are stripped but preserve case; +- `features_digest` is stripped but otherwise preserved because it is + already canonical JSON bytes decoded as UTF-8. + +`constraint_id` intentionally remains on the legacy lowercase +canonicalization path pending separate constraint-field review. +v2 and v3 scenarios are NOT node-id- or edge-id-comparable. """ import hashlib @@ -101,10 +130,33 @@ def canonical_id(*fields: str) -> str: return hashlib.sha256(canonical.encode("utf-8")).hexdigest() +def _validate_id_field(field: str, index: int) -> str: + if not isinstance(field, str): + raise ValueError(f"canonical_id field {index} must be a string, got {type(field).__name__}: {field!r}") + stripped = field.strip() + if not stripped: + raise ValueError(f"canonical_id field {index} is empty after stripping: {field!r}") + return stripped + + +def _hash_processed_fields(fields: list[str]) -> str: + canonical = "\x00".join(fields) + return hashlib.sha256(canonical.encode("utf-8")).hexdigest() + + +def _structural_field(field: str, index: int) -> str: + return _validate_id_field(field, index).lower() + + +def _provider_identity_field(field: str, index: int) -> str: + return _validate_id_field(field, index) + + def node_id(provider: str, node_type: str, provider_id: str) -> str: """Compute deterministic node ID. - Formula: canonical_id(provider, node_type, provider_id) + Formula (v3): sha256(provider.lower(), node_type.lower(), + case-preserved provider_id) Args: provider: Provider string (e.g., "aws"). @@ -114,7 +166,13 @@ def node_id(provider: str, node_type: str, provider_id: str) -> str: Returns: 64-character hex string. """ - return canonical_id(provider, node_type, provider_id) + return _hash_processed_fields( + [ + _structural_field(provider, 0), + _structural_field(node_type, 1), + _provider_identity_field(provider_id, 2), + ] + ) def edge_id( @@ -124,10 +182,12 @@ def edge_id( region: str, features_digest: str, ) -> str: - """Compute deterministic edge ID (sha256_null_separated_v2). + """Compute deterministic edge ID (sha256_null_separated_v3). - Formula: canonical_id(edge_type, src_provider_id, dst_provider_id, - region, features_digest) + Formula (v3): sha256(edge_type.lower(), case-preserved + src_provider_id, case-preserved + dst_provider_id, region.lower(), + features_digest) v2 (v0.2.37+) adds `features_digest` as a required fifth field — see module docstring for the v1→v2 migration rationale (reviewer @@ -153,7 +213,15 @@ def edge_id( Returns: 64-character hex string. """ - return canonical_id(edge_type, src_provider_id, dst_provider_id, region, features_digest) + return _hash_processed_fields( + [ + _structural_field(edge_type, 0), + _provider_identity_field(src_provider_id, 1), + _provider_identity_field(dst_provider_id, 2), + _structural_field(region, 3), + _provider_identity_field(features_digest, 4), + ] + ) def constraint_id( diff --git a/tests/conftest.py b/tests/conftest.py index b18a235..98d7a54 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -111,7 +111,7 @@ def minimal_metadata() -> ScenarioMetadata: return ScenarioMetadata( collector="iamscope", collector_version="0.2.0", - id_algorithm="sha256_null_separated_v2", + id_algorithm="sha256_null_separated_v3_case_sensitive_provider_ids", org_id="o-testorg", accounts_collected=1, accounts_skipped=0, diff --git a/tests/fixtures/demo/complex_replay_subset_passrole_lambda/expected_rows.json b/tests/fixtures/demo/complex_replay_subset_passrole_lambda/expected_rows.json index fd92688..e8b7fe6 100644 --- a/tests/fixtures/demo/complex_replay_subset_passrole_lambda/expected_rows.json +++ b/tests/fixtures/demo/complex_replay_subset_passrole_lambda/expected_rows.json @@ -2,8 +2,8 @@ "expected_rows": [ { "expected_generation": "replayed_by_existing_passrole_lambda_reasoner", - "finding_id": "bb1a6460e5e4002f2af318a0d5548ef9556a5908066cf8dfc560de86d6a39346", - "finding_key": "c0753d2cd79b37bafa93ea4139cdab85460fca677b89620ebb6d86d1e7bc5d87", + "finding_id": "62317bed606ab926df95ef1aecbcb1af51b738539bd0be385fed67c8fa3ece9f", + "finding_key": "5e00684bab5af59abccb3eb836889e106c9480f90cab77938aecc7f4ebbeda8f", "pattern_id": "passrole_lambda", "row_id": "subset-row-allowed-passrole-lambda", "severity": "high", @@ -24,7 +24,7 @@ "no composite benchmark score", "no pass/fail benchmark label" ], - "scenario_hash": "9d8c55160555d53c2864b5adc53922f930ac90215ef7db4f7cd8d5b630a7252f", + "scenario_hash": "536d02868ab7c610a800105e94482725142b0f70c28cdc975091d0b384714926", "schema_version": "complex_passrole_lambda_replay_subset_expected_rows.v1", "static_only_rows": [ { diff --git a/tests/fixtures/demo/complex_replay_subset_passrole_lambda/scenario.json b/tests/fixtures/demo/complex_replay_subset_passrole_lambda/scenario.json index d364c47..2e21e19 100644 --- a/tests/fixtures/demo/complex_replay_subset_passrole_lambda/scenario.json +++ b/tests/fixtures/demo/complex_replay_subset_passrole_lambda/scenario.json @@ -1 +1 @@ -{"constraints":[],"edge_constraints":[],"edges":[{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","region":"-"},"edge_id":"060d02a2081b082f55f48abae0fdc2e03eab8449403d7e2958be80af885e2e51","edge_type":"lambda:CreateFunction_permission","features":{"action_matched_via":"exact","allow_controls":[{"control_type":"PERMISSION","digest":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","policy_arn":"arn:aws:iam::000000000000:policy/ReplaySubsetPolicy","statement_index":0,"summary":"synthetic lambda:CreateFunction grant for replay subset"}],"effect":"Allow","has_conditions":false,"is_wildcard_resource":false,"layer":"permission","policy_arn":"arn:aws:iam::000000000000:policy/ReplaySubsetPolicy","policy_name":"ReplaySubsetPolicy","raw_conditions":{},"resource_pattern":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","statement_index":0},"region":"-","src":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::000000000000:user/ReplaySubsetMissingPassRole","region":"-"}},{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","region":"-"},"edge_id":"120b96c7afc792a560ef7bc0105bef906ab1c0e582f9be09a259615bd791fd9b","edge_type":"sts:AssumeRole_trust","features":{"allow_controls":[{"control_type":"TRUST","digest":"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd","policy_arn":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","statement_index":0,"summary":"synthetic trust for lambda.amazonaws.com"}],"effect":"Allow","has_conditions":false,"is_wildcard_principal":false,"layer":"trust","principal_type":"Service","raw_conditions":{},"statement_index":0},"region":"-","src":{"node_type":"AWSService","provider":"aws","provider_id":"lambda.amazonaws.com","region":"-"}},{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","region":"-"},"edge_id":"1db5c2299b5ce9981cc4e61a4263779104e37554075ccc2fca9b8f35d415b0bd","edge_type":"iam:PassRole_permission","features":{"action_matched_via":"exact","allow_controls":[{"control_type":"PERMISSION","digest":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","policy_arn":"arn:aws:iam::000000000000:policy/ReplaySubsetPolicy","statement_index":1,"summary":"synthetic iam:PassRole grant for replay subset"}],"effect":"Allow","has_conditions":false,"is_wildcard_resource":false,"layer":"permission","policy_arn":"arn:aws:iam::000000000000:policy/ReplaySubsetPolicy","policy_name":"ReplaySubsetPolicy","raw_conditions":{},"resource_pattern":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","statement_index":1},"region":"-","src":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::000000000000:user/ReplaySubsetAllowed","region":"-"}},{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","region":"-"},"edge_id":"7c176d6c2b7181c890ddce1b112b0c250091a16a0c7d908db7dc7744cab946ed","edge_type":"lambda:CreateFunction_permission","features":{"action_matched_via":"exact","allow_controls":[{"control_type":"PERMISSION","digest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","policy_arn":"arn:aws:iam::000000000000:policy/ReplaySubsetPolicy","statement_index":0,"summary":"synthetic lambda:CreateFunction grant for replay subset"}],"effect":"Allow","has_conditions":false,"is_wildcard_resource":false,"layer":"permission","policy_arn":"arn:aws:iam::000000000000:policy/ReplaySubsetPolicy","policy_name":"ReplaySubsetPolicy","raw_conditions":{},"resource_pattern":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","statement_index":0},"region":"-","src":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::000000000000:user/ReplaySubsetAllowed","region":"-"}}],"metadata":{"accounts_collected":1,"accounts_skipped":0,"canonical_hash":"9d8c55160555d53c2864b5adc53922f930ac90215ef7db4f7cd8d5b630a7252f","collection_duration_seconds":0.0,"collection_failures":[],"collection_timestamp":"2026-06-03T00:00:00Z","collector":"iamscope-replay-subset-fixture","collector_version":"0.0.0-local-fixture","graph_stats":{"fixture_id":"complex_replay_subset_passrole_lambda_001","local_only":true},"hash_scope":"canonical_hash excludes metadata block","id_algorithm":"sha256_null_separated_v2","noise_filter":{},"org_id":""},"nodes":[{"node_id":"10493adf436117443c09f96d89f1cf5f5f39b75cd49cae7edc3ecf8147950e5c","node_type":"IAMUser","properties":{"account_id":"000000000000","fixture_role":"replay_subset_missing_passrole_source"},"provider":"aws","provider_id":"arn:aws:iam::000000000000:user/ReplaySubsetMissingPassRole","region":"-"},{"node_id":"6f3c9ca807bb2e2425e19bb3280174d12e3b3cccdc7b744058811a342f3297d2","node_type":"IAMUser","properties":{"account_id":"000000000000","fixture_role":"replay_subset_allowed_source"},"provider":"aws","provider_id":"arn:aws:iam::000000000000:user/ReplaySubsetAllowed","region":"-"},{"node_id":"dc053712755ec0319f0a3231d46d2048f5b8b14378a198f58bd2decebff48fb5","node_type":"IAMRole","properties":{"account_id":"000000000000","fixture_role":"replay_subset_target_role"},"provider":"aws","provider_id":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","region":"-"},{"node_id":"dd76780b75405f72452650208a1129ca4dc959c43bcea761cbc3686e576d16d0","node_type":"AWSService","properties":{"service":"lambda"},"provider":"aws","provider_id":"lambda.amazonaws.com","region":"-"}],"objectives":[],"observations":[]} \ No newline at end of file +{"constraints":[],"edge_constraints":[],"edges":[{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","region":"-"},"edge_id":"1579ef00649e31d4d2ff230d0dfc8232b0b58f28a82ba5c1d864fce28ddb8d31","edge_type":"sts:AssumeRole_trust","features":{"allow_controls":[{"control_type":"TRUST","digest":"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd","policy_arn":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","statement_index":0,"summary":"synthetic trust for lambda.amazonaws.com"}],"effect":"Allow","has_conditions":false,"is_wildcard_principal":false,"layer":"trust","principal_type":"Service","raw_conditions":{},"statement_index":0},"region":"-","src":{"node_type":"AWSService","provider":"aws","provider_id":"lambda.amazonaws.com","region":"-"}},{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","region":"-"},"edge_id":"62d7539a2975a3756e40af05f5dca002f09851daee19c1b317ce9a2bf0f83819","edge_type":"lambda:CreateFunction_permission","features":{"action_matched_via":"exact","allow_controls":[{"control_type":"PERMISSION","digest":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","policy_arn":"arn:aws:iam::000000000000:policy/ReplaySubsetPolicy","statement_index":0,"summary":"synthetic lambda:CreateFunction grant for replay subset"}],"effect":"Allow","has_conditions":false,"is_wildcard_resource":false,"layer":"permission","policy_arn":"arn:aws:iam::000000000000:policy/ReplaySubsetPolicy","policy_name":"ReplaySubsetPolicy","raw_conditions":{},"resource_pattern":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","statement_index":0},"region":"-","src":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::000000000000:user/ReplaySubsetMissingPassRole","region":"-"}},{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","region":"-"},"edge_id":"8b0fc08e9e37389c4c8362d697a2e8a50fa03159d4cc75ddfb215850584f077a","edge_type":"lambda:CreateFunction_permission","features":{"action_matched_via":"exact","allow_controls":[{"control_type":"PERMISSION","digest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","policy_arn":"arn:aws:iam::000000000000:policy/ReplaySubsetPolicy","statement_index":0,"summary":"synthetic lambda:CreateFunction grant for replay subset"}],"effect":"Allow","has_conditions":false,"is_wildcard_resource":false,"layer":"permission","policy_arn":"arn:aws:iam::000000000000:policy/ReplaySubsetPolicy","policy_name":"ReplaySubsetPolicy","raw_conditions":{},"resource_pattern":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","statement_index":0},"region":"-","src":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::000000000000:user/ReplaySubsetAllowed","region":"-"}},{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","region":"-"},"edge_id":"d53ac072c4f459282e74e342116a73b86aa167cd69446b51b6bea318c7552d6a","edge_type":"iam:PassRole_permission","features":{"action_matched_via":"exact","allow_controls":[{"control_type":"PERMISSION","digest":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","policy_arn":"arn:aws:iam::000000000000:policy/ReplaySubsetPolicy","statement_index":1,"summary":"synthetic iam:PassRole grant for replay subset"}],"effect":"Allow","has_conditions":false,"is_wildcard_resource":false,"layer":"permission","policy_arn":"arn:aws:iam::000000000000:policy/ReplaySubsetPolicy","policy_name":"ReplaySubsetPolicy","raw_conditions":{},"resource_pattern":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","statement_index":1},"region":"-","src":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::000000000000:user/ReplaySubsetAllowed","region":"-"}}],"metadata":{"accounts_collected":1,"accounts_skipped":0,"canonical_hash":"536d02868ab7c610a800105e94482725142b0f70c28cdc975091d0b384714926","collection_duration_seconds":0.0,"collection_failures":[],"collection_timestamp":"2026-06-03T00:00:00Z","collector":"iamscope-replay-subset-fixture","collector_version":"0.0.0-local-fixture","graph_stats":{"fixture_id":"complex_replay_subset_passrole_lambda_001","local_only":true},"hash_scope":"canonical_hash excludes metadata block","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","noise_filter":{},"org_id":""},"nodes":[{"node_id":"35536e494c0a910e4dd2bfbef83a17ab0be435884c1c954ce11c60712caa2e43","node_type":"IAMUser","properties":{"account_id":"000000000000","fixture_role":"replay_subset_allowed_source"},"provider":"aws","provider_id":"arn:aws:iam::000000000000:user/ReplaySubsetAllowed","region":"-"},{"node_id":"60bd3c82167e73badcc1d5969eef7aec0c5140890db8ec5b51a82b8e76fa0ced","node_type":"IAMRole","properties":{"account_id":"000000000000","fixture_role":"replay_subset_target_role"},"provider":"aws","provider_id":"arn:aws:iam::000000000000:role/ReplaySubsetLambdaExecutionRole","region":"-"},{"node_id":"7d3ea41430751ef29d784667e1210536b87e692dc16df22d91ae682d6ec96ee1","node_type":"IAMUser","properties":{"account_id":"000000000000","fixture_role":"replay_subset_missing_passrole_source"},"provider":"aws","provider_id":"arn:aws:iam::000000000000:user/ReplaySubsetMissingPassRole","region":"-"},{"node_id":"dd76780b75405f72452650208a1129ca4dc959c43bcea761cbc3686e576d16d0","node_type":"AWSService","properties":{"service":"lambda"},"provider":"aws","provider_id":"lambda.amazonaws.com","region":"-"}],"objectives":[],"observations":[]} \ No newline at end of file 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 ee71811..daf994e 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":["77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"node_refs":["195f0ed8d079747982d19ef2dc2333d20c00ffadee387a289b48f243050d940d","3a88944e339a515bdee1317a68f43b8b892543efdb82f3ac43fca77ba3b9fbd0"],"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\u003333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033","444444\u003444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"333333\u003333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u003444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034":["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":"7e8614ff662329a3b9a6b8b1be0d25685287d6cf00248d0b97afcdba2ef3e703","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":["77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":["cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":["77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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"},{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"node_refs":["195f0ed8d079747982d19ef2dc2333d20c00ffadee387a289b48f243050d940d","3a88944e339a515bdee1317a68f43b8b892543efdb82f3ac43fca77ba3b9fbd0","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031","222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032","333333\u003333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033","444444\u003444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032":["arn:aws:iam::111111\u003111111:role/DevOps",0,"trust arn:aws:iam::111111\u003111111:user/Alice"],"333333\u003333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u003444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034":["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":"a5473f14ff1fb91d6318156b478f80c031884191e56623a5048e40af8ba32c6d","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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":["cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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"}],"metadata":{"canonical_hash":"6d8712256c3ae9a2514ebcc58e99c9427c628b4d653d875ea206bafe83bf8bce","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_v2","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":[],"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 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 f8f9c34..475cd32 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":["29770cd3768ae9fc3c9a539c40337301690d0bd91a688f7817c1a1f41a1fed1b","30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","b3ec332a904af5ba1f6a321dd34a7285fe7b05d163b50876b2f1e48a6e8fc140","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12","ff1499d5cbe71d74c3380da6e566935e4c22002690f5cb7b5f3dcb83628ffb36"],"node_refs":["195f0ed8d079747982d19ef2dc2333d20c00ffadee387a289b48f243050d940d","3a88944e339a515bdee1317a68f43b8b892543efdb82f3ac43fca77ba3b9fbd0","9cd26bcc5ccdc7df931845\u00327327992cb0f9771f1a848ac9446b1581abf6670ec","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031","222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032","333333\u003333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033","444444\u003444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034","555555\u003555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u0035","666666\u003666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u0036","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032":["arn:aws:iam::111111\u003111111:role/DevOps",0,"trust arn:aws:iam::111111\u003111111:user/Alice"],"333333\u003333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u003444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034":["arn:aws:iam::111111\u003111111:role/Admin",0,"trust arn:aws:iam::111111\u003111111:role/DevOps"],"555555\u003555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u0035":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"666666\u003666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u0036":["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":"cc65926022f64a90bc10b3d42ab2b48038340ef79637ac31550314e948a81a92","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":["29770cd3768ae9fc3c9a539c40337301690d0bd91a688f7817c1a1f41a1fed1b","30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","b3ec332a904af5ba1f6a321dd34a7285fe7b05d163b50876b2f1e48a6e8fc140","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12","ff1499d5cbe71d74c3380da6e566935e4c22002690f5cb7b5f3dcb83628ffb36"],"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":["b3ec332a904af5ba1f6a321dd34a7285fe7b05d163b50876b2f1e48a6e8fc140","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["29770cd3768ae9fc3c9a539c40337301690d0bd91a688f7817c1a1f41a1fed1b","30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","b3ec332a904af5ba1f6a321dd34a7285fe7b05d163b50876b2f1e48a6e8fc140","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12","ff1499d5cbe71d74c3380da6e566935e4c22002690f5cb7b5f3dcb83628ffb36"],"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":["29770cd3768ae9fc3c9a539c40337301690d0bd91a688f7817c1a1f41a1fed1b","30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","b3ec332a904af5ba1f6a321dd34a7285fe7b05d163b50876b2f1e48a6e8fc140","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12","ff1499d5cbe71d74c3380da6e566935e4c22002690f5cb7b5f3dcb83628ffb36"],"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":["29770cd3768ae9fc3c9a539c40337301690d0bd91a688f7817c1a1f41a1fed1b","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","b3ec332a904af5ba1f6a321dd34a7285fe7b05d163b50876b2f1e48a6e8fc140","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12","ff1499d5cbe71d74c3380da6e566935e4c22002690f5cb7b5f3dcb83628ffb36"],"node_refs":["195f0ed8d079747982d19ef2dc2333d20c00ffadee387a289b48f243050d940d","3a88944e339a515bdee1317a68f43b8b892543efdb82f3ac43fca77ba3b9fbd0","9cd26bcc5ccdc7df931845\u00327327992cb0f9771f1a848ac9446b1581abf6670ec"],"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\u003333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033","444444\u003444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034","555555\u003555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u0035","666666\u003666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u0036","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"333333\u003333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u003444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034":["arn:aws:iam::111111\u003111111:role/Admin",0,"trust arn:aws:iam::111111\u003111111:role/DevOps"],"555555\u003555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u003555555\u0035":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"666666\u003666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u003666666\u0036":["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":"dad525d698247ac04fc5dc1aad184b061def67e26a78a2ff463bb37bacc20a6d","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":["29770cd3768ae9fc3c9a539c40337301690d0bd91a688f7817c1a1f41a1fed1b","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","b3ec332a904af5ba1f6a321dd34a7285fe7b05d163b50876b2f1e48a6e8fc140","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12","ff1499d5cbe71d74c3380da6e566935e4c22002690f5cb7b5f3dcb83628ffb36"],"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":["b3ec332a904af5ba1f6a321dd34a7285fe7b05d163b50876b2f1e48a6e8fc140","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["29770cd3768ae9fc3c9a539c40337301690d0bd91a688f7817c1a1f41a1fed1b","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","b3ec332a904af5ba1f6a321dd34a7285fe7b05d163b50876b2f1e48a6e8fc140","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12","ff1499d5cbe71d74c3380da6e566935e4c22002690f5cb7b5f3dcb83628ffb36"],"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":["29770cd3768ae9fc3c9a539c40337301690d0bd91a688f7817c1a1f41a1fed1b","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","b3ec332a904af5ba1f6a321dd34a7285fe7b05d163b50876b2f1e48a6e8fc140","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12","ff1499d5cbe71d74c3380da6e566935e4c22002690f5cb7b5f3dcb83628ffb36"],"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":"172901e6fccbc64b8fe5265febdbae837ab4eecf928a074e3c47f9efe3763c7f","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_v2","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":[],"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 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 44c4f6e..e16f8ba 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":["2a6e028c48af413e7920f13f9d945efe4e665aa5b267c7944068650c43a8b213","30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"node_refs":["195f0ed8d079747982d19ef2dc2333d20c00ffadee387a289b48f243050d940d","3a88944e339a515bdee1317a68f43b8b892543efdb82f3ac43fca77ba3b9fbd0","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031","222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032":["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":"3b3d8bc1679cb4be005d27a3bd4257fd24fb6107b515c7a4c381d7d2e565c95e","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":["2a6e028c48af413e7920f13f9d945efe4e665aa5b267c7944068650c43a8b213","30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["2a6e028c48af413e7920f13f9d945efe4e665aa5b267c7944068650c43a8b213","30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["2a6e028c48af413e7920f13f9d945efe4e665aa5b267c7944068650c43a8b213","30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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"},{"assumptions":[],"blockers_observed":[],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["2a6e028c48af413e7920f13f9d945efe4e665aa5b267c7944068650c43a8b213","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"node_refs":["195f0ed8d079747982d19ef2dc2333d20c00ffadee387a289b48f243050d940d","3a88944e339a515bdee1317a68f43b8b892543efdb82f3ac43fca77ba3b9fbd0"],"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\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031","222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032":["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":"ab809a5e503db6be40e3a2a9f10ff7f0317f9affb5caa00c08062a8cf363cbf0","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":["2a6e028c48af413e7920f13f9d945efe4e665aa5b267c7944068650c43a8b213","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["2a6e028c48af413e7920f13f9d945efe4e665aa5b267c7944068650c43a8b213","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["2a6e028c48af413e7920f13f9d945efe4e665aa5b267c7944068650c43a8b213","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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"}],"metadata":{"canonical_hash":"37d67e7a599dc59e14fc8d9160d4895bd30bcee0883c0fd4d58ef72bb6dba154","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_v2","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":[],"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 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 db86024..6b80216 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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"node_refs":["195f0ed8d079747982d19ef2dc2333d20c00ffadee387a289b48f243050d940d","3a88944e339a515bdee1317a68f43b8b892543efdb82f3ac43fca77ba3b9fbd0","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6"],"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":["3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6"],"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":["3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6"],"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":["3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6"],"reason":"clean witnesses on 2/2 hops","result":"PASS","step":7}],"statement_digests":["111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031","222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032","333333\u003333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033","444444\u003444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032":["arn:aws:iam::111111\u003111111:role/DevOps",0,"trust arn:aws:iam::111111\u003111111:user/Alice"],"333333\u003333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u003444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034":["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":"dc800366fe6f2410d2eb319010ab8c14a48b34da5de9801fb00e79a577337ee4","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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":["cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":"f26a20f4b3a6ac68ce0ff4c46186553d9a05b9b4c738d8df7c4dcc926fe5dad4","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_v2","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":[],"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 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 3ac273b..a54ed79 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":"3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","kind":"scp","reason":"SCP 58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2 denies sts:AssumeRole"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2"],"edge_constraint_refs":["3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19:58618e23d1d1aea5899c5e0a0c32473f7e55335948b024f6c1b78eb02c080ee2"],"edge_refs":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"node_refs":["195f0ed8d079747982d19ef2dc2333d20c00ffadee387a289b48f243050d940d","3a88944e339a515bdee1317a68f43b8b892543efdb82f3ac43fca77ba3b9fbd0","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6"],"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":["3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6"],"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":["3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6"],"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":["3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6"],"reason":"clean witnesses on 2/2 hops","result":"PASS","step":7}],"statement_digests":["111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031","222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032","333333\u003333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033","444444\u003444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032":["arn:aws:iam::111111\u003111111:role/DevOps",0,"trust arn:aws:iam::111111\u003111111:user/Alice"],"333333\u003333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u003333333\u0033":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"444444\u003444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u003444444\u0034":["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":"f335bc43f15abca8e0013169868d8e692c336229dadbb96fec986be8ed77b473","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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":["cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3f793b1008a5c829df673bf1cc6314182db315ac45e93f9c3a72766e7e13cd19","77cad71a08c2a929b740220b7be3ef7ffd51fe0958a4e35f9ceece0a3d1025a6","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f","f52e059ba5703a02a1e9c33dd706f1000ea5ecb011834c8f99903d97678dde12"],"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":"0a51550e428a90d40de2d2536f5bc06ea70e65240125784e6ee41c4359a36ca3","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_v2","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"}],"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 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 da51e5d..bbda6a6 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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3ff10d861e6a0a77871972c7481eca0f219b85636ceaa5173dc7ea1114a823a9","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","9a676c0961bf0ad6fc4f1b045a62886eefea27f4d5d12d1ab44393bed42ce878","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"node_refs":["195f0ed8d079747982d19ef2dc2333d20c00ffadee387a289b48f243050d940d","3a88944e339a515bdee1317a68f43b8b892543efdb82f3ac43fca77ba3b9fbd0","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["3ff10d861e6a0a77871972c7481eca0f219b85636ceaa5173dc7ea1114a823a9","9a676c0961bf0ad6fc4f1b045a62886eefea27f4d5d12d1ab44393bed42ce878"],"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":["3ff10d861e6a0a77871972c7481eca0f219b85636ceaa5173dc7ea1114a823a9","9a676c0961bf0ad6fc4f1b045a62886eefea27f4d5d12d1ab44393bed42ce878"],"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":["3ff10d861e6a0a77871972c7481eca0f219b85636ceaa5173dc7ea1114a823a9","9a676c0961bf0ad6fc4f1b045a62886eefea27f4d5d12d1ab44393bed42ce878"],"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":["3ff10d861e6a0a77871972c7481eca0f219b85636ceaa5173dc7ea1114a823a9","9a676c0961bf0ad6fc4f1b045a62886eefea27f4d5d12d1ab44393bed42ce878"],"reason":"clean witnesses on 1/2 hops","result":"UNKNOWN","step":7}],"statement_digests":["111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031","222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/AssumeRolePerms",0,"sts:AssumeRole grant"],"222222\u003222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u003222222\u0032":["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":"b918fccf38484dce61741cacfa85edc8a3b113370d47447541c955e913de2d2f","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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3ff10d861e6a0a77871972c7481eca0f219b85636ceaa5173dc7ea1114a823a9","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","9a676c0961bf0ad6fc4f1b045a62886eefea27f4d5d12d1ab44393bed42ce878","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3ff10d861e6a0a77871972c7481eca0f219b85636ceaa5173dc7ea1114a823a9","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","9a676c0961bf0ad6fc4f1b045a62886eefea27f4d5d12d1ab44393bed42ce878","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3ff10d861e6a0a77871972c7481eca0f219b85636ceaa5173dc7ea1114a823a9","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","9a676c0961bf0ad6fc4f1b045a62886eefea27f4d5d12d1ab44393bed42ce878","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3ff10d861e6a0a77871972c7481eca0f219b85636ceaa5173dc7ea1114a823a9","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","9a676c0961bf0ad6fc4f1b045a62886eefea27f4d5d12d1ab44393bed42ce878","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3ff10d861e6a0a77871972c7481eca0f219b85636ceaa5173dc7ea1114a823a9","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","9a676c0961bf0ad6fc4f1b045a62886eefea27f4d5d12d1ab44393bed42ce878","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":["30a9d414e16b9af37102b0377f271ac37a4a0a834835a107cd272647da3bebc7","3ff10d861e6a0a77871972c7481eca0f219b85636ceaa5173dc7ea1114a823a9","78c2be2ab19a20cdb31436cedc15aedfefd111b851fe3283b5a822967c21f8c2","9a676c0961bf0ad6fc4f1b045a62886eefea27f4d5d12d1ab44393bed42ce878","cb53f722a64f2f67a9bbcb5fc144d28ecf46ddb75f3913a61fc17c0d0ab8711f"],"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":"96ad50ca773f8f4713f56fbfdfd8b60bc2b2f3952d6225a46836828666f25af1","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_v2","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":[],"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 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 859988e..77f7974 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":["963c67858177beaf1ff7a4a652bfa813a4c054e937ab023fea74549a66f10991"],"node_refs":["101dfe7ea2f60bd30f5f0f5b512235f80c8292ad05fcf2e71de606c98b9894e3","cabb4d0f67c54b91a05d62dbc78e1e480c93c1a97d40adec7583def867a9b089"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["963c67858177beaf1ff7a4a652bfa813a4c054e937ab023fea74549a66f10991"],"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":["963c67858177beaf1ff7a4a652bfa813a4c054e937ab023fea74549a66f10991"],"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":"be96e9309581a5aa1d0834b4370d07811397c6e4f89bacd4336aea866c8eda62","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":["963c67858177beaf1ff7a4a652bfa813a4c054e937ab023fea74549a66f10991"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["963c67858177beaf1ff7a4a652bfa813a4c054e937ab023fea74549a66f10991"],"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":["963c67858177beaf1ff7a4a652bfa813a4c054e937ab023fea74549a66f10991"],"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":["963c67858177beaf1ff7a4a652bfa813a4c054e937ab023fea74549a66f10991"],"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":["963c67858177beaf1ff7a4a652bfa813a4c054e937ab023fea74549a66f10991"],"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":["963c67858177beaf1ff7a4a652bfa813a4c054e937ab023fea74549a66f10991"],"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":"b0f01c894ea6902369a2e177ef8d44454f44d23f657996aa4514fc04b3109097","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_v2","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":[],"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 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 8c670f0..75fe16e 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\u0030674850d334ff02010c4296682a43516","edge_id":"b68021e774084aba76c502a73fd8d7b301fe5e9aff088dc42fa06118bafbd071","kind":"scp","reason":"SCP DenyAssumeRoleProd at OU ou-prod denies sts:AssumeRole"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["cf50a880bb88876a04d3aa31ae577629\u0030674850d334ff02010c4296682a43516"],"edge_constraint_refs":["b68021e774084aba76c502a73fd8d7b301fe5e9aff088dc42fa06118bafbd071|cf50a880bb88876a04d3aa31ae577629\u0030674850d334ff02010c4296682a43516"],"edge_refs":["b68021e774084aba76c502a73fd8d7b301fe5e9aff088dc42fa06118bafbd071"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","cabb4d0f67c54b91a05d62dbc78e1e480c93c1a97d40adec7583def867a9b089"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["b68021e774084aba76c502a73fd8d7b301fe5e9aff088dc42fa06118bafbd071"],"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\u0030674850d334ff02010c4296682a43516"],"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":"89c2c9887fb0bcb29280d593c727840e2040d88a5f88dd8b02fba94116f6f303","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":["b68021e774084aba76c502a73fd8d7b301fe5e9aff088dc42fa06118bafbd071"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["b68021e774084aba76c502a73fd8d7b301fe5e9aff088dc42fa06118bafbd071"],"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":["b68021e774084aba76c502a73fd8d7b301fe5e9aff088dc42fa06118bafbd071"],"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\u0030674850d334ff02010c4296682a43516"],"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":["b68021e774084aba76c502a73fd8d7b301fe5e9aff088dc42fa06118bafbd071"],"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":["b68021e774084aba76c502a73fd8d7b301fe5e9aff088dc42fa06118bafbd071"],"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":"aabd464d2c2ed3d1eb16c66844ecc2c3fba5d100ae35dad06526b944e85beedb","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_v2","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":"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 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 49f1c34..1719bb7 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":["330962fcb3fa0a83730d074393a5831f5db62fd97b5021b8a229ad375072053e"],"node_refs":["6b0c9a72608383faa5682fe0434b41cbd2894ce3916f6a1ccabb437e815b4bba","cabb4d0f67c54b91a05d62dbc78e1e480c93c1a97d40adec7583def867a9b089"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["330962fcb3fa0a83730d074393a5831f5db62fd97b5021b8a229ad375072053e"],"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":["330962fcb3fa0a83730d074393a5831f5db62fd97b5021b8a229ad375072053e"],"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":"7456b03ac40c46fc36a96b3a22454b852a6036a937a81f219164\u003145\u003287158\u003911","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":["330962fcb3fa0a83730d074393a5831f5db62fd97b5021b8a229ad375072053e"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["330962fcb3fa0a83730d074393a5831f5db62fd97b5021b8a229ad375072053e"],"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":["330962fcb3fa0a83730d074393a5831f5db62fd97b5021b8a229ad375072053e"],"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":["330962fcb3fa0a83730d074393a5831f5db62fd97b5021b8a229ad375072053e"],"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":["330962fcb3fa0a83730d074393a5831f5db62fd97b5021b8a229ad375072053e"],"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":["330962fcb3fa0a83730d074393a5831f5db62fd97b5021b8a229ad375072053e"],"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":"78631a72c75882122f2952447c8121c47167d6a79022f8739e3c8db3e1b588fc","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_v2","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":[],"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 diff --git a/tests/fixtures/expected_output/findings/cross_account_trust/fixture_d_conditioned_no_finding.json b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_d_conditioned_no_finding.json index 029f51e..60e3776 100644 --- a/tests/fixtures/expected_output/findings/cross_account_trust/fixture_d_conditioned_no_finding.json +++ b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_d_conditioned_no_finding.json @@ -1 +1 @@ -{"findings":[],"metadata":{"canonical_hash":"6dbe69fd4043fd592dfeb130fc77f9b21b820852b136afd704c1f37c0b7490e2","collector":"iamscope","collector_version":"0.2.0","findings_count":0,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v2","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":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":[],"metadata":{"canonical_hash":"98c8aa7d55388f99498790bf7e5f2d1f60e28df8ac8ea9eb137c2b0595cfcb17","collector":"iamscope","collector_version":"0.2.0","findings_count":0,"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":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_e_scp_partial_inconclusive.json b/tests/fixtures/expected_output/findings/cross_account_trust/fixture_e_scp_partial_inconclusive.json index a66ecd1..308a08c 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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5|d5efb7277849deacae17c738f0675c5a400b01ae850a82e98b70e1552dd5e6b9"],"edge_refs":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","cabb4d0f67c54b91a05d62dbc78e1e480c93c1a97d40adec7583def867a9b089"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":"783231aff5d1b0d278f9b397da8108a2f39e938685c9a4f2ea102a7eaa4f2a43","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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":"f8599649806ae7945ddf3e7b043add2cea12ffe890ceefda841a3f0f78019841","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_v2","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":[],"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 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 f507f17..ecc04a1 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":["2c453ce289132af5a3010ad0f6cf6361193b0aea975971df947a71e25f75191b"],"node_refs":["951c922d1c9940139d76ca22d35589e88a333646\u0030288133ad31defddf50de78b","cabb4d0f67c54b91a05d62dbc78e1e480c93c1a97d40adec7583def867a9b089"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["2c453ce289132af5a3010ad0f6cf6361193b0aea975971df947a71e25f75191b"],"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":["2c453ce289132af5a3010ad0f6cf6361193b0aea975971df947a71e25f75191b"],"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":"9baafda83277f7fc3b41d962e139d753cce845554b5149299855bd798effa457","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":["2c453ce289132af5a3010ad0f6cf6361193b0aea975971df947a71e25f75191b"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["2c453ce289132af5a3010ad0f6cf6361193b0aea975971df947a71e25f75191b"],"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":["2c453ce289132af5a3010ad0f6cf6361193b0aea975971df947a71e25f75191b"],"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":["2c453ce289132af5a3010ad0f6cf6361193b0aea975971df947a71e25f75191b"],"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":["2c453ce289132af5a3010ad0f6cf6361193b0aea975971df947a71e25f75191b"],"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":["2c453ce289132af5a3010ad0f6cf6361193b0aea975971df947a71e25f75191b"],"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":"34a05fb5a740b81f58d5c9776d30456cf03f2764b3c77e311fb114c3cb040b99","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_v2","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":[],"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 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 00622ea..75a179c 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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5|ff41779feaf5a6a5e23f817c865d24e009ecaac99e50f09a911bbacc612451e4"],"edge_refs":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","cabb4d0f67c54b91a05d62dbc78e1e480c93c1a97d40adec7583def867a9b089"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":"68302fce086a1e5e341238\u003607\u003292611a7968bc3f30eadf51b0464228f3217cce","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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":"3f7b68810d613bbb07be89deb99f41b224f4d6722cda46bb564baf512f78c467","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_v2","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":[],"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 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 dae1bdf..9fdd80b 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":["9fe204bfdbdaff3f707784f7004ebb7ef375084d766f42e67bfbd314fc1a52bb"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","cabb4d0f67c54b91a05d62dbc78e1e480c93c1a97d40adec7583def867a9b089"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["9fe204bfdbdaff3f707784f7004ebb7ef375084d766f42e67bfbd314fc1a52bb"],"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":["9fe204bfdbdaff3f707784f7004ebb7ef375084d766f42e67bfbd314fc1a52bb"],"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":"9b615639013f0194e755301cd52f922f953556\u003644410aa76698a5fc6c5d272ff","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":["9fe204bfdbdaff3f707784f7004ebb7ef375084d766f42e67bfbd314fc1a52bb"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["9fe204bfdbdaff3f707784f7004ebb7ef375084d766f42e67bfbd314fc1a52bb"],"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":["9fe204bfdbdaff3f707784f7004ebb7ef375084d766f42e67bfbd314fc1a52bb"],"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":["9fe204bfdbdaff3f707784f7004ebb7ef375084d766f42e67bfbd314fc1a52bb"],"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":["9fe204bfdbdaff3f707784f7004ebb7ef375084d766f42e67bfbd314fc1a52bb"],"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":["9fe204bfdbdaff3f707784f7004ebb7ef375084d766f42e67bfbd314fc1a52bb"],"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":"bfbece7d299aca6d1e5de2a74d0a6208c39440d91b9394bbbca1a1f96166a00b","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_v2","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":[],"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 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 f24be6f..82a43b0 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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"node_refs":["48d5fd24a282e9f13701eb27a851211d4e8f18930896bdcb74bcbcda2702a960","cabb4d0f67c54b91a05d62dbc78e1e480c93c1a97d40adec7583def867a9b089"],"reasoning_trace":[{"action":"check_edge_is_cross_account","inputs":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":"0784e45e4de8ebc7a316695eca10927ec77bc9b3a4f9630d65e8d33f8b4b393a","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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"name":"edge_is_cross_account","reason":"features.cross_account is True","state":"pass"},{"description":"naked_trust classification is in the risky set","evidence_refs":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":["0f19d78acc9f8e1800238d6416a9d30ccad61b25ca055108f40ad39e92fe7da5"],"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":"dc9c83db697a7b13aecf550cabfaad5462805619c69f5ddc39c021eb3e5c7a5d","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_v2","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":[],"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 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 888bd8c..d27e4bd 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":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233","3f017b22b20401d9369cdae3595bbe6f999263\u003869918bcf94e1c7ecb8dac4ca6"],"node_refs":["835475b429f0cb46c9e74f852d0278a2c5ecbe3f353eb5a76e9f6c7e867e229b","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"reason":"witness edge resolves to specific target group","result":"PASS","step":2},{"action":"check_no_scp_blocks_add_user_to_group","inputs":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_add_user_to_group","inputs":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_add_user_to_group","inputs":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"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 3f017b22b204\u2026)","result":"PASS","step":6}],"statement_digests":["111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/GroupMgmt",0,"iam:AddUserToGroup grant"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"de357ff9e108454f8396c7c714517e673875d358c6acbbf7f973029ba08154e3","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":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"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":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"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":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"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":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"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":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"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":["3f017b22b20401d9369cdae3595bbe6f999263\u003869918bcf94e1c7ecb8dac4ca6"],"name":"target_group_is_admin_equivalent","reason":"target group has admin-equivalent permissions (witness edge 3f017b22b204\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":"15a8518e1cf92ca9b1ba2921e8058de8b7cace7b455cdd78089eca0ecca654ff","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_v2","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":[],"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 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 9d87cfd..68a668e 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":["1f07686874758f1face5afa77b1ec0171c9cb44e0310c2f40e291c779ba2f465","3f017b22b20401d9369cdae3595bbe6f999263\u003869918bcf94e1c7ecb8dac4ca6"],"node_refs":["835475b429f0cb46c9e74f852d0278a2c5ecbe3f353eb5a76e9f6c7e867e229b","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["1f07686874758f1face5afa77b1ec0171c9cb44e0310c2f40e291c779ba2f465"],"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":["1f07686874758f1face5afa77b1ec0171c9cb44e0310c2f40e291c779ba2f465"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_add_user_to_group","inputs":["1f07686874758f1face5afa77b1ec0171c9cb44e0310c2f40e291c779ba2f465"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_add_user_to_group","inputs":["1f07686874758f1face5afa77b1ec0171c9cb44e0310c2f40e291c779ba2f465"],"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 3f017b22b204\u2026)","result":"PASS","step":6}],"statement_digests":["111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/GroupMgmt",0,"iam:AddUserToGroup grant"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"f4025da51d663e0250272f248679315a8e58b473d6b97be2f58eae1cb602a1c9","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":["1f07686874758f1face5afa77b1ec0171c9cb44e0310c2f40e291c779ba2f465"],"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":["1f07686874758f1face5afa77b1ec0171c9cb44e0310c2f40e291c779ba2f465"],"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":["1f07686874758f1face5afa77b1ec0171c9cb44e0310c2f40e291c779ba2f465"],"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":["1f07686874758f1face5afa77b1ec0171c9cb44e0310c2f40e291c779ba2f465"],"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":["1f07686874758f1face5afa77b1ec0171c9cb44e0310c2f40e291c779ba2f465"],"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":["3f017b22b20401d9369cdae3595bbe6f999263\u003869918bcf94e1c7ecb8dac4ca6"],"name":"target_group_is_admin_equivalent","reason":"target group has admin-equivalent permissions (witness edge 3f017b22b204\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":"e75f91c2a243898a02164f0fb757a39cbf2300a9ce0fd65822295fa64bd37e83","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_v2","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":[],"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 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 992417e..cbaca8b 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":"09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233","kind":"scp","reason":"constraint 54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b affects AddUserToGroup"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b"],"edge_constraint_refs":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233:54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b"],"edge_refs":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233","3f017b22b20401d9369cdae3595bbe6f999263\u003869918bcf94e1c7ecb8dac4ca6"],"node_refs":["835475b429f0cb46c9e74f852d0278a2c5ecbe3f353eb5a76e9f6c7e867e229b","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"reason":"witness edge resolves to specific target group","result":"PASS","step":2},{"action":"check_no_scp_blocks_add_user_to_group","inputs":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"reason":"SCP 54dd4490ef401e466f26c1dca81114182a8386006a06a947773609feeb67b16b blocks (complete)","result":"FAIL","step":3},{"action":"check_no_boundary_blocks_add_user_to_group","inputs":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_add_user_to_group","inputs":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"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 3f017b22b204\u2026)","result":"PASS","step":6}],"statement_digests":["111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/GroupMgmt",0,"iam:AddUserToGroup grant"],"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":["arn:aws:iam::aws:policy/AdministratorAccess",0,"iam:*"]}},"finding_id":"28efcb282d88741c4940324041ecfd9f0c63073b8097375841d6abb16a011984","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":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"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":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"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":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"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":["09359eef08a8852842b50a3c0764fc4619f7e219d59632106156db3dfaa5f233"],"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":["3f017b22b20401d9369cdae3595bbe6f999263\u003869918bcf94e1c7ecb8dac4ca6"],"name":"target_group_is_admin_equivalent","reason":"target group has admin-equivalent permissions (witness edge 3f017b22b204\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":"0524b72a1f677db6a6c0d3aa41aeff96367c556cea7ef19e733a8d3721a9e5b2","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_v2","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"}],"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 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 a2dc261..50c5991 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":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3","b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd"],"node_refs":["2941e3b2bfb0fed742b3342df67aeef99a305953fafefb22cab46a8d9044d309","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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 b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":"87bb16b540cd6c0fe5eaec24bc5f545ff959e246ba25445dbfe08e02fc5034bd","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":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":"0318c7b182f53918fb145fb2d67a0fadc08870717fca1f3aa32cacf4fe4ad250","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_v2","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":[],"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 diff --git a/tests/fixtures/expected_output/findings/passrole_ecs/fixture_b_trust_missing_no_findings.json b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_b_trust_missing_no_findings.json index 7ff63dd..87ed669 100644 --- a/tests/fixtures/expected_output/findings/passrole_ecs/fixture_b_trust_missing_no_findings.json +++ b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_b_trust_missing_no_findings.json @@ -1 +1 @@ -{"findings":[],"metadata":{"canonical_hash":"b27993021d2fe0cf547ca810198e8ec25156fcb25385d9584ddd36d3a77fd55d","collector":"iamscope","collector_version":"0.2.0","findings_count":0,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v2","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":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":[],"metadata":{"canonical_hash":"d72ff0b7130a1b8dd277b318e80c467e4c5a0e5879b938cc1c0eb4f1ba98b971","collector":"iamscope","collector_version":"0.2.0","findings_count":0,"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":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_c_blocked_by_scp.json b/tests/fixtures/expected_output/findings/passrole_ecs/fixture_c_blocked_by_scp.json index c912794..c3bd4aa 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":"8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3","kind":"scp","reason":"SCP DenyEcsRegister at OU ou-prod denies ecs:RegisterTaskDefinition"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["6f1183562bba96f4b1029a8cdafd687fda3fce37693dd59985541fda1075d7cc"],"edge_constraint_refs":["8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3|6f1183562bba96f4b1029a8cdafd687fda3fce37693dd59985541fda1075d7cc"],"edge_refs":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3","b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd"],"node_refs":["2941e3b2bfb0fed742b3342df67aeef99a305953fafefb22cab46a8d9044d309","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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 b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":"d18ed77443264f605df24d3912aa78a006a9aaa245cb856dcba1b713b91a0b2b","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":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":"deb73f026320a2b95869eccd8b272468800d6d47e620989fa7712f8fa3410d6a","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_v2","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"}],"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 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 bddf23d..551750b 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":"8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3","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":["8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3|ce4f30a750a2653fe026e35cf86526de3dcdd218a48ae64747dcbdf1dfc79519"],"edge_refs":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3","b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd"],"node_refs":["2941e3b2bfb0fed742b3342df67aeef99a305953fafefb22cab46a8d9044d309","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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 b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":"304ae63469cc9137da1e089253bc622c7b12b3992a8ac429f21cfc59444b65ba","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":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":"a2a13dcbf6832d5e6d4b603bbcd8a2f4aa6e1f1b8bd90056e47364adf7734ff4","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_v2","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)"}],"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 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 4e73345..7a334a3 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":["8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3|fe4f9a59334201dbedb60fcda04e68e83a77cd134bba8f154578ac7d61c9c663"],"edge_refs":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3","b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd"],"node_refs":["2941e3b2bfb0fed742b3342df67aeef99a305953fafefb22cab46a8d9044d309","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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 b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":"9343ab25763c45b4e7e2e83ffa75447f8798eeb36a290db6b38d9e038acf7f4d","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":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":["4796aa5edcb171bc9b21bd655a24c4f130db0f8efa8db56616073d5cbcbf1751"],"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":"84fb39178aadf8d7aebfd7fe10419cf615ef24fdb667022b8d7c5710bae80847","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_v2","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":[],"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 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 74b1c44..50a6c1a 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":["07169816c46b64c05b4f65f8b5727b7f3ed137b1add59a5a49b33bab59829156","3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c","b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","cdcb3171ec87e7a5212c9452a4c2781128a527ad10174e613fba5df4686a23b4"],"node_refs":["2941e3b2bfb0fed742b3342df67aeef99a305953fafefb22cab46a8d9044d309","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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 b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["07169816c46b64c05b4f65f8b5727b7f3ed137b1add59a5a49b33bab59829156","cdcb3171ec87e7a5212c9452a4c2781128a527ad10174e613fba5df4686a23b4"],"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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["07169816c46b64c05b4f65f8b5727b7f3ed137b1add59a5a49b33bab59829156","cdcb3171ec87e7a5212c9452a4c2781128a527ad10174e613fba5df4686a23b4"],"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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":"0b0ba86e44bb3ae274d5a74efd856442\u0031354986faa5281718f66e685450c1b0a","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":["07169816c46b64c05b4f65f8b5727b7f3ed137b1add59a5a49b33bab59829156","cdcb3171ec87e7a5212c9452a4c2781128a527ad10174e613fba5df4686a23b4"],"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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":["b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["07169816c46b64c05b4f65f8b5727b7f3ed137b1add59a5a49b33bab59829156"],"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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":["07169816c46b64c05b4f65f8b5727b7f3ed137b1add59a5a49b33bab59829156"],"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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":"0e25ad0fca66b162356afa84591e1377c464d290c04f983dcb284e7c11420894","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_v2","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":[],"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 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 ecba400..1109548 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":"ff097ab2b1e47779317a2a124df099bde6a599afd5f6807994b9c12a91919386","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":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3","b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","ff097ab2b1e47779317a2a124df099bde6a599afd5f6807994b9c12a91919386"],"node_refs":["2941e3b2bfb0fed742b3342df67aeef99a305953fafefb22cab46a8d9044d309","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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 b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","result":"PASS","step":3},{"action":"check_no_scp_blocks_ecs_create_or_run","inputs":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["ff097ab2b1e47779317a2a124df099bde6a599afd5f6807994b9c12a91919386"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_ecs_create_or_run","inputs":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["ff097ab2b1e47779317a2a124df099bde6a599afd5f6807994b9c12a91919386"],"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":["ff097ab2b1e47779317a2a124df099bde6a599afd5f6807994b9c12a91919386"],"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":"708aa295963eeb4d7027765fd3baa5b3795854f0366f8d3e741fd17176c8de3f","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":["1fc0b3326fbb482dfddaf1c66a72c65749f3e9c790bec6b4fc186f5863857af7","8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["ff097ab2b1e47779317a2a124df099bde6a599afd5f6807994b9c12a91919386"],"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":["b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd"],"name":"target_trusts_ecs_tasks_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts ecs-tasks.amazonaws.com via trust edge b4dfed2296c02a5704bd85930de085dfbed8f256d46f27794353979a300158bd","state":"pass"},{"description":"No SCP blocks ecs:RegisterTaskDefinition or ecs:RunTask with full confidence","evidence_refs":["8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["ff097ab2b1e47779317a2a124df099bde6a599afd5f6807994b9c12a91919386"],"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":["8e707fa98a99c6f7b260ad0e23473c90d93b89c9b90891a1c9096fd33ca19ea3"],"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":["ff097ab2b1e47779317a2a124df099bde6a599afd5f6807994b9c12a91919386"],"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":["ff097ab2b1e47779317a2a124df099bde6a599afd5f6807994b9c12a91919386"],"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":"d4de55e5321a415b2bb5271f2e3c60200293c7d6513fe07d45ef3c05a52b44d4","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_v2","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":"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 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 1b8d0e4..599bd55 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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1","b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b","bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323"],"node_refs":["2941e3b2bfb0fed742b3342df67aeef99a305953fafefb22cab46a8d9044d309","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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 bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"reason":"no SCP bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":"b2576e5fa50bec927d0a1efc3083cd6776d8301f6b5c670c22cfb2ee3fb93884","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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":"1302da0028b7894c09abd4478c8f33370ad9d5ed0f8c9955ca33fc17b6799efb","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_v2","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":[],"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 diff --git a/tests/fixtures/expected_output/findings/passrole_lambda/fixture_b_trust_missing_no_finding.json b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_b_trust_missing_no_finding.json index 6178839..ed79dec 100644 --- a/tests/fixtures/expected_output/findings/passrole_lambda/fixture_b_trust_missing_no_finding.json +++ b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_b_trust_missing_no_finding.json @@ -1 +1 @@ -{"findings":[],"metadata":{"canonical_hash":"03edb9df5049f450c6faf10043722df1cb7758d3d67ac3478402d990b06da8c2","collector":"iamscope","collector_version":"0.2.0","findings_count":0,"hash_scope":"canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds","id_algorithm":"sha256_null_separated_v2","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":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":[],"metadata":{"canonical_hash":"dcfcfedebbc3d8c5fe047251255ecb4a425e918667060cef2e8cd593133a59b2","collector":"iamscope","collector_version":"0.2.0","findings_count":0,"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":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_c_blocked_by_scp.json b/tests/fixtures/expected_output/findings/passrole_lambda/fixture_c_blocked_by_scp.json index 000f744..d10300c 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":"8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1","kind":"scp","reason":"SCP DenyLambdaCreate at OU ou-prod denies lambda:CreateFunction"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["b8be6a39dd4b8ff39656668b709a83d2854492fc9cf7d0243fc9df2f74701cf5"],"edge_constraint_refs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1|b8be6a39dd4b8ff39656668b709a83d2854492fc9cf7d0243fc9df2f74701cf5"],"edge_refs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1","b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b","bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323"],"node_refs":["2941e3b2bfb0fed742b3342df67aeef99a305953fafefb22cab46a8d9044d309","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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 bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":"634145761e7c2d811a8d0a90ac589fa6cbce199ee50de9ab1017697197ac024f","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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323","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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":"14d51efb863be2e263d930435\u003600\u003794239\u00384e8ed65943f0d84ac71dfd88f3335","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_v2","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"}],"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 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 cc66929..bb90bba 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":"8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1","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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1|b364ac9edf52f48c1a9919828b1cb92a76552feef31828701aae82499204d674"],"edge_refs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1","b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b","bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323"],"node_refs":["2941e3b2bfb0fed742b3342df67aeef99a305953fafefb22cab46a8d9044d309","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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 bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"reason":"no SCP bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":"133bfa05de6f1a9519f0d3c2602bf3f92846c5bf75cb6f882b8553883ddcd2a0","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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":"744b2608183c40eec9c88c220331af630fbd4e965a6b3b6505b7d02f10c23bc9","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_v2","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)"}],"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 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 335628f..61d7d7e 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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1|6ec4df77d19c38ae8d5cd296db1aacdbee1e49c188c85fd237e09452c570d096"],"edge_refs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1","b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b","bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323"],"node_refs":["2941e3b2bfb0fed742b3342df67aeef99a305953fafefb22cab46a8d9044d309","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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 bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":"c82c19c790368a2ff80386dbc45723d27f70e0e6f188ca840951ecbfbc926904","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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323","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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":["b9fcf46c3a575af806f0e476868f17258798bbf285e6b22e6a0e5f76d57c109b"],"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":"273d490aa6e68c01374d7bd03e0ae69311ac99ce9c682ab5386de07ba5243612","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_v2","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":[],"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 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 68d39d1..71383a5 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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c","50a8d8ce032ef0e3e1d902cfdaf6647c1e89efa05ff616126ae7b5a9f631c73c","bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323"],"node_refs":["2941e3b2bfb0fed742b3342df67aeef99a305953fafefb22cab46a8d9044d309","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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 bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["50a8d8ce032ef0e3e1d902cfdaf6647c1e89efa05ff616126ae7b5a9f631c73c"],"reason":"no SCP bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["50a8d8ce032ef0e3e1d902cfdaf6647c1e89efa05ff616126ae7b5a9f631c73c"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":["50a8d8ce032ef0e3e1d902cfdaf6647c1e89efa05ff616126ae7b5a9f631c73c"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":"b5d9d5414c41e757eb6777f8086b6a0366ee1effcddcb94ddd166e03dae85883","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":["50a8d8ce032ef0e3e1d902cfdaf6647c1e89efa05ff616126ae7b5a9f631c73c"],"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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":["bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["50a8d8ce032ef0e3e1d902cfdaf6647c1e89efa05ff616126ae7b5a9f631c73c"],"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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":["50a8d8ce032ef0e3e1d902cfdaf6647c1e89efa05ff616126ae7b5a9f631c73c"],"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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":["50a8d8ce032ef0e3e1d902cfdaf6647c1e89efa05ff616126ae7b5a9f631c73c"],"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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":["3894a931dbbd15470bd6372d56f01e33ceeaafbb9a2376ecd6232969e433802c"],"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":"64359462276c14212ba43e77d35a943a674932e9c18ee80a8dff7a318cbc420d","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_v2","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":[],"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 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 0e9125a..8c949ff 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":"d84858815f426dbff5fc94056c943410cbcfff1c42d6524ae5fd673411b7b471","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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1","bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323","d84858815f426dbff5fc94056c943410cbcfff1c42d6524ae5fd673411b7b471"],"node_refs":["2941e3b2bfb0fed742b3342df67aeef99a305953fafefb22cab46a8d9044d309","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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 bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323","result":"PASS","step":3},{"action":"check_no_scp_blocks_lambda_create_function","inputs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"reason":"no SCP bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":4},{"action":"check_no_scp_blocks_passrole","inputs":["d84858815f426dbff5fc94056c943410cbcfff1c42d6524ae5fd673411b7b471"],"reason":"no SCP bindings observed on iam:PassRole witness edge","result":"PASS","step":5},{"action":"check_no_boundary_blocks_lambda_create_function","inputs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"reason":"no permission boundary bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":6},{"action":"check_no_boundary_blocks_passrole","inputs":["d84858815f426dbff5fc94056c943410cbcfff1c42d6524ae5fd673411b7b471"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"reason":"no identity policy Deny bindings observed on lambda:CreateFunction witness edge","result":"PASS","step":8},{"action":"check_no_identity_deny_blocks_passrole","inputs":["d84858815f426dbff5fc94056c943410cbcfff1c42d6524ae5fd673411b7b471"],"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":["d84858815f426dbff5fc94056c943410cbcfff1c42d6524ae5fd673411b7b471"],"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":"b0205b58124e00f91a6b391660f7f1890be3808a595513ba63461904f3a539c4","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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["d84858815f426dbff5fc94056c943410cbcfff1c42d6524ae5fd673411b7b471"],"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":["bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323"],"name":"target_trusts_lambda_service","reason":"target role arn:aws:iam::111111\u003111111:role/AdminRole trusts lambda.amazonaws.com via trust edge bf6135505167b0e720854f45cc568800752b00c663b2c4896d0a4627f165d323","state":"pass"},{"description":"No SCP blocks lambda:CreateFunction with full confidence","evidence_refs":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["d84858815f426dbff5fc94056c943410cbcfff1c42d6524ae5fd673411b7b471"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["d84858815f426dbff5fc94056c943410cbcfff1c42d6524ae5fd673411b7b471"],"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":["8248b754180c9995ed106b47f6f0260284b24ac8b5fda427f011102\u003747729cf1"],"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":["d84858815f426dbff5fc94056c943410cbcfff1c42d6524ae5fd673411b7b471"],"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":["d84858815f426dbff5fc94056c943410cbcfff1c42d6524ae5fd673411b7b471"],"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":"54f0730f27a6e5b92a922cdd3ed0fd0a3f432de9e599485339ffc44ee30dade3","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_v2","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"}],"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 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 41e3ea1..1aac4db 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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"node_refs":["3698aa56e2b6f8b7e5769b63328e45d96642bcba2cd6bc9de5e4b259586b9de6","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"reason":"no SCP bindings observed","result":"PASS","step":4},{"action":"check_no_boundary_blocks_put_bucket_policy","inputs":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":"e6091fadcb0f61074bf72938dc5269c03c163c962b57c326e757851e22f0ac69","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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":"4ed979a6bdda9488af1eb7ef32948317358a0dbb060d7c38a49b9680e59a92d0","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_v2","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":[],"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 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 d9803fb..d6b6a48 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":["0e4f6b8396c298e1b2880feb066c021ff62e4f08698a9cd2ae4d890d71761845"],"node_refs":["3698aa56e2b6f8b7e5769b63328e45d96642bcba2cd6bc9de5e4b259586b9de6","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["0e4f6b8396c298e1b2880feb066c021ff62e4f08698a9cd2ae4d890d71761845"],"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":["0e4f6b8396c298e1b2880feb066c021ff62e4f08698a9cd2ae4d890d71761845"],"reason":"no SCP bindings observed","result":"PASS","step":4},{"action":"check_no_boundary_blocks_put_bucket_policy","inputs":["0e4f6b8396c298e1b2880feb066c021ff62e4f08698a9cd2ae4d890d71761845"],"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":"7072cb21a0c744de44bda7517f9126d34d89d6c5b6e8628fb6f93bc139b24ae6","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":["0e4f6b8396c298e1b2880feb066c021ff62e4f08698a9cd2ae4d890d71761845"],"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":["0e4f6b8396c298e1b2880feb066c021ff62e4f08698a9cd2ae4d890d71761845"],"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":["0e4f6b8396c298e1b2880feb066c021ff62e4f08698a9cd2ae4d890d71761845"],"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":["0e4f6b8396c298e1b2880feb066c021ff62e4f08698a9cd2ae4d890d71761845"],"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":["0e4f6b8396c298e1b2880feb066c021ff62e4f08698a9cd2ae4d890d71761845"],"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":["0e4f6b8396c298e1b2880feb066c021ff62e4f08698a9cd2ae4d890d71761845"],"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":"4832830039b0250982e043f13d646466692d452a07c27bf40f3bacf5b595ac15","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_v2","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":[],"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 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 f90122b..a89a61c 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":"ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057","kind":"scp","reason":"constraint 5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5 affects PutBucketPolicy"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5"],"edge_constraint_refs":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057:5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5"],"edge_refs":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"node_refs":["3698aa56e2b6f8b7e5769b63328e45d96642bcba2cd6bc9de5e4b259586b9de6","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"reason":"SCP 5367b8ead62235ac6c8e64554eb270ff09b558111f007f2467dad988e1f703c5 blocks (complete)","result":"FAIL","step":4},{"action":"check_no_boundary_blocks_put_bucket_policy","inputs":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":"67de97b83a1e45a94ca3584bea811d0837ada8f1245c028ff86a8d6554c3aec7","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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":["ace127988856b808ad4e8d905a13462eca50cdab22afa554898e2bb0e6b8c057"],"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":"ed54bcb7e18c08f959174facc521594e8e472e71e4d6673224cae5a8118d0924","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_v2","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"}],"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 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 512182e..d9cffa7 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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"node_refs":["c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"reason":"clean","result":"PASS","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"0cf673745665cb5e9cd3d8ee74901e339469b069c0eef48182af17c4bec3bf12","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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":"5c411a34ddf52f1048e7589145829fdc58c86d54d3ced79377692271f7025dc3","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_v2","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":[],"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 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 a081d20..1e5eb2e 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":"0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294","kind":"scp","reason":"constraint 773590\u003869181a5093a333af3daa5927c5ce66a69a47d51488fc619529\u003319759b affects GetSecretValue"}],"evidence":{"condition_context_assumed":[],"constraint_refs":["773590\u003869181a5093a333af3daa5927c5ce66a69a47d51488fc619529\u003319759b"],"edge_constraint_refs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294:773590\u003869181a5093a333af3daa5927c5ce66a69a47d51488fc619529\u003319759b"],"edge_refs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"node_refs":["c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"reason":"clean","result":"PASS","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"reason":"SCP 773590\u003869181a5093a333af3daa5927c5ce66a69a47d51488fc619529\u003319759b blocks (complete)","result":"FAIL","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"e2ed2c0628c2d81f70124d5bc9292e352543c6abca796a4ec027a0115d3c47d1","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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":"6552bc966d5941a5e48333fb0789ad1dc9a723c7b759ebe7f49dfd1e63d3630a","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_v2","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":"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 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 bb2f24e..1024c2d 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":["9595a291f179f026465659fb43665983f907f564dd9ea32bf1f311d7dfd02637"],"node_refs":["c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["9595a291f179f026465659fb43665983f907f564dd9ea32bf1f311d7dfd02637"],"reason":"ambiguous","result":"UNKNOWN","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["9595a291f179f026465659fb43665983f907f564dd9ea32bf1f311d7dfd02637"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["9595a291f179f026465659fb43665983f907f564dd9ea32bf1f311d7dfd02637"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["9595a291f179f026465659fb43665983f907f564dd9ea32bf1f311d7dfd02637"],"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\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"8b9a23a68d40ae536af1b251ce219c7bc172d646f83121472d600af5a0af0783","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":["9595a291f179f026465659fb43665983f907f564dd9ea32bf1f311d7dfd02637"],"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":["9595a291f179f026465659fb43665983f907f564dd9ea32bf1f311d7dfd02637"],"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":["9595a291f179f026465659fb43665983f907f564dd9ea32bf1f311d7dfd02637"],"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":["9595a291f179f026465659fb43665983f907f564dd9ea32bf1f311d7dfd02637"],"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":["9595a291f179f026465659fb43665983f907f564dd9ea32bf1f311d7dfd02637"],"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":["9595a291f179f026465659fb43665983f907f564dd9ea32bf1f311d7dfd02637"],"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":"db087d73b133828b2a2ecc3074c5571eb7c6c4ca170385bc598e763cf509743b","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_v2","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":[],"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 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 72fd962..7f4a513 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":"0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294","kind":"kms_key_policy","reason":"no KMS policy Allow statement covers principal"}],"evidence":{"condition_context_assumed":[],"constraint_refs":[],"edge_constraint_refs":[],"edge_refs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"node_refs":["951e7e5545c007f8abad41c65dd6da49c324e78203835e2ace0ebd84ee700281","c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"reason":"clean","result":"PASS","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"e4518339d18d7efd178d0857a5e596bfe8aa5bab113c39ef16f2122d2747096b","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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":"b9dbd6132c790fd682f7aaec8bd1ad90aa59f05a1bc77fd0b6be27b619b13276","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_v2","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"}],"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 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 a180a66..a2a8ec6 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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"node_refs":["951e7e5545c007f8abad41c65dd6da49c324e78203835e2ace0ebd84ee700281","c88173f1d9e1142e6a0c72d29a656ca79ddaaf2e974e4419f9b5176909fda5fa","d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"reason":"clean","result":"PASS","step":2},{"action":"check_no_scp_blocks_get_secret_value","inputs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"reason":"no SCP bindings observed","result":"PASS","step":3},{"action":"check_no_boundary_blocks_get_secret_value","inputs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"reason":"no permission boundary bindings observed","result":"PASS","step":4},{"action":"check_no_identity_deny_blocks_get_secret_value","inputs":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031"],"statement_sources":{"111111\u003111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u003111111\u0031":["arn:aws:iam::111111\u003111111:policy/SecretsAccess",0,"secretsmanager:GetSecretValue grant"]}},"finding_id":"39218c199a15874267845abc84278b19f94cc9fad97e58b9415e9f5a1f3f04a8","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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":["0aea8d21acb37ee9e27bdf253c98532253a791b5e37b48b65d074bc4ff4ae294"],"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":"5a774a442149d3646052391e0486a2f66d88d7d9511f5984f1e54e35b60857d7","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_v2","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":[],"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 diff --git a/tests/fixtures/expected_output/minimal_scenario.json b/tests/fixtures/expected_output/minimal_scenario.json index 253cb91..f54831f 100644 --- a/tests/fixtures/expected_output/minimal_scenario.json +++ b/tests/fixtures/expected_output/minimal_scenario.json @@ -1 +1 @@ -{"constraints":[{"confidence_q":800,"constraint_id":"bf6a9a87d94e2f3a9cc222c1fec7fd4f2bb12b1a824a172b12749132c203dfcc","constraint_type":"SCP","policy_id":"p-1234567890","properties":{"deny_actions":["sts:AssumeRole"],"deny_not_actions":[],"exception_principal_patterns":[],"parse_status":"complete","policy_name":"DenyAssumeRoleProd","resource_patterns":["*"]},"provider":"aws","region":"-","scope_id":"ou-abc123-prodou","scope_type":"OU","statement_id":"DenyAssumeRole","status":"ACTIVE","validation_status":"UNVALIDATED"}],"edge_constraints":[{"constraint_id":"bf6a9a87d94e2f3a9cc222c1fec7fd4f2bb12b1a824a172b12749132c203dfcc","edge_id":"0801a1a15799e9c2572ab836d7b4a76d000cbeaa348db8f5b3b9a54aeaf6861c"}],"edges":[{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/TestRole","region":"-"},"edge_id":"0801a1a15799e9c2572ab836d7b4a76d000cbeaa348db8f5b3b9a54aeaf6861c","edge_type":"sts:AssumeRole_trust","features":{"allow_controls":[{"control_type":"TRUST","digest":"cbee7c137dfdd478759d2683e6081eb9326fd90f9d4153830affa8ef44f466b7","policy_arn":"arn:aws:iam::111111\u003111111:role/TestRole","statement_index":0,"summary":"trust policy for arn:aws:iam::111111\u003111111:role/TestRole"}],"cross_account":true,"has_external_id":false,"layer":"trust","naked_trust":true,"trust_scope":"account_root"},"region":"-","src":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::222222\u003222222:root","region":"-"}},{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/TestRole","region":"-"},"edge_id":"e3f881d664808e575335\u00330755340e3f694a2f91a3082f538f5a6e30e7e27faa2","edge_type":"iam:PassRole_permission","features":{"action_matched_via":"exact","allow_controls":[{"control_type":"IDENTITY_POLICY","digest":"2cf55b941ae2159528f7cc95648db7cb55525d9cf5eda3c1cce5edb0ca8f07aa","statement_index":0,"summary":"inline:AliceAdmin"}],"effect":"Allow","has_conditions":false,"is_wildcard_resource":false,"layer":"permission","permission_source":"inline","policy_arn":"","policy_name":"AliceAdmin","raw_conditions":{},"resource_pattern":"arn:aws:iam::111111\u003111111:role/TestRole","statement_index":0},"region":"-","src":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"}}],"metadata":{"accounts_collected":2,"accounts_skipped":0,"canonical_hash":"1ef3da8327d13ea21c80923c79076db819b0cea74320b18671bc4a8c06f81150","collection_duration_seconds":1.0,"collection_failures":[],"collection_timestamp":"2026-01-01T00:00:00Z","collector":"iamscope","collector_version":"0.2.0","graph_stats":{"total_edges":2,"total_nodes":3},"hash_scope":"canonical_hash excludes metadata block","id_algorithm":"sha256_null_separated_v2","noise_filter":{"exclude_service_linked":true,"expansion_mode":"warn"},"org_id":"o-golden"},"nodes":[{"node_id":"206187ea2a20e21c8a86281ec9021fefe6cd59889d7cdd08cf4e7be6f0c098e1","node_type":"AccountPrincipalSet","properties":{"account_id":"222222\u003222222","is_synthetic":true,"principal_count":50},"provider":"aws","provider_id":"arn:aws:iam::222222\u003222222:root","region":"-"},{"node_id":"576a7a252d335cb469865cdffcc5f2fef1faa5fcd614d2088bf2078f29bcbd7c","node_type":"IAMRole","properties":{"account_id":"111111\u003111111","is_synthetic":false,"path":"/"},"provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/TestRole","region":"-"},{"node_id":"d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599","node_type":"IAMUser","properties":{"account_id":"111111\u003111111","is_synthetic":false,"path":"/"},"provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"}],"objectives":[],"observations":[]} \ No newline at end of file +{"constraints":[{"confidence_q":800,"constraint_id":"bf6a9a87d94e2f3a9cc222c1fec7fd4f2bb12b1a824a172b12749132c203dfcc","constraint_type":"SCP","policy_id":"p-1234567890","properties":{"deny_actions":["sts:AssumeRole"],"deny_not_actions":[],"exception_principal_patterns":[],"parse_status":"complete","policy_name":"DenyAssumeRoleProd","resource_patterns":["*"]},"provider":"aws","region":"-","scope_id":"ou-abc123-prodou","scope_type":"OU","statement_id":"DenyAssumeRole","status":"ACTIVE","validation_status":"UNVALIDATED"}],"edge_constraints":[{"constraint_id":"bf6a9a87d94e2f3a9cc222c1fec7fd4f2bb12b1a824a172b12749132c203dfcc","edge_id":"091433c6b5cadc515a885f5566d8216da6ed751c9ef5a981b27981865a1c809f"}],"edges":[{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/TestRole","region":"-"},"edge_id":"091433c6b5cadc515a885f5566d8216da6ed751c9ef5a981b27981865a1c809f","edge_type":"sts:AssumeRole_trust","features":{"allow_controls":[{"control_type":"TRUST","digest":"cbee7c137dfdd478759d2683e6081eb9326fd90f9d4153830affa8ef44f466b7","policy_arn":"arn:aws:iam::111111\u003111111:role/TestRole","statement_index":0,"summary":"trust policy for arn:aws:iam::111111\u003111111:role/TestRole"}],"cross_account":true,"has_external_id":false,"layer":"trust","naked_trust":true,"trust_scope":"account_root"},"region":"-","src":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::222222\u003222222:root","region":"-"}},{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/TestRole","region":"-"},"edge_id":"eb9ef7054dc30e4bc5051983d25bdda96113b867e9a9426f5b43174c0d21b0e5","edge_type":"iam:PassRole_permission","features":{"action_matched_via":"exact","allow_controls":[{"control_type":"IDENTITY_POLICY","digest":"2cf55b941ae2159528f7cc95648db7cb55525d9cf5eda3c1cce5edb0ca8f07aa","statement_index":0,"summary":"inline:AliceAdmin"}],"effect":"Allow","has_conditions":false,"is_wildcard_resource":false,"layer":"permission","permission_source":"inline","policy_arn":"","policy_name":"AliceAdmin","raw_conditions":{},"resource_pattern":"arn:aws:iam::111111\u003111111:role/TestRole","statement_index":0},"region":"-","src":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"}}],"metadata":{"accounts_collected":2,"accounts_skipped":0,"canonical_hash":"96a91e9047f88ea44088bb6c37113cdc093597821c26953218d742f2f062a6be","collection_duration_seconds":1.0,"collection_failures":[],"collection_timestamp":"2026-01-01T00:00:00Z","collector":"iamscope","collector_version":"0.2.0","graph_stats":{"total_edges":2,"total_nodes":3},"hash_scope":"canonical_hash excludes metadata block","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","noise_filter":{"exclude_service_linked":true,"expansion_mode":"warn"},"org_id":"o-golden"},"nodes":[{"node_id":"206187ea2a20e21c8a86281ec9021fefe6cd59889d7cdd08cf4e7be6f0c098e1","node_type":"AccountPrincipalSet","properties":{"account_id":"222222\u003222222","is_synthetic":true,"principal_count":50},"provider":"aws","provider_id":"arn:aws:iam::222222\u003222222:root","region":"-"},{"node_id":"a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","node_type":"IAMUser","properties":{"account_id":"111111\u003111111","is_synthetic":false,"path":"/"},"provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"},{"node_id":"b774bddfefb7554f5eb5c9917b1f20ff4187a5a3ecae0e06e0419c86f8f58a5a","node_type":"IAMRole","properties":{"account_id":"111111\u003111111","is_synthetic":false,"path":"/"},"provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/TestRole","region":"-"}],"objectives":[],"observations":[]} \ No newline at end of file diff --git a/tests/fixtures/expected_output/scp_binding_scenario.json b/tests/fixtures/expected_output/scp_binding_scenario.json index 2fb8405..dfb137f 100644 --- a/tests/fixtures/expected_output/scp_binding_scenario.json +++ b/tests/fixtures/expected_output/scp_binding_scenario.json @@ -1 +1 @@ -{"constraints":[{"confidence_q":800,"constraint_id":"b765e6ebbda2af657f6d2f7b33e591ff193c0ee2c5291b82bbfb0c10641fb5f2","constraint_type":"SCP","policy_id":"p-block","properties":{"deny_actions":["sts:AssumeRole"],"deny_not_actions":[],"exception_principal_patterns":[],"parse_status":"complete","resource_patterns":["*"]},"provider":"aws","region":"-","scope_id":"ou-prod","scope_type":"OU","statement_id":"DenyAssumeRole","status":"ACTIVE","validation_status":"UNVALIDATED"},{"confidence_q":500,"constraint_id":"ede90b216ef1446277aebd4055ac0043a9e7dadadbc5ac11b25fbd1d5a8d7b03","constraint_type":"SCP","policy_id":"p-except","properties":{"deny_actions":["sts:*"],"deny_not_actions":[],"exception_principal_patterns":["arn:aws:iam::*:role/BreakGlass*"],"parse_status":"complete","resource_patterns":["*"]},"provider":"aws","region":"-","scope_id":"ou-prod","scope_type":"OU","statement_id":"DenyAllStsExceptBreakGlass","status":"ACTIVE","validation_status":"UNVALIDATED"}],"edge_constraints":[{"constraint_id":"b765e6ebbda2af657f6d2f7b33e591ff193c0ee2c5291b82bbfb0c10641fb5f2","edge_id":"257b182863037e6cd9e7e59899d766e8d21a8a6f4f6442965f2ed13c4313c2b6"},{"constraint_id":"ede90b216ef1446277aebd4055ac0043a9e7dadadbc5ac11b25fbd1d5a8d7b03","edge_id":"257b182863037e6cd9e7e59899d766e8d21a8a6f4f6442965f2ed13c4313c2b6"}],"edges":[{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdDeploy","region":"-"},"edge_id":"257b182863037e6cd9e7e59899d766e8d21a8a6f4f6442965f2ed13c4313c2b6","edge_type":"sts:AssumeRole_trust","features":{"allow_controls":[{"control_type":"TRUST","digest":"cbee7c137dfdd478759d2683e6081eb9326fd90f9d4153830affa8ef44f466b7","policy_arn":"arn:aws:iam::111111\u003111111:role/ProdDeploy","statement_index":0,"summary":"trust policy for arn:aws:iam::111111\u003111111:role/ProdDeploy"}],"cross_account":true,"has_external_id":false,"layer":"trust","naked_trust":"BROAD_NAKED","trust_scope":"account_root"},"region":"-","src":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::222222\u003222222:root","region":"-"}},{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdDeploy","region":"-"},"edge_id":"4c676ec61dafa4bca90bfb047811ee72723e88fcfead5d48ca747a6a0ce32616","edge_type":"sts:AssumeRole_permission","features":{"action_matched_via":"exact","allow_controls":[{"control_type":"IDENTITY_POLICY","digest":"c9cd469f47d910de320c5b9e606e93c3525fd47d09609f19c58794900616b26f","statement_index":0,"summary":"inline:AliceAssume"}],"effect":"Allow","has_conditions":false,"is_wildcard_resource":false,"layer":"permission","permission_source":"inline","policy_arn":"","policy_name":"AliceAssume","raw_conditions":{},"resource_pattern":"arn:aws:iam::111111\u003111111:role/ProdDeploy","statement_index":0},"region":"-","src":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"}}],"metadata":{"accounts_collected":2,"accounts_skipped":0,"canonical_hash":"c2c40451ac7ef118f5c06c4581baff29a58306c9d6fefb8ab6fae6feed0fbdf0","collection_duration_seconds":2.0,"collection_failures":[],"collection_timestamp":"2026-01-01T00:00:00Z","collector":"iamscope","collector_version":"0.2.0","graph_stats":{"total_edges":2,"total_nodes":3},"hash_scope":"canonical_hash excludes metadata block","id_algorithm":"sha256_null_separated_v2","noise_filter":{"exclude_service_linked":true,"expansion_mode":"warn"},"org_id":"o-golden-scp"},"nodes":[{"node_id":"206187ea2a20e21c8a86281ec9021fefe6cd59889d7cdd08cf4e7be6f0c098e1","node_type":"AccountPrincipalSet","properties":{"account_id":"222222\u003222222","is_synthetic":true,"principal_count":50},"provider":"aws","provider_id":"arn:aws:iam::222222\u003222222:root","region":"-"},{"node_id":"9692672c6893212bc4a127c8f9c2fcb5347f33b84f50c86e8c560bd38b2663c8","node_type":"IAMRole","properties":{"account_id":"111111\u003111111","is_synthetic":false,"path":"/"},"provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdDeploy","region":"-"},{"node_id":"d4afe9f6244e0163de4e0d5b157205fc6797c1d476f1e67908cff3d7f46b2599","node_type":"IAMUser","properties":{"account_id":"111111\u003111111","is_synthetic":false,"path":"/"},"provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"}],"objectives":[],"observations":[]} \ No newline at end of file +{"constraints":[{"confidence_q":800,"constraint_id":"b765e6ebbda2af657f6d2f7b33e591ff193c0ee2c5291b82bbfb0c10641fb5f2","constraint_type":"SCP","policy_id":"p-block","properties":{"deny_actions":["sts:AssumeRole"],"deny_not_actions":[],"exception_principal_patterns":[],"parse_status":"complete","resource_patterns":["*"]},"provider":"aws","region":"-","scope_id":"ou-prod","scope_type":"OU","statement_id":"DenyAssumeRole","status":"ACTIVE","validation_status":"UNVALIDATED"},{"confidence_q":500,"constraint_id":"ede90b216ef1446277aebd4055ac0043a9e7dadadbc5ac11b25fbd1d5a8d7b03","constraint_type":"SCP","policy_id":"p-except","properties":{"deny_actions":["sts:*"],"deny_not_actions":[],"exception_principal_patterns":["arn:aws:iam::*:role/BreakGlass*"],"parse_status":"complete","resource_patterns":["*"]},"provider":"aws","region":"-","scope_id":"ou-prod","scope_type":"OU","statement_id":"DenyAllStsExceptBreakGlass","status":"ACTIVE","validation_status":"UNVALIDATED"}],"edge_constraints":[{"constraint_id":"b765e6ebbda2af657f6d2f7b33e591ff193c0ee2c5291b82bbfb0c10641fb5f2","edge_id":"e53ea6deda5cda340d6427dadad63a520ff8312c4215d7e1a2c44d8a965e3b7a"},{"constraint_id":"ede90b216ef1446277aebd4055ac0043a9e7dadadbc5ac11b25fbd1d5a8d7b03","edge_id":"e53ea6deda5cda340d6427dadad63a520ff8312c4215d7e1a2c44d8a965e3b7a"}],"edges":[{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdDeploy","region":"-"},"edge_id":"322d55ef28c42bae348a2e78c3c43ccbd137780e8372c9f9ba035392d34551ad","edge_type":"sts:AssumeRole_permission","features":{"action_matched_via":"exact","allow_controls":[{"control_type":"IDENTITY_POLICY","digest":"c9cd469f47d910de320c5b9e606e93c3525fd47d09609f19c58794900616b26f","statement_index":0,"summary":"inline:AliceAssume"}],"effect":"Allow","has_conditions":false,"is_wildcard_resource":false,"layer":"permission","permission_source":"inline","policy_arn":"","policy_name":"AliceAssume","raw_conditions":{},"resource_pattern":"arn:aws:iam::111111\u003111111:role/ProdDeploy","statement_index":0},"region":"-","src":{"node_type":"IAMUser","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"}},{"dst":{"node_type":"IAMRole","provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdDeploy","region":"-"},"edge_id":"e53ea6deda5cda340d6427dadad63a520ff8312c4215d7e1a2c44d8a965e3b7a","edge_type":"sts:AssumeRole_trust","features":{"allow_controls":[{"control_type":"TRUST","digest":"cbee7c137dfdd478759d2683e6081eb9326fd90f9d4153830affa8ef44f466b7","policy_arn":"arn:aws:iam::111111\u003111111:role/ProdDeploy","statement_index":0,"summary":"trust policy for arn:aws:iam::111111\u003111111:role/ProdDeploy"}],"cross_account":true,"has_external_id":false,"layer":"trust","naked_trust":"BROAD_NAKED","trust_scope":"account_root"},"region":"-","src":{"node_type":"AccountPrincipalSet","provider":"aws","provider_id":"arn:aws:iam::222222\u003222222:root","region":"-"}}],"metadata":{"accounts_collected":2,"accounts_skipped":0,"canonical_hash":"af33382ddabd14f94f05fe823efb7438b65a631785e1a04a6924e2c30c8637bd","collection_duration_seconds":2.0,"collection_failures":[],"collection_timestamp":"2026-01-01T00:00:00Z","collector":"iamscope","collector_version":"0.2.0","graph_stats":{"total_edges":2,"total_nodes":3},"hash_scope":"canonical_hash excludes metadata block","id_algorithm":"sha256_null_separated_v3_case_sensitive_provider_ids","noise_filter":{"exclude_service_linked":true,"expansion_mode":"warn"},"org_id":"o-golden-scp"},"nodes":[{"node_id":"157406fda5deba84fe8cabe98698aa2113f22d63549243a9beb19531336a4288","node_type":"IAMRole","properties":{"account_id":"111111\u003111111","is_synthetic":false,"path":"/"},"provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:role/ProdDeploy","region":"-"},{"node_id":"206187ea2a20e21c8a86281ec9021fefe6cd59889d7cdd08cf4e7be6f0c098e1","node_type":"AccountPrincipalSet","properties":{"account_id":"222222\u003222222","is_synthetic":true,"principal_count":50},"provider":"aws","provider_id":"arn:aws:iam::222222\u003222222:root","region":"-"},{"node_id":"a970d5d6a2cf62045e244b625e67bf599f7af628930862c1c5950df58029c578","node_type":"IAMUser","properties":{"account_id":"111111\u003111111","is_synthetic":false,"path":"/"},"provider":"aws","provider_id":"arn:aws:iam::111111\u003111111:user/Alice","region":"-"}],"objectives":[],"observations":[]} \ No newline at end of file diff --git a/tests/fixtures/live_binding/passrole_lambda_denied_missing_passrole/scenario.json b/tests/fixtures/live_binding/passrole_lambda_denied_missing_passrole/scenario.json index 8a26e43..1e3c2b8 100644 --- a/tests/fixtures/live_binding/passrole_lambda_denied_missing_passrole/scenario.json +++ b/tests/fixtures/live_binding/passrole_lambda_denied_missing_passrole/scenario.json @@ -12,7 +12,7 @@ "provider_id": "arn:aws:iam::000000000000:role/IAMScopeLiveBindingLambdaExecutionRole", "region": "-" }, - "edge_id": "ee650f233a6ed64f2e59a9e0f86a3c9d5bb0735730b8bcf021ecbd2a6d2fbaba", + "edge_id": "ba7b5752c650b6d1f8c33246f30d2be59129386e6b4000fc67706119f961769d", "edge_type": "lambda:CreateFunction_permission", "features": { "allow_controls": [ @@ -47,7 +47,7 @@ "provider_id": "arn:aws:iam::000000000000:role/IAMScopeLiveBindingLambdaExecutionRole", "region": "-" }, - "edge_id": "4e82d4996c7212e2ab5e5ef7088fba44653dde7d70ace38f1e182f55ffacb86b", + "edge_id": "e9e3d8a8c41a2090317655ca4bf91092fd16b4ebd2a37c0fd129816dd28b6b6a", "edge_type": "sts:AssumeRole_trust", "features": { "allow_controls": [ @@ -81,7 +81,7 @@ "live_aws_used": false, "nodes": [ { - "node_id": "8333d429ff01cd4e221057d52b64796ad32a7d358126bbd9a4874a00d140f090", + "node_id": "53aadae287ce372434592eb499386e179fd1fea3db1c786d1dc86edffee2f97d", "node_type": "IAMRole", "properties": { "account_id": "000000000000", @@ -92,7 +92,7 @@ "region": "-" }, { - "node_id": "9a8beffce76dcd9a52962f1be19c603a4bc97287d51b54296eec70942325d4ec", + "node_id": "581b077d55e9c2c1250c7f3cd22149e84f8ad4d6626a5a617b4db73f06f687ec", "node_type": "IAMRole", "properties": { "account_id": "000000000000", diff --git a/tests/fixtures/live_binding/passrole_lambda_selected_finding/expected_finding.json b/tests/fixtures/live_binding/passrole_lambda_selected_finding/expected_finding.json index 78d3f01..85486a6 100644 --- a/tests/fixtures/live_binding/passrole_lambda_selected_finding/expected_finding.json +++ b/tests/fixtures/live_binding/passrole_lambda_selected_finding/expected_finding.json @@ -25,7 +25,7 @@ "blockers_observed_count": 0, "expected_classification": "selected_local_createfunction_passrole_finding", "expected_verdict": "validated", - "finding_id": "dc284c673334e54974e229c9ac006684b3e928d0d03936f857fe93068dc74dc8", + "finding_id": "d611535f5dc10e74aab2214bddc46c1e0cf44ea8e35a8c5d9d47d5562908ccd5", "finding_key": "e7aa122330b61ce55fdb3cd017139b3c9b8c941fdd95fec676e2c337cde58b1e", "live_behavior_alignment": "service-mediated CreateFunction plus PassRole plus Lambda trust only", "pattern_id": "passrole_lambda", diff --git a/tests/fixtures/live_binding/passrole_lambda_selected_finding/scenario.json b/tests/fixtures/live_binding/passrole_lambda_selected_finding/scenario.json index 19f81b6..2deaa73 100644 --- a/tests/fixtures/live_binding/passrole_lambda_selected_finding/scenario.json +++ b/tests/fixtures/live_binding/passrole_lambda_selected_finding/scenario.json @@ -12,7 +12,7 @@ "provider_id": "arn:aws:iam::000000000000:role/IAMScopeLiveBindingLambdaExecutionRole", "region": "-" }, - "edge_id": "bcf0f131f4a66f2a73a77bae0ad7bc4b6928c92bf924da5f93a359547425b522", + "edge_id": "4172e020cfe6ded291527ee3ad4243ee5553452726a25516454f3134e635cf17", "edge_type": "lambda:CreateFunction_permission", "features": { "allow_controls": [ @@ -47,7 +47,7 @@ "provider_id": "arn:aws:iam::000000000000:role/IAMScopeLiveBindingLambdaExecutionRole", "region": "-" }, - "edge_id": "8fbe3500a1361fd80425ac88a46d7767c2413c97cc82b940525dead1001879b9", + "edge_id": "1fe5c6ebad2a369aff16fea0a8af1dd92c1a90fb611de401093c4f074089fd02", "edge_type": "iam:PassRole_permission", "features": { "allow_controls": [ @@ -82,7 +82,7 @@ "provider_id": "arn:aws:iam::000000000000:role/IAMScopeLiveBindingLambdaExecutionRole", "region": "-" }, - "edge_id": "24ad4d35b4441651fb0a4a321b53addfbdd843dc3653ff54dea29c220c478668", + "edge_id": "3ddf91eedddc15e944f67b4bef8559d68acdeedb57e19f2a557a4c5a45a4b165", "edge_type": "sts:AssumeRole_trust", "features": { "allow_controls": [ @@ -116,7 +116,7 @@ "live_aws_used": false, "nodes": [ { - "node_id": "c23e6af725515592a63bf88a0d1bf77bbd18927e492295d573e5f058bcb84425", + "node_id": "70d88ec4ec607a117bf0823f8f593f6d99c757e3ae2b0addeebfab7cf2b76039", "node_type": "IAMUser", "properties": { "account_id": "000000000000", @@ -127,7 +127,7 @@ "region": "-" }, { - "node_id": "9a8beffce76dcd9a52962f1be19c603a4bc97287d51b54296eec70942325d4ec", + "node_id": "581b077d55e9c2c1250c7f3cd22149e84f8ad4d6626a5a617b4db73f06f687ec", "node_type": "IAMRole", "properties": { "account_id": "000000000000", diff --git a/tests/identity/test_case_sensitive_id_collision.py b/tests/identity/test_case_sensitive_id_collision.py index cbbdc06..f956308 100644 --- a/tests/identity/test_case_sensitive_id_collision.py +++ b/tests/identity/test_case_sensitive_id_collision.py @@ -1,19 +1,11 @@ """Regression evidence for case-sensitive ARN deterministic ID collisions.""" -from __future__ import annotations - -import pytest - +from iamscope.constants import ID_ALGORITHM from iamscope.identity.deterministic_ids import edge_id, node_id -_XFAIL_REASON = ( - "known v2 canonical_id lowercases provider_id; fixed by planned v3 case-preserving provider_id algorithm" -) - _EMPTY_FEATURES_DIGEST = "{}" -@pytest.mark.xfail(reason=_XFAIL_REASON, strict=True) def test_case_distinct_iam_role_provider_ids_do_not_collide_under_node_id() -> None: upper_role = node_id( "aws", @@ -29,7 +21,6 @@ def test_case_distinct_iam_role_provider_ids_do_not_collide_under_node_id() -> N assert upper_role != lower_role -@pytest.mark.xfail(reason=_XFAIL_REASON, strict=True) def test_case_distinct_iam_user_provider_ids_do_not_collide_under_node_id() -> None: upper_user = node_id( "aws", @@ -45,7 +36,6 @@ def test_case_distinct_iam_user_provider_ids_do_not_collide_under_node_id() -> N assert upper_user != lower_user -@pytest.mark.xfail(reason=_XFAIL_REASON, strict=True) def test_case_distinct_source_provider_ids_do_not_collide_under_edge_id() -> None: upper_source = edge_id( "sts:AssumeRole_permission", @@ -65,7 +55,6 @@ def test_case_distinct_source_provider_ids_do_not_collide_under_edge_id() -> Non assert upper_source != lower_source -@pytest.mark.xfail(reason=_XFAIL_REASON, strict=True) def test_case_distinct_destination_provider_ids_do_not_collide_under_edge_id() -> None: upper_destination = edge_id( "sts:AssumeRole_permission", @@ -113,3 +102,7 @@ def test_exact_repeated_inputs_remain_deterministic() -> None: assert first_node_id == second_node_id assert first_edge_id == second_edge_id + + +def test_id_algorithm_exposes_v3_case_sensitive_provider_id_value() -> None: + assert ID_ALGORITHM == "sha256_null_separated_v3_case_sensitive_provider_ids" diff --git a/tests/test_cli.py b/tests/test_cli.py index 55fa97b..9d277cb 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -366,7 +366,7 @@ def test_validate_valid(self, tmp_path) -> None: md = ScenarioMetadata( collector="iamscope", collector_version="0.2.0", - id_algorithm="sha256_null_separated_v2", + id_algorithm="sha256_null_separated_v3_case_sensitive_provider_ids", ) scenario_bytes, _ = emit_scenario( nodes=[role_a, role_b], diff --git a/tests/test_deterministic_ids.py b/tests/test_deterministic_ids.py index 48313de..27e8e84 100644 --- a/tests/test_deterministic_ids.py +++ b/tests/test_deterministic_ids.py @@ -1,12 +1,13 @@ """Tests for deterministic ID generation. -Tests verify the pinned sha256_null_separated_v2 algorithm (v0.2.37+): +Tests verify the pinned sha256_null_separated_v3_case_sensitive_provider_ids algorithm: - Same input → same output (stability) - Different input → different output (uniqueness) -- Case insensitive (lowercased before hashing) +- `canonical_id` remains legacy case insensitive +- `node_id` and `edge_id` preserve provider-owned identity field case - Whitespace insensitive (stripped before hashing) - Stable formula (known input → known hash, pinned across versions) -- Edge ID uses correct component fields (v2: now includes +- Edge ID uses correct component fields (v2+ includes features_digest as the fifth field) - Constraint ID includes statement_id per R14 @@ -21,6 +22,7 @@ import pytest +from iamscope.constants import ID_ALGORITHM from iamscope.identity.deterministic_ids import ( canonical_id, constraint_id, @@ -105,12 +107,13 @@ def test_rejects_no_fields(self) -> None: class TestNodeId: - """Tests for node_id formula: canonical_id(provider, node_type, provider_id).""" + """Tests for node_id formula with case-preserved provider_id.""" def test_node_id_components(self) -> None: """node_id must use (provider, node_type, provider_id).""" nid = node_id("aws", "IAMRole", "arn:aws:iam::111111\u003111111:role/TestRole") - expected = canonical_id("aws", "IAMRole", "arn:aws:iam::111111\u003111111:role/TestRole") + canonical = "\x00".join(["aws", "iamrole", "arn:aws:iam::111111\u003111111:role/TestRole"]) + expected = hashlib.sha256(canonical.encode("utf-8")).hexdigest() assert nid == expected def test_different_node_types_different_ids(self) -> None: @@ -119,11 +122,21 @@ def test_different_node_types_different_ids(self) -> None: user_id = node_id("aws", "IAMUser", "arn:aws:iam::111:role/Test") assert role_id != user_id + def test_provider_id_case_is_preserved(self) -> None: + """v3 keeps provider-owned identity field case in node IDs.""" + role_upper = node_id("aws", "IAMRole", "arn:aws:iam::000000000000:role/CaseRole") + role_lower = node_id("aws", "IAMRole", "arn:aws:iam::000000000000:role/caserole") + assert role_upper != role_lower + + def test_structural_fields_remain_case_normalized(self) -> None: + """Provider and node_type remain structural, case-normalized fields.""" + lower_structural = node_id("aws", "iamrole", "arn:aws:iam::000000000000:role/CaseRole") + mixed_structural = node_id("AWS", "IAMRole", "arn:aws:iam::000000000000:role/CaseRole") + assert lower_structural == mixed_structural + class TestEdgeId: - """Tests for edge_id formula (v2): canonical_id(edge_type, src, dst, - region, features_digest). v2 added `features_digest` as a fifth - field in v0.2.37 — see module docstring.""" + """Tests for edge_id formula with case-preserved src/dst provider IDs.""" # Empty-features digest: canonical_json_bytes({}).decode("utf-8") == "{}" # Used as a stable "no features" placeholder in these unit tests so @@ -140,13 +153,16 @@ def test_edge_id_components(self) -> None: "-", self._EMPTY_FEATURES_DIGEST, ) - expected = canonical_id( - "sts:AssumeRole_trust", - "arn:aws:iam::222222\u003222222:role/DevJump", - "arn:aws:iam::333333\u003333333:role/ProdDeploy", - "-", - self._EMPTY_FEATURES_DIGEST, + canonical = "\x00".join( + [ + "sts:assumerole_trust", + "arn:aws:iam::222222\u003222222:role/DevJump", + "arn:aws:iam::333333\u003333333:role/ProdDeploy", + "-", + self._EMPTY_FEATURES_DIGEST, + ] ) + expected = hashlib.sha256(canonical.encode("utf-8")).hexdigest() assert eid == expected def test_different_edge_types_different_ids(self) -> None: @@ -186,6 +202,60 @@ def test_regional_edge_id(self) -> None: ) assert global_id != regional_id + def test_source_provider_id_case_is_preserved(self) -> None: + """v3 keeps source provider-owned identity field case in edge IDs.""" + upper_source = edge_id( + "sts:AssumeRole_permission", + "arn:aws:iam::000000000000:user/CaseUser", + "arn:aws:iam::000000000000:role/TargetRole", + "-", + self._EMPTY_FEATURES_DIGEST, + ) + lower_source = edge_id( + "sts:AssumeRole_permission", + "arn:aws:iam::000000000000:user/caseuser", + "arn:aws:iam::000000000000:role/TargetRole", + "-", + self._EMPTY_FEATURES_DIGEST, + ) + assert upper_source != lower_source + + def test_destination_provider_id_case_is_preserved(self) -> None: + """v3 keeps destination provider-owned identity field case in edge IDs.""" + upper_destination = edge_id( + "sts:AssumeRole_permission", + "arn:aws:iam::000000000000:user/SourceUser", + "arn:aws:iam::000000000000:role/CaseRole", + "-", + self._EMPTY_FEATURES_DIGEST, + ) + lower_destination = edge_id( + "sts:AssumeRole_permission", + "arn:aws:iam::000000000000:user/SourceUser", + "arn:aws:iam::000000000000:role/caserole", + "-", + self._EMPTY_FEATURES_DIGEST, + ) + assert upper_destination != lower_destination + + def test_structural_fields_remain_case_normalized(self) -> None: + """Edge type and region remain structural, case-normalized fields.""" + lower_structural = edge_id( + "sts:assumerole_permission", + "arn:aws:iam::000000000000:user/CaseUser", + "arn:aws:iam::000000000000:role/CaseRole", + "us-east-1", + self._EMPTY_FEATURES_DIGEST, + ) + mixed_structural = edge_id( + "sts:AssumeRole_permission", + "arn:aws:iam::000000000000:user/CaseUser", + "arn:aws:iam::000000000000:role/CaseRole", + "US-EAST-1", + self._EMPTY_FEATURES_DIGEST, + ) + assert lower_structural == mixed_structural + class TestConstraintId: """Tests for constraint_id formula including statement_id (R14).""" @@ -202,6 +272,16 @@ def test_constraint_id_same_statement_same_id(self) -> None: cid2 = constraint_id("aws", "SCP", "OU", "ou-abc", "p-123", "stmt_0") assert cid1 == cid2 + def test_constraint_id_remains_legacy_case_normalized_pending_review(self) -> None: + """Constraint IDs stay on the legacy lowercase canonicalization path.""" + cid_lower = constraint_id("aws", "scp", "ou", "ou-abc", "p-123", "case-sensitive-sid") + cid_mixed = constraint_id("AWS", "SCP", "OU", "ou-abc", "p-123", "Case-Sensitive-Sid") + assert cid_lower == cid_mixed + + +def test_id_algorithm_constant_is_v3_case_sensitive_provider_ids() -> None: + assert ID_ALGORITHM == "sha256_null_separated_v3_case_sensitive_provider_ids" + class TestEdgeConstraintSortKey: """Tests for edge_constraint sort key.""" diff --git a/tests/test_golden.py b/tests/test_golden.py index a2b3d32..5e8316e 100644 --- a/tests/test_golden.py +++ b/tests/test_golden.py @@ -37,7 +37,7 @@ def test_golden_valid_json(self) -> None: def test_golden_canonical_hash_pinned(self) -> None: """Canonical hash from golden fixture must match pinned value. - PINNED HASH: 1ef3da8327d13ea21c80923c79076db819b0cea74320b18671bc4a8c06f81150 + PINNED HASH: 96a91e9047f88ea44088bb6c37113cdc093597821c26953218d742f2f062a6be Re-pinned in v0.2.37 (Session 2 edge_id v1→v2): edge_id formula now includes features_digest, so every edge_id in this fixture @@ -59,7 +59,7 @@ def test_golden_canonical_hash_pinned(self) -> None: scenario = json.loads(golden_path.read_bytes()) stored_hash = scenario["metadata"]["canonical_hash"] - pinned_hash = "1ef3da8327d13ea21c80923c79076db819b0cea74320b18671bc4a8c06f81150" + pinned_hash = "96a91e9047f88ea44088bb6c37113cdc093597821c26953218d742f2f062a6be" assert stored_hash == pinned_hash, ( f"Golden canonical hash changed!\n" f" Expected: {pinned_hash}\n" @@ -83,17 +83,7 @@ def test_golden_file_hash_stable(self) -> None: # now include the new empty-list field. Re-pinned again in # v0.2.37 (Session 2 edge_id v1→v2) — both the canonical_hash # and the raw file bytes changed because edge_ids shifted.) - pinned_file_hash = "".join( - [ - "0b34673195", - "12483f35c2", - "5b57475258", - "2cc3e1a779", - "0c6eff7713", - "dc944ea250", - "e3d0", - ] - ) + pinned_file_hash = "231d3e02f6097fd11acfeadfca9339f1c1593847c78f87fdeaf3ccad6a2f448a" # Note: raw file hash includes metadata (timestamps etc). # If only metadata changed, canonical_hash test above still passes. @@ -263,7 +253,7 @@ def test_golden_reproduces_from_code(self) -> None: meta = ScenarioMetadata( collector="iamscope", collector_version="0.2.0", - id_algorithm="sha256_null_separated_v2", + id_algorithm="sha256_null_separated_v3_case_sensitive_provider_ids", org_id="o-golden", accounts_collected=2, collection_timestamp="2026-01-01T00:00:00Z", diff --git a/tests/test_golden_scp.py b/tests/test_golden_scp.py index 7b1115a..44df2e8 100644 --- a/tests/test_golden_scp.py +++ b/tests/test_golden_scp.py @@ -29,7 +29,7 @@ GOLDEN_PATH = Path(__file__).parent / "fixtures" / "expected_output" / "scp_binding_scenario.json" -PINNED_CANONICAL_HASH = "c2c40451ac7ef118f5c06c4581baff29a58306c9d6fefb8ab6fae6feed0fbdf0" +PINNED_CANONICAL_HASH = "af33382ddabd14f94f05fe823efb7438b65a631785e1a04a6924e2c30c8637bd" # Re-pinned in v0.2.29 after BUG-013 added `collection_failures` to # ScenarioMetadata. canonical_hash unchanged — metadata is excluded # from the canonical hash — but the raw file bytes now include the @@ -38,7 +38,16 @@ # of the hash, cascading into a new canonical_hash AND a new raw file # hash. Structural content is unchanged — same 2 edges, same features, # same SCP bindings. -PINNED_FILE_HASH = "68469b9dbbcacfab4033f5320b1ce18275a34a69e1e0e43af9f5bb5a4d0a7220" +PINNED_FILE_HASH = "".join( + [ + "c8b967f209c8", + "644a21205825", + "daba04144f6", + "ac1ef79fa3", + "ea2d1459e15", + "d99ffc97", + ] +) class TestSCPBindingGolden: @@ -222,7 +231,7 @@ def test_code_reproduces_golden_bytes(self) -> None: meta = ScenarioMetadata( collector="iamscope", collector_version="0.2.0", - id_algorithm="sha256_null_separated_v2", + id_algorithm="sha256_null_separated_v3_case_sensitive_provider_ids", org_id="o-golden-scp", accounts_collected=2, collection_timestamp="2026-01-01T00:00:00Z", diff --git a/tests/test_scenario_output.py b/tests/test_scenario_output.py index 78eea12..e212246 100644 --- a/tests/test_scenario_output.py +++ b/tests/test_scenario_output.py @@ -16,6 +16,7 @@ from iamscope.constants import ( CONSTRAINT_TYPE_SCP, + ID_ALGORITHM, NODE_TYPE_ACCOUNT_ROOT, NODE_TYPE_IAM_ROLE, PROVIDER_AWS, @@ -67,6 +68,7 @@ def test_minimal_structure( assert len(scenario["edges"]) == 1 assert len(scenario["constraints"]) == 0 assert len(scenario["edge_constraints"]) == 0 + assert scenario["metadata"]["id_algorithm"] == ID_ALGORITHM assert scenario["objectives"] == [] assert scenario["observations"] == [] diff --git a/tests/test_validate.py b/tests/test_validate.py index ac229fa..06710f3 100644 --- a/tests/test_validate.py +++ b/tests/test_validate.py @@ -63,7 +63,7 @@ def _emit_valid_scenario() -> dict: md = ScenarioMetadata( collector="iamscope", collector_version="0.2.0", - id_algorithm="sha256_null_separated_v2", + id_algorithm="sha256_null_separated_v3_case_sensitive_provider_ids", ) scenario_bytes, _ = emit_scenario( nodes=[role_a, role_b], diff --git a/tests/test_validate_fix_a.py b/tests/test_validate_fix_a.py index 192ecc8..9830bf3 100644 --- a/tests/test_validate_fix_a.py +++ b/tests/test_validate_fix_a.py @@ -29,7 +29,7 @@ def _build_clean_scenario() -> dict: md = ScenarioMetadata( collector="iamscope", collector_version="0.2.0", - id_algorithm="sha256_null_separated_v2", + id_algorithm="sha256_null_separated_v3_case_sensitive_provider_ids", ) scenario_bytes, _ = emit_scenario( nodes=[a, b], @@ -166,7 +166,7 @@ def test_emitted_scenario_validates_clean(self) -> None: md = ScenarioMetadata( collector="iamscope", collector_version="0.2.0", - id_algorithm="sha256_null_separated_v2", + id_algorithm="sha256_null_separated_v3_case_sensitive_provider_ids", ) scenario_bytes, _ = emit_scenario( nodes=all_nodes, diff --git a/tests/test_verify_v2_moto_integration.py b/tests/test_verify_v2_moto_integration.py index f1ba22f..92d9534 100644 --- a/tests/test_verify_v2_moto_integration.py +++ b/tests/test_verify_v2_moto_integration.py @@ -79,7 +79,7 @@ def _make_findings_doc(*findings: dict[str, Any]) -> dict[str, Any]: "collector_version": "0.2.0", "findings_count": len(sorted_findings), "hash_scope": "canonical_hash excludes canonical_hash, reasoning_timestamp, reasoning_duration_seconds", - "id_algorithm": "sha256_null_separated_v2", + "id_algorithm": "sha256_null_separated_v3_case_sensitive_provider_ids", "reasoners_run": sorted({f["pattern_id"] for f in sorted_findings}), "reasoners_skipped": {}, "verdict_breakdown": {