Issue with create_exception in FortiEDR

I'm trying to create an exception in FortiEDR using the create_exception method from the Exceptions class in the fortiedr Python library. I have confirmed that the credentials that I am using are correct and are working perfectly. 

Here is the script I'm using (Exception_generator.py):

 

 

 

import requests
import json
from fortiedr import auth, Events, Administrator, Policies, Audit, SystemInventory, Forensics, HashSearch, ignore_certificate, Exceptions
from urllib.parse import urljoin


class FortiEDRManager:
    def __init__(self, api_url, username, password, organization, siemplify_logger=None):
        ignore_certificate()
        self.api_url = api_url.rstrip('/')
        self.username = username
        self.password = password
        self.organization = organization
        self.logger = siemplify_logger
        self.session = self._authenticate()

    def _authenticate(self):
        session = auth(
            user=self.username,
            passw=self.password,
            host=self.api_url,
            org=self.organization
        )
        if not session:
            raise Exception("Authentication failed")
        return session

    def create_exception(self, exception_payload):
        exceptions_client = Events()
        response = exceptions_client.create_exception(
            allCollectorGroups=exception_payload.get("allCollectorGroups", False),
            allDestinations=exception_payload.get("allDestinations", False),
            allOrganizations=exception_payload.get("allOrganizations", False),
            allUsers=exception_payload.get("allUsers", False),
            collectorGroups=exception_payload.get("collectorGroups", []),
            comment=exception_payload.get("comment", ""),
            destinations=exception_payload.get("destinations", []),
            eventId=exception_payload.get("eventId"),
            exceptionId=exception_payload.get("exceptionId"),
            useAnyPath=exception_payload.get("useAnyPath"),
            useCommandLine=None,
            useInException=exception_payload.get("useInException"),
            wildcardFiles=exception_payload.get("wildcardFiles"),
            wildcardPaths=exception_payload.get("wildcardPaths"),
            forceCreate=exception_payload.get("forceCreate", False),
            isHidden=exception_payload.get("isHidden", False),
            organization=self.organization,
            users=exception_payload.get("users", [])
        )
        return response


# ====================== REAL CONFIGURATION ======================
API_URL = "https://<YOUR_INSTANCE_URL>"         # 🔒 Replace with your real URL
USERNAME = "<YOUR_USERNAME>"                    # 🔒 Replace with your actual username
PASSWORD = "<YOUR_PASSWORD>"                    # 🔒 Replace with your actual password
ORGANIZATION = "<YOUR_ORGANIZATION>"            # 🔒 Exact organization name in FortiEDR
# ================================================================

# Real event provided
event_data = {
    "eventId": 92053159,
    "process": "MyProcess.exe",
    "processPath": "C:\\Program Files (x86)\\Common Files\\Adobe\\ARM\\Execute\\22751\\MyProcess.exe",
    "certified": True,
    "rules": ["Access to Critical System Information"],
    "loggedUsers": ["PETER\\JGarcia"],
    "collectors": [{"device": "LTJGARCIA", "collectorGroup": "IGOR"}],
    "classification": "Likely Safe",
    "hash": "abcdef1234567890",
    "destinations": ["Sensitive Information Access"]
}

# Extract key data
event_id = event_data["eventId"]
process_name = event_data["process"]
process_path = event_data["processPath"]
device_name = event_data["collectors"][0]["device"]
collector_group = event_data["collectors"][0]["collectorGroup"]
user = event_data["loggedUsers"][0]
rules = event_data["rules"]
certified = event_data["certified"]
file_hash = event_data.get("hash")
destinations = event_data.get("destinations", [])

# Initialize FortiEDR Manager
manager = FortiEDRManager(
    api_url=API_URL,
    username=USERNAME,
    password=PASSWORD,
    organization=ORGANIZATION
)

# Build exception payload compatible with create_exception
exception_payload = {
    "allCollectorGroups": True,
    "allDestinations": False,
    "allOrganizations": False,
    "allUsers": False,
    "collectorGroups": ["IGOR"],
    "comment": f"Automatically created exception for {process_name} on {device_name}",
    "destinations": destinations,
    "eventId": event_id,
    "exceptionId": None,
    "useAnyPath": None,
    "useInException": None,
    "wildcardFiles": None,
    "wildcardPaths": None,
    "forceCreate": True,
    "isHidden": False,
    "organization": ORGANIZATION,
    "users": [user]
}

# Submit the exception
try:
    print("📦 exception_payload:")
    for k, v in exception_payload.items():
        print(f"  {k}: {v} (type: {type(v).__name__})")
    print("\n Attempting create_exception...")
    response = manager.create_exception(exception_payload)
    print(" create_exception executed successfully.")
    print(json.dumps(response, indent=4))
except Exception as e1:
    import traceback
    print(" Error in create_exception:")
    traceback.print_exc()
    print("Error type:", type(e1))
    print("Error details:", e1)

 

 

 

 

When I try to send this using:

 

 

response = manager.create_exception(exception_payload)

 

 

The call returns:

 

 

{
    "status": false,
    "data": {
        "status_code": 400,
        "error_message": "Total Exception count exceeded max count of 30,000"
    }
}

 

 

 

 

This doesn’t make sense — I have access to the FortiEDR console and I can confirm I don’t even have 600 exceptions, let alone 30,000. So I suspect the error might be generic or misleading.

