def search_rules(query, columns, language, verbose=True): """Use KQL or EQL to find matching rules.""" from kql import get_evaluator from eql.table import Table from eql.build import get_engine from eql import parse_query from eql.pipes import CountPipe flattened_rules = [] for file_name, rule_doc in rule_loader.load_rule_files().items(): flat = {"file": os.path.relpath(file_name)} flat.update(rule_doc) flat.update(rule_doc["metadata"]) flat.update(rule_doc["rule"]) attacks = [ threat for threat in rule_doc["rule"].get("threat", []) if threat["framework"] == "MITRE ATT&CK" ] techniques = [ t["id"] for threat in attacks for t in threat.get("technique", []) ] tactics = [threat["tactic"]["name"] for threat in attacks] flat.update(techniques=techniques, tactics=tactics) flattened_rules.append(flat) flattened_rules.sort(key=lambda dct: dct["name"]) filtered = [] if language == "kql": evaluator = get_evaluator(query) if query else lambda x: True filtered = list(filter(evaluator, flattened_rules)) elif language == "eql": parsed = parse_query(query, implied_any=True, implied_base=True) evaluator = get_engine(parsed) filtered = [ result.events[0].data for result in evaluator(flattened_rules) ] if not columns and any( isinstance(pipe, CountPipe) for pipe in parsed.pipes): columns = ["key", "count", "percent"] if columns: columns = ",".join(columns).split(",") else: columns = ["rule_id", "file", "name"] table = Table.from_list(columns, filtered) if verbose: click.echo(table) return filtered
def evaluate(rule, events): """Evaluate a query against events.""" evaluator = kql.get_evaluator(kql.parse(rule.query)) filtered = list(filter(evaluator, events)) return filtered
def search_rules(query, columns, language, count, verbose=True, rules: Dict[str, dict] = None, pager=False): """Use KQL or EQL to find matching rules.""" from kql import get_evaluator from eql.table import Table from eql.build import get_engine from eql import parse_query from eql.pipes import CountPipe flattened_rules = [] rules = rules or rule_loader.load_rule_files(verbose=verbose) for file_name, rule_doc in rules.items(): flat = {"file": os.path.relpath(file_name)} flat.update(rule_doc) flat.update(rule_doc["metadata"]) flat.update(rule_doc["rule"]) tactic_names = [] technique_ids = [] subtechnique_ids = [] for entry in rule_doc['rule'].get('threat', []): if entry["framework"] != "MITRE ATT&CK": continue techniques = entry.get('technique', []) tactic_names.append(entry['tactic']['name']) technique_ids.extend([t['id'] for t in techniques]) subtechnique_ids.extend([ st['id'] for t in techniques for st in t.get('subtechnique', []) ]) flat.update(techniques=technique_ids, tactics=tactic_names, subtechniques=subtechnique_ids, unique_fields=Rule.get_unique_query_fields( rule_doc['rule'])) flattened_rules.append(flat) flattened_rules.sort(key=lambda dct: dct["name"]) filtered = [] if language == "kql": evaluator = get_evaluator(query) if query else lambda x: True filtered = list(filter(evaluator, flattened_rules)) elif language == "eql": parsed = parse_query(query, implied_any=True, implied_base=True) evaluator = get_engine(parsed) filtered = [ result.events[0].data for result in evaluator(flattened_rules) ] if not columns and any( isinstance(pipe, CountPipe) for pipe in parsed.pipes): columns = ["key", "count", "percent"] if count: click.echo(f'{len(filtered)} rules') return filtered if columns: columns = ",".join(columns).split(",") else: columns = ["rule_id", "file", "name"] table = Table.from_list(columns, filtered) if verbose: click.echo_via_pager(table) if pager else click.echo(table) return filtered
def evaluate(self, source_text, document=None): if document is None: document = self.document evaluator = kql.get_evaluator(source_text, optimize=False) return evaluator(document)