let allowed_accounts = [ # we use regex over string, so that we can match inside ARNs as well
    /111122223333/,
    /444455556666/
]

rule sns_cross_account_only_allowed_accounts {
    Resources.* 
    {
        when Type == 'AWS::SNS::TopicPolicy' 
        {
            Properties.PolicyDocument.Statement[*] 
            {
                when Effect         ==  "Allow"
                {
                    Action[*]       ==  /(sns|SNS):Publish/          # ensure that we do not sneak in other permission other than sns:Publish

                    #
                    # Check for different combinations. 
                    # 
                    # We are not using OR disjunction as all of these 
                    # can ALL occur within the same statement and must be checked 
                    # 
                    when Principal is_string {
                        Principal   in  %allowed_accounts                   # when used directly as a string for principal
                    }

                    when Principal.AWS exists {
                        Principal.AWS[*] in %allowed_accounts               # when used for providing access from other accounts
                    }

                    # 
                    # when accessed via any AWS service, one MUST specify a Condition with sourceAccount|Owner
                    # Arn. 
                    # 
                    when Principal.Service exists {
                        # 
                        # when accessed via any AWS services, ensure that source account is only from allowed lists
                        # We want to check StringEquals, StringLike, ArnEquals, ArnLike checks
                        # 
                        let expected_conditions = Condition[ keys == /String(Equals|Like)|Arn(Equals|Like)/ ].*

                        # 
                        # Ensure that these are specified, else it is an error 
                        # 
                        %expected_conditions not empty 

                        # 
                        # Then extract values against these (aws|AWS):[sS]ourceAccount, (aws|AWS):[sS]ourceOwner, (aws|AWS):[sS]ource(Arn|ARN)
                        # 
                        let source_accounts = %expected_conditions[ keys == /(aws|AWS):[sS]ource(Account|Owner|Arn|ARN)/ ].*

                        # 
                        # It is an error to not specify this. Ensure the ones specified match allowed accounts
                        #
                        %source_accounts in %allowed_accounts
                    }
                }
            }
        }
    }
}