I also tried sending values for useAnyPath, useInException, wildcardFiles, and wildcardPaths as dicts or lists, as shown here:

 

 

import requests
import json
from fortiedr import auth, Events, Administrator, Policies, Audit, SystemInventory, Forensics, HashSearch, ignore_certificate, Exceptions
from urllib.parse import urljoin


class FortiEDRManager:
    def __init__(self, api_url, username, password, organization, siemplify_logger=None):
        ignore_certificate()
        self.api_url = api_url.rstrip('/')
        self.username = username
        self.password = password
        self.organization = organization
        self.logger = siemplify_logger
        self.session = self._authenticate()

    def _authenticate(self):
        session = auth(
            user=self.username,
            passw=self.password,
            host=self.api_url,
            org=self.organization
        )
        if not session:
            raise Exception("Authentication failed")
        return session

    def create_exception(self, exception_payload):
        exceptions_client = Events()
        response = exceptions_client.create_exception(
            allCollectorGroups=exception_payload.get("allCollectorGroups", False),
            allDestinations=exception_payload.get("allDestinations", False),
            allOrganizations=exception_payload.get("allOrganizations", False),
            allUsers=exception_payload.get("allUsers", False),
            collectorGroups=exception_payload.get("collectorGroups", []),
            comment=exception_payload.get("comment", ""),
            destinations=exception_payload.get("destinations", []),
            eventId=exception_payload.get("eventId"),
            exceptionId=exception_payload.get("exceptionId"),
            useAnyPath=exception_payload.get("useAnyPath"),
            useCommandLine=None,
            useInException=exception_payload.get("useInException"),
            wildcardFiles=exception_payload.get("wildcardFiles"),
            wildcardPaths=exception_payload.get("wildcardPaths"),
            forceCreate=exception_payload.get("forceCreate", False),
            isHidden=exception_payload.get("isHidden", False),
            organization=self.organization,
            users=exception_payload.get("users", [])
        )
        return response


# ====================== REAL CONFIGURATION ======================
API_URL = "https://<YOUR_INSTANCE_URL>"         # 🔒 Replace with your real URL
USERNAME = "<YOUR_USERNAME>"                    # 🔒 Replace with your actual username
PASSWORD = "<YOUR_PASSWORD>"                    # 🔒 Replace with your actual password
ORGANIZATION = "<YOUR_ORGANIZATION>"            # 🔒 Exact organization name in FortiEDR
# ================================================================

# Real event provided
event_data = {
    "eventId": 92053159,
    "process": "MyProcess.exe",
    "processPath": "C:\\Program Files (x86)\\Common Files\\Adobe\\ARM\\Execute\\22751\\MyProcess.exe",
    "certified": True,
    "rules": ["Access to Critical System Information"],
    "loggedUsers": ["PETER\\JGarcia"],
    "collectors": [{"device": "LTJGARCIA", "collectorGroup": "IGOR"}],
    "classification": "Likely Safe",
    "hash": "abcdef1234567890",
    "destinations": ["Sensitive Information Access"]
}

# Extract key data
event_id = event_data["eventId"]
process_name = event_data["process"]
process_path = event_data["processPath"]
device_name = event_data["collectors"][0]["device"]
collector_group = event_data["collectors"][0]["collectorGroup"]
user = event_data["loggedUsers"][0]
rules = event_data["rules"]
certified = event_data["certified"]
file_hash = event_data.get("hash")
destinations = event_data.get("destinations", [])

# Initialize FortiEDR Manager
manager = FortiEDRManager(
    api_url=API_URL,
    username=USERNAME,
    password=PASSWORD,
    organization=ORGANIZATION
)

# Build exception payload compatible with create_exception
exception_payload = {
    "allCollectorGroups": True,
    "allDestinations": False,
    "allOrganizations": False,
    "allUsers": False,
    "collectorGroups": ["IGOR"],
    "comment": f"Automatically created exception for {process_name} on {device_name}",
    "destinations": destinations,
    "eventId": event_id,
    "exceptionId": None,
    "useAnyPath": {
        process_name: {
            rule: False for rule in rules
        }
    },
    "useInException": {
        process_name: {
            rule: True for rule in rules
        }
    },
    "wildcardFiles": [process_name],
    "wildcardPaths": [process_path.rsplit("\\", 1)[0] + "\\"],
    "forceCreate": True,
    "isHidden": False,
    "organization": ORGANIZATION,
    "users": [user]
}

# Submit the exception
try:
    print("📦 exception_payload:")
    for k, v in exception_payload.items():
        print(f"  {k}: {v} (type: {type(v).__name__})")
    print("\n Attempting create_exception...")
    response = manager.create_exception(exception_payload)
    print(" create_exception executed successfully.")
    print(json.dumps(response, indent=4))
except Exception as e1:
    import traceback
    print(" Error in create_exception:")
    traceback.print_exc()
    print("Error type:", type(e1))
    print("Error details:", e1)

 

 

But that resulted in a KeyError: 'object' inside the validate_params function of the SDK, which seems to treat those parameters strangely.

Has anyone encountered this before? Is there a special format for these fields that avoids the 'object' key error, or is there something wrong on the FortiEDR backend?

Any help would be much appreciated!

0 0 45
0 REPLIES 0
OSZAR »