Skip to content

New Defender Plugin#55

Open
jame2O wants to merge 9 commits into
mainfrom
work/jd/defender
Open

New Defender Plugin#55
jame2O wants to merge 9 commits into
mainfrom
work/jd/defender

Conversation

@jame2O
Copy link
Copy Markdown
Contributor

@jame2O jame2O commented Jun 1, 2026

🔌 Plugin overview

  • Plugin name: Microsoft Defender for Endpoint
  • Purpose / problem solved: Connects to the Microsoft Graph API to access security (defender for endpoint) based data
  • Primary audience (e.g. platform teams, SREs, product teams): IT Administrators / Security Teams & Specialists
  • Authentication method(s) (e.g. OAuth, Username/Password, API Key): OAuth Client Credentials (ID & Secret)

🖼️ Plugin screenshots

Plugin configuration

image image

Default dashboards

image image image image

🧪 Testing


⚠️ Known limitations

We originally developed this plugin for the Microsoft Defender for Endpoint API. However we found that a lot of data streams we wanted to include had been moved to the Security portion of the Microsoft Graph API. Upon weighing up both API's, we decided to go with the latter.

I have tried to clone the dashboards from the old plugin as best as I could. Some data streams (e.g Recommendations, Vulnerabilities), do not have direct endpoints in the graph API so I have fectched similar data using Advanced Hunting. There are of course some missing pieces:

📚 Checklist

  • Plugin, datastream and UI naming follow SquaredUp guidelines
  • Logo added
  • One or more dashboards added
  • README added including configuration guidance
  • No secrets or credentials included
  • I agree to the Code of Conduct

