PicusNG

Picus - The Complete Security Control Validation NG Platform.

Network Security · PicusNGAutomation

Details

IDPicusNG
ProviderPicus Security
CategoryNetwork Security
From Version6.5.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

This integration was integrated and tested with Picus NG

Configure PicusNG in Cortex

image

Parameter Required
Picus Manager URL True
Trust any certificate (not secure) False
Use system proxy settings False
Picus Refresh Token True

image

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

picus-get-access-token


OAuth2 protocol is used to authorize Refresh/Access tokens. To obtain the API, you should generate a refresh token and then request an access token using this command.

Base Command

picus-get-access-token

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

picus-get-agent-list


This command returns the simulation agent list with agent name, id, status, agent type and platform information as in Picus platform > Agents.

Base Command

picus-get-agent-list

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

picus-get-agent-detail


This command returns agent name, status, agent type, platform and also mitigation devices and attack modules using the agent ID.

Base Command

picus-get-agent-detail

Input

Argument Name Description Required
id Agent id. Required

Context Output

There is no context output for this command.

picus-create-simulation


This command creates and runs a simulation as requested; scheduled or instant.
You can create a simulation by giving your simulation a name and adding the template ID, agent ID.

Base Command

picus-create-simulation

Input

Argument Name Description Required
agent_id ID of the agent. Required
description Description of the simulation. Default is this simulation created with cortex app. Optional
name Name of the simulation. Required
schedule_now Schedule now state. Possible values are: True, False. Required
template_id Id of the template. Required

Context Output

There is no context output for this command.

picus-get-template-list


This command returns template list that contains template name, id, description, content type, category as in Picus Platform> Threat Templates.

Base Command

picus-get-template-list

Input

Argument Name Description Required
offset Pagination value. Optional
limit Pagination value. Optional

Context Output

There is no context output for this command.

picus-get-integration-agent-list


This command returns the agent name, id, status, agent installation information and the token expiration information.

Base Command

picus-get-integration-agent-list

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

picus-get-simulation-list


This command returns the list of you latest simulation result overview as in Picus Platform > Simulations tab. Latest simulation run results are included.

Base Command

picus-get-simulation-list

Input

Argument Name Description Required
offset Pagination value. Optional
limit Pagination value. Optional

Context Output

Path Type Description
Picus.simulationlist.simulation_id Number ID of the simulation

picus-simulate-now


This command runs the existing simulation using the Simulation ID.

Base Command

picus-simulate-now

Input

Argument Name Description Required
id Simulation ID. Required

Context Output

There is no context output for this command.

picus-get-simulation-detail


This command returns the information about a specific simulation.

Base Command

picus-get-simulation-detail

Input

Argument Name Description Required
id Simulation ID. Required

Context Output

There is no context output for this command.

picus-get-latest-simulation-result


This command returns detailed information about simulation results including Prevention and Detection result details for the latest simulation run.

Base Command

picus-get-latest-simulation-result

Input

Argument Name Description Required
id Simulation ID. Required

Context Output

Path Type Description
Picus.latestSimulationResult.simulation_run_id String Simulation Run ID
Picus.latestSimulationResult.simulation_id String Simulation ID
Picus.latestSimulationResult.status String Simulation Status

picus-get-simulation-result


This command returns detailed information about simulation results including Prevention and Detection result details for a specific simulation run with run id.

Base Command

picus-get-simulation-result

Input

Argument Name Description Required
id Simulation ID. Required
run_id Simulation Run ID. Required

Context Output

There is no context output for this command.

picus-get-simulation-threats


This command returns simulation result based on threats. Threat result includes threat id and name, prevention result and action count.

Base Command

picus-get-simulation-threats

Input

Argument Name Description Required
id Simulation ID. Required
run_id Simulation Run ID. Required
limit Pagination value. Optional
offset Pagination value. Optional

Context Output

Path Type Description
Picus.SimulationThreats String Threat list of simulation

picus-get-simulation-actions


This command returns simulation result based on action list using threat ID, simulation ID and simulation run ID.

Base Command

picus-get-simulation-actions

Input

Argument Name Description Required
id Simulation ID. Required
run_id Simulation Run ID. Required
threat_ids Threat ID list (“111,222,333,…”) or single threat ID can be given. Required
limit Pagination value. Optional
offset Pagination value. Optional

Context Output

Path Type Description
Picus.SimulationActions String Action Results(ID and result combination)

picus-get-mitigation-devices


