Symantec Endpoint Protection V2
Query the Symantec Endpoint Protection Manager using the official REST API.
Endpoint · Symantec Endpoint Protection
Details
| ID | Symantec Endpoint Protection V2 |
|---|---|
| Provider | Broadcom |
| Category | Endpoint |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Query the Symantec Endpoint Protection Manager using the official REST API.
Use Cases
- Scan/Quarantine/content-update an endpoint.
- Assign policy to an endpoint.
- Move client to different group.
Unsupported use cases in the API:
- Get scan results
- Get reports/logs
- Receive system alerts
Required Permissions
The following role is required to use the Symantec Endpoint Protection API:
- sysadmin
Note: An Administrator role does not have enough permissions for this integration. A System Administrator (sysadmin) role is required.
Configure Symantec Endpoint Protection V2 in Cortex
| Parameter | Required |
|---|---|
| Server (e.g., https://1.2.3.4:8446) | True |
| Authentication | True |
| Password | True |
| SEPM domain for the user | False |
| Trust any certificate (not secure) | False |
| Use system proxy settings | False |
| Local time zone (e.g., +02:30,-06:00) | False |
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.
sep-endpoints-info
Returns information about endpoints.
Base Command
sep-endpoints-info
Input
| Argument Name | Description | Required |
|---|---|---|
| columns | A CSV list of the displayed columns. | Optional |
| computerName | Filters by the host name of the computer. A wild card search can be done using ‘*’ at the end of the query. | Optional |
| lastUpdate | Indicates when a computer’s status was last updated. The default is “0”, which returns all results. Default is 0. | Optional |
| os | The operating system by which to filter. Possible values are: CentOs, Debian, Fedora, MacOSX, Oracle, OSX, RedHat, SUSE, Ubuntu, Win10, Win2K, Win7, Win8, WinEmb7, WinEmb8, WinEmb81, WinFundamental, WinNT, Win2K3, Win2K8, Win2K8R2, WinVista, WinXP, WinXPEmb, WinXPProf64. | Optional |
| pageSize | The number of results to include on each page. The default is 20. | Optional |
| groupName | The name of the group to which the endpoint belongs. A wild card search can be done using ‘*’ at the end of the query. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| SEPM.Endpoint.Hostname | String | The hostname of the endpoint. |
| SEPM.Endpoint.Domain | String | The domain of the endpoint. |
| SEPM.Endpoint.IPAddresses | String | The IP addresses of the endpoint. |
| SEPM.Endpoint.OS | String | The OS information of the endpoint. |
| SEPM.Endpoint.Description | String | The description of the endpoint. |
| SEPM.Endpoint.MACAddresses | String | The MAC address of the endpoint. |
| SEPM.Endpoint.BIOSVersion | String | The BIOS version of the endpoint. |
| SEPM.Endpoint.DHCPServer | String | The DHCP server address of the endpoint. |
| SEPM.Endpoint.HardwareKey | String | The hardware key of the client to be moved. |
| SEPM.Endpoint.LastScanTime | String | The last scan time of the endpoint. |
| SEPM.Endpoint.RunningVersion | String | The running version of the endpoint. |
| SEPM.Endpoint.TargetVersion | String | The target version of the endpoint. |
| IP.Address | String | The IP address of the endpoint. |
| IP.Host | String | The IP host of the endpoint. |
| Endpoint.Hostname | Unknown | The hostname of the endpoint. |
| Endpoint.MACAddress | Unknown | The MAC address of the endpoint. |
| Endpoint.Domain | Unknown | The domain of the endpoint. |
| Endpoint.IPAddress | Unknown | The IP address of the endpoint. |
| Endpoint.DHCPServer | Unknown | The DHCP server of the endpoint. |
| Endpoint.OS | String | The OS of the endpoint. |
| Endpoint.OSVersion | String | The OS version of the endpoint. |
| Endpoint.BIOSVersion | String | The BIOS version of the endpoint. |
| Endpoint.Memory | String | The memory of the endpoint. |
| Endpoint.Processors | String | The processors that the endpoint uses. |
| IP.Hostname | String | The hostname that is mapped to this IP address. |
| SEPM.Endpoint.Group | String | The group of the endpoint. |
| SEPM.Endpoint.PatternIdx | String | The PatternIdx of the endpoint. |
| SEPM.Endpoint.OnlineStatus | String | The online status of the endpoint. |
| SEPM.Endpoint.UpdateTime | String | The update time of the endpoint. |
Command Example
!sep-endpoints-info
Human Readable Output

sep-groups-info
Returns information about groups.
Base Command
sep-groups-info
Input
| Argument Name | Description | Required |
|---|---|---|
| columns | The column by which the results are sorted. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| SEPM.Groups | Unknown | The list of groups. |
| SEPM.Groups.created | number | The time of creation time (in Epoch). |
| SEPM.Groups.fullPathName | string | The name of the group. |
| SEPM.Groups.id | string | The ID of the group. |
| SEPM.Groups.numberOfPhysicalComputers | number | The number of physical computers in the group. |
| SEPM.Groups.numberOfRegisteredUsers | number | The number of registered users in the group. |
| SEPM.Groups.policyDate | number | The date of the policy (in Epoch). |
| SEPM.Groups.policySerialNumber | number | The serial number of the policy. |
Command Example
!sep-groups-info
Human Readable Output

sep-system-info
Returns information about the system, such as version or AV definition.
Base Command
sep-system-info
Input
There are no input arguments for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| SEPM.ServerAVDefVersion | string | The version of the AV definition. |
Command Example
!sep-system-info
Human Readable Output

sep-command-status
Retrieves the status of a command.
Base Command
sep-command-status
Input
| Argument Name | Description | Required |
|---|---|---|
| commandId | The ID of the command. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| SEPM.LastCommand.CommandDetails | string | The details of the command. |
| SEPM.LastCommand.CommandId | string | The ID of the command. |
Command Example
!sep-command-status commandId=04A68CA5952B4726AAFEB421E0EB436C
Human Readable Output

sep-client-content
Retrieves the content of the client.
Base Command
sep-client-content
Input
There are no input arguments for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| SEPM.ClientContentVersions | string | Displays the versions for each client. |
| SEPM.LastUpdated | string | The last update of a date. |
Command Example
!sep-client-content
Human Readable Output

sep-list-policies
Retrieves a list of existing policies.
Base Command
sep-list-policies
Input
There are no input arguments for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| SEPM.PoliciesList.PolicyName | string | The name of the policy. |
| SEPM.PoliciesList.Type | string | The type of the policy. |
| SEPM.PoliciesList.ID | string | The ID of the policy. |
| SEPM.PoliciesList.Description | string | The description of the policy. |
| SEPM.PoliciesList.Enabled | boolean | Whether the list of polices is enabled. Enabled if “True”. |
| SEPM.PoliciesList.AssignedLocations.GroupID | string | The ID of the group of the locations assigned to this policy. |
| SEPM.PoliciesList.AssignedLocations.Locations | string | The list of location IDs assigned to this policy. |
| SEPM.PoliciesList.AssignedCloudGroups.GroupID | string | The ID of the cloud group of the locations assigned to this policy. |
| SEPM.PoliciesList.AssignedCloudGroups.Locations | string | The list of location IDs belonging to a cloud group assigned to this policy. |
Command Example
!sep-list-policies
Human Readable Output

sep-assign-policy
Assigns an existing policy to a specified location.
Base Command
sep-assign-policy
Input
| Argument Name | Description | Required |
|---|---|---|
| groupID | The ID of the group to which the endpoint belongs. | Required |
| locationID | The ID of the location of the endpoint. | Required |
| policyType | The type of policy to be assigned. | Required |
| policyID | The ID of the policy to be assigned. | Required |
Context Output
There is no context output for this command.
Command Example
!sep-assign-policy groupID=44BE96AFC0A8010B0CFACB30929326C2 locationID=50FEEA3FC0A8010B739E49CB0C321A7E policyID=A00ADE188AA148D7AD319CBCA1FA2F23 policyType=hi
Human Readable Output

sep-list-locations
Retrieves a list of location IDs for a specified group.
Base Command
sep-list-locations
Input
| Argument Name | Description | Required |
|---|---|---|
| groupID | The group ID for which to list locations. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| SEPM.Locations.ID | Unknown | The ID of the location. |
Command Example
!sep-list-locations groupID=44BE96AFC0A8010B0CFACB30929326C2
Human Readable Output

sep-endpoint-quarantine
Quarantines an endpoint according to its policy.
Base Command
sep-endpoint-quarantine
Input
| Argument Name | Description | Required |
|---|---|---|
| endpoint | The IP or hostname of the endpoint. | Required |
| actionType | Adds or removes an endpoint from quarantine. Possible values are: Add, Remove. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| SEPM.Quarantine.CommandID | string | The ID of the command that was run. |
| SEPM.Quarantine.Action | string | The type of the action type. Can be “Add” or “Remove”. |
| SEPM.Quarantine.Endpoint | string | The IP or hostname of the identifier of the endpoint. |
Command Example
!sep-endpoint-quarantine actionType=add endpoint=demisto-PC
Human Readable Output

sep-scan-endpoint
Scans an endpoint.
Base Command
sep-scan-endpoint
Input
| Argument Name | Description | Required |
|---|---|---|
| endpoint | The IP address or hostname of the endpoint. | Required |
| scanType | The scan type of the endpoint. Can be “ScanNow_Quick”, “ScanNow_Full”, or “ScanNow_Custom”. Possible values are: ScanNow_Quick, ScanNow_Full, ScanNow_Custom. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| SEPM.Scan.CommandID | string | The ID of the command that was run. |
| SEPM.Scan.Type | string | The type of the scan. Can be “ScanNow_Quick”, “ScanNow_Full”, or “ScanNow_Custom”. |
| SEPM.Scan.Endpoint | Unknown | The IP or hostname of the identifier of the endpoint. |
Command Example
!sep-scan-endpoint endpoint=demisto-PC scanType=ScanNow_Quick
Human Readable Output

sep-update-endpoint-content
Updates the content of a specified client.
Base Command
sep-update-endpoint-content
Input
| Argument Name | Description | Required |
|---|---|---|
| endpoint | The IP address or hostname of the endpoint. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| SEPM.Update.Endpoint | String | The endpoint that is being updated. |
| SEPM.Update.CommandID | String | The ID of the command for which to check the status. |
Command Example
!sep-update-endpoint-content endpoint=demisto-PC
Human Readable Output

sep-move-client-to-group
Moves a client to a group.
Base Command
sep-move-client-to-group
Input
| Argument Name | Description | Required |
|---|---|---|
| groupID | The ID of the group to which to move the client. | Required |
| hardwareKey | The hardware key of the client to be moved. | Required |
Context Output
There is no context output for this command.
Command Example
!sep-move-client-to-group groupID=AA51516BC0A8010B3BFBBE37F7B71214 hardwareKey=269CE816FDB1BA25A2505D0A5A59294C
Human Readable Output

sep-identify-old-clients
Get endpoints for a running version that is different than the target version or the desired version (if specified).
Base Command
sep-identify-old-clients
Input
| Argument Name | Description | Required |
|---|---|---|
| columns | Sets which columns will be displayed. | Optional |
| computerName | Filters by the host name of the computer. A wild card search can be done using ‘*’ at the end of the query. | Optional |
| lastUpdate | Indicates when a computer’s status was last updated. The default is “0”, which returns all results. | Optional |
| os | The operating system by which to filter. | Optional |
| pageSize | The number of results to include on each page. The default is 20. | Optional |
| groupName | The name of the group to which the endpoint belongs. A wild card search can be done using ‘*‘at the end of the query. | Optional |
| desiredVersion | desiredVersion. | Optional |
Context Output
There is no context output for this command.
Command Example
!sep-identify-old-clients desiredVersion=10
Human Readable Output

Known Limitations
- SEPM REST- API currently exposes statistics, but does not expose extended information about Risks, Application and Device control, and Network logs.
- SEPM REST- API currently does not support an operation to get Host Names or IP addresses of clients who don’t have an update content version.
- SEPM REST- API currently does not support an operation to create or download reports.
Configuration parameters
server— Server (e.g., https://1.2.3.4:8446) (required)authentication— Authentication (required)domain— SEPM domain for the userinsecure— Trust any certificate (not secure)proxy— Use system proxy settingstimeZone— Local time zone (e.g., +02:30,-06:00)
Commands (13)
-
sep-assign-policyAssigns an existing policy to a specified location.
-
sep-client-contentRetrieves the content of the client.
-
sep-command-statusRetrieves the status of a command.
-
sep-endpoint-quarantineQuarantines an endpoint according to its policy.
-
sep-endpoints-infoReturns information about endpoints.
-
sep-groups-infoReturns information about groups.
-
sep-identify-old-clientsGet endpoints for a running version that is different than the target version or the desired version (if specified).
-
sep-list-locationsRetrieves a list of location IDs for a specified group.
-
sep-list-policiesRetrieves a list of existing policies.
-
sep-move-client-to-groupMoves a client to a group.
-
sep-scan-endpointScans an endpoint.
-
sep-system-infoReturns information about the system, such as version or AV definition.
-
sep-update-endpoint-contentUpdates the content of a specified client.
import demistomock as demisto from CommonServerPython import * import requests import json import re import urllib.request import urllib.parse import urllib.error import urllib3 urllib3.disable_warnings() handle_proxy() ENDPOINTS_INFO_DEFAULT_COLUMNS = [ "computerName", "ipAddresses", "operatingSystem", "osBitness", "cidsDefsetVersion", "lastScanTime", "description", "quarantineDesc", "domainOrWorkgroup", "macAddresses", "group", "dhcpServer", "biosVersion", "virtualizationPlatform", "computerTimeStamp", "creationTime", "agentTimestamp", "hardwareKey", ] GROUPS_INFO_DEFAULT_COLUMNS = [ "fullPathName", "numberOfPhysicalComputers", "numberOfRegisteredUsers", "policySerialNumber", "policyDate", "description", "created", "id", ] """LITERALS""" EPOCH_MINUTE = 60 * 1000 EPOCH_HOUR = 60 * EPOCH_MINUTE """HELPER FUNCTIONS""" def fix_url(base): return base if base.endswith("/") else (base + "/") def endpoint_ip_extract(raw_json): ips_array = [] for content in raw_json: ip = {"Address": content.get("ipAddresses", [""])[0], "Mac": content.get("computerName")} ip = createContext(ip, removeNull=True) if ip: ips_array.append(ip) return ips_array def endpoint_endpoint_extract(raw_json): endpoints_arr = [] for content in raw_json: endpoint = { "Hostname": content.get("computerName"), "MACAddress": content.get("macAddresses", [""])[0], "Domain": content.get("domainOrWorkgroup"), "IPAddress": content.get("ipAddresses", [""])[0], "DHCPServer": content.get("dhcpServer"), "OS": content.get("operatingSystem"), "OSVersion": content.get("osVersion"), "BIOSVersion": content.get("biosVersion"), "Memory": content.get("memory"), "Processors": content.get("processorType"), } endpoint = createContext(endpoint, removeNull=True) if endpoint: endpoints_arr.append(endpoint) return endpoints_arr def build_query_params(params): list_params = [key + "=" + str(params[key]) for key in params] query_params = "&".join(list_params) return "?" + query_params if query_params else "" def do_auth(server, crads, insecure, domain): url = fix_url(str(server)) + "sepm/api/v1/identity/authenticate" body = { "username": crads.get("identifier") if crads.get("identifier") else "", "password": urllib.parse.quote(crads.get("password")) if crads.get("password") else "", "domain": domain if domain else "", } res = requests.post(url, headers={"Content-Type": "application/json"}, data=json.dumps(body), verify=not insecure) res.raise_for_status() return parse_response(res) def do_get(token, raw, suffix): insecure = demisto.getParam("insecure") server = demisto.getParam("server") url = fix_url(server) + suffix res = requests.get(url, headers={"Authorization": "Bearer " + token}, verify=not insecure) res.raise_for_status() if raw: return res else: return parse_response(res) def do_post(token, is_xml, suffix, body): insecure = demisto.getParam("insecure") server = demisto.getParam("server") url = fix_url(server) + suffix res = requests.post(url, headers={"Authorization": "Bearer " + token}, data=body, verify=not insecure) res.raise_for_status() parsed_response = {} if is_xml: if res.content: parsed_response = xml2json(res.content) else: return_error(f"Unable to parse the following response: {res}") else: parsed_response = parse_response(res) return parsed_response def do_put(token, suffix, body): insecure = demisto.getParam("insecure") server = demisto.getParam("server") url = fix_url(server) + suffix res = requests.put( url, headers={"Authorization": "Bearer " + token, "Content-Type": "application/json"}, data=json.dumps(body), verify=not insecure, ) parsed_response = parse_response(res) return parsed_response def do_patch(token, suffix, body): insecure = demisto.getParam("insecure") server = demisto.getParam("server") url = fix_url(server) + suffix res = requests.patch( url, headers={"Authorization": "Bearer " + token, "Content-Type": "application/json"}, data=json.dumps(body), verify=not insecure, ) res.raise_for_status() parsed_response = parse_response(res) return parsed_response def parse_response(resp): if resp.status_code == 200 or resp.status_code == 207: if resp.text == "": return resp try: return resp.json() except Exception as ex: return_error(f"Unable to parse response: {ex}") else: try: message = resp.json().get("errorMessage") return_error(f"Error: {message}") except Exception: return_error(f"Error: {resp}") def get_token_from_response(resp): if resp.get("token"): return resp.get("token") else: return_error(f"No token: {resp}") # noqa: RET503 def choose_columns(column_arg, default_list): if not column_arg: columns_list = default_list columns_list.sort() elif column_arg == "all" or column_arg == "*": columns_list = [] else: columns_list = argToList(column_arg) return columns_list def build_command_xml(data): return ( '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"' f' xmlns:com="http://command.client.webservice.sepm.symantec.com/"> \ <soapenv:Header/><soapenv:Body>{data}</soapenv:Body></soapenv:Envelope>' ) def build_client_xml(data): return ( '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" ' f'xmlns:cli="http://client.webservice.sepm.symantec.com/"> \ <soapenv:Header/><soapenv:Body>{data}</soapenv:Body></soapenv:Envelope>' ) def get_command_status_details(token, command_id): xml = build_command_xml(f"<com:getCommandStatusDetails><commandID>{command_id}</commandID></com:getCommandStatusDetails>") res_json = do_post(token, True, "sepm/ws/v1/CommandService", xml) return res_json def build_command_response_output(title, command_id, message, response): cmd_status_details = response.get("cmdStatusDetail") cmd_status_details.pop("hardwareKey", None) md = tableToMarkdown(title, cmd_status_details) + "\n" md += f"### Command ID: {command_id}\n" md += "### " + message demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": {"cmdStatusDetail": cmd_status_details, "commandId": command_id}, "HumanReadable": md, "EntryContext": { "SEPM.LastCommand": createContext( {"CommandDetails": cmd_status_details, "CommandId": command_id}, removeNull=True ) }, } ) def get_computer_id_by_ip(token, ip): xml = build_client_xml(f"<cli:getComputersByIP><ipAddresses>{ip}</ipAddresses></cli:getComputersByIP>") res_json = do_post(token, True, "sepm/ws/v1/ClientService", xml) return demisto.get(json.loads(res_json), "Envelope.Body.getComputersByIPResponse.ComputerResult.computers.computerId") def get_computer_id_by_hostname(token, hostname): xml = build_client_xml( f"<cli:getComputersByHostName><computerHostNames>{hostname}</computerHostNames></cli:getComputersByHostName>" ) res_json = do_post(token, True, "sepm/ws/v1/ClientService", xml) return demisto.get(json.loads(res_json), "Envelope.Body.getComputersByHostNameResponse.ComputerResult.computers.computerId") def get_computer_id(token, endpoint_ip, endpoint_host_name): computer_id = "" if endpoint_ip: try: computer_id = get_computer_id_by_ip(token, endpoint_ip) except Exception: return_error("Failed to locate the endpoint by its IP address.") elif endpoint_host_name: try: computer_id = get_computer_id_by_hostname(token, endpoint_host_name) except Exception: return_error("Failed to locat the endpoint by its hostname.") else: return_error("Please provide the IP address or the hostname of endpoint.") return computer_id def update_content(token, computer_id): xml = build_command_xml( f"<com:runClientCommandUpdateContent><computerGUIDList>{computer_id}</computerGUIDList>" "</com:runClientCommandUpdateContent>" ) res_json = do_post(token, True, "sepm/ws/v1/CommandService", xml) command_id = demisto.get( json.loads(res_json), "Envelope.Body.runClientCommandUpdateContentResponse.CommandClientResult.commandId" ) if not command_id: error_code = demisto.get( res_json, "Envelope.Body.runClientCommandUpdateContentResponse.CommandClientResult.inputErrors.errorCode" ) error_message = demisto.get( res_json, "Envelope.Body.runClientCommandUpdateContentResponse.CommandClientResult.inputErrors.errorMessage" ) if error_code or error_message: return_error(f"An error response has returned from server: {error_message} with code: {error_code}") else: return_error("Could not retrieve command ID, no error was returned from server") return command_id def scan(token, computer_id, scan_type): xml = build_command_xml( f"<com:runClientCommandScan><computerGUIDList>{computer_id}</computerGUIDList>" f"<scanType>{scan_type}</scanType></com:runClientCommandScan>" ) res_json = do_post(token, True, "sepm/ws/v1/CommandService", xml) command_id = demisto.get(json.loads(res_json), "Envelope.Body.runClientCommandScanResponse.CommandClientResult.commandId") if not command_id: error_code = demisto.get( json.loads(res_json), "Envelope.Body.runClientCommandScanResponse.CommandClientResult.inputErrors.errorCode" ) error_message = demisto.get( json.loads(res_json), "Envelope.Body.runClientCommandScanResponse.CommandClientResult.inputErrors.errorMessage" ) if error_code or error_message: return_error(f"An error response has returned from server: {error_message} with code: {error_code}") else: return_error("Could not retrieve command ID, no error was returned from server") return command_id def quarantine(token, computer_id, action_type): xml = build_command_xml( f"<com:runClientCommandQuarantine><command><commandType>{action_type}</commandType><targetObjectIds>{computer_id}" "</targetObjectIds><targetObjectType>COMPUTER</targetObjectType></command>" "</com:runClientCommandQuarantine>" ) res_json = do_post(token, True, "sepm/ws/v1/CommandService", xml) command_id = demisto.get( json.loads(res_json), "Envelope.Body.runClientCommandQuarantineResponse.CommandClientResult.commandId" ) if not command_id: error_code = demisto.get( json.loads(res_json), "Envelope.Body.runClientCommandQuarantineResponse.CommandClientResult.inputErrors.errorCode" ) error_message = demisto.get( json.loads(res_json), "Envelope.Body.runClientCommandQuarantineResponse.CommandClientResult.inputErrors.errorMessage" ) if error_code or error_message: return_error(f"An error response has returned from server: {error_message} with code: {error_code}") else: return_error("Could not retrieve command ID, no error was returned from server") return command_id def validate_time_zone(time_zone): pattern = re.compile("^[+-][0-9][0-9]:[0-9][0-9]") return bool(pattern.match(time_zone)) def parse_epoch_to_local(epoch, time_zone): if not validate_time_zone(time_zone): return_error("timeZone param should be in the format of [+/-][h][h]:[m][m]. For exmaple +04:30") operator = time_zone[0] hour = int(time_zone[1:3]) minutes = int(time_zone[4:6]) time_zone_epoch = hour * EPOCH_HOUR + minutes * EPOCH_MINUTE local = int(epoch) + time_zone_epoch if operator == "+" else int(epoch) - time_zone_epoch return local def change_assigined(policy): new_format = { "Policy Name": policy.get("PolicyName"), "Type": policy.get("Type"), "ID": policy.get("ID"), "Assigned": bool(policy.get("AssignedLocations")) or bool(policy.get("AssignedCloudGroups")), "Discription": policy.get("Discription"), "Enabled": policy.get("Enabled"), } return new_format def sanitize_policies_list_for_md(policies_list): return list(map(change_assigined, policies_list)) def sanitize_policies_list(policies_list): return list( # noqa: C417 map( lambda policy: { "PolicyName": policy["name"], "Type": policy["policytype"], "ID": policy["id"], "Description": policy["desc"], "Enabled": policy["enabled"], "AssignedLocations": list( # noqa: C417 map( lambda location: {"GroupID": location.get("groupId"), "Locations": location.get("locationIds")}, policy.get("assignedtolocations") if policy.get("assignedtolocations") else [], ) ), "AssignedCloudGroups": list( # noqa: C417 map( lambda location: {"GroupID": location.get("groupId"), "Locations": location.get("locationIds")}, policy.get("assignedtocloudgroups") if policy.get("assignedtocloudgroups") else [], ) ), }, policies_list, ) ) def validate_ip(ip): pattern = re.compile(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$") return bool(pattern.match(ip)) def get_client_content(token, time_zone): client_content_json = do_get(token, False, "sepm/api/v1/stats/client/content") epoch_time = client_content_json.get("lastUpdated") if time_zone: epoch_time = parse_epoch_to_local(epoch_time, time_zone) last_update_date = timestamp_to_datestring(epoch_time, "%a %b %d %y %H:%M:%S %z") client_version = client_content_json.get("clientDefStatusList") return client_content_json, client_version, last_update_date def get_endpoints_info(token, computer_name, last_update, os, page_size, columns, group_name=None): params = {"computerName": computer_name, "lastUpdate": last_update, "os": os, "pageSize": page_size, "columns": columns} params = createContext(params, removeNull=True) json_response = do_get(token, False, "sepm/api/v1/computers" + build_query_params(params)) filtered_json_response = json_response.get("content") final_json = [] entry_context = [] for content in filtered_json_response: group = content.get("group", {"name": ""}) bool_start = group.get("name").startswith(group_name[:-1]) if group_name and group_name[-1] == "*" else False if (not group_name) or group.get("name") == group_name or bool_start: # No group name filter # used `set` on the mac address list as it sometimes contained duplicated values content["macAddresses"] = list(set(content.get("macAddresses"))) entry_context.append( { "Hostname": content.get("computerName"), "Domain": content.get("domainOrWorkgroup"), "IPAddresses": content.get("ipAddresses"), "OS": content.get("operatingSystem", "") + " | " + content.get("osBitness", ""), "Description": content.get("content.description"), "MACAddresses": content.get("macAddresses"), "BIOSVesrsion": content.get("biosVersion"), "DHCPServer": content.get("dhcpServer"), "HardwareKey": content.get("hardwareKey"), "LastScanTime": epochToTimestamp(content.get("lastScanTime")), "RunningVersion": content.get("deploymentRunningVersion"), "TargetVersion": content.get("deploymentTargetVersion"), "Group": group.get("name"), "PatternIdx": content.get("patternIdx"), "OnlineStatus": content.get("onlineStatus"), "UpdateTime": epochToTimestamp(content.get("lastUpdateTime")), } ) final_json.append(content) return final_json, entry_context def create_endpints_filter_string(computer_name, last_update, os, page_size, group_name=None): md = "## Endpoints Information" if last_update != "0": md += f", filtered for last updated status: {last_update}" if last_update else "" md += f", filtered for hostname: {computer_name}" if computer_name else "" md += f", filtered for os: {os}" if os else "" md += f", filtered for group name: {group_name}" if group_name else "" md += f", page size: {page_size}" if page_size else "" md += "\n" return md def get_groups_info(token, columns): json_res = do_get(token, False, "sepm/api/v1/groups" + build_query_params({"columns": columns})) sepm_groups = [] filtered_json_response = json_res.get("content") for entry in filtered_json_response: group = {} for header in GROUPS_INFO_DEFAULT_COLUMNS: group[header] = entry[header] sepm_groups.append(group) return filtered_json_response, json_res, sepm_groups def get_command_status(token, command_id): command_status_json = get_command_status_details(token, command_id) cmd_status_detail = demisto.get( json.loads(command_status_json), "Envelope.Body.getCommandStatusDetailsResponse.CommandStatusDetailResult.cmdStatusDetail", ) cmd_status_detail.pop("hardwareKey", None) state_id = cmd_status_detail.get("stateId") is_done = False if state_id == "2" or state_id == "3": is_done = True message = "Command is done." if is_done else "Command is in progress. Run !sep-command-status to check again." return cmd_status_detail, message def get_list_of_policies(token): policies_list = do_get(token, False, "sepm/api/v1/policies/summary").get("content") fixed_policy_list = sanitize_policies_list(policies_list) md_list = sanitize_policies_list_for_md(fixed_policy_list) return md_list, policies_list, fixed_policy_list def endpoint_quarantine(token, endpoint, action): action_type = "Quarantine" if action == "Add" else "Undo" computer_id = get_id_by_endpoint(token, endpoint) command_id = quarantine(token, computer_id, action_type) return command_id def get_location_list(token, group_id): url = f"sepm/api/v1/groups/{group_id}/locations" url_resp = do_get(token, False, url) location_ids = list(map(lambda location_string: {"ID": location_string.split("/")[-1]}, url_resp)) # noqa: C417 return url_resp, location_ids def get_id_by_endpoint(token, endpoint): computer_id = "" if not endpoint: return_error("Please provide the IP address or the hostname of endpoint.") elif validate_ip(endpoint): computer_id = get_computer_id(token, endpoint, None) else: computer_id = get_computer_id(token, None, endpoint) return computer_id def scan_endpoint(token, endpoint, scan_type): computer_id = get_id_by_endpoint(token, endpoint) command_id = scan(token, computer_id, scan_type) return command_id def update_endpoint_content(token, endpoint): computer_id = get_id_by_endpoint(token, endpoint) command_id = update_content(token, computer_id) return command_id def filter_only_old_clients(filtered_json_response, desired_version): filtered = [] for content in filtered_json_response: RunningVersion = content.get("deploymentRunningVersion") TargetVersion = content.get("deploymentTargetVersion") if (desired_version and RunningVersion != desired_version) or (not desired_version and RunningVersion != TargetVersion): filtered.append(content) return filtered """COMMANDS""" def system_info_command(token): version_json = do_get(token, False, "sepm/api/v1/version") avdef_json = do_get(token, False, "sepm/api/v1/content/avdef/latest") system_info_json = {"version": version_json, "avdef": avdef_json} md = "## System Information\n" md += tableToMarkdown("Version", version_json) md += tableToMarkdown("AV Definitions", avdef_json) context = avdef_json.get("publishedBySymantec") if type(context) is dict: context = createContext(context, removeNull=True) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": system_info_json, "HumanReadable": md, "EntryContext": {"SEPM.ServerAVDefVersion": context}, } ) def old_clients_command(token): computer_name = demisto.getArg("computerName") last_update = demisto.getArg("lastUpdate") os = demisto.getArg("os") page_size = demisto.getArg("pageSize") columns = demisto.getArg("columns") group_name = demisto.getArg("groupName") desired_version = demisto.getArg("desiredVersion") filtered_json_response, entry_context = get_endpoints_info( token, computer_name, last_update, os, page_size, columns, group_name ) columns_list = choose_columns(columns, ENDPOINTS_INFO_DEFAULT_COLUMNS) filtered_json_response = filter_only_old_clients(filtered_json_response, desired_version) md = create_endpints_filter_string(computer_name, last_update, os, page_size, group_name) md += tableToMarkdown("Old Endpoints", filtered_json_response, columns_list) demisto.results( {"Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": filtered_json_response, "HumanReadable": md} ) def client_content_command(token): time_zone = demisto.getParam("timeZone") client_content_json, client_version, last_update_date = get_client_content(token, time_zone) md = f"## Client Content, last updated on {last_update_date}\n" md += tableToMarkdown("Client Content Versions", client_version) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": client_content_json, "HumanReadable": md, "EntryContext": {"SEPM.ClientContentVersions": client_version, "SEPM.LastUpdated": last_update_date}, } ) def endpoints_info_command(token): computer_name = demisto.getArg("computerName") last_update = demisto.getArg("lastUpdate") os = demisto.getArg("os") page_size = demisto.getArg("pageSize") columns = demisto.getArg("columns") group_name = demisto.getArg("groupName") filtered_json_response, entry_context = get_endpoints_info( token, computer_name, last_update, os, page_size, columns, group_name ) columns_list = choose_columns(columns, ENDPOINTS_INFO_DEFAULT_COLUMNS) md = create_endpints_filter_string(computer_name, last_update, os, page_size, group_name) md += tableToMarkdown("Endpoints", filtered_json_response, columns_list) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": filtered_json_response, "HumanReadable": md, "IgnoreAutoExtract": True, "EntryContext": { "SEPM.Endpoint(val.Hostname == obj.Hostname)": createContext(entry_context, removeNull=True), "IP(val.Address === obj.Address)": endpoint_ip_extract(filtered_json_response), "Endpoint(val.Hostname == obj.Hostname)": endpoint_endpoint_extract(filtered_json_response), }, } ) def groups_info_command(token): columns = demisto.getArg("columns") filtered_json_response, json_res, sepm_groups = get_groups_info(token, columns) columns_list = choose_columns(columns, GROUPS_INFO_DEFAULT_COLUMNS) md = tableToMarkdown("Groups Information", filtered_json_response, columns_list) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": json_res, "HumanReadable": md, "IgnoreAutoExtract": True, "EntryContext": {"SEPM.Groups": sepm_groups}, } ) def command_status(token): command_id = demisto.getArg("commandId") cmd_status_detail, message = get_command_status(token, command_id) md = f"### Command ID: {command_id}\n" md += f"### State ID: {cmd_status_detail.get('stateId')}\n" md += "### " + message demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": {"cmdStatusDetail": cmd_status_detail, "commandId": command_id}, "HumanReadable": md, "IgnoreAutoExtract": True, "EntryContext": { "SEPM.LastCommand(val.CommandID && val.CommandID == obj.CommandID)": createContext( {"CommandDetails": cmd_status_detail, "CommandID": command_id}, removeNull=True ) }, } ) def list_policies_command(token): md_list, policies_list, fixed_policy_list = get_list_of_policies(token) md = tableToMarkdown( "List of existing policies", md_list, ["Policy Name", "Type", "ID", "Enabled", "Assigned", "Description"] ) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": policies_list, "HumanReadable": md, "IgnoreAutoExtract": True, "EntryContext": {"SEPM.PoliciesList": createContext(fixed_policy_list, removeNull=True)}, } ) def assign_policie_command(token): group_id = demisto.getArg("groupID") locatoion_id = demisto.getArg("locationID") policy_type = demisto.getArg("policyType").lower() policy_id = demisto.getArg("policyID") do_put(token, f"sepm/api/v1/groups/{group_id}/locations/{locatoion_id}/policies/{policy_type}", {"id": policy_id}) md = f"### Policy: {policy_id}, of type: {policy_type}, was assigned to location: {locatoion_id}, in group: {group_id}" demisto.results( {"Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": "", "HumanReadable": md, "EntryContext": {}} ) def list_locations_command(token): group_id = demisto.getArg("groupID") url_resp, location_ids = get_location_list(token, group_id) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": url_resp, "HumanReadable": tableToMarkdown( "Locations", list(map(lambda location: {"Location ID": location.get("ID")}, location_ids)), # noqa: C417 ), "IgnoreAutoExtract": True, "EntryContext": {"SEPM.Locations": location_ids}, } ) def endpoint_quarantine_command(token): endpoint = demisto.getArg("endpoint") action = demisto.getArg("actionType") command_id = endpoint_quarantine(token, endpoint, action) message = ( f"### Initiated quarantine for endpoint {endpoint}. Command ID: {command_id}." if action == "Add" else f"### Removing endpoint: {endpoint} from quarantine. Command ID: {command_id}." ) context = {"CommandID": command_id, "Action": action, "Endpoint": endpoint} demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["text"], "Contents": command_id, "HumanReadable": message, "IgnoreAutoExtract": True, "EntryContext": {"SEPM.Quarantine": context}, } ) def scan_endpoint_command(token): endpoint = demisto.getArg("endpoint") scan_type = demisto.getArg("scanType") command_id = scan_endpoint(token, endpoint, scan_type) message = f"### Initiated scan on endpoint: {endpoint} with type: {scan_type}. Command ID: {command_id}." context = {"CommandID": command_id, "Type": scan_type, "Endpoint": endpoint} demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["text"], "Contents": command_id, "HumanReadable": message, "IgnoreAutoExtract": True, "EntryContext": {"SEPM.Scan": context}, } ) def update_endpoint_content_command(token): endpoint = demisto.getArg("endpoint") command_id = update_endpoint_content(token, endpoint) message = f"### Updating endpoint: {endpoint}. Command ID: {command_id}." context = {"CommandID": command_id, "Endpoint": endpoint} demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["text"], "Contents": command_id, "HumanReadable": message, "IgnoreAutoExtract": True, "EntryContext": {"SEPM.Update": context}, } ) def move_client_to_group(token, group_id, hardware_key): body = [{"group": {"id": group_id}, "hardwareKey": hardware_key}] response = do_patch(token, "sepm/api/v1/computers", body) message = ( "### Moved client to requested group successfully" if response[0].get("responseCode") == "200" else "### Error moving client" ) return response, message def move_client_to_group_command(token): group_id = demisto.getArg("groupID") hardware_key = demisto.getArg("hardwareKey") response, message = move_client_to_group(token, group_id, hardware_key) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["text"], "Contents": response, "HumanReadable": message, "IgnoreAutoExtract": True, } ) def main(): current_command = demisto.command() try: """ Before EVERY command the following tow lines are performed (do_auth and get_token_from_response) """ resp = do_auth( server=demisto.getParam("server"), crads=demisto.getParam("authentication"), insecure=demisto.getParam("insecure"), domain=demisto.getParam("domain"), ) token = get_token_from_response(resp) if current_command == "test-module" and token: # This is the call made when pressing the integration test button. demisto.results("ok") if current_command == "sep-system-info": system_info_command(token) if current_command == "sep-client-content": client_content_command(token) if current_command == "sep-endpoints-info": endpoints_info_command(token) if current_command == "sep-groups-info": groups_info_command(token) if current_command == "sep-command-status": command_status(token) if current_command == "sep-list-policies": list_policies_command(token) if current_command == "sep-assign-policy": assign_policie_command(token) if current_command == "sep-list-locations": list_locations_command(token) if current_command == "sep-endpoint-quarantine": endpoint_quarantine_command(token) if current_command == "sep-scan-endpoint": scan_endpoint_command(token) if current_command == "sep-update-endpoint-content": update_endpoint_content_command(token) if current_command == "sep-move-client-to-group": move_client_to_group_command(token) if current_command == "sep-identify-old-clients": old_clients_command(token) except Exception as ex: return_error(f"Cannot perform the command: {current_command}. Error: {ex}") if __name__ in ("__main__", "__builtin__", "builtins"): main()