Skip to content

Commit

Permalink
Splunk mirroring comments (demisto#28198)
Browse files Browse the repository at this point in the history
* mirror in and out

* layout

* comments script

* tests

* release notes

* rn

* readme

* layout

* release notes

* rn incident fields

* new unit test and remove debug logs

* comment to table test

* convert comment to table test

* rn

* rename test

* fix unit test

* rn

* fix

* new rn

* fix test

* docker update

* comments

* Bump pack from version SplunkPy to 3.1.3.

* change the mirror in query

* tests

* Apply suggestions from doc review

Co-authored-by: ShirleyDenkberg <62508050+ShirleyDenkberg@users.noreply.github.com>

* change docker image and releasenotes

---------

Co-authored-by: Content Bot <bot@demisto.com>
Co-authored-by: ShirleyDenkberg <62508050+ShirleyDenkberg@users.noreply.github.com>
  • Loading branch information
3 people authored Aug 20, 2023
1 parent 8c2289b commit 4117cff
Show file tree
Hide file tree
Showing 16 changed files with 523 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,15 @@
"dbotMirrorInstance": {
"simple": "mirror_instance"
},
"dbotMirrorTags": {
"simple": "mirror_tags"
},
"details": {
"simple": "rule_description"
},
"Splunk Comments": {
"simple": "SplunkComments"
},
"name": {
"complex": {
"filters": [],
Expand Down Expand Up @@ -164,6 +170,9 @@
},
"dbotMirrorInstance": {
"simple": "mirror_instance"
},
"dbotMirrorTags": {
"simple": "mirror_tags"
}
}
}
Expand Down
33 changes: 33 additions & 0 deletions Packs/SplunkPy/IncidentFields/incidentfiels-Notable_Comments.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"associatedToAll": false,
"associatedTypes": [
"Splunk Notable Generic"
],
"caseInsensitive": true,
"cliName": "splunkcomments",
"closeForm": false,
"content": true,
"description": "the notable comments",
"editForm": true,
"group": 0,
"hidden": false,
"id": "incident_splunkComments",
"isReadOnly": false,
"locked": false,
"name": "Splunk Comments",
"neverSetAsRequired": false,
"ownerOnly": false,
"propagationLabels": [
"all"
],
"required": false,
"sla": 0,
"system": false,
"threshold": 72,
"type": "multiSelect",
"unmapped": false,
"unsearchable": true,
"useAsKpi": false,
"version": -1,
"fromVersion": "6.0.0"
}
169 changes: 133 additions & 36 deletions Packs/SplunkPy/Integrations/SplunkPy/SplunkPy.py

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions Packs/SplunkPy/Integrations/SplunkPy/SplunkPy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,18 @@ configuration:
section: Collect
advanced: true
required: false
- display: Comment tag from Splunk
name: comment_tag_from_splunk
defaultvalue: FROM SPLUNK
type: 0
required: false
additionalinfo: Add this tag to an entry to mirror it as a comment from Splunk.
- display: Comment tag to Splunk
name: comment_tag_to_splunk
defaultvalue: FROM XSOAR
type: 0
required: false
additionalinfo: Add this tag to an entry to mirror it as a comment to Splunk.
description: Runs queries on Splunk servers.
display: SplunkPy
name: SplunkPy
Expand Down
79 changes: 68 additions & 11 deletions Packs/SplunkPy/Integrations/SplunkPy/SplunkPy_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ def test_fetch_incidents(mocker):
service = mocker.patch('splunklib.client.connect', return_value=None)
mocker.patch('splunklib.results.JSONResultsReader', return_value=SAMPLE_RESPONSE)
mapper = UserMappingObject(service, False)
splunk.fetch_incidents(service, mapper)
splunk.fetch_incidents(service, mapper, 'from_xsoar', 'from_splunk')
incidents = demisto.incidents.call_args[0][0]
assert demisto.incidents.call_count == 1
assert len(incidents) == 1
Expand Down Expand Up @@ -648,11 +648,12 @@ def test_fetch_notables(mocker):
service = Service('DONE')
mocker.patch('splunklib.results.JSONResultsReader', return_value=SAMPLE_RESPONSE)
mapper = splunk.UserMappingObject(service, False)
splunk.fetch_incidents(service, mapper=mapper)
splunk.fetch_incidents(service, mapper=mapper, comment_tag_to_splunk='comment_tag_to_splunk',
comment_tag_from_splunk='comment_tag_from_splunk')
cache_object = splunk.Cache.load_from_integration_context(get_integration_context())
assert cache_object.submitted_notables
notable = cache_object.submitted_notables[0]
incident_from_cache = notable.to_incident(mapper)
incident_from_cache = notable.to_incident(mapper, 'comment_tag_to_splunk', 'comment_tag_from_splunk')
incidents = demisto.incidents.call_args[0][0]
assert demisto.incidents.call_count == 1
assert len(incidents) == 0
Expand All @@ -661,7 +662,8 @@ def test_fetch_notables(mocker):
assert not incident_from_cache.get('owner')

