diff --git a/Packs/BPA/ReleaseNotes/1_1_0.md b/Packs/BPA/ReleaseNotes/1_1_0.md
new file mode 100644
index 00000000000..27041df3b07
--- /dev/null
+++ b/Packs/BPA/ReleaseNotes/1_1_0.md
@@ -0,0 +1,4 @@
+
+#### Integrations
+##### BPA
+- Added the option to exclude passed checks in ***pan-os-bpa-get-job-results*** command.
diff --git a/Packs/BPA/TestPlaybooks/playbook-BPA-test.yml b/Packs/BPA/TestPlaybooks/playbook-BPA-test.yml
index d155f1ca780..98c0c1d3c15 100644
--- a/Packs/BPA/TestPlaybooks/playbook-BPA-test.yml
+++ b/Packs/BPA/TestPlaybooks/playbook-BPA-test.yml
@@ -1,14 +1,14 @@
id: Test-BPA
-version: -1
+version: 8
name: Test-BPA
starttaskid: "0"
tasks:
"0":
id: "0"
- taskid: 33930745-eeda-4467-808f-b3109964dd33
+ taskid: 06afda6f-3011-4b7d-81cd-d7d68376ef74
type: start
task:
- id: 33930745-eeda-4467-808f-b3109964dd33
+ id: 06afda6f-3011-4b7d-81cd-d7d68376ef74
version: -1
name: ""
iscommand: false
@@ -31,10 +31,10 @@ tasks:
quietmode: 0
"1":
id: "1"
- taskid: a584565d-45e0-48d6-8b3e-911de18654d2
+ taskid: 7c822901-b728-43ea-87a5-20d0a5712cec
type: regular
task:
- id: a584565d-45e0-48d6-8b3e-911de18654d2
+ id: 7c822901-b728-43ea-87a5-20d0a5712cec
version: -1
name: DeleteContext
description: Delete field from context
@@ -67,10 +67,10 @@ tasks:
quietmode: 0
"2":
id: "2"
- taskid: 864913ea-71c9-49dd-88d4-1d93b5bdff93
+ taskid: 1523fbb5-8f28-49f0-8562-ec01e9e8a2c4
type: regular
task:
- id: 864913ea-71c9-49dd-88d4-1d93b5bdff93
+ id: 1523fbb5-8f28-49f0-8562-ec01e9e8a2c4
version: -1
name: pan-os-bpa-submit-job
description: Submits a BPA job.
@@ -99,10 +99,10 @@ tasks:
quietmode: 0
"3":
id: "3"
- taskid: 333cfc71-49f5-4be0-8e02-bd8f2dac3835
+ taskid: 25a4742a-99e5-457d-87fb-9c3bd103cc36
type: playbook
task:
- id: 333cfc71-49f5-4be0-8e02-bd8f2dac3835
+ id: 25a4742a-99e5-457d-87fb-9c3bd103cc36
version: -1
name: GenericPolling
description: |-
@@ -155,10 +155,10 @@ tasks:
quietmode: 0
"5":
id: "5"
- taskid: e3843b2a-b8e9-45ae-8820-2788455081eb
+ taskid: 9bc78e2e-5722-4e96-8a5a-1f503d1dd7a2
type: condition
task:
- id: e3843b2a-b8e9-45ae-8820-2788455081eb
+ id: 9bc78e2e-5722-4e96-8a5a-1f503d1dd7a2
version: -1
name: Test BPA Checks Are Returned
type: condition
@@ -166,7 +166,7 @@ tasks:
brand: ""
nexttasks:
"yes":
- - "7"
+ - "10"
separatecontext: false
conditions:
- label: "yes"
@@ -206,10 +206,10 @@ tasks:
quietmode: 0
"7":
id: "7"
- taskid: 86e0b324-2b01-4958-8c27-78c66a293c83
+ taskid: 86268dac-3b75-4713-8a92-13672c5a90d0
type: regular
task:
- id: 86e0b324-2b01-4958-8c27-78c66a293c83
+ id: 86268dac-3b75-4713-8a92-13672c5a90d0
version: -1
name: closeInvestigation
description: Close the current incident
@@ -230,7 +230,7 @@ tasks:
{
"position": {
"x": 50,
- "y": 1245
+ "y": 1770
}
}
note: false
@@ -240,10 +240,10 @@ tasks:
quietmode: 0
"8":
id: "8"
- taskid: 548b923e-b896-40bf-8d8f-42ac39c45874
+ taskid: 0642547e-3930-45ed-8c6c-04f10290e6b1
type: regular
task:
- id: 548b923e-b896-40bf-8d8f-42ac39c45874
+ id: 0642547e-3930-45ed-8c6c-04f10290e6b1
version: -1
name: pan-os-get-documentation
description: Get documentaion
@@ -269,10 +269,10 @@ tasks:
quietmode: 0
"9":
id: "9"
- taskid: 4f8f98eb-d21f-4d4b-828a-eada7d92941a
+ taskid: 9da2cd68-133e-4adc-8526-e5b7cb3ef2ef
type: regular
task:
- id: 4f8f98eb-d21f-4d4b-828a-eada7d92941a
+ id: 9da2cd68-133e-4adc-8526-e5b7cb3ef2ef
version: -1
name: pan-os-bpa-get-job-results
description: Returns results of BPA job.
@@ -299,12 +299,126 @@ tasks:
ignoreworker: false
skipunavailable: false
quietmode: 0
+ "10":
+ id: "10"
+ taskid: 9f72939b-1058-45b6-88b1-6cc21b0bfa04
+ type: regular
+ task:
+ id: 9f72939b-1058-45b6-88b1-6cc21b0bfa04
+ version: -1
+ name: DeleteContext
+ description: Delete field from context
+ scriptName: DeleteContext
+ type: regular
+ iscommand: false
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "11"
+ scriptarguments:
+ all:
+ simple: "yes"
+ index: {}
+ key: {}
+ keysToKeep:
+ simple: PAN-OS-BPA.SubmittedJob.JobID
+ subplaybook: {}
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 1245
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "11":
+ id: "11"
+ taskid: ec9101b0-65a8-49be-8794-304ab19e17f0
+ type: regular
+ task:
+ id: ec9101b0-65a8-49be-8794-304ab19e17f0
+ version: -1
+ name: pan-os-bpa-get-job-results
+ description: Returns results of BPA job.
+ script: BPA|||pan-os-bpa-get-job-results
+ type: regular
+ iscommand: true
+ brand: BPA
+ nexttasks:
+ '#none#':
+ - "12"
+ scriptarguments:
+ exclude_passed_checks:
+ simple: "true"
+ task_id:
+ simple: ${PAN-OS-BPA.SubmittedJob.JobID}
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 1420
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "12":
+ id: "12"
+ taskid: 1a12b604-293a-4fdf-8b0b-1cf8e7b0d2a7
+ type: condition
+ task:
+ id: 1a12b604-293a-4fdf-8b0b-1cf8e7b0d2a7
+ version: -1
+ name: Test BPA Checks Are Returned
+ type: condition
+ iscommand: false
+ brand: ""
+ nexttasks:
+ "yes":
+ - "7"
+ separatecontext: false
+ conditions:
+ - label: "yes"
+ condition:
+ - - operator: isEmpty
+ left:
+ value:
+ complex:
+ root: PAN-OS-BPA
+ filters:
+ - - operator: isTrue
+ left:
+ value:
+ simple: PAN-OS-BPA.JobResults.Checks.check_passed
+ iscontext: true
+ accessor: JobResults.Checks
+ iscontext: true
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 1595
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
view: |-
{
"linkLabelsPosition": {},
"paper": {
"dimensions": {
- "height": 1290,
+ "height": 1815,
"width": 380,
"x": 50,
"y": 50
diff --git a/Packs/BPA/pack_metadata.json b/Packs/BPA/pack_metadata.json
index fa0a665cd85..b58b6390b81 100644
--- a/Packs/BPA/pack_metadata.json
+++ b/Packs/BPA/pack_metadata.json
@@ -1,16 +1,16 @@
{
- "name": "Palo Alto Networks BPA",
- "description": "Palo Alto Networks Best Practice Assessment (BPA) analyzes NGFW and Panorama configurations and compares them to the best practices.",
- "support": "xsoar",
- "currentVersion": "1.0.0",
- "author": "Cortex XSOAR",
- "url": "https://www.paloaltonetworks.com/cortex",
- "email": "",
- "created": "2020-04-14T00:00:00Z",
- "categories": [
- "Utilities"
- ],
- "tags": [],
- "useCases": [],
- "keywords": []
+ "name": "Palo Alto Networks BPA",
+ "description": "Palo Alto Networks Best Practice Assessment (BPA) analyzes NGFW and Panorama configurations and compares them to the best practices.",
+ "support": "xsoar",
+ "currentVersion": "1.1.0",
+ "author": "Cortex XSOAR",
+ "url": "https://www.paloaltonetworks.com/cortex",
+ "email": "",
+ "created": "2020-04-14T00:00:00Z",
+ "categories": [
+ "Utilities"
+ ],
+ "tags": [],
+ "useCases": [],
+ "keywords": []
}
\ No newline at end of file
diff --git a/Packs/Base/ReleaseNotes/1_0_10.md b/Packs/Base/ReleaseNotes/1_0_10.md
new file mode 100644
index 00000000000..6d4fad919d8
--- /dev/null
+++ b/Packs/Base/ReleaseNotes/1_0_10.md
@@ -0,0 +1,4 @@
+
+#### Scripts
+- __CommonServerPython__
+Fixed incorrect time zone parsing for **timestamp_to_datestring**
diff --git a/Packs/Base/ReleaseNotes/1_0_11.md b/Packs/Base/ReleaseNotes/1_0_11.md
new file mode 100644
index 00000000000..13ca42aa852
--- /dev/null
+++ b/Packs/Base/ReleaseNotes/1_0_11.md
@@ -0,0 +1,4 @@
+#### Scripts
+##### SaneDocReports
+- Fixed SVG image rendering in doc reports.
+- Added the ability to add customer logos to doc reports.
diff --git a/Packs/Base/ReleaseNotes/1_0_12.md b/Packs/Base/ReleaseNotes/1_0_12.md
new file mode 100644
index 00000000000..79db85bebd4
--- /dev/null
+++ b/Packs/Base/ReleaseNotes/1_0_12.md
@@ -0,0 +1,4 @@
+
+#### Scripts
+##### SanePdfReports
+- Fixes word overlapping in graphs.
diff --git a/Packs/Base/ReleaseNotes/1_0_13.md b/Packs/Base/ReleaseNotes/1_0_13.md
new file mode 100644
index 00000000000..c8d36bf3c7d
--- /dev/null
+++ b/Packs/Base/ReleaseNotes/1_0_13.md
@@ -0,0 +1,6 @@
+#### Scripts
+##### SaneDocReports
+- Reverted changes made in 1.0.12
+
+##### SanePdfReports
+- Rolled back the docker image to fix a conflict issue.
\ No newline at end of file
diff --git a/Packs/Base/ReleaseNotes/1_0_6.md b/Packs/Base/ReleaseNotes/1_0_6.md
index 30f0c43c414..4e3ea4ef9d4 100644
--- a/Packs/Base/ReleaseNotes/1_0_6.md
+++ b/Packs/Base/ReleaseNotes/1_0_6.md
@@ -1,4 +1,4 @@
#### Scripts
-- __CommonServerPython__
- - Added support for the CVE indicator class.
+##### __CommonServerPython__
+- Added support for the CVE indicator class.
diff --git a/Packs/Base/ReleaseNotes/1_0_7.md b/Packs/Base/ReleaseNotes/1_0_7.md
new file mode 100644
index 00000000000..37b72573f64
--- /dev/null
+++ b/Packs/Base/ReleaseNotes/1_0_7.md
@@ -0,0 +1,4 @@
+
+#### Scripts
+##### __CommonServerPython__
+- Add safeget from python dict function.
diff --git a/Packs/Base/ReleaseNotes/1_0_8.md b/Packs/Base/ReleaseNotes/1_0_8.md
new file mode 100644
index 00000000000..5fe6c57aded
--- /dev/null
+++ b/Packs/Base/ReleaseNotes/1_0_8.md
@@ -0,0 +1,4 @@
+
+#### Scripts
+##### CommonServerPowerShell
+- Updated the **ReturnOutputs** function to support *object* types.
diff --git a/Packs/Base/ReleaseNotes/1_0_9.md b/Packs/Base/ReleaseNotes/1_0_9.md
new file mode 100644
index 00000000000..aca8a2d5ab5
--- /dev/null
+++ b/Packs/Base/ReleaseNotes/1_0_9.md
@@ -0,0 +1,4 @@
+
+#### Scripts
+##### __CommonServerPython__
+- Fixed an issue where the **argToList** function did not behave as expected. This fix breaks backward compatibility.
diff --git a/Packs/Base/Scripts/CommonServerPowerShell/CommonServerPowerShell.Tests.ps1 b/Packs/Base/Scripts/CommonServerPowerShell/CommonServerPowerShell.Tests.ps1
index 2c5bdb6640f..7bd873f43e5 100644
--- a/Packs/Base/Scripts/CommonServerPowerShell/CommonServerPowerShell.Tests.ps1
+++ b/Packs/Base/Scripts/CommonServerPowerShell/CommonServerPowerShell.Tests.ps1
@@ -1,4 +1,7 @@
-. $PSScriptRoot\CommonServerPowerShell.ps1
+BeforeAll {
+ . $PSScriptRoot\CommonServerPowerShell.ps1
+}
+
Describe 'Check-DemistoServerRequest' {
It 'Check that a call to demisto DemistoServerRequest mock works. Should always return an empty response' {
@@ -41,6 +44,20 @@ Describe 'Check-UtilityFunctions' {
$r.Contents | Should -Be $msg
}
+ It "ReturnOutputs PsCustomObject" {
+ $msg = "Human readable"
+ $output = [PsCustomObject]@{Test="test"}
+ $raw = [PSCustomObject]@{Raw="raw"}
+ $r = ReturnOutputs $msg $output $raw
+ $r.ContentsFormat | Should -Be "json"
+ $r.HumanReadable | Should -Be $msg
+ $r.EntryContext.Test | Should -Be "test"
+ $r.Contents.Raw | Should -Be "raw"
+ $r = ReturnOutputs $msg
+ $r.ContentsFormat | Should -Be "text"
+ $r.Contents | Should -Be $msg
+ }
+
It "ReturnError simple" {
$msg = "this is an error"
$r = ReturnError $msg
@@ -50,8 +67,10 @@ Describe 'Check-UtilityFunctions' {
$r.EntryContext | Should -BeNullOrEmpty
}
Context "Check log function" {
- Mock DemistoServerLog {}
-
+ BeforeAll {
+ Mock DemistoServerLog {}
+ }
+
It "ReturnError complex" {
# simulate an error
Test-JSON "{badjson}" -ErrorAction SilentlyContinue -ErrorVariable err
@@ -59,8 +78,9 @@ Describe 'Check-UtilityFunctions' {
$r = ReturnError $msg $err @{Failed = $true}
$r.Contents | Should -Be $msg
$r.EntryContext.Failed | Should -BeTrue
+ # ReturnError call demisto.Error() make sure it was called
Assert-MockCalled -CommandName DemistoServerLog -Times 2 -ParameterFilter {$level -eq "error"}
- Assert-MockCalled -CommandName DemistoServerLog -Times 1 -ParameterFilter {$msg.Contains("Test-JSON : Cannot parse the JSON")}
+ Assert-MockCalled -CommandName DemistoServerLog -Times 1 -ParameterFilter {$msg.Contains("Cannot parse the JSON")}
}
}
}
diff --git a/Packs/Base/Scripts/CommonServerPowerShell/CommonServerPowerShell.ps1 b/Packs/Base/Scripts/CommonServerPowerShell/CommonServerPowerShell.ps1
index c67a7dea7d0..69af1ec013d 100644
--- a/Packs/Base/Scripts/CommonServerPowerShell/CommonServerPowerShell.ps1
+++ b/Packs/Base/Scripts/CommonServerPowerShell/CommonServerPowerShell.ps1
@@ -367,7 +367,7 @@ raw response from the 3rd party service (optional)
.OUTPUTS
The entry object returned to the server
#>
-function ReturnOutputs([string]$ReadableOutput, [hashtable]$Outputs, [hashtable]$RawResponse) {
+function ReturnOutputs([string]$ReadableOutput, [object]$Outputs, [object]$RawResponse) {
$entry = @{
Type = [EntryTypes]::note;
ContentsFormat = [EntryFormats]::json.ToString();
diff --git a/Packs/Base/Scripts/CommonServerPowerShell/CommonServerPowerShell.yml b/Packs/Base/Scripts/CommonServerPowerShell/CommonServerPowerShell.yml
index 06ef8f2e1bc..697470d2d50 100644
--- a/Packs/Base/Scripts/CommonServerPowerShell/CommonServerPowerShell.yml
+++ b/Packs/Base/Scripts/CommonServerPowerShell/CommonServerPowerShell.yml
@@ -10,5 +10,8 @@ tags:
comment: Common code that will be merged into each PowerShell script/integration when it runs
system: true
fromversion: 5.5.0
+dockerimage: demisto/powershell:6.2.4.6166
+alt_dockerimages: # used for unit testing
+ - demisto/powershell:7.0.1.9103
tests:
- PowerShellCommon-Test
diff --git a/Packs/Base/Scripts/CommonServerPython/CHANGELOG.md b/Packs/Base/Scripts/CommonServerPython/CHANGELOG.md
index 61e84cd307d..ad52cf10bc8 100644
--- a/Packs/Base/Scripts/CommonServerPython/CHANGELOG.md
+++ b/Packs/Base/Scripts/CommonServerPython/CHANGELOG.md
@@ -1,6 +1,7 @@
## [Unreleased]
- Added **Endpoint** Common class.
- Added a new function **auto_detect_indicator_type** which detects indicators.
+ - Fixed an issue where the **argToList** function did not behave as expected. This fix is breaking backward compatibility.
## [20.5.2] - 2020-05-26
- Fixed IPv4 regex to only catch IPv4 addresses, not CIDR ranges.
diff --git a/Packs/Base/Scripts/CommonServerPython/CommonServerPython.py b/Packs/Base/Scripts/CommonServerPython/CommonServerPython.py
index 27a3ee7ad2d..85d6d71cdda 100644
--- a/Packs/Base/Scripts/CommonServerPython/CommonServerPython.py
+++ b/Packs/Base/Scripts/CommonServerPython/CommonServerPython.py
@@ -1193,7 +1193,7 @@ def argToList(arg, separator=','):
if arg[0] == '[' and arg[-1] == ']':
return json.loads(arg)
return [s.strip() for s in arg.split(separator)]
- return arg
+ return [arg]
def argToBoolean(value):
@@ -3055,7 +3055,7 @@ def parse_date_range(date_range, date_format=None, to_timestamp=False, timezone=
return start_time, end_time
-def timestamp_to_datestring(timestamp, date_format="%Y-%m-%dT%H:%M:%S.000Z"):
+def timestamp_to_datestring(timestamp, date_format="%Y-%m-%dT%H:%M:%S.000Z", is_utc=False):
"""
Parses timestamp (milliseconds) to a date string in the provided date format (by default: ISO 8601 format)
Examples: (1541494441222, 1541495441000, etc.)
@@ -3066,9 +3066,15 @@ def timestamp_to_datestring(timestamp, date_format="%Y-%m-%dT%H:%M:%S.000Z"):
:type date_format: ``str``
:param date_format: The date format the timestamp should be parsed to. (optional)
+ :type is_utc: ``bool``
+ :param is_utc: Should the string representation of the timestamp use UTC time or the local machine time
+
:return: The parsed timestamp in the date_format
:rtype: ``str``
"""
+ use_utc_time = is_utc or date_format.endswith('Z')
+ if use_utc_time:
+ return datetime.utcfromtimestamp(int(timestamp) / 1000.0).strftime(date_format)
return datetime.fromtimestamp(int(timestamp) / 1000.0).strftime(date_format)
@@ -3785,6 +3791,29 @@ def batch(iterable, batch_size=1):
current_batch = not_batched[:batch_size]
not_batched = not_batched[batch_size:]
+def dict_safe_get(dict_object, keys, default_return_value = None):
+ """Recursive safe get query, If keys found return value othewise return None or default value.
+
+ :type dict_object: ``dict``
+ :param dict_object: dictionary to query.
+
+ :type keys: ``list``
+ :param keys: keys for recursive get.
+
+ :type default_return_value: ``object``
+ :param default_return_value: Value to return when no key availble.
+
+ :rtype: ``object``
+ :return:: Value found.
+ """
+ for key in keys:
+ try:
+ dict_object = dict_object[key]
+ except (KeyError, TypeError):
+ return default_return_value
+
+ return dict_object
+
class DemistoException(Exception):
pass
diff --git a/Packs/Base/Scripts/CommonServerPython/CommonServerPython_test.py b/Packs/Base/Scripts/CommonServerPython/CommonServerPython_test.py
index 5e1bd984996..aee1063f1a6 100644
--- a/Packs/Base/Scripts/CommonServerPython/CommonServerPython_test.py
+++ b/Packs/Base/Scripts/CommonServerPython/CommonServerPython_test.py
@@ -460,12 +460,19 @@ def test_argToList():
test2 = 'a,b,c'
test3 = '["a","b","c"]'
test4 = 'a;b;c'
+ test5 = 1
+ test6 = '1'
+ test7 = True
results = [argToList(test1), argToList(test2), argToList(test2, ','), argToList(test3), argToList(test4, ';')]
for result in results:
assert expected == result, 'argToList test failed, {} is not equal to {}'.format(str(result), str(expected))
+ assert argToList(test5) == [1]
+ assert argToList(test6) == ['1']
+ assert argToList(test7) == [True]
+
def test_remove_nulls():
temp_dictionary = {"a": "b", "c": 4, "e": [], "f": {}, "g": None, "h": "", "i": [1], "k": ()}
@@ -1267,6 +1274,7 @@ class TestBaseClient:
'post'
]
+ @pytest.mark.skip(reason="Test - too long, only manual")
@pytest.mark.parametrize('method', RETRIES_POSITIVE_TEST)
def test_http_requests_with_retry_sanity(self, method):
"""
@@ -1291,9 +1299,9 @@ def test_http_requests_with_retry_sanity(self, method):
('get', 400), ('get', 401), ('get', 500),
('put', 400), ('put', 401), ('put', 500),
('post', 400), ('post', 401), ('post', 500),
-
]
+ @pytest.mark.skip(reason="Test - too long, only manual")
@pytest.mark.parametrize('method, status', RETRIES_NEGATIVE_TESTS_INPUT)
def test_http_requests_with_retry_negative_sanity(self, method, status):
"""
@@ -1884,3 +1892,16 @@ def test_handle_proxy(mocker):
mocker.patch.object(demisto, 'params', return_value={'unsecure': True})
handle_proxy()
assert os.getenv('REQUESTS_CA_BUNDLE') is None
+
+
+@pytest.mark.parametrize(argnames="dict_obj, keys, expected, default_return_value",
+ argvalues=[
+ ({'a': '1'}, ['a'], '1', None),
+ ({'a': {'b': '2'}}, ['a', 'b'], '2', None),
+ ({'a': {'b': '2'}}, ['a', 'c'], 'test', 'test'),
+ ])
+def test_safe_get(dict_obj, keys, expected, default_return_value):
+ from CommonServerPython import dict_safe_get
+ assert expected == dict_safe_get(dict_object=dict_obj,
+ keys=keys,
+ default_return_value=default_return_value)
diff --git a/Packs/Base/Scripts/DBotMLFetchData/DBotMLFetchData.py b/Packs/Base/Scripts/DBotMLFetchData/DBotMLFetchData.py
new file mode 100644
index 00000000000..7d52b9d114e
--- /dev/null
+++ b/Packs/Base/Scripts/DBotMLFetchData/DBotMLFetchData.py
@@ -0,0 +1,20 @@
+import demistomock as demisto
+from CommonServerPython import *
+from CommonServerUserPython import *
+
+
+def return_json_entry(obj):
+ entry = {
+ "Type": entryTypes["note"],
+ "ContentsFormat": formats["json"],
+ "Contents": obj,
+ }
+ demisto.results(entry)
+
+
+def main():
+ return_json_entry({})
+
+
+if __name__ in ['__main__', '__builtin__', 'builtins']:
+ main()
diff --git a/Packs/Base/Scripts/DBotMLFetchData/DBotMLFetchData.yml b/Packs/Base/Scripts/DBotMLFetchData/DBotMLFetchData.yml
new file mode 100644
index 00000000000..f21afd379d0
--- /dev/null
+++ b/Packs/Base/Scripts/DBotMLFetchData/DBotMLFetchData.yml
@@ -0,0 +1,14 @@
+comment: Collect telemetry data from the environment
+commonfields:
+ id: DBotMLFetchData
+ version: -1
+enabled: false
+name: DBotMLFetchData
+script: '-'
+subtype: python3
+system: false
+tags:
+ - ml
+timeout: '0'
+type: python
+deprecated: true
diff --git a/Packs/Base/Scripts/FindSimilarIncidentsByText/FindSimilarIncidentsByText.yml b/Packs/Base/Scripts/FindSimilarIncidentsByText/FindSimilarIncidentsByText.yml
index 5e778bd1ce0..f8c33f75aed 100644
--- a/Packs/Base/Scripts/FindSimilarIncidentsByText/FindSimilarIncidentsByText.yml
+++ b/Packs/Base/Scripts/FindSimilarIncidentsByText/FindSimilarIncidentsByText.yml
@@ -110,4 +110,4 @@ type: python
dockerimage: demisto/machine-learning
runonce: false
tests:
-- dedup_-_generic_-_test
+- Dedup - Generic v2 - Test
diff --git a/Packs/Base/Scripts/SaneDocReportV2/CHANGELOG.md b/Packs/Base/Scripts/SaneDocReportV2/CHANGELOG.md
index 6a7dd05397b..eef528a8e27 100644
--- a/Packs/Base/Scripts/SaneDocReportV2/CHANGELOG.md
+++ b/Packs/Base/Scripts/SaneDocReportV2/CHANGELOG.md
@@ -1,5 +1,5 @@
## [Unreleased]
-
+-
## [20.5.0] - 2020-05-12
-
diff --git a/Packs/Base/Scripts/SaneDocReportV2/SaneDocReportV2.yml b/Packs/Base/Scripts/SaneDocReportV2/SaneDocReportV2.yml
index 24ba05ed8ff..438f2992454 100644
--- a/Packs/Base/Scripts/SaneDocReportV2/SaneDocReportV2.yml
+++ b/Packs/Base/Scripts/SaneDocReportV2/SaneDocReportV2.yml
@@ -40,4 +40,4 @@ runonce: false
tests:
- No Test
deprecated: true
-fromversion: 5.5.0
+fromversion: 5.5.0
\ No newline at end of file
diff --git a/Packs/Base/Scripts/SanePdfReport/CHANGELOG.md b/Packs/Base/Scripts/SanePdfReport/CHANGELOG.md
index 789a8342d23..82c3c467bf4 100644
--- a/Packs/Base/Scripts/SanePdfReport/CHANGELOG.md
+++ b/Packs/Base/Scripts/SanePdfReport/CHANGELOG.md
@@ -1,5 +1,5 @@
## [Unreleased]
-
+-
## [20.5.0] - 2020-05-12
- Fixed logos usage and added failure verbose output.
diff --git a/Packs/Base/pack_metadata.json b/Packs/Base/pack_metadata.json
index ddd5833622f..23c3ccf1fb7 100644
--- a/Packs/Base/pack_metadata.json
+++ b/Packs/Base/pack_metadata.json
@@ -2,7 +2,7 @@
"name": "Base",
"description": "The base pack for Cortex XSOAR.",
"support": "xsoar",
- "currentVersion": "1.0.6",
+ "currentVersion": "1.0.13",
"author": "Cortex XSOAR",
"url": "https://www.paloaltonetworks.com/cortex",
"email": "",
@@ -15,5 +15,6 @@
"keywords": [
"base",
"common"
- ]
-}
\ No newline at end of file
+ ],
+ "dependencies": {}
+}
diff --git a/Packs/BastilleNetworks/pack_metadata.json b/Packs/BastilleNetworks/pack_metadata.json
index 59b19684479..fe90181ee87 100644
--- a/Packs/BastilleNetworks/pack_metadata.json
+++ b/Packs/BastilleNetworks/pack_metadata.json
@@ -16,10 +16,6 @@
"wireless"
],
"created": "2020-05-07T16:59:04Z",
- "updated": "2020-05-07T16:59:04Z",
- "beta": false,
- "deprecated": false,
"useCases": [],
- "keywords": [],
- "dependencies": {}
+ "keywords": []
}
diff --git a/Packs/BigFix/Integrations/BigFix/BigFix.py b/Packs/BigFix/Integrations/BigFix/BigFix.py
new file mode 100644
index 00000000000..7ff9594c504
--- /dev/null
+++ b/Packs/BigFix/Integrations/BigFix/BigFix.py
@@ -0,0 +1,797 @@
+import requests
+
+import demistomock as demisto
+from CommonServerPython import *
+
+requests.packages.urllib3.disable_warnings()
+
+BASE_URL = demisto.params().get('url')
+VERIFY_CERTIFICATE = not demisto.params().get('unsecure')
+
+USERNAME = demisto.params()['credentials']['identifier']
+PASSWORD = demisto.params()['credentials']['password']
+
+if not demisto.params()['proxy']:
+ del os.environ['HTTP_PROXY']
+ del os.environ['HTTPS_PROXY']
+ del os.environ['http_proxy']
+ del os.environ['https_proxy']
+
+
+def get_first(iterable, default=None):
+ if iterable:
+ for item in iterable:
+ return item
+ return default
+
+
+def get_sites():
+ fullurl = BASE_URL + '/api/sites'
+ res = requests.get(
+ fullurl,
+ auth=(USERNAME, PASSWORD),
+ verify=VERIFY_CERTIFICATE
+ )
+
+ if res.status_code < 200 or res.status_code >= 300:
+ return_error('Failed to get sites.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(
+ fullurl, res.status_code, res.content))
+
+ raw_sites = json.loads(xml2json(res.content))
+
+ if not raw_sites or 'BESAPI' not in raw_sites:
+ return []
+
+ sites = []
+ master_sites = demisto.get(raw_sites, 'BESAPI.ActionSite')
+
+ if master_sites and not isinstance(master_sites, list):
+ master_sites = [master_sites]
+ if master_sites:
+ for idx, site in enumerate(master_sites):
+ master_sites[idx]['Type'] = 'master'
+ master_sites[idx]['Resource'] = master_sites[idx]['@Resource']
+ del master_sites[idx]['@Resource']
+ else:
+ master_sites = []
+
+ external_sites = demisto.get(raw_sites, 'BESAPI.ExternalSite')
+ if external_sites and not isinstance(external_sites, list):
+ external_sites = [external_sites]
+ if external_sites:
+ for idx, site in enumerate(external_sites):
+ external_sites[idx]['Type'] = 'external'
+ external_sites[idx]['Resource'] = external_sites[idx]['@Resource']
+ del external_sites[idx]['@Resource']
+ else:
+ external_sites = []
+
+ operator_sites = demisto.get(raw_sites, 'BESAPI.OperatorSite')
+ if operator_sites and not isinstance(operator_sites, list):
+ operator_sites = [operator_sites]
+ if operator_sites:
+ for idx, site in enumerate(operator_sites):
+ operator_sites[idx]['Type'] = 'operator'
+ operator_sites[idx]['Resource'] = operator_sites[idx]['@Resource']
+ del operator_sites[idx]['@Resource']
+ else:
+ operator_sites = []
+
+ custom_sites = demisto.get(raw_sites, 'BESAPI.CustomSite')
+ if custom_sites and not isinstance(custom_sites, list):
+ custom_sites = [custom_sites]
+
+ if custom_sites:
+ for idx, site in enumerate(custom_sites):
+ custom_sites[idx]['Type'] = 'custom'
+ custom_sites[idx]['Resource'] = custom_sites[idx]['@Resource']
+ del custom_sites[idx]['@Resource']
+ else:
+ custom_sites = []
+
+ sites = master_sites + external_sites + operator_sites + custom_sites
+ for idx, site in enumerate(sites):
+ site_details = get_site(site['Type'], site['Name'])
+ sites[idx] = site_details
+
+ return sites
+
+
+def get_sites_command():
+ sites = get_sites()
+ demisto.results({
+ 'Type': entryTypes['note'],
+ 'ContentsFormat': formats['json'],
+ 'Contents': sites,
+ 'HumanReadable': tableToMarkdown(
+ 'BigFix Sites',
+ sites,
+ ['Name', 'Type', 'GatherURL', 'Description', 'GlobalReadPermissions', 'Subscription']
+ ),
+ 'EntryContext': {
+ 'Bigfix.Site(val.Resource==obj.Resource)': sites
+ }
+ })
+
+
+def get_site(site_type, site_name):
+ fullurl = BASE_URL + '/api/site/' + site_type
+ if site_type != 'master':
+ # if site name is not empty the add to url
+ fullurl += '/' + site_name
+
+ res = requests.get(
+ fullurl,
+ auth=(USERNAME, PASSWORD),
+ verify=VERIFY_CERTIFICATE
+ )
+
+ if res.status_code < 200 or res.status_code >= 300:
+ return_error('Failed to get site {}.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(
+ site_name, fullurl, res.status_code, res.content))
+
+ raw_site = json.loads(xml2json(res.content))
+
+ if not raw_site or 'BES' not in raw_site:
+ return None
+
+ site = None
+ if site_type == 'master':
+ site = demisto.get(raw_site, 'BES.ActionSite')
+ elif site_type == 'external':
+ site = demisto.get(raw_site, 'BES.ExternalSite')
+ elif site_type == 'custom':
+ site = demisto.get(raw_site, 'BES.CustomSite')
+ elif site_type == 'operator':
+ site = demisto.get(raw_site, 'BES.OperatorSite')
+
+ if site is not None:
+ site['Type'] = site_type
+ site['Resource'] = BASE_URL + '/api/site/{}/{}'.format(site_type, site_name)
+
+ return site
+
+
+def get_site_command():
+ site_name = demisto.args().get('site_name')
+ site_type = demisto.args().get('site_type')
+ site = get_site(site_type, site_name)
+
+ if site is None:
+ demisto.results('No site found')
+ sys.exit(0)
+
+ demisto.results({
+ 'Type': entryTypes['note'],
+ 'ContentsFormat': formats['json'],
+ 'Contents': site,
+ 'HumanReadable': tableToMarkdown(
+ 'BigFix Site: {} - {}'.format(site_type, site_name),
+ [site],
+ ['Name', 'Type', 'GatherURL', 'Description', 'GlobalReadPermissions', 'Subscription']
+ ),
+ 'EntryContext': {
+ 'Bigfix.Site(val.Resource==obj.Resource)': site
+ }
+ })
+
+
+def get_endpoints(should_get_endpoint_details):
+ fullurl = BASE_URL + '/api/computers'
+
+ res = requests.get(
+ fullurl,
+ auth=(USERNAME, PASSWORD),
+ verify=VERIFY_CERTIFICATE
+ )
+
+ if res.status_code < 200 or res.status_code >= 300:
+ return_error('Failed to get endpoints.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(
+ fullurl, res.status_code, res.content))
+
+ raw_endpoints = json.loads(xml2json(res.content))
+
+ if not raw_endpoints or 'BESAPI' not in raw_endpoints:
+ return None
+
+ raw_endpoints = demisto.get(raw_endpoints, 'BESAPI.Computer')
+ if raw_endpoints and not isinstance(raw_endpoints, list):
+ raw_endpoints = [raw_endpoints]
+
+ for idx, endpoint in enumerate(raw_endpoints):
+ raw_endpoints[idx]['Resource'] = raw_endpoints[idx]['@Resource']
+ del raw_endpoints[idx]['@Resource']
+
+ if should_get_endpoint_details:
+ endpoints_with_details = []
+ for raw_endpoint in raw_endpoints:
+ endpoint = get_endpoint_details(raw_endpoint.get('ID'))
+ endpoints_with_details.append(endpoint)
+ return endpoints_with_details
+ else:
+ return raw_endpoints
+
+
+def get_endpoints_command():
+ should_get_endpoint_details = demisto.args().get('get_endpoint_details') == 'true'
+ endpoints = get_endpoints(should_get_endpoint_details)
+ headers = ['ID', 'Resource', 'LastReportTime']
+ if should_get_endpoint_details:
+ headers.extend([
+ 'ActiveDirectoryPath',
+ 'AgentType',
+ 'AgentVersion',
+ 'BESRelaySelectionMethod',
+ 'BESRelayServiceInstalled',
+ 'BESRootServer',
+ 'BIOS',
+ 'CPU',
+ 'ClientSettings',
+ 'ComputerName',
+ 'ComputerType',
+ 'DNSName',
+ 'DeviceType',
+ 'DistancetoBESRelay',
+ 'FreeSpaceonSystemDrive',
+ 'IPAddress',
+ 'LicenseType',
+ 'Locked',
+ 'OS',
+ 'RAM',
+ 'Relay',
+ 'RelayNameOfClient',
+ 'SubnetAddress',
+ 'SubscribedSites',
+ 'TotalSizeofSystemDrive',
+ 'UserName'
+ ])
+ demisto.results({
+ 'Type': entryTypes['note'],
+ 'ContentsFormat': formats['json'],
+ 'Contents': endpoints,
+ 'HumanReadable': tableToMarkdown('BigFix Computers', endpoints, headers=headers),
+ 'EntryContext': {
+ 'Bigfix.Endpoint(val.ID==obj.ID)': endpoints
+ }
+ })
+
+
+def get_endpoint_details(computer_id):
+ fullurl = BASE_URL + '/api/computer/{}'.format(computer_id)
+ res = requests.get(
+ fullurl,
+ auth=(USERNAME, PASSWORD),
+ verify=VERIFY_CERTIFICATE
+ )
+
+ if res.status_code < 200 or res.status_code >= 300:
+ return_error(
+ 'Failed to get computer {}.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(
+ computer_id, fullurl, res.status_code, res.content)
+ )
+
+ raw_endpoint = json.loads(xml2json(res.content))
+ if not raw_endpoint or 'BESAPI' not in raw_endpoint:
+ return None
+
+ raw_endpoint = demisto.get(raw_endpoint, 'BESAPI.Computer')
+
+ endpoint = {
+ 'ID': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "ID")=val["#text"]')),
+ 'Resource': demisto.get(raw_endpoint, '@Resource'),
+ 'LastReportTime': get_first(
+ demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Last Report Time")=val["#text"]')
+ ),
+ 'ActiveDirectoryPath': get_first(
+ demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Active Directory Path")=val["#text"]')
+ ),
+ 'AgentType': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Agent Type")=val["#text"]')),
+ 'AgentVersion': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Agent Version")=val["#text"]')),
+ 'BESRelaySelectionMethod': get_first(
+ demisto.dt(raw_endpoint, 'Property(val["@Name"] == "BES Relay Selection Method")=val["#text"]')
+ ),
+ 'BESRelayServiceInstalled': get_first(
+ demisto.dt(raw_endpoint, 'Property(val["@Name"] == "BES Relay Selection Method")=val["#text"]')
+ ),
+ 'BESRootServer': get_first(
+ demisto.dt(raw_endpoint, 'Property(val["@Name"] == "BES Root Server")=val["#text"]')
+ ),
+ 'BIOS': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "BIOS")=val["#text"]')),
+ 'CPU': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "CPU")=val["#text"]')),
+ 'ClientSettings': demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Client Settings")=val["#text"]'),
+ 'ComputerName': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Computer Name")=val["#text"]')),
+ 'ComputerType': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Computer Type")=val["#text"]')),
+ 'DNSName': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "DNS Name")=val["#text"]')),
+ 'IPAddress': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "IP Address")=val["#text"]')),
+ 'DeviceType': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Device Type")=val["#text"]')),
+ 'DistancetoBESRelay': get_first(
+ demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Distance to BES Relay")=val["#text"]')
+ ),
+ 'FreeSpaceonSystemDrive': get_first(
+ demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Free Space on System Drive")=val["#text"]')
+ ),
+ 'LicenseType': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "License Type")=val["#text"]')),
+ 'Locked': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Locked")=val["#text"]')),
+ 'OS': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "OS")=val["#text"]')),
+ 'RAM': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "RAM")=val["#text"]')),
+ 'Relay': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Relay")=val["#text"]')),
+ 'RelayNameOfClient': get_first(
+ demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Relay Name of Client")=val["#text"]')
+ ),
+ 'SubnetAddress': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Subnet Address")=val["#text"]')),
+ 'SubscribedSites': get_first(
+ demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Subscribed Sites")=val["#text"]')
+ ),
+ 'TotalSizeofSystemDrive': get_first(
+ demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Total Size of System Drive")=val["#text"]')
+ ),
+ 'UserName': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "User Name")=val["#text"]'))
+ }
+
+ return endpoint
+
+
+def get_endpoint_details_command():
+ computer_id = demisto.args().get('computer_id')
+
+ endpoint = get_endpoint_details(computer_id)
+ if endpoint is None:
+ demisto.results('Endpoint with id {} was not found'.format(computer_id))
+ sys.exit(0)
+
+ markdown = tableToMarkdown('BigFix Endpoint {}'.format(computer_id), [endpoint], headers=[
+ 'ID',
+ 'Resource',
+ 'LastReportTime',
+ 'ActiveDirectoryPath',
+ 'AgentType',
+ 'AgentVersion',
+ 'BESRelaySelectionMethod',
+ 'BESRelayServiceInstalled',
+ 'BESRootServer',
+ 'BIOS',
+ 'CPU',
+ 'ClientSettings',
+ 'ComputerName',
+ 'ComputerType',
+ 'DNSName',
+ 'DeviceType',
+ 'DistancetoBESRelay',
+ 'FreeSpaceonSystemDrive',
+ 'IPAddress',
+ 'LicenseType',
+ 'Locked',
+ 'OS',
+ 'RAM',
+ 'Relay',
+ 'RelayNameOfClient',
+ 'SubnetAddress',
+ 'SubscribedSites',
+ 'TotalSizeofSystemDrive',
+ 'UserName'
+ ])
+
+ demisto.results({
+ 'Type': entryTypes['note'],
+ 'ContentsFormat': formats['json'],
+ 'Contents': endpoint,
+ 'HumanReadable': markdown,
+ 'EntryContext': {
+ 'Bigfix.Endpoint(val.ID==obj.ID)': endpoint
+ }
+ })
+
+
+def get_patches(site_type='', site_name=''):
+ fullurl = BASE_URL + '/api/fixlets/{}'.format(site_type)
+ if site_type != 'master':
+ # if site name is not empty the add to url
+ fullurl += '/' + site_name
+
+ res = requests.get(
+ fullurl,
+ auth=(USERNAME, PASSWORD),
+ verify=VERIFY_CERTIFICATE
+ )
+
+ if res.status_code < 200 or res.status_code >= 300:
+ return_error(
+ 'Failed to get patches. Request URL: {}\nStatusCode: {}\nResponse Body: {}'.format(
+ fullurl, res.status_code, res.content)
+ )
+
+ raw_patches = json.loads(xml2json(res.content))
+ if not raw_patches or 'BESAPI' not in raw_patches:
+ return None
+
+ raw_patches = demisto.get(raw_patches, 'BESAPI.Fixlet')
+ if raw_patches and not isinstance(raw_patches, list):
+ raw_patches = [raw_patches]
+
+ patches_with_details = []
+ for raw_patch in raw_patches:
+ patch = get_patch_details(site_type, site_name, raw_patch.get('ID'))
+ patch['LastModified'] = raw_patch['@LastModified']
+ patches_with_details.append(patch)
+
+ return patches_with_details
+
+
+def get_patches_command():
+ site_name = demisto.args().get('site_name')
+ site_type = demisto.args().get('site_type')
+ patches = get_patches(site_type, site_name)
+
+ markdown = tableToMarkdown('BigFix Patches', patches, headers=[
+ 'ID',
+ 'Name',
+ 'Description',
+ 'LastModified',
+ 'Resource',
+ 'Relevance',
+ 'Category',
+ 'DownloadSize',
+ 'Source',
+ 'SourceID',
+ 'SourceReleaseDate',
+ 'SourceSeverity',
+ 'ActionID',
+ 'ActionScript'
+ ])
+
+ demisto.results({
+ 'Type': entryTypes['note'],
+ 'ContentsFormat': formats['json'],
+ 'Contents': patches,
+ 'HumanReadable': markdown,
+ 'EntryContext': {
+ 'Bigfix.Patch(val.ID==obj.ID)': patches
+ }
+ })
+
+
+def get_patch_details(site_type, site_name, patch_id):
+ if site_type == 'master':
+ fullurl = BASE_URL + '/api/fixlet/master/{}'.format(patch_id)
+ else:
+ fullurl = BASE_URL + '/api/fixlet/{}/{}/{}'.format(site_type, site_name, patch_id)
+
+ res = requests.get(
+ fullurl,
+ auth=(USERNAME, PASSWORD),
+ verify=VERIFY_CERTIFICATE
+ )
+
+ if res.status_code < 200 or res.status_code >= 300:
+ return_error('Failed to get patch/fixlet {}. Request URL: {}\nStatusCode: {}\nResponse Body: {}'.format(
+ patch_id, fullurl, res.status_code, res.content)
+ )
+
+ raw_patch = json.loads(xml2json(res.content))
+ if not raw_patch or 'BES' not in raw_patch:
+ return None
+
+ raw_patch = demisto.get(raw_patch, 'BES.Fixlet')
+ patch = {
+ 'ID': patch_id,
+ 'Name': demisto.get(raw_patch, 'Title'),
+ 'Resource': fullurl,
+ 'Description': demisto.get(raw_patch, 'Description'),
+ 'Relevance': demisto.get(raw_patch, 'Relevance'),
+ 'Category': demisto.get(raw_patch, 'Category'),
+ 'DownloadSize': demisto.get(raw_patch, 'DownloadSize'),
+ 'Source': demisto.get(raw_patch, 'Source'),
+ 'SourceID': demisto.get(raw_patch, 'SourceID'),
+ 'SourceReleaseDate': demisto.get(raw_patch, 'SourceReleaseDate'),
+ 'SourceSeverity': demisto.get(raw_patch, 'SourceSeverity'),
+ 'ActionID': demisto.get(raw_patch, 'DefaultAction.@ID'),
+ 'ActionScript': demisto.get(raw_patch, 'DefaultAction.ActionScript')
+ }
+
+ return patch
+
+
+def get_patch_details_command():
+ site_type = demisto.args().get('site_type')
+ site_name = demisto.args().get('site_name')
+ patch_id = demisto.args().get('id')
+
+ patch = get_patch_details(site_type, site_name, patch_id)
+ markdown = tableToMarkdown('BigFix Patch {}'.format(patch_id), [patch], headers=[
+ 'ID',
+ 'Name',
+ 'Resource',
+ 'Description',
+ 'Relevance',
+ 'Category',
+ 'DownloadSize',
+ 'Source',
+ 'SourceID',
+ 'SourceReleaseDate',
+ 'SourceSeverity',
+ 'ActionID',
+ 'ActionScript'
+ ])
+
+ demisto.results({
+ 'Type': entryTypes['note'],
+ 'ContentsFormat': formats['json'],
+ 'Contents': patch,
+ 'HumanReadable': markdown,
+ 'EntryContext': {
+ 'Bigfix.Patch(val.ID==obj.ID)': patch
+ }
+ })
+
+
+def deploy_patch(site_name, computer_ids, fixlet_id, action_id):
+ if 'all' in computer_ids:
+ target = 'true'
+ else:
+ target = '\n'.join(['{}'.format(computer_id) for computer_id in computer_ids])
+
+ request_body = """
+
+
+
+ {}
+ {}
+ {}
+
+
+ {}
+
+ 1000
+
+
+ """.format(site_name, fixlet_id, action_id, target)
+ LOG('deploy_patch - request: ' + request_body)
+
+ fullurl = BASE_URL + '/api/actions'
+ res = requests.post(
+ fullurl,
+ auth=(USERNAME, PASSWORD),
+ verify=VERIFY_CERTIFICATE,
+ data=request_body
+ )
+
+ LOG('deploy_patch - raw response: ' + res.content)
+ if res.status_code < 200 or res.status_code >= 300:
+ return_error('Failed to deploy patch {}.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(
+ fixlet_id, fullurl, res.status_code, res.content))
+
+ raw_action = json.loads(xml2json(res.content))
+ if not raw_action or 'BESAPI' not in raw_action:
+ return None
+
+ raw_action = demisto.get(raw_action, 'BESAPI.Action')
+ raw_action['FixletID'] = fixlet_id
+ raw_action['ComputerIDs'] = computer_ids
+ raw_action['SiteName'] = site_name
+ raw_action['Resource'] = raw_action['@Resource']
+ del raw_action['@Resource']
+ if 'all' in computer_ids:
+ raw_action['AllComputers'] = True
+ del raw_action['ComputerIDs']
+
+ return raw_action
+
+
+def deploy_patch_command():
+ site_name = demisto.args().get('site_name')
+ computer_ids = argToList(demisto.args().get('computer_ids'))
+
+ fixlet_id = demisto.args().get('fixlet_id')
+ action_id = demisto.args().get('action_id')
+
+ action = deploy_patch(site_name, computer_ids, fixlet_id, action_id)
+
+ markdown = tableToMarkdown('BigFix Action {}'.format(action_id), [action], headers=[
+ 'ID',
+ 'Name',
+ 'FixletID',
+ 'ComputerIDs',
+ 'SiteName',
+ 'Resource'
+ ])
+
+ demisto.results({
+ 'Type': entryTypes['note'],
+ 'ContentsFormat': formats['json'],
+ 'Contents': action,
+ 'HumanReadable': markdown,
+ 'EntryContext': {
+ 'Bigfix.Action(val.ID==obj.ID)': action
+ }
+ })
+
+
+def action_delete(action_id):
+ fullurl = BASE_URL + '/api/action/' + action_id
+ res = requests.delete(
+ fullurl,
+ auth=(USERNAME, PASSWORD),
+ verify=VERIFY_CERTIFICATE
+ )
+
+ if res.status_code < 200 or res.status_code >= 300:
+ return_error('Failed to delete action {}.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(
+ action_id, fullurl, res.status_code, res.content))
+
+
+def action_delete_command():
+ action_id = demisto.args().get('action_id')
+
+ action_delete(action_id)
+
+ demisto.results('Action {} was deleted successfully'.format(action_id))
+
+
+def get_action_status(action_id):
+ fullurl = BASE_URL + '/api/action/' + action_id + '/status'
+ res = requests.get(
+ fullurl,
+ auth=(USERNAME, PASSWORD),
+ verify=VERIFY_CERTIFICATE
+ )
+
+ if res.status_code < 200 or res.status_code >= 300:
+ return_error('Failed to get action {} status.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(
+ action_id, fullurl, res.status_code, res.content))
+
+ raw_action = json.loads(xml2json(res.content))
+ if not raw_action or 'BESAPI' not in raw_action:
+ return None
+
+ raw_action = demisto.get(raw_action, 'BESAPI.ActionResults')
+ return raw_action.get('Status')
+
+
+def get_action_status_command():
+ action_id = demisto.args().get('action_id')
+
+ status = get_action_status(action_id)
+
+ output = {
+ 'ID': action_id,
+ 'Status': status
+ }
+ demisto.results({
+ 'Type': entryTypes['note'],
+ 'ContentsFormat': formats['json'],
+ 'Contents': output,
+ 'HumanReadable': 'Action {} status is {}'.format(action_id, status),
+ 'EntryContext': {
+ 'Bigfix.Action(val.ID==obj.ID)': output
+ }
+ })
+
+
+def action_stop(action_id):
+ fullurl = BASE_URL + '/api/action/' + action_id + '/stop'
+ res = requests.post(
+ fullurl,
+ auth=(USERNAME, PASSWORD),
+ verify=VERIFY_CERTIFICATE
+ )
+
+ if res.status_code < 200 or res.status_code >= 300:
+ return_error('Failed to stop action {}.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(
+ action_id, fullurl, res.status_code, res.content))
+
+
+def action_stop_command():
+ action_id = demisto.args().get('action_id')
+
+ action_stop(action_id)
+
+ demisto.results('Action {} was stopped successfully'.format(action_id))
+
+
+def query(relevance):
+ fullurl = BASE_URL + '/api/query'
+ params = {
+ 'relevance': relevance
+ }
+ res = requests.get(
+ fullurl,
+ auth=(USERNAME, PASSWORD),
+ verify=VERIFY_CERTIFICATE,
+ params=params
+ )
+
+ if res.status_code < 200 or res.status_code >= 300:
+ return_error('Query failed.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(
+ fullurl, res.status_code, res.content))
+
+ raw_action = json.loads(xml2json(res.content))
+ if not raw_action or 'BESAPI' not in raw_action:
+ demisto.info('BigFix query has incorrect response format. Response Body: {}'.format(res.content))
+ return_error('The response has incorrect format. Check the logs')
+
+ if demisto.get(raw_action, 'BESAPI.Query.Error'):
+ error = demisto.get(raw_action, 'BESAPI.Query.Error')
+ return_error(error)
+
+ raw_query_results = demisto.get(raw_action, 'BESAPI.Query')
+ return raw_query_results
+
+
+def query_command():
+ relevance = demisto.args().get('relevance')
+ results = query(relevance)
+
+ if results is None:
+ demisto.results('No results')
+ sys.exit(0)
+
+ output = demisto.dt(results, 'Result.Answer.#text')
+ if not isinstance(output, list):
+ output = [output]
+
+ demisto.results({
+ 'Type': entryTypes['note'],
+ 'ContentsFormat': formats['json'],
+ 'Contents': results,
+ 'HumanReadable': tableToMarkdown('Query Results: {}'.format(relevance), output, ['Results']),
+ 'EntryContext': {
+ 'Bigfix.QueryResults': output
+ }
+ })
+
+
+try:
+ # do requets to /api/help
+ # should be good indicator for test connectivity
+ def test():
+ fullurl = BASE_URL + '/api/help'
+ res = requests.get(
+ fullurl,
+ auth=(USERNAME, PASSWORD),
+ verify=VERIFY_CERTIFICATE
+ )
+ res.raise_for_status()
+
+ if demisto.command() == 'test-module':
+ # do requets to /api/help
+ # should be good indicator for test connectivity
+ test()
+ demisto.results('ok')
+
+ elif demisto.command() == 'bigfix-get-sites':
+ get_sites_command()
+
+ elif demisto.command() == 'bigfix-get-site':
+ get_site_command()
+
+ elif demisto.command() == 'bigfix-get-endpoints':
+ get_endpoints_command()
+
+ elif demisto.command() == 'bigfix-get-endpoint':
+ get_endpoint_details_command()
+
+ elif demisto.command() == 'bigfix-get-patches':
+ get_patches_command()
+
+ elif demisto.command() == 'bigfix-get-patch':
+ get_patch_details_command()
+
+ elif demisto.command() == 'bigfix-deploy-patch':
+ deploy_patch_command()
+
+ elif demisto.command() == 'bigfix-action-delete':
+ action_delete_command()
+
+ elif demisto.command() == 'bigfix-action-status':
+ get_action_status_command()
+
+ elif demisto.command() == 'bigfix-action-stop':
+ action_stop_command()
+
+ elif demisto.command():
+ query_command()
+
+except Exception, e:
+ LOG(e.message)
+ LOG.print_log()
+ return_error(e.message)
diff --git a/Packs/BigFix/Integrations/BigFix/BigFix.yml b/Packs/BigFix/Integrations/BigFix/BigFix.yml
new file mode 100644
index 00000000000..eb70bf549a4
--- /dev/null
+++ b/Packs/BigFix/Integrations/BigFix/BigFix.yml
@@ -0,0 +1,576 @@
+category: Vulnerability Management
+commonfields:
+ id: BigFix
+ version: -1
+configuration:
+- display: Server url (e.g https://192.168.10.1:52311)
+ name: url
+ required: true
+ type: 0
+- display: Username
+ name: credentials
+ required: true
+ type: 9
+- display: Trust any certificate (not secure)
+ name: unsecure
+ required: false
+ type: 8
+- display: Use system proxy settings
+ name: proxy
+ required: false
+ type: 8
+description: IBM BigFix Patch provides an automated, simplified patching process that
+ is administered from a single console.
+display: BigFix
+name: BigFix
+script:
+ commands:
+ - deprecated: false
+ description: Retrieves all the sites
+ execution: false
+ name: bigfix-get-sites
+ outputs:
+ - contextPath: Bigfix.Site
+ description: Site
+ type: unknown
+ - contextPath: Bigfix.Site.Name
+ description: Name of the site
+ type: string
+ - contextPath: Bigfix.Site.Description
+ description: Description of the site
+ type: string
+ - contextPath: Bigfix.Site.Resource
+ description: Link to the endpoint resource.
+ type: string
+ - contextPath: Bigfix.Site.Type
+ description: Type of the site (master,custom,external,operator)
+ type: string
+ - contextPath: Bigfix.Site.Domain
+ description: Site domain
+ type: string
+ - contextPath: Bigfix.Site.GatherURL
+ description: Gather URL
+ type: string
+ - contextPath: Bigfix.Site.GlobalReadPermission
+ description: Global Read Permission available or not.
+ type: string
+ - arguments:
+ - default: false
+ description: Name of the site. If the site is external or operator then site
+ must be provided
+ isArray: false
+ name: site_name
+ required: false
+ secret: false
+ - auto: PREDEFINED
+ default: false
+ defaultValue: master
+ description: 'Type of the site. One of the following options: external,operator,master,custom'
+ isArray: false
+ name: site_type
+ predefined:
+ - external
+ - operator
+ - master
+ - custom
+ required: true
+ secret: false
+ deprecated: false
+ description: Retrieve single site by name and type
+ execution: false
+ name: bigfix-get-site
+ outputs:
+ - contextPath: Bigfix.Site
+ description: Site
+ type: unknown
+ - contextPath: Bigfix.Site.Name
+ description: Name of the site
+ type: string
+ - contextPath: Bigfix.Site.Description
+ description: Description of the site
+ type: string
+ - contextPath: Bigfix.Site.Resource
+ description: Link to the endpoint resource.
+ type: string
+ - contextPath: Bigfix.Site.Type
+ description: Type of the site (master,custom,external,operator)
+ type: string
+ - contextPath: Bigfix.Site.Domain
+ description: Site domain
+ type: string
+ - contextPath: Bigfix.Site.GatherURL
+ description: Gather URL
+ type: string
+ - contextPath: Bigfix.Site.GlobalReadPermission
+ description: Global Read Permission available or not.
+ type: string
+ - arguments:
+ - auto: PREDEFINED
+ default: false
+ defaultValue: master
+ description: 'Type of the site. One of the following options: external,operator,master,custom'
+ isArray: false
+ name: site_type
+ predefined:
+ - external
+ - operator
+ - master
+ - custom
+ required: true
+ secret: false
+ - default: false
+ description: Name of the site. If the site is external or operator then site
+ must be provided
+ isArray: false
+ name: site_name
+ required: false
+ secret: false
+ deprecated: false
+ description: Retrieve all the patches (fixlets) of site
+ execution: false
+ name: bigfix-get-patches
+ outputs:
+ - contextPath: Bigfix.Patch.ID
+ description: Patch (fixlet) ID
+ type: string
+ - contextPath: Bigfix.Patch.LastModified
+ description: Last modified Timestamp.
+ type: date
+ - contextPath: Bigfix.Patch.Name
+ description: Name of the Patch requested.
+ type: string
+ - contextPath: Bigfix.Patch.Resource
+ description: The link for the patch
+ type: string
+ - contextPath: Bigfix.Patch.Description
+ description: Description of the Patch requested.
+ type: string
+ - contextPath: Bigfix.Patch.Relevance
+ description: Relevance of the Patch requested.
+ type: string
+ - contextPath: Bigfix.Patch.Category
+ description: Category of the Patch requested.
+ type: string
+ - contextPath: Bigfix.Patch.DownloadSize
+ description: Download size.
+ type: string
+ - contextPath: Bigfix.Patch.Source
+ description: Source from where the patch is coming from.
+ type: string
+ - contextPath: Bigfix.Patch.SourceID
+ description: Source ID of the Patch requested.
+ type: string
+ - contextPath: Bigfix.Patch.SourceSeverity
+ description: Source Severity of the Patch requested.
+ type: string
+ - contextPath: Bigfix.Patch.SourceReleaseDate
+ description: Source Release Date of the Patch requested.
+ type: string
+ - contextPath: Bigfix.Patch.ActionID
+ description: Action ID of the Patch requested.
+ type: string
+ - contextPath: Bigfix.Patch.ActionScript
+ description: Action Script of the Patch requested.
+ type: string
+ - arguments:
+ - auto: PREDEFINED
+ default: false
+ defaultValue: 'true'
+ description: Whether to get endpoint full details of each endpoint or just basic
+ details, such as ID and last reported time. We recommend setting this to false
+ if there are many endpoints to retrieve.
+ isArray: false
+ name: get_endpoint_details
+ predefined:
+ - 'true'
+ - 'false'
+ required: false
+ secret: false
+ deprecated: false
+ description: Retrieve all the endpoints (computers)
+ execution: false
+ name: bigfix-get-endpoints
+ outputs:
+ - contextPath: Bigfix.Endpoint
+ description: Endpoint (computer)
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.ID
+ description: The if of the endpoint (computer ID)
+ type: string
+ - contextPath: Bigfix.Endpoint.Resource
+ description: URL to the endpoint details
+ type: string
+ - contextPath: Bigfix.Endpoint.LastReportTime
+ description: Last report time of the endpoint
+ type: date
+ - contextPath: Bigfix.Endpoint.ActiveDirectoryPath
+ description: Active directory path of the endpoint device.
+ type: string
+ - contextPath: Bigfix.Endpoint.AgentType
+ description: Agent Type of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.AgentVersion
+ description: Agent Version of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.BESRelaySelectionMethod
+ description: Relay selection method of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.BESRelayServiceInstalled
+ description: Relay service installed of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.BESRootServer
+ description: Root server of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.BIOS
+ description: BIOS of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.CPU
+ description: CPU of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.ClientSettings
+ description: Client settings of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.ComputerName
+ description: Computer name of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.ComputerType
+ description: Computer Type of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.DNSName
+ description: DNS Name of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.DeviceType
+ description: Device Type of the endpoint device.
+ type: string
+ - contextPath: Bigfix.Endpoint.DistancetoBESRelay
+ description: Distance to BES Relay of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.FreeSpaceonSystemDrive
+ description: Free space on sytem drive of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.IPAddress
+ description: IP of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.LicenseType
+ description: License of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.Locked
+ description: Locked of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.OS
+ description: OS of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.RAM
+ description: RAM of the endpoint.
+ type: number
+ - contextPath: Bigfix.Endpoint.Relay
+ description: Relay of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.RelayNameOfClient
+ description: Relay Name of the client.
+ type: string
+ - contextPath: Bigfix.Endpoint.SubnetAddress
+ description: Subnet Address of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.SubscribedSites
+ description: Subscribed sites.
+ type: string
+ - contextPath: Bigfix.Endpoint.TotalSizeofSystemDrive
+ description: Total size of system drive.
+ type: number
+ - contextPath: Bigfix.Endpoint.UserName
+ description: User name.
+ type: string
+ - arguments:
+ - default: false
+ description: Computer ID
+ isArray: false
+ name: computer_id
+ required: true
+ secret: false
+ deprecated: false
+ description: Retrieve endpoint (computer) details
+ execution: false
+ name: bigfix-get-endpoint
+ outputs:
+ - contextPath: Bigfix.Endpoint
+ description: Endpoint (computer)
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.ID
+ description: The if of the endpoint (computer ID)
+ type: string
+ - contextPath: Bigfix.Endpoint.Resource
+ description: URL to the endpoint details
+ type: string
+ - contextPath: Bigfix.Endpoint.LastReportTime
+ description: Last report time of the endpoint
+ type: date
+ - contextPath: Bigfix.Endpoint.ActiveDirectoryPath
+ description: Active directory path of the endpoint device.
+ type: string
+ - contextPath: Bigfix.Endpoint.AgentType
+ description: Agent Type of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.AgentVersion
+ description: Agent Version of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.BESRelaySelectionMethod
+ description: Relay selection method of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.BESRelayServiceInstalled
+ description: Relay service installed of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.BESRootServer
+ description: Root server of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.BIOS
+ description: BIOS of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.CPU
+ description: CPU of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.ClientSettings
+ description: Client settings of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.ComputerName
+ description: Computer name of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.ComputerType
+ description: Computer Type of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.DNSName
+ description: DNS Name of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.DeviceType
+ description: Device Type of the endpoint device.
+ type: string
+ - contextPath: Bigfix.Endpoint.DistancetoBESRelay
+ description: Distance to BES Relay of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.FreeSpaceonSystemDrive
+ description: Free space on sytem drive of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.IPAddress
+ description: IP of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.LicenseType
+ description: License of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.Locked
+ description: Locked of the endpoint.
+ type: Unknown
+ - contextPath: Bigfix.Endpoint.OS
+ description: OS of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.RAM
+ description: RAM of the endpoint.
+ type: number
+ - contextPath: Bigfix.Endpoint.Relay
+ description: Relay of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.RelayNameOfClient
+ description: Relay Name of the client.
+ type: string
+ - contextPath: Bigfix.Endpoint.SubnetAddress
+ description: Subnet Address of the endpoint.
+ type: string
+ - contextPath: Bigfix.Endpoint.SubscribedSites
+ description: Subscribed sites.
+ type: string
+ - contextPath: Bigfix.Endpoint.TotalSizeofSystemDrive
+ description: Total size of system drive.
+ type: number
+ - contextPath: Bigfix.Endpoint.UserName
+ description: User name.
+ type: string
+ - arguments:
+ - default: false
+ description: Name of the site. If the site is external or operator then site
+ must be provided
+ isArray: false
+ name: site_name
+ required: true
+ secret: false
+ - default: false
+ description: Provide ids of computers to deploy the patch. Pass 'all' to deploy
+ to all the computers
+ isArray: true
+ name: computer_ids
+ required: true
+ secret: false
+ - default: false
+ description: The Fixlet ID. To use the action script from the original Fixlet
+ or Task Message.
+ isArray: false
+ name: fixlet_id
+ required: true
+ secret: false
+ - default: false
+ description: The action ID. The specified action will run on target computers.
+ isArray: false
+ name: action_id
+ required: true
+ secret: false
+ deprecated: false
+ description: Create an action on BigFix that will run the given action from the
+ given fixlet on target computers. The computerID parameter takes a comma-separated
+ list of BigFix computer IDs. If no computers are given, the action will be run
+ on the default computers configured on BigFix. If the action should run on all
+ computers set the computerID parameter to all.
+ execution: false
+ name: bigfix-deploy-patch
+ outputs:
+ - contextPath: Bigfix.Action.ID
+ description: Action ID
+ type: number
+ - contextPath: Bigfix.Action.Name
+ description: Action Name
+ type: string
+ - contextPath: Bigfix.Action.SiteName
+ description: Site name
+ type: string
+ - contextPath: Bigfix.Action.ComputerIDs
+ description: Computers IDs to which the patch was applied to
+ type: Unknown
+ - contextPath: Bigfix.Action.AllComputers
+ description: true if patch was applied to all the computers
+ type: boolean
+ - contextPath: Bigfix.Action.Resource
+ description: Link to action in bigfix
+ type: string
+ - arguments:
+ - default: false
+ description: Fixlet id
+ isArray: false
+ name: id
+ required: true
+ secret: false
+ - auto: PREDEFINED
+ default: false
+ description: 'Type of the site. One of the following options: external,operator,master,custom'
+ isArray: false
+ name: site_type
+ predefined:
+ - external
+ - operator
+ - master
+ - custom
+ required: true
+ secret: false
+ - default: false
+ description: Name of the site. If the site is external or operator then site
+ must be provided
+ isArray: false
+ name: site_name
+ required: false
+ secret: false
+ deprecated: false
+ description: Retrieve patch (fixlet) by id
+ execution: false
+ name: bigfix-get-patch
+ outputs:
+ - contextPath: Bigfix.Patch.ID
+ description: Patch(fixlet) id
+ type: Unknown
+ - contextPath: Bigfix.Patch.Name
+ description: Patch name
+ type: Unknown
+ - contextPath: Bigfix.Patch.Resource
+ description: Link (URL) to the patch
+ type: Unknown
+ - contextPath: Bigfix.Patch.Description
+ description: Description
+ type: Unknown
+ - contextPath: Bigfix.Patch.Relevance
+ description: Relevance of the Patch requested.
+ type: Unknown
+ - contextPath: Bigfix.Patch.Category
+ description: Category of the Patch requested.
+ type: string
+ - contextPath: Bigfix.Patch.DownloadSize
+ description: Download size.
+ type: Unknown
+ - contextPath: Bigfix.Patch.Source
+ description: Source from where the patch is coming from.
+ type: Unknown
+ - contextPath: Bigfix.Patch.SourceID
+ description: Source ID of the Patch requested.
+ type: Unknown
+ - contextPath: Bigfix.Patch.SourceSeverity
+ description: Source Severity of the Patch requested.
+ type: Unknown
+ - contextPath: Bigfix.Patch.SourceReleaseDate
+ description: Source Release Date of the Patch requested.
+ type: Unknown
+ - contextPath: Bigfix.Patch.ActionID
+ description: Action ID of the Patch requested.
+ type: string
+ - contextPath: Bigfix.Patch.ActionScript
+ description: Action Script of the Patch requested.
+ type: string
+ - arguments:
+ - default: false
+ description: Action ID
+ isArray: false
+ name: action_id
+ required: true
+ secret: false
+ deprecated: false
+ description: 'Stops and deletes the specified action. Note: You cannot delete
+ actions that are members of a Multiple Action Group. This note applies to IBM
+ BigFix V9.2 and later.'
+ execution: false
+ name: bigfix-action-delete
+ - arguments:
+ - default: false
+ description: Action ID
+ isArray: false
+ name: action_id
+ required: true
+ secret: false
+ deprecated: false
+ description: Gets the status of an action against it's targets.
+ execution: false
+ name: bigfix-action-status
+ outputs:
+ - contextPath: Bigfix.Action.ID
+ description: Action ID
+ type: string
+ - contextPath: Bigfix.Action.Status
+ description: Action Status (e.g Open, Stopped)
+ type: string
+ - arguments:
+ - default: false
+ description: Action ID
+ isArray: false
+ name: action_id
+ required: true
+ secret: false
+ deprecated: false
+ description: Stops the specified action.
+ execution: false
+ name: bigfix-action-stop
+ - arguments:
+ - default: false
+ description: 'Relevance query (example: names of bes computers)'
+ isArray: false
+ name: relevance
+ required: true
+ secret: false
+ deprecated: false
+ description: Evaluate a relevance expression and get the result. This runs request
+ is processed through the server to WebReports.
+ execution: false
+ name: bigfix-query
+ outputs:
+ - contextPath: Bigfix.QueryResults
+ description: The results of the query
+ type: unknown
+ feed: false
+ isfetch: false
+ longRunning: false
+ longRunningPort: false
+ runonce: false
+ script: '-'
+ subtype: python2
+ type: python
diff --git a/Packs/BigFix/Integrations/BigFix/BigFix_image.png b/Packs/BigFix/Integrations/BigFix/BigFix_image.png
new file mode 100644
index 00000000000..6e8a7360f02
Binary files /dev/null and b/Packs/BigFix/Integrations/BigFix/BigFix_image.png differ
diff --git a/Packs/BigFix/Integrations/integration-BigFix_CHANGELOG.md b/Packs/BigFix/Integrations/BigFix/CHANGELOG.md
similarity index 100%
rename from Packs/BigFix/Integrations/integration-BigFix_CHANGELOG.md
rename to Packs/BigFix/Integrations/BigFix/CHANGELOG.md
diff --git a/Packs/BigFix/Integrations/BigFix/Pipfile b/Packs/BigFix/Integrations/BigFix/Pipfile
new file mode 100644
index 00000000000..5b6d66dc82c
--- /dev/null
+++ b/Packs/BigFix/Integrations/BigFix/Pipfile
@@ -0,0 +1,25 @@
+[[source]]
+name = "pypi"
+url = "https://pypi.org/simple"
+verify_ssl = true
+
+[dev-packages]
+pylint = "*"
+pytest = "*"
+pytest-mock = "*"
+requests-mock = "*"
+pytest-xdist = "*"
+pytest-json = "*"
+flake8 = "*"
+
+[packages]
+certifi = "==2017.11.5"
+chardet = "==3.0.4"
+idna = "==2.6"
+olefile = "==0.44"
+requests = "==2.18.4"
+urllib3 = "==1.22"
+PyYAML = "==3.12"
+
+[requires]
+python_version = "2.7"
diff --git a/Packs/BigFix/Integrations/BigFix/Pipfile.lock b/Packs/BigFix/Integrations/BigFix/Pipfile.lock
new file mode 100644
index 00000000000..9c6aa954ea0
--- /dev/null
+++ b/Packs/BigFix/Integrations/BigFix/Pipfile.lock
@@ -0,0 +1,347 @@
+{
+ "_meta": {
+ "hash": {
+ "sha256": "17cd8f31f8b4bdfa7fedfae87e0eca4d9e1a602f54d991932f84ea3d2084e8f6"
+ },
+ "pipfile-spec": 6,
+ "requires": {
+ "python_version": "2.7"
+ },
+ "sources": [
+ {
+ "name": "pypi",
+ "url": "https://pypi.org/simple",
+ "verify_ssl": true
+ }
+ ]
+ },
+ "default": {},
+ "develop": {
+ "apipkg": {
+ "hashes": [
+ "sha256:37228cda29411948b422fae072f57e31d3396d2ee1c9783775980ee9c9990af6",
+ "sha256:58587dd4dc3daefad0487f6d9ae32b4542b185e1c36db6993290e7c41ca2b47c"
+ ],
+ "version": "==1.5"
+ },
+ "astroid": {
+ "hashes": [
+ "sha256:87de48a92e29cedf7210ffa853d11441e7ad94cb47bacd91b023499b51cbc756",
+ "sha256:d25869fc7f44f1d9fb7d24fd7ea0639656f5355fc3089cd1f3d18c6ec6b124c7"
+ ],
+ "version": "==1.6.6"
+ },
+ "atomicwrites": {
+ "hashes": [
+ "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
+ "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
+ ],
+ "version": "==1.3.0"
+ },
+ "attrs": {
+ "hashes": [
+ "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
+ "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
+ ],
+ "version": "==19.3.0"
+ },
+ "backports.functools-lru-cache": {
+ "hashes": [
+ "sha256:0bada4c2f8a43d533e4ecb7a12214d9420e66eb206d54bf2d682581ca4b80848",
+ "sha256:8fde5f188da2d593bd5bc0be98d9abc46c95bb8a9dde93429570192ee6cc2d4a"
+ ],
+ "markers": "python_version < '3.2'",
+ "version": "==1.6.1"
+ },
+ "certifi": {
+ "hashes": [
+ "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
+ "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
+ ],
+ "version": "==2019.11.28"
+ },
+ "chardet": {
+ "hashes": [
+ "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
+ "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
+ ],
+ "version": "==3.0.4"
+ },
+ "configparser": {
+ "hashes": [
+ "sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c",
+ "sha256:c7d282687a5308319bf3d2e7706e575c635b0a470342641c93bea0ea3b5331df"
+ ],
+ "markers": "python_version < '3'",
+ "version": "==4.0.2"
+ },
+ "contextlib2": {
+ "hashes": [
+ "sha256:01f490098c18b19d2bd5bb5dc445b2054d2fa97f09a4280ba2c5f3c394c8162e",
+ "sha256:3355078a159fbb44ee60ea80abd0d87b80b78c248643b49aa6d94673b413609b"
+ ],
+ "markers": "python_version < '3'",
+ "version": "==0.6.0.post1"
+ },
+ "enum34": {
+ "hashes": [
+ "sha256:13ef9a1c478203252107f66c25b99b45b1865693ca1284aab40dafa7e1e7ac17",
+ "sha256:708aabfb3d5898f99674c390d360d59efdd08547019763622365f19e84a7fef4",
+ "sha256:98df1f1937840b7d8012fea7f0b36392a3e6fd8a2f429c48a3ff4b1aad907f3f"
+ ],
+ "markers": "python_version < '3.4'",
+ "version": "==1.1.9"
+ },
+ "execnet": {
+ "hashes": [
+ "sha256:cacb9df31c9680ec5f95553976c4da484d407e85e41c83cb812aa014f0eddc50",
+ "sha256:d4efd397930c46415f62f8a31388d6be4f27a91d7550eb79bc64a756e0056547"
+ ],
+ "version": "==1.7.1"
+ },
+ "funcsigs": {
+ "hashes": [
+ "sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca",
+ "sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"
+ ],
+ "markers": "python_version < '3.0'",
+ "version": "==1.0.2"
+ },
+ "futures": {
+ "hashes": [
+ "sha256:49b3f5b064b6e3afc3316421a3f25f66c137ae88f068abbf72830170033c5e16",
+ "sha256:7e033af76a5e35f58e56da7a91e687706faf4e7bdfb2cbc3f2cca6b9bcda9794"
+ ],
+ "markers": "python_version < '3.2'",
+ "version": "==3.3.0"
+ },
+ "idna": {
+ "hashes": [
+ "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
+ "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
+ ],
+ "version": "==2.9"
+ },
+ "importlib-metadata": {
+ "hashes": [
+ "sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302",
+ "sha256:b97607a1a18a5100839aec1dc26a1ea17ee0d93b20b0f008d80a5a050afb200b"
+ ],
+ "markers": "python_version < '3.8'",
+ "version": "==1.5.0"
+ },
+ "isort": {
+ "hashes": [
+ "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1",
+ "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"
+ ],
+ "version": "==4.3.21"
+ },
+ "lazy-object-proxy": {
+ "hashes": [
+ "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d",
+ "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449",
+ "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08",
+ "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a",
+ "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50",
+ "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd",
+ "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239",
+ "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb",
+ "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea",
+ "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e",
+ "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156",
+ "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142",
+ "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442",
+ "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62",
+ "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db",
+ "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531",
+ "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383",
+ "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a",
+ "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357",
+ "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4",
+ "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"
+ ],
+ "version": "==1.4.3"
+ },
+ "mccabe": {
+ "hashes": [
+ "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
+ "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
+ ],
+ "version": "==0.6.1"
+ },
+ "mock": {
+ "hashes": [
+ "sha256:83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3",
+ "sha256:d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8"
+ ],
+ "markers": "python_version < '3.0'",
+ "version": "==3.0.5"
+ },
+ "more-itertools": {
+ "hashes": [
+ "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4",
+ "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc",
+ "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"
+ ],
+ "markers": "python_version <= '2.7'",
+ "version": "==5.0.0"
+ },
+ "packaging": {
+ "hashes": [
+ "sha256:170748228214b70b672c581a3dd610ee51f733018650740e98c7df862a583f73",
+ "sha256:e665345f9eef0c621aa0bf2f8d78cf6d21904eef16a93f020240b704a57f1334"
+ ],
+ "version": "==20.1"
+ },
+ "pathlib2": {
+ "hashes": [
+ "sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db",
+ "sha256:6cd9a47b597b37cc57de1c05e56fb1a1c9cc9fab04fe78c29acd090418529868"
+ ],
+ "markers": "python_version < '3'",
+ "version": "==2.3.5"
+ },
+ "pluggy": {
+ "hashes": [
+ "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
+ "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
+ ],
+ "version": "==0.13.1"
+ },
+ "py": {
+ "hashes": [
+ "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa",
+ "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"
+ ],
+ "version": "==1.8.1"
+ },
+ "pylint": {
+ "hashes": [
+ "sha256:367e3d49813d349a905390ac27989eff82ab84958731c5ef0bef867452cfdc42",
+ "sha256:97a42df23d436c70132971d1dcb9efad2fe5c0c6add55b90161e773caf729300"
+ ],
+ "index": "pypi",
+ "version": "==1.9.5"
+ },
+ "pyparsing": {
+ "hashes": [
+ "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f",
+ "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"
+ ],
+ "version": "==2.4.6"
+ },
+ "pytest": {
+ "hashes": [
+ "sha256:19e8f75eac01dd3f211edd465b39efbcbdc8fc5f7866d7dd49fedb30d8adf339",
+ "sha256:c77a5f30a90e0ce24db9eaa14ddfd38d4afb5ea159309bdd2dae55b931bc9324"
+ ],
+ "index": "pypi",
+ "version": "==4.6.9"
+ },
+ "pytest-forked": {
+ "hashes": [
+ "sha256:1805699ed9c9e60cb7a8179b8d4fa2b8898098e82d229b0825d8095f0f261100",
+ "sha256:1ae25dba8ee2e56fb47311c9638f9e58552691da87e82d25b0ce0e4bf52b7d87"
+ ],
+ "version": "==1.1.3"
+ },
+ "pytest-json": {
+ "hashes": [
+ "sha256:8bf4e1be1691f4416bc12b14785b5ad9e842887b0b2b2d61b37dcb555b208630"
+ ],
+ "index": "pypi",
+ "version": "==0.4.0"
+ },
+ "pytest-mock": {
+ "hashes": [
+ "sha256:b35eb281e93aafed138db25c8772b95d3756108b601947f89af503f8c629413f",
+ "sha256:cb67402d87d5f53c579263d37971a164743dc33c159dfb4fb4a86f37c5552307"
+ ],
+ "index": "pypi",
+ "version": "==2.0.0"
+ },
+ "pytest-xdist": {
+ "hashes": [
+ "sha256:0f46020d3d9619e6d17a65b5b989c1ebbb58fc7b1da8fb126d70f4bac4dfeed1",
+ "sha256:7dc0d027d258cd0defc618fb97055fbd1002735ca7a6d17037018cf870e24011"
+ ],
+ "index": "pypi",
+ "version": "==1.31.0"
+ },
+ "requests": {
+ "hashes": [
+ "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
+ "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
+ ],
+ "version": "==2.23.0"
+ },
+ "requests-mock": {
+ "hashes": [
+ "sha256:510df890afe08d36eca5bb16b4aa6308a6f85e3159ad3013bac8b9de7bd5a010",
+ "sha256:88d3402dd8b3c69a9e4f9d3a73ad11b15920c6efd36bc27bf1f701cf4a8e4646"
+ ],
+ "index": "pypi",
+ "version": "==1.7.0"
+ },
+ "scandir": {
+ "hashes": [
+ "sha256:2586c94e907d99617887daed6c1d102b5ca28f1085f90446554abf1faf73123e",
+ "sha256:2ae41f43797ca0c11591c0c35f2f5875fa99f8797cb1a1fd440497ec0ae4b022",
+ "sha256:2b8e3888b11abb2217a32af0766bc06b65cc4a928d8727828ee68af5a967fa6f",
+ "sha256:2c712840c2e2ee8dfaf36034080108d30060d759c7b73a01a52251cc8989f11f",
+ "sha256:4d4631f6062e658e9007ab3149a9b914f3548cb38bfb021c64f39a025ce578ae",
+ "sha256:67f15b6f83e6507fdc6fca22fedf6ef8b334b399ca27c6b568cbfaa82a364173",
+ "sha256:7d2d7a06a252764061a020407b997dd036f7bd6a175a5ba2b345f0a357f0b3f4",
+ "sha256:8c5922863e44ffc00c5c693190648daa6d15e7c1207ed02d6f46a8dcc2869d32",
+ "sha256:92c85ac42f41ffdc35b6da57ed991575bdbe69db895507af88b9f499b701c188",
+ "sha256:b24086f2375c4a094a6b51e78b4cf7ca16c721dcee2eddd7aa6494b42d6d519d",
+ "sha256:cb925555f43060a1745d0a321cca94bcea927c50114b623d73179189a4e100ac"
+ ],
+ "markers": "python_version < '3.5'",
+ "version": "==1.10.0"
+ },
+ "singledispatch": {
+ "hashes": [
+ "sha256:5b06af87df13818d14f08a028e42f566640aef80805c3b50c5056b086e3c2b9c",
+ "sha256:833b46966687b3de7f438c761ac475213e53b306740f1abfaa86e1d1aae56aa8"
+ ],
+ "markers": "python_version < '3.4'",
+ "version": "==3.4.0.3"
+ },
+ "six": {
+ "hashes": [
+ "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
+ "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
+ ],
+ "version": "==1.14.0"
+ },
+ "urllib3": {
+ "hashes": [
+ "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
+ "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
+ ],
+ "version": "==1.25.8"
+ },
+ "wcwidth": {
+ "hashes": [
+ "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603",
+ "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"
+ ],
+ "version": "==0.1.8"
+ },
+ "wrapt": {
+ "hashes": [
+ "sha256:0ec40d9fd4ec9f9e3ff9bdd12dbd3535f4085949f4db93025089d7a673ea94e8"
+ ],
+ "version": "==1.12.0"
+ },
+ "zipp": {
+ "hashes": [
+ "sha256:c70410551488251b0fee67b460fb9a536af8d6f9f008ad10ac51f615b6a521b1",
+ "sha256:e0d9e63797e483a30d27e09fffd308c59a700d365ec34e93cc100844168bf921"
+ ],
+ "version": "==1.2.0"
+ }
+ }
+}
diff --git a/Packs/BigFix/Integrations/integration-BigFix_README.md b/Packs/BigFix/Integrations/BigFix/README.md
similarity index 98%
rename from Packs/BigFix/Integrations/integration-BigFix_README.md
rename to Packs/BigFix/Integrations/BigFix/README.md
index 679f898881b..447cfec342c 100644
--- a/Packs/BigFix/Integrations/integration-BigFix_README.md
+++ b/Packs/BigFix/Integrations/BigFix/README.md
@@ -425,6 +425,24 @@
Retrieves all endpoints (computers).
Base Command
bigfix-get-endpoints
+
Input
+
+
+
+
Argument Name
+
Description
+
Required
+
+
+
+
+
get_endpoint_details
+
Whether to get endpoint full details of each endpoint or just basic details, such as ID and last reported time. We recommend setting this to false if there are many endpoints to retrieve.
+
Optional
+
+
+
+
Context Output
diff --git a/Packs/BigFix/Integrations/integration-BigFix.yml b/Packs/BigFix/Integrations/integration-BigFix.yml
deleted file mode 100644
index d5f2f300d9b..00000000000
--- a/Packs/BigFix/Integrations/integration-BigFix.yml
+++ /dev/null
@@ -1,1205 +0,0 @@
-commonfields:
- id: BigFix
- version: -1
-name: BigFix
-display: BigFix
-category: Vulnerability Management
-image: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYQAAACCCAMAAABxTU9IAAAAw1BMVEX///9vmb0AAACrxFCRkZFqlrt5n8FYWFhUVFQgICCGhoZnlLpkkrmmvtRolbq1tbXU1NRnZ2cvLy9NTU15eXmcnJzj4+PNzc3y8vL3+fvs7OyNjY2OrsqlpaXG1eO1ydt/pMTr8PU3NzemwUK/v7/U3+qBgYEMDAzb29sVFRWurq5GRkbM2uZhYWHe5++qwdaUss3k7MzZ5LYyMjInJyfD1Yi0ymbz9+ivx1nf6MHT4KrK2phxcXHu89/k7Mv4+vG80HmFQn/9AAAKLUlEQVR4nO2da3vaOBCFcRJys6FpmoQADpcADSEE6L1N2m3//69aA5awrTNjyzbhaTrvh93WlWVbR5qRRhcqFUEQBEEQBEEQBEEQSqQ/6nRrC9/v+f5i3p3Omrt+oX+OTs/zXNetKoI/e1W/O9v1e/1T1FzHpFr3XL8jLeKlqFWBCCshXM+f7vrt/hFIEVY6OF1pDi8AJ0JA3a2JDFsnRQTHcevzXb/jqydVhEAGR3zDdskgguN4vtik8qiZV7KIELhoaQwlMXMc41o2EYLGYOon5KDjVQ+Ni1lFcOqHYpKKU/Mcp4AIgUnq7+CtXxeLZXyiiAiBSZKIUjH8VYyomAiON9rBm78eFus4XUERRIUihBoUFkEsUn7mKl5dWASnKt45Hx1PFWEJIjjSU83DTGtQgghO1d/BJ/z1NCMlWIIIjtfdwUf87fiRYi5DBHHO9mwcQlkigAiUwNKMakCIsFxisVpwsfpfugauBPPsWMQKFYgQFLvfHfXXfZ7+rFM7dNECjBhikKyYxRoCEmFhDIL7nV5Ke6j2XuLdXw2H8dIzRcDMFrwMnszxZGeasCxZRQiag+/h8rfNSDgsUHZTh2kMXmdr7/zaSDYEuwrc9BkPLU0hK71kXbYsujltklwJamdjZtRk2/rbIVXYVQip2bq6au3m0fkwR8PWRoRWweNj2sP2QZzLm+EZU3o3q0TtIZdno/2wF3J0MGYSHhBcGikn+LGt0/UN7Sv4UWY+NGVY8i6lQp2P473fQ9yTr/9unaBN53iZzGtCJr2DDw9uoR57kLx+FN5xG73YUvmwVSXOyCy/HO60VidU6LG3nRLlsNfA6Y+J0lAMQVafqdZwQjz7s5HyiHhsU91yYybe+8l+eRwQm8vTpzG8ewi/AoYUYQ9X4BQRLnBeRJUsLkLljbrnWl8ampfSSQ4ScorQJwySyw4VaBH2zlB6XoR3VF64XZUggnqhvbf6yn145Q333QlmoPBy9e67eLjA948YEc5RelaEIzozWCspEUyfQItwndRZfdER99lJusCY5xtigSa1gpttDl/5YXKz5rL9U9UkaEQ4EQ50KX66bLRaravJsb7yiG4IRXhsn8Yxc6dFqEzCJ9yt/3qlnmjVQV4AW55PhA5uCmxAOxThbfSaKkzk2BgRxurz7zbqnenKjpzzCS12AkaEiuoPn67+piziDUpKgupvzmADbgqsU0AiVB7D5gHSMyKowniItrzmeXj1AtwRipDBeHMi6Lq/HCwoR43enaaJ/Cktwq+PT09P//3G/4i9QpWbYIMiNNYXT0B6WoTwpqQr0Z12kFk5IuiRyW2ky3oFU1KMUMERIvx+2h+s+fMB/XsTi9BjHg9FGOuPMqBFeCTsTnt19d0FcM0liVC5DR890V1kchyDgYYci/BjMNhXDP6g1uDjsQLzeK4lWPkEVQeNm1rH7SFRL8sSQXsjNURAbZhjnlmE540EK76ZSbBr9pjuERQhrE9WvSP1/XB0QVCWCGFr22DzEktQ5wiK8JTQYH9gtgU8YONieEiE0LmZvfUKI8J75iaK0kSofI5pgDoBLNCCABE+JDXY3/9qpoL9I5fpo4YiHLWulozHZ42J6uTBniMpwu36H+B4gKI8Ec5iIti8wwoY8gEiGBIETeGjkQpKyu1WoEfMOJBKihDexYRXTUIRTs6jnCBN0kSIBa0sgqchPVR3TRF+mA0hwEgGF+px+2opEe6I6kmJoPyy1RgJhy1Q5DBVBB0xSvq3TEADYorwFWkwMDqqKAbCjtYoEajCpERQw4FNCV4/vIvyAAqwTBF07PTOJngakk2E37AhDJ6T6WD3KI8IlAwWIiTyA96yTBF+bl+EX1CE/T/JdFPUPconAg5DUiKoIt+VCJHJJKu+wZpsPgH0jZZ8T6YDs3SZfML97YpP5/eR0kAqpDnmjTvPLQJqgqkiRGdKbSYS1mTromYVwVjBtBIhvXcUdWbjA6Y40kTYDJizi3AwPouCYtBpIsRn9KwNEhysGYEGwhwZIwXoE7hYNhwxM0E3UoRwdLGJXmYXofg4oRF/lNV8zhLYqawmAw3YMe8bjhnGUW1HzJuAsDkrSYqgms+mFjbWnB1vX4SoEc2YYwxYbObkPO6i/pdMhscJtrEjZuhFitAgv//t1kVQ63butSG13LoKrbg5xv2ImsLAyC3j0G8DIcI5VXB0KDv8fHM2Zesi6JjFUAe1LXtIaJ7fcc0VW0CEwVMyURPkxc/0EyLc24twoUsiwdZFUNG7o2hQOz3PCHBmDZQbiFsYfSOsaJU7oRCLoDzzqZGeFkF/f7J387hlEXQc+zr5l+zA0ZrhmSuVL0kVBuaEAnYw1nPMevhpFg8zx6yWVpzEVVDFsi0RtPjrQZ5qFnYRJOhL0T6nhAqDX2YS6BLYJcFIhKbudZvViRFBT7hHS3WiOy7bEkGtJAidke6tWhkk2LWHdjw6rTP4CibWoDXitzOrdUc3l4pTdrEQt+5osxT48+VZ67p1NYyOobYkgu4QqSlUpgbRWMyGfXserKaZB4PvP1BWeMyx4J7OxI7gkgV2Bd4jl5npX8oQQTc/HS/RCy6sDBKeDSPWqXx4ev7y/BFYogq12CL3WlTO8BMddmYd5HuQvAQRbsPsIwttdCzPxiDNYeCCG2FRGWERcq7KxjUpZVU23u4QOGu4Iri4CNoCRhfa6AZpYZCwJWeXbEGwXUvZL0WLgMs5bX9CA0VGT4h9IoVF0MYoZut01MrGIOHVi9ZnIuBFR7w1IqvuBbFUKH2nzptPiaweif0mOv6cQQRip86tUjl+Wa0RtjFIcE7SekEqsW0NjDiiJPesBd2jmwm1WKuSbc/a+PIo7Jje3Z4OGZsQbjjLsEoIP3Z8SuSgPyc9ZwWxvaNuZZD6xO4Etm+0Pa6XK2j+qu2beE7Bbjv+IZGHHPSSEWKQZXO8KbEKVU7Cyw5RhNlVWBB7N+WU2uxQTYGdo4/gExrIgUc2UE0h05mOfcIfiEewg9r/GrSF1J9omZIHT+2qa/S3Qm7Id6r8cKu5oI944TYmCACyJIPG0CPda7Pr0UdOpQyWBQO4dk5X6R500P25RzYg8cp5wDFQLUO1Noobl36357G3yM+65IA6IURVbM/rLbrT0Ww2mnbmvuPV+fRyEmQe+umH/q7PCHY9t5qaVo4HzgfrFiyReEVe6EPUrDWQX1vLDXmImq0Gjjjl/DCHa1qJIBoUoQwVqqJBQYpbJPlNo+LAvX8WuL1df8FrYMadQJ6K/BZwObAnkPOkhFwFC7pZfjIH4PbEJZdHv5ejMVTBBh+hCB2HiVJDPF+aQdk05+k/HxWVgJ75EQrQrHFzNjFD5B1K4HpbNLsOM32pcD1fWsFWGS08tqvkeodd8QXbZ1Rz4DRO1XW9nijwYvQ7td6yzN36cmZt+Z/AXSy6M4kSvTTN2ajTXdGZzvpS/oIgCIIgCIIgCIIgCIIgCIIgCIIgCK+f/wFFC7Z9cbrtAAAAAABJRU5ErkJggg==
-description: IBM BigFix Patch provides an automated, simplified patching process that
- is administered from a single console.
-configuration:
-- display: Server url (e.g https://192.168.10.1:52311)
- name: url
- defaultvalue: ""
- type: 0
- required: true
-- display: Username
- name: credentials
- defaultvalue: ""
- type: 9
- required: true
-- display: Trust any certificate (not secure)
- name: unsecure
- defaultvalue: ""
- type: 8
- required: false
-- display: Use system proxy settings
- name: proxy
- defaultvalue: ""
- type: 8
- required: false
-script:
- script: |
- import requests
- requests.packages.urllib3.disable_warnings()
-
- BASE_URL = demisto.params().get('url')
- VERIFY_CERTIFICATE = not demisto.params().get('unsecure')
-
- USERNAME = demisto.params()['credentials']['identifier']
- PASSWORD = demisto.params()['credentials']['password']
-
- if not demisto.params()['proxy']:
- del os.environ['HTTP_PROXY']
- del os.environ['HTTPS_PROXY']
- del os.environ['http_proxy']
- del os.environ['https_proxy']
-
- def get_first(iterable, default=None):
- if iterable:
- for item in iterable:
- return item
- return default
-
- def get_sites():
- fullurl = BASE_URL + '/api/sites'
- res = requests.get(
- fullurl,
- auth=(USERNAME, PASSWORD),
- verify=VERIFY_CERTIFICATE
- )
-
- if res.status_code < 200 or res.status_code >= 300:
- return_error('Failed to get sites.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(fullurl, res.status_code, res.content))
-
- raw_sites = json.loads(xml2json(res.content))
-
- if (not raw_sites or not raw_sites.has_key('BESAPI')):
- return []
-
- sites = []
- master_sites = demisto.get(raw_sites, 'BESAPI.ActionSite')
-
- if master_sites and not isinstance(master_sites, list):
- master_sites = [master_sites]
- if master_sites:
- for idx, site in enumerate(master_sites):
- master_sites[idx]['Type'] = 'master'
- master_sites[idx]['Resource'] = master_sites[idx]['@Resource']
- del master_sites[idx]['@Resource']
- else:
- master_sites = []
-
-
- external_sites = demisto.get(raw_sites, 'BESAPI.ExternalSite')
- if external_sites and not isinstance(external_sites, list):
- external_sites = [external_sites]
- if external_sites:
- for idx, site in enumerate(external_sites):
- external_sites[idx]['Type'] = 'external'
- external_sites[idx]['Resource'] = external_sites[idx]['@Resource']
- del external_sites[idx]['@Resource']
- else:
- external_sites = []
-
-
- operator_sites = demisto.get(raw_sites, 'BESAPI.OperatorSite')
- if operator_sites and not isinstance(operator_sites, list):
- operator_sites = [operator_sites]
- if operator_sites:
- for idx, site in enumerate(operator_sites):
- operator_sites[idx]['Type'] = 'operator'
- operator_sites[idx]['Resource'] = operator_sites[idx]['@Resource']
- del operator_sites[idx]['@Resource']
- else:
- operator_sites = []
-
-
- custom_sites = demisto.get(raw_sites, 'BESAPI.CustomSite')
- if custom_sites and not isinstance(custom_sites, list):
- custom_sites = [custom_sites]
-
- if custom_sites:
- for idx, site in enumerate(custom_sites):
- custom_sites[idx]['Type'] = 'custom'
- custom_sites[idx]['Resource'] = custom_sites[idx]['@Resource']
- del custom_sites[idx]['@Resource']
- else:
- custom_sites = []
-
- sites = master_sites + external_sites + operator_sites + custom_sites
- for idx, site in enumerate(sites):
- site_details = get_site(site['Type'], site['Name'])
- sites[idx] = site_details
-
- return sites
-
- def get_sites_command():
- sites = get_sites()
- demisto.results({
- 'Type': entryTypes['note'],
- 'ContentsFormat': formats['json'],
- 'Contents': sites,
- 'HumanReadable': tableToMarkdown('BigFix Sites', sites, ['Name', 'Type', 'GatherURL', 'Description', 'GlobalReadPermissions', 'Subscription']),
- 'EntryContext': {
- 'Bigfix.Site(val.Resource==obj.Resource)': sites
- }
- })
-
-
- def get_site(site_type, site_name):
- fullurl = BASE_URL + '/api/site/' + site_type
- if site_type != 'master':
- # if site name is not empty the add to url
- fullurl += '/' + site_name
-
- res = requests.get(
- fullurl,
- auth=(USERNAME, PASSWORD),
- verify=VERIFY_CERTIFICATE
- )
-
- if res.status_code < 200 or res.status_code >= 300:
- return_error('Failed to get site {}.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(site_name, fullurl, res.status_code, res.content))
-
- raw_site = json.loads(xml2json(res.content))
-
- if (not raw_site or not raw_site.has_key('BES')):
- return None
-
- site = None
- if site_type == 'master':
- site = demisto.get(raw_site, 'BES.ActionSite')
- elif site_type == 'external':
- site = demisto.get(raw_site, 'BES.ExternalSite')
- elif site_type == 'custom':
- site = demisto.get(raw_site, 'BES.CustomSite')
- elif site_type == 'operator':
- site = demisto.get(raw_site, 'BES.OperatorSite')
-
- if site is not None:
- site['Type'] = site_type
- site['Resource'] = BASE_URL + '/api/site/{}/{}'.format(site_type, site_name)
-
- return site
-
- def get_site_command():
- site_name = demisto.args().get('site_name')
- site_type = demisto.args().get('site_type')
- site = get_site(site_type, site_name)
-
- if site is None:
- demisto.results('No site found')
- sys.exit(0)
-
- demisto.results({
- 'Type': entryTypes['note'],
- 'ContentsFormat': formats['json'],
- 'Contents': site,
- 'HumanReadable': tableToMarkdown('BigFix Site: {} - {}'.format(site_type, site_name) , [site], ['Name', 'Type', 'GatherURL', 'Description', 'GlobalReadPermissions', 'Subscription']),
- 'EntryContext': {
- 'Bigfix.Site(val.Resource==obj.Resource)': site
- }
- })
-
-
- def get_endpoints():
- fullurl = BASE_URL + '/api/computers'
-
- res = requests.get(
- fullurl,
- auth=(USERNAME, PASSWORD),
- verify=VERIFY_CERTIFICATE
- )
-
- if res.status_code < 200 or res.status_code >= 300:
- return_error('Failed to get endpoints.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(fullurl, res.status_code, res.content))
-
- raw_endpoints = json.loads(xml2json(res.content))
- if (not raw_endpoints or not raw_endpoints.has_key('BESAPI')):
- return None
-
- raw_endpoints = demisto.get(raw_endpoints, 'BESAPI.Computer')
- if raw_endpoints and not isinstance(raw_endpoints, list):
- raw_endpoints = [raw_endpoints]
-
- for idx, endpoint in enumerate(raw_endpoints):
- raw_endpoints[idx]['Resource'] = raw_endpoints[idx]['@Resource']
- del raw_endpoints[idx]['@Resource']
-
- endpoints_with_details = []
- for raw_endpoint in raw_endpoints:
- endpoint = get_endpoint_details(raw_endpoint.get('ID'))
- endpoints_with_details.append(endpoint)
-
- return endpoints_with_details
-
- def get_endpoints_command():
- endpoints = get_endpoints()
- demisto.results({
- 'Type': entryTypes['note'],
- 'ContentsFormat': formats['json'],
- 'Contents': endpoints,
- 'HumanReadable': tableToMarkdown('BigFix Computers' , endpoints, headers = [
- 'ID',
- 'Resource',
- 'LastReportTime',
- 'ActiveDirectoryPath',
- 'AgentType',
- 'AgentVersion',
- 'BESRelaySelectionMethod',
- 'BESRelayServiceInstalled',
- 'BESRootServer',
- 'BIOS',
- 'CPU',
- 'ClientSettings',
- 'ComputerName',
- 'ComputerType',
- 'DNSName',
- 'DeviceType',
- 'DistancetoBESRelay',
- 'FreeSpaceonSystemDrive',
- 'IPAddress',
- 'LicenseType',
- 'Locked',
- 'OS',
- 'RAM',
- 'Relay',
- 'RelayNameOfClient',
- 'SubnetAddress',
- 'SubscribedSites',
- 'TotalSizeofSystemDrive',
- 'UserName'
- ]),
- 'EntryContext': {
- 'Bigfix.Endpoint(val.ID==obj.ID)': endpoints
- }
- })
-
- def get_endpoint_details(computer_id):
- fullurl = BASE_URL + '/api/computer/{}'.format(computer_id)
- res = requests.get(
- fullurl,
- auth=(USERNAME, PASSWORD),
- verify=VERIFY_CERTIFICATE
- )
-
- if res.status_code < 200 or res.status_code >= 300:
- return_error(
- 'Failed to get computer {}.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(computer_id, fullurl, res.status_code, res.content)
- )
-
- raw_endpoint = json.loads(xml2json(res.content))
- if (not raw_endpoint or not raw_endpoint.has_key('BESAPI')):
- return None
-
- raw_endpoint = demisto.get(raw_endpoint, 'BESAPI.Computer')
-
- endpoint = {
- 'ID': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "ID")=val["#text"]')),
- 'Resource': demisto.get(raw_endpoint, '@Resource'),
- 'LastReportTime': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Last Report Time")=val["#text"]')),
- 'ActiveDirectoryPath': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Active Directory Path")=val["#text"]')),
- 'AgentType': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Agent Type")=val["#text"]')),
- 'AgentVersion': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Agent Version")=val["#text"]')),
- 'BESRelaySelectionMethod': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "BES Relay Selection Method")=val["#text"]')),
- 'BESRelayServiceInstalled': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "BES Relay Selection Method")=val["#text"]')),
- 'BESRootServer': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "BES Root Server")=val["#text"]')),
- 'BIOS': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "BIOS")=val["#text"]')),
- 'CPU': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "CPU")=val["#text"]')),
- 'ClientSettings': demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Client Settings")=val["#text"]'),
- 'ComputerName': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Computer Name")=val["#text"]')),
- 'ComputerType': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Computer Type")=val["#text"]')),
- 'DNSName': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "DNS Name")=val["#text"]')),
- 'IPAddress': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "IP Address")=val["#text"]')),
- 'DeviceType': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Device Type")=val["#text"]')),
- 'DistancetoBESRelay': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Distance to BES Relay")=val["#text"]')),
- 'FreeSpaceonSystemDrive': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Free Space on System Drive")=val["#text"]')),
- 'LicenseType': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "License Type")=val["#text"]')),
- 'Locked': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Locked")=val["#text"]')),
- 'OS': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "OS")=val["#text"]')),
- 'RAM': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "RAM")=val["#text"]')),
- 'Relay': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Relay")=val["#text"]')),
- 'RelayNameOfClient': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Relay Name of Client")=val["#text"]')),
- 'SubnetAddress': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Subnet Address")=val["#text"]')),
- 'SubscribedSites': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Subscribed Sites")=val["#text"]')),
- 'TotalSizeofSystemDrive': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Total Size of System Drive")=val["#text"]')),
- 'UserName': get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "User Name")=val["#text"]'))
- }
-
- return endpoint
-
- def get_endpoint_details_command():
- computer_id = demisto.args().get('computer_id')
-
- endpoint = get_endpoint_details(computer_id)
- if endpoint is None:
- demisto.results('Endpoint with id {} was not found'.format(computer_id))
- sys.exit(0)
-
- markdown = tableToMarkdown('BigFix Endpoint {}'.format(computer_id) , [endpoint], headers = [
- 'ID',
- 'Resource',
- 'LastReportTime',
- 'ActiveDirectoryPath',
- 'AgentType',
- 'AgentVersion',
- 'BESRelaySelectionMethod',
- 'BESRelayServiceInstalled',
- 'BESRootServer',
- 'BIOS',
- 'CPU',
- 'ClientSettings',
- 'ComputerName',
- 'ComputerType',
- 'DNSName',
- 'DeviceType',
- 'DistancetoBESRelay',
- 'FreeSpaceonSystemDrive',
- 'IPAddress',
- 'LicenseType',
- 'Locked',
- 'OS',
- 'RAM',
- 'Relay',
- 'RelayNameOfClient',
- 'SubnetAddress',
- 'SubscribedSites',
- 'TotalSizeofSystemDrive',
- 'UserName'
- ])
-
- demisto.results({
- 'Type': entryTypes['note'],
- 'ContentsFormat': formats['json'],
- 'Contents': endpoint,
- 'HumanReadable': markdown,
- 'EntryContext': {
- 'Bigfix.Endpoint(val.ID==obj.ID)': endpoint
- }
- })
-
-
-
- def get_patches(site_type='', site_name=''):
- fullurl = BASE_URL + '/api/fixlets/{}'.format(site_type)
- if site_type != 'master':
- # if site name is not empty the add to url
- fullurl += '/' + site_name
-
- res = requests.get(
- fullurl,
- auth=(USERNAME, PASSWORD),
- verify=VERIFY_CERTIFICATE
- )
-
- if res.status_code < 200 or res.status_code >= 300:
- return_error(
- 'Failed to get patches. Request URL: {}\nStatusCode: {}\nResponse Body: {}'.format(fullurl, res.status_code, res.content)
- )
-
- raw_patches = json.loads(xml2json(res.content))
- if (not raw_patches or not raw_patches.has_key('BESAPI')):
- return None
-
- raw_patches = demisto.get(raw_patches, 'BESAPI.Fixlet')
- if raw_patches and not isinstance(raw_patches, list):
- raw_patches = [raw_patches]
-
- patches_with_details = []
- for raw_patch in raw_patches:
- patch = get_patch_details(site_type, site_name, raw_patch.get('ID'))
- patch['LastModified'] = raw_patch['@LastModified']
- patches_with_details.append(patch)
-
- return patches_with_details
-
-
- def get_patches_command():
- site_name = demisto.args().get('site_name')
- site_type = demisto.args().get('site_type')
- patches = get_patches(site_type, site_name)
-
- markdown = tableToMarkdown('BigFix Patches' , patches, headers = [
- 'ID',
- 'Name',
- 'Description',
- 'LastModified',
- 'Resource',
- 'Relevance',
- 'Category',
- 'DownloadSize',
- 'Source',
- 'SourceID',
- 'SourceReleaseDate',
- 'SourceSeverity',
- 'ActionID',
- 'ActionScript'
- ])
-
- demisto.results({
- 'Type': entryTypes['note'],
- 'ContentsFormat': formats['json'],
- 'Contents': patches,
- 'HumanReadable': markdown,
- 'EntryContext': {
- 'Bigfix.Patch(val.ID==obj.ID)': patches
- }
- })
-
-
- def get_patch_details(site_type, site_name, patch_id):
- fullurl = ''
- if site_type == 'master':
- fullurl = BASE_URL + '/api/fixlet/master/{}'.format(patch_id)
- else:
- fullurl = BASE_URL + '/api/fixlet/{}/{}/{}'.format(site_type, site_name, patch_id)
-
- res = requests.get(
- fullurl,
- auth=(USERNAME, PASSWORD),
- verify=VERIFY_CERTIFICATE
- )
-
- if res.status_code < 200 or res.status_code >= 300:
- return_error('Failed to get patch/fixlet {}. Request URL: {}\nStatusCode: {}\nResponse Body: {}'.format(patch_id, fullurl, res.status_code, res.content))
-
- raw_patch = json.loads(xml2json(res.content))
- if (not raw_patch or not raw_patch.has_key('BES')):
- return None
-
- raw_patch = demisto.get(raw_patch, 'BES.Fixlet')
- patch = {
- 'ID': patch_id,
- 'Name': demisto.get(raw_patch, 'Title'),
- 'Resource': fullurl,
- 'Description': demisto.get(raw_patch, 'Description'),
- 'Relevance': demisto.get(raw_patch, 'Relevance'),
- 'Category': demisto.get(raw_patch, 'Category'),
- 'DownloadSize': demisto.get(raw_patch, 'DownloadSize'),
- 'Source': demisto.get(raw_patch, 'Source'),
- 'SourceID': demisto.get(raw_patch, 'SourceID'),
- 'SourceReleaseDate': demisto.get(raw_patch, 'SourceReleaseDate'),
- 'SourceSeverity': demisto.get(raw_patch, 'SourceSeverity'),
- 'ActionID': demisto.get(raw_patch, 'DefaultAction.@ID'),
- 'ActionScript': demisto.get(raw_patch, 'DefaultAction.ActionScript')
- }
-
- return patch
-
- def get_patch_details_command():
- site_type = demisto.args().get('site_type')
- site_name = demisto.args().get('site_name')
- patch_id = demisto.args().get('id')
-
- patch = get_patch_details(site_type, site_name, patch_id)
- markdown = tableToMarkdown('BigFix Patch {}'.format(patch_id) , [patch], headers = [
- 'ID',
- 'Name',
- 'Resource',
- 'Description',
- 'Relevance',
- 'Category',
- 'DownloadSize',
- 'Source',
- 'SourceID',
- 'SourceReleaseDate',
- 'SourceSeverity',
- 'ActionID',
- 'ActionScript'
- ])
-
- demisto.results({
- 'Type': entryTypes['note'],
- 'ContentsFormat': formats['json'],
- 'Contents': patch,
- 'HumanReadable': markdown,
- 'EntryContext': {
- 'Bigfix.Patch(val.ID==obj.ID)': patch
- }
- })
-
- def deploy_patch(site_name, computer_ids, fixlet_id, action_id):
- target = ''
- if 'all' in computer_ids:
- target = 'true'
- else:
- target = '\n'.join(['{}'.format(computer_id) for computer_id in computer_ids])
-
- request_body = """
-
-
-
- {}
- {}
- {}
-
-
- {}
-
- 1000
-
-
- """.format(site_name, fixlet_id, action_id, target)
- LOG('deploy_patch - request: ' + request_body)
-
- fullurl = BASE_URL + '/api/actions'
- res = requests.post(
- fullurl,
- auth=(USERNAME, PASSWORD),
- verify=VERIFY_CERTIFICATE,
- data=request_body
- )
-
- LOG('deploy_patch - raw response: ' + res.content)
- if res.status_code < 200 or res.status_code >= 300:
- return_error('Failed to deploy patch {}.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(fixlet_id, fullurl, res.status_code, res.content))
-
- raw_action = json.loads(xml2json(res.content))
- if (not raw_action or not raw_action.has_key('BESAPI')):
- return None
-
- raw_action = demisto.get(raw_action, 'BESAPI.Action')
- raw_action['FixletID'] = fixlet_id
- raw_action['ComputerIDs'] = computer_ids
- raw_action['SiteName'] = site_name
- raw_action['Resource'] = raw_action['@Resource']
- del raw_action['@Resource']
- if 'all' in computer_ids:
- raw_action['AllComputers'] = True
- del raw_action['ComputerIDs']
-
- return raw_action
-
- def deploy_patch_command():
- site_name = demisto.args().get('site_name')
- all_computers = False
- computer_ids = argToList(demisto.args().get('computer_ids'))
-
- fixlet_id = demisto.args().get('fixlet_id')
- action_id = demisto.args().get('action_id')
-
- action = deploy_patch(site_name, computer_ids, fixlet_id, action_id)
-
- markdown = tableToMarkdown('BigFix Action {}'.format(action_id) , [action], headers = [
- 'ID',
- 'Name',
- 'FixletID',
- 'ComputerIDs',
- 'SiteName',
- 'Resource'
- ])
-
- demisto.results({
- 'Type': entryTypes['note'],
- 'ContentsFormat': formats['json'],
- 'Contents': action,
- 'HumanReadable': markdown,
- 'EntryContext': {
- 'Bigfix.Action(val.ID==obj.ID)': action
- }
- })
-
-
- def action_delete(action_id):
- res = requests.delete(
- BASE_URL + '/api/action/' + action_id,
- auth=(USERNAME, PASSWORD),
- verify=VERIFY_CERTIFICATE
- )
-
- if res.status_code < 200 or res.status_code >= 300:
- return_error('Failed to delete action {}.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(action_id, fullurl, res.status_code, res.content))
-
-
- def action_delete_command():
- action_id = demisto.args().get('action_id')
-
- action_delete(action_id)
-
- demisto.results('Action {} was deleted successfully'.format(action_id))
-
-
- def get_action_status(action_id):
- fullurl = BASE_URL + '/api/action/' + action_id + '/status'
- res = requests.get(
- fullurl,
- auth=(USERNAME, PASSWORD),
- verify=VERIFY_CERTIFICATE
- )
-
- if res.status_code < 200 or res.status_code >= 300:
- return_error('Failed to get action {} status.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(action_id, fullurl, res.status_code, res.content))
-
- raw_action = json.loads(xml2json(res.content))
- if (not raw_action or not raw_action.has_key('BESAPI')):
- return None
-
- raw_action = demisto.get(raw_action, 'BESAPI.ActionResults')
- return raw_action.get('Status')
-
- def get_action_status_command():
- action_id = demisto.args().get('action_id')
-
- status = get_action_status(action_id)
-
- output = {
- 'ID': action_id,
- 'Status': status
- }
- demisto.results({
- 'Type': entryTypes['note'],
- 'ContentsFormat': formats['json'],
- 'Contents': output,
- 'HumanReadable': 'Action {} status is {}'.format(action_id, status),
- 'EntryContext': {
- 'Bigfix.Action(val.ID==obj.ID)': output
- }
- })
-
- def action_stop(action_id):
- res = requests.post(
- BASE_URL + '/api/action/' + action_id + '/stop',
- auth=(USERNAME, PASSWORD),
- verify=VERIFY_CERTIFICATE
- )
-
- if res.status_code < 200 or res.status_code >= 300:
- return_error('Failed to stop action {}.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(action_id, fullurl, res.status_code, res.content))
-
-
- def action_stop_command():
- action_id = demisto.args().get('action_id')
-
- action_stop(action_id)
-
- demisto.results('Action {} was stopped successfully'.format(action_id))
-
- def query(relevance):
- fullurl = BASE_URL + '/api/query'
- params = {
- 'relevance': relevance
- }
- res = requests.get(
- fullurl,
- auth=(USERNAME, PASSWORD),
- verify=VERIFY_CERTIFICATE,
- params=params
- )
-
- if res.status_code < 200 or res.status_code >= 300:
- return_error('Query failed.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}'.format(fullurl, res.status_code, res.content))
-
- raw_action = json.loads(xml2json(res.content))
- if (not raw_action or not raw_action.has_key('BESAPI')):
- demisto.info('BigFix query has incorrect response format. Response Body: {}'.format(res.content))
- return_error('The response has incorrect format. Check the logs')
-
- if demisto.get(raw_action, 'BESAPI.Query.Error'):
- error = demisto.get(raw_action, 'BESAPI.Query.Error')
- return_error(error)
-
- raw_query_results = demisto.get(raw_action, 'BESAPI.Query')
- return raw_query_results
-
- def query_command():
- relevance = demisto.args().get('relevance')
- results = query(relevance)
-
- if results == None:
- demisto.results('No results')
- sys.exit(0)
-
- output = demisto.dt(results, 'Result.Answer.#text')
- if not isinstance(output, list):
- output = [output]
-
- demisto.results({
- 'Type': entryTypes['note'],
- 'ContentsFormat': formats['json'],
- 'Contents': results,
- 'HumanReadable': tableToMarkdown('Query Results: {}'.format(relevance), output, ['Results']),
- 'EntryContext': {
- 'Bigfix.QueryResults': output
- }
- })
-
- try:
- # do requets to /api/help
- # should be good indicator for test connectivity
- def test():
- fullurl = BASE_URL + '/api/help'
- res = requests.get(
- fullurl,
- auth=(USERNAME, PASSWORD),
- verify=VERIFY_CERTIFICATE
- )
- res.raise_for_status()
-
- if demisto.command() == 'test-module':
- # do requets to /api/help
- # should be good indicator for test connectivity
- test()
- demisto.results('ok')
-
- elif demisto.command() == 'bigfix-get-sites':
- get_sites_command()
-
- elif demisto.command() == 'bigfix-get-site':
- get_site_command()
-
- elif demisto.command() == 'bigfix-get-endpoints':
- get_endpoints_command()
-
- elif demisto.command() == 'bigfix-get-endpoint':
- get_endpoint_details_command()
-
- elif demisto.command() == 'bigfix-get-patches':
- get_patches_command()
-
- elif demisto.command() == 'bigfix-get-patch':
- get_patch_details_command()
-
- elif demisto.command() == 'bigfix-deploy-patch':
- deploy_patch_command()
-
- elif demisto.command() == 'bigfix-action-delete':
- action_delete_command()
-
- elif demisto.command() == 'bigfix-action-status':
- get_action_status_command()
-
- elif demisto.command() == 'bigfix-action-stop':
- action_stop_command()
-
- elif demisto.command():
- query_command()
-
- except Exception, e:
- LOG(e.message)
- LOG.print_log()
- return_error(e.message)
- type: python
- subtype: python2
- commands:
- - name: bigfix-get-sites
- arguments: []
- outputs:
- - contextPath: Bigfix.Site
- description: Site
- type: unknown
- - contextPath: Bigfix.Site.Name
- description: Name of the site
- type: string
- - contextPath: Bigfix.Site.Description
- description: Description of the site
- type: string
- - contextPath: Bigfix.Site.Resource
- description: Link to the endpoint resource.
- type: string
- - contextPath: Bigfix.Site.Type
- description: Type of the site (master,custom,external,operator)
- type: string
- - contextPath: Bigfix.Site.Domain
- description: Site domain
- type: string
- - contextPath: Bigfix.Site.GatherURL
- description: Gather URL
- type: string
- - contextPath: Bigfix.Site.GlobalReadPermission
- description: Global Read Permission available or not.
- type: string
- description: Retrieves all the sites
- - name: bigfix-get-site
- arguments:
- - name: site_name
- description: Name of the site. If the site is external or operator then site
- must be provided
- - name: site_type
- required: true
- auto: PREDEFINED
- predefined:
- - external
- - operator
- - master
- - custom
- description: 'Type of the site. One of the following options: external,operator,master,custom'
- defaultValue: master
- outputs:
- - contextPath: Bigfix.Site
- description: Site
- type: unknown
- - contextPath: Bigfix.Site.Name
- description: Name of the site
- type: string
- - contextPath: Bigfix.Site.Description
- description: Description of the site
- type: string
- - contextPath: Bigfix.Site.Resource
- description: Link to the endpoint resource.
- type: string
- - contextPath: Bigfix.Site.Type
- description: Type of the site (master,custom,external,operator)
- type: string
- - contextPath: Bigfix.Site.Domain
- description: Site domain
- type: string
- - contextPath: Bigfix.Site.GatherURL
- description: Gather URL
- type: string
- - contextPath: Bigfix.Site.GlobalReadPermission
- description: Global Read Permission available or not.
- type: string
- description: Retrieve single site by name and type
- - name: bigfix-get-patches
- arguments:
- - name: site_type
- required: true
- auto: PREDEFINED
- predefined:
- - external
- - operator
- - master
- - custom
- description: 'Type of the site. One of the following options: external,operator,master,custom'
- defaultValue: master
- - name: site_name
- description: Name of the site. If the site is external or operator then site
- must be provided
- outputs:
- - contextPath: Bigfix.Patch.ID
- description: Patch (fixlet) ID
- type: string
- - contextPath: Bigfix.Patch.LastModified
- description: Last modified Timestamp.
- type: date
- - contextPath: Bigfix.Patch.Name
- description: Name of the Patch requested.
- type: string
- - contextPath: Bigfix.Patch.Resource
- description: The link for the patch
- type: string
- - contextPath: Bigfix.Patch.Description
- description: Description of the Patch requested.
- type: string
- - contextPath: Bigfix.Patch.Relevance
- description: Relevance of the Patch requested.
- type: string
- - contextPath: Bigfix.Patch.Category
- description: Category of the Patch requested.
- type: string
- - contextPath: Bigfix.Patch.DownloadSize
- description: Download size.
- type: string
- - contextPath: Bigfix.Patch.Source
- description: Source from where the patch is coming from.
- type: string
- - contextPath: Bigfix.Patch.SourceID
- description: Source ID of the Patch requested.
- type: string
- - contextPath: Bigfix.Patch.SourceSeverity
- description: Source Severity of the Patch requested.
- type: string
- - contextPath: Bigfix.Patch.SourceReleaseDate
- description: Source Release Date of the Patch requested.
- type: string
- - contextPath: Bigfix.Patch.ActionID
- description: Action ID of the Patch requested.
- type: string
- - contextPath: Bigfix.Patch.ActionScript
- description: Action Script of the Patch requested.
- type: string
- description: Retrieve all the patches (fixlets) of site
- - name: bigfix-get-endpoints
- arguments: []
- outputs:
- - contextPath: Bigfix.Endpoint
- description: Endpoint (computer)
- - contextPath: Bigfix.Endpoint.ID
- description: The if of the endpoint (computer ID)
- type: string
- - contextPath: Bigfix.Endpoint.Resource
- description: URL to the endpoint details
- type: string
- - contextPath: Bigfix.Endpoint.LastReportTime
- description: Last report time of the endpoint
- type: date
- - contextPath: Bigfix.Endpoint.ActiveDirectoryPath
- description: Active directory path of the endpoint device.
- type: string
- - contextPath: Bigfix.Endpoint.AgentType
- description: Agent Type of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.AgentVersion
- description: Agent Version of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.BESRelaySelectionMethod
- description: Relay selection method of the endpoint.
- - contextPath: Bigfix.Endpoint.BESRelayServiceInstalled
- description: Relay service installed of the endpoint.
- - contextPath: Bigfix.Endpoint.BESRootServer
- description: Root server of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.BIOS
- description: BIOS of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.CPU
- description: CPU of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.ClientSettings
- description: Client settings of the endpoint.
- - contextPath: Bigfix.Endpoint.ComputerName
- description: Computer name of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.ComputerType
- description: Computer Type of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.DNSName
- description: DNS Name of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.DeviceType
- description: Device Type of the endpoint device.
- type: string
- - contextPath: Bigfix.Endpoint.DistancetoBESRelay
- description: Distance to BES Relay of the endpoint.
- - contextPath: Bigfix.Endpoint.FreeSpaceonSystemDrive
- description: Free space on sytem drive of the endpoint.
- - contextPath: Bigfix.Endpoint.IPAddress
- description: IP of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.LicenseType
- description: License of the endpoint.
- - contextPath: Bigfix.Endpoint.Locked
- description: Locked of the endpoint.
- - contextPath: Bigfix.Endpoint.OS
- description: OS of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.RAM
- description: RAM of the endpoint.
- type: number
- - contextPath: Bigfix.Endpoint.Relay
- description: Relay of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.RelayNameOfClient
- description: Relay Name of the client.
- type: string
- - contextPath: Bigfix.Endpoint.SubnetAddress
- description: Subnet Address of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.SubscribedSites
- description: Subscribed sites.
- type: string
- - contextPath: Bigfix.Endpoint.TotalSizeofSystemDrive
- description: Total size of system drive.
- type: number
- - contextPath: Bigfix.Endpoint.UserName
- description: User name.
- type: string
- description: Retrieve all the endpoints (computers)
- - name: bigfix-get-endpoint
- arguments:
- - name: computer_id
- required: true
- description: Computer ID
- outputs:
- - contextPath: Bigfix.Endpoint
- description: Endpoint (computer)
- - contextPath: Bigfix.Endpoint.ID
- description: The if of the endpoint (computer ID)
- type: string
- - contextPath: Bigfix.Endpoint.Resource
- description: URL to the endpoint details
- type: string
- - contextPath: Bigfix.Endpoint.LastReportTime
- description: Last report time of the endpoint
- type: date
- - contextPath: Bigfix.Endpoint.ActiveDirectoryPath
- description: Active directory path of the endpoint device.
- type: string
- - contextPath: Bigfix.Endpoint.AgentType
- description: Agent Type of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.AgentVersion
- description: Agent Version of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.BESRelaySelectionMethod
- description: Relay selection method of the endpoint.
- - contextPath: Bigfix.Endpoint.BESRelayServiceInstalled
- description: Relay service installed of the endpoint.
- - contextPath: Bigfix.Endpoint.BESRootServer
- description: Root server of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.BIOS
- description: BIOS of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.CPU
- description: CPU of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.ClientSettings
- description: Client settings of the endpoint.
- - contextPath: Bigfix.Endpoint.ComputerName
- description: Computer name of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.ComputerType
- description: Computer Type of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.DNSName
- description: DNS Name of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.DeviceType
- description: Device Type of the endpoint device.
- type: string
- - contextPath: Bigfix.Endpoint.DistancetoBESRelay
- description: Distance to BES Relay of the endpoint.
- - contextPath: Bigfix.Endpoint.FreeSpaceonSystemDrive
- description: Free space on sytem drive of the endpoint.
- - contextPath: Bigfix.Endpoint.IPAddress
- description: IP of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.LicenseType
- description: License of the endpoint.
- - contextPath: Bigfix.Endpoint.Locked
- description: Locked of the endpoint.
- - contextPath: Bigfix.Endpoint.OS
- description: OS of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.RAM
- description: RAM of the endpoint.
- type: number
- - contextPath: Bigfix.Endpoint.Relay
- description: Relay of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.RelayNameOfClient
- description: Relay Name of the client.
- type: string
- - contextPath: Bigfix.Endpoint.SubnetAddress
- description: Subnet Address of the endpoint.
- type: string
- - contextPath: Bigfix.Endpoint.SubscribedSites
- description: Subscribed sites.
- type: string
- - contextPath: Bigfix.Endpoint.TotalSizeofSystemDrive
- description: Total size of system drive.
- type: number
- - contextPath: Bigfix.Endpoint.UserName
- description: User name.
- type: string
- description: Retrieve endpoint (computer) details
- - name: bigfix-deploy-patch
- arguments:
- - name: site_name
- required: true
- description: Name of the site. If the site is external or operator then site
- must be provided
- - name: computer_ids
- required: true
- description: Provide ids of computers to deploy the patch. Pass 'all' to deploy
- to all the computers
- isArray: true
- - name: fixlet_id
- required: true
- description: The Fixlet ID. To use the action script from the original Fixlet
- or Task Message.
- - name: action_id
- required: true
- description: The action ID. The specified action will run on target computers.
- outputs:
- - contextPath: Bigfix.Action.ID
- description: Action ID
- type: number
- - contextPath: Bigfix.Action.Name
- description: Action Name
- type: string
- - contextPath: Bigfix.Action.SiteName
- description: Site name
- type: string
- - contextPath: Bigfix.Action.ComputerIDs
- description: Computers IDs to which the patch was applied to
- - contextPath: Bigfix.Action.AllComputers
- description: true if patch was applied to all the computers
- type: boolean
- - contextPath: Bigfix.Action.Resource
- description: Link to action in bigfix
- type: string
- description: Create an action on BigFix that will run the given action from the
- given fixlet on target computers. The computerID parameter takes a comma-separated
- list of BigFix computer IDs. If no computers are given, the action will be run
- on the default computers configured on BigFix. If the action should run on all
- computers set the computerID parameter to all.
- - name: bigfix-get-patch
- arguments:
- - name: id
- required: true
- description: Fixlet id
- - name: site_type
- required: true
- auto: PREDEFINED
- predefined:
- - external
- - operator
- - master
- - custom
- description: 'Type of the site. One of the following options: external,operator,master,custom'
- - name: site_name
- description: Name of the site. If the site is external or operator then site
- must be provided
- outputs:
- - contextPath: Bigfix.Patch.ID
- description: Patch(fixlet) id
- - contextPath: Bigfix.Patch.Name
- description: Patch name
- - contextPath: Bigfix.Patch.Resource
- description: Link (URL) to the patch
- - contextPath: Bigfix.Patch.Description
- description: Description
- - contextPath: Bigfix.Patch.Relevance
- description: Relevance of the Patch requested.
- - contextPath: Bigfix.Patch.Category
- description: Category of the Patch requested.
- type: string
- - contextPath: Bigfix.Patch.DownloadSize
- description: Download size.
- - contextPath: Bigfix.Patch.Source
- description: Source from where the patch is coming from.
- - contextPath: Bigfix.Patch.SourceID
- description: Source ID of the Patch requested.
- - contextPath: Bigfix.Patch.SourceSeverity
- description: Source Severity of the Patch requested.
- - contextPath: Bigfix.Patch.SourceReleaseDate
- description: Source Release Date of the Patch requested.
- - contextPath: Bigfix.Patch.ActionID
- description: Action ID of the Patch requested.
- type: string
- - contextPath: Bigfix.Patch.ActionScript
- description: Action Script of the Patch requested.
- type: string
- description: Retrieve patch (fixlet) by id
- - name: bigfix-action-delete
- arguments:
- - name: action_id
- required: true
- description: Action ID
- description: 'Stops and deletes the specified action. Note: You cannot delete
- actions that are members of a Multiple Action Group. This note applies to IBM
- BigFix V9.2 and later.'
- - name: bigfix-action-status
- arguments:
- - name: action_id
- required: true
- description: Action ID
- outputs:
- - contextPath: Bigfix.Action.ID
- description: Action ID
- type: string
- - contextPath: Bigfix.Action.Status
- description: Action Status (e.g Open, Stopped)
- type: string
- description: Gets the status of an action against it's targets.
- - name: bigfix-action-stop
- arguments:
- - name: action_id
- required: true
- description: Action ID
- description: Stops the specified action.
- - name: bigfix-query
- arguments:
- - name: relevance
- required: true
- description: 'Relevance query (example: names of bes computers)'
- outputs:
- - contextPath: Bigfix.QueryResults
- description: The results of the query
- type: unknown
- description: Evaluate a relevance expression and get the result. This runs request
- is processed through the server to WebReports.
- runonce: false
diff --git a/Packs/BigFix/ReleaseNotes/1_0_1.md b/Packs/BigFix/ReleaseNotes/1_0_1.md
new file mode 100644
index 00000000000..9c876bfe798
--- /dev/null
+++ b/Packs/BigFix/ReleaseNotes/1_0_1.md
@@ -0,0 +1,3 @@
+#### Integrations
+##### __BigFix__
+- Added **get_endpoints_details** argument to the ***bigfix-get-endpoints*** command to set if details of endpoints should be retrieved or not.
diff --git a/Packs/BigFix/TestPlaybooks/playbook-BigFixTest.yml b/Packs/BigFix/TestPlaybooks/playbook-BigFixTest.yml
index 409b3b37046..040dbdcde60 100644
--- a/Packs/BigFix/TestPlaybooks/playbook-BigFixTest.yml
+++ b/Packs/BigFix/TestPlaybooks/playbook-BigFixTest.yml
@@ -1,31 +1,40 @@
id: BigFixTest
version: -1
name: BigFixTest
-starttaskid: '0'
+starttaskid: "0"
tasks:
- '0':
- id: '0'
- taskid: 2c9d96a0-f815-4c0c-8088-29eae39ef7c9
+ "0":
+ id: "0"
+ taskid: 67df1a23-4949-410d-8480-15cd8be6e6b5
type: start
task:
- id: 2c9d96a0-f815-4c0c-8088-29eae39ef7c9
+ id: 67df1a23-4949-410d-8480-15cd8be6e6b5
version: -1
- name: ''
+ name: ""
iscommand: false
- brand: ''
- description: ''
+ brand: ""
nexttasks:
'#none#':
- - '7'
+ - "7"
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 50\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 50
+ }
+ }
note: false
- '1':
- id: '1'
- taskid: fe4946dd-7be1-4cb8-8e7c-04d791c0e022
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "1":
+ id: "1"
+ taskid: 0d4dbaf8-58e2-490f-877f-c99f72591aa0
type: regular
task:
- id: fe4946dd-7be1-4cb8-8e7c-04d791c0e022
+ id: 0d4dbaf8-58e2-490f-877f-c99f72591aa0
version: -1
name: bigfix-get-sites
description: Retrieves all the sites
@@ -35,18 +44,28 @@ tasks:
brand: BigFix
nexttasks:
'#none#':
- - '8'
- - '9'
- - '10'
+ - "8"
+ - "9"
+ - "10"
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 370\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 370
+ }
+ }
note: false
- '2':
- id: '2'
- taskid: 047cd2f3-3b2d-46dc-8be9-3622e1addc02
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "2":
+ id: "2"
+ taskid: c678d099-ac20-41f0-8ec5-eaa8acce8bf2
type: regular
task:
- id: 047cd2f3-3b2d-46dc-8be9-3622e1addc02
+ id: c678d099-ac20-41f0-8ec5-eaa8acce8bf2
version: -1
name: bigfix-get-site (default master)
description: Retrieve single site by name and type
@@ -56,21 +75,31 @@ tasks:
brand: BigFix
nexttasks:
'#none#':
- - '12'
- - '13'
- - '14'
+ - "12"
+ - "13"
+ - "14"
scriptarguments:
site_name: {}
site_type: {}
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 1420\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 1420
+ }
+ }
note: false
- '3':
- id: '3'
- taskid: 8a932779-f2b1-487a-8fce-c71ff0fe5a82
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "3":
+ id: "3"
+ taskid: 4e2ea098-248d-46bf-8c96-6680ef0aa9de
type: regular
task:
- id: 8a932779-f2b1-487a-8fce-c71ff0fe5a82
+ id: 4e2ea098-248d-46bf-8c96-6680ef0aa9de
version: -1
name: bigfix-get-site (external BES Support)
description: Retrieve single site by name and type
@@ -80,23 +109,33 @@ tasks:
brand: BigFix
nexttasks:
'#none#':
- - '16'
- - '17'
- - '18'
+ - "16"
+ - "17"
+ - "18"
scriptarguments:
site_name:
simple: BES Support
site_type:
simple: external
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 895\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 895
+ }
+ }
note: false
- '4':
- id: '4'
- taskid: f738eb65-123b-4635-899e-0e99363c98ee
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "4":
+ id: "4"
+ taskid: ebf4a1a3-f3e1-4c1f-87f6-00c7ed4e07bf
type: regular
task:
- id: f738eb65-123b-4635-899e-0e99363c98ee
+ id: ebf4a1a3-f3e1-4c1f-87f6-00c7ed4e07bf
version: -1
name: bigfix-get-patches
description: Retrieve all the patches (fixlets) of site
@@ -106,21 +145,31 @@ tasks:
brand: BigFix
nexttasks:
'#none#':
- - '21'
- - '22'
+ - "21"
+ - "22"
scriptarguments:
site_name: {}
site_type:
simple: master
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 2295\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 2295
+ }
+ }
note: false
- '5':
- id: '5'
- taskid: 355ac42b-fc75-459e-8e96-2a21872c1fe0
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "5":
+ id: "5"
+ taskid: 25fde8e9-77f9-480d-8d1c-75ca6b555c52
type: regular
task:
- id: 355ac42b-fc75-459e-8e96-2a21872c1fe0
+ id: 25fde8e9-77f9-480d-8d1c-75ca6b555c52
version: -1
name: bigfix-deploy-patch
description: Deploy patch on site
@@ -130,465 +179,643 @@ tasks:
brand: BigFix
nexttasks:
'#none#':
- - '24'
+ - "24"
scriptarguments:
action_id:
simple: Action2
computer_ids:
- simple: '3385267'
+ simple: "3385267"
fixlet_id:
- simple: '1759'
+ simple: "1759"
site_name:
simple: BES Support
site_type:
simple: master
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 2645\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 2645
+ }
+ }
note: false
- '6':
- id: '6'
- taskid: 673663a6-79c0-4bd8-8695-643da5c57840
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "6":
+ id: "6"
+ taskid: 974a014a-b2b8-43fc-8b11-32b5877bf929
type: regular
task:
- id: 673663a6-79c0-4bd8-8695-643da5c57840
+ id: 974a014a-b2b8-43fc-8b11-32b5877bf929
version: -1
name: bigfix-get-endpoints
script: BigFix|||bigfix-get-endpoints
type: regular
iscommand: true
brand: BigFix
- description: ''
nexttasks:
'#none#':
- - '23'
+ - "23"
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 1770\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 1770
+ }
+ }
note: false
- '7':
- id: '7'
- taskid: 3d9ef20e-ae32-43a9-8084-5acedc2b1041
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "7":
+ id: "7"
+ taskid: 36465eb9-089c-46db-8427-b223072c8df0
type: regular
task:
- id: 3d9ef20e-ae32-43a9-8084-5acedc2b1041
+ id: 36465eb9-089c-46db-8427-b223072c8df0
version: -1
name: DeleteContext
description: Delete field from context
scriptName: DeleteContext
type: regular
iscommand: false
- brand: ''
+ brand: ""
nexttasks:
'#none#':
- - '1'
+ - "1"
scriptarguments:
all:
- simple: yes
+ simple: "yes"
key: {}
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 195\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 195
+ }
+ }
note: false
- '8':
- id: '8'
- taskid: cb760a6b-80be-4a21-8bd9-5dd145aee5c5
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "8":
+ id: "8"
+ taskid: 58a142c0-72b6-4020-814d-7d2cbdaf4b06
type: regular
task:
- id: cb760a6b-80be-4a21-8bd9-5dd145aee5c5
+ id: 58a142c0-72b6-4020-814d-7d2cbdaf4b06
version: -1
name: VerifyContext
- description: "Verifies path in context:\n- Verifies path existence\n- If matching\
- \ object is an array: verify fields exists in each of the objects in the array\n\
- - If matching object is not an array: verify fields exists in matching object\n\
- - if 'expectedValue' is given: ensure that the given value is equal to the\
- \ context path"
+ description: |-
+ Verifies path in context:
+ - Verifies path existence
+ - If matching object is an array: verify fields exists in each of the objects in the array
+ - If matching object is not an array: verify fields exists in matching object
+ - if 'expectedValue' is given: ensure that the given value is equal to the context path
scriptName: VerifyContext
type: regular
iscommand: false
- brand: ''
+ brand: ""
nexttasks:
'#none#':
- - '11'
+ - "11"
scriptarguments:
expectedValue: {}
fields: {}
path:
simple: Bigfix.Site.Name
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 50,\n \"y\": 545\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 545
+ }
+ }
note: false
- '9':
- id: '9'
- taskid: 7b25a9e5-2290-4102-8765-14f68c31291b
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "9":
+ id: "9"
+ taskid: f4e6e81d-3c60-48e1-8450-18cead4e751b
type: regular
task:
- id: 7b25a9e5-2290-4102-8765-14f68c31291b
+ id: f4e6e81d-3c60-48e1-8450-18cead4e751b
version: -1
name: VerifyContext
- description: "Verifies path in context:\n- Verifies path existence\n- If matching\
- \ object is an array: verify fields exists in each of the objects in the array\n\
- - If matching object is not an array: verify fields exists in matching object\n\
- - if 'expectedValue' is given: ensure that the given value is equal to the\
- \ context path"
+ description: |-
+ Verifies path in context:
+ - Verifies path existence
+ - If matching object is an array: verify fields exists in each of the objects in the array
+ - If matching object is not an array: verify fields exists in matching object
+ - if 'expectedValue' is given: ensure that the given value is equal to the context path
scriptName: VerifyContext
type: regular
iscommand: false
- brand: ''
+ brand: ""
nexttasks:
'#none#':
- - '11'
+ - "11"
scriptarguments:
expectedValue: {}
fields: {}
path:
simple: Bigfix.Site.Resource
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 545\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 545
+ }
+ }
note: false
- '10':
- id: '10'
- taskid: 1a1e91bf-e6f7-421a-8497-87f307824559
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "10":
+ id: "10"
+ taskid: f78807ab-84f6-408b-8a3d-4d15ba7ac791
type: regular
task:
- id: 1a1e91bf-e6f7-421a-8497-87f307824559
+ id: f78807ab-84f6-408b-8a3d-4d15ba7ac791
version: -1
name: VerifyContext
- description: "Verifies path in context:\n- Verifies path existence\n- If matching\
- \ object is an array: verify fields exists in each of the objects in the array\n\
- - If matching object is not an array: verify fields exists in matching object\n\
- - if 'expectedValue' is given: ensure that the given value is equal to the\
- \ context path"
+ description: |-
+ Verifies path in context:
+ - Verifies path existence
+ - If matching object is an array: verify fields exists in each of the objects in the array
+ - If matching object is not an array: verify fields exists in matching object
+ - if 'expectedValue' is given: ensure that the given value is equal to the context path
scriptName: VerifyContext
type: regular
iscommand: false
- brand: ''
+ brand: ""
nexttasks:
'#none#':
- - '11'
+ - "11"
scriptarguments:
expectedValue: {}
fields: {}
path:
simple: Bigfix.Site.Type
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 910,\n \"y\": 545\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 910,
+ "y": 545
+ }
+ }
note: false
- '11':
- id: '11'
- taskid: 54c5763c-c5a9-4619-8076-c6c0771bfe39
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "11":
+ id: "11"
+ taskid: 9bbf977e-cc7d-4534-845c-1c0f9398231c
type: regular
task:
- id: 54c5763c-c5a9-4619-8076-c6c0771bfe39
+ id: 9bbf977e-cc7d-4534-845c-1c0f9398231c
version: -1
name: Delete Context
scriptName: DeleteContext
type: regular
iscommand: false
- brand: ''
- description: ''
+ brand: ""
nexttasks:
'#none#':
- - '3'
+ - "3"
scriptarguments:
all:
- simple: yes
+ simple: "yes"
key: {}
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 720\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 720
+ }
+ }
note: false
- '12':
- id: '12'
- taskid: 75be5e31-bc88-459c-83f4-76edf213ea58
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "12":
+ id: "12"
+ taskid: f932b88b-7547-4054-8bf0-7049bc12ce0d
type: regular
task:
- id: 75be5e31-bc88-459c-83f4-76edf213ea58
+ id: f932b88b-7547-4054-8bf0-7049bc12ce0d
version: -1
name: VerifyContext
- description: "Verifies path in context:\n- Verifies path existence\n- If matching\
- \ object is an array: verify fields exists in each of the objects in the array\n\
- - If matching object is not an array: verify fields exists in matching object\n\
- - if 'expectedValue' is given: ensure that the given value is equal to the\
- \ context path"
+ description: |-
+ Verifies path in context:
+ - Verifies path existence
+ - If matching object is an array: verify fields exists in each of the objects in the array
+ - If matching object is not an array: verify fields exists in matching object
+ - if 'expectedValue' is given: ensure that the given value is equal to the context path
scriptName: VerifyContext
type: regular
iscommand: false
- brand: ''
+ brand: ""
nexttasks:
'#none#':
- - '6'
+ - "6"
scriptarguments:
expectedValue: {}
fields: {}
path:
simple: Bigfix.Site.Name
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 50,\n \"y\": 1595\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 1595
+ }
+ }
note: false
- '13':
- id: '13'
- taskid: ee4f72e4-8c06-4bd2-8cbc-4b14dcd257dc
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "13":
+ id: "13"
+ taskid: 7c221d99-daca-4c5c-848f-5cde42a83332
type: regular
task:
- id: ee4f72e4-8c06-4bd2-8cbc-4b14dcd257dc
+ id: 7c221d99-daca-4c5c-848f-5cde42a83332
version: -1
name: VerifyContext
- description: "Verifies path in context:\n- Verifies path existence\n- If matching\
- \ object is an array: verify fields exists in each of the objects in the array\n\
- - If matching object is not an array: verify fields exists in matching object\n\
- - if 'expectedValue' is given: ensure that the given value is equal to the\
- \ context path"
+ description: |-
+ Verifies path in context:
+ - Verifies path existence
+ - If matching object is an array: verify fields exists in each of the objects in the array
+ - If matching object is not an array: verify fields exists in matching object
+ - if 'expectedValue' is given: ensure that the given value is equal to the context path
scriptName: VerifyContext
type: regular
iscommand: false
- brand: ''
+ brand: ""
nexttasks:
'#none#':
- - '6'
+ - "6"
scriptarguments:
expectedValue: {}
fields: {}
path:
simple: Bigfix.Site.Resource
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 1595\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 1595
+ }
+ }
note: false
- '14':
- id: '14'
- taskid: 691fe7b7-448d-478a-88fe-2c54ee12326c
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "14":
+ id: "14"
+ taskid: 221e5ca2-1b40-41e6-87d5-ffc63888ad9e
type: regular
task:
- id: 691fe7b7-448d-478a-88fe-2c54ee12326c
+ id: 221e5ca2-1b40-41e6-87d5-ffc63888ad9e
version: -1
name: VerifyContext
- description: "Verifies path in context:\n- Verifies path existence\n- If matching\
- \ object is an array: verify fields exists in each of the objects in the array\n\
- - If matching object is not an array: verify fields exists in matching object\n\
- - if 'expectedValue' is given: ensure that the given value is equal to the\
- \ context path"
+ description: |-
+ Verifies path in context:
+ - Verifies path existence
+ - If matching object is an array: verify fields exists in each of the objects in the array
+ - If matching object is not an array: verify fields exists in matching object
+ - if 'expectedValue' is given: ensure that the given value is equal to the context path
scriptName: VerifyContext
type: regular
iscommand: false
- brand: ''
+ brand: ""
nexttasks:
'#none#':
- - '6'
+ - "6"
scriptarguments:
expectedValue: {}
fields: {}
path:
simple: Bigfix.Site.Type
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 910,\n \"y\": 1595\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 910,
+ "y": 1595
+ }
+ }
note: false
- '16':
- id: '16'
- taskid: 1eb56d17-07d5-464e-8d80-60cdfe905eab
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "16":
+ id: "16"
+ taskid: b8b68950-8566-48f7-8aa1-21fbd7888df7
type: regular
task:
- id: 1eb56d17-07d5-464e-8d80-60cdfe905eab
+ id: b8b68950-8566-48f7-8aa1-21fbd7888df7
version: -1
name: VerifyContext
- description: "Verifies path in context:\n- Verifies path existence\n- If matching\
- \ object is an array: verify fields exists in each of the objects in the array\n\
- - If matching object is not an array: verify fields exists in matching object\n\
- - if 'expectedValue' is given: ensure that the given value is equal to the\
- \ context path"
+ description: |-
+ Verifies path in context:
+ - Verifies path existence
+ - If matching object is an array: verify fields exists in each of the objects in the array
+ - If matching object is not an array: verify fields exists in matching object
+ - if 'expectedValue' is given: ensure that the given value is equal to the context path
scriptName: VerifyContext
type: regular
iscommand: false
- brand: ''
+ brand: ""
nexttasks:
'#none#':
- - '19'
+ - "19"
scriptarguments:
expectedValue: {}
fields: {}
path:
simple: Bigfix.Site.Name
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 50,\n \"y\": 1070\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 1070
+ }
+ }
note: false
- '17':
- id: '17'
- taskid: a7825e03-6121-454d-89a4-6bc4fd2bba71
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "17":
+ id: "17"
+ taskid: fcc0b6fa-dc54-4bc8-84e3-05e6804140d6
type: regular
task:
- id: a7825e03-6121-454d-89a4-6bc4fd2bba71
+ id: fcc0b6fa-dc54-4bc8-84e3-05e6804140d6
version: -1
name: VerifyContext
- description: "Verifies path in context:\n- Verifies path existence\n- If matching\
- \ object is an array: verify fields exists in each of the objects in the array\n\
- - If matching object is not an array: verify fields exists in matching object\n\
- - if 'expectedValue' is given: ensure that the given value is equal to the\
- \ context path"
+ description: |-
+ Verifies path in context:
+ - Verifies path existence
+ - If matching object is an array: verify fields exists in each of the objects in the array
+ - If matching object is not an array: verify fields exists in matching object
+ - if 'expectedValue' is given: ensure that the given value is equal to the context path
scriptName: VerifyContext
type: regular
iscommand: false
- brand: ''
+ brand: ""
nexttasks:
'#none#':
- - '19'
+ - "19"
scriptarguments:
expectedValue: {}
fields: {}
path:
simple: Bigfix.Site.Resource
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 1070\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 1070
+ }
+ }
note: false
- '18':
- id: '18'
- taskid: a9da70c1-243d-4ff5-8fd5-fa74981aea5f
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "18":
+ id: "18"
+ taskid: 7fd45f79-3e21-4e59-8342-5b01eaac954f
type: regular
task:
- id: a9da70c1-243d-4ff5-8fd5-fa74981aea5f
+ id: 7fd45f79-3e21-4e59-8342-5b01eaac954f
version: -1
name: VerifyContext
- description: "Verifies path in context:\n- Verifies path existence\n- If matching\
- \ object is an array: verify fields exists in each of the objects in the array\n\
- - If matching object is not an array: verify fields exists in matching object\n\
- - if 'expectedValue' is given: ensure that the given value is equal to the\
- \ context path"
+ description: |-
+ Verifies path in context:
+ - Verifies path existence
+ - If matching object is an array: verify fields exists in each of the objects in the array
+ - If matching object is not an array: verify fields exists in matching object
+ - if 'expectedValue' is given: ensure that the given value is equal to the context path
scriptName: VerifyContext
type: regular
iscommand: false
- brand: ''
+ brand: ""
nexttasks:
'#none#':
- - '19'
+ - "19"
scriptarguments:
expectedValue: {}
fields: {}
path:
simple: Bigfix.Site.Type
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 910,\n \"y\": 1070\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 910,
+ "y": 1070
+ }
+ }
note: false
- '19':
- id: '19'
- taskid: 57f4cde6-d8ec-4cde-8b7e-b45d34012c16
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "19":
+ id: "19"
+ taskid: 007103d1-f242-4fea-8528-d74625ddef2c
type: regular
task:
- id: 57f4cde6-d8ec-4cde-8b7e-b45d34012c16
+ id: 007103d1-f242-4fea-8528-d74625ddef2c
version: -1
name: Delete Context
scriptName: DeleteContext
type: regular
iscommand: false
- brand: ''
- description: ''
+ brand: ""
nexttasks:
'#none#':
- - '2'
+ - "2"
scriptarguments:
all:
- simple: yes
+ simple: "yes"
key: {}
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 1245\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 1245
+ }
+ }
note: false
- '20':
- id: '20'
- taskid: 6981e34d-806b-4768-8cbd-ee21be93b5de
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "20":
+ id: "20"
+ taskid: 268bdfcd-194f-4679-83c8-1b110e7c9bf8
type: regular
task:
- id: 6981e34d-806b-4768-8cbd-ee21be93b5de
+ id: 268bdfcd-194f-4679-83c8-1b110e7c9bf8
version: -1
name: Fail test
- description: "Verifies path in context:\n- Verifies path existence\n- If matching\
- \ object is an array: verify fields exists in each of the objects in the array\n\
- - If matching object is not an array: verify fields exists in matching object\n\
- - if 'expectedValue' is given: ensure that the given value is equal to the\
- \ context path"
+ description: |-
+ Verifies path in context:
+ - Verifies path existence
+ - If matching object is an array: verify fields exists in each of the objects in the array
+ - If matching object is not an array: verify fields exists in matching object
+ - if 'expectedValue' is given: ensure that the given value is equal to the context path
scriptName: VerifyContext
type: regular
iscommand: false
- brand: ''
+ brand: ""
nexttasks:
'#none#':
- - '4'
+ - "4"
scriptarguments:
expectedValue: {}
fields: {}
path:
simple: ThereIsNoSuchPath
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 367.5,\n \"y\": 2120\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 367.5,
+ "y": 2120
+ }
+ }
note: false
- '21':
- id: '21'
- taskid: 85e9f1bf-6dfd-43d7-8595-207b2002c0a5
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "21":
+ id: "21"
+ taskid: cce78046-3f5a-456f-8e95-5662ef41553e
type: regular
task:
- id: 85e9f1bf-6dfd-43d7-8595-207b2002c0a5
+ id: cce78046-3f5a-456f-8e95-5662ef41553e
version: -1
name: VerifyContext
- description: "Verifies path in context:\n- Verifies path existence\n- If matching\
- \ object is an array: verify fields exists in each of the objects in the array\n\
- - If matching object is not an array: verify fields exists in matching object\n\
- - if 'expectedValue' is given: ensure that the given value is equal to the\
- \ context path"
+ description: |-
+ Verifies path in context:
+ - Verifies path existence
+ - If matching object is an array: verify fields exists in each of the objects in the array
+ - If matching object is not an array: verify fields exists in matching object
+ - if 'expectedValue' is given: ensure that the given value is equal to the context path
scriptName: VerifyContext
type: regular
iscommand: false
- brand: ''
+ brand: ""
nexttasks:
'#none#':
- - '5'
+ - "5"
scriptarguments:
expectedValue: {}
fields: {}
path:
simple: Bigfix.Patch.ID
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 265,\n \"y\": 2470\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 265,
+ "y": 2470
+ }
+ }
note: false
- '22':
- id: '22'
- taskid: b08f8a3c-0c73-44a8-846e-cb0da44d0f7b
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "22":
+ id: "22"
+ taskid: 15c98843-da79-462a-803a-b6bafa37db5f
type: regular
task:
- id: b08f8a3c-0c73-44a8-846e-cb0da44d0f7b
+ id: 15c98843-da79-462a-803a-b6bafa37db5f
version: -1
name: VerifyContext
- description: "Verifies path in context:\n- Verifies path existence\n- If matching\
- \ object is an array: verify fields exists in each of the objects in the array\n\
- - If matching object is not an array: verify fields exists in matching object\n\
- - if 'expectedValue' is given: ensure that the given value is equal to the\
- \ context path"
+ description: |-
+ Verifies path in context:
+ - Verifies path existence
+ - If matching object is an array: verify fields exists in each of the objects in the array
+ - If matching object is not an array: verify fields exists in matching object
+ - if 'expectedValue' is given: ensure that the given value is equal to the context path
scriptName: VerifyContext
type: regular
iscommand: false
- brand: ''
+ brand: ""
nexttasks:
'#none#':
- - '5'
+ - "5"
scriptarguments:
expectedValue: {}
fields: {}
path:
simple: Bigfix.Patch.Name
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 695,\n \"y\": 2470\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 695,
+ "y": 2470
+ }
+ }
note: false
- '23':
- id: '23'
- taskid: bf6f1bac-3ccb-42c4-8554-d94227d2122a
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "23":
+ id: "23"
+ taskid: 4522953c-dc17-43a0-88a5-f6ee40c047ed
type: condition
task:
- id: bf6f1bac-3ccb-42c4-8554-d94227d2122a
+ id: 4522953c-dc17-43a0-88a5-f6ee40c047ed
version: -1
name: Verify endpoint
type: condition
iscommand: false
- brand: ''
- description: ''
+ brand: ""
nexttasks:
'#default#':
- - '20'
- yes:
- - '4'
+ - "20"
+ "yes":
+ - "4"
separatecontext: false
conditions:
- - label: yes
+ - label: "yes"
condition:
- - operator: isEqualString
left:
@@ -597,76 +824,254 @@ tasks:
iscontext: true
right:
value:
- simple: '3385267'
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 1945\n }\n}"
+ simple: "3385267"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 1945
+ }
+ }
note: false
- '24':
- id: '24'
- taskid: c2a4f754-6742-4844-8fe2-83eef7b3f392
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "24":
+ id: "24"
+ taskid: 7e627aea-9bfb-45a8-81de-09c0b5d84290
type: regular
task:
- id: c2a4f754-6742-4844-8fe2-83eef7b3f392
+ id: 7e627aea-9bfb-45a8-81de-09c0b5d84290
version: -1
name: Stop action
script: '|||bigfix-action-stop'
type: regular
iscommand: true
- brand: ''
- description: ''
+ brand: ""
nexttasks:
'#none#':
- - '25'
+ - "25"
scriptarguments:
action_id:
simple: ${Bigfix.Action.ID}
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 2850\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 2820
+ }
+ }
note: false
- '25':
- id: '25'
- taskid: 2bf85dd9-1ff3-4e4a-81dd-4dea22ad7620
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "25":
+ id: "25"
+ taskid: 33316be1-3176-4fd1-8a8d-9f12682c18a6
type: regular
task:
- id: 2bf85dd9-1ff3-4e4a-81dd-4dea22ad7620
+ id: 33316be1-3176-4fd1-8a8d-9f12682c18a6
version: -1
name: Get action status
script: '|||bigfix-action-status'
type: regular
iscommand: true
- brand: ''
- description: ''
+ brand: ""
nexttasks:
'#none#':
- - '26'
+ - "26"
scriptarguments:
action_id:
simple: ${Bigfix.Action.ID}
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 3050\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 2995
+ }
+ }
note: false
- '26':
- id: '26'
- taskid: 4fb123eb-7745-4130-8067-b22c09ba4c10
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "26":
+ id: "26"
+ taskid: 17435711-47c4-4e97-853c-d391854defb3
type: regular
task:
- id: 4fb123eb-7745-4130-8067-b22c09ba4c10
+ id: 17435711-47c4-4e97-853c-d391854defb3
version: -1
name: Delete action
script: '|||bigfix-action-delete'
type: regular
iscommand: true
- brand: ''
- description: ''
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "28"
scriptarguments:
action_id:
simple: ${Bigfix.Action.ID}
separatecontext: false
- view: "{\n \"position\": {\n \"x\": 480,\n \"y\": 3270\n }\n}"
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 3170
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "27":
+ id: "27"
+ taskid: 4303c0bf-08ac-4bb6-8bf8-3df2d24d5b49
+ type: regular
+ task:
+ id: 4303c0bf-08ac-4bb6-8bf8-3df2d24d5b49
+ version: -1
+ name: Get Endpoints without details
+ script: BigFix|||bigfix-get-endpoints
+ type: regular
+ iscommand: true
+ brand: BigFix
+ nexttasks:
+ '#none#':
+ - "29"
+ scriptarguments:
+ get_endpoint_details:
+ simple: "false"
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 3520
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "28":
+ id: "28"
+ taskid: f4b23c6d-c0e2-410b-86ee-99bc03753628
+ type: regular
+ task:
+ id: f4b23c6d-c0e2-410b-86ee-99bc03753628
+ version: -1
+ name: Delete Context
+ description: Delete field from context
+ scriptName: DeleteContext
+ type: regular
+ iscommand: false
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "27"
+ scriptarguments:
+ all:
+ simple: "yes"
+ index: {}
+ key: {}
+ keysToKeep: {}
+ subplaybook: {}
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 3345
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "29":
+ id: "29"
+ taskid: 2fdf6266-de58-4335-8aec-34134d2e237e
+ type: condition
+ task:
+ id: 2fdf6266-de58-4335-8aec-34134d2e237e
+ version: -1
+ name: Verify endpoints with details outputs
+ type: condition
+ iscommand: false
+ brand: ""
+ nexttasks:
+ "yes":
+ - "30"
+ separatecontext: false
+ conditions:
+ - label: "yes"
+ condition:
+ - - operator: isExists
+ left:
+ value:
+ simple: Bigfix.Endpoint.ID
+ iscontext: true
+ - - operator: isEmpty
+ left:
+ value:
+ simple: Bigfix.Endpoint.OS
+ iscontext: true
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 3695
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "30":
+ id: "30"
+ taskid: 0085f81a-5a60-4dea-8cc8-bef087b50c9f
+ type: title
+ task:
+ id: 0085f81a-5a60-4dea-8cc8-bef087b50c9f
+ version: -1
+ name: Success
+ type: title
+ iscommand: false
+ brand: ""
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 480,
+ "y": 3870
+ }
+ }
note: false
-view: "{\n \"linkLabelsPosition\": {},\n \"paper\": {\n \"dimensions\": {\n \
- \ \"height\": 3315,\n \"width\": 1240,\n \"x\": 50,\n \"y\":\
- \ 50\n }\n }\n}"
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+view: |-
+ {
+ "linkLabelsPosition": {},
+ "paper": {
+ "dimensions": {
+ "height": 3885,
+ "width": 1240,
+ "x": 50,
+ "y": 50
+ }
+ }
+ }
inputs: []
outputs: []
-fromversion: 5.0.0
-description: ''
diff --git a/Packs/BigFix/pack_metadata.json b/Packs/BigFix/pack_metadata.json
index c5ad8d0734b..bb034d001df 100644
--- a/Packs/BigFix/pack_metadata.json
+++ b/Packs/BigFix/pack_metadata.json
@@ -1,16 +1,16 @@
{
- "name": "BigFix",
- "description": "IBM BigFix Patch provides an automated, simplified patching process that is administered from a single console.",
- "support": "xsoar",
- "currentVersion": "1.0.0",
- "author": "Cortex XSOAR",
- "url": "https://www.paloaltonetworks.com/cortex",
- "email": "",
- "created": "2020-04-14T00:00:00Z",
- "categories": [
- "Vulnerability Management"
- ],
- "tags": [],
- "useCases": [],
- "keywords": []
+ "name": "BigFix",
+ "description": "IBM BigFix Patch provides an automated, simplified patching process that is administered from a single console.",
+ "support": "xsoar",
+ "currentVersion": "1.0.1",
+ "author": "Cortex XSOAR",
+ "url": "https://www.paloaltonetworks.com/cortex",
+ "email": "",
+ "created": "2020-04-14T00:00:00Z",
+ "categories": [
+ "Vulnerability Management"
+ ],
+ "tags": [],
+ "useCases": [],
+ "keywords": []
}
diff --git a/Packs/BitDam/pack_metadata.json b/Packs/BitDam/pack_metadata.json
index ba85fe025f8..10a0510cd1b 100644
--- a/Packs/BitDam/pack_metadata.json
+++ b/Packs/BitDam/pack_metadata.json
@@ -1,11 +1,11 @@
{
"name": "BitDam",
"description": "BitDam secure email gateway protects from advanced content-borne threats with the most accurate prevention of known and unknown threats, at their source.",
- "support": "xsoar",
+ "support": "partner",
"currentVersion": "1.0.0",
- "author": "Cortex XSOAR",
- "url": "https://www.paloaltonetworks.com/cortex",
- "email": "",
+ "author": "BitDam",
+ "url": "",
+ "email": "info@bitdam.com",
"created": "2020-04-14T00:00:00Z",
"categories": [
"Email Gateway"
diff --git a/Packs/BreachNotification-US/pack_metadata.json b/Packs/BreachNotification-US/pack_metadata.json
index 7c1ddcffa3a..98f8b16aa67 100644
--- a/Packs/BreachNotification-US/pack_metadata.json
+++ b/Packs/BreachNotification-US/pack_metadata.json
@@ -4,19 +4,15 @@
"support": "xsoar",
"currentVersion": "1.0.2",
"author": "Cortex XSOAR",
- "url": "",
+ "url": "https://www.paloaltonetworks.com/cortex",
"email": "",
"categories": [],
"tags": [
- "Breach",
"Breach Notification",
"PII",
"Compliance"
],
"created": "2020-05-06T18:47:57Z",
- "updated": "2020-05-06T18:47:57Z",
- "beta": false,
- "deprecated": false,
"useCases": [
"Breach Notification"
],
@@ -26,6 +22,5 @@
"PII",
"Compliance",
"Personal Information"
- ],
- "dependencies": {}
+ ]
}
\ No newline at end of file
diff --git a/Packs/CarbonBlackEnterpriseEDR/Integrations/CarbonBlackEnterpriseEDR/CarbonBlackEnterpriseEDR.py b/Packs/CarbonBlackEnterpriseEDR/Integrations/CarbonBlackEnterpriseEDR/CarbonBlackEnterpriseEDR.py
index 3a396892b6d..2af8b675482 100644
--- a/Packs/CarbonBlackEnterpriseEDR/Integrations/CarbonBlackEnterpriseEDR/CarbonBlackEnterpriseEDR.py
+++ b/Packs/CarbonBlackEnterpriseEDR/Integrations/CarbonBlackEnterpriseEDR/CarbonBlackEnterpriseEDR.py
@@ -911,8 +911,8 @@ def create_report_command(client: Client, args: Dict) -> CommandResults:
'Link': ioc.get('link')
})
- readable_output = tableToMarkdown(f'The report was created successfully.', contents, headers, removeNull=True)
- ioc_output = tableToMarkdown(f'The IOCs for the report', ioc_contents, removeNull=True)
+ readable_output = tableToMarkdown('The report was created successfully.', contents, headers, removeNull=True)
+ ioc_output = tableToMarkdown('The IOCs for the report', ioc_contents, removeNull=True)
results = CommandResults(
outputs_prefix='CarbonBlackEEDR.Report',
outputs_key_field='ID',
@@ -1016,7 +1016,7 @@ def update_report_command(client: Client, args: Dict) -> CommandResults:
'Link': ioc.get('link')
})
- readable_output = tableToMarkdown(f'The report was updated successfully.', contents, headers, removeNull=True)
+ readable_output = tableToMarkdown('The report was updated successfully.', contents, headers, removeNull=True)
ioc_output = tableToMarkdown('The IOCs for the report', ioc_contents, removeNull=True)
results = CommandResults(
outputs_prefix='CarbonBlackEEDR.Report',
diff --git a/Packs/CarbonBlackEnterpriseEDR/Integrations/CarbonBlackEnterpriseEDR/CarbonBlackEnterpriseEDR.yml b/Packs/CarbonBlackEnterpriseEDR/Integrations/CarbonBlackEnterpriseEDR/CarbonBlackEnterpriseEDR.yml
index 27c48a0c73b..ca2fd3d97f1 100644
--- a/Packs/CarbonBlackEnterpriseEDR/Integrations/CarbonBlackEnterpriseEDR/CarbonBlackEnterpriseEDR.yml
+++ b/Packs/CarbonBlackEnterpriseEDR/Integrations/CarbonBlackEnterpriseEDR/CarbonBlackEnterpriseEDR.yml
@@ -1553,7 +1553,7 @@ script:
description: The total number of file paths that have been observed, by this
organization, for this file.
type: Number
- dockerimage: demisto/python3:3.7.4.2245
+ dockerimage: demisto/python3:3.8.3.8715
feed: false
isfetch: true
longRunning: false
diff --git a/Packs/CarbonBlackEnterpriseEDR/ReleaseNotes/1_0_1.md b/Packs/CarbonBlackEnterpriseEDR/ReleaseNotes/1_0_1.md
new file mode 100644
index 00000000000..b780c7d950f
--- /dev/null
+++ b/Packs/CarbonBlackEnterpriseEDR/ReleaseNotes/1_0_1.md
@@ -0,0 +1,4 @@
+
+#### Integrations
+##### Carbon Black Enterprise EDR
+- Internal code improvements.
diff --git a/Packs/CarbonBlackEnterpriseEDR/pack_metadata.json b/Packs/CarbonBlackEnterpriseEDR/pack_metadata.json
index 80fc2ca7137..200493afa75 100644
--- a/Packs/CarbonBlackEnterpriseEDR/pack_metadata.json
+++ b/Packs/CarbonBlackEnterpriseEDR/pack_metadata.json
@@ -2,7 +2,7 @@
"name": "Carbon Black Cloud Enterprise EDR",
"description": "Advanced threat hunting and incident response solution.",
"support": "xsoar",
- "currentVersion": "1.0.0",
+ "currentVersion": "1.0.1",
"author": "Cortex XSOAR",
"url": "https://www.paloaltonetworks.com/cortex",
"email": "",
diff --git a/Packs/CarbonBlackProtect/Integrations/CarbonBlackProtect/CHANGELOG.md b/Packs/CarbonBlackProtect/Integrations/CarbonBlackProtect/CHANGELOG.md
index e0053696846..1feea660775 100644
--- a/Packs/CarbonBlackProtect/Integrations/CarbonBlackProtect/CHANGELOG.md
+++ b/Packs/CarbonBlackProtect/Integrations/CarbonBlackProtect/CHANGELOG.md
@@ -1,5 +1,5 @@
## [Unreleased]
-
+Added documentation notes
## [20.4.1] - 2020-04-29
-
diff --git a/Packs/CarbonBlackProtect/Integrations/CarbonBlackProtect/CarbonBlackProtect.yml b/Packs/CarbonBlackProtect/Integrations/CarbonBlackProtect/CarbonBlackProtect.yml
index 0f59af7c492..242339e6ae3 100644
--- a/Packs/CarbonBlackProtect/Integrations/CarbonBlackProtect/CarbonBlackProtect.yml
+++ b/Packs/CarbonBlackProtect/Integrations/CarbonBlackProtect/CarbonBlackProtect.yml
@@ -45,6 +45,7 @@ configuration:
description: Carbon Black Enterprise Protection is a next-generation endpoint threat
prevention solution to deliver a portfolio of protection policies, real-time visibility
across environments, and comprehensive compliance rule sets in a single platform.
+ This integration only supports Carbon Black on-premise APIs.
display: Carbon Black Enterprise Protection v2
name: CarbonBlackProtectionV2
script:
@@ -2520,7 +2521,7 @@ script:
- contextPath: CBP.FileRule.ReportOnly
description: Is this rule "reporting only" or is it also "enforcing".
type: String
- dockerimage: demisto/python3:3.7.3.221
+ dockerimage: demisto/python3:3.8.3.8715
subtype: python3
isfetch: true
runonce: false
diff --git a/Packs/CarbonBlackProtect/Integrations/CarbonBlackProtect/CarbonBlackProtect_description.md b/Packs/CarbonBlackProtect/Integrations/CarbonBlackProtect/CarbonBlackProtect_description.md
index 7650e80da5f..2f28fc323fa 100644
--- a/Packs/CarbonBlackProtect/Integrations/CarbonBlackProtect/CarbonBlackProtect_description.md
+++ b/Packs/CarbonBlackProtect/Integrations/CarbonBlackProtect/CarbonBlackProtect_description.md
@@ -1,2 +1,8 @@
-To find a API key corresponding with a particular Carbon Black user account, log into the console as that user, then click the username in the upper right -> Profile info.
-Then, click the "API Token" button on the left hand side to reveal the API token for the logged-in user. If there is no API token displayed, click the "Reset" button to create a new one.
\ No newline at end of file
+## Retrieve Your API Key
+Follow these instructions to find an API key that corresponds to a particular Carbon Black user account.
+1. Log in to the Carbon Black console as the user for which you want to get the API key.
+2. Click the username in the upper-right corner.
+3. Click the **API Token** button on the left-hand side to reveal the API token for the logged-in user.
+4. (Optional) If no API token is displayed, click the **Reset** button to create a new one.
+
+* This integration only supports Carbon Black Cloud on-premise APIs.
diff --git a/Packs/CarbonBlackProtect/ReleaseNotes/1_0_1.md b/Packs/CarbonBlackProtect/ReleaseNotes/1_0_1.md
new file mode 100644
index 00000000000..406863dd8cd
--- /dev/null
+++ b/Packs/CarbonBlackProtect/ReleaseNotes/1_0_1.md
@@ -0,0 +1,4 @@
+
+#### Integrations
+##### __CarbonBlackProtectionV2__
+- Added documentation notes
diff --git a/Packs/CarbonBlackProtect/pack_metadata.json b/Packs/CarbonBlackProtect/pack_metadata.json
index 562e656f59f..26837cad4b0 100644
--- a/Packs/CarbonBlackProtect/pack_metadata.json
+++ b/Packs/CarbonBlackProtect/pack_metadata.json
@@ -1,16 +1,16 @@
{
- "name": "Carbon Black Enterprise Protection",
- "description": "Carbon Black Enterprise Protection is a next-generation endpoint threat prevention solution to deliver a portfolio of protection policies, real-time visibility across environments, and comprehensive compliance rule sets in a single platform.",
- "support": "xsoar",
- "currentVersion": "1.0.0",
- "author": "Cortex XSOAR",
- "url": "https://www.paloaltonetworks.com/cortex",
- "email": "",
- "created": "2020-04-14T00:00:00Z",
- "categories": [
- "Endpoint"
- ],
- "tags": [],
- "useCases": [],
- "keywords": []
-}
+ "name": "Carbon Black Enterprise Protection",
+ "description": "Carbon Black Enterprise Protection is a next-generation endpoint threat prevention solution to deliver a portfolio of protection policies, real-time visibility across environments, and comprehensive compliance rule sets in a single platform.",
+ "support": "xsoar",
+ "currentVersion": "1.0.1",
+ "author": "Cortex XSOAR",
+ "url": "https://www.paloaltonetworks.com/cortex",
+ "email": "",
+ "created": "2020-04-14T00:00:00Z",
+ "categories": [
+ "Endpoint"
+ ],
+ "tags": [],
+ "useCases": [],
+ "keywords": []
+}
\ No newline at end of file
diff --git a/Packs/Carbon_Black_Enterprise_Response/.pack-ignore b/Packs/Carbon_Black_Enterprise_Response/.pack-ignore
index e69de29bb2d..2bc6dc0c23b 100644
--- a/Packs/Carbon_Black_Enterprise_Response/.pack-ignore
+++ b/Packs/Carbon_Black_Enterprise_Response/.pack-ignore
@@ -0,0 +1,11 @@
+[file:playbook-Block_Endpoint_-_Carbon_Black_Response.yml]
+ignore=BA101
+
+[file:playbook-Get_File_Sample_By_Hash_-_Carbon_Black_Enterprise_Response.yml]
+ignore=BA101
+
+[file:playbook-Get_File_Sample_From_Path_-_Carbon_Black_Enterprise_Response.yml]
+ignore=BA101
+
+[file:playbook-Block_File_-_Carbon_Black_Response.yml]
+ignore=BA101
diff --git a/Packs/Carbon_Black_Enterprise_Response/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_V2.yml b/Packs/Carbon_Black_Enterprise_Response/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_V2.yml
new file mode 100644
index 00000000000..c351d69d996
--- /dev/null
+++ b/Packs/Carbon_Black_Enterprise_Response/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_V2.yml
@@ -0,0 +1,278 @@
+id: Search Endpoints By Hash - Carbon Black Response V2
+version: -1
+name: Search Endpoints By Hash - Carbon Black Response V2
+description: Hunt for malicious indicators using Carbon Black
+starttaskid: "0"
+tasks:
+ "0":
+ id: "0"
+ taskid: edc8b672-13e8-48fa-87c9-594118876ac4
+ type: start
+ task:
+ id: edc8b672-13e8-48fa-87c9-594118876ac4
+ version: -1
+ name: ""
+ description: Playbook start point
+ iscommand: false
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "2"
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 50
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ "1":
+ id: "1"
+ taskid: 71c8642a-2af7-4af6-8015-5a2f89fd40d2
+ type: regular
+ task:
+ id: 71c8642a-2af7-4af6-8015-5a2f89fd40d2
+ version: -1
+ name: Hunt MD5 Hash
+ description: Query processes based on given parameters
+ script: '|||cb-get-processes'
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "3"
+ scriptarguments:
+ facet: {}
+ group: {}
+ hostname: {}
+ md5:
+ complex:
+ root: inputs
+ accessor: Hash
+ name: {}
+ parent-process-name: {}
+ process-path: {}
+ query: {}
+ rows: {}
+ sort: {}
+ start: {}
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 500,
+ "y": 680
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ "2":
+ id: "2"
+ taskid: a43ea7bf-d5e3-4c0b-8da7-5ce65cc1aae3
+ type: condition
+ task:
+ id: a43ea7bf-d5e3-4c0b-8da7-5ce65cc1aae3
+ version: -1
+ name: Is Carbon Black enabled?
+ description: Is Carbon Black enabled?
+ type: condition
+ iscommand: false
+ brand: ""
+ nexttasks:
+ '#default#':
+ - "7"
+ "yes":
+ - "5"
+ scriptarguments:
+ value:
+ simple: ${modules(val.brand == 'carbonblack' && val.state == 'active')}
+ separatecontext: false
+ conditions:
+ - label: "yes"
+ condition:
+ - - operator: isExists
+ left:
+ value:
+ complex:
+ root: modules
+ filters:
+ - - operator: isEqualString
+ left:
+ value:
+ simple: modules.brand
+ iscontext: true
+ right:
+ value:
+ simple: carbonblack-v2
+ - - operator: isEqualString
+ left:
+ value:
+ simple: modules.state
+ iscontext: true
+ right:
+ value:
+ simple: active
+ accessor: brand
+ iscontext: true
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 195
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ "3":
+ id: "3"
+ taskid: 108ba97d-b9da-4162-808d-025b5f6b7986
+ type: title
+ task:
+ id: 108ba97d-b9da-4162-808d-025b5f6b7986
+ version: -1
+ name: Done
+ description: Done
+ type: title
+ iscommand: false
+ brand: ""
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 865
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ "5":
+ id: "5"
+ taskid: 00c09fd8-6352-4b6c-8e7a-5a2459544fc7
+ type: condition
+ task:
+ id: 00c09fd8-6352-4b6c-8e7a-5a2459544fc7
+ version: -1
+ name: Is there an MD5 hash to hunt?
+ description: Checks if there is an MD5 hash to hunt.
+ type: condition
+ iscommand: false
+ brand: ""
+ nexttasks:
+ '#default#':
+ - "7"
+ "yes":
+ - "6"
+ separatecontext: false
+ conditions:
+ - label: "yes"
+ condition:
+ - - operator: isNotEmpty
+ left:
+ value:
+ complex:
+ root: inputs.Hash
+ iscontext: true
+ view: |-
+ {
+ "position": {
+ "x": 290,
+ "y": 370
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ "6":
+ id: "6"
+ taskid: 4fd8eec7-f7ec-4f53-8473-932db46cc0ec
+ type: title
+ task:
+ id: 4fd8eec7-f7ec-4f53-8473-932db46cc0ec
+ version: -1
+ name: Hunt MD5
+ type: title
+ iscommand: false
+ brand: ""
+ description: ''
+ nexttasks:
+ '#none#':
+ - "1"
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 500,
+ "y": 540
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ "7":
+ id: "7"
+ taskid: 863da4aa-ab0b-416b-8072-1f36602773aa
+ type: title
+ task:
+ id: 863da4aa-ab0b-416b-8072-1f36602773aa
+ version: -1
+ name: No Integration \ No Hash
+ type: title
+ iscommand: false
+ brand: ""
+ description: ''
+ nexttasks:
+ '#none#':
+ - "3"
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 540
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+view: |-
+ {
+ "linkLabelsPosition": {
+ "2_5_yes": 0.41,
+ "2_7_#default#": 0.42,
+ "5_6_yes": 0.47,
+ "5_7_#default#": 0.41
+ },
+ "paper": {
+ "dimensions": {
+ "height": 880,
+ "width": 830,
+ "x": 50,
+ "y": 50
+ }
+ }
+ }
+inputs:
+- key: Hash
+ value:
+ complex:
+ root: File
+ accessor: MD5
+ required: false
+ description: MD5 Hash
+outputs:
+- contextPath: Endpoint.Hostname
+ description: The device hostname
+ type: string
+- contextPath: Endpoint
+ description: The endpoint
+ type: unknown
+fromversion: 4.5.0
+tests:
+- No tests
diff --git a/Packs/Carbon_Black_Enterprise_Response/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_V2_README.md b/Packs/Carbon_Black_Enterprise_Response/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_V2_README.md
new file mode 100644
index 00000000000..17a15fc24db
--- /dev/null
+++ b/Packs/Carbon_Black_Enterprise_Response/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_V2_README.md
@@ -0,0 +1,35 @@
+Hunt for malicious indicators using Carbon Black
+
+## Dependencies
+This playbook uses the following sub-playbooks, integrations, and scripts.
+
+### Sub-playbooks
+This playbook does not use any sub-playbooks.
+
+### Integrations
+* integration-Carbon_Black_Enterprise_Response
+
+### Scripts
+This playbook does not use any scripts.
+
+### Commands
+* cb-get-processes
+
+## Playbook Inputs
+---
+
+| **Name** | **Description** | **Default Value** | **Required** |
+| --- | --- | --- | --- |
+| Hash | MD5 Hash | File.MD5 | Optional |
+
+## Playbook Outputs
+---
+
+| **Path** | **Description** | **Type** |
+| --- | --- | --- |
+| Endpoint.Hostname | The device hostname | string |
+| Endpoint | The endpoint | unknown |
+
+## Playbook Image
+---
+
\ No newline at end of file
diff --git a/Packs/Carbon_Black_Enterprise_Response/ReleaseNotes/1_0_2.md b/Packs/Carbon_Black_Enterprise_Response/ReleaseNotes/1_0_2.md
index 49ea2d1f62c..51e32536679 100644
--- a/Packs/Carbon_Black_Enterprise_Response/ReleaseNotes/1_0_2.md
+++ b/Packs/Carbon_Black_Enterprise_Response/ReleaseNotes/1_0_2.md
@@ -5,3 +5,7 @@
- __CBEvents__
-
-->
+
+#### Playbook
+##### __Search Endpoints By Hash - Carbon Black Response V2__
+- Hunt for malicious indicators using Carbon Black
diff --git a/Packs/Carbon_Black_Enterprise_Response/ReleaseNotes/1_0_3.md b/Packs/Carbon_Black_Enterprise_Response/ReleaseNotes/1_0_3.md
index 33728ad8aaa..2649f99aa02 100644
--- a/Packs/Carbon_Black_Enterprise_Response/ReleaseNotes/1_0_3.md
+++ b/Packs/Carbon_Black_Enterprise_Response/ReleaseNotes/1_0_3.md
@@ -1,5 +1,9 @@
-### Integrations
-- __carbonblack-v2_
- - Fixed an issue where the file context did not behave as expected in the ***cb-get-processes*** command.
+#### Integrations
+##### __carbonblack-v2_
+- Fixed an issue where the file context did not behave as expected in the ***cb-get-processes*** command.
+
+#### Playbook
+##### __Search Endpoints By Hash - Carbon Black Response V2__
+- New playbook - Search Endpoints By Hash - Carbon Black Response V2 playbook with carbonblack-v2.
diff --git a/Packs/Carbon_Black_Enterprise_Response/doc_files/Search_Endpoints_By_Hash_-_Carbon_Black_Response_V2.png b/Packs/Carbon_Black_Enterprise_Response/doc_files/Search_Endpoints_By_Hash_-_Carbon_Black_Response_V2.png
new file mode 100644
index 00000000000..02849fb9c0c
Binary files /dev/null and b/Packs/Carbon_Black_Enterprise_Response/doc_files/Search_Endpoints_By_Hash_-_Carbon_Black_Response_V2.png differ
diff --git a/Packs/CheckpointFirewall/Integrations/integration-CheckpointFirewall.yml b/Packs/CheckpointFirewall/Integrations/integration-CheckpointFirewall.yml
index 2b086038416..8c335a55992 100644
--- a/Packs/CheckpointFirewall/Integrations/integration-CheckpointFirewall.yml
+++ b/Packs/CheckpointFirewall/Integrations/integration-CheckpointFirewall.yml
@@ -835,8 +835,9 @@ script:
description: Whether to block traffic "to" or "from" the IPs, or "both". Default is
"both".
- name: rulename
- required: true
+ required: false
description: Base name for added rules inside checkpoint db
+ deprecated: true
- name: ipname
required: true
description: Base name for added ip/hosts inside checkpoint db
diff --git a/Packs/CheckpointFirewall/Integrations/integration-CheckpointFirewall_README.md b/Packs/CheckpointFirewall/Integrations/integration-CheckpointFirewall_README.md
index ef1a2cda497..ee023926bd1 100644
--- a/Packs/CheckpointFirewall/Integrations/integration-CheckpointFirewall_README.md
+++ b/Packs/CheckpointFirewall/Integrations/integration-CheckpointFirewall_README.md
@@ -787,11 +787,6 @@
The base name for added rules inside Check Point DB.
Required
-
-
ipname
-
The base name for added IP addresses/hosts inside Check Point DB.
-
Required
-
diff --git a/Packs/CheckpointFirewall/ReleaseNotes/1_0_2.md b/Packs/CheckpointFirewall/ReleaseNotes/1_0_2.md
new file mode 100644
index 00000000000..28617978ef6
--- /dev/null
+++ b/Packs/CheckpointFirewall/ReleaseNotes/1_0_2.md
@@ -0,0 +1,4 @@
+
+#### Integrations
+##### Check Point
+- Deprecated the *ipname* argument from the ***checkpoint-block-ip*** command.
diff --git a/Packs/CheckpointFirewall/pack_metadata.json b/Packs/CheckpointFirewall/pack_metadata.json
index 175eae917b3..eac97bc4ea9 100644
--- a/Packs/CheckpointFirewall/pack_metadata.json
+++ b/Packs/CheckpointFirewall/pack_metadata.json
@@ -2,7 +2,7 @@
"name": "Check Point Firewall",
"description": "Manage Check Point firewall via API",
"support": "xsoar",
- "currentVersion": "1.0.1",
+ "currentVersion": "1.0.2",
"author": "Cortex XSOAR",
"url": "https://www.paloaltonetworks.com/cortex",
"email": "",
diff --git a/Packs/Cherwell/Integrations/Cherwell/Cherwell.py b/Packs/Cherwell/Integrations/Cherwell/Cherwell.py
index fc078f69b89..2c75a9a014f 100644
--- a/Packs/Cherwell/Integrations/Cherwell/Cherwell.py
+++ b/Packs/Cherwell/Integrations/Cherwell/Cherwell.py
@@ -539,8 +539,8 @@ def validate_query_list(query_list, is_fetch):
def validate_query_for_fetch_incidents(objects_names, query_string, real_fetch):
if not objects_names:
- no_objects_err_message = f'No business object name was given. \n In order to run advanced query, ' \
- f'fill the integration parameter-`Objects to fetch` with exactly one business object name.'
+ no_objects_err_message = 'No business object name was given. \n In order to run advanced query, ' \
+ 'fill the integration parameter-`Objects to fetch` with exactly one business object name.'
raise_or_return_error(no_objects_err_message, real_fetch)
if len(objects_names) > 1:
multiple_objects_error_message = f'Advanced query operation is supported for a single business object. ' \
@@ -584,8 +584,8 @@ def parse_string_query_to_list(query_string, is_fetch=False):
try:
query_list = json.loads(query_string)
except (ValueError, TypeError):
- err_message = f'Cannot parse query, should be of the form: `[["FieldName","Operator","Value"],' \
- f'["FieldName","Operator","Value"]]`.'
+ err_message = 'Cannot parse query, should be of the form: `[["FieldName","Operator","Value"],' \
+ '["FieldName","Operator","Value"]]`.'
raise_or_return_error(err_message, is_fetch)
validate_query_list(query_list, is_fetch)
return query_list
@@ -596,7 +596,7 @@ def query_business_object_string(business_object_name, query_string, max_results
try:
int(max_results)
except ValueError:
- return return_error(f'`max_results` argument received is not a number')
+ return return_error('`max_results` argument received is not a number')
business_object_id = resolve_business_object_id_by_name(business_object_name)
query_filters_list = parse_string_query_to_list(query_string)
return query_business_object(query_filters_list, business_object_id, max_results)
@@ -846,7 +846,7 @@ def get_attachments_info_command():
}
if attachments_info:
entry['EntryContext'] = {
- f'Cherwell.AttachmentsInfo': attachments_info}
+ 'Cherwell.AttachmentsInfo': attachments_info}
demisto.results(entry)
@@ -942,7 +942,7 @@ def cherwell_get_business_object_id_command():
args = demisto.args()
business_object_name = args.get('business_object_name')
result = cherwell_get_business_object_id(business_object_name)
- md = tableToMarkdown(f'Business Object Info:', result, headerTransform=pascalToSpace)
+ md = tableToMarkdown('Business Object Info:', result, headerTransform=pascalToSpace)
demisto.results({
'Type': entryTypes['note'],
'ContentsFormat': formats['text'],
diff --git a/Packs/Cherwell/Integrations/Cherwell/Cherwell.yml b/Packs/Cherwell/Integrations/Cherwell/Cherwell.yml
index 4180f73bc95..f88c1ce5b6a 100644
--- a/Packs/Cherwell/Integrations/Cherwell/Cherwell.yml
+++ b/Packs/Cherwell/Integrations/Cherwell/Cherwell.yml
@@ -526,7 +526,7 @@ script:
- contextPath: Cherwell.BusinessObjectInfo.BusinessObjectName
description: Business object name.
type: String
- dockerimage: demisto/python3:3.7.2.214
+ dockerimage: demisto/python3:3.8.3.8715
isfetch: true
runonce: false
script: '-'
diff --git a/Packs/Cherwell/ReleaseNotes/1_0_1.md b/Packs/Cherwell/ReleaseNotes/1_0_1.md
new file mode 100644
index 00000000000..013d1633b94
--- /dev/null
+++ b/Packs/Cherwell/ReleaseNotes/1_0_1.md
@@ -0,0 +1,4 @@
+
+#### Integrations
+##### Cherwell
+- Internal code improvements.
diff --git a/Packs/Cherwell/pack_metadata.json b/Packs/Cherwell/pack_metadata.json
index 55b96a97d33..2f492e0d1ea 100644
--- a/Packs/Cherwell/pack_metadata.json
+++ b/Packs/Cherwell/pack_metadata.json
@@ -1,16 +1,16 @@
{
- "name": "Cherwell",
- "description": "Cloud-based IT service management solution",
- "support": "xsoar",
- "currentVersion": "1.0.0",
- "author": "Cortex XSOAR",
- "url": "https://www.paloaltonetworks.com/cortex",
- "email": "",
- "created": "2020-04-14T00:00:00Z",
- "categories": [
- "Case Management"
- ],
- "tags": [],
- "useCases": [],
- "keywords": []
-}
+ "name": "Cherwell",
+ "description": "Cloud-based IT service management solution",
+ "support": "xsoar",
+ "currentVersion": "1.0.1",
+ "author": "Cortex XSOAR",
+ "url": "https://www.paloaltonetworks.com/cortex",
+ "email": "",
+ "created": "2020-04-14T00:00:00Z",
+ "categories": [
+ "Case Management"
+ ],
+ "tags": [],
+ "useCases": [],
+ "keywords": []
+}
\ No newline at end of file
diff --git a/Packs/Claroty/Integrations/Claroty/Claroty.py b/Packs/Claroty/Integrations/Claroty/Claroty.py
index 52100645ef4..c0e6fced935 100644
--- a/Packs/Claroty/Integrations/Claroty/Claroty.py
+++ b/Packs/Claroty/Integrations/Claroty/Claroty.py
@@ -245,13 +245,13 @@ def resolve_alert_command(client: Client, args: dict) -> Tuple:
"Claroty.Resolve_out": result
}
if result['success']:
- readable_output = f"## Alert was resolved successfully"
+ readable_output = "## Alert was resolved successfully"
else:
- readable_output = f"## Alert was not resolved"
+ readable_output = "## Alert was not resolved"
else:
result = {}
outputs = {}
- readable_output = f"## Bad input"
+ readable_output = "## Bad input"
return (
readable_output,
diff --git a/Packs/Claroty/Integrations/Claroty/Claroty.yml b/Packs/Claroty/Integrations/Claroty/Claroty.yml
index 83acf763289..a29f4b5b451 100644
--- a/Packs/Claroty/Integrations/Claroty/Claroty.yml
+++ b/Packs/Claroty/Integrations/Claroty/Claroty.yml
@@ -482,7 +482,7 @@ script:
- contextPath: Claroty.Alert.Severity
description: The alert severity.
type: String
- dockerimage: demisto/python3:3.8.1.5734
+ dockerimage: demisto/python3:3.8.3.8715
feed: false
isfetch: true
longRunning: false
diff --git a/Packs/Claroty/Integrations/Claroty/README.md b/Packs/Claroty/Integrations/Claroty/README.md
index 051434fd572..ba2d6ae9c13 100644
--- a/Packs/Claroty/Integrations/Claroty/README.md
+++ b/Packs/Claroty/Integrations/Claroty/README.md
@@ -1,8 +1,6 @@
-## Overview
----
-
Use the Claroty CTD integration to manage assets and alerts.
This integration was integrated and tested with version 4.0.1 of Claroty
+
## Claroty Playbook
Playbook 1: OT Asset Discovery
Maintaining an accurate enterprise asset database is extremely difficult,
@@ -537,16 +535,3 @@ Admin user.
|AlertType|AlertTypeID|Category|Description|Indicator|NetworkID|RelatedAssets|Resolved|ResourceID|Severity|
|---|---|---|---|---|---|---|---|---|---|
| PortScan | 28 | Integrity | UDP Port scan: Asset 192.168.1.10 sent probe packets to 192.168.1.25 IP address on different ports | Alert ID - 75 Description - This Event does not currently support Alert Indicators Points - 100
Searches the Cortex panw.traffic table, which is the traffic logs table for PAN-OS and Panorama.
diff --git a/Packs/DeprecatedContent/Integrations/PaloAltoNetworksCortex/doc_files/mceclip0.png b/Packs/DeprecatedContent/Integrations/PaloAltoNetworksCortex/doc_files/mceclip0.png
new file mode 100644
index 00000000000..7e302f0bd79
Binary files /dev/null and b/Packs/DeprecatedContent/Integrations/PaloAltoNetworksCortex/doc_files/mceclip0.png differ
diff --git a/Packs/EWS/Integrations/integration-Office_EWS.yml b/Packs/DeprecatedContent/Integrations/integration-Office_EWS.yml
similarity index 100%
rename from Packs/EWS/Integrations/integration-Office_EWS.yml
rename to Packs/DeprecatedContent/Integrations/integration-Office_EWS.yml
diff --git a/Packs/Hunting/Playbooks/playbook-Hunt_Extracted_Hashes.yml b/Packs/DeprecatedContent/Playbooks/playbook-Hunt_Extracted_Hashes.yml
similarity index 97%
rename from Packs/Hunting/Playbooks/playbook-Hunt_Extracted_Hashes.yml
rename to Packs/DeprecatedContent/Playbooks/playbook-Hunt_Extracted_Hashes.yml
index d6777da148f..b357425d695 100644
--- a/Packs/Hunting/Playbooks/playbook-Hunt_Extracted_Hashes.yml
+++ b/Packs/DeprecatedContent/Playbooks/playbook-Hunt_Extracted_Hashes.yml
@@ -1,7 +1,7 @@
id: Hunt Extracted Hashes
version: -1
name: Hunt Extracted Hashes
-description: "This playbook extracts IOCs from the incident details and attached\
+description: "Deprecated. Use the Hunt Extracted Hashes V2 playbook instead. This playbook extracts IOCs from the incident details and attached\
\ files using regular expressions and then hunts for hashes on endpoints in the organization\
\ using available tools.\nThe playbook supports multiple types of attachments. For\
\ the full supported attachments list, refer to \"Extract Indicators From\
diff --git a/Packs/Hunting/Playbooks/playbook-Hunt_Extracted_Hashes_CHANGELOG.md b/Packs/DeprecatedContent/Playbooks/playbook-Hunt_Extracted_Hashes_CHANGELOG.md
similarity index 87%
rename from Packs/Hunting/Playbooks/playbook-Hunt_Extracted_Hashes_CHANGELOG.md
rename to Packs/DeprecatedContent/Playbooks/playbook-Hunt_Extracted_Hashes_CHANGELOG.md
index e15cfa78a55..7c7ffc1f12c 100644
--- a/Packs/Hunting/Playbooks/playbook-Hunt_Extracted_Hashes_CHANGELOG.md
+++ b/Packs/DeprecatedContent/Playbooks/playbook-Hunt_Extracted_Hashes_CHANGELOG.md
@@ -1,5 +1,5 @@
## [Unreleased]
-
+- Deprecated. Use the Hunt Extracted Hashes V2 instead.
## [20.5.0] - 2020-05-12
#### New Playbook
diff --git a/Packs/Hunting/Playbooks/playbook-Hunt_Extracted_Hashes_README.md b/Packs/DeprecatedContent/Playbooks/playbook-Hunt_Extracted_Hashes_README.md
similarity index 78%
rename from Packs/Hunting/Playbooks/playbook-Hunt_Extracted_Hashes_README.md
rename to Packs/DeprecatedContent/Playbooks/playbook-Hunt_Extracted_Hashes_README.md
index da1e3e2f04a..0f20a792dc3 100644
--- a/Packs/Hunting/Playbooks/playbook-Hunt_Extracted_Hashes_README.md
+++ b/Packs/DeprecatedContent/Playbooks/playbook-Hunt_Extracted_Hashes_README.md
@@ -1,4 +1,4 @@
-This playbook extracts IOCs from the incident details and attached files using regular expressions and then hunts for hashes on endpoints in the organization using available tools.
+Deprecated. Use the Hunt Extracted Hashes V2 instead. This playbook extracts IOCs from the incident details and attached files using regular expressions and then hunts for hashes on endpoints in the organization using available tools.
The playbook supports multiple types of attachments. For the full supported attachments list, refer to "Extract Indicators From File - Generic v2".
## Dependencies
diff --git a/Packs/Legacy/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response.yml b/Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response.yml
similarity index 97%
rename from Packs/Legacy/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response.yml
rename to Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response.yml
index ab129a1413c..f2ab33025bb 100644
--- a/Packs/Legacy/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response.yml
+++ b/Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response.yml
@@ -3,7 +3,7 @@ fromversion: 3.5.0
system: true
version: -1
name: Search Endpoints By Hash - Carbon Black Response
-description: Hunt for malicious indicators using Carbon Black
+description: Deprecated. Use the Search Search Endpoints By Hash - Carbon Black Response V2 playbook instead. Hunt for malicious indicators using Carbon Black.
starttaskid: "0"
tasks:
"0":
@@ -286,4 +286,4 @@ outputs:
description: The endpoint
type: unknown
tests:
- - no test
\ No newline at end of file
+ - no test
diff --git a/Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_CHANGELOG.md b/Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_CHANGELOG.md
new file mode 100644
index 00000000000..c28dde49ec3
--- /dev/null
+++ b/Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_CHANGELOG.md
@@ -0,0 +1,2 @@
+## [Unreleased]
+- Deprecated. Use the Search Search Endpoints By Hash - Carbon Black Response V2 instead.
\ No newline at end of file
diff --git a/Packs/Legacy/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_README.md b/Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_README.md
similarity index 86%
rename from Packs/Legacy/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_README.md
rename to Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_README.md
index 5b905bfbc3e..1375cbeb0e2 100644
--- a/Packs/Legacy/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_README.md
+++ b/Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Carbon_Black_Response_README.md
@@ -1,4 +1,4 @@
-Hunts for malicious indicators using Carbon Black.
+Deprecated. Use the Search Search Endpoints By Hash - Carbon Black Response V2 instead. Hunts for malicious indicators using Carbon Black.
## Dependencies
This playbook uses the following sub-playbooks, integrations, and scripts.
diff --git a/Packs/CommonPlaybooks/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5.yml b/Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5.yml
similarity index 98%
rename from Packs/CommonPlaybooks/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5.yml
rename to Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5.yml
index edced6df2d1..1c7cdf3ea9e 100644
--- a/Packs/CommonPlaybooks/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5.yml
+++ b/Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5.yml
@@ -3,7 +3,7 @@ version: -1
name: Search Endpoints By Hash - Generic
fromversion: 4.5.0
system: true
-description: Hunt using available tools
+description: Deprecated. Use the Search Endpoints By Hash - Generic V2 playbook instead. Hunt using available tools
starttaskid: "0"
tasks:
"0":
diff --git a/Packs/CommonPlaybooks/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5_CHANGELOG.md b/Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5_CHANGELOG.md
similarity index 59%
rename from Packs/CommonPlaybooks/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5_CHANGELOG.md
rename to Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5_CHANGELOG.md
index e1df6b3d942..bd5606da344 100644
--- a/Packs/CommonPlaybooks/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5_CHANGELOG.md
+++ b/Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5_CHANGELOG.md
@@ -1,5 +1,5 @@
## [Unreleased]
-
+- Deprecated. Use the Search Endpoints By Hash - Generic V2 playbook instead.
## [20.5.2] - 2020-05-26
-
diff --git a/Packs/CommonPlaybooks/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5_README.md b/Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5_README.md
similarity index 92%
rename from Packs/CommonPlaybooks/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5_README.md
rename to Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5_README.md
index 6c319d601a2..82025b20461 100644
--- a/Packs/CommonPlaybooks/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5_README.md
+++ b/Packs/DeprecatedContent/Playbooks/playbook-Search_Endpoints_By_Hash_-_Generic_4_5_README.md
@@ -1,4 +1,4 @@
-Hunts using available tools.
+Deprecated. Use the Search Endpoints By Hash - Generic V2 playbook instead. Hunts using available tools.
## Dependencies
This playbook uses the following sub-playbooks, integrations, and scripts.
diff --git a/Packs/DeprecatedContent/Playbooks/playbook-malware.yml b/Packs/DeprecatedContent/Playbooks/playbook-malware.yml
index 28321bf4dbf..0d8eed399b5 100644
--- a/Packs/DeprecatedContent/Playbooks/playbook-malware.yml
+++ b/Packs/DeprecatedContent/Playbooks/playbook-malware.yml
@@ -4,7 +4,7 @@ system: true
fromversion: 2.5.0
name: Malware Playbook - Manual
description: |-
- Master playbook for investigating suspected malware presence on an endpoint.
+ DEPRECATED. Use "Malware Investigation - Manual" playbook instead. Master playbook for investigating suspected malware presence on an endpoint.
Labels:
- System: the hostname for the endpoint being investigated
tags:
diff --git a/Packs/DeprecatedContent/ReleaseNotes/1_2_0.md b/Packs/DeprecatedContent/ReleaseNotes/1_2_0.md
new file mode 100644
index 00000000000..2c02da05927
--- /dev/null
+++ b/Packs/DeprecatedContent/ReleaseNotes/1_2_0.md
@@ -0,0 +1,4 @@
+
+#### Playbooks
+##### Malware Playbook - Manual
+- DEPRECATED. Use "Malware Investigation - Manual" playbook instead.
diff --git a/Packs/Base/TestPlaybooks/playbook-Dedup_-_Generic_-_Test.yml b/Packs/DeprecatedContent/TestPlaybooks/playbook-Dedup_-_Generic_-_Test.yml
similarity index 100%
rename from Packs/Base/TestPlaybooks/playbook-Dedup_-_Generic_-_Test.yml
rename to Packs/DeprecatedContent/TestPlaybooks/playbook-Dedup_-_Generic_-_Test.yml
diff --git a/Packs/Base/TestPlaybooks/playbook-Dedup_-_Generic_-_Test_CHANGELOG.md b/Packs/DeprecatedContent/TestPlaybooks/playbook-Dedup_-_Generic_-_Test_CHANGELOG.md
similarity index 100%
rename from Packs/Base/TestPlaybooks/playbook-Dedup_-_Generic_-_Test_CHANGELOG.md
rename to Packs/DeprecatedContent/TestPlaybooks/playbook-Dedup_-_Generic_-_Test_CHANGELOG.md
diff --git a/Packs/DeprecatedContent/pack_metadata.json b/Packs/DeprecatedContent/pack_metadata.json
index 68101c6c015..afd3604e0c2 100644
--- a/Packs/DeprecatedContent/pack_metadata.json
+++ b/Packs/DeprecatedContent/pack_metadata.json
@@ -2,7 +2,7 @@
"name": "Deprecated Content",
"description": "Deprecated Cortex XSOAR content pack.",
"support": "xsoar",
- "currentVersion": "1.1.4",
+ "currentVersion": "1.2.0",
"author": "Cortex XSOAR",
"url": "https://www.paloaltonetworks.com/cortex",
"email": "",
diff --git a/Packs/Base/TestPlaybooks/playbook-TestCommonPython.yml b/Packs/DeveloperTools/TestPlaybooks/playbook-TestCommonPython.yml
similarity index 100%
rename from Packs/Base/TestPlaybooks/playbook-TestCommonPython.yml
rename to Packs/DeveloperTools/TestPlaybooks/playbook-TestCommonPython.yml
diff --git a/Packs/DigitalGuardian/pack_metadata.json b/Packs/DigitalGuardian/pack_metadata.json
index 2bfbffe049b..217a3adf562 100644
--- a/Packs/DigitalGuardian/pack_metadata.json
+++ b/Packs/DigitalGuardian/pack_metadata.json
@@ -2,7 +2,6 @@
"name": "Digital Guardian",
"description": "Digital Guardian ARC Watchlist Integration",
"support": "partner",
- "serverMinVersion": "5.0.0",
"currentVersion": "1.0.0",
"author": "Digital Guardian",
"url": "https://digitalguardian.com",
diff --git a/Packs/Digital_Defense_FrontlineVM/Playbooks/playbook-Digital_Defense_FrontlineVM_-_PAN-OS_block_assets.yml b/Packs/Digital_Defense_FrontlineVM/Playbooks/playbook-Digital_Defense_FrontlineVM_-_PAN-OS_block_assets.yml
index 73fb1619900..79259476c20 100644
--- a/Packs/Digital_Defense_FrontlineVM/Playbooks/playbook-Digital_Defense_FrontlineVM_-_PAN-OS_block_assets.yml
+++ b/Packs/Digital_Defense_FrontlineVM/Playbooks/playbook-Digital_Defense_FrontlineVM_-_PAN-OS_block_assets.yml
@@ -247,7 +247,9 @@ tasks:
id: 963529eb-2f34-4072-801f-9eed161669d1
version: -1
name: PAN-OS - Block IP and URL - External Dynamic List
- description: ''
+ description: |-
+ This playbook blocks IP addresses and URLs using PAN-OS External Dynamic Lists.
+ It checks if the EDL configuration is in place with the 'PAN-OS EDL Setup' sub-playbook (otherwise the list will be configured), and adds the input IPs and URLs to the relevant lists.
playbookName: PAN-OS - Block IP and URL - External Dynamic List
type: playbook
iscommand: false
@@ -276,7 +278,7 @@ tasks:
view: |-
{
"position": {
- "x": 50,
+ "x": -130,
"y": 1790
}
}
@@ -407,8 +409,10 @@ tasks:
iscommand: false
brand: ''
nexttasks:
- 'Yes':
- - '11'
+ "No":
+ - "20"
+ "Yes":
+ - "11"
separatecontext: false
view: |-
{
@@ -437,8 +441,35 @@ tasks:
retriesinterval: 360
completeafterreplies: 1
replyOptions:
- - 'Yes'
- - 'No'
+ - "Yes"
+ - "No"
+ skipunavailable: false
+ quietmode: 0
+ "20":
+ id: "20"
+ taskid: c7f0e8e6-a289-4533-8027-f11b67c15bf7
+ type: title
+ task:
+ description: Playbook is done
+ id: c7f0e8e6-a289-4533-8027-f11b67c15bf7
+ version: -1
+ name: done
+ type: title
+ iscommand: false
+ brand: ""
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 290,
+ "y": 1805
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
view: |-
{
"linkLabelsPosition": {
@@ -447,8 +478,8 @@ view: |-
"paper": {
"dimensions": {
"height": 1835,
- "width": 810,
- "x": 50,
+ "width": 990,
+ "x": -130,
"y": 50
}
}
@@ -456,4 +487,4 @@ view: |-
inputs: []
outputs: []
tests:
- - No test - manual task
\ No newline at end of file
+ - No test - manual task
diff --git a/Packs/Digital_Defense_FrontlineVM/ReleaseNotes/1_0_1.md b/Packs/Digital_Defense_FrontlineVM/ReleaseNotes/1_0_1.md
new file mode 100644
index 00000000000..cf1d06f3482
--- /dev/null
+++ b/Packs/Digital_Defense_FrontlineVM/ReleaseNotes/1_0_1.md
@@ -0,0 +1,5 @@
+
diff --git a/Packs/Digital_Defense_FrontlineVM/pack_metadata.json b/Packs/Digital_Defense_FrontlineVM/pack_metadata.json
index fe6cf1c6d92..e8ad933441f 100644
--- a/Packs/Digital_Defense_FrontlineVM/pack_metadata.json
+++ b/Packs/Digital_Defense_FrontlineVM/pack_metadata.json
@@ -1,16 +1,16 @@
{
- "name": "Digital Defense Frontline VM",
- "description": "Use the Digital Defense Frontline VM to identify and evaluate the security and business risks of network devices and applications deployed as premise, cloud, or hybrid network-based implementations.",
- "support": "xsoar",
- "currentVersion": "1.0.0",
- "author": "Cortex XSOAR",
- "url": "https://www.paloaltonetworks.com/cortex",
- "email": "",
- "created": "2020-04-14T00:00:00Z",
- "categories": [
- "Vulnerability Management"
- ],
- "tags": [],
- "useCases": [],
- "keywords": []
-}
+ "name": "Digital Defense Frontline VM",
+ "description": "Use the Digital Defense Frontline VM to identify and evaluate the security and business risks of network devices and applications deployed as premise, cloud, or hybrid network-based implementations.",
+ "support": "xsoar",
+ "currentVersion": "1.0.1",
+ "author": "Cortex XSOAR",
+ "url": "https://www.paloaltonetworks.com/cortex",
+ "email": "",
+ "created": "2020-04-14T00:00:00Z",
+ "categories": [
+ "Vulnerability Management"
+ ],
+ "tags": [],
+ "useCases": [],
+ "keywords": []
+}
\ No newline at end of file
diff --git a/Packs/DomainTools/pack_metadata.json b/Packs/DomainTools/pack_metadata.json
index 74eb210b1cb..0efa7fb7e96 100644
--- a/Packs/DomainTools/pack_metadata.json
+++ b/Packs/DomainTools/pack_metadata.json
@@ -1,11 +1,11 @@
{
"name": "DomainTools",
"description": "Domain name, DNS and Internet OSINT-based cyber threat intelligence and cybercrime forensics products and data",
- "support": "xsoar",
+ "support": "partner",
"currentVersion": "1.0.0",
- "author": "Cortex XSOAR",
- "url": "https://www.paloaltonetworks.com/cortex",
- "email": "",
+ "author": "DomainTools",
+ "url": "https://www.domaintools.com/support/",
+ "email": "memberservices@domaintools.com",
"created": "2020-04-14T00:00:00Z",
"categories": [
"Data Enrichment & Threat Intelligence"
diff --git a/Packs/DuoAdminApi/TestPlaybooks/playbook-DuoAdminaAPITest.yml b/Packs/DuoAdminApi/TestPlaybooks/playbook-DuoAdminaAPITest.yml
index 888ad65f56a..cc67ae51c1b 100644
--- a/Packs/DuoAdminApi/TestPlaybooks/playbook-DuoAdminaAPITest.yml
+++ b/Packs/DuoAdminApi/TestPlaybooks/playbook-DuoAdminaAPITest.yml
@@ -2,6 +2,7 @@ id: DuoAdmin API test playbook
version: -1
name: DuoAdmin API test playbook
starttaskid: "0"
+description: Test playbook for DuoAdmin Integration
tasks:
"0":
id: "0"
@@ -13,6 +14,7 @@ tasks:
name: ""
iscommand: false
brand: ""
+ description: ''
nexttasks:
'#none#':
- "13"
@@ -20,8 +22,8 @@ tasks:
view: |-
{
"position": {
- "x": 450,
- "y": -160
+ "x": 50,
+ "y": 50
}
}
note: false
@@ -44,15 +46,12 @@ tasks:
nexttasks:
'#none#':
- "16"
- - "17"
- - "19"
- - "30"
separatecontext: false
view: |-
{
"position": {
- "x": 450,
- "y": 120
+ "x": 50,
+ "y": 370
}
}
note: false
@@ -85,8 +84,8 @@ tasks:
view: |-
{
"position": {
- "x": 970,
- "y": 470
+ "x": 1200,
+ "y": 1390
}
}
note: false
@@ -105,6 +104,7 @@ tasks:
type: title
iscommand: false
brand: ""
+ description: ''
nexttasks:
'#none#':
- "40"
@@ -112,8 +112,8 @@ tasks:
view: |-
{
"position": {
- "x": 450,
- "y": 1500
+ "x": 890,
+ "y": 1905
}
}
note: false
@@ -147,8 +147,8 @@ tasks:
view: |-
{
"position": {
- "x": 450,
- "y": -40
+ "x": 50,
+ "y": 195
}
}
note: false
@@ -169,7 +169,7 @@ tasks:
brand: ""
nexttasks:
"yes":
- - "9"
+ - "30"
separatecontext: false
conditions:
- label: "yes"
@@ -184,8 +184,8 @@ tasks:
view: |-
{
"position": {
- "x": 970,
- "y": 910
+ "x": 1200,
+ "y": 1565
}
}
note: false
@@ -204,6 +204,7 @@ tasks:
type: title
iscommand: false
brand: ""
+ description: ''
nexttasks:
'#none#':
- "36"
@@ -211,8 +212,8 @@ tasks:
view: |-
{
"position": {
- "x": -100,
- "y": 315
+ "x": 50,
+ "y": 545
}
}
note: false
@@ -231,6 +232,7 @@ tasks:
type: title
iscommand: false
brand: ""
+ description: ''
nexttasks:
'#none#':
- "3"
@@ -238,8 +240,8 @@ tasks:
view: |-
{
"position": {
- "x": 970,
- "y": 315
+ "x": 1200,
+ "y": 1230
}
}
note: false
@@ -258,6 +260,7 @@ tasks:
type: title
iscommand: false
brand: ""
+ description: ''
nexttasks:
'#none#':
- "20"
@@ -265,8 +268,8 @@ tasks:
view: |-
{
"position": {
- "x": 450,
- "y": 315
+ "x": 650,
+ "y": 1230
}
}
note: false
@@ -293,8 +296,8 @@ tasks:
view: |-
{
"position": {
- "x": 450,
- "y": 470
+ "x": 650,
+ "y": 1390
}
}
note: false
@@ -315,7 +318,7 @@ tasks:
brand: ""
nexttasks:
"yes":
- - "9"
+ - "17"
separatecontext: false
conditions:
- label: "yes"
@@ -347,8 +350,8 @@ tasks:
view: |-
{
"position": {
- "x": 450,
- "y": 920
+ "x": 650,
+ "y": 1565
}
}
note: false
@@ -378,8 +381,8 @@ tasks:
view: |-
{
"position": {
- "x": -100,
- "y": 635
+ "x": 50,
+ "y": 865
}
}
note: false
@@ -435,8 +438,8 @@ tasks:
view: |-
{
"position": {
- "x": -100,
- "y": 800
+ "x": 50,
+ "y": 1040
}
}
note: false
@@ -466,8 +469,8 @@ tasks:
view: |-
{
"position": {
- "x": -100,
- "y": 1140
+ "x": 50,
+ "y": 1390
}
}
note: false
@@ -499,8 +502,8 @@ tasks:
view: |-
{
"position": {
- "x": -100,
- "y": 980
+ "x": 50,
+ "y": 1215
}
}
note: false
@@ -521,7 +524,7 @@ tasks:
brand: ""
nexttasks:
"no":
- - "9"
+ - "19"
separatecontext: false
conditions:
- label: "no"
@@ -556,8 +559,8 @@ tasks:
view: |-
{
"position": {
- "x": -100,
- "y": 1330
+ "x": 50,
+ "y": 1565
}
}
note: false
@@ -576,6 +579,7 @@ tasks:
type: title
iscommand: false
brand: ""
+ description: ''
nexttasks:
'#none#':
- "31"
@@ -583,8 +587,8 @@ tasks:
view: |-
{
"position": {
- "x": 1410,
- "y": 315
+ "x": 1740,
+ "y": 1230
}
}
note: false
@@ -618,8 +622,8 @@ tasks:
view: |-
{
"position": {
- "x": 1410,
- "y": 470
+ "x": 1750,
+ "y": 1390
}
}
note: false
@@ -677,8 +681,8 @@ tasks:
view: |-
{
"position": {
- "x": 1410,
- "y": 635
+ "x": 1750,
+ "y": 1565
}
}
note: false
@@ -735,8 +739,8 @@ tasks:
view: |-
{
"position": {
- "x": 1410,
- "y": 860
+ "x": 1750,
+ "y": 1765
}
}
note: false
@@ -792,8 +796,8 @@ tasks:
view: |-
{
"position": {
- "x": 1410,
- "y": 1235
+ "x": 1750,
+ "y": 2170
}
}
note: false
@@ -827,8 +831,8 @@ tasks:
view: |-
{
"position": {
- "x": 1410,
- "y": 1055
+ "x": 1750,
+ "y": 1970
}
}
note: false
@@ -860,8 +864,8 @@ tasks:
view: |-
{
"position": {
- "x": -100,
- "y": 470
+ "x": 50,
+ "y": 690
}
}
note: false
@@ -897,8 +901,8 @@ tasks:
view: |-
{
"position": {
- "x": 450,
- "y": 1650
+ "x": 890,
+ "y": 2075
}
}
note: false
@@ -917,12 +921,13 @@ tasks:
type: title
iscommand: false
brand: ""
+ description: ''
separatecontext: false
view: |-
{
"position": {
- "x": 450,
- "y": 2030
+ "x": 890,
+ "y": 2640
}
}
note: false
@@ -966,8 +971,8 @@ tasks:
view: |-
{
"position": {
- "x": 450,
- "y": 1830
+ "x": 890,
+ "y": 2265
}
}
note: false
@@ -999,8 +1004,8 @@ tasks:
view: |-
{
"position": {
- "x": 940,
- "y": 1920
+ "x": 650,
+ "y": 2460
}
}
note: false
@@ -1008,17 +1013,21 @@ tasks:
ignoreworker: false
skipunavailable: false
quietmode: 0
+system: true
view: |-
{
- "linkLabelsPosition": {},
+ "linkLabelsPosition": {
+ "29_19_no": 0.68
+ },
"paper": {
"dimensions": {
- "height": 2255,
- "width": 1890,
- "x": -100,
- "y": -160
+ "height": 2655,
+ "width": 2080,
+ "x": 50,
+ "y": 50
}
}
}
inputs: []
outputs: []
+fromversion: 5.0.0
diff --git a/Packs/EDL/pack_metadata.json b/Packs/EDL/pack_metadata.json
index 385e9d2ca71..35e98d322a9 100644
--- a/Packs/EDL/pack_metadata.json
+++ b/Packs/EDL/pack_metadata.json
@@ -11,11 +11,7 @@
],
"tags": [],
"created": "2020-04-14T00:00:00Z",
- "updated": "2020-03-14T00:00:00Z",
- "beta": false,
- "deprecated": false,
"useCases": [],
"keywords": [],
- "dependencies": {},
- "displayedImages": []
+ "dependencies": {}
}
diff --git a/Packs/EWS/.pack-ignore b/Packs/EWS/.pack-ignore
index fd509604faf..1e14a456dbb 100644
--- a/Packs/EWS/.pack-ignore
+++ b/Packs/EWS/.pack-ignore
@@ -1,2 +1,5 @@
[file:playbook-Search_And_Delete_Emails_-_EWS.yml]
ignore=BA101
+
+[file:playbook-Process_Email_-_EWS.yml]
+ignore=BA101
diff --git a/Packs/EWS/.secrets-ignore b/Packs/EWS/.secrets-ignore
index ed3dd016074..5dcba5d3360 100644
--- a/Packs/EWS/.secrets-ignore
+++ b/Packs/EWS/.secrets-ignore
@@ -26,3 +26,19 @@ https://docs..com
test@dev.on.com
https://
https://docs
+2603:10a6:20b:6e:cafe::20
+10.152.3.80
+2603:10a6:10:72::46
+CALOVw6vw2eOojGdALXwyz_McutDOuDfs_qecQSkph0yxwY_5tg@mail.gmail.com
+2603:10a6:20b:6e::48
+209.85.161.69
+2002:a05:6808:106::
+67.231.156.123
+2603:10a6:20b:f0::25
+2603:10a6:20b:2e::32
+2603:10a6:803:d4::19
+2603:10a6:10:72:cafe::cb
+8.16.0.42
+CALOVw6uFpJ2pBoehvve4TwavOUe0BY-KRXnDkwbBh2hKEyQeYg@mail.gmail.com
+2603:10a6:20b:6c::19
+10.152.4.255
diff --git a/Packs/EWS/Integrations/EWSO365/CHANGELOG.md b/Packs/EWS/Integrations/EWSO365/CHANGELOG.md
new file mode 100644
index 00000000000..1ed0ad7fa4f
--- /dev/null
+++ b/Packs/EWS/Integrations/EWSO365/CHANGELOG.md
@@ -0,0 +1,2 @@
+## [Unreleased]
+- New Integration EWS O365
diff --git a/Packs/EWS/Integrations/EWSO365/EWSO365.py b/Packs/EWS/Integrations/EWSO365/EWSO365.py
new file mode 100644
index 00000000000..671502a070e
--- /dev/null
+++ b/Packs/EWS/Integrations/EWSO365/EWSO365.py
@@ -0,0 +1,2081 @@
+from typing import Dict
+
+import demistomock as demisto
+from CommonServerPython import *
+from CommonServerUserPython import *
+
+import sys
+import traceback
+import json
+import os
+import hashlib
+from datetime import timedelta
+from io import StringIO
+import logging
+import warnings
+import email
+from requests.exceptions import ConnectionError
+from collections import deque
+
+from multiprocessing import Process
+import exchangelib
+from exchangelib.errors import (
+ ErrorItemNotFound,
+ ResponseMessageError,
+ RateLimitError,
+ ErrorInvalidIdMalformed,
+ ErrorFolderNotFound,
+ ErrorMailboxStoreUnavailable,
+ ErrorMailboxMoveInProgress,
+ ErrorNameResolutionNoResults,
+ MalformedResponseError,
+)
+from exchangelib.items import Item, Message, Contact
+from exchangelib.services.common import EWSService, EWSAccountService
+from exchangelib.util import create_element, add_xml_child, MNS, TNS
+from exchangelib import (
+ IMPERSONATION,
+ Account,
+ EWSDateTime,
+ EWSTimeZone,
+ Configuration,
+ FileAttachment,
+ Version,
+ Folder,
+ HTMLBody,
+ Body,
+ ItemAttachment,
+ OAUTH2,
+ OAuth2AuthorizationCodeCredentials,
+ Identity,
+)
+from oauthlib.oauth2 import OAuth2Token
+from exchangelib.version import EXCHANGE_O365
+from exchangelib.protocol import BaseProtocol, NoVerifyHTTPAdapter
+
+# Ignore warnings print to stdout
+warnings.filterwarnings("ignore")
+
+""" Constants """
+
+APP_NAME = "ms-ews-o365"
+FOLDER_ID_LEN = 120
+MAX_INCIDENTS_PER_FETCH = 50
+
+# move results
+MOVED_TO_MAILBOX = "movedToMailbox"
+MOVED_TO_FOLDER = "movedToFolder"
+
+# item types
+FILE_ATTACHMENT_TYPE = "FileAttachment"
+ITEM_ATTACHMENT_TYPE = "ItemAttachment"
+ATTACHMENT_TYPE = "attachmentType"
+
+TOIS_PATH = "/root/Top of Information Store/"
+
+# context keys
+ATTACHMENT_ID = "attachmentId"
+ATTACHMENT_ORIGINAL_ITEM_ID = "originalItemId"
+NEW_ITEM_ID = "newItemId"
+MESSAGE_ID = "messageId"
+ITEM_ID = "itemId"
+ACTION = "action"
+MAILBOX = "mailbox"
+MAILBOX_ID = "mailboxId"
+FOLDER_ID = "id"
+
+# context paths
+CONTEXT_UPDATE_EWS_ITEM = "EWS.Items(val.{0} === obj.{0} || (val.{1} && obj.{1} && val.{1} === obj.{1}))".format(
+ ITEM_ID, MESSAGE_ID
+)
+CONTEXT_UPDATE_EWS_ITEM_FOR_ATTACHMENT = "EWS.Items(val.{0} == obj.{1})".format(
+ ITEM_ID, ATTACHMENT_ORIGINAL_ITEM_ID
+)
+CONTEXT_UPDATE_ITEM_ATTACHMENT = ".ItemAttachments(val.{0} == obj.{0})".format(
+ ATTACHMENT_ID
+)
+CONTEXT_UPDATE_FILE_ATTACHMENT = ".FileAttachments(val.{0} == obj.{0})".format(
+ ATTACHMENT_ID
+)
+CONTEXT_UPDATE_FOLDER = "EWS.Folders(val.{0} == obj.{0})".format(FOLDER_ID)
+
+# fetch params
+LAST_RUN_TIME = "lastRunTime"
+LAST_RUN_IDS = "ids"
+LAST_RUN_FOLDER = "folderName"
+ERROR_COUNTER = "errorCounter"
+
+# headers
+ITEMS_RESULTS_HEADERS = [
+ "sender",
+ "subject",
+ "hasAttachments",
+ "datetimeReceived",
+ "receivedBy",
+ "author",
+ "toRecipients",
+ "textBody",
+]
+
+""" Classes """
+
+
+class ProxyAdapter(requests.adapters.HTTPAdapter):
+ """
+ Proxy Adapter used to add PROXY to requests
+ """
+ def send(self, *args, **kwargs):
+ kwargs['proxies'] = handle_proxy()
+ return super().send(*args, **kwargs)
+
+
+class InsecureProxyAdapter(NoVerifyHTTPAdapter):
+ """
+ Insecure Proxy Adapter used to add PROXY and INSECURE to requests
+ NoVerifyHTTPAdapter is a built-in insecure HTTPAdapter class
+ """
+ def send(self, *args, **kwargs):
+ kwargs['proxies'] = handle_proxy()
+ return super().send(*args, **kwargs)
+
+
+class EWSClient:
+ def __init__(
+ self,
+ default_target_mailbox,
+ client_id,
+ client_secret,
+ tenant_id,
+ folder="Inbox",
+ is_public_folder=False,
+ request_timeout="120",
+ max_fetch=MAX_INCIDENTS_PER_FETCH,
+ self_deployed=True,
+ insecure=True,
+ proxy=False,
+ **kwargs,
+ ):
+ """
+ Client used to communicate with EWS
+ :param default_target_mailbox: Email address from which to fetch incidents
+ :param client_id: Application client ID
+ :param client_secret: Application client secret
+ :param folder: Name of the folder from which to fetch incidents
+ :param is_public_folder: Public Folder flag
+ :param request_timeout: Timeout (in seconds) for HTTP requests to Exchange Server
+ :param max_fetch: Max incidents per fetch
+ :param insecure: Trust any certificate (not secure)
+ """
+ BaseProtocol.TIMEOUT = int(request_timeout)
+ self.ews_server = "https://outlook.office365.com/EWS/Exchange.asmx/"
+ self.ms_client = MicrosoftClient(
+ tenant_id=tenant_id,
+ auth_id=client_id,
+ enc_key=client_secret,
+ app_name=APP_NAME,
+ base_url=self.ews_server,
+ verify=not insecure,
+ proxy=proxy,
+ self_deployed=self_deployed,
+ scope="https://outlook.office.com/.default",
+ )
+ self.folder_name = folder
+ self.is_public_folder = is_public_folder
+ self.access_type = kwargs.get('access_type') or IMPERSONATION
+ self.max_fetch = min(MAX_INCIDENTS_PER_FETCH, int(max_fetch))
+ self.last_run_ids_queue_size = 500
+ self.client_id = client_id
+ self.client_secret = client_secret
+ self.account_email = default_target_mailbox
+ self.config = self.__prepare(insecure)
+ self.protocol = BaseProtocol(self.config)
+
+ def __prepare(self, insecure):
+ """
+ Prepares the client PROTOCOL, CREDENTIALS and CONFIGURATION
+ :param insecure: Trust any certificate (not secure)
+ :return: OAuth 2 Configuration
+ """
+ BaseProtocol.HTTP_ADAPTER_CLS = InsecureProxyAdapter if insecure else ProxyAdapter
+ access_token = self.ms_client.get_access_token()
+ oauth2_token = OAuth2Token({"access_token": access_token})
+ self.credentials = credentials = OAuth2AuthorizationCodeCredentials(
+ client_id=self.client_id,
+ client_secret=self.client_secret,
+ access_token=oauth2_token,
+ )
+ # need to add identity for protocol OAuth header
+ self.credentials.identity = Identity(upn=self.account_email)
+ config_args = {
+ "credentials": credentials,
+ "auth_type": OAUTH2,
+ "version": Version(EXCHANGE_O365),
+ "service_endpoint": "https://outlook.office365.com/EWS/Exchange.asmx",
+ }
+
+ return Configuration(**config_args)
+
+ def get_account(self, target_mailbox=None):
+ """
+ Request an account from EWS
+ :param (Optional) target_mailbox: Mailbox associated with the requested account
+ :return: exchangelib Account
+ """
+ if not target_mailbox:
+ target_mailbox = self.account_email
+ return Account(
+ primary_smtp_address=target_mailbox,
+ autodiscover=False,
+ config=self.config,
+ access_type=self.access_type,
+ )
+
+ def get_items_from_mailbox(self, account, item_ids):
+ """
+ Request specific items from a mailbox associated with an account
+ :param account: EWS account or target_mailbox associated with that account
+ :param item_ids: item_ids of the requested items
+ :return: list of exchangelib Items
+ """
+ # allow user to pass target_mailbox as account
+ if isinstance(account, str):
+ account = self.get_account(account)
+ else:
+ account = self.get_account(self.account_email)
+ if type(item_ids) is not list:
+ item_ids = [item_ids]
+ items = [Item(id=x) for x in item_ids]
+ result = list(account.fetch(ids=items))
+ result = [x for x in result if not isinstance(x, ErrorItemNotFound)]
+ if len(result) != len(item_ids):
+ raise Exception(
+ "One or more items were not found. Check the input item ids"
+ )
+ return result
+
+ def get_item_from_mailbox(self, account, item_id):
+ """
+ Request a single item from a mailbox associated with an account
+ :param account: EWS account or target_mailbox associated with that account
+ :param item_id: item_id of the requested item
+ :return: exchangelib Item
+ """
+ result = self.get_items_from_mailbox(account, [item_id])
+ if len(result) == 0:
+ raise Exception(f"ItemId {str(item_id)} not found")
+ return result[0]
+
+ def get_attachments_for_item(self, item_id, account, attachment_ids=None):
+ """
+ Request attachments for an item
+ :param item_id: item_id of the item to retrieve attachments from
+ :param account: EWS account or target_mailbox associated with that account
+ :param (Optional) attachment_ids: attachment_ids: attachment_ids to retrieve
+ :return: list of exchangelib Item.attachments
+ """
+ item = self.get_item_from_mailbox(account, item_id)
+ attachments = []
+ attachment_ids = argToList(attachment_ids)
+ if item:
+ if item.attachments:
+ for attachment in item.attachments:
+ if (
+ attachment_ids
+ and attachment.attachment_id.id not in attachment_ids
+ ):
+ continue
+ attachments.append(attachment)
+
+ else:
+ raise Exception("Message item not found: " + item_id)
+
+ if attachment_ids and len(attachments) < len(attachment_ids):
+ raise Exception(
+ "Some attachment id did not found for message:" + str(attachment_ids)
+ )
+
+ return attachments
+
+ def is_default_folder(self, folder_path, is_public=None):
+ """
+ Is the given folder_path public
+ :param folder_path: folder path to check if is public
+ :param is_public: (Optional) if provided, will return this value
+ :return: Boolean
+ """
+ if is_public is not None:
+ return is_public
+
+ if folder_path == self.folder_name:
+ return self.is_public_folder
+
+ return False
+
+ def get_folder_by_path(self, path, account=None, is_public=False):
+ """
+ Retrieve folder by path
+ :param path: path of the folder
+ :param account: account associated with the requested path
+ :param is_public: is the requested folder public
+ :return: exchangelib Folder
+ """
+ if account is None:
+ account = self.get_account()
+ # handle exchange folder id
+ if len(path) == FOLDER_ID_LEN:
+ folders_map = account.root._folders_map
+ if path in folders_map:
+ return account.root._folders_map[path]
+ if is_public:
+ folder_result = account.public_folders_root
+ elif path == "AllItems":
+ folder_result = account.root
+ else:
+ folder_result = account.inbox.parent # Top of Information Store
+ path = path.replace("/", "\\")
+ path = path.split("\\")
+ for sub_folder_name in path:
+ folder_filter_by_name = [
+ x
+ for x in folder_result.children
+ if x.name.lower() == sub_folder_name.lower()
+ ]
+ if len(folder_filter_by_name) == 0:
+ raise Exception(f"No such folder {path}")
+ folder_result = folder_filter_by_name[0]
+
+ return folder_result
+
+
+class MarkAsJunk(EWSAccountService):
+ """
+ EWSAccountService class used for marking items as junk
+ """
+ SERVICE_NAME = "MarkAsJunk"
+
+ def call(self, item_id, move_item):
+ elements = list(
+ self._get_elements(
+ payload=self.get_payload(item_id=item_id, move_item=move_item)
+ )
+ )
+ for element in elements:
+ if isinstance(element, ResponseMessageError):
+ return str(element)
+ return "Success"
+
+ def get_payload(self, item_id, move_item):
+ junk = create_element(
+ f"m:{self.SERVICE_NAME}",
+ {"IsJunk": "true", "MoveItem": "true" if move_item else "false"},
+ )
+
+ items_list = create_element("m:ItemIds")
+ item_element = create_element("t:ItemId", {"Id": item_id})
+ items_list.append(item_element)
+ junk.append(items_list)
+
+ return junk
+
+
+class GetSearchableMailboxes(EWSService):
+ """
+ EWSAccountService class used for getting Searchable Mailboxes
+ """
+ SERVICE_NAME = "GetSearchableMailboxes"
+ element_container_name = f"{{{MNS}}}SearchableMailboxes"
+
+ @staticmethod
+ def parse_element(element):
+ return {
+ MAILBOX: element.find(f"{{{TNS}}}PrimarySmtpAddress").text
+ if element.find(f"{{{TNS}}}PrimarySmtpAddress") is not None
+ else None,
+ MAILBOX_ID: element.find(f"{{{TNS}}}ReferenceId").text
+ if element.find(f"{{{TNS}}}ReferenceId") is not None
+ else None,
+ "displayName": element.find(f"{{{TNS}}}DisplayName").text
+ if element.find(f"{{{TNS}}}DisplayName") is not None
+ else None,
+ "isExternal": element.find(f"{{{TNS}}}IsExternalMailbox").text
+ if element.find(f"{{{TNS}}}IsExternalMailbox") is not None
+ else None,
+ "externalEmailAddress": element.find(f"{{{TNS}}}ExternalEmailAddress").text
+ if element.find(f"{{{TNS}}}ExternalEmailAddress") is not None
+ else None,
+ }
+
+ def call(self):
+ elements = self._get_elements(payload=self.get_payload())
+ return [
+ self.parse_element(x)
+ for x in elements
+ if x.find(f"{{{TNS}}}ReferenceId").text
+ ]
+
+ def get_payload(self):
+ element = create_element(f"m:{self.SERVICE_NAME}")
+ return element
+
+
+class ExpandGroup(EWSService):
+ """
+ EWSAccountService class used for expanding groups
+ """
+ SERVICE_NAME = "ExpandDL"
+ element_container_name = f"{{{MNS}}}DLExpansion"
+
+ @staticmethod
+ def parse_element(element):
+ return {
+ MAILBOX: element.find(f"{{{TNS}}}EmailAddress").text
+ if element.find(f"{{{TNS}}}EmailAddress") is not None
+ else None,
+ "displayName": element.find(f"{{{TNS}}}Name").text
+ if element.find(f"{{{TNS}}}Name") is not None
+ else None,
+ "mailboxType": element.find(f"{{{TNS}}}MailboxType").text
+ if element.find(f"{{{TNS}}}MailboxType") is not None
+ else None,
+ }
+
+ def call(self, email_address, recursive_expansion=False):
+ try:
+ if recursive_expansion == "True":
+ group_members: Dict = {}
+ self.expand_group_recursive(email_address, group_members)
+ return list(group_members.values())
+ else:
+ return self.expand_group(email_address)
+ except ErrorNameResolutionNoResults:
+ demisto.results("No results were found.")
+ sys.exit()
+
+ def get_payload(self, email_address):
+ element = create_element(f"m:{self.SERVICE_NAME}")
+ mailbox_element = create_element("m:Mailbox")
+ add_xml_child(mailbox_element, "t:EmailAddress", email_address)
+ element.append(mailbox_element)
+ return element
+
+ def expand_group(self, email_address):
+ """
+ Expand given group
+ :param email_address: email address of the group to expand
+ :return: list dict with parsed expanded group data
+ """
+ elements = self._get_elements(payload=self.get_payload(email_address))
+ return [self.parse_element(x) for x in elements]
+
+ def expand_group_recursive(self, email_address, non_dl_emails, dl_emails=None):
+ """
+ Expand group recursively
+ :param email_address: email address of the group to expand
+ :param non_dl_emails: non distribution only emails
+ :param dl_emails: (Optional) distribution only emails
+ :return: Set of dl emails and non dl emails (returned via reference)
+ """
+ if dl_emails is None:
+ dl_emails = set()
+ if email_address in non_dl_emails or email_address in dl_emails:
+ return None
+ dl_emails.add(email_address)
+
+ for member in self.expand_group(email_address):
+ if (
+ member["mailboxType"] == "PublicDL"
+ or member["mailboxType"] == "PrivateDL"
+ ):
+ self.expand_group_recursive(member.get("mailbox"), non_dl_emails, dl_emails)
+ else:
+ if member["mailbox"] not in non_dl_emails:
+ non_dl_emails[member["mailbox"]] = member
+
+
+# If you are modifying this probably also need to modify in other files
+def exchangelib_cleanup():
+ key_protocols = list(exchangelib.protocol.CachingProtocol._protocol_cache.items())
+ try:
+ exchangelib.close_connections()
+ except Exception as ex:
+ demisto.error("Error was found in exchangelib cleanup, ignoring: {}".format(ex))
+ for key, protocol in key_protocols:
+ try:
+ if "thread_pool" in protocol.__dict__:
+ demisto.debug(
+ "terminating thread pool key{} id: {}".format(
+ key, id(protocol.thread_pool)
+ )
+ )
+ protocol.thread_pool.terminate()
+ del protocol.__dict__["thread_pool"]
+ else:
+ demisto.info(
+ "Thread pool not found (ignoring terminate) in protcol dict: {}".format(
+ dir(protocol.__dict__)
+ )
+ )
+ except Exception as ex:
+ demisto.error("Error with thread_pool.terminate, ignoring: {}".format(ex))
+
+
+""" LOGGING """
+
+log_stream = None
+log_handler = None
+
+
+def start_logging():
+ global log_stream
+ global log_handler
+ logging.raiseExceptions = False
+ if log_stream is None:
+ log_stream = StringIO()
+ log_handler = logging.StreamHandler(stream=log_stream)
+ log_handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
+ logger = logging.getLogger()
+ logger.addHandler(log_handler)
+ logger.setLevel(logging.DEBUG)
+
+
+""" Helper Functions """
+
+
+def get_attachment_name(attachment_name):
+ """
+ Retrieve attachment name or error string if none is provided
+ :param attachment_name: attachment name to retrieve
+ :return: string
+ """
+ if attachment_name is None or attachment_name == "":
+ return "demisto_untitled_attachment"
+ return attachment_name
+
+
+def get_entry_for_object(title, context_key, obj, headers=None):
+ """
+ Create an entry for a given object
+ :param title: Title of the human readable
+ :param context_key: Context key used for entry context
+ :param obj: Object to create entry for
+ :param headers: (Optional) headers used in the tableToMarkDown
+ :return: Entry object to be used with demisto.results()
+ """
+ if len(obj) == 0:
+ return "There is no output results"
+ if headers and isinstance(obj, dict):
+ headers = list(set(headers).intersection(set(obj.keys())))
+
+ return {
+ "Type": entryTypes["note"],
+ "Contents": obj,
+ "ContentsFormat": formats["json"],
+ "ReadableContentsFormat": formats["markdown"],
+ "HumanReadable": tableToMarkdown(title, obj, headers),
+ "EntryContext": {context_key: obj},
+ }
+
+
+def prepare_args(args):
+ """
+ Prepare arguments to be used as the API expects it
+ :param args: demisto args
+ :return: transformed args
+ """
+ args = dict((k.replace("-", "_"), v) for k, v in list(args.items()))
+ if "is_public" in args:
+ args["is_public"] = args["is_public"] == "True"
+ return args
+
+
+def get_limited_number_of_messages_from_qs(qs, limit):
+ """
+ Retrieve a limited number of messages from query search
+ :param qs: query search to execute
+ :param limit: limit on number of items to retrieve from search
+ :return: list of exchangelib.Message
+ """
+ count = 0
+ results = []
+ for item in qs:
+ if count == limit:
+ break
+ if isinstance(item, Message):
+ count += 1
+ results.append(item)
+ return results
+
+
+def keys_to_camel_case(value):
+ """
+ Transform keys from snake to camel case (does nothing if no snakes are found)
+ :param value: value to transform
+ :return: transformed value
+ """
+ def str_to_camel_case(snake_str):
+ components = snake_str.split("_")
+ return components[0] + "".join(x.title() for x in components[1:])
+
+ if value is None:
+ return None
+ if isinstance(value, (list, set)):
+ return list(map(keys_to_camel_case, value))
+ if isinstance(value, dict):
+ return dict(
+ (
+ keys_to_camel_case(k),
+ keys_to_camel_case(v) if isinstance(v, (list, dict)) else v,
+ )
+ for (k, v) in list(value.items())
+ )
+
+ return str_to_camel_case(value)
+
+
+def get_last_run(client: EWSClient, last_run=None):
+ """
+ Retrieve the last run time
+ :param client: EWS Client
+ :param last_run: (Optional) last run object
+ :return: last run dict
+ """
+ if not last_run or last_run.get(LAST_RUN_FOLDER) != client.folder_name:
+ last_run = {
+ LAST_RUN_TIME: None,
+ LAST_RUN_FOLDER: client.folder_name,
+ LAST_RUN_IDS: [],
+ }
+ if LAST_RUN_TIME in last_run and last_run[LAST_RUN_TIME] is not None:
+ last_run[LAST_RUN_TIME] = EWSDateTime.from_string(last_run[LAST_RUN_TIME])
+
+ # In case we have existing last_run data
+ if last_run.get(LAST_RUN_IDS) is None:
+ last_run[LAST_RUN_IDS] = []
+
+ return last_run
+
+
+def email_ec(item):
+ """
+ Create entry context for an email
+ :param item: exchangelib.Item
+ :return: entry context dict
+ """
+ return {
+ "CC": None
+ if not item.cc_recipients
+ else [mailbox.email_address for mailbox in item.cc_recipients],
+ "BCC": None
+ if not item.bcc_recipients
+ else [mailbox.email_address for mailbox in item.bcc_recipients],
+ "To": None
+ if not item.to_recipients
+ else [mailbox.email_address for mailbox in item.to_recipients],
+ "From": item.author.email_address,
+ "Subject": item.subject,
+ "Text": item.text_body,
+ "HTML": item.body,
+ "HeadersMap": {header.name: header.value for header in item.headers},
+ }
+
+
+def parse_item_as_dict(item, email_address=None, camel_case=False, compact_fields=False):
+ """
+ Parses an exchangelib item as a dict
+ :param item: exchangelib.Item to parse
+ :param (Optional) email_address: string mailbox
+ :param (Optional) camel_case: Is camel case
+ :param (Optional) compact_fields: Is compact fields
+ :return: Item as a dict
+ """
+ def parse_object_as_dict(obj):
+ raw_dict = {}
+ if obj is not None:
+ for field in obj.FIELDS:
+ raw_dict[field.name] = getattr(obj, field.name, None)
+ return raw_dict
+
+ def parse_folder_as_json(folder):
+ raw_dict = parse_object_as_dict(folder)
+ if "parent_folder_id" in raw_dict:
+ raw_dict["parent_folder_id"] = parse_folder_as_json(
+ raw_dict["parent_folder_id"]
+ )
+ if "effective_rights" in raw_dict:
+ raw_dict["effective_rights"] = parse_object_as_dict(
+ raw_dict["effective_rights"]
+ )
+ return raw_dict
+
+ raw_dict = {}
+ for field, value in item._field_vals():
+ if type(value) in [str, str, int, float, bool, Body, HTMLBody, None]:
+ raw_dict[field] = value
+ raw_dict["id"] = item.id
+ if getattr(item, "attachments", None):
+ raw_dict["attachments"] = [
+ parse_attachment_as_dict(item.id, x) for x in item.attachments
+ ]
+
+ for time_field in [
+ "datetime_sent",
+ "datetime_created",
+ "datetime_received",
+ "last_modified_time",
+ "reminder_due_by",
+ ]:
+ value = getattr(item, time_field, None)
+ if value:
+ raw_dict[time_field] = value.ewsformat()
+
+ for dict_field in [
+ "effective_rights",
+ "parent_folder_id",
+ "conversation_id",
+ "author",
+ "extern_id",
+ "received_by",
+ "received_representing",
+ "reply_to",
+ "sender",
+ "folder",
+ ]:
+ value = getattr(item, dict_field, None)
+ if value:
+ raw_dict[dict_field] = parse_object_as_dict(value)
+
+ for list_dict_field in ["headers", "cc_recipients", "to_recipients"]:
+ value = getattr(item, list_dict_field, None)
+ if value:
+ raw_dict[list_dict_field] = [parse_object_as_dict(x) for x in value]
+
+ if getattr(item, "folder", None):
+ raw_dict["folder"] = parse_folder_as_json(item.folder)
+ folder_path = (
+ item.folder.absolute[len(TOIS_PATH):]
+ if item.folder.absolute.startswith(TOIS_PATH)
+ else item.folder.absolute
+ )
+ raw_dict["folder_path"] = folder_path
+
+ if compact_fields:
+ new_dict = {}
+ # noinspection PyListCreation
+ fields_list = [
+ "datetime_created",
+ "datetime_received",
+ "datetime_sent",
+ "sender",
+ "has_attachments",
+ "importance",
+ "message_id",
+ "last_modified_time",
+ "size",
+ "subject",
+ "text_body",
+ "headers",
+ "body",
+ "folder_path",
+ "is_read",
+ ]
+
+ if "id" in raw_dict:
+ new_dict["itemId"] = raw_dict["id"]
+ fields_list.append("itemId")
+
+ for field in fields_list:
+ if field in raw_dict:
+ new_dict[field] = raw_dict.get(field)
+ for field in ["received_by", "author", "sender"]:
+ if field in raw_dict:
+ new_dict[field] = raw_dict.get(field, {}).get("email_address")
+ for field in ["to_recipients"]:
+ if field in raw_dict:
+ new_dict[field] = [x.get("email_address") for x in raw_dict[field]]
+ attachments = raw_dict.get("attachments")
+ if attachments and len(attachments) > 0:
+ file_attachments = [
+ x for x in attachments if x[ATTACHMENT_TYPE] == FILE_ATTACHMENT_TYPE
+ ]
+ if len(file_attachments) > 0:
+ new_dict["FileAttachments"] = file_attachments
+ item_attachments = [
+ x for x in attachments if x[ATTACHMENT_TYPE] == ITEM_ATTACHMENT_TYPE
+ ]
+ if len(item_attachments) > 0:
+ new_dict["ItemAttachments"] = item_attachments
+
+ raw_dict = new_dict
+
+ if camel_case:
+ raw_dict = keys_to_camel_case(raw_dict)
+
+ if email_address:
+ raw_dict[MAILBOX] = email_address
+ return raw_dict
+
+
+def get_entry_for_file_attachment(item_id, attachment):
+ """
+ Creates a file entry for an attachment
+ :param item_id: item_id of the attachment
+ :param attachment: attachment dict
+ :return: file entry dict for attachment
+ """
+ entry = fileResult(get_attachment_name(attachment.name), attachment.content)
+ entry["EntryContext"] = {
+ CONTEXT_UPDATE_EWS_ITEM_FOR_ATTACHMENT
+ + CONTEXT_UPDATE_FILE_ATTACHMENT: parse_attachment_as_dict(item_id, attachment)
+ }
+ return entry
+
+
+def parse_attachment_as_dict(item_id, attachment):
+ """
+ Creates a note entry for an attachment
+ :param item_id: item_id of the attachment
+ :param attachment: attachment dict
+ :return: note entry dict for attachment
+ """
+ try:
+ attachment_content = (
+ attachment.content
+ if isinstance(attachment, FileAttachment)
+ else attachment.item.mime_content
+ )
+ return {
+ ATTACHMENT_ORIGINAL_ITEM_ID: item_id,
+ ATTACHMENT_ID: attachment.attachment_id.id,
+ "attachmentName": get_attachment_name(attachment.name),
+ "attachmentSHA256": hashlib.sha256(attachment_content).hexdigest()
+ if attachment_content
+ else None,
+ "attachmentContentType": attachment.content_type,
+ "attachmentContentId": attachment.content_id,
+ "attachmentContentLocation": attachment.content_location,
+ "attachmentSize": attachment.size,
+ "attachmentLastModifiedTime": attachment.last_modified_time.ewsformat(),
+ "attachmentIsInline": attachment.is_inline,
+ ATTACHMENT_TYPE: FILE_ATTACHMENT_TYPE
+ if isinstance(attachment, FileAttachment)
+ else ITEM_ATTACHMENT_TYPE,
+ }
+ except TypeError as e:
+ if str(e) != "must be string or buffer, not None":
+ raise
+ return {
+ ATTACHMENT_ORIGINAL_ITEM_ID: item_id,
+ ATTACHMENT_ID: attachment.attachment_id.id,
+ "attachmentName": get_attachment_name(attachment.name),
+ "attachmentSHA256": None,
+ "attachmentContentType": attachment.content_type,
+ "attachmentContentId": attachment.content_id,
+ "attachmentContentLocation": attachment.content_location,
+ "attachmentSize": attachment.size,
+ "attachmentLastModifiedTime": attachment.last_modified_time.ewsformat(),
+ "attachmentIsInline": attachment.is_inline,
+ ATTACHMENT_TYPE: FILE_ATTACHMENT_TYPE
+ if isinstance(attachment, FileAttachment)
+ else ITEM_ATTACHMENT_TYPE,
+ }
+
+
+def get_entry_for_item_attachment(item_id, attachment, target_email):
+ """
+ Creates a note entry for an item attachment
+ :param item_id: Item id
+ :param attachment: exchangelib attachment
+ :param target_email: target email
+ :return: note entry dict for item attachment
+ """
+ item = attachment.item
+ dict_result = parse_attachment_as_dict(item_id, attachment)
+ dict_result.update(
+ parse_item_as_dict(item, target_email, camel_case=True, compact_fields=True)
+ )
+ title = f'EWS get attachment got item for "{target_email}", "{get_attachment_name(attachment.name)}"'
+
+ return get_entry_for_object(
+ title,
+ CONTEXT_UPDATE_EWS_ITEM_FOR_ATTACHMENT + CONTEXT_UPDATE_ITEM_ATTACHMENT,
+ dict_result,
+ )
+
+
+""" Command Functions """
+
+
+def get_expanded_group(client: EWSClient, email_address, recursive_expansion=False):
+ """
+ Retrieve expanded group command
+ :param client: EWS Client
+ :param email_address: Email address of the group to expand
+ :param (Optional) recursive_expansion: Whether to enable recursive expansion. Default is "False".
+ :return: Expanded groups output tuple
+ """
+ group_members = ExpandGroup(protocol=client.protocol).call(
+ email_address, recursive_expansion
+ )
+ group_details = {"name": email_address, "members": group_members}
+ output = {"EWS.ExpandGroup": group_details}
+ readable_output = tableToMarkdown("Group Members", group_members)
+ return readable_output, output, group_details
+
+
+def get_searchable_mailboxes(client: EWSClient):
+ """
+ Retrieve searchable mailboxes command
+ :param client: EWS Client
+ :return: Searchable mailboxes output tuple
+ """
+ searchable_mailboxes = GetSearchableMailboxes(protocol=client.protocol).call()
+ readable_output = tableToMarkdown(
+ "Searchable mailboxes", searchable_mailboxes, headers=["displayName", "mailbox"]
+ )
+ output = {"EWS.Mailboxes": searchable_mailboxes}
+ return readable_output, output, searchable_mailboxes
+
+
+def delete_attachments_for_message(
+ client: EWSClient, item_id, target_mailbox=None, attachment_ids=None
+):
+ """
+ Deletes attachments for a given message
+ :param client: EWS Client
+ :param item_id: item id
+ :param (Optional) target_mailbox: target mailbox
+ :param (Optional) attachment_ids: attachment ids to delete
+ :return: entries that were delted
+ """
+ attachments = client.get_attachments_for_item(
+ item_id, target_mailbox, attachment_ids
+ )
+ deleted_file_attachments = []
+ deleted_item_attachments = [] # type: ignore
+ for attachment in attachments:
+ attachment_deleted_action = {
+ ATTACHMENT_ID: attachment.attachment_id.id,
+ ACTION: "deleted",
+ }
+ if isinstance(attachment, FileAttachment):
+ deleted_file_attachments.append(attachment_deleted_action)
+ else:
+ deleted_item_attachments.append(attachment_deleted_action)
+ attachment.detach()
+
+ entries = []
+ if len(deleted_file_attachments) > 0:
+ entry = get_entry_for_object(
+ "Deleted file attachments",
+ "EWS.Items" + CONTEXT_UPDATE_FILE_ATTACHMENT,
+ deleted_file_attachments,
+ )
+ entries.append(entry)
+ if len(deleted_item_attachments) > 0:
+ entry = get_entry_for_object(
+ "Deleted item attachments",
+ "EWS.Items" + CONTEXT_UPDATE_ITEM_ATTACHMENT,
+ deleted_item_attachments,
+ )
+ entries.append(entry)
+
+ return entries
+
+
+def fetch_attachments_for_message(
+ client: EWSClient, item_id, target_mailbox=None, attachment_ids=None
+):
+ """
+ Fetches attachments for a message
+ :param client: EWS Client
+ :param item_id: item id
+ :param (Optional) target_mailbox: target mailbox
+ :param (Optional) attachment_ids: attachment ids
+ :return: list of parsed entries
+ """
+ account = client.get_account(target_mailbox)
+ attachments = client.get_attachments_for_item(item_id, account, attachment_ids)
+ entries = []
+ for attachment in attachments:
+ if isinstance(attachment, FileAttachment):
+ try:
+ if attachment.content:
+ entries.append(get_entry_for_file_attachment(item_id, attachment))
+ except TypeError as e:
+ if str(e) != "must be string or buffer, not None":
+ raise
+ else:
+ entries.append(
+ get_entry_for_item_attachment(
+ item_id, attachment, account.primary_smtp_address
+ )
+ )
+ if attachment.item.mime_content:
+ entries.append(
+ fileResult(
+ get_attachment_name(attachment.name) + ".eml",
+ attachment.item.mime_content,
+ )
+ )
+
+ return entries
+
+
+def move_item_between_mailboxes(
+ client: EWSClient,
+ item_id,
+ destination_mailbox,
+ destination_folder_path,
+ source_mailbox=None,
+ is_public=None,
+):
+ """
+ Moves item between mailboxes
+ :param client: EWS Client
+ :param item_id: item id
+ :param destination_mailbox: destination mailbox
+ :param destination_folder_path: destination folder path
+ :param (Optional) source_mailbox: source mailbox
+ :param (Optional) is_public: is the destination folder public
+ :return: Output tuple
+ """
+ source_account = client.get_account(source_mailbox)
+ destination_account = client.get_account(destination_mailbox)
+ is_public = client.is_default_folder(destination_folder_path, is_public)
+ destination_folder = client.get_folder_by_path(
+ destination_folder_path, destination_account, is_public
+ )
+ item = client.get_item_from_mailbox(source_account, item_id)
+
+ exported_items = source_account.export([item])
+ destination_account.upload([(destination_folder, exported_items[0])])
+ source_account.bulk_delete([item])
+
+ move_result = {
+ MOVED_TO_MAILBOX: destination_mailbox,
+ MOVED_TO_FOLDER: destination_folder_path,
+ }
+ readable_output = "Item was moved successfully."
+ output = {f"EWS.Items(val.itemId === '{item_id}')": move_result}
+ return readable_output, output, move_result
+
+
+def move_item(
+ client: EWSClient, item_id, target_folder_path, target_mailbox=None, is_public=None
+):
+ """
+ Moves an item within the same mailbox
+ :param client: EWS Client
+ :param item_id: item id
+ :param target_folder_path: target folder path
+ :param (Optional) target_mailbox: mailbox containing the item
+ :param (Optional) is_public: is the destination folder public
+ :return: Output tuple
+ """
+ account = client.get_account(target_mailbox)
+ is_public = client.is_default_folder(target_folder_path, is_public)
+ target_folder = client.get_folder_by_path(target_folder_path, is_public=is_public)
+ item = client.get_item_from_mailbox(account, item_id)
+ if isinstance(item, ErrorInvalidIdMalformed):
+ raise Exception("Item not found")
+ item.move(target_folder)
+ move_result = {
+ NEW_ITEM_ID: item.id,
+ ITEM_ID: item_id,
+ MESSAGE_ID: item.message_id,
+ ACTION: "moved",
+ }
+ readable_output = tableToMarkdown("Moved items", move_result)
+ output = {CONTEXT_UPDATE_EWS_ITEM: move_result}
+ return readable_output, output, move_result
+
+
+def delete_items(client: EWSClient, item_ids, delete_type, target_mailbox=None):
+ """
+ Delete items in a mailbox
+ :param client: EWS Client
+ :param item_ids: items ids to delete
+ :param delete_type: delte type soft/hard
+ :param (Optional) target_mailbox: mailbox containinf the items
+ :return: Output tuple
+ """
+ deleted_items = []
+ item_ids = argToList(item_ids)
+ items = client.get_items_from_mailbox(target_mailbox, item_ids)
+ delete_type = delete_type.lower()
+
+ for item in items:
+ item_id = item.id
+ if delete_type == "trash":
+ item.move_to_trash()
+ elif delete_type == "soft":
+ item.soft_delete()
+ elif delete_type == "hard":
+ item.delete()
+ else:
+ raise Exception(
+ f'invalid delete type: {delete_type}. Use "trash" \\ "soft" \\ "hard"'
+ )
+ deleted_items.append(
+ {
+ ITEM_ID: item_id,
+ MESSAGE_ID: item.message_id,
+ ACTION: f"{delete_type}-deleted",
+ }
+ )
+
+ readable_output = tableToMarkdown(
+ f"Deleted items ({delete_type} delete type)", deleted_items
+ )
+ output = {CONTEXT_UPDATE_EWS_ITEM: deleted_items}
+ return readable_output, output, deleted_items
+
+
+def search_items_in_mailbox(
+ client: EWSClient,
+ query=None,
+ message_id=None,
+ folder_path="",
+ limit=100,
+ target_mailbox=None,
+ is_public=None,
+ selected_fields="all",
+):
+ """
+ Search items in mailbox
+ :param client: EWS Client
+ :param (Optional) query: query to execute
+ :param (Optional) message_id: message ids to search
+ :param (Optional) folder_path: folder path to search
+ :param (Optional) limit: max amount of items to fetch
+ :param (Optional) target_mailbox: mailbox containing the items
+ :param (Optional) is_public: is the targeted folder public
+ :param (Optional) selected_fields: Selected fields
+ :return: Output tuple
+ """
+ if not query and not message_id:
+ return_error("Missing required argument. Provide query or message-id")
+
+ if message_id and message_id[0] != "<" and message_id[-1] != ">":
+ message_id = "<{}>".format(message_id)
+
+ account = client.get_account(target_mailbox)
+ limit = int(limit)
+ if folder_path.lower() == "inbox":
+ folders = [account.inbox]
+ elif folder_path:
+ is_public = client.is_default_folder(folder_path, is_public)
+ folders = [client.get_folder_by_path(folder_path, account, is_public)]
+ else:
+ folders = account.inbox.parent.walk() # pylint: disable=E1101
+
+ items = [] # type: ignore
+ selected_all_fields = selected_fields == "all"
+
+ if selected_all_fields:
+ restricted_fields = list([x.name for x in Message.FIELDS]) # type: ignore
+ else:
+ restricted_fields = set(argToList(selected_fields)) # type: ignore
+ restricted_fields.update(["id", "message_id"]) # type: ignore
+
+ for folder in folders:
+ if Message not in folder.supported_item_models:
+ continue
+ if query:
+ items_qs = folder.filter(query).only(*restricted_fields)
+ else:
+ items_qs = folder.filter(message_id=message_id).only(*restricted_fields)
+ items += get_limited_number_of_messages_from_qs(items_qs, limit)
+ if len(items) >= limit:
+ break
+
+ items = items[:limit]
+ searched_items_result = [
+ parse_item_as_dict(
+ item,
+ account.primary_smtp_address,
+ camel_case=True,
+ compact_fields=selected_all_fields,
+ )
+ for item in items
+ ]
+
+ if not selected_all_fields:
+ searched_items_result = [
+ {k: v for (k, v) in i.items() if k in keys_to_camel_case(restricted_fields)}
+ for i in searched_items_result
+ ]
+
+ for item in searched_items_result:
+ item["itemId"] = item.pop("id", "")
+
+ readable_output = tableToMarkdown(
+ "Searched items",
+ searched_items_result,
+ headers=ITEMS_RESULTS_HEADERS if selected_all_fields else None,
+ )
+ output = {CONTEXT_UPDATE_EWS_ITEM: searched_items_result}
+ return readable_output, output, searched_items_result
+
+
+def get_out_of_office_state(client: EWSClient, target_mailbox=None):
+ """
+ Retrieve get out of office state of the targeted mailbox
+ :param client: EWS Client
+ :param (Optional) target_mailbox: target mailbox
+ :return: Output tuple
+ """
+ account = client.get_account(target_mailbox)
+ oof = account.oof_settings
+ oof_dict = {
+ "state": oof.state, # pylint: disable=E1101
+ "externalAudience": getattr(oof, "external_audience", None),
+ "start": oof.start.ewsformat() if oof.start else None, # pylint: disable=E1101
+ "end": oof.end.ewsformat() if oof.end else None, # pylint: disable=E1101
+ "internalReply": getattr(oof, "internal_replay", None),
+ "externalReply": getattr(oof, "external_replay", None),
+ MAILBOX: account.primary_smtp_address,
+ }
+ readable_output = tableToMarkdown(
+ f"Out of office state for {account.primary_smtp_address}", oof_dict
+ )
+ output = {f"Account.Email(val.Address == obj.{MAILBOX}).OutOfOffice": oof_dict}
+ return readable_output, output, oof_dict
+
+
+def recover_soft_delete_item(
+ client: EWSClient,
+ message_ids,
+ target_folder_path="Inbox",
+ target_mailbox=None,
+ is_public=None,
+):
+ """
+ Recovers soft deleted items
+ :param client: EWS Client
+ :param message_ids: Message ids to recover
+ :param (Optional) target_folder_path: target folder path
+ :param (Optional) target_mailbox: target mailbox
+ :param (Optional) is_public: is the target folder public
+ :return:
+ """
+ account = client.get_account(target_mailbox)
+ is_public = client.is_default_folder(target_folder_path, is_public)
+ target_folder = client.get_folder_by_path(target_folder_path, account, is_public)
+ recovered_messages = []
+ message_ids = argToList(message_ids)
+
+ items_to_recover = account.recoverable_items_deletions.filter( # pylint: disable=E1101
+ message_id__in=message_ids
+ ).all() # pylint: disable=E1101
+
+ recovered_items = set()
+ for item in items_to_recover:
+ recovered_items.add(item)
+ if len(recovered_items) != len(message_ids):
+ missing_items = set(message_ids).difference(recovered_items)
+ raise Exception(
+ f"Some message ids are missing in recoverable items directory: {missing_items}"
+ )
+
+ for item in recovered_items:
+ item.move(target_folder)
+ recovered_messages.append(
+ {ITEM_ID: item.id, MESSAGE_ID: item.message_id, ACTION: "recovered"}
+ )
+
+ readable_output = tableToMarkdown("Recovered messages", recovered_messages)
+ output = {CONTEXT_UPDATE_EWS_ITEM: recovered_messages}
+ return readable_output, output, recovered_messages
+
+
+def get_contacts(client: EWSClient, limit, target_mailbox=None):
+ """
+ Retrieve contacts of the target mailbox or client mailbox
+ :param client: EWS Client
+ :param limit: max amount of contacts to retrieve
+ :param (Optional) target_mailbox: Target mailbox
+ :return:
+ """
+ def parse_physical_address(address):
+ result = {}
+ for attr in ["city", "country", "label", "state", "street", "zipcode"]:
+ result[attr] = getattr(address, attr, None)
+ return result
+
+ def parse_phone_number(phone_number):
+ result = {}
+ for attr in ["label", "phone_number"]:
+ result[attr] = getattr(phone_number, attr, None)
+ return result
+
+ def parse_contact(contact):
+ contact_dict = dict(
+ (k, v if not isinstance(v, EWSDateTime) else v.ewsformat())
+ for k, v in list(contact._field_vals())
+ if isinstance(v, str) or isinstance(v, EWSDateTime)
+ )
+ if isinstance(contact, Contact) and contact.physical_addresses:
+ contact_dict["physical_addresses"] = list(
+ map(parse_physical_address, contact.physical_addresses)
+ )
+ if isinstance(contact, Contact) and contact.phone_numbers:
+ contact_dict["phone_numbers"] = list(
+ map(parse_phone_number, contact.phone_numbers)
+ )
+ if (
+ isinstance(contact, Contact)
+ and contact.email_addresses
+ and len(contact.email_addresses) > 0
+ ):
+ contact_dict["emailAddresses"] = [x.email for x in contact.email_addresses]
+ contact_dict = keys_to_camel_case(contact_dict)
+ contact_dict = dict((k, v) for k, v in list(contact_dict.items()) if v)
+ contact_dict.pop("mimeContent", None)
+ contact_dict["originMailbox"] = target_mailbox
+ return contact_dict
+
+ account = client.get_account(target_mailbox)
+ contacts = []
+
+ for contact in account.contacts.all()[: int(limit)]: # pylint: disable=E1101
+ contacts.append(parse_contact(contact))
+ readable_output = tableToMarkdown(f"Email contacts for {target_mailbox}", contacts)
+ output = {"Account.Email(val.Address == obj.originMailbox).EwsContacts": contacts}
+ return readable_output, output, contacts
+
+
+def create_folder(client: EWSClient, new_folder_name, folder_path, target_mailbox=None):
+ """
+ Creates a folder in the target mailbox or the client mailbox
+ :param client: EWS Client
+ :param new_folder_name: new folder name
+ :param folder_path: path of the new folder
+ :param (Optional) target_mailbox: target mailbox
+ :return: Output tuple
+ """
+ account = client.get_account(target_mailbox)
+ full_path = os.path.join(folder_path, new_folder_name)
+ try:
+ if client.get_folder_by_path(full_path, account):
+ return f"Folder {full_path} already exists",
+ except Exception:
+ pass
+ parent_folder = client.get_folder_by_path(folder_path, account)
+ f = Folder(parent=parent_folder, name=new_folder_name)
+ f.save()
+ client.get_folder_by_path(full_path, account)
+ return f"Folder {full_path} created successfully",
+
+
+def find_folders(client: EWSClient, target_mailbox=None):
+ """
+ Finds folders in the mailbox
+ :param client: EWS Client
+ :param (Optional) target_mailbox: target mailbox
+ :return: Output tuple
+ """
+ account = client.get_account(target_mailbox)
+ root = account.root
+ if client.is_public_folder:
+ root = account.public_folders_root
+ folders = []
+ for f in root.walk(): # pylint: disable=E1101
+ folder = folder_to_context_entry(f)
+ folders.append(folder)
+ folders_tree = root.tree() # pylint: disable=E1101
+ readable_output = folders_tree
+ output = {"EWS.Folders(val.id == obj.id)": folders}
+ return readable_output, output, folders
+
+
+def mark_item_as_junk(client: EWSClient, item_id, move_items, target_mailbox=None):
+ """
+ Marks item as junk in the target mailbox or client mailbox
+ :param client: EWS Client
+ :param item_id: item ids to mark as junk
+ :param move_items: "yes" or "no" - to move or not to move to trash
+ :param (Optional) target_mailbox: target mailbox
+ :return:
+ """
+ account = client.get_account(target_mailbox)
+ move_items = move_items.lower() == "yes"
+ ews_result = MarkAsJunk(account=account).call(item_id=item_id, move_item=move_items)
+ mark_as_junk_result = {
+ ITEM_ID: item_id,
+ }
+ if ews_result == "Success":
+ mark_as_junk_result[ACTION] = "marked-as-junk"
+ else:
+ raise Exception("Failed mark-item-as-junk with error: " + ews_result)
+
+ readable_output = tableToMarkdown("Mark item as junk", mark_as_junk_result)
+ output = {CONTEXT_UPDATE_EWS_ITEM: mark_as_junk_result}
+ return readable_output, output, mark_as_junk_result
+
+
+def get_items_from_folder(
+ client: EWSClient,
+ folder_path,
+ limit=100,
+ target_mailbox=None,
+ is_public=None,
+ get_internal_item="no",
+):
+ """
+ Retrieve items from folder path
+ :param client: EWS Client
+ :param folder_path: folder path
+ :param (Optional) limit: max amount of items to retrieve
+ :param (Optional) target_mailbox: target mailbox
+ :param (Optional) is_public: is the folder public
+ :param (Optional) get_internal_item: should also retrieve internal items ("no" by default)
+ :return: Output tuple
+ """
+ account = client.get_account(target_mailbox)
+ limit = int(limit)
+ get_internal_item = get_internal_item == "yes"
+ is_public = client.is_default_folder(folder_path, is_public)
+ folder = client.get_folder_by_path(folder_path, account, is_public)
+ qs = folder.filter().order_by("-datetime_created")[:limit]
+ items = get_limited_number_of_messages_from_qs(qs, limit)
+ items_result = []
+
+ for item in items:
+ item_attachment = parse_item_as_dict(
+ item, account.primary_smtp_address, camel_case=True, compact_fields=True
+ )
+ for attachment in item.attachments:
+ if (
+ get_internal_item
+ and isinstance(attachment, ItemAttachment)
+ and isinstance(attachment.item, Message)
+ ):
+ # if found item attachment - switch item to the attchment
+ item_attachment = parse_item_as_dict(
+ attachment.item,
+ account.primary_smtp_address,
+ camel_case=True,
+ compact_fields=True,
+ )
+ break
+ items_result.append(item_attachment)
+
+ hm_headers = [
+ "sender",
+ "subject",
+ "hasAttachments",
+ "datetimeReceived",
+ "receivedBy",
+ "author",
+ "toRecipients",
+ "id",
+ ]
+ readable_output = tableToMarkdown(
+ "Items in folder " + folder_path, items_result, headers=hm_headers
+ )
+ output = {CONTEXT_UPDATE_EWS_ITEM: items_result}
+ return readable_output, output, items_result
+
+
+def get_items(client: EWSClient, item_ids, target_mailbox=None):
+ """
+ Get items from target mailbox or client mailbox
+ :param client: EWS Client
+ :param item_ids: item ids to retrieve
+ :param (Optional) target_mailbox: target mailbox to retrieve items from
+ :return:
+ """
+ item_ids = argToList(item_ids)
+ account = client.get_account(target_mailbox)
+ items = client.get_items_from_mailbox(account, item_ids)
+ items = [x for x in items if isinstance(x, Message)]
+ items_as_incidents = [parse_incident_from_item(x) for x in items]
+ items_to_context = [
+ parse_item_as_dict(x, account.primary_smtp_address, True, True) for x in items
+ ]
+ readable_output = tableToMarkdown(
+ "Get items", items_to_context, ITEMS_RESULTS_HEADERS
+ )
+ output = {
+ CONTEXT_UPDATE_EWS_ITEM: items_to_context,
+ "Email": [email_ec(item) for item in items],
+ }
+ return readable_output, output, items_as_incidents
+
+
+def get_folder(client: EWSClient, folder_path, target_mailbox=None, is_public=None):
+ """
+ Retrieve a folder from the target mailbox or client mailbox
+ :param client: EWS Client
+ :param folder_path: folder path to retrieve
+ :param (Optional) target_mailbox: target mailbox
+ :param (Optional) is_public: is the folder public
+ :return:
+ """
+ account = client.get_account(target_mailbox)
+ is_public = client.is_default_folder(folder_path, is_public)
+ folder = folder_to_context_entry(
+ client.get_folder_by_path(folder_path, account=account, is_public=is_public)
+ )
+ readable_output = tableToMarkdown(f"Folder {folder_path}", folder)
+ output = {CONTEXT_UPDATE_FOLDER: folder}
+ return readable_output, output, folder
+
+
+def folder_to_context_entry(f):
+ """
+ Create a context entry from a folder response
+ :param f: folder response
+ :return: dict context entry
+ """
+ try:
+ f_entry = {
+ "name": f.name,
+ "totalCount": f.total_count,
+ "id": f.id,
+ "childrenFolderCount": f.child_folder_count,
+ "changeKey": f.changekey,
+ }
+
+ if "unread_count" in [x.name for x in Folder.FIELDS]:
+ f_entry["unreadCount"] = f.unread_count
+ return f_entry
+ except AttributeError:
+ if isinstance(f, dict):
+ return {
+ "name": f.get("name"),
+ "totalCount": f.get("total_count"),
+ "id": f.get("id"),
+ "childrenFolderCount": f.get("child_folder_count"),
+ "changeKey": f.get("changekey"),
+ "unreadCount": f.get("unread_count"),
+ }
+
+
+def mark_item_as_read(
+ client: EWSClient, item_ids, operation="read", target_mailbox=None
+):
+ """
+ Marks item as read
+ :param client: EWS Client
+ :param item_ids: items ids to mark as read
+ :param (Optional) operation: operation to execute
+ :param (Optional) target_mailbox: target mailbox
+ :return: Output tuple
+ """
+ marked_items = []
+ item_ids = argToList(item_ids)
+ items = client.get_items_from_mailbox(target_mailbox, item_ids)
+ items = [x for x in items if isinstance(x, Message)]
+
+ for item in items:
+ item.is_read = operation == "read"
+ item.save()
+
+ marked_items.append(
+ {
+ ITEM_ID: item.id,
+ MESSAGE_ID: item.message_id,
+ ACTION: "marked-as-{}".format(operation),
+ }
+ )
+
+ readable_output = tableToMarkdown(
+ f"Marked items ({operation} marked operation)", marked_items
+ )
+ output = {CONTEXT_UPDATE_EWS_ITEM: marked_items}
+ return readable_output, output, marked_items
+
+
+def get_item_as_eml(client: EWSClient, item_id, target_mailbox=None):
+ """
+ Retrieve item as an eml
+ :param client: EWS Client
+ :param item_id: Item id to retrieve
+ :param (Optional) target_mailbox: target mailbox
+ :return: Output tuple
+ """
+ account = client.get_account(target_mailbox)
+ item = client.get_item_from_mailbox(account, item_id)
+
+ if item.mime_content:
+ mime_content = item.mime_content
+ if isinstance(mime_content, bytes):
+ email_content = email.message_from_bytes(mime_content)
+ else:
+ email_content = email.message_from_string(mime_content)
+ if item.headers:
+ attached_email_headers = [
+ (h, " ".join(map(str.strip, v.split("\r\n"))))
+ for (h, v) in list(email_content.items())
+ ]
+ for header in item.headers:
+ if (
+ header.name,
+ header.value,
+ ) not in attached_email_headers and header.name != "Content-Type":
+ email_content.add_header(header.name, header.value)
+
+ eml_name = item.subject if item.subject else "demisto_untitled_eml"
+ file_result = fileResult(eml_name + ".eml", email_content.as_string())
+ file_result = (
+ file_result if file_result else "Failed uploading eml file to war room"
+ )
+
+ return file_result
+
+
+def parse_incident_from_item(item):
+ """
+ Parses an incident from an item
+ :param item: item to parse
+ :return: Parsed item
+ """
+ incident = {}
+ labels = []
+
+ try:
+ incident["details"] = item.text_body or item.body
+ except AttributeError:
+ incident["details"] = item.body
+ incident["name"] = item.subject
+ labels.append({"type": "Email/subject", "value": item.subject})
+ incident["occurred"] = item.datetime_created.ewsformat()
+
+ # handle recipients
+ if item.to_recipients:
+ for recipient in item.to_recipients:
+ labels.append({"type": "Email", "value": recipient.email_address})
+
+ # handle cc
+ if item.cc_recipients:
+ for recipient in item.cc_recipients:
+ labels.append({"type": "Email/cc", "value": recipient.email_address})
+ # handle email from
+ if item.sender:
+ labels.append({"type": "Email/from", "value": item.sender.email_address})
+
+ # email format
+ email_format = ""
+ try:
+ if item.text_body:
+ labels.append({"type": "Email/text", "value": item.text_body})
+ email_format = "text"
+ except AttributeError:
+ pass
+ if item.body:
+ labels.append({"type": "Email/html", "value": item.body})
+ email_format = "HTML"
+ labels.append({"type": "Email/format", "value": email_format})
+
+ # handle attachments
+ if item.attachments:
+ incident["attachment"] = []
+ for attachment in item.attachments:
+ file_result = None
+ label_attachment_type = None
+ label_attachment_id_type = None
+ if isinstance(attachment, FileAttachment):
+ try:
+ if attachment.content:
+ # file attachment
+ label_attachment_type = "attachments"
+ label_attachment_id_type = "attachmentId"
+
+ # save the attachment
+ file_name = get_attachment_name(attachment.name)
+ file_result = fileResult(file_name, attachment.content)
+
+ # check for error
+ if file_result["Type"] == entryTypes["error"]:
+ demisto.error(file_result["Contents"])
+ raise Exception(file_result["Contents"])
+
+ # save attachment to incident
+ incident["attachment"].append(
+ {
+ "path": file_result["FileID"],
+ "name": get_attachment_name(attachment.name),
+ }
+ )
+ except TypeError as e:
+ if str(e) != "must be string or buffer, not None":
+ raise
+ continue
+ else:
+ # other item attachment
+ label_attachment_type = "attachmentItems"
+ label_attachment_id_type = "attachmentItemsId"
+
+ # save the attachment
+ if attachment.item.mime_content:
+ attached_email = email.message_from_string(
+ attachment.item.mime_content
+ )
+ if attachment.item.headers:
+ attached_email_headers = [
+ (h, " ".join(map(str.strip, v.split("\r\n"))))
+ for (h, v) in list(attached_email.items())
+ ]
+ for header in attachment.item.headers:
+ if (
+ (header.name, header.value)
+ not in attached_email_headers
+ and header.name != "Content-Type"
+ ):
+ attached_email.add_header(header.name, header.value)
+
+ file_result = fileResult(
+ get_attachment_name(attachment.name) + ".eml",
+ attached_email.as_string(),
+ )
+
+ if file_result:
+ # check for error
+ if file_result["Type"] == entryTypes["error"]:
+ demisto.error(file_result["Contents"])
+ raise Exception(file_result["Contents"])
+
+ # save attachment to incident
+ incident["attachment"].append(
+ {
+ "path": file_result["FileID"],
+ "name": get_attachment_name(attachment.name) + ".eml",
+ }
+ )
+
+ labels.append(
+ {
+ "type": label_attachment_type,
+ "value": get_attachment_name(attachment.name),
+ }
+ )
+ labels.append(
+ {"type": label_attachment_id_type, "value": attachment.attachment_id.id}
+ )
+
+ # handle headers
+ if item.headers:
+ headers = []
+ for header in item.headers:
+ labels.append(
+ {
+ "type": "Email/Header/{}".format(header.name),
+ "value": str(header.value),
+ }
+ )
+ headers.append("{}: {}".format(header.name, header.value))
+ labels.append({"type": "Email/headers", "value": "\r\n".join(headers)})
+
+ # handle item id
+ if item.message_id:
+ labels.append({"type": "Email/MessageId", "value": str(item.message_id)})
+
+ if item.id:
+ labels.append({"type": "Email/ID", "value": item.id})
+ labels.append({"type": "Email/itemId", "value": item.id})
+
+ # handle conversion id
+ if item.conversation_id:
+ labels.append({"type": "Email/ConversionID", "value": item.conversation_id.id})
+
+ incident["labels"] = labels
+ incident["rawJSON"] = json.dumps(parse_item_as_dict(item, None), ensure_ascii=False)
+
+ return incident
+
+
+def fetch_emails_as_incidents(client: EWSClient, last_run):
+ """
+ Fetch incidents
+ :param client: EWS Client
+ :param last_run: last run dict
+ :return:
+ """
+ last_run = get_last_run(client, last_run)
+
+ try:
+ last_emails = fetch_last_emails(
+ client,
+ client.folder_name,
+ last_run.get(LAST_RUN_TIME),
+ last_run.get(LAST_RUN_IDS),
+ )
+
+ ids = deque(
+ last_run.get(LAST_RUN_IDS, []), maxlen=client.last_run_ids_queue_size
+ )
+ incidents = []
+ incident: Dict[str, str] = {}
+ for item in last_emails:
+ if item.message_id:
+ ids.append(item.message_id)
+ incident = parse_incident_from_item(item)
+ incidents.append(incident)
+
+ if len(incidents) >= client.max_fetch:
+ break
+
+ last_run_time = incident.get("occurred", last_run.get(LAST_RUN_TIME))
+ if isinstance(last_run_time, EWSDateTime):
+ last_run_time = last_run_time.ewsformat()
+
+ new_last_run = {
+ LAST_RUN_TIME: last_run_time,
+ LAST_RUN_FOLDER: client.folder_name,
+ LAST_RUN_IDS: list(ids),
+ ERROR_COUNTER: 0,
+ }
+
+ demisto.setLastRun(new_last_run)
+ return incidents
+
+ except RateLimitError:
+ if LAST_RUN_TIME in last_run:
+ last_run[LAST_RUN_TIME] = last_run[LAST_RUN_TIME].ewsformat()
+ if ERROR_COUNTER not in last_run:
+ last_run[ERROR_COUNTER] = 0
+ last_run[ERROR_COUNTER] += 1
+ demisto.setLastRun(last_run)
+ if last_run[ERROR_COUNTER] > 2:
+ raise
+ return []
+
+
+def fetch_last_emails(
+ client: EWSClient, folder_name="Inbox", since_datetime=None, exclude_ids=None
+):
+ """
+ Fetches last emails
+ :param client: EWS client
+ :param (Optional) folder_name: folder name to pull from
+ :param (Optional) since_datetime: items will be searched after this datetime
+ :param (Optional) exclude_ids: exclude ids from fetch
+ :return: list of exchangelib.Items
+ """
+ qs = client.get_folder_by_path(folder_name, is_public=client.is_public_folder)
+ if since_datetime:
+ qs = qs.filter(datetime_received__gte=since_datetime)
+ else:
+ last_10_min = EWSDateTime.now(tz=EWSTimeZone.timezone("UTC")) - timedelta(
+ minutes=10
+ )
+ qs = qs.filter(last_modified_time__gte=last_10_min)
+ qs = qs.filter().only(*[x.name for x in Message.FIELDS])
+ qs = qs.filter().order_by("datetime_received")
+
+ result = qs.all()
+ result = [x for x in result if isinstance(x, Message)]
+ if exclude_ids and len(exclude_ids) > 0:
+ exclude_ids = set(exclude_ids)
+ result = [x for x in result if x.message_id not in exclude_ids]
+ return result
+
+
+def test_module(client: EWSClient, max_fetch):
+ """
+ test-module
+ * Max incidents per fetch <= MAX_INCIDENTS_PER_FETCH
+ * Account can be retrieved
+ * Account has read rights
+ * Test access to fetch folder
+ :param client: EWS Client
+ :param max_fetch: Max fetches per incident
+ :return: "ok"
+ """
+ try:
+ if int(max_fetch) > MAX_INCIDENTS_PER_FETCH:
+ return_error(f'Error - Max incidents per fetch cannot be greater than {MAX_INCIDENTS_PER_FETCH}. '
+ f'You provided: {max_fetch}')
+ account = client.get_account()
+ if not account.root.effective_rights.read: # pylint: disable=E1101
+ raise Exception(
+ "Success to authenticate, but user has no permissions to read from the mailbox. "
+ "Need to delegate the user permissions to the mailbox - "
+ "please read integration documentation and follow the instructions"
+ )
+ client.get_folder_by_path(
+ client.folder_name, account, client.is_public_folder
+ ).test_access()
+ except ErrorFolderNotFound as e:
+ if "Top of Information Store" in str(e):
+ raise Exception(
+ "Success to authenticate, but user probably has no permissions to read from the specific folder."
+ "Check user permissions. You can try !ews-find-folders command to "
+ "get all the folders structure that the user has permissions to"
+ )
+
+ return "ok"
+
+
+def sub_main():
+ is_test_module = False
+ params = demisto.params()
+ client = EWSClient(**params)
+ args = prepare_args(demisto.args())
+ start_logging()
+ try:
+ command = demisto.command()
+ # commands that return a single note result
+ normal_commands = {
+ "ews-get-searchable-mailboxes": get_searchable_mailboxes,
+ "ews-move-item-between-mailboxes": move_item_between_mailboxes,
+ "ews-move-item": move_item,
+ "ews-delete-items": delete_items,
+ "ews-search-mailbox": search_items_in_mailbox,
+ "ews-get-contacts": get_contacts,
+ "ews-get-out-of-office": get_out_of_office_state,
+ "ews-recover-messages": recover_soft_delete_item,
+ "ews-create-folder": create_folder,
+ "ews-mark-item-as-junk": mark_item_as_junk,
+ "ews-find-folders": find_folders,
+ "ews-get-items-from-folder": get_items_from_folder,
+ "ews-get-items": get_items,
+ "ews-get-folder": get_folder,
+ "ews-expand-group": get_expanded_group,
+ "ews-mark-items-as-read": mark_item_as_read,
+ }
+
+ # commands that may return multiple results or non-note result
+ special_output_commands = {
+ "ews-get-attachment": fetch_attachments_for_message,
+ "ews-delete-attachment": delete_attachments_for_message,
+ "ews-get-items-as-eml": get_item_as_eml,
+ }
+ # system commands:
+ if command == "test-module":
+ is_test_module = True
+ demisto.results(test_module(client, params.get('max_fetch')))
+ elif command == "fetch-incidents":
+ last_run = demisto.getLastRun()
+ incidents = fetch_emails_as_incidents(client, last_run)
+ demisto.incidents(incidents)
+
+ # special outputs commands
+ elif command in special_output_commands:
+ demisto.results(special_output_commands[command](client, **args)) # type: ignore[operator]
+
+ # normal commands
+ else:
+ output = normal_commands[command](client, **args) # type: ignore[operator]
+ return_outputs(*output)
+
+ except Exception as e:
+ start_logging()
+ debug_log = log_stream.getvalue() # type: ignore[union-attr]
+ error_message_simple = ""
+
+ # Office365 regular maintenance case
+ if isinstance(e, ErrorMailboxStoreUnavailable) or isinstance(
+ e, ErrorMailboxMoveInProgress
+ ):
+ log_message = (
+ "Office365 is undergoing load balancing operations. "
+ "As a result, the service is temporarily unavailable."
+ )
+ if demisto.command() == "fetch-incidents":
+ demisto.info(log_message)
+ demisto.incidents([])
+ sys.exit(0)
+ if is_test_module:
+ demisto.results(
+ log_message + " Please retry the instance configuration test."
+ )
+ sys.exit(0)
+ error_message_simple = log_message + " Please retry your request."
+
+ if isinstance(e, ConnectionError):
+ error_message_simple = (
+ "Could not connect to the server.\n"
+ f"Additional information: {str(e)}"
+ )
+ else:
+ if is_test_module and isinstance(e, MalformedResponseError):
+ error_message_simple = (
+ "Got invalid response from the server.\n"
+ )
+
+ # Legacy error handling
+ if "Status code: 401" in debug_log:
+ error_message_simple = (
+ "Got unauthorized from the server. "
+ )
+
+ if "Status code: 503" in debug_log:
+ error_message_simple = (
+ "Got timeout from the server. "
+ "Probably the server is not reachable with the current settings. "
+ )
+
+ if not error_message_simple:
+ error_message = error_message_simple = str(e)
+ else:
+ error_message = error_message_simple + "\n" + str(e)
+
+ stacktrace = traceback.format_exc()
+ if stacktrace:
+ error_message += "\nFull stacktrace:\n" + stacktrace
+
+ if debug_log:
+ error_message += "\nFull debug log:\n" + debug_log
+
+ if demisto.command() == "fetch-incidents":
+ raise
+ if demisto.command() == "ews-search-mailbox" and isinstance(e, ValueError):
+ return_error(
+ message="Selected invalid field, please specify valid field name.",
+ error=e,
+ )
+ if is_test_module:
+ demisto.results(error_message_simple)
+ else:
+ demisto.results(
+ {
+ "Type": entryTypes["error"],
+ "ContentsFormat": formats["text"],
+ "Contents": error_message_simple,
+ }
+ )
+ demisto.error(f"{e.__class__.__name__}: {error_message}")
+ finally:
+ exchangelib_cleanup()
+ if log_stream:
+ try:
+ logging.getLogger().removeHandler(log_handler) # type: ignore
+ log_stream.close()
+ except Exception as ex:
+ demisto.error(
+ "EWS: unexpected exception when trying to remove log handler: {}".format(
+ ex
+ )
+ )
+
+
+def process_main():
+ """setup stdin to fd=0 so we can read from the server"""
+ sys.stdin = os.fdopen(0, "r")
+ sub_main()
+
+
+def main():
+ # When running big queries, like 'ews-search-mailbox' the memory might not freed by the garbage
+ # collector. `separate_process` flag will run the integration on a separate process that will prevent
+ # memory leakage.
+ separate_process = demisto.params().get("separate_process", False)
+ demisto.debug("Running as separate_process: {}".format(separate_process))
+ if separate_process:
+ try:
+ p = Process(target=process_main)
+ p.start()
+ p.join()
+ except Exception as ex:
+ demisto.error("Failed starting Process: {}".format(ex))
+ else:
+ sub_main()
+
+
+from MicrosoftApiModule import * # noqa: E402
+
+if __name__ in ("__main__", "__builtin__", "builtins"):
+ main()
diff --git a/Packs/EWS/Integrations/EWSO365/EWSO365.yml b/Packs/EWS/Integrations/EWSO365/EWSO365.yml
new file mode 100644
index 00000000000..dc99373365a
--- /dev/null
+++ b/Packs/EWS/Integrations/EWSO365/EWSO365.yml
@@ -0,0 +1,1051 @@
+category: Messaging
+commonfields:
+ id: EWSO365
+ version: -1
+configuration:
+- additionalinfo: ID can be received from the admin consent procedure - see Detailed Instructions.
+ display: ID / Application ID
+ name: client_id
+ required: true
+ type: 4
+- additionalinfo: Token can be received from the admin consent procedure - see Detailed Instructions.
+ display: Token / Tenant ID
+ name: tenant_id
+ required: true
+ type: 4
+- additionalinfo: Key can be received from the admin consent procedure - see Detailed Instructions.
+ display: Key / Application Secret
+ name: client_secret
+ required: true
+ type: 4
+- additionalinfo: Mailbox to run commands on and to fetch incidents from.
+ display: Email Address
+ name: default_target_mailbox
+ required: true
+ type: 0
+- additionalinfo: Supports Exchange Folder ID and sub-folders e.g. Inbox/Phishing.
+ defaultvalue: Inbox
+ display: Name of the folder from which to fetch incidents
+ name: folder
+ required: true
+ type: 0
+- defaultvalue: 'false'
+ display: Public Folder
+ name: is_public_folder
+ required: false
+ type: 8
+- display: Fetch incidents
+ name: isFetch
+ required: false
+ type: 8
+- display: Incident type
+ name: incidentType
+ required: false
+ type: 13
+- defaultvalue: '50'
+ display: Max incidents per fetch (up to 50)
+ name: max_fetch
+ required: false
+ type: 0
+- defaultvalue: '120'
+ display: Timeout (in seconds) for HTTP requests to Exchange Server
+ name: request_timeout
+ required: false
+ type: 0
+- display: Trust any certificate (not secure)
+ name: insecure
+ required: false
+ type: 8
+- defaultvalue: 'false'
+ display: Use system proxy settings
+ name: proxy
+ required: false
+ type: 8
+- defaultvalue: 'false'
+ display: Run as a separate process (protects against memory depletion)
+ name: separate_process
+ required: false
+ type: 8
+- display: Use a self deployed Azure Application
+ hidden: false
+ name: self_deployed
+ required: false
+ type: 8
+description: Exchange Web Services and Office 365 (mail)
+display: EWS O365
+name: EWSO365
+script:
+ commands:
+ - arguments:
+ - default: false
+ description: The ID of the email message for which to get the attachments.
+ isArray: false
+ name: item-id
+ required: true
+ secret: false
+ - default: false
+ description: The mailbox in which this attachment was found. If empty, the default
+ mailbox is used. Otherwise the user might require impersonation rights to
+ this mailbox.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ - default: false
+ description: The attachments ids to get. If none - all attachments will be retrieve
+ from the message. Support multiple attachments with comma-separated value
+ or array.
+ isArray: true
+ name: attachment-ids
+ required: false
+ secret: false
+ deprecated: false
+ description: Retrieves the actual attachments from an item (email message). To
+ get all attachments for a message, only specify the item-id argument.
+ execution: false
+ name: ews-get-attachment
+ outputs:
+ - contextPath: EWS.Items.FileAttachments.attachmentId
+ description: The attachment ID. Used for file attachments only.
+ type: string
+ - contextPath: EWS.Items.FileAttachments.attachmentName
+ description: The attachment name. Used for file attachments only.
+ type: string
+ - contextPath: EWS.Items.FileAttachments.attachmentSHA256
+ description: The SHA256 hash of the attached file.
+ type: string
+ - contextPath: EWS.Items.FileAttachments.attachmentLastModifiedTime
+ description: The attachment last modified time. Used for file attachments only.
+ type: date
+ - contextPath: EWS.Items.ItemAttachments.datetimeCreated
+ description: The created time of the attached email.
+ type: date
+ - contextPath: EWS.Items.ItemAttachments.datetimeReceived
+ description: The received time of the attached email.
+ type: date
+ - contextPath: EWS.Items.ItemAttachments.datetimeSent
+ description: The sent time of the attached email.
+ type: date
+ - contextPath: EWS.Items.ItemAttachments.receivedBy
+ description: The received by address of the attached email.
+ type: string
+ - contextPath: EWS.Items.ItemAttachments.subject
+ description: The subject of the attached email.
+ type: string
+ - contextPath: EWS.Items.ItemAttachments.textBody
+ description: The body of the attached email (as text).
+ type: string
+ - contextPath: EWS.Items.ItemAttachments.headers
+ description: The headers of the attached email.
+ type: Unknown
+ - contextPath: EWS.Items.ItemAttachments.hasAttachments
+ description: Whether the attached email has attachments.
+ type: boolean
+ - contextPath: EWS.Items.ItemAttachments.itemId
+ description: The attached email item ID.
+ type: string
+ - contextPath: EWS.Items.ItemAttachments.toRecipients
+ description: A list of recipient email addresses for the attached email.
+ type: Unknown
+ - contextPath: EWS.Items.ItemAttachments.body
+ description: The body of the attached email (as HTML).
+ type: string
+ - contextPath: EWS.Items.ItemAttachments.attachmentSHA256
+ description: The SHA256 hash of the attached email (as EML file).
+ type: string
+ - contextPath: EWS.Items.ItemAttachments.FileAttachments.attachmentSHA256
+ description: SHA256 hash of the attached files inside of the attached email.
+ type: string
+ - contextPath: EWS.Items.ItemAttachments.ItemAttachments.attachmentSHA256
+ description: SHA256 hash of the attached emails inside of the attached email.
+ type: string
+ - contextPath: EWS.Items.ItemAttachments.isRead
+ description: The read status of the attachment.
+ type: String
+ - arguments:
+ - default: false
+ description: The ID of the email message for which to delete attachments.
+ isArray: false
+ name: item-id
+ required: true
+ secret: false
+ - default: false
+ description: The mailbox in which this attachment was found. If empty, the default
+ mailbox is used. Otherwise the user might require impersonation rights to
+ this mailbox.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ - default: false
+ description: A comma-separated list (or array) of attachment IDs to delete. If empty, all
+ attachments will be deleted from the message.
+ isArray: true
+ name: attachment-ids
+ required: false
+ secret: false
+ deprecated: false
+ description: Deletes the attachments of an item (email message).
+ execution: false
+ name: ews-delete-attachment
+ outputs:
+ - contextPath: EWS.Items.FileAttachments.attachmentId
+ description: The ID of the deleted attachment, in case of file attachment.
+ type: string
+ - contextPath: EWS.Items.ItemAttachments.attachmentId
+ description: The ID of the deleted attachment, in case of other attachment (for
+ example, "email").
+ type: string
+ - contextPath: EWS.Items.FileAttachments.action
+ description: 'The deletion action in case of file attachment. This is a constant
+ value: ''deleted''.'
+ type: string
+ - contextPath: EWS.Items.ItemAttachments.action
+ description: 'The deletion action in case of other attachment (for example,
+ "email"). This is a constant value: ''deleted''.'
+ type: string
+ - deprecated: false
+ description: Returns a list of searchable mailboxes. This command requires eDiscovery
+ permissions to the Exchange Server. For more information, see the EWSv2 integration
+ documentation.
+ execution: false
+ name: ews-get-searchable-mailboxes
+ outputs:
+ - contextPath: EWS.Mailboxes.mailbox
+ description: Addresses of the searchable mailboxes.
+ type: string
+ - contextPath: EWS.Mailboxes.mailboxId
+ description: IDs of the searchable mailboxes.
+ type: string
+ - contextPath: EWS.Mailboxes.displayName
+ description: The email display name.
+ type: string
+ - contextPath: EWS.Mailboxes.isExternal
+ description: Whether the mailbox is external.
+ type: boolean
+ - contextPath: EWS.Mailboxes.externalEmailAddress
+ description: The external email address.
+ type: string
+ - arguments:
+ - default: false
+ description: The ID of the item to move.
+ isArray: false
+ name: item-id
+ required: true
+ secret: false
+ - default: false
+ description: The path to the folder to which to move the item. Complex paths
+ are supported, for example, "Inbox\Phishing".
+ isArray: false
+ name: target-folder-path
+ required: true
+ secret: false
+ - default: false
+ description: The mailbox on which to run the command.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ - auto: PREDEFINED
+ default: false
+ description: Whether the target folder is a public folder. Can be "True" or "False".
+ isArray: false
+ name: is-public
+ predefined:
+ - 'True'
+ - 'False'
+ required: false
+ secret: false
+ deprecated: false
+ description: Move an item to different folder in the mailbox.
+ execution: false
+ name: ews-move-item
+ outputs:
+ - contextPath: EWS.Items.newItemID
+ description: The item ID after move.
+ type: string
+ - contextPath: EWS.Items.messageID
+ description: The item message ID.
+ type: string
+ - contextPath: EWS.Items.itemId
+ description: The original item ID.
+ type: string
+ - contextPath: EWS.Items.action
+ description: The action taken. The value will be "moved".
+ type: string
+ - arguments:
+ - default: false
+ description: The item IDs to delete.
+ isArray: false
+ name: item-ids
+ required: true
+ secret: false
+ - default: false
+ defaultValue: soft
+ description: Deletion type. Can be "trash", "soft", or "hard".
+ isArray: false
+ name: delete-type
+ required: true
+ secret: false
+ - default: false
+ description: The mailbox on which to run the command.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ deprecated: false
+ description: Delete items from mailbox.
+ execution: false
+ name: ews-delete-items
+ outputs:
+ - contextPath: EWS.Items.itemId
+ description: The deleted item ID.
+ type: string
+ - contextPath: EWS.Items.messageId
+ description: The deleted message ID.
+ type: string
+ - contextPath: EWS.Items.action
+ description: The deletion action. Can be 'trash-deleted', 'soft-deleted', or
+ 'hard-deleted'.
+ type: string
+ - arguments:
+ - default: false
+ description: 'The search query string. For more information about the query
+ syntax, see the Microsoft documentation: https://msdn.microsoft.com/en-us/library/ee693615.aspx'
+ isArray: false
+ name: query
+ required: false
+ secret: false
+ - default: false
+ description: The folder path in which to search. If empty, searches all
+ folders in the mailbox.
+ isArray: false
+ name: folder-path
+ required: false
+ secret: false
+ - default: false
+ defaultValue: '50'
+ description: Maximum number of results to return. The default is 50.
+ isArray: false
+ name: limit
+ required: false
+ secret: false
+ - default: false
+ description: The mailbox on which to apply the search.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ - auto: PREDEFINED
+ default: false
+ description: Whether the folder is a public folder. Can be "True" or "False".
+ isArray: false
+ name: is-public
+ predefined:
+ - 'True'
+ - 'False'
+ required: false
+ secret: false
+ - default: false
+ description: The message ID of the email. This will be ignored if a query argument
+ is provided.
+ isArray: false
+ name: message-id
+ required: false
+ secret: false
+ - default: false
+ defaultValue: all
+ description: A comma-separated list of fields to retrieve.
+ isArray: true
+ name: selected-fields
+ predefined:
+ - ''
+ required: false
+ secret: false
+ deprecated: false
+ description: Searches for items in the specified mailbox. Specific permissions
+ are needed for this operation to search in a target mailbox other than the default.
+ execution: false
+ name: ews-search-mailbox
+ outputs:
+ - contextPath: EWS.Items.itemId
+ description: The email item ID.
+ type: string
+ - contextPath: EWS.Items.hasAttachments
+ description: Whether the email has attachments.
+ type: boolean
+ - contextPath: EWS.Items.datetimeReceived
+ description: Received time of the email.
+ type: date
+ - contextPath: EWS.Items.datetimeSent
+ description: Sent time of the email.
+ type: date
+ - contextPath: EWS.Items.headers
+ description: Email headers (list).
+ type: Unknown
+ - contextPath: EWS.Items.sender
+ description: Sender email address of the email.
+ type: string
+ - contextPath: EWS.Items.subject
+ description: Subject of the email.
+ type: string
+ - contextPath: EWS.Items.textBody
+ description: Body of the email (as text).
+ type: string
+ - contextPath: EWS.Items.size
+ description: Email size.
+ type: number
+ - contextPath: EWS.Items.toRecipients
+ description: List of email recipients addresses.
+ type: Unknown
+ - contextPath: EWS.Items.receivedBy
+ description: Received by address of the email.
+ type: Unknown
+ - contextPath: EWS.Items.messageId
+ description: Email message ID.
+ type: string
+ - contextPath: EWS.Items.body
+ description: Body of the email (as HTML).
+ type: string
+ - contextPath: EWS.Items.FileAttachments.attachmentId
+ description: Attachment ID of the file attachment.
+ type: unknown
+ - contextPath: EWS.Items.ItemAttachments.attachmentId
+ description: Attachment ID of the item attachment.
+ type: unknown
+ - contextPath: EWS.Items.FileAttachments.attachmentName
+ description: Attachment name of the file attachment.
+ type: unknown
+ - contextPath: EWS.Items.ItemAttachments.attachmentName
+ description: Attachment name of the item attachment.
+ type: unknown
+ - contextPath: EWS.Items.isRead
+ description: The read status of the email.
+ type: String
+ - arguments:
+ - default: false
+ description: The mailbox for which to retrieve the contacts.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ - default: false
+ defaultValue: '50'
+ description: Maximum number of results to return. The default is 50.
+ isArray: false
+ name: limit
+ required: false
+ secret: false
+ deprecated: false
+ description: Retrieves contacts for a specified mailbox.
+ execution: false
+ name: ews-get-contacts
+ outputs:
+ - contextPath: Account.Email.EwsContacts.displayName
+ description: The contact name.
+ type: Unknown
+ - contextPath: Account.Email.EwsContacts.lastModifiedTime
+ description: The time that the contact was last modified.
+ type: Unknown
+ - contextPath: Account.Email.EwsContacts.emailAddresses
+ description: Phone numbers of the contact.
+ type: Unknown
+ - contextPath: Account.Email.EwsContacts.physicalAddresses
+ description: Physical addresses of the contact.
+ type: Unknown
+ - contextPath: Account.Email.EwsContacts.phoneNumbers.phoneNumber
+ description: Email addresses of the contact.
+ type: Unknown
+ - arguments:
+ - default: false
+ description: The mailbox for which to get the out-of-office status.
+ isArray: false
+ name: target-mailbox
+ required: true
+ secret: false
+ deprecated: false
+ description: Retrieves the out-of-office status for a specified mailbox.
+ execution: false
+ name: ews-get-out-of-office
+ outputs:
+ - contextPath: Account.Email.OutOfOffice.state
+ description: 'Out-of-office state. Result can be: Enabled, Scheduled, Disabled.'
+ type: Unknown
+ - contextPath: Account.Email.OutOfOffice.externalAudience
+ description: Out-of-office external audience. Can be "None", "Known", or "All".
+ type: Unknown
+ - contextPath: Account.Email.OutOfOffice.start
+ description: Out-of-office start date.
+ type: Unknown
+ - contextPath: Account.Email.OutOfOffice.end
+ description: Out-of-office end date.
+ type: Unknown
+ - contextPath: Account.Email.OutOfOffice.internalReply
+ description: Out-of-office internal reply.
+ type: Unknown
+ - contextPath: Account.Email.OutOfOffice.externalReply
+ description: Out-of-office external reply.
+ type: Unknown
+ - contextPath: Account.Email.OutOfOffice.mailbox
+ description: Out-of-office mailbox.
+ type: Unknown
+ - arguments:
+ - default: false
+ description: A comma-separated list of message IDs. Run the py-ews-delete-items command
+ to retrieve the message IDs
+ isArray: false
+ name: message-ids
+ required: true
+ secret: false
+ - default: false
+ defaultValue: Inbox
+ description: The folder path to recover the messages to.
+ isArray: false
+ name: target-folder-path
+ required: true
+ secret: false
+ - default: false
+ description: The mailbox in which the messages found. If empty, will use the
+ default mailbox. If you specify a different mailbox, you might need impersonation
+ rights to the mailbox.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ - auto: PREDEFINED
+ default: false
+ description: Whether the target folder is a Public Folder. Can be "True" or "False".
+ isArray: false
+ name: is-public
+ predefined:
+ - 'True'
+ - 'False'
+ required: false
+ secret: false
+ deprecated: false
+ description: Recovers messages that were soft-deleted.
+ execution: false
+ name: ews-recover-messages
+ outputs:
+ - contextPath: EWS.Items.itemId
+ description: The item ID of the recovered item.
+ type: Unknown
+ - contextPath: EWS.Items.messageId
+ description: The message ID of the recovered item.
+ type: Unknown
+ - contextPath: EWS.Items.action
+ description: The action taken on the item. The value will be 'recovered'.
+ type: Unknown
+ - arguments:
+ - default: false
+ description: The name of the new folder.
+ isArray: false
+ name: new-folder-name
+ required: true
+ secret: false
+ - default: false
+ defaultValue: Inbox
+ description: Path to locate the new folder. Exchange folder ID is also supported.
+ isArray: false
+ name: folder-path
+ required: true
+ secret: false
+ - default: false
+ description: The mailbox in which to create the folder.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ deprecated: false
+ description: Creates a new folder in a specified mailbox.
+ execution: false
+ name: ews-create-folder
+ - arguments:
+ - default: false
+ description: The item ID to mark as junk.
+ isArray: false
+ name: item-id
+ required: true
+ secret: false
+ - auto: PREDEFINED
+ default: false
+ defaultValue: 'yes'
+ description: Whether to move the item from the original folder to the junk folder. Can be "yes" or "no". The default is "yes".
+ isArray: false
+ name: move-items
+ predefined:
+ - 'yes'
+ - 'no'
+ required: false
+ secret: false
+ - default: false
+ description: If empty, will use the default mailbox. If you specify a different
+ mailbox, you might need impersonation rights to the mailbox.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ deprecated: false
+ description: 'Marks an item as junk. This is commonly used to block an email address.
+ For more information, see the Microsoft documentation: https://msdn.microsoft.com/en-us/library/office/dn481311(v=exchg.150).aspx'
+ execution: false
+ name: ews-mark-item-as-junk
+ - arguments:
+ - default: false
+ description: The mailbox on which to apply the command.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ deprecated: false
+ description: Retrieves information for folders for a specified mailbox. Only folders
+ with read permissions will be returned. Your visual folders on the mailbox,
+ such as "Inbox", are under the folder "Top of Information Store".
+ execution: false
+ name: ews-find-folders
+ outputs:
+ - contextPath: EWS.Folders.name
+ description: Folder name.
+ type: string
+ - contextPath: EWS.Folders.id
+ description: Folder ID.
+ type: string
+ - contextPath: EWS.Folders.totalCount
+ description: Number of items in the folder.
+ type: Unknown
+ - contextPath: EWS.Folders.unreadCount
+ description: Number of unread items in the folder.
+ type: number
+ - contextPath: EWS.Folders.changeKey
+ description: Folder change key.
+ type: number
+ - contextPath: EWS.Folders.childrenFolderCount
+ description: Number of sub-folders.
+ type: number
+ - arguments:
+ - default: false
+ description: The folder path from which to get the items.
+ isArray: false
+ name: folder-path
+ required: true
+ secret: false
+ - default: false
+ defaultValue: '50'
+ description: Maximum number of items to return. The default is 50.
+ isArray: false
+ name: limit
+ required: false
+ secret: false
+ - default: false
+ description: The mailbox to on which to apply the command.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ - auto: PREDEFINED
+ default: false
+ description: Whether the folder is a public folder. Can be "True" or "False". The default is "False".
+ isArray: false
+ name: is-public
+ predefined:
+ - 'True'
+ - 'False'
+ required: false
+ secret: false
+ - auto: PREDEFINED
+ default: false
+ defaultValue: 'no'
+ description: If the email item contains another email as an attachment (EML
+ or MSG file), whether to retrieve the EML/MSG file attachment. Can be "yes"
+ or "no". The default is "no".
+ isArray: false
+ name: get-internal-item
+ predefined:
+ - 'yes'
+ - 'no'
+ required: false
+ secret: false
+ deprecated: false
+ description: Retrieves items from a specified folder in a mailbox. The items are
+ order by the item created time, most recent is first.
+ execution: false
+ name: ews-get-items-from-folder
+ outputs:
+ - contextPath: EWS.Items.itemId
+ description: The item ID of the email.
+ type: string
+ - contextPath: EWS.Items.hasAttachments
+ description: Whether the email has attachments.
+ type: boolean
+ - contextPath: EWS.Items.datetimeReceived
+ description: Received time of the email.
+ type: date
+ - contextPath: EWS.Items.datetimeSent
+ description: Sent time of the email.
+ type: date
+ - contextPath: EWS.Items.headers
+ description: Email headers (list).
+ type: Unknown
+ - contextPath: EWS.Items.sender
+ description: Sender mail address of the email.
+ type: string
+ - contextPath: EWS.Items.subject
+ description: Subject of the email.
+ type: string
+ - contextPath: EWS.Items.textBody
+ description: Body of the email (as text).
+ type: string
+ - contextPath: EWS.Items.size
+ description: Email size.
+ type: number
+ - contextPath: EWS.Items.toRecipients
+ description: Email recipients addresses (list).
+ type: Unknown
+ - contextPath: EWS.Items.receivedBy
+ description: Received by address of the email.
+ type: Unknown
+ - contextPath: EWS.Items.messageId
+ description: Email message ID.
+ type: string
+ - contextPath: EWS.Items.body
+ description: Body of the email (as HTML).
+ type: string
+ - contextPath: EWS.Items.FileAttachments.attachmentId
+ description: Attachment ID of file attachment.
+ type: unknown
+ - contextPath: EWS.Items.ItemAttachments.attachmentId
+ description: Attachment ID of the item attachment.
+ type: unknown
+ - contextPath: EWS.Items.FileAttachments.attachmentName
+ description: Attachment name of the file attachment.
+ type: unknown
+ - contextPath: EWS.Items.ItemAttachments.attachmentName
+ description: Attachment name of the item attachment.
+ type: unknown
+ - contextPath: EWS.Items.isRead
+ description: The read status of the email.
+ type: String
+ - arguments:
+ - default: false
+ description: A comma-separated list if item IDs.
+ isArray: true
+ name: item-ids
+ required: true
+ secret: false
+ - default: false
+ description: The mailbox on which to run the command.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ deprecated: false
+ description: Retrieves items by item ID.
+ execution: false
+ name: ews-get-items
+ outputs:
+ - contextPath: EWS.Items.itemId
+ description: The email item ID.
+ type: string
+ - contextPath: EWS.Items.hasAttachments
+ description: Whether the email has attachments.
+ type: boolean
+ - contextPath: EWS.Items.datetimeReceived
+ description: Received time of the email.
+ type: date
+ - contextPath: EWS.Items.datetimeSent
+ description: Sent time of the email.
+ type: date
+ - contextPath: EWS.Items.headers
+ description: Email headers (list).
+ type: Unknown
+ - contextPath: EWS.Items.sender
+ description: Sender mail address of the email.
+ type: string
+ - contextPath: EWS.Items.subject
+ description: Subject of the email.
+ type: string
+ - contextPath: EWS.Items.textBody
+ description: Body of the email (as text).
+ type: string
+ - contextPath: EWS.Items.size
+ description: Email size.
+ type: number
+ - contextPath: EWS.Items.toRecipients
+ description: Email recipients addresses (list).
+ type: Unknown
+ - contextPath: EWS.Items.receivedBy
+ description: Received by address of the email.
+ type: Unknown
+ - contextPath: EWS.Items.messageId
+ description: Email message ID.
+ type: string
+ - contextPath: EWS.Items.body
+ description: Body of the email (as HTML).
+ type: string
+ - contextPath: EWS.Items.FileAttachments.attachmentId
+ description: Attachment ID of the file attachment.
+ type: unknown
+ - contextPath: EWS.Items.ItemAttachments.attachmentId
+ description: Attachment ID of the item attachment.
+ type: unknown
+ - contextPath: EWS.Items.FileAttachments.attachmentName
+ description: Attachment name of the file attachment.
+ type: unknown
+ - contextPath: EWS.Items.ItemAttachments.attachmentName
+ description: Attachment name of the item attachment.
+ type: unknown
+ - contextPath: EWS.Items.isRead
+ description: The read status of the email.
+ type: String
+ - contextPath: Email.CC
+ description: Email addresses CC'ed to the email.
+ type: String
+ - contextPath: Email.BCC
+ description: Email addresses BCC'ed to the email.
+ type: String
+ - contextPath: Email.To
+ description: The recipient of the email.
+ type: String
+ - contextPath: Email.From
+ description: The sender of the email.
+ type: String
+ - contextPath: Email.Subject
+ description: The subject of the email.
+ type: String
+ - contextPath: Email.Text
+ description: The plain-text version of the email.
+ type: String
+ - contextPath: Email.HTML
+ description: The HTML version of the email.
+ type: String
+ - contextPath: Email.HeadersMap
+ description: The headers of the email.
+ type: String
+ - arguments:
+ - default: false
+ description: The item ID to move.
+ isArray: false
+ name: item-id
+ required: true
+ secret: false
+ - default: false
+ description: The folder in the destination mailbox to which to move the item.
+ You can specify a complex path, for example, "Inbox\Phishing".
+ isArray: false
+ name: destination-folder-path
+ required: true
+ secret: false
+ - default: false
+ description: The mailbox to which to move the item.
+ isArray: false
+ name: destination-mailbox
+ required: true
+ secret: false
+ - default: false
+ description: The mailbox from which to move the item (conventionally called
+ the "target-mailbox", the target mailbox on which to run the command).
+ isArray: false
+ name: source-mailbox
+ required: false
+ secret: false
+ - auto: PREDEFINED
+ default: false
+ description: Whether the destination folder is a Public Folder. Can be "True" or "False". Default is "False".
+ isArray: false
+ name: is-public
+ predefined:
+ - 'True'
+ - 'False'
+ required: false
+ secret: false
+ deprecated: false
+ description: Moves an item from one mailbox to different mailbox.
+ execution: false
+ name: ews-move-item-between-mailboxes
+ outputs:
+ - contextPath: EWS.Items.movedToMailbox
+ description: The mailbox wo which the item was moved.
+ type: string
+ - contextPath: EWS.Items.movedToFolder
+ description: The folder to which the item was moved.
+ type: string
+ - contextPath: EWS.Items.action
+ description: The action taken on the item. The value will be "moved".
+ type: string
+ - arguments:
+ - default: false
+ description: The mailbox on which to run the search.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ - default: true
+ defaultValue: AllItems
+ description: The path of the folder to retrieve. If empty, will retrieve the
+ folder "AllItems".
+ isArray: false
+ name: folder-path
+ required: false
+ secret: false
+ - auto: PREDEFINED
+ default: false
+ description: Whether the folder is a Public Folder. Default is "False".
+ isArray: false
+ name: is-public
+ predefined:
+ - 'True'
+ - 'False'
+ required: false
+ secret: false
+ deprecated: false
+ description: Retrieves a single folder.
+ execution: false
+ name: ews-get-folder
+ outputs:
+ - contextPath: EWS.Folders.id
+ description: Folder ID.
+ type: string
+ - contextPath: EWS.Folders.name
+ description: Folder name.
+ type: string
+ - contextPath: EWS.Folders.changeKey
+ description: Folder change key.
+ type: string
+ - contextPath: EWS.Folders.totalCount
+ description: Total number of emails in the folder.
+ type: number
+ - contextPath: EWS.Folders.childrenFolderCount
+ description: Number of sub-folders.
+ type: number
+ - contextPath: EWS.Folders.unreadCount
+ description: Number of unread emails in the folder.
+ type: number
+ - arguments:
+ - default: false
+ description: Email address of the group to expand.
+ isArray: false
+ name: email-address
+ required: true
+ secret: false
+ - auto: PREDEFINED
+ default: false
+ defaultValue: 'False'
+ description: Whether to enable recursive expansion. Can be "True" or "False". Default is "False".
+ isArray: false
+ name: recursive-expansion
+ predefined:
+ - 'True'
+ - 'False'
+ required: false
+ secret: false
+ deprecated: false
+ description: Expands a distribution list to display all members. By default, expands
+ only first layer of the distribution list. If recursive-expansion is "True",
+ the command expands nested distribution lists and returns all members.
+ execution: false
+ name: ews-expand-group
+ - arguments:
+ - default: false
+ description: A comma-separated list of item IDs.
+ isArray: true
+ name: item-ids
+ required: true
+ secret: false
+ - auto: PREDEFINED
+ default: false
+ defaultValue: read
+ description: How to mark the item. Can be "read" or "unread". Default is "read".
+ isArray: false
+ name: operation
+ predefined:
+ - read
+ - unread
+ required: false
+ secret: false
+ - default: false
+ description: The mailbox on which to run the command. If empty, the command
+ will be applied on the default mailbox.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ deprecated: false
+ description: Marks items as read or unread.
+ execution: false
+ name: ews-mark-items-as-read
+ outputs:
+ - contextPath: EWS.Items.action
+ description: The action that was performed on item.
+ type: String
+ - contextPath: EWS.Items.itemId
+ description: The ID of the item.
+ type: String
+ - contextPath: EWS.Items.messageId
+ description: The message ID of the item.
+ type: String
+ - arguments:
+ - default: false
+ description: The item ID of item to upload as and EML file.
+ isArray: false
+ name: item-id
+ required: true
+ secret: false
+ - default: false
+ description: The mailbox in which this email was found. If empty, the default
+ mailbox is used. Otherwise the user might require impersonation rights to
+ this mailbox.
+ isArray: false
+ name: target-mailbox
+ required: false
+ secret: false
+ deprecated: false
+ description: Retrieves items by item ID and uploads its content as an EML file.
+ execution: false
+ name: ews-get-items-as-eml
+ outputs:
+ - contextPath: File.Size
+ description: The size of the file.
+ type: String
+ - contextPath: File.SHA1
+ description: The SHA1 hash of the file.
+ type: String
+ - contextPath: File.SHA256
+ description: The SHA256 hash of the file.
+ type: String
+ - contextPath: File.SHA512
+ description: The SHA512 hash of the file.
+ type: String
+ - contextPath: File.Name
+ description: The name of the file.
+ type: String
+ - contextPath: File.SSDeep
+ description: The SSDeep hash of the file.
+ type: String
+ - contextPath: File.EntryID
+ description: EntryID of the file
+ type: String
+ - contextPath: File.Info
+ description: Information about the file.
+ type: String
+ - contextPath: File.Type
+ description: The file type.
+ type: String
+ - contextPath: File.MD5
+ description: The MD5 hash of the file.
+ type: String
+ - contextPath: File.Extension
+ description: The extension of the file.
+ type: String
+ dockerimage: demisto/py3ews:1.0.0.8854
+ feed: false
+ isfetch: true
+ longRunning: false
+ longRunningPort: false
+ runonce: false
+ script: '-'
+ subtype: python3
+ type: python
+tests:
+- pyEWS_Test
+- EWS search-mailbox test
+fromversion: 5.0.0
diff --git a/Packs/EWS/Integrations/EWSO365/EWSO365_description.md b/Packs/EWS/Integrations/EWSO365/EWSO365_description.md
new file mode 100644
index 00000000000..1b3079c5a6a
--- /dev/null
+++ b/Packs/EWS/Integrations/EWSO365/EWSO365_description.md
@@ -0,0 +1,6 @@
+To allow access to EWS O365, an administrator has to approve the Demisto app using an admin consent flow, by clicking on the following [link](https://oproxy.demisto.ninja/ms-ews-o365).
+After authorizing the Demisto app, you will get an ID, Token, and Key, which needs to be added to the integration instance configuration's corresponding fields.
+
+### Required Permissions for self deployed Azure Application:
+#### Exchange
+* **full_access_as_app** - Application
diff --git a/Packs/EWS/Integrations/EWSO365/EWSO365_image.png b/Packs/EWS/Integrations/EWSO365/EWSO365_image.png
new file mode 100644
index 00000000000..97612d231c0
Binary files /dev/null and b/Packs/EWS/Integrations/EWSO365/EWSO365_image.png differ
diff --git a/Packs/EWS/Integrations/EWSO365/EWSO365_test.py b/Packs/EWS/Integrations/EWSO365/EWSO365_test.py
new file mode 100644
index 00000000000..2d1c4688ac4
--- /dev/null
+++ b/Packs/EWS/Integrations/EWSO365/EWSO365_test.py
@@ -0,0 +1,148 @@
+import json
+
+from EWSO365 import (
+ find_folders,
+ get_searchable_mailboxes,
+ GetSearchableMailboxes,
+ ExpandGroup,
+ get_expanded_group,
+)
+
+with open("test_data/commands_outputs.json", "r") as f:
+ COMMAND_OUTPUTS = json.load(f)
+with open("test_data/raw_responses.json", "r") as f:
+ RAW_RESPONSES = json.load(f)
+
+
+class TestNormalCommands:
+ """
+ The test class checks the following normal_commands:
+ * ews-find-folders
+ """
+
+ class MockClient:
+ class MockAccount:
+ def __init__(self):
+ self.root = self
+ self.walk_res = []
+ self.all_res = ""
+ self.contacts = self
+
+ def walk(self):
+ return self.walk_res
+
+ def tree(self):
+ return ""
+
+ def all(self):
+ return self.all_res
+
+ def __init__(self):
+ self.default_target_mailbox = ""
+ self.client_id = ""
+ self.client_secret = ""
+ self.tenant_id = ""
+ self.folder = ""
+ self.is_public_folder = ""
+ self.request_timeout = ""
+ self.max_fetch = ""
+ self.self_deployed = ""
+ self.insecure = ""
+ self.proxy = ""
+ self.account = self.MockAccount()
+ self.protocol = ""
+
+ def get_account(self, target_mailbox=None, access_type=None):
+ return self.account
+
+ def get_items_from_mailbox(self, account, item_ids):
+ return ""
+
+ def get_item_from_mailbox(self, account, item_id):
+ return ""
+
+ def get_attachments_for_item(self, item_id, account, attachment_ids=None):
+ return ""
+
+ def is_default_folder(self, folder_path, is_public):
+ return ""
+
+ def get_folder_by_path(self, path, account=None, is_public=False):
+ return ""
+
+ def test_ews_find_folders(self):
+ """
+ This test checks the following normal_command:
+ * ews-find-folders
+ Using this method:
+ Given:
+ - command name is ews-find-folders
+ - client function name to mock
+ - expected raw result
+ - expected command result
+ When:
+ - we want to execute the command function
+ Then:
+ - the expected result will be the same as the entry context
+ """
+ command_name = "ews-find-folders"
+
+ raw_response = RAW_RESPONSES[command_name]
+ expected = COMMAND_OUTPUTS[command_name]
+ client = self.MockClient()
+ client.account.walk_res = raw_response
+ res = find_folders(client)
+ actual_ec = res[1]
+ assert expected == actual_ec
+
+ def test_get_searchable_mailboxes(self, mocker):
+ """
+ This test checks the following normal_command:
+ * ews-get-searchable-mailboxes
+ Using this method:
+ Given:
+ - command name is ews-get-searchable-mailboxes
+ - client function name to mock
+ - expected raw result
+ - expected command result
+ When:
+ - we want to execute the command function
+ Then:
+ - the expected result will be the same as the entry context
+ """
+ command_name = "ews-get-searchable-mailboxes"
+ expected = COMMAND_OUTPUTS[command_name]
+ raw_response = RAW_RESPONSES["ews-get-searchable-mailboxes"]
+ mocker.patch.object(GetSearchableMailboxes, "__init__", return_value=None)
+ mocker.patch.object(GetSearchableMailboxes, "call", return_value=raw_response)
+ client = self.MockClient()
+ res = get_searchable_mailboxes(client)
+ actual_ec = res[1]
+ assert expected == actual_ec
+
+ def test_expand_group(self, mocker):
+ """
+ This test checks the following normal_command:
+ * ews-expand-group
+ Using this method:
+ Given:
+ - command name is ews-expand-group
+ - client function name to mock
+ - expected raw result
+ - expected command result
+ When:
+ - we want to execute the command function
+ Then:
+ - the expected result will be the same as the entry context
+ """
+ command_name = "ews-expand-group"
+ expected = COMMAND_OUTPUTS[command_name]
+ raw_response = RAW_RESPONSES[command_name]
+ mocker.patch.object(ExpandGroup, "__init__", return_value=None)
+ mocker.patch.object(ExpandGroup, "call", return_value=raw_response)
+ client = self.MockClient()
+ res = get_expanded_group(
+ client, email_address="testgroup-1@demistodev.onmicrosoft.com"
+ )
+ actual_ec = res[1]
+ assert expected == actual_ec
diff --git a/Packs/EWS/Integrations/EWSO365/Pipfile b/Packs/EWS/Integrations/EWSO365/Pipfile
new file mode 100644
index 00000000000..3523d3b6b93
--- /dev/null
+++ b/Packs/EWS/Integrations/EWSO365/Pipfile
@@ -0,0 +1,18 @@
+[[source]]
+name = "pypi"
+url = "https://pypi.org/simple"
+verify_ssl = true
+
+[dev-packages]
+pylint = "*"
+pytest = "==5.0.1"
+pytest-mock = "*"
+requests-mock = "*"
+pytest-asyncio = "*"
+
+[packages]
+pytest = "*"
+requests = "*"
+
+[requires]
+python_version = "3.7"
diff --git a/Packs/EWS/Integrations/EWSO365/Pipfile.lock b/Packs/EWS/Integrations/EWSO365/Pipfile.lock
new file mode 100644
index 00000000000..6bdb9313414
--- /dev/null
+++ b/Packs/EWS/Integrations/EWSO365/Pipfile.lock
@@ -0,0 +1,369 @@
+{
+ "_meta": {
+ "hash": {
+ "sha256": "278db815bec49c11262633d34305f9b33f09432a223bedd5329a04f758f78b55"
+ },
+ "pipfile-spec": 6,
+ "requires": {
+ "python_version": "3.7"
+ },
+ "sources": [
+ {
+ "name": "pypi",
+ "url": "https://pypi.org/simple",
+ "verify_ssl": true
+ }
+ ]
+ },
+ "default": {
+ "atomicwrites": {
+ "hashes": [
+ "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
+ "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
+ ],
+ "version": "==1.3.0"
+ },
+ "attrs": {
+ "hashes": [
+ "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
+ "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
+ ],
+ "version": "==19.1.0"
+ },
+ "certifi": {
+ "hashes": [
+ "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50",
+ "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef"
+ ],
+ "version": "==2019.9.11"
+ },
+ "chardet": {
+ "hashes": [
+ "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
+ "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
+ ],
+ "version": "==3.0.4"
+ },
+ "idna": {
+ "hashes": [
+ "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
+ "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
+ ],
+ "version": "==2.8"
+ },
+ "importlib-metadata": {
+ "hashes": [
+ "sha256:652234b6ab8f2506ae58e528b6fbcc668831d3cc758e1bc01ef438d328b68cdb",
+ "sha256:6f264986fb88042bc1f0535fa9a557e6a376cfe5679dc77caac7fe8b5d43d05f"
+ ],
+ "markers": "python_version < '3.8'",
+ "version": "==0.22"
+ },
+ "more-itertools": {
+ "hashes": [
+ "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832",
+ "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"
+ ],
+ "version": "==7.2.0"
+ },
+ "packaging": {
+ "hashes": [
+ "sha256:a7ac867b97fdc07ee80a8058fe4435ccd274ecc3b0ed61d852d7d53055528cf9",
+ "sha256:c491ca87294da7cc01902edbe30a5bc6c4c28172b5138ab4e4aa1b9d7bfaeafe"
+ ],
+ "version": "==19.1"
+ },
+ "pluggy": {
+ "hashes": [
+ "sha256:0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6",
+ "sha256:fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"
+ ],
+ "version": "==0.13.0"
+ },
+ "py": {
+ "hashes": [
+ "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
+ "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
+ ],
+ "version": "==1.8.0"
+ },
+ "pyparsing": {
+ "hashes": [
+ "sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80",
+ "sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"
+ ],
+ "version": "==2.4.2"
+ },
+ "pytest": {
+ "hashes": [
+ "sha256:95d13143cc14174ca1a01ec68e84d76ba5d9d493ac02716fd9706c949a505210",
+ "sha256:b78fe2881323bd44fd9bd76e5317173d4316577e7b1cddebae9136a4495ec865"
+ ],
+ "index": "pypi",
+ "version": "==5.1.2"
+ },
+ "requests": {
+ "hashes": [
+ "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
+ "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
+ ],
+ "index": "pypi",
+ "version": "==2.22.0"
+ },
+ "six": {
+ "hashes": [
+ "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
+ "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
+ ],
+ "version": "==1.12.0"
+ },
+ "urllib3": {
+ "hashes": [
+ "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
+ "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
+ ],
+ "version": "==1.25.3"
+ },
+ "wcwidth": {
+ "hashes": [
+ "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
+ "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
+ ],
+ "version": "==0.1.7"
+ },
+ "zipp": {
+ "hashes": [
+ "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e",
+ "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"
+ ],
+ "version": "==0.6.0"
+ }
+ },
+ "develop": {
+ "astroid": {
+ "hashes": [
+ "sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4",
+ "sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4"
+ ],
+ "version": "==2.2.5"
+ },
+ "atomicwrites": {
+ "hashes": [
+ "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
+ "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
+ ],
+ "version": "==1.3.0"
+ },
+ "attrs": {
+ "hashes": [
+ "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
+ "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
+ ],
+ "version": "==19.1.0"
+ },
+ "certifi": {
+ "hashes": [
+ "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50",
+ "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef"
+ ],
+ "version": "==2019.9.11"
+ },
+ "chardet": {
+ "hashes": [
+ "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
+ "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
+ ],
+ "version": "==3.0.4"
+ },
+ "idna": {
+ "hashes": [
+ "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
+ "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
+ ],
+ "version": "==2.8"
+ },
+ "importlib-metadata": {
+ "hashes": [
+ "sha256:652234b6ab8f2506ae58e528b6fbcc668831d3cc758e1bc01ef438d328b68cdb",
+ "sha256:6f264986fb88042bc1f0535fa9a557e6a376cfe5679dc77caac7fe8b5d43d05f"
+ ],
+ "markers": "python_version < '3.8'",
+ "version": "==0.22"
+ },
+ "isort": {
+ "hashes": [
+ "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1",
+ "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"
+ ],
+ "version": "==4.3.21"
+ },
+ "lazy-object-proxy": {
+ "hashes": [
+ "sha256:02b260c8deb80db09325b99edf62ae344ce9bc64d68b7a634410b8e9a568edbf",
+ "sha256:18f9c401083a4ba6e162355873f906315332ea7035803d0fd8166051e3d402e3",
+ "sha256:1f2c6209a8917c525c1e2b55a716135ca4658a3042b5122d4e3413a4030c26ce",
+ "sha256:2f06d97f0ca0f414f6b707c974aaf8829c2292c1c497642f63824119d770226f",
+ "sha256:616c94f8176808f4018b39f9638080ed86f96b55370b5a9463b2ee5c926f6c5f",
+ "sha256:63b91e30ef47ef68a30f0c3c278fbfe9822319c15f34b7538a829515b84ca2a0",
+ "sha256:77b454f03860b844f758c5d5c6e5f18d27de899a3db367f4af06bec2e6013a8e",
+ "sha256:83fe27ba321e4cfac466178606147d3c0aa18e8087507caec78ed5a966a64905",
+ "sha256:84742532d39f72df959d237912344d8a1764c2d03fe58beba96a87bfa11a76d8",
+ "sha256:874ebf3caaf55a020aeb08acead813baf5a305927a71ce88c9377970fe7ad3c2",
+ "sha256:9f5caf2c7436d44f3cec97c2fa7791f8a675170badbfa86e1992ca1b84c37009",
+ "sha256:a0c8758d01fcdfe7ae8e4b4017b13552efa7f1197dd7358dc9da0576f9d0328a",
+ "sha256:a4def978d9d28cda2d960c279318d46b327632686d82b4917516c36d4c274512",
+ "sha256:ad4f4be843dace866af5fc142509e9b9817ca0c59342fdb176ab6ad552c927f5",
+ "sha256:ae33dd198f772f714420c5ab698ff05ff900150486c648d29951e9c70694338e",
+ "sha256:b4a2b782b8a8c5522ad35c93e04d60e2ba7f7dcb9271ec8e8c3e08239be6c7b4",
+ "sha256:c462eb33f6abca3b34cdedbe84d761f31a60b814e173b98ede3c81bb48967c4f",
+ "sha256:fd135b8d35dfdcdb984828c84d695937e58cc5f49e1c854eb311c4d6aa03f4f1"
+ ],
+ "version": "==1.4.2"
+ },
+ "mccabe": {
+ "hashes": [
+ "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
+ "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
+ ],
+ "version": "==0.6.1"
+ },
+ "more-itertools": {
+ "hashes": [
+ "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832",
+ "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"
+ ],
+ "version": "==7.2.0"
+ },
+ "packaging": {
+ "hashes": [
+ "sha256:a7ac867b97fdc07ee80a8058fe4435ccd274ecc3b0ed61d852d7d53055528cf9",
+ "sha256:c491ca87294da7cc01902edbe30a5bc6c4c28172b5138ab4e4aa1b9d7bfaeafe"
+ ],
+ "version": "==19.1"
+ },
+ "pluggy": {
+ "hashes": [
+ "sha256:0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6",
+ "sha256:fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"
+ ],
+ "version": "==0.13.0"
+ },
+ "py": {
+ "hashes": [
+ "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
+ "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
+ ],
+ "version": "==1.8.0"
+ },
+ "pylint": {
+ "hashes": [
+ "sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09",
+ "sha256:723e3db49555abaf9bf79dc474c6b9e2935ad82230b10c1138a71ea41ac0fff1"
+ ],
+ "index": "pypi",
+ "version": "==2.3.1"
+ },
+ "pyparsing": {
+ "hashes": [
+ "sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80",
+ "sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"
+ ],
+ "version": "==2.4.2"
+ },
+ "pytest": {
+ "hashes": [
+ "sha256:95d13143cc14174ca1a01ec68e84d76ba5d9d493ac02716fd9706c949a505210",
+ "sha256:b78fe2881323bd44fd9bd76e5317173d4316577e7b1cddebae9136a4495ec865"
+ ],
+ "index": "pypi",
+ "version": "==5.1.2"
+ },
+ "pytest-asyncio": {
+ "hashes": [
+ "sha256:9fac5100fd716cbecf6ef89233e8590a4ad61d729d1732e0a96b84182df1daaf",
+ "sha256:d734718e25cfc32d2bf78d346e99d33724deeba774cc4afdf491530c6184b63b"
+ ],
+ "index": "pypi",
+ "version": "==0.10.0"
+ },
+ "pytest-mock": {
+ "hashes": [
+ "sha256:43ce4e9dd5074993e7c021bb1c22cbb5363e612a2b5a76bc6d956775b10758b7",
+ "sha256:5bf5771b1db93beac965a7347dc81c675ec4090cb841e49d9d34637a25c30568"
+ ],
+ "index": "pypi",
+ "version": "==1.10.4"
+ },
+ "requests": {
+ "hashes": [
+ "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
+ "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
+ ],
+ "index": "pypi",
+ "version": "==2.22.0"
+ },
+ "requests-mock": {
+ "hashes": [
+ "sha256:510df890afe08d36eca5bb16b4aa6308a6f85e3159ad3013bac8b9de7bd5a010",
+ "sha256:88d3402dd8b3c69a9e4f9d3a73ad11b15920c6efd36bc27bf1f701cf4a8e4646"
+ ],
+ "index": "pypi",
+ "version": "==1.7.0"
+ },
+ "six": {
+ "hashes": [
+ "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
+ "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
+ ],
+ "version": "==1.12.0"
+ },
+ "typed-ast": {
+ "hashes": [
+ "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e",
+ "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e",
+ "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0",
+ "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c",
+ "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631",
+ "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4",
+ "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34",
+ "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b",
+ "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a",
+ "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233",
+ "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1",
+ "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36",
+ "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d",
+ "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a",
+ "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
+ ],
+ "markers": "implementation_name == 'cpython'",
+ "version": "==1.4.0"
+ },
+ "urllib3": {
+ "hashes": [
+ "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
+ "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
+ ],
+ "version": "==1.25.3"
+ },
+ "wcwidth": {
+ "hashes": [
+ "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
+ "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
+ ],
+ "version": "==0.1.7"
+ },
+ "wrapt": {
+ "hashes": [
+ "sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1"
+ ],
+ "version": "==1.11.2"
+ },
+ "zipp": {
+ "hashes": [
+ "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e",
+ "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"
+ ],
+ "version": "==0.6.0"
+ }
+ }
+}
diff --git a/Packs/EWS/Integrations/EWSO365/README.md b/Packs/EWS/Integrations/EWSO365/README.md
new file mode 100644
index 00000000000..d9a5c5f446d
--- /dev/null
+++ b/Packs/EWS/Integrations/EWSO365/README.md
@@ -0,0 +1,1354 @@
+Exchange Web Services (EWS) provides the functionality to enable client applications to communicate with the Exchange server. EWS provides access to much of the same data that is made available through Microsoft OfficeOutlook.
+
+The EWS O365 integration implants EWS leading services. The integration allows getting information on emails and activities in a target mailbox, and some active operations on the mailbox such as deleting emails and attachments or moving emails from folder to folder.
+
+## EWS O365 Playbook
+
+* Office 365 Search and Delete
+* Search And Delete Emails - EWS
+* Get Original Email - EWS
+* Process Email - EWS
+
+## Use Cases
+
+The EWS integration can be used for the following use cases.
+
+* Monitor a specific email account and create incidents from incoming emails to the defined folder.
+ Follow the instructions in the Fetched Incidents Data section.
+
+* Search for an email message across mailboxes and folders.
+ This can be achieved in the following ways:
+
+ 1. Use the `ews-search-mailboxes` command to search for all emails in a specific scope of mailboxes.
+ Use the filter argument to narrow the search for emails sent from a specific account and more.
+ 2. Use the `ews-search-mailbox` command to search for all emails in a specific folder within the target mailbox.
+ Use the query argument to narrow the search for emails sent from a specific account and more.
+ * Both of these commands retrieve the _ItemID_ field for each email item listed in the results. The `ItemID` can be used in the `ews-get-items` command in order to get more information about the email item itself.
+ * For instance, use the `ews-search-mailboxes` command to hunt for emails that were marked as malicious in prior investigations, across organization mailboxes. Focus your hunt on emails sent from a specific mail account, emails with a specific subject and more.
+* Get email attachment information.
+ Use the `ews-get-attachment` command to retrieve information on one attachment or all attachments of a message at once. It supports both file attachments and item attachments (e.g., email messages).
+
+* Delete email items from a mailbox.
+ First, make sure you obtain the email item ID. The item ID can be obtained with one of the integration’s search commands.
+ Use the `ews-delete-items` command to delete one or more items from the target mailbox in a single action.
+ A less common use case is to remove emails that were marked as malicious from a user’s mailbox.
+ You can delete the items permanently (hard delete), or delete the items (soft delete), so they can be recovered by running the `ews-recover-messages` command.
+
+## Configure EWS O365 on Demisto
+
+1. Navigate to **Settings** > **Integrations** > **Servers & Services**.
+2. Search for EWS O365.
+3. Click **Add instance** to create and configure a new integration instance.
+ * **Name**: a textual name for the integration instance.
+ * **ID / Application ID**: ID recieved from https://oproxy.demisto.ninja/ms-ews-o365 app registration, or a self deployed Application ID.
+ * **Token / Tenant ID**: Token recieved from https://oproxy.demisto.ninja/ms-ews-o365 app registration, or a self deployed Application Tenant ID.
+ * **Key / Application Secret**: Key recieved from https://oproxy.demisto.ninja/ms-ews-o365 app registration, or a self deployed Application Secret.
+ * **Email Address**: Mailbox to run commands on, and to fetch incidents from. This argument can take various user accounts in your organization. Usually is used as phishing mailbox.
+ Note: To use this functionality, your account must have impersonation rights or delegation for the account specified. For more information on impersonation rights see ‘Additional Information’ section below.
+ * **Name of the folder from which to fetch incidents**: Supports Exchange Folder ID and sub-folders e.g. Inbox/Phishing. Please note, if Exchange is configured with an international flavor `Inbox` will be named according to the configured language.
+ * **Public Folder**
+ * **Use system proxy settings**
+ * **Trust any certificate (not secure)**
+ * **Timeout (in seconds) for HTTP requests to Exchange Server**
+ * **Use a self deployed Azure Application**
+4. Click **Test** to validate the URLs, token, and connection.
+
+## Use a Self-Deployed Azure Application
+
+To use a self-configured Azure application, you need to add a new Azure App Registration in the Azure Portal. To add the registration, refer to the [Microsoft documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app)
+ The Tenant ID, Client ID, and Client secret are required for the integration.
+* ID - Application (Client) ID
+* Token - Tenant ID
+* Key - Application (Client) Secret
+
+## Fetched Incidents Data
+
+The integration imports email messages from the destination folder in the target mailbox as incidents. If the message contains any attachments, they are uploaded to the War Room as files. If the attachment is an email, Demisto fetches information about the attached email and downloads all of its attachments (if there are any) as files.
+
+To use Fetch incidents, configure a new instance and select the `Fetches incidents` option in the instance settings.
+
+IMPORTANT: The initial fetch interval is the previous 10 minutes. If no emails were fetched before from the destination folder- all emails from 10 minutes prior to the instance configuration and up to the current time will be fetched.
+
+Pay special attention to the following fields in the instance settings:
+
+`Email Address` – mailbox to fetch incidents from.
+`Name of the folder from which to fetch incidents` – use this field to configure the destination folder from where emails should be fetched. The default is Inbox folder. Please note, if Exchange is configured with an international flavor `Inbox` will be named according to the configured language.
+
+## Commands
+
+You can execute these commands from the Demisto 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.
+
+1. [Get the attachments of an item: ews-get-attachment](#h_22ec0bbb-12b3-4f1c-9159-b1a4daa114c7)
+2. [Delete the attachments of an item: ews-delete-attachment](#h_cae18768-1dd5-4cd1-b2c9-abfd0e7787f3)
+3. [Get a list of searchable mailboxes: ews-get-searchable-mailboxes](#h_7bdec9fe-e3d9-4645-8da4-337ee3798a84)
+5. [Move an item to a different folder: ews-move-item](#h_0661f657-850a-430a-8fe1-aacf7e3ce40b)
+6. [Delete an item from a mailbox: ews-delete-items](#h_712791a3-5937-4641-8e02-1fd773ab3211)
+7. [Search a single mailbox: ews-search-mailbox](#h_2b4fd205-165c-489f-b58c-3bb77a86acfc)
+8. [Get the contacts for a mailbox: ews-get-contacts](#h_3b6dc53b-4c1a-4479-a529-0ff3300dc4f5)
+9. [Get the out-of-office status for a mailbox: ews-get-out-of-office](#h_b592e5fe-af2a-4d3c-90aa-b933e69a7526)
+10. [Recover soft-deleted messages: ews-recover-messages](#h_212102bb-4ad8-4bb8-9c05-1b1197e2a9c9)
+11. [Create a folder: ews-create-folder](#h_4ab168b9-21e9-4ce1-b18c-56bc22c0e0bd)
+12. [Mark an item as junk: ews-mark-item-as-junk](#h_01b093ea-bc1c-46a3-b694-8cd45effeaa0)
+13. [Search for folders: ews-find-folders](#h_3f9e1f1e-e634-4f92-b2a2-cdca5ca662eb)
+14. [Get items of a folder: ews-get-items-from-folder](#h_0035899d-fdd0-43b7-bf7b-11a38a2e575a)
+15. [Get items: ews-get-items](#h_e8f449a2-aecf-4d65-8d04-a38c6d4bfe62)
+16. [Move an item to a different mailbox: ews-move-item-between-mailboxes](#h_88c0edd5-09b0-42a1-a671-b36b73772898)
+17. [Get a folder: ews-get-folder](#h_87ca72d4-d98a-462e-9829-c940321663c2)
+18. [Expand a distribution list: ews-expand-group](#h_d91ca450-7004-4a19-a88d-840389b21556)
+19. [Mark items as read: ews-mark-items-as-read](#h_e278dc88-b4b0-4330-b849-3069b770e5ba)
+
+### 1\. Get the attachments of an item
+
+* * *
+
+Retrieves the actual attachments from an item (email message). To get all attachments for a message, only specify the item-id argument.
+
+##### Required Permissions
+
+Impersonation rights required. In order to perform actions on the target mailbox of other users, the service account must be part of the ApplicationImpersonation role.
+
+##### Base Command
+
+`ews-get-attachment`
+
+##### Input
+
+|**Argument Name**|**Description**|**Required**|
+|--- |--- |--- |
+|item-id|The ID of the email message for which to get the attachments.|Required|
+|target-mailbox|The mailbox in which this attachment was found. If empty, the default mailbox is used. Otherwise, the user might require impersonation rights to this mailbox.|Optional|
+|attachment-ids|The attachments ids to get. If none - all attachments will be retrieved from the message. Support multiple attachments with comma-separated value or array.|Optional|
+
+
+##### Context Output
+
+|**Path**|**Type**|**Description**|
+|--- |--- |--- |
+|EWS.Items.FileAttachments.attachmentId|string|The attachment ID. Used for file attachments only.|
+|EWS.Items.FileAttachments.attachmentName|string|The attachment name. Used for file attachments only.|
+|EWS.Items.FileAttachments.attachmentSHA256|string|The SHA256 hash of the attached file.|
+|EWS.Items.FileAttachments.attachmentLastModifiedTime|date|The attachment last modified time. Used for file attachments only.|
+|EWS.Items.ItemAttachments.datetimeCreated|date|The created time of the attached email.|
+|EWS.Items.ItemAttachments.datetimeReceived|date|The received time of the attached email.|
+|EWS.Items.ItemAttachments.datetimeSent|date|The sent time of the attached email.|
+|EWS.Items.ItemAttachments.receivedBy|string|The received by address of the attached email.|
+|EWS.Items.ItemAttachments.subject|string|The subject of the attached email.|
+|EWS.Items.ItemAttachments.textBody|string|The body of the attached email (as text).|
+|EWS.Items.ItemAttachments.headers|Unknown|The headers of the attached email.|
+|EWS.Items.ItemAttachments.hasAttachments|boolean|Whether the attached email has attachments.|
+|EWS.Items.ItemAttachments.itemId|string|The attached email item ID.|
+|EWS.Items.ItemAttachments.toRecipients|Unknown|A list of recipient email addresses for the attached email.|
+|EWS.Items.ItemAttachments.body|string|The body of the attached email (as HTML).|
+|EWS.Items.ItemAttachments.attachmentSHA256|string|SHA256 hash of the attached email (as EML file).|
+|EWS.Items.ItemAttachments.FileAttachments.attachmentSHA256|string|SHA256 hash of the attached files inside of the attached email.|
+|EWS.Items.ItemAttachments.ItemAttachments.attachmentSHA256|string|SHA256 hash of the attached emails inside of the attached email.|
+|EWS.Items.ItemAttachments.isRead|String|The read status of the attachment.|
+
+
+##### Command Example
+
+```
+!ews-get-attachment item-id=BBFDShfdafFSDF3FADR3434DFASDFADAFDADFADFCJebinpkUAAAfxuiVAAA= target-mailbox=test@demistodev.onmicrosoft.com
+```
+
+##### Context Example
+
+```
+{
+ "EWS": {
+ "Items": {
+ "ItemAttachments": {
+ "originalItemId": "BBFDShfdafFSDF3FADR3434DFASDFADAFDADFADFCJebinpkUAAAfxuiVAAA=",
+ "attachmentSize": 2956,
+ "receivedBy": "test@demistodev.onmicrosoft.com",
+ "size": 28852,
+ "author": "test2@demistodev.onmicrosoft.com",
+ "attachmentLastModifiedTime": "2019-08-11T15:01:30+00:00",
+ "subject": "Moving Email between mailboxes",
+ "body": "Some text inside",
+ "datetimeCreated": "2019-08-11T15:01:47Z",
+ "importance": "Normal",
+ "attachmentType": "ItemAttachment",
+ "toRecipients": [
+ "test@demistodev.onmicrosoft.com"
+ ],
+ "mailbox": "test@demistodev.onmicrosoft.com",
+ "isRead": false,
+ "attachmentIsInline": false,
+ "datetimeSent": "2019-08-07T12:50:19Z",
+ "lastModifiedTime": "2019-08-11T15:01:30Z",
+ "sender": "test2@demistodev.onmicrosoft.com",
+ "attachmentName": "Moving Email between mailboxes",
+ "datetimeReceived": "2019-08-07T12:50:20Z",
+ "attachmentSHA256": "119e27b28dc81bdfd4f498d44bd7a6d553a74ee03bdc83e6255a53",
+ "hasAttachments": false,
+ "headers": [
+ {
+ "name": "Subject",
+ "value": "Moving Email between mailboxes"
+ }
+ ...
+ ],
+ "attachmentId": "BBFDShfdafFSDF3FADR3434DFASDFADAFDADFADFCJebinpkUAAAfxuiVAAABEgAQAOpEfpzDB4dFkZ+/K4XSj44=",
+ "messageId": "message_id"
+ }
+ }
+ }
+
+```
+
+### 2\. Delete the attachments of an item
+
+* * *
+
+Deletes the attachments of an item (email message).
+
+##### Required Permissions
+
+Impersonation rights required. In order to perform actions on the target mailbox of other users, the service account must be part of the ApplicationImpersonation role.
+
+##### Base Command
+
+`ews-delete-attachment`
+
+##### Input
+
+|**Argument Name**|**Description**|**Required**|
+|--- |--- |--- |
+|item-id|The ID of the email message for which to delete attachments.|Required|
+|target-mailbox|The mailbox in which this attachment was found. If empty, the default mailbox is used. Otherwise, the user might require impersonation rights to this mailbox.|Optional|
+|attachment-ids|A CSV list (or array) of attachment IDs to delete. If empty, all attachments will be deleted from the message.|Optional|
+
+##### Context Output
+
+|**Path**|**Type**|**Description**|
+|--- |--- |--- |
+|EWS.Items.FileAttachments.attachmentId|string|The ID of the deleted attachment, in case of file attachment.|
+|EWS.Items.ItemAttachments.attachmentId|string|The ID of the deleted attachment, in case of other attachment (for example, "email").|
+|EWS.Items.FileAttachments.action|string|The deletion action in case of file attachment. This is a constant value: 'deleted'.|
+|EWS.Items.ItemAttachments.action|string|The deletion action in case of other attachment (for example, "email"). This is a constant value: 'deleted'.|
+
+##### Command Example
+
+```
+!ews-delete-attachment item-id=AAMkADQ0NmwBGAAAAAAA4kxh+ed3JTJPMPXU3wX3aBwCyyVyFtlsUQZfBJjfaljfAFDVSDinpkUAAAfxxd9AAA= target-mailbox=test@demistodev.onmicrosoft.com
+```
+
+##### Human Readable Output
+
+|action|attachmentId|
+|--- |--- |
+|deleted|AAMkADQ0NmwBGAAAAAAA4kxh+ed3JTJPMPXU3wX3aBwCyyVyFtlsUQZfBJjfaljfAFDVSDinpkUAAAfxxd9AAABEgAQAIUht2vrOdErec33=|
+
+### Context Example
+
+```
+{
+ "EWS": {
+ "Items": {
+ "FileAttachments": {
+ "action": "deleted",
+ "attachmentId": "AAMkADQ0NmwBGAAAAAAA4kxh+ed3JTJPMPXU3wX3aBwCyyVyFtlsUQZfBJjfaljfAFDVSDinpkUAAAfxxd9AAABEgAQAIUht2vrOdErec33="
+ }
+ }
+ }
+}
+
+```
+
+### 3\. Get a list of searchable mailboxes
+
+* * *
+
+Returns a list of searchable mailboxes.
+
+##### Required Permissions
+
+Requires eDiscovery permissions to the Exchange Server. For more information see the [Microsoft documentation](https://technet.microsoft.com/en-us/library/dd298059(v=exchg.160).aspx).
+
+##### Base Command
+
+`ews-get-searchable-mailboxes`
+
+##### Input
+
+There are no input arguments for this command.
+
+##### Context Output
+
+|**Path**|**Type**|**Description**|
+|--- |--- |--- |
+|EWS.Mailboxes.mailbox|string|Addresses of the searchable mailboxes.|
+|EWS.Mailboxes.mailboxId|string|IDs of the searchable mailboxes.|
+|EWS.Mailboxes.displayName|string|The email display name.|
+|EWS.Mailboxes.isExternal|boolean|Whether the mailbox is external.|
+|EWS.Mailboxes.externalEmailAddress|string|The external email address.|
+
+##### Command Example
+
+```
+!ews-get-searchable-mailboxes
+```
+
+##### Human Readable Output
+
+|displayName|isExternal|mailbox|mailboxId|
+|--- |--- |--- |--- |
+|test|false|test@demistodev.onmicrosoft.com|/o=Exchange***/ou=Exchange Administrative Group ()/cn=**/cn=**-**|
+
+##### Context Example
+
+```
+{
+ "EWS": {
+ "Mailboxes": [
+ {
+ "mailbox": "test@demistodev.onmicrosoft.com",
+ "displayName": "test",
+ "mailboxId": "/o=Exchange***/ou=Exchange Administrative Group ()/cn=**/cn=**-**",
+ "isExternal": "false"
+ }
+ ...
+ ]
+ }
+}
+
+```
+
+### 4\. Move an item to a different folder
+
+* * *
+
+Move an item to a different folder in the mailbox.
+
+##### Required Permissions
+
+Impersonation rights required. In order to perform actions on the target mailbox of other users, the service account must be part of the ApplicationImpersonation role.
+
+##### Base Command
+
+`ews-move-item`
+
+##### Input
+
+|**Argument Name**|**Description**|**Required**|
+|--- |--- |--- |
+|item-id|The ID of the item to move.|Required|
+|target-folder-path|The path to the folder to which to move the item. Complex paths are supported, for example, "Inbox\Phishing".|Required|
+|target-mailbox|The mailbox on which to run the command.|Optional|
+|is-public|Whether the target folder is a public folder.|Optional|
+
+##### Context Output
+
+|**Path**|**Type**|**Description**|
+|--- |--- |--- |
+|EWS.Items.newItemID|string|The item ID after the move.|
+|EWS.Items.messageID|string|The item message ID.|
+|EWS.Items.itemId|string|The original item ID.|
+|EWS.Items.action|string|The action taken. The value will be "moved".|
+
+##### Command Example
+
+```
+!ews-move-item item-id=VDAFNTZjNTMxNwBGAAAAAAA4kxh+ed3JTJPMPXU34cSCSSSfBJebinpkUAAAAAAEMAACyyVyFtlsUQZfBJebinpkUAAAfxuiRAAA= target-folder-path=Moving target-mailbox=test@demistodev.onmicrosoft.com
+```
+
+##### Human Readable Output
+
+|action|itemId|messageId|newItemId|
+|--- |--- |--- |--- |
+|moved|VDAFNTZjNTMxNwBGAAAAAAA4kxh+ed3JTJPMPXU34cSCSSSfBJebinpkUAAAAAAEMAACyyVyFtlsUQZfBJebinpkUAAAfxuiRAAA||AAVAAAVN2NkLThmZjdmNTZjNTMxFFFFJTJPMPXU3wX3aBwCyyVyFtlsUQZfBJebinpkUAAAa2bUBAACyyVfafainpkUAAAfxxd+AAA=|
+
+##### Context Example
+
+ {
+ "EWS": {
+ "Items": {
+ "action": "moved",
+ "itemId": "VDAFNTZjNTMxNwBGAAAAAAA4kxh+ed3JTJPMPXU34cSCSSSfBJebinpkUAAAAAAEMAACyyVyFtlsUQZfBJebinpkUAAAfxuiRAAA",
+ "newItemId": "AAVAAAVN2NkLThmZjdmNTZjNTMxFFFFJTJPMPXU3wX3aBwCyyVyFtlsUQZfBJebinpkUAAAa2bUBAACyyVfafainpkUAAAfxxd+AAA=",
+ "messageId": ""
+ }
+ }
+ }
+
+### 5\. Delete an item from a mailbox
+
+* * *
+
+Delete items from mailbox.
+
+##### Required Permissions
+
+Impersonation rights required. In order to perform actions on the target mailbox of other users, the service account must be part of the ApplicationImpersonation role.
+
+##### Base Command
+
+`ews-delete-items`
+
+##### Input
+
+|**Argument Name**|**Description**|**Required**|
+|--- |--- |--- |
+|item-ids|The item IDs to delete.|Required|
+|delete-type|Deletion type. Can be "trash", "soft", or "hard".|Required|
+|target-mailbox|The mailbox on which to run the command.|Optional|
+
+##### Context Output
+
+|**Path**|**Type**|**Description**|
+|--- |--- |--- |
+|EWS.Items.itemId|string|The deleted item ID.|
+|EWS.Items.messageId|string|The deleted message ID.|
+|EWS.Items.action|string|The deletion action. Can be 'trash-deleted', 'soft-deleted', or 'hard-deleted'.|
+
+##### Command Example
+
+```
+!ews-delete-items item-ids=VWAFA3hmZjdmNTZjNTMxNwBGAAAAAAA4kxh+ed3JTJPMPXU3wX3aBwCyyVyFtlsUQZfBJebinpkUAAABjKMGAACyw+kAAA= delete-type=soft target-mailbox=test@demistodev.onmicrosoft.com
+```
+
+##### Human Readable Output
+
+|action|itemId|messageId|
+|--- |--- |--- |
+|soft-deleted|VWAFA3hmZjdmNTZjNTMxNwBGAAAAAAA4kxh+ed3JTJPMPXU3wX3aBwCyyVyFtlsUQZfBJebinpkUAAABjKMGAACyw+kAAA=||
+
+##### Context Example
+
+```
+{
+ "EWS": {
+ "Items": {
+ "action": "soft-deleted",
+ "itemId": "VWAFA3hmZjdmNTZjNTMxNwBGAAAAAAA4kxh+ed3JTJPMPXU3wX3aBwCyyVyFtlsUQZfBJebinpkUAAABjKMGAACyw+kAAA=",
+ "messageId": "messaage_id"
+ }
+ }
+}
+
+```
+
+### 6\. Search a single mailbox
+
+* * *
+
+Searches for items in the specified mailbox. Specific permissions are needed for this operation to search in a target mailbox other than the default.
+
+##### Required Permissions
+
+Impersonation rights required. To perform actions on the target mailbox of other users, the service account must be part of the ApplicationImpersonation role.
+
+##### Base Command
+
+`ews-search-mailbox`
+
+##### Input
+
+|**Argument Name**|**Description**|**Required**|
+|--- |--- |--- |
+|query|The search query string. For more information about the query syntax, see the [Microsoft documentation](https://msdn.microsoft.com/en-us/library/ee693615.aspx).|Optional|
+|folder-path|The folder path in which to search. If empty, searches all the folders in the mailbox.|Optional|
+|limit|Maximum number of results to return.|Optional|
+|target-mailbox|The mailbox on which to apply the search.|Optional|
+|is-public|Whether the folder is a Public Folder?|Optional|
+|message-id|The message ID of the email. This will be ignored if a query argument is provided.|Optional|
+
+##### Context Output
+
+|**Path**|**Type**|**Description**|
+|--- |--- |--- |
+|EWS.Items.itemId|string|The email item ID.|
+|EWS.Items.hasAttachments|boolean|Whether the email has attachments.|
+|EWS.Items.datetimeReceived|date|Received time of the email.|
+|EWS.Items.datetimeSent|date|Sent time of the email.|
+|EWS.Items.headers|Unknown|Email headers (list).|
+|EWS.Items.sender|string|Sender email address of the email.|
+|EWS.Items.subject|string|Subject of the email.|
+|EWS.Items.textBody|string|Body of the email (as text).|
+|EWS.Items.size|number|Email size.|
+|EWS.Items.toRecipients|Unknown|List of email recipients addresses.|
+|EWS.Items.receivedBy|Unknown|Email received by address.|
+|EWS.Items.messageId|string|Email message ID.|
+|EWS.Items.body|string|Body of the email (as HTML).|
+|EWS.Items.FileAttachments.attachmentId|unknown|Attachment ID of the file attachment.|
+|EWS.Items.ItemAttachments.attachmentId|unknown|Attachment ID of the item attachment.|
+|EWS.Items.FileAttachments.attachmentName|unknown|Attachment name of the file attachment.|
+|EWS.Items.ItemAttachments.attachmentName|unknown|Attachment name of the item attachment.|
+|EWS.Items.isRead|String|The read status of the email.|
+
+##### Command Example
+
+```
+!ews-search-mailbox query="subject:"Get Attachment Email" target-mailbox=test@demistodev.onmicrosoft.com limit=1
+```
+
+##### Human Readable Output
+
+|sender|subject|hasAttachments|datetimeReceived|receivedBy|author|toRecipients|
+|--- |--- |--- |--- |--- |--- |--- |
+|test2@demistodev.onmicrosoft.com|Get Attachment Email|true|2019-08-11T10:57:37Z|test@demistodev.onmicrosoft.com|test2@demistodev.onmicrosoft.com|test@demistodev.onmicrosoft.com|
+
+##### Context Example
+
+```
+{
+ "EWS": {
+ "Items": {
+ "body": "\r\n\r\n\r\n\r\n\r\n\r\n
\r\n
Some text inside email
\r\n
\r\n\r\n\r\n",
+ "itemId": "AAMkADQ0NmFFijer3FFmNTZjNTMxNwBGAAAAAAFSAAfxw+jAAA=",
+ "toRecipients": [
+ "test@demistodev.onmicrosoft.com"
+ ],
+ "datetimeCreated": "2019-08-11T10:57:37Z",
+ "datetimeReceived": "2019-08-11T10:57:37Z",
+ "author": "test2@demistodev.onmicrosoft.com",
+ "hasAttachments": true,
+ "size": 30455,
+ "subject": "Get Attachment Email",
+ "FileAttachments": [
+ {
+ "attachmentName": "atta1.rtf",
+ "attachmentSHA256": "csfd81097bc049fbcff6e637ade0407a00308bfdfa339e31a44a1c4e98f28ce36e4f",
+ "attachmentType": "FileAttachment",
+ "attachmentSize": 555,
+ "attachmentId": "AAMkADQ0NmFkODFkLWQ4MDEtNDE4Mi1hN2NkLThmZjdmNTZjNTMxNwBGAAAAAAA4kxh+ed3JTJPMPXU3wX3aBwCyyVyFtlsUQZfBJebinpkUAAABjKMGAACyyVyFtlsUQZfBJebinpkUAAAfxw+jAAABEgAQAEyq1TB2nKBLpKUiFUJ5Geg=",
+ "attachmentIsInline": false,
+ "attachmentLastModifiedTime": "2019-08-11T11:06:02+00:00",
+ "attachmentContentLocation": null,
+ "attachmentContentType": "text/rtf",
+ "originalItemId": "AAMkADQ0NmFFijer3FFmNTZjNTMxNwBGAAAAAAFSAAfxw+jAAA=",
+ "attachmentContentId": null
+ }
+ ],
+ "headers": [
+ {
+ "name": "Subject",
+ "value": "Get Attachment Email"
+ },
+ ...
+ ],
+ "isRead": true,
+ "messageId": "",
+ "receivedBy": "test@demistodev.onmicrosoft.com",
+ "datetimeSent": "2019-08-11T10:57:36Z",
+ "lastModifiedTime": "2019-08-11T11:13:59Z",
+ "mailbox": "test@demistodev.onmicrosoft.com",
+ "importance": "Normal",
+ "textBody": "Some text inside email\r\n",
+ "sender": "test2@demistodev.onmicrosoft.com"
+ }
+ }
+}
+
+```
+
+### 7\. Get the contacts for a mailbox
+
+* * *
+
+Retrieves contacts for a specified mailbox.
+
+##### Required Permissions
+
+Impersonation rights required. In order to perform actions on the target mailbox of other users, the service account must be part of the ApplicationImpersonation role.
+
+##### Base Command
+
+`ews-get-contacts`
+
+##### Input
+
+|**Argument Name**|**Description**|**Required**|
+|--- |--- |--- |
+|target-mailbox|The mailbox for which to retrieve the contacts.|Optional|
+|limit|Maximum number of results to return.|Optional|
+
+##### Context Output
+
+|**Path**|**Type**|**Description**|
+|--- |--- |--- |
+|Account.Email.EwsContacts.displayName|Unknown|The contact name.|
+|Account.Email.EwsContacts.lastModifiedTime|Unknown|The time that the contact was last modified.|
+|Account.Email.EwsContacts.emailAddresses|Unknown|Phone numbers of the contact.|
+|Account.Email.EwsContacts.physicalAddresses|Unknown|Physical addresses of the contact.|
+|Account.Email.EwsContacts.phoneNumbers.phoneNumber|Unknown|Email addresses of the contact.|
+
+##### Command Example
+
+```
+!ews-get-contacts limit="1"
+```
+
+##### Human Readable Output
+
+|changekey|culture|datetimeCreated|datetimeReceived|datetimeSent|displayName|emailAddresses|fileAs|fileAsMapping|givenName|id|importance|itemClass|lastModifiedName|lastModifiedTime|postalAddressIndex|sensitivity|subject|uniqueBody|webClientReadFormQueryString|
+|--- |--- |--- |--- |--- |--- |--- |--- |--- |--- |--- |--- |--- |--- |--- |--- |--- |--- |--- |--- |
+|EABYACAADcsxRwRjq/zTrN6vWSzKAK1Dl3N|en-US|2019-08-05T12:35:36Z|2019-08-05T12:35:36Z|2019-08-05T12:35:36Z|Contact Name|some@dev.microsoft.com|Contact Name|LastCommaFirst|Contact Name|AHSNNK3NQNcasnc3SAS/zTrN6vWSzK4OWAAAAAAEOAADrxRwRjq/zTrNFSsfsfVWAAK1KsF3AAA=|Normal|IPM.Contact|John Smith|2019-08-05T12:35:36Z|None|Normal|Contact Name||https://outlook.office365.com/owa/?ItemID=***|
+
+##### Context Example
+
+```
+{
+ "Account.Email": [
+ {
+ "itemClass": "IPM.Contact",
+ "lastModifiedName": "John Smith",
+ "displayName": "Contact Name",
+ "datetimeCreated": "2019-08-05T12:35:36Z",
+ "datetimeReceived": "2019-08-05T12:35:36Z",
+ "fileAsMapping": "LastCommaFirst",
+ "importance": "Normal",
+ "sensitivity": "Normal",
+ "postalAddressIndex": "None",
+ "webClientReadFormQueryString": "https://outlook.office365.com/owa/?ItemID=***",
+ "uniqueBody": "",
+ "fileAs": "Contact Name",
+ "culture": "en-US",
+ "changekey": "EABYACAADcsxRwRjq/zTrN6vWSzKAK1Dl3N",
+ "lastModifiedTime": "2019-08-05T12:35:36Z",
+ "datetimeSent": "2019-08-05T12:35:36Z",
+ "emailAddresses": [
+ "some@dev.microsoft.com"
+ ],
+ "givenName": "Contact Name",
+ "id": "AHSNNK3NQNcasnc3SAS/zTrN6vWSzK4OWAAAAAAEOAADrxRwRjq/zTrNFSsfsfVWAAK1KsF3AAA=",
+ "subject": "Contact Name"
+ }
+ ]
+}
+
+```
+
+### 8\. Get the out-of-office status for a mailbox
+
+* * *
+
+Retrieves the out-of-office status for a specified mailbox.
+
+##### Required Permissions
+
+Impersonation rights are required. To perform actions on the target mailbox of other users, the service account must be part the ApplicationImpersonation role.
+
+##### Base Command
+
+`ews-get-out-of-office`
+
+##### Input
+
+|**Argument Name**|**Description**|**Required**|
+|--- |--- |--- |
+|target-mailbox|The mailbox for which to get the out-of-office status.|Required|
+
+##### Context Output
+
+|**Path**|**Type**|**Description**|
+|--- |--- |--- |
+|Account.Email.OutOfOffice.state|Unknown|Out-of-office state. The result can be: "Enabled", "Scheduled", or "Disabled".|
+|Account.Email.OutOfOffice.externalAudience|Unknown|Out-of-office external audience. Can be "None", "Known", or "All".|
+|Account.Email.OutOfOffice.start|Unknown|Out-of-office start date.|
+|Account.Email.OutOfOffice.end|Unknown|Out-of-office end date.|
+|Account.Email.OutOfOffice.internalReply|Unknown|Out-of-office internal reply.|
+|Account.Email.OutOfOffice.externalReply|Unknown|Out-of-office external reply.|
+|Account.Email.OutOfOffice.mailbox|Unknown|Out-of-office mailbox.|
+
+##### Command Example
+
+```
+!ews-get-out-of-office target-mailbox=test@demistodev.onmicrosoft.com
+```
+
+##### Human Readable Output
+
+|end|externalAudience|mailbox|start|state|
+|--- |--- |--- |--- |--- |
+|2019-08-12T13:00:00Z|All|test@demistodev.onmicrosoft.com|2019-08-11T13:00:00Z|Disabled|
+
+##### Context Example
+
+```
+{
+ "Account": {
+ "Email": {
+ "OutOfOffice": {
+ "start": "2019-08-11T13:00:00Z",
+ "state": "Disabled",
+ "mailbox": "test@demistodev.onmicrosoft.com",
+ "end": "2019-08-12T13:00:00Z",
+ "externalAudience": "All"
+ }
+ }
+ }
+}
+
+```
+
+### 9\. Recover soft-deleted messages
+
+* * *
+
+Recovers messages that were soft-deleted.
+
+##### Required Permissions
+
+Impersonation rights are required. To perform actions on the target mailbox of other users, the service account must be part of the ApplicationImpersonation role.
+
+##### Base Command
+
+`ews-recover-messages`
+
+##### Input
+
+|**Argument Name**|**Description**|**Required**|
+|--- |--- |--- |
+|message-ids|A CSV list of message IDs. Run the py-ews-delete-items command to retrieve the message IDs|Required|
+|target-folder-path|The folder path to recover the messages to.|Required|
+|target-mailbox|The mailbox in which the messages found. If empty, will use the default mailbox. If you specify a different mailbox, you might need impersonation rights to the mailbox.|Optional|
+|is-public|Whether the target folder is a Public Folder.|Optional|
+
+##### Context Output
+
+|**Path**|**Type**|**Description**|
+|--- |--- |--- |
+|EWS.Items.itemId|Unknown|The item ID of the recovered item.|
+|EWS.Items.messageId|Unknown|The message ID of the recovered item.|
+|EWS.Items.action|Unknown|The action taken on the item. The value will be 'recovered'.|
+
+##### Command Example
+
+```
+!ews-recover-messages message-ids= target-folder-path=Moving target-mailbox=test@demistodev.onmicrosoft.com
+```
+
+##### Human Readable Output
+
+|action|itemId|messageId|
+|--- |--- |--- |
+|recovered|AAVCSVS1hN2NkLThmZjdmNTZjNTMxNwBGAAAAAAA4kxh+ed33wX3aBwCyyVyFtlsUQZfBJebinpkUAAAa2bUBAACyyVyFtlscfxxd/AAA=||
+
+##### Context Example
+
+```
+{
+ "EWS": {
+ "Items": {
+ "action": "recovered",
+ "itemId": "AAVCSVS1hN2NkLThmZjdmNTZjNTMxNwBGAAAAAAA4kxh+ed33wX3aBwCyyVyFtlsUQZfBJebinpkUAAAa2bUBAACyyVyFtlscfxxd/AAA=",
+ "messageId": ""
+ }
+ }
+}
+
+```
+
+### 10\. Create a folder
+
+* * *
+
+Creates a new folder in a specified mailbox.
+
+##### Required Permissions
+
+Impersonation rights are required. To perform actions on the target mailbox of other users, the service account must be part of the ApplicationImpersonation role.
+
+##### Base Command
+
+`ews-create-folder`
+
+##### Input
+
+|**Argument Name**|**Description**|**Required**|
+|--- |--- |--- |
+|new-folder-name|The name of the new folder.|Required|
+|folder-path|Path to locate the new folder. Exchange folder ID is also supported.|Required|
+|target-mailbox|The mailbox in which to create the folder.|Optional|
+
+##### Context Output
+
+There is no context output for this command.
+
+##### Command Example
+
+```
+!ews-create-folder folder-path=Inbox new-folder-name="Created Folder" target-mailbox=test@demistodev.onmicrosoft.com
+```
+
+##### Human Readable Output
+
+Folder Inbox\Created Folder created successfully
+
+### 11\. Mark an item as junk
+
+* * *
+
+Marks an item as junk. This is commonly used to block an email address. For more information, see the [Microsoft documentation](https://msdn.microsoft.com/en-us/library/office/dn481311(v=exchg.150).aspx).
+
+##### Required Permissions
+
+Impersonation rights are required. To perform actions on the target mailbox of other users, the service account must be part of the ApplicationImpersonation role.
+
+##### Base Command
+
+`ews-mark-item-as-junk`
+
+##### Input
+
+|**Argument Name**|**Description**|**Required**|
+|--- |--- |--- |
+|item-id|The item ID to mark as junk.|Required|
+|move-items|Whether to move the item from the original folder to the junk folder.|Optional|
+|target-mailbox|If empty, will use the default mailbox. If you specify a different mailbox, you might need impersonation rights to the mailbox.|Optional|
+
+##### Context Output
+
+There is no context output for this command.
+
+##### Command Example
+
+```
+!ews-mark-item-as-junk item-id=AAMkcSQ0NmFkOhmZjdmNTZjNTMxNwBGAAAAAAA4kxh+ed3JTJPMPXU3wX3aBwCyyVyFtlsUcsBJebinpkUAAAAAAEMASFDkUAAAfxuiSAAA= move-items=yes target-mailbox=test@demistodev.onmicrosoft.com
+```
+
+##### Human Readable Output
+
+|action|itemId|
+|--- |--- |
+|marked-as-junk|AAMkcSQ0NmFkOhmZjdmNTZjNTMxNwBGAAAAAAA4kxh+ed3JTJPMPXU3wX3aBwCyyVyFtlsUcsBJebinpkUAAAAAAEMASFDkUAAAfxuiSAAA=|
+
+##### Context Example
+
+```
+{
+ "EWS": {
+ "Items": {
+ "action": "marked-as-junk",
+ "itemId": "AAMkcSQ0NmFkOhmZjdmNTZjNTMxNwBGAAAAAAA4kxh+ed3JTJPMPXU3wX3aBwCyyVyFtlsUcsBJebinpkUAAAAAAEMASFDkUAAAfxuiSAAA="
+ }
+ }
+}
+
+```
+
+### 12\. Search for folders
+
+* * *
+
+Retrieves information for the folders of the specified mailbox. Only folders with read permissions will be returned. Your visual folders on the mailbox, such as "Inbox", are under the folder "Top of Information Store".
+
+##### Required Permissions
+
+Impersonation rights are required. To perform actions on the target mailbox of other users, the service account must be part of the ApplicationImpersonation role.
+
+##### Base Command
+
+`ews-find-folders`
+
+##### Input
+
+|**Argument Name**|**Description**|**Required**|
+|--- |--- |--- |
+|target-mailbox|The mailbox on which to apply the command.|Optional|
+|is-public|Whether to find Public Folders.|Optional|
+
+##### Context Output
+
+|**Path**|**Type**|**Description**|
+|--- |--- |--- |
+|EWS.Folders.name|string|Folder name.|
+|EWS.Folders.id|string|Folder ID.|
+|EWS.Folders.totalCount|Unknown|Number of items in the folder.|
+|EWS.Folders.unreadCount|number|Number of unread items in the folder.|
+|EWS.Folders.changeKey|number|Folder change key.|
+|EWS.Folders.childrenFolderCount|number|Number of sub-folders.|
+
+##### Command Example
+
+```
+!ews-find-folders target-mailbox=test@demistodev.onmicrosoft.com
+```
+
+##### Human Readable Output
+
+```
+root
+├── AllContacts
+├── AllItems
+├── Common Views
+├── Deferred Action
+├── ExchangeSyncData
+├── Favorites
+├── Freebusy Data
+├── Location
+├── MailboxAssociations
+├── My Contacts
+├── MyContactsExtended
+├── People I Know
+├── PeopleConnect
+├── Recoverable Items
+│ ├── Calendar Logging
+│ ├── Deletions
+│ ── Purges
+│ └── Versions
+├── Reminders
+├── Schedule
+├── Sharing
+├── Shortcuts
+├── Spooler Queue
+├── System
+├── To-Do Search
+├── Top of Information Store
+│ ├── Calendar
+│ ├── Contacts
+│ │ ├── GAL Contacts
+│ │ ├── Recipient Cache
+│ ├── Conversation Action Settings
+│ ├── Deleted Items
+│ │ └── Create1
+│ ├── Drafts
+│ ├── Inbox
+...
+
+```
+
+##### Context Example
+
+```
+{
+ "EWS": {
+ "Folders": [
+ {
+ "unreadCount": 1,
+ "name": "Inbox",
+ "childrenFolderCount": 1,
+ "totalCount": 44,
+ "changeKey": "**********fefsduQi0",
+ "id": "*******VyFtlFDSAFDSFDAAA="
+ }
+ ...
+ ]
+ }
+}
+
+```
+
+### 13\. Get items of a folder
+
+* * *
+
+Retrieves items from a specified folder in a mailbox. The items are ordered by the item created time, most recent is first.
+
+##### Required Permissions
+
+Impersonation rights are required. To perform actions on the target mailbox of other users, the service account must be part of the ApplicationImpersonation role.
+
+##### Base Command
+
+`ews-get-items-from-folder`
+
+##### Input
+
+|**Argument Name**|**Description**|**Required**|
+|--- |--- |--- |
+|folder-path|The folder path from which to get the items.|Required|
+|limit|Maximum number of items to return.|Optional|
+|target-mailbox|The mailbox on which to apply the command.|Optional|
+|is-public|Whether the folder is a Public Folder. Default is 'False'.|Optional|
+|get-internal-items|If the email item contains another email as an attachment (EML or MSG file), whether to retrieve the EML/MSG file attachment. Can be "yes" or "no". Default is "no".|Optional|
+
+##### Context Output
+
+|**Path**|**Type**|**Description**|
+|--- |--- |--- |
+|EWS.Items.itemId|string|The item ID of the email.|
+|EWS.Items.hasAttachments|boolean|Whether the email has attachments.|
+|EWS.Items.datetimeReceived|date|Received time of the email.|
+|EWS.Items.datetimeSent|date|Sent time of the email.|
+|EWS.Items.headers|Unknown|Email headers (list).|
+|EWS.Items.sender|string|Sender mail address of the email.|
+|EWS.Items.subject|string|Subject of the email.|
+|EWS.Items.textBody|string|Body of the email (as text).|
+|EWS.Items.size|number|Email size.|
+|EWS.Items.toRecipients|Unknown|Email recipients addresses (list).|
+|EWS.Items.receivedBy|Unknown|Received by address of the email.|
+|EWS.Items.messageId|string|Email message ID.|
+|EWS.Items.body|string|Body of the email (as HTML).|
+|EWS.Items.FileAttachments.attachmentId|unknown|Attachment ID of file attachment.|
+|EWS.Items.ItemAttachments.attachmentId|unknown|Attachment ID of the item attachment.|
+|EWS.Items.FileAttachments.attachmentName|unknown|Attachment name of the file attachment.|
+|EWS.Items.ItemAttachments.attachmentName|unknown|Attachment name of the item attachment.|
+|Email.Items.ItemAttachments.attachmentName|unknown|Attachment name of the item attachment.|
+|EWS.Items.isRead|String|The read status of the email.|
+
+##### Command Example
+
+```
+!ews-get-items-from-folder folder-path=Test target-mailbox=test@demistodev.onmicrosoft.com limit=1
+```
+
+##### Human Readable Output
+
+|sender|subject|hasAttachments|datetimeReceived|receivedBy|author|toRecipients|itemId|
+|--- |--- |--- |--- |--- |--- |--- |--- |
+|test2@demistodev.onmicrosoft.com|Get Attachment Email|true|2019-08-11T10:57:37Z|test@demistodev.onmicrosoft.com|test2@demistodev.onmicrosoft.com|test@demistodev.onmicrosoft.com|AAFSFSFFtlsUQZfBJebinpkUAAABjKMGAACyyVyFtlsUQZfBJebinpkUAAAsfw+jAAA=|
+
+##### Context Example
+
+```
+{
+ "EWS": {
+ "Items": {
+ "body": "\r\n\r\n\r\n\r\n\r\n\r\n
",
+ "isRead": false,
+ "receivedBy": "testbox@demistodev.onmicrosoft.com",
+ "author": "darbel@paloaltonetworks.com",
+ "toRecipients": [
+ "testbox@demistodev.onmicrosoft.com"
+ ],
+ "FileAttachments": [
+ {
+ "originalItemId": "originalItemId",
+ "attachmentId": "attachmentId",
+ "attachmentName": "anar (1).jpeg",
+ "attachmentSHA256": "afc8f82063b4985b57292f07682c3010eef0d3cf3132482d418ad47df2993cca",
+ "attachmentContentType": "image/jpeg",
+ "attachmentContentId": "f_kal0kd8h0",
+ "attachmentContentLocation": null,
+ "attachmentSize": 31555,
+ "attachmentLastModifiedTime": "2020-05-24T15:05:22+03:00",
+ "attachmentIsInline": false,
+ "attachmentType": "FileAttachment"
+ }
+ ],
+ "mailbox": "testbox@demistodev.onmicrosoft.com"
+ }
+ ],
+ "ews-expand-group": [
+ {
+ "mailbox": "avishai@demistodev.onmicrosoft.com",
+ "displayName": "Avishai Brandeis",
+ "mailboxType": "Mailbox"
+ },
+ {
+ "mailbox": "testbox@demistodev.onmicrosoft.com",
+ "displayName": "test box",
+ "mailboxType": "Mailbox"
+ }
+ ]
+}
diff --git a/Packs/EWS/Integrations/EWSv2/CHANGELOG.md b/Packs/EWS/Integrations/EWSv2/CHANGELOG.md
index 8b9ef983762..a05f705f791 100644
--- a/Packs/EWS/Integrations/EWSv2/CHANGELOG.md
+++ b/Packs/EWS/Integrations/EWSv2/CHANGELOG.md
@@ -1,5 +1,5 @@
## [Unreleased]
--
+- Fixed a bug in the **test module** which failed on a delegated mailbox.
## [20.5.0] - 2020-05-12
- Command - "ews-get-items", add Email entry context by demisto standards as an output.
diff --git a/Packs/EWS/Integrations/EWSv2/EWSv2.py b/Packs/EWS/Integrations/EWSv2/EWSv2.py
index bd1af87aeb5..cfa97752299 100644
--- a/Packs/EWS/Integrations/EWSv2/EWSv2.py
+++ b/Packs/EWS/Integrations/EWSv2/EWSv2.py
@@ -1983,11 +1983,12 @@ def test_module():
global IS_TEST_MODULE
IS_TEST_MODULE = True
account = get_account(ACCOUNT_EMAIL)
- if not account.root.effective_rights.read: # pylint: disable=E1101
+ folder = get_folder_by_path(account, FOLDER_NAME, IS_PUBLIC_FOLDER)
+ if not folder.effective_rights.read: # pylint: disable=E1101
raise Exception("Success to authenticate, but user has no permissions to read from the mailbox. "
"Need to delegate the user permissions to the mailbox - "
"please read integration documentation and follow the instructions")
- get_folder_by_path(account, FOLDER_NAME, IS_PUBLIC_FOLDER).test_access()
+ folder.test_access()
except ErrorFolderNotFound as e:
if "Top of Information Store" in e.message:
raise Exception(
@@ -2145,7 +2146,7 @@ def sub_main():
error_message += "\nFull debug log:\n" + debug_log
if demisto.command() == 'fetch-incidents':
- raise
+ raise Exception(str(e) + traceback.format_exc())
if demisto.command() == 'ews-search-mailbox' and isinstance(e, ValueError):
return_error(message="Selected invalid field, please specify valid field name.", error=e)
if IS_TEST_MODULE:
diff --git a/Packs/EWS/Integrations/EWSv2/README.md b/Packs/EWS/Integrations/EWSv2/README.md
index 8f796c7d0fd..25462e28949 100644
--- a/Packs/EWS/Integrations/EWSv2/README.md
+++ b/Packs/EWS/Integrations/EWSv2/README.md
@@ -72,7 +72,7 @@
To use Fetch incidents, configure a new instance and select theFetches incidentsoption in the instance settings.
IMPORTANT: The initial fetch interval is the previous 10 minutes. If no emails were fetched before from the destination folder- all emails from 10 minutes prior to the instance configuration and up to the current time will be fetched. Additionally moving messages manually to the destination folder will not trigger fetch incident. Define rules on phishing/target mailbox instead of moving messages manually.
Pay special attention to the following fields in the instance settings:
-
Email address from which to fetch incidents– mailbox to fetch incidents from. Name of the folder from which to fetch incidents– use this field to configure the destination folder from where emails should be fetched. The default is Inbox folder. Has impersonation rights– mark this option if you set the target mailbox to an account different than your personal account. Otherwise Delegation access will be used instead of Impersonation. Find more information on impersonation or delegation rights at ‘Additional Info’ section below.
+
Email address from which to fetch incidents– mailbox to fetch incidents from. Name of the folder from which to fetch incidents– use this field to configure the destination folder from where emails should be fetched. The default is Inbox folder. Please note, if Exchange is configured with an international flavor `Inbox` will be named according to the configured language. Has impersonation rights– mark this option if you set the target mailbox to an account different than your personal account. Otherwise Delegation access will be used instead of Impersonation. Find more information on impersonation or delegation rights at ‘Additional Info’ section below.
Commands
You can execute these commands from the Demisto 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.
@@ -2796,4 +2796,4 @@
New-Compliance Search
-
The EWS v2 integration uses remote ps-session to run commands of compliance search as part of Office 365. To check if your account can connect to Office 365 Security & Compliance Center via powershell, check the followingsteps. New-Compliance search is a long-running task which has no limitation of searched mailboxes and therefore the suggestion is to useOffice 365 Search and Deleteplaybook. New-Compliance search returns statistics of matched content search query and doesn't return preview of found emails in contrast toews-search-mailboxescommand.
\ No newline at end of file
+
The EWS v2 integration uses remote ps-session to run commands of compliance search as part of Office 365. To check if your account can connect to Office 365 Security & Compliance Center via powershell, check the followingsteps. New-Compliance search is a long-running task which has no limitation of searched mailboxes and therefore the suggestion is to useOffice 365 Search and Deleteplaybook. New-Compliance search returns statistics of matched content search query and doesn't return preview of found emails in contrast toews-search-mailboxescommand.
diff --git a/Packs/EWS/ReleaseNotes/1_1_0.md b/Packs/EWS/ReleaseNotes/1_1_0.md
new file mode 100644
index 00000000000..2de5c7d1cf8
--- /dev/null
+++ b/Packs/EWS/ReleaseNotes/1_1_0.md
@@ -0,0 +1,4 @@
+
+#### Integrations
+##### __EWSO365__
+The new EWS O365 integration uses OAuth 2.0 protocol and can be used with Exchange Online and Office 365 (mail).
diff --git a/Packs/EWS/ReleaseNotes/1_1_1.md b/Packs/EWS/ReleaseNotes/1_1_1.md
new file mode 100644
index 00000000000..d0a86d27f83
--- /dev/null
+++ b/Packs/EWS/ReleaseNotes/1_1_1.md
@@ -0,0 +1,4 @@
+
+#### Integrations
+##### __EWS v2__
+- Fixed a bug in the **test module** which failed on a delegated mailbox.
diff --git a/Packs/EWS/ReleaseNotes/1_1_2.md b/Packs/EWS/ReleaseNotes/1_1_2.md
new file mode 100644
index 00000000000..6eb618031f7
--- /dev/null
+++ b/Packs/EWS/ReleaseNotes/1_1_2.md
@@ -0,0 +1,4 @@
+
+#### Integrations
+##### __EWS v2__
+- Improved handling of errors raised in the incident fetch flow.
diff --git a/Packs/EWS/TestPlaybooks/playbook-EWS_O365_test.yml b/Packs/EWS/TestPlaybooks/playbook-EWS_O365_test.yml
new file mode 100644
index 00000000000..4bedd091880
--- /dev/null
+++ b/Packs/EWS/TestPlaybooks/playbook-EWS_O365_test.yml
@@ -0,0 +1,859 @@
+id: EWS_O365_test
+version: -1
+name: EWS_O365_test
+fromversion: 5.0.0
+description: ""
+starttaskid: "0"
+tasks:
+ "0":
+ id: "0"
+ taskid: 868b5714-1f56-40ad-8e0b-64e807805489
+ type: start
+ task:
+ description: ""
+ id: 868b5714-1f56-40ad-8e0b-64e807805489
+ version: -1
+ name: ""
+ iscommand: false
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "1"
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 50
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "1":
+ id: "1"
+ taskid: 4aa13052-7a37-4389-83c0-3b103179d59f
+ type: regular
+ task:
+ description: ""
+ id: 4aa13052-7a37-4389-83c0-3b103179d59f
+ version: -1
+ name: DeleteContext
+ script: DeleteContext
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "2"
+ scriptarguments:
+ all:
+ simple: "yes"
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 195
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "2":
+ id: "2"
+ taskid: 140a8a39-d6f0-4a0d-8652-ce4cc7b155b9
+ type: regular
+ task:
+ description: ""
+ id: 140a8a39-d6f0-4a0d-8652-ce4cc7b155b9
+ version: -1
+ name: ews-get-attachment
+ script: '|||ews-get-attachment'
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "3"
+ scriptarguments:
+ attachment-ids: {}
+ item-id:
+ simple: AAMkAGZiODc1MGY3LTBiODEtNDQ3ZC05YzdjLWVkYjhiMjFlMTk1MABGAAAAAAB4Hvam6Pd3Sqro7SBw8T0oBwDj9X3Q04QOTYf/bBd1+UDZAAAAAAEMAADj9X3Q04QOTYf/bBd1+UDZAAAHclB6AAA=
+ target-mailbox: {}
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 370
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "3":
+ id: "3"
+ taskid: e4b0396b-9fd8-481b-8722-e09914b5340f
+ type: condition
+ task:
+ description: ""
+ id: e4b0396b-9fd8-481b-8722-e09914b5340f
+ version: -1
+ name: Verify Outputs
+ type: condition
+ iscommand: false
+ brand: ""
+ nexttasks:
+ "yes":
+ - "6"
+ separatecontext: false
+ conditions:
+ - label: "yes"
+ condition:
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Items.FileAttachments.attachmentId
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Items.FileAttachments.attachmentName
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Items.FileAttachments.attachmentSHA256
+ iscontext: true
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 545
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "6":
+ id: "6"
+ taskid: 8e54bd48-8d5c-485f-852d-59793c325221
+ type: regular
+ task:
+ description: ""
+ id: 8e54bd48-8d5c-485f-852d-59793c325221
+ version: -1
+ name: ews-get-searchable-mailboxes
+ script: '|||ews-get-searchable-mailboxes'
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "7"
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 720
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "7":
+ id: "7"
+ taskid: 4daf91b6-b466-41f3-8b7c-d6b160b086c1
+ type: condition
+ task:
+ description: ""
+ id: 4daf91b6-b466-41f3-8b7c-d6b160b086c1
+ version: -1
+ name: Verify Outputs
+ type: condition
+ iscommand: false
+ brand: ""
+ nexttasks:
+ "yes":
+ - "14"
+ separatecontext: false
+ conditions:
+ - label: "yes"
+ condition:
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Mailboxes.mailbox
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Mailboxes.mailboxId
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Mailboxes.displayName
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Mailboxes.isExternal
+ iscontext: true
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 895
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "14":
+ id: "14"
+ taskid: 04028000-32ce-44b2-84bc-e4e5dad7c7f4
+ type: regular
+ task:
+ description: ""
+ id: 04028000-32ce-44b2-84bc-e4e5dad7c7f4
+ version: -1
+ name: ews-search-mailbox
+ script: '|||ews-search-mailbox'
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "15"
+ scriptarguments:
+ folder-path: {}
+ is-public: {}
+ limit: {}
+ message-id: {}
+ query:
+ simple: move me
+ selected-fields: {}
+ target-mailbox: {}
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 1070
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "15":
+ id: "15"
+ taskid: e034e902-096d-451d-8751-44a53b74b098
+ type: condition
+ task:
+ description: ""
+ id: e034e902-096d-451d-8751-44a53b74b098
+ version: -1
+ name: Verify Outputs
+ type: condition
+ iscommand: false
+ brand: ""
+ nexttasks:
+ "yes":
+ - "16"
+ separatecontext: false
+ conditions:
+ - label: "yes"
+ condition:
+ - - operator: isEqualString
+ left:
+ value:
+ simple: EWS.Items.itemId
+ iscontext: true
+ right:
+ value:
+ simple: AAMkAGZiODc1MGY3LTBiODEtNDQ3ZC05YzdjLWVkYjhiMjFlMTk1MABGAAAAAAB4Hvam6Pd3Sqro7SBw8T0oBwDj9X3Q04QOTYf/bBd1+UDZAAAAAAEMAADj9X3Q04QOTYf/bBd1+UDZAAAHclB4AAA=
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 1245
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "16":
+ id: "16"
+ taskid: f54e460a-9d04-4fe9-8a06-ad6272af79fb
+ type: regular
+ task:
+ description: ""
+ id: f54e460a-9d04-4fe9-8a06-ad6272af79fb
+ version: -1
+ name: ews-get-contacts
+ script: '|||ews-get-contacts'
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "18"
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 1420
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "18":
+ id: "18"
+ taskid: 0d4138c5-30e9-437b-85e4-720d8862d74f
+ type: regular
+ task:
+ description: ""
+ id: 0d4138c5-30e9-437b-85e4-720d8862d74f
+ version: -1
+ name: ews-get-out-of-office
+ script: '|||ews-get-out-of-office'
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "19"
+ scriptarguments:
+ target-mailbox:
+ simple: avishai@demistodev.onmicrosoft.com
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 1595
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "19":
+ id: "19"
+ taskid: 7eb0d117-14c3-4b78-89a1-71dd92f62edf
+ type: condition
+ task:
+ description: ""
+ id: 7eb0d117-14c3-4b78-89a1-71dd92f62edf
+ version: -1
+ name: Verify Outputs
+ type: condition
+ iscommand: false
+ brand: ""
+ nexttasks:
+ "yes":
+ - "24"
+ separatecontext: false
+ conditions:
+ - label: "yes"
+ condition:
+ - - operator: isEqualString
+ left:
+ value:
+ simple: Account.Email.OutOfOffice.state
+ iscontext: true
+ right:
+ value:
+ simple: Disabled
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 1770
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "24":
+ id: "24"
+ taskid: 9e3cacfb-f02d-4b5f-8b10-66f150c4c27f
+ type: regular
+ task:
+ description: ""
+ id: 9e3cacfb-f02d-4b5f-8b10-66f150c4c27f
+ version: -1
+ name: ews-find-folders
+ script: '|||ews-find-folders'
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "25"
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 1945
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "25":
+ id: "25"
+ taskid: e4c73fb6-9c0f-419b-85f9-116c4e4b69be
+ type: condition
+ task:
+ description: ""
+ id: e4c73fb6-9c0f-419b-85f9-116c4e4b69be
+ version: -1
+ name: Verify Outputs
+ type: condition
+ iscommand: false
+ brand: ""
+ nexttasks:
+ "yes":
+ - "26"
+ separatecontext: false
+ conditions:
+ - label: "yes"
+ condition:
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Folders.name
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Folders.id
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Folders.totalCount
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Folders.unreadCount
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Folders.changeKey
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Folders.childrenFolderCount
+ iscontext: true
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 2120
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "26":
+ id: "26"
+ taskid: bfc3bff4-3c18-4d72-8bcf-3a6626b047e6
+ type: regular
+ task:
+ description: ""
+ id: bfc3bff4-3c18-4d72-8bcf-3a6626b047e6
+ version: -1
+ name: ews-get-items-from-folder
+ script: '|||ews-get-items-from-folder'
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "28"
+ scriptarguments:
+ folder-path:
+ simple: AllItems
+ get-internal-item: {}
+ is-public: {}
+ limit:
+ simple: "5"
+ target-mailbox: {}
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 2295
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "28":
+ id: "28"
+ taskid: 92eb2cdc-6f16-4597-8851-09984d8f2778
+ type: regular
+ task:
+ description: ""
+ id: 92eb2cdc-6f16-4597-8851-09984d8f2778
+ version: -1
+ name: ews-get-items
+ script: '|||ews-get-items'
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "29"
+ scriptarguments:
+ item-ids:
+ simple: AAMkAGZiODc1MGY3LTBiODEtNDQ3ZC05YzdjLWVkYjhiMjFlMTk1MABGAAAAAAB4Hvam6Pd3Sqro7SBw8T0oBwDj9X3Q04QOTYf/bBd1+UDZAAAAAAEMAADj9X3Q04QOTYf/bBd1+UDZAAAHclB6AAA=
+ target-mailbox: {}
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 2470
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "29":
+ id: "29"
+ taskid: 5f7483e1-47b0-4686-8240-bdd61149800a
+ type: condition
+ task:
+ description: ""
+ id: 5f7483e1-47b0-4686-8240-bdd61149800a
+ version: -1
+ name: Verify Outputs
+ type: condition
+ iscommand: false
+ brand: ""
+ nexttasks:
+ "yes":
+ - "32"
+ separatecontext: false
+ conditions:
+ - label: "yes"
+ condition:
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Items.itemId
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Items.datetimeReceived
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Items.datetimeSent
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Items.headers
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Items.sender
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Items.subject
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Items.size
+ iscontext: true
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 2645
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "32":
+ id: "32"
+ taskid: 0b3e36db-8292-42ad-8789-9cebe311dc1c
+ type: regular
+ task:
+ description: ""
+ id: 0b3e36db-8292-42ad-8789-9cebe311dc1c
+ version: -1
+ name: ews-get-folder
+ script: '|||ews-get-folder'
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "33"
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 2820
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "33":
+ id: "33"
+ taskid: cb980652-ce60-461e-82b7-82580cdab17b
+ type: condition
+ task:
+ description: ""
+ id: cb980652-ce60-461e-82b7-82580cdab17b
+ version: -1
+ name: Verify Outputs
+ type: condition
+ iscommand: false
+ brand: ""
+ nexttasks:
+ "yes":
+ - "34"
+ separatecontext: false
+ conditions:
+ - label: "yes"
+ condition:
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Folders.id
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Folders.name
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Folders.changeKey
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Folders.totalCount
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Folders.childrenFolderCount
+ iscontext: true
+ - - operator: isNotEmpty
+ left:
+ value:
+ simple: EWS.Folders.unreadCount
+ iscontext: true
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 2995
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "34":
+ id: "34"
+ taskid: 0001331a-66c0-4fa3-8434-71dfee5da637
+ type: regular
+ task:
+ description: ""
+ id: 0001331a-66c0-4fa3-8434-71dfee5da637
+ version: -1
+ name: ews-expand-group
+ script: '|||ews-expand-group'
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "40"
+ scriptarguments:
+ email-address:
+ simple: testgroup-1@demistodev.onmicrosoft.com
+ recursive-expansion: {}
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 3170
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "39":
+ id: "39"
+ taskid: 8ff5c4d0-6f1c-4a82-8e83-cc4a011931a7
+ type: title
+ task:
+ description: ""
+ id: 8ff5c4d0-6f1c-4a82-8e83-cc4a011931a7
+ version: -1
+ name: Test Done
+ type: title
+ iscommand: false
+ brand: ""
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 3695
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "40":
+ id: "40"
+ taskid: 9607062f-6cf7-495e-822e-e465eec9d0a3
+ type: regular
+ task:
+ id: 9607062f-6cf7-495e-822e-e465eec9d0a3
+ version: -1
+ name: ews-get-items-as-eml
+ description: Retrieves items by item ID and uploads its content as an EML file.
+ script: '|||ews-get-items-as-eml'
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "41"
+ scriptarguments:
+ item-id:
+ simple: AAMkAGZiODc1MGY3LTBiODEtNDQ3ZC05YzdjLWVkYjhiMjFlMTk1MABGAAAAAAB4Hvam6Pd3Sqro7SBw8T0oBwDj9X3Q04QOTYf/bBd1+UDZAAAAAAEMAADj9X3Q04QOTYf/bBd1+UDZAAAHclB6AAA=
+ target-mailbox: {}
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 3345
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+ "41":
+ id: "41"
+ taskid: 0e0078d8-7bed-4443-80ff-f12361f6626b
+ type: regular
+ task:
+ id: 0e0078d8-7bed-4443-80ff-f12361f6626b
+ version: -1
+ name: Get public folder
+ description: Retrieves a single folder.
+ script: '|||ews-get-folder'
+ type: regular
+ iscommand: true
+ brand: ""
+ nexttasks:
+ '#none#':
+ - "39"
+ scriptarguments:
+ folder-path:
+ simple: test_p
+ is-public:
+ simple: "True"
+ target-mailbox: {}
+ separatecontext: false
+ view: |-
+ {
+ "position": {
+ "x": 50,
+ "y": 3520
+ }
+ }
+ note: false
+ timertriggers: []
+ ignoreworker: false
+ skipunavailable: false
+ quietmode: 0
+view: |-
+ {
+ "linkLabelsPosition": {},
+ "paper": {
+ "dimensions": {
+ "height": 3710,
+ "width": 380,
+ "x": 50,
+ "y": 50
+ }
+ }
+ }
+inputs: []
+outputs: []
diff --git a/TestPlaybooks/NonCircleTests/playbook-Send-Email-To-Recipients-Test.yml b/Packs/EWS/TestPlaybooks/playbook-Send-Email-To-Recipients-Test.yml
similarity index 100%
rename from TestPlaybooks/NonCircleTests/playbook-Send-Email-To-Recipients-Test.yml
rename to Packs/EWS/TestPlaybooks/playbook-Send-Email-To-Recipients-Test.yml
diff --git a/Packs/EWS/pack_metadata.json b/Packs/EWS/pack_metadata.json
index d4d697d9d1e..9335a270f26 100644
--- a/Packs/EWS/pack_metadata.json
+++ b/Packs/EWS/pack_metadata.json
@@ -2,7 +2,7 @@
"name": "EWS",
"description": "Exchange Web Services and Office 365 (mail)",
"support": "Cortex XSOAR",
- "currentVersion": "1.0.1",
+ "currentVersion": "1.1.2",
"author": "Cortex XSOAR",
"url": "https://www.paloaltonetworks.com/cortex",
"email": "",
diff --git a/TestPlaybooks/playbook-ExifReadTest.yml b/Packs/ExifRead/TestPlaybooks/playbook-ExifReadTest.yml
similarity index 100%
rename from TestPlaybooks/playbook-ExifReadTest.yml
rename to Packs/ExifRead/TestPlaybooks/playbook-ExifReadTest.yml
diff --git a/Packs/Expanse/pack_metadata.json b/Packs/Expanse/pack_metadata.json
index 017fe4aacd0..16e69431a48 100644
--- a/Packs/Expanse/pack_metadata.json
+++ b/Packs/Expanse/pack_metadata.json
@@ -12,7 +12,6 @@
],
"tags": [],
"useCases": [],
- "certification": "certified",
"keywords": [
"Expanse"
]
diff --git a/Packs/ExportIndicators/Integrations/ExportIndicators/README.md b/Packs/ExportIndicators/Integrations/ExportIndicators/README.md
index ea34ecebf1b..368dff31608 100644
--- a/Packs/ExportIndicators/Integrations/ExportIndicators/README.md
+++ b/Packs/ExportIndicators/Integrations/ExportIndicators/README.md
@@ -47,7 +47,7 @@ To access the Export Indicators service by instance name, make sure ***Instance
1. In Demisto, go to **Settings > About > Troubleshooting**.
2. In the **Server Configuration** section, verify that the ***instance.execute.external*** key is set to *true*. If this key does not exist, click **+ Add Server Configuration** and add the *instance.execute.external* and set the value to *true*. See [this documentation](https://xsoar.pan.dev/docs/integrations/long-running#invoking-http-integrations-via-cortex-xsoar-servers-route-handling) for further information.
-3. In a web browser, go to **https://**/instance/execute/**** .
+3. In a web browser, go to `https://**/instance/execute/**` .
### Update values in the export indicators service
---
@@ -60,17 +60,17 @@ Use the following arguments in the URL to change the request:
| **Argument Name** | **Description** | **Example** |
| --- | --- | --- |
-| n | The maximum number of entries in the output. If no value is provided, will use the value specified in the List Size parameter configured in the instance configuration. | https://{demisto_instance}/instance/execute/{ExportIndicators_instance_name}?n=50 |
-| s | The starting entry index from which to export the indicators. | https://{demisto_instance}/instance/execute/{ExportIndicators_instance_name}?s=10&n=50 |
-| v | The output format. Supports `text`, `csv`, `json`, `json-seq`,`xsoar-json`, `xsoar-seq`, `xsoar-csv`, `mwg`, `panosurl` and `proxysg` (alias: `bluecoat`). | https://{demisto_instance}/instance/execute/{ExportIndicators_instance_name}?v=json |
-| q | The query used to retrieve indicators from the system. | https://{demisto_instance}/instance/execute/{ExportIndicators_instance_name}?q="type:ip and sourceBrand:my_source" |
-| t | Only with `mwg` format. The type indicated on the top of the exported list. Supports: string, applcontrol, dimension, category, ip, mediatype, number and regex. | https://{demisto_instance}/instance/execute/{ExportIndicators_instance_name}?v=mwg&t=ip |
-| sp | Only with `panosurl` format. If set will strip ports off URLs, otherwise will ignore URLs with ports. | https://{demisto_instance}/instance/execute/{ExportIndicators_instance_name}?v=panosurl&sp |
-| di | Only with `panosurl` format. If set will ignore urls which are not compliant with PAN-OS URL format instead of being re-written. | https://{demisto_instance}/instance/execute/{ExportIndicators_instance_name}?v=panosurl&di |
-| cd | Only with `proxysg` format. The default category for the exported indicators. | https://{demisto_instance}/instance/execute/{ExportIndicators_instance_name}?v=proxysg&cd=default_category |
-| ca | Only with `proxysg` format. The categories which will be exported. Indicators not falling to these categories will be classified as the default category. | https://{demisto_instance}/instance/execute/{ExportIndicators_instance_name}?v=proxysg&ca=category1,category2 |
-| tr | Whether to collapse IPs. 0 - to not collapse, 1 - collapse to ranges or 2 - collapse to CIDRs | https://{demisto_instance}/instance/execute/{ExportIndicators_instance_name}?q="type:ip and sourceBrand:my_source"&tr=1 |
-| tx | Whether to output `csv` or `xsoar-csv` formats as textual web pages. | https://{demisto_instance}/instance/execute/{ExportIndicators_instance_name}?v=xsoar-csv&tx |
+| n | The maximum number of entries in the output. If no value is provided, will use the value specified in the List Size parameter configured in the instance configuration. | `https://{server_host}/instance/execute/{instance_name}?n=50` |
+| s | The starting entry index from which to export the indicators. | `https://{server_host}/instance/execute/{instance_name}?s=10&n=50` |
+| v | The output format. Supports `text`, `csv`, `json`, `json-seq`,`xsoar-json`, `xsoar-seq`, `xsoar-csv`, `mwg`, `panosurl` and `proxysg` (alias: `bluecoat`). | `https://{server_host}/instance/execute/{instance_name}?v=json` |
+| q | The query used to retrieve indicators from the system. | `https://{server_host}/instance/execute/{instance_name}?q="type:ip and sourceBrand:my_source"` |
+| t | Only with `mwg` format. The type indicated on the top of the exported list. Supports: string, applcontrol, dimension, category, ip, mediatype, number and regex. | `https://{server_host}/instance/execute/{instance_name}?v=mwg&t=ip` |
+| sp | Only with `panosurl` format. If set will strip ports off URLs, otherwise will ignore URLs with ports. | `https://{server_host}/instance/execute/{instance_name}?v=panosurl&sp` |
+| di | Only with `panosurl` format. If set will ignore urls which are not compliant with PAN-OS URL format instead of being re-written. | `https://{server_host}/instance/execute/{instance_name}?v=panosurl&di` |
+| cd | Only with `proxysg` format. The default category for the exported indicators. | `https://{server_host}/instance/execute/{instance_name}?v=proxysg&cd=default_category` |
+| ca | Only with `proxysg` format. The categories which will be exported. Indicators not falling to these categories will be classified as the default category. | `https://{server_host}/instance/execute/{instance_name}?v=proxysg&ca=category1,category2` |
+| tr | Whether to collapse IPs. 0 - to not collapse, 1 - collapse to ranges or 2 - collapse to CIDRs | `https://{server_host}/instance/execute/{instance_name}?q="type:ip and sourceBrand:my_source"&tr=1` |
+| tx | Whether to output `csv` or `xsoar-csv` formats as textual web pages. | `https://{server_host}/instance/execute/{instance_name}?v=xsoar-csv&tx` |
##### Base Command
diff --git a/Packs/ExtraHop/pack_metadata.json b/Packs/ExtraHop/pack_metadata.json
index ec5bfa455e4..39e7177b71a 100644
--- a/Packs/ExtraHop/pack_metadata.json
+++ b/Packs/ExtraHop/pack_metadata.json
@@ -1,11 +1,11 @@
{
"name": "ExtraHop Reveal(x)",
"description": "Network detection and response. Complete visibility of network communications at enterprise scale, real-time threat detections backed by machine learning, and guided investigation workflows that simplify response.",
- "support": "xsoar",
+ "support": "partner",
"currentVersion": "1.0.0",
- "author": "Cortex XSOAR",
- "url": "https://www.paloaltonetworks.com/cortex",
- "email": "",
+ "author": "ExtraHop",
+ "url": "",
+ "email": "support@extrahop.com",
"created": "2020-04-14T00:00:00Z",
"categories": [
"Network Security"
diff --git a/Packs/FeedAWS/pack_metadata.json b/Packs/FeedAWS/pack_metadata.json
index c37c6618dc5..a79ec81d80b 100644
--- a/Packs/FeedAWS/pack_metadata.json
+++ b/Packs/FeedAWS/pack_metadata.json
@@ -10,7 +10,9 @@
"categories": [
"Data Enrichment & Threat Intelligence"
],
- "tags": [],
+ "tags": [
+ "TIM"
+ ],
"useCases": [],
"keywords": [
"AWS",
diff --git a/Packs/FeedAlienVault/Integrations/FeedAlienVaultOTXTaxii/README.md b/Packs/FeedAlienVault/Integrations/FeedAlienVaultOTXTaxii/README.md
index 8ef7c39ac5e..24ef8ff567b 100644
--- a/Packs/FeedAlienVault/Integrations/FeedAlienVaultOTXTaxii/README.md
+++ b/Packs/FeedAlienVault/Integrations/FeedAlienVaultOTXTaxii/README.md
@@ -69,7 +69,7 @@ There is no context output for this command.
## Video Demo
diff --git a/Packs/FeedAlienVault/Integrations/FeedAlienVaultOTXTaxii/doc_files/AlienVault_OTX_Feed_Demo.mp4 b/Packs/FeedAlienVault/Integrations/FeedAlienVaultOTXTaxii/doc_files/AlienVault_OTX_Feed_Demo.mp4
deleted file mode 100644
index b8d8c8dfea6..00000000000
--- a/Packs/FeedAlienVault/Integrations/FeedAlienVaultOTXTaxii/doc_files/AlienVault_OTX_Feed_Demo.mp4
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b4446ea4054d235b517dd5d72279a2a59d5758b22ba7087a8790b3adb24c686f
-size 5836016
diff --git a/Packs/FeedAutofocus/Integrations/FeedAutofocus/README.md b/Packs/FeedAutofocus/Integrations/FeedAutofocus/README.md
index 40395aaee3c..8a790821222 100644
--- a/Packs/FeedAutofocus/Integrations/FeedAutofocus/README.md
+++ b/Packs/FeedAutofocus/Integrations/FeedAutofocus/README.md
@@ -100,7 +100,7 @@ To bring the next batch of indicators run:
## Demo Video
diff --git a/Packs/FeedAutofocus/Integrations/FeedAutofocus/demo_video/AutoFocus_Feed_demo.mp4 b/Packs/FeedAutofocus/Integrations/FeedAutofocus/demo_video/AutoFocus_Feed_demo.mp4
deleted file mode 100644
index 846180217fa..00000000000
--- a/Packs/FeedAutofocus/Integrations/FeedAutofocus/demo_video/AutoFocus_Feed_demo.mp4
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d545c97529202820027aa264de2a811f24f0f6110fee4459816afb6c25c22449
-size 5364646
diff --git a/Packs/FeedAutofocus/pack_metadata.json b/Packs/FeedAutofocus/pack_metadata.json
index 29613f49410..eb122ae3e5a 100644
--- a/Packs/FeedAutofocus/pack_metadata.json
+++ b/Packs/FeedAutofocus/pack_metadata.json
@@ -1,19 +1,20 @@
{
- "name": "Autofocus Feed",
- "description": "Indicators feed from Autofocus",
- "support": "xsoar",
- "currentVersion": "1.0.0",
- "author": "Cortex XSOAR",
- "url": "https://www.paloaltonetworks.com/cortex",
- "email": "",
- "created": "2020-03-09T16:04:45Z",
- "categories": [
- "Data Enrichment & Threat Intelligence"
- ],
- "tags": [],
- "useCases": [],
- "keywords": [
- "Autofocus",
- "Feed"
- ]
+ "name": "Autofocus Feed",
+ "description": "Indicators feed from Autofocus",
+ "support": "xsoar",
+ "currentVersion": "1.0.0",
+ "author": "Cortex XSOAR",
+ "url": "https://www.paloaltonetworks.com/cortex",
+ "email": "",
+ "created": "2020-03-09T16:04:45Z",
+ "categories": [
+ "Data Enrichment & Threat Intelligence"
+ ],
+ "tags": [],
+ "useCases": [],
+ "keywords": [
+ "Autofocus",
+ "Feed"
+ ],
+ "dependencies": {}
}
\ No newline at end of file
diff --git a/Packs/FeedAzure/Integrations/FeedAzure/FeedAzure.py b/Packs/FeedAzure/Integrations/FeedAzure/FeedAzure.py
index 41fd970ad1a..b3233cf3ca4 100644
--- a/Packs/FeedAzure/Integrations/FeedAzure/FeedAzure.py
+++ b/Packs/FeedAzure/Integrations/FeedAzure/FeedAzure.py
@@ -201,7 +201,7 @@ def build_iterator(self) -> List:
except RuntimeError as err:
demisto.debug(str(err))
- raise RuntimeError(F'Could not fetch download link from Azure')
+ raise RuntimeError('Could not fetch download link from Azure')
except ValueError as err:
demisto.debug(str(err))
diff --git a/Packs/FeedAzure/Integrations/FeedAzure/FeedAzure.yml b/Packs/FeedAzure/Integrations/FeedAzure/FeedAzure.yml
index 108ae35582c..0e560fd7d97 100644
--- a/Packs/FeedAzure/Integrations/FeedAzure/FeedAzure.yml
+++ b/Packs/FeedAzure/Integrations/FeedAzure/FeedAzure.yml
@@ -184,7 +184,7 @@ script:
description: Gets indicators from the feed.
execution: false
name: azure-get-indicators
- dockerimage: demisto/python3:3.8.2.6981
+ dockerimage: demisto/python3:3.8.3.8715
feed: true
isfetch: false
longRunning: false
diff --git a/Packs/FeedAzure/ReleaseNotes/1_0_1.md b/Packs/FeedAzure/ReleaseNotes/1_0_1.md
new file mode 100644
index 00000000000..a586a88319f
--- /dev/null
+++ b/Packs/FeedAzure/ReleaseNotes/1_0_1.md
@@ -0,0 +1,4 @@
+
+#### Integrations
+##### AzureFeed
+- Internal code improvements.
\ No newline at end of file
diff --git a/Packs/FeedAzure/pack_metadata.json b/Packs/FeedAzure/pack_metadata.json
index f73e298a0ca..b8e06afa6fe 100644
--- a/Packs/FeedAzure/pack_metadata.json
+++ b/Packs/FeedAzure/pack_metadata.json
@@ -1,19 +1,21 @@
{
- "name": "Azure Feed",
- "description": "Indicators feed from Azure",
- "support": "xsoar",
- "currentVersion": "1.0.0",
- "author": "Cortex XSOAR",
- "url": "https://www.paloaltonetworks.com/cortex",
- "email": "",
- "created": "2020-03-09T16:04:45Z",
- "categories": [
- "Data Enrichment & Threat Intelligence"
- ],
- "tags": [],
- "useCases": [],
- "keywords": [
- "Azure",
- "Feed"
- ]
+ "name": "Azure Feed",
+ "description": "Indicators feed from Azure",
+ "support": "xsoar",
+ "currentVersion": "1.0.1",
+ "author": "Cortex XSOAR",
+ "url": "https://www.paloaltonetworks.com/cortex",
+ "email": "",
+ "created": "2020-03-09T16:04:45Z",
+ "categories": [
+ "Data Enrichment & Threat Intelligence"
+ ],
+ "tags": [
+ "TIM"
+ ],
+ "useCases": [],
+ "keywords": [
+ "Azure",
+ "Feed"
+ ]
}
\ No newline at end of file
diff --git a/Packs/Exchange/.pack-ignore b/Packs/FeedAzureADConnectHealth/.pack-ignore
similarity index 100%
rename from Packs/Exchange/.pack-ignore
rename to Packs/FeedAzureADConnectHealth/.pack-ignore
diff --git a/Packs/FeedAzureADConnectHealth/.secrets-ignore b/Packs/FeedAzureADConnectHealth/.secrets-ignore
new file mode 100644
index 00000000000..bab06396502
--- /dev/null
+++ b/Packs/FeedAzureADConnectHealth/.secrets-ignore
@@ -0,0 +1,2 @@
+https://sts.contoso.com
+https://aka.ms
\ No newline at end of file
diff --git a/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/AzureADConnectHealthFeed.py b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/AzureADConnectHealthFeed.py
new file mode 100644
index 00000000000..65f3d56460e
--- /dev/null
+++ b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/AzureADConnectHealthFeed.py
@@ -0,0 +1,176 @@
+import demistomock as demisto
+from CommonServerPython import *
+
+from typing import Any, Callable, Dict, List, Tuple
+
+import urllib3
+from bs4 import BeautifulSoup
+import re
+
+# disable insecure warnings
+urllib3.disable_warnings()
+
+INTEGRATION_NAME = 'Microsoft Azure AD Connect Health Feed'
+
+
+class Client(BaseClient):
+ """
+ Client to use in the Microsoft Azure Feed integration. Overrides BaseClient.
+ """
+
+ def __init__(self, base_url: str, verify: bool = False, proxy: bool = False):
+ """
+ Implements class for Microsoft Azure feeds.
+ :param url: the Azure endpoint URL
+ :verify: boolean, if *false* feed HTTPS server certificate is verified. Default: *false*
+ :param proxy: boolean, if *false* feed HTTPS server certificate will not use proxies. Default: *false*
+ """
+ super().__init__(base_url, verify=verify, proxy=proxy)
+
+ def build_iterator(self) -> List:
+ """Retrieves all entries from the feed.
+ Returns:
+ A list of objects, containing the indicators.
+ """
+ result = []
+ r = self._http_request('GET', url_suffix='', full_url=self._base_url, resp_type='text')
+
+ soup = BeautifulSoup(r, 'html.parser')
+
+ pattern = re.compile("(https?:\/\/|\*\.)(\w+\.|\w+-\w+\.){1,3}\w{2,3}")
+ scraped_indicators = list(set([pattern.match(cell.text).group(0) for cell in soup.select( # type: ignore # noqa
+ "tbody tr td li") if pattern.match(cell.text)]))
+ for indicator in scraped_indicators:
+ result.append({
+ 'value': indicator,
+ 'type': FeedIndicatorType.DomainGlob if '*' in indicator else FeedIndicatorType.URL,
+ 'FeedURL': self._base_url
+ })
+
+ return result
+
+
+def test_module(client: Client, *_) -> Tuple[str, Dict[Any, Any], Dict[Any, Any]]:
+ """Builds the iterator to check that the feed is accessible.
+ Args:
+ client: Client object.
+ Returns:
+ Outputs.
+ """
+ client.build_iterator()
+ return 'ok', {}, {}
+
+
+def fetch_indicators(client: Client, feed_tags: List = [], limit: int = -1) -> List[Dict]:
+ """Retrieves indicators from the feed
+ Args:
+ client (Client): Client object with request
+ feed_tags (list): tags to assign fetched indicators
+ limit (int): limit the results
+ Returns:
+ Indicators.
+ """
+ iterator = client.build_iterator()
+ indicators = []
+ if limit > 0:
+ iterator = iterator[:limit]
+ for item in iterator:
+ value = item.get('value')
+ type_ = item.get('type', FeedIndicatorType.Domain)
+ raw_data = {
+ 'value': value,
+ 'type': type_,
+ }
+ for key, val in item.items():
+ raw_data.update({key: val})
+ indicator_obj = {
+ 'value': value,
+ 'type': type_,
+ 'service': 'Azure AD Connect Health Feed',
+ 'rawJSON': raw_data,
+ }
+ if feed_tags:
+ indicator_obj['fields'] = {
+ 'tags': feed_tags
+ }
+ indicators.append(indicator_obj)
+ return indicators
+
+
+def get_indicators_command(client: Client,
+ params: Dict[str, str],
+ args: Dict[str, str]
+ ) -> Tuple[str, Dict[Any, Any], Dict[Any, Any]]:
+ """Wrapper for retrieving indicators from the feed to the war-room.
+ Args:
+ client: Client object with request
+ params: demisto.params()
+ args: demisto.args()
+ Returns:
+ Outputs.
+ """
+ feed_tags = argToList(params.get('feedTags', ''))
+ limit = int(args.get('limit', '10'))
+ indicators = fetch_indicators(client, feed_tags, limit)
+ human_readable = tableToMarkdown('Indicators from Microsoft Azure Feed:', indicators,
+ headers=['value', 'type'], removeNull=True)
+
+ return human_readable, {}, {'raw_response': indicators}
+
+
+def fetch_indicators_command(client: Client, params: Dict[str, str]) -> List[Dict]:
+ """Wrapper for fetching indicators from the feed to the Indicators tab.
+ Args:
+ client: Client object with request
+ params: demisto.params()
+ Returns:
+ Indicators.
+ """
+ feed_tags = argToList(params.get('feedTags', ''))
+ indicators = fetch_indicators(client, feed_tags)
+ return indicators
+
+
+def main():
+ """
+ PARSE AND VALIDATE INTEGRATION PARAMS
+ """
+ params = demisto.params()
+ base_url = params.get('url')
+ insecure = not params.get('insecure', False)
+ proxy = params.get('proxy', False)
+
+ command = demisto.command()
+ demisto.info(f'Command being called in {INTEGRATION_NAME} is {command}')
+
+ try:
+ client = Client(
+ base_url=base_url,
+ verify=insecure,
+ proxy=proxy,
+ )
+
+ commands: Dict[
+ str, Callable[[Client, Dict[str, str], Dict[str, str]], Tuple[str, Dict[Any, Any], Dict[Any, Any]]]
+ ] = {
+ 'test-module': test_module,
+ 'azure-ad-health-get-indicators': get_indicators_command
+ }
+ if command in commands:
+ return_outputs(*commands[command](client, demisto.params(), demisto.args()))
+
+ elif command == 'fetch-indicators':
+ indicators = fetch_indicators_command(client, demisto.params())
+ for iter_ in batch(indicators, batch_size=2000):
+ demisto.createIndicators(iter_)
+
+ else:
+ raise NotImplementedError(f'Command {command} is not implemented.')
+
+ except Exception as err:
+ err_msg = f'Error in {INTEGRATION_NAME} Integration. [{err}]'
+ return_error(err_msg)
+
+
+if __name__ in ['__main__', 'builtin', 'builtins']:
+ main()
diff --git a/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/AzureADConnectHealthFeed.yml b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/AzureADConnectHealthFeed.yml
new file mode 100644
index 00000000000..8529683f0b6
--- /dev/null
+++ b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/AzureADConnectHealthFeed.yml
@@ -0,0 +1,103 @@
+commonfields:
+ id: Azure AD Connect Health Feed
+ version: -1
+fromversion: 5.5.0
+name: Azure AD Connect Health Feed
+display: Azure AD Connect Health Feed
+category: Data Enrichment & Threat Intelligence
+description: Use the Microsoft Azure AD Connect Health Feed integration to get indicators
+ from the feed.
+configuration:
+- display: Fetch indicators
+ name: feed
+ defaultvalue: "true"
+ type: 8
+ required: false
+- display: Indicator Reputation
+ name: feedReputation
+ defaultvalue: Good
+ type: 18
+ required: false
+ options:
+ - None
+ - Good
+ - Suspicious
+ - Bad
+ additionalinfo: Indicators from this integration instance will be marked with this
+ reputation
+- display: Source Reliability
+ name: feedReliability
+ defaultvalue: F - Reliability cannot be judged
+ type: 15
+ required: true
+ options:
+ - A - Completely reliable
+ - B - Usually reliable
+ - C - Fairly reliable
+ - D - Not usually reliable
+ - E - Unreliable
+ - F - Reliability cannot be judged
+ additionalinfo: Reliability of the source providing the intelligence data
+- display: ""
+ name: feedExpirationPolicy
+ defaultvalue: suddenDeath
+ type: 17
+ required: false
+ options:
+ - never
+ - interval
+ - indicatorType
+ - suddenDeath
+- display: ""
+ name: feedExpirationInterval
+ defaultvalue: "20160"
+ type: 1
+ required: false
+- display: Feed Fetch Interval
+ name: feedFetchInterval
+ defaultvalue: "30"
+ type: 19
+ required: false
+- display: The Microsoft Azure endpoint URL
+ name: url
+ defaultvalue: https://docs.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-health-agent-install#outbound-connectivity-to-the-azure-service-endpoints
+ type: 0
+ required: true
+- display: Tags
+ name: feedTags
+ defaultvalue: ""
+ type: 0
+ required: false
+ additionalinfo: Supports CSV values.
+- display: Bypass exclusion list
+ name: feedBypassExclusionList
+ defaultvalue: "true"
+ type: 8
+ required: false
+ additionalinfo: When selected, the exclusion list is ignored for indicators from
+ this feed. This means that if an indicator from this feed is on the exclusion
+ list, the indicator might still be added to the system.
+- display: Trust any certificate (not secure)
+ name: insecure
+ defaultvalue: ""
+ type: 8
+ required: false
+- display: Use system proxy settings
+ name: proxy
+ defaultvalue: ""
+ type: 8
+ required: false
+script:
+ script: ''
+ type: python
+ commands:
+ - name: azure-ad-health-get-indicators
+ arguments:
+ - name: limit
+ description: The maximum number of results to return. The default value is 10.
+ defaultValue: "0"
+ description: Gets indicators from the feed.
+ dockerimage: demisto/btfl-soup:1.0.1.9249
+ feed: true
+ runonce: false
+ subtype: python3
diff --git a/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/AzureADConnectHealthFeed_description.md b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/AzureADConnectHealthFeed_description.md
new file mode 100644
index 00000000000..26626ccb5c1
--- /dev/null
+++ b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/AzureADConnectHealthFeed_description.md
@@ -0,0 +1,12 @@
+## Microsoft Azure AD Connect Health web scraper:
+https://docs.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-health-agent-install#outbound-connectivity-to-the-azure-service-endpoints
+
+NOTE: As we scrape this page, we can not ensure the integrity of the fetched indicators upon a change in the source webpage.
+
+Most IT services are moving from on-premise solutions to cloud-based solutions. The public IP addresses, domains, and URLs that function as the endpoints for these solutions are very often not fixed, and the providers of the service publish their details on their websites in a less than ideal format (i.e.: HTML) rather than through a proper REST API (i.e.: JSON).
+
+This fact makes it very difficult for IT and Security teams to provide these services with an appropriate level of security and automation. Any changes in the HTML schema of the provider website, will break the automation and has the potential to cause serious disruption to the users and the business. The alternative is to compromise on the security posture of the organization.
+
+One example of these providers is Microsoft, and an example of their services is Microsoft Azure AD Connect Health.
+
+The goal of this pack is to address this issue by automating the collection of endpoint data in the form of an indicator feed. This will facilitate validation of the indicators before using them in enforcement points, for example firewalls, proxies, and more.
diff --git a/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/AzureADConnectHealthFeed_image.png b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/AzureADConnectHealthFeed_image.png
new file mode 100644
index 00000000000..9164d824b4b
Binary files /dev/null and b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/AzureADConnectHealthFeed_image.png differ
diff --git a/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/FeedAzureADConnectHealth_test.py b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/FeedAzureADConnectHealth_test.py
new file mode 100644
index 00000000000..a6c2db613be
--- /dev/null
+++ b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/FeedAzureADConnectHealth_test.py
@@ -0,0 +1,22 @@
+from AzureADConnectHealthFeed import Client
+
+
+URL = 'https://docs.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-health-agent-install#outbound-connectivity-to-the-azure-service-endpoints' # noqa
+
+
+def test_build_iterator(requests_mock):
+ with open('test_data/Microsoft_endpoint_mock.html', 'r') as file:
+ response = file.read()
+ requests_mock.get(URL, text=response)
+ expected_url = 'https://login.microsoftonline.com'
+ expected_domain_glob = '*.blob.core.windows.net'
+ client = Client(
+ base_url=URL,
+ verify=False,
+ proxy=False,
+ )
+ indicators = client.build_iterator()
+ url_indicators = {indicator['value'] for indicator in indicators if indicator['type'] == 'URL'}
+ domain_glob_indicators = {indicator['value'] for indicator in indicators if indicator['type'] == 'DomainGlob'}
+ assert expected_url in url_indicators
+ assert expected_domain_glob in domain_glob_indicators
diff --git a/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/README.md b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/README.md
new file mode 100644
index 00000000000..5190f7f2bf4
--- /dev/null
+++ b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/README.md
@@ -0,0 +1,61 @@
+Use the Microsoft Azure AD Connect Health Feed integration to get indicators from the feed.
+This integration was integrated and tested with version 1 of Azure AD Connect Health Feed
+## Configure Azure AD Connect Health Feed on Cortex XSOAR
+
+1. Navigate to **Settings** > **Integrations** > **Servers & Services**.
+2. Search for Azure AD Connect Health Feed.
+3. Click **Add instance** to create and configure a new integration instance.
+
+| **Parameter** | **Description** | **Required** |
+| --- | --- | --- |
+| feed | Fetch indicators | False |
+| feedReputation | Indicator Reputation | False |
+| feedReliability | Source Reliability | True |
+| feedExpirationPolicy | | False |
+| feedExpirationInterval | | False |
+| feedFetchInterval | Feed Fetch Interval | False |
+| url | The Microsoft Azure endpoint URL | True |
+| feedTags | Tags | False |
+| feedBypassExclusionList | Bypass exclusion list | False |
+| insecure | Trust any certificate \(not secure\) | False |
+| proxy | Use system proxy settings | False |
+
+4. Click **Test** to validate the URLs, token, and connection.
+## Commands
+You can execute these commands from the Demisto 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.
+### azure-ad-health-get-indicators
+***
+Gets indicators from the feed.
+
+
+#### Base Command
+
+`azure-ad-health-get-indicators`
+#### Input
+
+| **Argument Name** | **Description** | **Required** |
+| --- | --- | --- |
+| limit | The maximum number of results to return. The default value is 10. | Optional |
+
+
+#### Context Output
+
+There is no context output for this command.
+
+#### Command Example
+```!azure-ad-health-get-indicators```
+
+#### Context Example
+```
+{}
+```
+
+#### Human Readable Output
+
+>### Indicators from Microsoft Azure Feed:
+>|value|type|
+>|---|---|
+>| https://login.microsoftonline.com | URL |
+>| https://secure.aadcdn.microsoftonline-p.com | URL |
+>| https://login.windows.net | URL |
diff --git a/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/command_examples.txt b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/command_examples.txt
new file mode 100644
index 00000000000..6a77cb5c837
--- /dev/null
+++ b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/command_examples.txt
@@ -0,0 +1 @@
+!azure-ad-health-get-indicators
diff --git a/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/test_data/Microsoft_endpoint_mock.html b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/test_data/Microsoft_endpoint_mock.html
new file mode 100644
index 00000000000..715f11d7dc6
--- /dev/null
+++ b/Packs/FeedAzureADConnectHealth/Integrations/AzureADConnectHealthFeed/test_data/Microsoft_endpoint_mock.html
@@ -0,0 +1,1047 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Azure AD Connect Health Agent installation | Microsoft Docs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
You must be a global administrator of your Azure AD to get started with Azure AD Connect Health
+
By default, only the global administrators can install and configure the health agents to get started, access the portal, and perform any operations within Azure AD Connect Health. For more information, see Administering your Azure AD directory.
Important: The account used when installing the agents must be a work or school account. It cannot be a Microsoft account. For more information, see Sign up for Azure as an organization
+
+
+
Azure AD Connect Health Agent is installed on each targeted server
+
Azure AD Connect Health requires the Health Agents to be installed and configured on targeted servers to receive the data and provide the Monitoring and Analytics capabilities.
For example, to get data from your AD FS infrastructure, the agent must be installed on the AD FS and Web Application Proxy servers. Similarly, to get data on your on-premises AD DS infrastructure, the agent must be installed on the domain controllers.
+
+
+
Outbound connectivity to the Azure service endpoints
+
During installation and runtime, the agent requires connectivity to Azure AD Connect Health service endpoints. If outbound connectivity is blocked using Firewalls, ensure that the following endpoints are added to the allowed list. See outbound connectivity endpoints
+
+
+
Outbound connectivity based on IP Addresses
+
For IP address based filtering on firewalls, refer to the Azure IP Ranges.
+
+
+
TLS Inspection for outbound traffic is filtered or disabled
+
The agent registration step or data upload operations may fail if there is TLS inspection or termination for outbound traffic at the network layer. Read more about how to setup TLS inspection
+
+
+
Firewall ports on the server running the agent
+
The agent requires the following firewall ports to be open in order for the agent to communicate with the Azure AD Health service endpoints.
TCP port 443
TCP port 5671
Note that port 5671 is no longer required for the latest version of agent. Upgrade to the latest version so only port 443 is required. Read more about enable firewall ports
+
+
+
Allow the following websites if IE Enhanced Security is enabled
+
If IE Enhanced Security is enabled, then the following websites must be allowed on the server that is going to have the agent installed.
https://login.microsoftonline.com
https://secure.aadcdn.microsoftonline-p.com
https://login.windows.net
https://aadcdn.msftauth.net
The federation server for your organization trusted by Azure Active Directory. For example: https://sts.contoso.com
Read more about how to configure IE. In case you have a proxy within your network , please see note below.
Windows Server 2012 ships with PowerShell v3.0, which is insufficient for the agent. Update the Windows Management Framework.
Windows Server 2012 R2 and later ship with a sufficiently recent version of PowerShell.
+
+
+
Disable FIPS
+
FIPS is not supported by Azure AD Connect Health agents.
+
+
+
+
+
Note
+
If you have a highly locked-down and extremely restricted environment, you would require to whitelist the URLs mentioned in the Service endpoint lists below in addition to the ones listed in the Allowed IE enhanced Security configuration above.
+
+
Outbound connectivity to the Azure service endpoints
+
During installation and runtime, the agent requires connectivity to Azure AD Connect Health service endpoints. If outbound connectivity is blocked using Firewalls, make sure that the following URLs are not blocked by default. Do not disable security monitoring or inspection of these URLs, but allow them as you would other internet traffic. They permit communication with Azure AD Connect Health service endpoints. Learn how to check outbound connectivity with Test-AzureADConnectHealthConnectivity.
+
+
+
+
Domain Environment
+
Required Azure service endpoints
+
+
+
+
+
General Public
+
*.blob.core.windows.net
*.aadconnecthealth.azure.com
*.servicebus.windows.net - Port: 5671
*.adhybridhealth.azure.com/
https://management.azure.com
https://policykeyservice.dc.ad.msft.net/
https://login.windows.net
https://login.microsoftonline.com
https://secure.aadcdn.microsoftonline-p.com
https://www.office.com *this endpoint is only used for discovery purposes during registration.
+
+
+
Azure Germany
+
*.blob.core.cloudapi.de
*.servicebus.cloudapi.de
*.aadconnecthealth.microsoftazure.de
https://management.microsoftazure.de
https://policykeyservice.aadcdi.microsoftazure.de
https://login.microsoftonline.de
https://secure.aadcdn.microsoftonline-p.de
https://www.office.de *this endpoint is only used for discovery purposes during registration.
+
+
+
Azure Government
+
*.blob.core.usgovcloudapi.net
*.servicebus.usgovcloudapi.net
*.aadconnecthealth.microsoftazure.us
https://management.usgovcloudapi.net
https://policykeyservice.aadcdi.azure.us
https://login.microsoftonline.us
https://secure.aadcdn.microsoftonline-p.com
https://www.office.com *this endpoint is only used for discovery purposes during registration.
+
+
+
+
Download and install the Azure AD Connect Health Agent
Installing the Azure AD Connect Health Agent for AD FS
+
+
Note
+
AD FS server should be different from your Sync server. Do not install AD FS agent to your Sync server.
+
+
Before installation, make sure your AD FS server host name is unique and not present in the AD FS service.
+To start the agent installation, double-click the .exe file that you downloaded. On the first screen, click Install.
+
+
Once the installation is finished, click Configure Now.
+
+
This launches a PowerShell window to initiate the agent registration process. When prompted, sign in with an Azure AD account that has access to perform agent registration. By default the Global Admin account has access.
+
+
After signing in, PowerShell will continue. Once it completes, you can close PowerShell and the configuration is complete.
+
At this point, the agent services should be started automatically allowing the agent upload the required data to the cloud service in a secure manner.
+
If you have not met all the pre-requisites outlined in the previous sections, warnings appear in the PowerShell window. Be sure to complete the requirements before installing the agent. The following screenshot is an example of these errors.
+
+
To verify the agent has been installed, look for the following services on the server. If you completed the configuration, they should already be running. Otherwise, they are stopped until the configuration is complete.
+
+
Azure AD Connect Health AD FS Diagnostics Service
+
Azure AD Connect Health AD FS Insights Service
+
Azure AD Connect Health AD FS Monitoring Service
+
+
+
Agent installation on Windows Server 2008 R2 Servers
+
Steps for Windows Server 2008 R2 servers:
+
+
Ensure that the server is running at Service Pack 1 or higher.
+
Turn off IE ESC for agent installation:
+
Install Windows PowerShell 4.0 on each of the servers ahead of installing the AD Health agent. To install Windows PowerShell 4.0:
+
Install Internet Explorer version 10 or above on the server. (Required by the Health Service to authenticate, using your Azure Admin credentials.)
+
+
+
For more information on installing Windows PowerShell 4.0 on Windows Server 2008 R2, see the wiki article here.
+
+
Enable Auditing for AD FS
+
+
Note
+
This section only applies to AD FS servers. You do not have to follow these steps on the Web Application Proxy Servers.
+
+
In order for the Usage Analytics feature to gather and analyze data, the Azure AD Connect Health agent needs the information in the AD FS Audit Logs. These logs are not enabled by default. Use the following procedures to enable AD FS auditing and to locate the AD FS audit logs, on your AD FS servers.
+
To enable auditing for AD FS on Windows Server 2008 R2
+
+
Click Start, point to Programs, point to Administrative Tools, and then click Local Security Policy.
+
Navigate to the Security Settings\Local Policies\User Rights Assignment folder, and then double-click Generate security audits.
+
On the Local Security Setting tab, verify that the AD FS 2.0 service account is listed. If it is not present, click Add User or Group and add it to the list, and then click OK.
+
To enable auditing, open a Command Prompt with elevated privileges and run the following command: auditpol.exe /set /subcategory:{0CCE9222-69AE-11D9-BED3-505054503030} /failure:enable /success:enable
+
Close Local Security Policy.
+ -- The following steps are only required for primary AD FS servers. --
+
Open the AD FS Management snap-in. To open the AD FS Management snap-in, click Start, point to Programs, point to Administrative Tools, and then click AD FS 2.0 Management.
+
In the Actions pane, click Edit Federation Service Properties.
+
In the Federation Service Properties dialog box, click the Events tab.
+
Select the Success audits and Failure audits check boxes.
+
Click OK.
+
+
To enable auditing for AD FS on Windows Server 2012 R2
+
+
Open Local Security Policy by opening Server Manager on the Start screen, or Server Manager in the taskbar on the desktop, then click Tools/Local Security Policy.
+
Navigate to the Security Settings\Local Policies\User Rights Assignment folder, and then double-click Generate security audits.
+
On the Local Security Setting tab, verify that the AD FS service account is listed. If it is not present, click Add User or Group and add it to the list, and then click OK.
+
To enable auditing, open a command prompt with elevated privileges and run the following command: auditpol.exe /set /subcategory:{0CCE9222-69AE-11D9-BED3-505054503030} /failure:enable /success:enable
+
Close Local Security Policy.
+ -- The following steps are only required for primary AD FS servers. --
+
Open the AD FS Management snap-in (in Server Manager, click Tools, and then select AD FS Management).
+
In the Actions pane, click Edit Federation Service Properties.
+
In the Federation Service Properties dialog box, click the Events tab.
+
Select the Success audits and Failure audits check boxes and then click OK.
+
+
To enable auditing for AD FS on Windows Server 2016
+
+
Open Local Security Policy by opening Server Manager on the Start screen, or Server Manager in the taskbar on the desktop, then click Tools/Local Security Policy.
+
Navigate to the Security Settings\Local Policies\User Rights Assignment folder, and then double-click Generate security audits.
+
On the Local Security Setting tab, verify that the AD FS service account is listed. If it is not present, click Add User or Group and add the AD FS service account to the list, and then click OK.
+
To enable auditing, open a command prompt with elevated privileges and run the following command: auditpol.exe /set /subcategory:{0CCE9222-69AE-11D9-BED3-505054503030} /failure:enable /success:enable
+
Close Local Security Policy.
+ -- The following steps are only required for primary AD FS servers. --
+
Open the AD FS Management snap-in (in Server Manager, click Tools, and then select AD FS Management).
+
In the Actions pane, click Edit Federation Service Properties.
+
In the Federation Service Properties dialog box, click the Events tab.
+
Select the Success audits and Failure audits check boxes and then click OK. This should be enabled by default.
+
Open a PowerShell window and run the following command: Set-AdfsProperties -AuditLevel Verbose.
A group policy can disable AD FS auditing. If AD FS auditing is disabled, usage analytics about login activities are not available. Ensure that you don’t have a group policy that disables AD FS auditing.>
+
+
Installing the Azure AD Connect Health agent for sync
+
The Azure AD Connect Health agent for sync is installed automatically in the latest build of Azure AD Connect. To use Azure AD Connect for sync, you need to download the latest version of Azure AD Connect and install it. You can download the latest version here.
+
To verify the agent has been installed, look for the following services on the server. If you completed the configuration, they should already be running. Otherwise, they are stopped until the configuration is complete.
+
+
Azure AD Connect Health Sync Insights Service
+
Azure AD Connect Health Sync Monitoring Service
+
+
+
+
Note
+
Remember that using Azure AD Connect Health requires Azure AD Premium. If you do not have Azure AD Premium, you are unable to complete the configuration in the Azure portal. For more information, see the requirements page.
+
+
Manual Azure AD Connect Health for Sync registration
+
If the Azure AD Connect Health for Sync agent registration fails after successfully installing Azure AD Connect, you can use the following PowerShell command to manually register the agent.
+
+
Important
+
Using this PowerShell command is only required if the agent registration fails after installing Azure AD Connect.
+
+
The following PowerShell command is required ONLY when the health agent registration fails even after a successful installation and configuration of Azure AD Connect. The Azure AD Connect Health services will start after the agent has been successfully registered.
+
You can manually register the Azure AD Connect Health agent for sync using the following PowerShell command:
AttributeFiltering: $true (default) - if Azure AD Connect is not syncing the default attribute set and has been customized to use a filtered attribute set. $false otherwise.
+
StagingMode: $false (default) - if the Azure AD Connect server is NOT in staging mode, $true if the server is configured to be in staging mode.
+
+
When prompted for authentication you should use the same global admin account (such as admin@domain.onmicrosoft.com) that was used for configuring Azure AD Connect.
+
Installing the Azure AD Connect Health Agent for AD DS
+
To start the agent installation, double-click the .exe file that you downloaded. On the first screen, click Install.
+
+
Once the installation is finished, click Configure Now.
+
+
A command prompt is launched, followed by some PowerShell that executes Register-AzureADConnectHealthADDSAgent. When prompted to sign in to Azure, go ahead and sign in.
+
+
After signing in, PowerShell will continue. Once it completes, you can close PowerShell and the configuration is complete.
+
At this point, the services should be started automatically allowing the agent to monitor and gather data. If you have not met all the pre-requisites outlined in the previous sections, warnings appear in the PowerShell window. Be sure to complete the requirements before installing the agent. The following screenshot is an example of these errors.
+
+
To verify the agent has been installed, look for the following services on the domain controller.
+
+
Azure AD Connect Health AD DS Insights Service
+
Azure AD Connect Health AD DS Monitoring Service
+
+
If you completed the configuration, these services should already be running. Otherwise, they are stopped until the configuration is complete.
+
+
Quick agent installation in multiple servers
+
+
Create a user account in Azure AD with a password.
+
Assign the Owner role for this local AAD account in Azure AD Connect Health via the portal. Follow the steps here. Assign the role to all service instances.
+
Download the .exe MSI file in local domain controller for installation.
+
Run the following script to registration. Replace the parameters with the new user account created and its password.
Once you are done, you can remove access for the local account by doing one or more of the following:
+
+
Remove the role assignment for the local account for AAD Connect Health
+
Rotate the password for the local account.
+
Disable the AAD local account
+
Delete the AAD local account
+
+
+
+
Agent Registration using PowerShell
+
After installing the appropriate agent setup.exe, you can perform the agent registration step using the following PowerShell commands depending on the role. Open a PowerShell Window and execute the appropriate command:
These commands accept "Credential" as a parameter to complete the registration in a non-interactive manner or on a Server-Core machine.
+
+
The Credential can be captured in a PowerShell variable that is passed as a parameter.
+
You can provide any Azure AD Identity that has access to register the agents and does NOT have MFA enabled.
+
By default Global Admins have access to perform agent registration. You can also allow other less privileged identities to perform this step. Read more about Role Based Access Control.
Configure Azure AD Connect Health Agents to use HTTP Proxy
+
You can configure Azure AD Connect Health Agents to work with an HTTP Proxy.
+
+
Note
+
+
Using “Netsh WinHttp set ProxyServerAddress” is not supported as the agent uses System.Net to make web requests instead of Microsoft Windows HTTP Services.
+
The configured Http Proxy address is used to pass-through encrypted Https messages.
+
Authenticated proxies (using HTTPBasic) are not supported.
+
+
+
Change Health Agent Proxy Configuration
+
You have the following options to configure Azure AD Connect Health Agent to use an HTTP Proxy.
+
+
Note
+
All Azure AD Connect Health Agent services must be restarted, in order for the proxy settings to be updated. Run the following command:
+Restart-Service AzureADConnectHealth*
+
+
Import existing proxy Settings
+
Import from Internet Explorer
+
Internet Explorer HTTP proxy settings can be imported, to be used by the Azure AD Connect Health Agents. On each of the servers running the Health agent, execute the following PowerShell command:
WinHTTP proxy settings can be imported, to be used by the Azure AD Connect Health Agents. On each of the servers running the Health agent, execute the following PowerShell command:
"address" can be a DNS resolvable server name or an IPv4 address
+
"port" can be omitted. If omitted then 443 is chosen as default port.
+
+
Clear existing proxy configuration
+
You can clear the existing proxy configuration by running the following command:
+
Set-AzureAdConnectHealthProxySettings -NoProxy
+
+
Read current proxy settings
+
You can read the currently configured proxy settings by running the following command:
+
Get-AzureAdConnectHealthProxySettings
+
+
Test Connectivity to Azure AD Connect Health Service
+
It is possible that issues may arise that cause the Azure AD Connect Health agent to lose connectivity with the Azure AD Connect Health service. These include network issues, permission issues, or various other reasons.
+
If the agent is unable to send data to the Azure AD Connect Health service for longer than two hours, it is indicated with the following alert in the portal: "Health Service data is not up to date." You can confirm if the affected Azure AD Connect Health agent is able to upload data to the Azure AD Connect Health service by running the following PowerShell command:
The role parameter currently takes the following values:
+
+
ADFS
+
Sync
+
ADDS
+
+
+
Note
+
To use the connectivity tool, you must first complete the agent registration. If you are not able to complete the agent registration, make sure that you have met all the requirements for Azure AD Connect Health. This connectivity test is performed by default during agent registration.
+Important Notice: Please update all of your Zoom applications to version 5.0 or higher. After May 30, 2020, all Zoom applications on older versions will receive a forced upgrade when trying to join meetings as GCM Encryption will be fully enabled across the Zoom platform. Learn more on how to update your Zoom application or update now.
+Note: As our world comes together to slow the spread of COVID-19 pandemic, the Zoom Support Center has continued to operate 24x7 globally to support you. Please see the updated Support Guidelines during these unprecedented times.
+
Network Firewall or Proxy Server Settings for Zoom Follow
+
+
+
+
+
+
Network Firewall or Web Security Gateway
+
If your app stays in a "connecting" mode or timed out due to "Network error, please try again" or "Can't connect to our service, please check your network connection and try again" - it could be related to your network connection, network firewall settings or web security gateway settings.
+
Note: Check your network connection by opening a browser and ensure that you can access https://www.demisto.com
+
To configure your network firewall, please see the following table: