Merge pull request #4595 from sivanguetta/users/sivang/parsersKqlValidator

Add Kql validation tests for normalization parsers functions
This commit is contained in:
Amit Bergman 2022-04-11 13:19:13 +03:00 коммит произвёл GitHub
Родитель 1fd9ae3c2b a94a9a24c9
Коммит 786d7c7e5c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
18 изменённых файлов: 2795 добавлений и 163 удалений

Просмотреть файл

@ -0,0 +1,293 @@
{
"Name": "ASimDnsActivityLogs",
"Properties": [
{
"Name": "TenantId",
"Type": "String"
},
{
"Name": "TimeGenerated",
"Type": "DateTime"
},
{
"Name": "EventCount",
"Type": "Int32"
},
{
"Name": "EventType",
"Type": "String"
},
{
"Name": "EventSubType",
"Type": "String"
},
{
"Name": "EventResult",
"Type": "String"
},
{
"Name": "EventResultDetails",
"Type": "String"
},
{
"Name": "EventOriginalType",
"Type": "String"
},
{
"Name": "EventProduct",
"Type": "String"
},
{
"Name": "EventVendor",
"Type": "String"
},
{
"Name": "DvcIpAddr",
"Type": "String"
},
{
"Name": "DvcHostname",
"Type": "String"
},
{
"Name": "DvcDomain",
"Type": "String"
},
{
"Name": "DvcDomainType",
"Type": "String"
},
{
"Name": "DvcOs",
"Type": "String"
},
{
"Name": "DvcOsVersion",
"Type": "String"
},
{
"Name": "AdditionalFields",
"Type": "Object"
},
{
"Name": "SrcIpAddr",
"Type": "String"
},
{
"Name": "SrcPortNumber",
"Type": "Int32"
},
{
"Name": "DstIpAddr",
"Type": "String"
},
{
"Name": "DnsQuery",
"Type": "String"
},
{
"Name": "DnsQueryType",
"Type": "Int32"
},
{
"Name": "DnsQueryTypeName",
"Type": "String"
},
{
"Name": "DnsResponseCode",
"Type": "Int32"
},
{
"Name": "TransactionIdHex",
"Type": "String"
},
{
"Name": "NetworkProtocol",
"Type": "String"
},
{
"Name": "DnsQueryClass",
"Type": "Int32"
},
{
"Name": "DnsQueryClassName",
"Type": "String"
},
{
"Name": "DnsNetworkDuration",
"Type": "Int32"
},
{
"Name": "DnsFlagsAuthenticated",
"Type": "SByte"
},
{
"Name": "DnsFlagsAuthoritative",
"Type": "SByte"
},
{
"Name": "DnsFlagsRecursionDesired",
"Type": "SByte"
},
{
"Name": "DnsSessionId",
"Type": "String"
},
{
"Name": "EventMessage",
"Type": "String"
},
{
"Name": "EventOriginalUid",
"Type": "String"
},
{
"Name": "EventReportUrl",
"Type": "String"
},
{
"Name": "DvcFQDN",
"Type": "String"
},
{
"Name": "DvcId",
"Type": "String"
},
{
"Name": "DvcIdType",
"Type": "String"
},
{
"Name": "SrcHostname",
"Type": "String"
},
{
"Name": "SrcDomain",
"Type": "String"
},
{
"Name": "SrcDomainType",
"Type": "String"
},
{
"Name": "SrcFQDN",
"Type": "String"
},
{
"Name": "SrcDvcId",
"Type": "String"
},
{
"Name": "SrcDvcIdType",
"Type": "String"
},
{
"Name": "SrcDeviceType",
"Type": "String"
},
{
"Name": "SrcUserId",
"Type": "String"
},
{
"Name": "SrcUserIdType",
"Type": "String"
},
{
"Name": "SrcUsername",
"Type": "String"
},
{
"Name": "SrcUsernameType",
"Type": "String"
},
{
"Name": "SrcUserType",
"Type": "String"
},
{
"Name": "SrcOriginalUserType",
"Type": "String"
},
{
"Name": "SrcProcessName",
"Type": "String"
},
{
"Name": "SrcProcessId",
"Type": "String"
},
{
"Name": "SrcProcessGuid",
"Type": "String"
},
{
"Name": "DstPortNumber",
"Type": "Int32"
},
{
"Name": "DstHostname",
"Type": "String"
},
{
"Name": "DstDomain",
"Type": "String"
},
{
"Name": "DstDomainType",
"Type": "String"
},
{
"Name": "DstFQDN",
"Type": "String"
},
{
"Name": "DstDvcId",
"Type": "String"
},
{
"Name": "DstDvcIdType",
"Type": "String"
},
{
"Name": "DstDeviceType",
"Type": "String"
},
{
"Name": "UrlCategory",
"Type": "String"
},
{
"Name": "ThreatCategory",
"Type": "String"
},
{
"Name": "DvcAction",
"Type": "String"
},
{
"Name": "DnsFlagsCheckingDisabled",
"Type": "SByte"
},
{
"Name": "DnsFlagsRecursionAvailable",
"Type": "SByte"
},
{
"Name": "DnsFlagsTruncates",
"Type": "SByte"
},
{
"Name": "DnsFlagsZ",
"Type": "SByte"
},
{
"Name": "SourceSystem",
"Type": "String"
},
{
"Name": "Type",
"Type": "String"
}
]
}

Просмотреть файл

@ -0,0 +1,133 @@
{
"Name": "AWSVPCFlow",
"Properties": [
{
"Name": "TenantId",
"Type": "String"
},
{
"Name": "TimeGenerated",
"Type": "DateTime"
},
{
"Name": "Version",
"Type": "Int"
},
{
"Name": "AccountId",
"Type": "String"
},
{
"Name": "InterfaceId",
"Type": "String"
},
{
"Name": "SrcAddr",
"Type": "String"
},
{
"Name": "DstAddr",
"Type": "String"
},
{
"Name": "SrcPort",
"Type": "Int"
},
{
"Name": "DstPort",
"Type": "Int"
},
{
"Name": "Protocol",
"Type": "Int"
},
{
"Name": "Packets",
"Type": "Int"
},
{
"Name": "Bytes",
"Type": "Long"
},
{
"Name": "End",
"Type": "DateTime"
},
{
"Name": "Action",
"Type": "String"
},
{
"Name": "LogStatus",
"Type": "String"
},
{
"Name": "VpcId",
"Type": "String"
},
{
"Name": "SubnetId",
"Type": "String"
},
{
"Name": "InstanceId",
"Type": "String"
},
{
"Name": "TcpFlags",
"Type": "Int"
},
{
"Name": "TrafficType",
"Type": "String"
},
{
"Name": "PktSrcAddr",
"Type": "String"
},
{
"Name": "PktDstAddr",
"Type": "String"
},
{
"Name": "Region",
"Type": "String"
},
{
"Name": "AzId",
"Type": "String"
},
{
"Name": "SublocationType",
"Type": "String"
},
{
"Name": "SublocationId",
"Type": "String"
},
{
"Name": "PktSrcAwsService",
"Type": "String"
},
{
"Name": "PktDstAwsService",
"Type": "String"
},
{
"Name": "FlowDirection",
"Type": "String"
},
{
"Name": "TrafficPath",
"Type": "String"
},
{
"Name": "SourceSystem",
"Type": "String"
},
{
"Name": "Type",
"Type": "String"
}
]
}

Просмотреть файл

