Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merging next to main for release 3.1.0 #15

Merged
merged 2 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/phantomcyber/dev-cicd-tools
rev: v1.16
rev: v1.17
hooks:
- id: org-hook
- id: package-app-dependencies
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright (c) 2014-2023 Splunk Inc.
Copyright (c) 2014-2024 Splunk Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion NOTICE
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Splunk SOAR Cisco ISE
Copyright (c) 2014-2023 Splunk Inc.
Copyright (c) 2014-2024 Splunk Inc.

Third-party Software Attributions:

Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
# Cisco ISE

Publisher: Splunk
Connector Version: 3.0.2
Connector Version: 3.1.0
Product Vendor: Cisco Systems
Product Name: Cisco ISE
Product Version Supported (regex): "/([2].[67])|([3].[01])/"
Minimum Product Version: 5.1.0
Minimum Product Version: 6.2.0

This app implements investigative and containment actions on a Cisco ISE device

[comment]: # " File: README.md"
[comment]: # " Copyright (c) 2014-2023 Splunk Inc."
[comment]: # " Copyright (c) 2014-2024 Splunk Inc."
[comment]: # ""
[comment]: # " SPLUNK CONFIDENTIAL - Use or disclosure of this material in whole or in part"
[comment]: # " without a valid written license from Splunk Inc. is PROHIBITED."
Expand Down Expand Up @@ -52,7 +52,9 @@ This app implements investigative and containment actions on a Cisco ISE device
- update resource
- apply policy
- create policy
3. An ISE node can assume any or all of the following personas: Administration, Policy Service, and
3. If resource is **Guest User** in resource related actions, it is required to use **Sponsor Account** credentials to access the GuestAPI, For creating sponsor account refer this document: [Set Up Admin and Sponsor Account for ERS](https://www.cisco.com/c/en/us/support/docs/security/identity-services-engine/215476-configure-ise-guest-accounts-with-rest-a.html)
4. Once you have internal user created from step #3, Add username and password in **ers_username** and **ers_password** in asset configuration respectively.
5. An ISE node can assume any or all of the following personas: Administration, Policy Service, and
Monitoring. For detailed info: [Types of
nodes](https://www.cisco.com/en/US/docs/security/ise/1.0/user_guide/ise10_dis_deploy.html#wp1123452)
- All actions can run on Administration node.
Expand All @@ -61,7 +63,7 @@ This app implements investigative and containment actions on a Cisco ISE device
node
- Actions quarantine device, unquarantine device, apply policy, clear policy, and terminate
session can run on Policy Service node
4. For create resource action, user needs to provide valid json with required fields of that
6. For create resource action, user needs to provide valid json with required fields of that
specified resource (For more details head over to [API
Reference](https://developer.cisco.com/docs/identity-services-engine/v1/#!endpoint) ). Examples
as below
Expand Down
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File: __init__.py
#
# Copyright (c) 2014-2023 Splunk Inc.
# Copyright (c) 2014-2024 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
12 changes: 6 additions & 6 deletions ciscoise.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@
"publisher": "Splunk",
"type": "network security",
"main_module": "ciscoise_connector.py",
"app_version": "3.0.2",
"app_version": "3.1.0",
"utctime_updated": "2022-03-11T04:16:56.000000Z",
"package_name": "phantom_ciscoise",
"product_vendor": "Cisco Systems",
"product_name": "Cisco ISE",
"product_version_regex": "/([2].[67])|([3].[01])/",
"min_phantom_version": "5.1.0",
"min_phantom_version": "6.2.0",
"logo": "logo_cisco.svg",
"logo_dark": "logo_cisco_dark.svg",
"license": "Copyright (c) 2014-2023 Splunk Inc.",
"license": "Copyright (c) 2014-2024 Splunk Inc.",
"python_version": "3",
"fips_compliant": true,
"latest_tested_version": [
"Cisco ISE version 3.0.0.458, 22th Feb 2022"
"Cisco ISE version 3.0.0.458, 29 Apr 2024"
],
"configuration": {
"device": {
Expand Down Expand Up @@ -1921,12 +1921,12 @@
},
{
"module": "setuptools",
"input_file": "wheels/py3/setuptools-69.0.2-py3-none-any.whl"
"input_file": "wheels/py3/setuptools-69.2.0-py3-none-any.whl"
},
{
"module": "xmltodict",
"input_file": "wheels/shared/xmltodict-0.12.0-py2.py3-none-any.whl"
}
]
}
}
}
149 changes: 104 additions & 45 deletions ciscoise_connector.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File: ciscoise_connector.py
#
# Copyright (c) 2014-2023 Splunk Inc.
# Copyright (c) 2014-2024 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -16,6 +16,7 @@
#
# Phantom imports
import json
import sys

import phantom.app as phantom
import requests
Expand Down Expand Up @@ -49,7 +50,6 @@ class CiscoISEConnector(BaseConnector):
ACTION_ID_DELETE_POLICY = "delete_policy"

def __init__(self):

# Call the BaseConnectors init first
super(CiscoISEConnector, self).__init__()

Expand Down Expand Up @@ -116,14 +116,16 @@ def make_another_call(*args, **kwargs):

return make_another_call

def _call_ers_api(self, endpoint, action_result, data=None, allow_unknown=True, method="get", try_ha_device=False):
def _call_ers_api(self, endpoint, action_result, data=None, allow_unknown=True, method="get", try_ha_device=False, params=None):
auth_method = self._ers_auth or self._auth
if not auth_method:
return action_result.set_status(phantom.APP_ERROR, CISCOISE_ERS_CRED_MISSING), None
url = "{0}{1}".format(self._base_url, endpoint)
if try_ha_device:
url = "{0}{1}".format(self._ha_device_url, endpoint)

self.debug_print("url for calling an ERS API: {}".format(url))

ret_data = None

config = self.get_config()
Expand All @@ -140,8 +142,10 @@ def _call_ers_api(self, endpoint, action_result, data=None, allow_unknown=True,
json=data,
verify=verify,
headers=headers,
auth=auth_method
auth=auth_method,
params=params
)

except Exception as e:
self.debug_print("Exception occurred: {}".format(e))
return action_result.set_status(phantom.APP_ERROR, CISCOISE_ERROR_REST_API, e), ret_data
Expand Down Expand Up @@ -424,44 +428,43 @@ def _terminate_session(self, param):

return action_result.set_status(phantom.APP_SUCCESS, CISCOISE_SUCC_SESSION_TERMINATED)

def _paginator(self, endpoint, action_result, payload=None, limit=None):
def _paginator(self, endpoint, action_result, limit=None):

items_list = list()

if not payload:
payload = {}

page = 1
payload["size"] = DEFAULT_MAX_RESULTS
payload["page"] = page
params = {}
if limit:
params["size"] = min(DEFAULT_MAX_RESULTS, limit)
else:
params["size"] = DEFAULT_MAX_RESULTS

while True:
ret_val, items = self._call_ers_api(endpoint, action_result, data=payload)

ret_val, items = self._call_ers_api(endpoint, action_result, params=params)
if phantom.is_fail(ret_val):
self.debug_print("Call to ERS API Failed")
return None
items_from_page = items.get("SearchResult", {}).get("resources", [])

items_list.extend(items.get("SearchResult", {}).get("resources"))
items_list.extend(items_from_page)
self.debug_print("Retrieved {} records from the endpoint {}".format(len(items_from_page), endpoint))

next_page_dict = items.get("SearchResult", {}).get("nextPage")

if limit and len(items_list) >= limit:
self.debug_print("Maximum limit reached")
return items_list[:limit]

if len(items.get("SearchResult", {}).get("resources")) < DEFAULT_MAX_RESULTS:
break

if len(items_list) == items.get("SearchResult", {}).get("total"):
break

page = page + 1
payload["page"] = page

return items_list
else:
if not next_page_dict:
self.debug_print("No more records left to retrieve")
return items_list
else:
endpoint = next_page_dict.get("href").replace(self._base_url, "")
self.debug_print("Next page available")

def _list_resources(self, param):

action_result = self.add_action_result(ActionResult(dict(param)))
resource = self._map_resource_type(param["resource"], action_result)
ret_val, max_result = self._validate_integers(action_result, param.get("max_results"), 'max_result')
ret_val, max_result = self._validate_integers(action_result, param.get("max_results"), 'max results')

if phantom.is_fail(ret_val):
return action_result.get_status()
Expand Down Expand Up @@ -707,30 +710,39 @@ def _test_connectivity_to_device(self, base_url, verify=True):
auth=self._auth,
verify=verify)
except Exception as e:
self.debug_print("Exception is test connectivity: {}".format(e))
return self.set_status_save_progress(phantom.APP_ERROR, CISCOISE_ERROR_TEST_CONNECTIVITY_FAILED)
return False, str(e)

