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!