Example #1
0
def set_ui4(**kwargs):
    """
    Set the UI 4 redirect ON or OFF

    Variables:
    None

    Arguments:
    None

    Data Block:
    True/False

    Result example:
    {
     "success": True
    }

    """
    ui4 = request.json

    if not isinstance(ui4, bool):
        return make_api_response("", err="Invalid data block", status_code=400)

    user_settings = STORAGE.user_settings.get(kwargs['user']['uname'])
    if not user_settings:
        return make_api_response("", err="Cannot find user", status_code=404)

    user_settings.ui4 = ui4
    user_settings.ui4_ask = False
    return make_api_response({"success": STORAGE.user_settings.save(kwargs['user']['uname'], user_settings)})
Example #2
0
def remove_workflow(workflow_id, **_):
    """
    Remove the specified workflow.

    Variables:
    workflow_id       => ID of the workflow to remove

    Arguments:
    None

    Data Block:
    None

    Result example:
    {
     "success": true  # Was the remove successful?
    }
    """
    wf = STORAGE.workflow.get(workflow_id)
    if wf:
        return make_api_response({"success": STORAGE.workflow.delete(workflow_id)})
    else:
        return make_api_response({"success": False},
                                 err="Workflow ID %s does not exist" % workflow_id,
                                 status_code=404)
Example #3
0
def delete_signature(sid, **kwargs):
    """
    Delete a signature based of its ID

    Variables:
    sid    =>     Signature ID

    Arguments:
    None

    Data Block:
    None

    Result example:
    {"success": True}  # Signature delete successful
    """
    user = kwargs['user']
    data = STORAGE.signature.get(sid, as_obj=False)
    if data:
        if not Classification.is_accessible(
                user['classification'],
                data.get('classification', Classification.UNRESTRICTED)):
            return make_api_response(
                "", "Your are not allowed to delete this signature.", 403)

        ret_val = STORAGE.signature.delete(sid)

        _reset_service_updates(data['type'])
        return make_api_response({"success": ret_val})
    else:
        return make_api_response("", f"Signature not found. ({sid})", 404)
Example #4
0
def get_tag_safelist(**_):
    """
    Get the current tag_safelist

    Variables:
    None

    Arguments:
    default    =>  Load the default values that came with the system

    Data Block:
    None

    Result example:
    <current tag_safelist.yml file>
    """
    default = request.args.get('default', 'false').lower() in ['true', '']

    with forge.get_cachestore('system', config=config,
                              datastore=STORAGE) as cache:
        tag_safelist_yml = cache.get('tag_safelist_yml')
        if not tag_safelist_yml or default:
            yml_data = forge.get_tag_safelist_data()
            if yml_data:
                return make_api_response(yaml.safe_dump(yml_data))

            return make_api_response(
                None, "Could not find the tag_safelist.yml file", 404)

        return make_api_response(safe_str(tag_safelist_yml))
Example #5
0
def setup_watch_queue(sid, **kwargs):
    """
    Starts a watch queue to get live results

    Variables:
    sid      => Submission ID

    Arguments:
    None

    Data Block:
    None

    Result example:
    {"wq_id": "D-c7668cfa-...-c4132285142e-WQ"} #ID of the watch queue
    """
    data = STORAGE.submission.get(sid, as_obj=False)
    user = kwargs['user']

    if user and data and Classification.is_accessible(user['classification'], data['classification']):
        wq_id = DispatchClient(datastore=STORAGE).setup_watch_queue(sid)
        if wq_id:
            return make_api_response({"wq_id": wq_id})
        return make_api_response("", "No dispatchers are processing this submission.", 404)
    else:
        return make_api_response("", "You are not allowed to access this submissions.", 403)
