Пример #1
0
    def default_sharing(self, id):
        """Change a user's default sharing.

        .. :quickref: User; Change default sharing

        When used on another user account, requires the `manage_users` permission.

        :param id: user id.

        :>json User user: modified user.
        """
        self.ensure_permission(id)

        user = User(get_or_404(User.get_collection(), _id=id))
        groups = request.form.get('groups', '').split(',')

        for group in groups:
            if group in user['groups']:
                break
        else:
            flash('You have to at least keep one of your groups.', 'danger')
            return redirect(request.referrer)

        user.update_value('default_sharing', groups)

        return redirect({'user': clean_users(user)}, request.referrer)
Пример #2
0
    def configuration(self, id):
        """Configure a named configuration.

        .. :quickref: Module; Configure a named configuration

        Requires the `manage_modules` permission.

        For each configuration available, you should set the value in a form
        parameter named ``config_NAME``. For boolean values, any value not ``0``
        or ``False`` is considered to be ``True``.

        If successful, will return the named configuration in ``config``.
        Otherwise, errors will be available in ``errors``.

        :param id: id of the named configuration.
        """
        config = Config(get_or_404(Config.get_collection(), _id=id))

        if request.method == "POST":
            errors = update_config(config['config'])
            if errors is not None:
                return errors

            config.save()
            dispatcher.reload()
            return redirect({'config': config}, url_for('ModulesView:index'))
        else:
            return render({'config': config}, 'modules/configuration.html')
Пример #3
0
    def update(self, id):
        """Update a user.

        .. :quickref: User; Update existing user

        Requires the `manage_users` permission.

        When succesful, the new user will be returned in the ``user`` field.
        Otherwise, an ``errors`` field will list errors.

        :form name: full name
        :form email: email address
        :form groups: comma-delimited list of groups
        :form permission_VALUE: specify a value different than ``0`` or ``False``
            for all permissions the user should have.
        """
        name = request.form.get('name')
        email = request.form.get('email').lower()
        groups = [g for g in request.form.get('groups', '').split(',') if g]

        user = User(get_or_404(User.get_collection(), _id=id))

        if not self._valid_form(name, email, groups, user['email']):
            return validation_error()

        user['name'] = name
        user['email'] = email
        user['groups'] = groups
        user['permissions'] = self.get_permissions(user['permissions'])
        user.save()

        return redirect({'user': clean_users(user)},
                        url_for('UsersView:get', id=user['_id']))
Пример #4
0
    def repository_new(self):
        """Add a repository

        .. :quickref: Module; Add repository

        Requires the `manage_modules` permission.

        If successful, will return the repository in ``repository``.
        Otherwise, errors will be available in ``errors``.

        :form name: name of the repository (should be a valid package name).
        :form address: HTTPs or SSH address of the repository.
        :form private: boolean specifying if the repository is private. See
            Administration Guide for more details on private repositories.
        """
        deploy_key = get_deploy_key()
        repository = Repository()

        if request.method == 'POST':
            for field in ['name', 'address']:
                repository[field] = request.form.get(field)
                if repository[field] is None or repository[field] == "":
                    flash("{} is required.".format(field), 'danger')
                    return validation_error()

                existing_repository = Repository.get(
                    **{field: repository[field]})
                if existing_repository:
                    flash(
                        "There is already a repository with this {}.".format(
                            field), 'danger')
                    return validation_error()

            value = request.form.get('private')
            repository['private'] = (value is not None) and (value not in [
                '0', 'False'
            ])

            if repository['private'] and deploy_key is None:
                flash(
                    "Private repositories are disabled because of a problem with your installation (you do not have a deploy key in 'conf/id_rsa.pub')",
                    'danger')
                return validation_error()

            repository['status'] = 'cloning'
            repository.save()
            repository.clone()
            return redirect({'repository': clean_repositories(repository)},
                            url_for('ModulesView:index'))

        return render({
            'repository': repository,
            'deploy_key': deploy_key
        }, 'modules/repository_new.html')
Пример #5
0
    def refresh_iocs(self, id):
        """Refresh IOCs with Threat Intel modules

        .. :quickref: Analysis; Refresh IOCs with Threat Intel modules.

        :param id: id of the analysis.
        """
        analysis = Analysis(get_or_404(current_user.analyses, _id=id))
        analysis.refresh_iocs()

        return redirect(analysis, url_for('AnalysesView:get', id=analysis["_id"]))
