Пример #1
0
    def test_yaml_docs_with_defaults(self):

        failing = []

        with get_app().test_request_context():
            for endpoint in get_app().view_functions:
                spec.path(view=get_app().view_functions[endpoint],
                          app=get_app())

        spec_yaml = yaml.load(spec.to_yaml(), Loader=yaml.BaseLoader)

        for path_key, path_value in spec_yaml["paths"].items():

            path_temp = {path_key: {}}

            for data_key, data_value in path_value.items():
                if not any(data_value):
                    path_temp[path_key][data_key] = data_value

            if any(path_temp[path_key]):
                failing.append(path_temp)

        if any(failing):
            print("Endpoints with default docs:\n")
            print(json.dumps(failing, indent=1))
        assert not any(failing)
Пример #2
0
def test_v2_in_v3_endpoints():
    exceptions = {
        '/v3/ws/<workspace_id>/activate',
        '/v3/ws/<workspace_id>/change_readonly',
        '/v3/ws/<workspace_id>/deactivate',
        '/v3/ws/<workspace_name>/hosts/bulk_delete',
        '/v3/ws/<workspace_name>/vulns/bulk_delete',
        '/v3/ws/<workspace_name>/vulns/<int:vuln_id>/attachments'
    }
    rules_v2 = set(
        map(
            lambda rule: rule.rule.replace("v2", "v3").rstrip("/"),
            filter(lambda rule: rule.rule.startswith("/v2"),
                   get_app().url_map.iter_rules())))
    rules = set(
        map(
            lambda rule: rule.rule,
            filter(lambda rule: rule.rule.startswith("/v3"),
                   get_app().url_map.iter_rules())))
    exceptions_present_v2 = rules_v2.intersection(exceptions)
    assert len(exceptions_present_v2) == len(exceptions), sorted(
        exceptions_present_v2)
    exceptions_present = rules.intersection(exceptions)
    assert len(exceptions_present) == 0, sorted(exceptions_present)
    # We can have extra endpoints in v3 (like all the PATCHS)
    difference = rules_v2.difference(rules).difference(exceptions)
    assert len(difference) == 0, sorted(difference)
Пример #3
0
    def test_case_ws_with_invalid_authentication_token(self, test_client,
                                                       session):
        """
            Use of an invalid auth token
        """
        # clean cookies make sure test_client has no session
        test_client.cookie_jar.clear()
        secret_key = get_app().config['SECRET_KEY']
        alice = factories.UserFactory.create(
            active=True,
            username='******',
            password=hash_password('passguord'),
            role='pentester')
        session.add(alice)
        session.commit()

        ws = factories.WorkspaceFactory.create(name='wonderland')
        session.add(ws)
        session.commit()

        serializer = TimedJSONWebSignatureSerializer(
            get_app().config['SECRET_KEY'], expires_in=500, salt="token")
        token = serializer.dumps({'user_id': alice.id})

        headers = {'Authorization': b'Token ' + token}

        ws = test_client.get(self.check_url('/v2/ws/wonderland/'),
                             headers=headers)
        assert ws.status_code == 401
Пример #4
0
    def test_yaml_docs_with_no_doc(self):

        exc = {
            '/login', '/logout', '/change', '/reset', '/reset/{token}',
            '/verify'
        }
        failing = []

        with get_app().test_request_context():
            for endpoint in get_app().view_functions:
                spec.path(view=get_app().view_functions[endpoint],
                          app=get_app())

        spec_yaml = yaml.load(spec.to_yaml(), Loader=yaml.BaseLoader)

        for path_key, path_value in spec_yaml["paths"].items():

            if path_key in exc:
                continue

            path_temp = {path_key: {}}

            if not any(path_value):
                failing.append(path_temp)

        if any(failing):
            print("Endpoints with no docs\n")
            print(json.dumps(failing, indent=1))
        assert not any(failing)
