def test_write_template_with_sts_actions(self): cfg = { "mode": "crud", "name": "RoleNameWithCRUD", "sts": { "assume-role-with-web-identity": ["arn:aws:iam::123456789012:role/demo"] }, "list": [ "arn:aws:secretsmanager:us-east-1:123456789012:secret:anothersecret" ], } sid_group = SidGroup() rendered_policy = sid_group.process_template(cfg) desired_output = { "Version": "2012-10-17", "Statement": [{ "Sid": "AssumeRoleWithWebIdentity", "Effect": "Allow", "Action": ["sts:AssumeRoleWithWebIdentity"], "Resource": ["arn:aws:iam::123456789012:role/demo"] }] } # print(json.dumps(rendered_policy, indent=4)) self.assertEqual(rendered_policy, desired_output)
def test_write_actions_policy_with_library_only(self): """test_write_actions_policy_with_library_only: Write an actions mode policy without using the command line at all (library only)""" db_session = connect_db("bundled") actions_template = get_actions_template_dict() # print(actions_template) actions_to_add = [ "kms:CreateGrant", "kms:CreateCustomKeyStore", "ec2:AuthorizeSecurityGroupEgress", "ec2:AuthorizeSecurityGroupIngress", ] actions_template["mode"] = "actions" actions_template["actions"].extend(actions_to_add) # Modify it sid_group = SidGroup() minimize = None policy = sid_group.process_template( db_session, actions_template, minimize=minimize ) self.maxDiff = None # print("desired_actions_policy") # print(json.dumps(desired_actions_policy, indent=4)) # print("policy") print(json.dumps(policy, indent=4)) self.assertDictEqual(desired_actions_policy, policy)
def test_write_actions_policy_with_library_only(self): """test_write_actions_policy_with_library_only: Write an actions mode policy without using the command line at all (library only)""" actions_template = get_actions_template_dict() # print(actions_template) actions_to_add = [ "kms:CreateGrant", "kms:CreateCustomKeyStore", "ec2:AuthorizeSecurityGroupEgress", "ec2:AuthorizeSecurityGroupIngress", ] actions_template["mode"] = "actions" actions_template["actions"].extend(actions_to_add) # Modify it sid_group = SidGroup() minimize = None policy = sid_group.process_template(actions_template, minimize=minimize) self.maxDiff = None print(json.dumps(policy, indent=4)) expected_statement_ids = [ "KmsPermissionsmanagementKey", "MultMultNone", "Ec2WriteSecuritygroup" ] # self.assertDictEqual(policy, desired_actions_policy) for statement in policy.get("Statement"): self.assertTrue(statement.get("Sid") in expected_statement_ids)
def test_actions_test_case(self): cfg = { "mode": "actions", "name": "RoleNameWithCRUD", "description": "Why I need these privs", "role_arn": "arn:aws:iam::123456789012:role/RiskyEC2", "actions": [ "kms:CreateGrant", "kms:CreateCustomKeyStore", "ec2:AuthorizeSecurityGroupEgress", "ec2:AuthorizeSecurityGroupIngress", ], } sid_group = SidGroup() output = sid_group.process_template(db_session, cfg) print(json.dumps(output, indent=4)) desired_output = { "Version": "2012-10-17", "Statement": [ { "Sid": "KmsPermissionsmanagementKey", "Effect": "Allow", "Action": ["kms:CreateGrant"], "Resource": ["arn:${Partition}:kms:${Region}:${Account}:key/${KeyId}"], }, { "Sid": "Ec2WriteSecuritygroup", "Effect": "Allow", "Action": [ "ec2:AuthorizeSecurityGroupEgress", "ec2:AuthorizeSecurityGroupIngress", ], "Resource": [ "arn:${Partition}:ec2:${Region}:${Account}:security-group/${SecurityGroupId}" ], }, { "Sid": "MultMultNone", "Effect": "Allow", "Action": ["cloudhsm:DescribeClusters", "kms:CreateCustomKeyStore"], "Resource": ["*"], }, ], } self.maxDiff = None print(output) self.assertDictEqual(output, desired_output)
def test_write_with_template(self): cfg = { "mode": "crud", "name": "RoleNameWithCRUD", "permissions-management": ["arn:aws:s3:::example-org-s3-access-logs"], "list": [ "arn:aws:secretsmanager:us-east-1:123456789012:secret:anothersecret" ], } sid_group = SidGroup() rendered_policy = sid_group.process_template(cfg) desired_output = { "Version": "2012-10-17", "Statement": [{ "Sid": "S3PermissionsmanagementBucket", "Effect": "Allow", "Action": [ "s3:DeleteBucketPolicy", "s3:PutBucketAcl", "s3:PutBucketPolicy", "s3:PutBucketPublicAccessBlock", ], "Resource": ["arn:aws:s3:::example-org-s3-access-logs"], }], } # print(json.dumps(rendered_policy, indent=4)) self.assertEqual(rendered_policy, desired_output)
def test_add_by_list_of_actions(self): actions_test_data_1 = ["kms:CreateCustomKeyStore", "kms:CreateGrant"] sid_group = SidGroup() sid_group.add_by_list_of_actions(actions_test_data_1) output = sid_group.get_rendered_policy() desired_output = { "Version": "2012-10-17", "Statement": [ { "Sid": "KmsPermissionsmanagementKey", "Effect": "Allow", "Action": ["kms:CreateGrant"], "Resource": ["arn:${Partition}:kms:${Region}:${Account}:key/${KeyId}"], }, { "Sid": "MultMultNone", "Effect": "Allow", "Action": [ "cloudhsm:DescribeClusters", "kms:CreateCustomKeyStore", ], "Resource": ["*"], }, ], } print(json.dumps(output, indent=4)) self.maxDiff = None self.assertDictEqual(output, desired_output)
def test_add_crud_with_wildcard(self): cfg = { "mode": "crud", "name": "RoleNameWithCRUD", "description": "Why I need these privs", "role_arn": "arn:aws:iam::123456789012:role/RiskyEC2", "permissions-management": ["arn:aws:s3:::example-org-s3-access-logs"], "wildcard": [ # The first three are legitimately wildcard only. # Verify with `policy_sentry query action-table --service secretsmanager --wildcard-only` "ram:enablesharingwithawsorganization", "ram:getresourcepolicies", "secretsmanager:createsecret", # This last one can be "secret" ARN type OR wildcard. We want to prevent people from # bypassing this mechanism, while allowing them to explicitly # request specific privs that require wildcard mode. This next value - # secretsmanager:putsecretvalue - is an example of someone trying to beat the tool. "secretsmanager:putsecretvalue", ], } sid_group = SidGroup() rendered_policy = sid_group.process_template(db_session, cfg) desired_output = { "Version": "2012-10-17", "Statement": [ { "Sid": "MultMultNone", "Effect": "Allow", "Action": [ "ram:enablesharingwithawsorganization", "ram:getresourcepolicies", "secretsmanager:createsecret", ], "Resource": ["*"], }, { "Sid": "S3PermissionsmanagementBucket", "Effect": "Allow", "Action": [ "s3:deletebucketpolicy", "s3:putbucketacl", "s3:putbucketpolicy", "s3:putbucketpublicaccessblock", ], "Resource": ["arn:aws:s3:::example-org-s3-access-logs"], }, ], } # print(json.dumps(rendered_policy, indent=4)) self.assertDictEqual(rendered_policy, desired_output)
def test_minimize_rw_different(self): cfg = { "mode": "crud", "name": "", "read": [ "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter", "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter2" ], "write": [ "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter", "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter10" ] } sid_grp = SidGroup() write_format = sid_grp.process_template(cfg, minimize=1) write_format.pop('Version') self.assertEqual(len(write_format['Statement']), 2, "More than one statement returned, expected 1") self.assertEqual(write_format['Statement'][0]['Action'], ['ssm:getpar*'], "extra actions are returned") self.assertEqual(write_format['Statement'][0]['Resource'], cfg['read'], "Wrong resources were returned") self.assertEqual(write_format['Statement'][1]['Action'], ['ssm:deletepar*', 'ssm:la*', 'ssm:putp*'], "extra actions are returned") self.assertEqual(write_format['Statement'][1]['Resource'], cfg['write'], "Wrong resources were returned")
def test_write_crud_policy_with_library_only(self): """test_write_crud_policy_with_library_only: Write a policy in CRUD mode without using the command line at all (library only)""" another_crud_template = get_crud_template_dict() wildcard_actions_to_add = [ "kms:CreateCustomKeyStore", # "cloudhsm:describeclusters", ] another_crud_template["mode"] = "crud" another_crud_template["read"].append( "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret") another_crud_template["write"].append( "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret") another_crud_template["list"].append( "arn:aws:s3:::example-org-sbx-vmimport/stuff") another_crud_template["permissions-management"].append( "arn:aws:kms:us-east-1:123456789012:key/123456") another_crud_template["tagging"].append( "arn:aws:ssm:us-east-1:123456789012:parameter/test") another_crud_template["wildcard-only"]["single-actions"].extend( wildcard_actions_to_add) another_crud_template["sts"]["assume-role"].append( "arn:aws:iam::123456789012:role/demo") # Modify it sid_group = SidGroup() # minimize = None result = sid_group.process_template(another_crud_template) expected_statement_ids = [ "MultMultNone", "SecretsmanagerReadSecret", "SecretsmanagerWriteSecret", "S3ListObject", "SsmTaggingParameter", "KmsPermissionsmanagementKey", "AssumeRole" ] self.maxDiff = None print(json.dumps(result, indent=4)) for statement in result.get("Statement"): self.assertTrue(statement.get("Sid") in expected_statement_ids)
def test_write_crud_policy_with_library_only(self): """test_write_crud_policy_with_library_only: Write a policy in CRUD mode without using the command line at all (library only)""" db_session = connect_db("bundled") crud_template = get_crud_template_dict() wildcard_actions_to_add = [ "kms:CreateCustomKeyStore", # "cloudhsm:describeclusters", ] crud_template["mode"] = "crud" crud_template["read"].append( "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret" ) crud_template["write"].append( "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret" ) crud_template["list"].append("arn:aws:s3:::example-org-sbx-vmimport/stuff") crud_template["permissions-management"].append( "arn:aws:kms:us-east-1:123456789012:key/123456" ) crud_template["wildcard"].extend(wildcard_actions_to_add) crud_template["tagging"].append( "arn:aws:ssm:us-east-1:123456789012:parameter/test" ) # Modify it sid_group = SidGroup() minimize = None policy = sid_group.process_template( db_session, crud_template, minimize=minimize ) # print("desired_crud_policy") # print(json.dumps(desired_crud_policy, indent=4)) # print("policy") # print(json.dumps(policy, indent=4)) self.maxDiff = None self.assertDictEqual(desired_crud_policy, policy)
def test_exclude_actions_empty_sid_from_crud_output(self): sid_group = SidGroup() crud_with_exclude_actions_empty_sid = os.path.abspath( os.path.join( os.path.dirname(__file__), os.path.pardir, os.path.pardir, "examples", "yml", "crud-with-exclude-actions-empty-sid.yml", )) with open(crud_with_exclude_actions_empty_sid, "r") as this_yaml_file: crud_with_exclude_actions_empty_sid_cfg = yaml.safe_load( this_yaml_file) # crud_with_exclude_actions_empty_sid_cfg = { # "mode": "crud", # "write": [ # "arn:aws:s3:::test" # ], # "exclude-actions": [ # "iam:Pass*" # ] # } # print(json.dumps(crud_with_exclude_actions_empty_sid_cfg, indent=4)) sid_group.process_template(crud_with_exclude_actions_empty_sid_cfg) result = sid_group.get_rendered_policy( crud_with_exclude_actions_empty_sid_cfg) # print(json.dumps(result, indent=4)) expected_result = { "Version": "2012-10-17", "Statement": [{ "Sid": "S3WriteBucket", "Effect": "Allow", "Action": [ "s3:CreateBucket", "s3:DeleteBucket", "s3:DeleteBucketOwnershipControls", "s3:DeleteBucketWebsite", "s3:PutAccelerateConfiguration", "s3:PutAnalyticsConfiguration", "s3:PutBucketCORS", "s3:PutBucketLogging", "s3:PutBucketNotification", "s3:PutBucketObjectLockConfiguration", "s3:PutBucketOwnershipControls", "s3:PutBucketRequestPayment", "s3:PutBucketVersioning", "s3:PutBucketWebsite", "s3:PutEncryptionConfiguration", "s3:PutIntelligentTieringConfiguration", "s3:PutInventoryConfiguration", "s3:PutLifecycleConfiguration", "s3:PutMetricsConfiguration", "s3:PutReplicationConfiguration" ], "Resource": ["arn:aws:s3:::test"] }] } self.assertDictEqual(result, expected_result)
def test_add_crud_with_wildcard(self): cfg = { "mode": "crud", "name": "RoleNameWithCRUD", "permissions-management": ["arn:aws:s3:::example-org-s3-access-logs"], "wildcard-only": { "single-actions": [ # The first three are legitimately wildcard only. # Verify with `policy_sentry query action-table --service secretsmanager --wildcard-only` "ram:enablesharingwithawsorganization", "ram:getresourcepolicies", "secretsmanager:createsecret", # This last one can be "secret" ARN type OR wildcard. We want to prevent people from # bypassing this mechanism, while allowing them to explicitly # request specific privs that require wildcard mode. This next value - # secretsmanager:putsecretvalue - is an example of someone trying to beat the tool. "secretsmanager:putsecretvalue", ], } } sid_group = SidGroup() output = sid_group.process_template(db_session, cfg) desired_output = { "Version": "2012-10-17", "Statement": [ { "Sid": "MultMultNone", "Effect": "Allow", "Action": [ "ram:EnableSharingWithAwsOrganization", "ram:GetResourcePolicies", "secretsmanager:CreateSecret", ], "Resource": ["*"], }, { "Sid": "S3PermissionsmanagementBucket", "Effect": "Allow", "Action": [ "s3:DeleteBucketPolicy", "s3:PutBucketAcl", "s3:PutBucketPolicy", "s3:PutBucketPublicAccessBlock", ], "Resource": ["arn:aws:s3:::example-org-s3-access-logs"], }, ], } self.maxDiff = None print("Yolo") print(json.dumps(output, indent=4)) self.assertDictEqual(output, desired_output)
def test_minimize_rw_same(self): cfg = { "mode": "crud", "name": "", "read": [ "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter", "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter2" ], "write": [ "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter", "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter2" ] } sid_grp = SidGroup() write_format = sid_grp.process_template(cfg, minimize=1) # print(json.dumps(write_format, indent=4)) """ the output will look like: { "Version": "2012-10-17", "Statement": [ { "Sid": "SsmParameterMyparameter", "Effect": "Allow", "Action": [ "ssm:getpar*", "ssm:deletepar*", "ssm:la*", "ssm:putp*" ], "Resource": [ "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter", "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter2" ] } ] } """ # To future-proof this unit test... # (1) check that there is only one SID so it was combined during minimization sid_names = get_sid_names_from_policy(write_format) self.assertEqual(len(sid_names), 1, "More than one statement returned, expected 1") # (2) Check for the presence of certain actions that we know will be there expected_action = [ 'ssm:getpar*', 'ssm:deletepar*', 'ssm:la*', 'ssm:putp*' ] self.assertEqual(write_format['Statement'][0]['Action'], expected_action, "extra actions are returned") self.assertEqual(write_format['Statement'][0]['Resource'], cfg['read'], "Wrong resources were returned")
def test_minimize_rw_different(self): cfg = { "mode": "crud", "name": "", "read": [ "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter", "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter2" ], "write": [ "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter", "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter10" ] } sid_grp = SidGroup() write_format = sid_grp.process_template(cfg, minimize=1) """ Expected result: { "Version": "2012-10-17", "Statement": [ { "Sid": "SsmReadParameter", "Effect": "Allow", "Action": [ "ssm:getpar*" ], "Resource": [ "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter", "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter2" ] }, { "Sid": "SsmWriteParameter", "Effect": "Allow", "Action": [ "ssm:deletepar*", "ssm:la*", "ssm:putp*" ], "Resource": [ "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter", "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter10" ] } ] } """ self.assertEqual(len(write_format['Statement']), 2, "More than two statements returned, expected 2") self.assertEqual(write_format['Statement'][0]['Action'], ['ssm:getpar*'], "extra actions are returned") self.assertEqual(write_format['Statement'][0]['Resource'], cfg['read'], "Wrong resources were returned") self.assertEqual(write_format['Statement'][1]['Action'], ['ssm:deletepar*', 'ssm:la*', 'ssm:putp*'], "extra actions are returned") self.assertEqual(write_format['Statement'][1]['Resource'], cfg['write'], "Wrong resources were returned")
def test_minimize_arn_case_7(self): """minimization test with ARN types from test_does_arn_match_case_1""" cfg = { "mode": "crud", "read": ["arn:aws:greengrass:${Region}:${Account}:/greengrass/definition/devices/1234567/versions/1"], "write": ["arn:aws:greengrass:${Region}:${Account}:/greengrass/definition/devices/1234567/versions/1"] } sid_group = SidGroup() results = sid_group.process_template(cfg, minimize=0) sid_names = get_sid_names_from_policy(results) self.assertEqual(len(sid_names), 1, "More than one statement returned, expected 1")
def write_policy_with_template(db_session, cfg, minimize=None): """ This function is called by write-policy so the config can be passed in as a dict without running into a Click-related error. Use this function, rather than the write-policy function, if you are using Policy Sentry as a python library. :param db_session: SQL Alchemy database session object :param cfg: The loaded YAML as a dict. Must follow Policy Sentry dictated format. :param minimize: Minimize the resulting statement with *safe* usage of wildcards to reduce policy length. Set this to the character length you want - for example, 0, or 4. Defaults to none. """ sid_group = SidGroup() policy = sid_group.process_template(db_session, cfg, minimize) return policy
def test_minimize_arn_case_6(self): """minimization test with ARN types from test_does_arn_match_case_1""" cfg = { "mode": "crud", "read": ["arn:aws:states:region:account-id:execution:stateMachineName:executionName"], "write": ["arn:aws:states:region:account-id:execution:stateMachineName:executionName"] } sid_group = SidGroup() results = sid_group.process_template(cfg, minimize=0) sid_names = get_sid_names_from_policy(results) self.assertEqual(len(sid_names), 1, "More than one statement returned, expected 1")
def test_minimize_arn_case_1(self): """minimization test with ARN types from test_does_arn_match_case_1""" cfg = { "mode": "crud", "read": ["arn:aws:codecommit:us-east-1:123456789012:MyDemoRepo"], "write": ["arn:aws:codecommit:us-east-1:123456789012:MyDemoRepo"] } sid_group = SidGroup() results = sid_group.process_template(cfg, minimize=0) sid_names = get_sid_names_from_policy(results) self.assertEqual(len(sid_names), 1, "More than one statement returned, expected 1")
def test_minimize_arn_case_3(self): """minimization test with ARN types from test_does_arn_match_case_1""" cfg = { "mode": "crud", "read": ["arn:aws:kinesis:us-east-1:account-id:firehose/myfirehose/consumer/someconsumer:${ConsumerCreationTimpstamp}"], "write": ["arn:aws:kinesis:us-east-1:account-id:firehose/myfirehose/consumer/someconsumer:${ConsumerCreationTimpstamp}"] } sid_group = SidGroup() results = sid_group.process_template(cfg, minimize=0) sid_names = get_sid_names_from_policy(results) self.assertEqual(len(sid_names), 1, "More than one statement returned, expected 1")
def write_policy_with_template(cfg, minimize=None): """ This function is called by write-policy so the config can be passed in as a dict without running into a Click-related error. Use this function, rather than the write-policy function, if you are using Policy Sentry as a python library. Arguments: cfg: The loaded YAML as a dict. Must follow Policy Sentry dictated format. minimize: Minimize the resulting statement with *safe* usage of wildcards to reduce policy length. Set this to the character length you want - for example, 0, or 4. Defaults to none. Returns: Dictionary: The JSON policy """ if minimize is not None and minimize < 0: minimize = None sid_group = SidGroup() policy = sid_group.process_template(cfg, minimize) return policy
def test_minimize_arn_case_bucket(self): cfg = { "mode": "crud", "read": ["arn:aws:s3:::bucket_name"], "write": ["arn:aws:s3:::bucket_name"] } sid_group = SidGroup() results = sid_group.process_template(cfg, minimize=0) # print(json.dumps(results, indent=4)) # To future-proof this unit test... # (1) check that there is only one SID so it was combined during minimization sid_names = get_sid_names_from_policy(results) # For S3, it does require iam:PassRole lol because of s3:PutReplicationConfiguration requiring it as an action self.assertEqual(len(sid_names), 2, "More than 2 statements returned, expected 2")
def test_sid_group_override(self): sid_group = SidGroup() output = sid_group.process_template(crud_with_override_template_cfg) self.maxDiff = None desired_output = { "Version": "2012-10-17", "Statement": [ { "Sid": "MultMultNone", "Effect": "Allow", "Action": [ "secretsmanager:CreateSecret" ], "Resource": [ "*" ] }, { "Sid": "S3PermissionsmanagementBucket", "Effect": "Allow", "Action": [ "s3:DeleteBucketPolicy", "s3:PutBucketAcl", "s3:PutBucketPolicy", "s3:PutBucketPublicAccessBlock" ], "Resource": [ "arn:aws:s3:::example-org-s3-access-logs" ] }, { "Sid": "SkipResourceConstraints", "Effect": "Allow", "Action": [ "ssm:GetParameter", "ssm:GetParameters", "ssm:GetParametersByPath" ], "Resource": [ "*" ] } ] } print(json.dumps(output, indent=4)) self.assertDictEqual(output, desired_output)
def test_resource_restriction_plus_dependent_action(self): """ test_resource_restriction_plus_dependent_action """ # Given iam:generateorganizationsaccessreport with resource constraint, make sure these are added: # organizations:DescribePolicy,organizations:ListChildren,organizations:ListParents, # organizations:ListPoliciesForTarget,organizations:ListRoots,organizations:ListTargetsForPolicy actions_test_data_1 = ["iam:generateorganizationsaccessreport"] sid_group = SidGroup() sid_group.add_by_list_of_actions(actions_test_data_1) output = sid_group.get_rendered_policy() # print(json.dumps(rendered_policy, indent=4)) desired_output = { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "organizations:DescribePolicy", "organizations:ListChildren", "organizations:ListParents", "organizations:ListPoliciesForTarget", "organizations:ListRoots", "organizations:ListTargetsForPolicy", ], "Resource": ["*"], "Sid": "MultMultNone", }, { "Sid": "IamReadAccessreport", "Effect": "Allow", "Action": ["iam:GenerateOrganizationsAccessReport"], "Resource": [ "arn:${Partition}:iam::${Account}:access-report/${EntityPath}" ], }, ], } self.maxDiff = None print(json.dumps(output, indent=4)) self.assertDictEqual(output, desired_output)
def test_resource_restriction_plus_dependent_action_simple_2(self): """ test_resource_restriction_plus_dependent_action_simple_2 """ # Given iam:generateorganizationsaccessreport with resource constraint, make sure these are added: # organizations:DescribePolicy,organizations:ListChildren,organizations:ListParents, # organizations:ListPoliciesForTarget,organizations:ListRoots,organizations:ListTargetsForPolicy sid_group = SidGroup() sid_group.add_by_arn_and_access_level( db_session, ["arn:aws:iam::000000000000:access-report/somepath"], "Read") output = sid_group.get_rendered_policy(db_session) desired_output = { "Version": "2012-10-17", "Statement": [ { "Sid": "MultMultNone", "Effect": "Allow", "Action": [ "organizations:DescribePolicy", "organizations:ListChildren", "organizations:ListParents", "organizations:ListPoliciesForTarget", "organizations:ListRoots", "organizations:ListTargetsForPolicy", ], "Resource": ["*"], }, { "Sid": "IamReadAccessreport", "Effect": "Allow", "Action": ["iam:GenerateOrganizationsAccessReport"], "Resource": ["arn:aws:iam::000000000000:access-report/somepath"], }, ], } print(json.dumps(output, indent=4)) self.assertDictEqual(output, desired_output)
def test_GH_364_remove_duplicates_from_minimize(self): cfg = { "mode": "crud", "read": ["arn:aws:rds:*:*:*:test-*"], "write": ["arn:aws:rds:*:*:*:test-*"], "list": ["arn:aws:rds:*:*:*:test-*"], "tagging": ["arn:aws:rds:*:*:*:test-*"], "permissions-management": ["arn:aws:rds:*:*:*:test-*"], } # Bug introduced in https://github.com/salesforce/policy_sentry/pull/252 sid_group = SidGroup() results = sid_group.process_template(cfg, minimize=0) print(json.dumps(results, indent=4)) actions = results["Statement"][0]["Action"] actions_set = set(actions) self.assertEqual( len(actions_set), len(actions), "There should be no duplicate strings in the actions.")
def test_sid_group_override(self): sid_group = SidGroup() output = sid_group.process_template(crud_with_override_template_cfg) self.maxDiff = None desired_output = { "Version": "2012-10-17", "Statement": [{ "Sid": "MultMultNone", "Effect": "Allow", "Action": ["ram:GetResourcePolicies"], "Resource": ["*"] }, { "Sid": "S3PermissionsmanagementBucket", "Effect": "Allow", "Action": [ "s3:DeleteBucketPolicy", "s3:PutBucketAcl", "s3:PutBucketPolicy", "s3:PutBucketPublicAccessBlock" ], "Resource": ["arn:aws:s3:::example-org-s3-access-logs"] }, { "Sid": "SkipResourceConstraints", "Effect": "Allow", "Action": [ "ssm:GetParameter", "ssm:GetParameters", "ssm:GetParametersByPath" ], "Resource": ["*"] }] } # print(json.dumps(output, indent=4)) expected_statement_ids = [ "MultMultNone", "S3PermissionsmanagementBucket", "SkipResourceConstraints" ] for statement in output.get("Statement"): self.assertTrue(statement.get("Sid") in expected_statement_ids)
def test_minimize_rw_same_one(self): cfg = { "mode": "crud", "name": "", "read": [ "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter", ], "write": [ "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter", ] } sid_grp = SidGroup() write_format = sid_grp.process_template(cfg, minimize=0) """ Expected result: { "Version": "2012-10-17", "Statement": [ { "Sid": "SsmMultParametermyparameter", "Effect": "Allow", "Action": [ "ssm:getpar*", "ssm:deletepar*", "ssm:la*", "ssm:putp*" ], "Resource": [ "arn:aws:ssm:us-east-1:123456789012:parameter/myparameter" ] } ] } """ # To future-proof this unit test... # (1) check that there is only one SID so it was combined during minimization sid_names = get_sid_names_from_policy(write_format) self.assertEqual(len(sid_names), 1, "More than one statement returned, expected 1")
def test_write_with_template(self): cfg = { "mode": "crud", "name": "RoleNameWithCRUD", "description": "Why I need these privs", "role_arn": "arn:aws:iam::123456789012:role/RiskyEC2", "permissions-management": ["arn:aws:s3:::example-org-s3-access-logs"], "list": [ "arn:aws:secretsmanager:us-east-1:123456789012:secret:anothersecret" ], } sid_group = SidGroup() rendered_policy = sid_group.process_template(db_session, cfg) desired_output = { "Version": "2012-10-17", "Statement": [{ "Sid": "S3PermissionsmanagementBucket", "Effect": "Allow", "Action": [ "s3:deletebucketpolicy", "s3:putbucketacl", "s3:putbucketpolicy", "s3:putbucketpublicaccessblock", ], "Resource": ["arn:aws:s3:::example-org-s3-access-logs"], }], } # print(json.dumps(rendered_policy, indent=4)) self.assertEqual(rendered_policy, desired_output)
def test_sid_group(self): desired_output = { "S3PermissionsmanagementBucket": { "arn": ["arn:aws:s3:::example-org-s3-access-logs"], "service": "s3", "access_level": "Permissions management", "arn_format": "arn:${Partition}:s3:::${BucketName}", "actions": [ "s3:DeleteBucketPolicy", "s3:PutBucketAcl", "s3:PutBucketPolicy", "s3:PutBucketPublicAccessBlock", ], "conditions": [], } } sid_group = SidGroup() arn_list_from_user = ["arn:aws:s3:::example-org-s3-access-logs"] access_level = "Permissions management" sid_group.add_by_arn_and_access_level(db_session, arn_list_from_user, access_level) status = sid_group.get_sid_group() self.maxDiff = None # print(json.dumps(status, indent=4)) self.assertEqual(status, desired_output) rendered_policy = sid_group.get_rendered_policy(db_session) desired_policy = { "Version": "2012-10-17", "Statement": [{ "Sid": "S3PermissionsmanagementBucket", "Effect": "Allow", "Action": [ "s3:DeleteBucketPolicy", "s3:PutBucketAcl", "s3:PutBucketPolicy", "s3:PutBucketPublicAccessBlock", ], "Resource": ["arn:aws:s3:::example-org-s3-access-logs"], }], } # print(json.dumps(rendered_policy, indent=4)) self.maxDiff = None self.assertDictEqual(desired_policy, rendered_policy)
def test_exclude_actions_from_crud_output(self): sid_group = SidGroup() crud_with_exclude_actions = os.path.abspath( os.path.join( os.path.dirname(__file__), os.path.pardir, os.path.pardir, "examples", "yml", "crud-with-exclude-actions.yml", )) with open(crud_with_exclude_actions, "r") as this_yaml_file: crud_with_exclude_actions_cfg = yaml.safe_load(this_yaml_file) # crud_with_exclude_actions_cfg = { # "mode": "crud", # "write": [ # "arn:aws:kms:us-east-1:123456789012:key/aaaa-bbbb-cccc" # ], # "exclude-actions": [ # "kms:Delete*" # ] # } # print(json.dumps(crud_with_exclude_actions_cfg, indent=4)) sid_group.process_template(crud_with_exclude_actions_cfg) result = sid_group.get_rendered_policy(crud_with_exclude_actions_cfg) print(json.dumps(result, indent=4)) expected_result = { "Version": "2012-10-17", "Statement": [{ "Sid": "KmsWriteKey", "Effect": "Allow", "Action": [ "kms:CancelKeyDeletion", "kms:CreateAlias", "kms:Decrypt", "kms:EnableKey", "kms:EnableKeyRotation", "kms:Encrypt", "kms:GenerateDataKey", "kms:GenerateDataKeyPair", "kms:GenerateDataKeyPairWithoutPlaintext", "kms:GenerateDataKeyWithoutPlaintext", "kms:ImportKeyMaterial", "kms:ReEncryptFrom", "kms:ReEncryptTo", "kms:Sign", "kms:UpdateAlias", "kms:UpdateKeyDescription", "kms:Verify" ], "Resource": ["arn:aws:kms:us-east-1:123456789012:key/aaaa-bbbb-cccc"] }] } self.assertDictEqual(result, expected_result) crud_with_exclude_actions_cfg = { "mode": "crud", "write": ["arn:aws:kms:us-east-1:123456789012:key/aaaa-bbbb-cccc"], # This is really only because KMS is a special case. "exclude-actions": [ "kms:Delete*", "kms:Disable*", "kms:Enable*", "kms:Generate*", "kms:Cancel*", "kms:Create*", "kms:Import*", "kms:ReEncrypt*", "kms:Sign*", "kms:Schedule*", "kms:Update*", "kms:Verify*" ] } sid_group.process_template(crud_with_exclude_actions_cfg) results = sid_group.get_rendered_policy() print(json.dumps(results, indent=4)) expected_result = { "Version": "2012-10-17", "Statement": [{ "Sid": "KmsWriteKey", "Effect": "Allow", "Action": ["kms:Decrypt", "kms:Encrypt"], "Resource": ["arn:aws:kms:us-east-1:123456789012:key/aaaa-bbbb-cccc"] }] } self.assertDictEqual(results, expected_result)