Пример #6
0
    def enable(self, id):
        """Enable a module

        .. :quickref: Module; Enable a module

        Requires the `manage_modules` permission.

        If successful, will return the module in ``module``.
        Otherwise, errors will be available in ``errors``.

        :param id: id of the module to enable.
        """
        module = ModuleInfo(get_or_404(ModuleInfo.get_collection(), _id=id))

        if 'error' in module:
            flash(
                "Cannot enable '{}' because of errors installing dependencies."
                .format(module['name']), 'danger')
            return validation_error(url_for('ModulesView:index'))

        # See if module is properly configured
        module_class = get_class(module['path'], module['class'])
        module_class.info = module
        try:
            module_class()
        except MissingConfiguration as e:
            if e.name:
                flash(
                    "You must configure '{}' before trying to enable '{}'".
                    format(e.name, module['name']), 'warning')
                return validation_error(
                    url_for('ModulesView:configuration', id=e.id))
            else:
                flash(
                    "You must configure '{}' before trying to enable it.".
                    format(module['name']), 'warning')
                return validation_error(
                    url_for('ModulesView:configure', id=module['_id']))

        module.update_value('enabled', True)
        dispatcher.reload()

        readme = module.get_readme()
        if readme:
            flash(readme, 'persistent')

        return redirect({'module': clean_modules(module)},
                        url_for('ModulesView:index'))
Пример #7
0
    def repository_delete(self, id):
        """Delete a repository

        .. :quickref: Module; Delete repository

        Requires the `manage_modules` permission.
        Returns "ok".

        :param id: id of the repository.
        """
        repository = Repository(get_or_404(Repository.get_collection(),
                                           _id=id))
        repository.delete()
        dispatcher.reload()

        return redirect('ok', url_for('ModulesView:index'))
Пример #8
0
    def disable(self, id):
        """Disable a user.

        .. :quickref: User; Disable a user

        Requires the `manage_users` permission.

        :param id: user id.

        :>json User user: modified user.
        """
        user = User(get_or_404(User.get_collection(), _id=id))
        user.update_value('enabled', False)

        return redirect({'user': clean_users(user)},
                        url_for('UsersView:index'))
Пример #9
0
    def disable(self, id):
        """Disable a module

        .. :quickref: Module; Disable a module

        Requires the `manage_modules` permission.

        :param id: id of the module to disable.
        :>json Module module: resulting module.
        """
        module = ModuleInfo(get_or_404(ModuleInfo.get_collection(), _id=id))
        module.update_value('enabled', False)
        dispatcher.reload()

        return redirect({'module': clean_modules(module)},
                        url_for('ModulesView:index'))
Пример #10
0
    def repository_update(self, id):
        """Update a repository

        .. :quickref: Module; Update repository

        Requires the `manage_modules` permission.

        :param id: id of the repository.

        :>json Repository repository: the repository.
        """
        repository = Repository(get_or_404(Repository.get_collection(),
                                           _id=id))
        repository.pull()

        return redirect({'repository': clean_repositories(repository)},
                        url_for('ModulesView:index'))
Пример #11
0
    def reset_api(self, id):
        """Reset a user's API key.

        .. :quickref: User; Reset API key

        When used on another user account, requires the `manage_users` permission.

        :param id: user id.

        :>json User user: modified user.
        """
        self.ensure_permission(id)

        user = User(get_or_404(User.get_collection(), _id=id))
        user.update_value('api_key', User.generate_api_key())

        return redirect({'user': clean_users(user)}, request.referrer)
Пример #12
0
    def reload(self):
        """Reload the workers

        .. :quickref: Module; Reload workers

        Requires the `manage_modules` permission.

        Returns "ok".
        """
        for repository in Repository.get_collection().find():
            dispatcher.update_modules(Repository(repository))

        updates = Internals(
            get_or_404(Internals.get_collection(), name="updates"))
        updates.update_value("last_update", time())

        flash(
            'Workers will reload once they are done with their current tasks',
            'success')

        return redirect('ok', url_for('ModulesView:index'))