Пример #5
0
def check_locks_postgresql():
    with get_app().app_context():
        psql_status = check_postgres()
        if psql_status:
            result = db.engine.execute("""SELECT blocked_locks.pid     AS blocked_pid,
                                            blocked_activity.usename  AS blocked_user,
                                            blocking_locks.pid     AS blocking_pid,
                                            blocking_activity.usename AS blocking_user,
                                            blocked_activity.query    AS blocked_statement,
                                            blocking_activity.query   AS current_statement_in_blocking_process
                                        FROM  pg_catalog.pg_locks         blocked_locks
                                            JOIN pg_catalog.pg_stat_activity blocked_activity  ON blocked_activity.pid = blocked_locks.pid
                                        JOIN pg_catalog.pg_locks         blocking_locks
                                            ON blocking_locks.locktype = blocked_locks.locktype
                                            AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE
                                            AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
                                            AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
                                            AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
                                            AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
                                            AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
                                            AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
                                            AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
                                            AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
                                            AND blocking_locks.pid != blocked_locks.pid
                                        JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
                                            WHERE NOT blocked_locks.GRANTED;""")
            fetch = result.fetchall()
            if fetch:
                return True
            else:
                return False

        else:
            return None
Пример #6
0
def check_alembic_version():
    config = Config()
    config.set_main_option("script_location", "migrations")
    script = ScriptDirectory.from_config(config)

    head_revision = script.get_current_head()
    with get_app().app_context():
        try:
            conn = db.session.connection()
        except ImportError:
            if not faraday.server.config.database.connection_string:
                print("\n\nNo database configuration found. Did you execute \"faraday-manage initdb\"? \n\n")
                sys.exit(1)
        except sqlalchemy.exc.OperationalError:
            print("Bad Credentials, please check the .faraday/config/server.ini")
            sys.exit(1)

        context = MigrationContext.configure(conn)

        current_revision = context.get_current_revision()
        if head_revision != current_revision:
            version_path = faraday.server.config.FARADAY_BASE / 'migrations'\
                           / 'versions'
            if list(version_path.glob(f'{current_revision}_*.py')):
                print('--' * 20)
                print('Missing migrations, please execute: \n\n')
                print('faraday-manage migrate')
                sys.exit(1)
            else:
                logger.warning(
                    "You are using an unknown schema version. If you are a "
                    "developer, this probably happened because you used branch "
                    "with a schema migration not merged yet. If you are a "
                    "normal user, consider reporting this bug back to us"
                    )
Пример #7
0
def test_v3_endpoints():
    rules = list(
        filter(
            lambda rule: rule.rule.startswith("/v3") and rule.rule.endswith("/"
                                                                            ),
            get_app().url_map.iter_rules()))
    assert len(rules) == 0, [rule.rule for rule in rules]
Пример #8
0
 def delete_configuration(self):
     from faraday.server.web import get_app  # pylint: disable=import-outside-toplevel
     with get_app().app_context():
         db.session.query(Configuration).filter(
             Configuration.key == self.settings_key).delete()
         db.session.commit()
         self.__class__.value.fget.cache_clear()
Пример #9
0
def create_superuser(username, email, password):
    with get_app().app_context():
        if db.session.query(User).filter_by(active=True).count() > 0:
            print(
                "Can't create more users. The community edition only allows one user. "
                "Please contact support for further information.")
            sys.exit(1)

        get_app().user_datastore.create_user(username=username,
                                       email=email,
                                       password=hash_password(password),
                                       roles=['admin'],
                                       is_ldap=False)
        db.session.commit()
        click.echo(click.style(
            f'User {username} created successfully!',
            fg='green', bold=True))
Пример #10
0
def check_postgresql_encoding():
    with get_app().app_context():
        psql_status = check_postgres()
        if psql_status:
            encoding = db.engine.execute("SHOW SERVER_ENCODING").first()[0]
            return encoding
        else:
            return None
Пример #11
0
    def test_tags_sorted_correctly(self):

        tags = set()

        with get_app().test_request_context():
            for endpoint in get_app().view_functions:
                spec.path(view=get_app().view_functions[endpoint],
                          app=get_app())

        spec_yaml = yaml.load(spec.to_yaml(), Loader=yaml.BaseLoader)

        for path_value in spec_yaml["paths"].values():
            for data_value in path_value.values():
                if 'tags' in data_value and any(data_value['tags']):
                    for tag in data_value['tags']:
                        tags.add(tag)

        assert sorted(tags) == openapi_format(return_tags=True)