if resp.status_code == 200:
return self.set_status_save_progress(phantom.APP_SUCCESS, CISCOISE_SUCC_TEST_CONNECTIVITY_PASSED)
else:
return self.set_status_save_progress(
phantom.APP_ERROR,
CISCOISE_TEST_CONNECTIVITY_FAILED_ERROR_CODE,
code=resp.status_code
)
return True, ''

def _test_connectivity(self, param):
return False, resp.text

def _test_connectivity(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
config = self.get_config()
verify = config[phantom.APP_JSON_VERIFY]
self.save_progress("Connecting to first device")
result = self._test_connectivity_to_device(self._base_url, verify)
result, message = self._test_connectivity_to_device(self._base_url, verify)

if not result:
self.save_progress("Error occurred while connecting to primary device")
self.save_progress(str(message))
self.save_progress(CISCOISE_ERROR_TEST_CONNECTIVITY_FAILED_PRIMARY_DEVICE)
action_result.set_status(phantom.APP_ERROR)
else:
self.save_progress(CISCOISE_SUCC_TEST_CONNECTIVITY_PASSED_1)
action_result.set_status(phantom.APP_SUCCESS, CISCOISE_SUCC_TEST_CONNECTIVITY_PASSED_1)

if self._ha_device:
self.save_progress("Connecting to second device")
result = self._test_connectivity_to_device(self._ha_device_url, verify)
result, message = self._test_connectivity_to_device(self._ha_device_url, verify)

return result
if not result:
self.save_progress("Error occurred while connecting to high availability device")
self.save_progress(str(message))
self.save_progress(CISCOISE_ERROR_TEST_CONNECTIVITY_FAILED_HA_DEVICE)
else:
self.save_progress(CISCOISE_SUCC_TEST_CONNECTIVITY_PASSED_2)

return action_result.get_status()

def handle_action(self, param):

Expand Down Expand Up @@ -775,17 +787,60 @@ def handle_action(self, param):
return result


if __name__ == "__main__":
if __name__ == '__main__':

import sys
import argparse

import pudb

pudb.set_trace()

argparser = argparse.ArgumentParser()

argparser.add_argument('input_test_json', help='Input Test JSON file')
argparser.add_argument('-u', '--username', help='username', required=False)
argparser.add_argument('-p', '--password', help='password', required=False)
argparser.add_argument('-v', '--verify', action='store_true', help='verify', required=False, default=False)

args = argparser.parse_args()
session_id = None

username = args.username
password = args.password
verify = args.verify

if username is not None and password is None:

# User specified a username but not a password, so ask
import getpass
password = getpass.getpass("Password: ")

if username and password:
login_url = BaseConnector._get_phantom_base_url() + "login"
try:
print("Accessing the Login page")
r = requests.get(login_url, verify=verify, timeout=30)
csrftoken = r.cookies['csrftoken']

data = dict()
data['username'] = username
data['password'] = password
data['csrfmiddlewaretoken'] = csrftoken

headers = dict()
headers['Cookie'] = 'csrftoken=' + csrftoken
headers['Referer'] = login_url

print("Logging into Platform to get the session id")
r2 = requests.post(login_url, verify=verify, data=data, headers=headers, timeout=30)
session_id = r2.cookies['sessionid']
except Exception as e:
print("Unable to get session id from the platfrom. Error: " + str(e))
sys.exit(1)

if len(sys.argv) < 2:
print("No test json specified as input")
sys.exit(1)
sys.exit(0)

with open(sys.argv[1]) as f:
in_json = f.read()
Expand All @@ -794,6 +849,10 @@ def handle_action(self, param):

connector = CiscoISEConnector()
connector.print_progress_message = True

if session_id is not None:
in_json['user_session_token'] = session_id

ret_val = connector._handle_action(json.dumps(in_json), None)
print(json.dumps(json.loads(ret_val), indent=4))

Expand Down
12 changes: 7 additions & 5 deletions ciscoise_consts.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File: ciscoise_consts.py
#
# Copyright (c) 2014-2023 Splunk Inc.
# Copyright (c) 2014-2024 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -33,9 +33,11 @@
ERS_POLICIES = ":9060/ers/config/ancpolicy"

# Error/Success
CISCOISE_ERROR_TEST_CONNECTIVITY_FAILED = "Test connectivity failed"
CISCOISE_ERROR_TEST_CONNECTIVITY_FAILED_PRIMARY_DEVICE = "Test connectivity failed for primary device"
CISCOISE_ERROR_TEST_CONNECTIVITY_FAILED_HA_DEVICE = "Test connectivity failed for high availability device"
CISCOISE_TEST_CONNECTIVITY_FAILED_ERROR_CODE = "Test connectivity failed with status code: '{code}'"
CISCOISE_SUCC_TEST_CONNECTIVITY_PASSED = "Test connectivity passed"
CISCOISE_SUCC_TEST_CONNECTIVITY_PASSED_1 = "Test connectivity passed for primary device"
CISCOISE_SUCC_TEST_CONNECTIVITY_PASSED_2 = "Test connectivity passed for second device"
CISCOISE_ERROR_REST_API = "REST Api error"
CISCOISE_REST_API_ERROR_CODE = "REST Api error with status code: {code}, Message from server: {message}"
CISCOISE_ERROR_UNABLE_TO_PARSE_REPLY = "Parsing error, Unable to convert xml reply to json"
Expand All @@ -48,10 +50,10 @@
CISCOISE_SUCC_LIST_ENDPOINTS = "{0} Endpoints found"
CISCOISE_SUCC_GET_ENDPOINT = "Endpoint found"
CISCOISE_SUCC_UPDATE_ENDPOINT = "Endpoint updated"
CISCOISE_ERROR_INVALID_PARAM = "Please provide a non-zero positive integer in {param}"
CISCOISE_ERROR_INVALID_PARAM = "Please provide a non-zero positive integer in '{}'"
CISCOISE_MAP_IP_ABSENT_ERROR = "Please provide either mac address or ip address"
CISCOISE_ERS_CRED_MISSING = "ERS credentials in asset configuration are required for this action"
DEFAULT_MAX_RESULTS = 7
DEFAULT_MAX_RESULTS = 100

# Json reply schema
IS_MAC_QUARAN_RESP_SCHEMA = {
Expand Down
Loading
Loading