<filename>Packs/AttivoBotsink/Integrations/AttivoBotsink/AttivoBotsink.py
import base64
import json
import os
import socket
import time
from datetime import datetime

import requests
import urllib3

import demistomock as demisto
from CommonServerPython import *

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

SERVER = demisto.params()['server']
API_USER = demisto.params()['credentials']['identifier']
API_PASS = demisto.params()['credentials']['password']
VERIFY_SSL = not demisto.params()['insecure']

handle_proxy()


class BSAPI:
    BS_DEFAULT_PORT = 8443
    TIMEOUT = 20

    def __init__(self, bs_host, bs_port=BS_DEFAULT_PORT, verify_ssl=False, timeout=TIMEOUT):
        self.bs_host = bs_host
        self.bs_port = bs_port
        self.timeout = timeout
        self.session_key = None
        self.base_url = "https://{host}:{port}/api".format(host=self.bs_host, port=self.bs_port)
        self.verify_ssl = verify_ssl

    def do_request(self, url, data=None, headers=None, files=None, method=None, content_type='application/json',
                   json_dump=True):
        # Guess the method if not provided
        if not method:
            if data:
                method = 'post'

            else:
                method = 'get'

        headers = {}
        if self.session_key:
            headers = {'sessionKey': self.session_key}

        if content_type:
            headers['content-type'] = content_type

        url = self.base_url + url
        # Convert data dictionary to a string
        if data and json_dump:
            data = json.dumps(data)

        request_func = getattr(requests, method)
        r = None

        try:
            r = request_func(url, headers=headers, data=data, files=files, verify=self.verify_ssl)
        except requests.exceptions.SSLError:
            demisto.error("SSL verification failed")
            demisto.results("SSL verification to {url} failed".format(url=url))
        except requests.exceptions.ConnectionError as e:
            demisto.error("Could not connect to: {server}".format(server=SERVER))
            demisto.error("Exception: {}".format(e))
            demisto.results("Could not connect to {server} ({exception})".format(server=SERVER, exception=e))
        except Exception as e:
            demisto.error("Generic Exception: {exception}".format(exception=e))
            demisto.error("Type is: {type}".format(type=e.__class__.__name__))

        if r is not None and r.content:
            return r.json()
        else:
            return None

    def login(self, bs_user, bs_pass):
        url = "/auth/login"
        login_data = {'userName': base64.b64encode(bs_user.encode()).decode(),
                      'password': <PASSWORD>(<PASSWORD>.encode()).decode()}

        login_status = self.do_request(url, data=login_data)
        if login_status and 'sessionKey' in login_status:
            self.session_key = login_status['sessionKey']

        return (login_status)

    def logout(self):
        url = "/auth/logout"
        logout_status = self.do_request(url)
        return (logout_status)

    def deploy_decoys(self, target_ip, vlan=None, decoy_number=1):
        url = "/autodeploy/config"
        if vlan:
            data = {"config": [{"ipAddress": target_ip, "vlanID": vlan, "numberOfIPsToAcquire": decoy_number}]}
        else:
            data = {"config": [{"ipAddress": target_ip, "numberOfIPsToAcquire": decoy_number}]}

        deploy_status = self.do_request(url, data=data, content_type=None)
        return (deploy_status)

    def get_threatdirect_rules(self):
        url = "/nwinterfaces/get"
        td_decoys = self.do_request(url)
        return (td_decoys)

    def get_bs_health(self):
        url = "/device/health"
        health = self.do_request(url)
        return health

    def get_monitoring_rules(self):
        url = "/interfaces/get"
        monitoring_rules = self.do_request(url, data='{}', method='post', json_dump=None)
        return (monitoring_rules)

    def get_deceptive_objects(self, object_type, object_id):
        if object_type == 'USERS':
            if object_id == 'ALL':
                url = "/obj_group_cfg/summary/user"
            else:
                url = "/obj_group_cfg/user/{object_id}".format(object_id=object_id)
        else:
            response = "Unknown option: {object_type}".format(object_type=object_type)
            return (response)

        deceptive_objects = self.do_request(url)
        return (deceptive_objects)

    def get_playbooks(self):
        url = '/pb/getAll'
        return self.do_request(url)

    def run_playbook(self, playbook_id, attacker_ip):
        'This simulates an internal playbook execution based on the attacker IP'
        url = '/pb/runplaybook'
        data = {'attacker_ip': attacker_ip, 'playbook_id': playbook_id}
        return self.do_request(url, data=data)

    def get_events(self, severity_start=None, severity_end=None, timestamp_start=None, timestamp_end=None,
                   offset=None, acknowledged='unacknowledged', attacker_ip=None, category=None,
                   device=None, service=None, target_os=None, target_host=None, target_ip=None,
                   target_vlan=None, keywords=None, description=None, comments=None):

        url = "/eventsquery/alerts"

        query_data = {
            'severity_start': severity_start,
            'severity_end': severity_end,
            'timestampStart': timestamp_start,
            'timestampEnd': timestamp_end,
            'offset': offset,
            'acknowledged': acknowledged,
            'attackerIp': [] if attacker_ip is None else attacker_ip,
            'category': [] if category is None else category,
            'device': [] if device is None else device,
            'service': [] if service is None else service,
            'targetOs': [] if target_os is None else target_os,
            'targetHost': [] if target_host is None else target_host,
            'targetIP': [] if target_ip is None else target_ip,
            'targetVLAN': [] if target_vlan is None else target_vlan,
            'keywords': [] if keywords is None else keywords,
            'description': [] if description is None else description,
            'comments': [] if comments is None else comments
        }

        event_data = self.do_request(url, data=query_data)
        return (event_data)

    def convert_severity_string(self, severity_string):
        conversion = {
            'VeryHigh': 14,
            'Very High': 14,
            'High': 11,
            'Medium': 7,
            'Low': 4,
            'VeryLow': 3,
            'Very Low': 3,
            'SystemActivity': 0,
            'System Activity': 0
        }
        if severity_string in conversion:
            return conversion[severity_string]
        else:
            return None

    def convert_to_demisto_severity(self, attivo_severity):
        if attivo_severity >= 14:  # Very High
            demisto_severity = 3
        elif attivo_severity >= 11:  # High
            demisto_severity = 3
        elif attivo_severity >= 7:  # Medium
            demisto_severity = 2
        else:  # Low
            demisto_severity = 1

        return demisto_severity


def valid_ip(host):
    try:
        socket.inet_aton(host)
        return True
    except Exception:
        return False


def date_to_epoch(date):
    date_pattern1 = '\d{4}-\d{2}-\d{2}$'
    date_format1 = '%Y-%m-%d'
    date_pattern2 = '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$'
    date_format2 = '%Y-%m-%dT%H:%M:%SZ'

    epoch = None
    if re.match(date_pattern2, date):
        epoch = int(time.mktime(time.strptime(date, date_format2))) * 1000
    elif re.match(date_pattern1, date):
        epoch = int(time.mktime(time.strptime(date, date_format1))) * 1000

    return epoch


''' EXECUTION CODE '''
attivo_api = BSAPI(SERVER, verify_ssl=VERIFY_SSL)

