> ## Documentation Index
> Fetch the complete documentation index at: https://docs.casebender.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Microsoft Defender XDR

> Bi-directional integration between CaseBender and Microsoft Defender XDR (Windows Defender) for alert/incident ingestion and disposition sync.

## 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**.

<CardGroup cols={2}>
  <Card title="Inbound Ingestion" icon="arrow-right-to-bracket">
    Defender alerts and incidents are ingested into CaseBender, normalized, enriched with
    observables and MITRE ATT\&CK techniques, and turned into alerts/cases.
  </Card>

  <Card title="Outbound Disposition Sync" icon="arrow-right-from-bracket">
    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.
  </Card>
</CardGroup>

<Note>
  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.
</Note>

## Capabilities

| Capability                | Direction      | Description                                                                                  |
| ------------------------- | -------------- | -------------------------------------------------------------------------------------------- |
| Alert ingestion           | Inbound        | Defender alerts are normalized into CaseBender alerts                                        |
| Incident ingestion        | Inbound        | Defender incidents (with child alerts) are ingested                                          |
| Observable extraction     | Inbound        | IPs, URLs, file hashes, file names, hostnames, and user accounts are extracted from evidence |
| Asset extraction          | Inbound        | Device hostname, IP, and OS platform are captured from `devices[]`                           |
| MITRE ATT\&CK correlation | Inbound        | Technique IDs are added as tags and TTPs (e.g. `mitre:T1078`)                                |
| Alert disposition sync    | Outbound       | Alert `status`, `classification`, `determination`, and comments are pushed on case close     |
| Incident disposition sync | Outbound       | Incident `status`, `classification`, `determination`, and comments are pushed on case close  |
| Connection test           | Bi-directional | Validates OAuth2 credentials and Graph Security API access                                   |

## Prerequisites

<Steps>
  <Step title="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.
  </Step>

  <Step title="Network egress">
    The CaseBender deployment must be able to reach:

    * `https://login.microsoftonline.com` (OAuth2 token endpoint)
    * `https://graph.microsoft.com` (Graph Security API)
  </Step>

  <Step title="CaseBender administrator role">
    You must be able to create and manage integrations in **Settings → Integrations**.
  </Step>
</Steps>

## Part A — Register an Azure AD application