Пример #13
0
    def create(self):
        """Create a user.

        .. :quickref: User; Create new user

        Requires the `manage_users` permission.

        When succesful, the new user will be returned in the ``user`` field.
        Otherwise, an ``errors`` field will list errors.

        :form name: full name
        :form email: email address
        :form groups: comma-delimited list of groups
        :form permission_VALUE: specify a value different than ``0`` or ``False``
            for all permissions the user should have.
        """
        name = request.form.get('name')
        email = request.form.get('email').lower()
        groups = [g for g in request.form.get('groups', '').split(',') if g]

        if not self._valid_form(name, email, groups):
            return validation_error()

        user = User({
            'name': name,
            'email': email.lower(),
            'groups': groups,
            'default_sharing': groups,
            'permissions': self.get_permissions(),
            'enabled': True
        })

        if not auth_module.create_user(user):
            return validation_error()

        user.save()

        return redirect({'user': clean_users(user)},
                        url_for('UsersView:index'))
Пример #14
0
    def configure(self, id):
        """Configure a module.

        .. :quickref: Module; Configure a module

        Requires the `manage_modules` permission.

        For each configuration available, you should set the value in a form
        parameter named ``config_NAME``. For boolean values, any value not ``0``
        or ``False`` is considered to be ``True``.

        If the setting should be an option (be available per analysis), you have
        to set ``config_NAME_option`` to any value but ``0`` or ``False``.

        If successful, will return the module in ``module``.
        Otherwise, errors will be available in ``errors``.

        :param id: id of the named configuration.

        :form acts_on: comma-delimited list of FAME types this module can act on
            (only for Processing modules).
        :form triggered_by: comma-delimited list of triggers (only for Processing
            modules).
        :form queue: name of the queue to use for this module (only for Processing
            modules).
        """
        module = ModuleInfo(get_or_404(ModuleInfo.get_collection(), _id=id))

        if request.method == "POST":
            if module['type'] == 'Processing':
                if 'acts_on' in request.form:
                    module.update_setting_value(
                        'acts_on', request.form.get('acts_on', ''))

                if 'triggered_by' in request.form:
                    module.update_setting_value(
                        'triggered_by', request.form.get('triggered_by', ''))

                if 'queue' in request.form:
                    new_queue = request.form.get('queue')

                    if module['queue'] == '':
                        flash('queue cannot be empty', 'danger')
                        return validation_error()
                    else:
                        if module['queue'] != new_queue:
                            module.update_setting_value('queue', new_queue)
                            updates = Internals(
                                get_or_404(Internals.get_collection(),
                                           name="updates"))
                            updates.update_value("last_update", time())

                            flash(
                                'Workers will reload once they are done with their current tasks',
                                'success')

            errors = update_config(module['config'],
                                   options=(module['type'] == 'Processing'))
            if errors is not None:
                return errors

            module.save()
            dispatcher.reload()
            return redirect({'module': clean_modules(module)},
                            url_for('ModulesView:index'))
        else:
            return render({'module': clean_modules(module)},
                          'modules/module_configuration.html')
Пример #15
0
    def configure(self, id):
        """Configure a module.

        .. :quickref: Module; Configure a module

        Requires the `manage_modules` permission.

        For each configuration available, you should set the value in a form
        parameter named ``config_NAME``. For boolean values, any value not ``0``
        or ``False`` is considered to be ``True``.

        If the setting should be an option (be available per analysis), you have
        to set ``config_NAME_option`` to any value but ``0`` or ``False``.

        If successful, will return the module in ``module``.
        Otherwise, errors will be available in ``errors``.

        :param id: id of the named configuration.

        :form acts_on: comma-delimited list of FAME types this module can act on
            (only for Processing modules).
        :form triggered_by: comma-delimited list of triggers (only for Processing
            modules).
        :form queue: name of the queue to use for this module (for Processing and
            Preloading modules).
        """
        module = ModuleInfo(get_or_404(ModuleInfo.get_collection(), _id=id))
        module['readme'] = module.get_readme()

        if request.method == "POST":
            if module['type'] == 'Filetype':
                if 'acts_on' in request.form:
                    module.update_setting_value(
                        'acts_on', request.form.get('acts_on', ''))
            elif module['type'] == 'Processing':
                if 'acts_on' in request.form:
                    module.update_setting_value(
                        'acts_on', request.form.get('acts_on', ''))

                if 'triggered_by' in request.form:
                    module.update_setting_value(
                        'triggered_by', request.form.get('triggered_by', ''))

                if 'queue' in request.form:
                    update_queue(module, request.form.get('queue', ''))

            elif module['type'] == "Preloading":
                if 'queue' in request.form:
                    update_queue(module, request.form.get('queue', ''))

                if 'priority' in request.form:
                    update_priority(module, request.form.get('priority', ''))

            errors = update_config(module['config'],
                                   options=(module['type']
                                            in ['Preloading', 'Processing']))
            if errors is not None:
                return errors

            module.save()
            dispatcher.reload()
            return redirect({'module': clean_modules(module)},
                            url_for('ModulesView:index'))
        else:
            return render({'module': clean_modules(module)},
                          'modules/module_configuration.html')