if demisto.command() == 'attivo-get-events':
    args = demisto.args()
    login_status = attivo_api.login(API_USER, API_PASS)
    attacker_ip = args['attacker_ip']
    severity_string = args['severity']

    start_date = args.get('alerts_start_date')
    if start_date is not None:
        timestampStart = date_to_epoch(start_date)
    else:
        one_day = 24 * 60 * 60
        timestampStart = (int(time.time()) - one_day) * 1000

    end_date = args.get('alerts_end_date')
    if end_date is not None:
        timestampEnd = date_to_epoch(end_date)
    else:
        timestampEnd = int(time.time()) * 1000

    if timestampEnd is None:
        demisto.info("Bad date: {}\nDate should be of the format yyyy-mm-dd or yyyy-mm-ddThh:mm:ssZ".format(end_date))
        return_error("Bad date: {}\nDate should be of the format yyyy-mm-dd or yyyy-mm-ddThh:mm:ssZ".format(end_date))

    if timestampStart is None:
        demisto.info(
            "\nBad date: {}\nDate should be of the format yyyy-mm-dd or yyyy-mm-ddThh:mm:ssZ".format(start_date))
        return_error(
            "\nBad date: {}\nDate should be of the format yyyy-mm-dd or yyyy-mm-ddThh:mm:ssZ".format(start_date))

    severity_end = "15"
    severity_start = attivo_api.convert_severity_string(severity_string)
    attacker_ips = [attacker_ip]

    demisto.info("Pulling events for {src_ip} and severity {severity} from {start_date} "
                 "to {end_date}".format(src_ip=attacker_ip, severity=severity_start,
                                        start_date=timestampStart, end_date=timestampEnd))

    events = attivo_api.get_events(severity_start=severity_start, severity_end=severity_end,
                                   timestamp_start=timestampStart, timestamp_end=timestampEnd,
                                   attacker_ip=attacker_ips)

    attivo_api.logout()

    brief_events = []
    context = []
    for event in events['eventdata']:
        brief_events.append({
            'Attack Name': event['attackName'],
            'Severity': event['details']['Severity'],
            'Target IP': event['details']['Target IP'],
            'Target OS': event['details']['Target OS'],
            'Timestamp': event['details']['Timestamp'],
        })
        context.append({k.replace(' ', ''): v for k, v in event['details'].items()})

    headers = ['Attack Name', 'Severity', 'Timestamp', 'Target IP', 'Target OS']
    entry = {
        'Type': entryTypes['note'],
        'ContentsFormat': formats['json'],
        'Contents': events['eventdata'],
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': tableToMarkdown('Found {} events:'.format(len(brief_events)), brief_events, headers=headers),
        'EntryContext': {'Attivo.Events.Count': len(events['eventdata']),
                         'Attivo.Events.List': context}
    }

    demisto.results(entry)

if demisto.command() == 'fetch-incidents':
    login_status = attivo_api.login(API_USER, API_PASS)
    date_pattern = "%Y-%m-%dT%H:%M:%S.%fZ"
    os.environ['TZ'] = 'UTC'

    FETCH_SEVERITY = demisto.params()['fetch_severity']
    FIRST_FETCH = int(demisto.params()['first_fetch'])

    severity_start = attivo_api.convert_severity_string(FETCH_SEVERITY)
    if not severity_start:
        demisto.info("Attivo fetch-incidents: Unknown severity specified ('{}') using Medium".format(FETCH_SEVERITY))
        severity_start = 7  # Medium
    severity_end = "15"  # Very High

    # When run for the first time, get events from the specified number of days
    one_day = 24 * 60 * 60
    first_fetch_seconds = (int(time.time()) - (one_day * FIRST_FETCH)) * 1000
    last_run_time = demisto.getLastRun().get('time', None)

    if last_run_time is None or last_run_time == 0:
        last_run_time = first_fetch_seconds

    demisto.info("Attivo fetch-incidents: Last run time {}, severity {}:{}".format(last_run_time, FETCH_SEVERITY,
                                                                                   severity_start))

    new_last_run = 0
    incidents = []

    events = attivo_api.get_events(timestamp_start=last_run_time, timestamp_end='now',
                                   severity_start=severity_start, severity_end=severity_end)
    if 'error' in events:
        demisto.error("fetch-incidents error: {}".format(events['error']))
        sys.exit()

    demisto.info("Total new Attivo incidents to add: {}".format(len(events['eventdata'])))

    for event in events['eventdata']:
        event_date = event['timeStamp']
        date_obj = datetime.strptime(event_date, date_pattern)
        event_timestamp = int((date_obj - datetime(1970, 1, 1)).total_seconds()) * 1000 + date_obj.microsecond / 1000
        new_last_run = max(new_last_run, event_timestamp)

        demisto_severity = attivo_api.convert_to_demisto_severity(event['alertLevel'])
        event_type = event['details']['Attack Phase']
        incidents.append({
            'name': event['attackName'],
            'occurred': event_date,
            'details': event['attackDesc'],
            'severity': demisto_severity,
            'type': event_type,
            'rawJSON': json.dumps(event)
        })

    if len(incidents) > 0 and new_last_run > 0:
        new_last_run += 1
        demisto.info("Setting new last run value to {}".format(new_last_run))
        demisto.setLastRun({'time': new_last_run})
    else:
        demisto.info("No new Attivo incidents to add")

    logout_status = attivo_api.logout()
    demisto.incidents(incidents)

if demisto.command() == 'test-module':
    # This is the call made when pressing the integration test button.
    login_status = attivo_api.login(API_USER, API_PASS)

    if login_status and 'sessionKey' in login_status:
        demisto.info("Attivo Login successful (session key = {})".format(login_status['sessionKey']))
        logout_status = attivo_api.logout()
        demisto.results('ok')
        sys.exit(0)
    else:
        demisto.error("Login to {} failed".format(SERVER))
        demisto.error("API Results: {}".format(login_status))
        demisto.results("Login to {} failed\n{}".format(SERVER, login_status))

if demisto.command() == 'attivo-list-playbooks':
    login_status = attivo_api.login(API_USER, API_PASS)
    all_playbooks = attivo_api.get_playbooks()
    brief_playbooks = []
    for playbook in all_playbooks['pb']:
        brief_playbook = {
            'ID': playbook['id'],
            'Name': playbook['name']
        }
        demisto.info("INVESTIGATE {}".format(playbook['investigate']))
        if len(playbook['investigate']) > 0:
            investigate_names = []
            for investigate in playbook['investigate']:
                investigate_names.append(investigate['name'])
            brief_playbook['Investigate'] = ', '.join(investigate_names)
        else:
            brief_playbook['Investigate'] = []

        if len(playbook['analyze']) > 0:
            analyze_names = []
            for analyze in playbook['analyze']:
                analyze_names.append(analyze['name'])
            brief_playbook['Analyze'] = ', '.join(analyze_names)
        else:
            brief_playbook['Analyze'] = []

        if len(playbook['manage']) > 0:
            manage_names = []
            for manage in playbook['manage']:
                manage_names.append(manage['name'])
            brief_playbook['Manage'] = ', '.join(manage_names)
        else:
            brief_playbook['Manage'] = []

        if len(playbook['isolate']) > 0:
            isolate_names = []
            for isolate in playbook['isolate']:
                isolate_names.append(isolate['name'])
            brief_playbook['Isolate'] = ', '.join(isolate_names)
        else:
            brief_playbook['Isolate'] = []

        brief_playbooks.append(brief_playbook)

    headers = ['Name', 'ID', 'Investigate', 'Analyze', 'Manage', 'Isolate']
    entry = {
        'Type': entryTypes['note'],
        'Contents': brief_playbooks,
        'ContentsFormat': formats['json'],
        'HumanReadable': tableToMarkdown('Attivo playbooks', brief_playbooks, headers=headers),
        'ReadableContentsFormat': formats['markdown'],
        'EntryContext': {}
    }

    demisto.results(entry)
    logout_status = attivo_api.logout()

