↳ GitHub sourceAnalytics ruleMedium

Synqly Alert Event

Description

Creates one Microsoft Sentinel alert for each qualifying Synqly-attributed ASIM Alert Event row. Native ASIM values take precedence, with retained OCSF data used to recover investigation context when normalized fields are empty. Replayed source rows can create additional alerts, and Microsoft Sentinel or Defender XDR may correlate related alerts into a shared incident.
Rule type
Scheduled
Version
1.0.0
Declared status
Available
Query frequency
5m
Query period
5m
Trigger
gt 0

Declared sources

Metadata from the source file. No dependencies inferred from KQL.

Connectors

Data types

KQL query

Original query, unchanged.

ASimAlertEventLogs
| where tostring(AdditionalFields._ConnectorId) == "SynqlySentinelASIMConnector"
| extend OCSF = coalesce(todynamic(AdditionalFields.OCSF), dynamic({}))
| mv-apply Evidence = array_concat(coalesce(todynamic(OCSF.evidences), dynamic([])), dynamic([{}])) on (
    mv-apply EvidenceHash = array_concat(coalesce(todynamic(Evidence.data.file.hashes), dynamic([])), coalesce(todynamic(Evidence.data.process.hashes), dynamic([])), dynamic([{}])) on (
        summarize
            OcsfSHA256 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == "SHA256"),
            OcsfSHA1 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == "SHA1"),
            OcsfMD5 = take_anyif(tostring(EvidenceHash.value), toupper(tostring(EvidenceHash.algorithm)) == "MD5")
    )
    | summarize
        OcsfUserName = take_anyif(tostring(Evidence.data.process.user.name), isnotempty(tostring(Evidence.data.process.user.name))),
        OcsfUserSid = take_anyif(tostring(Evidence.data.process.user.uid), isnotempty(tostring(Evidence.data.process.user.uid))),
        OcsfUserDomain = take_anyif(tostring(Evidence.data.process.user.domain), isnotempty(tostring(Evidence.data.process.user.domain))),
        OcsfProcessId = take_anyif(tostring(Evidence.data.process.pid), isnotempty(tostring(Evidence.data.process.pid))),
        OcsfProcessCommandLine = take_anyif(tostring(Evidence.data.process.cmd_line), isnotempty(tostring(Evidence.data.process.cmd_line))),
        OcsfFileName = take_anyif(tostring(Evidence.data.file.name), isnotempty(tostring(Evidence.data.file.name))),
        OcsfFilePath = take_anyif(tostring(Evidence.data.file.path), isnotempty(tostring(Evidence.data.file.path))),
        OcsfSourceHost = take_anyif(tostring(Evidence.data.network.src_endpoint.host.hostname), isnotempty(tostring(Evidence.data.network.src_endpoint.host.hostname))),
        OcsfSourceIp = take_anyif(tostring(Evidence.data.network.src_endpoint.ip), isnotempty(tostring(Evidence.data.network.src_endpoint.ip))),
        OcsfDestinationIp = take_anyif(tostring(Evidence.data.network.dst_endpoint.ip), isnotempty(tostring(Evidence.data.network.dst_endpoint.ip))),
        OcsfDestinationDomain = take_anyif(tostring(Evidence.data.network.dst_endpoint.domain), isnotempty(tostring(Evidence.data.network.dst_endpoint.domain))),
        OcsfSHA256 = take_anyif(OcsfSHA256, isnotempty(OcsfSHA256)),
        OcsfSHA1 = take_anyif(OcsfSHA1, isnotempty(OcsfSHA1)),
        OcsfMD5 = take_anyif(OcsfMD5, isnotempty(OcsfMD5))
)
| mv-apply Attack = array_concat(coalesce(todynamic(OCSF.attacks), dynamic([])), dynamic([{}])) on (
    extend TacticId = tostring(Attack.tactic.uid), TechniqueId = tostring(Attack.technique.uid), SubTechniqueId = tostring(Attack.sub_technique.uid)
    | extend TacticName = case(
        TacticId == "TA0043", "Reconnaissance", TacticId == "TA0042", "ResourceDevelopment",
        TacticId == "TA0001", "InitialAccess", TacticId == "TA0002", "Execution",
        TacticId == "TA0003", "Persistence", TacticId == "TA0004", "PrivilegeEscalation",
        TacticId == "TA0005", "DefenseEvasion", TacticId == "TA0006", "CredentialAccess",
        TacticId == "TA0007", "Discovery", TacticId == "TA0008", "LateralMovement",
        TacticId == "TA0009", "Collection", TacticId == "TA0010", "Exfiltration",
        TacticId == "TA0011", "CommandAndControl", TacticId == "TA0040", "Impact", "")
    | summarize
        OcsfTactics = make_set_if(TacticName, isnotempty(TacticName), 14),
        OcsfTechniques = make_set_if(TechniqueId, isnotempty(TechniqueId), 50),
        OcsfSubTechniques = make_set_if(SubTechniqueId, isnotempty(SubTechniqueId), 50)
)
| extend
    ActorUser = todynamic(OCSF.actor.process.user),
    Malware = todynamic(OCSF.malware[0]),
    NativeConfidenceValue = toreal(ThreatConfidence),
    OcsfConfidenceValue = toreal(OCSF.confidence_score),
    NativeConfidenceLabel = tolower(trim(" ", tostring(ThreatOriginalConfidence))),
    OcsfConfidenceLabel = tolower(trim(" ", tostring(OCSF.confidence))),
    NativeSourceId = coalesce(tostring(EventUid), tostring(AlertId), tostring(EventOriginalUid)),
    OcsfSourceId = coalesce(tostring(OCSF.finding_info.uid), tostring(OCSF.uid)),
    SourceIdMaterial = tostring(pack_array(tostring(OCSF.class_uid), tostring(OCSF.type_uid), tostring(OCSF.activity_id), tostring(EventStartTime), tostring(EventEndTime), tostring(EventVendor), tostring(EventProduct), tostring(EventMessage), tostring(OCSF["time"])))