<Steps>
  <Step title="Create the app registration">
    In the [Microsoft Entra admin center](https://entra.microsoft.com), go to
    **Identity → Applications → App registrations → New registration**. Give it a name
    (e.g. `CaseBender Defender Integration`) and register it.
  </Step>

  <Step title="Record the identifiers">
    From the application **Overview**, copy the **Application (client) ID** and the
    **Directory (tenant) ID**. You will enter these into CaseBender.
  </Step>

  <Step title="Create a client secret">
    Under **Certificates & secrets → New client secret**, create a secret and copy its
    **Value** immediately (it is only shown once).
  </Step>

  <Step title="Grant Graph Security API permissions">
    Under **API permissions → Add a permission → Microsoft Graph → Application permissions**,
    add the following and then click **Grant admin consent**:

    | Permission                       | Purpose                            |
    | -------------------------------- | ---------------------------------- |
    | `SecurityAlert.ReadWrite.All`    | Read and update Defender alerts    |
    | `SecurityIncident.ReadWrite.All` | Read and update Defender incidents |

    <Warning>
      Use **Application** permissions (not Delegated). The integration runs headless with the
      client-credentials flow and requires tenant admin consent.
    </Warning>
  </Step>
</Steps>

## Part B — Configure the integration in CaseBender

<Steps>
  <Step title="Open the integration catalog">
    Go to **Settings → Integrations → Create**, then choose **Microsoft Defender XDR** from the
    **EDR/XDR** category.
  </Step>

  <Step title="Enter Azure AD credentials">
    Provide the values captured in Part A:

    | Field          | Description             |
    | -------------- | ----------------------- |
    | `tenantId`     | Directory (tenant) ID   |
    | `clientId`     | Application (client) ID |
    | `clientSecret` | Client secret value     |
  </Step>

  <Step title="Configure sync options">
    Enable the behaviors you need:

    | Option                      | Effect                                                      |
    | --------------------------- | ----------------------------------------------------------- |
    | `syncCaseUpdates`           | Push case updates back to Defender                          |
    | `syncCaseClose`             | Push case closure back to Defender                          |
    | `autoCreateCases`           | Automatically create cases from ingested Defender incidents |
    | `closeAlertsOnCaseClose`    | Resolve the linked Defender **alert** when a case closes    |
    | `closeIncidentsOnCaseClose` | Resolve the linked Defender **incident** when a case closes |
  </Step>

  <Step title="Test the connection">
    Use **Test Connection** to validate. CaseBender requests an OAuth2 token and calls
    `GET /security/alerts_v2?$top=1`.

    <Note>
      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.
    </Note>
  </Step>
</Steps>

## Inbound: Ingesting Defender alerts and incidents

### Endpoint

Defender (or an intermediary such as Logic Apps, Sentinel, or a webhook forwarder) sends
alert/incident payloads to the CaseBender ingestion endpoint:

```
POST https://<your-casebender-domain>/api/v1/ingest/defender
```

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 `sk_def_`.

### Payload formats

Both a **batch** format (Graph `value[]` array) and a **single alert** object are supported.

<CodeGroup>
  ```bash Batch (Graph value[]) theme={null}
  curl -X POST "https://<your-casebender-domain>/api/v1/ingest/defender" \
    -H "x-api-key: sk_def_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "value": [
        {
          "id": "da637...",
          "incidentId": "12345",
          "title": "Suspicious PowerShell execution",
          "severity": "high",
          "status": "new",
          "classification": "unknown",
          "createdDateTime": "2026-07-03T18:20:00Z",
          "mitreTechniques": ["T1059.001"],
          "evidence": [
            { "@odata.type": "#microsoft.graph.security.fileEvidence",
              "sha256": "9f2b...", "fileName": "payload.ps1" }
          ]
        }
      ]
    }'
  ```

  ```bash Single alert theme={null}
  curl -X POST "https://<your-casebender-domain>/api/v1/ingest/defender" \
    -H "x-api-key: sk_def_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "id": "da637...",
      "title": "Malware detected on endpoint",
      "severity": "medium",
      "status": "new",
      "createdDateTime": "2026-07-03T18:20:00Z"
    }'
  ```
</CodeGroup>

A successful request returns HTTP `202 Accepted`:

```json theme={null}
{ "success": true, "processed": 1, "failed": 0 }
```

### Processing pipeline

The payload flows through CaseBender's ingestion services:

<Steps>
  <Step title="Ingestion proxy">
    `/api/v1/ingest/defender` validates the source and forwards the request to the ingestion
    service (`POST /v1/sources/defender`).
  </Step>

  <Step title="Queue publish">
    The ingestion service authenticates the API key, validates the payload, and publishes each
    alert to the processing queue.
  </Step>

  <Step title="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.
  </Step>
</Steps>

### Data mapping reference

**Severity mapping** (Defender → CaseBender 1–4):

| Defender severity | CaseBender severity |
| ----------------- | ------------------- |
| `high`            | 1                   |
| `medium`          | 2                   |
| `low`             | 3                   |
| `informational`   | 4                   |
| `unknown`         | 3                   |

**Observable extraction** (from `evidence[]`):

| Evidence field  | Observable type           |
| --------------- | ------------------------- |
| `ipAddress`     | `ip`                      |
| `url`           | `url`                     |
| `sha256`        | `hash`                    |
| `fileName`      | `filename`                |
| `deviceDnsName` | `hostname`                |
| `userAccount`   | `user` (`DOMAIN\account`) |

**Tags** applied on ingest include `defender`, `xdr`, `service:<serviceSource>`,
`category:<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`
2. `sourceRef` when `extraData.source === "defender"`
3. `sourceRef` when it looks like a Defender alert reference (prefix `da`)

If no alert or incident ID is found, the case close is skipped for this integration.

### Resolution → classification mapping

| CaseBender resolution | Defender classification         |
| --------------------- | ------------------------------- |
| `TruePositive`        | `truePositive`                  |
| `FalsePositive`       | `falsePositive`                 |
| `Duplicate`           | `informationalExpectedActivity` |
| `NoImpact`            | `informationalExpectedActivity` |
| *(other / none)*      | `truePositive` (default)        |

On close, CaseBender sets the alert/incident `status` to `resolved`, applies the mapped
`classification`, and adds a comment such as:

```
Case <case-id> closed in CaseBender with resolution: <resolution>
```

<Note>
  Outbound alert updates require `closeAlertsOnCaseClose` to be enabled; incident updates
  require `closeIncidentsOnCaseClose`. If neither is enabled, no outbound sync occurs.
</Note>

## 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** — grant only `SecurityAlert.ReadWrite.All` and
  `SecurityIncident.ReadWrite.All`. 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 `sk_def_` 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

<AccordionGroup>
  <Accordion title="Test Connection fails with an OAuth2 error">
    Verify the `tenantId`, `clientId`, and `clientSecret`. Confirm the client secret has not
    expired and that admin consent was granted for the Graph application permissions.
  </Accordion>

  <Accordion title="Ingestion returns 401 Unauthorized">
    The `x-api-key` header is missing or invalid. Confirm you are sending the `sk_def_` key that
    matches the integration's configured webhook API key.
  </Accordion>

  <Accordion title="Ingestion returns 400 'No alerts in payload'">
    The payload had neither a `value[]` array nor a top-level `id`. Send a Graph batch object
    or a single alert object.
  </Accordion>

  <Accordion title="Case close doesn't update Defender">
    Ensure `closeAlertsOnCaseClose` / `closeIncidentsOnCaseClose` is enabled and that the case
    carries a Defender alert/incident ID (via `extraData` or `sourceRef`).
  </Accordion>

  <Accordion title="Outbound update returns 403">
    The Azure AD app lacks write permission. Confirm `SecurityAlert.ReadWrite.All` and
    `SecurityIncident.ReadWrite.All` are granted with admin consent.
  </Accordion>
</AccordionGroup>

## Related documentation

* [Integrations overview](./introduction.mdx)
* [Microsoft Graph Security API](https://learn.microsoft.com/en-us/graph/api/resources/security-api-overview)
* [Microsoft Defender XDR](https://learn.microsoft.com/en-us/defender-xdr/)