if demisto.command() == 'attivo-run-playbook':
    playbook_name = demisto.args()['playbook_name']
    attacker_ip = demisto.args()['attacker_ip']
    playbook_id = None
    playbook_status = None
    login_status = attivo_api.login(API_USER, API_PASS)
    all_playbooks = attivo_api.get_playbooks()
    for playbook in all_playbooks['pb']:
        if playbook['name'] == playbook_name:
            playbook_id = playbook['id']
            break

    if not playbook_id:
        demisto.error("ID not found for Attivo playbook named: {}".format(playbook_name))
        status_message = "Failed: could not find playbook named '{}'".format(playbook_name)
        status = False
    else:
        demisto.info(
            "Running Attivo playbook named {} ({}) with attacker IP {}".format(playbook_name, playbook_id, attacker_ip))
        playbook_status = attivo_api.run_playbook(playbook_id, attacker_ip)
        demisto.info("Run playbook status = {}".format(playbook_status))

        if 'error' in playbook_status:
            error_text = playbook_status['error']
            status_message = error_text
            status = False
        elif 'status' in playbook_status:
            status_text = playbook_status['status']
            if status_text == 'submitted':
                status = True
                status_message = "Attivo playbook '{}' (ID={}) has been run with attacker " \
                                 "IP {}".format(playbook_name, playbook_id, attacker_ip)
            else:
                status = False
                status_message = "Attivo playbook has not been run.  Status = '{}'".format(status_text)
        else:
            status = False
            status_message = "Attivo playbook has not been run.  Status = 'Unknown failure'"

    entry = {
        'Type': entryTypes['note'],
        'Contents': playbook_status,
        'ContentsFormat': formats['json'],
        'HumanReadable': status_message,
        'ReadableContentsFormat': formats['text'],
        'EntryContext': {'Attivo.Playbook.Status': status,
                         'Attivo.Playbook.Message': status_message}
    }

    demisto.results(entry)
    logout_status = attivo_api.logout()

if demisto.command() == 'attivo-deploy-decoy':
    vulnerable_ip = demisto.args()['vulnerable_ip']
    decoy_number = demisto.args()['decoy_number']
    login_status = attivo_api.login(API_USER, API_PASS)
    demisto.info("Deploying {} decoy(s) on the subnet of {}".format(decoy_number, vulnerable_ip))
    deploy_status = {}
    deploy_status = attivo_api.deploy_decoys(vulnerable_ip, decoy_number=decoy_number)
    demisto.info("Deployment status = {}".format(deploy_status))

    status = False
    status_text = "Unknown failure"
    if 'result' in deploy_status:
        status_text = deploy_status['result'][0]['success']
        if status_text is True:
            status = True
    elif 'success' in deploy_status:
        status_text = deploy_status['success']
        if status_text is True:
            status = True
    elif 'error' in deploy_status:
        status_text = deploy_status['error']

    if status:
        status_message = "{} new Attivo decoy(s) deployed on the subnet with {}".format(decoy_number, vulnerable_ip)
    else:
        status_message = "No Attivo decoys have been deployed. {}".format(status_text)

    entry = {
        'Type': entryTypes['note'],
        'Contents': deploy_status,
        'ContentsFormat': formats['json'],
        'HumanReadable': status_message,
        'ReadableContentsFormat': formats['text'],
        'EntryContext': {'Attivo.DeployDecoy.Status': status,
                         'Attivo.DeployDecoy.Message': status_message}
    }

    demisto.results(entry)
    logout_status = attivo_api.logout()

if demisto.command() == 'attivo-list-users':
    demisto.info("Retrieving information about all deceptive users")
    login_status = attivo_api.login(API_USER, API_PASS)

    user_groups = attivo_api.get_deceptive_objects('USERS', 'ALL')
    users = {}  # type: Dict
    for user_group in user_groups['objGroup']:
        group_id = user_group['esid']
        group_name = user_group['name']
        users_in_group = attivo_api.get_deceptive_objects('USERS', group_id)
        for user_object in users_in_group['objGroup']['objects']:
            user = user_object['username']
            if user in users:
                users[user].append(group_name)
            else:
                users[user] = [group_name]

    all_users = []
    for user in sorted(users.keys(), key=lambda x: x.lower()):
        user_entry = {'User': user, 'Groups': ", ".join(users[user])}
        all_users.append(user_entry)

    headers = ['User', 'Groups']
    entry = {
        'Type': entryTypes['note'],
        'Contents': all_users,
        'ContentsFormat': formats['json'],
        'HumanReadable': tableToMarkdown('Attivo deceptive users', all_users, headers=headers),
        'ReadableContentsFormat': formats['markdown'],
        'EntryContext': {}
    }

    demisto.results(entry)
    logout_status = attivo_api.logout()

if demisto.command() == 'attivo-check-user':
    user = demisto.args()['user']
    demisto.info("Check Attivo for user = {}".format(user))
    login_status = attivo_api.login(API_USER, API_PASS)

    is_deceptive = False
    this_user_object = None

    user_groups = attivo_api.get_deceptive_objects('USERS', 'ALL')
    in_groups = []
    for user_group in user_groups['objGroup']:
        group_id = user_group['esid']
        users_in_group = attivo_api.get_deceptive_objects('USERS', group_id)
        for user_object in users_in_group['objGroup']['objects']:
            this_user = user_object['username']
            if this_user == user:
                this_user_object = user_object
                is_deceptive = True
                in_groups.append(user_group['name'])
                break

    output_table = {'User': user, 'Is Deceptive': str(is_deceptive), 'Groups': ', '.join(in_groups)}

    entry = {
        'Type': entryTypes['note'],
        'Contents': this_user_object,
        'ContentsFormat': formats['text'],
        'HumanReadable': output_table,
        'ReadableContentsFormat': formats['table'],
        'EntryContext': {
            'Attivo.User.Name': user,
            'Attivo.User.IsDeceptive': is_deceptive,
            'Attivo.User.Groups': in_groups
        }
    }

    demisto.info(
        "User {user}, deceptive = {is_deceptive}, group(s) = {groups}".format(user=user, is_deceptive=is_deceptive,
                                                                              groups=in_groups))
    demisto.results(entry)
    logout_status = attivo_api.logout()

