Beispiel #1
0
 async def _get_documents(self, sha, sca, cmd, args, fmt, otpt):
     documents = []
     cur = db.async_command_collection.select_many(sha256_digest=sha,
                                                   scale=sca,
                                                   command=cmd,
                                                   args=args,
                                                   sort="timestamp")
     while await cur.fetch_next:
         doc = cur.next_object()
         doc = schema.CommandSchema().load(doc)
         try:  # Ignore output for missing scales and/or commands
             scale = scale_manager.get_scale(doc['scale'])
             commands = scale_manager.get_component(
                 scale, enums.ScaleComponent.COMMANDS)
         except Exception as err:
             print("%s - %s" % (doc['scale'], err))  # TODO: Output to log
             continue
         output = None
         if '_output_id' in doc and doc['_output_id']:
             output = await db.async_command_output_collection.get(
                 doc['_output_id'])
         doc = schema.CommandSchema().dump(doc)
         try:
             if otpt:
                 doc['output'] = commands.snake.format(fmt, cmd, output)
             doc['format'] = fmt
         except (SnakeError, TypeError) as err:
             print("%s - %s" % (doc['scale'], err))  # TODO: Output to log
             continue
         documents += [doc]
     return documents
Beispiel #2
0
    async def post(self, scale):
        if not self.request.body:
            self.write_warning("scale/interface - no request body found", 422,
                               scale)
            self.finish()
            return
        data = escape.json_decode(self.request.body)

        try:
            data = self.InterfaceSchema().dump(
                self.InterfaceSchema().load(data))
        except exceptions.ValidationError as err:
            self.write_warning(
                self.json_decode(('%s' % err.messages).replace("'", '"')), 422)
            self.finish()
            return

        document = await db.async_file_collection.select(data['sha256_digest'])
        if not document:
            self.write_warning("scale/interface - no sample for given data",
                               404, data)
            self.finish()
            return

        # Get the push/pull and args
        _scale = scale_manager.get_scale(scale)
        interface = scale_manager.get_component(_scale,
                                                enums.ScaleComponent.INTERFACE)
        command = scale_manager.get_interface_command(interface, data['type'],
                                                      data['command'])

        data['timestamp'] = datetime.utcnow()

        # Execute command
        # TODO: Handle status don't always chuck errors...
        try:
            loop = asyncio.get_event_loop()
            output = await loop.run_in_executor(None, command, data['args'],
                                                data['sha256_digest'])
            # output = p(data['args'], data['sha256_digest'])
        except exceptions.ValidationError as err:
            self.write_warning(
                self.json_decode(
                    ('{"args": %s}' % err.messages).replace("'", '"')), 422)
            self.finish()
            return
        except error.SnakeError as err:
            self.write_warning("%s" % err, err.status_code, data)
            self.finish()
            return

        # Run formating
        data['output'] = interface.snake.format(data['format'],
                                                data['command'], output)

        self.jsonify({"interface": data})
        self.finish()
Beispiel #3
0
    async def post(self, data):
        # Check that there is a file for this hash
        document = await db.async_file_collection.select(data['sha256_digest'])
        if not document:
            self.write_warning("no sample for given data", 404, data)
            self.finish()
            return

        # Check scale support
        try:
            scale = scale_manager.get_scale(data['scale'],
                                            document['file_type'])
            commands = scale_manager.get_component(
                scale, enums.ScaleComponent.COMMANDS)
            cmd = commands.snake.command(data['command'])
        except SnakeError as err:
            self.write_warning("%s" % err, 404, data)
            self.finish()
            return

        # Validate arguments as to not waste users time, yes this is also done on execution
        result, args = validate_args(cmd, data['args'])
        if not result:
            self.write_warning(args, 422, data)
            self.finish()
            return
        data['args'] = args

        # Queue command
        try:
            document = await route_support.queue_command(data)
        except SnakeError as err:
            self.write_warning("%s" % err, 500, data)
            self.finish()
            return

        document = schema.CommandSchema().load(document)
        output = None
        if document['_output_id']:
            output = await db.async_command_output_collection.get(
                document['_output_id'])
        try:
            document['output'] = commands.snake.format(data['format'],
                                                       document['command'],
                                                       output)
            document['format'] = data['format']
        except SnakeError as err:
            self.write_warning("%s" % err, 404, data)
            self.finish()
            return

        # Dump and finish
        document = schema.CommandSchema().dump(document)
        self.jsonify({"command": document})
        self.finish()