# now call second time to make sure that the incident fetched
splunk.fetch_incidents(service, mapper=mapper)
splunk.fetch_incidents(service, mapper=mapper, comment_tag_to_splunk='comment_tag_to_splunk',
comment_tag_from_splunk='comment_tag_from_splunk')
incidents = demisto.incidents.call_args[0][0]
assert len(incidents) == 1
assert incidents[0]["name"] == "Endpoint - Recurring Malware Infection - Rule : Endpoint - " \
Expand Down Expand Up @@ -1072,7 +1074,8 @@ def __init__(self):
mocker.patch('SplunkPy.results.JSONResultsReader', return_value=notable_data)
mocker.patch.object(demisto, 'results')
service = Service()
splunk.get_remote_data_command(service, args, mapper=splunk.UserMappingObject(service, False), **func_call_kwargs)
splunk.get_remote_data_command(service, args, mapper=splunk.UserMappingObject(service, False),
comment_tag_from_splunk='comment_tag_from_splunk', **func_call_kwargs)
results = demisto.results.call_args[0][0]

expected_results = [notable_data[1]]
Expand Down Expand Up @@ -1120,11 +1123,63 @@ def __init__(self):
)
mocker.patch("SplunkPy.isinstance", return_value=True)

splunk.get_remote_data_command(Service(), **func_call_kwargs)
splunk.get_remote_data_command(Service(), comment_tag_from_splunk='from_splunk', **func_call_kwargs)
(info_message,) = info_mock.call_args_list[0][0]
assert info_message == "Splunk-SDK message: test message"


@pytest.mark.parametrize("notable_data, func_call_kwargs, expected_closure_data",
[({'status_label': 'New', 'event_id': 'id', 'status_end': 'false',
'comment': 'new comment from splunk', 'reviewer': 'admin',
'review_time': '1612881691.589575'},
{'close_incident': True, 'close_end_statuses': False, 'close_extra_labels': []},
None,
)])
def test_get_remote_data_command_add_comment(mocker, notable_data: dict,
func_call_kwargs: dict, expected_closure_data: dict):
"""
Test case for get_remote_data_command with comment addition.
Given:
- notable data with new comment
When:
new comment added in splunk
Then:
- ensure the comment added as a new note
- ensure the event was updated
"""
class Jobs:
def oneshot(self, _, output_mode: str):
assert output_mode == splunk.OUTPUT_MODE_JSON
return notable_data

class Service:
def __init__(self):
self.jobs = Jobs()

args = {'lastUpdate': '2021-02-09T16:41:30.589575+02:00', 'id': 'id'}
mocker.patch.object(demisto, 'params', return_value={'timezone': '0'})
mocker.patch.object(demisto, 'debug')
mocker.patch.object(demisto, 'info')
mocker.patch('SplunkPy.results.JSONResultsReader', return_value=[notable_data])
mocker.patch.object(demisto, 'results')
service = Service()

expected_comment_note = {'Type': 1, 'Contents': 'new comment from splunk',
'ContentsFormat': 'text', 'Tags': ['from_splunk'], 'Note': True}
splunk.get_remote_data_command(service, args, mapper=splunk.UserMappingObject(service, False),
comment_tag_from_splunk='from_splunk', **func_call_kwargs)
results = demisto.results.call_args[0][0][0]
notable_data.update({'SplunkComments': [{'Comment': 'new comment from splunk'}]})
note_results = demisto.results.call_args[0][0][1]

expected_results = [notable_data][0]

assert demisto.results.call_count == 1
assert results == expected_results
assert note_results == expected_comment_note


def test_get_modified_remote_data_command(mocker):
updated_incidet_review = {'rule_id': 'id'}

Expand Down Expand Up @@ -1183,12 +1238,12 @@ def test_edit_notable_event__failed_to_update(mocker, requests_mock):

@pytest.mark.parametrize('args, params, call_count, success', [
({'delta': {'status': '2'}, 'remoteId': '12345', 'status': 2, 'incidentChanged': True},
{'host': 'ec.com', 'port': '8089', 'authentication': {'identifier': 'i', 'password': 'p'}}, 3, True),
{'host': 'ec.com', 'port': '8089', 'authentication': {'identifier': 'i', 'password': 'p'}}, 4, True),
({'delta': {'status': '2'}, 'remoteId': '12345', 'status': 2, 'incidentChanged': True},
{'host': 'ec.com', 'port': '8089', 'authentication': {'identifier': 'i', 'password': 'p'}}, 2, False),
{'host': 'ec.com', 'port': '8089', 'authentication': {'identifier': 'i', 'password': 'p'}}, 3, False),
({'delta': {'status': '2'}, 'remoteId': '12345', 'status': 2, 'incidentChanged': True},
{'host': 'ec.com', 'port': '8089', 'authentication': {'identifier': 'i', 'password': 'p'}, 'close_notable': True},
4, True)
5, True)
])
def test_update_remote_system(args, params, call_count, success, mocker, requests_mock):