| extend
    SynqlyAlertId = coalesce(NativeSourceId, OcsfSourceId, strcat("generated-", hash_sha256(SourceIdMaterial))),
    SynqlyAlertName = coalesce(tostring(AlertName), tostring(OCSF.finding_info["title"]), tostring(OCSF.class_name), "Synqly normalized alert event"),
    SynqlyAlertDescription = coalesce(tostring(EventMessage), tostring(AlertDescription), tostring(OCSF.message), "Synqly normalized alert event"),
    SeverityValue = tolower(coalesce(tostring(EventSeverity), tostring(EventOriginalSeverity), tostring(OCSF.severity))),
    SynqlyTactics = coalesce(tostring(AttackTactics), strcat_array(OcsfTactics, ",")),
    AllAttackTechniques = coalesce(tostring(AttackTechniques), strcat_array(array_concat(OcsfTechniques, OcsfSubTechniques), ",")),
    SynqlyConfidenceScore = coalesce(
        iff(NativeConfidenceValue between (0.0 .. 100.0), NativeConfidenceValue / 100.0, real(null)),
        iff(OcsfConfidenceValue between (0.0 .. 100.0), OcsfConfidenceValue / 100.0, real(null))),
    SynqlyConfidenceLevel = case(
        NativeConfidenceLabel == "high", "High",
        NativeConfidenceLabel == "low", "Low",
        NativeConfidenceLabel == "unknown", "Unknown",
        isnotempty(NativeConfidenceLabel), "",
        OcsfConfidenceLabel == "high", "High",
        OcsfConfidenceLabel == "low", "Low",
        OcsfConfidenceLabel == "unknown", "Unknown",
        ""),
    SynqlyRemediation = coalesce(tostring(AttackRemediationSteps), strcat_array(todynamic(OCSF.remediation.kb_articles), "; ")),
    SynqlyAlertLink = coalesce(tostring(EventReportUrl), tostring(OCSF.finding_info.src_url)),
    SourceVendor = coalesce(tostring(EventVendor), tostring(OCSF.metadata.product.vendor_name)),
    SourceProduct = coalesce(tostring(EventProduct), tostring(OCSF.metadata.product.name)),
    ProductVersion = coalesce(tostring(EventProductVersion), tostring(OCSF.metadata.product.version)),
    OriginalSeverity = coalesce(tostring(EventOriginalSeverity), tostring(OCSF.severity)),
    SourceRuleId = coalesce(
        tostring(Rule),
        tostring(OCSF.finding_info.analytic.uid),
        iff(isnotempty(tostring(RuleName)) and not(tostring(RuleName) matches regex @"^\d+$"), tostring(RuleName), "")),
    AccountName = coalesce(tostring(Username), tostring(User), tostring(ActorUser.name), OcsfUserName),
    AccountSid = coalesce(iff(tolower(tostring(UserIdType)) contains "sid", tostring(UserId), ""), tostring(ActorUser.uid), OcsfUserSid),
    AccountDomain = coalesce(tostring(UserScope), tostring(ActorUser.domain), OcsfUserDomain),
    NativeDeviceHost = coalesce(tostring(DvcHostname), tostring(DvcFQDN)),
    NativeDeviceHostDomain = coalesce(tostring(DvcDomain), extract(@"^[^.]+\.(.+)$", 1, tostring(DvcFQDN)), extract(@"^[^.]+\.(.+)$", 1, tostring(DvcHostname))),
    DeviceIp = tostring(DvcIpAddr),
    DestinationDomain = OcsfDestinationDomain,
    EntityUrl = coalesce(tostring(Url), tostring(OCSF.finding_info.src_url)),
    FileValue = coalesce(tostring(FilePath), OcsfFilePath),
    ProcessIdValue = coalesce(tostring(ProcessId), OcsfProcessId),
    ProcessCommandLineValue = coalesce(tostring(ProcessCommandLine), OcsfProcessCommandLine),
    MalwareName = coalesce(tostring(ThreatName), tostring(Malware.name)),
    FileHashValue = coalesce(tostring(FileSHA256), OcsfSHA256, tostring(FileSHA1), OcsfSHA1, tostring(FileMD5), OcsfMD5)
