예제 #1
0
    def __init__(self, url, username, password, map_file, proxies=None, verify=True):
        # Ruleset to map fields from MISP to ArcSight
        # For each field of MISP attribute convert function (example: self._convertComma) will be executed
        # If dots are in misp field name threat it as a nested dict. Example A.B.C -> misp_entry[A][B][C]
        self.test = False
        self.helper = Helper()
        self.maps = yaml.load(open(map_file, 'r'))
        if not self._verifyMap(self.maps):
            logging.error('There is an error in the map file: {}'.format(map_file))
            exit()

        self.default_merge_function = 'space'
        self.default_convert_function = 'return'

        self.active_lists = ActiveLists(url, username, password, proxies=proxies, verify=False)
        if not self.active_lists:
            logging.error('Could not connect to ArcSight ESM')
            exit()

        self.entries = {}
        self.to_add = {}
        self.to_delete = {}
        self.attributes_to_skip = []

        for i in range(len(self.maps)):
            active_list = self.maps[i]
            # Select active list primary key if there is none
            if 'primary_key' not in active_list:
                for rule in active_list['map']:
                    if rule['misp'] == 'value':
                        self.maps[i]['primary_key'] = rule['arcsight']
                        break
            self.to_add[active_list['id']] = []
            self.to_delete[active_list['id']] = []
            self.entries[active_list['id']] = self.active_lists.getEntries(active_list['id'])
예제 #2
0
 def stop_watch(self):
     """This method needs to be called for rate estimations"""
     self.__iteration += 1
     if Stats.track_xp:
         Stats.set_value_with_ocr("XP")
         if not Stats.OCR_failed:
             cxp = Stats.xp
             dxp = cxp - self.last_xp
             self.dxp_log.append(dxp)
             self.last_xp = cxp
         else:
             print("Problems with OCR, skipping stats for this run")
             self.last_timestamp = time.time()
             return
     if Stats.track_pp:
         Stats.set_value_with_ocr("PP")
         if not Stats.OCR_failed:
             cpp = Stats.pp
             dpp = cpp - self.last_pp
             self.dpp_log.append(dpp)
             self.last_pp = cpp
         else:
             print("Problems with OCR, skipping stats for this run")
             self.last_timestamp = time.time()
             return
     dtime = time.time() - self.last_timestamp
     self.dtime_log.append(dtime)
     self.last_timestamp = time.time()
     print("This run: {:^8}{:^3}This run: {:^8}".format(
         Helper.human_format(dxp), "|", Helper.human_format(dpp)))
예제 #3
0
    def buy(self):
        """Buy upgrades for both attack and defense

        Requires the confirmation popup button for EXP purchases in settings
        to be turned OFF.

        This uses all available exp, so use with caution.
        """
        Stats.set_value_with_ocr("XP")

        if Stats.OCR_failed:
            print('OCR failed, exiting upgrade routine.')
            return

        current_exp = Stats.xp

        if current_exp < 1000:
            return

        total_price = (coords.RATTACK_COST * self.attack)
        total_price += (coords.RDEFENSE_COST * self.defense)

        # Skip upgrading if we don't have enough exp to buy at least one
        # complete set of upgrades, in order to maintain our perfect ratios :)

        if total_price > current_exp:
            if self.report:
                print("No XP Upgrade :{:^8} of {:^8}".format(
                    Helper.human_format(current_exp),
                    Helper.human_format(total_price)))
            return

        amount = int(current_exp // total_price)

        a_attack = amount * self.attack
        a_defense = amount * self.defense

        Navigation.exp_rich()

        Inputs.click(*coords.EM_ADV_BOX)
        Inputs.send_string(str(a_attack))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_ADV_BOX)
        Inputs.send_string(str(a_defense))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_ADV_BOX)
        Inputs.click(*coords.EM_ADV_BOX)

        Stats.set_value_with_ocr("XP")

        total_spent = coords.RATTACK_COST * a_attack
        total_spent += coords.RDEFENSE_COST * a_defense

        if self.report:
            print("Spent XP:{:^8}{:^3}Attack:{:^8}{:^3}Defense:{:^8}".format(
                Helper.human_format(total_spent), "|",
                Helper.human_format(a_attack), "|",
                Helper.human_format(a_defense)))
예제 #4
0
 def __show_progress(self):
     if self.__iteration == 1:
         print('Starting: {:^8}{:^3}Starting: {:^8}'.format(
             Helper.human_format(Stats.xp), "|",
             Helper.human_format(Stats.pp)))
     else:
         elapsed = self.elapsed_time()
         xph, pph = self.__estimaterate.rates()
         report_time = "\n{0:^40}\n".format(elapsed)
         print('Current:  {:^8}{:^3}Current:  {:^8}'.format(
             Helper.human_format(Stats.xp), "|",
             Helper.human_format(Stats.pp)))
         print('Per hour: {:^8}{:^3}Per hour: {:^8}'.format(
             Helper.human_format(xph), "|", Helper.human_format(pph)))
         print(report_time)
예제 #5
0
"""ITOPOD Sniping script."""
import time
# Helper classes
from classes.features import Adventure, GoldDiggers, MoneyPit, Inventory
from classes.helper import Helper
from classes.stats import Tracker

import constants as const

Helper.init(True)
Helper.requirements()

tracker = Tracker(5)

while True:  # main loop
    titans = Adventure.check_titan_status()
    if titans:
        for titan in titans:
            Adventure.kill_titan(titan)
    Adventure.itopod_snipe(300)
    MoneyPit.pit()
    tracker.progress()
    GoldDiggers.gold_diggers(const.DEFAULT_DIGGER_ORDER)
    Inventory.boost_equipment(boost_cube=True)
    time.sleep(3)  # Need to wait for tooltip to disappear
예제 #6
0
"""24-hour rebirth script."""
import time
# Helper classes
from classes.features   import (AdvancedTraining, Adventure, Augmentation, FightBoss, Inventory, Misc,
                                BloodMagic, GoldDiggers, NGU, Wandoos, TimeMachine, MoneyPit, Rebirth,
                                Questing, Yggdrasil)
from classes.helper     import Helper

# Set these to your own loadouts
respawn_loadout = 1
ygg_loadout = 2

Helper.init()
Helper.requirements()


def rebirth_init(rt):
    """Procedure that handles start of rebirth."""
    Misc.reclaim_all()  # make sure we reset e/m if we run this mid-rebirth
    FightBoss.nuke(101)  # PPP
    Inventory.loadout(respawn_loadout)
    Adventure.adventure(highest=True)
    TimeMachine.time_machine(5e11, magic=True)
    Augmentation.augments({"CI": 0.7, "ML": 0.3}, 1e12)
    BloodMagic.blood_magic(8)
    BloodMagic.toggle_auto_spells()
    GoldDiggers.gold_diggers()

    if rt.timestamp.tm_hour > 0 or rt.timestamp.tm_min >= 13:
        print("assigning adv training")
    else:
예제 #7
0
 def __init__(self, url, key, verify_cert):
     self.pymisp = PyMISP(url, key, verify_cert)
     self.helper = Helper()