This command returns mitigation device information as obtained under Picus platform > Mitigation > Vendor Based Mitigations. Vendor based mitigation devices can be fetched using this command.

Base Command

picus-get-mitigation-devices

Input

Argument Name Description Required
ids Simulation ID list (“111,222,333,…”) or single simulation ID can be given. Optional

Context Output

There is no context output for this command.

picus-get-signature-list


This command returns action based signature suggestions.

Base Command

picus-get-signature-list

Input

Argument Name Description Required
device_id Mitigation Device ID. Required
action_ids Action ID list (“111,222,333,…”) or single action ID can be given. Required

Context Output

There is no context output for this command.

picus-set-paramPB


Set parameter on playbook. (This command is only used on playbook)

Base Command

picus-set-paramPB

Input

Argument Name Description Required
agent_id Simulation Agent ID. Required
device_id Device ID . Optional
simulation_id Simulation ID. Required

Context Output

Path Type Description
Picus.param.agent_id String Agent ID
Picus.param.device_id String Device ID
Picus.param.simulation_id String Simulation ID

picus-filter-insecure-attacks


Filter insecure attacks on playbook. (This command is only used on playbook)

Base Command

picus-filter-insecure-attacks

Input

Argument Name Description Required
threatinfo Threat id and result combine. Used for playbook. Required

Context Output

Path Type Description
Picus.filterinsecure String Insecure Attack List