Example #6
0
def set_user_avatar(username, **kwargs):
    """
    Sets the user's Avatar

    Variables:
    username    => Name of the user you want to set the avatar for

    Arguments:
    None

    Data Block:
    "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD..."

    Result example:
    {
     "success": true    # Was saving the avatar successful ?
    }
    """
    if username != kwargs['user']['uname']:
        return make_api_response({"success": False}, "Cannot save the avatar of another user.", 403)

    data = request.data
    if data:
        data = data.decode('utf-8')
        if not isinstance(data, str) or not STORAGE.user_avatar.save(username, data):
            make_api_response({"success": False}, "Data block should be a base64 encoded image "
                                                  "that starts with 'data:image/<format>;base64,'", 400)
    else:
        STORAGE.user_avatar.delete(username)
    return make_api_response({"success": True})
Example #7
0
def get_identify_custom_yara_file(**_):
    """
    Get identify's current custom Yara file

    Variables:
    None

    Arguments:
    default    =>  Load the default values that came with the system

    Data Block:
    None

    Result example:
    <current custom.yara file>
    """
    default = request.args.get('default', 'false').lower() in ['true', '']

    with forge.get_cachestore('system', config=config,
                              datastore=STORAGE) as cache:
        custom_yara = cache.get('custom_yara')
        if not custom_yara or default:
            with open(constants.YARA_RULE_PATH) as mfh:
                return make_api_response(mfh.read())

        return make_api_response(custom_yara.decode('utf-8'))
Example #8
0
def outstanding_services(sid, **kwargs):
    """
    List outstanding services and the number of file each
    of them still have to process.
    
    Variables:
    sid      => Submission ID
    
    Arguments:
    None
    
    Data Block:
    None
    
    Result example:
    {"MY SERVICE": 1, ... } # Dictionnary of services and number of files
    """
    data = STORAGE.submission.get(sid, as_obj=False)
    user = kwargs['user']

    if user and data and Classification.is_accessible(user['classification'],
                                                      data['classification']):
        return make_api_response(
            DispatchClient(datastore=STORAGE).outstanding_services(sid))
    else:
        return make_api_response(
            {}, "You are not allowed to access this submissions.", 403)
Example #9
0
def list_index_fields(index, **kwargs):
    """
    List all available fields for a given index

    Variables:
    index  =>     Which specific index you want to know the fields for


    Arguments:
    None

    Data Block:
    None

    Result example:
    {
        "<<FIELD_NAME>>": {      # For a given field
            indexed: True,        # Is the field indexed
            stored: False,        # Is the field stored
            type: string          # What type of data in the field
            },
        ...

    }
    """
    user = kwargs['user']
    collection = get_collection(index, user)
    if collection is not None:
        return make_api_response(collection.fields())
    elif index == "ALL":
        return make_api_response(list_all_fields(user))
    else:
        return make_api_response("",
                                 f"Not a valid index to search in: {index}",
                                 400)
Example #10
0
def get_service_error(cache_key, **_):
    """
    Get the content off a given service error cache key.

    Variables:
    cache_key     => Service result cache key
                     as SHA256.ServiceName.ServiceVersion.Configuration.e

    Arguments:
    None

    Data Block:
    None

    Result example:
    {"created": "1900-01-01T00:00:00Z",   # Time at which the error was created
     "response": {                        # Service Response
         "message": "Err message",           # Error Message
         "service_debug_info": "",           # Infromation about where the job was processed
         "service_name": "NSRL",             # Service Name
         "service_version": "",              # Service Version
         "status": "FAIL"}                   # Status
     "sha256": "123456...123456"}         # SHA256 of the files in error
    """
    data = STORAGE.error.get(cache_key, as_obj=False)
    if data is None:
        return make_api_response("",
                                 "Cache key %s does not exists." % cache_key,
                                 404)

    return make_api_response(data)
Example #11
0
def list_errors(**_):
    """
    List all error in the system (per page)
    
    Variables:
    None
    
    Arguments: 
    offset       => Offset at which we start giving errors
    query        => Query to apply to the error list
    rows         => Numbers of errors to return
    
    Data Block:
    None
    
    Result example:
    {"total": 201,                # Total errors found
     "offset": 0,                 # Offset in the error list
     "count": 100,                # Number of errors returned
     "items": []                  # List of error blocks
    }
    """
    offset = int(request.args.get('offset', 0))
    rows = int(request.args.get('rows', 100))
    query = request.args.get('query', "id:*") or "id:*"

    try:
        return make_api_response(STORAGE.error.search(query, offset=offset, rows=rows, as_obj=False,
                                                      sort="created desc"))
    except SearchException as e:
        return make_api_response("", f"The specified search query is not valid. ({e})", 400)