Пример #12
0
def check_postgres():
    with get_app().app_context():
        try:
            result = (
                db.session.query("version()").one(), db.session.query("current_setting('server_version_num')").one())
            return result
        except sqlalchemy.exc.OperationalError:
            return False
        except sqlalchemy.exc.ArgumentError:
            return None
Пример #13
0
 def load_configuration(self) -> Dict:
     from faraday.server.web import get_app  # pylint: disable=import-outside-toplevel
     with get_app().app_context():
         query = db.session.query(Configuration).filter(
             Configuration.key == self.settings_key).first()
         settings_config = self.get_default_config()
         if query:
             settings_config.update(query.value)
             settings_config = self.clear_configuration(settings_config)
     return settings_config
Пример #14
0
def changes_password(username, password):
    with get_app().app_context():
        user = User.query.filter_by(username=username).first()
        if user:
            user.password = hash_password(password)
            db.session.add(user)
            db.session.commit()
            print("Password changed succesfully")
        else:
            print("User not found in Faraday's Database")
def import_vulnerability_templates(language):
    imported_rows = 0
    duplicated_rows = 0
    with get_app().app_context():
        try:
            res = requests.get(f'{CWE_URL}/cwe_{language}.csv')
        except Exception as e:
            print(
                f'[{Fore.RED}-{Style.RESET_ALL}] An error has occurred downloading the file.\n{e}'
            )
            return None

        if res.status_code != 200:
            print(
                f'[{Fore.RED}-{Style.RESET_ALL}] An error has occurred downloading the file.'
                f' Response was {res.status_code}')
            return None

        cwe_file = tempfile.TemporaryFile(mode="w+t")
        cwe_file.write(res.content.decode('utf8'))
        cwe_file.seek(0)

        vulnerability_templates = csv.DictReader(cwe_file)
        for vulnerability_template in vulnerability_templates:
            vulnerability_template = dict(vulnerability_template)

            references = [
                ref.strip()
                for ref in vulnerability_template['references'].split(',')
            ]
            try:
                v = VulnerabilityTemplate(
                    name=vulnerability_template['name'],
                    description=vulnerability_template['description'],
                    severity=vulnerability_template['exploitation'],
                    resolution=vulnerability_template['resolution'],
                    references=references,
                    shipped=True)
                db.session.add(v)
                db.session.flush()
                imported_rows += 1
            except IntegrityError:
                duplicated_rows += 1
                db.session.rollback()
        db.session.commit()

        if imported_rows > 0:
            print(
                f'[{Fore.GREEN}+{Style.RESET_ALL}] {imported_rows} new vulnerability templates were imported'
            )
        else:
            print(
                f'[{Fore.YELLOW}+{Style.RESET_ALL}] {duplicated_rows} vulnerability templates were already imported'
            )
Пример #16
0
 def send_report_request(self, workspace_name: str, command_id: int,
                         report_json: dict, user_id: int):
     logger.info("Send Report data to workspace [%s]", workspace_name)
     from faraday.server.web import get_app  # pylint:disable=import-outside-toplevel
     with get_app().app_context():
         ws = Workspace.query.filter_by(name=workspace_name).one()
         command = Command.query.filter_by(id=command_id).one()
         user = User.query.filter_by(id=user_id).one()
         schema = BulkCreateSchema()
         data = schema.load(report_json)
         data = add_creator(data, user)
         bulk_create(ws, command, data, True, True)
