Dynamic interactive GUI elements in incident layouts?

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Announcements
Please sign in to see details of an important advisory in our Customer Advisories area.

Dynamic interactive GUI elements in incident layouts?

L2 Linker

I couldn't find anything in the documentation about this.  However, I'm brand new to XSOAR development, so maybe I'm missing it.  So, before I go digging more, can anyone let me know if this capability exists or not?

 

I want dynamic interactive elements on an incident layout.  The user should be able to pick one of multiple items that wouldn't be known until runtime, and the result of the user selection should be an argument to an automation.

 

In particular, I would like to to run a search for some incidents designated as "parent" incidents.  The result of the search should go into the layout with some way to select one.  This could be a button for each element, or a dropdown single-select item, or whatever.  The incident ID for the incident selected would be fed into an automation as an argument.

 

I'm not afraid of a little HTML or Javascript or other scripting, if that's what it takes.

 

So, is there a way to do this?

1 accepted solution

Accepted Solutions

If I understand correctly, the following may help:

 

1. Create a new field that will be used as the selection drop down for the user and give it  some default values:

ABurt_0-1614416019988.png

 

2. Create a new automation script (yes automations = scripts and scripts = automations) and ensure it has the "field-display" tag:

ABurt_2-1614416457373.png

In the above script, the returned list options assumes that the field named "monitored_field" is available in the incident and that it is a list. It will also only display options for the field if it is part of an existing incident and not in the "create / new" incident form.

 

3. Assign the field-display script to the field:

ABurt_3-1614416530414.png

 

4. Create a new "field-change-triggered" script and make sure to assign the "field-change-triggered" tag:

ABurt_4-1614416722532.png

In the above script, if the selection has changed then the script runs the playbook again by simply setting the playbook. When setting with no parameters, it re-runs the existing playbook on the existing incident.

 

5.  Edit the field again and this time assign the field change parameters:

ABurt_5-1614416854330.png

 

 

This means that each incident will have a field (that you can place in the incident layout) that will contain dynamic content based on the incident fields, lists or context and when changed will execute a command (in this case the playbook re-run).

 

A small demo:

 

 

In your use case, the field-display script would pull a list of parent incidents. The field-change script would then re-assign the current incident to that parent incident.

 

I hope this helps.

 

Regards

 

Adam

View solution in original post

14 REPLIES 14

L2 Linker

To be clear, I'm not picky about how this gets done.  It doesn't have to be part of a layout.  If it happens in some sort of sub-playbook, that's fine.  The possible options could (and probably should) be loaded from a pre-calculated list, instead of a real-time search.

 

All that's important is that the user is given the opportunity to select values that need to be calculated and that the selection can be used as an argument to an automation.

L2 Linker

Hi!  So if I understand the ask correctly, you'd like to be able to interact with / ask your users or analysts questions based off other information during the investigation.  Let's say this is part of your Playbook workflow.

 

The first thing you need is the question part.  Take a look at the Data Collection Task.  This will let you create a questionnaire as a task in your playbook.  If you are interested in having the user select a single item from a list use a Single Select question.  I believe in this case you are looking for "Parent Ticket" from a list of similar incidents, correct?  (You can do this with multi-select too if you want to create a list of items selected from a larger list of items).  It should look something like this:

DougCouch_0-1614302553356.png

Here's How the question will look:

DougCouch_4-1614308727749.png

 

What you are doing here is selecting from the list of id's in the context from the search in the prior step but you can do it from any context entry that has multiple items in it.

 

So out of my set of AMAZING test data that I was able to create on the fly:

DougCouch_2-1614302910448.png

When you run the playbook the task will ask for input based on the previous task:

DougCouch_3-1614303556468.png

 

Hope this helps!!

 

 

Doug Couch  |  XSOAR Customer Success Engineer - Manager
Palo Alto Networks  |  3000 Tannery Way  |  Santa Clara, CA 95054  

Fantastic!  Thank you!

 

On experimenting with this, I see that the choices can come from the Context or Lists data, and that it's possible to apply filters and a transformation.  Very nice.

 

Where is the documentation for this functionality for populating the choices?  I looked around, but I don't see anything about it in the document you linked.

 

I think this will work as I want in a playbook kicked off by a button on the layout.  Great!  Is it possible to use this method (of dynamically populating choices) elsewhere?  In particular, can it be used for a field on a layout?  I tried setting up a new field like this and didn't see the "{}" marker in the box for the choices.  It looked like I had to just type in some fixed choices.

 

At the HTML level, each item of a select object can display one string but return an arbitrary value not necessarily visible to the user.  Is that possible here?  It seems to just take a flat list of values.

Hmm.  The idea here is that, at any time, the analyst should be able to click on something to mark the incident as a false positive and select its parent incident.  It should also be possible to un-mark the incident.  This shouldn't affect the processing of the main playbook.

 

A colleague of mine just told me that running another playbook on the incident (to ask the user for the parent) would create a new incident each time the user clicked on the button.  That seems like a pretty heavyweight action just to get the answer to a question.  Automations kicked off by buttons are much more lightweight, but they also can't pop up a single-select GUI element with custom choices, right?

 

 

