Exemplo n.º 1
0
def test_isotime_local():
    local_date = now_as_local()
    local_format = re.compile(r'[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{6}.*')

    assert isinstance(local_date, str)
    assert local_format.match(local_date)
    assert epoch_to_local(local_to_epoch(local_date)) == local_date
    assert local_date == epoch_to_local(iso_to_epoch(epoch_to_iso(local_to_epoch(local_date))))
Exemplo n.º 2
0
    def _get_version_map(self):
        self.engine_map = {}
        engine_list = []
        newest_dat = 0
        oldest_dat = now()

        url = self.cfg.get('BASE_URL') + "stat/engines"
        try:
            r = self.session.get(url=url, timeout=self.timeout)
        except requests.exceptions.Timeout:
            raise Exception("Metadefender service timeout.")

        engines = r.json()

        for engine in engines:
            if self.cfg.get("MD_VERSION") == 4:
                name = self._format_engine_name(engine["eng_name"])
                version = engine['eng_ver']
                def_time = engine['def_time']
                etype = engine['engine_type']
            elif self.cfg.get("MD_VERSION") == 3:
                name = self._format_engine_name(engine["eng_name"]).replace(
                    "scanengine", "")
                version = engine['eng_ver']
                def_time = engine['def_time'].replace(" AM", "").replace(
                    " PM", "").replace("/", "-").replace(" ", "T")
                def_time = def_time[6:10] + "-" + def_time[:5] + def_time[
                    10:] + "Z"
                etype = engine['eng_type']
            else:
                raise Exception("Unknown metadefender version")

            # Compute newest DAT
            dat_epoch = iso_to_epoch(def_time)
            if dat_epoch > newest_dat:
                newest_dat = dat_epoch

            if dat_epoch < oldest_dat and dat_epoch != 0 and etype in [
                    "av", "Bundled engine"
            ]:
                oldest_dat = dat_epoch

            self.engine_map[name] = {
                'version': version,
                'def_time': iso_to_local(def_time)[:19]
            }
            engine_list.append(name)
            engine_list.append(version)
            engine_list.append(def_time)

        self.newest_dat = epoch_to_local(newest_dat)[:19]
        self.oldest_dat = epoch_to_local(oldest_dat)[:19]
        self.dat_hash = hashlib.md5("".join(engine_list)).hexdigest()
Exemplo n.º 3
0
def test_isotime_iso():
    iso_date = now_as_iso()
    iso_format = re.compile(r'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{6}Z')

    assert isinstance(iso_date, str)
    assert iso_format.match(iso_date)
    assert epoch_to_iso(iso_to_epoch(iso_date)) == iso_date
    assert iso_date == epoch_to_iso(local_to_epoch(epoch_to_local(iso_to_epoch(iso_date))))
Exemplo n.º 4
0
 def __init__(self, cfg=None):
     super(MetaDefender, self).__init__(cfg)
     self.dat_hash = "0"
     self.engine_map = {}
     self.engine_list = []
     self.newest_dat = epoch_to_local(0)
     self.oldest_dat = now_as_local()
     self.session = None
     self._updater_id = "ENABLE_SERVICE_BLK_MSG"
     self.timeout = cfg.get('MD_TIMEOUT', (self.SERVICE_TIMEOUT * 2) / 3)
     self.init_vmap = False
def test_isotime_rounding_error():
    for t in ["2020-01-29 18:41:25.758416", "2020-01-29 18:41:25.127600"]:
        epoch = local_to_epoch(t)
        local = epoch_to_local(epoch)
        assert local == t
def test_isotime_epoch():
    epoch_date = now(200)

    assert epoch_date == local_to_epoch(epoch_to_local(epoch_date))
    assert epoch_date == iso_to_epoch(epoch_to_iso(epoch_date))
    assert isinstance(epoch_date, float)