Beispiel #4
0
 async def get(self, scale):
     try:
         _scale = scale_manager.get_scale(scale)
         commands = scale_manager.get_component(
             _scale, enums.ScaleComponent.COMMANDS)
     except error.SnakeError as err:
         self.write_warning("scale - %s" % err, err.status_code, scale)
         self.finish()
         return
     self.jsonify({"commands": commands.snake.info()})
     self.finish()
Beispiel #5
0
 async def get(self, scale):
     reload = self.get_argument('reload', "false")
     if reload.lower() == "true":  # Ignore all other values
         scale_manager.reload_scales()
     try:
         _scale = scale_manager.get_scale(scale)
     except error.SnakeError as err:
         self.write_warning("scale - %s" % err, err.status_code, scale)
         self.finish()
         return
     self.jsonify({"scale": _scale.info()})
     self.finish()
Beispiel #6
0
    async def get(self, data):
        # NOTE: Tornado/Marshmallow does not like Dict in args, will have to parse manually
        # TODO: Use marshmallow validation
        if 'args' in self.request.arguments and self.request.arguments['args']:
            data['args'] = json.loads(self.request.arguments['args'][0])
        else:
            data['args'] = {}
        document = await db.async_command_collection.select(
            data['sha256_digest'], data['scale'], data['command'],
            data['args'])
        if not document:
            self.write_warning("no output for given data", 404, data)
            self.finish()
            return

        if document['status'] == enums.Status.ERROR:
            self.write_warning("%s" % document['output'], 404, data)
            self.finish()
            return

        document = schema.CommandSchema().load(document)
        output = None
        if document['_output_id']:
            output = await db.async_command_output_collection.get(
                document['_output_id'])
        try:
            scale = scale_manager.get_scale(data['scale'])
            commands = scale_manager.get_component(
                scale, enums.ScaleComponent.COMMANDS)
            if data['output']:
                document['output'] = commands.snake.format(
                    data['format'], document['command'], output)
            document['format'] = data['format']
        except (SnakeError, TypeError) as err:
            self.write_warning("%s" % err, 404, data)
            self.finish()
            return

        document = schema.CommandSchema().dump(document)
        self.jsonify({'command': document})
        self.finish()
Beispiel #7
0
 async def get(self, scale):
     _scale = scale_manager.get_scale(scale)
     interface = scale_manager.get_component(_scale,
                                             enums.ScaleComponent.INTERFACE)
     self.jsonify({"interface": interface.snake.info()})
     self.finish()
Beispiel #8
0
    async def post(self, scale):  # pylint: disable=too-many-locals
        if not self.request.body:
            self.write_warning("scale/upload - no request body found", 422,
                               scale)
            self.finish()
            return
        data = escape.json_decode(self.request.body)

        # Validate args
        try:
            data = self.UploadSchema().dump(self.UploadSchema().load(data))
        except exceptions.ValidationError as err:
            self.write_warning(
                self.json_decode(('%s' % err.messages).replace("'", '"')), 422)
            self.finish()
            return

        scale_ = scale_manager.get_scale(scale)
        upload = scale_manager.get_component(scale_,
                                             enums.ScaleComponent.UPLOAD)

        # Validate arguments and update
        upld_args = upload.arguments()
        try:
            if upld_args:
                data['args'] = schema.Schema(fields=upld_args).load(
                    data['args'])
        except exceptions.ValidationError as err:
            self.write_warning(
                self.json_decode(
                    ('{"args": %s}' % err.messages).replace("'", '"')), 422)
            self.finish()
            return

        # Get the file
        with tempfile.TemporaryDirectory(dir=path.abspath(
                path.expanduser(snake_config['cache_dir']))) as temp_dir:
            loop = asyncio.get_event_loop()
            f_name = await loop.run_in_executor(None, upload.upload,
                                                data['args'], temp_dir)
            f_path = path.join(temp_dir, f_name)

            # Extract if required, zip only
            if data['extract']:
                f_path = await route_support.unzip_file(
                    f_path, data['password'])
                f_name = path.basename(f_path)

            # Update name if not overriden
            if not data['name']:
                data['name'] = f_name

            # Set submission type
            data['submission_type'] = 'upload:{}'.format(scale)

            # Check that the file is not empty
            if path.getsize(f_path) == 0:
                self.write_warning("scale/upload - sample is empty", 422)
                self.finish()
                return

            # Hash the file
            sha2 = hashlib.sha256()
            with open(f_path, 'rb') as f:
                chunk = f.read(4096)
                while chunk:
                    sha2.update(chunk)
                    chunk = f.read(4096)
            sha256_digest = sha2.hexdigest()

            # Check if the file already exists
            document = await db.async_file_collection.select(sha256_digest)
            if document:
                document = schema.FileSchema().dump(
                    schema.FileSchema().load(document))
                self.write_warning(
                    "scale/upload - sample already exists for given sha256 digest",
                    409, {'sample': document})
                self.finish()
                return

            # Save the file and add it to the database
            document = await route_support.store_file(sha256_digest, f_path,
                                                      data['file_type'], data)
            document = schema.FileSchema().dump(
                schema.FileSchema().load(document))
            self.jsonify({'sample': document})
            self.finish()
