def update_object_value(type_, oid, object_type, value, new_value, analyst): """ Update an object value. :param type_: The top-level object type. :type type_: str :param oid: The ObjectId of the top-level object. :type oid: str :param object_type: The type of the object to update. :type object_type: str :param value: The value of the object to update. :type value: str :param new_value: The new value to use. :type new_value: str :param analyst: The user removing this object. :type analyst: str :returns: dict with keys "success" (boolean) and "message" (str) """ obj = class_from_id(type_, oid) if not obj: return {'success': False, 'message': "Could not find item to update object."} try: obj.update_object_value(object_type, value, new_value) obj.save(username=analyst) return {'success': True, 'message': 'Object value updated successfully.'} except ValidationError, e: return {'success': False, 'message': e}
def delete_all_relationships(left_class=None, left_type=None, left_id=None, analyst=None): """ Delete all relationships for this top-level object. :param left_class: The top-level object to delete relationships for. :type left_class: :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :param left_type: The type of the top-level object. :type left_type: str :param left_id: The ObjectId of the top-level object. :type left_id: str :param analyst: The user deleting these relationships. :type analyst: str :returns: dict with keys "success" (boolean) and "message" (str) """ if not left_class: if left_type and left_id: left_class = class_from_id(left_type, left_id) if not left_class: return {'success': False, 'message': "Unable to get object."} else: return {'success': False, 'message': "Need a valid left type and id"} return left_class.delete_all_relationships()
def update_object_value(type_, oid, object_type, value, new_value, analyst): """ Update an object value. :param type_: The top-level object type. :type type_: str :param oid: The ObjectId of the top-level object. :type oid: str :param object_type: The type of the object to update. :type object_type: str :param value: The value of the object to update. :type value: str :param new_value: The new value to use. :type new_value: str :param analyst: The user removing this object. :type analyst: str :returns: dict with keys "success" (boolean) and "message" (str) """ obj = class_from_id(type_, oid) if not obj: return { 'success': False, 'message': "Could not find item to update object." } try: obj.update_object_value(object_type, value, new_value) obj.save(username=analyst) return { 'success': True, 'message': 'Object value updated successfully.' } except ValidationError, e: return {'success': False, 'message': e}
def create_indicator_from_object(rel_type, rel_id, ind_type, value, source_name, method, reference, analyst, request): """ Create an indicator out of this object. :param rel_type: The top-level object type this object is for. :type rel_type: str :param rel_id: The ObjectId of the top-level object. :param ind_type: The indicator type to use. :type ind_type: str :param value: The indicator value. :type value: str :param source_name: The source name for the indicator. :type source_name: str :param method: The source method for the indicator. :type method: str :param reference: The source reference for the indicator. :type reference: str :param analyst: The user creating this indicator. :type analyst: str :param request: The Django request. :type request: :class:`django.http.HttpRequest` :returns: dict with keys "success" (bool) and "message" (str) """ result = None me = class_from_id(rel_type, rel_id) if not me: result = {'success': False, 'message': "Could not find %s" % rel_type} elif value == None or value.strip() == "": result = { 'success': False, 'message': "Can't create indicator with an empty value field" } elif ind_type == None or ind_type.strip() == "": result = { 'success': False, 'message': "Can't create indicator with an empty type field" } elif source_name == None or source_name.strip() == "": result = { 'success': False, 'message': "Can't create indicator with an empty source field" } else: value = value.lower().strip() ind_type = ind_type.strip() source_name = source_name.strip() create_indicator_result = {} result = {'success': False, 'message': "Could not create indicator"} return result
def create_indicator_from_object(rel_type, rel_id, ind_type, value, source_name, method, reference, analyst, request): """ Create an indicator out of this object. :param rel_type: The top-level object type this object is for. :type rel_type: str :param rel_id: The ObjectId of the top-level object. :param ind_type: The indicator type to use. :type ind_type: str :param value: The indicator value. :type value: str :param source_name: The source name for the indicator. :type source_name: str :param method: The source method for the indicator. :type method: str :param reference: The source reference for the indicator. :type reference: str :param analyst: The user creating this indicator. :type analyst: str :param request: The Django request. :type request: :class:`django.http.HttpRequest` :returns: dict with keys "success" (bool) and "message" (str) """ result = None me = class_from_id(rel_type, rel_id) if not me: result = {'success': False, 'message': "Could not find %s" % rel_type} elif value == None or value.strip() == "": result = {'success': False, 'message': "Can't create indicator with an empty value field"} elif ind_type == None or ind_type.strip() == "": result = {'success': False, 'message': "Can't create indicator with an empty type field"} elif source_name == None or source_name.strip() == "": result = {'success': False, 'message': "Can't create indicator with an empty source field"} else: value = value.lower().strip() ind_type = ind_type.strip() source_name = source_name.strip() create_indicator_result = {} result = {'success': False, 'message': "Could not create indicator"} return result
def delete_object(type_, oid, object_type, value, analyst, get_objects=True): """ Delete an object. :param type_: The top-level object type. :type type_: str :param oid: The ObjectId of the top-level object. :type oid: str :param object_type: The type of the object to remove. :type object_type: str :param value: The value of the object to remove. :type value: str :param analyst: The user removing this object. :type analyst: str :param get_objects: Return the list of objects. :type get_objects: bool :returns: dict with keys: "success" (boolean), "message" (str), "objects" (list) """ obj = class_from_id(type_, oid) if not obj: return { 'success': False, 'message': "Could not find item to remove object from." } try: cur_len = len(obj.obj) obj.remove_object(object_type, value) obj.save(username=analyst) new_len = len(obj.obj) result = {} if new_len < cur_len: result['success'] = True result['message'] = "Object removed successfully!" else: result['success'] = False result['message'] = "Could not find object to remove!" if (get_objects): result['objects'] = obj.sort_objects() return result except ValidationError, e: return {'success': False, 'message': e}
def delete_object(type_, oid, object_type, value, analyst, get_objects=True): """ Delete an object. :param type_: The top-level object type. :type type_: str :param oid: The ObjectId of the top-level object. :type oid: str :param object_type: The type of the object to remove. :type object_type: str :param value: The value of the object to remove. :type value: str :param analyst: The user removing this object. :type analyst: str :param get_objects: Return the list of objects. :type get_objects: bool :returns: dict with keys: "success" (boolean), "message" (str), "objects" (list) """ obj = class_from_id(type_, oid) if not obj: return {'success': False, 'message': "Could not find item to remove object from."} try: cur_len = len(obj.obj) obj.remove_object(object_type, value) obj.save(username=analyst) new_len = len(obj.obj) result = {} if new_len < cur_len: result['success'] = True result['message'] = "Object removed successfully!" else: result['success'] = False result['message'] = "Could not find object to remove!" if (get_objects): result['objects'] = obj.sort_objects() return result except ValidationError, e: return {'success': False, 'message': e}
def get_relationships(obj=None, type_=None, id_=None, analyst=None): """ Get relationships for a top-level object. :param obj: The top-level object to get relationships for. :type obj: :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :param type_: The top-level object type to get relationships for. :type type_: str :param id_: The ObjectId of the top-level object. :type id_: str :param analyst: The user requesting the relationships. :type analyst: str :returns: dict """ if obj: return obj.sort_relationships("%s" % analyst, meta=True) elif type_ and id_: obj = class_from_id(type_, id_) if not obj: return {} return obj.sort_relationships("%s" % analyst, meta=True) else: return {}
def run_service(name, type_, id_, user, obj=None, execute='local', custom_config={}, **kwargs): """ Run a service. :param name: The name of the service to run. :type name: str :param type_: The type of the object. :type type_: str :param id_: The identifier of the object. :type id_: str :param user: The user running the service. :type user: str :param obj: The CRIPTs object, if given this overrides cripts_type and identifier. :type obj: CRIPTs object. :param analyst: The user updating the results. :type analyst: str :param execute: The execution type. :type execute: str :param custom_config: Use a custom configuration for this run. :type custom_config: dict """ result = {'success': False} if type_ not in settings.CRIPTS_TYPES: result['html'] = "Unknown CRIPTs type." return result if name not in enabled_services(): result['html'] = "Service %s is unknown or not enabled." % name return result service_class = cripts.services.manager.get_service_class(name) if not service_class: result['html'] = "Unable to get service class." return result if not obj: obj = class_from_id(type_, id_) if not obj: result['html'] = 'Could not find object.' return result service = CRIPTsService.objects(name=name).first() if not service: result['html'] = "Unable to find service in database." return result # See if the object is a supported type for the service. if not service_class.supported_for_type(type_): result['html'] = "Service not supported for type '%s'" % type_ return result # When running in threaded mode, each thread needs to have its own copy of # the object. If we do not do this then one thread may read() from the # object (to get the binary) and then the second would would read() without # knowing and get undefined behavior as the file pointer would be who knows # where. By giving each thread a local copy they can operate independently. # # When not running in thread mode this has no effect except wasted memory. local_obj = local() local_obj.obj = copy.deepcopy(obj) # Give the service a chance to check for required fields. try: service_class.valid_for(local_obj.obj) if hasattr(local_obj.obj, 'filedata'): if local_obj.obj.filedata.grid_id: # Reset back to the start so the service gets the full file. local_obj.obj.filedata.seek(0) except ServiceConfigError as e: result['html'] = str(e) return result # Get the config from the database and validate the submitted options # exist. db_config = service.config.to_dict() try: service_class.validate_runtime(custom_config, db_config) except ServiceConfigError as e: result['html'] = str(e) return result final_config = db_config # Merge the submitted config with the one from the database. # This is because not all config options may be submitted. final_config.update(custom_config) form = service_class.bind_runtime_form(user, final_config) if form: if not form.is_valid(): # TODO: return corrected form via AJAX result['html'] = str(form.errors) return result # If the form is valid, create the config using the cleaned data. final_config = db_config final_config.update(form.cleaned_data) logger.info("Running %s on %s, execute=%s" % (name, local_obj.obj.id, execute)) service_instance = service_class(notify=update_analysis_results, complete=finish_task) # Give the service a chance to modify the config that gets saved to the DB. saved_config = dict(final_config) service_class.save_runtime_config(saved_config) task = AnalysisTask(local_obj.obj, service_instance, user) task.config = AnalysisConfig(**saved_config) task.start() add_task(task) service_instance.set_task(task) if execute == 'process': p = Process(target=service_instance.execute, args=(final_config,)) p.start() elif execute == 'thread': t = Thread(target=service_instance.execute, args=(final_config,)) t.start() elif execute == 'process_pool': if __service_process_pool__ is not None and service.compatability_mode != True: __service_process_pool__.apply_async(func=service_work_handler, args=(service_instance, final_config,)) else: logger.warning("Could not run %s on %s, execute=%s, running in process mode" % (name, local_obj.obj.id, execute)) p = Process(target=service_instance.execute, args=(final_config,)) p.start() elif execute == 'thread_pool': if __service_thread_pool__ is not None and service.compatability_mode != True: __service_thread_pool__.apply_async(func=service_work_handler, args=(service_instance, final_config,)) else: logger.warning("Could not run %s on %s, execute=%s, running in thread mode" % (name, local_obj.obj.id, execute)) t = Thread(target=service_instance.execute, args=(final_config,)) t.start() elif execute == 'local': service_instance.execute(final_config) # Return after starting thread so web request can complete. result['success'] = True return result
def bulk_add_object_inline(request): """ Bulk add objects inline. :param request: The Django request. :type request: :class:`django.http.HttpRequest` :returns: :class:`django.http.HttpResponse` """ formdict = form_to_dict(AddObjectForm(request.user)) if request.method == "POST" and request.is_ajax(): response = parse_bulk_upload( request, parse_row_to_bound_object_form, add_new_handler_object_via_bulk, formdict) secondary_data_array = response.get('secondary') if secondary_data_array: latest_secondary_data = secondary_data_array[-1] class_type = class_from_id( latest_secondary_data['type'], latest_secondary_data['id']) subscription = {'type': latest_secondary_data['type'], 'id': latest_secondary_data['id'], 'value': latest_secondary_data['id']} object_listing_html = render_to_string('objects_listing_widget.html', {'objects': class_type.sort_objects(), 'subscription': subscription}, RequestContext(request)) response['html'] = object_listing_html is_relationship_made = False for secondary_data in secondary_data_array: if secondary_data.get('relationships'): is_relationship_made = True break if is_relationship_made == True: rel_html = render_to_string('relationships_listing_widget.html', {'relationship': subscription, 'relationships': class_type.sort_relationships(request.user, meta=True)}, RequestContext(request)) response['rel_msg'] = rel_html response['rel_made'] = True return HttpResponse(json.dumps(response, default=json_handler), content_type="application/json") else: is_prevent_initial_table = request.GET.get('isPreventInitialTable', False) is_use_item_source = request.GET.get('useItemSource', False) if is_use_item_source == True or is_use_item_source == "true": otype = request.GET.get('otype') oid = request.GET.get('oid') # Get the item with the type and ID from the database obj = class_from_id(otype, oid) if obj: source_field_name = get_source_field_for_class(otype) if source_field_name: # If the item has a source, then use the source value # to set as the default source if hasattr(obj, "source"): source_field = get_field_from_label("source", formdict) earliest_source = None earliest_date = None # Get the earliest source, compared by date for source in obj.source: for source_instance in source.instances: if earliest_source == None or source_instance.date < earliest_date: earliest_date = source_instance.date earliest_source = source if earliest_source: source_field['initial'] = earliest_source.name return render_to_response('bulk_add_object_inline.html', {'formdict': formdict, 'title': "Bulk Add Objects", 'is_prevent_initial_table': is_prevent_initial_table, 'table_name': 'object_inline'}, RequestContext(request))
def get_notification_details(request, newer_than): """ Generate the data to render the notification dialogs. :param request: The Django request. :type request: :class:`django.http.HttpRequest` :param newer_than: A filter that specifies that only notifications newer than this time should be returned. :type newer_than: str in ISODate format. :returns: arguments (dict) """ username = request.user.username notifications_list = [] notifications = None latest_notification_time = None lock = NotificationLockManager.get_notification_lock(username) timeout = 0 # Critical section, check if there are notifications to be consumed. lock.acquire() try: notifications = get_user_notifications(username, newer_than=newer_than) if len(notifications) > 0: latest_notification_time = str(notifications[0].created) else: # no new notifications -- block until time expiration or lock release lock.wait(60) # lock was released, check if there is any new information yet notifications = get_user_notifications(username, newer_than=newer_than) if len(notifications) > 0: latest_notification_time = str(notifications[0].created) finally: lock.release() if latest_notification_time is not None: acknowledgement_type = request.user.get_preference('toast_notifications', 'acknowledgement_type', 'sticky') if acknowledgement_type == 'timeout': timeout = request.user.get_preference('toast_notifications', 'timeout', 30) * 1000 for notification in notifications: obj = class_from_id(notification.obj_type, notification.obj_id) if obj is not None: link_url = obj.get_details_url() header = generate_notification_header(obj) else: if notification.header is not None: header = notification.header else: header = "%s %s" % (notification.obj_type, notification.obj_id) if notification.link_url is not None: link_url = notification.link_url else: link_url = None notification_type = notification.notification_type if notification_type is None or notification_type not in NotificationType.ALL: notification_type = NotificationType.ALERT notification_data = { "header": header, "message": notification.notification, "date_modified": str(notification.created.strftime("%Y/%m/%d %H:%M:%S")), "link": link_url, "modified_by": notification.analyst, "id": str(notification.id), "type": notification_type, } notifications_list.append(notification_data) return { 'notifications': notifications_list, 'newest_notification': latest_notification_time, 'server_time': str(datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")), 'timeout': timeout, }
def process_changed_fields(initial_message, changed_fields, obj): """ Processes the changed fields to determine what actually changed. :param message: An initial message to include. :type message: str :param changed_fields: A list of field names that were changed. :type changed_fields: list of str :param obj: The object. :type obj: class which inherits from :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :returns: str: Returns a message indicating what was changed. """ obj_type = obj._meta['cripts_type'] message = initial_message if message is None: message = '' source_filter = None for changed_field in changed_fields: # Fields may be fully qualified, e.g. source.1.instances.0.reference # So, split on the '.' character and get the root of the changed field base_changed_field = MappedMongoFields.get_mapped_mongo_field(obj_type, changed_field.split('.')[0]) new_value = getattr(obj, base_changed_field, '') old_obj = class_from_id(obj_type, obj.id) old_value = getattr(old_obj, base_changed_field, '') change_handler = ChangeParser.get_changed_field_handler(obj_type, base_changed_field) if change_handler is not None: change_message = change_handler(old_value, new_value, base_changed_field) if isinstance(change_message, dict): if change_message.get('source_filter') is not None: new_source_filter = change_message.get('source_filter') source_filter = combine_source_filters(source_filter, new_source_filter) change_message = change_message.get('message') if change_message is not None: message += "\n" + change_message[:1].capitalize() + change_message[1:] else: change_field_handler = ChangeParser.generic_single_field_change_handler if isinstance(old_value, BaseList): list_value = None if len(old_value) > 0: list_value = old_value[0] elif len(new_value) > 0: list_value = new_value[0] if isinstance(list_value, basestring): change_field_handler = ChangeParser.generic_list_change_handler elif isinstance(list_value, EmbeddedDocument): change_field_handler = ChangeParser.generic_list_json_change_handler change_message = change_field_handler(old_value, new_value, base_changed_field) if isinstance(change_message, dict): if change_message.get('source_filter') is not None: new_source_filter = change_message.get('source_filter') combine_source_filters(source_filter, new_source_filter) change_message = change_message.get('message') if change_message is not None: message += "\n" + change_message[:1].capitalize() + change_message[1:] return {'message': message, 'source_filter': source_filter}
def forge_relationship(type_=None, id_=None, class_=None, right_type=None, right_id=None, right_class=None, rel_type=None, rel_date=None, user=None, rel_reason="", rel_confidence='unknown', get_rels=False, **kwargs): """ Forge a relationship between two top-level objects. :param type_: The type of first top-level object to relate to. :type type_: str :param id_: The ObjectId of the first top-level object. :type id_: str :param class_: The first top-level object to relate to. :type class_: :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :param right_type: The type of second top-level object to relate to. :type right_type: str :param right_id: The ObjectId of the second top-level object. :type right_id: str :param right_class: The second top-level object to relate to. :type right_class: :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :param rel_type: The type of relationship. :type rel_type: str :param rel_date: The date this relationship applies. :type rel_date: datetime.datetime :param user: The user forging this relationship. :type user: str :param rel_reason: The reason for the relationship. :type rel_reason: str :param rel_confidence: The confidence of the relationship. :type rel_confidence: str :param get_rels: Return the relationships after forging. :type get_rels: boolean :returns: dict with keys: "success" (boolean) "message" (str if fail, EmbeddedObject if success) "relationships" (dict) """ if rel_date == 'None': rel_date = None elif isinstance(rel_date, basestring) and rel_date != '': rel_date = parse(rel_date, fuzzy=True) elif not isinstance(rel_date, datetime.datetime): rel_date = None if not class_: if type_ and id_: class_ = class_from_id(type_, id_) if not class_: return {'success': False, 'message': "Failed to get left TLO"} if not right_class: if right_type and right_id: print ("right type:" + str(right_type)) print ("right id:" + str(right_id)) right_class = class_from_id(right_type, right_id) if not right_class: return {'success': False, 'message': "Failed to get right TLO"} try: # forge relationship results = class_.add_relationship(right_class, rel_type, rel_date, user, rel_confidence, rel_reason) except Exception as e: return {'success': False, 'message': e} if results['success']: class_.update(add_to_set__relationships=results['message']) if get_rels: results['relationships'] = class_.sort_relationships("%s" % user, meta=True) return results
def get_notification_details(request, newer_than): """ Generate the data to render the notification dialogs. :param request: The Django request. :type request: :class:`django.http.HttpRequest` :param newer_than: A filter that specifies that only notifications newer than this time should be returned. :type newer_than: str in ISODate format. :returns: arguments (dict) """ username = request.user.username notifications_list = [] notifications = None latest_notification_time = None lock = NotificationLockManager.get_notification_lock(username) timeout = 0 # Critical section, check if there are notifications to be consumed. lock.acquire() try: notifications = get_user_notifications(username, newer_than=newer_than) if len(notifications) > 0: latest_notification_time = str(notifications[0].created) else: # no new notifications -- block until time expiration or lock release lock.wait(60) # lock was released, check if there is any new information yet notifications = get_user_notifications(username, newer_than=newer_than) if len(notifications) > 0: latest_notification_time = str(notifications[0].created) finally: lock.release() if latest_notification_time is not None: acknowledgement_type = request.user.get_preference( 'toast_notifications', 'acknowledgement_type', 'sticky') if acknowledgement_type == 'timeout': timeout = request.user.get_preference('toast_notifications', 'timeout', 30) * 1000 for notification in notifications: obj = class_from_id(notification.obj_type, notification.obj_id) if obj is not None: link_url = obj.get_details_url() header = generate_notification_header(obj) else: if notification.header is not None: header = notification.header else: header = "%s %s" % (notification.obj_type, notification.obj_id) if notification.link_url is not None: link_url = notification.link_url else: link_url = None notification_type = notification.notification_type if notification_type is None or notification_type not in NotificationType.ALL: notification_type = NotificationType.ALERT notification_data = { "header": header, "message": notification.notification, "date_modified": str(notification.created.strftime("%Y/%m/%d %H:%M:%S")), "link": link_url, "modified_by": notification.analyst, "id": str(notification.id), "type": notification_type, } notifications_list.append(notification_data) return { 'notifications': notifications_list, 'newest_notification': latest_notification_time, 'server_time': str(datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")), 'timeout': timeout, }
def add_object(type_, id_, object_type, source, method, reference, user, value=None, file_=None, add_indicator=False, get_objects=True, tlo=None, is_sort_relationships=False, is_validate_only=False, is_validate_locally=False, cache={}, **kwargs): """ Add an object to the database. :param type_: The top-level object type. :type type_: str :param id_: The ObjectId of the top-level object. :type id_: str :param object_type: The type of the ObjectType being added. :type object_type: str :param source: The name of the source adding this object. :type source: str :param method: The method for this object. :type method: str :param reference: The reference for this object. :type reference: str :param user: The user adding this object. :type user: str :param value: The value of the object. :type value: str :param file_: The file if the object is a file upload. :type file_: file handle. :param add_indicator: Also add an indicator for this object. :type add_indicator: bool :param get_objects: Return the formatted list of objects when completed. :type get_objects: bool :param tlo: The CRIPTs top-level object we are adding objects to. This is an optional parameter used mainly for performance reasons (by not querying mongo if we already have the top level-object). :type tlo: :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :param is_sort_relationships: Return all relationships and meta, sorted :type is_sort_relationships: bool :param is_validate_only: Validate, but do not add to TLO. :type is_validate_only: bool :param is_validate_locally: Validate, but do not add b/c there is no TLO. :type is_validate_locally: bool :param cache: Cached data, typically for performance enhancements during bulk operations. :type cache: dict :returns: dict with keys: "success" (boolean), "message" (str), "objects" (list), "relationships" (list) """ if is_validate_locally: # no TLO provided return {"success": True} if not tlo: if type_ and id_: tlo = class_from_id(type_, id_) if not tlo: return {'success': False, 'message': "Failed to find TLO"} try: if file_: data = file_.read() filename = file_.name md5sum = md5(data).hexdigest() value = md5sum reference = filename ret = tlo.add_object(object_type, value, source, method, reference, user) if not ret['success']: msg = '%s! [Type: "%s"][Value: "%s"]' return { "success": False, "message": msg % (ret['message'], object_type, value) } else: results = {'success': True} if not is_validate_only: # save the object tlo.update(add_to_set__obj=ret['object']) results['message'] = "Object added successfully" if file_: #XXX: MongoEngine provides no direct GridFS access so we # need to use pymongo directly. col = settings.COL_OBJECTS grid = mongo_connector("%s.files" % col) if grid.find({'md5': md5sum}).count() == 0: put_file(filename, data, collection=col) if is_sort_relationships == True: results['relationships'] = tlo.sort_relationships(user, meta=True) if get_objects: results['objects'] = tlo.sort_objects() results['id'] = str(tlo.id) return results except ValidationError as e: return {'success': False, 'message': str(e)}
def add_new_event(title, description, event_type, source, method, reference, date, analyst, bucket_list=None, ticket=None, related_id=None, related_type=None, relationship_type=None): """ Add a new Event to CRIPTs. :param title: Event title. :type title: str :param description: Event description. :type description: str :param event_type: Event type. :type event_type: str :param source: The source which provided this information. :type source: str :param method: THe method of acquiring this information. :type method: str :param reference: Reference to this data. :type reference: str :param date: Date of acquiring this data. :type date: datetime.datetime :param analyst: The user adding this Event. :type analyst: str :param bucket_list: The bucket(s) to associate with this Event. :type: str :param ticket: Ticket to associate with this event. :type ticket: str :param related_id: ID of object to create relationship with :type related_id: str :param related_type: Type of object to create relationship with :type related_type: str :param relationship_type: Type of relationship to create. :type relationship_type: str :returns: dict with keys "success" (boolean) and "message" (str) """ result = dict() if not source: return {'success': False, 'message': "Missing source information."} event = Event() event.title = title event.description = description event.set_event_type(event_type) s = create_embedded_source(name=source, reference=reference, method=method, analyst=analyst, date=date) event.add_source(s) if bucket_list: event.add_bucket_list(bucket_list, analyst) if ticket: event.add_ticket(ticket, analyst) related_obj = None if related_id: related_obj = class_from_id(related_type, related_id) if not related_obj: retVal['success'] = False retVal['message'] = 'Related Object not found.' return retVal try: event.save(username=analyst) if related_obj and event and relationship_type: relationship_type=RelationshipTypes.inverse(relationship=relationship_type) event.add_relationship(related_obj, relationship_type, analyst=analyst, get_rels=False) event.save(username=analyst) # run event triage event.reload() run_triage(event, analyst) message = ('<div>Success! Click here to view the new event: <a href=' '"%s">%s</a></div>' % (reverse('cripts.events.views.view_event', args=[event.id]), title)) result = {'success': True, 'message': message, 'id': str(event.id), 'object': event} except ValidationError, e: result = {'success': False, 'message': e}
def add_object(type_, id_, object_type, source, method, reference, user, value=None, file_=None, add_indicator=False, get_objects=True, tlo=None, is_sort_relationships=False, is_validate_only=False, is_validate_locally=False, cache={}, **kwargs): """ Add an object to the database. :param type_: The top-level object type. :type type_: str :param id_: The ObjectId of the top-level object. :type id_: str :param object_type: The type of the ObjectType being added. :type object_type: str :param source: The name of the source adding this object. :type source: str :param method: The method for this object. :type method: str :param reference: The reference for this object. :type reference: str :param user: The user adding this object. :type user: str :param value: The value of the object. :type value: str :param file_: The file if the object is a file upload. :type file_: file handle. :param add_indicator: Also add an indicator for this object. :type add_indicator: bool :param get_objects: Return the formatted list of objects when completed. :type get_objects: bool :param tlo: The CRIPTs top-level object we are adding objects to. This is an optional parameter used mainly for performance reasons (by not querying mongo if we already have the top level-object). :type tlo: :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :param is_sort_relationships: Return all relationships and meta, sorted :type is_sort_relationships: bool :param is_validate_only: Validate, but do not add to TLO. :type is_validate_only: bool :param is_validate_locally: Validate, but do not add b/c there is no TLO. :type is_validate_locally: bool :param cache: Cached data, typically for performance enhancements during bulk operations. :type cache: dict :returns: dict with keys: "success" (boolean), "message" (str), "objects" (list), "relationships" (list) """ if is_validate_locally: # no TLO provided return {"success": True} if not tlo: if type_ and id_: tlo = class_from_id(type_, id_) if not tlo: return {'success': False, 'message': "Failed to find TLO"} try: if file_: data = file_.read() filename = file_.name md5sum = md5(data).hexdigest() value = md5sum reference = filename ret = tlo.add_object(object_type, value, source, method, reference, user) if not ret['success']: msg = '%s! [Type: "%s"][Value: "%s"]' return {"success": False, "message": msg % (ret['message'], object_type, value)} else: results = {'success': True} if not is_validate_only: # save the object tlo.update(add_to_set__obj=ret['object']) results['message'] = "Object added successfully" if file_: #XXX: MongoEngine provides no direct GridFS access so we # need to use pymongo directly. col = settings.COL_OBJECTS grid = mongo_connector("%s.files" % col) if grid.find({'md5': md5sum}).count() == 0: put_file(filename, data, collection=col) if is_sort_relationships == True: results['relationships'] = tlo.sort_relationships(user, meta=True) if get_objects: results['objects'] = tlo.sort_objects() results['id'] = str(tlo.id) return results except ValidationError as e: return {'success': False, 'message': str(e)}
def add_new_handler_object(data, rowData, request, is_validate_only=False, is_sort_relationships=False, cache={}, obj=None): """ Add an object to the database. :param data: The data for the object. :type data: dict :param rowData: Data from the row if using mass object upload. :type rowData: dict :param request: The Django request. :type request: :class:`django.http.HttpRequest` :param is_validate_only: Only validate. :type is_validate_only: bool :param cache: Cached data, typically for performance enhancements during bulk operations. :type cache: dict :param obj: The CRIPTs top-level object we are adding objects to. This is an optional parameter used mainly for performance reasons (by not querying mongo if we already have the top level-object). :type obj: :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :returns: tuple (<result>, <retVal>) """ result = False retVal = {} username = request.user.username if data: object_type = data.get('object_type') value = data.get('value') source = data.get('source') method = data.get('method') reference = data.get('reference') otype = data.get('otype') oid = data.get('oid') add_indicator = data.get('add_indicator') elif rowData: object_type = rowData.get(form_consts.Object.OBJECT_TYPE) value = rowData.get(form_consts.Object.VALUE) source = rowData.get(form_consts.Object.SOURCE) method = rowData.get(form_consts.Object.METHOD) reference = rowData.get(form_consts.Object.REFERENCE) otype = rowData.get(form_consts.Object.PARENT_OBJECT_TYPE) oid = rowData.get(form_consts.Object.PARENT_OBJECT_ID) add_indicator = rowData.get(form_consts.Object.ADD_INDICATOR) is_validate_locally = False analyst = "%s" % username # Default the user source to the user's organization if not specified if not source: source = cache.get('object_user_source') if not source: source = get_user_organization(analyst) cache['object_user_source'] = source if (otype == "" or otype == None) or (oid == "" or oid == None): is_validate_locally = True # TODO file_ object_result = add_object( otype, oid, object_type, source, method, reference, analyst, value=value, file_=None, add_indicator=add_indicator, get_objects=False, tlo=obj, is_validate_only=is_validate_only, is_sort_relationships=is_sort_relationships, is_validate_locally=is_validate_locally, cache=cache ) if object_result['success']: result = True if 'message' in object_result: retVal['message'] = object_result['message'] if is_validate_only == False: if obj == None: obj = class_from_id(otype, oid) if obj: retVal['secondary'] = {'type': otype, 'id': oid} if object_result.get('relationships'): retVal['secondary']['relationships'] = object_result.get('relationships') else: retVal['message'] = object_result['message'] return result, retVal
def update_relationship_dates(left_class=None, right_class=None, left_type=None, left_id=None, right_type=None, right_id=None, rel_type=None, rel_date=None, new_date=None,analyst=None): """ Update the relationship date between two top-level objects. :param left_class: The first top-level object. :type left_class: :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :param right_class: The second top-level object. :type right_class: :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :param left_type: The type of first top-level object. :type left_type: str :param left_id: The ObjectId of the first top-level object. :type left_id: str :param right_type: The type of second top-level object. :type right_type: str :param right_id: The ObjectId of the second top-level object. :type right_id: str :param rel_type: The type of relationship. :type rel_type: str :param rel_date: The date this relationship applies. :type rel_date: datetime.datetime :param new_date: The new date of the relationship. :type new_date: str :param analyst: The user updating this relationship. :type analyst: str :returns: dict with keys "success" (boolean) and "message" (str) """ if rel_date is None or rel_date == 'None': rel_date = None elif isinstance(rel_date, basestring) and rel_date != '': rel_date = parse(rel_date, fuzzy=True) elif not isinstance(rel_date, datetime.datetime): rel_date = None if new_date is None or new_date == 'None': new_date = None elif isinstance(new_date, basestring) and new_date != '': new_date = parse(new_date, fuzzy=True) elif not isinstance(new_date, datetime.datetime): new_date = None if not left_class: if left_type and left_id: left_class = class_from_id(left_type, left_id) if not left_class: return {'success': False, 'message': "Unable to get object."} else: return {'success': False, 'message': "Need a valid left type and id"} # update relationship if right_class: results = left_class.edit_relationship_date(rel_item=right_class, rel_type=rel_type, rel_date=rel_date, new_date=new_date, analyst=analyst) left_class.save(username=analyst) right_class.save(username=analyst) else: if right_type and right_id: results = left_class.edit_relationship_date(type_=right_type, rel_id=right_id, rel_type=rel_type, rel_date=rel_date, new_date=new_date, analyst=analyst) left_class.save(username=analyst) else: return {'success': False, 'message': "Need a valid right type and id"} return results
def bulk_add_object_inline(request): """ Bulk add objects inline. :param request: The Django request. :type request: :class:`django.http.HttpRequest` :returns: :class:`django.http.HttpResponse` """ formdict = form_to_dict(AddObjectForm(request.user)) if request.method == "POST" and request.is_ajax(): response = parse_bulk_upload(request, parse_row_to_bound_object_form, add_new_handler_object_via_bulk, formdict) secondary_data_array = response.get('secondary') if secondary_data_array: latest_secondary_data = secondary_data_array[-1] class_type = class_from_id(latest_secondary_data['type'], latest_secondary_data['id']) subscription = { 'type': latest_secondary_data['type'], 'id': latest_secondary_data['id'], 'value': latest_secondary_data['id'] } object_listing_html = render_to_string( 'objects_listing_widget.html', { 'objects': class_type.sort_objects(), 'subscription': subscription }, RequestContext(request)) response['html'] = object_listing_html is_relationship_made = False for secondary_data in secondary_data_array: if secondary_data.get('relationships'): is_relationship_made = True break if is_relationship_made == True: rel_html = render_to_string( 'relationships_listing_widget.html', { 'relationship': subscription, 'relationships': class_type.sort_relationships(request.user, meta=True) }, RequestContext(request)) response['rel_msg'] = rel_html response['rel_made'] = True return HttpResponse(json.dumps(response, default=json_handler), content_type="application/json") else: is_prevent_initial_table = request.GET.get('isPreventInitialTable', False) is_use_item_source = request.GET.get('useItemSource', False) if is_use_item_source == True or is_use_item_source == "true": otype = request.GET.get('otype') oid = request.GET.get('oid') # Get the item with the type and ID from the database obj = class_from_id(otype, oid) if obj: source_field_name = get_source_field_for_class(otype) if source_field_name: # If the item has a source, then use the source value # to set as the default source if hasattr(obj, "source"): source_field = get_field_from_label("source", formdict) earliest_source = None earliest_date = None # Get the earliest source, compared by date for source in obj.source: for source_instance in source.instances: if earliest_source == None or source_instance.date < earliest_date: earliest_date = source_instance.date earliest_source = source if earliest_source: source_field['initial'] = earliest_source.name return render_to_response( 'bulk_add_object_inline.html', { 'formdict': formdict, 'title': "Bulk Add Objects", 'is_prevent_initial_table': is_prevent_initial_table, 'table_name': 'object_inline' }, RequestContext(request))
def add_new_handler_object(data, rowData, request, is_validate_only=False, is_sort_relationships=False, cache={}, obj=None): """ Add an object to the database. :param data: The data for the object. :type data: dict :param rowData: Data from the row if using mass object upload. :type rowData: dict :param request: The Django request. :type request: :class:`django.http.HttpRequest` :param is_validate_only: Only validate. :type is_validate_only: bool :param cache: Cached data, typically for performance enhancements during bulk operations. :type cache: dict :param obj: The CRIPTs top-level object we are adding objects to. This is an optional parameter used mainly for performance reasons (by not querying mongo if we already have the top level-object). :type obj: :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :returns: tuple (<result>, <retVal>) """ result = False retVal = {} username = request.user.username if data: object_type = data.get('object_type') value = data.get('value') source = data.get('source') method = data.get('method') reference = data.get('reference') otype = data.get('otype') oid = data.get('oid') add_indicator = data.get('add_indicator') elif rowData: object_type = rowData.get(form_consts.Object.OBJECT_TYPE) value = rowData.get(form_consts.Object.VALUE) source = rowData.get(form_consts.Object.SOURCE) method = rowData.get(form_consts.Object.METHOD) reference = rowData.get(form_consts.Object.REFERENCE) otype = rowData.get(form_consts.Object.PARENT_OBJECT_TYPE) oid = rowData.get(form_consts.Object.PARENT_OBJECT_ID) add_indicator = rowData.get(form_consts.Object.ADD_INDICATOR) is_validate_locally = False analyst = "%s" % username # Default the user source to the user's organization if not specified if not source: source = cache.get('object_user_source') if not source: source = get_user_organization(analyst) cache['object_user_source'] = source if (otype == "" or otype == None) or (oid == "" or oid == None): is_validate_locally = True # TODO file_ object_result = add_object(otype, oid, object_type, source, method, reference, analyst, value=value, file_=None, add_indicator=add_indicator, get_objects=False, tlo=obj, is_validate_only=is_validate_only, is_sort_relationships=is_sort_relationships, is_validate_locally=is_validate_locally, cache=cache) if object_result['success']: result = True if 'message' in object_result: retVal['message'] = object_result['message'] if is_validate_only == False: if obj == None: obj = class_from_id(otype, oid) if obj: retVal['secondary'] = {'type': otype, 'id': oid} if object_result.get('relationships'): retVal['secondary']['relationships'] = object_result.get( 'relationships') else: retVal['message'] = object_result['message'] return result, retVal
def process_changed_fields(initial_message, changed_fields, obj): """ Processes the changed fields to determine what actually changed. :param message: An initial message to include. :type message: str :param changed_fields: A list of field names that were changed. :type changed_fields: list of str :param obj: The object. :type obj: class which inherits from :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :returns: str: Returns a message indicating what was changed. """ obj_type = obj._meta['cripts_type'] message = initial_message if message is None: message = '' source_filter = None for changed_field in changed_fields: # Fields may be fully qualified, e.g. source.1.instances.0.reference # So, split on the '.' character and get the root of the changed field base_changed_field = MappedMongoFields.get_mapped_mongo_field( obj_type, changed_field.split('.')[0]) new_value = getattr(obj, base_changed_field, '') old_obj = class_from_id(obj_type, obj.id) old_value = getattr(old_obj, base_changed_field, '') change_handler = ChangeParser.get_changed_field_handler( obj_type, base_changed_field) if change_handler is not None: change_message = change_handler(old_value, new_value, base_changed_field) if isinstance(change_message, dict): if change_message.get('source_filter') is not None: new_source_filter = change_message.get('source_filter') source_filter = combine_source_filters( source_filter, new_source_filter) change_message = change_message.get('message') if change_message is not None: message += "\n" + change_message[:1].capitalize( ) + change_message[1:] else: change_field_handler = ChangeParser.generic_single_field_change_handler if isinstance(old_value, BaseList): list_value = None if len(old_value) > 0: list_value = old_value[0] elif len(new_value) > 0: list_value = new_value[0] if isinstance(list_value, basestring): change_field_handler = ChangeParser.generic_list_change_handler elif isinstance(list_value, EmbeddedDocument): change_field_handler = ChangeParser.generic_list_json_change_handler change_message = change_field_handler(old_value, new_value, base_changed_field) if isinstance(change_message, dict): if change_message.get('source_filter') is not None: new_source_filter = change_message.get('source_filter') combine_source_filters(source_filter, new_source_filter) change_message = change_message.get('message') if change_message is not None: message += "\n" + change_message[:1].capitalize( ) + change_message[1:] return {'message': message, 'source_filter': source_filter}
def add_new_event(title, description, event_type, source, method, reference, date, analyst, bucket_list=None, ticket=None, related_id=None, related_type=None, relationship_type=None): """ Add a new Event to CRIPTs. :param title: Event title. :type title: str :param description: Event description. :type description: str :param event_type: Event type. :type event_type: str :param source: The source which provided this information. :type source: str :param method: THe method of acquiring this information. :type method: str :param reference: Reference to this data. :type reference: str :param date: Date of acquiring this data. :type date: datetime.datetime :param analyst: The user adding this Event. :type analyst: str :param bucket_list: The bucket(s) to associate with this Event. :type: str :param ticket: Ticket to associate with this event. :type ticket: str :param related_id: ID of object to create relationship with :type related_id: str :param related_type: Type of object to create relationship with :type related_type: str :param relationship_type: Type of relationship to create. :type relationship_type: str :returns: dict with keys "success" (boolean) and "message" (str) """ result = dict() if not source: return {'success': False, 'message': "Missing source information."} event = Event() event.title = title event.description = description event.set_event_type(event_type) s = create_embedded_source(name=source, reference=reference, method=method, analyst=analyst, date=date) event.add_source(s) if bucket_list: event.add_bucket_list(bucket_list, analyst) if ticket: event.add_ticket(ticket, analyst) related_obj = None if related_id: related_obj = class_from_id(related_type, related_id) if not related_obj: retVal['success'] = False retVal['message'] = 'Related Object not found.' return retVal try: event.save(username=analyst) if related_obj and event and relationship_type: relationship_type = RelationshipTypes.inverse( relationship=relationship_type) event.add_relationship(related_obj, relationship_type, analyst=analyst, get_rels=False) event.save(username=analyst) # run event triage event.reload() run_triage(event, analyst) message = ('<div>Success! Click here to view the new event: <a href=' '"%s">%s</a></div>' % (reverse('cripts.events.views.view_event', args=[event.id ]), title)) result = { 'success': True, 'message': message, 'id': str(event.id), 'object': event } except ValidationError, e: result = {'success': False, 'message': e}
def create_notification(obj, username, message, source_filter=None, notification_type=NotificationType.ALERT): """ Generate a notification -- based on mongo obj. :param obj: The object. :type obj: class which inherits from :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :param username: The user creating the notification. :type username: str :param message: The notification message. :type message: str :param source_filter: Filter on who can see this notification. :type source_filter: list(str) :param notification_type: The notification type (e.g. alert, error). :type notification_type: str """ n = Notification() n.analyst = username obj_type = obj._meta['cripts_type'] users = set() if notification_type not in NotificationType.ALL: notification_type = NotificationType.ALERT n.notification_type = notification_type if obj_type == 'Comment': n.obj_id = obj.obj_id n.obj_type = obj.obj_type n.notification = "%s added a comment: %s" % (username, obj.comment) users.update(obj.users) # notify mentioned users # for comments, use the sources from the object that it is linked to # instead of the comments's sources obj = class_from_id(n.obj_type, n.obj_id) else: n.notification = message n.obj_id = obj.id n.obj_type = obj_type if hasattr(obj, 'source'): sources = [s.name for s in obj.source] subscribed_users = get_subscribed_users(n.obj_type, n.obj_id, sources) # Filter on users that have access to the source of the object for subscribed_user in subscribed_users: allowed_sources = user_sources(subscribed_user) for allowed_source in allowed_sources: if allowed_source in sources: if source_filter is None or allowed_source in source_filter: users.add(subscribed_user) break else: users.update(get_subscribed_users(n.obj_type, n.obj_id, [])) users.discard(username) # don't notify the user creating this notification n.users = list(users) if not len(n.users): return try: n.save() except ValidationError: pass # Signal potentially waiting threads that notification information is available for user in n.users: notification_lock = NotificationLockManager.get_notification_lock(user) notification_lock.acquire() try: notification_lock.notifyAll() finally: notification_lock.release()
def delete_relationship(left_class=None, right_class=None, left_type=None, left_id=None, right_type=None, right_id=None, rel_type=None, rel_date=None, analyst=None, get_rels=True): """ Delete a relationship between two top-level objects. :param left_class: The first top-level object. :type left_class: :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :param right_class: The second top-level object. :type right_class: :class:`cripts.core.cripts_mongoengine.CriptsBaseAttributes` :param left_type: The type of first top-level object. :type left_type: str :param left_id: The ObjectId of the first top-level object. :type left_id: str :param right_type: The type of second top-level object. :type right_type: str :param right_id: The ObjectId of the second top-level object. :type right_id: str :param rel_type: The type of relationship. :type rel_type: str :param rel_date: The date this relationship applies. :type rel_date: datetime.datetime :param analyst: The user deleting this relationship. :type analyst: str :param get_rels: Return the relationships after forging. :type get_rels: boolean :returns: dict with keys "success" (boolean) and "message" (str if failed, dict if successful) """ if rel_date is None or rel_date == 'None': rel_date = None elif isinstance(rel_date, basestring) and rel_date != '': rel_date = parse(rel_date, fuzzy=True) elif not isinstance(rel_date, datetime.datetime): rel_date = None if not left_class: if left_type and left_id: left_class = class_from_id(left_type, left_id) if not left_class: return {'success': False, 'message': "Unable to get object."} else: return {'success': False, 'message': "Need a valid left type and id"} # delete relationship if right_class: results = left_class.delete_relationship(rel_item=right_class, rel_type=rel_type, rel_date=rel_date, analyst=analyst) else: if right_type and right_id: results = left_class.delete_relationship(type_=right_type, rel_id=right_id, rel_type=rel_type, rel_date=rel_date, analyst=analyst) else: return {'success': False, 'message': "Need a valid right type and id"} if results['success']: left_class.save(username=analyst) if get_rels: results['relationships'] = left_class.sort_relationships("%s" % analyst, meta=True) return results