Пример #17
0
def process_report(workspace_name: str, command_id: int, file_path: Path,
                   plugin_id: Optional[int], user_id: Optional[int]):
    from faraday.server.web import get_app  # pylint:disable=import-outside-toplevel
    with get_app().app_context():
        if plugin_id is not None:
            plugins_manager = PluginsManager(
                ReportsSettings.settings.custom_plugins_folder,
                ignore_info=ReportsSettings.settings.ignore_info_severity)
            logger.info(
                f"Reports Manager: [Custom plugins folder: "
                f"[{ReportsSettings.settings.custom_plugins_folder}]"
                f"[Ignore info severity: {ReportsSettings.settings.ignore_info_severity}]"
            )
            plugin = plugins_manager.get_plugin(plugin_id)
            if plugin:
                try:
                    logger.info(
                        f"Processing report [{file_path}] with plugin ["
                        f"{plugin.id}]")
                    plugin.processReport(str(file_path))
                    vulns_data = plugin.get_data()
                    del vulns_data['command']['duration']
                except Exception as e:
                    logger.error("Processing Error: %s", e)
                    logger.exception(e)
                    return
            else:
                logger.error(f"No plugin detected for report [{file_path}]")
                return
        else:
            try:
                with file_path.open("r") as f:
                    vulns_data = json.load(f)
            except Exception as e:
                logger.error("Loading data from json file: %s [%s]", file_path,
                             e)
                logger.exception(e)
                return
        if plugin_id is None:
            logger.debug("Removing file: %s", file_path)
            os.remove(file_path)
        else:
            if faraday_server.delete_report_after_process:
                os.remove(file_path)
        set_end_date = True
        try:
            send_report_data(workspace_name, command_id, vulns_data, user_id,
                             set_end_date)
            logger.info("Report processing finished")
        except Exception as e:
            logger.exception(e)
            logger.error("Save Error: %s", e)
Пример #18
0
def test_v2_endpoints_removed_in_v3():
    exceptions = set()
    actaul_rules_v2 = list(
        filter(lambda rule: rule.rule.startswith("/v2"),
               get_app().url_map.iter_rules()))
    assert len(actaul_rules_v2) == 0, actaul_rules_v2
    rules_v2 = set(
        map(
            lambda rule: rule.rule.replace("v2", "v3").rstrip("/"),
            filter(lambda rule: rule.rule.startswith("/v2"),
                   get_app().url_map.iter_rules())))
    rules = set(
        map(
            lambda rule: rule.rule,
            filter(lambda rule: rule.rule.startswith("/v3"),
                   get_app().url_map.iter_rules())))
    exceptions_present_v2 = rules_v2.intersection(exceptions)
    assert len(exceptions_present_v2) == 0, sorted(exceptions_present_v2)
    exceptions_present = rules.intersection(exceptions)
    assert len(exceptions_present) == 0, sorted(exceptions_present)
    # We can have extra endpoints in v3 (like all the PATCHS)
    difference = rules_v2.difference(rules).difference(exceptions)
    assert len(difference) == 0, sorted(difference)
Пример #19
0
def validate_user_unique_field(ctx, param, value):
    with get_app().app_context():
        try:
            if User.query.filter_by(**{param.name: value}).count():
                raise click.ClickException("User already exists")
        except OperationalError:
            logger = logging.getLogger(__name__)
            logger.error(
                'Could not connect to PostgreSQL. Please check: '
                 'if database is running or if the configuration settings are correct.'
            )
            sys.exit(1)

    return value
Пример #20
0
def create_tables():
    with get_app().app_context():
        # Ugly hack to create tables and also setting alembic revision
        conn_string = faraday.server.config.database.connection_string
        if not conn_string:
            logger = logging.getLogger(__name__)
            logger.error((
                'No database configuration found. Please check: '
                'if the database is running or if the configuration settings are correct. '
                'For first time installations execute: faraday-manage initdb'))
            sys.exit(1)
        InitDB()._create_tables(conn_string)
        click.echo(
            click.style('Tables created successfully!', fg='green', bold=True))
Пример #21
0
def run_migrations_online():
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    with get_app().app_context():
        connectable = db.engine

        with connectable.connect() as connection:
            context.configure(connection=connection,
                              target_metadata=target_metadata,
                              compare_type=True)

            with context.begin_transaction():
                context.run_migrations()
