Best way to load JSON "filters" from an external source and apply to node output

Describe the problem/error/question

I am processing Microsoft Defender alerts from the https://graph.microsoft.com/v1.0/security/alerts_v2 endpoint. This API returns many different types of alerts with widely varying formats/fields/etc.

I have a parent workflow that retrieves all of the alerts and then uses a switch node to call a specific sub-workflow for each distinct “detection type” (a combination of these two fields):

    "productName": "Microsoft Defender for Cloud",
    "detectorId": "SQL.VM_HarmfulApplication",

Within each sub-workflow, I want to apply detection-specific filtering/tuning rules, e.g… block if this pseudocode matches (and if it does not match, move to the next rule):

additionalData."Client application": "Nessus" and additionalData."Client IP address" == "10.YYX.11.146"

As these tuning rules will be frequently updated and be managed by security analysts, not n8n developers, I do not want to keep these filters in the workflow code. I am anticipating keeping these “filters” in a redis database under a key like:

graph.microsoft.com/v1.0/security/alerts_v2:Microsoft Defender for Cloud:SQL.VM_HarmfulApplication

My question is, what options do I have for these loaded-at-runtime filters?

  • I know the $jmespath() function is available in the Function node. While JMESPath is very flexible, it still lacks some basic search-style capabilities (e.g. a like operator)
  • I have not seen any way to run “dynamic” Javascript, and even if there is a way, I’m not sure that is very user-friendly option

For context, if you are familiar with Splunk ES, I want to come as close as possible to replicating its suppression feature.

Are there other options/approaches I should consider?