Example #12
0
def get_file_ascii(sha256, **kwargs):
    """
    Return the ascii values for a file where ascii chars are replaced by DOTs.

    Variables:
    sha256       => A resource locator for the file (sha256)

    Arguments:
    None

    Data Block:
    None

    Result example:
    <THE ASCII FILE>
    """

    user = kwargs['user']
    file_obj = STORAGE.file.get(sha256, as_obj=False)

    if not file_obj:
        return make_api_response({}, "The file was not found in the system.", 404)

    if user and Classification.is_accessible(user['classification'], file_obj['classification']):
        with forge.get_filestore() as f_transport:
            data = f_transport.get(sha256)

        if not data:
            return make_api_response({}, "This file was not found in the system.", 404)

        return make_api_response(data.translate(FILTER_ASCII).decode())
    else:
        return make_api_response({}, "You are not allowed to view this file.", 403)
Example #13
0
def get_file_hex(sha256, **kwargs):
    """
    Returns the file hex representation
    
    Variables: 
    sha256       => A resource locator for the file (sha256)
    
    Arguments: 
    None
    
    Data Block:
    None

    API call example:
    /api/v4/file/hex/123456...654321/

    Result example:
    <THE FILE HEX REPRESENTATION>
    """
    user = kwargs['user']
    file_obj = STORAGE.file.get(sha256, as_obj=False)

    if not file_obj:
        return make_api_response({}, "The file was not found in the system.", 404)
    
    if user and Classification.is_accessible(user['classification'], file_obj['classification']):
        with forge.get_filestore() as f_transport:
            data = f_transport.get(sha256)

        if not data:
            return make_api_response({}, "This file was not found in the system.", 404)

        return make_api_response(hexdump(data))
    else:
        return make_api_response({}, "You are not allowed to view this file.", 403)
def delete_submission(sid, **kwargs):
    """
    Delete a submission as well as all related
    files, results and errors

    Variables:
    sid         => Submission ID to be deleted

    Arguments:
    None

    Data Block:
    None

    Result example:
    {success: true}
    """
    user = kwargs['user']
    submission = STORAGE.submission.get(sid, as_obj=False)

    if not submission:
        return make_api_response("", f"There are not submission with sid: {sid}", 404)

    if Classification.is_accessible(user['classification'], submission['classification']) \
            and (submission['params']['submitter'] == user['uname'] or 'admin' in user['type']):
        STORAGE.delete_submission_tree_bulk(sid, Classification, transport=FILESTORE)
        STORAGE.submission.commit()
        return make_api_response({"success": True})
    else:
        return make_api_response("", "Your are not allowed to delete this submission.", 403)
Example #15
0
def get_alert(alert_id, **kwargs):
    """
    Get the alert details for a given alert key
    
    Variables:
    alert_id         => ID of the alert to get the details for
    
    Arguments: 
    None
    
    Data Block:
    None

    API call example:
    /api/v4/alert/1234567890/

    Result example:
    {
        KEY: VALUE,   # All fields of an alert in key/value pair
    }
    """
    user = kwargs['user']
    data = STORAGE.alert.get(alert_id, as_obj=False)

    if not data:
        return make_api_response("", "This alert does not exists...", 404)

    if user and Classification.is_accessible(user['classification'],
                                             data['classification']):
        return make_api_response(data)
    else:
        return make_api_response("",
                                 "You are not allowed to see this alert...",
                                 403)
Example #16
0
def delete_obo_token(token_id, **kwargs):
    """
    Delete an application access to your profile

    Variables:
    None

    Arguments:
    token_id     =>   ID of the application token to delete

    Data Block:
    None

    Result example:
    {'success': true}
    """

    uname = kwargs['user']['uname']
    user_data = STORAGE.user.get(uname, as_obj=False)
    if token_id not in user_data.get('apps', {}):
        return make_api_response({"success": False}, "Token ID does not exist",
                                 404)

    user_data['apps'].pop(token_id)
    STORAGE.user.save(uname, user_data)
    return make_api_response({"success": True})