if demisto.command() == 'attivo-list-hosts':
    demisto.info('Retrieving information about all deceptive hosts')
    login_status = attivo_api.login(API_USER, API_PASS)

    all_hosts = []

    td_monitoring = attivo_api.get_threatdirect_rules()
    bs_monitoring = attivo_api.get_monitoring_rules()

    if td_monitoring.get('forwarder_vm_monitoring_rules') is not None \
            and td_monitoring.get('forwarder_vm_monitoring_rules').get('forwarderVmMonitoringRules') is not None:

        for rule in td_monitoring['forwarder_vm_monitoring_rules']['forwarderVmMonitoringRules']:
            if rule['type'] == 'onNet':
                td_type = "EP"
            else:
                td_type = "VM"

            host_names = []
            if 'dnsName' in rule and rule['dnsName']:
                host_names.append(rule['dnsName'])

            host_entry = {'IP': rule['ip'],
                          'MAC': rule['customized_mac'],
                          'VLAN': rule['vlanID'],
                          'DHCP': rule['dhcpip'],
                          'TD Name': rule['threatDirectName'],
                          'TD Type': td_type,
                          'Host Name': ', '.join(host_names)
                          }
            all_hosts.append(host_entry)

    if bs_monitoring.get('cfg_monitoring_rules') is not None \
            and bs_monitoring.get('cfg_monitoring_rules').get('monitoringRules') is not None:

        for rule in bs_monitoring['cfg_monitoring_rules']['monitoringRules']:
            # demisto.info("BS RULE: {}".format(rule))
            vlan = rule['vlanID']
            if vlan == -1:
                vlan = None

            host_names = []
            if 'dnsName' in rule and rule['dnsName']:
                host_names.append(rule['dnsName'])
            if 'interfaceName' in rule and rule['interfaceName']:
                host_names.append(rule['interfaceName'])

            host_entry = {'IP': rule['ipAddress'],
                          'MAC': rule['externalMAC'],
                          'DHCP': rule['isDHCPIP'],
                          'VLAN': vlan,
                          'User Defined': rule['userDefined'],
                          'Host Name': ", ".join(host_names)
                          }
            if td_monitoring is not None:
                host_entry['TD Name'] = ''
                host_entry['TD Type'] = ''
            all_hosts.append(host_entry)

    headers = ['IP', 'Host Name', 'MAC', 'VLAN', 'DHCP']

    if td_monitoring['forwarder_vm_monitoring_rules']['forwarderVmMonitoringRules']:
        headers.append('TD Name')
        headers.append('TD Type')

    entry = {
        'Type': entryTypes['note'],
        'Contents': all_hosts,
        'ContentsFormat': formats['json'],
        'HumanReadable': tableToMarkdown("Attivo deceptive hosts (network decoys): {}".format(len(all_hosts)),
                                         all_hosts, headers=headers),
        'ReadableContentsFormat': formats['markdown'],
        'EntryContext': {}
    }

    demisto.results(entry)
    logout_status = attivo_api.logout()

if demisto.command() == 'attivo-check-host':
    host = demisto.args()['host']
    demisto.info("Check Attivo for host = {}".format(host))
    login_status = attivo_api.login(API_USER, API_PASS)

    is_deceptive = False
    this_rule = None
    host_info = {}

    if valid_ip(host):
        ip_address = host
        host_name = None
    else:
        host_name = host
        ip_address = None

    # Check native Monitoring Rules
    bs_monitoring = attivo_api.get_monitoring_rules()
    if bs_monitoring is not None and bs_monitoring.get('cfg_monitoring_rules') is not None and \
            bs_monitoring.get('cfg_monitoring_rules').get('monitoringRules') is not None:

        for rule in bs_monitoring['cfg_monitoring_rules']['monitoringRules']:
            this_ip = rule['ipAddress']
            mac = rule['externalMAC']
            dhcp = rule['isDHCPIP']
            vlan = rule['vlanID']
            if vlan == -1:
                vlan = None
            user_defined = rule['userDefined']
            this_host_name = []
            if 'dnsName' in rule and rule['dnsName']:
                this_host_name.append(rule['dnsName'])
            if 'interfaceName' in rule and rule['interfaceName']:
                this_host_name.append(rule['interfaceName'])

            if (ip_address and this_ip == ip_address) or (host_name and this_host_name and host_name in this_host_name):
                this_rule = rule
                is_deceptive = True
                demisto.info(
                    "Attivo BOTSink IP/Host match ({ip}) ({name}) ({user_defined}) ({mac}) ({dhcp}) ({vlan})".format(
                        ip=this_ip, name=this_host_name, user_defined=user_defined, mac=mac, dhcp=dhcp, vlan=vlan))
                host_info = {'ip': this_ip, 'name': this_host_name, 'user_defined': user_defined, 'mac': mac,
                             'dhcp': dhcp, 'vlan': vlan}
                break

    if not is_deceptive:
        # Check ThreatDirect Monitoring Rules
        td_monitoring = attivo_api.get_threatdirect_rules()
        if td_monitoring is not None and td_monitoring.get('forwarder_vm_monitoring_rules') is not None \
                and td_monitoring.get('forwarder_vm_monitoring_rules').get('forwarderVmMonitoringRules') is not None:

            for rule in td_monitoring['forwarder_vm_monitoring_rules']['forwarderVmMonitoringRules']:
                this_ip = rule['ip']
                this_host_name = []
                mac = rule['customized_mac']
                vlan = rule['vlanID']
                dhcp = rule['dhcpip']
                td_name = rule['threatDirectName']
                if rule['type'] == 'onNet':
                    td_type = "EP"
                else:
                    td_type = "VM"
                if 'dnsName' in rule and rule['dnsName']:
                    this_host_name.append(rule['dnsName'])

                if (ip_address and this_ip == ip_address) or (
                        host_name and this_host_name and host_name in this_host_name):
                    this_rule = rule
                    is_deceptive = True
                    demisto.info(
                        "Attivo ThreatDirect IP match ({ip}) ({name}) ({mac}) ({dhcp}) ({vlan}) ({td_name}) ({td_type})".format(
                            ip=this_ip, name=this_host_name, mac=mac, dhcp=dhcp, vlan=vlan, td_name=td_name,
                            td_type=td_type))
                    host_info = {'ip': this_ip, 'name': this_host_name, 'mac': mac, 'dhcp': dhcp, 'vlan': vlan,
                                 'td_name': td_name, 'td_type': td_type}
                    break
                # elif host_name and this_host_name and host_name in this_host_name:
                #   this_rule = rule
                #   is_deceptive = True
                #   demisto.info("Attivo ThreatDirect host match ({ip}) ({name})
                #   ({user_defined}) ({mac}) ({dhcp}) ({vlan})".format(this_ip, #this_host_name,
                #   user_defined, mac, dhcp, vlan))
                # break

    if is_deceptive:
        output_table = {'Is Deceptive': 'True',
                        'IP Address': this_ip,
                        'Host Names': ', '.join(this_host_name),
                        'MAC Address': mac,
                        'DHCP': str(dhcp),
                        'User Defined': str(user_defined),
                        'VLAN': vlan
                        }
    else:
        output_table = {'Is Deceptive': 'False',
                        'IP Address': ip_address,
                        'Host Names': host_name,
                        'MAC Address': '',
                        'DHCP': '',
                        'User Defined': '',
                        'VLAN': ''
                        }

    entry = {'Type': entryTypes['note'],
             'Contents': output_table,
             'ContentsFormat': formats['table'],
             'HumanReadable': output_table,
             'ReadableContentsFormat': formats['table'],
             'EntryContext': {'Attivo.Host.HostInfo': host_info,
                              'Attivo.Host.IsDeceptive': is_deceptive
                              }
             }

    demisto.info("Deception status for {host} is {is_deceptive}".format(host=host, is_deceptive=is_deceptive))
    demisto.results(entry)
    logout_status = attivo_api.logout()

<filename>Packs/AttivoBotsink/Integrations/AttivoBotsink/AttivoBotsink.py
import base64
import json
import os
import socket
import time
from datetime import datetime

import requests
import urllib3

import demistomock as demisto
from CommonServerPython import *

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

SERVER = demisto.params()['server']
API_USER = demisto.params()['credentials']['identifier']
API_PASS = demisto.params()['credentials']['password']
VERIFY_SSL = not demisto.params()['insecure']