@jame2O jame2O requested review from clarkd and vinbab June 1, 2026 07:18
@jame2O jame2O added enhancement New feature or request new-plugin Used to PR newly added plugins labels Jun 1, 2026
@jame2O jame2O marked this pull request as ready for review June 1, 2026 15:00
@jame2O jame2O requested a review from a team June 1, 2026 15:01
Comment on lines +1 to +8
{
"name": "microsoft-defender",
"displayName": "Microsoft Defender for Endpoint",
"version": "1.0.0",
"author": {
"name": "SquaredUp Labs",
"type": "community"
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Missing docs/README.md and links array in metadata.json. The PR checklist explicitly leaves '[ ] README added including configuration guidance' unchecked, and the README is shown in-product during plugin configuration — without it users have no setup guidance for the OAuth Client Credentials flow (Entra app registration, required Graph API permissions like SecurityAlert.Read.All, ThreatHunting.Read.All, etc.). Also, per repo guidelines, metadata.json should typically include a links array with a source link to the GitHub repo and a documentation link to the README — all other plugins (Phare, UniFi, Rootly, DigiCert, GoogleSheets, UptimeRobot, …) follow this convention.

Extended reasoning...

What is missing

This new community plugin for Microsoft Defender for Endpoint ships without two pieces of metadata that the repo guidelines require:

  1. No docs/README.md — the plugins/MicrosoftDefender/v1/ tree has no docs/ folder at all. The PR description itself confirms this in the checklist:

    - [ ] README added including configuration guidance
    
  2. No links array in metadata.json — the new metadata.json has name, displayName, description, base, etc., but no top-level links field. The repository review guidelines say:

    links — Should typically contain two links, one with category: source linking to the GitHub repository, and another with category: documentation linking to the markdown documentation in the repository.

Why this matters

The README is rendered in-product during plugin configuration, so its absence is a real UX gap for first-time users — not just a docs nicety. The Microsoft Defender plugin uses OAuth 2.0 Client Credentials against the Microsoft Graph API (see metadata.json lines 13–41), which means the user has to perform a non-trivial setup before the plugin will work:

  • Register an application in Microsoft Entra ID (Azure AD).
  • Generate a client secret.
  • Grant application (not delegated) Graph API permissions — at minimum the ones implied by the data streams in this PR:
    • SecurityAlert.Read.All (for alerts.json)
    • SecurityIncident.Read.All (for incidents.json)
    • ThreatHunting.Read.All (for runHuntingQuery used by advancedHuntingQuery, Vulnerabilities, devices, recommendations, listDevices)
    • SecurityEvents.Read.All / SecureScore.Read.All (for secureScoreHistory.json)
  • Grant admin consent on those permissions.
  • Note the tenant ID, client ID, and client secret to enter into ui.json's three required fields.

Without a README, none of this is discoverable from inside SquaredUp; the validation step (configValidation.json) will simply return "Cannot access the Endpoint API - check your client ID & secret. Do you have the correct permissions set?" with no further guidance.

Step-by-step proof

  1. ls plugins/MicrosoftDefender/v1/ → no docs/ directory; no README.md anywhere in the plugin.
  2. jq '.links' plugins/MicrosoftDefender/v1/metadata.jsonnull (the key does not exist).
  3. Compare to e.g. plugins/Phare/v1/metadata.json, which has "links": [ { "category": "source", "url": "…" }, { "category": "documentation", "url": "…" } ]. The same is true of UniFi, Rootly, DigiCert, GoogleSheets, UptimeRobot, AutoTask, DattoRMM, FantasyPremierLeague, Postcoder — i.e. essentially every other plugin in this repo.
  4. The PR description checklist explicitly leaves the README item unchecked, confirming the author is aware this is outstanding work.

How to fix

  • Add plugins/MicrosoftDefender/v1/docs/README.md with at least: overview, prerequisites (Entra app registration walkthrough), required Graph API application permissions, how to fill in the three UI fields (tenant ID, client ID, client secret), and a short description of each dashboard/data stream.

  • Add a links array to metadata.json, e.g.:

    "links": [
        { "category": "source", "label": "GitHub", "url": "https://github.com/squaredup/community-plugins/tree/main/plugins/MicrosoftDefender/v1" },
        { "category": "documentation", "label": "Documentation", "url": "https://github.com/squaredup/community-plugins/blob/main/plugins/MicrosoftDefender/v1/docs/README.md" }
    ]
  • Tick the README checkbox in the PR description once added.

Comment on lines +11 to +13
"name": "DeviceName",
"type": {
"value": "Defender Device"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 The sourceType is set to "Defender Device" in indexDefinitions/default.json and custom_types.json, which prefixes it with the plugin name. Per the repository naming guidelines, source types should be named after the upstream product/API (e.g. device or machine — matching Microsoft Graph / Defender terminology) — the guideline literally calls out NinjaOne Device as the anti-pattern to avoid, and Defender Device is structurally identical. Move the friendly display name Defender Device into custom_types.json's name field and use the upstream term for sourceType; this also requires updating defaultContent/scopes.json and the matches clauses in devices.json, recommendations.json, and Vulnerabilities.json (plus the gremlin query in cockpit.dash.json and recommendations.dash.json).

Extended reasoning...

What the bug is

The SquaredUp plugin naming guidelines explicitly state:

Name source types after how they are referred to in the upstream product or API (e.g. agent, device). Do not prefix them with the plugin name (e.g. avoid NinjaOne Device). A separate friendly display name can be configured if needed (via custom_types.json).

This PR sets the sourceType to "Defender Device" in two places — exactly the <plugin-name> Device anti-pattern the guideline calls out.

Where it manifests in the code

  1. plugins/MicrosoftDefender/v1/indexDefinitions/default.json (L11–13) — objectMapping.type.value is set to "Defender Device", which stamps every imported device with that sourceType.
  2. plugins/MicrosoftDefender/v1/custom_types.json — both name and sourceType are "Defender Device", whereas name should be the friendly display label and sourceType should be the API-native key.

The mismatch propagates to several other files that filter on the type:

  • defaultContent/scopes.json — the "Defender Devices" scope matches sourceType oneOf: ["Defender Device"].
  • dataStreams/devices.json, dataStreams/recommendations.json, dataStreams/Vulnerabilities.json — each has a matches clause keyed off sourceType == "Defender Device".
  • defaultContent/cockpit.dash.json and defaultContent/recommendations.dash.json — the gremlin scope queries contain __.has("sourceType", "Defender Device").

Why existing code doesn't prevent it

sourceType is a free-form string identifier — there's no schema-level validator that rejects plugin-name prefixes. The validation step in configValidation.json only checks API connectivity, not naming conventions. Nothing in the harness catches this; it's purely a convention defined in the contributor guidelines.

Impact

The string "Defender Device" becomes part of the durable identity of every imported object. Once a customer installs this plugin and ingests devices, that sourceType is baked into the data graph, dashboards, scopes, and any user-built content. Changing it after release is a breaking change that orphans existing scopes, breaks user dashboards filtered by that sourceType, and forces a re-import of every device. Fixing it now — before release — is virtually free; fixing it later is painful or impractical, which is why this convention is enforced pre-merge.

How to fix

Use the upstream Microsoft Graph / Defender term — device (or machine, depending on which API surface is canonical for the contributor) — as the sourceType, and keep Defender Device as the friendly display name only in custom_types.json:

[
    {
        "name": "Defender Device",
        "sourceType": "device",
        "icon": "server",
        "singular": "Device",
        "plural": "Devices"
    }
]

Then update every other reference to the same key:

  • indexDefinitions/default.json: type.value"device"
  • defaultContent/scopes.json: oneOf: ["device"]
  • dataStreams/devices.json, recommendations.json, Vulnerabilities.json: matches.sourceType.value"device"
  • dataStreams/devices.json and Vulnerabilities.json metadata: sourceType"device"
  • cockpit.dash.json and recommendations.dash.json: __.has("sourceType", "device")

Step-by-step proof

  1. Open plugins/MicrosoftDefender/v1/indexDefinitions/default.json. Line 11–13:
    "type": {
        "value": "Defender Device"
    }
    Every device imported gets sourceType = "Defender Device".
  2. Open plugins/MicrosoftDefender/v1/custom_types.json. The sourceType field is also "Defender Device" — so the type registration matches what the index writes. Both halves carry the plugin-prefixed string.
  3. Compare with the guideline example: the guideline says "avoid NinjaOne Device". Substitute plugin name → Defender Device is the same anti-pattern, byte for byte by shape.
  4. The Microsoft Graph / Defender API itself refers to these as device (e.g. /security/devices, the DeviceInfo / DeviceId columns in advanced hunting) and machine in the legacy Defender for Endpoint API. Either of those — not Defender Device — is the upstream-native term the guideline asks for.
  5. The friendly display name Defender Device is exactly what custom_types.json's name, singular, plural fields are designed to surface — so renaming sourceType to device keeps the UI label identical for end users.

"name": "SquaredUp Labs",
"type": "community"
},
"description": "Visualize Defender insights including advanced hunting, exposure, devices, and vulnerabilities.",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The category field in metadata.json is set to "User Defined", the generic fallback shown when no real category is selected. Repository guidelines state: "category - Mandatory. Reuse an existing category from other plugins where possible." Since this is a Microsoft Defender for Endpoint (EDR) plugin, "Security" would be a much more natural fit — and it is already an established category used by the Phare plugin.

Extended reasoning...

What the bug is\n\nIn plugins/MicrosoftDefender/v1/metadata.json line 9, the category field is set to:\n\njson\n"category": "User Defined"\n\n\n"User Defined" is effectively the no-category fallback — the placeholder shown when an author has not selected a real category. For a plugin whose entire purpose is to surface security/EDR data (alerts, incidents, vulnerabilities, recommendations, secure score, antivirus detections, exploit guard events), this is a missed categorization.\n\n### Why it matters\n\nThe repository's plugin-authoring guidelines explicitly state:\n\n> category – Mandatory. Reuse an existing category from other plugins where possible.\n\nA quick sweep of the existing plugins shows the categories already in use across plugins/*/v*/metadata.json include Security, Monitoring, Utility, Network, Database, Fun, etc. Security is already established (used by Phare) and is the obvious fit for a Microsoft Defender for Endpoint plugin. Only one other plugin in the repo currently uses "User Defined", so this is genuinely an outlier.\n\n### Impact\n\nThe plugin still functions correctly — this is not a runtime bug. The impact is on discoverability and consistency: when users browse plugins in-product by category, a Defender plugin filed under "User Defined" will not show up alongside other security tooling, and the catalog ends up with the generic fallback category accumulating mismatched plugins.\n\n### How to fix\n\nChange line 9 of plugins/MicrosoftDefender/v1/metadata.json from:\n\njson\n"category": "User Defined",\n\n\nto:\n\njson\n"category": "Security",\n\n\n("Monitoring" would also be defensible given the dashboards and secure-score-history aspects, but "Security" is the more precise fit for an EDR/Defender plugin.)\n\n### Step-by-step proof\n\n1. Open plugins/MicrosoftDefender/v1/metadata.json — line 9 reads "category": "User Defined".\n2. The repo's contribution guidelines require reusing an existing category.\n3. grep across plugins/*/v*/metadata.json confirms "Security" is already an established category (used by Phare), and "Monitoring" is used by multiple plugins.\n4. The plugin's PR description states its audience is "IT Administrators / Security Teams & Specialists" and its purpose is "access security (defender for endpoint) based data" — confirming "Security" is the natural category.\n5. Therefore the current value violates the guideline by selecting the generic fallback when a fitting existing category exists.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

🧩 Plugin PR Summary

📦 Modified Plugins

  • plugins/MicrosoftDefender/v1

📋 Results

Step Status
Validation ✅ Passed
Deployment 🚀 Deployed

🔍 Validation Details

microsoft-defender
{
  "valid": true,
  "pluginName": "microsoft-defender",
  "pluginType": "hybrid",
  "summary": {
    "Data Streams": 9,
    "Import Definitions": 1,
    "UI Configuration": true,
    "Has Icon": true,
    "Has Default Content": true,
    "Config Validation": true,
    "Custom Types": true
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request new-plugin Used to PR newly added plugins

Development

Successfully merging this pull request may close these issues.

1 participant