| extend
    SourceIp = OcsfSourceIp,
    DestinationIp = OcsfDestinationIp,
    HostValue = coalesce(NativeDeviceHost, OcsfSourceHost),
    HostDomainValue = iff(isnotempty(NativeDeviceHost), NativeDeviceHostDomain, extract(@"^[^.]+\.(.+)$", 1, OcsfSourceHost))
| extend
    PrimaryIp = coalesce(SourceIp, DestinationIp, tostring(DvcIpAddr)),
    PrimaryIpRole = case(isnotempty(SourceIp), "Source", isnotempty(DestinationIp), "Destination", isnotempty(tostring(DvcIpAddr)), "Device", "")
| extend
    SynqlyAlertSeverity = case(
        SeverityValue == "informational", "Informational",
        SeverityValue == "low", "Low",
        SeverityValue == "medium", "Medium",
        SeverityValue in ("high", "critical", "fatal"), "High",
        "Medium"),
    HostNameValue = coalesce(extract(@"^([^.]+)", 1, HostValue), HostValue),
    FileHashAlgorithm = case(
        isnotempty(FileSHA256) or isnotempty(OcsfSHA256), "SHA256",
        isnotempty(FileSHA1) or isnotempty(OcsfSHA1), "SHA1",
        isnotempty(FileMD5) or isnotempty(OcsfMD5), "MD5",
        "")
| project
    TimeGenerated,
    SynqlyAlertId,
    SynqlyAlertName,
    SynqlyAlertDescription,
    SynqlyAlertSeverity,
    SynqlyTactics,
    AllAttackTechniques,
    SynqlyConfidenceLevel,
    SynqlyConfidenceScore,
    SynqlyRemediation,
    SynqlyAlertLink,
    SourceVendor,
    SourceProduct,
    ProductVersion,
    OriginalSeverity,
    SourceRuleId,
    AccountName,
    AccountSid,
    AccountDomain,
    HostNameValue,
    HostDomainValue,
    SourceIp,
    DestinationIp,
    DeviceIp,
    PrimaryIp,
    PrimaryIpRole,
    DestinationDomain,
    EntityUrl,
    FileValue,
    FileHashAlgorithm,
    FileHashValue,
    ProcessIdValue,
    ProcessCommandLineValue,
    MalwareName

Declared entities

AccountHostIPFileHashProcess

Related content

Links established from declared identifiers and solution manifests.

Source provenance

GitHub

Displayed values come from files in Azure/Azure-Sentinel. They describe the published template, not your workspace configuration.

Source identifier
3192085a-e97e-440f-acb7-9227622949a4
Additional source files 2Solutions/SynqlyIntegrationConnector/Analytic Rules/SynqlyAlertEventToSentinelAlert.yamlsource ↗Solutions/SynqlyIntegrationConnector/Data/Solution_Synqly.jsonsolution-membership ↗
GSTEP / CATALOG TRACKING

Added to catalog : 16 Sept 2026 · 05:49 UTC
Last change observed : 16 Sept 2026 · 05:49 UTC

GSTEP sync dates, separate from the source content’s publication dates.