Example #17
0
def remove_user_account(username, **_):
    """
    Remove the account specified by the username.

    Variables:
    username       => Name of the user to get the account info

    Arguments:
    None

    Data Block:
    None

    Result example:
    {
     "success": true  # Was the remove successful?
    }
    """

    user_data = STORAGE.user.get(username)
    if user_data:
        user_deleted = STORAGE.user.delete(username)
        avatar_deleted = STORAGE.user_avatar.delete(username)
        favorites_deleted = STORAGE.user_favorites.delete(username)
        settings_deleted = STORAGE.user_settings.delete(username)

        if not user_deleted or not avatar_deleted or not favorites_deleted or not settings_deleted:
            return make_api_response({"success": False})

        return make_api_response({"success": True})
    else:
        return make_api_response({"success": False},
                                 err=f"User {username} does not exist",
                                 status_code=404)
Example #18
0
def logout(**_):
    """
    Logout from the system clearing the current session

    Variables:
    None

    Arguments:
    None

    Data Block:
    None

    Result example:
    {
     "success": true
    }
    """
    try:
        session_id = flsk_session.get('session_id', None)
        if session_id:
            KV_SESSION.pop(session_id)
        flsk_session.clear()
        res = make_api_response({"success": True})
        res.set_cookie('XSRF-TOKEN', '', max_age=0)
        return res
    except ValueError:
        return make_api_response("", err="No user logged in?", status_code=400)
Example #19
0
def get_identify_magic_patterns(**_):
    """
    Get identify's magic patterns

    Variables:
    None

    Arguments:
    default    =>  Load the default values that came with the system

    Data Block:
    None

    Result example:
    <current identify's magic patterns>
    """
    default = request.args.get('default', 'false').lower() in ['true', '']

    with forge.get_cachestore('system', config=config,
                              datastore=STORAGE) as cache:
        custom_patterns = cache.get('custom_patterns')
        if not custom_patterns or default:
            return make_api_response(yaml.safe_dump(magic_patterns))

        return make_api_response(custom_patterns.decode('utf-8'))
Example #20
0
def signup_validate(**_):
    """
    Validate a user's signup request

    Variables:
    None

    Arguments:
    None

    Data Block:
    {
     "registration_key": "234234...ADFCB"    # Key used to validate the user's signup process
    }

    Result example:
    {
     "success": true
    }
    """
    if not config.auth.internal.signup.enabled:
        return make_api_response({"success": False},
                                 "Signup process has been disabled", 403)

    try:
        data = request.json
    except BadRequest:
        data = request.values

    registration_key = data.get('registration_key', None)

    if registration_key:
        try:
            signup_queue = get_signup_queue(registration_key)
            members = signup_queue.members()
            signup_queue.delete()
            if members:
                user_info = members[0]

                # Add dynamic classification group
                user_info['classification'] = get_dynamic_classification(
                    user_info.get('classification',
                                  Classification.UNRESTRICTED),
                    user_info['email'])

                user = User(user_info)
                username = user.uname

                STORAGE.user.save(username, user)
                return make_api_response({"success": True})
        except (KeyError, ValueError) as e:
            LOGGER.warning(f"Fail to signup user: {str(e)}")
            pass
    else:
        return make_api_response(
            {"success": False},
            "Not enough information to proceed with user creation", 400)

    return make_api_response({"success": False}, "Invalid registration key",
                             400)
Example #21
0
def set_system_message(**kwargs):
    """
    Set the current system message

    Variables:
    None

    Arguments:
    None

    Data Block:
    {
      "title": "Message title",
      "severity": "info",
      "message": "This is a test message"
    }

    Result example:
    {"success": true}
    """
    msg = request.json
    if isinstance(msg, dict) and 'severity' in msg and 'message' in msg:
        msg['user'] = kwargs['user']['uname']
        msg = {
            k: v
            for k, v in msg.items()
            if k in ['severity', 'message', 'title', 'user']
        }
        UI_MESSAGING.set('system_message', msg)
        return make_api_response({"success": True})

    return make_api_response(None, "Invalid system message submitted.", 400)