예제 #8
0
class Misp:
    """Based on the rules and priorities pull data from MISP. Conduct filtering, merging,
    prioritization and manipulation of data from MISP.

    Example:
        misp = Misp(config.get('misp-url'), config.get('misp-key'), not config.get('misp-no-verify-cert'))

        misp.loadRules(config.get('rules-file'))
        misp.downloadOrganisations()
        misp.loadPriorities(config.get('priorities-file'))
        misp.downloadAttributes(config.get('timestamp'))
        misp.applyProposals()
        misp.filterAttributes()
        misp.combineIdenticalAttributes()
        print(json.dumps(misp.getToAddAttributes()))
        print(json.dumps(misp.getToDeleteAttributes()))

    Attributes:
        conditions (list): List of available conditions in rules file.
        available_operators (list): List of available operators in rules file.
        available_fields (dict): Dictionary of available fields in rules file.
            with corresponding conditions and value type.
        attributes_to_add (list of dicts): List of attributes to create in receiving class.
            Each attribute is dict with key names from MISP.
        attributes_to_del (list of dicts): List of attributes to remove in receiving class.
            Each attribute is dict with key names from MISP.
        organisations (list of dicts): List of all available organisations with its
            corresponding priorities.
        pymisp (PyMISP): PyMISP object for handling communication with MISP.
        helper (Helper): Object with some helping methods for processing.
        misp_attributes (list of dicts): Attributes downloaded from MISP
        rules (): ...

    Args:
        url (str): MISP URL.
        key (str): MISP API key.
        verify_cert (bool): Check the validity of MISP certificate

    """
    conditions = [ '=', '!=', '~', '!~', '[]', '![]', '>', '<', '>=', '<=', 'r', '!r' ]
    available_operators = ['&', '|']
    available_fields = {
            # TODO - create separate dict with types and avaliable operators
            # TODO - there are the same conditions for every field with the same type
            # Attribute
            'id': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'event_id': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'object_id': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'category': {
                'conditions': [ '=', '!=', '[]', '![]' ],
                'value': ['Antivirus detection', 'Artifacts dropped', 'Attribution', 'External analysis', 'Financial fraud', 'Internal reference', 'Network activity', 'Other', 'Payload delivery', 'Payload installation', 'Payload type', 'Persistence mechanism', 'Person', 'Social network', 'Support Tool', 'Targeting data'],
            },
            'type': {
                'conditions': [ '=', '!=', '[]', '![]' ],
                # ip type is only for domain|ip type
                'value': ['ip', 'ip-src', 'ip-dst', 'email-src', 'email-dst', 'url', 'domain', 'hostname', 'email-subject', 'email-attachment', 'user-agent', 'target-email', 'filename', 'domain|ip', 'filename|md5', 'filename|sha1', 'filename|sha256']
            },
            'to_ids': {
                'conditions': [ '=', '!=' ],
                'value': [True, False],
            },
            'uuid': {
                'conditions': [ '=', '!=', '~', '!~', '[]', '![]', 'r', '!r' ],
                'value': '[STR]',
            },
            'timestamp': {
                'conditions': [ '=', '!=', '>', '<', '>=', '<=' ],
                'value': '[TIME]', # '^[0-9]+(y|m|d|h)$'
            },
            'distribution': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'sharing_group_id': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'comment': {
                'conditions': [ '=', '!=', '~', '!~', '[]', '![]', 'r', '!r' ],
                'value': '[STR]',
            },
            # TODO - deleted
            'to_ids': {
                'conditions': [ '=', '!=' ],
                'value': [True, False],
            },
            'value': {
                'conditions': [ '=', '!=', '~', '!~', '[]', '![]', 'r', '!r' ],
                'value': '[STR]',
            },

            # Event
            'Event.id': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'Event.org_id': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'Event.orgc_id': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'Event.date': {
                'conditions': [ '=', '!=', '>', '<', '>=', '<=' ],
                'value': '[TIME]', # '^[0-9]+(y|m|d|h)$'
            },
            'Event.threat_level_id': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'Event.info': {
                'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ],
                'value': '[STR]'
            },
            'Event.published': {
                'conditions': [ '=', '!=' ],
                'value': [True, False],
            },
            'Event.uuid': {
                'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ],
                'value': '[STR]'
            },
            'Event.analysis': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'Event.timestamp': {
                'conditions': [ '=', '!=', '>', '<', '>=', '<=' ],
                'value': '[TIME]', # '^[0-9]+(y|m|d|h)$'
            },
            'Event.distribution': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'Event.proposal_email_lock': {
                'conditions': [ '=', '!=' ],
                'value': [True, False],
            },
            'Event.locked': {
                'conditions': [ '=', '!=' ],
                'value': [True, False],
            },
            'Event.publish_timestamp': {
                'conditions': [ '=', '!=', '>', '<', '>=', '<=' ],
                'value': '[TIME]', # '^[0-9]+(y|m|d|h)$'
            },
            'Event.sharing_group_id': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'Event.disable_correlation': {
                'conditions': [ '=', '!=' ],
                'value': [True, False],
            },
            # Event Org
            'Event.Org.name': {
                'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ],
                'value': '[STR]'
            },
            'Event.Org.uuid': {
                'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ],
                'value': '[STR]'
            },
            # Event Tag
            'Event.Tag.id': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'Event.Tag.name': {
                'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ],
                'value': '[STR]'
            },
            # Event Galaxy
            'Event.Galaxy.id': {
                'conditions': ['=', '!=', '[]', '![]', '>', '<', '>=', '<='],
                'value': '[INT]'
            },
            'Event.Galaxy.uuid': {
                'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ],
                'value': '[STR]'
            },
            'Event.Galaxy.name': {
                'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ],
                'value': '[STR]'
            },
            'Event.Galaxy.type': {
                'conditions': [ '=', '!=', '~', '!~', '[]', '![]' ],
                'value': '[STR]'
            },
    }
    attributes_to_add = []
    attributes_to_del = []
    organisations = []
    
    def __init__(self, url, key, verify_cert):
        self.pymisp = PyMISP(url, key, verify_cert)
        self.helper = Helper()

    def loadRules(self, rules_file):
        """Load and verify rules from file.
        It accepts yaml and json files. Saves rules to self.rules

        Args:
            rules_file (str): Rules filename.

        Exits:
            If there is wrong file type or if there is an bug in the format of the file.

        """

        # Rules file check
        if rules_file[-5:] == '.yaml':
            rules = yaml.load(open(rules_file, 'r'))
        elif rules_file[-5:] == '.json':
            rules = json.load(open(rules_file, 'r'))
        else:
            logging.error('Rules file in wrong format: {} insted of {}'.format(rules_file[-5:], ['yaml', 'json']))
            exit()

        if not self.verifyRules(rules):
            logging.error('There is an error in the rules file: {}'.format(rules_file))
            exit()

        self.rules = rules

    def downloadAttributes(self, timestamp):
        """Download attributes from MISP in a given timestamp.
        It enriches each attribute with data about event.
        Save attributes to self.misp_attributes.
        Sends one REST API request to MISP.

        Args:
            timestamp (str): Interval since last update (in seconds or 1d, 1h, ...).

        """
        p = self.pymisp.search('attributes', timestamp=timestamp, deleted=True, includeProposals=True)['response']
        if 'Attribute' not in p:
            logging.info('No new or changed attributes in MISP.')
            return

        self.misp_attributes = p['Attribute']

        # Enrich attributes with Event info
        if len(self.misp_attributes) != 0:
            if 'threat_level_id' not in self.misp_attributes[0]['Event']:
                self.enrichAttributes()
            
            self.misp_attributes = self.explodePiped(self.misp_attributes)

        try:
            time_readable = datetime.utcfromtimestamp(int(timestamp)).strftime('%Y-%m-%d %H:%M:%S')
        except ValueError:
            time_readable = timestamp
        logging.info('Downloaded {} attributes from MISP since {}.'.format(len(self.misp_attributes), time_readable))

    def loadAttributes(self, fname):
        """Load attributes from JSON file.

        Args:
            fname (str): JSON filename

        Exits:
            If extension of the file is not json.

        """
        if fname[-5:] != '.json':
            logging.error('Input file in wrong format: {} insted of json'.format(priorities_file[-5:]))
            exit()
        self.misp_attributes = json.load(open(fname, 'r'))
        logging.info('Loaded {} attributes from MISP.'.format(len(self.misp_attributes)))

    def enrichAttributes(self):
        """Pull additional data about events and enrich attributes with that
        Modify self.misp_attributes and saves result there.
        Sends one REST API request to MISP.

        """

        event_ids = {}
        for attribute in self.misp_attributes:
            event_ids[int(attribute['Event']['id'])] = True

        event_ids = list(event_ids.keys())
        events = self.pymisp.search('events', eventid=event_ids)['response']
        for i in range(len(events)):
            del events[i]['Event']['Attribute']
            del events[i]['Event']['ShadowAttribute']
            del events[i]['Event']['RelatedEvent']

        for attribute in self.misp_attributes:
            for event in events:
                event = event['Event']
                if event['id'] == attribute['event_id']:
                    break
            attribute['Event'] = event

    def printAttributes(self):
        """Print downloaded attributes in json format"""

        print(json.dumps(self.misp_attributes))

    def downloadOrganisations(self):
        """Download all organisations
        Saves result to self.organisations

        """

        organisations = self.pymisp.get_organisations_list('all')['response']

        # Get max organisation id
        max_id = 0
        for org in organisations:
            id = int(org['Organisation']['id'])
            if id > max_id:
                max_id = id

        self.organisations = [None] * (max_id + 1)

        for org in organisations:
            o = org['Organisation']
            self.organisations[int(o['id'])] = {'name': o['name']}

        logging.info('Loaded {} organisations from MISP.'.format(len(organisations)))
    
    def loadPriorities(self, priorities_file):
        """Load and verify priorities from file
        Accept yaml and json files.
        Save priorities to self.organisations.

        Args:
            priorities_file (str): Priorities filename.

        Exits:
            If there is wrong file type or if there is an bug in the format of the file.

        """

        # Priorities file check
        if priorities_file[-5:] == '.yaml':
            priorities = yaml.load(open(priorities_file, 'r'))
        elif priorities_file[-5:] == '.json':
            priorities = json.load(open(priorities_file, 'r'))
        else:
            logging.error('Priorities file in wrong format: {} insted of {}'.format(priorities_file[-5:], ['yaml', 'json']))
            exit()

        verified = self.verifyPriorities(priorities)
        if not verified:
            logging.error('There is an error in the priorities file: {}'.format(priorities_file))
            exit()

        loaded = 0
        for org in verified:
            for id in range(len(self.organisations)):
                if self.organisations[id] is not None and org['name'] == self.organisations[id]['name']:
                    loaded += 1
                    self.organisations[id]['priority'] = org['priority']
        
        logging.info('Loaded {} priorities.'.format(loaded))

    def explodePiped(self, misp_attributes):
        """Explode each piped attribute into two
        It uses deepcopy to completely copy attribute with.

        Args:
            misp_attributes (list of dicts): List of attributes

        Returns:
            list: List of attributes

        """

        piped = []
        for misp_attribute in misp_attributes:
            if '|' in misp_attribute['type']:
                attribute1 = copy.deepcopy(misp_attribute)
                attribute1['type'] = attribute1['type'].split('|')[0]
                attribute1['value'] = attribute1['value'].split('|')[0]

                attribute2 = copy.deepcopy(misp_attribute)
                attribute2['type'] = attribute2['type'].split('|')[1]
                attribute2['value'] = attribute2['value'].split('|')[1]

                piped.append(attribute1)
                piped.append(attribute2)
            else:
                piped.append(misp_attribute)

        return piped

    def applyProposals(self):
        """Apply proposals to attributes for further processing.
        Use priorities to decide which proposal is more important.
        Operates on self.misp_attributes
        Saves result to self.attributes_to_add

        Exits:
            If no organisations have been loaded.

        """

        if self.misp_attributes is None:
            logging.warning('No MISP attributes to parse')
            return
        if self.organisations is None:
            logging.error('No organisations loaded')
            exit()

        merged = []
        count = 0
        for attribute in self.misp_attributes:
            if 'ShadowAttribute' not in attribute:
                merged.append(attribute)
                continue

            final = attribute
            event = attribute['Event']

            for shadow in attribute['ShadowAttribute']:
                # Compare orgs priorities
                final_priority = self.organisations[int(event['orgc_id'])].get('priority', 0)
                shadow_priority = self.organisations[int(shadow['org_id'])].get('priority', 0)
                if shadow_priority > final_priority:
                    final = shadow
                    final['Event'] = event
                    continue
                elif final_priority > shadow_priority:
                    continue

                # Compare ids flags
                final_ids = final['to_ids']
                final_delete = final['deleted']
                final_decision = not final_delete if final_delete else final_ids

                shadow_ids = shadow['to_ids']
                shadow_delete = shadow['proposal_to_delete']
                shadow_decision = not shadow_delete if shadow_delete else shadow_ids
                # When two shadow attributes are with different decision take this one where decision is positive
                # old_id key exists only in final which is shadow attribute
                if 'old_id' in final and final_decision != shadow_decision and final_decision:
                    continue

                # By default replace final attribute with the newest shadow attribute
                final = shadow
                final['Event'] = event

            merged.append(final)
            if 'old_id' in final:
                count += 1

        self.attributes_to_add = merged
        # self.merged_attributes = merged
        logging.info('{} proposals applied to attributes.'.format(count))

    def filterAttributes(self):
        """Filter attributes according to rules
        Operates on self.attributes_to_add
        Saves result to self.attributes_to_add

        Exits:
            If there is no rules loaded.

        """

        if self.attributes_to_add is None:
            logging.warning('No MISP attributes to parse')
            return
        if self.rules is None:
            logging.error('No rules found')
            exit()

        filtered = []
        for attribute in self.attributes_to_add:
            if self.checkAttribute(attribute, self.rules):
                filtered.append(attribute)
            else:
                self.attributes_to_del.append(attribute)

        logging.info('With total {} attributes {} marked to add, {} marked to delete according to rules.'.format(len(self.attributes_to_add), len(filtered), len(self.attributes_to_del)))
        self.attributes_to_add = filtered
        #logging.info('{} out of {} attributes filtered out according to rules.'.format(len(self.merged_attributes) - len(self.attributes_to_add), len(self.merged_attributes)))

    def combineIdenticalAttributes(self):
        """Combine attributes with identical values from different events.
        Operates on self.attributes_to_add

        Todo:
            Combine identical to_delete (not only to_add)

        """
        combined = []
        # Search for attrs with identical value and merge them by adding dict key 'Related' to first of them
        for left in self.attributes_to_add:
            if 'already_parsed' in left:
                continue

            related = []
            for right in self.attributes_to_add:
                if left['value'] == right['value']:
                    if left['uuid'] == right['uuid']:
                        continue

                    related.append(copy.deepcopy(right))
                    right['already_parsed'] = 1

            tmp = copy.deepcopy(left)
            if len(related) > 0:
                tmp['Related'] = related
            combined.append(tmp)

        logging.info('Found {} identical attributes. After combine {} attributes left.'.format(len(self.attributes_to_add) - len(combined), len(combined)))
        # self.combined_attributes = combined
        self.attributes_to_add = combined

    def skipUnpublished(self):
        """Remove from attributes_to_add and attributes_to_delete attributes from unpublished events"""

        skipped = 0
        for i in range(len(self.attributes_to_add)):
            if not self.checkIfPublished(self.attributes_to_add[i]):
                del self.attributes_to_add[i]
                skipped += 1

        for i in range(len(self.attributes_to_del)):
            if not self.checkIfPublished(self.attributes_to_del[i]):
                del self.attributes_to_del[i]
                skipped += 1

        logging.info('Skipped {} attributes from unpublished events.'.format(skipped))

    def checkIfPublished(self, attribute):
        """Check if attribute's event is published
        
        Args:
            attribute (dict): Attribute dictionary with all the fields from MISP.

        Returns:
            bool: True if event of a given attribute is published. False otherwise.

        """

        if 'Event' not in attribute:
            logging.error('Attribute {} from event {} does not have event details. Published state cannot be established.'.format(attribute['value'], attribute['event_id']))
            exit()

        if 'published' not in attribute['Event']:
            logging.error('Attribute {} from event {} does not have publish state.'.format(attribute['value'], attribute['event_id']))
            exit()

        return attribute['Event']['published']

    def getToAddAttributes(self):
        """Return attributes which should be added"""

        return self.attributes_to_add

    def getToDeleteAttributes(self):
        """Return attributes which should be removed"""

        return self.attributes_to_del

    def checkAttribute(self, attribute, rules):
        """Recursively check if attribute passes rules filter or not

        Args:
            attribute (dict): Attribute dictionary with all the fields from MISP.
            rules (nested dict): Rules dictionary.

        Returns:
            bool: True if attribute passes filter. False otherwise.

        Exits:
            If rules dictionary is incorrectly build (e.g. no operator, or condition is incorrect)

        """
        
        components = rules.get('components')
        operator = rules.get('operator')
        if operator is None:
            logging.error('Error in rules file. Rules doesn\'t have operator field.')
            exit()
        if components is None:
            logging.error('Error in rules file. Rules doesn\'t have components field.')
            exit()
        if not isinstance(components, list):
            logging.error('Error in rules file. Components field is type {} insted of a list.'.format(type(components)))
            exit()
        if operator not in self.available_operators:
            logging.error('Error in rules file. Operator {} is not allowed operator. Allowed operators are: {}'.format(operator, self.available_operators))
            exit()

        # Set first left side of the condition
        if operator == '&':
            result = True
        else:
            result = False

        for c in components:
            if 'field' in c and 'condition' in c and 'value' in c:
                if '.' in c['field']:
                    attribute_values = self.helper.getValueFromObject(attribute, c['field'])
                else:
                    attribute_values = attribute[c['field']]

                if not isinstance(attribute_values, list):
                    attribute_values = [attribute_values]

                if len(attribute_values) == 0:
                    cond_result = False
                else:
                    # Check if any of the values meet the condition
                    for attribute_value in attribute_values:
                        # Analyzing one condition
                        if c['condition'] == '=':
                            cond_result = attribute_value == c['value']
                        elif c['condition'] == '!=':
                            cond_result = attribute_value != c['value']
                        elif c['condition'] == '~':
                            cond_result = c['value'] in attribute_value
                        elif c['condition'] == '!~':
                            cond_result = c['value'] not in attribute_value
                        elif c['condition'] == '[]':
                            cond_result = attribute_value in c['value']
                        elif c['condition'] == '![]':
                            cond_result = attribute_value not in c['value']
                        elif c['condition'] == '>':
                            cond_result = int(attribute_value) > int(c['value'])
                        elif c['condition'] == '<':
                            cond_result = int(attribute_value) < int(c['value'])
                        elif c['condition'] == '>=':
                            cond_result = int(attribute_value) >= int(c['value'])
                        elif c['condition'] == '<=':
                            cond_result = int(attribute_value) <= int(c['value'])
                        elif c['condition'] == 'r':
                            regex = c['value']
                            cond_result = re.search(regex, attribute_value) is not None
                        elif c['condition'] == '!r':
                            regex = c['value']
                            cond_result = re.search(regex, attribute_value) is None
                        else:
                            logging.error('Error in checking attribute {}'.format(attribute_value))
                            exit()

                        if cond_result:
                            break
            elif 'components' in c and 'operator' in c:
                # Going down to deeper conditions
                cond_result = self.checkAttribute(attribute, c)
            else:
                logging.warning('Component has invalid structure. It has to have fields: "field", "value", "condition" or "components", "operator". Instead it has: {}'.format(', '.join(c.keys())))
                continue

            if operator == '&':
                result = result and cond_result
            else:
                result = result or cond_result

        return result

    # def extendAttributes(self):
    #     """Extends attributes with additional fields."""

    #     for num in range(len(self.attributes_to_add)):
    #         a = self.extendAttribute(self.attributes_to_add[num])
    #         self.attributes_to_add[num] = a

    # def extendAttribute(self, attribute):
    #     """Extends attribute with additional fields ingested by ArcSight."""

    #     # 1. Organisation name
    #     org_id = int(attribute['Event']['org_id'])
    #     orgc_id = int(attribute['Event']['orgc_id'])
    #     attribute['Event']['org_name'] = self.organisations[org_id]['name']
    #     attribute['Event']['orgc_name'] = self.organisations[orgc_id]['name']

    #     return attribute

    def verifyPriorities(self, priorities):
        """Verify correctness of priorities
        Verify all provided priorities. Skip organisation if there is some error.

        Args:
            priorities (dict): Priorities to verify. Example
                {'organisations': [
                    {'name': 'Organisation A', 'priority': 1},
                    {'name': 'Organisation B', 'priority': 2}
                ]}:

        Returns:
            bool: False if there is no organisations key in dictionary.
            list: List of parsed priorities.

        """
        org_priorities = priorities.get('organisations')
        if org_priorities is None:
            logging.warning('Error in priorities file. Organisations field is required.')
            return False

        verified = []
        for priority_org in org_priorities:
            if 'name' not in priority_org:
                logging.warning('Error in priorities file. Organisation doesn\'t have name field. Skipping.')
                continue
            else:
                # TODO Write verification if this prioritzanisation exists in MISP
                orgs = [org['name'] for org in self.organisations if org is not None]
                if priority_org['name'] not in orgs:
                    logging.warning('Error in priorities file. Organisation {} doesn\'t exist. Skipping.'.format(priority_org.get('name')))
                    continue

            if 'priority' not in priority_org:
                logging.warning('Error in priorities file. Organisation {} doesn\'t have priority field. Skipping.'.format(priority_org.get('name')))
                continue
            elif not isinstance(priority_org.get('priority'), int):
                logging.warning('Error in priorities file. Organisation {} priority is not int. It is: {}. Skipping.'.format(priority_org.get('name'), type(priority_org.get('priority'))))
                continue

            verified.append(priority_org)
        return verified

    def verifyRules(self, rules):
        """Verify correctness of rules
        Verify recursively all provided operators, fields, conditions and values.

        Args:
            rules (nested dict): Rules to verify:
                operator: "&"
                components:
                - field: Event.Tag.name
                  condition: "="
                  value: "APT"

        Returns:
            bool: True if successfully parsed rules, False otherwise.

        """
        components = rules.get('components')
        operator = rules.get('operator')

        if operator is None:
            logging.warning('Error in rules file. Rules doesn\'t have operator field.')
            return False
        if components is None:
            logging.warning('Error in rules file. Rules doesn\'t have components field.')
            return False
        if not isinstance(components, list):
            logging.warning('Error in rules file. Components field is type "{}" insted of a list.'.format(type(components)))
            return False
        if operator not in self.available_operators:
            logging.warning('Error in rules file. Operator "{}" is not allowed operator. Allowed operators are: {}'.format(operator, self.available_operators))
            return False

        result = True
        for c in components:
            if 'field' in c and 'value' in c and 'condition' in c:
                if c['condition'] not in self.conditions:
                    logging.warning('Condition "{}" is invalid for the field "{}". All posible conditions: {}'.format(c['condition'], c['field'], ', '.join(self.conditions)))
                    result = False
                # Check if field is correct
                if c['field'] not in self.available_fields:
                    logging.warning('Field "{}" is invalid. Valid fields are: {}'.format(c['field'], ', '.join(self.available_fields.keys())))
                    result = False
                # Check if condition is appropriate for the field
                elif c['condition'] not in self.available_fields[c['field']]['conditions']:
                    logging.warning('Field "{}" cannot be processed with condition "{}". Available conditions: {}'.format(c['field'], c['condition'], ', '.join(self.available_fields[c['field']]['conditions'])))
                    result = False
                # Check if value is appropriate type for the field
                else:
                    if self.available_fields[c['field']]['value'] == '[INT]':
                        if isinstance(c['value'], list):
                            for temp_value in c['value']:
                                if not isinstance(temp_value, int):
                                    logging.warning('Field "{}" can have only int values. One of the value "{}" is type "{}".'.format(c['field'], temp_value, type(temp_value)))
                                    result = False
                        elif not isinstance(c['value'], int):
                            logging.warning('Field "{}" can have only int values. Value "{}" is type "{}".'.format(c['field'], c['value'], type(c['value'])))
                            result = False
                    elif self.available_fields[c['field']]['value'] == '[STR]':
                        if isinstance(c['value'], list):
                            for temp_value in c['value']:
                                if not isinstance(temp_value, str):
                                    logging.warning('Field "{}" can have only str values. One of the value "{}" is type "{}".'.format(c['field'], temp_value, type(temp_value)))
                                    result = False
                        elif not isinstance(c['value'], (str, list)):
                            logging.warning('Field "{}" can have only str values. Value "{}" is type "{}".'.format(c['field'], c['value'], type(c['value'])))
                            result = False
                    elif self.available_fields[c['field']]['value'] == '[TIME]':
                        search = re.search(r'^[0-9]+(y|m|d|h)?$', c['value'], re.I)
                        if not search:
                            logging.warning('Field "{}" can have only time values (1d, 3h, 2m). Value "{}" is type "{}".'.format(c['field'], c['value'], type(c['value'])))
                            result = False
                    elif isinstance(self.available_fields[c['field']]['value'], list):
                        if isinstance(c['value'], list):
                            for temp_value in c['value']:
                                if temp_value not in self.available_fields[c['field']]['value']:
                                    logging.warning('One of the values "{}" is invalid for the field "{}". It has to be one of the following: {}'.format(temp_value, c['field'], ', '.join([str(x) for x in self.available_fields[c['field']]['value']])))
                                    result = False
                        elif c['value'] not in self.available_fields[c['field']]['value']:
                            logging.warning('Value "{}" is invalid for the field "{}". It has to be one of the following: {}'.format(c['value'], c['field'], ', '.join([str(x) for x in self.available_fields[c['field']]['value']])))
                            result = False
            elif 'components' in c and 'operator' in c:
                # Going down to deeper conditions
                result = result and self.verifyRules(c)
            else:
                logging.warning('Component has invalid structure. It has to have fields: "field", "value", "condition" or "components", "operator". Instead it has: {}'.format(', '.join(c.keys())))
                result = False

        return result
