def download_signatures(**kwargs): """ Download signatures from the system. Variables: None Arguments: query => SOLR query to filter the signatures Default: All deployed signatures safe => Get a ruleset that will work in yara Default: False Data Block: None Result example: <A .YAR SIGNATURE FILE> """ user = kwargs['user'] query = request.args.get('query', 'meta.al_status:DEPLOYED') safe = request.args.get('safe', "false") if safe.lower() == 'true': safe = True else: safe = False access = user['access_control'] last_modified = STORAGE.get_signatures_last_modified() query_hash = md5(query + access + last_modified).hexdigest() + ".yar" signature_cache = TransportLocal( base=os.path.join(config.system.root, 'var', 'cache', 'signatures') ) response = _get_cached_signatures( signature_cache, last_modified, query_hash ) if response: return response with ExclusionWindow(query_hash, 30): response = _get_cached_signatures( signature_cache, last_modified, query_hash ) if response: return response keys = STORAGE.list_filtered_signature_keys(query, access_control=access) signature_list = STORAGE.get_signatures(keys) # Sort rules to satisfy dependencies duplicate_rules = [] error_rules = [] global_rules = [] private_rules_no_dep = [] private_rules_dep = [] rules_no_dep = [] rules_dep = [] if safe: rules_map = {} for s in signature_list: name = s.get('name', None) if not name: continue version = int(s.get('meta', {}).get('rule_version', '1')) p = rules_map.get(name, {}) pversion = int(p.get('meta', {}).get('rule_version', '0')) if version < pversion: duplicate_rules.append(name) continue rules_map[name] = s signature_list = rules_map.values() name_map = {} for s in signature_list: if s['type'].startswith("global"): global_rules.append(s) name_map[s['name']] = True elif s['type'].startswith("private"): if s['depends'] is None or len(s['depends']) == 0: private_rules_no_dep.append(s) name_map[s['name']] = True else: private_rules_dep.append(s) else: if s['depends'] is None or len(s['depends']) == 0: rules_no_dep.append(s) name_map[s['name']] = True else: rules_dep.append(s) global_rules = sorted(global_rules, key=lambda k: k['meta']['id']) private_rules_no_dep = sorted(private_rules_no_dep, key=lambda k: k['meta']['id']) rules_no_dep = sorted(rules_no_dep, key=lambda k: k['meta']['id']) private_rules_dep = sorted(private_rules_dep, key=lambda k: k['meta']['id']) rules_dep = sorted(rules_dep, key=lambda k: k['meta']['id']) signature_list = global_rules + private_rules_no_dep while private_rules_dep: new_private_rules_dep = [] for r in private_rules_dep: found = False for d in r['depends']: if not name_map.get(d, False): new_private_rules_dep.append(r) found = True break if not found: name_map[r['name']] = True signature_list.append(r) if private_rules_dep == new_private_rules_dep: for x in private_rules_dep: error_rules += [d for d in x["depends"]] if not safe: for s in private_rules_dep: name_map[s['name']] = True signature_list += private_rules_dep new_private_rules_dep = [] private_rules_dep = new_private_rules_dep signature_list += rules_no_dep while rules_dep: new_rules_dep = [] for r in rules_dep: found = False for d in r['depends']: if not name_map.get(d, False): new_rules_dep.append(r) found = True break if not found: name_map[r['name']] = True signature_list.append(r) if rules_dep == new_rules_dep: error_rules += [x["name"] for x in rules_dep] if not safe: for s in rules_dep: name_map[s['name']] = True signature_list += rules_dep new_rules_dep = [] rules_dep = new_rules_dep # End of sort error = "" if duplicate_rules: if safe: err_txt = "were skipped" else: err_txt = "exist" error += dedent("""\ // [ERROR] Duplicates rules {msg}: // // {rules} // """).format(msg=err_txt, rules="\n//\t".join(duplicate_rules)) if error_rules: if safe: err_txt = "were skipped due to" else: err_txt = "are" error += dedent("""\ // [ERROR] Some rules {msg} missing dependencies: // // {rules} // """).format(msg=err_txt, rules="\n//\t".join(error_rules)) # noinspection PyAugmentAssignment header = dedent("""\ // Signatures last updated: {last_modified} // Signatures matching filter: // // {query} // {error} // Number of rules in file: // """).format(query=query, error=error, last_modified=last_modified) rule_file_bin = header + YARA_PARSER().dump_rule_file(signature_list) rule_file_bin = rule_file_bin signature_cache.save(query_hash, rule_file_bin) return make_file_response( rule_file_bin, "al_yara_signatures.yar", len(rule_file_bin), content_type="text/yara" )
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)