def get_potential_versions(servicename, **_):
    """
    List the different versions of a service stored in the system

    Variables:
    servicename       => Name of the service to get the versions

    Arguments:
    None

    Data Block:
    None

    Result example:
    ['3.1.0', '3.2.0', '3.3.0', '4.0.0', ...]     # List of service versions
    """
    service = STORAGE.service_delta.get(servicename)
    if service:
        return make_api_response(
            sorted([
                item.version
                for item in STORAGE.service.stream_search(f"id:{servicename}*",
                                                          fl="version")
            ],
                   key=lambda x: parse(x),
                   reverse=True))
    else:
        return make_api_response("",
                                 err=f"{servicename} service does not exist",
                                 status_code=404)
Example #23
0
def list_bucket_fields(bucket, **kwargs):
    """
    List all available fields for a given bucket

    Variables:
    bucket  =>     Which specific bucket you want to know the fields for


    Arguments:
    None

    Data Block:
    None

    Result example:
    {
        "<<FIELD_NAME>>": {      # For a given field
            indexed: True,        # Is the field indexed
            stored: False,        # Is the field stored
            type: string          # What type of data in the field
            },
        ...

    }
    """
    if bucket in BUCKET_MAP or ():
        return make_api_response(BUCKET_MAP[bucket].fields())
    elif 'admin' in kwargs['user']['type'] and hasattr(STORAGE, bucket):
        return make_api_response(getattr(STORAGE, bucket).fields())
    elif bucket == "ALL":
        return make_api_response(list_all_fields())
    else:
        return make_api_response("",
                                 f"Not a valid bucket to search in: {bucket}",
                                 400)
def get_message(message_type, **_):
    """
    Read a message from the queue

    Variables:
    message_type         =>    Type of message (file | submission | alert)

    Arguments:
    None

    Data Block:
    None

    Result example:
    {
        ...  # Message from the queue
    }
    """
    if message_type not in ['file', 'submission', 'alert']:
        return make_api_response(
            "",
            f"{message_type.upper()} is not a valid message type for this API.",
            400)

    return make_api_response(QUEUE_MAP[message_type].pop(blocking=True,
                                                         timeout=30))
Example #25
0
def get_workflow(workflow_id, **kwargs):
    """
    Load the user account information.

    Variables:
    workflow_id       => ID of the workflow

    Arguments:
    None

    Data Block:
    None

    Result example:
    {
     "name": "Workflow name",    # Name of the workflow
     "classification": "",       # Max classification for workflow
     "label": ['label1'],        # Labels for the workflow
     "priority": "LOW",          # Priority of the workflow
     "status": "MALICIOUS",      # Status of the workflow
     "query": "*:*"              # Query to match the data
    }
    """
    wf = STORAGE.workflow.get(workflow_id, as_obj=False)
    if wf:
        if CLASSIFICATION.is_accessible(kwargs['user']['classification'], wf['classification']):
            return make_api_response(wf)
        else:
            return make_api_response({},
                                     err="You're not allowed to view workflow ID: %s" % workflow_id,
                                     status_code=403)
    else:
        return make_api_response({},
                                 err="Workflow ID %s does not exist" % workflow_id,
                                 status_code=404)
def request_replay(index, doc_id, **kwargs):
    """
    Request an alert or a submission to be transfered to another system

    Variables:
    index         =>    Type of document to be transfered (alert or submission)
    doc_id        =>    ID of the document to transfer

    Arguments:
    None

    Data Block:
    None

    Result example:
    {"success": true}
    """
    user = kwargs['user']

    if index not in ['alert', 'submission']:
        return make_api_response(
            "", f"{index.upper()} is not a valid index for this API.", 400)

    index_ds = STORAGE.get_collection(index)
    doc = index_ds.get_if_exists(doc_id, as_obj=False)
    if not doc or not Classification.is_accessible(user['classification'],
                                                   doc['classification']):
        return make_api_response(
            "",
            f"You are not allowed to modify the {index} with the following ID: {doc_id}",
            403)

    operations = [(index_ds.UPDATE_SET, 'metadata.replay', REPLAY_REQUESTED)]
    return make_api_response({'success': index_ds.update(doc_id, operations)})