handle_proxy()


class BSAPI:
    BS_DEFAULT_PORT = 8443
    TIMEOUT = 20

    def __init__(self, bs_host, bs_port=BS_DEFAULT_PORT, verify_ssl=False, timeout=TIMEOUT):
        self.bs_host = bs_host
        self.bs_port = bs_port
        self.timeout = timeout
        self.session_key = None
        self.base_url = "https://{host}:{port}/api".format(host=self.bs_host, port=self.bs_port)
        self.verify_ssl = verify_ssl

    def do_request(self, url, data=None, headers=None, files=None, method=None, content_type='application/json',
                   json_dump=True):
        # Guess the method if not provided
        if not method:
            if data:
                method = 'post'

            else:
                method = 'get'

        headers = {}
        if self.session_key:
            headers = {'sessionKey': self.session_key}

        if content_type:
            headers['content-type'] = content_type

        url = self.base_url + url
        # Convert data dictionary to a string
        if data and json_dump:
            data = json.dumps(data)

        request_func = getattr(requests, method)
        r = None

        try:
            r = request_func(url, headers=headers, data=data, files=files, verify=self.verify_ssl)
        except requests.exceptions.SSLError:
            demisto.error("SSL verification failed")
            demisto.results("SSL verification to {url} failed".format(url=url))
        except requests.exceptions.ConnectionError as e:
            demisto.error("Could not connect to: {server}".format(server=SERVER))
            demisto.error("Exception: {}".format(e))
            demisto.results("Could not connect to {server} ({exception})".format(server=SERVER, exception=e))
        except Exception as e:
            demisto.error("Generic Exception: {exception}".format(exception=e))
            demisto.error("Type is: {type}".format(type=e.__class__.__name__))

        if r is not None and r.content:
            return r.json()
        else:
            return None

    def login(self, bs_user, bs_pass):
        url = "/auth/login"
        login_data = {'userName': base64.b64encode(bs_user.encode()).decode(),
                      'password': <PASSWORD>(<PASSWORD>.encode()).decode()}

        login_status = self.do_request(url, data=login_data)
        if login_status and 'sessionKey' in login_status:
            self.session_key = login_status['sessionKey']

        return (login_status)

    def logout(self):
        url = "/auth/logout"
        logout_status = self.do_request(url)
        return (logout_status)

    def deploy_decoys(self, target_ip, vlan=None, decoy_number=1):
        url = "/autodeploy/config"
        if vlan:
            data = {"config": [{"ipAddress": target_ip, "vlanID": vlan, "numberOfIPsToAcquire": decoy_number}]}
        else:
            data = {"config": [{"ipAddress": target_ip, "numberOfIPsToAcquire": decoy_number}]}

        deploy_status = self.do_request(url, data=data, content_type=None)
        return (deploy_status)

    def get_threatdirect_rules(self):
        url = "/nwinterfaces/get"
        td_decoys = self.do_request(url)
        return (td_decoys)

    def get_bs_health(self):
        url = "/device/health"
        health = self.do_request(url)
        return health

    def get_monitoring_rules(self):
        url = "/interfaces/get"
        monitoring_rules = self.do_request(url, data='{}', method='post', json_dump=None)
        return (monitoring_rules)

    def get_deceptive_objects(self, object_type, object_id):
        if object_type == 'USERS':
            if object_id == 'ALL':
                url = "/obj_group_cfg/summary/user"
            else:
                url = "/obj_group_cfg/user/{object_id}".format(object_id=object_id)
        else:
            response = "Unknown option: {object_type}".format(object_type=object_type)
            return (response)

        deceptive_objects = self.do_request(url)
        return (deceptive_objects)

    def get_playbooks(self):
        url = '/pb/getAll'
        return self.do_request(url)

    def run_playbook(self, playbook_id, attacker_ip):
        'This simulates an internal playbook execution based on the attacker IP'
        url = '/pb/runplaybook'
        data = {'attacker_ip': attacker_ip, 'playbook_id': playbook_id}
        return self.do_request(url, data=data)

    def get_events(self, severity_start=None, severity_end=None, timestamp_start=None, timestamp_end=None,
                   offset=None, acknowledged='unacknowledged', attacker_ip=None, category=None,
                   device=None, service=None, target_os=None, target_host=None, target_ip=None,
                   target_vlan=None, keywords=None, description=None, comments=None):

        url = "/eventsquery/alerts"

        query_data = {
            'severity_start': severity_start,
            'severity_end': severity_end,
            'timestampStart': timestamp_start,
            'timestampEnd': timestamp_end,
            'offset': offset,
            'acknowledged': acknowledged,
            'attackerIp': [] if attacker_ip is None else attacker_ip,
            'category': [] if category is None else category,
            'device': [] if device is None else device,
            'service': [] if service is None else service,
            'targetOs': [] if target_os is None else target_os,
            'targetHost': [] if target_host is None else target_host,
            'targetIP': [] if target_ip is None else target_ip,
            'targetVLAN': [] if target_vlan is None else target_vlan,
            'keywords': [] if keywords is None else keywords,
            'description': [] if description is None else description,
            'comments': [] if comments is None else comments
        }

        event_data = self.do_request(url, data=query_data)
        return (event_data)

    def convert_severity_string(self, severity_string):
        conversion = {
            'VeryHigh': 14,
            'Very High': 14,
            'High': 11,
            'Medium': 7,
            'Low': 4,
            'VeryLow': 3,
            'Very Low': 3,
            'SystemActivity': 0,
            'System Activity': 0
        }
        if severity_string in conversion:
            return conversion[severity_string]
        else:
            return None

    def convert_to_demisto_severity(self, attivo_severity):
        if attivo_severity >= 14:  # Very High
            demisto_severity = 3
        elif attivo_severity >= 11:  # High
            demisto_severity = 3
        elif attivo_severity >= 7:  # Medium
            demisto_severity = 2
        else:  # Low
            demisto_severity = 1

        return demisto_severity


def valid_ip(host):
    try:
        socket.inet_aton(host)
        return True
    except Exception:
        return False


def date_to_epoch(date):
    date_pattern1 = '\d{4}-\d{2}-\d{2}$'
    date_format1 = '%Y-%m-%d'
    date_pattern2 = '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$'
    date_format2 = '%Y-%m-%dT%H:%M:%SZ'

    epoch = None
    if re.match(date_pattern2, date):
        epoch = int(time.mktime(time.strptime(date, date_format2))) * 1000
    elif re.match(date_pattern1, date):
        epoch = int(time.mktime(time.strptime(date, date_format1))) * 1000

    return epoch


''' EXECUTION CODE '''
attivo_api = BSAPI(SERVER, verify_ssl=VERIFY_SSL)

