diff --git a/policies/aws_iam_policies/aws_iam_role_github_actions_trust.py b/policies/aws_iam_policies/aws_iam_role_github_actions_trust.py index 77ef6b02d..5f0cdbe55 100644 --- a/policies/aws_iam_policies/aws_iam_role_github_actions_trust.py +++ b/policies/aws_iam_policies/aws_iam_role_github_actions_trust.py @@ -1,38 +1,55 @@ +import json + from panther_base_helpers import deep_get def policy(resource): - assume_role_policy = deep_get(resource, "AssumeRolePolicyDocument", "Statement", default=[]) - is_valid = False + # check if resource.AssumRolePolicyDocument is a string, and if so convert to json + if isinstance(resource.get("AssumeRolePolicyDocument"), str): + policy_document = json.loads(resource.get("AssumeRolePolicyDocument", {})) + else: + policy_document = resource.get("AssumeRolePolicyDocument", {}) + assume_role_policy = policy_document.get("Statement", []) for statement in assume_role_policy: - if statement.get( - "Effect" - ) != "Allow" or "sts:AssumeRoleWithWebIdentity" not in statement.get("Action", []): + # only check for Allow sts:AssumeRoleWithWebIdentity + if ( + statement.get("Effect") != "Allow" + or statement.get("Action") != "sts:AssumeRoleWithWebIdentity" + ): continue principal = deep_get(statement, "Principal", "Federated") - if not principal or principal == "*": - return False - if "oidc-provider/token.actions.githubusercontent.com" not in principal: - continue - - # Validate the conditions only if the Principal is valid for GitHub Actions - conditions = statement.get("Condition", {}) - audience = deep_get(conditions, "StringEquals", "token.actions.githubusercontent.com:aud") + audience = deep_get( + statement, "Condition", "StringEquals", "token.actions.githubusercontent.com:aud" + ) subject = deep_get( - conditions, "StringLike", "token.actions.githubusercontent.com:sub", default="" + statement, + "Condition", + "StringLike", + "token.actions.githubusercontent.com:sub", + default="", ) or deep_get( - conditions, "StringEquals", "token.actions.githubusercontent.com:sub", default="" + statement, + "Condition", + "StringEquals", + "token.actions.githubusercontent.com:sub", + default="", ) - if ( - audience != "sts.amazonaws.com" - or not subject.startswith("repo:") - or ("*" in subject and not subject.startswith("repo:org/repo:*")) - ): - return False - - is_valid = True # Mark as valid if all checks pass - - return is_valid + if subject.startswith("repo:"): + # repo subjects must have github as the principal and sts.amazonaws.com as the audience + if any( + [ + "oidc-provider/token.actions.githubusercontent.com" not in principal, + audience != "sts.amazonaws.com", + ("*" in subject and not subject.startswith("repo:org/repo:*")), + ] + ): + return False + else: + # non-repo subjects must not have github as the principal + if "oidc-provider/token.actions.githubusercontent.com" in principal: + return False + + return True diff --git a/policies/aws_iam_policies/aws_iam_role_github_actions_trust.yml b/policies/aws_iam_policies/aws_iam_role_github_actions_trust.yml index 9dee27bb7..f6b9757da 100644 --- a/policies/aws_iam_policies/aws_iam_role_github_actions_trust.yml +++ b/policies/aws_iam_policies/aws_iam_role_github_actions_trust.yml @@ -200,3 +200,25 @@ Tests: ] } } + + - Name: Non-GitHub IAM Role + ExpectedResult: true + Resource: + { + "AccountId": "123412341233", + "Arn": "arn:aws:iam::123412341233:role/DevAdministrator", + "AssumeRolePolicyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"arn:aws:iam::12341523456:root\",\"arn:aws:iam::123412341233:root\"]},\"Action\":\"sts:AssumeRole\",\"Condition\":{\"Bool\":{\"aws:MultiFactorAuthPresent\":\"true\",\"aws:SecureTransport\":\"true\"},\"NumericLessThan\":{\"aws:MultiFactorAuthAge\":\"28800\"}}}]}", + "ManagedPolicyARNs": [ + "arn:aws:iam::aws:policy/AdministratorAccess" + ], + "ManagedPolicyNames": [ + "AdministratorAccess" + ], + "MaxSessionDuration": 28800, + "Name": "DevAdministrator", + "Path": "/", + "Region": "global", + "ResourceId": "arn:aws:iam::123412341233:role/DevAdministrator", + "ResourceType": "AWS.IAM.Role", + "TimeCreated": "2023-11-08T23:50:46Z" + } \ No newline at end of file