def add_signature(**kwargs): """ Add a signature to the system and assigns it a new ID WARNING: If two person call this method at exactly the same time, they might get the same ID. Variables: None Arguments: None Data Block (REQUIRED): # Signature block {"name": "sig_name", # Signature name "tags": ["PECheck"], # Signature tags "comments": [""], # Signature comments lines "meta": { # Meta fields ( **kwargs ) "id": "SID", # Mandatory ID field "rule_version": 1 }, # Mandatory Revision field "type": "rule", # Rule type (rule, private rule ...) "strings": ['$ = "a"'], # Rule string section (LIST) "condition": ["1 of them"]} # Rule condition section (LIST) Result example: {"success": true, #If saving the rule was a success or not "sid": "0000000000", #SID that the rule was assigned "rev": 2 } #Revision number at which the rule was saved. """ user = kwargs['user'] new_id = STORAGE.get_last_signature_id(ORGANISATION) + 1 new_rev = 1 data = request.json if not Classification.is_accessible(user['classification'], data['meta'].get('classification', Classification.UNRESTRICTED)): return make_api_response("", "You are not allowed to add a signature with " "higher classification than yours", 403) if not user['is_admin'] and "global" in data['type']: return make_api_response("", "Only admins are allowed to add global signatures.", 403) sid = "%s_%06d" % (data['meta']['organisation'], new_id) data['meta']['id'] = sid data['meta']['rule_version'] = new_rev data['meta']['creation_date'] = datetime.date.today().isoformat() data['meta']['last_saved_by'] = user['uname'] key = "%sr.%s" % (data['meta']['id'], data['meta']['rule_version']) yara_version = data['meta'].get('yara_version', None) data['depends'], data['modules'] = \ YARA_PARSER.parse_dependencies(data['condition'], YARA_PARSER.YARA_MODULES.get(yara_version, None)) res = YARA_PARSER.validate_rule(data) if res['valid']: query = "name:{name} AND NOT _yz_rk:{sid}*" other = STORAGE.direct_search( 'signature', query.format(name=data['name'], sid=sid), args=[('fl', '_yz_rk'), ('rows', '0')], ) if other.get('response', {}).get('numFound', 0) > 0: return make_api_response( {"success": False}, "A signature with that name already exists", 403 ) data['warning'] = res.get('warning', None) STORAGE.save_signature(key, data) return make_api_response({"success": True, "sid": data['meta']['id'], "rev": data['meta']['rule_version']}) else: return make_api_response({"success": False}, res, 403)
def set_signature(sid, rev, **kwargs): """ [INCOMPLETE] - CHECK IF SIGNATURE NAME ALREADY EXISTS Update a signature defined by a sid and a rev. NOTE: The API will compare they old signature with the new one and will make the decision to increment the revision number or not. Variables: sid => Signature ID rev => Signature revision number Arguments: None Data Block (REQUIRED): # Signature block {"name": "sig_name", # Signature name "tags": ["PECheck"], # Signature tags "comments": [""], # Signature comments lines "meta": { # Meta fields ( **kwargs ) "id": "SID", # Mandatory ID field "rule_version": 1 }, # Mandatory Revision field "type": "rule", # Rule type (rule, private rule ...) "strings": ['$ = "a"'], # Rule string section (LIST) "condition": ["1 of them"]} # Rule condition section (LIST) Result example: {"success": true, #If saving the rule was a success or not "sid": "0000000000", #SID that the rule was assigned (Same as provided) "rev": 2 } #Revision number at which the rule was saved. """ user = kwargs['user'] key = "%sr.%s" % (sid, rev) old_data = STORAGE.get_signature(key) if old_data: data = request.json if not Classification.is_accessible(user['classification'], data['meta'].get('classification', Classification.UNRESTRICTED)): return make_api_response("", "You are not allowed to change a signature to an " "higher classification than yours", 403) if old_data['meta']['al_status'] != data['meta']['al_status']: return make_api_response({"success": False}, "You cannot change the signature " "status through this API.", 403) if not Classification.is_accessible(user['classification'], old_data['meta'].get('classification', Classification.UNRESTRICTED)): return make_api_response("", "You are not allowed to change a signature with " "higher classification than yours", 403) if not user['is_admin'] and "global" in data['type']: return make_api_response("", "Only admins are allowed to add global signatures.", 403) if YARA_PARSER.require_bump(data, old_data): data['meta']['rule_version'] = STORAGE.get_last_rev_for_id(sid) + 1 data['meta']['creation_date'] = datetime.date.today().isoformat() if 'modification_date' in data['meta']: del(data['meta']['modification_date']) if 'al_state_change_date' in data['meta']: del(data['meta']['al_state_change_date']) if 'al_state_change_user' in data['meta']: del(data['meta']['al_state_change_user']) data['meta']['al_status'] = "TESTING" key = "%sr.%s" % (sid, data['meta']['rule_version']) else: data['meta']['modification_date'] = datetime.date.today().isoformat() if data['meta']['modification_date'] == data['meta'].get('creation_date', None): del(data['meta']['modification_date']) data['meta']['last_saved_by'] = user['uname'] yara_version = data['meta'].get('yara_version', None) data['depends'], data['modules'] = \ YARA_PARSER.parse_dependencies(data['condition'], YARA_PARSER.YARA_MODULES.get(yara_version, None)) res = YARA_PARSER.validate_rule(data) if res['valid']: data['warning'] = res.get('warning', None) STORAGE.save_signature(key, data) return make_api_response({"success": True, "sid": data['meta']['id'], "rev": data['meta']['rule_version']}) else: return make_api_response({"success": False}, res, 403) else: return make_api_response({"success": False}, "Signature not found. %s" % key, 404)
def change_status(sid, rev, status, **kwargs): """ [INCOMPLETE] - DISABLE OTHER REVISION OF THE SAME SIGNTURE WHEN DEPLOYING ONE Change the status of a signature Variables: sid => ID of the signature rev => Revision number of the signature status => New state Arguments: None Data Block: None Result example: { "success" : true } #If saving the rule was a success or not """ DEPLOYED_STATUSES = ['DEPLOYED', 'NOISY', 'DISABLED'] DRAFT_STATUSES = ['STAGING', 'TESTING'] STALE_STATUSES = ['INVALID'] user = kwargs['user'] if status == 'INVALID': return make_api_response("", "INVALID signature status is reserved for service use only.", 403) if not user['is_admin'] and status in DEPLOYED_STATUSES: return make_api_response("", "Only admins are allowed to change the signature status to a deployed status.", 403) key = "%sr.%s" % (sid, rev) data = STORAGE.get_signature(key) if data: if not Classification.is_accessible(user['classification'], data['meta'].get('classification', Classification.UNRESTRICTED)): return make_api_response("", "You are not allowed change status on this signature", 403) if data['meta']['al_status'] in STALE_STATUSES and status not in DRAFT_STATUSES: return make_api_response("", "Only action available while signature in {} status is to change " "signature to a DRAFT status" .format(data['meta']['al_status']), 403) if data['meta']['al_status'] in DEPLOYED_STATUSES and status in DRAFT_STATUSES: return make_api_response("", "You cannot change the status of signature %s r.%s from %s to %s." % (sid, rev, data['meta']['al_status'], status), 403) query = "meta.al_status:{status} AND _yz_rk:{sid}* AND NOT _yz_rk:{key}" today = datetime.date.today().isoformat() uname = user['uname'] if status not in ['DISABLED', 'INVALID', 'TESTING']: for other in STORAGE.get_signatures( STORAGE.list_filtered_signature_keys( query.format(key=key, sid=sid, status=status) ) ): other['meta']['al_state_change_date'] = today other['meta']['al_state_change_user'] = uname other['meta']['al_status'] = 'DISABLED' other_sid = other['meta']['id'] other_rev = other['meta']['rule_version'] other_key = "%sr.%s" % (other_sid, other_rev) STORAGE.save_signature(other_key, other) data['meta']['al_state_change_date'] = today data['meta']['al_state_change_user'] = uname data['meta']['al_status'] = status STORAGE.save_signature(key, data) return make_api_response({"success": True}) else: return make_api_response("", "Signature not found. (%s r.%s)" % (sid, rev), 404)