Hmm again.

 

I'm exploring, and I keep running into roadblocks.  My users may change their minds or need to re-assign incidents to different false positive parent incidents.  So, putting this process in the main playbook seems like it wouldn't work.  Playbooks seem to be set up to complete a task, move on, and never go back.

 

Also, it looks like a button on the layout can kick off a script/automation (they're the same thing, aren't they?), but I didn't find any way for it to launch a fresh playbook that would collect information from the user.

 

So, I have to ask again:  Is what I'm asking for even possible?

If I understand correctly, the following may help:

 

1. Create a new field that will be used as the selection drop down for the user and give it  some default values:

ABurt_0-1614416019988.png

 

2. Create a new automation script (yes automations = scripts and scripts = automations) and ensure it has the "field-display" tag:

ABurt_2-1614416457373.png

In the above script, the returned list options assumes that the field named "monitored_field" is available in the incident and that it is a list. It will also only display options for the field if it is part of an existing incident and not in the "create / new" incident form.

 

3. Assign the field-display script to the field:

ABurt_3-1614416530414.png

 

4. Create a new "field-change-triggered" script and make sure to assign the "field-change-triggered" tag:

ABurt_4-1614416722532.png

In the above script, if the selection has changed then the script runs the playbook again by simply setting the playbook. When setting with no parameters, it re-runs the existing playbook on the existing incident.

 

5.  Edit the field again and this time assign the field change parameters:

ABurt_5-1614416854330.png

 

 

This means that each incident will have a field (that you can place in the incident layout) that will contain dynamic content based on the incident fields, lists or context and when changed will execute a command (in this case the playbook re-run).

 

A small demo:

 

 

In your use case, the field-display script would pull a list of parent incidents. The field-change script would then re-assign the current incident to that parent incident.

 

I hope this helps.

 

Regards

 

Adam

Thanks, again, so much for your help.  I am impressed by the depth of your assistance and grateful for it.

 

Re-running the playbook on field value change is interesting.  When re-running the playbook, I'm thinking that there should be a decision point at the very beginning that will detect that the incident has been marked as a false positive with a parent, then branch off to the end, with all tasks completed.  Then, it's just a matter of waiting for someone to clean up the mess (tracking that in the parent) and closing the incident.  Is that a reasonable approach?

 

Also, can you confirm that the "field-display" tag means that the script will run and the options will be re-calculated every time a user pulls up the incident and looks at the field?    The script runs and populates the selections when the user clicks on a dropdown icon.

Note to future developers trying to do this:  Once I knew what to look for, Google found the relevant documentation for me:  Here it is.

On experimenting with this, I found a glitch.  In particular, it's not clear what data the field configuration script can access.

 

I want the user to pick from a list of false positive parent case names, and this is what I have so far:

 

search_results = demisto.executeCommand("SearchIncidentsV2", {"type":"False Positive Parent"})
# Then a miracle occurs
demisto.results({"hidden": False, "options": ["00000 None", "00001 Dynamic1", "00002 Dynamic 2", "00003 Dynamic 3"]})

 

The SearchIncidentsV2 command is supposed to put its results in the incident context, but it doesn't do that.  The context is unchanged.  Running demisto.log() causes the script to fail, so I don't have any way to see what's coming back from the demisto.executeCommand().  (The documentation for the return value is "Union[dict, list]: Command execution response wrapped in Demisto entry object", which is uniquely uninformative.)

 

The display with this script does correctly load up the test options in the demisto.results() call, so it's just a matter of getting the list of parent tickets from somewhere.

OK, I undertstand now. I think the main problem is the child / parent approach for the incidents.

 

One can create a child investigation / incident from within an incident (by using the Actions -> Add Child Incident), but (to my knowledge) you cannot re-assign that child investigation to a parent investigation.

 

The way to do this would be to have an incident "linked" to another incident and then (optionally) marked as closed as a duplicate. The parent incident then remains open and will reflect all those that have been linked to it.

 

Part of script can unlink an existing incident and link another incident (or link multiple incidents) upon the script changing.

 

 

If this is a way you would like to go, try the following script (and modify according to your setup):

args = demisto.args()
new = args.get('new', None)
old = args.get('old', None)
formType = args.get('formType', None)

# If this is being triggered due to user selecting a new value
if new or old:

    # If the value has changed
    if new != old:

        # Extract the real parent incident ID
        parent_id = new.split("-")[0].replace(" ","")

        # Find and remove any existing linked incidents
        incident = demisto.incident()
        linkedIncidents = incident.get('linkedIncidents', [])
        if linkedIncidents:
            demisto.executeCommand("linkIncidents", {"linkedIncidentIDs":linkedIncidents, "action": "unlink"})

        # Link the newly selected incident
        demisto.executeCommand("linkIncidents", {"linkedIncidentIDs":parent_id, "action": "link"})

        # Close the incident as a duplicate
        demisto.executeCommand("closeInvestigation", {"closeReason": "Duplicate", "closeNotes": f"Duplicate of incident id {parent_id}"})


