Skip to content

New Defender Plugin#55

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

New Defender Plugin#55
jame2O wants to merge 12 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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we should add links here with relevant config - if the docs are external, rather than a local README.md, we can link to them on docs.squaredup.com.

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.

Comment thread plugins/MicrosoftDefender/v1/metadata.json
Comment thread plugins/MicrosoftDefender/v1/metadata.json Outdated
Comment thread plugins/MicrosoftDefender/v1/metadata.json Outdated
Comment thread plugins/MicrosoftDefender/v1/metadata.json Outdated
Copy link
Copy Markdown
Contributor

@vinbab vinbab left a comment

Choose a reason for hiding this comment

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

@jame2O Alerts data stream

  1. Alerts: Error message when Status is New
Image 2) Alerts: Timeframe column needs to be on the Timeframe tab 3) let's order the columns, mimic the portal, everything else is hidden: - Alert name - Tags - Severity - Integration state - Status - Category - Detection source - Impacted Assets - First activity - Last activity - Policy name - Classification - Determination - Assigned To - Workspace - Cloud Scopes
  1. When I change the time column to Last Update Time, I get this error
Image

Copy link
Copy Markdown
Contributor

@vinbab vinbab left a comment

Choose a reason for hiding this comment

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

@jame2O change the order of the columns to mimic the portal, and hide everything else:

  • Name
  • IP
  • Criticality
  • Device category
  • Device type
  • Domain
  • Device AAD id
  • Risk Level
  • Exposure level
  • OS platform
  • OS version
  • Sensor health state
  • Onboarding status
  • Discovery sources
  • Last device update
  • Tags
  • Device Role
  • Managed by
  • Managed by status
  • Migration status
  • Cloud platforms

Copy link
Copy Markdown
Contributor

@vinbab vinbab left a comment

Choose a reason for hiding this comment

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

@jame2O Incidents data stream.

  1. Move the timeframe column to Timeframe tab.
  2. Timeframe options: like in Alerts, not all data/time fields are in the list. Is that a fixed list you are manually populating?
  3. order the columns like in the portal, hiding all others:
  • Incident name
  • Incident id
  • Priority score
  • Tags
  • Severity
  • Investigation state
  • Categories
  • Impacted assets
  • Active alerts
  • Service sources
  • Detection sources
  • Last update time
  • Last activity
  • Policy name
  • Data sensitivity
  • Status
  • Assigned to
  • Classification
  • Determination
  • Device groups
  • Creation time
  • Workspaces
  • Cloud Scopes

Copy link
Copy Markdown
Contributor

@vinbab vinbab left a comment

Choose a reason for hiding this comment

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

@jame2O Recommendations data stream:

  1. can you add the sourceId to the Device Name
    Order the columns like this (hiding all others):
  • Risk description
  • Device Name
  • Timestamp
  • Configuration Name
  • Configuration Category
  • Configuration Subcategory
  • Configuration Impact
  • Remediation Options
  • Is Applicable
  • Is Compliant

Copy link
Copy Markdown
Contributor

@vinbab vinbab left a comment

Choose a reason for hiding this comment

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

@jame2O Secure Score History

  1. Order the columns like this:
  • Created Date Time
  • Current Score
  • And everything else after
  1. what are the 1 / , 2/ columns?

Copy link
Copy Markdown
Contributor

@vinbab vinbab left a comment

Choose a reason for hiding this comment

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

@jame2O
Order the columns like so:

  • Cve Id
  • Severity
  • CVSS
  • Affected Software
  • Age
  • Published
  • First detected
  • Updated
  • Exposes devices
  • Tags

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 3, 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
  }
}

},
"isClearable": true
},
{
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this field show on the Timeframe tab? You can use tileEditorStep to set this

},
"timeframes": false,
"providesPluginDiagnostics": true,
"tags": []
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Worth adding tags to any datastreams? These allow the user to filter via the UI.

"visibility": {
"type": "hidden"
},
"options": { "noMatch": true }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this needed?

},
"_type": "tile/data-stream",
"description": "",
"monitorOld": {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please remove this redundant config

"scope": "{{scopes.[All Objects]}}"
},
"_type": "tile/data-stream",
"monitorOld": {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As above - please check any other dashboards for this

"PublicIP",
"OSBuild",
"OSArchitecture",
{
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This pattern is typically no longer needed - the column set as the id above, is automatically made available as the rawId.

"name": "validation"
},
"success": "Successfully connected to Endpoint",
"error": "Cannot access the Endpoint API - check your client ID & secret. Do you have the correct permissions set?",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
"error": "Cannot access the Endpoint API - check your client ID & secret. Do you have the correct permissions set?",
"error": "Cannot access the Endpoint API - check your client ID, secret and permissions.",

{
"steps": [
{
"displayName": "API Access Test",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
"displayName": "API Access Test",
"displayName": "API Access",

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
Member

Choose a reason for hiding this comment

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

I think we should add links here with relevant config - if the docs are external, rather than a local README.md, we can link to them on docs.squaredup.com.

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.

3 participants