예제 #9
0
class ArcSight:
    """Update ArcSight active lists with provided data.
    Take data from MISP and push it to ArcSight.
    After updating active lists it pulls data from current Active Lists
    and make diff between those entries and entries in memory (from MISP or other source).

    Example:
        arcsight = ArcSight(config.get('arcsight-url'), config.get('arcsight-username'),
        config.get('arcsight-password'), config.get('arcsight-map'), proxies=proxy)
        arcsight.addEntries(to_add)
        arcsight.deleteEntries(to_delete)
        if arcsight.verifyChanges() is not False:
            print('MISP to ArcSight export went successful. Expected result achieved.')

    Attributes:
        test (bool): If true don't update active lists just print actions.
        helper (Helper): Object with some helping methods for processing.
        maps (dict): Mapping rules of active lists.
        default_merge_function (str): Default function used to merge multiple values.
        default_convert_function (str): Default function used to conver mapped values.
        active_lists (ActiveLists): pyasesm.ActiveLists object for handling communication with ArcSight Active Lists.
        entries (dict of lists): Dictionary with current active lists entries.
        to_add (dict of lists): Dictionary with active lists entries to add.
        to_delete (dict of lists): Dictionary with active lists entries to remove.
        attributes_to_skip (list of dicts): List of attributes to skip. Those attributes will not change their state.
            They will not be added to active list or removed/updated from one.

    Args:
        url (str): ArcSight ESM URL.
        username (str): ArcSight ESM username.
        password (str): ArcSight ESM password.
        map_file (str): Filename of yaml file with mapping rules.
        proxies (dict): Proxy configuration
        verify (bool): Check the validity of ArcSight ESM certificate

    Exits:
        If there is wrong map_file type or there was an error in ESM connection.

    """
    def __init__(self, url, username, password, map_file, proxies=None, verify=True):
        # Ruleset to map fields from MISP to ArcSight
        # For each field of MISP attribute convert function (example: self._convertComma) will be executed
        # If dots are in misp field name threat it as a nested dict. Example A.B.C -> misp_entry[A][B][C]
        self.test = False
        self.helper = Helper()
        self.maps = yaml.load(open(map_file, 'r'))
        if not self._verifyMap(self.maps):
            logging.error('There is an error in the map file: {}'.format(map_file))
            exit()

        self.default_merge_function = 'space'
        self.default_convert_function = 'return'

        self.active_lists = ActiveLists(url, username, password, proxies=proxies, verify=False)
        if not self.active_lists:
            logging.error('Could not connect to ArcSight ESM')
            exit()

        self.entries = {}
        self.to_add = {}
        self.to_delete = {}
        self.attributes_to_skip = []

        for i in range(len(self.maps)):
            active_list = self.maps[i]
            # Select active list primary key if there is none
            if 'primary_key' not in active_list:
                for rule in active_list['map']:
                    if rule['misp'] == 'value':
                        self.maps[i]['primary_key'] = rule['arcsight']
                        break
            self.to_add[active_list['id']] = []
            self.to_delete[active_list['id']] = []
            self.entries[active_list['id']] = self.active_lists.getEntries(active_list['id'])

    def skipAttributes(self, attributes):
        """Saves attributes to skip.

        Args:
            attributes (list of dicts): MISP attributes to skip

        """

        for attribute in attributes:
            self.attributes_to_skip.append(attribute['value'])

    def addEntries(self, to_add):
        """Add list of entries to arcsight.

        Args:
            to_add (list): List of entries.

        Returns:
            int: Number of added entries.

        """

        for new in to_add:
            self.prepareEntryToAdd(new)

        result = 0
        for list_id in self.to_add:
            if len(self.to_add[list_id]) > 0:
                result += self.active_lists.addEntries(self.to_add[list_id], list_id)
        return result

    def prepareEntryToAdd(self, misp_attribute):
        """Prepare entry by mapping it from MISP field names to Active List.
        Save result to self.to_add

        Args:
            misp_attribute (dict): MISP attribute with all the properties for preparation to add.
        
        Todo:
            Comment on for's what are they for.

        """

        assigned_num = 0
        for active_list in self.maps:
            arcsight_entry = {}
            if isinstance(active_list['value'], list):
                if misp_attribute.get(active_list['key']) in active_list['value']:
                    logging.debug('Attribute {} assigned to list {}.'.format(misp_attribute['value'].replace("\r\n",' '), active_list['name']))
                    assigned_num += 1
                else:
                    continue
            else:
                if misp_attribute.get(active_list['key']) == active_list['value']:
                    logging.debug('Attribute {} assigned to list {}.'.format(misp_attribute['value'].replace("\r\n",' '), active_list['name']))
                    assigned_num += 1
                else:
                    continue
            for rule in active_list['map']:
                # Iterate through rules

                # Prepare function for converting MISP value for ArcSight
                convert_name = rule.get('convert', self.default_convert_function)
                convert_name = convert_name[0].upper() + convert_name[1:]
                convertFunction = getattr(self, '_convert'+convert_name)

                merge_name = rule.get('merge', self.default_merge_function)
                merge_name = merge_name[0].upper() + merge_name[1:]
                mergeFunction = getattr(self, '_merge'+merge_name)

                if isinstance(rule['misp'], list):
                    map_misp_fields = rule['misp']
                else:
                    # Create one-element list for easier and more clear processing
                    map_misp_fields = [rule['misp']]

                misp_values = []
                # 1. Iterate through all fields in MISP and merge them
                for dot_filter in map_misp_fields:
                    # if dot_filter[len(dot_filter)-1] != '.':
                    #     # Add dot at the end for easier and more clear processing
                    #     dot_filter += '.'

                    # 2. Iterate through dotted field names and extract nested falue (example: misp_attribute['Event']['info'])
                    misp_values.append(self.helper.getValueFromObject(misp_attribute, dot_filter, convertFunction, mergeFunction))

                arcsight_entry[rule['arcsight']] = mergeFunction(misp_values)

            self.to_add[active_list['id']].append(arcsight_entry)

        logging.debug('Attribute {} assigned to {} lists.'.format(misp_attribute['value'].replace("\r\n",' '), assigned_num))

    def deleteEntries(self, to_delete):
        """Remove list of entries to arcsight.

        Args:
            to_add (list): List of entries.

        Returns:
            int: Number of removed entries

        """

        for entry in to_delete:
            self.prepareEntryToDelete(entry)

        result = 0
        for list_id in self.to_delete:
            if len(self.to_delete[list_id]) > 0:
                result += self.active_lists.deleteEntries(self.to_delete[list_id], list_id)
        return result

    def prepareEntryToDelete(self, misp_attribute):
        """Prepare entry by mapping it from MISP field names to Active List.
        Save result to self.to_delete.

        Args:
            misp_attribute (dict): MISP attribute with all the properties for preparation to delete.

        Todo:
            verify correctness of this function. Last line of this method is incorrect (logging.debug)

        """
        assigned_num = 0
        for active_list in self.maps:
            if isinstance(active_list['value'], list):
                if misp_attribute.get(active_list['key']) in active_list['value']:
                    logging.debug('Attribute {} assigned to list {}.'.format(misp_attribute['value'].replace("\r\n",' '), active_list['name']))
                    assigned_num += 1
                else:
                    continue
            else:
                if misp_attribute.get(active_list['key']) == active_list['value']:
                    logging.debug('Attribute {} assigned to list {}.'.format(misp_attribute['value'].replace("\r\n",' '), active_list['name']))
                    assigned_num += 1
                else:
                    continue
            for entry in self.entries[active_list['id']]:
                if entry[active_list['primary_key']] == misp_attribute['value']:
                    self.to_delete[active_list['id']].append(entry)

        logging.debug('Attribute {} assigned to {} lists.'.format(misp_attribute['value'].replace("\r\n",' '), assigned_num))

    def testDifferences(self, to_add, to_delete):
        """Prepare lists of attributes to add and to delete.
        Additionaly save the different jsons to logs directory.

        Args:
            to_add (list): List of entries to add
            to_delete (list): List of entries to delete

        Returns:
            bool: True if adding and removing entries from active lists went successful.
            
        """

        self.test = True
        for entry in to_add:
            self.prepareEntryToAdd(entry)

        for entry in to_delete:
            self.prepareEntryToDelete(entry)
        
        result = self.verifyChanges()
        if result is True:
            return True

        # Prints warning messages - differences
        for error in result:
            print('List: {}. Attribute: {} = {}. {}'.format(error['list'], error['primary_key'], error['value'], error['message']))

        # Save differences
        now = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
        with open('logs/{}_current.json'.format(now), 'w') as file:
            json.dump(self.entries, file)
        with open('logs/{}_expected.json'.format(now), 'w') as file:
            json.dump(self.expected, file)

        return False

    def selectActiveList(self, misp_attribute):
        """Get num and id of active list for particular misp attribute
        
        Args:
            misp_attribute (dict): MISP attribute with all the properties.

        Returns:
            dict: List number and list id of a given misp attribute.

        """

        list_num = list_id = None
        for i in range(len(self.maps)):
            list_map = self.maps[i]
            if isinstance(list_map['value'], list):
                if misp_attribute.get(list_map['key']) in list_map['value']:
                    list_num = i
                    list_id = self.maps[i]['id']
                    logging.debug('Attribute {} assigned to list {}.'.format(misp_attribute['value'], list_map['name']))
                    break
            else:
                if misp_attribute.get(list_map['key']) == list_map['value']:
                    list_num = i
                    list_id = self.maps[i]['id']
                    logging.debug('Attribute {} assigned to list {}.'.format(misp_attribute['value'], list_map['name']))
                    break

        if list_num is None:
            return False
        return {'num': list_num, 'id': list_id}

    def info(self, list_id=None):
        """Get the details of active list

        Args:
            list_id (int): Id of active list.
        
        Returns:
            dict: Active list properties.

        """
        return self.active_lists.info(list_id)

    def verifyChanges(self):
        """Pull entries from ArcSight again and compare it from those before the change

        Returns:
            bool: True if lists are equal and export went successfully.
            List: List with differencies.

        """

        # Prepare list with expected ArcSight entries
        self.expected = {}
        for active_list in self.maps:
            list_id = active_list['id']
            primary_key = active_list['primary_key']
            self.expected[list_id] = copy.deepcopy(self.entries[list_id])

            if len(self.to_add[list_id]) == 0:
                continue
            for to_add_entry in self.to_add[list_id]:
                exists = False
                for expected_entry in self.expected[list_id]:
                    if to_add_entry[primary_key] == expected_entry[primary_key]:
                        exists = True
                        break
                if not exists:
                    self.expected[list_id].append(to_add_entry)

            # for entry in self.entries[list_id]:
            #     exists = False
            #     for to_add_entry in self.to_add[list_id]:
            #         if to_add_entry[primary_key] == entry[primary_key]:
            #             exists = True
            #             self.expected[list_id].append(to_add_entry)
            #             break
            #     if not exists:
            #         self.expected[list_id].append(entry)
            # print(json.dumps(self.to_add))
            # exit()
            new_expected = []
            for expected_entry in self.expected[list_id]:
                add = True
                for to_delete_entry in self.to_delete[list_id]:
                    if to_delete_entry[primary_key] == expected_entry[primary_key]:
                        add = False
                        break
                if add:
                    new_expected.append(expected_entry)
            self.expected[list_id] = new_expected
            del new_expected

        valid_str = '_valid_arcsight_entry'
        errors = []
        for active_list in self.maps:
            list_id = active_list['id']
            primary_key = active_list['primary_key']
            if self.test:
                current = self.entries[list_id]
            else:
                current = self.active_lists.getEntries(list_id)

            # Compare precisely current ArcSight database and expected result
            for expected_entry in self.expected[list_id]:
                found = False
                current_entry = None
                for i in range(len(current)):
                    current_entry = current[i]
                    if expected_entry[primary_key] != current_entry[primary_key]:
                        continue

                    found = True
                    current_entry[valid_str] = True
                    errors.extend(self._compareEntries(current_entry, expected_entry, primary_key, active_list['name']))
                    break
                if not found:
                # if current_entry is None or valid_str not in current_entry:
                    errors.append({'primary_key': primary_key, 'value': expected_entry[primary_key], 'list': active_list['name'], 'message': 'Entry does not exist in ArcSight.'})
                    if not self.test:
                        logging.error('ArcSight entry {} does not exist in list {}.'.format(expected_entry[primary_key], active_list['name']))

            # Check if there are some current entries which are not in the expected list
            for current_entry in current:
                if valid_str not in current_entry:
                    errors.append({'primary_key': primary_key, 'value': current_entry[primary_key], 'list': active_list['name'], 'message': 'Entry should not exists in ArcSight'})
                    if not self.test:
                        logging.error('Entry {} should not exist in ArcSight {}.'.format(current_entry[primary_key], active_list['name']))

        if errors:
            return errors
        return True

    def _compareEntries(self, current, expected, primary_key, list_name):
        """Compares current and expected ArcSight entry

        Args:
            current (dict): Current entry from active list.
            expected (dict): Entry which is expected to be on active list.
            primary_key (str): Primary key based on which comparison is made.
            list_name (str): List name for messaging purposes.

        Returns:
            list: Errors if any or empty list.

        """

        errors = []
        for key, expected_value in expected.items():
            # This is temporary bypass for auto-updating time in ticketDate.
            # Remove it when GMT to timestamp conversion is ready and 
            #  addEntries is modified so it sends only changed entries
            if key in ('ticketDate', 'publishedDate'):
                continue
            if key not in current:
                errors.append({'primary_key': primary_key, 'value': expected[primary_key], 'list': list_name, 'message': 'missing key: '+key, })
                if not self.test:
                    logging.error('ArcSight entry {} was not updated in list {}. {} does not exist in ArcSight and should be "{}".'.format(expected[primary_key], list_name, key, expected_value))
            else:

                if current[key] != expected_value:
                    errors.append({'primary_key': primary_key, 'value': expected[primary_key], 'list': list_name, 'message': 'value is different than expected for key: {}. Current: {}. Expected: {}.'.format(key, current[key], expected_value), })
                    if not self.test:
                        logging.error('ArcSight entry {} was not updated in list {}. {} is "{}" but should be "{}".'.format(expected[primary_key], list_name, key, current[key], expected_value))

        return errors

    def _mergeComma(self, values):
        """Merge values by comma

        Args:
            values (list/any): list of values to merge.

        Returns:
            str: Merged values or original value if not list was provided.

        """
        if isinstance(values, list):
            return ','.join(list(filter(None, values)))
        return values

    def _mergeSpace(self, values):
        """Merge values by space

        Args:
            values (list/any): list of values to merge.

        Returns:
            str: Merged values or original value if not list was provided.

        """
        if isinstance(values, list):
            return ' '.join(list(filter(None, values)))
        return values

    def _mergeDash(self, values):
        """Merge values by dash

        Args:
            values (list/any): list of values to merge.

        Returns:
            str: Merged values or original value if not list was provided.

        """

        if isinstance(values, list):
            return '-'.join(list(filter(None, values)))
        return values

    def _convertReturn(self, value):
        """Return the same value. This method is required to keep the code clean.

        Args:
            values (any): Value to return.

        Returns:
            any: Not modified value.

        """

        return value

    def _convertTimestampToEpoch(self, timestamps):
        """Convert timestamp to epoch timestamp

        Args:
            timestamps (list/str): List of timestamps or one timestamp in int.

        Returns:
            str: Merged with comma string with epoch format of given timestaps.

        """

        if isinstance(timestamps, list):
            return self._mergeComma([timestamp+'000' for timestamp in timestamps])
        return timestamps+'000'

    def _convertDateToEpoch(self, dates):
        """Convert dates to epoch timestamp

        Args:
            dates (list/str): Dates in format Y-m-d or list of dates

        Returns:
            str: Merged with comma string with epoch format of given dates.

        """

        if isinstance(dates, list):
            # convert all dates in list to epoch and merge with commas
            return self._mergeComma([str(int(time.mktime(time.strptime(str(date), '%Y-%m-%d'))))+'000' for date in dates])

        date = dates
        return str(int(time.mktime(time.strptime(str(date), '%Y-%m-%d'))))+'000'

    def _convertIpToClassC(self, ips):
        """Convert ip to class C with just first three octets

        Args:
            ips (list/str): Ip addess or list of ip addresses.

        Returns:
            str: First three octets of each ip merged with comma.

        """
        if isinstance(ips, list):
            return self._mergeComma([ip[ip.rfind('.')] for ip in ips])
        return ips[:ips.rfind('.')]

    def _find(self, value, list_id):
        """Find attributes in the current active list based on the value

        Args:
            value (str): Value to search for.
            list_id (int): Id of active list where the search is done.

        Returns:
            bool: True if found, false otherwise.

        """

        # Select active list primary key
        for active_list in self.maps:
            if active_list['id'] != list_id:
                continue
            primary_key = active_list['primary_key']
            break

        for entry in self.entries[list_id]:
            if entry[primary_key] == value:
                #return entry
                return True

        return False

    def _verifyMap(self, map_list):
        """Verify map file content correctness

        Args:
            map_list (dict): Map

        Returns:
            bool: True if map has all required fields. False otherwise.

        Todo:
            Code it. Right now it's doing nothing.

        """

        if map_list:
            return True
        return False
