def update_available(**_): # pylint: disable=W0613 """ Check if updated signatures are. Variables: None Arguments: last_update => Epoch time of last update. Data Block: None Result example: { "update_available" : true } # If updated rules are available. """ last_update = iso_to_epoch(request.args.get('last_update')) last_modified = iso_to_epoch(STORAGE.get_signatures_last_modified()) return make_api_response({"update_available": last_modified > last_update})
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" )