Пример #16
0
class ModulesView(FlaskView, UIView):
    @requires_permission('manage_modules')
    def index(self):
        """Get the list of modules.

        .. :quickref: Module; Get the list of modules

        Requires the `manage_modules` permission.

        The response is a dict with several elements:

        * ``modules``, which is a list of modules, sorted by type::

            "modules": {
                "Antivirus": [
                    ...
                ],
                "Preloading": [
                    ...
                ],
                "Processing": [
                    {
                        "_id": {
                            "$oid": "MODULE_ID"
                        },
                        "acts_on": [
                            ACTS_ON_FAME_TYPES
                        ],
                        "class": "CLASS_NAME",
                        "config": [ CONFIG_OPTIONS ],
                        "description": "DESCRIPTION",
                        "enabled": false,
                        "generates": [GENERATES],
                        "name": "NAME",
                        "path": "MODULE_PATH",
                        "queue": "QUEUE",
                        "triggered_by": [
                            TRIGGERS
                        ],
                        "type": "Processing"
                    },
                    ...
                ],
                "Reporting": [
                    ...
                ],
                "Threat Intelligence": [
                    ...
                ],
                "Filetype": [
                    ...
                ]
            }

        * ``repositories``: list of configured repositories::

            "repositories": [
                {
                    "_id": {
                        "$oid": "ID"
                    },
                    "address": "[email protected]:certsocietegenerale/fame_modules.git",
                    "name": "community",
                    "private": false,
                    "status": "active"
                },
                ...
            ]

        * ``configs``: list of named configurations::

            "configs": [
                {
                    "_id": {
                        "$oid": "ID"
                    },
                    "config": [
                        {
                            "description": "List of patterns (strings) to look for in malware configurations. There should be one pattern per line.",
                            "name": "monitor",
                            "type": "text",
                            "value": null
                        }
                    ],
                    "description": "Needed in order to be able to track malware targets",
                    "name": "malware_config"
                },
                ...
            ]
        """
        types = {
            'Preloading': [],
            'Processing': [],
            'Reporting': [],
            'Threat Intelligence': [],
            'Antivirus': [],
            'Virtualization': [],
            'Filetype': []
        }

        for module in ModuleInfo.get_collection().find():
            types[module['type']].append(clean_modules(module))

        for type in types:
            types[type] = sorted(types[type], key=get_name)

        configs = Config.get_collection().find()

        repositories = clean_repositories(
            list(Repository.get_collection().find()))

        return render(
            {
                'modules': types,
                'configs': configs,
                'repositories': repositories
            }, 'modules/index.html')

    @requires_permission('manage_modules')
    @route('/<id>/disable', methods=['POST'])
    def disable(self, id):
        """Disable a module

        .. :quickref: Module; Disable a module

        Requires the `manage_modules` permission.

        :param id: id of the module to disable.
        :>json Module module: resulting module.
        """
        module = ModuleInfo(get_or_404(ModuleInfo.get_collection(), _id=id))
        module.update_value('enabled', False)
        dispatcher.reload()

        return redirect({'module': clean_modules(module)},
                        url_for('ModulesView:index'))

    @requires_permission('manage_modules')
    @route('/<id>/enable', methods=['POST'])
    def enable(self, id):
        """Enable a module

        .. :quickref: Module; Enable a module

        Requires the `manage_modules` permission.

        If successful, will return the module in ``module``.
        Otherwise, errors will be available in ``errors``.

        :param id: id of the module to enable.
        """
        module = ModuleInfo(get_or_404(ModuleInfo.get_collection(), _id=id))

        if 'error' in module:
            flash(
                "Cannot enable '{}' because of errors installing dependencies."
                .format(module['name']), 'danger')
            return validation_error(url_for('ModulesView:index'))

        # See if module is properly configured
        module_class = get_class(module['path'], module['class'])
        module_class.info = module
        try:
            module_class()
        except MissingConfiguration, e:
            if e.name:
                flash(
                    "You must configure '{}' before trying to enable '{}'".
                    format(e.name, module['name']), 'warning')
                return validation_error(
                    url_for('ModulesView:configuration', id=e.id))
            else:
                flash(
                    "You must configure '{}' before trying to enable it.".
                    format(module['name']), 'warning')
                return validation_error(
                    url_for('ModulesView:configure', id=module['_id']))

        module.update_value('enabled', True)
        dispatcher.reload()

        readme = module.get_readme()
        if readme:
            flash(readme, 'persistent')

        return redirect({'module': clean_modules(module)},
                        url_for('ModulesView:index'))
