Skip to content
This repository was archived by the owner on Feb 18, 2021. It is now read-only.
Open
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,7 @@ docs/_build/
# PyBuilder
target/

.idea/
.idea/

# IDE
.vscode/
7 changes: 5 additions & 2 deletions pyFireEye/hx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
Indicators, \
IndicatorCategories, \
Quarantine, \
Scripts
Scripts, \
ProcessTracker, \
MessageBus


class HX:
Expand Down Expand Up @@ -48,6 +50,8 @@ def __init__(self, hx_host, hx_port=None, verify=False, token_auth=False, userna
self.scripts = Scripts(hx_host=hx_host, hx_port=hx_port, verify=verify, authenticator=self._authenticator)
self.containment = Containment(hx_host=hx_host, hx_port=hx_port, verify=verify, authenticator=self._authenticator)
self.custom_channels = CustomChannels(hx_host=hx_host, hx_port=hx_port, verify=verify, authenticator=self._authenticator)
self.process_tracker = ProcessTracker(hx_host=hx_host, hx_port=hx_port, verify=verify, authenticator=self._authenticator)
self.message_bus = MessageBus(hx_host=hx_host, hx_port=hx_port, verify=verify, authenticator=self._authenticator)

def reauth(self):
if self._authenticator.token_auth:
Expand All @@ -62,4 +66,3 @@ def logout(self):
self._authenticator.token_auth = True
self._authenticator.logout()
self._authenticator.token_auth = token_auth

100 changes: 70 additions & 30 deletions pyFireEye/hx/hx_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@


class _HX:

"""
Base class for hx endpoints to derive from. will not work by itself unless you construct authenticator
and set it manually, but you should never need to use it directly
Expand Down Expand Up @@ -172,9 +172,9 @@ def __init__(self, hx_host, hx_port=None, verify=False, authenticator=None, user

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/hosts",
request_params=[HAS_ACTIVE_THREATS, HAS_ALERTS, HAS_EXECUTION_ALERTS, HAS_EXPLOIT_ALERTS,
HAS_EXPLOIT_BLOCKS, HAS_MALWARE_ALERTS, HAS_MALWARE_CLEANED,
HAS_MALWARE_QUARANTINED, HAS_PRESENCE_ALERTS, HAS_SHARE_MODE, HOSTS_SET_ID, LIMIT,
request_params=[HAS_ACTIVE_THREATS, HAS_ALERTS, HAS_EXECUTION_ALERTS, HAS_EXPLOIT_ALERTS,
HAS_EXPLOIT_BLOCKS, HAS_MALWARE_ALERTS, HAS_MALWARE_CLEANED,
HAS_MALWARE_QUARANTINED, HAS_PRESENCE_ALERTS, HAS_SHARE_MODE, HOSTS_SET_ID, LIMIT,
OFFSET, SEARCH, SORT, FILTER_FIELD])
def get_list_of_hosts(self, has_active_threats=None, has_alerts=None, has_execution_alerts=None,
has_exploit_alerts=None, has_exploit_blocks=None, has_malware_alerts=None,
Expand Down Expand Up @@ -215,7 +215,7 @@ def new_file_acquisition_for_host(self, agent_id, req_path, req_filename, req_co
@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/hosts/<agent_id>/live",
request_params=[OFFSET, LIMIT, SORT, FILTER_FIELD])
def get_list_data_acquisitions_for_host(self, agent_id, offset=None, limit=None, sort=None,
def get_list_data_acquisitions_for_host(self, agent_id, offset=None, limit=None, sort=None,
filter_field=None, **kwargs):
return self._base_request(**kwargs)

Expand Down Expand Up @@ -364,16 +364,16 @@ def delete_search_by_id(self, id, **kwargs):
@template_request(method="POST", route="/searches/<id>/actions/stop")
def stop_running_search(self, id, **kwargs):
return self._base_request(**kwargs)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/searches/<id>/hosts",
@template_request(method="GET", route="/searches/<id>/hosts",
request_params=[OFFSET, LIMIT, SORT, FILTER_FIELD])
def get_list_hosts_states_for_search(self, id, offset=None, limit=None, sort=None, filter_field=None, **kwargs):
return self._base_request(**kwargs)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/searches/<id>/skipped_hosts",
request_params=[HAS_ACTIVE_THREATS, HAS_EXPLOIT_ALERTS, HAS_EXPLOIT_BLOCKS, HAS_MALWARE_ALERTS,
@template_request(method="GET", route="/searches/<id>/skipped_hosts",
request_params=[HAS_ACTIVE_THREATS, HAS_EXPLOIT_ALERTS, HAS_EXPLOIT_BLOCKS, HAS_MALWARE_ALERTS,
HOSTS_SET_ID, LIMIT, OFFSET, SORT, FILTER_FIELD])
def get_list_hosts_skipped_for_search(self, id, has_active_threats=None, has_exploit_alerts=None,
has_exploit_blocks=None, has_malware_alerts=None, host_set_id=None,
Expand All @@ -392,7 +392,7 @@ def get_search_results(self, id, offset=None, limit=None, **kwargs):

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/searchs/<id>/results/<row_id>/hosts",
request_params=[HAS_ACTIVE_THREATS, HAS_EXPLOIT_ALERTS, HAS_EXPLOIT_BLOCKS, HAS_MALWARE_ALERTS,
request_params=[HAS_ACTIVE_THREATS, HAS_EXPLOIT_ALERTS, HAS_EXPLOIT_BLOCKS, HAS_MALWARE_ALERTS,
HAS_SHARE_MODE, HOSTS_SET_ID, LIMIT, OFFSET, SORT, FILTER_FIELD])
def get_host_results_for_grid_row_search(self, id, row_id, has_active_threats=None, has_exploit_alerts=None,
has_exploit_blocks=None, has_malware_alerts=None, has_share_mode=None,
Expand Down Expand Up @@ -473,7 +473,7 @@ def bulk_replace_indicator_conditions(self, category, indicator, conditions, **k

:param category: newline separated string of conditions
:param indicator:
:param conditions:
:param conditions:
:param kwargs:
:return:
"""
Expand All @@ -486,7 +486,7 @@ def bulk_append_indicator_conditions(self, category, indicator, conditions, **kw

:param category: newline separated string of conditions
:param indicator:
:param conditions:
:param conditions:
:param kwargs:
:return:
"""
Expand Down Expand Up @@ -545,22 +545,22 @@ def get_condition_by_id(self, condition_id, **kwargs):
return self._base_request(**kwargs)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/conditions",
@template_request(method="GET", route="/conditions",
request_params=[SEARCH, OFFSET, LIMIT, ENABLED, HAS_ALERTS, HAS_SHARE_MODE])
def get_list_conditions_all_hosts(self, search=None, offset=None, limit=None, enabled=None,
has_alerts=None, has_share_mode=None, **kwargs):
return self._base_request(**kwargs)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/conditions/<condition_id>/indicators",
@template_request(method="GET", route="/conditions/<condition_id>/indicators",
request_params=[OFFSET, LIMIT, CATEGORY_SHARE_MODE, SORT])
def get_indicators_using_condition(self, condition_id, offset=None, limit=None,
category_share_mode=None, sort=None, **kwargs):
return self._base_request(**kwargs)


class IndicatorCategories(_HX):

def __init__(self, hx_host, hx_port=None, verify=False, authenticator=None, username="", password=""):
_HX.__init__(self, hx_host, hx_port=hx_port, verify=verify)
if isinstance(authenticator, Authentication):
Expand All @@ -571,7 +571,7 @@ def __init__(self, hx_host, hx_port=None, verify=False, authenticator=None, user
@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/indicator_categories",
request_params=[OFFSET, LIMIT, SHARE_MODE, SORT, FILTER_FIELD])
def get_list_indicator_categories(self, offset=None, limit=None, share_mode=None,
def get_list_indicator_categories(self, offset=None, limit=None, share_mode=None,
sort=None, filter_field=None, **kwargs):
return self._base_request(**kwargs)

Expand All @@ -582,7 +582,7 @@ def get_indicator_category(self, category, share_mode, **kwargs):

@expected_response(expected_status_code=201, expected_format=JSON)
@template_request(method="PUT", route="/indicator_categories/<category_name>",
json_body=["display_name", "retention_policy", "ui_edit_policy",
json_body=["display_name", "retention_policy", "ui_edit_policy",
"ui_signature_enabled", "ui_source_alerts_enabled"])
def new_indicator_category(self, category_name, display_name=None, retention_policy=None, ui_edit_policy=None,
ui_signature_enabled=None, ui_source_alerts_enabled=None, **kwargs):
Expand Down Expand Up @@ -615,18 +615,18 @@ def __init__(self, hx_host, hx_port=None, verify=False, authenticator=None, user
self.AUTHENTICATION = authenticator
elif username and password:
self.AUTHENTICATION = Authentication(hx_host=hx_host, hx_port=hx_port, verify=verify, username=username, password=password)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/alerts/<alert_id>")
def get_alert_by_id(self, alert_id, **kwargs):
return self._base_request(**kwargs)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/alerts",
request_params=[OFFSET, LIMIT, HAS_SHARE_MODE, SORT, FILTER_FIELD, FILTER_QUERY])
def get_list_alerts_all_hosts(self, offset=None, limit=None, has_share_mode=None, sort=None, filter_field=None, filterQuery=None, **kwargs):
return self._base_request(**kwargs)

@expected_response(expected_status_code=204, expected_format=DEFAULT)
@template_request(method="DELETE", route="/alerts/<alert_id>")
def suppress_alert(self, alert_id, **kwargs):
Expand All @@ -648,14 +648,14 @@ def get_source_alert_by_id(self, source_alert_id, **kwargs):
return self._base_request(**kwargs)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/source_alerts/",
@template_request(method="GET", route="/source_alerts/",
request_params=[PRIMARY_INDICATOR_ID, OFFSET, LIMIT, SORT])
def get_list_source_alerts_all_hosts(self, primary_indicator_id=None, offset=None, limit=None, sort=None, **kwargs):
return self._base_request(**kwargs)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/source_alerts/<source_alert_id>/alerted_hosts",
request_params=[HAS_ACTIVE_THREATS, HAS_EXPLOIT_ALERTS, HAS_EXPLOIT_BLOCKS,
@template_request(method="GET", route="/source_alerts/<source_alert_id>/alerted_hosts",
request_params=[HAS_ACTIVE_THREATS, HAS_EXPLOIT_ALERTS, HAS_EXPLOIT_BLOCKS,
HAS_MALWARE_ALERTS, LIMIT, OFFSET, SEARCH, SORT, FILTER_FIELD])
def get_list_alerted_hosts_for_source_alert(self, source_alert_id, has_active_threates=None,
has_exploit_alerts=None, has_exploit_blocks=None,
Expand All @@ -664,7 +664,7 @@ def get_list_alerted_hosts_for_source_alert(self, source_alert_id, has_active_th
return self._base_request(**kwargs)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/source_alerts/<source_alert_id>/alerts",
@template_request(method="GET", route="/source_alerts/<source_alert_id>/alerts",
request_params=[OFFSET, LIMIT, SORT, FILTER_FIELD])
def get_lists_of_alerts_for_source_alert(self, source_alert_id, offset=None, limit=None, sort=None, filter_field=None, **kwargs):
return self._base_request(**kwargs)
Expand All @@ -688,7 +688,7 @@ def __init__(self, hx_host, hx_port=None, verify=False, authenticator=None, user
self.AUTHENTICATION = authenticator
elif username and password:
self.AUTHENTICATION = Authentication(hx_host=hx_host, hx_port=hx_port, verify=verify, username=username, password=password)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/acqs/files", request_params=[SEARCH, OFFSET, LIMIT, SORT, FILTER_FIELD])
def get_list_file_acquisitions_all_hosts(self, search=None, offset=None, limit=None,
Expand Down Expand Up @@ -773,15 +773,15 @@ def delete_host_bulk_acquisition_package_by_host(self, bulk_id, agent_id, **kwar
return self._base_request(**kwargs)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/acqs/bulk/<bulk_id>/hosts",
@template_request(method="GET", route="/acqs/bulk/<bulk_id>/hosts",
request_params=[OFFSET, LIMIT, SORT, FILTER_FIELD])
def get_list_hosts_in_bulk_acquisition(self, bulk_id, offset=None, limit=None, sort=None,
filter_field=None, **kwargs):
return self._base_request(**kwargs)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/acqs/bulk/<bulk_id>/skipped_hosts",
request_params=[HAS_ACTIVE_THREATS, HAS_EXPLOIT_ALERTS, HAS_EXPLOIT_BLOCKS, HAS_MALWARE_ALERTS,
request_params=[HAS_ACTIVE_THREATS, HAS_EXPLOIT_ALERTS, HAS_EXPLOIT_BLOCKS, HAS_MALWARE_ALERTS,
HOSTS_SET_ID, LIMIT, OFFSET, SORT, FILTER_FIELD])
def get_hosts_skipped_in_bulk_acquisition(self, bulk_id, has_active_threats=None, has_exploit_alerts=None,
has_exploit_blocks=None, has_malware_alerts=None, host_set_id=None,
Expand Down Expand Up @@ -837,12 +837,12 @@ def __init__(self, hx_host, hx_port=None, verify=False, authenticator=None, user
@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/hosts/<agent_id>/quarantines",
request_params=[OFFSET, LIMIT, SORT, FILTER_FIELD])
def get_list_quarantined_files_for_host(self, agent_id=None, offset=None, limit=None,
def get_list_quarantined_files_for_host(self, agent_id=None, offset=None, limit=None,
sort=None, filter_field=None, **kwargs):
return self._base_request(**kwargs)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/quarantines/",
@template_request(method="GET", route="/quarantines/",
request_params=[OFFSET, LIMIT, SORT, FILTER_FIELD])
def get_list_quarantined_files(self, offset=None, limit=None, sort=None, filter_field=None, **kwargs):
return self._base_request(**kwargs)
Expand Down Expand Up @@ -949,7 +949,7 @@ def __init__(self, hx_host, hx_port=None, verify=False, authenticator=None, user
self.AUTHENTICATION = Authentication(hx_host=hx_host, hx_port=hx_port, verify=verify, username=username, password=password)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/host_policies/channels",
@template_request(method="GET", route="/host_policies/channels",
request_params=[OFFSET, LIMIT, SEARCH, SORT])
def get_list_configuration_channels(self, offset=None, limit=None, search=None, sort=None, **kwargs):
return self._base_request(**kwargs)
Expand Down Expand Up @@ -1001,3 +1001,43 @@ def get_list_hosts_for_channel(self, channel_id, has_active_threats=None, has_ex
host_set_id=None, limit=None, offset=None, search=None, sort=None,
filter_field=None,**kwargs):
return self._base_request(**kwargs)


class ProcessTracker(_HX):

API_BASE_ROUTE = "/hx/api/plugins"

def __init__(self, hx_host, hx_port=None, verify=False, authenticator=None, username="", password=""):
_HX.__init__(self, hx_host, hx_port=hx_port, verify=verify)
if isinstance(authenticator, Authentication):
self.AUTHENTICATION = authenticator
elif username and password:
self.AUTHENTICATION = Authentication(hx_host=hx_host, hx_port=hx_port, verify=verify, username=username, password=password)

@expected_response(expected_status_code=200, expected_format=JSON)
@template_request(method="GET", route="/process-tracker/v1/events",
request_params=[OFFSET, LIMIT, SORT, FILTER_FIELD])
def get_processtracker_events(self, offset=None, limit=None, sort=None, filter_field=None, **kwargs):
return self._base_request(**kwargs)


class MessageBus(_HX):

API_BASE_ROUTE = "/hx/api/services/topic"

def __init__(self, hx_host, hx_port=None, verify=False, authenticator=None, username="", password=""):
_HX.__init__(self, hx_host, hx_port=hx_port, verify=verify)
if isinstance(authenticator, Authentication):
self.AUTHENTICATION = authenticator
elif username and password:
self.AUTHENTICATION = Authentication(hx_host=hx_host, hx_port=hx_port, verify=verify, username=username, password=password)

@expected_response(expected_status_code=[200, 204], expected_format=JSON)
@template_request(method="GET", route="/PROCESS_TRACKER", request_headers=[X_OFFSET, X_MAX_MESSAGES, X_POLL_TIMEOUT])
def get_process_events(self, x_offset=None, x_max_messages=None, x_poll_timeout="45", **kwargs):
return self._base_request(**kwargs)

@expected_response(expected_status_code=[200, 204], expected_format=JSON)
@template_request(method="GET", route="/HX_ALERTS", request_headers=[X_OFFSET, X_MAX_MESSAGES, X_POLL_TIMEOUT])
def get_process_alerts(self, x_offset=None, x_max_messages=None, x_poll_timeout="45", **kwargs):
return self._base_request(**kwargs)
11 changes: 10 additions & 1 deletion pyFireEye/utilities/params_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,21 @@
# Containment
STATE_UPDATE_TIME = "state_update_time"

# Headers
X_OFFSET = "X-OFFSET"
X_MAX_MESSAGES = "X-MAX-MESSAGES"
X_POLL_TIMEOUT = "X-POLL-TIMEOUT"

# IF YOU NEED TO USE ONE OF THE PARAMETERS IN THIS DICTIONARY, USE THE REPLACEMENT
param_arg_map = {
# Params
"host_set_id": HOSTS_SET_ID,
"category_share_mode": CATEGORY_SHARE_MODE,
"primary_indicator_id": PRIMARY_INDICATOR_ID
"primary_indicator_id": PRIMARY_INDICATOR_ID,
# Headers
"x_offset": X_OFFSET,
"x_max_messages": X_MAX_MESSAGES,
"x_poll_timeout": X_POLL_TIMEOUT
}

# AX/CM Params
Expand Down
4 changes: 3 additions & 1 deletion pyFireEye/utilities/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,11 @@ def _json_response(response):
data = content.get("data") if isinstance(content, dict) else None
if data:
del content["data"]
entries = data.get("entries") if data else None
entries = data.get("entries") if isinstance(data, dict) else None
if entries:
del data["entries"]
if not entries and isinstance(data, list):
entries = data

return JsonResponse(message=message, status=status, headers=headers, content=content, data=data, entries=entries)

Expand Down
Loading