Пример #22
0
def check_postgresql():
    with get_app().app_context():
        try:
            if not db.session.query(Workspace).count():
                logger.warning('No workspaces found')
        except sqlalchemy.exc.ArgumentError:
            logger.error(
                f'\n{Fore.RED}Please check your PostgreSQL connection string in the file ~/.faraday/config/server.ini on your home directory.{Fore.WHITE} \n'
            )
            sys.exit(1)
        except sqlalchemy.exc.OperationalError:
            logger.error(
                    '\n\n{RED}Could not connect to PostgreSQL.\n{WHITE}Please check: \n{YELLOW}  * if database is running \n  * configuration settings are correct. \n\n{WHITE}For first time installations execute{WHITE}: \n\n {GREEN} faraday-manage initdb\n\n'.format(GREEN=Fore.GREEN, YELLOW=Fore.YELLOW, WHITE=Fore.WHITE, RED=Fore.RED))
            sys.exit(1)
        except sqlalchemy.exc.ProgrammingError:
            logger.error(
                    f'\n\nn{Fore.WHITE}Missing migrations, please execute: \n\nfaraday-manage migrate')
            sys.exit(1)
Пример #23
0
def change_username(current_username, new_username):
    with get_app().app_context():
        user = User.query.filter_by(username=current_username).first()
        if not user:
            print(
                f"\nERROR: User {current_username} was not found in Faraday's Database."
            )
            sys.exit(1)
        else:
            print(
                f"\nThe user named {current_username} will be changed to {new_username}."
            )
            confirm = click.prompt("Do you want to continue? (y/n)")
            print("")

            if confirm == "y":
                user.username = new_username
                db.session.add(user)
                db.session.commit()
                print(f"Username {current_username} changed to {new_username}")
            else:
                print("Username not changed.")
Пример #24
0
def reset_db():
    with get_app().app_context():
        reset_db_all()
Пример #25
0
def initdb(choose_password, password):
    with get_app().app_context():
        InitDB().run(choose_password=choose_password, faraday_user_password=password)
Пример #26
0
def show_all_urls():
    print(get_app().url_map)
Пример #27
0
def openapi_format(format="yaml",
                   server="localhost",
                   no_servers=False,
                   return_tags=False):
    extra_specs = {
        'info': {
            'description':
            'The Faraday REST API enables you to interact with '
            '[our server](https://github.com/infobyte/faraday).\n'
            'Use this API to interact or integrate with Faraday'
            ' server. This page documents the REST API, with HTTP'
            ' response codes and example requests and responses.'
        },
        'security': {
            "ApiKeyAuth": []
        }
    }

    if not no_servers:
        extra_specs['servers'] = [{'url': f'https://{server}/_api'}]

    spec = APISpec(
        title="Faraday API",
        version="2",
        openapi_version="3.0.2",
        plugins=[FaradayAPIPlugin(),
                 FlaskPlugin(),
                 MarshmallowPlugin()],
        **extra_specs)
    api_key_scheme = {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization"
    }

    spec.components.security_scheme("API_KEY", api_key_scheme)
    response_401_unauthorized = {
        "description":
        "You are not authenticated or your API key is missing "
        "or invalid"
    }
    spec.components.response("UnauthorizedError", response_401_unauthorized)

    tags = set()

    with get_app().test_request_context():
        for endpoint in get_app().view_functions.values():
            spec.path(view=endpoint, app=get_app())

        # Set up global tags
        spec_yaml = yaml.load(spec.to_yaml(), Loader=yaml.SafeLoader)
        for path_value in spec_yaml["paths"].values():
            for data_value in path_value.values():
                if 'tags' in data_value and any(data_value['tags']):
                    for tag in data_value['tags']:
                        tags.add(tag)
        for tag in sorted(tags):
            spec.tag({'name': tag})

        if return_tags:
            return sorted(tags)

        if format.lower() == "yaml":
            print(spec.to_yaml())
        else:
            print(json.dumps(spec.to_dict(), indent=2))
Пример #28
0
def add_custom_field_main():
    with get_app().app_context():
        add_custom_field_wizard()