Configuration parameters

  • picus_server — Picus Manager URL (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • picus_apikey — Picus Refresh Token (required)

Commands (16)

  • picus-create-simulation

    This command creates and runs a simulation as requested; scheduled or instant. You can create a simulation by giving your simulation a name and adding the template ID, agent ID.

  • picus-filter-insecure-attacks

    Filter insecure attacks on playbook. (This command is only used on playbook).

  • picus-get-agent-detail

    This command returns agent name, status, agent type, platform and also mitigation devices and attack modules using the agent ID.

  • picus-get-agent-list

    This command returns the simulation agent list with agent name, id, status, agent type and platform information as in Picus platform > Agents.

  • picus-get-integration-agent-list

    This command returns the agent name, id, status, agent installation information and the token expiration information.

  • picus-get-latest-simulation-result

    This command returns detailed information about simulation results including Prevention and Detection result details for the latest simulation run.

  • picus-get-mitigation-devices

    This command returns mitigation device information as obtained under Picus platform > Mitigation > Vendor Based Mitigations. Vendor based mitigation devices can be fetched using this command.

  • picus-get-signature-list

    This command returns action based signature suggestions.

  • picus-get-simulation-actions

    This command returns simulation result based on action list using threat ID, simulation ID and simulation run ID.

  • picus-get-simulation-detail

    This command returns the information about a specific simulation.

  • picus-get-simulation-list

    This command returns the list of you latest simulation result overview as in Picus Platform > Simulations tab. Latest simulation run results are included.

  • picus-get-simulation-result

    This command returns detailed information about simulation results including Prevention and Detection result details for a specific simulation run with run id.

  • picus-get-simulation-threats

    This command returns simulation result based on threats. Threat result includes threat id and name, prevention result and action count.

  • picus-get-template-list

    This command returns template list that contains template name, id, description, content type, category as in Picus Platform> Threat Templates.

  • picus-set-paramPB

    Set parameter on playbook. (This command is only used on playbook).

  • picus-simulate-now

    This command runs the existing simulation using the Simulation ID.

import demistomock as demisto
from CommonServerPython import *  # noqa # pylint: disable=unused-wildcard-import
from CommonServerUserPython import *  # noqa

import urllib3
from typing import Any

# Disable insecure warnings
urllib3.disable_warnings()


""" CONSTANTS """
DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"  # ISO8601 format with UTC, default in XSOAR


""" CLIENT CLASS """


class Client(BaseClient):
    def __init__(self, api_key: str, base_url: str, proxy: bool, verify: bool):
        super().__init__(base_url=base_url, proxy=proxy, verify=verify)
        self.api_key = api_key

        if self.api_key:
            self._headers = {"Content-Type": "application/json"}

        self.access_token = self.get_access_token()
        picus_headers = {"Content-Type": "application/json", "Authorization": ""}
        picus_headers["Authorization"] = "Bearer " + self.access_token
        self._headers = picus_headers

    def get_access_token(self):
        picus_token_data = {"refresh_token": ""}
        picus_token_data["refresh_token"] = str(self.api_key)

        picus_auth_endpoint = "/v1/auth/token"
        picus_auth_response = self.http_request(method="POST", json_data=picus_token_data, url_suffix=picus_auth_endpoint)
        picus_accessToken = picus_auth_response["token"]

        return picus_accessToken

    def http_request(
        self,
        method: str,
        url_suffix: str,
        params: Optional[dict] = None,
        json_data: Optional[dict] = None,
        additional_headers: Optional[dict] = None,
        timeout: Optional[int] = None,
        data: Optional[dict] = None,
    ):
        headers = {**additional_headers, **self._headers} if additional_headers else self._headers
        return self._http_request(
            method=method,
            url_suffix=url_suffix,
            params=params,
            json_data=json_data,
            data=data,
            headers=headers,
            timeout=timeout,
            ok_codes=(200,),
        )

    def get_agent_list(self):
        return self.http_request(method="GET", url_suffix="/v1/agents")

    def get_agent_detail(self, agent_id: str):
        return self.http_request(method="GET", url_suffix="/v1/agents/" + agent_id)

    def get_integration_agent_list(self):
        return self.http_request(method="GET", url_suffix="/v1/integrations/agents")

    def get_template_list(self, query_parameters: str):
        return self.http_request(method="GET", url_suffix="/v1/templates" + query_parameters)

    def create_simulation(self, picus_post_data_simulation: dict):
        return self.http_request(method="POST", url_suffix="/v1/simulations", json_data=picus_post_data_simulation)

    def get_simulation_list(self, query_parameters: str):
        return self.http_request(method="GET", url_suffix="/v1/simulations" + query_parameters)

    def simulate_now(self, simulation_id: str):
        return self.http_request(method="POST", url_suffix="/v1/simulations/" + simulation_id + "/simulate-now")

    def get_simulation_detail(self, simulation_id: str):
        return self.http_request(method="GET", url_suffix="/v1/simulations/" + simulation_id)

    def get_latest_simulation_result(self, simulation_id: str):
        return self.http_request(method="GET", url_suffix="/v1/simulations/" + simulation_id + "/run/latest")

    def get_simulation_result(self, simulation_id: str, run_id: str):
        return self.http_request(method="GET", url_suffix="/v1/simulations/" + simulation_id + "/run/" + run_id)

    def get_simulation_threats(self, query_parameters: str, simulation_id: str, run_id: str):
        return self.http_request(
            method="GET", url_suffix="/v1/simulations/" + simulation_id + "/run/" + run_id + "/threats" + query_parameters
        )

    def get_simulation_actions(self, query_parameters: str, simulation_id: str, run_id: str, threat_id: str):
        return self.http_request(
            method="GET",
            url_suffix="/v1/simulations/"
            + simulation_id
            + "/run/"
            + run_id
            + "/threats/"
            + threat_id
            + "/actions"
            + query_parameters,
        )

    def get_mitigation_devices(self, query_parameters: str):
        return self.http_request(method="GET", url_suffix="/v2/mitigation/devices" + query_parameters)

    def get_signature_list(self, query_parameters: str, device_id: str):
        return self.http_request(
            method="GET", url_suffix="/v1/mitigation/devices/" + device_id + "/signatures" + query_parameters
        )


""" COMMAND FUNCTIONS """


def test_module(client: Client) -> str:
    """Tests API connectivity and authentication'

    Returning 'ok' indicates that the integration works like it is supposed to.
    Connection to the service is successful.
    Raises exceptions if something goes wrong.

    :type client: ``Client``
    :param Client: client to use

    :return: 'ok' if test passed, anything else will fail the test.
    :rtype: ``str``
    """

    message: str = ""
    picus_accessToken: str = ""

    try:
        client.get_access_token()
        message = "ok"
    except Exception as e:
        if (
            "Forbidden" in str(e)
            or "Authorization" in str(e)
            or "NewConnectionError" in str(e)
            or "Unauthorized" in str(e)
            or picus_accessToken is None
        ):
            message = "Authorization Error: make sure API Key or Picus URL is correctly set"
        else:
            raise e
    return message


def get_agent_list_command(client: Client) -> CommandResults:
    picus_endpoint_response = client.get_agent_list()
    picus_agents = picus_endpoint_response["agents"]
    for agent in picus_agents:
        agent["created_at"] = str(datetime.fromtimestamp(agent["created_at"] / 1000))

    table_name = "Picus Agent List"
    table_headers = ["id", "name", "status", "type", "version", "created_at", "platform_name", "platform_architecture"]
    md_table = tableToMarkdown(
        table_name, picus_agents, headers=table_headers, removeNull=True, headerTransform=string_to_table_header
    )
    return CommandResults(readable_output=md_table, outputs=picus_agents, outputs_prefix="Picus.agentlist")


def get_agent_detail_command(client: Client) -> CommandResults:
    agent_id = demisto.args().get("id")
    tmp_attack_modules: dict = {}

    picus_endpoint_response = client.get_agent_detail(agent_id)
    picus_agent_detail = picus_endpoint_response
    picus_agent_attack_modules = picus_endpoint_response["attack_modules"]
    picus_agent_detail.pop("attack_modules")

    for modules in picus_agent_attack_modules:
        tmp_attack_modules[modules["name"]] = modules["enabled"]

    picus_agent_detail.update(tmp_attack_modules)

    table_name = "Picus Agent Details"
    table_headers = [
        "id",
        "name",
        "status",
        "File Download",
        "Endpoint Scenario",
        "Web Application",
        "Email",
        "Data Exfiltration",
    ]
    md_table = tableToMarkdown(
        table_name, picus_agent_detail, headers=table_headers, removeNull=True, headerTransform=string_to_table_header
    )
    results = CommandResults(readable_output=md_table, outputs=picus_agent_detail, outputs_prefix="Picus.agentdetail")
    return results


def get_integration_agent_list_command(client: Client) -> CommandResults:
    picus_endpoint_response = client.get_integration_agent_list()
    picus_integration_agents = picus_endpoint_response["integration_agents"]
    for agent in picus_integration_agents:
        agent["created_at"] = str(datetime.fromtimestamp(agent["created_at"] / 1000))
        agent["updated_at"] = str(datetime.fromtimestamp(agent["updated_at"] / 1000))

    table_name = "Picus Integration Agent List"
    table_headers = ["id", "name", "status", "created_at", "updated_at", "installed", "token_expired"]
    md_table = tableToMarkdown(
        table_name, picus_integration_agents, headers=table_headers, removeNull=True, headerTransform=string_to_table_header
    )
    return CommandResults(readable_output=md_table, outputs=picus_integration_agents, outputs_prefix="Picus.integrationagentlist")


def get_template_list_command(client: Client) -> CommandResults:
    query_parameters: str = ""
    offset = demisto.args().get("offset")
    limit = demisto.args().get("limit")

    if offset is not None and limit is None:
        raise DemistoException("limit should be set.")
    elif offset is None and limit is not None:
        raise DemistoException("offset should be set.")

    if offset is not None and limit is not None:
        query_parameters = "?" + "limit=" + limit + "&" + "offset=" + offset

    picus_endpoint_response = client.get_template_list(query_parameters)
    picus_templates = picus_endpoint_response["templates"]

    table_name = "Picus Template List"
    table_headers = ["id", "name", "description", "threat_count", "category_name", "content_type", "agent_types"]
    md_table = tableToMarkdown(
        table_name, picus_templates, headers=table_headers, removeNull=True, headerTransform=string_to_table_header
    )
    return CommandResults(readable_output=md_table, outputs=picus_templates, outputs_prefix="Picus.templatelist")


def create_simulation_command(client: Client) -> CommandResults:
    picus_simulation_creation_results: dict = {}

    agent_id = int(demisto.args().get("agent_id"))
    simulation_description = demisto.args().get("description")
    simulation_name = demisto.args().get("name")
    schedule_now = bool(demisto.args().get("schedule_now"))
    template_id = int(demisto.args().get("template_id"))

    picus_post_data_simulation = {
        "agent_id": agent_id,
        "description": simulation_description,
        "name": simulation_name,
        "schedule_now": schedule_now,
        "template_id": template_id,
    }
    picus_endpoint_response = client.create_simulation(picus_post_data_simulation)

    picus_endpoint_response_all = picus_endpoint_response
    picus_created_simulation = picus_endpoint_response["simulation"]
    picus_simulation_run_info = picus_endpoint_response["run_info"]
    picus_simulation_creation_results["simulation_id"] = picus_created_simulation["id"]
    picus_simulation_creation_results["name"] = picus_created_simulation["name"]
    picus_simulation_creation_results["description"] = picus_created_simulation["description"]
    picus_simulation_creation_results["run_immediately"] = picus_endpoint_response_all["run_immediately"]
    picus_simulation_creation_results["simulation_status"] = picus_simulation_run_info["status"]

    table_name = "Picus Simulation Create Status"
    table_headers = ["simulation_id", "name", "description", "run_immediately", "simulation_status"]
    md_table = tableToMarkdown(
        table_name,
        picus_simulation_creation_results,
        headers=table_headers,
        removeNull=True,
        headerTransform=string_to_table_header,
    )
    return CommandResults(
        readable_output=md_table, outputs=picus_simulation_creation_results, outputs_prefix="Picus.createsimulation"
    )


def get_simulation_list_command(client: Client) -> CommandResults:
    query_parameters: str = ""
    offset = demisto.args().get("offset")
    limit = demisto.args().get("limit")

    if offset is not None and limit is None:
        raise DemistoException("limit should be set.")
    elif offset is None and limit is not None:
        raise DemistoException("offset sohuld be set.")

    if offset is not None and limit is not None:
        query_parameters = "?" + "limit=" + limit + "&" + "offset=" + offset

    picus_endpoint_response = client.get_simulation_list(query_parameters)
    picus_simulations = picus_endpoint_response["simulations"]

    table_name = "Picus Simulation List"
    table_headers = [
        "simulation_id",
        "simulation_name",
        "status",
        "has_detection_analysis",
        "has_last_run_detection_analysis",
        "last_detection_security_score",
        "last_prevention_security_score",
        "agent",
    ]
    md_table = tableToMarkdown(
        table_name, picus_simulations, headers=table_headers, removeNull=True, headerTransform=string_to_table_header
    )
    return CommandResults(readable_output=md_table, outputs=picus_simulations, outputs_prefix="Picus.simulationlist")


def simulate_now_command(client: Client) -> CommandResults:
    simulation_id = demisto.args().get("id")

    picus_endpoint_response = client.simulate_now(simulation_id)
    picus_simulateNow = picus_endpoint_response["run_info"]

    table_name = "Picus Simulate Now Status"
    table_headers = ["browser", "id", "status"]
    md_table = tableToMarkdown(
        table_name, picus_simulateNow, headers=table_headers, removeNull=True, headerTransform=string_to_table_header
    )
    return CommandResults(readable_output=md_table, outputs=picus_simulateNow, outputs_prefix="Picus.simulatenow")


def get_simulation_detail_command(client: Client) -> CommandResults:
    simulation_id = demisto.args().get("id")

    picus_endpoint_response = client.get_simulation_detail(simulation_id)
    picus_simulationDetail = picus_endpoint_response["simulation_run"]
    for sRun in picus_simulationDetail:
        sRun["started_at"] = str(datetime.fromtimestamp(sRun["started_at"] / 1000))
        sRun["completed_at"] = str(datetime.fromtimestamp(sRun["completed_at"] / 1000))

    table_name = "Picus Simulation Detail"
    table_headers = ["id", "started_at", "completed_at", "status"]
    md_table = tableToMarkdown(
        table_name, picus_simulationDetail, headers=table_headers, removeNull=True, headerTransform=string_to_table_header
    )
    return CommandResults(readable_output=md_table, outputs=picus_simulationDetail, outputs_prefix="Picus.simulationDetail")


def get_latest_simulation_result_command(client: Client) -> CommandResults:
    simulation_id = demisto.args().get("id")

    picus_latestSimulation = client.get_latest_simulation_result(simulation_id)

    if picus_latestSimulation["status"] == "COMPLETED":
        picus_latestSimulation["started_at"] = str(datetime.fromtimestamp(picus_latestSimulation["started_at"] / 1000))
        picus_latestSimulation["completed_at"] = str(datetime.fromtimestamp(picus_latestSimulation["completed_at"] / 1000))
        picus_latestSimulation["prevention_security_score"] = picus_latestSimulation["results"]["prevention"]["security_score"]
        picus_latestSimulation["prevention_total_threat"] = picus_latestSimulation["results"]["prevention"]["threat"][
            "total_count"
        ]
        picus_latestSimulation["prevention_blocked_threat"] = picus_latestSimulation["results"]["prevention"]["threat"][
            "blocked_count"
        ]
        picus_latestSimulation["prevention_not_blocked_threat"] = picus_latestSimulation["results"]["prevention"]["threat"][
            "not_blocked_count"
        ]
        picus_latestSimulation["prevention_not_tested_threat"] = picus_latestSimulation["results"]["prevention"]["threat"][
            "not_tested_count"
        ]
        picus_latestSimulation["prevention_total_attacker_objectives"] = picus_latestSimulation["results"]["prevention"][
            "attacker_objectives"
        ]["total_count"]
        picus_latestSimulation["prevention_achieved_objectives"] = picus_latestSimulation["results"]["prevention"][
            "attacker_objectives"
        ]["achived_count"]
        picus_latestSimulation["prevention_unachieved_objectives"] = picus_latestSimulation["results"]["prevention"][
            "attacker_objectives"
        ]["unachived_count"]
        picus_latestSimulation["prevention_not_tested_objectives"] = picus_latestSimulation["results"]["prevention"][
            "attacker_objectives"
        ]["not_tested_count"]
        picus_latestSimulation["has_detection_analysis"] = picus_latestSimulation["results"]["has_detection_analysis"]
    else:
        picus_latestSimulation["started_at"] = str(datetime.fromtimestamp(picus_latestSimulation["started_at"] / 1000))

    table_name = "Picus Latest Simulation Result"
    table_headers = [
        "started_at",
        "completed_at",
        "simulation_id",
        "simulation_run_id",
        "template_id",
        "status",
        "prevention_security_score",
        "prevention_total_threat",
        "prevention_blocked_threat",
        "prevention_not_blocked_threat",
        "prevention_not_tested_threat",
        "prevention_total_attacker_objectives",
        "prevention_achieved_objectives",
        "prevention_unachieved_objectives",
        "prevention_not_tested_objectives",
        "has_detection_analysis",
    ]
    md_table = tableToMarkdown(
        table_name, picus_latestSimulation, headers=table_headers, removeNull=True, headerTransform=string_to_table_header
    )
    return CommandResults(readable_output=md_table, outputs=picus_latestSimulation, outputs_prefix="Picus.latestSimulationResult")


def get_simulation_result_command(client: Client) -> CommandResults:
    simulation_id = demisto.args().get("id")
    run_id = demisto.args().get("run_id")

    picus_latestSimulation = client.get_simulation_result(simulation_id, run_id)

    picus_latestSimulation["started_at"] = str(datetime.fromtimestamp(picus_latestSimulation["started_at"] / 1000))
    picus_latestSimulation["completed_at"] = str(datetime.fromtimestamp(picus_latestSimulation["completed_at"] / 1000))
    picus_latestSimulation["prevention_security_score"] = picus_latestSimulation["results"]["prevention"]["security_score"]
    picus_latestSimulation["prevention_total_threat"] = picus_latestSimulation["results"]["prevention"]["threat"]["total_count"]
    picus_latestSimulation["prevention_blocked_threat"] = picus_latestSimulation["results"]["prevention"]["threat"][
        "blocked_count"
    ]
    picus_latestSimulation["prevention_not_blocked_threat"] = picus_latestSimulation["results"]["prevention"]["threat"][
        "not_blocked_count"
    ]
    picus_latestSimulation["prevention_not_tested_threat"] = picus_latestSimulation["results"]["prevention"]["threat"][
        "not_tested_count"
    ]
    picus_latestSimulation["prevention_total_attacker_objectives"] = picus_latestSimulation["results"]["prevention"][
        "attacker_objectives"
    ]["total_count"]
    picus_latestSimulation["prevention_achieved_objectives"] = picus_latestSimulation["results"]["prevention"][
        "attacker_objectives"
    ]["achived_count"]
    picus_latestSimulation["prevention_unachieved_objectives"] = picus_latestSimulation["results"]["prevention"][
        "attacker_objectives"
    ]["unachived_count"]
    picus_latestSimulation["prevention_not_tested_objectives"] = picus_latestSimulation["results"]["prevention"][
        "attacker_objectives"
    ]["not_tested_count"]
    picus_latestSimulation["has_detection_analysis"] = picus_latestSimulation["results"]["has_detection_analysis"]

    table_name = "Picus Simulation Result"
    table_headers = [
        "started_at",
        "completed_at",
        "simulation_id",
        "simulation_run_id",
        "template_id",
        "status",
        "prevention_security_score",
        "prevention_total_threat",
        "prevention_blocked_threat",
        "prevention_not_blocked_threat",
        "prevention_not_tested_threat",
        "prevention_total_attacker_objectives",
        "prevention_achieved_objectives",
        "prevention_unachieved_objectives",
        "prevention_not_tested_objectives",
        "has_detection_analysis",
    ]
    md_table = tableToMarkdown(
        table_name, picus_latestSimulation, headers=table_headers, removeNull=True, headerTransform=string_to_table_header
    )
    return CommandResults(readable_output=md_table, outputs=picus_latestSimulation, outputs_prefix="Picus.SimulationResult")


def get_simulation_threats_command(client: Client) -> CommandResults:
    query_parameters: str = ""
    simulation_id = str(demisto.args().get("id"))
    run_id = str(demisto.args().get("run_id"))
    picus_threat_list = ""

    offset = demisto.args().get("offset")
    limit = demisto.args().get("limit")

    if offset is not None and limit is None:
        raise DemistoException("limit should be set.")
    elif offset is None and limit is not None:
        raise DemistoException("offset sohuld be set.")

    if offset is not None and limit is not None:
        query_parameters = "?" + "limit=" + limit + "&" + "offset=" + offset

    picus_endpoint_response = client.get_simulation_threats(query_parameters, simulation_id, run_id)
    picus_simulationThreats = picus_endpoint_response["threats"]
    for threat in picus_simulationThreats:
        action_count = 0
        threat_objectives = threat["objectives"]
        for objective in threat_objectives:
            action_count += len(objective["actions"])
        threat["action_count"] = action_count
        picus_threat_list += str(threat["threat_id"]) + ","

    if len(picus_threat_list) != 0:
        picus_threat_list = picus_threat_list[:-1]

    table_name = "Picus Simulation Threats"
    table_headers = ["threat_id", "threat_name", "severity", "prevention", "action_count"]
    md_table = tableToMarkdown(
        table_name, picus_simulationThreats, headers=table_headers, removeNull=True, headerTransform=string_to_table_header
    )
    return CommandResults(readable_output=md_table, outputs=picus_threat_list, outputs_prefix="Picus.SimulationThreats")


def get_simulation_actions_command(client: Client) -> CommandResults:
    query_parameters: str = ""
    simulation_id = demisto.args().get("id")
    run_id = demisto.args().get("run_id")
    threat_ids = demisto.args().get("threat_ids")
    threat_ids = list(threat_ids.split(","))
    picus_action_raw_results = ""
    picus_action_results: dict[str, Any] = {"results": []}

    offset = demisto.args().get("offset")
    limit = demisto.args().get("limit")

    if offset is not None and limit is None:
        raise DemistoException("limit should be set.")
    elif offset is None and limit is not None:
        raise DemistoException("offset sohuld be set.")

    for threat_id in threat_ids:
        if offset is not None and limit is not None:
            query_parameters = "?" + "limit=" + limit + "&" + "offset=" + offset
        picus_endpoint_response = client.get_simulation_actions(query_parameters, simulation_id, run_id, threat_id)
        picus_simulationActions = picus_endpoint_response["actions"]
        for action in picus_simulationActions:
            picus_action_raw_results += str(action["action_id"]) + "=" + str(action["prevention"]) + ","
        for action in picus_simulationActions:
            picus_action_results["results"].append(action)

    if len(picus_action_raw_results) != 0:
        picus_action_raw_results = picus_action_raw_results[:-1]
    picus_action_results = picus_action_results["results"]

    table_name = "Picus Simulation Actions"
    table_headers = ["action_id", "display_id", "action_name", "affected_os", "attack_module", "category", "prevention"]
    md_table = tableToMarkdown(
        table_name, picus_action_results, headers=table_headers, removeNull=True, headerTransform=string_to_table_header
    )
    return CommandResults(readable_output=md_table, outputs=picus_action_raw_results, outputs_prefix="Picus.SimulationActions")


def get_mitigation_devices_command(client: Client) -> CommandResults:
    query_parameters: str = ""
    simulation_ids = demisto.args().get("ids")

    if simulation_ids is not None:
        query_parameters = "?" + "simulation_ids=" + simulation_ids

    picus_mitigationDevices = client.get_mitigation_devices(query_parameters)

    table_name = "Picus Mitigation Devices"
    table_headers = ["id", "device_name", "score", "total_action_count", "blocked_action_count", "not_blocked_action_count"]
    md_table = tableToMarkdown(
        table_name, picus_mitigationDevices, headers=table_headers, removeNull=True, headerTransform=string_to_table_header
    )
    return CommandResults(readable_output=md_table, outputs=picus_mitigationDevices, outputs_prefix="Picus.MitigationDevices")


def get_signature_list_command(client: Client) -> CommandResults:
    device_id = demisto.args().get("device_id")
    action_ids = demisto.args().get("action_ids")
    picus_signature_raw_results: dict[str, Any] = {"results": []}
    picus_signature_all_results: dict[str, Any] = {"results": []}

    action_ids = action_ids.split(",")
    for action in action_ids:
        query_parameters = "?" + "action_ids=" + action
        picus_mitigationSignatures = client.get_signature_list(query_parameters, device_id)
        for mitigation in picus_mitigationSignatures:
            mitigation["action_id"] = action
        picus_signature_raw_results["results"].append(picus_mitigationSignatures)

    picus_signature_raw_results = picus_signature_raw_results["results"]
    for raw_results_list in picus_signature_raw_results:
        for raw_results in raw_results_list:
            picus_signature_all_results["results"].append(raw_results)
    picus_signature_all_results = picus_signature_all_results["results"]

    table_name = "Picus Mitigation Signature List"
    table_headers = [
        "action_id",
        "signature_id",
        "name",
        "signature_category",
        "signature_version",
        "product_platform",
        "product_version",
        "vendor_severity",
    ]
    md_table = tableToMarkdown(
        table_name, picus_signature_all_results, headers=table_headers, removeNull=True, headerTransform=string_to_table_header
    )
    return CommandResults(
        readable_output=md_table, outputs=picus_signature_all_results, outputs_prefix="Picus.MitigationSignatures"
    )


def setParamPB():
    agent_id = demisto.args().get("agent_id")
    device_id = demisto.args().get("device_id")
    simulation_id = demisto.args().get("simulation_id")

    param_data = {"agent_id": agent_id, "device_id": device_id, "simulation_id": simulation_id}
    return CommandResults(outputs=param_data, outputs_prefix="Picus.param")


def filterInsecureAttacks():
    threatinfo = demisto.args().get("threatinfo")
    threat_ids = ""

    threatinfo = list(threatinfo.split(","))
    threatinfo = [th_info for th_info in threatinfo if "unblocked" in th_info]

    for th_info in threatinfo:
        threat_id = th_info.split("=")[0]
        threat_ids += str(threat_id) + ","

    if len(threat_ids) != 0:
        threat_ids = threat_ids[:-1]

    return CommandResults(readable_output=threat_ids, outputs=threat_ids, outputs_prefix="Picus.filterinsecure")


""" MAIN FUNCTION """


def main() -> None:
    params = demisto.params()
    command = demisto.command()

    picus_apikey = params.get("picus_apikey")
    picus_server = params.get("picus_server")
    picus_server = picus_server[:-1] if picus_server.endswith("/") else picus_server
    verify_certificate = not params.get("insecure", False)
    proxy = params.get("proxy", False)

    demisto.debug(f"Command being called is {demisto.command()}")
    try:
        client = Client(api_key=picus_apikey, base_url=picus_server, verify=verify_certificate, proxy=proxy)
        if command == "test-module":
            # This is the call made when pressing the integration Test button.
            return_results(test_module(client))
        elif command == "picus-get-agent-list":
            return_results(get_agent_list_command(client))
        elif command == "picus-get-agent-detail":
            return_results(get_agent_detail_command(client))
        elif command == "picus-create-simulation":
            return_results(create_simulation_command(client))
        elif command == "picus-get-template-list":
            return_results(get_template_list_command(client))
        elif command == "picus-get-integration-agent-list":
            return_results(get_integration_agent_list_command(client))
        elif command == "picus-get-simulation-list":
            return_results(get_simulation_list_command(client))
        elif command == "picus-simulate-now":
            return_results(simulate_now_command(client))
        elif command == "picus-get-simulation-detail":
            return_results(get_simulation_detail_command(client))
        elif command == "picus-get-latest-simulation-result":
            return_results(get_latest_simulation_result_command(client))
        elif command == "picus-get-simulation-result":
            return_results(get_simulation_result_command(client))
        elif command == "picus-get-simulation-threats":
            return_results(get_simulation_threats_command(client))
        elif command == "picus-get-simulation-actions":
            return_results(get_simulation_actions_command(client))
        elif command == "picus-get-mitigation-devices":
            return_results(get_mitigation_devices_command(client))
        elif demisto.command() == "picus-get-signature-list":
            return_results(get_signature_list_command(client))
        elif command == "picus-set-paramPB":
            return_results(setParamPB())
        elif command == "picus-filter-insecure-attacks":
            return_results(filterInsecureAttacks())

    # Log exceptions and return errors
    except Exception as e:
        return_error(f"Failed to execute {demisto.command()} command.\nError:\n{str(e)}")


""" ENTRY POINT """


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()