diff --git a/conf/outputs.json b/conf/outputs.json index bd4dcd6ba..9e0a821b5 100644 --- a/conf/outputs.json +++ b/conf/outputs.json @@ -2,8 +2,11 @@ "aws-lambda": { "sample-lambda": "function-name:qualifier" }, + "aws-firehose": { + "alerts": "_streamalert_alert_delivery" + }, "aws-s3": { - "sample-bucket": "sample.bucket.name" + "bucket": "aws-s3-bucket" }, "komand": [ "sample-integration" diff --git a/docs/source/rules.rst b/docs/source/rules.rst index 46f2ed6ed..af5467c25 100644 --- a/docs/source/rules.rst +++ b/docs/source/rules.rst @@ -129,9 +129,11 @@ An alert can be sent to multiple destinations. req_subkeys ~~~~~~~~~~~ -``req_subkeys`` is an optional argument which defines required sub-keys that must exist in the incoming record in order for it to be evaluated. +``req_subkeys`` is an optional argument which defines sub-keys that must exist in the incoming record in order for it to be evaluated. -This feature should be avoided, but it is useful if you defined a loose schema to trade flexibility for safety; see `Schemas `_. +Each defined sub-key must have a non zero value as well in order for the rule to evaluate the log. + +This feature should be used if you have logs with a loose schema defined in order to avoid ``KeyError`` in rules. Examples: @@ -143,15 +145,11 @@ Examples: @rule(logs=['osquery:differential'], outputs=['pagerduty', 'aws-s3'], req_subkeys={'columns':['address', 'hostnames']}) - ... - - # The 'columns' key must contain - # sub-keys of 'port' and 'protocol' + def osquery_host_check(rec): + # If all logs did not have the 'address' sub-key, this rule would + # throw a KeyError. Using req_subkeys avoids this. + return rec['columns']['address'] == '127.0.0.1' - @rule(logs=['osquery:differential'], - outputs=['pagerduty', 'aws-s3'], - req_subkeys={'columns':['port', 'protocol']}) - ... context ~~~~~~~~~~~ diff --git a/helpers/base.py b/helpers/base.py index 7c024a68a..a26e70965 100644 --- a/helpers/base.py +++ b/helpers/base.py @@ -15,6 +15,7 @@ """ from fnmatch import fnmatch import logging +import json import time from netaddr import IPAddress, IPNetwork @@ -217,3 +218,17 @@ def data_has_value_from_substring_list(data, needle_list): return False return any(needle in data for needle in needle_list) + +def safe_json_loads(data): + """Safely load a JSON string into a dictionary + + Args: + data (str): A JSON string + + Returns: + dict: The loaded JSON string or empty dict + """ + try: + return json.loads(data) + except ValueError: + return {} diff --git a/rules/community/cloudtrail/cloudtrail_critical_api_calls.py b/rules/community/cloudtrail/cloudtrail_critical_api_calls.py index 2204fa4fb..bb9cb871d 100644 --- a/rules/community/cloudtrail/cloudtrail_critical_api_calls.py +++ b/rules/community/cloudtrail/cloudtrail_critical_api_calls.py @@ -6,11 +6,9 @@ disable = StreamRules.disable() -@rule(logs=['cloudtrail:events'], - matchers=[], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) +@rule( + logs=['cloudtrail:events'], + outputs=['aws-firehose:alerts']) def cloudtrail_critical_api_calls(rec): """ author: airbnb_csirt diff --git a/rules/community/cloudtrail/cloudtrail_mfa_policy_abuse_attempt.py b/rules/community/cloudtrail/cloudtrail_mfa_policy_abuse_attempt.py index 543b2eb6a..18489c747 100644 --- a/rules/community/cloudtrail/cloudtrail_mfa_policy_abuse_attempt.py +++ b/rules/community/cloudtrail/cloudtrail_mfa_policy_abuse_attempt.py @@ -6,11 +6,9 @@ disable = StreamRules.disable() -@rule(logs=['cloudtrail:events'], - matchers=[], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) +@rule( + logs=['cloudtrail:events'], + outputs=['aws-firehose:alerts']) def cloudtrail_mfa_policy_abuse_attempt(rec): """ author: Scott Piper of Summit Route in collaboration with Duo Security @@ -70,8 +68,8 @@ def cloudtrail_mfa_policy_abuse_attempt(rec): # - 'AccessDenied' # - 'EntityAlreadyExists': Can't create another MFA device with the same name. # - 'LimitExceeded': Can't enable a second MFA device for the same user. - if ('errorCode' in rec and - in_set(rec['eventName'], {'CreateVirtualMFADevice', 'EnableMFADevice'})): + if ('errorCode' in rec + and in_set(rec['eventName'], {'CreateVirtualMFADevice', 'EnableMFADevice'})): return True return False diff --git a/rules/community/cloudtrail/cloudtrail_network_acl_ingress_anywhere.py b/rules/community/cloudtrail/cloudtrail_network_acl_ingress_anywhere.py index 5590bffe3..664b22b02 100644 --- a/rules/community/cloudtrail/cloudtrail_network_acl_ingress_anywhere.py +++ b/rules/community/cloudtrail/cloudtrail_network_acl_ingress_anywhere.py @@ -3,12 +3,13 @@ rule = StreamRules.rule -@rule(logs=['cloudwatch:events'], - matchers=[], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel'], - req_subkeys={'detail': ['eventName', 'requestParameters']}) + +@rule( + logs=['cloudwatch:events'], + outputs=['aws-firehose:alerts'], + req_subkeys={ + 'detail': ['eventName', 'requestParameters'] + }) def cloudtrail_network_acl_ingress_anywhere(rec): """ author: @mimeframe @@ -22,8 +23,6 @@ def cloudtrail_network_acl_ingress_anywhere(rec): req_params = rec['detail']['requestParameters'] - return ( - req_params['cidrBlock'] == '0.0.0.0/0' and - req_params['ruleAction'] == 'allow' and - req_params['egress'] is False - ) + return (req_params['cidrBlock'] == '0.0.0.0/0' + and req_params['ruleAction'] == 'allow' + and req_params['egress'] is False) diff --git a/rules/community/cloudtrail/cloudtrail_put_bucket_acl.py b/rules/community/cloudtrail/cloudtrail_put_bucket_acl.py index ea9b6b9b8..3a9d0d117 100644 --- a/rules/community/cloudtrail/cloudtrail_put_bucket_acl.py +++ b/rules/community/cloudtrail/cloudtrail_put_bucket_acl.py @@ -4,12 +4,12 @@ rule = StreamRules.rule -@rule(logs=['cloudwatch:events'], - matchers=[], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel'], - req_subkeys={'detail': ['requestParameters', 'eventName']}) +@rule( + logs=['cloudwatch:events'], + outputs=['aws-firehose:alerts'], + req_subkeys={ + 'detail': ['requestParameters', 'eventName'] + }) def cloudtrail_put_bucket_acl(rec): """ author: airbnb_csirt diff --git a/rules/community/cloudtrail/cloudtrail_put_object_acl_public.py b/rules/community/cloudtrail/cloudtrail_put_object_acl_public.py index a8ef0c4d8..153d80bf6 100644 --- a/rules/community/cloudtrail/cloudtrail_put_object_acl_public.py +++ b/rules/community/cloudtrail/cloudtrail_put_object_acl_public.py @@ -4,11 +4,13 @@ rule = StreamRules.rule -@rule(logs=['cloudwatch:events'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel'], - req_subkeys={'detail': ['requestParameters']}) + +@rule( + logs=['cloudwatch:events'], + outputs=['aws-firehose:alerts'], + req_subkeys={ + 'detail': ['requestParameters'] + }) def cloudtrail_put_object_acl_public(rec): """ author: @mimeframe @@ -25,9 +27,7 @@ def cloudtrail_put_object_acl_public(rec): } # s3 buckets that are expected to have public objects - public_buckets = { - 'example-bucket-to-ignore' - } + public_buckets = {'example-bucket-to-ignore'} request_params = rec['detail']['requestParameters'] return ( @@ -35,6 +35,5 @@ def cloudtrail_put_object_acl_public(rec): # note: substring is used because it can exist as: # "http://acs.amazonaws.com/groups/global/AllUsers" or # "uri=http://acs.amazonaws.com/groups/global/AllUsers" - data_has_value_from_substring_list(request_params, public_acls) and - not in_set(request_params.get('bucketName'), public_buckets) - ) + data_has_value_from_substring_list(request_params, public_acls) + and not in_set(request_params.get('bucketName'), public_buckets)) diff --git a/rules/community/cloudtrail/cloudtrail_root_account_usage.py b/rules/community/cloudtrail/cloudtrail_root_account_usage.py index 6092022cf..04ed46c5a 100644 --- a/rules/community/cloudtrail/cloudtrail_root_account_usage.py +++ b/rules/community/cloudtrail/cloudtrail_root_account_usage.py @@ -4,12 +4,12 @@ rule = StreamRules.rule -@rule(logs=['cloudwatch:events'], - matchers=[], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel'], - req_subkeys={'detail': ['userIdentity', 'eventType']}) +@rule( + logs=['cloudwatch:events'], + outputs=['aws-firehose:alerts'], + req_subkeys={ + 'detail': ['userIdentity', 'eventType'] + }) def cloudtrail_root_account_usage(rec): """ author: airbnb_csirt @@ -22,8 +22,6 @@ def cloudtrail_root_account_usage(rec): (b) ping the individual to determine if intentional and/or legitimate """ # reference_1 contains details on logic below - return ( - rec['detail']['userIdentity']['type'] == 'Root' and - rec['detail']['userIdentity'].get('invokedBy') is None and - rec['detail']['eventType'] != 'AwsServiceEvent' - ) + return (rec['detail']['userIdentity']['type'] == 'Root' + and rec['detail']['userIdentity'].get('invokedBy') is None + and rec['detail']['eventType'] != 'AwsServiceEvent') diff --git a/rules/community/cloudtrail/cloudtrail_security_group_ingress_anywhere.py b/rules/community/cloudtrail/cloudtrail_security_group_ingress_anywhere.py index 75670faf4..37d3c8b98 100644 --- a/rules/community/cloudtrail/cloudtrail_security_group_ingress_anywhere.py +++ b/rules/community/cloudtrail/cloudtrail_security_group_ingress_anywhere.py @@ -4,12 +4,13 @@ rule = StreamRules.rule -@rule(logs=['cloudwatch:events'], - matchers=[], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel'], - req_subkeys={'detail': ['eventName', 'requestParameters']}) + +@rule( + logs=['cloudwatch:events'], + outputs=['aws-firehose:alerts'], + req_subkeys={ + 'detail': ['eventName', 'requestParameters'] + }) def cloudtrail_security_group_ingress_anywhere(rec): """ author: @mimeframe, @ryandeivert diff --git a/rules/community/duo_administrator/duo_bypass_code_create_non_auto_generated.py b/rules/community/duo_administrator/duo_bypass_code_create_non_auto_generated.py index 819093aee..d3ad2408d 100644 --- a/rules/community/duo_administrator/duo_bypass_code_create_non_auto_generated.py +++ b/rules/community/duo_administrator/duo_bypass_code_create_non_auto_generated.py @@ -1,20 +1,16 @@ """Alert when a DUO bypass code is artisanly crafted and not auto-generated.""" -import json +from helpers.base import safe_json_loads from stream_alert.rule_processor.rules_engine import StreamRules rule = StreamRules.rule -@rule(logs=['duo:administrator'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['duo:administrator'], outputs=['aws-firehose:alerts']) def duo_bypass_code_create_non_auto_generated(rec): """ author: @mimeframe description: Alert when a DUO bypass code is artisanly crafted and not auto-generated. reference: https://duo.com/docs/administration-users#generating-a-bypass-code """ - return ( - rec['action'] == 'bypass_create' and - json.loads(rec['description']).get('auto_generated') is False - ) + return (rec['action'] == 'bypass_create' + and safe_json_loads(rec['description']).get('auto_generated') is False) diff --git a/rules/community/duo_administrator/duo_bypass_code_create_non_expiring.py b/rules/community/duo_administrator/duo_bypass_code_create_non_expiring.py index f11184b52..41110c517 100644 --- a/rules/community/duo_administrator/duo_bypass_code_create_non_expiring.py +++ b/rules/community/duo_administrator/duo_bypass_code_create_non_expiring.py @@ -1,20 +1,16 @@ """Alert when a DUO bypass code is created that is non-expiring.""" -import json +from helpers.base import safe_json_loads from stream_alert.rule_processor.rules_engine import StreamRules rule = StreamRules.rule -@rule(logs=['duo:administrator'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['duo:administrator'], outputs=['aws-firehose:alerts']) def duo_bypass_code_create_non_expiring(rec): """ author: @mimeframe description: Alert when a DUO bypass code is created that is non-expiring. reference: https://duo.com/docs/administration-users#generating-a-bypass-code """ - return ( - rec['action'] == 'bypass_create' and - json.loads(rec['description']).get('valid_secs') is None - ) + return (rec['action'] == 'bypass_create' + and safe_json_loads(rec['description']).get('valid_secs') is None) diff --git a/rules/community/duo_administrator/duo_bypass_code_create_unlimited_use.py b/rules/community/duo_administrator/duo_bypass_code_create_unlimited_use.py index 6708718d4..7caf9e9d2 100644 --- a/rules/community/duo_administrator/duo_bypass_code_create_unlimited_use.py +++ b/rules/community/duo_administrator/duo_bypass_code_create_unlimited_use.py @@ -1,20 +1,16 @@ """Alert when a DUO bypass code is created that has unlimited use.""" -import json +from helpers.base import safe_json_loads from stream_alert.rule_processor.rules_engine import StreamRules rule = StreamRules.rule -@rule(logs=['duo:administrator'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['duo:administrator'], outputs=['aws-firehose:alerts']) def duo_bypass_code_create_unlimited_use(rec): """ author: @mimeframe description: Alert when a DUO bypass code is created that has unlimited use. reference: https://duo.com/docs/administration-users#generating-a-bypass-code """ - return ( - rec['action'] == 'bypass_create' and - json.loads(rec['description']).get('remaining_uses') is None - ) + return (rec['action'] == 'bypass_create' + and safe_json_loads(rec['description']).get('remaining_uses') is None) diff --git a/rules/community/duo_authentication/duo_anonymous_ip_failure.py b/rules/community/duo_authentication/duo_anonymous_ip_failure.py index bc26568c7..eb0136205 100644 --- a/rules/community/duo_authentication/duo_anonymous_ip_failure.py +++ b/rules/community/duo_authentication/duo_anonymous_ip_failure.py @@ -3,10 +3,8 @@ rule = StreamRules.rule -@rule(logs=['duo:authentication'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['duo:authentication'], outputs=['aws-firehose:alerts']) def duo_anonymous_ip_failure(rec): """ author: airbnb_csirt diff --git a/rules/community/duo_authentication/duo_fraud.py b/rules/community/duo_authentication/duo_fraud.py index 0a04e9290..2348b37e7 100644 --- a/rules/community/duo_authentication/duo_fraud.py +++ b/rules/community/duo_authentication/duo_fraud.py @@ -3,10 +3,8 @@ rule = StreamRules.rule -@rule(logs=['duo:authentication'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['duo:authentication'], outputs=['aws-firehose:alerts']) def duo_fraud(rec): """ author: airbnb_csirt diff --git a/rules/community/github/github_disable_dismiss_stale_pull_request_approvals.py b/rules/community/github/github_disable_dismiss_stale_pull_request_approvals.py index b7cdfd4be..cb6d33aa9 100644 --- a/rules/community/github/github_disable_dismiss_stale_pull_request_approvals.py +++ b/rules/community/github/github_disable_dismiss_stale_pull_request_approvals.py @@ -3,10 +3,8 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) def github_disable_dismiss_stale_pull_request_approvals(rec): """ author: @mimeframe diff --git a/rules/community/github/github_disable_protect_this_branch.py b/rules/community/github/github_disable_protect_this_branch.py index 21c76ed9b..eaf86b4b6 100644 --- a/rules/community/github/github_disable_protect_this_branch.py +++ b/rules/community/github/github_disable_protect_this_branch.py @@ -3,10 +3,8 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) def github_disable_protect_this_branch(rec): """ author: @mimeframe diff --git a/rules/community/github/github_disable_required_pull_request_reviews.py b/rules/community/github/github_disable_required_pull_request_reviews.py index 0c780b779..eca9f1a20 100644 --- a/rules/community/github/github_disable_required_pull_request_reviews.py +++ b/rules/community/github/github_disable_required_pull_request_reviews.py @@ -4,10 +4,8 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) def github_disable_required_pull_request_reviews(rec): """ author: @mimeframe @@ -20,10 +18,7 @@ def github_disable_required_pull_request_reviews(rec): (c) Click 'Save Changes' reference: https://help.github.com/articles/enabling-required-reviews-for-pull-requests/ """ - actor_ignorelist = { - } - return ( - rec['action'] == 'protected_branch.dismissal_restricted_users_teams' and - rec['data'].get('authorized_actors_only') is True and - not in_set(rec['actor'], actor_ignorelist) - ) + actor_ignorelist = {} + return (rec['action'] == 'protected_branch.dismissal_restricted_users_teams' + and rec['data'].get('authorized_actors_only') is True + and not in_set(rec['actor'], actor_ignorelist)) diff --git a/rules/community/github/github_disable_required_status_checks.py b/rules/community/github/github_disable_required_status_checks.py index ac3e3c948..b28f6833c 100644 --- a/rules/community/github/github_disable_required_status_checks.py +++ b/rules/community/github/github_disable_required_status_checks.py @@ -3,10 +3,8 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) def github_disable_required_status_checks(rec): """ author: @mimeframe @@ -22,5 +20,4 @@ def github_disable_required_status_checks(rec): # 0 => unchecked # 1 => enabled for users # 2 => enabled for users and admins ('Include administrators') - rec['data'].get('required_status_checks_enforcement_level') == 0 - ) + rec['data'].get('required_status_checks_enforcement_level') == 0) diff --git a/rules/community/github/github_disable_two_factor_requirement_org.py b/rules/community/github/github_disable_two_factor_requirement_org.py index 41e1b81ec..8473aa933 100644 --- a/rules/community/github/github_disable_two_factor_requirement_org.py +++ b/rules/community/github/github_disable_two_factor_requirement_org.py @@ -3,10 +3,8 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) def github_disable_two_factor_requirement_org(rec): """ author: @mimeframe diff --git a/rules/community/github/github_disable_two_factor_requirement_user.py b/rules/community/github/github_disable_two_factor_requirement_user.py index f3680940f..ae9de9ada 100644 --- a/rules/community/github/github_disable_two_factor_requirement_user.py +++ b/rules/community/github/github_disable_two_factor_requirement_user.py @@ -3,10 +3,8 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) def github_disable_two_factor_requirement_user(rec): """ author: @mimeframe diff --git a/rules/community/github/github_oauth_application_create.py b/rules/community/github/github_oauth_application_create.py index 0e8401500..cd91a355d 100644 --- a/rules/community/github/github_oauth_application_create.py +++ b/rules/community/github/github_oauth_application_create.py @@ -3,10 +3,8 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) def github_oauth_application_create(rec): """ author: @mimeframe diff --git a/rules/community/github/github_site_admin_action.py b/rules/community/github/github_site_admin_action.py index b2f79aa83..7679d0417 100644 --- a/rules/community/github/github_site_admin_action.py +++ b/rules/community/github/github_site_admin_action.py @@ -3,10 +3,8 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) def github_site_admin_action(rec): """ author: @mimeframe diff --git a/rules/community/github/github_site_admin_user_promotion.py b/rules/community/github/github_site_admin_user_promotion.py index cbd0c4e2c..50477e88a 100644 --- a/rules/community/github/github_site_admin_user_promotion.py +++ b/rules/community/github/github_site_admin_user_promotion.py @@ -3,10 +3,8 @@ rule = StreamRules.rule -@rule(logs=['ghe:general'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration', - 'slack:sample-channel']) + +@rule(logs=['ghe:general'], outputs=['aws-firehose:alerts']) def github_site_admin_user_promotion(rec): """ author: @fusionrace, @mimeframe diff --git a/rules/community/guardduty/guard_duty_all.py b/rules/community/guardduty/guard_duty_all.py index 7227228c4..3e334aa3e 100644 --- a/rules/community/guardduty/guard_duty_all.py +++ b/rules/community/guardduty/guard_duty_all.py @@ -5,9 +5,7 @@ disable = StreamRules.disable() -@rule(logs=['cloudwatch:events'], - matchers=['guard_duty'], - outputs=['slack:sample-channel']) +@rule(logs=['cloudwatch:events'], matchers=['guard_duty'], outputs=['aws-firehose:alerts']) def guard_duty_all(*_): """ author: spiper @@ -18,5 +16,4 @@ def guard_duty_all(*_): testing: From the GuardDuty AWS page (https://console.aws.amazon.com/guardduty/home) click the button to "Generate Sample Findings" """ - return True diff --git a/rules/community/mitre_attack/defense_evasion/multi/obfuscated_files_or_information/right_to_left_character.py b/rules/community/mitre_attack/defense_evasion/multi/obfuscated_files_or_information/right_to_left_character.py index c2147d3e2..d2f9d6334 100644 --- a/rules/community/mitre_attack/defense_evasion/multi/obfuscated_files_or_information/right_to_left_character.py +++ b/rules/community/mitre_attack/defense_evasion/multi/obfuscated_files_or_information/right_to_left_character.py @@ -4,8 +4,10 @@ rule = StreamRules.rule -@rule(datatypes=['command', 'filePath', 'processPath', 'fileName'], - outputs=['aws-s3:sample-bucket', 'pagerduty:sample-integration']) + +@rule( + datatypes=['command', 'filePath', 'processPath', 'fileName'], + outputs=['aws-firehose:alerts']) def right_to_left_character(rec): """ author: @javutin diff --git a/rules/community/onelogin/onelogin_events_assumed_role.py b/rules/community/onelogin/onelogin_events_assumed_role.py index b9e4ed127..8ae6cc9f3 100644 --- a/rules/community/onelogin/onelogin_events_assumed_role.py +++ b/rules/community/onelogin/onelogin_events_assumed_role.py @@ -3,9 +3,8 @@ rule = StreamRules.rule -@rule(logs=['onelogin:events'], - outputs=['aws-s3:sample-bucket', - 'pagerduty:sample-integration']) + +@rule(logs=['onelogin:events'], outputs=['aws-firehose:alerts']) def onelogin_events_assumed_role(rec): """ author: @javutin diff --git a/stream_alert/rule_processor/rules_engine.py b/stream_alert/rule_processor/rules_engine.py index 8ccce5dcb..e93ecc591 100644 --- a/stream_alert/rule_processor/rules_engine.py +++ b/stream_alert/rule_processor/rules_engine.py @@ -15,7 +15,6 @@ """ from collections import namedtuple from copy import copy -import json from stream_alert.rule_processor import LOGGER from stream_alert.rule_processor.threat_intel import StreamThreatIntel @@ -324,13 +323,9 @@ def process_subkeys(record, payload_type, rule): if not record.get(key): LOGGER.debug( 'The required subkey %s is not found when trying to process %s: \n%s', - key, - rule.rule_name, - json.dumps( - record, - indent=2)) + key, rule.rule_name, record) return False - if not all(x in record[key] for x in nested_keys): + if not all(record[key].get(x) for x in nested_keys): return False return True diff --git a/tests/unit/stream_alert_rule_processor/test_rule_helpers.py b/tests/unit/stream_alert_rule_processor/test_rule_helpers.py index c02d8edcb..623dd4f39 100644 --- a/tests/unit/stream_alert_rule_processor/test_rule_helpers.py +++ b/tests/unit/stream_alert_rule_processor/test_rule_helpers.py @@ -15,7 +15,7 @@ """ import time -from nose.tools import assert_equal +from nose.tools import assert_equal, assert_false, assert_true from helpers import base @@ -123,3 +123,21 @@ def test_fetch_values_by_datatype(): assert_equal(len(base.fetch_values_by_datatype(rec, 'ipv4')), 2) assert_equal(len(base.fetch_values_by_datatype(rec, 'cmd')), 0) assert_equal(base.fetch_values_by_datatype(rec, 'username'), ['alice']) + +def test_safe_json_loads_valid(): + """Helpers - Loading valid JSON""" + json_str = '{"test": 0, "values": [1, 2, 3]}' + loaded_json = base.safe_json_loads(json_str) + + assert_equal(type(loaded_json), dict) + assert_true(loaded_json) + assert_equal(loaded_json, {'test': 0, 'values': [1, 2, 3]}) + +def test_safe_json_loads_invalid(): + """Helpers - Loading invalid JSON""" + json_str = 'invalid json string!!!!' + loaded_json = base.safe_json_loads(json_str) + + assert_equal(type(loaded_json), dict) + assert_false(loaded_json) + assert_equal(loaded_json, {})