diff --git a/check_query.py b/check_query.py deleted file mode 100644 index 3e4d180..0000000 --- a/check_query.py +++ /dev/null @@ -1,89 +0,0 @@ -from elasticsearch import Elasticsearch ## ES SDK -# from elastic_transport import RequestsHttpNode ## UNMARK IF RUNING BEHIND PROXY -# from datetime import datetime, timedelta, timezone ## UNMARK TO USE CUSTOM TIME - -## TIMEFRAME 2D /7D /30D -'''Uncomment this if U want to change the search timeframe -Once in use, must change the value in query_string var below''' -# current_time = datetime.now(timezone.utc) -# start_time = current_time - timedelta(days=3) -# fcurrent_time = current_time.strftime('%Y-%m-%dT%H:%M:%S') -# fstart_time = start_time.strftime('%Y-%m-%dT%H:%M:%S') - -# PROXIES (IF NEEDED) -proxies = { - 'http_proxy': '', - 'https_proxy': '' -} - -## CONSTRUCT DSL QUERY TO CHECK FOR RESULTS IN LAST ONE WEEK -def eql_query(esurl, es_key, rule_kuery, rule_index): - ''' - This function get elastic url, elastic token, query search, and index. - It return the number of results (if so) matched in the past week. - Note! Configure a class CustomHttpNode(RequestsHttpNode) if needed to run behind proxy - ''' - es = Elasticsearch( - esurl, - api_key=es_key, - # node_class=CustomHttpNode ## use this if using proxy - ) - - query_string = { - "query": { - "bool": { - "must": [ - { - "query_string": { - "query": f"{rule_kuery}" - } - }, - { - "range": { - "@timestamp": { - "gte": "now-1w/w", ## here you can adjust the time as needed. defult I set to 1week. - "lt": "now" - } - } - } - ] - } - } - } - - try: - response = es.search(index=rule_index, body=query_string) - ''' - Using the search method from elasticsearch library. - Uncomment next 3 line if result data is needed - For hit in response['hits']['hits']: - print(hit['_source']) - num_results = response._body['hits']['total']['value']''' - - num_results = response._body['hits']['total']['value'] - - except Exception as e: - print(f"[X] Error executing EQL query: {e}") - - return num_results - - -def check_main(filename, rule_kuery, rule_index): - '''This function is a child one of check_query_main.py. - Receives: - - filename = json file name - - rule_kuery = search query - - rule_index = index to search in - ''' - filename = filename - - '''get your elk creds from anywhere you saved them. - Make sure to retreive them securely - do not save them hardcoded here!!!''' - esurl = '' - es_key = '' - - ## TRIGGER eql_query FUNCTION - results = eql_query(esurl, es_key, rule_kuery, rule_index) - - ## PRINT RESULTS - print(f"Provided query resulted with {results} results") diff --git a/check_query_main.py b/check_query_main.py deleted file mode 100644 index c20fe8c..0000000 --- a/check_query_main.py +++ /dev/null @@ -1,79 +0,0 @@ -import validate_query -import check_query -import os -import json - -## GET CREDS -'''Use this if you need to retreive credentials from an external vault''' -# def creds(): -# '''Retreive credentials from your desire vault. -# This function must use an API of the vualt you use (Secret Manager/ CyberArk/ etc.)''' -# esurl = '' -# es_key = '' - -# creds_data = { -# 'esurl':esurl, -# 'es_key':es_key -# } - -# return creds_data - - -## GET .JSON RULE DATA FILE NAME -def get_json(): - '''look for th json file to extract the detection query and other data the check the sytnax. - Returns an array with the KQL query, query index, and a rule name.''' - current_dir = os.getcwd() - files = os.listdir(current_dir) - json_files = [file for file in files if file.endswith('.json')] - filename = json_files[0] - - with open(filename, 'r') as file01: - json_data = json.load (file01) - - rule_kuery = json_data['query'] - rule_name = json_data['name'] - rule_index = json_data['index'][0] - - json_info = { - 'rule_kuery':rule_kuery, - 'rule_name':rule_name, - 'rule_index':rule_index, - 'filename':filename - } - - return json_info - -def main(): - '''Note! - - validate_query.main get: rule_index & rule_name. - - validate_query.main trigger validate_query.eql_query() function. - - validate_query.eql_query() also get esurl & api_key there.''' - - ## GET CREDS (IF NEEDED) - # credentials = creds() - - ## TRIGGER TO GET JSON FILE - json_info = get_json() - - ## TRIGGER THE VALIDATE QUERY (SYNTAX CHECK) TO RETURN TRUE/FALSE - ''' validate_query.main method receive: - - json_info['rule_index'] = rule index - - json_info['rule_kuery'] = Elastic EQL query - The Method return true/false based on syatax check ''' - boolean = validate_query.main(json_info['rule_index'], - json_info['rule_kuery']) - - ## CHECK FOR RESULTS FROM KUERY - num_of_results = check_query.check_main(json_info['filename'], - json_info['rule_kuery'], - json_info['rule_index']) - - ## PRINT CHECKS OUTPUT - print("Query Validation Result:", boolean) - print(f"Numebr of results: {num_of_results}") - - -## TRIGGER THE CODE -if __name__ == '__main__': - main() diff --git a/craft_json.py b/craft_json.py deleted file mode 100644 index 847fab1..0000000 --- a/craft_json.py +++ /dev/null @@ -1,394 +0,0 @@ -import re -import requests -from requests.auth import HTTPBasicAuth -import json -import random -import string - -''' -SentintelByte -Date: 26 Aug 2024 -''' - -def get_creds(): - '''This function manage all necessary credentials. - Use your own Vualt (AWS Secret Manager/ Akeyless/ CyberArk) to retrieve these keys securly. - This Function do not receive any arguments. Just fetch keys from a secret manager. - Finally it return an array of creds necessary for other functions to run. - kb_user = Kibana endpoint username - kb_pwd = Kibana endpointpassword - es_key = Elastic token - es_url = Base url for yourr elastic endpoint - detection_endpoint = API endpoint for elastic connectors (to be able to set actions) - ''' - ## KIBANA USER - kb_user = '' - - ## KIBANA PASSWORD - kb_pwd = '' - - ## ELASTIC TOKEN - es_key = '' - - ## ELASTIC URL - es_url = '' - - ## KIBANA DETECTION POST URL - kb_url = '' - detection_endpoint = "/api/actions/connectors" # ENDPOINT PATH - connector_url = f'{kb_url}{detection_endpoint}' # FULL API URL - - # QUERY PARAMS - headers = { - 'Content-Type': 'application/json', - 'kbn-xsrf': 'true' # REQUIRED FOR KNB REQUESTS - } - - # JSON WITH CREDS - creds = { - 'kb_user': kb_user, - 'kb_pass': kb_pwd, - 'es_key': es_key, - 'connectors_url': connector_url, - 'headers': headers, - 'es_url':es_url - } - - return creds - - -# GET KIBANA CONNECTOR NAMES & IDs -''' API - GET kbn:/api/actions/connectors ''' - - -def kb_get(kb_url, headers, kb_user, kb_pass): - connectors = [] - # Used this methid to fetch all indices for example - response = requests.get(kb_url, - headers=headers, - auth=HTTPBasicAuth(kb_user, kb_pass)) - - if response.status_code == 200: - data = response.json() - for connector in data: - connectors.append(f"{connector['name']}:{connector['id']}") - else: - print( - f"Failed to retrieve data:\n{response.status_code}-{response.text}") - - return connectors - - -def construct_connectors(kb_url=creds['connectors_url'], headers=creds['headers'], kb_user=creds['kb_user'], kb_pass=creds['kb_pass']): - # CONSTRUCT CONNECTOR LIST FOR USER REFERENCE - get_connector_list = kb_get(kb_url, headers, kb_user, kb_pass) - num = 0 - final_conn_list = [] - while len(get_connector_list) > num: - for object in get_connector_list: - final_conn_list.append(f'{num} - {get_connector_list[num]}') - num += 1 - - return final_conn_list - - -# REGEX CHECK FOR TAGS -def input_with_commas(tags): - '''This function checks if custom tags are seperated as needed for an array [] construction''' - # pattern = r'^([a-z0-9*]+)(?:,([a-z0-9*]+))*$' - pattern = r'^([a-z0-9_*]+)(?:,([a-z0-9_*]+))*$' - - return bool(re.match(pattern, tags)) - - -def syntax_check(data, datatype): - '''This function get data source (tags/index). - Chceck syntax based on a specific criteria mention in KB API''' - while input_with_commas(data) == False: - print("Syntax isn't correct") - data = input( - f"[+] Insert {datatype} Seperated by commas (,)\nexample,*aa*,bbb,111*\n > ") - else: - data_list = [word.strip() for word in data.split(',')] - return data_list - - -## CHECK RULE NAME FOR FILE NAME SYNTAX -def check_unsupported_characters(unsupported_chars, rulename): - '''This function receives the rule name the user insert - And check for pre defined unsupported characters. - This is based on a Unix/Windows FS specifications''' - unsupported_chars = re.findall(unsupported_chars, rulename) - - if unsupported_chars: - return True, list(set(unsupported_chars)) - else: - return False, [] - - -## GENERATE RANDOM RULE ID -def generate_random_string(): - '''This function uses the random() lib to create a 32 bit long rule ID''' - segment_lengths = [8, 4, 4, 4, 12] - segments = [] - - for length in segment_lengths: - segment = ''.join(random.choices(string.ascii_lowercase + string.digits, k=length)) - segments.append(segment) - - return '-'.join(segments) - - -# INPUT FROM USER, SYNTAX CHECKS & ARRAY CONSTRUCTION -def data(conn_list): - ''' - 1. This function Recieves: - - conn_list = connector list from Kibana - - User inputs - 2. Check user input syntax. - 3. Construct connector IDs. - 4. Construct JSON for rule creation. - ** Note! this function can get more params for rule creation. refer to: - https://www.elastic.co/guide/en/security/current/rule-api-overview.html - ''' - ## PLATFORM - '''Logs data source. e.g. azure/aws/windows/etc.''' - platform = input( - '[+] Enter Platform name\ne.g. Azure, AWS, OKTA, etc.\n> ') - - ## RULE NAME - unsupported_chars = r'[\\/:*?"<>|]' - while True: - rulename = input("[+] Choose Rule Name\n> ") - has_unsupported, chars = check_unsupported_characters( - unsupported_chars, rulename) - if has_unsupported: - print(f"[NOTE] Unsupported characters found: {chars}") - else: - print(f"[OK] {rulename} is Valid. Pls Continue.") - break - - ## RULE INDEX - indices = input( - "[+] Insert a single Index - e.g. *test*\n> ") - index_list = syntax_check(indices, 'Indices') - - ## KUERY + SYNTAX CHECK - detection_query = input( - "[+] Insert Your Query (Kuery Syntax)\ne.g. '_index:example_index AND _id:example_id'\n> ") - '''Uncomment the following lines if a syntax check is necessary''' - # try: - # boolean = validate_query.main(index_list, detection_query) - # while boolean == False: - # detection_query = input( - # "[NOTE] Query or Index are NOT valid\nTry again or contact team lead for further help.\nSee examples below:\n\tINDEX:*test*\n\tQUERY:'_index:aws AND _id:jS_QAJEB67nTnoI_wr7r'\nEnter new Query\n> ") - # indices = input("[+] Make sure U enter the right index pattern.\nValidate index in Kibana GUI.\nEnter new valid index> ") - # index_list = syntax_check(indices, 'Indices') - # boolean = validate_query.main(index_list, detection_query) - - # if detection_query == True: - # print("Query Validation Result:", boolean) - # break - # else: - # continue - - # except Exception as e: - # print(f"[NOTE] Error - {e}") - # print("Please try again or contact team lead for further help.") - - - ## RULE DESCRIPTION - description = '' - while len(description) == 0: - description = input("[+] Rule Description\n> ") - - ## TAGS (MATURITY, PLATFORM, RISK, USER_APPROACH, AUTO_REM, TECTIC) - '''Tags can be cahnged base on your needs - Make sure to remove any unnecessary tags and insert those you want. - Finally make sure U append anything to "tags". - Note that "tags" is an array list''' - tags = [] - print("[+] Mendatory Tags:\n ") - tags.append(f"platform:{platform.lower()}") ## PLATFORM TAG - - maturity = input( - "[+] Choose maturity level\n\t(1) Staging\n\t(2) Production\n> ") ## MATURITY TAG - if int(maturity) == 1: - tags.append("maturity:staging") - elif int(maturity) == 2: - tags.append("maturity:production") - - risk_tag = input( - "[+] Choose Risk (Choose No.)\n\t(1) Incident\n\t(2) Misconfiguration\n\t(3) Compliance\n\t(4) Monitoring\n> ") ## RISK TAGS - if int(risk_tag) == 1: - tags.append("risk:incident") - tactics = {'1': 'initial access', - '2': 'execution', - '3': 'persistence', - '4': 'privilege escalation', - '5': 'defense evasion', - '6': 'credential access', - '7': 'discovery', - '8': 'lateral movement', - '9': 'collection', - '10': 'exfiltration', - '11': 'command and control', - '12': 'impact' - } # TACTICS - - tactic_num = input("[+] Choose Attack Tactic (Choose No.):\n\t(1) initial access\n\t(2) execution\n\t(3) persistence\n\t(4) privilege escalation\n\t(5) defense evasion\n\t(6) credential access\n\t(7) discovery\n\t(8) lateral movement\n\t(9) collection\n\t(10) exfiltration\n\t(11) command & control\n\t(11) impact\n> ") - tactic_choosen = tactics[f'{tactic_num}'] - tags.append(f"tactic:{tactic_choosen}") - - elif int(risk_tag) == 2: - tags.append("risk: misconfiguration") - elif int(risk_tag) == 3: - tags.append("risk: compliance") - elif int(risk_tag) == 4: - tags.append("risk: monitoring") - - user_approach = input( - "[+] Does User Approach?\n\t(1) False\n\t(2) True\n> ") # USER APPROACH - if int(user_approach) == 1: - tags.append("user approach: false") - elif int(user_approach) == 2: - tags.append("user approach: true") - - auto_remediation = input( - "[+] Does auto remediation exists?\n\t(1) False\n\t(2) True\n> ") # AUTO REMEDIATION - if int(auto_remediation) == 1: - tags.append("auto remediation: false") - elif int(auto_remediation) == 2: - tags.append("auto remediation: true") - - '''Other custome detection rule tags''' - other_tags = input( - "[+] Insert Other Tags Seperated by commas (,)\ne.g. example,*aa*,bbb,111*\n> ") # CUSTOM TAGS - - '''Syntax check for other (custom) tags the user inserts''' - syntax_check(other_tags, 'Tags') - other_tags = other_tags.split(',') - for custom_tag in other_tags: - custom_tag.lower() - tags.append(custom_tag) - - ## KIBANA CONNECTORS - print('~~~ Connector list ~~~') - for object in conn_list: - print(object) - - while True: - choose_torq_connector_num = input( - f"[+] Choose Torq connector from the list above.\nChoice Must be INT!\n> ") - try: - int_value = int(choose_torq_connector_num) - colon_pos = conn_list[int_value].find(':') - choosen_connector = conn_list[int_value][colon_pos + 1:] - print("~~~~~~~~~\nChoosen Connector: ", choosen_connector) - break - except ValueError: - print( - "[-] Not valid integer. Choose connector number from the list above ^") - - ## RULE ID STRING - '''Generate random 32bit long for rule ID''' - rule_id = generate_random_string() - - - ## SEVERITY RATING STRING - severity = input("[+] Severity Tag [low/ medium/ high/ critical]\n> ") - - ## RISK SCORE - while True: - risk_score = input( - "[+] Choose Rule Risk Score between - Only Numbers 1-100\n> ") - if risk_score.isdigit(): - if 0 < int(risk_score) <= 100: - break - else: - risk_score = input( - "[+] Choose Rule Risk Score between - Only Numbers 1-100\n> ") - - ## CREATOR NAME - creator_name = input("Pls Enter your name here > ") - - ## FALSE POSITIVE CASES [ARRAY] - false_positives = input( - "Insert false positive cases seperated by commas (,)\n> ") - false_positive_list = syntax_check(false_positives, 'false positive') - - print('~~~~~~~~~~~\nFinal JSON\n~~~~~~~~~~~') - ## CONSTRUCT RULE DATA IN A JSON FORMAT - json_data = { - "rule_id": rule_id, - "risk_score": int(risk_score), - "description": description.lower(), - "interval": '30m', - "name": f"{platform}_{rulename}".lower(), - "severity": severity, - "tags": tags, - "type": "query", - "from": f"now-45m", - "query": detection_query.lower(), - "language": "kuery", - "filters": [ - { - "query": { - "match": { - "event.action": { - "query": "Process Create (rule: ProcessCreate)", - "type": "phrase" - } - } - } - } - ], - "index": index_list, - "actions": [ - { - "action_type_id": ".torq", - "group": "default", - "id": choosen_connector, - "params": { - "message": "{{context.rule.description}}" - } - } - ], - "author": [f"{creator_name.lower()}"], - "false_positives": false_positive_list, - "enabled": False - } - - print(json_data) - rule_json = json.dumps(json_data, indent=4) - return rule_json, rulename, platform - - -def send_json(json_data, rule_name, platform): - ''' - This function receives: - - json_data = A json with the detection info (based on user inputs) - - rule_name = The rule name for the file name - - platform = Platform of the logs data source - Create the json file''' - with open(f'{platform}_{rule_name}.json'.lower(), 'w') as file01: - file01.write(json_data) - # json.dump(json_data) - - -if __name__ == "__main__": - ## CALL THE CREDS() FUNCTION TO GET ALL CREDS - creds = get_creds() - - ## TRIGGER TO GET KB CONNECTORS - conn_list = construct_connectors(kb_url=creds['connectors_url'], - headers=creds['headers'], - kb_user=creds['kb_user'], - kb_pass=creds['kb_pass']) - - ## TRIGGER TO GET USER INPUT FOR JSON RULE DATA - rule_data = data(conn_list) - # rule_name = rule_data['name'] - - ## TRIGGER JSON FILE WRITING TO CURRENT DIR - send_json(rule_data[0], rule_data[1], rule_data[2]) ## 0 > JSON DATA | 1 > RULE NAME diff --git a/create_rule.py b/create_rule.py deleted file mode 100644 index b923432..0000000 --- a/create_rule.py +++ /dev/null @@ -1,128 +0,0 @@ -import requests -from requests.auth import HTTPBasicAuth -import json -import os - -''' -SentinelByte | Aug 26, 2024 -References: -- https://www.elastic.co/guide/en/kibana/current/api.html -- https://www.elastic.co/guide/en/kibana/current/create-rule-api.html -- https://www.elastic.co/guide/en/security/8.14/rule-api-overview.html -- https://www.elastic.co/guide/en/security/8.14/rules-api-create.html#ref-fields-all -''' - -def get_creds(): - ## ELASTIC SECRETS - kb_user = '' - kb_pwd = '' - - ## DETECTION POST URL - TO BE FETCHED FROM AKEYLESS!! - kb_url = '' - detection_endpoint = "/api/detection_engine/rules" - detection_post_url = f'{kb_url}{detection_endpoint}' - - ## MORE ENDPOINTS - # endpoint = "/api/index_management/indices" # INDEX GET URL - # endpoint = "/api/alerting/rule" # ALERTS POST URL - - ## QUERY PARAMS - headers = { - 'Content-Type': 'application/json', - 'kbn-xsrf': 'true' ## Required for Kibana requests - } - - creds_data = {'kb_user':kb_user, - 'kb_pass':kb_pwd, - 'detection_url':detection_post_url, - 'headers':headers - } - - return creds_data - - -## KIBANA REQUEST GET -'''Use this function if you want to receive dat afrom kibana -Get request method -Change the endpoint URL based on the data you want to fetch -Make sure you are using credentials with proper permission to fetch the data you desire''' -# def kb_get(query_get_url, headers, kb_user, kb_pwd): -# # Use this method to fetch all indices for example -# response = requests.get(query_get_url, -# headers=headers, -# auth=HTTPBasicAuth(kb_user, kb_pwd)) - -# if response.status_code == 200: -# data = response.json() -# print(data) -# else: -# print( -# f"Failed to retrieve data:\n{response.status_code} - {response.text}") - - -## KIBANA REQUEST POST -'''''' -def kb_post(detection_post_url, headers, rule_body, kb_user, kb_pwd): - ''' - Create a detection rule in Kibana via Requests and Kibana API. - This function gets: - - detection_post_url = kibana detection url - - headers = Necessary headers for Kibana request - - rule_body = The detection rule data - - kb_user = Kibana username for auth - - kb_pwd = Kibana password for auth - ''' - response = requests.post(detection_post_url, - headers=headers, - json=rule_body, - auth=HTTPBasicAuth(kb_user, kb_pwd)) - - if response.status_code == 200: - data = response.json() - return data - - elif response.status_code == 409: - print("[!] Rule name already exists.\n\t- Delete previous JSON.\n\t- Create a new one via craft_json.py.") - - else: - print( - f"Failed to retrieve data:\n{response.status_code} - {response.text}") - - -## MAIN TRIGGER FUNCTION -def main_func(): - ''' - This MAIN function look for json file in current working directory. - This json file should be created using the create_json.py file. - Make sure it saves the json files in the right system path you wish. - Then, trigger creds function to receive credentials. - Later, trigger the kb_post function which gets: - - creds['detection_url'] = kibana detection url - - creds['headers'] = Necessary headers for Kibana request - - rule_body = The detection rule data - - creds['kb_user'] = Kibana username for auth - - creds['kb_pass'] = Kibana password for auth - ''' - - ## LOAD JSON - current_dir = os.getcwd() - files = os.listdir(current_dir) - json_files = [file for file in files if file.endswith('.json')] - filename = json_files[0] - with open(filename, 'r') as file1: - rule_body = json.load(file1) - - ## TRIGGER GET CREDS - creds = get_creds() - - ## KB FUNCTIONS (GET/POST) - '''Uncomment kb_get if you wish to fetch data from Kibana''' - # kb_get(query_get_url, headers, kb_user, kb_pwd) - - '''See funtion explaination above ^^ ''' - kb_post(creds['detection_url'], creds['headers'], rule_body, creds['kb_user'], creds['kb_pass']) - - -'''SentinelByte | Aug 26, 2024''' -if __name__ == '__main__': - main_func() diff --git a/push_to_github.bash b/push_to_github.bash deleted file mode 100644 index 985f2bf..0000000 --- a/push_to_github.bash +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -# Directory containing your Git repository -REPO_DIR="/path/to/your/repo" - -# Directory where archived files will be moved -ARCHIVE_DIR="$REPO_DIR/archive" - -# File to push -FILE_TO_PUSH="example_rule_01.json" - -# Change to the repository directory -cd $REPO_DIR - -# Search for the file -FILE_PATH=$(find $REPO_DIR -name $FILE_TO_PUSH) - -# Check if the file exists -if [ -f "$FILE_PATH" ]; then - # Add the file to git - git add "$FILE_PATH" - - # Commit changes with a message - git commit -m "Update $FILE_TO_PUSH" - - # Push changes to the remote repository - git push origin main - - # Create archive directory if it doesn't exist - mkdir -p $ARCHIVE_DIR - - # Move the file to the archive directory - mv "$FILE_PATH" "$ARCHIVE_DIR/" - - # Optionally: Remove any cached files from the git index - git rm --cached "$ARCHIVE_DIR/$FILE_TO_PUSH" - - echo "File $FILE_TO_PUSH pushed and archived successfully." -else - echo "File $FILE_TO_PUSH not found." -fi diff --git a/validate_query.py b/validate_query.py deleted file mode 100644 index c17f8ac..0000000 --- a/validate_query.py +++ /dev/null @@ -1,46 +0,0 @@ -from elasticsearch import Elasticsearch # ES SDK -# from elastic_transport import RequestsHttpNode # UNMARK THIS IF USING PROXY - -## PROXIES (IF NEEDED) -'''Uncomment next 4 lines to use proxies''' -# proxies = { -# "http_proxy": "", -# "https_proxy": "" -# } - -def eql_query(esurl, api_key, index, eql_kuery): - '''Ajust the class CustomHttpNode(RequestsHttpNode) to use proxy if needed - If used - make sure to unmark the node_class=CustomHttpNode in the es param below''' - - es = Elasticsearch( - esurl, - api_key=api_key, - # node_class=CustomHttpNode - ) - - '''Making a request using built in elasticsearch validate query - This should return true/false. - Indicating query is working or not''' - try: - response = es.indices.validate_query(index=index, q=eql_kuery, explain='true') - if response._body['valid'] == True: - return True - else: - return False - - except Exception as e: - return f"Error executing EQL query: {e}" - - -def main(index, eql_kuery): - '''This is a child function of check_query_main.py - Triggered by check_query_main.py.''' - # ELASTIC CREDS - es_key = '' - esurl = '' - - ## TRIGGER eql_query() FUNCTION - results = eql_query(esurl, es_key, index, eql_kuery) - - ## RETURN RESULTS (TRUE/FALSE) - return results