Пример #1
0
 def test_get_sequence_alert_shell(self):
     default_shell = get_sequence_alert_shell({})
     assert default_shell["alert_name"] == "unnamed"
     assert default_shell["alert_type"] == "sequence"
     assert default_shell["severity"] == "INFO"
     assert default_shell["lifespan"] == "3 days"
     # ensure it's not naive
     assert "+00:00" in default_shell["utctimestamp"]
Пример #2
0
 def test_save_alert(self, mongo_connection):
     db = mongo_connection.test_alerta
     alerts = db["alerts"]
     assert alerts.count_documents({}) == 0
     alert_shell = get_sequence_alert_shell({})
     alert_shell["alert_name"] == "test"
     save_alert(db, alert_shell)
     assert alerts.count_documents({}) == 1
     alerts.delete_many({})
     assert alerts.count_documents({}) == 0
Пример #3
0
    def test_expire_sequence_alerts(self, mongo_connection):
        # setup
        db = mongo_connection.test_alerta
        inflight_alerts = db["inflight_alerts"]
        inflight_alerts.delete_many({})
        assert inflight_alerts.count_documents({}) == 0
        # create an expired sequence alert, and see if the routine removes it
        offset = pd.Timedelta("7 days").to_pytimedelta()
        last_week = utcnow() - offset
        alert_shell = {"utctimestamp": last_week.isoformat(), "lifespan": "1 day"}
        alert_shell = get_sequence_alert_shell(alert_shell)
        # print(alert_shell)
        save_inflight_alert(db, alert_shell)
        assert inflight_alerts.count_documents({}) == 1
        expire_sequence_alerts(db)
        assert inflight_alerts.count_documents({}) == 0

        # tear down
        inflight_alerts.delete_many({})
        assert inflight_alerts.count_documents({}) == 0
Пример #4
0
    def test_threshold_deadman_sequence_alert(self, mongo_connection):
        # test a combination of threshold, deadman alert
        # mock up of an alert to look for an aws root account
        # login without a corresponding password manager access
        # meaning the credentials aren't stored in a password manager
        # threshold looks for the login, deadman looks for the lack of
        # password manager use

        # modeled after https://bitwarden.com/help/api/
        # but no sample events available,YMMV

        # setup
        db = mongo_connection.test_alerta
        inflight_alerts = db["inflight_alerts"]
        alerts = db["alerts"]
        alerts.delete_many({})
        inflight_alerts.delete_many({})
        assert alerts.count_documents({}) == 0
        assert inflight_alerts.count_documents({}) == 0
        # create an fulfilled sequence alert, and see if
        # it triggers an alert creation
        alert_shell = yaml.safe_load(
            open("./tests/samples/sequence_threshold_deadman_alert.yml")
        )
        alert_shell = get_sequence_alert_shell(alert_shell)
        # create some events that satisfy the sequence
        # root user
        events = []
        for file in glob.glob("./tests/samples/sample_cloudtrail_login_no_mfa.json"):
            events += json.load(open(file))
        assert len(events) > 0

        # since we are injecting alerts instead of querying athena
        # resolve the slot threshold alert manually
        for alert in determine_threshold_trigger(alert_shell["slots"][0], events):
            # did the snippet get resolved?
            assert "root logins" in alert["summary"]
            # did events get copied into the resulting alert?
            assert len(alert["events"]) > 0
            # add this resolved threshold alert as a slot in the sequence alert
            alert_shell["slots"][0] = alert
        # is slot 0 tripped?
        assert "triggered" in alert_shell["slots"][0]
        assert len(alert_shell["slots"][0]["events"]) > 0
        # save this inflight sequence alert
        save_inflight_alert(db, alert_shell)
        assert inflight_alerts.count_documents({}) == 1

        # resolve the deadman in slot 1
        alerts = db.inflight_alerts.find({})
        # instead of calling process_sequence_alert(config, db, session, athena, alert)
        # since we don't have athena in pytest, run through the steps to resolve slot 1
        alert_params = alerts[0]
        index, slot = first_matching_index_value(
            alert_params["slots"], condition=lambda i: not "triggered" in i
        )
        assert index == 1
        assert slot["alert_name"] == "no_password_manager_use"
        criteria = chevron.render(slot["criteria"], alert_params)
        assert "bitwarden" in criteria
        # test the lack of events triggering slot 1
        events = []
        alerts = list(determine_deadman_trigger(alert_params["slots"][1], events))
        assert len(alerts) > 0
        for alert in alerts:
            logger.info(f"deadman summary: {alert['summary']}")
            assert "Expected bitwarden aws root item access" in alert["summary"]
            assert "deadman" in alert["tags"]
            # update the slot
            alert_params["slots"][1] = alert

        # save inflight alert
        save_inflight_alert(db, alert_params)
        # run the routine resolving sequence alerts: create_sequence_alerts
        create_sequence_alerts(db)
        # assert there is a new alert created
        alerts = db["alerts"]
        assert alerts.count_documents({}) == 1
        for alert in alerts.find({}):
            logger.info(f"found db alert: {alert['summary']}")
            # ensure the summary description was
            # resolved correctly by chevron
            assert "without use of a password manager" in alert["summary"]
            # ensure event snippets are preseved
            assert "root logins" in alert["slots"][0]["summary"]
            assert "root item access" in alert["slots"][1]["summary"]
            logger.info(f"found slot in sequence: {alert['slots'][1]['summary']}")
        # assert the inflight alert is removed
        assert inflight_alerts.count_documents({}) == 0