예제 #10
0
    def buy(self):
        """Buy upgrades for both energy and magic.

        Requires the confirmation popup button for EXP purchases in settings
        to be turned OFF.

        This uses all available exp, so use with caution.
        """
        if self.ecap < 10000 or self.ecap % 250 != 0:
            print("Ecap value not divisible by 250 or lower than 10000, not" +
                  " spending exp.")
            return
        if self.mcap < 10000 or self.mcap % 250 != 0:
            print("Mcap value not divisible by 250 or lower than 10000, not" +
                  " spending exp.")
            return

        Stats.set_value_with_ocr("XP")
        if Stats.OCR_failed:
            print('OCR failed, exiting upgrade routine.')
            return

        current_exp = Stats.xp

        e_cost = coords.EPOWER_COST + coords.ECAP_COST * self.ecap + (
            coords.EBAR_COST * self.ebar)

        m_cost = coords.MPOWER_COST + coords.MCAP_COST * self.mcap + (
            coords.MBAR_COST * self.mbar)

        total_price = m_cost + self.e2m_ratio * e_cost

        # Skip upgrading if we don't have enough exp to buy at least one
        # complete set of upgrades, in order to maintain our perfect ratios :)

        if total_price > current_exp:
            if self.report:
                print("No XP Upgrade :{:^8} of {:^8}".format(
                    Helper.human_format(current_exp),
                    Helper.human_format(total_price)))
            return

        amount = int(current_exp // total_price)

        e_power = amount * self.e2m_ratio
        e_cap = amount * self.ecap * self.e2m_ratio
        e_bars = amount * self.ebar * self.e2m_ratio
        m_power = amount
        m_cap = amount * self.mcap
        m_bars = amount * self.mbar

        Navigation.exp()

        Inputs.click(*coords.EM_POW_BOX)
        Inputs.send_string(str(e_power))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_CAP_BOX)
        Inputs.send_string(str(e_cap))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_BAR_BOX)
        Inputs.send_string(str(e_bars))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_POW_BUY)
        Inputs.click(*coords.EM_CAP_BUY)
        Inputs.click(*coords.EM_BAR_BUY)

        Navigation.exp_magic()

        Inputs.click(*coords.EM_POW_BOX)
        Inputs.send_string(str(m_power))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_CAP_BOX)
        Inputs.send_string(str(m_cap))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_BAR_BOX)
        Inputs.send_string(str(m_bars))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_POW_BUY)
        Inputs.click(*coords.EM_CAP_BUY)
        Inputs.click(*coords.EM_BAR_BUY)

        Stats.set_value_with_ocr("XP")

        total_spent = coords.EPOWER_COST * e_power + coords.ECAP_COST * e_cap + coords.EBAR_COST * e_bars
        total_spent += coords.MPOWER_COST * m_power + coords.MCAP_COST * m_cap + coords.MBAR_COST * m_bars

        if self.report:
            print("Spent XP:{:^8}".format(Helper.human_format(total_spent)))
            print(
                "Energy | Pow:{:^8}{:^3}Cap:{:^8}{:^3}Bar:{:^8}{:^3}Magic | Pow:{:^8}{:^3}Cap:{:^8}{:^3}Bar:{:^8}"
                .format(Helper.human_format(e_power), "|",
                        Helper.human_format(e_cap), "|",
                        Helper.human_format(e_bars), "|",
                        Helper.human_format(m_power), "|",
                        Helper.human_format(m_cap), "|",
                        Helper.human_format(m_bars)))