Expand All @@ -1212,7 +1267,8 @@ def __init__(self):
mocker.patch.object(demisto, 'error')
service = Service()
mapper = splunk.UserMappingObject(service, False)
assert splunk.update_remote_system_command(args, params, service, None, mapper=mapper) == args['remoteId']
assert splunk.update_remote_system_command(args, params, service, None, mapper=mapper,
comment_tag_to_splunk='comment_tag_to_splunk') == args['remoteId']
assert demisto.debug.call_count == call_count
if not success:
assert demisto.error.call_count == 1
Expand Down Expand Up @@ -1648,7 +1704,8 @@ def test_labels_with_non_str_values(mocker):
# run
service = mocker.patch('splunklib.client.connect', return_value=None)
mapper = UserMappingObject(service, False)
splunk.fetch_incidents(service, mapper)
splunk.fetch_incidents(service, mapper, comment_tag_to_splunk='comment_tag_to_splunk',
comment_tag_from_splunk='comment_tag_from_splunk')
incidents = demisto.incidents.call_args[0][0]

# validate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,60 @@
"w": 1,
"x": 1,
"y": 2
},
{
"displayType": "ROW",
"h": 2,
"hideName": false,
"i": "splunk-comments-field",
"items": [],
"maxH": null,
"maxW": 3,
"minH": 1,
"moved": false,
"name": "Splunk Comments",
"query": "SplunkConvertCommentsToTable",
"queryType": "script",
"static": false,
"type": "dynamic",
"w": 2,
"x": 0,
"y": 8
},
{
"displayType": "ROW",
"h": 1,
"hideName": false,
"i": "splunk-add-comment-botton",
"items": [
{
"args": {
"tags": {
"simple": "",
"userMarkedRequired": true
}
},
"buttonClass": "warning",
"endCol": 2,
"fieldId": "",
"height": 22,
"id": "button",
"index": 0,
"name": "Press to add comment to Splunk",
"scriptId": "SplunkAddComment",
"sectionItemType": "button",
"startCol": 0
}
],
"maxH": null,
"maxW": 3,
"minH": 1,
"moved": false,
"name": "Add Comment",
"static": false,
"w": 1,
"x": 2,
"y": 8
}
],
"type": "custom"
Expand Down
34 changes: 34 additions & 0 deletions Packs/SplunkPy/ReleaseNotes/3_1_4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

#### Incident Fields

- New: **Splunk Comments**

#### Integrations

##### SplunkPy

- Added a *comments* to the mirror in/out functionality.

#### Layouts

##### Splunk Notable Generic

- Added a table to show the comments of the Splunk notable.
- Added a button that enables the user to create a note and tag it, without the need of going to the War Room to tag it.

#### Mappers

##### Splunk - Notable Generic Incoming Mapper

- Added the following new fields to the integration incoming mapper:
- **dbotMirrorTags**
- **Splunk Comments**

#### Scripts

##### New: SplunkAddComment

- New: Use this script to add a comment with a tag (the "Comment tag to Splunk" defined in the instance configuration) as an entry in Cortex XSOAR, which will then be mirrored as a comment to a Splunk issue. This script should be run within an incident. (Available from Cortex XSOAR 6.0.0).
##### New: SplunkConvertCommentsToTable

- New: This script is used to convert Splunk comments to a table. (Available from Cortex XSOAR 6.0.0).
24 changes: 24 additions & 0 deletions Packs/SplunkPy/Scripts/SplunkAddComment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Use this script to add a comment with a tag (the "Comment tag to Splunk" defined in the instance configuration) as an entry in Cortex XSOAR, which will then be mirrored as a comment to a Splunk issue. This script should be run within an incident.

## Script Data

---

| **Name** | **Description** |
| --- | --- |
| Script Type | python3 |
| Cortex XSOAR Version | 6.0.0 |

## Inputs

---

| **Argument Name** | **Description** |
| --- | --- |
| comment | Comment to be added to the Splunk issue. |
| tag | The comment tag. Use the comment entry tag \(defined in your instance configuration\) to mirror the comment to splunk. |

## Outputs

---
There are no outputs for this script.
26 changes: 26 additions & 0 deletions Packs/SplunkPy/Scripts/SplunkAddComment/SplunkAddComment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import demistomock as demisto # noqa: F401
from CommonServerPython import * # noqa: F401


def add_comment(args: Dict[str, Any]) -> CommandResults:
demisto.debug("adding comment")
tags = argToList(args.get('tags', 'FROM XSOAR'))
comment_body = args.get('comment', '')

return CommandResults(
readable_output=comment_body, mark_as_note=True, tags=tags
)


def main(): # pragma: no cover
try:
demisto.debug('SplunkAddComment is being called')
res = add_comment(demisto.args())
return_results(res)

except Exception as ex:
return_error(f'Failed to execute SplunkAddComment. Error: {str(ex)}')


if __name__ in ["__builtin__", "builtins", '__main__']:
main()
Loading

0 comments on commit 4117cff

Please sign in to comment.