@ -44,6 +44,678 @@
{
"Name": "DestPort_d",
"Type": "Double"
},
{
"Name": "SourceSystem",
"Type": "String"
},
{
"Name": "MG",
"Type": "String"
},
{
"Name": "ManagementGroupName",
"Type": "String"
},
{
"Name": "RawData",
"Type": "String"
},
{
"Name": "FlowEndTime_s",
"Type": "String"
},
{
"Name": "FlowStartTime_s",
"Type": "String"
},
{
"Name": "Subscription2_s",
"Type": "String"
},
{
"Name": "Subscription1_s",
"Type": "String"
},
{
"Name": "FASchemaVersion_s",
"Type": "String"
},
{
"Name": "FlowIntervalStartTime_t",
"Type": "DateTime"
},
{
"Name": "FlowIntervalEndTime_t",
"Type": "DateTime"
},
{
"Name": "FlowStartTime_t",
"Type": "DateTime"
},
{
"Name": "FlowEndTime_t",
"Type": "DateTime"
},
{
"Name": "NetworkFlowType_s",
"Type": "String"
},
{
"Name": "SrcIP_s",
"Type": "String"
},
{
"Name": "DestIP_s",
"Type": "String"
},
{
"Name": "VMIP_s",
"Type": "String"
},
{
"Name": "DestPort_d",
"Type": "Double"
},
{
"Name": "L4Protocol_s",
"Type": "String"
},
{
"Name": "IsFlowCapturedAtUDRHop_b",
"Type": "SByte"
},
{
"Name": "NSGList_s",
"Type": "String"
},
{
"Name": "NSGRules_s",
"Type": "String"
},
{
"Name": "NSGRule_s",
"Type": "String"
},
{
"Name": "NSGRuleType_s",
"Type": "String"
},
{
"Name": "Subscription1_g",
"Type": "String"
},
{
"Name": "Subscription2_g",
"Type": "String"
},
{
"Name": "Region1_s",
"Type": "String"
},
{
"Name": "Region2_s",
"Type": "String"
},
{
"Name": "NIC_s",
"Type": "String"
},
{
"Name": "NIC1_s",
"Type": "String"
},
{
"Name": "NIC2_s",
"Type": "String"
},
{
"Name": "VM_s",
"Type": "String"
},
{
"Name": "VM1_s",
"Type": "String"
},
{
"Name": "VM2_s",
"Type": "String"
},
{
"Name": "Subnet_s",
"Type": "String"
},
{
"Name": "Routes_s",
"Type": "String"
},
{
"Name": "ApplicationGateway1_s",
"Type": "String"
},
{
"Name": "ApplicationGateway2_s",
"Type": "String"
},
{
"Name": "LoadBalancer1_s",
"Type": "String"
},
{
"Name": "LoadBalancer2_s",
"Type": "String"
},
{
"Name": "LocalNetworkGateway1_s",
"Type": "String"
},
{
"Name": "LocalNetworkGateway2_s",
"Type": "String"
},
{
"Name": "ExpressRouteCircuit1_s",
"Type": "String"
},
{
"Name": "ExpressRouteCircuit2_s",
"Type": "String"
},
{
"Name": "ExpressRouteCircuitPeeringType_s",
"Type": "String"
},
{
"Name": "ConnectionName_s",
"Type": "String"
},
{
"Name": "ConnectingVNets_s",
"Type": "String"
},
{
"Name": "Country_s",
"Type": "String"
},
{
"Name": "AzureRegion_s",
"Type": "String"
},
{
"Name": "AllowedInFlows_d",
"Type": "Double"
},
{
"Name": "DeniedInFlows_d",
"Type": "Double"
},
{
"Name": "AllowedOutFlows_d",
"Type": "Double"
},
{
"Name": "DeniedOutFlows_d",
"Type": "Double"
},
{
"Name": "FlowCount_d",
"Type": "Double"
},
{
"Name": "InboundPackets_d",
"Type": "Double"
},
{
"Name": "OutboundPackets_d",
"Type": "Double"
},
{
"Name": "InboundBytes_d",
"Type": "Double"
},
{
"Name": "OutboundBytes_d",
"Type": "Double"
},
{
"Name": "CompletedFlows_d",
"Type": "Double"
},
{
"Name": "batchSizeInBytes_d",
"Type": "Double"
},
{
"Name": "Priority_d",
"Type": "Double"
},
{
"Name": "Tags_s",
"Type": "String"
},
{
"Name": "VmssName_s",
"Type": "String"
},
{
"Name": "AddressPrefixes_s",
"Type": "String"
},
{
"Name": "RouteTable_s",
"Type": "String"
},
{
"Name": "AddressPrefix_s",
"Type": "String"
},
{
"Name": "NextHopIP_s",
"Type": "String"
},
{
"Name": "NextHopType_s",
"Type": "String"
},
{
"Name": "FlowLogStorageAccount_s",
"Type": "String"
},
{
"Name": "IsFlowEnabled_b",
"Type": "SByte"
},
{
"Name": "Access_s",
"Type": "String"
},
{
"Name": "Description_s",
"Type": "String"
},
{
"Name": "DestinationAddressPrefix_s",
"Type": "String"
},
{
"Name": "DestinationPortRange_s",
"Type": "String"
},
{
"Name": "Direction_s",
"Type": "String"
},
{
"Name": "RuleType_s",
"Type": "String"
},
{
"Name": "SourceAddressPrefix_s",
"Type": "String"
},
{
"Name": "SourcePortRange_s",
"Type": "String"
},
{
"Name": "ApplicationGatewayBackendPools_s",
"Type": "String"
},
{
"Name": "EnableIPForwarding_b",
"Type": "SByte"
},
{
"Name": "LoadBalancerBackendPools_s",
"Type": "String"
},
{
"Name": "MACAddress_s",
"Type": "String"
},
{
"Name": "NSG_s",
"Type": "String"
},
{
"Name": "PrivateIPAddresses_s",
"Type": "String"
},
{
"Name": "PublicIPAddresses_s",
"Type": "String"
},
{
"Name": "Subnetwork_s",
"Type": "String"
},
{
"Name": "VirtualMachine_s",
"Type": "String"
},
{
"Name": "IsVirtualAppliance_b",
"Type": "SByte"
},
{
"Name": "IPAddress",
"Type": "String"
},
{
"Name": "SubnetPrefixes_s",
"Type": "String"
},
{
"Name": "BGPEnabled_b",
"Type": "SByte"
},
{
"Name": "GatewayType_s",
"Type": "String"
},
{
"Name": "SKU_s",
"Type": "String"
},
{
"Name": "VIPAddress_s",
"Type": "String"
},
{
"Name": "VirtualSubnetwork_s",
"Type": "String"
},
{
"Name": "VpnClientAddressPrefixes_s",
"Type": "String"
},
{
"Name": "ConnectionStatus_s",
"Type": "String"
},
{
"Name": "ConnectionType_s",
"Type": "String"
},
{
"Name": "EgressBytesTransferred_d",
"Type": "Double"
},
{
"Name": "GatewayConnectionType_s",
"Type": "String"
},
{
"Name": "IngressBytesTransferred_d",
"Type": "Double"
},
{
"Name": "LocalNetworkGateway_s",
"Type": "String"
},
{
"Name": "Peer_s",
"Type": "String"
},
{
"Name": "RoutingWeight_d",
"Type": "Double"
},
{
"Name": "VirtualNetworkGateway1_s",
"Type": "String"
},
{
"Name": "VirtualNetworkGateway2_s",
"Type": "String"
},
{
"Name": "AllowForwardedTraffic_b",
"Type": "SByte"
},
{
"Name": "AllowGatewayTransit_b",
"Type": "SByte"
},
{
"Name": "AllowVirtualNetworkAccess_b",
"Type": "SByte"
},
{
"Name": "UseRemoteGateways_b",
"Type": "SByte"
},
{
"Name": "VirtualNetwork1_s",
"Type": "String"
},
{
"Name": "VirtualNetwork2_s",
"Type": "String"
},
{
"Name": "AppGatewayType_s",
"Type": "String"
},
{
"Name": "GatewaySubnet_s",
"Type": "String"
},
{
"Name": "PrivateFrontendIPs_s",
"Type": "String"
},
{
"Name": "PublicFrontendIPs_s",
"Type": "String"
},
{
"Name": "BackendSubnets_s",
"Type": "String"
},
{
"Name": "FrontendIPs_s",
"Type": "String"
},
{
"Name": "FrontendSubnet_s",
"Type": "String"
},
{
"Name": "FrontendSubnets_s",
"Type": "String"
},
{
"Name": "LoadBalancerType_s",
"Type": "String"
},
{
"Name": "Subnet1_s",
"Type": "String"
},
{
"Name": "Subnet2_s",
"Type": "String"
},
{
"Name": "SubnetRegion1_s",
"Type": "String"
},
{
"Name": "SubnetRegion2_s",
"Type": "String"
},
{
"Name": "VirtualAppliances_s",
"Type": "String"
},
{
"Name": "BackendIPAddress_s",
"Type": "String"
},
{
"Name": "BackendAddressPool_s",
"Type": "String"
},
{
"Name": "BackendPort_d",
"Type": "Double"
},
{
"Name": "FloatingIPEnabled_b",
"Type": "SByte"
},
{
"Name": "FrontendIPAddress_s",
"Type": "String"
},
{
"Name": "FrontendPort_d",
"Type": "Double"
},
{
"Name": "Protocol_s",
"Type": "String"
},
{
"Name": "CircuitProvisioningState_s",
"Type": "String"
},
{
"Name": "ServiceProviderProperties_s",
"Type": "String"
},
{
"Name": "ServiceProviderProvisioningState_s",
"Type": "String"
},
{
"Name": "SkuDetail_s",
"Type": "String"
},
{
"Name": "AzureASN_d",
"Type": "Double"
},
{
"Name": "PeerASN_d",
"Type": "Double"
},
{
"Name": "PeeringType_s",
"Type": "String"
},
{
"Name": "PrimaryAzurePort_s",
"Type": "String"
},
{
"Name": "PrimaryPeerAddressPrefix_s",
"Type": "String"
},
{
"Name": "PrimarybytesIn_d",
"Type": "Double"
},
{
"Name": "PrimarybytesOut_d",
"Type": "Double"
},
{
"Name": "SecondaryAzurePort_s",
"Type": "String"
},
{
"Name": "SecondaryPeerAddressPrefix_s",
"Type": "String"
},
{
"Name": "SecondarybytesIn_d",
"Type": "Double"
},
{
"Name": "SecondarybytesOut_d",
"Type": "Double"
},
{
"Name": "State_s",
"Type": "String"
},
{
"Name": "VlanId_d",
"Type": "Double"
},
{
"Name": "SchemaVersion_s",
"Type": "String"
},
{
"Name": "Name_s",
"Type": "String"
},
{
"Name": "Region_s",
"Type": "String"
},
{
"Name": "Network_s",
"Type": "String"
},
{
"Name": "PrimaryNextHop_s",
"Type": "String"
},
{
"Name": "SecondaryNextHop_s",
"Type": "String"
},
{
"Name": "Weight_d",
"Type": "Double"
},
{
"Name": "ComponentType_s",
"Type": "String"
},
{
"Name": "DiscoveryRegion_s",
"Type": "String"
},
{
"Name": "ResourceType",
"Type": "String"
},
{
"Name": "Status_s",
"Type": "String"
},
{
"Name": "SubType_s",
"Type": "String"
},
{
"Name": "Subscription_g",
"Type": "String"
},
{
"Name": "SubscriptionName_s",
"Type": "String"
},
{
"Name": "TimeProcessed_t",
"Type": "DateTime"
},
{
"Name": "TopologyVersion_s",
"Type": "String"
},
{
"Name": "Type",
"Type": "String"
},
{
"Name": "_ResourceId",
"Type": "String"
}
]
}

