Overview
The Microsoft Defender XDR integration (INT-021) provides bi-directional synchronization between CaseBender and Microsoft’s extended detection and response platform, including Microsoft Defender for Endpoint (MDE) and Microsoft Defender XDR.Inbound Ingestion
Outbound Disposition Sync
https://graph.microsoft.com/v1.0/security).
It authenticates with an Azure AD (Entra ID) application using the OAuth2 client-credentials flow.Capabilities
Prerequisites
Microsoft Defender / Entra ID access
Network egress
https://login.microsoftonline.com(OAuth2 token endpoint)https://graph.microsoft.com(Graph Security API)
CaseBender administrator role
Part A — Register an Azure AD application
Create the app registration
CaseBender Defender Integration) and register it.Record the identifiers
Create a client secret
Grant Graph Security API permissions
Read.All scopes. The ReadWrite.All scopes are required only for the
optional outbound close-back (syncCaseClose) — closing a Defender alert/incident from
CaseBender. Since ReadWrite.All also includes read access, granting just the two
ReadWrite.All scopes covers both directions.Part B — Configure the integration in CaseBender
Open the integration catalog
Enter Azure AD credentials
Enable automatic polling (recommended)
Configure sync options
syncCaseClose on but both close…OnCaseClose toggles off, CaseBender still
writes the classification and audit comment to Defender — it just won’t flip the
Defender entity to resolved.Test the connection
GET /security/alerts_v2?$top=1.403 response during the test is treated as success — it confirms authentication
worked even when the app has not yet been granted read scope on that specific endpoint.Inbound (automatic polling, recommended)
With Automatic polling enabled, CaseBender periodically calls the Graph Security API and ingests new incidents on its own. This is the simplest and most reliable path: it needs no Logic App, Sentinel automation rule, or webhook forwarder — only the Azure AD app registration you already created in Part A.How it works
Scheduled pull
pollingIntervalMinutes), CaseBender requests incidents updated since
the last cursor:pollingInitialLookbackHours are imported.Cursor + deduplication
lastUpdateDateTime it has
seen, so each run only fetches new activity. Incidents are also deduplicated by
Defender incident ID, so an incident is never ingested twice — even if the polling
window overlaps.Normalization
SecurityIncident.Read.All (covered by
SecurityIncident.ReadWrite.All). With autoCreateCases enabled, each polled incident
becomes a Case automatically (deduplicated by incident ID) — this matches the “pull
incident → work it as a case” flow. With it disabled, incidents land in the Alerts inbox
for manual promotion. Either way the Defender link is preserved so closing the case syncs
back to Defender.login.microsoftonline.com and graph.microsoft.com (directly or via your configured
proxy). On proxy-only networks, see the proxy section of the deployment guide.Inbound (webhook / push)
As an alternative (or in addition) to polling, Defender — or an intermediary such as Logic Apps, Sentinel, or a webhook forwarder — can push alert/incident payloads to the CaseBender ingestion endpoint.Endpoint
The sender posts alert/incident payloads to the CaseBender ingestion endpoint:x-api-key header
(the authorization: Bearer <key> header is also accepted). Defender webhook API keys are
prefixed with cbr_defender_.
Payload formats
Both a batch format (Graphvalue[] array) and a single alert object are supported.
202 Accepted:
Processing pipeline
The payload flows through CaseBender’s ingestion services:Ingestion proxy
/api/v1/ingest/defender validates the source and forwards the request to the ingestion
service (POST /v1/sources/defender).Queue publish
Normalization
Data mapping reference
Severity mapping (Defender → CaseBender 1–4):categories[] collection and the deprecated
single category field. Portal labels and Graph values are normalized, so values such as
Command and control and CommandAndControl are equivalent.
other. Missing and unknown future Defender
categories remain unclassified and do not block ingestion.
Observable extraction (from evidence[]):
defender, xdr, one category:<category> per Defender
category, incident (for incidents), and one mitre:<technique> tag per MITRE technique.
Ingested records default to TLP:2 and PAP:2.
Outbound: Syncing case dispositions to Defender
When a case is closed in CaseBender, thecase_closed event is dispatched to the Defender
handler. If the case is linked to a Defender alert or incident, CaseBender pushes the
resolution back through the Graph Security API.
How the linked Defender entity is resolved
The handler looks for the Defender identifiers in this order:extraData.defenderAlertId/extraData.defenderIncidentIdon the casesourceRefwhenextraData.source === "defender"- The Defender alert(s) linked to the case — the ingest pipeline stamps the Defender
incident/alert ID onto each alert’s
customFields, so a case promoted from a polled or webhook Defender alert resolves automatically, regardless of how it was created.
Resolution → classification mapping
classification and adds a comment such as:
status to resolved when the matching toggle
(closeAlertsOnCaseClose for alerts, closeIncidentsOnCaseClose for incidents) is enabled.
The outcome is written to the case timeline so analysts can confirm the sync.
syncCaseClose is the master switch for outbound sync (default on). When it is off, no
outbound calls are made. Alert updates use the modern /security/alerts_v2/{id} endpoint;
incident updates use /security/incidents/{id}.Security considerations
- Secret handling — the client secret is stored in the integration settings; rotate it on the schedule your organization requires and update the integration when you do.
- Least privilege — for inbound-only deployments grant just
SecurityAlert.Read.AllandSecurityIncident.Read.All; add the matchingReadWrite.Allscopes only if you enable outbound close-back. Do not add broader Graph scopes. - Token caching — access tokens are cached in memory per integration and refreshed one minute before expiry; no tokens are persisted to disk.
- Webhook keys — treat the
cbr_defender_API key as a secret. Rotate it if exposed and update the sender configuration. - Network — restrict egress to
login.microsoftonline.comandgraph.microsoft.com.
Troubleshooting
Test Connection fails with an OAuth2 error
Test Connection fails with an OAuth2 error
tenantId, clientId, and clientSecret. Confirm the client secret has not
expired and that admin consent was granted for the Graph application permissions.Ingestion returns 400 'No alerts in payload'
Ingestion returns 400 'No alerts in payload'
value[] array nor a top-level id. Send a Graph batch object
or a single alert object.Polling is enabled but no incidents appear
Polling is enabled but no incidents appear
SecurityIncident.Read.All (or ReadWrite.All) with admin consent, that the
poller service can reach graph.microsoft.com (and your proxy, if any), and that there
are incidents newer than the cursor. On first run only incidents within
pollingInitialLookbackHours are imported. Polled incidents land in the Alerts inbox.Make sure all CaseBender services are running. Incidents are fetched by the background
processor and turned into alerts (and optionally cases) by the background worker. If the
worker isn’t running, incidents are fetched but never show up. With Docker, run
docker compose ps and confirm the worker and misp-processor services are Up; if not,
run docker compose up -d. The standard installer and quickstart set everything up
automatically — you do not need to configure any queue or Redis settings yourself.Case close doesn't update Defender
Case close doesn't update Defender
syncCaseClose is on (it is the master switch). To also mark the Defender entity
resolved, enable closeAlertsOnCaseClose / closeIncidentsOnCaseClose. The case must be
linked to a Defender alert/incident — closing a case promoted from a polled/webhook
Defender alert resolves the link automatically. Check the case timeline for the sync
result.Outbound update returns 403 or 404
Outbound update returns 403 or 404
403 means the Azure AD app lacks write permission — confirm SecurityAlert.ReadWrite.All
and SecurityIncident.ReadWrite.All are granted with admin consent. A 404 on an alert
update usually means a legacy endpoint; CaseBender uses /security/alerts_v2/{id} for
modern Defender XDR alerts.