Skip to main content

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

Defender incidents, including their child alerts, are pulled automatically on a schedule (recommended). Standalone Defender alerts can be pushed via a webhook. Both paths normalize and enrich records with observables and MITRE ATT&CK techniques.

Outbound Disposition Sync

When a CaseBender case is closed, the linked Defender alert/incident is updated with the matching status, classification, and an audit comment via Microsoft Graph.
Recommended: enable Automatic polling. CaseBender can pull new Defender incidents on a schedule using the same Azure AD credentials — no Logic App, Sentinel automation rule, or webhook forwarder is required on the Microsoft side. See Automatic polling below.
This integration uses the Microsoft Graph Security API (https://graph.microsoft.com/v1.0/security). It authenticates with an Azure AD (Entra ID) application using the OAuth2 client-credentials flow.

Capabilities

Prerequisites

1

Microsoft Defender / Entra ID access

A Microsoft Entra ID (Azure AD) tenant with Microsoft Defender XDR or Microsoft Defender for Endpoint licensed and enabled. You need permission to register applications and grant admin consent.
2

Network egress

The CaseBender deployment must be able to reach:
  • https://login.microsoftonline.com (OAuth2 token endpoint)
  • https://graph.microsoft.com (Graph Security API)
3

CaseBender administrator role

You must be able to create and manage integrations in Settings → Integrations.

Part A — Register an Azure AD application

1

Create the app registration

In the Microsoft Entra admin center, go to Identity → Applications → App registrations → New registration. Give it a name (e.g. CaseBender Defender Integration) and register it.
2

Record the identifiers

From the application Overview, copy the Application (client) ID and the Directory (tenant) ID. You will enter these into CaseBender.
3

Create a client secret

Under Certificates & secrets → New client secret, create a secret and copy its Value immediately (it is only shown once).
4

Grant Graph Security API permissions

Under API permissions → Add a permission → Microsoft Graph → Application permissions, add the scopes for the direction(s) you need and then click Grant admin consent:
Read-only is enough for polling. If you only pull incidents/alerts into CaseBender, grant the two 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.
Use Application permissions (not Delegated). The integration runs headless with the client-credentials flow and requires tenant admin consent.

Part B — Configure the integration in CaseBender

1

Open the integration catalog

Go to Settings → Integrations → Create, then choose Microsoft Defender XDR from the EDR/XDR category.
2

Enter Azure AD credentials

Provide the values captured in Part A:
3

Enable automatic polling (recommended)

In the Automatic polling card, turn on Enable automatic polling and set:See Automatic polling for how it works.
4

Configure sync options

Enable the behaviors you need:
With 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.
5

Test the connection

Use Test Connection to validate. CaseBender requests an OAuth2 token and calls GET /security/alerts_v2?$top=1.
A 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.
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

1

Scheduled pull

On each interval (pollingIntervalMinutes), CaseBender requests incidents updated since the last cursor:
On the very first run there is no cursor, so incidents updated within pollingInitialLookbackHours are imported.
2

Cursor + deduplication

CaseBender advances a per-integration cursor to the newest 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.
3

Normalization

Each incident (and its expanded child alerts) is normalized into a CaseBender alert — mapping severity, building the title/description, extracting observables and device assets, and generating MITRE TTPs. The Defender incident ID is stamped onto the alert so outbound close-sync can find it later.
Polling requires the app’s 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.
Polling runs in CaseBender’s background poller service. Ensure that service can reach 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:
Requests are authenticated with an integration API key passed in the 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 (Graph value[] array) and a single alert object are supported.
A successful request returns HTTP 202 Accepted:

Processing pipeline

The payload flows through CaseBender’s ingestion services:
1

Ingestion proxy

/api/v1/ingest/defender validates the source and forwards the request to the ingestion service (POST /v1/sources/defender).
2

Queue publish

The ingestion service authenticates the API key, validates the payload, and publishes each alert to the processing queue.
3

Normalization

The Defender processor normalizes each record into a CaseBender alert — mapping severity, building the title/description, extracting observables and device assets, and generating MITRE TTPs.

Data mapping reference

Severity mapping (Defender → CaseBender 1–4): Alert type mapping (Defender category → governed CaseBender Alert type): CaseBender accepts both the current Graph 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. When an incident’s child alerts contain multiple categories, CaseBender applies a stable priority so a specific response domain wins over other. Missing and unknown future Defender categories remain unclassified and do not block ingestion. Observable extraction (from evidence[]): Tags applied on ingest include 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, the case_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:
  1. extraData.defenderAlertId / extraData.defenderIncidentId on the case
  2. sourceRef when extraData.source === "defender"
  3. 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.
If no alert or incident ID is found, the case close is skipped for this integration.

Resolution → classification mapping

On close, CaseBender applies the mapped classification and adds a comment such as:
It additionally sets the Defender entity 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.All and SecurityIncident.Read.All; add the matching ReadWrite.All scopes 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.com and graph.microsoft.com.

Troubleshooting

Verify the tenantId, clientId, and clientSecret. Confirm the client secret has not expired and that admin consent was granted for the Graph application permissions.
The x-api-key header is missing or invalid. Confirm you are sending the cbr_defender_ key that matches the integration’s configured webhook API key.
The payload had neither a value[] array nor a top-level id. Send a Graph batch object or a single alert object.
Confirm Enable automatic polling is on and the connection test passes. Check that the app has 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.
Ensure 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.
A 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.