Пример #29
0
    def onMessage(self, payload, is_binary):
        """
            We only support JOIN and LEAVE workspace messages.
            When authentication is implemented we need to verify
            that the user can join the selected workspace.
            When authentication is implemented we need to reply
            the client if the join failed.
        """
        from faraday.server.web import get_app  # pylint:disable=import-outside-toplevel
        if not is_binary:
            message = json.loads(payload)
            if message['action'] == 'JOIN_WORKSPACE':
                if 'workspace' not in message or 'token' not in message:
                    logger.warning(
                        f'Invalid join workspace message: {message}')
                    self.sendClose()
                    return
                signer = itsdangerous.TimestampSigner(
                    get_app().config['SECRET_KEY'], salt="websocket")
                try:
                    workspace_id = signer.unsign(message['token'], max_age=60)
                except itsdangerous.BadData as e:
                    self.sendClose()
                    logger.warning('Invalid websocket token for workspace '
                                   '{}'.format(message['workspace']))
                    logger.exception(e)
                else:
                    with get_app().app_context():
                        workspace = Workspace.query.get(int(workspace_id))
                    if workspace.name != message['workspace']:
                        logger.warning(
                            'Trying to join workspace {} with token of '
                            'workspace {}. Rejecting.'.format(
                                message['workspace'], workspace.name))
                        self.sendClose()
                    else:
                        self.factory.join_workspace(self, message['workspace'])
            if message['action'] == 'LEAVE_WORKSPACE':
                self.factory.leave_workspace(self, message['workspace'])
            if message['action'] == 'JOIN_AGENT':
                if 'token' not in message or 'executors' not in message:
                    logger.warning("Invalid agent join message")
                    self.sendClose(1000, reason="Invalid JOIN_AGENT message")
                    return False
                with get_app().app_context():
                    try:
                        agent = decode_agent_websocket_token(message['token'])
                        update_executors(agent, message['executors'])
                    except ValueError:
                        logger.warning('Invalid agent token!')
                        self.sendClose(1000, reason="Invalid agent token!")
                        return False
                    # factory will now send broadcast messages to the agent
                    return self.factory.join_agent(self, agent)
            if message['action'] == 'LEAVE_AGENT':
                with get_app().app_context():
                    (agent_id, ) = (k for (k, v) in connected_agents.items()
                                    if v == self)
                    agent = Agent.query.get(agent_id)
                    assert agent is not None  # TODO the agent could be deleted here
                return self.factory.leave_agent(self, agent)
            if message['action'] == 'RUN_STATUS':
                with get_app().app_context():
                    if 'executor_name' not in message:
                        logger.warning(
                            f'Missing executor_name param in message: {message}'
                        )
                        return True

                    (agent_id, ) = (k for (k, v) in connected_agents.items()
                                    if v == self)
                    agent = Agent.query.get(agent_id)
                    assert agent is not None  # TODO the agent could be deleted here

                    execution_id = message.get('execution_id', None)
                    assert execution_id is not None
                    agent_execution = AgentExecution.query.filter(
                        AgentExecution.id == execution_id).first()
                    if agent_execution:

                        if agent_execution.workspace.name not in \
                                [
                                    workspace.name
                                    for workspace in agent.workspaces
                                ]:
                            logger.exception(
                                ValueError(
                                    f"The {agent.name} agent has permission "
                                    f"to workspace {agent.workspaces} and "
                                    "ask to write to workspace "
                                    f"{agent_execution.workspace.name}"))
                        else:
                            agent_execution.successful = message.get(
                                'successful', None)
                            agent_execution.running = message.get(
                                'running', None)
                            agent_execution.message = message.get(
                                'message', '')
                            db.session.commit()
                    else:
                        logger.exception(
                            NoResultFound(
                                f"No row was found for agent executor id {execution_id}"
                            ))
Пример #30
0
def test_options(test_client):
    for rule in get_app().url_map.iter_rules():
        if 'OPTIONS' in rule.methods:
            res = test_client.options(replace_placeholders(rule.rule))
            assert res.status_code == 200, rule.rule