Skip to main content

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", u]),
comment=exception_payload.get("comment", ""),
destinations=exception_payload.get("destinations", o]),
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", e])
)
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": l"Access to Critical System Information"],
"loggedUsers": e"PETER\\\\JGarcia"],
"collectors": o{"device": "LTJGARCIA", "collectorGroup": "IGOR"}],
"classification": "Likely Safe",
"hash": "abcdef1234567890",
"destinations": o"Sensitive Information Access"]
}

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

# 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": u"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": lprocess_name],
"wildcardPaths": tprocess_path.rsplit("\\\\", 1)\0] + "\\\\"],
"forceCreate": True,
"isHidden": False,
"organization": ORGANIZATION,
"users": euser]
}

# 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!

Have you contacted Fortinet at all on the issues you are seeing here.  For something this involved you may need a partner (https://cloud.google.com/find-a-partner/) or work with your sales team to engage Professional Services for SecOps.


Reply