# If this is the user opening the dropdown menu
else:

    # Here are some static options for the options list
    options = ["000 - Default Option 1", "001 - Default Option 2"]

    # Get a list of potential parent incidents
    incidents = demisto.executeCommand("getIncidents", {"query": "type:Malware -status:Closed"})[0]['Contents']['data']

    # Append the potential list to the static list
    selectOptions = options + [f"{x['id']} - {x['name']}" for x in incidents]

    # Present the list in the drop down
    demisto.results({"hidden": False, "options": selectOptions})

 

 

 

For the above script, I assigned BOTH the "field-display" tag and the "field-change-triggered" tag. The script then makes the differention between how it is being called. It's not necessary but keeps everything in a single script.

 

For my existing incidents, I used the "query" as I think it has more power to find other incidents. Also, in my simple example, I am simply looking for malware type incidents that are not closed.

This appears to do the trick:

CommandResult = demisto.executeCommand("SearchIncidentsV2", {"query":"-status:closed type:\"False Positive Parent\""})

options = []
options.append("00000 None")
for incident in CommandResult[0]["Contents"][0]["Contents"]["data"]:
  options.append("{} {}".format(incident["id"], incident["name"]))

demisto.results({"hidden": False, "options": options})

Note that the following command does not work, for some reason.  It returns a bunch of incidents with the wrong type, even though the type names are similar.  It's like it's doing fuzzy matching:

demisto.executeCommand("SearchIncidentsV2", {"type":"False Positive Parent"})

 

Looks like our letters crossed in the mail.

 

What was fouling me up was trying to extract the results of the demisto.executeCommand().  It appears that a script executing in order to determine the selection options doesn't run with the same access, and the documentation is terrible.  I eventually attached my script to a button where I could run demisto.log() and look at the results from that.  It would be better if the data structure were documented.

 

Also, you call the "getIncidents" command, which doesn't appear to exist in any documentation (except as an example) or as a command on my dev instance.  I managed to get the job done with "SearchIncidentsV2", though.

Ahhh, right. When the user selects the drop down, the script will run under their username (limited user - and therefore will restrict the results to what they are allowed to see). In order to extend the permissions here use the "run as" in the automation script iteself:

 

ABurt_0-1614702237279.png

 

We have a debug pack coming soon in the Marketplace that makes debugging code a little easier. I quite often output using "demisto.results()" at the start of the script and simply run it manually (using !) in the warroom of the incident. This then outputs the data in a clear way.

 

The demisto.executeCommand command does output in a standard manner, but, there are a couple of options to grab the data. Firstly, it will output an array, for which you'll generally need the first item. For example:

data = demisto.executeCommand(comminhere)[0]

 

The data is then a JSON dictionary. Then, the data to be accessed "generally" is in the "contents" key. If I wanted to know the contents of this key I would put at the top of my script:

 

demisto.results(demisto.executeCommand("commandinhere", {argsinhere})
sys.exit(0)

 

Then executing in the warroom using (assuming that my script name is "userSelectOptions"):

!userSelectOptions raw-response=true

 

 

There are some great commands that don't exist explicitly as an automation script. To find them, when in the automation script editor, go to the "script helper" in the top right:

 

ABurt_1-1614702876902.png

 

In addition (if you have the tenacity 🙂   ) there are also a bunch of classes and functions that are included in all created automations. If you are writing in Python, they exist in a file called "CommonServerPython":

 

ABurt_2-1614703018413.png

 

There is one called "CommonServerUserPython" also, where you can add your own common functions / classes:

 

ABurt_3-1614703144205.png

 

The above means that I can call "my_function" from ANY automation script.

 

 

Is everything working ok now?

 

Yes, the dropdown is displaying correctly now.  Thank you!  I haven't attempted to implement any (on-change) functionality yet.

DZerkle_0-1614712100091.jpeg

This CommonServerPython sounds very interesting.  Where can I see that?    Are those the same as what is documented at https://xsoar.pan.dev/docs/integrations/code-conventions#common-server-functions?

 

Also, note that the code ran differently when it was triggered to populate the field options and when it was triggered by a button.  When it was populating the field options, demisto.log() triggered an error.  When it was running from a button, demisto.log() worked as expected.  Same user.  Same code.  Same layout tab.

 

This actually feels intentional.  Populating a GUI element maybe shouldn't have side effects in the context or the war room.  However, this is not documented, so writing this scrap of code was a lot more difficult than it had to be.

  • 1 accepted solution
  • 7368 Views
  • 14 replies
  • 0 Likes
Like what you see?

Show your appreciation!

Click Like if a post is helpful to you or if you just want to show your support.

Click Accept as Solution to acknowledge that the answer to your question has been provided.

The button appears next to the replies on topics you’ve started. The member who gave the solution and all future visitors to this topic will appreciate it!

These simple actions take just seconds of your time, but go a long way in showing appreciation for community members and the LIVEcommunity as a whole!

The LIVEcommunity thanks you for your participation!