Просмотреть файл

@ -0,0 +1,53 @@
{
"Name": "Corelight_CL",
"Properties": [
{
"Name": "TenantId",
"Type": "String"
},
{
"Name": "SourceSystem",
"Type": "String"
},
{
"Name": "MG",
"Type": "String"
},
{
"Name": "ManagementGroupName",
"Type": "String"
},
{
"Name": "TimeGenerated",
"Type": "DateTime"
},
{
"Name": "Computer",
"Type": "String"
},
{
"Name": "RawData",
"Type": "String"
},
{
"Name": "Message",
"Type": "String"
},
{
"Name": "log_file_s",
"Type": "String"
},
{
"Name": "hostname_s",
"Type": "String"
},
{
"Name": "Type",
"Type": "String"
},
{
"Name": "_ResourceId",
"Type": "String"
}
]
}

Просмотреть файл

@ -19,7 +19,7 @@
},
{
"Name": "TimeGenerated",
"Type": "String"
"Type": "DateTime"
},
{
"Name": "Computer",

Просмотреть файл

@ -0,0 +1,277 @@
{
"Name": "NXLog_DNS_Server_CL",
"Properties": [
{
"Name": "TenantId",
"Type": "String"
},
{
"Name": "SourceSystem",
"Type": "String"
},
{
"Name": "MG",
"Type": "String"
},
{
"Name": "ManagementGroupName",
"Type": "String"
},
{
"Name": "TimeGenerated",
"Type": "DateTime"
},
{
"Name": "Computer",
"Type": "String"
},
{
"Name": "RawData",
"Type": "String"
},
{
"Name": "DNS_LogType_s",
"Type": "String"
},
{
"Name": "FilePath_s",
"Type": "String"
},
{
"Name": "DNSSeverType_s",
"Type": "String"
},
{
"Name": "SourceName_s",
"Type": "String"
},
{
"Name": "ProviderGuid_g",
"Type": "String"
},
{
"Name": "EventID_d",
"Type": "Double"
},
{
"Name": "Version_d",
"Type": "Double"
},
{
"Name": "ChannelID_d",
"Type": "Double"
},
{
"Name": "OpcodeValue_d",
"Type": "Double"
},
{
"Name": "TaskValue_d",
"Type": "Double"
},
{
"Name": "Keywords_s",
"Type": "String"
},
{
"Name": "EventTime_t",
"Type": "DateTime"
},
{
"Name": "ExecutionProcessID_d",
"Type": "Double"
},
{
"Name": "ExecutionThreadID_d",
"Type": "Double"
},
{
"Name": "EventType_s",
"Type": "String"
},
{
"Name": "SeverityValue_d",
"Type": "Double"
},
{
"Name": "Severity_s",
"Type": "String"
},
{
"Name": "Hostname_s",
"Type": "String"
},
{
"Name": "Domain_s",
"Type": "String"
},
{
"Name": "AccountName_s",
"Type": "String"
},
{
"Name": "UserID_s",
"Type": "String"
},
{
"Name": "AccountType_s",
"Type": "String"
},
{
"Name": "Flags_s",
"Type": "String"
},
{
"Name": "TCP_s",
"Type": "String"
},
{
"Name": "Source_s",
"Type": "String"
},
{
"Name": "InterfaceIP_s",
"Type": "String"
},
{
"Name": "AA_s",
"Type": "String"
},
{
"Name": "AD_s",
"Type": "String"
},
{
"Name": "QNAME_s",
"Type": "String"
},
{
"Name": "QTYPE_s",
"Type": "String"
},
{
"Name": "XID_s",
"Type": "String"
},
{
"Name": "RecursionDepth_s",
"Type": "String"
},
{
"Name": "Port_s",
"Type": "String"
},
{
"Name": "RecursionScope_s",
"Type": "String"
},
{
"Name": "CacheScope_s",
"Type": "String"
},
{
"Name": "BufferSize_s",
"Type": "String"
},
{
"Name": "PacketData_s",
"Type": "String"
},
{
"Name": "AdditionalInfo_s",
"Type": "String"
},
{
"Name": "GUID_g",
"Type": "String"
},
{
"Name": "EventReceivedTime_t",
"Type": "DateTime"
},
{
"Name": "SourceModuleName_s",
"Type": "String"
},
{
"Name": "SourceModuleType_s",
"Type": "String"
},
{
"Name": "HostIP_s",
"Type": "String"
},
{
"Name": "Destination_s",
"Type": "String"
},
{
"Name": "RD_s",
"Type": "String"
},
{
"Name": "QXID_s",
"Type": "String"
},
{
"Name": "PolicyName_s",
"Type": "String"
},
{
"Name": "DNSSEC_s",
"Type": "String"
},
{
"Name": "RCODE_s",
"Type": "String"
},
{
"Name": "Scope_s",
"Type": "String"
},
{
"Name": "Zone_s",
"Type": "String"
},
{
"Name": "ElapsedTime_s",
"Type": "String"
},
{
"Name": "Type_s",
"Type": "String"
},
{
"Name": "NAME_s",
"Type": "String"
},
{
"Name": "TTL_s",
"Type": "String"
},
{
"Name": "RDATA_s",
"Type": "String"
},
{
"Name": "ZoneScope_s",
"Type": "String"
},
{
"Name": "VirtualizationID_s",
"Type": "String"
},
{
"Name": "Reason_s",
"Type": "String"
},
{
"Name": "Type",
"Type": "String"
},
{
"Name": "_ResourceId",
"Type": "String"
}
]
}

Просмотреть файл

@ -0,0 +1,113 @@
{
"Name": "SquidProxy_CL",
"Properties": [
{
"Name": "TenantId",
"Type": "String"
},
{
"Name": "SourceSystem",
"Type": "String"
},
{
"Name": "MG",
"Type": "String"
},
{
"Name": "ManagementGroupName",
"Type": "String"
},
{
"Name": "TimeGenerated",
"Type": "DateTime"
},
{
"Name": "Computer",
"Type": "String"
},
{
"Name": "RawData",
"Type": "String"
},
{
"Name": "TenantId_s",
"Type": "String"
},
{
"Name": "MG_s",
"Type": "String"
},
{
"Name": "TimeGenerated_UTC__s",
"Type": "String"
},
{
"Name": "Type_s",
"Type": "String"
},
{
"Name": "_ResourceId_s",
"Type": "String"
},
{
"Name": "Duration_s",
"Type": "String"
},
{
"Name": "SrcIpAddr_s",
"Type": "String"
},
{
"Name": "ResultCode_s",
"Type": "String"
},
{
"Name": "StatusCode_s",
"Type": "String"
},
{
"Name": "Bytes_s",
"Type": "String"
},
{
"Name": "RequstMethod_s",
"Type": "String"
},
{
"Name": "Url_s",
"Type": "String"
},
{
"Name": "Username_s",
"Type": "String"
},
{
"Name": "PeerStatus_s",
"Type": "String"
},
{
"Name": "PeerHost_s",
"Type": "String"
},
{
"Name": "ContentType_s",
"Type": "String"
},
{
"Name": "EventTime_UTC__s",
"Type": "String"
},
{
"Name": "Description_s",
"Type": "String"
},
{
"Name": "Type",
"Type": "String"
},
{
"Name": "_ResourceId",
"Type": "String"
}
]
}

Просмотреть файл

@ -0,0 +1,857 @@
{
"Name": "VectraStream_CL",
"Properties": [
{
"Name": "TenantId",
"Type": "String"
},
{
"Name": "SourceSystem",
"Type": "String"
},
{
"Name": "MG",
"Type": "String"
},
{
"Name": "ManagementGroupName",
"Type": "String"
},
{
"Name": "TimeGenerated",
"Type": "DateTime"
},
{
"Name": "Computer",
"Type": "String"
},
{
"Name": "RawData",
"Type": "String"
},
{
"Name": "client_dig_product_id_s",
"Type": "String"
},
{
"Name": "client_dig_protocol_id_s",
"Type": "String"
},
{
"Name": "client_name_s",
"Type": "String"
},
{
"Name": "dir_confidence_d",
"Type": "Double"
},
{
"Name": "san_ip_s",
"Type": "String"
},
{
"Name": "host_key_s",
"Type": "String"
},
{
"Name": "application_s",
"Type": "String"
},
{
"Name": "error_msg_s",
"Type": "String"
},
{
"Name": "reply_to_s",
"Type": "String"
},
{
"Name": "useragent_s",
"Type": "String"
},
{
"Name": "second_received_s",
"Type": "String"
},
{
"Name": "spf_mailfrom_s",
"Type": "String"
},
{
"Name": "x_originating_ip_s",
"Type": "String"
},
{
"Name": "first_received_s",
"Type": "String"
},
{
"Name": "in_reply_to_s",
"Type": "String"
},
{
"Name": "rcpt_to_s",
"Type": "String"
},
{
"Name": "to_s",
"Type": "String"
},
{
"Name": "date_s",
"Type": "String"
},
{
"Name": "from_s",
"Type": "String"
},
{
"Name": "helo_s",
"Type": "String"
},
{
"Name": "mail_from_s",
"Type": "String"
},
{
"Name": "msgid_s",
"Type": "String"
},
{
"Name": "tls_b",
"Type": "SByte"
},
{
"Name": "proxied_s",
"Type": "String"
},
{
"Name": "error_s",
"Type": "String"
},
{
"Name": "matched_dn_s",
"Type": "String"
},
{
"Name": "referrer_s",
"Type": "String"
},
{
"Name": "client_build_s",
"Type": "String"
},
{
"Name": "desktop_height_d",
"Type": "Double"
},
{
"Name": "desktop_width_d",
"Type": "Double"
},
{
"Name": "keyboard_layout_s",
"Type": "String"
},
{
"Name": "beacon_type_s",
"Type": "String"
},
{
"Name": "beacon_uid_s",
"Type": "String"
},
{
"Name": "first_event_time_d",
"Type": "Double"
},
{
"Name": "last_event_time_d",
"Type": "Double"
},
{
"Name": "orig_ip_bytes_d",
"Type": "Double"
},
{
"Name": "resp_domains_s",
"Type": "String"
},
{
"Name": "resp_ip_bytes_d",
"Type": "Double"
},
{
"Name": "session_count_d",
"Type": "Double"
},
{
"Name": "resp_filename_s",
"Type": "String"
},
{
"Name": "response_content_disposition_s",
"Type": "String"
},
{
"Name": "cookie_s",
"Type": "String"
},
{
"Name": "cookie_vars_s",
"Type": "String"
},
{
"Name": "orig_mime_types_s",
"Type": "String"
},
{
"Name": "domain_s",
"Type": "String"
},
{
"Name": "status_d",
"Type": "Double"
},
{
"Name": "username_s",
"Type": "String"
},
{
"Name": "cipher_alg_s",
"Type": "String"
},
{
"Name": "compression_alg_s",
"Type": "String"
},
{
"Name": "hassh_g",
"Type": "String"
},
{
"Name": "hasshServer_g",
"Type": "String"
},
{
"Name": "host_key_alg_s",
"Type": "String"
},
{
"Name": "kex_alg_s",
"Type": "String"
},
{
"Name": "mac_alg_s",
"Type": "String"
},
{
"Name": "server_s",
"Type": "String"
},
{
"Name": "client_s",
"Type": "String"
},
{
"Name": "data_source_s",
"Type": "String"
},
{
"Name": "error_code_s",
"Type": "String"
},
{
"Name": "orig_host_observed_privilege_d",
"Type": "Double"
},
{
"Name": "protocol_d",
"Type": "Double"
},
{
"Name": "rep_cipher_s",
"Type": "String"
},
{
"Name": "reply_timestamp_d",
"Type": "Double"
},
{
"Name": "req_ciphers_s",
"Type": "String"
},
{
"Name": "request_type_s",
"Type": "String"
},
{
"Name": "success_b",
"Type": "SByte"
},
{
"Name": "attributes_s",
"Type": "String"
},
{
"Name": "bind_error_count_d",
"Type": "Double"
},
{
"Name": "encrypted_sasl_payload_count_d",
"Type": "Double"
},
{
"Name": "is_close_b",
"Type": "SByte"
},
{
"Name": "is_query_b",
"Type": "SByte"
},
{
"Name": "logon_failure_error_count_d",
"Type": "Double"
},
{
"Name": "message_id_d",
"Type": "Double"
},
{
"Name": "query_scope_s",
"Type": "String"
},
{
"Name": "request_bytes_s",
"Type": "String"
},
{
"Name": "response_bytes_s",
"Type": "String"
},
{
"Name": "result_s",
"Type": "String"
},
{
"Name": "result_code_s",
"Type": "String"
},
{
"Name": "result_count_d",
"Type": "Double"
},
{
"Name": "base_object_s",
"Type": "String"
},
{
"Name": "endpoint_s",
"Type": "String"
},
{
"Name": "operation_s",
"Type": "String"
},
{
"Name": "rtt_d",
"Type": "Double"
},
{
"Name": "action_s",
"Type": "String"
},
{
"Name": "delete_on_close_b",
"Type": "SByte"
},
{
"Name": "name_s",
"Type": "String"
},
{
"Name": "hostname_s",
"Type": "String"
},
{
"Name": "client_issuer_s",
"Type": "String"
},
{
"Name": "client_subject_s",
"Type": "String"
},
{
"Name": "certificate_serial_g",
"Type": "String"
},
{
"Name": "resp_mime_types_s",
"Type": "String"
},
{
"Name": "response_cache_control_s",
"Type": "String"
},
{
"Name": "lease_time_d",
"Type": "Double"
},
{
"Name": "mac_s",
"Type": "String"
},
{
"Name": "assigned_ip_s",
"Type": "String"
},
{
"Name": "dhcp_server_ip_s",
"Type": "String"
},
{
"Name": "dns_server_ips_s",
"Type": "String"
},
{
"Name": "request_cache_control_s",
"Type": "String"
},
{
"Name": "response_expires_s",
"Type": "String"
},
{
"Name": "user_agent_s",
"Type": "String"
},
{
"Name": "path_s",
"Type": "String"
},
{
"Name": "host_s",
"Type": "String"
},
{
"Name": "host_multihomed_b",
"Type": "SByte"
},
{
"Name": "is_proxied_b",
"Type": "SByte"
},
{
"Name": "method_s",
"Type": "String"
},
{
"Name": "request_body_len_d",
"Type": "Double"
},
{
"Name": "request_header_count_d",
"Type": "Double"
},
{
"Name": "response_body_len_d",
"Type": "Double"
},
{
"Name": "response_header_count_d",
"Type": "Double"
},
{
"Name": "status_code_d",
"Type": "Double"
},
{
"Name": "status_msg_s",
"Type": "String"
},
{
"Name": "uri_s",
"Type": "String"
},
{
"Name": "certificate_curve_s",
"Type": "String"
},
{
"Name": "AA_b",
"Type": "SByte"
},
{
"Name": "RA_b",
"Type": "SByte"
},
{
"Name": "RD_b",
"Type": "SByte"
},
{
"Name": "TC_b",
"Type": "SByte"
},
{
"Name": "TTLs_s",
"Type": "String"
},
{
"Name": "answers_s",
"Type": "String"
},
{
"Name": "auth_s",
"Type": "String"
},
{
"Name": "qclass_d",
"Type": "Double"
},
{
"Name": "qclass_name_s",
"Type": "String"
},
{
"Name": "qtype_d",
"Type": "Double"
},
{
"Name": "qtype_name_s",
"Type": "String"
},
{
"Name": "query_s",
"Type": "String"
},
{
"Name": "rcode_d",
"Type": "Double"
},
{
"Name": "rcode_name_s",
"Type": "String"
},
{
"Name": "rejected_b",
"Type": "SByte"
},
{
"Name": "saw_query_b",
"Type": "SByte"
},
{
"Name": "saw_reply_b",
"Type": "SByte"
},
{
"Name": "total_answers_d",
"Type": "Double"
},
{
"Name": "total_replies_d",
"Type": "Double"
},
{
"Name": "trans_id_d",
"Type": "Double"
},
{
"Name": "issuer_s",
"Type": "String"
},
{
"Name": "next_protocol_s",
"Type": "String"
},
{
"Name": "subject_s",
"Type": "String"
},
{
"Name": "basic_constraints_ca_b",
"Type": "SByte"
},
{
"Name": "basic_constraints_path_len_d",
"Type": "Double"
},
{
"Name": "certificate_cn_s",
"Type": "String"
},
{
"Name": "certificate_exponent_s",
"Type": "String"
},
{
"Name": "certificate_issuer_s",
"Type": "String"
},
{
"Name": "certificate_key_alg_s",
"Type": "String"
},
{
"Name": "certificate_key_length_s",
"Type": "String"
},
{
"Name": "certificate_key_type_s",
"Type": "String"
},
{
"Name": "certificate_not_valid_after_d",
"Type": "Double"
},
{
"Name": "certificate_not_valid_before_d",
"Type": "Double"
},
{
"Name": "certificate_self_issued_b",
"Type": "SByte"
},
{
"Name": "certificate_serial_s",
"Type": "String"
},
{
"Name": "certificate_sig_alg_s",
"Type": "String"
},
{
"Name": "certificate_subject_s",
"Type": "String"
},
{
"Name": "certificate_version_d",
"Type": "Double"
},
{
"Name": "san_dns_s",
"Type": "String"
},
{
"Name": "san_other_fields_b",
"Type": "SByte"
},
{
"Name": "community_id_s",
"Type": "String"
},
{
"Name": "conn_state_s",
"Type": "String"
},
{
"Name": "duration_d",
"Type": "Double"
},
{
"Name": "first_orig_resp_data_pkt_s",
"Type": "String"
},
{
"Name": "first_orig_resp_data_pkt_time_d",
"Type": "Double"
},
{
"Name": "first_orig_resp_pkt_time_d",
"Type": "Double"
},
{
"Name": "first_resp_orig_data_pkt_s",
"Type": "String"
},
{
"Name": "first_resp_orig_data_pkt_time_d",
"Type": "Double"
},
{
"Name": "first_resp_orig_pkt_time_d",
"Type": "Double"
},
{
"Name": "id_ip_ver_s",
"Type": "String"
},
{
"Name": "id_orig_h_s",
"Type": "String"
},
{
"Name": "id_orig_p_d",
"Type": "Double"
},
{
"Name": "id_resp_h_s",
"Type": "String"
},
{
"Name": "id_resp_p_d",
"Type": "Double"
},
{
"Name": "local_orig_b",
"Type": "SByte"
},
{
"Name": "local_resp_b",
"Type": "SByte"
},
{
"Name": "metadata_type_s",
"Type": "String"
},
{
"Name": "orig_hostname_s",
"Type": "String"
},
{
"Name": "orig_huid_s",
"Type": "String"
},
{
"Name": "orig_ip_bytes_s",
"Type": "String"
},
{
"Name": "orig_pkts_d",
"Type": "Double"
},
{
"Name": "orig_sluid_s",
"Type": "String"
},
{
"Name": "orig_vlan_id_d",
"Type": "Double"
},
{
"Name": "proto_d",
"Type": "Double"
},
{
"Name": "protoName_s",
"Type": "String"
},
{
"Name": "resp_domain_s",
"Type": "String"
},
{
"Name": "resp_ip_bytes_s",
"Type": "String"
},
{
"Name": "resp_multihomed_b",
"Type": "SByte"
},
{
"Name": "resp_pkts_d",
"Type": "Double"
},
{
"Name": "resp_vlan_id_d",
"Type": "Double"
},
{
"Name": "sensor_uid_s",
"Type": "String"
},
{
"Name": "service_s",
"Type": "String"
},
{
"Name": "session_start_time_d",
"Type": "Double"
},
{
"Name": "ts_d",
"Type": "Double"
},
{
"Name": "uid_s",
"Type": "String"
},
{
"Name": "cipher_s",
"Type": "String"
},
{
"Name": "client_curve_num_s",
"Type": "String"
},
{
"Name": "client_ec_point_format_s",
"Type": "String"
},
{
"Name": "client_extension_s",
"Type": "String"
},
{
"Name": "client_version_s",
"Type": "String"
},
{
"Name": "client_version_num_d",
"Type": "Double"
},
{
"Name": "curve_s",
"Type": "String"
},
{
"Name": "established_b",
"Type": "SByte"
},
{
"Name": "ja3_g",
"Type": "String"
},
{
"Name": "ja3s_g",
"Type": "String"
},
{
"Name": "server_extensions_s",
"Type": "String"
},
{
"Name": "server_name_s",
"Type": "String"
},
{
"Name": "version_s",
"Type": "String"
},
{
"Name": "version_num_d",
"Type": "Double"
},
{
"Name": "resp_hostname_s",
"Type": "String"
},
{
"Name": "resp_huid_s",
"Type": "String"
},
{
"Name": "resp_sluid_s",
"Type": "String"
},
{
"Name": "Type",
"Type": "String"
},
{
"Name": "_ResourceId",
"Type": "String"
}
]
}

Просмотреть файл

@ -0,0 +1,106 @@
using Microsoft.Azure.Sentinel.KustoServices.Contract;
using Microsoft.Azure.Sentinel.KustoServices.Implementation;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using YamlDotNet.Serialization;
namespace Kqlvalidations.Tests.FunctionSchemasLoaders
{
public class ParsersCustomJsonDirectoryFunctionsLoader : CustomJsonDirectoryObjectsLoader<FunctionSchema>, IFunctionSchemaLoader
{
public ParsersCustomJsonDirectoryFunctionsLoader(string directoryPath) : base(directoryPath)
{
}
public override IEnumerable<FunctionSchema> Load()
{
var sampleFunctions = base.Load();
Dictionary<string, List<Column>> schemaToResultColumnsMapping = GetSchemaToResultColumnsMapping(sampleFunctions);
var functions = GetFunctions(schemaToResultColumnsMapping);
return functions;
}
/// <summary>
/// Currently there are 3 parser schemas: DNS, network sessions and web sessions.
/// Parser functions with the same schema, should have the same result columns.
/// CustomFunctions directory contains a sample function for each schema.
/// This function extracts the result columns from the sample function and creates a mapping between the schema and the functions result columns.
/// </summary>
/// <param name="sampleFunctions">The sample functions</param>
/// <returns>Mapping between the schema and the functions result columns</returns>
private Dictionary<string, List<Column>> GetSchemaToResultColumnsMapping(IEnumerable<FunctionSchema> sampleFunctions)
{
Dictionary<string, string> sampleFunctionToSchemaMapping = ParsersDatabase.Parsers.ToDictionary(keySelector: parser => parser.SampleFunctionName, elementSelector: parser => parser.Schema);
return sampleFunctions.ToDictionary(keySelector: sampleFunction => sampleFunctionToSchemaMapping[sampleFunction.FunctionName], elementSelector: sampleFunction => sampleFunction.FunctionResultColumns);
}
/// <summary>
/// Generates the FunctionSchema for each parser.
/// </summary>
/// <param name="schemaToResultColumnsMapping">Mapping between schema and its result columns</param>
/// <returns>FunctionSchema foreach parser</returns>
private IEnumerable<FunctionSchema> GetFunctions(Dictionary<string, List<Column>> schemaToResultColumnsMapping)
{
var parsersYamlFilesLoader = new ParsersYamlFilesLoader();
var parsersYamlFiles = parsersYamlFilesLoader.GetFilesNames();
return parsersYamlFiles.Select(fileName =>
{
var schema = fileName.Split(Path.DirectorySeparatorChar)[^3];
var resultColumns = schemaToResultColumnsMapping[schema];
return GetParserFunctionSchema(fileName, resultColumns);
});
}
/// <summary>
/// Extracts the parser name and parameters from the yaml file and creates the FunctionSchema.
/// </summary>
/// <param name="fileName">The parser's yaml file</param>
/// <param name="resultColumns">The parser's result columns (taken from the sample data)</param>
/// <returns>The parser's function schema</returns>
private FunctionSchema GetParserFunctionSchema(string fileName, List<Column> resultColumns)
{
var deserializer = new DeserializerBuilder().Build();
var yaml = deserializer.Deserialize<dynamic>(File.ReadAllText(fileName));
return new FunctionSchema()
{
FunctionName = yaml["ParserName"],
FunctionParameters = GetFunctionParameters(yaml),
FunctionResultColumns = resultColumns,
};
}
/// <summary>
/// Extract fuction parameters from parser's ymal file
/// </summary>
/// <param name="yaml">The parser's yaml file</param>
/// <returns>The parser's function parameters</returns>
private List<FunctionParameter> GetFunctionParameters(dynamic yaml)
{
var functionParameters = new List<FunctionParameter>();
if (yaml.TryGetValue("ParserParams", out object functionParamsObject))
{
var parserParams = (List<object>)functionParamsObject;
functionParameters = parserParams.Select(ConvertObjectToFunctionParameter).ToList();
}
return functionParameters;
}
/// <summary>
/// Convert object to function parameter
/// </summary>
/// <param name="parameter">The function parameter as an object</param>
/// <returns>The function parameter</returns>
private FunctionParameter ConvertObjectToFunctionParameter(object parameter)
{
var dictionary = (Dictionary<object, object>)parameter;
return new FunctionParameter()
{
Name = (string)dictionary["Name"],
Type = (string)dictionary["Type"],
IsRequired = false,
};
}
}
}

Просмотреть файл

@ -0,0 +1,46 @@
using Microsoft.Azure.Sentinel.KustoServices.Contract;
using Microsoft.Azure.Sentinel.KustoServices.Implementation;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using YamlDotNet.Serialization;
namespace Kqlvalidations.Tests.FunctionSchemasLoaders
{
public static class ParsersDatabase
{
public static List<ParserConfiguration> Parsers => new List<ParserConfiguration>()
{
new ParserConfiguration()
{
Schema= "ASimDns",
SampleFunctionName= "_Im_Dns",
},
new ParserConfiguration()
{
Schema= "ASimWebSession",
SampleFunctionName= "_Im_WebSession",
},
new ParserConfiguration()
{
Schema= "ASimNetworkSession",
SampleFunctionName= "_Im_NetworkSession",
},
};
}
public class ParserConfiguration
{
/// <summary>
/// The schema name
/// </summary>
public string Schema { get; set; }
/// <summary>
/// A sample function for this schema. Parser functions that use this schema duplicate the result columns from the sample function.
/// </summary>
public string SampleFunctionName { get; set; }
}
}

Просмотреть файл

@ -1,164 +1,215 @@
using System.Collections.Generic;
using Microsoft.Azure.Sentinel.KustoServices.Contract;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Xunit;
using YamlDotNet.Serialization;
using System.Collections.Generic;
using Microsoft.Azure.Sentinel.KustoServices.Contract;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Xunit;
using YamlDotNet.Serialization;
using Microsoft.Azure.Sentinel.KustoServices.Implementation;
using Kqlvalidations.Tests.FunctionSchemasLoaders;
using System;
namespace Kqlvalidations.Tests
{
public class KqlValidationTests
{
namespace Kqlvalidations.Tests
{
public class KqlValidationTests
{
private readonly IKqlQueryAnalyzer _queryValidator;
private const int TestFolderDepth = 3;
public KqlValidationTests()
{
_queryValidator = new KqlQueryAnalyzerBuilder()
.WithSentinelDefaultTablesAndFunctionsSchemas()
.WithCustomTableSchemasLoader(new CustomJsonDirectoryTablesLoader(Path.Combine(Utils.GetTestDirectory(TestFolderDepth), "CustomTables")))
.WithCustomFunctionSchemasLoader(new CustomJsonDirectoryFunctionsLoader(Path.Combine(Utils.GetTestDirectory(TestFolderDepth), "CustomFunctions")))
.Build();
}
// We pass File name to test because in the result file we want to show an informative name for the test
[Theory]
[ClassData(typeof(DetectionsYamlFilesTestData))]
public void Validate_DetectionQueries_HaveValidKql(string fileName, string encodedFilePath)
{
var res = ReadAndDeserializeYaml(encodedFilePath);
var queryStr = (string) res["query"];
var id = (string) res["id"];
//we ignore known issues
if (ShouldSkipTemplateValidation(id))
{
return;
}
ValidateKql(id, queryStr);
}
// We pass File name to test because in the result file we want to show an informative name for the test
[Theory]
[ClassData(typeof(DetectionsYamlFilesTestData))]
public void Validate_DetectionQueries_SkippedTemplatesDoNotHaveValidKql(string fileName, string encodedFilePath)
{
var res = ReadAndDeserializeYaml(encodedFilePath);
var queryStr = (string) res["query"];
var id = (string) res["id"];
//Templates that are in the skipped templates should not pass the validation (if they pass, why skip?)
if (ShouldSkipTemplateValidation(id))
{
var validationRes = _queryValidator.ValidateSyntax(queryStr);
Assert.False(validationRes.IsValid, $"Template Id:{id} is valid but it is in the skipped validation templates. Please remove it from the templates that are skipped since it is valid.");
}
}
// // We pass File name to test because in the result file we want to show an informative name for the test
// [Theory]
// [ClassData(typeof(InsightsYamlFilesTestData))]
// public void Validate_InsightsQueries_HaveValidKqlBaseQuery(string fileName, string encodedFilePath)
// {
// var res = ReadAndDeserializeYaml(encodedFilePath);
// var queryStr = (string) res["BaseQuery"];
//
// ValidateKql(fileProp.FileName, queryStr);
// }
[Theory]
[ClassData(typeof(ExplorationQueriesYamlFilesTestData))]
public void Validate_ExplorationQueries_HaveValidKql(string fileName, string encodedFilePath)
{
var res = ReadAndDeserializeYaml(encodedFilePath);
var queryStr = (string) res["query"];
var id = (string) res["Id"];
//we ignore known issues
if (ShouldSkipTemplateValidation(id))
{
return;
}
ValidateKql(id, queryStr);
}
[Theory]
[ClassData(typeof(ExplorationQueriesYamlFilesTestData))]
public void Validate_ExplorationQueries_SkippedTemplatesDoNotHaveValidKql(string fileName, string encodedFilePath)
{
var res = ReadAndDeserializeYaml(encodedFilePath);
var queryStr = (string) res["query"];
var id = (string) res["Id"];
//Templates that are in the skipped templates should not pass the validation (if they pass, why skip?)
if (ShouldSkipTemplateValidation(id))
{
var validationRes = _queryValidator.ValidateSyntax(queryStr);
Assert.False(validationRes.IsValid, $"Template Id:{id} is valid but it is in the skipped validation templates. Please remove it from the templates that are skipped since it is valid.");
}
}
private void ValidateKql(string id, string queryStr)
{
var validationResult = _queryValidator.ValidateSyntax(queryStr);
var firstErrorLocation = (Line: 0, Col: 0);
if (!validationResult.IsValid)
{
firstErrorLocation = GetLocationInQuery(queryStr, validationResult.Diagnostics.First(d => d.Severity == "Error").Start);
}
var listOfDiagnostics = validationResult.Diagnostics;
bool isQueryValid = !(from p in listOfDiagnostics
where !p.Message.Contains("_GetWatchlist") //We do not validate the getWatchList, since the result schema is not known
select p).Any();
Assert.True(
isQueryValid,
isQueryValid
? string.Empty
: @$"Template Id: {id} is not valid in Line: {firstErrorLocation.Line} col: {firstErrorLocation.Col}
Errors: {validationResult.Diagnostics.Select(d => d.ToString()).ToList().Aggregate((s1, s2) => s1 + "," + s2)}");
}
private Dictionary<object, object> ReadAndDeserializeYaml(string encodedFilePath)
{
var yaml = File.ReadAllText(Utils.DecodeBase64(encodedFilePath));
var deserializer = new DeserializerBuilder().Build();
return deserializer.Deserialize<dynamic>(yaml);
}
private bool ShouldSkipTemplateValidation(string templateId)
{
return TemplatesToSkipValidationReader.WhiteListTemplates
.Where(template => template.id == templateId)
.Where(template => !string.IsNullOrWhiteSpace(template.validationFailReason))
.Where(template => !string.IsNullOrWhiteSpace(template.templateName))
.Any();
}
private (int Line, int Col) GetLocationInQuery(string queryStr, int pos)
{
var lines = Regex.Split(queryStr, "\n");
var curlineIndex = 0;
var curPos = 0;
while (lines.Length > curlineIndex && pos > curPos + lines[curlineIndex].Length + 1)
{
curPos += lines[curlineIndex].Length + 1;
curlineIndex++;
}
var col = (pos - curPos + 1);
return (curlineIndex + 1, col);
}
}
}
private const int TestFolderDepth = 3;
public KqlValidationTests()
{
_queryValidator = new KqlQueryAnalyzerBuilder()
.WithSentinelDefaultTablesAndFunctionsSchemas()
.WithCustomTableSchemasLoader(new CustomJsonDirectoryTablesLoader(Path.Combine(Utils.GetTestDirectory(TestFolderDepth), "CustomTables")))
.WithCustomFunctionSchemasLoader(new CustomJsonDirectoryFunctionsLoader(Path.Combine(Utils.GetTestDirectory(TestFolderDepth), "CustomFunctions")))
.WithCustomFunctionSchemasLoader(new ParsersCustomJsonDirectoryFunctionsLoader(Path.Combine(Utils.GetTestDirectory(TestFolderDepth), "CustomFunctions")))
.Build();
}
// We pass File name to test because in the result file we want to show an informative name for the test
[Theory]
[ClassData(typeof(DetectionsYamlFilesTestData))]
public void Validate_DetectionQueries_HaveValidKql(string fileName, string encodedFilePath)
{
var res = ReadAndDeserializeYaml(encodedFilePath);
var queryStr = (string) res["query"];
var id = (string) res["id"];
//we ignore known issues
if (ShouldSkipTemplateValidation(id))
{
return;
}
ValidateKql(id, queryStr);
}
// We pass File name to test because in the result file we want to show an informative name for the test
[Theory]
[ClassData(typeof(DetectionsYamlFilesTestData))]
public void Validate_DetectionQueries_SkippedTemplatesDoNotHaveValidKql(string fileName, string encodedFilePath)
{
var res = ReadAndDeserializeYaml(encodedFilePath);
var queryStr = (string) res["query"];
var id = (string) res["id"];
//Templates that are in the skipped templates should not pass the validation (if they pass, why skip?)
if (ShouldSkipTemplateValidation(id))
{
var validationRes = _queryValidator.ValidateSyntax(queryStr);
Assert.False(validationRes.IsValid, $"Template Id:{id} is valid but it is in the skipped validation templates. Please remove it from the templates that are skipped since it is valid.");
}
}
// // We pass File name to test because in the result file we want to show an informative name for the test
// [Theory]
// [ClassData(typeof(InsightsYamlFilesTestData))]
// public void Validate_InsightsQueries_HaveValidKqlBaseQuery(string fileName, string encodedFilePath)
// {
// var res = ReadAndDeserializeYaml(encodedFilePath);
// var queryStr = (string) res["BaseQuery"];
//
// ValidateKql(fileProp.FileName, queryStr);
// }
[Theory]
[ClassData(typeof(ExplorationQueriesYamlFilesTestData))]
public void Validate_ExplorationQueries_HaveValidKql(string fileName, string encodedFilePath)
{
var res = ReadAndDeserializeYaml(encodedFilePath);
var queryStr = (string) res["query"];
var id = (string) res["Id"];
//we ignore known issues
if (ShouldSkipTemplateValidation(id))
{
return;
}
ValidateKql(id, queryStr);
}
[Theory]
[ClassData(typeof(ExplorationQueriesYamlFilesTestData))]
public void Validate_ExplorationQueries_SkippedTemplatesDoNotHaveValidKql(string fileName, string encodedFilePath)
{
var res = ReadAndDeserializeYaml(encodedFilePath);
var queryStr = (string) res["query"];
var id = (string) res["Id"];
//Templates that are in the skipped templates should not pass the validation (if they pass, why skip?)
if (ShouldSkipTemplateValidation(id))
{
var validationRes = _queryValidator.ValidateSyntax(queryStr);
Assert.False(validationRes.IsValid, $"Template Id:{id} is valid but it is in the skipped validation templates. Please remove it from the templates that are skipped since it is valid.");
}
}
// We pass File name to test because in the result file we want to show an informative name for the test
[Theory]
[ClassData(typeof(ParsersYamlFilesTestData))]
public void Validate_ParsersFunctions_HaveValidKql(string fileName, string encodedFilePath)
{
Dictionary<object, object> yaml = ReadAndDeserializeYaml(encodedFilePath);
var queryParamsAsLetStatements = GenerateFunctionParametersAsLetStatements(yaml);
var queryStr = queryParamsAsLetStatements + (string)yaml["ParserQuery"];
//Ignore known issues
yaml.TryGetValue("Id", out object id);
if (id != null && ShouldSkipTemplateValidation((string)yaml["Id"]))
{
return;
}
var parserName = (string)yaml["ParserName"];
ValidateKql(parserName, queryStr);
}
private void ValidateKql(string id, string queryStr)
{
var validationResult = _queryValidator.ValidateSyntax(queryStr);
var firstErrorLocation = (Line: 0, Col: 0);
if (!validationResult.IsValid)
{
firstErrorLocation = GetLocationInQuery(queryStr, validationResult.Diagnostics.First(d => d.Severity == "Error").Start);
}
var listOfDiagnostics = validationResult.Diagnostics;
bool isQueryValid = !(from p in listOfDiagnostics
where !p.Message.Contains("_GetWatchlist") //We do not validate the getWatchList, since the result schema is not known
select p).Any();
Assert.True(
isQueryValid,
isQueryValid
? string.Empty
: @$"Template Id: {id} is not valid in Line: {firstErrorLocation.Line} col: {firstErrorLocation.Col}
Errors: {validationResult.Diagnostics.Select(d => d.ToString()).ToList().Aggregate((s1, s2) => s1 + "," + s2)}");
}
private Dictionary<object, object> ReadAndDeserializeYaml(string encodedFilePath)
{
var yaml = File.ReadAllText(Utils.DecodeBase64(encodedFilePath));
var deserializer = new DeserializerBuilder().Build();
return deserializer.Deserialize<dynamic>(yaml);
}
private bool ShouldSkipTemplateValidation(string templateId)
{
return TemplatesToSkipValidationReader.WhiteListTemplates
.Where(template => template.id == templateId)
.Where(template => !string.IsNullOrWhiteSpace(template.validationFailReason))
.Where(template => !string.IsNullOrWhiteSpace(template.templateName))
.Any();
}
private (int Line, int Col) GetLocationInQuery(string queryStr, int pos)
{
var lines = Regex.Split(queryStr, "\n");
var curlineIndex = 0;
var curPos = 0;
while (lines.Length > curlineIndex && pos > curPos + lines[curlineIndex].Length + 1)
{
curPos += lines[curlineIndex].Length + 1;
curlineIndex++;
}
var col = (pos - curPos + 1);
return (curlineIndex + 1, col);
}
/// <summary>
/// Generate a string of function parameters as let statements.
/// </summary>
/// <param name="yaml">The parser's yaml file</param>
/// <returns>The function parameters as let statements</returns>
private string GenerateFunctionParametersAsLetStatements(Dictionary<object, object> yaml)
{
if (yaml.TryGetValue("ParserParams", out object parserParamsObject))
{
var parserParams = (List<object>)parserParamsObject;
return string.Join(Environment.NewLine, parserParams.Select(GenerateParamaterAsLetStatement).ToList());
}
return "";
}
/// <summary>
/// Convert function parameter to a let statement with the format 'let <parameterName>= <defaultValue>;
/// </summary>
/// <param name="parameter">A function parameter as an object</param>
/// <returns>A function parameter as a let statement</returns>
private string GenerateParamaterAsLetStatement(object parameter)
{
var dictionary = (Dictionary<object, object>)parameter;
string name = (string)dictionary["Name"];
string defaultValue = (string)dictionary["Type"] == "string" ? $"'{dictionary["Default"]}'" : (string)dictionary["Default"];
return $"let {name}= {defaultValue};";
}
}
}

Просмотреть файл

@ -12,7 +12,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="YamlDotNet" Version="6.0.0" />
<PackageReference Include="Microsoft.Azure.Sentinel.KustoServices" Version="4.0.0" />
<PackageReference Include="Microsoft.Azure.Sentinel.KustoServices" Version="5.0.1" />
</ItemGroup>
</Project>

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -23,5 +23,10 @@
"id": "ac9e233e-44d4-45eb-b522-6e47445f6582",
"templateName": "CrashdumpdisabledonhostASIM.yaml",
"validationFailReason": "Valid imTable"
},
{
"id": "bca035b7-7292-4145-ae8b-b7216bec9dd1",
"templateName": "vimNetworkSessionMicrosoftMD4IoT.yaml",
"validationFailReason": "The name 'LocalPort' does not refer to any known column, table, variable or function."
}
]

Просмотреть файл

@ -0,0 +1,16 @@
using Kqlvalidations.Tests.FunctionSchemasLoaders;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Kqlvalidations.Tests
{
public class ParsersYamlFilesLoader : YamlFilesLoader
{
protected override List<string> GetDirectoryPaths()
{
var basePath = Utils.GetTestDirectory(TestFolderDepth);
return ParsersDatabase.Parsers.Select(parser => Path.Combine(basePath, "Parsers", parser.Schema, "Parsers")).ToList();
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
namespace Kqlvalidations.Tests
{
public class ParsersYamlFilesTestData : YamlFilesTestData
{
public ParsersYamlFilesTestData() : base(new ParsersYamlFilesLoader())
{
}
}
}

Просмотреть файл

@ -1,3 +1,4 @@
Id: bca035b7-7292-4145-ae8b-b7216bec9dd1
Parser:
Title: Network Session ASIM filtering parser for Microsoft Defender for IoT
Version: '0.2'