Sample data from Microsoft Graph API

  {
    "id": "dc20deccc5-ec73-7fbb-85e8-37b2cc164754",
    "providerAlertId": "20deccc5-ec73-7fbb-85e8-37b2cc164754",
    "incidentId": "101947",
    "status": "new",
    "severity": "medium",
    "classification": null,
    "determination": null,
    "serviceSource": "unknownFutureValue",
    "detectionSource": "unknownFutureValue",
    "productName": "Microsoft Defender for Cloud",
    "detectorId": "SQL.VM_HarmfulApplication",
    "tenantId": "YYYYYYX-0c7f-4201-95da-06953b7d506f",
    "title": "Logon activity from a potentially harmful application",
    "description": "Failed logon attempt from a potentially harmful application was detected.",
    "recommendedActions": "1. To determine whether this was an operation performed by a legitimate application, contact the owner of the database.\r\n2. If the activity is legitimate, dismiss this alert. Otherwise, treat this as a database breach attempt and continue with the steps below.\r\n3. Block the IP address provided in the 'client IP address' field in the firewall / NSG. In addition, it is suggested to lock down the access to the database as tightly as possible and only allow access from known IP addresses / network segments to the database port.\r\n4. Protect all accounts that have access to the database using highly complex passwords, and avoid using built-in or known user accounts (as 'SA').\r\n5. Using audit Logs (if enabled) and Defender for Cloud alerts, review all activities performed from the same IP address and investigate suspicious activities / alerts.",
    "category": "InitialAccess",
    "assignedTo": null,
    "alertWebUrl": "https://security.microsoft.com/alerts/dc20deccc5-ec73-7fbb-85e8-37b2cc164754?tid=YYYYYYX-0c7f-4201-95da-06953b7d506f",
    "incidentWebUrl": "https://security.microsoft.com/incidents/101947?tid=YYYYYYX-0c7f-4201-95da-06953b7d506f",
    "actorDisplayName": null,
    "threatDisplayName": null,
    "threatFamilyName": null,
    "mitreTechniques": [],
    "createdDateTime": "2025-03-08T20:05:33.4333333Z",
    "lastUpdateDateTime": "2025-03-08T20:05:34.63Z",
    "resolvedDateTime": null,
    "firstActivityDateTime": "2025-03-08T20:04:43Z",
    "lastActivityDateTime": "2025-03-08T20:04:43Z",
    "systemTags": [],
    "alertPolicyId": null,
    "comments": [],
    "evidence": [
      {
        "@odata.type": "#microsoft.graph.security.deviceEvidence",
        "createdDateTime": "2025-03-08T20:05:33.55Z",
        "verdict": "suspicious",
        "remediationStatus": "none",
        "remediationStatusDetails": null,
        "roles": [],
        "detailedRoles": [],
        "tags": [],
        "firstSeenDateTime": "2025-02-20T21:01:18.5971601Z",
        "mdeDeviceId": "06bbae23dc1255af02db223f11241e5c5e9825d2",
        "azureAdDeviceId": null,
        "deviceDnsName": "XAPID1DBD1",
        "hostName": "Xapid1dbd1",
        "ntDomain": null,
        "dnsDomain": null,
        "osPlatform": "WindowsServer2022",
        "osBuild": 20348,
        "version": "21H2",
        "healthStatus": "active",
        "riskScore": "medium",
        "rbacGroupId": 587,
        "rbacGroupName": "UnassignedGroup",
        "onboardingStatus": "canBeOnboarded",
        "defenderAvStatus": "unknown",
        "lastIpAddress": "10.YYX.159.10",
        "lastExternalIpAddress": null,
        "ipInterfaces": [
          "10.YYX.11.146"
        ],
        "vmMetadata": {
          "vmId": "34c27854-0042-4360-YYYY-2ee79862c7fb",
          "cloudProvider": "azure",
          "resourceId": "/subscriptions/YYYYYYX-cda4-4500-9384-35745fac9351/resourceGroups/YYYYYYX-APID1-SHD-DEV1/providers/Microsoft.Compute/virtualMachines/Xapid1dbd1",
          "subscriptionId": null
        },
        "loggedOnUsers": []
      },
      {
        "@odata.type": "#microsoft.graph.security.ipEvidence",
        "createdDateTime": "2025-03-08T20:05:33.55Z",
        "verdict": "suspicious",
        "remediationStatus": "none",
        "remediationStatusDetails": null,
        "roles": [],
        "detailedRoles": [],
        "tags": [],
        "ipAddress": "10.YYX.11.146",
        "countryLetterCode": null,
        "stream": null,
        "location": {
          "countryName": "",
          "state": null,
          "city": "",
          "longitude": null,
          "latitude": null
        }
      },
      {
        "@odata.type": "#microsoft.graph.security.networkConnectionEvidence",
        "createdDateTime": "2025-03-08T20:05:33.55Z",
        "verdict": "suspicious",
        "remediationStatus": "none",
        "remediationStatusDetails": null,
        "roles": [],
        "detailedRoles": [],
        "tags": [],
        "sourcePort": 0,
        "destinationPort": 0,
        "protocol": "unknown",
        "destinationAddress": null,
        "sourceAddress": {
          "createdDateTime": "0001-01-01T00:00:00Z",
          "verdict": "unknown",
          "remediationStatus": "none",
          "remediationStatusDetails": null,
          "roles": [],
          "detailedRoles": [],
          "tags": [],
          "ipAddress": "10.YYX.11.146",
          "countryLetterCode": null,
          "stream": null,
          "location": {
            "countryName": "",
            "state": null,
            "city": "",
            "longitude": null,
            "latitude": null
          }
        }
      },
      {
        "@odata.type": "#microsoft.graph.security.userEvidence",
        "createdDateTime": "2025-03-08T20:05:33.55Z",
        "verdict": "suspicious",
        "remediationStatus": "none",
        "remediationStatusDetails": null,
        "roles": [],
        "detailedRoles": [],
        "tags": [],
        "stream": null,
        "userAccount": {
          "accountName": "NT AUTHORITY\\ANONYMOUS LOGON",
          "domainName": null,
          "userSid": null,
          "azureAdUserId": null,
          "userPrincipalName": null,
          "displayName": null
        }
      },
      {
        "@odata.type": "#microsoft.graph.security.azureResourceEvidence",
        "createdDateTime": "2025-03-08T20:05:33.55Z",
        "verdict": "suspicious",
        "remediationStatus": "none",
        "remediationStatusDetails": null,
        "roles": [],
        "detailedRoles": [],
        "tags": [],
        "resourceType": "Virtual Machine",
        "resourceName": "Xapid1dbd1",
        "resourceId": "/subscriptions/YYYYYYX-cda4-4500-9384-35745fac9351/resourcegroups/YYYYYYX-apid1-shd-dev1/providers/microsoft.compute/virtualmachines/Xapid1dbd1"
      }
    ],
    "additionalData": {
      "OriginalAlertProductName": "SQLThreatDetection",
      "OriginalAlertProviderName": "Microsoft Defender for Cloud",
      "AlertUri": "https://portal.azure.com/#blade/Microsoft_Azure_Security_AzureDefenderForData/AlertBlade/alertId/20deccc5-ec73-7fbb-85e8-37b2cc164754/subscriptionId/YYYYYYX-cda4-4500-9384-35745fac9351/resourceGroup/YYYYYYX-apid1-shd-dev1/referencedFrom/alertDeepLink/location/centralus",
      "TimeGenerated": "2025-03-08T20:05:12.143Z",
      "ProcessingEndTime": "2025-03-08T20:05:11.8179118Z",
      "[email protected]": "#Int64",
      "Intent": 1,
      "Compromised entity": "{DEFAULT_DATABASE}",
      "Client IP address": "10.YYX.11.146",
      "Client IP location": "Location couldn't be inferred from the IP address. This could happen if the IP address is private, behind a VNET, NAT or VPN gateway.",
      "Client principal name": "NT AUTHORITY\\ANONYMOUS LOGON",
      "Client application": "Nessus",
      "SQL server name": "XAPID1DBD1",
      "SQL instance name": "MSSQLSERVER",
      "Potential causes": "Penetration testing; malicious activity",
      "Investigation steps": "{\"displayValue\":\"How to investigate this alert using logs at your Log Analytics workspace.\",\"kind\":\"Link\",\"value\":\"https:\\/\\/go.microsoft.com\\/fwlink\\/?linkid=2091064\"}",
      "WorkspaceSubscriptionId": "YYYYYYX-cda4-4500-9384-35745fac9351",
      "EffectiveAzureResourceId": "/subscriptions/YYYYYYX-cda4-4500-9384-35745fac9351/resourcegroups/YYYYYYX-apid1-shd-dev1/providers/microsoft.compute/virtualmachines/Xapid1dbd1",
      "CompromisedEntity": "XAPID1DBD1",
      "ProductComponentName": "Databases",
      "EffectiveSubscriptionId": "YYYYYYX-cda4-4500-9384-35745fac9351",
      "InvestigationState": 8192
    }
  }

I will correct my underestimation of JMESPath. The following filter does work on the non-array object (line breaks added for clarity).

[additionalData][?
("Client IP address"==`10.YYX.11.146`||"Client IP address"==`10.YYX.11.46`) 
&& "Client application"==`Nessus`
]

you can evaluate expressions just like in Splunk

Yes, I understand that the expressions could be written directly in n8n. My goal is give people (security analysts) who are not n8n developers the ability to edit the rules (they won’t have access to the workflow).

I think I’ve worked out a basic scheme for this with Redis-hosted regexes

that are then used by an ordinary If node

This approach is tolerable but I’m open to more elegant ideas (that maintain this separation between rule storage and rule application)

I mean to evaluate a filter from some config (check the node ‘set rules’ for example). The rules will be evaluated in the node ‘evaluate filter’

1 Like

Thank you. Your workflow has a lot of nice (new to me) techniques in it - not just for the filtering (merging two streams, setting JSON in code rather than having to use a SET node). I appreciate your time and suggestions.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.