예제 #11
0
    def buy(self):
        """Buy upgrades for hack energy

        Requires the confirmation popup button for EXP purchases in settings
        to be turned OFF.

        This uses all available exp, so use with caution.
        """
        if (self.hcap < 10000 or self.hcap % 250 != 0) and self.hcap != 0:
            print("Ecap value not divisible by 250 or lower than 10000, not" +
                  " spending exp.")
            return

        Stats.set_value_with_ocr("XP")
        if Stats.OCR_failed:
            print('OCR failed, exiting upgrade routine.')
            return

        current_exp = Stats.xp

        total_price = coords.HPOWER_COST * self.hpower + coords.HCAP_COST * self.hcap + coords.HBAR_COST * self.hbar

        # Skip upgrading if we don't have enough exp to buy at least one
        # complete set of upgrades, in order to maintain our perfect ratios :)

        if total_price > current_exp:
            if self.report:
                print("No XP Upgrade :{:^8} of {:^8}".format(
                    Helper.human_format(current_exp),
                    Helper.human_format(total_price)))
            return

        amount = int(current_exp // total_price)

        h_power = amount * self.hpower
        h_cap = amount * self.hcap
        h_bars = amount * self.hbar

        Navigation.exp_hack()

        Inputs.click(*coords.EM_POW_BOX)
        Inputs.send_string(str(h_power))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_CAP_BOX)
        Inputs.send_string(str(h_cap))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_BAR_BOX)
        Inputs.send_string(str(h_bars))
        time.sleep(userset.MEDIUM_SLEEP)

        if h_power > 0:
            Inputs.click(*coords.EM_POW_BUY)
        if h_cap > 0:
            Inputs.click(*coords.EM_CAP_BUY)
        if h_bars > 0:
            Inputs.click(*coords.EM_BAR_BUY)

        Stats.set_value_with_ocr("XP")

        total_spent = coords.HPOWER_COST * h_power + coords.HCAP_COST * h_cap + coords.HBAR_COST * h_bars

        if self.report:
            print("Spent XP:{:^8}".format(Helper.human_format(total_spent)))
            print("New | Pow:{:^8}{:^3}Cap:{:^8}{:^3}Bar:{:^8}".format(
                Helper.human_format(h_power), "|", Helper.human_format(h_cap),
                "|", Helper.human_format(h_bars)))