if demisto.command() == 'attivo-get-events':
    args = demisto.args()
    login_status = attivo_api.login(API_USER, API_PASS)
    attacker_ip = args['attacker_ip']
    severity_string = args['severity']

    start_date = args.get('alerts_start_date')
    if start_date is not None:
        timestampStart = date_to_epoch(start_date)
    else:
        one_day = 24 * 60 * 60
        timestampStart = (int(time.time()) - one_day) * 1000

    end_date = args.get('alerts_end_date')
    if end_date is not None:
        timestampEnd = date_to_epoch(end_date)
    else:
        timestampEnd = int(time.time()) * 1000

    if timestampEnd is None:
        demisto.info("Bad date: {}\nDate should be of the format yyyy-mm-dd or yyyy-mm-ddThh:mm:ssZ".format(end_date))
        return_error("Bad date: {}\nDate should be of the format yyyy-mm-dd or yyyy-mm-ddThh:mm:ssZ".format(end_date))

    if timestampStart is None:
        demisto.info(
            "\nBad date: {}\nDate should be of the format yyyy-mm-dd or yyyy-mm-ddThh:mm:ssZ".format(start_date))
        return_error(
            "\nBad date: {}\nDate should be of the format yyyy-mm-dd or yyyy-mm-ddThh:mm:ssZ".format(start_date))

    severity_end = "15"
    severity_start = attivo_api.convert_severity_string(severity_string)
    attacker_ips = [attacker_ip]

    demisto.info("Pulling events for {src_ip} and severity {severity} from {start_date} "
                 "to {end_date}".format(src_ip=attacker_ip, severity=severity_start,
                                        start_date=timestampStart, end_date=timestampEnd))

    events = attivo_api.get_events(severity_start=severity_start, severity_end=severity_end,
                                   timestamp_start=timestampStart, timestamp_end=timestampEnd,
                                   attacker_ip=attacker_ips)

    attivo_api.logout()

    brief_events = []
    context = []
    for event in events['eventdata']:
        brief_events.append({
            'Attack Name': event['attackName'],
            'Severity': event['details']['Severity'],
            'Target IP': event['details']['Target IP'],
            'Target OS': event['details']['Target OS'],
            'Timestamp': event['details']['Timestamp'],
        })
        context.append({k.replace(' ', ''): v for k, v in event['details'].items()})

    headers = ['Attack Name', 'Severity', 'Timestamp', 'Target IP', 'Target OS']
    entry = {
        'Type': entryTypes['note'],
        'ContentsFormat': formats['json'],
        'Contents': events['eventdata'],
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': tableToMarkdown('Found {} events:'.format(len(brief_events)), brief_events, headers=headers),
        'EntryContext': {'Attivo.Events.Count': len(events['eventdata']),
                         'Attivo.Events.List': context}
    }

    demisto.results(entry)

if demisto.command() == 'fetch-incidents':
    login_status = attivo_api.login(API_USER, API_PASS)
    date_pattern = "%Y-%m-%dT%H:%M:%S.%fZ"
    os.environ['TZ'] = 'UTC'

    FETCH_SEVERITY = demisto.params()['fetch_severity']
    FIRST_FETCH = int(demisto.params()['first_fetch'])

    severity_start = attivo_api.convert_severity_string(FETCH_SEVERITY)
    if not severity_start:
        demisto.info("Attivo fetch-incidents: Unknown severity specified ('{}') using Medium".format(FETCH_SEVERITY))
        severity_start = 7  # Medium
    severity_end = "15"  # Very High

    # When run for the first time, get events from the specified number of days
    one_day = 24 * 60 * 60
    first_fetch_seconds = (int(time.time()) - (one_day * FIRST_FETCH)) * 1000
    last_run_time = demisto.getLastRun().get('time', None)

    if last_run_time is None or last_run_time == 0:
        last_run_time = first_fetch_seconds

    demisto.info("Attivo fetch-incidents: Last run time {}, severity {}:{}".format(last_run_time, FETCH_SEVERITY,
                                                                                   severity_start))

    new_last_run = 0
    incidents = []

    events = attivo_api.get_events(timestamp_start=last_run_time, timestamp_end='now',
                                   severity_start=severity_start, severity_end=severity_end)
    if 'error' in events:
        demisto.error("fetch-incidents error: {}".format(events['error']))
        sys.exit()

    demisto.info("Total new Attivo incidents to add: {}".format(len(events['eventdata'])))

    for event in events['eventdata']:
        event_date = event['timeStamp']
        date_obj = datetime.strptime(event_date, date_pattern)
        event_timestamp = int((date_obj - datetime(1970, 1, 1)).total_seconds()) * 1000 + date_obj.microsecond / 1000
        new_last_run = max(new_last_run, event_timestamp)

        demisto_severity = attivo_api.convert_to_demisto_severity(event['alertLevel'])
        event_type = event['details']['Attack Phase']
        incidents.append({
            'name': event['attackName'],
            'occurred': event_date,
            'details': event['attackDesc'],
            'severity': demisto_severity,
            'type': event_type,
            'rawJSON': json.dumps(event)
        })

    if len(incidents) > 0 and new_last_run > 0:
        new_last_run += 1
        demisto.info("Setting new last run value to {}".format(new_last_run))
        demisto.setLastRun({'time': new_last_run})
    else:
        demisto.info("No new Attivo incidents to add")

    logout_status = attivo_api.logout()
    demisto.incidents(incidents)

if demisto.command() == 'test-module':
    # This is the call made when pressing the integration test button.
    login_status = attivo_api.login(API_USER, API_PASS)

    if login_status and 'sessionKey' in login_status:
        demisto.info("Attivo Login successful (session key = {})".format(login_status['sessionKey']))
        logout_status = attivo_api.logout()
        demisto.results('ok')
        sys.exit(0)
    else:
        demisto.error("Login to {} failed".format(SERVER))
        demisto.error("API Results: {}".format(login_status))
        demisto.results("Login to {} failed\n{}".format(SERVER, login_status))

if demisto.command() == 'attivo-list-playbooks':
    login_status = attivo_api.login(API_USER, API_PASS)
    all_playbooks = attivo_api.get_playbooks()
    brief_playbooks = []
    for playbook in all_playbooks['pb']:
        brief_playbook = {
            'ID': playbook['id'],
            'Name': playbook['name']
        }
        demisto.info("INVESTIGATE {}".format(playbook['investigate']))
        if len(playbook['investigate']) > 0:
            investigate_names = []
            for investigate in playbook['investigate']:
                investigate_names.append(investigate['name'])
            brief_playbook['Investigate'] = ', '.join(investigate_names)
        else:
            brief_playbook['Investigate'] = []

        if len(playbook['analyze']) > 0:
            analyze_names = []
            for analyze in playbook['analyze']:
                analyze_names.append(analyze['name'])
            brief_playbook['Analyze'] = ', '.join(analyze_names)
        else:
            brief_playbook['Analyze'] = []

        if len(playbook['manage']) > 0:
            manage_names = []
            for manage in playbook['manage']:
                manage_names.append(manage['name'])
            brief_playbook['Manage'] = ', '.join(manage_names)
        else:
            brief_playbook['Manage'] = []

        if len(playbook['isolate']) > 0:
            isolate_names = []
            for isolate in playbook['isolate']:
                isolate_names.append(isolate['name'])
            brief_playbook['Isolate'] = ', '.join(isolate_names)
        else:
            brief_playbook['Isolate'] = []

        brief_playbooks.append(brief_playbook)

    headers = ['Name', 'ID', 'Investigate', 'Analyze', 'Manage', 'Isolate']
    entry = {
        'Type': entryTypes['note'],
        'Contents': brief_playbooks,
        'ContentsFormat': formats['json'],
        'HumanReadable': tableToMarkdown('Attivo playbooks', brief_playbooks, headers=headers),
        'ReadableContentsFormat': formats['markdown'],
        'EntryContext': {}
    }

    demisto.results(entry)
    logout_status = attivo_api.logout()