def get_error(error_key, **kwargs):
    """
    Get the error details for a given error key

    Variables:
    error_key         => Error key to get the details for

    Arguments:
    None

    Data Block:
    None

    Result example:
    {
        KEY: VALUE,   # All fields of an error in key/value pair
    }
    """
    user = kwargs['user']
    data = STORAGE.error.get(error_key, as_obj=False)

    if user and data:
        return make_api_response(data)
    else:
        return make_api_response("",
                                 "You are not allowed to see this error...",
                                 403)
Example #28
0
def get_stats_for_fields(fields, query, tc_start, tc, access_control):
    if not tc_start and "no_delay" not in request.args and config.core.alerter.delay != 0:
        tc_start = now_as_iso(config.core.alerter.delay * -1)
    if tc and config.ui.read_only:
        tc += config.ui.read_only_offset
    timming_filter = get_timming_filter(tc_start, tc)

    filters = [x for x in request.args.getlist("fq") if x != ""]
    if timming_filter:
        filters.append(timming_filter)

    try:
        if isinstance(fields, list):
            with concurrent.futures.ThreadPoolExecutor(
                    len(fields)) as executor:
                res = {
                    field: executor.submit(STORAGE.alert.facet,
                                           field,
                                           query=query,
                                           filters=filters,
                                           limit=100,
                                           access_control=access_control)
                    for field in fields
                }

            return make_api_response({k: v.result() for k, v in res.items()})
        else:
            return make_api_response(
                STORAGE.alert.facet(fields,
                                    query=query,
                                    filters=filters,
                                    limit=100,
                                    access_control=access_control))
    except SearchException as e:
        return make_api_response("", f"SearchException: {e}", 400)
Example #29
0
def get_heuristic(heuristic_id, **kwargs):
    """
    Get a specific heuristic's detail from the system
    
    Variables:
    heuristic_id  => ID of the heuristic
    
    Arguments: 
    None
    
    Data Block:
    None
    
    Result example:
    {"id": "AL_HEUR_001",               # Heuristics ID
     "filetype": ".*",                  # Target file type
     "name": "HEURISTICS_NAME",         # Heuristics name
     "description": ""}                 # Heuristics description
    """
    user = kwargs['user']

    h = STORAGE.heuristic.get(heuristic_id, as_obj=False)

    if not h:
        return make_api_response("", "Heuristic not found", 404)

    if user and Classification.is_accessible(user['classification'], h['classification']):
        # Always refresh stats when someone get a heuristic
        h.update({'stats': STORAGE.get_stat_for_heuristic(heuristic_id)})

        return make_api_response(h)
    else:
        return make_api_response("", "You are not allowed to see this heuristic...", 403)
Example #30
0
def update_service(**_):
    """
        Update a given service

        Variables:
        None

        Arguments:
        None

        Data Block:
        {
          "name": "ResultSample"
          "image": "cccs/assemblyline-service-resultsample:4.0.0dev0"
        }

        Result example:
        {
          success: true
        }
    """
    data = request.json
    service_key = f"{data['name']}_{data['update_data']['latest_tag'].replace('stable', '')}"

    # Check is the version we are trying to update to already exists
    if STORAGE.service.get_if_exists(service_key):
        operations = [(STORAGE.service_delta.UPDATE_SET, 'version',
                       data['update_data']['latest_tag'].replace('stable',
                                                                 ''))]
        if STORAGE.service_delta.update(data['name'], operations):
            return make_api_response({'success': True, 'status': "updated"})

    service_update.set(data['name'], data['update_data'])
    return make_api_response({'success': True, 'status': "updating"})