Пример #17
0
    def resume(self, id):
        analysis = Analysis(get_or_404(Analysis.get_collection(), _id=id))
        analysis.resume()

        flash("Resumed analysis {}".format(analysis['_id']))
        return redirect({}, url_for('SystemView:index'))
Пример #18
0
    def post(self):
        """Create a new analysis.

        .. :quickref: Analysis; Create an analysis

        Launch a new analysis. You have to specify on which object this analysis
        will be made, by specifying one of:

        * ``file_id`` for an existing object
        * ``file`` for file uploads
        * ``url``
        * ``hash`` if VirusTotal sample retrieval is enabled.

        You should also supply all enabled analysis options with the name
        ``options[OPTION_NAME]``. For boolean options, any value different than
        ``0`` or ``False`` means the option is enabled.

        If the submitted object already exists (and ``file_id`` was not specified),
        the response will be a file object. When a new analysis was successfuly
        created, the analysis object will be returned, in the ``analysis`` field.

        If there was error in your submission, they will be returned in the
        ``errors`` field.

        **Example request**::

            headers = {
                'Accept': "application/json",
                'X-API-KEY': FAME_API_KEY
            }

            with open(filepath, 'rb') as f:
                params = {
                    'options[allow_internet_access]':  "on",
                    'options[analysis_time]': "300",
                    'groups': "cert"
                }

                files = {
                    'file': f
                }

                r = requests.post(ENDPOINT, data=params, files=files, headers=headers)

        :form string file_id: (optional) the id of the object on which this analysis should run.
        :form file file: (optional) file to analyze.
        :form string url: (optional) url to analyze.
        :form string hash: (optional) hash to analyze.
        :form string module: (optional) the name of the target module.
        :form string groups: a comma-separated list of groups that will have access to this analysis.
        :form string comment: comment to add to this object.
        :form string option[*]: value of each enabled option.
        """
        file_id = request.form.get('file_id')
        modules = filter(None, request.form.get('modules', '').split(','))
        groups = request.form.get('groups', '').split(',')
        comment = request.form.get('comment', '')

        options = get_options()
        if options is None:
            return validation_error()

        valid_submission = self._validate_form(groups, modules, options)
        if not valid_submission:
            return validation_error()

        if file_id is not None:
            f = File(get_or_404(current_user.files, _id=file_id))
            analysis = {
                'analysis':
                f.analyze(groups, current_user['_id'], modules, options)
            }
            return redirect(
                analysis,
                url_for('AnalysesView:get', id=analysis['analysis']['_id']))
        else:
            # When this is a new submission, validate the comment
            if not self._validate_comment(comment):
                return validation_error()

            f = self._get_object_to_analyze()
            if f is not None:
                f.add_owners(set(current_user['groups']) & set(groups))

                if comment:
                    f.add_comment(current_user['_id'], comment)

                if f.existing:
                    f.add_groups(groups)
                    flash(
                        "File already exists, so the analysis was not launched."
                    )

                    return redirect(clean_files(f),
                                    url_for('FilesView:get', id=f['_id']))
                else:
                    analysis = {
                        'analysis':
                        clean_analyses(
                            f.analyze(groups, current_user['_id'], modules,
                                      options))
                    }
                    analysis['analysis']['file'] = clean_files(f)

                    return redirect(
                        analysis,
                        url_for('AnalysesView:get',
                                id=analysis['analysis']['_id']))
            else:
                return render_template('analyses/new.html',
                                       options=dispatcher.options)