예제 #12
0
    def buy(self):
        """Buy upgrades for power, toughness, health and regen

        Requires the confirmation popup button for EXP purchases in settings
        to be turned OFF.

        This uses all available exp, so use with caution.
        """
        Stats.set_value_with_ocr("XP")

        if Stats.OCR_failed:
            print('OCR failed, exiting upgrade routine.')
            return

        current_exp = Stats.xp

        total_price = (coords.APOWER_COST * self.power * self.ratio)
        total_price += (coords.ATOUGHNESS_COST * self.toughness * self.ratio)
        total_price += (coords.AHEALTH_COST * self.health * 10)
        total_price += math.floor(coords.AREGEN_COST * self.regen / 10)

        # Skip upgrading if we don't have enough exp to buy at least one
        # complete set of upgrades, in order to maintain our perfect ratios :)

        if total_price > current_exp:
            if self.report:
                print("No XP Upgrade :{:^8} of {:^8}".format(
                    Helper.human_format(current_exp),
                    Helper.human_format(total_price)))
            return

        amount = int(current_exp // total_price)

        a_power = amount * self.ratio
        a_toughness = amount * self.ratio
        a_health = amount * 10
        a_regen = math.floor(amount / 10)
        if a_regen < 1: a_regen = 1.0

        Navigation.exp_adventure()

        Inputs.click(*coords.EM_ADV_BOX)
        Inputs.send_string(str(a_power))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_POW_BOX)
        Inputs.send_string(str(a_toughness))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_CAP_BOX)
        Inputs.send_string(str(a_health))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_BAR_BOX)
        Inputs.send_string(str(a_regen))
        time.sleep(userset.MEDIUM_SLEEP)

        Inputs.click(*coords.EM_ADV_BUT)
        Inputs.click(*coords.EM_POW_BUY)
        Inputs.click(*coords.EM_CAP_BUY)
        Inputs.click(*coords.EM_BAR_BUY)

        Stats.set_value_with_ocr("XP")

        total_spent = coords.APOWER_COST * a_power
        total_spent += coords.ATOUGHNESS_COST * a_toughness
        total_spent += coords.AHEALTH_COST * a_health
        total_spent += coords.AREGEN_COST * a_regen

        if self.report:
            print("Spent XP:{:^8}".format(Helper.human_format(total_spent)))
            print(
                "Power:{:^8}{:^3} Defense:{:^8}{:^3} Health:{:^8}{:^3} Regen:{:^8}"
                .format(Helper.human_format(a_power), "|",
                        Helper.human_format(a_toughness), "|",
                        Helper.human_format(a_health), "|",
                        Helper.human_format(a_regen)))