Пример #5
0
    def test_save_resolved_sequence_alert(self, mongo_connection):
        # sequence alerts are just a series of alerts
        # which should all be resolved (in order) before the alert
        # is created
        # the alerts are carried in 'slots' in the
        # sequence alert, all slots full of events and the alert fires.

        # setup
        db = mongo_connection.test_alerta
        inflight_alerts = db["inflight_alerts"]
        alerts = db["alerts"]
        alerts.delete_many({})
        inflight_alerts.delete_many({})
        assert alerts.count_documents({}) == 0
        assert inflight_alerts.count_documents({}) == 0
        # create an fulfilled sequence alert, and see if
        # it triggers an alert creation
        alert_shell = {"utctimestamp": utcnow().isoformat(), "lifespan": "7 day"}
        alert_shell = get_sequence_alert_shell(alert_shell)
        # create some events that satisfy the sequence
        # root user with no mfa
        events = []
        for file in glob.glob("./tests/samples/sample_cloudtrail_login_no_mfa.json"):
            events += json.load(open(file))
        assert len(events) > 0
        # a summary that will get resolved by the events in the slots
        alert_shell[
            "summary"
        ] = "{{slots.0.events.0.eventname}} by {{slots.0.events.0.useridentity.type}} {{metadata.count}} mfa:{{slots.0.events.0.additionaleventdata.mfaused}}"

        alert_shell["slots"] = []
        # make a slot of threshold alert + events that trigger it
        alert_slot = get_threshold_alert_shell({})
        alert_slot[
            "event_snippet"
        ] = "{{eventname}}/{{responseelements.consolelogin}} mfa:{{additionaleventdata.mfaused}} from {{sourceipaddress}}"
        alert_slot["aggregation_key"] = "additionaleventdata.mfaused"
        alert_slot["events"] = events

        # since we are injecting alerts instead of querying athena
        # resolve the slot threshold alert manually
        for alert in determine_threshold_trigger(alert_slot, events):
            # did the snippet get resolved?
            assert "ConsoleLogin/Success" in alert["summary"]
            # did events get copied into the resulting alert?
            assert len(alert["events"]) > 0
            # add this resolved threshold alert as a slot in the sequence alert
            alert_shell["slots"].append(alert)
        # save this inflight sequence alert
        save_inflight_alert(db, alert_shell)
        assert inflight_alerts.count_documents({}) == 1
        # run the routine resolving sequence alerts: create_sequence_alerts
        create_sequence_alerts(db)
        # assert there is a new alert created
        assert alerts.count_documents({}) == 1
        for alert in alerts.find({}):
            logger.info(f"found db alert: {alert['summary']}")
            # ensure the summary description was
            # resolved correctly by chevron
            assert "ConsoleLogin by Root" in alert["summary"]
            # ensure event snippets are preseved
            assert "ConsoleLogin/Success" in alert["slots"][0]["summary"]
            logger.info(f"found slot in sequence: {alert['slots'][0]['summary']}")
        # assert the inflight alert is removed
        assert inflight_alerts.count_documents({}) == 0
Пример #6
0
    def test_remove_previously_alerted(self, mongo_connection):
        db = mongo_connection.test_alerta
        alerts = db["alerts"]
        inflight_alerts = db["inflight_alerts"]
        assert alerts.count_documents({}) == 0
        assert inflight_alerts.count_documents({}) == 0
        events = []
        for file in glob.glob("./tests/samples/sample_cloudtrail_event.json"):
            events += json.load(open(file))
        assert len(events) > 0

        # threshold event
        alert_shell = get_threshold_alert_shell({})
        alert_shell["alert_name"] == "test"
        save_alert(db, alert_shell)
        # alert has no events, remove should be a no-op
        resulting_events = remove_previously_alerted(db, events, alert_shell)
        assert len(resulting_events) == len(events)
        alerts.delete_many({})
        assert alerts.count_documents({}) == 0

        # add event(s) to alert and save
        alert_shell["events"] = events
        save_alert(db, alert_shell)
        # events we present should be a duplicate and removed
        resulting_events = remove_previously_alerted(db, events, alert_shell)
        assert len(resulting_events) == 0
        alerts.delete_many({})
        assert alerts.count_documents({}) == 0

        # sequence alert with slots of events
        alert_shell = get_sequence_alert_shell({})
        alert_shell["alert_name"] == "test"
        # make a slot of events
        alert_shell["slots"] = []
        alert_slot = get_threshold_alert_shell({})
        alert_shell["slots"].append(alert_slot)

        save_inflight_alert(db, alert_shell)
        assert inflight_alerts.count_documents({}) == 1
        # alert has no events, remove should be a no-op
        resulting_events = remove_inflight_events(db, events, alert_shell)
        assert len(resulting_events) == len(events)
        inflight_alerts.delete_many({})
        assert inflight_alerts.count_documents({}) == 0

        # # add event(s) to alert and save
        alert_shell["slots"] = [{"events": []}, {"events": []}, {"events": []}]
        alert_shell["slots"][0]["events"] = events
        save_inflight_alert(db, alert_shell)
        assert inflight_alerts.count_documents({}) == 1

        # # events we present should be a duplicate and removed
        resulting_events = remove_inflight_events(db, events, alert_shell)
        assert len(resulting_events) == 0
        inflight_alerts.delete_many({})
        assert inflight_alerts.count_documents({}) == 0

        # check another slot
        alert_shell["slots"] = [{"events": []}, {"events": []}, {"events": []}]
        alert_shell["slots"][1]["events"] = events
        save_inflight_alert(db, alert_shell)
        assert inflight_alerts.count_documents({}) == 1
        # # events we present should be a duplicate and removed
        resulting_events = remove_inflight_events(db, events, alert_shell)
        assert len(resulting_events) == 0
        inflight_alerts.delete_many({})
        assert inflight_alerts.count_documents({}) == 0