if demisto.command() == 'attivo-run-playbook':
    playbook_name = demisto.args()['playbook_name']
    attacker_ip = demisto.args()['attacker_ip']
    playbook_id = None
    playbook_status = None
    login_status = attivo_api.login(API_USER, API_PASS)
    all_playbooks = attivo_api.get_playbooks()
    for playbook in all_playbooks['pb']:
        if playbook['name'] == playbook_name:
            playbook_id = playbook['id']
            break

    if not playbook_id:
        demisto.error("ID not found for Attivo playbook named: {}".format(playbook_name))
        status_message = "Failed: could not find playbook named '{}'".format(playbook_name)
        status = False
    else:
        demisto.info(
            "Running Attivo playbook named {} ({}) with attacker IP {}".format(playbook_name, playbook_id, attacker_ip))
        playbook_status = attivo_api.run_playbook(playbook_id, attacker_ip)
        demisto.info("Run playbook status = {}".format(playbook_status))

        if 'error' in playbook_status:
            error_text = playbook_status['error']
            status_message = error_text
            status = False
        elif 'status' in playbook_status:
            status_text = playbook_status['status']
            if status_text == 'submitted':
                status = True
                status_message = "Attivo playbook '{}' (ID={}) has been run with attacker " \
                                 "IP {}".format(playbook_name, playbook_id, attacker_ip)
            else:
                status = False
                status_message = "Attivo playbook has not been run.  Status = '{}'".format(status_text)
        else:
            status = False
            status_message = "Attivo playbook has not been run.  Status = 'Unknown failure'"

    entry = {
        'Type': entryTypes['note'],
        'Contents': playbook_status,
        'ContentsFormat': formats['json'],
        'HumanReadable': status_message,
        'ReadableContentsFormat': formats['text'],
        'EntryContext': {'Attivo.Playbook.Status': status,
                         'Attivo.Playbook.Message': status_message}
    }

    demisto.results(entry)
    logout_status = attivo_api.logout()

if demisto.command() == 'attivo-deploy-decoy':
    vulnerable_ip = demisto.args()['vulnerable_ip']
    decoy_number = demisto.args()['decoy_number']
    login_status = attivo_api.login(API_USER, API_PASS)
    demisto.info("Deploying {} decoy(s) on the subnet of {}".format(decoy_number, vulnerable_ip))
    deploy_status = {}
    deploy_status = attivo_api.deploy_decoys(vulnerable_ip, decoy_number=decoy_number)
    demisto.info("Deployment status = {}".format(deploy_status))

    status = False
    status_text = "Unknown failure"
    if 'result' in deploy_status:
        status_text = deploy_status['result'][0]['success']
        if status_text is True:
            status = True
    elif 'success' in deploy_status:
        status_text = deploy_status['success']
        if status_text is True:
            status = True
    elif 'error' in deploy_status:
        status_text = deploy_status['error']

    if status:
        status_message = "{} new Attivo decoy(s) deployed on the subnet with {}".format(decoy_number, vulnerable_ip)
    else:
        status_message = "No Attivo decoys have been deployed. {}".format(status_text)

    entry = {
        'Type': entryTypes['note'],
        'Contents': deploy_status,
        'ContentsFormat': formats['json'],
        'HumanReadable': status_message,
        'ReadableContentsFormat': formats['text'],
        'EntryContext': {'Attivo.DeployDecoy.Status': status,
                         'Attivo.DeployDecoy.Message': status_message}
    }

    demisto.results(entry)
    logout_status = attivo_api.logout()

if demisto.command() == 'attivo-list-users':
    demisto.info("Retrieving information about all deceptive users")
    login_status = attivo_api.login(API_USER, API_PASS)

    user_groups = attivo_api.get_deceptive_objects('USERS', 'ALL')
    users = {}  # type: Dict
    for user_group in user_groups['objGroup']:
        group_id = user_group['esid']
        group_name = user_group['name']
        users_in_group = attivo_api.get_deceptive_objects('USERS', group_id)
        for user_object in users_in_group['objGroup']['objects']:
            user = user_object['username']
            if user in users:
                users[user].append(group_name)
            else:
                users[user] = [group_name]

    all_users = []
    for user in sorted(users.keys(), key=lambda x: x.lower()):
        user_entry = {'User': user, 'Groups': ", ".join(users[user])}
        all_users.append(user_entry)

    headers = ['User', 'Groups']
    entry = {
        'Type': entryTypes['note'],
        'Contents': all_users,
        'ContentsFormat': formats['json'],
        'HumanReadable': tableToMarkdown('Attivo deceptive users', all_users, headers=headers),
        'ReadableContentsFormat': formats['markdown'],
        'EntryContext': {}
    }

    demisto.results(entry)
    logout_status = attivo_api.logout()

if demisto.command() == 'attivo-check-user':
    user = demisto.args()['user']
    demisto.info("Check Attivo for user = {}".format(user))
    login_status = attivo_api.login(API_USER, API_PASS)

    is_deceptive = False
    this_user_object = None

    user_groups = attivo_api.get_deceptive_objects('USERS', 'ALL')
    in_groups = []
    for user_group in user_groups['objGroup']:
        group_id = user_group['esid']
        users_in_group = attivo_api.get_deceptive_objects('USERS', group_id)
        for user_object in users_in_group['objGroup']['objects']:
            this_user = user_object['username']
            if this_user == user:
                this_user_object = user_object
                is_deceptive = True
                in_groups.append(user_group['name'])
                break

    output_table = {'User': user, 'Is Deceptive': str(is_deceptive), 'Groups': ', '.join(in_groups)}

    entry = {
        'Type': entryTypes['note'],
        'Contents': this_user_object,
        'ContentsFormat': formats['text'],
        'HumanReadable': output_table,
        'ReadableContentsFormat': formats['table'],
        'EntryContext': {
            'Attivo.User.Name': user,
            'Attivo.User.IsDeceptive': is_deceptive,
            'Attivo.User.Groups': in_groups
        }
    }

    demisto.info(
        "User {user}, deceptive = {is_deceptive}, group(s) = {groups}".format(user=user, is_deceptive=is_deceptive,
                                                                              groups=in_groups))
    demisto.results(entry)
    logout_status = attivo_api.logout()