Exemplo n.º 7
0
    def test_get_version_map(metadefender_class_instance):
        from requests import Session, exceptions
        from assemblyline.common.isotime import epoch_to_local
        metadefender_class_instance.session = Session()
        node = "http://blah:1/"
        metadefender_class_instance.nodes[node] = {}
        with requests_mock.Mocker() as m:
            m.get(f"{node}stat/engines", status_code=200, json=[])
            metadefender_class_instance._get_version_map(node)
            metadefender_class_instance.nodes[node].pop("oldest_dat")
            assert metadefender_class_instance.nodes[node] == {
                "engine_count": 0,
                "newest_dat": epoch_to_local(0)[:19],
                "engine_list": ""
            }

            with pytest.raises(Exception):
                m.get(f"{node}stat/engines", exc=exceptions.Timeout)
                metadefender_class_instance._get_version_map(node)

            with pytest.raises(Exception):
                m.get(f"{node}stat/engines", exc=exceptions.ConnectionError)
                metadefender_class_instance._get_version_map(node)

            # MD Version 4
            metadefender_class_instance.nodes[node] = {"engine_map": {}}
            m.get(f"{node}stat/engines",
                  status_code=200,
                  json=[{
                      "active": False,
                      "eng_name": "blah",
                      "eng_ver": "blah",
                      "def_time": "1999-09-09T12:12:12",
                      "engine_type": "blah"
                  }])
            metadefender_class_instance._get_version_map(node)
            metadefender_class_instance.nodes[node].pop("oldest_dat")
            assert metadefender_class_instance.nodes[node] == {
                "engine_count": 0,
                "newest_dat": '1999-09-09 12:12:14',
                "engine_list": 'blahblah1999-09-09T12:12:12',
                "engine_map": {
                    'blah': {
                        'def_time': '1999-09-09 12:12:14',
                        'version': 'blah'
                    }
                }
            }

            m.get(f"{node}stat/engines",
                  status_code=200,
                  json=[{
                      "active": True,
                      "state": "blah",
                      "eng_name": "blah",
                      "eng_ver": "blah",
                      "def_time": "1999-09-09T12:12:12",
                      "engine_type": "blah"
                  }])
            metadefender_class_instance._get_version_map(node)
            metadefender_class_instance.nodes[node].pop("oldest_dat")
            assert metadefender_class_instance.nodes[node] == {
                "engine_count": 1,
                "newest_dat": '1999-09-09 12:12:14',
                "engine_list": 'blahblah1999-09-09T12:12:12',
                "engine_map": {
                    'blah': {
                        'def_time': '1999-09-09 12:12:14',
                        'version': 'blah'
                    }
                }
            }

            # MD Version 3
            metadefender_class_instance.config["md_version"] = 3
            m.get(f"{node}stat/engines",
                  status_code=200,
                  json=[{
                      "active": False,
                      "eng_name": "blah",
                      "eng_ver": "blah",
                      "def_time": "09-09-1999T12:12:12",
                      "engine_type": "blah",
                      "eng_type": "blah"
                  }])
            metadefender_class_instance._get_version_map(node)
            metadefender_class_instance.nodes[node].pop("oldest_dat")
            assert metadefender_class_instance.nodes[node] == {
                "engine_count": 0,
                "newest_dat": '1999-09-09 12:12:12',
                "engine_list": 'blahblah1999-09-09T12:12:12Z',
                "engine_map": {
                    'blah': {
                        'def_time': '1999-09-09 12:12:12',
                        'version': 'blah'
                    }
                }
            }

            # etype
            for etype in ["av", "Bundled engine"]:
                m.get(f"{node}stat/engines",
                      status_code=200,
                      json=[{
                          "active": False,
                          "eng_name": "blah",
                          "eng_ver": "blah",
                          "def_time": "09-09-1999T12:12:12",
                          "engine_type": "blah",
                          "eng_type": etype
                      }])
                metadefender_class_instance._get_version_map(node)
                assert metadefender_class_instance.nodes[node] == {
                    "engine_count": 0,
                    "newest_dat": '1999-09-09 12:12:12',
                    "engine_list": 'blahblah1999-09-09T12:12:12Z',
                    "engine_map": {
                        'blah': {
                            'def_time': '1999-09-09 12:12:12',
                            'version': 'blah'
                        }
                    },
                    "oldest_dat": '1999-09-09 12:12:12',
                }

            # Invalid MD Version
            metadefender_class_instance.config["md_version"] = 2
            with pytest.raises(Exception):
                m.get(f"{node}stat/engines",
                      status_code=200,
                      json=[{
                          "active": False,
                          "eng_name": "blah",
                          "eng_ver": "blah",
                          "def_time": "09-09-1999T12:12:12",
                          "engine_type": "blah",
                          "eng_type": "blah"
                      }])
                metadefender_class_instance._get_version_map(node)

            # Failed states
            metadefender_class_instance.config["md_version"] = 4
            for state in ["removed", "temporary failed", "permanently failed"]:
                m.get(f"{node}stat/engines",
                      status_code=200,
                      json=[{
                          "active": False,
                          "state": state,
                          "eng_name": "blah",
                          "eng_ver": "blah",
                          "def_time": "1999-09-09T12:12:12",
                          "engine_type": "blah"
                      }])
                metadefender_class_instance._get_version_map(node)
                metadefender_class_instance.nodes[node].pop("oldest_dat")
                assert metadefender_class_instance.nodes[node] == {
                    "engine_count": 0,
                    "newest_dat": '1999-09-09 12:12:14',
                    "engine_list": 'blahblah1999-09-09T12:12:12',
                    "engine_map": {
                        'blah': {
                            'def_time': '1999-09-09 12:12:14',
                            'version': 'blah'
                        }
                    }
                }
    def start(self) -> None:
        self.log.debug("MetaDefender service started")
        base_urls: List[str] = []
        if type(self.config.get("base_url")) == str:
            base_urls = [self.config.get("base_url")]
        elif type(self.config.get("base_url")) == list:
            for base_url in self.config.get("base_url"):
                prepared_base_url = base_url.replace(" ", "")
                base_urls.append(prepared_base_url)
        else:
            raise Exception(
                "Invalid format for BASE_URL service variable (must be str or list)"
            )
        av_config: Dict[str, Any] = self.config.get("av_config", {})
        self.blocklist: List[str] = av_config.get("blocklist", [])
        self.kw_score_revision_map: Dict[str, int] = av_config.get(
            "kw_score_revision_map", {})
        self.sig_score_revision_map: Dict[str, int] = av_config.get(
            "sig_score_revision_map", {})

        # Initialize a list of all nodes with default data
        for index, url in enumerate(base_urls):
            self.nodes[url] = {
                'engine_map': {},
                'engine_count': 0,
                'engine_list': "default",
                'newest_dat': epoch_to_local(0),
                'oldest_dat': now_as_local(),
                'file_count': 0,
                'queue_times': [],
                'average_queue_time': 0
            }

        # Get version map for all of the nodes
        self.session = Session()
        engine_count = 0
        for node in list(self.nodes.keys()):
            try:
                self._get_version_map(node)
            except Exception as e:
                self.log.error(
                    f"Unable to contact {node} due to {e}. Removing from node list."
                )
                del self.nodes[node]
                continue
            engine_count += self.nodes[node]['engine_count']

        if len(list(self.nodes.keys())) == 0:
            raise Exception(
                "All MetaDefender Core nodes are down. Please ensure that the URLs are correct in the "
                "service config and that the MetaDefender Core REST API is running at each one."
            )

        if engine_count == 0:
            raise Exception(
                f"MetaDefender Core nodes {list(self.nodes.keys())} have an active engine_count of 0"
            )

        # On first launch, choose random node to start with
        if not self.current_node:
            while True:
                self.current_node = random.choice(list(self.nodes.keys()))

                # Check to see if the chosen node has a version map, else try to get version map again
                if self.nodes[self.current_node]['engine_count'] >= 1:
                    self.log.info(
                        f"Node ({self.current_node}) chosen at launch")
                    break
                else:
                    self._get_version_map(self.current_node)

        # Start the global timer
        if not self.start_time:
            self.start_time = time.time()
    def _get_version_map(self, node: str) -> None:
        """
        Get the versions of all engines running on a given node
        :param node: The IP of the MetaDefender node
        :return: None
        """
        newest_dat = 0
        oldest_dat = now()
        engine_list = []
        active_engines = 0
        failed_states = ["removed", "temporary failed", "permanently failed"]
        url = urljoin(node, 'stat/engines')

        try:
            self.log.debug(f"_get_version_map: GET {url}")
            r = self.session.get(url=url, timeout=self.timeout)
            engines = r.json()

            for engine in engines:
                if engine['active'] and engine["state"] not in failed_states:
                    active_engines += 1

                if self.config.get("md_version") == 4:
                    name = self._format_engine_name(engine["eng_name"])
                    version = engine['eng_ver']
                    def_time = engine['def_time']
                    etype = engine['engine_type']
                elif self.config.get("md_version") == 3:
                    name = self._format_engine_name(
                        engine["eng_name"]).replace("scanengine", "")
                    version = engine['eng_ver']
                    def_time = engine['def_time'].replace(" AM", "").replace(
                        " PM", "").replace("/", "-").replace(" ", "T")
                    def_time = def_time[6:10] + "-" + def_time[:5] + def_time[
                        10:] + "Z"
                    etype = engine['eng_type']
                else:
                    raise Exception("Unknown version of MetaDefender")

                # Compute newest DAT
                dat_epoch = iso_to_epoch(def_time)
                if dat_epoch > newest_dat:
                    newest_dat = dat_epoch

                if dat_epoch < oldest_dat and dat_epoch != 0 and etype in [
                        "av", "Bundled engine"
                ]:
                    oldest_dat = dat_epoch

                self.nodes[node]['engine_map'][name] = {
                    'version': version,
                    'def_time': iso_to_local(def_time)[:19]
                }
                engine_list.append(name)
                engine_list.append(version)
                engine_list.append(def_time)

            self.nodes[node]['engine_count'] = active_engines
            self.nodes[node]['newest_dat'] = epoch_to_local(newest_dat)[:19]
            self.nodes[node]['oldest_dat'] = epoch_to_local(oldest_dat)[:19]
            self.nodes[node]['engine_list'] = "".join(engine_list)
        except exceptions.Timeout:
            raise Exception(
                f"Node ({node}) timed out after {self.timeout}s while trying to get engine version map"
            )
        except ConnectionError:
            raise Exception(
                f"Unable to connect to node ({node}) while trying to get engine version map"
            )