def on_request(self, request): """ Invoked when a request message is received. :param request: The request message """ # Handle request logger.info("Request received on topic: '{0}' with payload: '{1}'".format( request.destination_topic, MessageUtils.decode_payload(request))) try: # Parameters nmap_params = MessageUtils.json_payload_to_dict(request) logger.info("[1/2] Requested NMAP action ({0}) for request {1} is under processing...".format( MessageUtils.decode_payload(request), request.message_id)) # Create a report from Nmap tool execution nmap_response = self._do_nmap_scan(nmap_params, request) logger.info("[2/2] Requested NMAP action was processed successfully for request {0}." " Preparing response...".format(request.message_id)) # Create response res = Response(request) # Set payload MessageUtils.dict_to_json_payload(res, nmap_response) # Send response self._app.client.send_response(res) logger.info("Sending response for request {0}".format(request.message_id)) except Exception as ex: logger.exception("Error handling request") err_res = ErrorResponse(request, MessageUtils.encode(str(ex))) self._app.client.send_response(err_res)
def _invoke_service(self, request_method, request_dict): """ Invokes a request method on the MISP DXL service. :param str request_method: The request method to append to the topic for the request. :param dict request_dict: Dictionary containing request information. :return: Results of the service invocation. :rtype: dict """ if self._misp_service_unique_id: request_service_id = "/{}".format(self._misp_service_unique_id) else: request_service_id = "" # Create the DXL request message. request = Request("{}{}/{}".format(self._SERVICE_TYPE, request_service_id, request_method)) # Set the payload on the request message (Python dictionary to JSON # payload). MessageUtils.dict_to_json_payload(request, request_dict) # Perform a synchronous DXL request. response = self._dxl_sync_request(request) # Convert the JSON payload in the DXL response message to a Python # dictionary and return it. return MessageUtils.json_payload_to_dict(response)
def test_callback_parkeddomain(self): with BaseClientTest.create_client(max_retries=0) as dxl_client: dxl_client.connect() with MockServerRunner() as server_runner: with ApiVoidService(TEST_FOLDER) as apivoid_service: apivoid_service._dxl_client = dxl_client apivoid_service.API_VOID_URL_FORMAT = "http://127.0.0.1:" \ + str(server_runner.mock_server_port) \ + "/{0}/v1/pay-as-you-go/{1}" apivoid_service.run() request_topic = ApiVoidService.REQ_TOPIC_PARKED_DOMAIN req = Request(request_topic) MessageUtils.dict_to_json_payload( req, {ApiVoidCallback.PARAM_HOST: SAMPLE_HOST}) res = apivoid_service._dxl_client.sync_request(req, timeout=30) res_dict = MessageUtils.json_payload_to_dict(res) self.assertDictEqual(SAMPLE_PARKED_DOMAIN, res_dict)
def retrieve_policy_by_name(self, policy_name): """ Retrieve information for an ANC policy by name. :param str policy_name: Name of the policy. :return: The policy information, which should look similar to this: .. code-block:: json { "ancStatus": "success", "ancpolicy": [ { "action": [ "Quarantine" ], "name": "quarantine_policy" } ] } :rtype: dict :raises Exception: If the policy name has not been defined. """ request = Request(self._ANC_RETRIEVE_POLICY_BY_NAME) MessageUtils.dict_to_json_payload(request, {_PARAM_POLICY_NAME: policy_name}) return MessageUtils.json_payload_to_dict( self._pxgrid_client._dxl_sync_request(request))
def get_endpoint_by_mac(self, mac_address): """ Get information for an endpoint by its MAC address. :param str mac_address: MAC address of the endpoint. :return: The endpoint information, which should look similar to this: .. code-block:: json { "ancEndpoint": [ { "macAddress": "00:11:22:33:44:55", "policyName": "quarantine_policy" } ], "ancStatus": "success" } :rtype: dict :raises Exception: If no policy has been associated with the endpoint. """ request = Request(self._ANC_GET_ENDPOINT_BY_MAC) MessageUtils.dict_to_json_payload(request, {_PARAM_MAC: mac_address}) return MessageUtils.json_payload_to_dict( self._pxgrid_client._dxl_sync_request(request))
def send_mitigation_action_by_mac(self, mac_address, action): """ Send an EPS mitigation action for an endpoint MAC address. :param str mac_address: MAC address of the endpoint for which the action should be performed. :param dxlciscopxgridclient.constants.EpsAction action: The action to perform. :return: Results of the attempt to send the action. The results for a successful send should look similar to this: .. code-block:: json { "gid": "150", "macInterface": "00:11:22:33:44:55", "mitigationStatus": "complete" } :rtype: dict """ request = Request(self._EPS_SEND_MITIGATION_ACTION_BY_MAC) MessageUtils.dict_to_json_payload(request, { _PARAM_MAC: mac_address, _PARAM_ACTION: action }) return MessageUtils.json_payload_to_dict( self._pxgrid_client._dxl_sync_request(request))
def retrieve_all_policies(self): """ Retrieve information for all ANC policies. :return: The policy information, which should look similar to this: .. code-block:: json { "ancStatus": "success", "ancpolicy": [ { "action": [ "ShutDown" ], "name": "shutdown_policy" }, { "action": [ "Quarantine" ], "name": "quarantine_policy" } ] } :rtype: dict """ request = Request(self._ANC_RETRIEVE_ALL_POLICIES) return MessageUtils.json_payload_to_dict( self._pxgrid_client._dxl_sync_request(request))
def send_mitigation_action_by_ip(self, ip_address, action): """ Send an EPS mitigation action for an endpoint IP address. :param str ip_address: IP address of the endpoint for which the action should be performed. :param dxlciscopxgridclient.constants.EpsAction action: The action to perform. :return: Results of the attempt to send the action. The results for a successful send should look similar to this: .. code-block:: json { "gid": "150", "macInterface": "00:11:22:33:44:55", "mitigationStatus": "complete" } :rtype: dict :raises Exception: If no session has been established for an endpoint which corresponds to the IP address. """ request = Request(self._EPS_SEND_MITIGATION_ACTION_BY_IP) MessageUtils.dict_to_json_payload(request, { _PARAM_IP: ip_address, _PARAM_ACTION: action, "foo": "bar" }) return MessageUtils.json_payload_to_dict( self._pxgrid_client._dxl_sync_request(request))
def on_request(self, request): """ Callback invoked when a request is received. :param dxlclient.message.Request request: The request """ logger.info("Request received on topic '%s'", request.destination_topic) logger.debug("Payload for topic %s: %s", request.destination_topic, request.payload) try: request_dict = MessageUtils.json_payload_to_dict(request) \ if request.payload else {} if "event" in request_dict and \ type(request_dict["event"]).__name__ in ("str", "unicode") and \ request_dict["event"].isdigit(): request_dict["event"] = int(request_dict["event"]) response_data = self._api_method(**request_dict) if isinstance(response_data, dict) and \ response_data.get("errors", None): res = ErrorResponse(request, error_message=str( response_data["errors"][0])) else: res = Response(request) MessageUtils.dict_to_json_payload(res, response_data) except Exception as ex: error_str = str(ex) logger.exception("Error handling request: %s", error_str) res = ErrorResponse(request, error_message=MessageUtils.encode(error_str)) self._app.client.send_response(res)
def on_request(self, request): """ Invoked when a request message is received. :param request: The request message """ # Handle request logger.info("Request received on topic: '%s' with payload: '%s'", request.destination_topic, MessageUtils.decode_payload(request)) try: res = Response(request) request_dict = MessageUtils.json_payload_to_dict(request) \ if request.payload else {} # Ensure required parameters are present if self._required_params: for name in self._required_params: if name not in request_dict: raise Exception( "Required parameter not found: '{}'".format(name)) if "format" not in request_dict: request_dict["format"] = "json" elif request_dict["format"] not in ("json", "xml"): raise Exception( "Unsupported format requested: '{}'. {}".format( request_dict["format"], "Only 'json' and 'xml' are supported.")) # Invoke DomainTools API via client dt_response = \ getattr(self._app.domaintools_api, self._func_name)(**request_dict) # Set response payload response_data = dt_response.data() if isinstance(response_data, dict): MessageUtils.dict_to_json_payload(res, response_data) else: MessageUtils.encode_payload(res, response_data) except ServiceException as ex: logger.exception("Error handling request") msg = "%s: %s" % (ex.__class__.__name__, ex.reason) res = ErrorResponse(request, error_message=MessageUtils.encode(msg)) except Exception as ex: logger.exception("Error handling request") msg = str(ex) if not msg: msg = ex.__class__.__name__ res = ErrorResponse(request, error_message=MessageUtils.encode(msg)) # Send response self._app.client.send_response(res)
def on_request(self, request): """ Invoked when a request message is received. :param request: The request message """ request_payload = MessageUtils.json_payload_to_dict(request) try: res = Response(request) payload = {"code": 200, "body": {}} if request_payload["target"] == "/v1/simple": payload = self.v1_simple(request_payload, payload) else: payload = self.v1_complex(request_payload, payload) MessageUtils.dict_to_json_payload(res, payload) self._client.send_response(res) except Exception as ex: err_res = ErrorResponse(request, error_code=0, error_message=MessageUtils.encode(str(ex))) self._client.send_response(err_res)
def apply_endpoint_policy_by_mac(self, mac_address, policy_name): """ Apply a policy to an endpoint by its MAC address. :param str mac_address: MAC address of the endpoint. :param str policy_name: Name of the policy to apply. :return: Results of the attempt to apply the endpoint policy. On a successful application, the results should look similar to this: .. code-block:: json { "ancStatus": "success" } :rtype: dict :raises Exception: If the supplied policy name has already been applied to the endpoint or the policy name has not been defined. """ request = Request(self._ANC_APPLY_ENDPOINT_POLICY_BY_MAC) MessageUtils.dict_to_json_payload(request, { _PARAM_MAC: mac_address, _PARAM_POLICY_NAME: policy_name }) return MessageUtils.json_payload_to_dict( self._pxgrid_client._dxl_sync_request(request))
def get_broker_registry(self, client_id=None): """ Queries the broker for the broker registry and replaces the currently stored one with the new results. Notifies all connected web sockets that new broker information is available. """ req = Request(TopologyModule.BROKER_REGISTRY_QUERY_TOPIC) if client_id: req.payload = "{ \"brokerGuid\":\"" + client_id + "\"}" else: req.payload = "{}" # Send the request dxl_response = self.app.dxl_service_client.sync_request(req, 5) if dxl_response.message_type == Message.MESSAGE_TYPE_ERROR: logger.error( "Error response returned from the broker registry: %s", dxl_response.error_message) return {} dxl_response_dict = MessageUtils.json_payload_to_dict(dxl_response) logger.info("Broker registry response: %s", dxl_response_dict) brokers = {} for broker_guid in dxl_response_dict["brokers"]: brokers[broker_guid] = dxl_response_dict["brokers"][broker_guid] self.current_connected_broker = dxl_response.source_broker_id return brokers
def send_command(self, topic, command): """ Sends an Open Command and Control (OpenC2) message to the specified DXL service and returns the response. The `Lycan library <https://github.com/oasis-open/openc2-lycan-python>`_ contains the OpenC2 classes (Command, Response, etc.). :param topic: The DXL service topic to send the OpenC2 command to :param command: The `openc2.v10.Command` to send to the DXL service :return: The `openc2.v10.Response` received from the DXL service """ # Create the DXL request message request = Request(topic) # Serialize and encode the OpenC2 command request.payload = command.serialize().encode() # Perform a synchronous DXL request response = self._dxl_sync_request(request) if response.message_type == Message.MESSAGE_TYPE_ERROR: raise Exception(response.error_message) # Return the response response_dict = MessageUtils.json_payload_to_dict(response) return openc2.v10.Response(**response_dict)
def on_request(self, request): """ Invoked when a request message is received. :param request: The request message """ # Handle request request_payload = MessageUtils.json_payload_to_dict(request) logger.info( "Request received on topic: '{0}' with payload: '{1}'".format( request.destination_topic, request_payload)) if request.destination_topic in self._callbacks: try: self._callbacks[request.destination_topic](request, request_payload) except Exception as ex: logger.exception("Error handling request") err_res = ErrorResponse(request, error_code=0, error_message=MessageUtils.encode( str(ex))) self._app.client.send_response(err_res) else: logger.exception("Unknown topic: %s", request.destination_topic) err_res = ErrorResponse( request, error_code=0, error_message=MessageUtils.encode( "Unknown topic: " + request.destination_topic)) self._app.client.send_response(err_res)
def on_request(self, request): """ Invoked when a request message is received. :param request: The request message """ # Handle request request_payload = MessageUtils.json_payload_to_dict(request) if request.destination_topic in self._callbacks: try: self._callbacks[request.destination_topic](request, request_payload) except Exception as ex: err_res = ErrorResponse(request, error_code=0, error_message=MessageUtils.encode( str(ex))) self._client.send_response(err_res) else: err_res = ErrorResponse( request, error_code=0, error_message=MessageUtils.encode("Unknown topic: " + request.destination_topic)) self._client.send_response(err_res)
def _pop_attribute_from_request(self, request, attr_name): """ Pop the value for a named attribute from the supplied request JSON and return the remaining request body. :param request: :param attr_name: :return: :raises ValueError: if the named attribute does not appear in the request. :return: A tuple containing two elements: the value associated with the attr_name parameter and the request body (minus the attr_name attribute and its associated value), converted to a dict. :rtype: tuple """ try: request_body = MessageUtils.json_payload_to_dict(request) attr_value = request_body.pop(attr_name, None) if not attr_name: raise ValueError("Attribute {} is missing".format(attr_name)) except Exception as ex: request_body = {} attr_value = None error_str = str(ex) logger.exception("Error handling request: %s", error_str) res = ErrorResponse(request, error_message=MessageUtils.encode(error_str)) self._dxl_client.send_response(res) return (attr_value, request_body)
def on_request(self, request): """ Invoked when a request message is received. :param request: The request message """ # Handle request logger.info( "Request received on topic: '{0}' with payload: '{1}'".format( request.destination_topic, MessageUtils.decode_payload(request))) try: # Create response res = Response(request) # Read DXL request payload into dictionary params = MessageUtils.json_payload_to_dict(request) # Invoke API call if 'data' in params.keys() and params['data'] != "": tide_data = params['data'] if 'type' in params.keys(): tide_type = params['type'] else: tide_type = get_ioc_type(tide_data, 'tide') if 'rlimit' in params.keys( ) and params['max_rec'] < self._app.tide_max_rec: tide_max_rec = params['max_rec'] else: tide_max_rec = self._app.tide_max_rec #Error data is required if 'format' in params.keys(): tide_format = params['format'] else: tide_format = self._app.tide_format #Error data is required http_res = requests.get( "https://api.activetrust.net:8000/api/data/threats", params={ "type": tide_type, tide_type: tide_data, "data_format": tide_format, "rlimit": tide_max_rec }, auth=(self._app.api_key, '')) content = unicode(http_res.content, "utf-8") else: content = "{'status':'error','errorMessage':'The data field is required'}" #Error data is required # Add web service response to DXL response payload MessageUtils.encode_payload(res, content, enc='utf-8') # Send response self._app.client.send_response(res) except Exception as ex: logger.exception("Error handling request") err_res = ErrorResponse(request, error_code=0, error_message=MessageUtils.encode(str(ex))) self._app.client.send_response(err_res)
def _invoke_service(self, request_method, request_dict): """ Invokes a request method on the Elasticsearch DXL service. :param str request_method: The request method to append to the topic for the request. :param dict request_dict: Dictionary containing request information. :return: Results of the service invocation. :rtype: dict """ if self._elasticsearch_service_unique_id: request_service_id = "/{}".format( self._elasticsearch_service_unique_id) else: request_service_id = "" # Create the DXL request message. request = Request("{}{}/{}".format( self._SERVICE_TYPE, request_service_id, request_method)) # Set the payload on the request message (Python dictionary to JSON # payload). MessageUtils.dict_to_json_payload(request, request_dict) # Perform a synchronous DXL request. response = self._dxl_client.sync_request(request, timeout=self.response_timeout) if response.message_type == Message.MESSAGE_TYPE_ERROR: try: self._raise_exception_for_error_response( MessageUtils.json_payload_to_dict(response)) except ValueError: # If an appropriate exception cannot be constructed from the # error response data, raise a more generic Exception as a # fallback. raise Exception("Error: {} ({})".format( response.error_message, str(response.error_code))) # Convert the JSON payload in the DXL response message to a Python # dictionary and return it. return MessageUtils.json_payload_to_dict(response)
def on_event(self, event): """ Method invoked when an create policy event occurs. Derived classes can implement the :meth:`on_create_policy` method instead of this method in order to process the event payload as a `dict`. :param dxlclient.message.Event event: Message received for the event notification """ self.on_create_policy(MessageUtils.json_payload_to_dict(event))
def on_request(self, request): """ Invoked when a request message is received. :param request: The request message """ # Handle request logger.info("Request received on topic: '%s' with payload: '%s'", request.destination_topic, MessageUtils.decode_payload(request)) try: # API URL api_url = self._app.URL_VOID_API_URL_FORMAT.format(self._app.api_key) command = request.destination_topic[self._app.SERVICE_TYPE_LENGTH + 1:] params = {} if request.payload: params = MessageUtils.json_payload_to_dict(request) if self._required_params: self._validate(params) if command == self._app.CMD_HOST_INFO: host = params[self.PARAM_HOST] api_url = "{0}{1}/{2}".format(api_url, self.PARAM_HOST, host) elif command == self._app.CMD_HOST_RESCAN: host = params[self.PARAM_HOST] api_url = "{0}{1}/{2}/rescan".format(api_url, self.PARAM_HOST, host) elif command == self._app.CMD_HOST_SCAN: host = params[self.PARAM_HOST] api_url = "{0}{1}/{2}/scan".format(api_url, self.PARAM_HOST, host) elif command == self._app.CMD_STATS_REMAINED: api_url = "{0}{1}".format(api_url, command) # Invoke URLVoid API url_void_api_response = requests.get(api_url) # Check HTTP response code url_void_api_response.raise_for_status() # Create response res = Response(request) # Set payload MessageUtils.encode_payload(res, url_void_api_response.text) # Send response self._app.client.send_response(res) except Exception as ex: logger.exception("Error handling request") err_res = ErrorResponse(request, error_message=MessageUtils.encode(str(ex))) self._app.client.send_response(err_res)
def on_event(self, event): event_payload_dict = MessageUtils.json_payload_to_dict(event) # Check the event payload to see if the OpenDXL event was triggered # by the new MISP event that the sample created. if "Event" in event_payload_dict and \ "info" in event_payload_dict["Event"] and \ event_payload_dict["Event"]["info"] == \ "OpenDXL MISP event notification example": # Print the payload for the received event print("Received event:\n{}".format( MessageUtils.dict_to_json(event_payload_dict, pretty_print=True)))
def on_event(self, event): """ Notifies all clients that there are changes to the broker registry :param event: the incoming event """ fabric_change_event = MessageUtils.json_payload_to_dict(event) logger.info("Received broker fabric change event: %s", fabric_change_event) # Spawn a new thread to update the broker list as it requires a DXL request thread = Thread(target=self._module.update_broker_registry) thread.start()
def on_event(self, event): """ Invoked when an event is received. :param event: The event """ # Handle event event_payload = MessageUtils.json_payload_to_dict(event) if event.destination_topic in self._callbacks: self._callbacks[event.destination_topic](event_payload) else: raise NotImplementedError( MessageUtils.encode("Unknown topic: " + event.destination_topic))
def on_event(self, event): """ Notifies all clients that there are changes to the service registry :param event: the incoming event """ service_event = MessageUtils.json_payload_to_dict(event) logger.info("Received service registry event: %s", service_event) if event.destination_topic == MonitorModule.SERVICE_REGISTRY_REGISTER_EVENT_TOPIC: self._module.update_service(service_event) elif event.destination_topic == MonitorModule.SERVICE_REGISTRY_UNREGISTER_EVENT_TOPIC: self._module.remove_service(service_event) self._module.notify_web_sockets()
def _invoke_mar_search_api(self, payload_dict): """ Executes a query against the MAR search API :param payload_dict: The payload :return: A dictionary containing the results of the query """ # Create the request message req = Request(MAR_SEARCH_TOPIC) # Set the payload req.payload = json.dumps(payload_dict).encode(encoding="UTF-8") # Display the request that is going to be sent logger.debug( "Request:\n%s", json.dumps(payload_dict, sort_keys=True, indent=4, separators=(',', ': '))) # Send the request and wait for a response (synchronous) res = self._dxl_sync_request(req) # Return a dictionary corresponding to the response payload resp_dict = MessageUtils.json_payload_to_dict(res) # Display the response logger.debug( "Response:\n%s", json.dumps(resp_dict, sort_keys=True, indent=4, separators=(',', ': '))) if "code" in resp_dict: code = resp_dict['code'] if code < 200 or code >= 300: if "body" in resp_dict and "applicationErrorList" in \ resp_dict["body"]: error = resp_dict["body"]["applicationErrorList"][0] raise Exception(error["message"] + ": " + str(error["code"])) elif "body" in resp_dict: raise Exception(resp_dict["body"] + ": " + str(code)) else: raise Exception("Error: Received failure response code: " + str(code)) else: raise Exception("Error: unable to find response code") return resp_dict
def test_callback_urlscan(self): with MockServerRunner() as server_runner, \ VirusTotalApiService(TEST_FOLDER) as vt_service: vt_service.VTAPI_URL_FORMAT = "http://127.0.0.1:" \ + str(server_runner.mock_server_port) \ + "/vtapi/v2{0}" vt_service.run() request_topic = VirusTotalApiService.REQ_TOPIC_URL_SCAN req = Request(request_topic) MessageUtils.dict_to_json_payload( req, {VirusTotalApiRequestCallback.PARAM_URL: SAMPLE_URL}) res = vt_service._dxl_client.sync_request(req, timeout=30) res_dict = MessageUtils.json_payload_to_dict(res) self.assertDictEqual(SAMPLE_URL_SCAN, res_dict)
def on_request(self, request): """ Invoked when a request message is received. :param request: The request message """ # Handle request logger.info("Request received on topic: '%s' with payload: '%s'", request.destination_topic, MessageUtils.decode_payload(request)) try: # Retrieve the parameters from the request params = MessageUtils.json_payload_to_dict(request) if self._PARAM_HOST not in params: raise Exception( "Required parameter not specified: '{0}'".format( self._PARAM_HOST)) target = params[self._PARAM_HOST] ipaddr = socket.gethostbyname(target) logger.debug("IP to be located: %s", ipaddr) # Lookup the IP/host results = self._app.database.lookup_ip(ipaddr) logger.debug(results) # Create response res = Response(request) # Set payload MessageUtils.dict_to_json_payload(res, results) # Send response self._app.client.send_response(res) except Exception as ex: logger.exception("Error handling request") err_res = ErrorResponse(request, error_code=0, error_message=MessageUtils.encode(str(ex))) self._app.client.send_response(err_res)
def lookup_host(self, host): """ Looks up Geolocation information for the specified host/IP :param host: The host/IP to lookup :return: A dictionary (``dict``) containing the details of the Geolocation lookup """ # Create the DXL request message request = Request(self.HOST_LOOKUP_TOPIC) # Set the payload on the request message (Python dictionary to JSON payload) MessageUtils.dict_to_json_payload(request, {self._PARAM_HOST: host}) # Perform a synchronous DXL request response = self._dxl_sync_request(request) # Convert the JSON payload in the DXL response message to a Python dictionary # and return it. return MessageUtils.json_payload_to_dict(response)
def _get_index_parameters(self, event): """ Get parameters for an Elasticsearch 'index' operation for use in storing the payload from the supplied event to Elasticsearch. :param dxlclient.message.Event event: The event. :return: A dictionary with parameters for the 'index' operation. If the event payload cannot be converted into a JSON document or if the ID cannot be derived from the document, None is returned. :rtype: dict """ body = None document_id = None try: body = MessageUtils.json_payload_to_dict(event) if self._id_field_name: document_id = body.get(self._id_field_name) if not document_id: logger.error("%s from %s field in event, %s: %s", "Unable to obtain id", self._id_field_name, "skipping indexing for topic", event.destination_topic) except ValueError: if self._transform_script: body = event.payload else: logger.error("%s, skipping indexing for topic: %s", "Unable to parse event payload as JSON", event.destination_topic) if body: index_parameters = { "index": self._document_index, "doc_type": self._document_type, "body": body, "id": document_id } else: index_parameters = None return index_parameters