Beispiel #9
0
 async def get(self, scale):
     scale_ = scale_manager.get_scale(scale)
     upload = scale_manager.get_component(scale_,
                                          enums.ScaleComponent.UPLOAD)
     self.jsonify({"upload": upload.snake.info()})
     self.finish()
Beispiel #10
0
    async def post(self, data):  # pylint: disable=too-many-locals, too-many-branches, too-many-statements
        # XXX: Needs a major clean/rework
        if not data:
            self.write_warning("commands - no request body found", 422, data)
            self.finish()
            return

        # Find the commands and validate their arguments
        for d in data:
            # Find the command
            try:
                s = scale_manager.get_scale(d['scale'])
                c = scale_manager.get_component(s,
                                                enums.ScaleComponent.COMMANDS)
                cmd = c.snake.command(d['command'])
            except ScaleError as err:
                self.write_warning(err.message, err.status_code)
                self.finish()
                return

            result, args = validate_args(cmd, d['args'])
            if not result:
                self.write_warning(self.json_decode(args.replace("'", '"')),
                                   422, data)
                self.finish()
                return
            d['args'] = args

        # Validate hashes and validate them against scales
        missing = []
        unsupported = []
        for d in data:
            s = scale_manager.get_scale(d['scale'])
            for sha in d['sha256_digests']:
                if sha.lower() == 'all':
                    if not s.supports and not len(s.supports) == len(
                        [x for x in enums.FileType]):
                        unsupported += [d]
                    break
                elif sha.lower()[:4] == 'all:':
                    file_type = sha.lower().split(':')[1]
                    if file_type == 'file':
                        ft = enums.FileType.FILE
                    elif file_type == 'memory':
                        ft = enums.FileType.MEMORY
                    else:
                        ft = None
                    if ft is None or (s.supports and ft not in s.supports):
                        unsupported += [(sha, s.name)]
                    break
                else:
                    document = await db.async_file_collection.select(sha)
                    if not document:
                        missing += [d]
                    elif s.supports and document[
                            'file_type'] not in s.supports:  # Check scale support
                        unsupported += [d]
        if missing:
            self.write_warning("commands - no sample(s) for given data", 404,
                               missing)
            self.finish()
            return
        if unsupported:
            self.write_warning("commands - command unsupported for given data",
                               422, unsupported)
            self.finish()
            return

        # Queue commands
        documents = []
        for d in data:
            cmd_dict = {}
            for k, v in d.items():
                if k != 'sha256_digests':
                    cmd_dict[k] = v
            cmd_dict['asynchronous'] = True
            for sha in d['sha256_digests']:
                if sha.lower() == 'all':
                    cursor = db.async_file_collection.select_all()
                    while await cursor.fetch_next:
                        cmd_dict['sha256_digest'] = cursor.next_object(
                        )['sha256_digest']
                        cmd_d = schema.CommandSchema().load(cmd_dict)
                        documents += [await route_support.queue_command(cmd_d)]
                    break
                elif sha.lower()[:4] == 'all:':
                    ft = sha.lower().split(':')[1]
                    if ft == 'file':
                        ft = enums.FileType.FILE
                    elif ft == 'memory':
                        ft = enums.FileType.MEMORY
                    cursor = db.async_file_collection.select_many(file_type=ft)
                    while await cursor.fetch_next:
                        cmd_dict['sha256_digest'] = cursor.next_object(
                        )['sha256_digest']
                        cmd_d = schema.CommandSchema().load(cmd_dict)
                        documents += [await route_support.queue_command(cmd_d)]
                    break
                else:
                    cmd_dict['sha256_digest'] = sha
                    cmd_d = schema.CommandSchema().load(cmd_dict)
                    documents += [await route_support.queue_command(cmd_d)]

        # Dump and finish
        documents = schema.CommandSchema(many=True).load(documents)
        documents = schema.CommandSchema(many=True).dump(documents)
        self.jsonify({"commands": documents})
        self.finish()