def test_audit(self): statement = {'Effect': 'Allow', 'Principal': '*', 'Condition': {}} expected = 'Service is publicly accessible' self.assertTrue( expected in next(PolicyStatement(statement).audit())['text']) statement = {'Effect': 'Allow', 'NotPrincipal': 'arn:aws:'} expected = 'NotPrincipal with Allow' self.assertTrue( expected in next(PolicyStatement(statement).audit())['text']) statement = { 'Effect': 'Allow', 'Resource': 'arn:aws:sqs:eu-west-1:*:queue' } expected = 'Unrestricted AWS Account ID' self.assertTrue( expected in next(PolicyStatement(statement).audit())['text']) statement = {'Effect': 'Allow', 'Action': 'sqs:*'} expected = 'Unrestricted Action' self.assertTrue( expected in next(PolicyStatement(statement).audit())['text']) statement = {'Effect': 'Allow', 'NotAction': 'sqs:SendMessage'} expected = 'NotAction with Allow' self.assertTrue( expected in next(PolicyStatement(statement).audit())['text'])
def test_is_unrestricted(self): policy = json.loads( self.fixtures.joinpath('PolicyUnrestricted.json').read_text()) for statement in policy['Statement']: print(statement) self.assertTrue( PolicyStatement(statement).is_unrestricted('Principal')) self.assertTrue( PolicyStatement(statement).is_unrestricted('Action'))
def scan(self): ''' Scan Lambda report for vulnerabilities and provide recommendations. ''' # Audit Function policy if not self.report['policy']['function']: self.track(self.report['arn'], { 'level': 'info', 'text': 'Function policy is not defined' }) else: if 'Statement' in self.report['policy']['function']: for statement in self.report['policy']['function'][ 'Statement']: for _ in PolicyStatement(statement).audit(): self.track(self.report['arn'], _) # Audit Execution role policy if not len(self.report['policy']['role']['policies']): self.track(self.report['arn'], { 'level': 'info', 'text': 'Execution Role policy is not defined' }) else: for policy in self.report['policy']['role']['policies']: if 'Statement' in policy['document']: for statement in policy['document']['Statement']: for _ in PolicyStatement(statement, policy=policy).audit(): self.track(self.report['role'], _) # Audit Resources if 'logs' not in self.report['resources']['services']: self.track( self.report['arn'], { 'level': 'low', 'text': 'Function activity is not monitored by CloudWatch due to missing logs permissions' }) # Audit Triggers and Resources items = list( set( list(self.report['triggers']['items'].keys()) + list(self.report['resources']['items'].keys()))) for arn in items: if arn == '*': continue arn = arnparse(arn) if arn.resource in ['*', '']: continue self.item = None if arn.service == 's3': if arn.resource_type: # S3 object path continue self.item = S3(arn.full, profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key) for _ in AccessControlList(self.item.acl).audit(): self.track(arn.full, _) for _ in Encryption(self.item).audit(): self.track(arn.full, _) elif arn.service == 'sqs': self.item = SQS(arn.full, profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key) elif arn.service == 'sns': self.item = SNS(arn.full, profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key) elif arn.service == 'apigateway': self.item = APIGateway( arn.full, profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key) elif arn.service == 'dynamodb': self.item = DynamoDB(arn.full, profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key) if self.item: # Audit item Resource-based Policy if 'Statement' in self.item.policy: for statement in self.item.policy['Statement']: for _ in PolicyStatement(statement).audit(): self.track(arn.full, _) # If policy is missing, the the service is public else: for _ in Public(self.item).audit(): self.track(arn.full, _) # SonarQube if self.args.sonarqube: self.scan_sonarqube(arn, self.report['codeURL'], self.report['runtime']) for layer in self.report['layers']: self.scan_sonarqube(arn, layer['codeURL'], self.report['runtime']) # Sort findings by level sorted_items = [] for sort in ['high', 'medium', 'low', 'info']: for item in self.security['items']: if item['level'] == sort: if item not in sorted_items: sorted_items.append(item) self.security['items'] = sorted_items
def audit_policy_statements(self, arn, policy): if 'Statement' in policy: for statement in policy['Statement']: for _ in PolicyStatement(statement).audit(): self.track(arn.full, _)
def test_is_undefined(self): policy = json.loads(self.fixtures.joinpath('PolicyUnrestricted.json').read_text()) for statement in policy['Statement']: self.assertTrue(PolicyStatement(statement).is_undefined('Condition'))
def scan(self): """ Scan Lambda report for vulnerabilities and provide recommendations. """ # Audit Function policy if not self.report["policy"]["function"]: self.track( self.report["arn"], { "level": "info", "text": "Function policy is not defined" }, ) else: if "Statement" in self.report["policy"]["function"]: for statement in self.report["policy"]["function"][ "Statement"]: for _ in PolicyStatement(statement).audit(): self.track(self.report["arn"], _) # Audit Execution role policy if not len(self.report["policy"]["role"]["policies"]): self.track( self.report["arn"], { "level": "info", "text": "Execution Role policy is not defined" }, ) else: for policy in self.report["policy"]["role"]["policies"]: if "Statement" in policy["document"]: for statement in policy["document"]["Statement"]: for _ in PolicyStatement(statement, policy=policy).audit(): self.track(self.report["role"], _) # Audit KMS if "kms" in self.report: self.item = KMS( self.report["kms"], profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key, ) if not self.item.rotation: self.track( self.report["kms"], { "level": "medium", "text": ("Automatic rotation of key material is disabled\n" "https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html" ), }, ) for _, policy in self.item.policies.items(): self.audit_policy_statements(self.item.arn, policy) # Audit Resources if "logs" not in self.report["resources"]["services"]: self.track( self.report["arn"], { "level": "low", "text": "Function activity is not monitored by CloudWatch due to missing logs permissions", }, ) # Audit Triggers and Resources items = list( set( list(self.report["triggers"]["items"].keys()) + list(self.report["resources"]["items"].keys()))) for arn in items: if arn == "*": continue arn = arnparse(arn) if arn.resource in ["*", ""]: continue self.item = None if arn.service == "s3": if arn.resource_type: # S3 object path continue self.item = S3( arn.full, profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key, ) for _ in AccessControlList(self.item.acl).audit(): self.track(arn.full, _) for _ in Encryption(self.item).audit(): self.track(arn.full, _) elif arn.service == "sqs": self.item = SQS( arn.full, profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key, ) elif arn.service == "sns": self.item = SNS( arn.full, profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key, ) elif arn.service == "apigateway": self.item = APIGateway( arn.full, profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key, ) elif arn.service == "dynamodb": self.item = DynamoDB( arn.full, profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key, ) elif arn.service == "kms": self.item = KMS( arn.full, profile=self.profile, access_key_id=self.access_key_id, secret_access_key=self.secret_access_key, ) if self.item: if type(self.item) == KMS: # Audit KMS Policies for _, policy in self.item.policies.items(): self.audit_policy_statements(self.item.arn, policy) else: # Audit item Resource-based Policy self.audit_policy_statements(self.item.arn, self.item.policy) # If policy is missing, then the service is public for _ in Public(self.item).audit(): self.track(arn.full, _) # SonarQube if self.args.sonarqube: self.scan_sonarqube(arn, self.report["codeURL"], self.report["runtime"]) for layer in self.report["layers"]: self.scan_sonarqube(arn, layer["codeURL"], self.report["runtime"]) # Sort findings by level sorted_items = [] for sort in ["high", "medium", "low", "info"]: for item in self.security["items"]: if item["level"] == sort: if item not in sorted_items: sorted_items.append(item) self.security["items"] = sorted_items