diff --git a/Packs/RecordedFuture/Integrations/RecordedFuture/RecordedFuture.py b/Packs/RecordedFuture/Integrations/RecordedFuture/RecordedFuture.py
index 24298c5a947..5ee2b68426b 100644
--- a/Packs/RecordedFuture/Integrations/RecordedFuture/RecordedFuture.py
+++ b/Packs/RecordedFuture/Integrations/RecordedFuture/RecordedFuture.py
@@ -15,7 +15,7 @@
# disable insecure warnings
requests.packages.urllib3.disable_warnings() # type: ignore
-__version__ = '2.5.1'
+__version__ = "2.5.2"
# === === === === === === === === === === === === === === ===
@@ -39,47 +39,47 @@ def determine_hash(hash_value: str) -> str:
"""Determine hash type by length."""
hash_length = len(hash_value)
if hash_length == 128:
- return 'SHA512'
+ return "SHA512"
elif hash_length == 64:
- return 'SHA256'
+ return "SHA256"
elif hash_length == 40:
- return 'SHA1'
+ return "SHA1"
elif hash_length == 32:
- return 'MD5'
+ return "MD5"
elif hash_length == 8:
- return 'CRC32'
+ return "CRC32"
else:
- return 'CTPH'
+ return "CTPH"
def create_indicator(
entity: str,
entity_type: str,
score: int,
- description: str = '',
+ description: str = "",
location: Dict[str, Any] = None,
) -> Common.Indicator:
"""Create an Indicator object."""
demisto_params = demisto.params()
if location is None:
- location = dict()
+ location = {}
thresholds = {
- 'file': int(demisto_params.get('file_threshold', 65)),
- 'ip': int(demisto_params.get('ip_threshold', 65)),
- 'domain': int(demisto_params.get('domain_threshold', 65)),
- 'url': int(demisto_params.get('url_threshold', 65)),
- 'cve': int(demisto_params.get('cve_threshold', 65)),
+ "file": int(demisto_params.get("file_threshold", 65)),
+ "ip": int(demisto_params.get("ip_threshold", 65)),
+ "domain": int(demisto_params.get("domain_threshold", 65)),
+ "url": int(demisto_params.get("url_threshold", 65)),
+ "cve": int(demisto_params.get("cve_threshold", 65)),
}
dbot_score = translate_score(score, thresholds[entity_type])
dbot_description = (
- f'Score above {thresholds[entity_type]}'
+ f"Score above {thresholds[entity_type]}"
if dbot_score == Common.DBotScore.BAD
- else ''
+ else ""
)
- dbot_vendor = 'Recorded Future v2'
- if entity_type == 'ip':
+ dbot_vendor = "Recorded Future v2"
+ if entity_type == "ip":
return Common.IP(
entity,
Common.DBotScore(
@@ -88,12 +88,12 @@ def create_indicator(
dbot_vendor,
dbot_score,
dbot_description,
- reliability=demisto.params().get('integrationReliability'),
+ reliability=demisto.params().get("integrationReliability"),
),
- asn=location.get('asn', None),
- geo_country=location.get('location', dict()).get('country', None),
+ asn=location.get("asn", None),
+ geo_country=location.get("location", {}).get("country", None),
)
- elif entity_type == 'domain':
+ elif entity_type == "domain":
return Common.Domain(
entity,
Common.DBotScore(
@@ -102,32 +102,32 @@ def create_indicator(
dbot_vendor,
dbot_score,
dbot_description,
- reliability=demisto.params().get('integrationReliability'),
+ reliability=demisto.params().get("integrationReliability"),
),
)
- elif entity_type == 'file':
+ elif entity_type == "file":
dbot_obj = Common.DBotScore(
entity,
DBotScoreType.FILE,
dbot_vendor,
dbot_score,
dbot_description,
- reliability=demisto.params().get('integrationReliability'),
+ reliability=demisto.params().get("integrationReliability"),
)
hash_type = determine_hash(entity)
- if hash_type == 'MD5':
+ if hash_type == "MD5":
return Common.File(dbot_obj, md5=entity)
- elif hash_type == 'SHA1':
+ elif hash_type == "SHA1":
return Common.File(dbot_obj, sha1=entity)
- elif hash_type == 'SHA256':
+ elif hash_type == "SHA256":
return Common.File(dbot_obj, sha256=entity)
- elif hash_type == 'SHA512':
+ elif hash_type == "SHA512":
return Common.File(dbot_obj, sha512=entity)
else:
return Common.File(dbot_obj)
- elif entity_type == 'cve':
- return Common.CVE(entity, '', '', '', description)
- elif entity_type == 'url':
+ elif entity_type == "cve":
+ return Common.CVE(entity, "", "", "", description)
+ elif entity_type == "url":
return Common.URL(
entity,
Common.DBotScore(
@@ -136,12 +136,12 @@ def create_indicator(
dbot_vendor,
dbot_score,
dbot_description,
- reliability=demisto.params().get('integrationReliability'),
+ reliability=demisto.params().get("integrationReliability"),
),
)
else:
raise Exception(
- f'Could not create indicator for this type of entity: {entity_type}'
+ f"Could not create indicator for this type of entity: {entity_type}"
)
@@ -154,8 +154,8 @@ class Client(BaseClient):
def whoami(self) -> Dict[str, Any]:
return self._http_request(
- method='get',
- url_suffix='info/whoami',
+ method="get",
+ url_suffix="info/whoami",
timeout=60,
)
@@ -164,45 +164,45 @@ def _key_extraction(self, data, keys_to_keep):
def _clean_calling_context(self, calling_context):
calling_context_keys_to_keep = {"args", "command", "params", "context"}
- context_keys_to_keep = {
- "Incidents",
- "IntegrationInstance",
- "ParentEntry"
- }
- incidents_keys_to_keep = {
- "name",
- "type",
- "id"
- }
- parent_entry_keys_to_keep = {
- "entryTask",
- "scheduled",
- "recurrent"
- }
+ context_keys_to_keep = {"Incidents", "IntegrationInstance", "ParentEntry"}
+ incidents_keys_to_keep = {"name", "type", "id"}
+ parent_entry_keys_to_keep = {"entryTask", "scheduled", "recurrent"}
if context := calling_context.get("context", None):
context = self._key_extraction(context, context_keys_to_keep)
if incidents := context.get("Incidents", {}):
- incidents = [self._key_extraction(incident, incidents_keys_to_keep) for incident in incidents]
+ incidents = [
+ self._key_extraction(incident, incidents_keys_to_keep)
+ for incident in incidents
+ ]
context["Incidents"] = incidents
if parent_entry := context.get("ParentEntry", {}):
- parent_entry = self._key_extraction(parent_entry, parent_entry_keys_to_keep)
+ parent_entry = self._key_extraction(
+ parent_entry, parent_entry_keys_to_keep
+ )
context["ParentEntry"] = parent_entry
calling_context["context"] = context
- calling_context = self._key_extraction(calling_context, calling_context_keys_to_keep)
+ calling_context = self._key_extraction(
+ calling_context, calling_context_keys_to_keep
+ )
return calling_context
def _get_writeback_data(self):
if (
- demisto.params().get('collective_insights') == "On"
- and demisto.callingContext
- ):
+ demisto.params().get("collective_insights") == "On"
+ and demisto.args().get("collective_insights") != "off"
+ ) or demisto.args().get("collective_insights") == "on":
+ do_track = True
+ else:
+ do_track = False
+
+ if do_track and demisto.callingContext:
calling_context = copy.deepcopy(demisto.callingContext)
- calling_context.get('context', dict()).pop('ExecutionContext', None)
+ calling_context.get("context", {}).pop("ExecutionContext", None)
calling_context = self._clean_calling_context(calling_context)
return calling_context
@@ -211,17 +211,17 @@ def _get_writeback_data(self):
def _call(self, url_suffix, **kwargs):
json_data = {
- 'demisto_command': demisto.command(),
- 'demisto_args': demisto.args(),
+ "demisto_command": demisto.command(),
+ "demisto_args": demisto.args(),
}
request_kwargs = {
- 'method': 'post',
- 'url_suffix': url_suffix,
- 'json_data': json_data,
- 'timeout': 90,
- 'retries': 3,
- 'status_list_to_retry': STATUS_TO_RETRY,
+ "method": "post",
+ "url_suffix": url_suffix,
+ "json_data": json_data,
+ "timeout": 90,
+ "retries": 3,
+ "status_list_to_retry": STATUS_TO_RETRY,
}
request_kwargs.update(kwargs)
@@ -229,23 +229,23 @@ def _call(self, url_suffix, **kwargs):
# This need to be after 'request_kwargs.update(kwargs)'.
calling_context = self._get_writeback_data()
if calling_context:
- request_kwargs['json_data']['callingContext'] = calling_context
+ request_kwargs["json_data"]["callingContext"] = calling_context
try:
response = self._http_request(**request_kwargs)
- if isinstance(response, dict) and response.get('return_error'):
+ if isinstance(response, dict) and response.get("return_error"):
# This will raise the Exception or call "demisto.results()" for the error and sys.exit(0).
- return_error(**response['return_error'])
+ return_error(**response["return_error"])
except DemistoException as err:
- if '404' in str(err):
+ if "404" in str(err):
return CommandResults(
- outputs_prefix='',
- outputs=dict(),
- raw_response=dict(),
- readable_output='No results found.',
- outputs_key_field='',
+ outputs_prefix="",
+ outputs={},
+ raw_response={},
+ readable_output="No results found.",
+ outputs_key_field="",
)
else:
raise err
@@ -255,53 +255,53 @@ def _call(self, url_suffix, **kwargs):
def fetch_incidents(self) -> Dict[str, Any]:
"""Fetch incidents."""
return self._call(
- url_suffix=f'/v2/alert/fetch_incidents',
+ url_suffix="/v2/alert/fetch_incidents",
json_data={
- 'demisto_command': demisto.command(),
- 'demisto_args': demisto.args(),
- 'demisto_params': demisto.params(),
- 'demisto_last_run': demisto.getLastRun(),
+ "demisto_command": demisto.command(),
+ "demisto_args": demisto.args(),
+ "demisto_params": demisto.params(),
+ "demisto_last_run": demisto.getLastRun(),
},
timeout=120,
)
def entity_search(self) -> Dict[str, Any]:
"""Search for entities with entity type."""
- return self._call(url_suffix='/v2/search')
+ return self._call(url_suffix="/v2/search")
def entity_lookup(self) -> Dict[str, Any]:
"""Entity lookup."""
- return self._call(url_suffix='/v2/lookup/reputation', timeout=120)
+ return self._call(url_suffix="/v2/lookup/reputation", timeout=120)
def get_intelligence(self) -> Dict[str, Any]:
"""Entity enrich."""
- return self._call(url_suffix='/v2/lookup/intelligence')
+ return self._call(url_suffix="/v2/lookup/intelligence")
def get_links(self) -> Dict[str, Any]:
"""Entity enrich."""
- return self._call(url_suffix='/v2/lookup/links')
+ return self._call(url_suffix="/v2/lookup/links")
def get_single_alert(self) -> dict:
"""Get a single alert"""
- return self._call(url_suffix='/v2/alert/lookup')
+ return self._call(url_suffix="/v2/alert/lookup")
def get_alerts(self) -> Dict[str, Any]:
"""Get alerts."""
- return self._call(url_suffix='/v2/alert/search')
+ return self._call(url_suffix="/v2/alert/search")
def get_alert_rules(self) -> Dict[str, Any]:
"""Get alert rules."""
- return self._call(url_suffix='/v2/alert/rule')
+ return self._call(url_suffix="/v2/alert/rule")
def alert_set_status(self, data=None):
"""Update alert."""
# If data is None - we have alert_id and status in demisto.args().
return self._call(
- url_suffix='/v2/alert/set_status',
+ url_suffix="/v2/alert/set_status",
json_data={
- 'demisto_command': demisto.command(),
- 'demisto_args': demisto.args(),
- 'alerts_update_data': data,
+ "demisto_command": demisto.command(),
+ "demisto_args": demisto.args(),
+ "alerts_update_data": data,
},
)
@@ -309,29 +309,29 @@ def alert_set_note(self, data=None):
"""Update alert."""
# If data is None - we have alert_id and note in demisto.args().
return self._call(
- url_suffix='/v2/alert/set_note',
+ url_suffix="/v2/alert/set_note",
json_data={
- 'demisto_command': demisto.command(),
- 'demisto_args': demisto.args(),
- 'alerts_update_data': data,
+ "demisto_command": demisto.command(),
+ "demisto_args": demisto.args(),
+ "alerts_update_data": data,
},
)
def get_triage(self) -> Dict[str, Any]:
"""SOAR triage lookup."""
- return self._call(url_suffix='/v2/lookup/triage')
+ return self._call(url_suffix="/v2/lookup/triage")
def get_threat_map(self) -> Dict[str, Any]:
- return self._call(url_suffix='/v2/threat/actors')
+ return self._call(url_suffix="/v2/threat/actors")
def get_threat_links(self) -> Dict[str, Any]:
- return self._call(url_suffix='/v2/links/search')
+ return self._call(url_suffix="/v2/links/search")
def get_detection_rules(self) -> Dict[str, Any]:
- return self._call(url_suffix='/v2/detection_rules/search')
+ return self._call(url_suffix="/v2/detection_rules/search")
def submit_detection_to_collective_insight(self) -> Dict[str, Any]:
- return self._call(url_suffix='/v2/collective-insights/detections')
+ return self._call(url_suffix="/v2/collective-insights/detections")
# === === === === === === === === === === === === === === ===
@@ -354,32 +354,32 @@ def _process_result_actions(
# In case API returned a str - we don't want to call "response.get()" on a str object.
return None # type: ignore
- result_actions: Union[List[dict], None] = response.get('result_actions')
+ result_actions: Union[List[dict], None] = response.get("result_actions")
if not result_actions:
return None # type: ignore
command_results: List[CommandResults] = list()
for action in result_actions:
- if 'create_indicator' in action:
- indicator = create_indicator(**action['create_indicator'])
- if 'CommandResults' in action:
+ if "create_indicator" in action:
+ indicator = create_indicator(**action["create_indicator"])
+ if "CommandResults" in action:
# Custom CommandResults.
- command_results_kwargs = action['CommandResults']
- command_results_kwargs['indicator'] = indicator
+ command_results_kwargs = action["CommandResults"]
+ command_results_kwargs["indicator"] = indicator
command_results.append(CommandResults(**command_results_kwargs))
else:
# Default CommandResults after indicator creation.
command_results.append(
CommandResults(
readable_output=tableToMarkdown(
- 'New indicator was created.', indicator.to_context()
+ "New indicator was created.", indicator.to_context()
),
indicator=indicator,
)
)
- elif 'CommandResults' in action:
- command_results.append(CommandResults(**action['CommandResults']))
+ elif "CommandResults" in action:
+ command_results.append(CommandResults(**action["CommandResults"]))
return command_results
@@ -391,14 +391,17 @@ def fetch_incidents(self) -> None:
# 404 case.
return
- if response.get('incidents') is not None:
- incidents = response['incidents']
- demisto_last_run = response['demisto_last_run']
+ if (
+ response.get("incidents") is not None
+ and response.get("demisto_last_run")
+ ):
+ incidents = response["incidents"]
+ demisto_last_run = response["demisto_last_run"]
demisto.incidents(incidents)
demisto.setLastRun(demisto_last_run)
- update_alert_status = response.pop('alerts_update_data', None)
+ update_alert_status = response.pop("alerts_update_data", None)
if update_alert_status:
self.client.alert_set_status(update_alert_status)
@@ -470,6 +473,7 @@ def collective_insight_command(self) -> List[CommandResults]:
response = self.client.submit_detection_to_collective_insight()
return self._process_result_actions(response=response)
+
# === === === === === === === === === === === === === === ===
# === === === === === === === MAIN === === === === === === ==
# === === === === === === === === === === === === === === ===
@@ -479,17 +483,19 @@ def main() -> None: # pragma: no cover
"""Main method used to run actions."""
try:
demisto_params = demisto.params()
- base_url = demisto_params.get('server_url', '').rstrip('/')
- verify_ssl = not demisto_params.get('insecure', False)
- proxy = demisto_params.get('proxy', False)
- api_token = demisto_params.get("token_credential", {}).get("password") or demisto_params.get("token")
+ base_url = demisto_params.get("server_url", "").rstrip("/")
+ verify_ssl = not demisto_params.get("insecure", False)
+ proxy = demisto_params.get("proxy", False)
+ api_token = demisto_params.get("token_credential", {}).get(
+ "password"
+ ) or demisto_params.get("token")
if not api_token:
- return_error('Please provide a valid API token')
+ return_error("Please provide a valid API token")
headers = {
- 'X-RFToken': api_token,
- 'X-RF-User-Agent': (
- f'RecordedFuture.py/{__version__} ({platform.platform()}) '
- f'XSOAR/{__version__} '
+ "X-RFToken": api_token,
+ "X-RF-User-Agent": (
+ f"RecordedFuture.py/{__version__} ({platform.platform()}) "
+ f"XSOAR/{__version__} "
f'RFClient/{__version__} (Cortex_XSOAR_{demisto.demistoVersion()["version"]})'
),
}
@@ -499,7 +505,7 @@ def main() -> None: # pragma: no cover
command = demisto.command()
actions = Actions(client)
- if command == 'test-module':
+ if command == "test-module":
# This is the call made when pressing the integration Test button.
# Returning 'ok' indicates that the integration works like it suppose to and
# connection to the service is successful.
@@ -508,65 +514,68 @@ def main() -> None: # pragma: no cover
try:
client.whoami()
- return_results('ok')
+ return_results("ok")
except Exception as err:
message = str(err)
try:
- error = json.loads(str(err).split('\n')[1])
- if 'fail' in error.get('result', dict()).get('status', ''):
- message = error.get('result', dict())['message']
+ error = json.loads(str(err).split("\n")[1])
+ if "fail" in error.get("result", {}).get("status", ""):
+ message = error.get("result", {})["message"]
except Exception:
message = (
- 'Unknown error. Please verify that the API'
- f' URL and Token are correctly configured. RAW Error: {err}'
+ "Unknown error. Please verify that the API"
+ f" URL and Token are correctly configured. RAW Error: {err}"
)
- raise DemistoException(f'Failed due to - {message}')
+ raise DemistoException(f"Failed due to - {message}")
- elif command == 'fetch-incidents':
+ elif command == "fetch-incidents":
actions.fetch_incidents()
- elif command == 'recordedfuture-malware-search':
+ elif command == "recordedfuture-malware-search":
return_results(actions.malware_search_command())
- elif command in ['url', 'ip', 'domain', 'file', 'cve']:
+ elif command in ["url", "ip", "domain", "file", "cve"]:
return_results(actions.lookup_command())
- elif command == 'recordedfuture-intelligence':
+ elif command == "recordedfuture-intelligence":
return_results(actions.intelligence_command())
- elif command == 'recordedfuture-links':
+ elif command == "recordedfuture-links":
return_results(actions.get_links_command())
- elif command == 'recordedfuture-single-alert':
+ elif command == "recordedfuture-single-alert":
return_results(actions.get_single_alert_command())
- elif command == 'recordedfuture-alerts':
+ elif command == "recordedfuture-alerts":
return_results(actions.get_alerts_command())
- elif command == 'recordedfuture-alert-rules':
+ elif command == "recordedfuture-alert-rules":
return_results(actions.get_alert_rules_command())
- elif command == 'recordedfuture-alert-set-status':
+ elif command == "recordedfuture-alert-set-status":
return_results(actions.alert_set_status_command())
- elif command == 'recordedfuture-alert-set-note':
+ elif command == "recordedfuture-alert-set-note":
return_results(actions.alert_set_note_command())
- elif command == 'recordedfuture-threat-assessment':
+ elif command == "recordedfuture-threat-assessment":
return_results(actions.triage_command())
- elif command == 'recordedfuture-threat-map':
+ elif command == "recordedfuture-threat-map":
return_results(actions.threat_actors_command())
- elif command == 'recordedfuture-threat-links':
+ elif command == "recordedfuture-threat-links":
return_results(actions.threat_links_command())
- elif command == 'recordedfuture-detection-rules':
+ elif command == "recordedfuture-detection-rules":
return_results(actions.detection_rules_command())
- elif command == 'recordedfuture-collective-insight':
+ elif command == "recordedfuture-collective-insight":
return_results(actions.collective_insight_command())
except Exception as e:
- return_error(message=f'Failed to execute {demisto.command()} command: {str(e)}')
+ return_error(
+ message=f"Failed to execute {demisto.command()} command: {str(e)}",
+ error=e,
+ )
-if __name__ in ('__main__', '__builtin__', 'builtins'):
+if __name__ in ("__main__", "__builtin__", "builtins"):
main()
diff --git a/Packs/RecordedFuture/Integrations/RecordedFuture/RecordedFuture.yml b/Packs/RecordedFuture/Integrations/RecordedFuture/RecordedFuture.yml
index 365771117dc..b52312578c2 100644
--- a/Packs/RecordedFuture/Integrations/RecordedFuture/RecordedFuture.yml
+++ b/Packs/RecordedFuture/Integrations/RecordedFuture/RecordedFuture.yml
@@ -154,7 +154,7 @@ script:
script: ''
type: python
subtype: python3
- dockerimage: demisto/python3:3.10.14.91134
+ dockerimage: demisto/python3:3.11.10.111039
commands:
- name: domain
description: Gets a quick indicator of the risk associated with a domain.
@@ -164,6 +164,14 @@ script:
default: true
isArray: true
description: The domain for which to get the reputation.
+ - name: collective_insights
+ description: Save IOC to Collective Insights? If not specified - uses demisto param setting value.
+ required: false
+ default: false
+ auto: PREDEFINED
+ predefined:
+ - on
+ - off
outputs:
- contextPath: DBotScore.Indicator
description: The indicator that was tested.
@@ -231,6 +239,14 @@ script:
default: true
isArray: true
description: IP address for which to get the reputation.
+ - name: collective_insights
+ description: Save IOC to Collective Insights? If not specified - uses demisto param setting value.
+ required: false
+ default: false
+ auto: PREDEFINED
+ predefined:
+ - on
+ - off
outputs:
- contextPath: DBotScore.Indicator
description: The indicator that was tested.
@@ -298,6 +314,14 @@ script:
isArray: true
default: true
description: File hash for which to check the reputation. Can be an MD5, SHA1, SHA256, SHA512, CRC32 or CTPH.
+ - name: collective_insights
+ description: Save IOC to Collective Insights? If not specified - uses demisto param setting value.
+ required: false
+ default: false
+ auto: PREDEFINED
+ predefined:
+ - on
+ - off
outputs:
- contextPath: DBotScore.Indicator
description: The indicator that was tested.
@@ -380,6 +404,14 @@ script:
default: true
isArray: true
description: CVE for which to get the reputation.
+ - name: collective_insights
+ description: Save IOC to Collective Insights? If not specified - uses demisto param setting value.
+ required: false
+ default: false
+ auto: PREDEFINED
+ predefined:
+ - on
+ - off
outputs:
- contextPath: DBotScore.Indicator
description: The indicator that was tested.
@@ -441,6 +473,14 @@ script:
default: true
isArray: true
description: URL for which to get the reputation.
+ - name: collective_insights
+ description: Save IOC to Collective Insights? If not specified - uses demisto param setting value.
+ required: false
+ default: false
+ auto: PREDEFINED
+ predefined:
+ - on
+ - off
outputs:
- contextPath: DBotScore.Indicator
description: The indicator that was tested.
@@ -2866,6 +2906,7 @@ script:
- contextPath: RecordedFuture.ThreatMap.links
description: Recorded Future threat actor links by type.
type: string
+
- name: recordedfuture-threat-links
description: Search links.
arguments:
@@ -2923,6 +2964,7 @@ script:
description: Recorded Future link section.
- contextPath: RecordedFuture.Links.links.attributes
description: Recorded Future link attributes.
+
- name: recordedfuture-detection-rules
description: Search detection rules.
arguments:
@@ -3000,6 +3042,7 @@ script:
- contextPath: RecordedFuture.DetectionRules.rules.file_name
description: Recorded Future Detection rules file_name.
type: String
+
- name: recordedfuture-collective-insight
description: Post detection to collective insight.
arguments:
diff --git a/Packs/RecordedFuture/Integrations/RecordedFuture/RecordedFuture_test.py b/Packs/RecordedFuture/Integrations/RecordedFuture/RecordedFuture_test.py
index 4dc53e36ca8..31ddd7bcdf6 100644
--- a/Packs/RecordedFuture/Integrations/RecordedFuture/RecordedFuture_test.py
+++ b/Packs/RecordedFuture/Integrations/RecordedFuture/RecordedFuture_test.py
@@ -2,12 +2,12 @@ def create_client():
import os
from RecordedFuture import Client
- base_url = 'https://api.recordedfuture.com/gw/xsoar/'
+ base_url = "https://api.recordedfuture.com/gw/xsoar/"
verify_ssl = True
- token = os.environ.get('RF_TOKEN')
+ token = os.environ.get("RF_TOKEN")
headers = {
- 'X-RFToken': token,
- 'X-RF-User-Agent': "RecordedFuture.py/2.4 (Linux-5.13.0-1031-aws-x86_64-with) "
+ "X-RFToken": token,
+ "X-RF-User-Agent": "RecordedFuture.py/2.4 (Linux-5.13.0-1031-aws-x86_64-with) "
"XSOAR/2.4 RFClient/2.4 (Cortex_XSOAR_6.5.0)",
}
@@ -37,28 +37,28 @@ def test_translate_score(self):
def test_determine_hash(self):
from RecordedFuture import determine_hash
- assert determine_hash(hash_value='s' * 128) == 'SHA512'
- assert determine_hash(hash_value='s' * 64) == 'SHA256'
- assert determine_hash(hash_value='s' * 40) == 'SHA1'
- assert determine_hash(hash_value='s' * 32) == 'MD5'
- assert determine_hash(hash_value='s' * 8) == 'CRC32'
- assert determine_hash(hash_value='s' * 50) == 'CTPH'
- assert determine_hash(hash_value='s' * 10) == 'CTPH'
- assert determine_hash(hash_value='s') == 'CTPH'
+ assert determine_hash(hash_value="s" * 128) == "SHA512"
+ assert determine_hash(hash_value="s" * 64) == "SHA256"
+ assert determine_hash(hash_value="s" * 40) == "SHA1"
+ assert determine_hash(hash_value="s" * 32) == "MD5"
+ assert determine_hash(hash_value="s" * 8) == "CRC32"
+ assert determine_hash(hash_value="s" * 50) == "CTPH"
+ assert determine_hash(hash_value="s" * 10) == "CTPH"
+ assert determine_hash(hash_value="s") == "CTPH"
def test_create_indicator_ip(self, mocker):
from RecordedFuture import create_indicator
from CommonServerPython import Common, DBotScoreType
mock_return_value = mocker.Mock()
- mocker.patch('CommonServerPython.Common.IP', return_value=mock_return_value)
- dbot_score_spy = mocker.spy(Common, 'DBotScore')
+ mocker.patch("CommonServerPython.Common.IP", return_value=mock_return_value)
+ dbot_score_spy = mocker.spy(Common, "DBotScore")
- entity = '8.8.8.8'
- entity_type = 'ip'
+ entity = "8.8.8.8"
+ entity_type = "ip"
score = 45
- description = 'test_description'
- location = {'asn': 'test_asn', 'location': {'country': 'test_country'}}
+ description = "test_description"
+ location = {"asn": "test_asn", "location": {"country": "test_country"}}
result = create_indicator(
entity=entity,
@@ -73,9 +73,9 @@ def test_create_indicator_ip(self, mocker):
dbot_score_spy.assert_called_once_with(
entity,
DBotScoreType.IP,
- 'Recorded Future v2',
+ "Recorded Future v2",
Common.DBotScore.SUSPICIOUS,
- '',
+ "",
# reliability=DBotScoreReliability.B
reliability=None,
)
@@ -88,8 +88,8 @@ def test_create_indicator_ip(self, mocker):
# We can't assert it with `==` as the Common.IP does not implement `__eq__` method.
assert mock_call.kwargs == {
- 'asn': 'test_asn',
- 'geo_country': 'test_country',
+ "asn": "test_asn",
+ "geo_country": "test_country",
}
def test_create_indicator_domain(self, mocker):
@@ -97,13 +97,13 @@ def test_create_indicator_domain(self, mocker):
from CommonServerPython import Common, DBotScoreType
mock_return_value = mocker.Mock()
- mocker.patch('CommonServerPython.Common.Domain', return_value=mock_return_value)
- dbot_score_spy = mocker.spy(Common, 'DBotScore')
+ mocker.patch("CommonServerPython.Common.Domain", return_value=mock_return_value)
+ dbot_score_spy = mocker.spy(Common, "DBotScore")
- entity = 'google.com'
- entity_type = 'domain'
+ entity = "google.com"
+ entity_type = "domain"
score = 45
- description = 'test_description'
+ description = "test_description"
result = create_indicator(
entity=entity,
@@ -117,9 +117,9 @@ def test_create_indicator_domain(self, mocker):
dbot_score_spy.assert_called_once_with(
entity,
DBotScoreType.DOMAIN,
- 'Recorded Future v2',
+ "Recorded Future v2",
Common.DBotScore.SUSPICIOUS,
- '',
+ "",
# reliability=DBotScoreReliability.B
reliability=None,
)
@@ -133,13 +133,13 @@ def test_create_indicator_url(self, mocker):
from CommonServerPython import Common, DBotScoreType
mock_return_value = mocker.Mock()
- mocker.patch('CommonServerPython.Common.URL', return_value=mock_return_value)
- dbot_score_spy = mocker.spy(Common, 'DBotScore')
+ mocker.patch("CommonServerPython.Common.URL", return_value=mock_return_value)
+ dbot_score_spy = mocker.spy(Common, "DBotScore")
- entity = 'https://google.com'
- entity_type = 'url'
+ entity = "https://google.com"
+ entity_type = "url"
score = 45
- description = 'test_description'
+ description = "test_description"
result = create_indicator(
entity=entity,
@@ -153,9 +153,9 @@ def test_create_indicator_url(self, mocker):
dbot_score_spy.assert_called_once_with(
entity,
DBotScoreType.URL,
- 'Recorded Future v2',
+ "Recorded Future v2",
Common.DBotScore.SUSPICIOUS,
- '',
+ "",
# reliability=DBotScoreReliability.B
reliability=None,
)
@@ -169,12 +169,12 @@ def test_create_indicator_cve(self, mocker):
from CommonServerPython import Common
mock_return_value = mocker.Mock()
- mocker.patch('CommonServerPython.Common.CVE', return_value=mock_return_value)
+ mocker.patch("CommonServerPython.Common.CVE", return_value=mock_return_value)
- entity = 'CVE-123'
- entity_type = 'cve'
+ entity = "CVE-123"
+ entity_type = "cve"
score = 45
- description = 'test_description'
+ description = "test_description"
result = create_indicator(
entity=entity,
@@ -187,9 +187,9 @@ def test_create_indicator_cve(self, mocker):
mock_call = Common.CVE.mock_calls[0]
assert mock_call.args[0] == entity
- assert mock_call.args[1] == ''
- assert mock_call.args[2] == ''
- assert mock_call.args[3] == ''
+ assert mock_call.args[1] == ""
+ assert mock_call.args[2] == ""
+ assert mock_call.args[3] == ""
assert mock_call.args[4] == description
def test_create_indicator_file(self, mocker):
@@ -197,15 +197,15 @@ def test_create_indicator_file(self, mocker):
from CommonServerPython import Common, DBotScoreType
mock_return_value = mocker.Mock()
- mocker.patch('CommonServerPython.Common.File', return_value=mock_return_value)
- dbot_score_spy = mocker.spy(Common, 'DBotScore')
+ mocker.patch("CommonServerPython.Common.File", return_value=mock_return_value)
+ dbot_score_spy = mocker.spy(Common, "DBotScore")
- entity_type = 'file'
+ entity_type = "file"
score = 45
- description = 'test_description'
+ description = "test_description"
# MD5.
- entity = 's' * 32
+ entity = "s" * 32
result = create_indicator(
entity=entity,
entity_type=entity_type,
@@ -218,19 +218,19 @@ def test_create_indicator_file(self, mocker):
dbot_score_spy.assert_called_once_with(
entity,
DBotScoreType.FILE,
- 'Recorded Future v2',
+ "Recorded Future v2",
Common.DBotScore.SUSPICIOUS,
- '',
+ "",
# reliability=DBotScoreReliability.B
reliability=None,
)
mock_call = Common.File.mock_calls[0]
assert mock_call.args[0].indicator == entity
- assert mock_call.kwargs.get('md5') == entity
+ assert mock_call.kwargs.get("md5") == entity
# SHA1.
- entity = 's' * 40
+ entity = "s" * 40
result = create_indicator(
entity=entity,
entity_type=entity_type,
@@ -243,19 +243,19 @@ def test_create_indicator_file(self, mocker):
dbot_score_spy.assert_called_with(
entity,
DBotScoreType.FILE,
- 'Recorded Future v2',
+ "Recorded Future v2",
Common.DBotScore.SUSPICIOUS,
- '',
+ "",
# reliability=DBotScoreReliability.B
reliability=None,
)
mock_call = Common.File.mock_calls[-1]
assert mock_call.args[0].indicator == entity
- assert mock_call.kwargs.get('sha1') == entity
+ assert mock_call.kwargs.get("sha1") == entity
# SHA256.
- entity = 's' * 64
+ entity = "s" * 64
result = create_indicator(
entity=entity,
entity_type=entity_type,
@@ -268,19 +268,19 @@ def test_create_indicator_file(self, mocker):
dbot_score_spy.assert_called_with(
entity,
DBotScoreType.FILE,
- 'Recorded Future v2',
+ "Recorded Future v2",
Common.DBotScore.SUSPICIOUS,
- '',
+ "",
# reliability=DBotScoreReliability.B
reliability=None,
)
mock_call = Common.File.mock_calls[-1]
assert mock_call.args[0].indicator == entity
- assert mock_call.kwargs.get('sha256') == entity
+ assert mock_call.kwargs.get("sha256") == entity
# SHA512.
- entity = 's' * 128
+ entity = "s" * 128
result = create_indicator(
entity=entity,
entity_type=entity_type,
@@ -293,19 +293,19 @@ def test_create_indicator_file(self, mocker):
dbot_score_spy.assert_called_with(
entity,
DBotScoreType.FILE,
- 'Recorded Future v2',
+ "Recorded Future v2",
Common.DBotScore.SUSPICIOUS,
- '',
+ "",
# reliability=DBotScoreReliability.B
reliability=None,
)
mock_call = Common.File.mock_calls[-1]
assert mock_call.args[0].indicator == entity
- assert mock_call.kwargs.get('sha512') == entity
+ assert mock_call.kwargs.get("sha512") == entity
# CRC32.
- entity = 's' * 20 # Length different from any previous hashes.
+ entity = "s" * 20 # Length different from any previous hashes.
result = create_indicator(
entity=entity,
entity_type=entity_type,
@@ -318,9 +318,9 @@ def test_create_indicator_file(self, mocker):
dbot_score_spy.assert_called_with(
entity,
DBotScoreType.FILE,
- 'Recorded Future v2',
+ "Recorded Future v2",
Common.DBotScore.SUSPICIOUS,
- '',
+ "",
# reliability=DBotScoreReliability.B
reliability=None,
)
@@ -334,13 +334,13 @@ class TestRFClient:
def test_whoami(self, mocker):
client = create_client()
- mock_http_request = mocker.patch.object(client, '_http_request')
+ mock_http_request = mocker.patch.object(client, "_http_request")
client.whoami()
mock_http_request.assert_called_once_with(
- method='get',
- url_suffix='info/whoami',
+ method="get",
+ url_suffix="info/whoami",
timeout=60,
)
@@ -353,7 +353,7 @@ def test_get_writeback_data_writeback_off(self, mocker):
client = create_client()
- mocker.patch.object(demisto, 'params', return_value={'writeback': False})
+ mocker.patch.object(demisto, "params", return_value={"writeback": False})
assert client._get_writeback_data() is None
def test_get_writeback_data_writeback_on(self, mocker):
@@ -365,16 +365,14 @@ def test_get_writeback_data_writeback_on(self, mocker):
client = create_client()
mocker.patch.object(
- demisto, 'params', return_value={'collective_insights': 'On'}
+ demisto, "params", return_value={"collective_insights": "On"}
)
demisto.callingContext = {
- 'context': {'ExecutionContext': 'to be removed', 'Incidents': []}
+ "context": {"ExecutionContext": "to be removed", "Incidents": []}
}
- assert client._get_writeback_data() == {
- 'context': {'Incidents': []}
- }
+ assert client._get_writeback_data() == {"context": {"Incidents": []}}
#
def test_call_writeback_on(self, mocker):
@@ -388,48 +386,48 @@ def test_call_writeback_on(self, mocker):
STATUS_TO_RETRY = [500, 501, 502, 503, 504]
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
# Mock data for writeback.
mocker.patch.object(
demisto,
- 'params',
+ "params",
return_value={
- 'collective_insights': "On",
+ "collective_insights": "On",
},
)
mock_calling_context = {
- 'context': {'ExecutionContext': 'to be removed', 'Incidents': []},
- 'other': 'data',
+ "context": {"ExecutionContext": "to be removed", "Incidents": []},
+ "other": "data",
}
demisto.callingContext = mock_calling_context
client = create_client()
- mock_http_request = mocker.patch.object(client, '_http_request')
+ mock_http_request = mocker.patch.object(client, "_http_request")
- mock_url_suffix = 'mock_url_suffix'
+ mock_url_suffix = "mock_url_suffix"
client._call(url_suffix=mock_url_suffix)
json_data = {
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
- 'callingContext': {
- 'context': {'Incidents': []},
+ "demisto_command": mock_command_name,
+ "demisto_args": mock_command_args,
+ "callingContext": {
+ "context": {"Incidents": []},
},
}
mock_http_request.assert_called_once_with(
- method='post',
+ method="post",
url_suffix=mock_url_suffix,
json_data=json_data,
timeout=90,
@@ -448,45 +446,45 @@ def test_call_writeback_off(self, mocker):
STATUS_TO_RETRY = [500, 501, 502, 503, 504]
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
# Mock data for writeback.
mocker.patch.object(
demisto,
- 'params',
+ "params",
return_value={
- 'collective_insights': "Off",
+ "collective_insights": "Off",
},
)
mock_calling_context = {
- 'context': {'ExecutionContext': 'to be removed', 'other': 'data'},
- 'other': 'data',
+ "context": {"ExecutionContext": "to be removed", "other": "data"},
+ "other": "data",
}
demisto.callingContext = mock_calling_context
client = create_client()
- mock_http_request = mocker.patch.object(client, '_http_request')
+ mock_http_request = mocker.patch.object(client, "_http_request")
- mock_url_suffix = 'mock_url_suffix'
+ mock_url_suffix = "mock_url_suffix"
client._call(url_suffix=mock_url_suffix)
json_data = {
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
+ "demisto_command": mock_command_name,
+ "demisto_args": mock_command_args,
}
mock_http_request.assert_called_once_with(
- method='post',
+ method="post",
url_suffix=mock_url_suffix,
json_data=json_data,
timeout=90,
@@ -505,30 +503,30 @@ def test_call_with_kwargs(self, mocker):
STATUS_TO_RETRY = [500, 501, 502, 503, 504]
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_http_request = mocker.patch.object(client, '_http_request')
+ mock_http_request = mocker.patch.object(client, "_http_request")
- mock_url_suffix = 'mock_url_suffix'
+ mock_url_suffix = "mock_url_suffix"
client._call(url_suffix=mock_url_suffix, timeout=120, any_other_kwarg=True)
json_data = {
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
+ "demisto_command": mock_command_name,
+ "demisto_args": mock_command_args,
}
mock_http_request.assert_called_once_with(
- method='post',
+ method="post",
url_suffix=mock_url_suffix,
json_data=json_data,
timeout=120,
@@ -546,22 +544,22 @@ def test_call_returns_response(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_response = {'response': {'data': 'mock data'}}
+ mock_response = {"response": {"data": "mock data"}}
- mocker.patch.object(client, '_http_request', return_value=mock_response)
+ mocker.patch.object(client, "_http_request", return_value=mock_response)
- mock_url_suffix = 'mock_url_suffix'
+ mock_url_suffix = "mock_url_suffix"
response = client._call(url_suffix=mock_url_suffix)
assert response == mock_response
@@ -577,36 +575,36 @@ def test_call_response_processing_return_error(self, mocker):
STATUS_TO_RETRY = [500, 501, 502, 503, 504]
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
- mock_return_error = mocker.patch('RecordedFuture.return_error')
+ mock_return_error = mocker.patch("RecordedFuture.return_error")
client = create_client()
mock_http_request = mocker.patch.object(
client,
- '_http_request',
- return_value={'return_error': {'message': 'mock error'}},
+ "_http_request",
+ return_value={"return_error": {"message": "mock error"}},
)
- mock_url_suffix = 'mock_url_suffix'
+ mock_url_suffix = "mock_url_suffix"
client._call(url_suffix=mock_url_suffix)
json_data = {
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
+ "demisto_command": mock_command_name,
+ "demisto_args": mock_command_args,
}
mock_http_request.assert_called_once_with(
- method='post',
+ method="post",
url_suffix=mock_url_suffix,
json_data=json_data,
timeout=90,
@@ -614,7 +612,7 @@ def test_call_response_processing_return_error(self, mocker):
status_list_to_retry=STATUS_TO_RETRY,
)
- mock_return_error.assert_called_once_with(message='mock error')
+ mock_return_error.assert_called_once_with(message="mock error")
def test_call_response_processing_404(self, mocker):
"""
@@ -628,39 +626,39 @@ def test_call_response_processing_404(self, mocker):
STATUS_TO_RETRY = [500, 501, 502, 503, 504]
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
- mocker.patch('RecordedFuture.return_error')
+ mocker.patch("RecordedFuture.return_error")
client = create_client()
def mock_http_request_method(*args, **kwargs):
# Imitate how CommonServerPython handles bad responses (when status code not in ok_codes,
# or if ok_codes=None - it uses requests.Response.ok to check whether response is good).
- raise DemistoException('404')
+ raise DemistoException("404")
- mocker.patch.object(client, '_http_request', mock_http_request_method)
+ mocker.patch.object(client, "_http_request", mock_http_request_method)
- spy_http_request = mocker.spy(client, '_http_request')
+ spy_http_request = mocker.spy(client, "_http_request")
- mock_url_suffix = 'mock_url_suffix'
+ mock_url_suffix = "mock_url_suffix"
result = client._call(url_suffix=mock_url_suffix)
json_data = {
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
+ "demisto_command": mock_command_name,
+ "demisto_args": mock_command_args,
}
spy_http_request.assert_called_once_with(
- method='post',
+ method="post",
url_suffix=mock_url_suffix,
json_data=json_data,
timeout=90,
@@ -670,49 +668,49 @@ def mock_http_request_method(*args, **kwargs):
assert isinstance(result, CommandResults)
- assert result.outputs_prefix == ''
- assert result.outputs_key_field == ''
+ assert result.outputs_prefix == ""
+ assert result.outputs_key_field == ""
assert result.outputs == {}
assert result.raw_response == {}
- assert result.readable_output == 'No results found.'
+ assert result.readable_output == "No results found."
def test_fetch_incidents(self, mocker):
import os
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
- mock_params = {'param1': 'param1 value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
+ mock_params = {"param1": "param1 value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
- mocker.patch.object(demisto, 'params', return_value=mock_params)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
mock_last_run_dict = {"lastRun": "2022-08-31T12:12:20+00:00"}
- mocker.patch.object(demisto, 'getLastRun', return_value=mock_last_run_dict)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.fetch_incidents()
mock_call.assert_called_once_with(
json_data={
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
- 'demisto_last_run': mock_last_run_dict,
- 'demisto_params': mock_params,
+ "demisto_command": mock_command_name,
+ "demisto_args": mock_command_args,
+ "demisto_last_run": mock_last_run_dict,
+ "demisto_params": mock_params,
},
timeout=120,
- url_suffix='/v2/alert/fetch_incidents',
+ url_suffix="/v2/alert/fetch_incidents",
)
assert response == mock_call_response
@@ -722,25 +720,25 @@ def test_entity_search(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.entity_search()
- mock_call.assert_called_once_with(url_suffix='/v2/search')
+ mock_call.assert_called_once_with(url_suffix="/v2/search")
assert response == mock_call_response
@@ -749,25 +747,25 @@ def test_get_intelligence(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.get_intelligence()
- mock_call.assert_called_once_with(url_suffix='/v2/lookup/intelligence')
+ mock_call.assert_called_once_with(url_suffix="/v2/lookup/intelligence")
assert response == mock_call_response
@@ -776,25 +774,25 @@ def test_get_links(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.get_links()
- mock_call.assert_called_once_with(url_suffix='/v2/lookup/links')
+ mock_call.assert_called_once_with(url_suffix="/v2/lookup/links")
assert response == mock_call_response
@@ -803,25 +801,25 @@ def test_get_single_alert(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.get_single_alert()
- mock_call.assert_called_once_with(url_suffix='/v2/alert/lookup')
+ mock_call.assert_called_once_with(url_suffix="/v2/alert/lookup")
assert response == mock_call_response
@@ -830,25 +828,25 @@ def test_get_alerts(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.get_alerts()
- mock_call.assert_called_once_with(url_suffix='/v2/alert/search')
+ mock_call.assert_called_once_with(url_suffix="/v2/alert/search")
assert response == mock_call_response
@@ -857,25 +855,25 @@ def test_get_alert_rules(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.get_alert_rules()
- mock_call.assert_called_once_with(url_suffix='/v2/alert/rule')
+ mock_call.assert_called_once_with(url_suffix="/v2/alert/rule")
assert response == mock_call_response
@@ -884,31 +882,31 @@ def test_alert_set_status(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
- alert_data = {'mock': 'data'}
+ alert_data = {"mock": "data"}
response = client.alert_set_status(alert_data)
mock_call.assert_called_once_with(
- url_suffix='/v2/alert/set_status',
+ url_suffix="/v2/alert/set_status",
json_data={
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
- 'alerts_update_data': alert_data,
+ "demisto_command": mock_command_name,
+ "demisto_args": mock_command_args,
+ "alerts_update_data": alert_data,
},
)
@@ -917,11 +915,11 @@ def test_alert_set_status(self, mocker):
response = client.alert_set_status()
mock_call.assert_called_with(
- url_suffix='/v2/alert/set_status',
+ url_suffix="/v2/alert/set_status",
json_data={
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
- 'alerts_update_data': None,
+ "demisto_command": mock_command_name,
+ "demisto_args": mock_command_args,
+ "alerts_update_data": None,
},
)
@@ -932,31 +930,31 @@ def test_alert_set_note(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
- alert_data = {'mock': 'data'}
+ alert_data = {"mock": "data"}
response = client.alert_set_note(alert_data)
mock_call.assert_called_once_with(
- url_suffix='/v2/alert/set_note',
+ url_suffix="/v2/alert/set_note",
json_data={
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
- 'alerts_update_data': alert_data,
+ "demisto_command": mock_command_name,
+ "demisto_args": mock_command_args,
+ "alerts_update_data": alert_data,
},
)
@@ -965,11 +963,11 @@ def test_alert_set_note(self, mocker):
response = client.alert_set_note()
mock_call.assert_called_with(
- url_suffix='/v2/alert/set_note',
+ url_suffix="/v2/alert/set_note",
json_data={
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
- 'alerts_update_data': None,
+ "demisto_command": mock_command_name,
+ "demisto_args": mock_command_args,
+ "alerts_update_data": None,
},
)
@@ -980,25 +978,25 @@ def test_get_triage(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.get_triage()
- mock_call.assert_called_once_with(url_suffix='/v2/lookup/triage')
+ mock_call.assert_called_once_with(url_suffix="/v2/lookup/triage")
assert response == mock_call_response
@@ -1007,25 +1005,25 @@ def test_get_threat_map(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'threat_map'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "threat_map"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_call_response = {'response': {'data': 'threat map response'}}
+ mock_call_response = {"response": {"data": "threat map response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.get_threat_map()
- mock_call.assert_called_once_with(url_suffix='/v2/threat/actors')
+ mock_call.assert_called_once_with(url_suffix="/v2/threat/actors")
assert response == mock_call_response
@@ -1034,25 +1032,25 @@ def test_get_threat_links(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'threat_links'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "threat_links"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_call_response = {'response': {'data': 'threat links response'}}
+ mock_call_response = {"response": {"data": "threat links response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.get_threat_links()
- mock_call.assert_called_once_with(url_suffix='/v2/links/search')
+ mock_call.assert_called_once_with(url_suffix="/v2/links/search")
assert response == mock_call_response
@@ -1061,25 +1059,25 @@ def test_get_detection_rules(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'detection_rules'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "detection_rules"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_call_response = {'response': {'data': 'detection rules response'}}
+ mock_call_response = {"response": {"data": "detection rules response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.get_detection_rules()
- mock_call.assert_called_once_with(url_suffix='/v2/detection_rules/search')
+ mock_call.assert_called_once_with(url_suffix="/v2/detection_rules/search")
assert response == mock_call_response
@@ -1088,25 +1086,27 @@ def test_submit_collective_insight(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'collective_insight'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "collective_insight"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
client = create_client()
- mock_call_response = {'response': {'data': 'collective insight response'}}
+ mock_call_response = {"response": {"data": "collective insight response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.submit_detection_to_collective_insight()
- mock_call.assert_called_once_with(url_suffix='/v2/collective-insights/detections')
+ mock_call.assert_called_once_with(
+ url_suffix="/v2/collective-insights/detections"
+ )
assert response == mock_call_response
@@ -1128,7 +1128,7 @@ def test_process_result_actions_404(self, mocker):
# Test if response is CommandResults
# (case when we got 404 on response, and it was processed in self.client._call() method).
- response = CommandResults(readable_output='Mock')
+ response = CommandResults(readable_output="Mock")
result_actions = actions._process_result_actions(response=response)
assert result_actions == [response]
@@ -1139,7 +1139,7 @@ def test_process_result_actions_response_is_not_dict(self, mocker):
actions = Actions(mock_client)
# Test if response is not CommandResults and not Dict.
- response = 'Mock string - not CommandResults and not dict'
+ response = "Mock string - not CommandResults and not dict"
result_actions = actions._process_result_actions(response=response) # type: ignore
assert result_actions is None
@@ -1152,20 +1152,20 @@ def test_process_result_actions_no_or_empty_result_actions_in_response(
actions = Actions(mock_client)
# Test no results_actions in response.
- response = {'data': 'mock'}
+ response = {"data": "mock"}
result_actions = actions._process_result_actions(response=response)
assert result_actions is None
# Test case when bool(results_actions) in response is False.
- response = {'data': 'mock', 'result_actions': None}
+ response = {"data": "mock", "result_actions": None}
result_actions = actions._process_result_actions(response=response)
assert result_actions is None
- response = {'data': 'mock', 'result_actions': []}
+ response = {"data": "mock", "result_actions": []}
result_actions = actions._process_result_actions(response=response)
assert result_actions is None
- response = {'data': 'mock', 'result_actions': {}}
+ response = {"data": "mock", "result_actions": {}}
result_actions = actions._process_result_actions(response=response)
assert result_actions is None
@@ -1176,15 +1176,15 @@ def test_process_result_actions_command_results_only(self, mocker):
actions = Actions(mock_client)
response = {
- 'data': 'mock',
- 'result_actions': [
+ "data": "mock",
+ "result_actions": [
{
- 'CommandResults': {
- 'outputs_prefix': 'mock_outputs_prefix',
- 'outputs': 'mock_outputs',
- 'raw_response': 'mock_raw_response',
- 'readable_output': 'mock_readable_output',
- 'outputs_key_field': 'mock_outputs_key_field',
+ "CommandResults": {
+ "outputs_prefix": "mock_outputs_prefix",
+ "outputs": "mock_outputs",
+ "raw_response": "mock_raw_response",
+ "readable_output": "mock_readable_output",
+ "outputs_key_field": "mock_outputs_key_field",
},
}
],
@@ -1197,11 +1197,11 @@ def test_process_result_actions_command_results_only(self, mocker):
assert isinstance(r_a, CommandResults)
- assert r_a.outputs_prefix == 'mock_outputs_prefix'
- assert r_a.outputs == 'mock_outputs'
- assert r_a.raw_response == 'mock_raw_response'
- assert r_a.readable_output == 'mock_readable_output'
- assert r_a.outputs_key_field == 'mock_outputs_key_field'
+ assert r_a.outputs_prefix == "mock_outputs_prefix"
+ assert r_a.outputs == "mock_outputs"
+ assert r_a.raw_response == "mock_raw_response"
+ assert r_a.readable_output == "mock_readable_output"
+ assert r_a.outputs_key_field == "mock_outputs_key_field"
def test_process_result_actions_create_indicator_and_default_command_results(
self, mocker
@@ -1210,22 +1210,22 @@ def test_process_result_actions_create_indicator_and_default_command_results(
spy_create_indicator = mocker.spy(
RecordedFuture,
- 'create_indicator',
+ "create_indicator",
)
mock_client = mocker.Mock()
actions = RecordedFuture.Actions(mock_client)
response = {
- 'data': 'mock',
- 'result_actions': [
+ "data": "mock",
+ "result_actions": [
{
- 'create_indicator': {
- 'entity': 'mock_entity',
- 'entity_type': 'ip',
- 'score': 15,
- 'description': 'mock_description',
- 'location': {'country': 'mock_country', 'ans': 'mock_asn'},
+ "create_indicator": {
+ "entity": "mock_entity",
+ "entity_type": "ip",
+ "score": 15,
+ "description": "mock_description",
+ "location": {"country": "mock_country", "ans": "mock_asn"},
},
}
],
@@ -1233,11 +1233,11 @@ def test_process_result_actions_create_indicator_and_default_command_results(
result_actions = actions._process_result_actions(response=response)
spy_create_indicator.assert_called_once_with(
- entity='mock_entity',
- entity_type='ip',
+ entity="mock_entity",
+ entity_type="ip",
score=15,
- description='mock_description',
- location={'country': 'mock_country', 'ans': 'mock_asn'},
+ description="mock_description",
+ location={"country": "mock_country", "ans": "mock_asn"},
)
assert len(result_actions) == 1
@@ -1247,13 +1247,13 @@ def test_process_result_actions_create_indicator_and_default_command_results(
assert isinstance(r_a, RecordedFuture.CommandResults)
assert r_a.readable_output == (
- '### New indicator was created.\n'
- '|DBotScore(val.Indicator && val.Indicator == obj.Indicator && val.Vendor == '
- 'obj.Vendor && val.Type == obj.Type)|IP(val.Address && val.Address == '
- 'obj.Address)|\n'
- '|---|---|\n'
- '| Indicator: mock_entity
Type: ip
Vendor: Recorded Future v2
Score: '
- '0 | Address: mock_entity |\n'
+ "### New indicator was created.\n"
+ "|DBotScore(val.Indicator && val.Indicator == obj.Indicator && val.Vendor == "
+ "obj.Vendor && val.Type == obj.Type)|IP(val.Address && val.Address == "
+ "obj.Address)|\n"
+ "|---|---|\n"
+ "| Indicator: mock_entity
Type: ip
Vendor: Recorded Future v2
Score: "
+ "0 | Address: mock_entity |\n"
)
def test_process_result_actions_create_indicator_and_command_results(self, mocker):
@@ -1261,29 +1261,29 @@ def test_process_result_actions_create_indicator_and_command_results(self, mocke
spy_create_indicator = mocker.spy(
RecordedFuture,
- 'create_indicator',
+ "create_indicator",
)
mock_client = mocker.Mock()
actions = RecordedFuture.Actions(mock_client)
response = {
- 'data': 'mock',
- 'result_actions': [
+ "data": "mock",
+ "result_actions": [
{
- 'create_indicator': {
- 'entity': 'mock_entity',
- 'entity_type': 'ip',
- 'score': 15,
- 'description': 'mock_indicator_description',
+ "create_indicator": {
+ "entity": "mock_entity",
+ "entity_type": "ip",
+ "score": 15,
+ "description": "mock_indicator_description",
},
- 'CommandResults': {
- 'outputs_prefix': 'mock_outputs_prefix',
- 'outputs': 'mock_outputs',
- 'raw_response': 'mock_raw_response',
- 'readable_output': 'mock_readable_output',
- 'outputs_key_field': 'mock_outputs_key_field',
- 'indicator': 'indicator',
+ "CommandResults": {
+ "outputs_prefix": "mock_outputs_prefix",
+ "outputs": "mock_outputs",
+ "raw_response": "mock_raw_response",
+ "readable_output": "mock_readable_output",
+ "outputs_key_field": "mock_outputs_key_field",
+ "indicator": "indicator",
},
}
],
@@ -1291,10 +1291,10 @@ def test_process_result_actions_create_indicator_and_command_results(self, mocke
result_actions = actions._process_result_actions(response=response)
spy_create_indicator.assert_called_once_with(
- entity='mock_entity',
- entity_type='ip',
+ entity="mock_entity",
+ entity_type="ip",
score=15,
- description='mock_indicator_description',
+ description="mock_indicator_description",
)
assert len(result_actions) == 1
@@ -1303,20 +1303,20 @@ def test_process_result_actions_create_indicator_and_command_results(self, mocke
assert isinstance(r_a, RecordedFuture.CommandResults)
- assert r_a.outputs_prefix == 'mock_outputs_prefix'
- assert r_a.outputs == 'mock_outputs'
- assert r_a.raw_response == 'mock_raw_response'
- assert r_a.readable_output == 'mock_readable_output'
- assert r_a.outputs_key_field == 'mock_outputs_key_field'
+ assert r_a.outputs_prefix == "mock_outputs_prefix"
+ assert r_a.outputs == "mock_outputs"
+ assert r_a.raw_response == "mock_raw_response"
+ assert r_a.readable_output == "mock_readable_output"
+ assert r_a.outputs_key_field == "mock_outputs_key_field"
assert r_a.indicator.to_context() == {
- 'DBotScore(val.Indicator && val.Indicator == obj.Indicator && val.Vendor == obj.Vendor && val.Type == obj.Type)': {
- 'Indicator': 'mock_entity',
- 'Score': 0,
- 'Type': 'ip',
- 'Vendor': 'Recorded Future v2',
+ "DBotScore(val.Indicator && val.Indicator == obj.Indicator && val.Vendor == obj.Vendor && val.Type == obj.Type)": {
+ "Indicator": "mock_entity",
+ "Score": 0,
+ "Type": "ip",
+ "Vendor": "Recorded Future v2",
},
- 'IP(val.Address && val.Address == obj.Address)': {'Address': 'mock_entity'},
+ "IP(val.Address && val.Address == obj.Address)": {"Address": "mock_entity"},
}
def test_fetch_incidents_with_incidents_present(self, mocker):
@@ -1326,31 +1326,31 @@ def test_fetch_incidents_with_incidents_present(self, mocker):
client = create_client()
mock_incidents_value = [
- {'mock_incident_key1': 'mock_incident_value1'},
- {'mock_incident_key2': 'mock_incident_value2'},
+ {"mock_incident_key1": "mock_incident_value1"},
+ {"mock_incident_key2": "mock_incident_value2"},
]
- mock_demisto_last_run_value = 'mock_demisto_last_run'
+ mock_demisto_last_run_value = "mock_demisto_last_run"
- mock_alerts_update_data_value = 'mock_alerts_update_data_value'
+ mock_alerts_update_data_value = "mock_alerts_update_data_value"
mock_client_fetch_incidents_response = {
- 'incidents': mock_incidents_value,
- 'demisto_last_run': mock_demisto_last_run_value,
- 'data': 'mock',
- 'alerts_update_data': mock_alerts_update_data_value,
+ "incidents": mock_incidents_value,
+ "demisto_last_run": mock_demisto_last_run_value,
+ "data": "mock",
+ "alerts_update_data": mock_alerts_update_data_value,
}
mock_client_fetch_incidents = mocker.patch.object(
- client, 'fetch_incidents', return_value=mock_client_fetch_incidents_response
+ client, "fetch_incidents", return_value=mock_client_fetch_incidents_response
)
mock_client_alert_set_status = mocker.patch.object(
client,
- 'alert_set_status',
+ "alert_set_status",
)
- mock_demisto_incidents = mocker.patch.object(demisto, 'incidents')
- mock_demisto_set_last_run = mocker.patch.object(demisto, 'setLastRun')
+ mock_demisto_incidents = mocker.patch.object(demisto, "incidents")
+ mock_demisto_set_last_run = mocker.patch.object(demisto, "setLastRun")
actions = Actions(client)
@@ -1371,20 +1371,20 @@ def test_malware_search_command(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_entity_search = mocker.patch.object(
- client, 'entity_search', return_value=mock_response
+ client, "entity_search", return_value=mock_response
)
actions = Actions(client)
mock_process_result_actions_return_value = (
- 'mock_process_result_actions_return_value'
+ "mock_process_result_actions_return_value"
)
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -1401,20 +1401,20 @@ def test_lookup_command(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_entity_lookup = mocker.patch.object(
- client, 'entity_lookup', return_value=mock_response
+ client, "entity_lookup", return_value=mock_response
)
actions = Actions(client)
mock_process_result_actions_return_value = (
- 'mock_process_result_actions_return_value'
+ "mock_process_result_actions_return_value"
)
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -1431,20 +1431,20 @@ def test_intelligence_command(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_get_intelligence = mocker.patch.object(
- client, 'get_intelligence', return_value=mock_response
+ client, "get_intelligence", return_value=mock_response
)
actions = Actions(client)
mock_process_result_actions_return_value = (
- 'mock_process_result_actions_return_value'
+ "mock_process_result_actions_return_value"
)
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -1461,20 +1461,20 @@ def test_get_links_command(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_get_links = mocker.patch.object(
- client, 'get_links', return_value=mock_response
+ client, "get_links", return_value=mock_response
)
actions = Actions(client)
mock_process_result_actions_return_value = (
- 'mock_process_result_actions_return_value'
+ "mock_process_result_actions_return_value"
)
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -1491,20 +1491,20 @@ def test_get_single_alert_command_with_result_actions(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_get_single_alert = mocker.patch.object(
- client, 'get_single_alert', return_value=mock_response
+ client, "get_single_alert", return_value=mock_response
)
actions = Actions(client)
mock_process_result_actions_return_value = (
- 'mock_process_result_actions_return_value'
+ "mock_process_result_actions_return_value"
)
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -1522,10 +1522,10 @@ def test_get_single_alert_command_without_result_actions(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_get_single_alert = mocker.patch.object(
- client, 'get_single_alert', return_value=mock_response
+ client, "get_single_alert", return_value=mock_response
)
actions = Actions(client)
@@ -1533,7 +1533,7 @@ def test_get_single_alert_command_without_result_actions(self, mocker):
mock_process_result_actions_return_value = None
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -1551,10 +1551,10 @@ def test_get_alerts_command(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_get_alerts = mocker.patch.object(
- client, 'get_alerts', return_value=mock_response
+ client, "get_alerts", return_value=mock_response
)
actions = Actions(client)
@@ -1570,10 +1570,10 @@ def test_get_alert_rules_command(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_get_alert_rules = mocker.patch.object(
- client, 'get_alert_rules', return_value=mock_response
+ client, "get_alert_rules", return_value=mock_response
)
actions = Actions(client)
@@ -1589,20 +1589,20 @@ def test_alert_set_status_command(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_alert_set_status = mocker.patch.object(
- client, 'alert_set_status', return_value=mock_response
+ client, "alert_set_status", return_value=mock_response
)
actions = Actions(client)
mock_process_result_actions_return_value = (
- 'mock_process_result_actions_return_value'
+ "mock_process_result_actions_return_value"
)
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -1619,20 +1619,20 @@ def test_alert_set_note_command(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_alert_set_note = mocker.patch.object(
- client, 'alert_set_note', return_value=mock_response
+ client, "alert_set_note", return_value=mock_response
)
actions = Actions(client)
mock_process_result_actions_return_value = (
- 'mock_process_result_actions_return_value'
+ "mock_process_result_actions_return_value"
)
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -1649,20 +1649,20 @@ def test_triage_command(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_get_triage = mocker.patch.object(
- client, 'get_triage', return_value=mock_response
+ client, "get_triage", return_value=mock_response
)
actions = Actions(client)
mock_process_result_actions_return_value = (
- 'mock_process_result_actions_return_value'
+ "mock_process_result_actions_return_value"
)
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -1679,20 +1679,20 @@ def test_threat_map_command(self, mocker):
client = create_client()
- mock_response = 'mock_threat_map'
+ mock_response = "mock_threat_map"
mock_client_get_threat_map = mocker.patch.object(
- client, 'get_threat_map', return_value=mock_response
+ client, "get_threat_map", return_value=mock_response
)
actions = Actions(client)
mock_process_result_actions_return_value = (
- 'mock_process_result_actions_return_value'
+ "mock_process_result_actions_return_value"
)
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -1709,18 +1709,18 @@ def test_threat_links_command(self, mocker):
client = create_client()
- mock_response = 'mock_threat_links'
+ mock_response = "mock_threat_links"
mock_client_get_threat_links = mocker.patch.object(
- client, 'get_threat_links', return_value=mock_response
+ client, "get_threat_links", return_value=mock_response
)
actions = Actions(client)
- mock_process_result_actions_return_value = 'return_value'
+ mock_process_result_actions_return_value = "return_value"
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -1735,18 +1735,18 @@ def test_detection_rules_command(self, mocker):
client = create_client()
- mock_response = 'mock_detection_rules'
+ mock_response = "mock_detection_rules"
mock_get_detection_rules = mocker.patch.object(
- client, 'get_detection_rules', return_value=mock_response
+ client, "get_detection_rules", return_value=mock_response
)
actions = Actions(client)
- mock_process_result_actions_return_value = 'return_value'
+ mock_process_result_actions_return_value = "return_value"
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -1761,18 +1761,18 @@ def test_collective_insight_command(self, mocker):
client = create_client()
- mock_response = 'mock_collective_insight'
+ mock_response = "mock_collective_insight"
mock_submit_detection_to_collective_insight = mocker.patch.object(
- client, 'submit_detection_to_collective_insight', return_value=mock_response
+ client, "submit_detection_to_collective_insight", return_value=mock_response
)
actions = Actions(client)
- mock_process_result_actions_return_value = 'return_value'
+ mock_process_result_actions_return_value = "return_value"
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -1798,7 +1798,7 @@ def test_test_module(self, mocker):
mocker.patch.object(RecordedFuture.Client, "whoami")
mocked_return_res = mocker.patch.object(RecordedFuture, "return_results")
RecordedFuture.main()
- mocked_return_res.assert_called_with('ok')
+ mocked_return_res.assert_called_with("ok")
def test_test_module_with_boom(self, mocker):
import RecordedFuture
@@ -1819,8 +1819,9 @@ def test_test_module_with_boom(self, mocker):
RecordedFuture.main()
mocked_return_err.assert_called_with(
message=(
- f'Failed to execute {demisto.command()} command: Failed due to - '
- 'Unknown error. Please verify that the API URL and Token are correctly configured. '
- 'RAW Error: Side effect triggered'
- )
+ f"Failed to execute {demisto.command()} command: "
+ "Failed due to - Unknown error. Please verify that the API URL and Token are correctly configured. "
+ "RAW Error: Side effect triggered"
+ ),
+ error=mocker.ANY,
)
diff --git a/Packs/RecordedFuture/Integrations/RecordedFutureEventCollector/RecordedFutureEventCollector.yml b/Packs/RecordedFuture/Integrations/RecordedFutureEventCollector/RecordedFutureEventCollector.yml
index ba8b8f0d9c0..65770ce681a 100644
--- a/Packs/RecordedFuture/Integrations/RecordedFutureEventCollector/RecordedFutureEventCollector.yml
+++ b/Packs/RecordedFuture/Integrations/RecordedFutureEventCollector/RecordedFutureEventCollector.yml
@@ -55,7 +55,7 @@ script:
name: limit
description: Gets events from Recorded Future.
name: recorded-future-get-events
- dockerimage: demisto/python3:3.10.14.91134
+ dockerimage: demisto/python3:3.11.10.111039
isfetchevents: true
script: '-'
subtype: python3
diff --git a/Packs/RecordedFuture/Integrations/RecordedFutureLists/RecordedFutureLists.yml b/Packs/RecordedFuture/Integrations/RecordedFutureLists/RecordedFutureLists.yml
index f1e270d1e27..168be18aa60 100644
--- a/Packs/RecordedFuture/Integrations/RecordedFutureLists/RecordedFutureLists.yml
+++ b/Packs/RecordedFuture/Integrations/RecordedFutureLists/RecordedFutureLists.yml
@@ -31,7 +31,7 @@ script:
script: '-'
type: python
subtype: python3
- dockerimage: demisto/python3:3.10.14.91134
+ dockerimage: demisto/python3:3.11.10.111039
commands:
- name: recordedfuture-lists-search
description: Search for lists in Recorded Future.
diff --git a/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/README.md b/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/README.md
index b1d7dade1af..3b51c57ee8f 100644
--- a/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/README.md
+++ b/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/README.md
@@ -1,5 +1,7 @@
-Fetch & triage Recorded Future Playbook Alerts
-This integration was integrated and tested with version 1.0.2 of RecordedFuturePlaybookAlerts
+# Recorded Future - Playbook Alerts Integration
+
+Fetch & triage Recorded Future Playbook Alerts.
+
## Configure Recorded Future - Playbook Alerts on Cortex XSOAR
@@ -7,19 +9,19 @@ This integration was integrated and tested with version 1.0.2 of RecordedFutureP
2. Search for Recorded Future - Playbook Alerts.
3. Click **Add instance** to create and configure a new integration instance.
- | **Parameter** | **Description** | **Required** |
- | --- | --- | --- |
- | API URL (e.g., https://api.recordedfuture.com/gw/xsoar/) | | True |
- | API Token | | True |
- | Trust any certificate (not secure) | | False |
- | Use system proxy settings | | False |
- | Fetch incidents | | False |
- | First Incidient Fetch: Time Range | Limit incidents to include in the first fetch by time range. Input format: "NN hours" or "NN days". E.g., input "5 days" to fetch all incidents created in the last 5 days. | False |
- | Playbook Alerts: Fetched Categories | Some listed Playbook alert Categories might be unavailable due to limitations in the current Recorded Future subscription | False |
- | Maximum number of incidents per fetch | | False |
- | Playbook Alerts: Fetched Statuses | | False |
- | Playbook Alerts: Fetched Priorities Threshold | Returns alerts with this selected priority or higher. High > Moderate > Informational | False |
- | Incident type | | False |
+ | **Parameter** | **Description** | **Required** |
+ |----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
+ | API URL (e.g., https://api.recordedfuture.com/gw/xsoar/) | | True |
+ | API Token | | True |
+ | Trust any certificate (not secure) | | False |
+ | Use system proxy settings | | False |
+ | Fetch incidents | | False |
+ | First Incidient Fetch: Time Range | Limit incidents to include in the first fetch by time range. Input format: "NN hours" or "NN days". E.g., input "5 days" to fetch all incidents created in the last 5 days. | False |
+ | Playbook Alerts: Fetched Categories | Some listed Playbook alert Categories might be unavailable due to limitations in the current Recorded Future subscription | False |
+ | Maximum number of incidents per fetch | | False |
+ | Playbook Alerts: Fetched Statuses | | False |
+ | Playbook Alerts: Fetched Priorities Threshold | Returns alerts with this selected priority or higher. High > Moderate > Informational | False |
+ | Incident type | | False |
4. Click **Test** to validate the URLs, token, and connection.
@@ -48,232 +50,238 @@ The integration pulls in Playbook alerts from Recorded Future base on its update
You can execute these commands from the Cortex XSOAR 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.
-### recordedfuture-playbook-alerts-details
+
+### recordedfuture-playbook-alerts-search
***
-Get Playbook alert details by id.
+Search playbook alerts based on filters.
#### Base Command
-`recordedfuture-playbook-alerts-details`
+`recordedfuture-playbook-alerts-search`
#### Input
-| **Argument Name** | **Description** | **Required** |
-| --- | --- | --- |
-| alert_ids | Ids of the playbook alert that should be fetched. | Required |
-| detail_sections | What evidence sections to include in the fetch, fetches all available if not specified. Possible values are: status, action, summary, log, whois, dns. | Optional |
+| **Argument Name** | **Description** | **Required** |
+|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
+| category | The playbook alert categories to retrieve. Default is all_available. Possible values are: all_available, domain_abuse, vulnerability, code_repo_leakage. | Optional |
+| limit | The maximum number of alerts to fetch. | Optional |
+| time_since_update | The amount of time since the last update. E.g., "2 hours" or "7 days" ago. | Optional |
+| playbook_alert_status | The statuses to retrieve. Defaults to only new status if not specified. Possible values are: new, in-progress, dismissed, resolved. | Optional |
+| priority | Actions priority assigned in Recorded Future. Possible values are: high, moderate, informational. | Optional |
+| order_search_by | The order by which to search for playbook alerts. Possible values are: updated, created. | Optional |
##### Command Example
-```!recordedfuture-playbook-alerts-details alert_ids="12312312-1231-1231-1231-123123123123" detail_sections="status,log"```
+```!recordedfuture-playbook-alerts-search```
+```!recordedfuture-playbook-alerts-search category=domain_abuse```
+```!recordedfuture-playbook-alerts-search category=vulnerability```
+```!recordedfuture-playbook-alerts-search limit=10```
+```!recordedfuture-playbook-alerts-search playbook_alert_status=in-progress```
+```!recordedfuture-playbook-alerts-search priority=high```
+```!recordedfuture-playbook-alerts-search order_search_by=updated```
+
#### Context Output
-| **Path** | **Type** | **Description** |
-| --- | --- | --- |
-| RecordedFuture.PlaybookAlerts.playbook_alert_id | String | Unique id of the playbook alert |
-| RecordedFuture.PlaybookAlerts.category | String | Playbook alert category |
-| RecordedFuture.PlaybookAlerts.priority | String | Recommended Priority of the alert |
-| RecordedFuture.PlaybookAlerts.status | String | Current alert status in Recorded Future |
-| RecordedFuture.PlaybookAlerts.title | String | Title of the alert |
-| RecordedFuture.PlaybookAlerts.updated | date | Date of last update |
-| RecordedFuture.PlaybookAlerts.created | date | Date of creation |
-| RecordedFuture.PlaybookAlerts.organization_id | String | Organization uhash |
-| RecordedFuture.PlaybookAlerts.organization_name | String | Plaintext Organization name |
-| RecordedFuture.PlaybookAlerts.assignee_id | String | uhash of the assigned user |
-| RecordedFuture.PlaybookAlerts.assignee_name | String | name of the assigned user |
-| RecordedFuture.PlaybookAlerts.owner_id | String | uhash of the enterprise that owns the alert |
-| RecordedFuture.PlaybookAlerts.owner_name | String | Name of the enterprise that owns the alert |
-| RecordedFuture.PlaybookAlerts.panel_status.playbook_alert_id | String | Unique id of the playbook alert |
-| RecordedFuture.PlaybookAlerts.panel_status.category | String | Playbook alert category |
-| RecordedFuture.PlaybookAlerts.panel_status.priority | String | Recommended Priority of the alert |
-| RecordedFuture.PlaybookAlerts.panel_status.status | String | Current alert status in Recorded Future |
-| RecordedFuture.PlaybookAlerts.panel_status.title | String | Title of the alert |
-| RecordedFuture.PlaybookAlerts.panel_status.updated | date | Date of last update |
-| RecordedFuture.PlaybookAlerts.panel_status.created | date | Date of creation |
-| RecordedFuture.PlaybookAlerts.panel_status.organization_id | String | Organization uhash |
-| RecordedFuture.PlaybookAlerts.panel_status.organization_name | String | Plaintext Organization name |
-| RecordedFuture.PlaybookAlerts.panel_status.assignee_id | String | uhash of the assigned user |
-| RecordedFuture.PlaybookAlerts.panel_status.assignee_name | unknown | name of the assigned user |
-| RecordedFuture.PlaybookAlerts.panel_status.owner_id | String | uhash of the enterprise that owns the alert |
-| RecordedFuture.PlaybookAlerts.panel_status.owner_name | String | Name of the enterprise that owns the alert |
-| RecordedFuture.PlaybookAlerts.panel_status.case_rule_id | String | Id of the playbook alert category |
-| RecordedFuture.PlaybookAlerts.panel_status.case_rule_label | String | Name of the playbook alert category |
-| RecordedFuture.PlaybookAlerts.panel_status.context_list.context | Array | Context of entity connected to the Playbook alert. |
-| RecordedFuture.PlaybookAlerts.panel_status.created | String | Date marking the creation of the Playbook alert in Recorded Future |
-| RecordedFuture.PlaybookAlerts.panel_status.entity_criticality | String | Criticality of the Playbook alert |
-| RecordedFuture.PlaybookAlerts.panel_status.entity_id | String | Id of the entity in Recorded Future |
-| RecordedFuture.PlaybookAlerts.panel_status.entity_name | String | Name of the entity |
-| RecordedFuture.PlaybookAlerts.panel_status.risk_score | String | Risk score of the entity in Recorded Future |
-| RecordedFuture.PlaybookAlerts.panel_status.targets | Array | List of targets of the Playbook alert |
-| RecordedFuture.PlaybookAlerts.panel_status.lifecycle_stage | String | Indicates what lifecycle the vulerability is in |
-| RecordedFuture.PlaybookAlerts.panel_summary.explanation | String | Entails the explanation to the triggering of the Playbook alert |
-| RecordedFuture.PlaybookAlerts.panel_summary.resolved_record_list.context_list.context | String | Context of entity connected to the Playbook alert. |
-| RecordedFuture.PlaybookAlerts.panel_summary.resolved_record_list.criticality | String | Level of criticality |
-| RecordedFuture.PlaybookAlerts.panel_summary.resolved_record_list.entity | String | ID of the entitiy in Recorded Future |
-| RecordedFuture.PlaybookAlerts.panel_summary.resolved_record_list.record_type | String | Type of record A, CNAME or MX |
-| RecordedFuture.PlaybookAlerts.panel_summary.resolved_record_list.risk_score | String | Risk score of the entity in Recorded Future |
-| RecordedFuture.PlaybookAlerts.panel_summary.screenshots.description | String | Description of the image |
-| RecordedFuture.PlaybookAlerts.panel_summary.screenshots.image_id | String | ID of the screenshot in recorded future |
-| RecordedFuture.PlaybookAlerts.panel_summary.screenshots.tag | String | Image Analisys tag |
-| RecordedFuture.PlaybookAlerts.panel_summary.screenshots.created | String | When the image was created |
-| RecordedFuture.PlaybookAlerts.panel_summary.screenshots.base64 | String | The image binary encoded as a base64 string |
-| RecordedFuture.PlaybookAlerts.panel_summary.summary.targets.name | String | Target affected by the vulnerability |
-| RecordedFuture.PlaybookAlerts.panel_summary.summary.lifecycle_stage | String | The current lifecycle stage of the Playbook Alert |
-| RecordedFuture.PlaybookAlerts.panel_summary.summary.riskrules.rule | String | Name of the rule that triggered |
-| RecordedFuture.PlaybookAlerts.panel_summary.summary.riskrules.description | String | Short description of the trigger \(E.g 13 sightings on 1 source..\) |
-| RecordedFuture.PlaybookAlerts.panel_summary.affected_products.name | String | Name of of affected product |
-| RecordedFuture.PlaybookAlerts.panel_summary.insikt_notes.id | String | The id of the Insikt note |
-| RecordedFuture.PlaybookAlerts.panel_summary.insikt_notes.title | String | The title of the Insikt note |
-| RecordedFuture.PlaybookAlerts.panel_summary.insikt_notes.topic | String | The topic of the Insikt note |
-| RecordedFuture.PlaybookAlerts.panel_summary.insikt_notes.published | String | The time at which the Insikt note was published |
-| RecordedFuture.PlaybookAlerts.panel_summary.insikt_notes.fragment | String | A fragment of the Insikt note text |
-| RecordedFuture.PlaybookAlerts.panel_log.id | String | Log id in Recorded Future |
-| RecordedFuture.PlaybookAlerts.panel_log.actor_id | String | Id of the actor |
-| RecordedFuture.PlaybookAlerts.panel_log.created | String | When was the log created |
-| RecordedFuture.PlaybookAlerts.panel_log.modified | String | When was the log last modified |
-| RecordedFuture.PlaybookAlerts.panel_log.action_priority | String | The priority of the Playbook alert |
-| RecordedFuture.PlaybookAlerts.panel_log.message | String | Log message |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.assigne_change.old | String | Previous assignee |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.assigne_change.new | String | New assignee |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.assigne_change.type | String | Type of change |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.status_change.old | String | Previous status |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.status_change.new | String | New status |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.status_change.type | String | Type of change |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.title_change.old | String | Previous title |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.title_change.new | String | New title |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.title_change.type | String | Type of change |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.priority_change.old | String | Previous priority |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.priority_change.new | String | New priority |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.priority_change.type | String | Type of change |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.reopen_strategy_change.old | String | Previous reopen strategy |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.reopen_strategy_change.new | String | New reopen strategy |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.reopen_strategy_change.type | String | Type of change |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.entities_change.removed | String | Removed entity |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.entities_change.added | String | Added entity |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.entities_change.type | String | Type of change |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.related_entities_change.removed | String | Removed related entity |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.related_entities_change.added | String | Added related entity |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.related_entities_changetype | String | Type of change |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.description_change.old | String | Previous description |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.description_change.new | String | New description |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.description_change.type | String | Type of change |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.external_id_change.old | String | Previous external ID |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.external_id_change.new | String | New external ID |
-| RecordedFuture.PlaybookAlerts.panel_log.changes.external_id_change.type | String | Type of change |
-| RecordedFuture.PlaybookAlerts.panel_action.action | String | The name of the action |
-| RecordedFuture.PlaybookAlerts.panel_action.updated | String | When was the action last updated |
-| RecordedFuture.PlaybookAlerts.panel_action.assignee_name | String | Full name of the assignee |
-| RecordedFuture.PlaybookAlerts.panel_action.assignee_id | String | ID of the assignee |
-| RecordedFuture.PlaybookAlerts.panel_action.status | String | The status of the action |
-| RecordedFuture.PlaybookAlerts.panel_action.description | String | A short description of the action |
-| RecordedFuture.PlaybookAlerts.panel_action.link | String | A link associated with the action |
-| RecordedFuture.PlaybookAlerts.panel_dns.ip_list.record | String | The DNS record |
-| RecordedFuture.PlaybookAlerts.panel_dns.ip_list.risk_score | String | Risk score associated with the record |
-| RecordedFuture.PlaybookAlerts.panel_dns.ip_list.criticality | String | The level of criticality |
-| RecordedFuture.PlaybookAlerts.panel_dns.ip_list.record_type | String | Type of record A, CNAME or MX |
-| RecordedFuture.PlaybookAlerts.panel_dns.ip_list.context_list.context | String | Labels of malicious behavior types that can be associated with an entity. |
-| RecordedFuture.PlaybookAlerts.panel_dns.mx_list.record | String | The DNS record |
-| RecordedFuture.PlaybookAlerts.panel_dns.mx_list.risk_score | String | Risk score associated with the record |
-| RecordedFuture.PlaybookAlerts.panel_dns.mx_list.criticality | String | The level of criticality |
-| RecordedFuture.PlaybookAlerts.panel_dns.mx_list.record_type | String | Type of record A, CNAME or MX |
-| RecordedFuture.PlaybookAlerts.panel_dns.mx_list.context_list.context | String | Labels of malicious behavior types that can be associated with an entity. |
-| RecordedFuture.PlaybookAlerts.panel_dns.ns_list.record | String | The DNS record |
-| RecordedFuture.PlaybookAlerts.panel_dns.ns_list.risk_score | String | Risk score associated with the record |
-| RecordedFuture.PlaybookAlerts.panel_dns.ns_list.criticality | String | The level of criticality |
-| RecordedFuture.PlaybookAlerts.panel_dns.ns_list.record_type | String | Type of record A, CNAME or MX |
-| RecordedFuture.PlaybookAlerts.panel_dns.ns_list.context_list.context | String | Labels of malicious behavior types that can be associated with an entity. |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.added | String | When the whois information was added |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.attribute | String | Attribute, either whois or whoisContancts |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.entity | String | Id of whois entity |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.provider | String | Name of provider |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.createdDate | String | When was it created |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.nameServers | Array | List of name server IDs |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.privateRegistration | Bool | Boolean indicating private registration |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.registrarName | String | Name of the registrar |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.status | String | Status of registrar |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.city | String | Contact located in this city |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.country | String | Contact located in this city |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.name | String | Name of contact |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.organization | String | Name of contact organization |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.postalCode | String | Postal code of contact organization |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.state | String | Contact located in state |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.street1 | String | Street name of contact |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.telephone | String | Phone number of contact |
-| RecordedFuture.PlaybookAlerts.panel_whois.body.value.type | String | Type of contact |
+| **Path** | **Type** | **Description** |
+|-------------------------------------------------|----------|----------------------------------------------|
+| RecordedFuture.PlaybookAlerts.playbook_alert_id | String | Unique ID of the playbook alert. |
+| RecordedFuture.PlaybookAlerts.category | String | Playbook alert category. |
+| RecordedFuture.PlaybookAlerts.priority | String | Recommended Priority of the alert. |
+| RecordedFuture.PlaybookAlerts.status | String | Current alert status in Recorded Future. |
+| RecordedFuture.PlaybookAlerts.title | String | Title of the alert. |
+| RecordedFuture.PlaybookAlerts.updated | date | Date of last update. |
+| RecordedFuture.PlaybookAlerts.created | date | Date of creation. |
+| RecordedFuture.PlaybookAlerts.organization_id | String | Organization uhash. |
+| RecordedFuture.PlaybookAlerts.organization_name | String | Plaintext Organization name. |
+| RecordedFuture.PlaybookAlerts.assignee_id | String | uhash of the assigned user. |
+| RecordedFuture.PlaybookAlerts.assignee_name | unknown | name of the assigned user. |
+| RecordedFuture.PlaybookAlerts.owner_id | String | uhash of the enterprise that owns the alert. |
+| RecordedFuture.PlaybookAlerts.owner_name | String | Name of the enterprise that owns the alert. |
-### recordedfuture-playbook-alerts-update
+
+### recordedfuture-playbook-alerts-details
***
-Update the status of one or multiple Playbook alerts
+Get Playbook alert details by ID.
#### Base Command
-`recordedfuture-playbook-alerts-update`
+`recordedfuture-playbook-alerts-details`
#### Input
-| **Argument Name** | **Description** | **Required** |
-| --- | --- | --- |
-| alert_ids | Ids of the playbook alerts that will be updated. | Required |
-| new_status | New status to set for all alerts in alert_ids. Possible values are: new, in-progress, dismissed, resolved. | Required |
+| **Argument Name** | **Description** | **Required** |
+|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
+| alert_ids | IDs of the playbook alert that should be fetched. | Required |
+| detail_sections | What evidence sections to include in the fetch. Fetches all available if not specified. Possible values are: status, action, summary, log, whois, dns. | Optional |
##### Command Example
-```!recordedfuture-playbook-alerts-update alert_ids="12312312-1231-1231-1231-123123123123" new_status="New"```
+```!recordedfuture-playbook-alerts-details alert_ids="12312312-1231-1231-1231-123123123123" detail_sections="status,log"```
#### Context Output
-| **Path** | **Type** | **Description** |
-| --- | --- | --- |
-| RecordedFuture.PlaybookAlerts.playbook_alert_id | string | Unique id of the playbook alert in Recorded Future |
-| RecordedFuture.PlaybookAlerts.current_status | string | Current status of playbook alert in Recorded Future |
-| RecordedFuture.PlaybookAlerts.title | string | Title of the playbook alert in Recorded Future |
-| RecordedFuture.PlaybookAlerts.status_message | string | Message describing the outcome of the update |
+| **Path** | **Type** | **Description** |
+|---------------------------------------------------------------------------------------|----------|---------------------------------------------------------------------------|
+| RecordedFuture.PlaybookAlerts.playbook_alert_id | String | Unique ID of the playbook alert. |
+| RecordedFuture.PlaybookAlerts.category | String | Playbook alert category. |
+| RecordedFuture.PlaybookAlerts.priority | String | Recommended Priority of the alert. |
+| RecordedFuture.PlaybookAlerts.status | String | Current alert status in Recorded Future. |
+| RecordedFuture.PlaybookAlerts.title | String | Title of the alert. |
+| RecordedFuture.PlaybookAlerts.updated | date | Date of last update. |
+| RecordedFuture.PlaybookAlerts.created | date | Date of creation. |
+| RecordedFuture.PlaybookAlerts.organization_id | String | Organization uhash. |
+| RecordedFuture.PlaybookAlerts.organization_name | String | Plaintext Organization name. |
+| RecordedFuture.PlaybookAlerts.assignee_id | String | uhash of the assigned user. |
+| RecordedFuture.PlaybookAlerts.assignee_name | String | name of the assigned user. |
+| RecordedFuture.PlaybookAlerts.owner_id | String | uhash of the enterprise that owns the alert. |
+| RecordedFuture.PlaybookAlerts.owner_name | String | Name of the enterprise that owns the alert. |
+| RecordedFuture.PlaybookAlerts.panel_status.playbook_alert_id | String | Unique ID of the playbook alert. |
+| RecordedFuture.PlaybookAlerts.panel_status.category | String | Playbook alert category. |
+| RecordedFuture.PlaybookAlerts.panel_status.priority | String | Recommended Priority of the alert. |
+| RecordedFuture.PlaybookAlerts.panel_status.status | String | Current alert status in Recorded Future. |
+| RecordedFuture.PlaybookAlerts.panel_status.title | String | Title of the alert. |
+| RecordedFuture.PlaybookAlerts.panel_status.updated | date | Date of last update. |
+| RecordedFuture.PlaybookAlerts.panel_status.created | date | Date of creation |
+| RecordedFuture.PlaybookAlerts.panel_status.organization_id | String | Organization uhash. |
+| RecordedFuture.PlaybookAlerts.panel_status.organization_name | String | Plaintext Organization name. |
+| RecordedFuture.PlaybookAlerts.panel_status.assignee_id | String | uhash of the assigned user. |
+| RecordedFuture.PlaybookAlerts.panel_status.assignee_name | unknown | name of the assigned user. |
+| RecordedFuture.PlaybookAlerts.panel_status.owner_id | String | uhash of the enterprise that owns the alert. |
+| RecordedFuture.PlaybookAlerts.panel_status.owner_name | String | Name of the enterprise that owns the alert. |
+| RecordedFuture.PlaybookAlerts.panel_status.case_rule_id | String | ID of the playbook alert category. |
+| RecordedFuture.PlaybookAlerts.panel_status.case_rule_label | String | Name of the playbook alert category. |
+| RecordedFuture.PlaybookAlerts.panel_status.context_list.context | Array | Context of entity connected to the Playbook alert. |
+| RecordedFuture.PlaybookAlerts.panel_status.created | String | Date marking the creation of the Playbook alert in Recorded Future. |
+| RecordedFuture.PlaybookAlerts.panel_status.entity_criticality | String | Criticality of the Playbook alert. |
+| RecordedFuture.PlaybookAlerts.panel_status.entity_id | String | ID of the entity in Recorded Future. |
+| RecordedFuture.PlaybookAlerts.panel_status.entity_name | String | Name of the entity. |
+| RecordedFuture.PlaybookAlerts.panel_status.risk_score | String | Risk score of the entity in Recorded Future. |
+| RecordedFuture.PlaybookAlerts.panel_status.targets | Array | List of targets of the Playbook alert. |
+| RecordedFuture.PlaybookAlerts.panel_status.lifecycle_stage | String | Indicates what lifecycle the vulerability is in. |
+| RecordedFuture.PlaybookAlerts.panel_summary.explanation | String | Entails the explanation to the triggering of the Playbook alert. |
+| RecordedFuture.PlaybookAlerts.panel_summary.resolved_record_list.context_list.context | String | Context of entity connected to the Playbook alert. |
+| RecordedFuture.PlaybookAlerts.panel_summary.resolved_record_list.criticality | String | Level of criticality. |
+| RecordedFuture.PlaybookAlerts.panel_summary.resolved_record_list.entity | String | ID of the entitiy in Recorded Future. |
+| RecordedFuture.PlaybookAlerts.panel_summary.resolved_record_list.record_type | String | Type of record A, CNAME or MX. |
+| RecordedFuture.PlaybookAlerts.panel_summary.resolved_record_list.risk_score | String | Risk score of the entity in Recorded Future. |
+| RecordedFuture.PlaybookAlerts.panel_summary.screenshots.description | String | Description of the image. |
+| RecordedFuture.PlaybookAlerts.panel_summary.screenshots.image_id | String | ID of the screenshot in recorded future. |
+| RecordedFuture.PlaybookAlerts.panel_summary.screenshots.tag | String | Image Analisys tag. |
+| RecordedFuture.PlaybookAlerts.panel_summary.screenshots.created | String | When the image was created. |
+| RecordedFuture.PlaybookAlerts.panel_summary.screenshots.base64 | String | The image binary encoded as a base64 string. |
+| RecordedFuture.PlaybookAlerts.panel_summary.summary.targets.name | String | Target affected by the vulnerability. |
+| RecordedFuture.PlaybookAlerts.panel_summary.summary.lifecycle_stage | String | The current lifecycle stage of the Playbook Alert. |
+| RecordedFuture.PlaybookAlerts.panel_summary.summary.riskrules.rule | String | Name of the rule that triggered. |
+| RecordedFuture.PlaybookAlerts.panel_summary.summary.riskrules.description | String | Short description of the trigger \(E.g 13 sightings on 1 source..\). |
+| RecordedFuture.PlaybookAlerts.panel_summary.affected_products.name | String | Name of of affected product. |
+| RecordedFuture.PlaybookAlerts.panel_summary.insikt_notes.id | String | The ID of the Insikt note. |
+| RecordedFuture.PlaybookAlerts.panel_summary.insikt_notes.title | String | The title of the Insikt note. |
+| RecordedFuture.PlaybookAlerts.panel_summary.insikt_notes.topic | String | The topic of the Insikt note. |
+| RecordedFuture.PlaybookAlerts.panel_summary.insikt_notes.published | String | The time at which the Insikt note was published. |
+| RecordedFuture.PlaybookAlerts.panel_summary.insikt_notes.fragment | String | A fragment of the Insikt note text. |
+| RecordedFuture.PlaybookAlerts.panel_log.id | String | Log ID in Recorded Future. |
+| RecordedFuture.PlaybookAlerts.panel_log.actor_id | String | ID of the actor. |
+| RecordedFuture.PlaybookAlerts.panel_log.created | String | When was the log created. |
+| RecordedFuture.PlaybookAlerts.panel_log.modified | String | When was the log last modified. |
+| RecordedFuture.PlaybookAlerts.panel_log.action_priority | String | The priority of the Playbook alert. |
+| RecordedFuture.PlaybookAlerts.panel_log.message | String | Log message. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.assigne_change.old | String | Previous assignee. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.assigne_change.new | String | New assignee. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.assigne_change.type | String | Type of change. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.status_change.old | String | Previous status. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.status_change.new | String | New status. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.status_change.type | String | Type of change. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.title_change.old | String | Previous title. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.title_change.new | String | New title. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.title_change.type | String | Type of change. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.priority_change.old | String | Previous priority. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.priority_change.new | String | New priority. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.priority_change.type | String | Type of change. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.reopen_strategy_change.old | String | Previous reopen strategy. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.reopen_strategy_change.new | String | New reopen strategy. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.reopen_strategy_change.type | String | Type of change. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.entities_change.removed | String | Removed entity. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.entities_change.added | String | Added entity. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.entities_change.type | String | Type of change. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.related_entities_change.removed | String | Removed related entity. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.related_entities_change.added | String | Added related entity. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.related_entities_changetype | String | Type of change. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.description_change.old | String | Previous description. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.description_change.new | String | New description. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.description_change.type | String | Type of change. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.external_id_change.old | String | Previous external ID. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.external_id_change.new | String | New external ID. |
+| RecordedFuture.PlaybookAlerts.panel_log.changes.external_id_change.type | String | Type of change. |
+| RecordedFuture.PlaybookAlerts.panel_action.action | String | The name of the action. |
+| RecordedFuture.PlaybookAlerts.panel_action.updated | String | When was the action last updated. |
+| RecordedFuture.PlaybookAlerts.panel_action.assignee_name | String | Full name of the assignee. |
+| RecordedFuture.PlaybookAlerts.panel_action.assignee_id | String | ID of the assignee. |
+| RecordedFuture.PlaybookAlerts.panel_action.status | String | The status of the action. |
+| RecordedFuture.PlaybookAlerts.panel_action.description | String | A short description of the action. |
+| RecordedFuture.PlaybookAlerts.panel_action.link | String | A link associated with the action. |
+| RecordedFuture.PlaybookAlerts.panel_dns.ip_list.record | String | The DNS record. |
+| RecordedFuture.PlaybookAlerts.panel_dns.ip_list.risk_score | String | Risk score associated with the record. |
+| RecordedFuture.PlaybookAlerts.panel_dns.ip_list.criticality | String | The level of criticality. |
+| RecordedFuture.PlaybookAlerts.panel_dns.ip_list.record_type | String | Type of record A, CNAME or MX. |
+| RecordedFuture.PlaybookAlerts.panel_dns.ip_list.context_list.context | String | Labels of malicious behavior types that can be associated with an entity. |
+| RecordedFuture.PlaybookAlerts.panel_dns.mx_list.record | String | The DNS record. |
+| RecordedFuture.PlaybookAlerts.panel_dns.mx_list.risk_score | String | Risk score associated with the record. |
+| RecordedFuture.PlaybookAlerts.panel_dns.mx_list.criticality | String | The level of criticality. |
+| RecordedFuture.PlaybookAlerts.panel_dns.mx_list.record_type | String | Type of record A, CNAME or MX. |
+| RecordedFuture.PlaybookAlerts.panel_dns.mx_list.context_list.context | String | Labels of malicious behavior types that can be associated with an entity. |
+| RecordedFuture.PlaybookAlerts.panel_dns.ns_list.record | String | The DNS record. |
+| RecordedFuture.PlaybookAlerts.panel_dns.ns_list.risk_score | String | Risk score associated with the record. |
+| RecordedFuture.PlaybookAlerts.panel_dns.ns_list.criticality | String | The level of criticality. |
+| RecordedFuture.PlaybookAlerts.panel_dns.ns_list.record_type | String | Type of record A, CNAME or MX. |
+| RecordedFuture.PlaybookAlerts.panel_dns.ns_list.context_list.context | String | Labels of malicious behavior types that can be associated with an entity. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.added | String | When the whois information was added. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.attribute | String | Attribute, either whois or whoisContancts. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.entity | String | ID of whois entity. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.provider | String | Name of provider. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.createdDate | String | When was it created. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.nameServers | Array | List of name server IDs. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.privateRegistration | Bool | Boolean indicating private registration. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.registrarName | String | Name of the registrar. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.status | String | Status of registrar. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.city | String | Contact located in this city. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.country | String | Contact located in this city. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.name | String | Name of contact. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.organization | String | Name of contact organization. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.postalCode | String | Postal code of contact organization. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.state | String | Contact located in state. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.street1 | String | Street name of contact. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.telephone | String | Phone number of contact. |
+| RecordedFuture.PlaybookAlerts.panel_whois.body.value.type | String | Type of contact. |
-### recordedfuture-playbook-alerts-search
+### recordedfuture-playbook-alerts-update
***
-Search playbook alerts based on filters
+Update the status of one or multiple Playbook alerts
#### Base Command
-`recordedfuture-playbook-alerts-search`
+`recordedfuture-playbook-alerts-update`
#### Input
-| **Argument Name** | **Description** | **Required** |
-| --- | --- | --- |
-| category | filter what playbook alert categories that is wanted. (default = all available). Possible values are: all_available, domain_abuse, vulnerability, code_repo_leakage. | Optional |
-| limit | Limits the number of alerts to fetch. | Optional |
-| time_since_update | Time between now and e.g. "2 hours" or "7 days" ago. | Optional |
-| playbook_alert_status | Filter what statuses are fetched, defaults to only new status if not specified. Possible values are: new, in-progress, dismissed, resolved. | Optional |
-| priority | Actions pritority assigned in Recorded Future. Possible values are: high, moderate, informational. | Optional |
-| order_search_by | Actions pritority assigned in Recorded Future. Possible values are: updated, created. | Optional |
+| **Argument Name** | **Description** | **Required** |
+|-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
+| alert_ids | IDs of the playbook alerts that will be updated. | Required |
+| new_status | New status to set for all alerts in alert_ids. Possible values are: new, in-progress, dismissed, resolved. | Required |
+| comment | Add a comment to all alerts in alert_ids. | Optional |
+| reopen | Set the reopen strategy for the alert. Reopen on significant updates or keep the alert Resolved. Default: reopen on significant updates. Can only be used with new_status=resolved. | Optional |
##### Command Example
-```!recordedfuture-playbook-alerts-search```
-```!recordedfuture-playbook-alerts-search category=domain_abuse```
-```!recordedfuture-playbook-alerts-search category=vulnerability```
-```!recordedfuture-playbook-alerts-search limit=10```
-```!recordedfuture-playbook-alerts-search playbook_alert_status=in-progress```
-```!recordedfuture-playbook-alerts-search priority=high```
-```!recordedfuture-playbook-alerts-search order_search_by=updated```
-
+```!recordedfuture-playbook-alerts-update alert_ids="12312312-1231-1231-1231-123123123123" new_status="New"```
#### Context Output
-| **Path** | **Type** | **Description** |
-| --- | --- | --- |
-| RecordedFuture.PlaybookAlerts.playbook_alert_id | String | Unique id of the playbook alert |
-| RecordedFuture.PlaybookAlerts.category | String | Playbook alert category |
-| RecordedFuture.PlaybookAlerts.priority | String | Recommended Priority of the alert |
-| RecordedFuture.PlaybookAlerts.status | String | Current alert status in Recorded Future |
-| RecordedFuture.PlaybookAlerts.title | String | Title of the alert |
-| RecordedFuture.PlaybookAlerts.updated | date | Date of last update |
-| RecordedFuture.PlaybookAlerts.created | date | Date of creation |
-| RecordedFuture.PlaybookAlerts.organization_id | String | Organization uhash |
-| RecordedFuture.PlaybookAlerts.organization_name | String | Plaintext Organization name |
-| RecordedFuture.PlaybookAlerts.assignee_id | String | uhash of the assigned user |
-| RecordedFuture.PlaybookAlerts.assignee_name | unknown | name of the assigned user |
-| RecordedFuture.PlaybookAlerts.owner_id | String | uhash of the enterprise that owns the alert |
-| RecordedFuture.PlaybookAlerts.owner_name | String | Name of the enterprise that owns the alert |
+| **Path** | **Type** | **Description** |
+|-------------------------------------------------|----------|------------------------------------------------------|
+| RecordedFuture.PlaybookAlerts.playbook_alert_id | string | Unique ID of the playbook alert in Recorded Future. |
+| RecordedFuture.PlaybookAlerts.current_status | string | Current status of playbook alert in Recorded Future. |
+| RecordedFuture.PlaybookAlerts.title | string | Title of the playbook alert in Recorded Future. |
+| RecordedFuture.PlaybookAlerts.status_message | string | Message describing the outcome of the update. |
+
+---
\ No newline at end of file
diff --git a/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/RecordedFuturePlaybookAlerts.py b/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/RecordedFuturePlaybookAlerts.py
index 9b7a7b73645..41f0f672e53 100644
--- a/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/RecordedFuturePlaybookAlerts.py
+++ b/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/RecordedFuturePlaybookAlerts.py
@@ -1,10 +1,12 @@
import demistomock as demisto # noqa: F401
from CommonServerPython import * # noqa: F401
+
"""Recorded Future Playbook alerts Integration for Demisto."""
import platform
import json
import base64
+from typing import Any, Dict, List, Optional
# flake8: noqa: F402,F405 lgtm
@@ -13,7 +15,11 @@
# disable insecure warnings
requests.packages.urllib3.disable_warnings() # type: ignore
-__version__ = '1.0.2'
+__version__ = "1.0.3"
+
+TIMEOUT_60 = 60
+TIMEOUT_90 = 90
+TIMEOUT_120 = 120
# === === === === === === === === === === === === === === ===
@@ -25,31 +31,40 @@ class Client(BaseClient):
def whoami(self) -> Dict[str, Any]:
return self._http_request(
- method='get',
- url_suffix='info/whoami',
- timeout=60,
+ method="get",
+ url_suffix="info/whoami",
+ timeout=TIMEOUT_60,
)
- def _call(self, url_suffix, **kwargs):
+ def _call(self, url_suffix: str, **kwargs):
json_data = {
- 'demisto_command': demisto.command(),
- 'demisto_args': demisto.args(),
+ "demisto_command": demisto.command(),
+ "demisto_args": demisto.args(),
+ "demisto_params": demisto.params(),
+ "demisto_last_run": demisto.getLastRun(),
}
- if 'demisto_args' in kwargs.keys():
- if args := kwargs.get('demisto_args'):
- json_data.update({'demisto_args': args})
- kwargs.pop('demisto_args')
- method = kwargs.get('method', 'post')
+ overwrite_keys = (
+ "demisto_command",
+ "demisto_args",
+ "demisto_params",
+ "demisto_last_run",
+ )
+ for k in overwrite_keys:
+ if k in kwargs:
+ v = kwargs.pop(k)
+ json_data[k] = v
+
+ method = kwargs.get("method", "post")
request_kwargs = {
- 'method': method,
- 'url_suffix': url_suffix,
- 'json_data': json_data,
- 'timeout': 90,
- 'retries': 3,
- 'status_list_to_retry': STATUS_TO_RETRY,
+ "method": method,
+ "url_suffix": url_suffix,
+ "json_data": json_data,
+ "timeout": TIMEOUT_90,
+ "retries": 3,
+ "status_list_to_retry": STATUS_TO_RETRY,
}
request_kwargs.update(kwargs)
@@ -57,69 +72,51 @@ def _call(self, url_suffix, **kwargs):
try:
response = self._http_request(**request_kwargs)
- if isinstance(response, dict) and response.get('return_error'):
+ if isinstance(response, dict) and response.get("return_error"):
# This will raise the Exception or call "demisto.results()" for the error and sys.exit(0).
- return_error(**response['return_error'])
+ return_error(**response["return_error"])
+
+ return response
except DemistoException as err:
- if '404' in str(err):
+ if "404" in str(err):
return CommandResults(
- outputs_prefix='',
- outputs=dict(),
- raw_response=dict(),
- readable_output='No results found.',
- outputs_key_field='',
+ outputs_prefix="",
+ outputs={},
+ raw_response={},
+ readable_output="No results found.",
+ outputs_key_field="",
)
+ elif err.res is not None:
+ try:
+ error_response_json = err.res.json()
+ # This will raise the Exception or call "demisto.results()" for the error and sys.exit(0).
+ return_error(message=error_response_json["message"])
+ except (json.JSONDecodeError, KeyError):
+ raise err
else:
raise err
- return response
+ #######################################################
+ ################## Playbook alerts ####################
+ #######################################################
def fetch_incidents(self) -> Dict[str, Any]:
"""Fetch incidents."""
return self._call(
- url_suffix=f'/v2/playbook_alert/fetch',
- json_data={
- 'demisto_command': demisto.command(),
- 'demisto_args': demisto.args(),
- 'demisto_params': demisto.params(),
- 'demisto_last_run': demisto.getLastRun(),
- },
- timeout=120,
+ url_suffix="/v2/playbook_alert/fetch",
+ timeout=TIMEOUT_120,
)
- #######################################################
- ################## Playbook alerts ####################
- #######################################################
+ def search_playbook_alerts(self) -> Dict[str, Any]:
+ return self._call(url_suffix="/v2/playbook_alert/search")
def details_playbook_alerts(self) -> Dict[str, Any]:
- parsed_args = demisto.args()
- if alert_ids := parsed_args.get('alert_ids'):
- parsed_args['alert_ids'] = alert_ids.split(",")
- if sections := parsed_args.get('detail_sections'):
- parsed_args["detail_sections"] = sections.split(",")
"""Get details of a playbook alert"""
- return self._call(
- url_suffix='/v2/playbook_alert/lookup', demisto_args=parsed_args
- )
+ return self._call(url_suffix="/v2/playbook_alert/lookup")
def update_playbook_alerts(self) -> Dict[str, Any]:
- parsed_args = demisto.args()
- if ids := parsed_args.get('alert_ids'):
- parsed_args["alert_ids"] = ids.split(",")
- return self._call(
- url_suffix='/v2/playbook_alert/update', demisto_args=parsed_args
- )
-
- def search_playbook_alerts(self) -> Dict[str, Any]:
- parsed_args = demisto.args()
- if categories := parsed_args.get('category'):
- parsed_args["category"] = categories.split(",")
- if statuses := parsed_args.get('playbook_alert_status'):
- parsed_args["playbook_alert_status"] = statuses.split(",")
- return self._call(
- url_suffix='/v2/playbook_alert/search', demisto_args=parsed_args
- )
+ return self._call(url_suffix="/v2/playbook_alert/update")
# === === === === === === === === === === === === === === ===
@@ -142,18 +139,22 @@ def _process_result_actions(
# In case API returned a str - we don't want to call "response.get()" on a str object.
return None # type: ignore
- result_actions: Union[List[dict], None] = response.get('result_actions')
+ result_actions: Union[List[dict], None] = response.get("result_actions")
if not result_actions:
return None # type: ignore
command_results: List[CommandResults] = list()
for action in result_actions:
- if 'CommandResults' in action:
- command_results.append(CommandResults(**action['CommandResults']))
+ if "CommandResults" in action:
+ command_results.append(CommandResults(**action["CommandResults"]))
return command_results
+ #######################################################
+ ################## Playbook alerts ####################
+ #######################################################
+
def fetch_incidents(self) -> None:
response = self.client.fetch_incidents()
@@ -163,47 +164,50 @@ def fetch_incidents(self) -> None:
return
for _key, _val in response.items():
- if _key == 'demisto_last_run':
+ if _key == "demisto_last_run":
demisto.setLastRun(_val)
- if _key == 'incidents':
- for incident in _val:
- attachments = list()
- incident_json = json.loads(incident.get("rawJSON", "{}"))
- if incident_json.get("panel_evidence_summary", {}).get(
- "screenshots"
- ):
- for screenshot_data in incident_json["panel_evidence_summary"][
- "screenshots"
- ]:
- file_name = f'{screenshot_data.get("image_id", "").replace("img:","")}.png'
- file_data = screenshot_data.get("base64", "")
- file = fileResult(file_name, base64.b64decode(file_data))
- attachment = {
- "description": screenshot_data.get('description'),
- "name": file.get("File"),
- "path": file.get("FileID"),
- "showMediaFile": True,
- }
- attachments.append(attachment)
- incident['attachment'] = attachments
-
+ if _key == "incidents":
+ self._transform_incidents_attachments(_val)
demisto.incidents(_val)
#######################################################
################## Playbook alerts ####################
#######################################################
- def playbook_alert_details_command(self) -> List[CommandResults]:
+ def playbook_alert_search_command(self) -> Optional[List[CommandResults]]:
+ response = self.client.search_playbook_alerts()
+ return self._process_result_actions(response=response)
+
+ def playbook_alert_details_command(self) -> Optional[List[CommandResults]]:
response = self.client.details_playbook_alerts()
return self._process_result_actions(response=response)
- def playbook_alert_update_command(self) -> List[CommandResults]:
+ def playbook_alert_update_command(self) -> Optional[List[CommandResults]]:
response = self.client.update_playbook_alerts()
return self._process_result_actions(response=response)
- def playbook_alert_search_command(self) -> List[CommandResults]:
- response = self.client.search_playbook_alerts()
- return self._process_result_actions(response=response)
+ @staticmethod
+ def _transform_incidents_attachments(incidents: list) -> None:
+ for incident in incidents:
+ attachments = []
+ incident_json = json.loads(incident.get("rawJSON", "{}"))
+ if incident_json.get("panel_evidence_summary", {}).get("screenshots"):
+ for screenshot_data in incident_json["panel_evidence_summary"][
+ "screenshots"
+ ]:
+ file_name = (
+ f"{screenshot_data.get('image_id', '').replace('img:', '')}.png"
+ )
+ file_data = screenshot_data.get("base64", "")
+ file = fileResult(file_name, base64.b64decode(file_data))
+ attachment = {
+ "description": screenshot_data.get("description"),
+ "name": file.get("File"),
+ "path": file.get("FileID"),
+ "showMediaFile": True,
+ }
+ attachments.append(attachment)
+ incident["attachment"] = attachments
# === === === === === === === === === === === === === === ===
@@ -211,70 +215,89 @@ def playbook_alert_search_command(self) -> List[CommandResults]:
# === === === === === === === === === === === === === === ===
+def get_client() -> Client:
+ demisto_params = demisto.params()
+ base_url = demisto_params.get("server_url", "").rstrip("/")
+ verify_ssl = not demisto_params.get("unsecure", False)
+ handle_proxy()
+
+ api_token = demisto_params["token"].get("password")
+
+ if not api_token:
+ return_error(message="Please provide a valid API token")
+
+ headers = {
+ "X-RFToken": api_token,
+ "X-RF-User-Agent": (
+ f"RecordedFuturePlaybookAlerts.py/{__version__} ({platform.platform()}) "
+ f"(Cortex_XSOAR_{demisto.demistoVersion()['version']})"
+ ),
+ }
+
+ client = Client(
+ base_url=base_url,
+ verify=verify_ssl,
+ headers=headers,
+ )
+
+ return client
+
+
def main() -> None:
"""Main method used to run actions."""
try:
- demisto_params = demisto.params()
- base_url = demisto_params.get('server_url', '').rstrip('/')
- verify_ssl = not demisto_params.get('insecure', False)
- proxy = demisto_params.get('proxy', False)
-
- headers = {
- 'X-RFToken': demisto_params['token'].get('password'),
- 'X-RF-User-Agent': (
- f'RecordedFuturePlaybookAlerts.py/{__version__} ({platform.platform()}) '
- f'XSOAR/{__version__} '
- f'RFClient/{__version__} (Cortex_XSOAR_{demisto.demistoVersion()["version"]})'
- ),
- }
- client = Client(
- base_url=base_url, verify=verify_ssl, headers=headers, proxy=proxy
- )
+ client = get_client()
command = demisto.command()
actions = Actions(client)
- if command == 'test-module':
+ if command == "test-module":
# This is the call made when pressing the integration Test button.
- # Returning 'ok' indicates that the integration works like it suppose to and
+ # Returning "ok" indicates that the integration works like it suppose to and
# connection to the service is successful.
- # Returning 'ok' will make the test result be green.
+ # Returning "ok" will make the test result be green.
# Any other response will make the test result be red.
try:
client.whoami()
- return_results('ok')
+ return_results("ok")
except Exception as err:
message = str(err)
try:
- error = json.loads(str(err).split('\n')[1])
- if 'fail' in error.get('result', dict()).get('status', ''):
- message = error.get('result', dict())['message']
+ error = json.loads(str(err).split("\n")[1])
+ if "fail" in error.get("result", {}).get("status", ""):
+ message = error.get("result", {})["message"]
except Exception:
message = (
- 'Unknown error. Please verify that the API'
- f' URL and Token are correctly configured. RAW Error: {err}'
+ "Unknown error. Please verify that the API"
+ f" URL and Token are correctly configured. RAW Error: {err}"
)
- raise DemistoException(f'Failed due to - {message}')
+ raise DemistoException(f"Failed due to - {message}")
- elif command == 'fetch-incidents':
+ elif command == "fetch-incidents":
actions.fetch_incidents()
#######################################################
################## Playbook alerts ####################
#######################################################
- elif command == 'recordedfuture-playbook-alerts-details':
+ elif command == "recordedfuture-playbook-alerts-search":
+ return_results(actions.playbook_alert_search_command())
+
+ elif command == "recordedfuture-playbook-alerts-details":
return_results(actions.playbook_alert_details_command())
- elif command == 'recordedfuture-playbook-alerts-update':
+ elif command == "recordedfuture-playbook-alerts-update":
return_results(actions.playbook_alert_update_command())
- elif command == 'recordedfuture-playbook-alerts-search':
- return_results(actions.playbook_alert_search_command())
+ else:
+ return_error(message=f"Unknown command: {command}")
except Exception as e:
- return_error(message=f'Failed to execute {demisto.command()} command: {str(e)}')
+ return_error(
+ message=f"Failed to execute {demisto.command()} command. Error: {str(e)}",
+ error=e,
+ )
-if __name__ in ('__main__', '__builtin__', 'builtins'):
+if __name__ in ("__main__", "__builtin__", "builtins"):
main()
diff --git a/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/RecordedFuturePlaybookAlerts.yml b/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/RecordedFuturePlaybookAlerts.yml
index d1e13ac7b8b..34400fc23d9 100644
--- a/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/RecordedFuturePlaybookAlerts.yml
+++ b/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/RecordedFuturePlaybookAlerts.yml
@@ -83,7 +83,7 @@ script:
script: '-'
type: python
subtype: python3
- dockerimage: demisto/python3:3.10.14.91134
+ dockerimage: demisto/python3:3.11.10.111039
commands:
- name: recordedfuture-playbook-alerts-details
description: Get Playbook alert details by id.
@@ -92,7 +92,7 @@ script:
required: true
description: Ids of the playbook alert that should be fetched.
- name: detail_sections
- description: What evidence sections to include in the fetch, fetches all available if not specified.
+ description: What evidence sections to include in the fetch. Fetches all available if not specified.
auto: PREDEFINED
predefined:
- status
@@ -512,6 +512,16 @@ script:
- in-progress
- dismissed
- resolved
+ - name: comment
+ description: Add comment to all alerts in alert_ids.
+ required: false
+ - name: reopen
+ description: 'Set the reopen strategy for the alert. Reopen on significant updates or keep the alert Resolved. Default: reopen on significant updates. Can only be used with new_status=resolved.'
+ required: false
+ auto: PREDEFINED
+ predefined:
+ - never
+ - significant_updates
outputs:
- contextPath: RecordedFuture.PlaybookAlerts.playbook_alert_id
description: Unique id of the playbook alert in Recorded Future.
@@ -530,7 +540,7 @@ script:
description: Search playbook alerts based on filters.
arguments:
- name: category
- description: filter what playbook alert categories that is wanted. (default = all available).
+ description: The playbook alert categories to retrieve. Default is all_available.
auto: PREDEFINED
predefined:
- all_available
@@ -538,9 +548,9 @@ script:
- vulnerability
- code_repo_leakage
- name: limit
- description: Limits the number of alerts to fetch.
+ description: The maximum number of alerts to fetch.
- name: time_since_update
- description: Time between now and e.g. "2 hours" or "7 days" ago.
+ description: The amount of time since the last update. E.g., "2 hours" or "7 days" ago.
- name: playbook_alert_status
auto: PREDEFINED
predefined:
@@ -548,7 +558,7 @@ script:
- in-progress
- dismissed
- resolved
- description: Filter what statuses are fetched, defaults to only new status if not specified.
+ description: The statuses to retrieve. Defaults to only new status if not specified.
- name: priority
auto: PREDEFINED
predefined:
@@ -561,7 +571,7 @@ script:
predefined:
- updated
- created
- description: Actions pritority assigned in Recorded Future.
+ description: The order by which to search for playbook alerts.
outputs:
- contextPath: RecordedFuture.PlaybookAlerts.playbook_alert_id
description: Unique id of the playbook alert.
diff --git a/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/RecordedFuturePlaybookAlerts_test.py b/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/RecordedFuturePlaybookAlerts_test.py
index 6a99058efb4..09ce9bf1920 100644
--- a/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/RecordedFuturePlaybookAlerts_test.py
+++ b/Packs/RecordedFuture/Integrations/RecordedFuturePlaybookAlerts/RecordedFuturePlaybookAlerts_test.py
@@ -1,13 +1,16 @@
+import pytest
+
+
def create_client():
import os
from RecordedFuturePlaybookAlerts import Client, __version__
- base_url = 'https://api.recordedfuture.com/gw/xsoar/'
+ base_url = "https://api.recordedfuture.com/gw/xsoar/"
verify_ssl = True
- token = os.environ.get('RF_TOKEN')
+ token = os.environ.get("RF_TOKEN")
headers = {
- 'X-RFToken': token,
- 'X-RF-User-Agent': f"RecordedFuturePlaybookAlerts.py/{__version__} (Linux-5.13.0-1031-aws-x86_64-with) "
+ "X-RFToken": token,
+ "X-RF-User-Agent": f"RecordedFuturePlaybookAlerts.py/{__version__} (Linux-5.13.0-1031-aws-x86_64-with) "
"XSOAR/2.4 RFClient/2.4 (Cortex_XSOAR_6.5.0)",
}
@@ -18,13 +21,13 @@ class TestRFClient:
def test_whoami(self, mocker):
client = create_client()
- mock_http_request = mocker.patch.object(client, '_http_request')
+ mock_http_request = mocker.patch.object(client, "_http_request")
client.whoami()
mock_http_request.assert_called_once_with(
- method='get',
- url_suffix='info/whoami',
+ method="get",
+ url_suffix="info/whoami",
timeout=60,
)
@@ -39,30 +42,36 @@ def test_call_with_kwargs(self, mocker):
STATUS_TO_RETRY = [500, 501, 502, 503, 504]
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
+ mock_params = {"param1": "param1 value"}
+ mock_last_run_dict = {"lastRun": "2022-08-31T12:12:20+00:00"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
client = create_client()
- mock_http_request = mocker.patch.object(client, '_http_request')
+ mock_http_request = mocker.patch.object(client, "_http_request")
- mock_url_suffix = 'mock_url_suffix'
+ mock_url_suffix = "mock_url_suffix"
client._call(url_suffix=mock_url_suffix, timeout=120, any_other_kwarg=True)
json_data = {
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
+ "demisto_command": mock_command_name,
+ "demisto_args": mock_command_args,
+ "demisto_last_run": mock_last_run_dict,
+ "demisto_params": mock_params,
}
mock_http_request.assert_called_once_with(
- method='post',
+ method="post",
url_suffix=mock_url_suffix,
json_data=json_data,
timeout=120,
@@ -80,22 +89,26 @@ def test_call_returns_response(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
+ mock_params = {"param1": "param1 value"}
+ mock_last_run_dict = {"lastRun": "2022-08-31T12:12:20+00:00"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
client = create_client()
- mock_response = {'response': {'data': 'mock data'}}
+ mock_response = {"response": {"data": "mock data"}}
- mocker.patch.object(client, '_http_request', return_value=mock_response)
+ mocker.patch.object(client, "_http_request", return_value=mock_response)
- mock_url_suffix = 'mock_url_suffix'
+ mock_url_suffix = "mock_url_suffix"
response = client._call(url_suffix=mock_url_suffix)
assert response == mock_response
@@ -111,36 +124,42 @@ def test_call_response_processing_return_error(self, mocker):
STATUS_TO_RETRY = [500, 501, 502, 503, 504]
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
+ mock_params = {"param1": "param1 value"}
+ mock_last_run_dict = {"lastRun": "2022-08-31T12:12:20+00:00"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
- mock_return_error = mocker.patch('RecordedFuturePlaybookAlerts.return_error')
+ mock_return_error = mocker.patch("RecordedFuturePlaybookAlerts.return_error")
client = create_client()
mock_http_request = mocker.patch.object(
client,
- '_http_request',
- return_value={'return_error': {'message': 'mock error'}},
+ "_http_request",
+ return_value={"return_error": {"message": "mock error"}},
)
- mock_url_suffix = 'mock_url_suffix'
+ mock_url_suffix = "mock_url_suffix"
client._call(url_suffix=mock_url_suffix)
json_data = {
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
+ "demisto_command": mock_command_name,
+ "demisto_args": mock_command_args,
+ "demisto_last_run": mock_last_run_dict,
+ "demisto_params": mock_params,
}
mock_http_request.assert_called_once_with(
- method='post',
+ method="post",
url_suffix=mock_url_suffix,
json_data=json_data,
timeout=90,
@@ -148,7 +167,7 @@ def test_call_response_processing_return_error(self, mocker):
status_list_to_retry=STATUS_TO_RETRY,
)
- mock_return_error.assert_called_once_with(message='mock error')
+ mock_return_error.assert_called_once_with(message="mock error")
def test_call_response_processing_404(self, mocker):
"""
@@ -162,39 +181,45 @@ def test_call_response_processing_404(self, mocker):
STATUS_TO_RETRY = [500, 501, 502, 503, 504]
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
+ mock_params = {"param1": "param1 value"}
+ mock_last_run_dict = {"lastRun": "2022-08-31T12:12:20+00:00"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
- mocker.patch('RecordedFuturePlaybookAlerts.return_error')
+ mocker.patch("RecordedFuturePlaybookAlerts.return_error")
client = create_client()
def mock_http_request_method(*args, **kwargs):
# Imitate how CommonServerPython handles bad responses (when status code not in ok_codes,
# or if ok_codes=None - it uses requests.Response.ok to check whether response is good).
- raise DemistoException('404')
+ raise DemistoException("404")
- mocker.patch.object(client, '_http_request', mock_http_request_method)
+ mocker.patch.object(client, "_http_request", mock_http_request_method)
- spy_http_request = mocker.spy(client, '_http_request')
+ spy_http_request = mocker.spy(client, "_http_request")
- mock_url_suffix = 'mock_url_suffix'
+ mock_url_suffix = "mock_url_suffix"
result = client._call(url_suffix=mock_url_suffix)
json_data = {
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
+ "demisto_command": mock_command_name,
+ "demisto_args": mock_command_args,
+ "demisto_last_run": mock_last_run_dict,
+ "demisto_params": mock_params,
}
spy_http_request.assert_called_once_with(
- method='post',
+ method="post",
url_suffix=mock_url_suffix,
json_data=json_data,
timeout=90,
@@ -204,49 +229,42 @@ def mock_http_request_method(*args, **kwargs):
assert isinstance(result, CommandResults)
- assert result.outputs_prefix == ''
- assert result.outputs_key_field == ''
- assert result.outputs == dict()
- assert result.raw_response == dict()
- assert result.readable_output == 'No results found.'
+ assert result.outputs_prefix == ""
+ assert result.outputs_key_field == ""
+ assert result.outputs == {}
+ assert result.raw_response == {}
+ assert result.readable_output == "No results found."
def test_fetch_incidents(self, mocker):
import os
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
- mock_params = {'param1': 'param1 value'}
-
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
- mocker.patch.object(demisto, 'params', return_value=mock_params)
-
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
+ mock_params = {"param1": "param1 value"}
mock_last_run_dict = {"lastRun": "2022-08-31T12:12:20+00:00"}
- mocker.patch.object(demisto, 'getLastRun', return_value=mock_last_run_dict)
+
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.fetch_incidents()
mock_call.assert_called_once_with(
- json_data={
- 'demisto_command': mock_command_name,
- 'demisto_args': mock_command_args,
- 'demisto_last_run': mock_last_run_dict,
- 'demisto_params': mock_params,
- },
timeout=120,
- url_suffix='/v2/playbook_alert/fetch',
+ url_suffix="/v2/playbook_alert/fetch",
)
assert response == mock_call_response
@@ -256,27 +274,29 @@ def test_playbook_alert_search(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
+ mock_params = {"param1": "param1 value"}
+ mock_last_run_dict = {"lastRun": "2022-08-31T12:12:20+00:00"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.search_playbook_alerts()
- mock_call.assert_called_once_with(
- demisto_args=mock_command_args, url_suffix='/v2/playbook_alert/search'
- )
+ mock_call.assert_called_once_with(url_suffix="/v2/playbook_alert/search")
assert response == mock_call_response
@@ -284,32 +304,35 @@ def test_playbook_alert_details_multi_input(self, mocker):
import os
import demistomock as demisto
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
- mock_command_name = 'command_name'
+ mock_command_name = "command_name"
mock_alert_ids = "input1,mock_value"
mock_detail_sections = "input1,mock_value"
mock_command_args = {
- 'alert_ids': mock_alert_ids,
- 'detail_sections': mock_detail_sections,
+ "alert_ids": mock_alert_ids,
+ "detail_sections": mock_detail_sections,
}
- mock_args_processed = {k: v.split(",") for k, v in mock_command_args.items()}
+ # mock_args_processed = {k: v.split(",") for k, v in mock_command_args.items()}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mock_params = {"param1": "param1 value"}
+ mock_last_run_dict = {"lastRun": "2022-08-31T12:12:20+00:00"}
+
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
client = create_client()
mock_call_response = {"resonse": {"data": "mock respose"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.details_playbook_alerts()
- mock_call.assert_called_once_with(
- demisto_args=mock_args_processed, url_suffix='/v2/playbook_alert/lookup'
- )
+ mock_call.assert_called_once_with(url_suffix="/v2/playbook_alert/lookup")
assert response == mock_call_response
@@ -317,29 +340,32 @@ def test_playbook_alert_update_multi_input(self, mocker):
import os
import demistomock as demisto
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
- mock_command_name = 'command_name'
+ mock_command_name = "command_name"
mock_alert_ids = "input1,input2"
- mock_command_args = {'alert_ids': mock_alert_ids}
- mock_args_processed = {k: v.split(",") for k, v in mock_command_args.items()}
+ mock_command_args = {"alert_ids": mock_alert_ids}
+ # mock_args_processed = {k: v.split(",") for k, v in mock_command_args.items()}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mock_params = {"param1": "param1 value"}
+ mock_last_run_dict = {"lastRun": "2022-08-31T12:12:20+00:00"}
+
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
client = create_client()
mock_call_response = {"resonse": {"data": "mock respose"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.update_playbook_alerts()
- mock_call.assert_called_once_with(
- demisto_args=mock_args_processed, url_suffix='/v2/playbook_alert/update'
- )
+ mock_call.assert_called_once_with(url_suffix="/v2/playbook_alert/update")
assert response == mock_call_response
@@ -347,32 +373,35 @@ def test_playbook_alert_search_multi_input(self, mocker):
import os
import demistomock as demisto
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
- mock_command_name = 'command_name'
+ mock_command_name = "command_name"
mock_alert_ids = "ajdaojw,1woodaw"
mock_detail_sections = "sdadwa,adinhw0ijd"
mock_command_args = {
- 'category': mock_alert_ids,
- 'playbook_alert_status': mock_detail_sections,
+ "category": mock_alert_ids,
+ "playbook_alert_status": mock_detail_sections,
}
- mock_args_processed = {k: v.split(",") for k, v in mock_command_args.items()}
+ # mock_args_processed = {k: v.split(",") for k, v in mock_command_args.items()}
+
+ mock_params = {"param1": "param1 value"}
+ mock_last_run_dict = {"lastRun": "2022-08-31T12:12:20+00:00"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
client = create_client()
mock_call_response = {"resonse": {"data": "mock respose"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.search_playbook_alerts()
- mock_call.assert_called_once_with(
- demisto_args=mock_args_processed, url_suffix='/v2/playbook_alert/search'
- )
+ mock_call.assert_called_once_with(url_suffix="/v2/playbook_alert/search")
assert response == mock_call_response
@@ -381,27 +410,29 @@ def test_playbook_alert_details(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
+ mock_params = {"param1": "param1 value"}
+ mock_last_run_dict = {"lastRun": "2022-08-31T12:12:20+00:00"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.details_playbook_alerts()
- mock_call.assert_called_once_with(
- demisto_args=mock_command_args, url_suffix='/v2/playbook_alert/lookup'
- )
+ mock_call.assert_called_once_with(url_suffix="/v2/playbook_alert/lookup")
assert response == mock_call_response
@@ -410,30 +441,93 @@ def test_playbook_alert_update(self, mocker):
import demistomock as demisto
# This is needed for CommonServerPython module to not add demisto.params() into callingContext.
- os.environ['COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS'] = 'True'
+ os.environ["COMMON_SERVER_NO_AUTO_PARAMS_REMOVE_NULLS"] = "True"
# Mock demisto command and args.
- mock_command_name = 'command_name'
- mock_command_args = {'arg1': 'arg1_value', 'arg2': 'arg2_value'}
+ mock_command_name = "command_name"
+ mock_command_args = {"arg1": "arg1_value", "arg2": "arg2_value"}
+ mock_params = {"param1": "param1 value"}
+ mock_last_run_dict = {"lastRun": "2022-08-31T12:12:20+00:00"}
- mocker.patch.object(demisto, 'command', return_value=mock_command_name)
- mocker.patch.object(demisto, 'args', return_value=mock_command_args)
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
client = create_client()
- mock_call_response = {'response': {'data': 'mock response'}}
+ mock_call_response = {"response": {"data": "mock response"}}
mock_call = mocker.patch.object(
- client, '_call', return_value=mock_call_response
+ client, "_call", return_value=mock_call_response
)
response = client.update_playbook_alerts()
- mock_call.assert_called_once_with(
- demisto_args=mock_command_args, url_suffix='/v2/playbook_alert/update'
- )
+ mock_call.assert_called_once_with(url_suffix="/v2/playbook_alert/update")
assert response == mock_call_response
+ def test_call_DemistoException_res_json_error(self, mocker):
+ """Test _call when err.res.json() raises an exception."""
+ import demistomock as demisto
+ from CommonServerPython import DemistoException
+ import json
+
+ client = create_client()
+
+ mock_command_name = "command_name"
+ mock_command_args = {}
+ mock_params = {}
+ mock_last_run_dict = {}
+
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
+
+ class MockResponse:
+ def json(self):
+ raise json.JSONDecodeError("Expecting value", "doc", 0)
+
+ def mock_http_request(*args, **kwargs):
+ err = DemistoException("Error with response")
+ err.res = MockResponse()
+ raise err
+
+ mocker.patch.object(client, "_http_request", side_effect=mock_http_request)
+
+ with pytest.raises(DemistoException):
+ client._call(url_suffix="mock_url_suffix")
+
+ def test_call_DemistoException_res_None(self, mocker):
+ """Test _call when DemistoException has no response."""
+ import demistomock as demisto
+ from CommonServerPython import DemistoException
+
+ client = create_client()
+
+ mock_command_name = "command_name"
+ mock_command_args = {}
+ mock_params = {}
+ mock_last_run_dict = {}
+
+ mocker.patch.object(demisto, "command", return_value=mock_command_name)
+ mocker.patch.object(demisto, "args", return_value=mock_command_args)
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mocker.patch.object(demisto, "getLastRun", return_value=mock_last_run_dict)
+
+ def mock_http_request(*args, **kwargs):
+ err = DemistoException("Some error without response")
+ err.res = None
+ raise err
+
+ mocker.patch.object(client, "_http_request", side_effect=mock_http_request)
+
+ with pytest.raises(DemistoException) as excinfo:
+ client._call(url_suffix="mock_url_suffix")
+
+ assert str(excinfo.value) == "Some error without response"
+
class TestActions:
def test_init(self, mocker):
@@ -452,7 +546,7 @@ def test_process_result_actions_404(self, mocker):
# Test if response is CommandResults
# (case when we got 404 on response, and it was processed in self.client._call() method).
- response = CommandResults(readable_output='Mock')
+ response = CommandResults(readable_output="Mock")
result_actions = actions._process_result_actions(response=response)
assert result_actions == [response]
@@ -463,7 +557,7 @@ def test_process_result_actions_response_is_not_dict(self, mocker):
actions = Actions(mock_client)
# Test if response is not CommandResults and not Dict.
- response = 'Mock string - not CommandResults and not dict'
+ response = "Mock string - not CommandResults and not dict"
result_actions = actions._process_result_actions(response=response) # type: ignore
assert result_actions is None
@@ -476,20 +570,20 @@ def test_process_result_actions_no_or_empty_result_actions_in_response(
actions = Actions(mock_client)
# Test no results_actions in response.
- response = {'data': 'mock'}
+ response = {"data": "mock"}
result_actions = actions._process_result_actions(response=response)
assert result_actions is None
# Test case when bool(results_actions) in response is False.
- response = {'data': 'mock', 'result_actions': None}
+ response = {"data": "mock", "result_actions": None}
result_actions = actions._process_result_actions(response=response)
assert result_actions is None
- response = {'data': 'mock', 'result_actions': list()}
+ response = {"data": "mock", "result_actions": []}
result_actions = actions._process_result_actions(response=response)
assert result_actions is None
- response = {'data': 'mock', 'result_actions': dict()}
+ response = {"data": "mock", "result_actions": {}}
result_actions = actions._process_result_actions(response=response)
assert result_actions is None
@@ -500,15 +594,15 @@ def test_process_result_actions_command_results_only(self, mocker):
actions = Actions(mock_client)
response = {
- 'data': 'mock',
- 'result_actions': [
+ "data": "mock",
+ "result_actions": [
{
- 'CommandResults': {
- 'outputs_prefix': 'mock_outputs_prefix',
- 'outputs': 'mock_outputs',
- 'raw_response': 'mock_raw_response',
- 'readable_output': 'mock_readable_output',
- 'outputs_key_field': 'mock_outputs_key_field',
+ "CommandResults": {
+ "outputs_prefix": "mock_outputs_prefix",
+ "outputs": "mock_outputs",
+ "raw_response": "mock_raw_response",
+ "readable_output": "mock_readable_output",
+ "outputs_key_field": "mock_outputs_key_field",
},
}
],
@@ -521,11 +615,11 @@ def test_process_result_actions_command_results_only(self, mocker):
assert isinstance(r_a, CommandResults)
- assert r_a.outputs_prefix == 'mock_outputs_prefix'
- assert r_a.outputs == 'mock_outputs'
- assert r_a.raw_response == 'mock_raw_response'
- assert r_a.readable_output == 'mock_readable_output'
- assert r_a.outputs_key_field == 'mock_outputs_key_field'
+ assert r_a.outputs_prefix == "mock_outputs_prefix"
+ assert r_a.outputs == "mock_outputs"
+ assert r_a.raw_response == "mock_raw_response"
+ assert r_a.readable_output == "mock_readable_output"
+ assert r_a.outputs_key_field == "mock_outputs_key_field"
def test_fetch_incidents_with_attachment(self, mocker):
from RecordedFuturePlaybookAlerts import Actions
@@ -539,33 +633,33 @@ def test_fetch_incidents_with_attachment(self, mocker):
"screenshots": [
{
"image_id": "an_id",
- "base64": 'YWJhc2U2NHN0cmluZw==',
+ "base64": "YWJhc2U2NHN0cmluZw==",
"description": "vivid description of image",
}
]
}
}
mock_incidents_value = {
- 'name': 'incident_name',
+ "name": "incident_name",
"rawJSON": json.dumps(screenshot_dict),
}
- mock_demisto_last_run_value = 'mock_demisto_last_run'
+ mock_demisto_last_run_value = "mock_demisto_last_run"
mock_client_fetch_incidents_response = {
- 'incidents': [mock_incidents_value],
- 'demisto_last_run': mock_demisto_last_run_value,
+ "incidents": [mock_incidents_value],
+ "demisto_last_run": mock_demisto_last_run_value,
}
mock_client_fetch_incidents = mocker.patch.object(
- client, 'fetch_incidents', return_value=mock_client_fetch_incidents_response
+ client, "fetch_incidents", return_value=mock_client_fetch_incidents_response
)
- mock_demisto_incidents = mocker.patch.object(demisto, 'incidents')
- mock_demisto_set_last_run = mocker.patch.object(demisto, 'setLastRun')
+ mock_demisto_incidents = mocker.patch.object(demisto, "incidents")
+ mock_demisto_set_last_run = mocker.patch.object(demisto, "setLastRun")
mock_file_result = mocker.patch.object(
csp,
- 'fileResult',
+ "fileResult",
return_value={"File": "mockfilepath", "FileID": "mock_file_id"},
)
@@ -594,26 +688,26 @@ def test_fetch_incidents_with_incidents_present(self, mocker):
client = create_client()
mock_incidents_value = [
- {'mock_incident_key1': 'mock_incident_value1'},
- {'mock_incident_key2': 'mock_incident_value2'},
+ {"mock_incident_key1": "mock_incident_value1"},
+ {"mock_incident_key2": "mock_incident_value2"},
]
- mock_demisto_last_run_value = 'mock_demisto_last_run'
+ mock_demisto_last_run_value = "mock_demisto_last_run"
- mock_alerts_update_data_value = 'mock_alerts_update_data_value'
+ mock_alerts_update_data_value = "mock_alerts_update_data_value"
mock_client_fetch_incidents_response = {
- 'incidents': mock_incidents_value,
- 'demisto_last_run': mock_demisto_last_run_value,
- 'data': 'mock',
- 'alerts_update_data': mock_alerts_update_data_value,
+ "incidents": mock_incidents_value,
+ "demisto_last_run": mock_demisto_last_run_value,
+ "data": "mock",
+ "alerts_update_data": mock_alerts_update_data_value,
}
mock_client_fetch_incidents = mocker.patch.object(
- client, 'fetch_incidents', return_value=mock_client_fetch_incidents_response
+ client, "fetch_incidents", return_value=mock_client_fetch_incidents_response
)
- mock_demisto_incidents = mocker.patch.object(demisto, 'incidents')
- mock_demisto_set_last_run = mocker.patch.object(demisto, 'setLastRun')
+ mock_demisto_incidents = mocker.patch.object(demisto, "incidents")
+ mock_demisto_set_last_run = mocker.patch.object(demisto, "setLastRun")
actions = Actions(client)
@@ -629,20 +723,20 @@ def test_playbook_alert_details_command_with_result_actions(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_playbook_alert_details = mocker.patch.object(
- client, 'details_playbook_alerts', return_value=mock_response
+ client, "details_playbook_alerts", return_value=mock_response
)
actions = Actions(client)
mock_process_result_actions_return_value = (
- 'mock_process_result_actions_return_value'
+ "mock_process_result_actions_return_value"
)
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -660,10 +754,10 @@ def test_playbook_alert_details_command_without_result_actions(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_playbook_alert_details = mocker.patch.object(
- client, 'details_playbook_alerts', return_value=mock_response
+ client, "details_playbook_alerts", return_value=mock_response
)
actions = Actions(client)
@@ -671,7 +765,7 @@ def test_playbook_alert_details_command_without_result_actions(self, mocker):
mock_process_result_actions_return_value = None
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -686,10 +780,10 @@ def test_playbook_alert_search_command_without_result_actions(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_playbook_alert_search = mocker.patch.object(
- client, 'search_playbook_alerts', return_value=mock_response
+ client, "search_playbook_alerts", return_value=mock_response
)
actions = Actions(client)
@@ -697,7 +791,7 @@ def test_playbook_alert_search_command_without_result_actions(self, mocker):
mock_process_result_actions_return_value = None
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -712,20 +806,20 @@ def test_playbook_alert_update_command(self, mocker):
client = create_client()
- mock_response = 'mock_response'
+ mock_response = "mock_response"
mock_client_alert_set_status = mocker.patch.object(
- client, 'update_playbook_alerts', return_value=mock_response
+ client, "update_playbook_alerts", return_value=mock_response
)
actions = Actions(client)
mock_process_result_actions_return_value = (
- 'mock_process_result_actions_return_value'
+ "mock_process_result_actions_return_value"
)
mock_process_result_actions = mocker.patch.object(
actions,
- '_process_result_actions',
+ "_process_result_actions",
return_value=mock_process_result_actions_return_value,
)
@@ -755,7 +849,7 @@ def test_test_module(self, mocker):
RecordedFuturePlaybookAlerts, "return_results"
)
RecordedFuturePlaybookAlerts.main()
- mocked_return_res.assert_called_with('ok')
+ mocked_return_res.assert_called_with("ok")
def test_test_module_with_boom(self, mocker):
import RecordedFuturePlaybookAlerts
@@ -778,8 +872,117 @@ def test_test_module_with_boom(self, mocker):
RecordedFuturePlaybookAlerts.main()
mocked_return_err.assert_called_with(
message=(
- f'Failed to execute {demisto.command()} command: Failed due to - '
- 'Unknown error. Please verify that the API URL and Token are correctly configured. '
- 'RAW Error: Side effect triggered'
- )
+ f"Failed to execute {demisto.command()} command. Error: Failed due to - "
+ "Unknown error. Please verify that the API URL and Token are correctly configured. "
+ "RAW Error: Side effect triggered"
+ ),
+ error=mocker.ANY,
+ )
+
+ def test_transform_incidents_attachments_without_screenshots(self, mocker):
+ """Test transforming incidents without screenshots."""
+ from RecordedFuturePlaybookAlerts import Actions
+ import json
+
+ incidents = [{"rawJSON": json.dumps({"panel_evidence_summary": {}})}]
+
+ mock_fileResult = mocker.patch("RecordedFuturePlaybookAlerts.fileResult")
+
+ Actions._transform_incidents_attachments(incidents)
+
+ assert "attachment" not in incidents[0]
+ mock_fileResult.assert_not_called()
+
+ def test_process_result_actions_with_invalid_actions(self, mocker):
+ """Test processing result actions with invalid keys."""
+ from RecordedFuturePlaybookAlerts import Actions
+ from CommonServerPython import CommandResults
+
+ actions = Actions(rf_client=None)
+
+ response = {
+ "result_actions": [
+ {"InvalidKey": {}},
+ {
+ "CommandResults": {
+ "outputs_prefix": "mock_prefix",
+ "outputs": "mock_outputs",
+ }
+ },
+ {
+ "CommandResults": {
+ "outputs_prefix": "another_prefix",
+ "outputs": "another_outputs",
+ }
+ },
+ ]
+ }
+
+ result = actions._process_result_actions(response)
+
+ assert len(result) == 2
+ assert isinstance(result[0], CommandResults)
+ assert result[0].outputs_prefix == "mock_prefix"
+ assert result[0].outputs == "mock_outputs"
+ assert isinstance(result[1], CommandResults)
+ assert result[1].outputs_prefix == "another_prefix"
+ assert result[1].outputs == "another_outputs"
+
+
+class TestMain:
+ def test_main_with_unknown_command(self, mocker):
+ """Test main function with an unknown command."""
+ import RecordedFuturePlaybookAlerts
+ import demistomock as demisto
+
+ mocker.patch.object(demisto, "command", return_value="unknown-command")
+ mock_return_error = mocker.patch("RecordedFuturePlaybookAlerts.return_error")
+ mock_get_client = mocker.patch("RecordedFuturePlaybookAlerts.get_client")
+
+ RecordedFuturePlaybookAlerts.main()
+
+ mock_get_client.assert_called_once()
+ mock_return_error.assert_called_once_with(
+ message="Unknown command: unknown-command"
+ )
+
+ def test_get_client_no_api_token(self, mocker):
+ """Test get_client when no API token is provided."""
+ import RecordedFuturePlaybookAlerts
+ import demistomock as demisto
+
+ mock_params = {
+ "server_url": "https://api.recordedfuture.com/gw/xsoar/",
+ "unsecure": False,
+ "token": {"password": None},
+ }
+ mocker.patch.object(demisto, "params", return_value=mock_params)
+ mock_return_error = mocker.patch("RecordedFuturePlaybookAlerts.return_error")
+
+ RecordedFuturePlaybookAlerts.get_client()
+
+ mock_return_error.assert_called_once_with(
+ message="Please provide a valid API token"
+ )
+
+ def test_main_exception_handling(self, mocker):
+ """Test main function's exception handling."""
+ import RecordedFuturePlaybookAlerts
+ import demistomock as demisto
+
+ mocker.patch.object(demisto, "command", return_value="test-module")
+ mock_get_client = mocker.patch("RecordedFuturePlaybookAlerts.get_client")
+ mock_get_client.return_value.whoami.side_effect = Exception("Test exception")
+ mock_return_error = mocker.patch("RecordedFuturePlaybookAlerts.return_error")
+
+ RecordedFuturePlaybookAlerts.main()
+
+ mock_get_client.assert_called_once()
+ mock_return_error.assert_called_once_with(
+ message=(
+ f"Failed to execute {demisto.command()} command. Error: Failed due to - "
+ "Unknown error. Please verify that the API URL and Token are correctly configured. "
+ "RAW Error: Test exception"
+ ),
+ error=mocker.ANY,
)
diff --git a/Packs/RecordedFuture/ReleaseNotes/1_8_0.md b/Packs/RecordedFuture/ReleaseNotes/1_8_0.md
new file mode 100644
index 00000000000..a6d0556e2aa
--- /dev/null
+++ b/Packs/RecordedFuture/ReleaseNotes/1_8_0.md
@@ -0,0 +1,27 @@
+#### Integrations
+
+##### Recorded Future v2
+
+- Added *collective_insights* argument to the following commands:
+ - ***ip***
+ - ***domain***
+ - ***url***
+ - ***file***
+ - ***cve***
+- Updated the Docker image to: *demisto/python3:3.11.10.111039*.
+
+##### Recorded Future - Playbook Alerts
+
+- Added the following arguments to the ***recordedfuture-playbook-alerts-update*** command:
+ - *reopen*
+ - *comment*
+
+- Updated the Docker image to: *demisto/python3:3.11.10.111039*.
+
+##### Recorded Future - Lists
+
+Updated the Docker image to: *demisto/python3:3.11.10.111039*.
+
+##### Recorded Future Event Collector
+
+Updated the Docker image to: *demisto/python3:3.11.10.111039*.
diff --git a/Packs/RecordedFuture/pack_metadata.json b/Packs/RecordedFuture/pack_metadata.json
index ef1b0aa33a5..22c67482c26 100644
--- a/Packs/RecordedFuture/pack_metadata.json
+++ b/Packs/RecordedFuture/pack_metadata.json
@@ -2,7 +2,7 @@
"name": "Recorded Future Intelligence",
"description": "Recorded Future App, this pack is previously known as 'RecordedFuture v2'",
"support": "partner",
- "currentVersion": "1.7.13",
+ "currentVersion": "1.8.0",
"author": "Recorded Future",
"url": "https://www.recordedfuture.com/support/demisto-integration/",
"email": "support@recordedfuture.com",