if demisto.command() == 'attivo-list-hosts':
    demisto.info('Retrieving information about all deceptive hosts')
    login_status = attivo_api.login(API_USER, API_PASS)

    all_hosts = []

    td_monitoring = attivo_api.get_threatdirect_rules()
    bs_monitoring = attivo_api.get_monitoring_rules()

    if td_monitoring.get('forwarder_vm_monitoring_rules') is not None \
            and td_monitoring.get('forwarder_vm_monitoring_rules').get('forwarderVmMonitoringRules') is not None:

        for rule in td_monitoring['forwarder_vm_monitoring_rules']['forwarderVmMonitoringRules']:
            if rule['type'] == 'onNet':
                td_type = "EP"
            else:
                td_type = "VM"

            host_names = []
            if 'dnsName' in rule and rule['dnsName']:
                host_names.append(rule['dnsName'])

            host_entry = {'IP': rule['ip'],
                          'MAC': rule['customized_mac'],
                          'VLAN': rule['vlanID'],
                          'DHCP': rule['dhcpip'],
                          'TD Name': rule['threatDirectName'],
                          'TD Type': td_type,
                          'Host Name': ', '.join(host_names)
                          }
            all_hosts.append(host_entry)

    if bs_monitoring.get('cfg_monitoring_rules') is not None \
            and bs_monitoring.get('cfg_monitoring_rules').get('monitoringRules') is not None:

        for rule in bs_monitoring['cfg_monitoring_rules']['monitoringRules']:
            # demisto.info("BS RULE: {}".format(rule))
            vlan = rule['vlanID']
            if vlan == -1:
                vlan = None

            host_names = []
            if 'dnsName' in rule and rule['dnsName']:
                host_names.append(rule['dnsName'])
            if 'interfaceName' in rule and rule['interfaceName']:
                host_names.append(rule['interfaceName'])

            host_entry = {'IP': rule['ipAddress'],
                          'MAC': rule['externalMAC'],
                          'DHCP': rule['isDHCPIP'],
                          'VLAN': vlan,
                          'User Defined': rule['userDefined'],
                          'Host Name': ", ".join(host_names)
                          }
            if td_monitoring is not None:
                host_entry['TD Name'] = ''
                host_entry['TD Type'] = ''
            all_hosts.append(host_entry)

    headers = ['IP', 'Host Name', 'MAC', 'VLAN', 'DHCP']

    if td_monitoring['forwarder_vm_monitoring_rules']['forwarderVmMonitoringRules']:
        headers.append('TD Name')
        headers.append('TD Type')

    entry = {
        'Type': entryTypes['note'],
        'Contents': all_hosts,
        'ContentsFormat': formats['json'],
        'HumanReadable': tableToMarkdown("Attivo deceptive hosts (network decoys): {}".format(len(all_hosts)),
                                         all_hosts, headers=headers),
        'ReadableContentsFormat': formats['markdown'],
        'EntryContext': {}
    }

    demisto.results(entry)
    logout_status = attivo_api.logout()

if demisto.command() == 'attivo-check-host':
    host = demisto.args()['host']
    demisto.info("Check Attivo for host = {}".format(host))
    login_status = attivo_api.login(API_USER, API_PASS)

    is_deceptive = False
    this_rule = None
    host_info = {}

    if valid_ip(host):
        ip_address = host
        host_name = None
    else:
        host_name = host
        ip_address = None

    # Check native Monitoring Rules
    bs_monitoring = attivo_api.get_monitoring_rules()
    if bs_monitoring is not None and bs_monitoring.get('cfg_monitoring_rules') is not None and \
            bs_monitoring.get('cfg_monitoring_rules').get('monitoringRules') is not None:

        for rule in bs_monitoring['cfg_monitoring_rules']['monitoringRules']:
            this_ip = rule['ipAddress']
            mac = rule['externalMAC']
            dhcp = rule['isDHCPIP']
            vlan = rule['vlanID']
            if vlan == -1:
                vlan = None
            user_defined = rule['userDefined']
            this_host_name = []
            if 'dnsName' in rule and rule['dnsName']:
                this_host_name.append(rule['dnsName'])
            if 'interfaceName' in rule and rule['interfaceName']:
                this_host_name.append(rule['interfaceName'])

            if (ip_address and this_ip == ip_address) or (host_name and this_host_name and host_name in this_host_name):
                this_rule = rule
                is_deceptive = True
                demisto.info(
                    "Attivo BOTSink IP/Host match ({ip}) ({name}) ({user_defined}) ({mac}) ({dhcp}) ({vlan})".format(
                        ip=this_ip, name=this_host_name, user_defined=user_defined, mac=mac, dhcp=dhcp, vlan=vlan))
                host_info = {'ip': this_ip, 'name': this_host_name, 'user_defined': user_defined, 'mac': mac,
                             'dhcp': dhcp, 'vlan': vlan}
                break

    if not is_deceptive:
        # Check ThreatDirect Monitoring Rules
        td_monitoring = attivo_api.get_threatdirect_rules()
        if td_monitoring is not None and td_monitoring.get('forwarder_vm_monitoring_rules') is not None \
                and td_monitoring.get('forwarder_vm_monitoring_rules').get('forwarderVmMonitoringRules') is not None:

            for rule in td_monitoring['forwarder_vm_monitoring_rules']['forwarderVmMonitoringRules']:
                this_ip = rule['ip']
                this_host_name = []
                mac = rule['customized_mac']
                vlan = rule['vlanID']
                dhcp = rule['dhcpip']
                td_name = rule['threatDirectName']
                if rule['type'] == 'onNet':
                    td_type = "EP"
                else:
                    td_type = "VM"
                if 'dnsName' in rule and rule['dnsName']:
                    this_host_name.append(rule['dnsName'])

                if (ip_address and this_ip == ip_address) or (
                        host_name and this_host_name and host_name in this_host_name):
                    this_rule = rule
                    is_deceptive = True
                    demisto.info(
                        "Attivo ThreatDirect IP match ({ip}) ({name}) ({mac}) ({dhcp}) ({vlan}) ({td_name}) ({td_type})".format(
                            ip=this_ip, name=this_host_name, mac=mac, dhcp=dhcp, vlan=vlan, td_name=td_name,
                            td_type=td_type))
                    host_info = {'ip': this_ip, 'name': this_host_name, 'mac': mac, 'dhcp': dhcp, 'vlan': vlan,
                                 'td_name': td_name, 'td_type': td_type}
                    break
                # elif host_name and this_host_name and host_name in this_host_name:
                #   this_rule = rule
                #   is_deceptive = True
                #   demisto.info("Attivo ThreatDirect host match ({ip}) ({name})
                #   ({user_defined}) ({mac}) ({dhcp}) ({vlan})".format(this_ip, #this_host_name,
                #   user_defined, mac, dhcp, vlan))
                # break

    if is_deceptive:
        output_table = {'Is Deceptive': 'True',
                        'IP Address': this_ip,
                        'Host Names': ', '.join(this_host_name),
                        'MAC Address': mac,
                        'DHCP': str(dhcp),
                        'User Defined': str(user_defined),
                        'VLAN': vlan
                        }
    else:
        output_table = {'Is Deceptive': 'False',
                        'IP Address': ip_address,
                        'Host Names': host_name,
                        'MAC Address': '',
                        'DHCP': '',
                        'User Defined': '',
                        'VLAN': ''
                        }

    entry = {'Type': entryTypes['note'],
             'Contents': output_table,
             'ContentsFormat': formats['table'],
             'HumanReadable': output_table,
             'ReadableContentsFormat': formats['table'],
             'EntryContext': {'Attivo.Host.HostInfo': host_info,
                              'Attivo.Host.IsDeceptive': is_deceptive
                              }
             }

    demisto.info("Deception status for {host} is {is_deceptive}".format(host=host, is_deceptive=is_deceptive))
    demisto.results(entry)
    logout_status = attivo_api.logout()

# Guess the method if not provided # Convert data dictionary to a string # Very High # High # Medium # Low EXECUTION CODE # Medium # Very High # When run for the first time, get events from the specified number of days # This is the call made when pressing the integration test button. # type: Dict # demisto.info("BS RULE: {}".format(rule)) # Check native Monitoring Rules # Check ThreatDirect Monitoring Rules # elif host_name and this_host_name and host_name in this_host_name: #   this_rule = rule #   is_deceptive = True #   demisto.info("Attivo ThreatDirect host match ({ip}) ({name}) #   ({user_defined}) ({mac}) ({dhcp}) ({vlan})".format(this_ip, #this_host_name, #   user_defined, mac, dhcp, vlan)) # break