Is it possible to create an exception via an action in SOAR?

I'm working with SOAR and I would like to know if it's possible to create an exception through a custom action. The idea is for this action to be triggered once certain conditions are met within a playbook, thereby preventing similar alerts from being generated in the future. In case it is possible, what are the fields that must be added in the code in order to successfully create said exception?

There are a couple of rules generated through EDR that constantly generate the same alerts with the same data. I have a playbook that checks if those alerts are dangerous or not. I want to create a custom action that, in case the playbook confirms that said alert is actually a false alarm, generates an automatic exception in FortiEDR so that it doesn't create another alert in case it sees that there is the same information.

This is the code I have for the moment in the custom action create_exception:

from SiemplifyAction import SiemplifyAction
from SiemplifyUtils import output_handler
from FortiEDRManager import FortiEDRManager
from TIPCommon import extract_configuration_param, extract_action_param
import json

PROVIDER_NAME = "FortiEDR Integration"
SCRIPT_NAME = "CREATE EXCEPTION"

@output_handler
def main():
    siemplify = SiemplifyAction()
    siemplify.script_name = SCRIPT_NAME
    siemplify.LOGGER.info("--------------- Main - Parameter Initialization ---------------")

    # Get integration configuration parameters
    config = siemplify.get_configuration(PROVIDER_NAME)
    api_url = config.get("Instance URL")
    username = config.get("Usuario")
    password = config.get("Password")
    organization = config.get("Organization")

    # Initialize the FortiEDR manager
    manager = FortiEDRManager(
        api_url=api_url,
        username=username,
        password=password,
        organization=organization,
        siemplify_logger=siemplify.LOGGER
    )

    # Get action parameters
    event_json_str = extract_action_param(siemplify, param_name="Event JSON", is_mandatory=True)
    event_data = json.loads(event_json_str)

    # Extract event data
    event_id = event_data.get("eventId")
    process_name = event_data.get("process")
    process_path = event_data.get("processPath")
    device_name = event_data.get("collectors", [{}])[0].get("device")
    user = event_data.get("loggedUsers", [None])[0]
    rules = event_data.get("rules", [])
    classification = event_data.get("classification")
    certified = event_data.get("certified")

    # Try to get the file hash from the event JSON
    file_hash = event_data.get("hash")

    # If the hash is not present in the event JSON, request it as a parameter
    if not file_hash:
        hash_param = extract_action_param(siemplify, param_name="Hash", print_value=True)
        file_hash = hash_param

    # Search for hash details if available
    hash_result = None
    if file_hash:
        siemplify.LOGGER.info(f"Searching for hash: {file_hash}")
        try:
            hash_result = manager.search_hash(file_hash)
            siemplify.LOGGER.info(f"Hash search result: {hash_result}")
        except Exception as e:
            siemplify.LOGGER.error(f"Failed to search for hash: {e}")

    # Build the exception payload
    exception_payload = {
        "originEventId": event_id,
        "userName": username,
        "comment": f"Automatically created exception for process {process_name} on device {device_name}",
        "selectedDestinations": ["All Destinations"],
        "selectedCollectorGroups": ["All Collector Groups"],
        "alerts": []
    }

    for rule in rules:
        alert_entry = {
            "ruleName": rule,
            "process": {
                "name": process_name,
                "path": process_path,
                "usedInException": True,
                "useAnyPath": False,
                "signed": certified
            }
        }

        # Add the hash if it was found
        if file_hash:
            alert_entry["process"]["hash"] = file_hash

        exception_payload["alerts"].append(alert_entry)

    # Send the exception to FortiEDR via the API
    try:
        response = manager.create_exception(exception_payload)
        siemplify.LOGGER.info(f"Exception created successfully: {response}")
        siemplify.end("Exception created successfully.", True)
    except Exception as e:
        siemplify.LOGGER.error(f"Failed to create exception: {e}")
        siemplify.end(f"Failed to create exception: {e}", False)

if __name__ == "__main__":
    main()

 The "Event JSON" is a parameter of code type JSON with this info:


{
  "eventId": 92053159,
  "process": "MyProcess.exe",
  "processPath": "C:\\Program Files (x86)\\Common Files\\Adobe\\ARM\\Execute\\22751\\MyProcess.exe",
  "processType": "32 bit",
  "firstSeen": "2025-04-29 10:31:27",
  "lastSeen": "2025-04-29 10:31:27",
  "seen": false,
  "handled": false,
  "comment": null,
  "certified": true,
  "archived": false,
  "severity": "Medium",
  "classification": "Likely Safe",
  "destinations": [
    "Sensitive Information Access"
  ],
  "rules": [
    "Access to Critical System Information"
  ],
  "loggedUsers": [
    "PETER\\JGarcia"
  ],
  "organization": "Gaz",
  "muted": false,
  "muteEndTime": null,
  "processOwner": "Local System",
  "threatDetails": {
    "threatFamily": "Unknown",
    "threatType": "Unknown",
    "threatName": "Unknown"
  },
  "collectors": [
    {
      "lastSeen": "2025-04-29 10:23:18",
      "ip": "8.8.8.8",
      "collectorGroup": "IGOR",
      "macAddresses": [
        "Mac_Adress1",
        "Mac_Adress2"
      ],
      "id": 75173347,
      "device": "LTJGARCIA",
      "operatingSystem": "Windows 10 Pro"
    }
  ],
  "action": "Block"
}

 And I also have the parameter hash with this info:

mikelmi01_0-1746428415444.png

 

0 3 106
3 REPLIES 3

Could you please explain in detail 

Can you detail the Actions please as it might change the answer

In short you can use "custom lists" today as an exception list. This might be replaced by "Data Tables" one day, but not for a while.

In the marketplace today we have "Add String to Custom List" which will udpate a list. Then in the next alert use "Search Custom Lists" to see if the thing you are exempting has been added.  This Action has a JSON output of matches, so you will need to count() or similar depending on how you implement.

Does that help?  (apologies for any delays in response, I'm PTO for a few days now)

Hi @mikelmi01

Instead of using the SOAR to filter or suppress cases can you filter the SIEM rule creating the SOAR alerts?

We have EDR which likewise can generate a lot of events, many of which we consider low-priority. The way I see it there's two options: suppress in the EDR or suppress or filter in the SIEM rule.

If you suppress in the EDR, your SIEM never sees those EDR detections (of course, right?). So if you instead suppress low-value events and false positives in the SIEM rule, you'll have those detections available for SIEM work, reporting, enrichment, etc.

(you'll still make some EDR suppressions if they make sense for you.)

For filtering in the SIEM I recommend using reference lists or data tables for the filters. Then the rule doesn't become bloated with filters and comments explaining the filters (you could do that of course if it's expedient of course). The data tables will have the filter text, filter notes, and other info you want. SecOps data tables are not as easy to use (yet) as Splunk lookup tables, but they'll get there.

Conceptually does that make sense?