예제 #13
0
from classes.helper import Helper
from classes.inputs import Inputs

Helper.init()
Inputs.save_screenshot()
예제 #14
0
"""Snipe without Mega Buff."""
import time

# Helper classes
from classes.features   import Adventure, MoneyPit
from classes.helper     import Helper

Helper.init(True)

while True:  # main loop    
    Adventure.snipe(0, 10, manual=True, bosses=True, highest=True)
    Adventure.adventure(0)  # go wait at safe zone
    
    MoneyPit.pit()
    MoneyPit.spin()

    time.sleep(10)
예제 #15
0
class HelperTest( unittest.TestCase ):
	@staticmethod
	def buildSuite():
		suite = unittest.TestSuite()
		suite.addTest(HelperTest("testShouldReturnInt"))
		suite.addTest(HelperTest("testShouldReturnDeltaX"))
		suite.addTest(HelperTest("testShouldReturnDeltaY"))
		suite.addTest(HelperTest("testShouldReturnRandomColor"))
		suite.addTest(HelperTest("testShouldCollide"))
		suite.addTest(HelperTest("testShouldNotCollide"))
		suite.addTest(HelperTest("testShouldBeClose"))
		suite.addTest(HelperTest("testShouldNotBeClose"))
		return suite

	def setUp( self ):
		from classes.helper import Helper	
		self.Helper = Helper()

	def testShouldReturnInt( self ):
		a = float(1.5)
		b = self.Helper.fixed(a)
		assert type(b) == int, "int was not returned"

	def testShouldReturnDeltaX( self ):
		a = 100
		b = 2.5
		c = self.Helper.delta_x(a,b)
		self.assertAlmostEqual(c,-1.26591410277)

	def testShouldReturnDeltaY( self ):
		a = 100
		b = 2.5
		c = self.Helper.delta_y(a,b)
		self.assertAlmostEqual(c,2.15579718072)

	def testShouldReturnRandomColor( self ):
		color = self.Helper.random_color()
		for c in color:
			self.assertGreaterEqual(c, config.color_min)
			self.assertLessEqual(c, config.color_max)

	def testShouldCollide( self ):
		from classes.target import Target
		t1 = Target()
		t2 = Target()
		t1.x = 100
		t1.y = 100
		t2.x = 100
		t2.y = 100
		c = self.Helper.is_collided(t1,t2)
		assert c == True, "collision not detected"

	def testShouldNotCollide( self ):
		from classes.target import Target
		t1 = Target()
		t2 = Target()
		t1.x = 200
		t1.y = 200
		t2.x = 100
		t2.y = 100
		c = self.Helper.is_collided(t1,t2)
		assert c == False, "collision detected"

	def testShouldBeClose( self ):
		from classes.target import Target
		t1 = Target()
		t2 = Target()
		t1.x = 150
		t1.y = 150
		t2.x = 100
		t2.y = 100
		c = self.Helper.is_close(t1,t2)
		assert c == True, "objects are not close"

	def testShouldNotBeClose( self ):
		from classes.target import Target
		t1 = Target()
		t2 = Target()
		t1.x = 550
		t1.y = 550
		t2.x = 100
		t2.y = 100
		c = self.Helper.is_close(t1,t2)
		assert c == False, "objects close"

	def tearDown( self ):
		self.member = None
예제 #16
0
	def setUp( self ):
		from classes.helper import Helper	
		self.Helper = Helper()