From e4eafbc53e67b8fed056bfa625a55ea3a4470907 Mon Sep 17 00:00:00 2001 From: Alon Karmona Date: Tue, 20 Oct 2026 14:56:09 +0300 Subject: [PATCH] Migrate QualysVM (3.2.2) to Defender XDR Custom Detections (V3.1) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9f05623b-293a-427c-a28f-23d9b3fcc32c --- .../HighNumberofVulnDetectedV2.yaml | 187 +++++- ...rityVulnDetectedAcrossMulitpleHostsV2.yaml | 192 +++++- Solutions/QualysVM/Package/3.2.2.zip | Bin 0 -> 62761 bytes .../QualysVM/Package/createUiDefinition.json | 58 +- Solutions/QualysVM/Package/mainTemplate.json | 562 ++++++++++++++++-- .../QualysVM/Package/testParameters.json | 42 ++ Solutions/QualysVM/ReleaseNotes.md | 1 + Solutions/QualysVM/SolutionMetadata.json | 23 +- .../HighNumberofVulnDetectedV2.yaml | 299 ++++++++++ ...rityVulnDetectedAcrossMulitpleHostsV2.yaml | 292 +++++++++ .../QualysVM/data/Solution_QualysVM.json | 9 +- 11 files changed, 1605 insertions(+), 60 deletions(-) create mode 100644 Solutions/QualysVM/Package/3.2.2.zip create mode 100644 Solutions/QualysVM/XDR Detections/HighNumberofVulnDetectedV2.yaml create mode 100644 Solutions/QualysVM/XDR Detections/NewHighSeverityVulnDetectedAcrossMulitpleHostsV2.yaml diff --git a/Solutions/QualysVM/Analytic Rules/HighNumberofVulnDetectedV2.yaml b/Solutions/QualysVM/Analytic Rules/HighNumberofVulnDetectedV2.yaml index 5f90c15a779..5c873929f32 100644 --- a/Solutions/QualysVM/Analytic Rules/HighNumberofVulnDetectedV2.yaml +++ b/Solutions/QualysVM/Analytic Rules/HighNumberofVulnDetectedV2.yaml @@ -17,6 +17,189 @@ tactics: relevantTechniques: - T1190 query: | + let QualysHostDetection = view () { + let QualysHostDetection_view = view () { + QualysHostDetection_CL + | mv-expand todynamic(Detections_s) + | extend + Severity_s = tostring(Detections_s.Severity), + Status_s = tostring(Detections_s.Status), + QID_s = tostring(Detections_s.Results) + | extend + TenantId = column_ifexists('TenantId', ''), + SourceSystem = column_ifexists('SourceSystem', ''), + MG = column_ifexists('MG', ''), + ManagementGroupName = column_ifexists('ManagementGroupName', ''), + TimeGenerated = column_ifexists('TimeGenerated', ''), + Computer = column_ifexists('Computer', ''), + RawData = column_ifexists('RawData', ''), + Id = column_ifexists('Id_s', ''), + HostId = column_ifexists('HostId_s', ''), + HostTags = column_ifexists('HostTags_s', ''), + IPAddress = column_ifexists('IPAddress', ''), + TrackingMethod = column_ifexists('TrackingMethod_s', ''), + OperatingSystem = column_ifexists('OperatingSystem_s', ''), + DnsName = column_ifexists('DnsName_s', ''), + NetBios = column_ifexists('NetBios_s', ''), + QGHostId = column_ifexists('QGHostId_g', ''), + LastScanDateTime = column_ifexists('LastScanDateTime_t', ''), + LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''), + LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''), + QID = column_ifexists('QID_s', ''), + SSL = column_ifexists('SSL_s', ''), + Status = column_ifexists('Status_s', ''), + Ignored = column_ifexists('Ignored_s', ''), + Severity = column_ifexists('Severity_s', ''), + Disabled = column_ifexists('Disabled_s', ''), + LastFixed = column_ifexists('LastFixed_t', ''), + LastFound = column_ifexists('LastFound_t', ''), + TimesFound = column_ifexists('TimesFound_s', ''), + FirstFound = column_ifexists('FirstFound_t', ''), + LastUpdate = column_ifexists('LastUpdate_t', datetime(null)), + LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)), + Result_column_count = tostring(column_ifexists('Result_column_count_d', '')), + Results_0 = column_ifexists('Results_0_s', ''), + Type = column_ifexists('Type', ''), + _ResourceId = column_ifexists('_ResourceId', '') + }; + let QualysHostDetectionV2_view = view () { + QualysHostDetectionV2_CL + | extend + TenantId = column_ifexists('TenantId', ''), + SourceSystem = column_ifexists('SourceSystem', ''), + MG = column_ifexists('MG', ''), + ManagementGroupName = column_ifexists('ManagementGroupName', ''), + TimeGenerated = column_ifexists('TimeGenerated', ''), + Computer = column_ifexists('Computer', ''), + RawData = column_ifexists('RawData', ''), + Id = column_ifexists('Id_s', ''), + HostId = column_ifexists('HostId_s', ''), + IPAddress = column_ifexists('IPAddress', ''), + TrackingMethod = column_ifexists('TrackingMethod_s', ''), + OperatingSystem = column_ifexists('OperatingSystem_s', ''), + DnsName = column_ifexists('DnsName_s', ''), + NetBios = column_ifexists('NetBios_s', ''), + QGHostId = column_ifexists('QGHostId_g', ''), + LastScanDateTime = column_ifexists('LastScanDateTime_t', ''), + LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''), + LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''), + QID = column_ifexists('QID_s', ''), + SSL = column_ifexists('SSL_s', ''), + Status = column_ifexists('Status_s', ''), + Ignored = column_ifexists('Ignored_s', ''), + Severity = column_ifexists('Severity_s', ''), + Disabled = column_ifexists('Disabled_s', ''), + LastFixed = column_ifexists('LastFixed_t', ''), + LastFound = column_ifexists('LastFound_t', ''), + TimesFound = column_ifexists('TimesFound_s', ''), + FirstFound = column_ifexists('FirstFound_t', ''), + LastUpdate = column_ifexists('LastUpdate_t', datetime(null)), + LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)), + Result_column_count = tostring(column_ifexists('Result_column_count_d', '')), + Results_0 = column_ifexists('Results_0_s', ''), + Type = column_ifexists('Type', ''), + _ResourceId = column_ifexists('_ResourceId', '') + }; + let QualysHostDetectionV3_view = view () { + QualysHostDetectionV3_CL + | extend DetectionList = todynamic(DetectionList) + | extend DetectionItems = iff( + gettype(DetectionList.DETECTION) == "array", + DetectionList.DETECTION, + pack_array(DetectionList.DETECTION) + ) + | mv-expand Detections = DetectionItems to typeof(dynamic) + | extend + TenantId = column_ifexists('TenantId', ''), + SourceSystem = column_ifexists('SourceSystem', ''), + MG = column_ifexists('MG', ''), + ManagementGroupName = column_ifexists('ManagementGroupName', ''), + TimeGenerated = column_ifexists('TimeGenerated', ''), + Computer = column_ifexists('Computer', ''), + RawData = column_ifexists('RawData', ''), + Id = column_ifexists('Id', ''), + HostId = column_ifexists('HostId', ''), + HostTags = column_ifexists('HostTags', ''), + IPAddress = column_ifexists('IPAddress', ''), + TrackingMethod = column_ifexists('TrackingMethod', ''), + OperatingSystem = column_ifexists('OperatingSystem', ''), + DnsName = column_ifexists('DnsName', ''), + NetBios = column_ifexists('NetBios', ''), + QGHostId = column_ifexists('QGHostId', ''), + LastScanDateTime = column_ifexists('LastScanDateTime', ''), + LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime', ''), + LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime', ''), + QID = tostring(Detections.QID), + SSL = tostring(Detections.SSL), + Status = tostring(Detections.STATUS), + Ignored = tostring(Detections.IS_IGNORED), + Severity = tostring(Detections.SEVERITY), + Disabled = tostring(Detections.IS_DISABLED), + LastFixed = todatetime(Detections.LAST_FIXED_DATETIME), + LastFound = todatetime(Detections.LAST_FOUND_DATETIME), + TimesFound = tostring(Detections.TIMES_FOUND), + FirstFound = todatetime(Detections.FIRST_FOUND_DATETIME), + LastUpdate = todatetime(Detections.LAST_UPDATE_DATETIME), + LastProcessedDateTime = coalesce( + todatetime(Detections.LAST_PROCESSED_DATETIME), + todatetime(Detections.LAST_PROCESSED_DATE)), + Results_0 = tostring(coalesce(Detections.RESULTS['#cdata-section'], Detections.RESULTS)), + Result_column_count = "1", + Type = tostring(Detections.TYPE), + _ResourceId = column_ifexists('_ResourceId', '') + | extend _IngestionTime = todatetime(TimeGenerated) + | summarize arg_max(_IngestionTime, *) by HostId, QID, Status, SSL, Ignored, Severity, Disabled, LastFixed, LastFound, TimesFound, FirstFound, Result_column_count, Results_0, Type + | project-away _IngestionTime + }; + union isfuzzy=true + (QualysHostDetection_view), + (QualysHostDetectionV2_view), + (QualysHostDetectionV3_view) + | project + TenantId, + SourceSystem, + MG, + ManagementGroupName, + TimeGenerated, + Computer, + RawData, + Id, + HostId, + HostTags, + IPAddress, + TrackingMethod, + OperatingSystem, + DnsName, + NetBios, + QGHostId, + LastScanDateTime, + LastVMScannedDateTime, + LastVMAuthScannedDateTime, + QID, + SSL, + Type, + Status, + Ignored, + Severity, + Disabled, + LastFixed, + LastFound, + TimesFound, + FirstFound, + LastUpdate, + LastProcessedDateTime, + Result_column_count, + Results_0, + _ResourceId + | extend + HostId = tostring(HostId), + QID = tostring(QID), + Status = tostring(Status), + LastProcessedDateTime = todatetime(LastProcessedDateTime), + LastUpdate = todatetime(LastUpdate), + LastFound = todatetime(LastFound) + | extend DetectionTime = coalesce(LastProcessedDateTime, LastUpdate, LastFound, todatetime(TimeGenerated)) + }; let threshold = 10; QualysHostDetection | where Severity == "5" @@ -25,11 +208,11 @@ query: | entityMappings: - entityType: Host fieldMappings: - - identifier: FullName + - identifier: NetBiosName columnName: NetBios - entityType: IP fieldMappings: - identifier: Address columnName: IPAddress -version: 1.0.4 +version: 3.2.1 kind: Scheduled diff --git a/Solutions/QualysVM/Analytic Rules/NewHighSeverityVulnDetectedAcrossMulitpleHostsV2.yaml b/Solutions/QualysVM/Analytic Rules/NewHighSeverityVulnDetectedAcrossMulitpleHostsV2.yaml index 68d9f762d3e..aef21ba7b42 100644 --- a/Solutions/QualysVM/Analytic Rules/NewHighSeverityVulnDetectedAcrossMulitpleHostsV2.yaml +++ b/Solutions/QualysVM/Analytic Rules/NewHighSeverityVulnDetectedAcrossMulitpleHostsV2.yaml @@ -17,12 +17,200 @@ tactics: relevantTechniques: - T1190 query: | + let QualysHostDetection = view () { + let QualysHostDetection_view = view () { + QualysHostDetection_CL + | mv-expand todynamic(Detections_s) + | extend + Severity_s = tostring(Detections_s.Severity), + Status_s = tostring(Detections_s.Status), + QID_s = tostring(Detections_s.Results) + | extend + TenantId = column_ifexists('TenantId', ''), + SourceSystem = column_ifexists('SourceSystem', ''), + MG = column_ifexists('MG', ''), + ManagementGroupName = column_ifexists('ManagementGroupName', ''), + TimeGenerated = column_ifexists('TimeGenerated', ''), + Computer = column_ifexists('Computer', ''), + RawData = column_ifexists('RawData', ''), + Id = column_ifexists('Id_s', ''), + HostId = column_ifexists('HostId_s', ''), + HostTags = column_ifexists('HostTags_s', ''), + IPAddress = column_ifexists('IPAddress', ''), + TrackingMethod = column_ifexists('TrackingMethod_s', ''), + OperatingSystem = column_ifexists('OperatingSystem_s', ''), + DnsName = column_ifexists('DnsName_s', ''), + NetBios = column_ifexists('NetBios_s', ''), + QGHostId = column_ifexists('QGHostId_g', ''), + LastScanDateTime = column_ifexists('LastScanDateTime_t', ''), + LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''), + LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''), + QID = column_ifexists('QID_s', ''), + SSL = column_ifexists('SSL_s', ''), + Status = column_ifexists('Status_s', ''), + Ignored = column_ifexists('Ignored_s', ''), + Severity = column_ifexists('Severity_s', ''), + Disabled = column_ifexists('Disabled_s', ''), + LastFixed = column_ifexists('LastFixed_t', ''), + LastFound = column_ifexists('LastFound_t', ''), + TimesFound = column_ifexists('TimesFound_s', ''), + FirstFound = column_ifexists('FirstFound_t', ''), + LastUpdate = column_ifexists('LastUpdate_t', datetime(null)), + LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)), + Result_column_count = tostring(column_ifexists('Result_column_count_d', '')), + Results_0 = column_ifexists('Results_0_s', ''), + Type = column_ifexists('Type', ''), + _ResourceId = column_ifexists('_ResourceId', '') + }; + let QualysHostDetectionV2_view = view () { + QualysHostDetectionV2_CL + | extend + TenantId = column_ifexists('TenantId', ''), + SourceSystem = column_ifexists('SourceSystem', ''), + MG = column_ifexists('MG', ''), + ManagementGroupName = column_ifexists('ManagementGroupName', ''), + TimeGenerated = column_ifexists('TimeGenerated', ''), + Computer = column_ifexists('Computer', ''), + RawData = column_ifexists('RawData', ''), + Id = column_ifexists('Id_s', ''), + HostId = column_ifexists('HostId_s', ''), + IPAddress = column_ifexists('IPAddress', ''), + TrackingMethod = column_ifexists('TrackingMethod_s', ''), + OperatingSystem = column_ifexists('OperatingSystem_s', ''), + DnsName = column_ifexists('DnsName_s', ''), + NetBios = column_ifexists('NetBios_s', ''), + QGHostId = column_ifexists('QGHostId_g', ''), + LastScanDateTime = column_ifexists('LastScanDateTime_t', ''), + LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''), + LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''), + QID = column_ifexists('QID_s', ''), + SSL = column_ifexists('SSL_s', ''), + Status = column_ifexists('Status_s', ''), + Ignored = column_ifexists('Ignored_s', ''), + Severity = column_ifexists('Severity_s', ''), + Disabled = column_ifexists('Disabled_s', ''), + LastFixed = column_ifexists('LastFixed_t', ''), + LastFound = column_ifexists('LastFound_t', ''), + TimesFound = column_ifexists('TimesFound_s', ''), + FirstFound = column_ifexists('FirstFound_t', ''), + LastUpdate = column_ifexists('LastUpdate_t', datetime(null)), + LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)), + Result_column_count = tostring(column_ifexists('Result_column_count_d', '')), + Results_0 = column_ifexists('Results_0_s', ''), + Type = column_ifexists('Type', ''), + _ResourceId = column_ifexists('_ResourceId', '') + }; + let QualysHostDetectionV3_view = view () { + QualysHostDetectionV3_CL + | extend DetectionList = todynamic(DetectionList) + | extend DetectionItems = iff( + gettype(DetectionList.DETECTION) == "array", + DetectionList.DETECTION, + pack_array(DetectionList.DETECTION) + ) + | mv-expand Detections = DetectionItems to typeof(dynamic) + | extend + TenantId = column_ifexists('TenantId', ''), + SourceSystem = column_ifexists('SourceSystem', ''), + MG = column_ifexists('MG', ''), + ManagementGroupName = column_ifexists('ManagementGroupName', ''), + TimeGenerated = column_ifexists('TimeGenerated', ''), + Computer = column_ifexists('Computer', ''), + RawData = column_ifexists('RawData', ''), + Id = column_ifexists('Id', ''), + HostId = column_ifexists('HostId', ''), + HostTags = column_ifexists('HostTags', ''), + IPAddress = column_ifexists('IPAddress', ''), + TrackingMethod = column_ifexists('TrackingMethod', ''), + OperatingSystem = column_ifexists('OperatingSystem', ''), + DnsName = column_ifexists('DnsName', ''), + NetBios = column_ifexists('NetBios', ''), + QGHostId = column_ifexists('QGHostId', ''), + LastScanDateTime = column_ifexists('LastScanDateTime', ''), + LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime', ''), + LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime', ''), + QID = tostring(Detections.QID), + SSL = tostring(Detections.SSL), + Status = tostring(Detections.STATUS), + Ignored = tostring(Detections.IS_IGNORED), + Severity = tostring(Detections.SEVERITY), + Disabled = tostring(Detections.IS_DISABLED), + LastFixed = todatetime(Detections.LAST_FIXED_DATETIME), + LastFound = todatetime(Detections.LAST_FOUND_DATETIME), + TimesFound = tostring(Detections.TIMES_FOUND), + FirstFound = todatetime(Detections.FIRST_FOUND_DATETIME), + LastUpdate = todatetime(Detections.LAST_UPDATE_DATETIME), + LastProcessedDateTime = coalesce( + todatetime(Detections.LAST_PROCESSED_DATETIME), + todatetime(Detections.LAST_PROCESSED_DATE)), + Results_0 = tostring(coalesce(Detections.RESULTS['#cdata-section'], Detections.RESULTS)), + Result_column_count = "1", + Type = tostring(Detections.TYPE), + _ResourceId = column_ifexists('_ResourceId', '') + | extend _IngestionTime = todatetime(TimeGenerated) + | summarize arg_max(_IngestionTime, *) by HostId, QID, Status, SSL, Ignored, Severity, Disabled, LastFixed, LastFound, TimesFound, FirstFound, Result_column_count, Results_0, Type + | project-away _IngestionTime + }; + union isfuzzy=true + (QualysHostDetection_view), + (QualysHostDetectionV2_view), + (QualysHostDetectionV3_view) + | project + TenantId, + SourceSystem, + MG, + ManagementGroupName, + TimeGenerated, + Computer, + RawData, + Id, + HostId, + HostTags, + IPAddress, + TrackingMethod, + OperatingSystem, + DnsName, + NetBios, + QGHostId, + LastScanDateTime, + LastVMScannedDateTime, + LastVMAuthScannedDateTime, + QID, + SSL, + Type, + Status, + Ignored, + Severity, + Disabled, + LastFixed, + LastFound, + TimesFound, + FirstFound, + LastUpdate, + LastProcessedDateTime, + Result_column_count, + Results_0, + _ResourceId + | extend + HostId = tostring(HostId), + QID = tostring(QID), + Status = tostring(Status), + LastProcessedDateTime = todatetime(LastProcessedDateTime), + LastUpdate = todatetime(LastUpdate), + LastFound = todatetime(LastFound) + | extend DetectionTime = coalesce(LastProcessedDateTime, LastUpdate, LastFound, todatetime(TimeGenerated)) + }; let threshold = 10; QualysHostDetection | extend Status = tostring(Status), Vulnerability = tostring(QID), Severity = tostring(Severity) | where Status =~ "New" and Severity == "5" - | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), dcount(NetBios) by tostring(QID) + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), dcount(NetBios), RepresentativeHost = take_anyif(NetBios, isnotempty(NetBios)) by tostring(QID) | where dcount_NetBios >= threshold | extend timestamp = StartTime -version: 1.0.2 +entityMappings: + - entityType: Host + fieldMappings: + - identifier: NetBiosName + columnName: RepresentativeHost +version: 3.2.1 kind: Scheduled diff --git a/Solutions/QualysVM/Package/3.2.2.zip b/Solutions/QualysVM/Package/3.2.2.zip new file mode 100644 index 0000000000000000000000000000000000000000..e0958d0c48e1b73aaea1c69b375d67cb1badf8ac GIT binary patch literal 62761 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

p%y+lv>D7N3pl zA7qY#MLWv6f=IwJ#1wTgZY2XH$D`~{x>DQndBGP9-o7%o6IWn|F;xPo{WVOu-yy=h zx!XTx&+v8hbl*Td$)hIO68nXrk6FjZ&}}7j5J0k6ZxY;AO|)dm2j%chR7(7?o?}XL zLq$PnvQVKMxLoCM0Ocl5TZq;a0*3kG<>Ov8CAb@3Sm+(q21I!pIug|HVkhyb1;@cEOBdF z#V``+8S{9h-y}#FR{0g+FXg*G5<1chfHqRsjGs1iWYv~iWLq3<&SYjTxsH*t4G2AD zF8(ZA#$t3@WmfFkc0ib4h|JNXFNra&iHWRlW)mn4g-c=3b(}c49_n?4h#n8wkDgs@ z2&JcbA0z3;IAPx_^e`RTSc;zhBHP)vfy4Rbl_Vf$q=TxPB5_;Bcz$l zAP6G!TMp-g`xMzQYNU0NY-}T!8AEJuYefHKo`s#Pw+6MnKpZF1DT|68erBuhx^$As z9UK85i>`wIlMV#gI2^T*6XSBsFBXAR61Z_`ZdgiaV*5KhLL*$#ZHUd*3lqli^j7Tn z3#S3BqqYN6Q*m~G*D;uJ9>XNakl5^nhXDlfge(k9EmYLwGMBjokJ%mOf?+Hwmv?bG z!=*rIMUfUwp4sU4DSBU}uiKfvr;*%zTwGkc15h)5q0>`8($33&fRs{DB>dI`{;X0g zUWl_Whk#_NXn*fp7EUPhus6dfIdwTNF$)1@LLk2ykR}jgW!iGI#ziY{SWEe*SMVh) zEo#KxJGp!vg+6Rc9|A1d+V0uI2gduYx!YyDKV_$sw9!5UMCdf#vWk~zal_-+UccC7GPc+GW=}!lV$^25<3%fp6 zjmJ@422~d~#x}8Ofe4PKNBBHmH)QSzJT`=#`J3O=n)%0GH;Sq7F@J^W!7MSF8|QA~ z^HK=c7)J8OcL(Xolx`bTPgKA-N*UbH1Z|dhHmkn|$Js?W0fsgc$smmN#h=v;Ddml9 zD-56}*QjxN6bA+;DWii|IGgKLgte@I3?Iv5F(;_~NfGPz_4DVD%WiP7yK{7OBTpn9 zzE9<}P*24c{3qK=g}<}6z=M5Hljcs*t5;Ts7L~Fp%?Uq(yIwzwnKvrE5-egp3^jm?e~`1)b4 zq071XOxCjhD=seSz!*!MyckjM9kE>r1i?R{OHs2~$d#W<2)?}oW5HBL1w~eK(zx0 zl^skz^bT%U=yM>v;=zRgvRkA3;Y1Zua>{D>tGz^u!_oDZ7n-O@w-F6)g?N&wtFxtz%m~c)=5w>C^iVwN@u6h`WM>N3 z9)smV>Wzi0OoyzV8?p@IuV9EyFmKxe=^gX;SYny_lu7)b-x`ypmJKKL^-f+JP^O`>S|+2Up+lR9Lb< zbE0^wrK~m7K%T!)9188ni`Z>8v6eDiM5Pkm5W}-S<0}WGoQs8$UsCHU(>P z{f52IcMHW}28n3%Tv)D5wD?D5tPX`?yJS+4+kGZkhS;W8$I&>39MiIBUN5hob{CF) z_uMHPyRn(XY!!zx&?yBGU7sOP)x&BcYXlEe1v`s{a(sxzZ&EvkOlj^pbIQG*Ld#U4 z_TE>ox072$e}DNEZ=>s)$9fM3k}wZ9Xto{;u;@@xQ?gPR4iMQKUVdwX_>JZ)A8~_Y z5Snp0=)_-;IEv9pm9Q|@O`x&oo@nlr6^wgAXn)yXv9a3i{!08mP1D|Drqab>24k19`i zss4g%H&bSgGtFrHs3ae}I%LJ&LHMUUm=WBevSbX$1t)PL|)iQBVSEqY9&os zUG0yQh(7ZE?Ccj;H5H2}^(TsNkvt^+|CDx?L6tO3zQ+f5cXuD$-JOHGySu~S z?(Xi+;0*2ucOBey7~C$;d*9vIy}RFTpNKkH71{qU9Uak`^{dP)k_bV67b2Cd4FioA zag0#y+-A9389yE_IbWvLjPy z-ZySEM5{RjS`-)h8PfWmNWE)A;)zewZzU@|q`VT?`ihb(3%Z<%MJT0S9t%gYhdQ)j z>|fnjAIgI#H+$0J19EuBswC4yOc?M^vt4_)bf?Q#_OD!->G`70jMcGXHKq()>+8LH zq0QRuCCyoJk$m3AO9DDSU9fd?;fik!xd`PxSNGRqUM+n1UdCH^d-5(MSi$+yRD66f!k1wkPRooSU>t2y=a>GH`+WNwKPNz^ zu#)_t*^Zruj5nnv&($a91U2-*IvaoM@?qG z_u;d1bl~BO<74H_O;_#UF<9!657}d|RPOyuQEmNAF=&Yt5Nv}wakkhUoq8|zMqn)< zAsps zYip7YMo4#i5<)W-Alb0 zA=~M;zFQU0LsWHll#X5Y;6vB*Q0soRn=~t~fKSn25h!XPsnQPIMx3fbZP>n=DyQA~ zvTMjA9Ky|22xm1a3;v7%ycIRNMkwSy4kG~=Y2_DOv>%kbt>Nb$Wd8%eetPAPiml#M_8(Yc)?RgXTNgR(b|(N-Vsj z8=+}99(g(@4%QTe`29C7wJm%Ut*2^B`tYV(>NqFdlzf$T3NoHV*C>J5&n@MAu^i_r zJp@lme5RM0{mX&LR3$4PR*98;fYw~l9kYUv3~6TTYGRt9Pp2oJjG{CoG}d`#>B@4V zfB!Ph`NyzvN25}UIcg|x6nq*qcqaUN+2%>g#Zj5vn#B$B9iBywWhYCHxaUrlk5(x| z=^30YX4Db1Nsim;T{zQp%W548-M6TvUo-&gh|L%RQkR@P1ihZ`(8IYMVHQZzIi5wc zRN1sY{?uEmI4D}`|uH3a&mV!LL+X_zsTTJMZkt!tuw~`QWwxF8u zaFaG6P}3=++k_CAf;OqiK8VT(^$Evh8V$*b1bLh(UaQqehNR4YSHu&=S`3CvBEVZ! z=S^t=_XPv=CeA4Z0b*)RwZvV6VDVpv&aq~|LyI6WOu|U>gobQZmgEJ0@-!*-9&N{AssSx|up>EbuyA4-9%z^_(5fN=rVK9wCgkRA{AOpav28VM zHmYVTHJ-9FlRXB*HRH=n>Wdob*V4_f3XaV$(JLM#6lG%6gRq;PPr@*xtF-tuuuw^Z zic&t@H@Ejo81CB?3H#eOFV>yE7uvD^z9jSKjhT9Kyl)abhGu_ws!U`CWG7RfI(82i z8W=d}f!Up$sdKDTV=hrFw}<4^T3V=4ThS)>ls@Qubc&0#rnyltI6*R@mbra8?C3Pi zH|jj;Zw>7GM-ds0M6*j6Kx6xpz)OHlD}J@7gmWvK{OcD)QqF5`8ro06Z3-VDI$|iM zPZCtnM+GX%`F(S6lH25HyjTX~zJ~cK6-4efer zk5TM7EB9mCxg7%IJVZSO6E359WpyW>?%HP*5^0mt>VM+_lt`Zw|6q#8gU6gpPubu2OjKLLjb41GY1%f~%WJ9w7yKdz-gnuRM>_dG8L0 z+IkkQT71r31fBUlFR14;-WczL{iPls?Oz!-gSH%OmTn^nY~om<_vp=p*7RA|E&Y`d z?$DSE#LmfAvFG7md_QVQDXP+i2m6>z-Rjiqho_C$Z;}`KO&T^MiLlZL` z=!k-JJuPb()ay`RY-*V&O4RI^i}*C|SKlNcL3QnBR#ihgQ)?A}z^(0X@CNepxzyqB zOR#$SD=jgd=Hfop_!Kx}mN|NZ?-dK{@x8t8J9+!nbQuN62OboOX^Nm3Qb-0GOA199 zp~?yMk0g*zq(M|CIN7UulE^9~E~gkWHms|dMO#*aHqK)rsI~6Jbr;zQSofi0skKV! zhQOqhOsMZ_$g3-tPIf^2L7Sx`C`Ck&Wuyl&$pWxf2vtsSfU<}b)kAKC7+i7$`E8&m zJ@n_cyy4D`uk9GG6D^2U)^78!gO|gJk7DrO;YzWTn|j>%^ze4%EeaylDTL=!-ADH? z-Cy6IE3RlPoWra9jyovpEB`Th$`&}a5Z|DS$I*;#8{8mOsk|x|24BM}Yql=rBEAI2 z*{FgmJ7vBkH%?QFOUuc`#>7d&#>9)p#>5S&!K4ZhpJfmip3r>)bA!Ybn9?&kaA+*y zIp=t$N`eX5Jq~c`h@INO{3gIZ~<*kccV~z_~yFCdXN7UP++xu46{!5of z{rP2E?*7Z|?OgWP5PyAE|C@kdDS+?gHbUU+MoLi|R7bMtp1}l~{FeopEuQLVL2&98 zraQbW2OUOCR!em225n9jr`71a7t8s<(F!X8nwocKD^Oz1mnFf|hxkd)3kF&n&t>+g z=I6&~@;v4G{|#}U?fQNn7RT5)mCJK+6YHCw{>XR!iU62$eLCK23ItI}Gddw3WdXie zGkhKT{)nIUM!jyusD-SmW-w=D9wn`_mOJ-yC`^P-U`7Ku$C>!I{bN%3l7yjDQkjzt zikMyA2( z1L7LQz67JRV4E;}17^`2&Fbfx zAL`%LKs%sYgR}Ps&+^5RqvgMXVp+Qjcw#R>h4&f@4%JH!;h#(>H!XolOo<4&M?Y!A zgOx^Lta%CZ1lz^fZc4Dg`(MRAi2Wb4L4CNKZS@S}xmd=BR-uNlzuPhoN_b@+XQ@L( z-4sX0zM}@X%1T;wgr}MYl^i;zkM>g2Tq?`0Umsie;vtS)CmlfNx`Jxy9t6~VL?%S$c(GA6|*6S0Ioo<)Z$7CX{vc3WAT z^F_Q@%$Q>)T8r}z;Z~ocACn~5YnFAE%*XHdt{%}C^fFmDA_31;vn7eGkE%cU6?Gef zA&-lEC`YPu^GT(|o4r!_``9tmbQILP3jRk(_!c|7C2Tn&@(G&6^tCQB`HEyyG=FDv zhm<)|#6gE--OXm@cKTchsIq+%ngmxsy#%nwOmBsHShU_l z3L3ds3eTN9+6w{wd=H6i0-J|I1hSPK)`XnZG;P^+mTJ^toB+}4-ES9KeurRqiKny! zW7h()x1YBg0%70Grm_;LY-EZyJmZ8+4_zKGWJ*;yJPa(c{3bhNKSunIrl}Bda*DDx zqpUaOmblZc*Hqr^_`+SbP3fQK8%-6)RH!N7usX#c4TiqjOjw z18>jCo8+K3vm0?1wo9-n?l?!_f@(Lo|IDOBD&x~Q3^LS#xV^QSfc^Tq8 zdGEo{hS-}OGbF>{JVZ|)n{en|92n4;!S+_$rl{a=b;7-P1pD#^ zvYs_sIZ5kG9BLX0%J*)fCym>w#RP)JD2^z{-1>1Nu8KgQV1+>Gwe8&h*Xr`dBp~Vg zKqX#1lSPFL=e+T@wPrmKW0`f|j@qMQT-jrDOJV|J{(AdJmj+#+jX62hv~^ETBk;L3 zUngviGL^$CJ^gTYoI(AzKZc)l?e^#LzRhMneEsM%ngKHd$qK81;)}_yv8TXTZf+pM z->Z=2x`1>Vn9QPltDnI^-WaE2P>Kf7rD(B*YtEOV8+!W1!xnf>*U(jcyj>_T0;*HQhn8eV zejM0}WnkmX(^tx`WrnCkXcApLC3;SL(gir~PkR9uH(Q~DoQk);!E^!*y4XxJ>4weF zD4UmQN+kP{j?0(w;rrw6OUSGVyH9^!NI7-cW)acCH@GJ4>zL|7zCKs0knuDoq%nF< z0Yh>ayRo!Q@+lfgGRzt{P=lYIyNcsCO*yRVSNL|(>I~E9hX}Y#gA%qTfNHfCfn zyWa{%mJ7`-n=Fa>GS6X6(-6sWo&R_224&-bVx3RVhpET_U*qJshubavYo7?NQEP}E zsZ}L4oIYJlSxW#g?1q0G`QrOZEPEfLKv%6Y?{j~qNKZB!uDN*<*m&%PuBMs zs`&Zst4T0A-`4plQxG4Yt;Z!wPu=L&siEf6-Zc51gyl%<%65ckYx6^d?rL+GRalo| z?c_&G*L!*7Rc~GN{tF#m7=OyCE8ml=9w=^I6-lkTS^ys*rbKofC>WqHTQjuIc&Xq) zM&!qYNRNnEJw(3rx@pR9ya~m!ty8yM&UYc7n6p>WdsvjU5;ciezXCrDOS^ewvPXf0 zF4V7t68h(w^ zZtm5-ku4TqrntR(^(K>dv(BNwHtkIK;9ZiH_}Vb*igRQ1p+Jtng>gsyF>;rt`P1kQ zOyRvD)?|9u{@U;#@x*LB`p4+)3uCPlqG({QU1r_Of?_ZU9A|TO##Qyl-#f=QIym>% zY6xRq3k{gN=crJZZRPbhcgCFv9{RMAxOt1$+n*kkO&rnPZ(EQHdG0f^C#w42>Ho4` z62bMn@rUW>#dY-e?!0Bx5h>T+GAyPiV# zt=VEtu}!kWM53c-!{vaztfa+M+9p~3c$lj8=hypLHqFS#c4hc%&B=7>K9m?=dh&r7 z%PY4|QHz-Do}KP~*y2oinWP?m>Mqw7eT+zX`OC+|eVg`Qxv~zZ3)do~#|+ereIn z;^nbYZsy@DxLKQ zH49E{Ky>luAUVpqPyY~eR;hY{&w6SxO+m9ORr6XA?hNOz2Vr~k#3Z#Bbw>JFh9K!M zpQlpkaqv3y2^2bGU}*%L5h>X4mb>WWB7L*_)193iYdgN)-%zIjZ~k9ZTc5r<|4Z(C zjA#hjpiA(8lbF%?;HiR6`UNn&P$l9|eh-EaJZs%WYQ{c*XAx+27{xoc*kQIc4_Z`W ziM`z&SQmU;g!k-^rh7Es%a(kP;X5xPY6<;>j30xoVRl5D4gDKZRU9zOF3qH~?!$wL zW?&VANh+sq?h>=qHEu^jsfku?6gh*YN26=a%3Nr2x5fjGw@F>=*3O+9~8;yTm<-UcXo#JUH0T-{F1@m z17j3E+nx*-+ynQ`fb>bk+a8MlVtXE`GL6_nZ&dlFId}*TWQ8*&A*Ye#b|kl)R%pcpdTGs2|1t)gg{xJ9&0hD)l>M2Gu%bz9(f17Sg4348yp?%fPTPA#@8a#J$ zNH0N%40dMWBhs}qRMHt%FtbeeNUgjl&P{C#X%Y=-}0_I+bT37RxyP&rU#&_wdCy20~g- zPKq9ZdBs{$Atla^+Pdw+0*QUX@?+Nto!hGmD;$QcIx0JHCD!B64?XZE zd^-Y0Oz_PdPyFh2;z=M`@G>0YdWM`hKtJpmK7mG;jxuS>C|?wEy}ts&6xu>ccmzx) za?^y@4m*>SdkUb(g_%bF1-9hsv#<8ob<+|(s(wN6= zr1HA2m~SnP@yRlZK4|Kj?RgzK=}i)>*nBhbsg-9{MjSaaQ|*4*V0cdHU(8hGIP9-k zvq>_)0vi3Hw?3i%VNAEEK{6<_nWl+;FDuN&nF`rTTUVX*{NK5!wxo z$ri0=g9=rG;n_H8gD1<{cbVo!zu==U@TysO(~YzJpsTYkVemBnWak&@kwlcC8cH9DT*ky z&*ORS?-SOqAz4nZvRJD?8TX0w7W=2AV8zW8uTNg3w@%M&%8%NNm%tRn)BEkT8%oIuwA#wfV zxDR=&Tb!)`MH6AgZhaB&;DVro!+Vn}dGPT!wRhA>vFg;D z3jf0C#MObnx#MSO;*p2{JF(U|+nzlt0Qp6%aYXDMp%!D~BG$n3^lP85ZS>soA$;W~ zsb!~Tt^j(4B06D>RF;;`aU$Xe&cuFd-LG)B9;g36Yv^w2{{vdXfU#Cej4=ZKwA;^` zlTg^d&xPInCBm7&T>HyxwXl%o7H91Jt0YmFf_LnSM!2;gOYRh6-^rnvn2q5D5Dk5sE|~517Sp0iiRXvf&E~D_3YlHg0vx+}6D| z_&wJ6KW)ONm~PhE`8yqwbY30@|A?dizpyw~W+1p|a^mUli7ZuP;vUK=<95o_R}~yNaGs%oKzPBr zZ#-n&74w#Q{k5AevO}%^0mW$;pCJ=i4?h^$gf(uSBN}}bwC@%Tlr(vKKve~*iIUkE zb4@MOqp=)A|H2#95$s{{(#7~r-3}oY7Zc%|ZNeN@N>NG|6&H41*;&RDd9EsP#P;EI zYKN?>Hv(Wq@rshlZH-A`YTq1SW8W`VpFAzbxgguWxXi`?3_)Z=F`bQt@POUU5Zs12 zq$7=E{HnG>!Sn1EXYi+*-(pDua^ZOB@NPdz{*2lD7Z-O84t~V{Oi&1MO>sqIY_UUe zvbG#yum(npzk~iX)FHu)$h~IFZR$uGiA}o1?Ggk(tw&~$C?EXYjV)i4Kn7olTp^fe zTEx`x2+aArTG|p-36Oh~{fg>JVRv2r3O{SpaLCImz9X}av4=3b9Z){L8wnB3PvX+Q zhDO;-t3(wV@09!yB0XAC#_#s$wVKkSN`R&bmi$lG<(&wWZAKL$x2bd41Sn$l>H!^d zc*)T@cb`P>{j209%9Q*VcgqkBVCx1B`Xk zi=4KGMSp00myHky%E}Vk^pW4RiN8XS604&QlD(?Z9=-Sv9;+=7S?q>(i7=p(Z~1ua zF8qp8$HpS#HB#0TrW;kOD|88kEn}&uf{HktVpN2z{eKc}N2%tPzqcgQ0PH~3dPX<- zdH`e@d$1Z3ULdo0t7%(mp-1e)ZOg)Q<=sp9N^XESv`ma)YeLKeFLvqu*(ye{+9hNO za&i5MlrQXza0X#FB~xoR(<1Q$occ$A5a$h5$hn4$EnU2i%dzDnm0D|MO zY-B9*-=6W=lEQydlvTGndnqj|cj>gZRYxyPN~)76C=tHvGP7y}itG^yHfn$FG4g$b z@oF>sydO;yy&9;k>Pqd&RX^f5o}NA;%a4iBKZ>(6k^IYw)qSJt;J6(s*x=ddLI0UW z$jLjUVA!8rN5I|A8}|C+b<#V5r9@m10aKsmtPZkpx|H_XAK?@$8KP-Vu?2}Ltg?FI z4Lq$tFt;T~aWf)9O~C_cvf$4hh=tgw5ql5l-*nWQ>-@aHCJohk_~&E52NNgd zUs&`V1(3Gc-HZ3!^1YeFjt0U8eTQD%_0Mzuw-z~qfn`)|%0->43B2hwUzau9*SMe~ zmCk)ZuHTLa)ybGo$k>anpl)Lf{omCoi;6)Fgj}pzbf@MB+HS>_^U+PQG1;!3kPgr+ z0*3ev)#f0iNX~=O;c;?!mwC#?HC%D@#>zN>r$0EF%FD|s!$kP;Olt$naB3xGM?}s* z$H#o_SL6Gh0CrNdu5#QwY;0;QhUamgI~Dw#AKIIx8qvU^Vsv$jqKgsX&@r4Vt)Dl# z39R8^<3?ESjLf*xcwpf_{Sj%(WUL(6ZPE$q8}+FK_e*s#@QDMK<18Qi!B3pl<0d$g!y~a-^Ob zZVn9$IMYF!&Ze5+0d&dY-<$_SUgrD$;;i+qyE5NG3N3X|q!nRbn~ZB#9t4_B+7a_c z$6HjN<#eL<=_7CoM<`E(TONv-uEB6{`G_%_1sYn^1LQS%Gqjq!nB~3`uy$+%Q4- zCh#P8^$?Lh0}Yr0!$<#YTlRK96d-qstms zxcLfmffuZNplLCXo@XOJbnfZzX4ff_!X$9tlkLe1ZO*K*l z`y$~& ztAVB1<+C;BIn zu`o6S-c(~vfBgw#+x{}CMb>Nzp}37AW38gnF)a#=s3%n-*nP`~uE#5~UclVdcx$K2 z>zJ5Ct@#MXN_BAaApgy#fw@&?r$de@{wI4*dsoQsZQomy&1tqTAk3jcC{w3@bU@+B zY!Sm<7%TRM463z<{#IDj%UjQ>T~_|=u7=J9%x9oFk(zHjFe$`B>FJJfbROzFvobPH z0k498u4XXC-lffvZZ`F{(Phy|sS$>27{FFw!Y|qkuG+Q(x~-rEDa4?-hFwA!SMsuX z*F!O{tX{H;?oTmEEt-rLQlG@QO06qd{2XdXyeS#{i0{S6!*%d$i>4i_G(ji|LdJQ~ zxd-lT^3!p06zZ+YNVK<7W(HQ1^w!V}7c)k*3p>#p^#kuSyH7IWlnQ&(XBu$1N*!b^ z-!^8nRc$JXCRHHHe-s>!{(?NUUmBP%ZS{Rso7%t^^5V6dd_ zSwgZk_;OjS`skA%aQ`PqefJNaE-+blNED~nK;>LKIuv(62vei2RB_IL7|`*>j!uT~ z)aJeFR7_y=C2GLQARLz~LB0$ik@rJm4ADz7*?x-DGDEtM)xmx+s8}&O6Pqt8cL;`I zX?@$nDgiP{Lz|z`ev=XNc2fv2Bb-q!Bu%7Lz#xm0&B-TKt1Ebhsa(wD5fra_b0<`U zdgL@Bta1f~M>A+@z>HmsRp-`~V5W&(^f)m3JJPaYY3P@EIM$C0}nM%y(i6NOr)s3hp<6{p=@ z;uAGE{n<@aiO#FaK=BLrgGB`)c|4-F?S-8c_}y1Erndbhl}8Cr9Gg>xY7C_=;GZZHRFT=p&7)}P=G|6f=>Fq_>8+Q=dPbhAde3Fy7C=G&BBlfA+R_pkI%8P(6>-9QkdMq-VY_M7CzQcbqBWFW3nNuqk4oAIOWOFM(&w)dbYG&fF|JKd1?Ykq_S|^0bHr`UoQ;yF#6tIh z`AH5#FWa0tD}zKiu=8fkb(OCoPqB#j!uft{`0{N@4s~Jl)NuaYb@zJ6fr3TtTH(6d zeJ)onw7LKKU6m;xeoH$E;OFTlUQO7LH9Ha$t$1<9LW$;GZxQSy(gwO9JpuWOqn%E5uJdc1`aYCX!1tO%)RPmYF;G87`u>$*LpZmq z00L*EnduI-(Y>Opi$6S=ux=h$D?L+MkM4SYKhtvrS;Ck7`1Bv0?7zZ;tMo8O0F|sI zlFot}JeX}D)eU~uoeaWH^f$urn9TbXgPH4FIHf|Gg~6iBL?Ur#N`2~enW%tbGvG0w;yC*d=HZj=eQBTZ)34i>(@bN$V? z`v9kJzz~&Ge^QFV=T0P}@YWZL2Z;idN7zOoD_~YjJ5$mAwfqrku6_TPOi^s@_s|)v zFeHidpKzk6eFfR!JVAYf%VKJ$vv~$_yXf$&opJgLBiMxK!?ZpL zLL($22vwGykskAnb&)?rS!B$UcfB~+Ljj&3p2pRDyondbVtj94aVVs37a_c;J(40N z0Rt_3jOcISTW8|K1xQo6&_V(#mi`J&RIaJ&JT3XCp8Ca7fAkVr(?qZ7{ZJKp1~Q>S z*U@_-G7cZfqB9qLc&}{jqG?c%nL66NyYfdRX{;Rjr$N8fk*O5gHyBj3<9u|{tvdTb zG-=I5#z>4z?D1@_?_^)4yBS<0ty}EdDEI=cy51RMeoo>S@(R3vdk^QbhaL5-LNRdQ zO-k+uHN)6B9gq7Edz-@|ubBh8b{XC{##}pgo5sM|JooN%B5?55Arz_9+MhhwVcz7U z)igyj3n#o#&=sPA@|A)GI<`<;^egB>mDy>JpqzoIUp2>TdMQLyU_~6GI#RR-l_NZw z3_isA#lZc=M36*_ELQGe3hb@THjUxes^(J$ekeetl!q$AaAVbQW<~k1VZoQH*vSD` z*AbMh7thids%;54zrnL3vo(7{WM~fY5^O1ip6!#BLf;5njK32UU$jxSy`M)DD3|#l z{IZz!f$E9J_k{PJDR}(f2B2QN@O9=f!CkjWo(J$Q&TE_HggPo#kr1KR5M``G>0p5~TGfGliDCJUu z0gfBn3-UAJ_K}S`l2U&!tk5JVGcFioXoj8$RgP<42(QZR@to*ZxD>D8KFStTWaUeh{FO!UR>CR82F3{93*dOv4EIm-XfFUrp(l zuT|-OO%>)e^cV_AlP~jM4u_g%{}TJJzW-MEw-(8#rh0;%8KhHE`cSz(%#Ln8S%*%= z^Wx?;qMw&!4&n!=$EZPnId9$|4c$sstXR`RA{RF)q7yFlRG2v!8voa6JH9`SelP{bFe z@PWfFNs1Of>cn<7aZFFMC6x~&7mjaCN{c|IaY`i7k^?crgASTID2ex^jr3G@)XyL~%qhO=-y+kq zK|=};aAdakQPLcX!A8SSVK}Sq%-7c-@YjlzuO-3a{xB2ew^u?rb@Uf_GL5>4ikWxEY8#1${i1 z(k2o@MOBE(6v19}%HJs*1uzdm>VoNd7-ePMs#co?Ea-wk0;P3Yhb*Jc=Kp;-?ZWCs zeb_C?{$oQaHK6RudCz;EFN%C!i#uae<@726@iRa7I#h!}wK)RqZ(Hx@o#4mE%;(dm zk}N0~ItV%l2naNYnma(R+Rw5#91aAep9KU2>udGDUnb6GMy_URR$^x6R`yn|Ru1-z z)-DeA|E~y~n<-1l2?$8+Iy4B%e^coGlD0FlvIm&iIof_n{rh%EsDHe`zJ4KJ|M7qS Gnf)IXOugy= literal 0 HcmV?d00001 diff --git a/Solutions/QualysVM/Package/createUiDefinition.json b/Solutions/QualysVM/Package/createUiDefinition.json index 1b1a8c434da..9545e5356f1 100644 --- a/Solutions/QualysVM/Package/createUiDefinition.json +++ b/Solutions/QualysVM/Package/createUiDefinition.json @@ -6,7 +6,7 @@ "config": { "isWizard": false, "basics": { - "description": "\n\n**Note:** Please refer to the following before installing the solution: \n\n• Review the solution [Release Notes](https://github.com/Azure/Azure-Sentinel/tree/master/Solutions/QualysVM/ReleaseNotes.md)\n\n • There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing.\n\nThe [Qualys Vulnerability Management](https://www.qualys.com/apps/vulnerability-management-detection-response/) solution for Microsoft Sentinel enables you to ingest host vulnerability detection data into Microsoft Sentinel.\n\n **Underlying Microsoft Technologies used:** \n\n This solution takes a dependency on the following technologies, and some of these dependencies either may be in [Preview](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) state or might result in additional ingestion or operational costs:\n\n• [Microsoft Sentinel Codeless Connector Framework](https://aka.ms/Sentinel-CCP_Platform)\n\n**Data Connectors:** 1, **Parsers:** 1, **Workbooks:** 1, **Analytic Rules:** 2, **Custom Azure Logic Apps Connectors:** 1, **Playbooks:** 4\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", + "description": "\n\n**Note:** Please refer to the following before installing the solution: \n\n• Review the solution [Release Notes](https://github.com/Azure/Azure-Sentinel/tree/master/Solutions/QualysVM/ReleaseNotes.md)\n\n • There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing.\n\nThe [Qualys Vulnerability Management](https://www.qualys.com/apps/vulnerability-management-detection-response/) solution for Microsoft Sentinel enables you to ingest host vulnerability detection data into Microsoft Sentinel.\n\n **Underlying Microsoft Technologies used:** \n\n This solution takes a dependency on the following technologies, and some of these dependencies either may be in [Preview](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) state or might result in additional ingestion or operational costs:\n\n• [Microsoft Sentinel Codeless Connector Framework](https://aka.ms/Sentinel-CCP_Platform)\n\n**Data Connectors:** 1, **Parsers:** 1, **Workbooks:** 1, **Analytic Rules:** 2, **XDR Detections:** 2, **Custom Azure Logic Apps Connectors:** 1, **Playbooks:** 4\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", "subscription": { "resourceProviders": [ "Microsoft.OperationsManagement/solutions", @@ -51,6 +51,54 @@ } ], "steps": [ + { + "name": "contentSelection", + "label": "Content selection", + "elements": [ + { + "name": "DeployAnalyticsRule", + "type": "Microsoft.Common.CheckBox", + "label": "DeployAnalyticsRule", + "defaultValue": true, + "toolTip": "Deploy the Microsoft Sentinel analytic rules carried by this solution (ContentKind AnalyticsRule and its AnalyticsRuleTemplate registration face)." + }, + { + "name": "DeployPlaybook", + "type": "Microsoft.Common.CheckBox", + "label": "DeployPlaybook", + "defaultValue": true, + "toolTip": "Deploy the playbooks carried by this solution and the automation assets they depend on (PlaybookTemplate, AzureFunction, LogicAppsCustomConnector). These are never shipped independently of the playbook they serve." + }, + { + "name": "DeployWorkbook", + "type": "Microsoft.Common.CheckBox", + "label": "DeployWorkbook", + "defaultValue": true, + "toolTip": "Deploy the Microsoft Sentinel workbook content and its Content Hub registration carried by this solution (ContentKind Workbook and WorkbookTemplate)." + }, + { + "name": "DeployDataConnector", + "type": "Microsoft.Common.CheckBox", + "label": "DeployDataConnector", + "defaultValue": true, + "toolTip": "Deploy the data connectors carried by this solution, together with their metadata and catalog entries (ContentKind DataConnector, ResourceDataConnector and DataType)." + }, + { + "name": "DeployCustomDetection", + "type": "Microsoft.Common.CheckBox", + "label": "DeployCustomDetection", + "defaultValue": false, + "toolTip": "Deploy the XDR-native custom detection resources and their Content Hub registrations (ContentKind CustomDetection and CustomDetectionTemplate). Independent of DeployAnalyticsRule: both kinds can be selected together. Existing analytic-rule instances are not disabled or removed. CD enabled or disabled status is controlled separately from this deployment switch." + }, + { + "name": "DeployParser", + "type": "Microsoft.Common.CheckBox", + "label": "DeployParser", + "defaultValue": true, + "toolTip": "Deploy the parsers and their registrations carried by this solution." + } + ] + }, { "name": "dataconnectors", "label": "Data Connectors", @@ -205,7 +253,13 @@ "outputs": { "workspace-location": "[first(map(filter(basics('getLAWorkspace').value, (filter) => and(contains(toLower(filter.id), toLower(resourceGroup().name)),equals(filter.name,basics('workspace')))), (item) => item.location))]", "location": "[location()]", - "workspace": "[basics('workspace')]" + "workspace": "[basics('workspace')]", + "DeployAnalyticsRule": "[steps('contentSelection').DeployAnalyticsRule]", + "DeployPlaybook": "[steps('contentSelection').DeployPlaybook]", + "DeployWorkbook": "[steps('contentSelection').DeployWorkbook]", + "DeployDataConnector": "[steps('contentSelection').DeployDataConnector]", + "DeployCustomDetection": "[steps('contentSelection').DeployCustomDetection]", + "DeployParser": "[steps('contentSelection').DeployParser]" } } } diff --git a/Solutions/QualysVM/Package/mainTemplate.json b/Solutions/QualysVM/Package/mainTemplate.json index bc37c8efacf..9d6e5f3ec2e 100644 --- a/Solutions/QualysVM/Package/mainTemplate.json +++ b/Solutions/QualysVM/Package/mainTemplate.json @@ -49,17 +49,59 @@ "metadata": { "description": "Name for the workbook" } + }, + "DeployAnalyticsRule": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Deploy the Microsoft Sentinel analytic rules carried by this solution (ContentKind AnalyticsRule and its AnalyticsRuleTemplate registration face)." + } + }, + "DeployPlaybook": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Deploy the playbooks carried by this solution and the automation assets they depend on (PlaybookTemplate, AzureFunction, LogicAppsCustomConnector). These are never shipped independently of the playbook they serve." + } + }, + "DeployWorkbook": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Deploy the Microsoft Sentinel workbook content and its Content Hub registration carried by this solution (ContentKind Workbook and WorkbookTemplate)." + } + }, + "DeployDataConnector": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Deploy the data connectors carried by this solution, together with their metadata and catalog entries (ContentKind DataConnector, ResourceDataConnector and DataType)." + } + }, + "DeployCustomDetection": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Deploy the XDR-native custom detection resources and their Content Hub registrations (ContentKind CustomDetection and CustomDetectionTemplate). Independent of DeployAnalyticsRule: both kinds can be selected together. Existing analytic-rule instances are not disabled or removed. CD enabled or disabled status is controlled separately from this deployment switch." + } + }, + "DeployParser": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Deploy the parsers and their registrations carried by this solution." + } } }, "variables": { "email": "support@microsoft.com", "_email": "[variables('email')]", "_solutionName": "QualysVM", - "_solutionVersion": "3.1.1", + "_solutionVersion": "3.2.2", "solutionId": "azuresentinel.azure-sentinel-qualysvm", "_solutionId": "[variables('solutionId')]", "workspaceResourceId": "[resourceId('microsoft.OperationalInsights/Workspaces', parameters('workspace'))]", - "dataConnectorCCPVersion": "3.1.1", + "dataConnectorCCPVersion": "3.2.2", "_dataConnectorContentIdConnectorDefinition1": "QualysVMLogsCCPDefinition", "dataConnectorTemplateNameConnectorDefinition1": "[concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentIdConnectorDefinition1')))]", "_dataConnectorContentIdConnections1": "QualysVMLogsCCPDefinitionConnections", @@ -80,18 +122,18 @@ "_workbookContentId1": "[variables('workbookContentId1')]", "_workbookcontentProductId1": "[concat(take(variables('_solutionId'),50),'-','wb','-', uniqueString(concat(variables('_solutionId'),'-','Workbook','-',variables('_workbookContentId1'),'-', variables('workbookVersion1'))))]", "analyticRuleObject1": { - "analyticRuleVersion1": "1.0.4", + "analyticRuleVersion1": "3.2.1", "_analyticRulecontentId1": "3edb7215-250b-40c0-8b46-79093949242d", "analyticRuleId1": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '3edb7215-250b-40c0-8b46-79093949242d')]", "analyticRuleTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('3edb7215-250b-40c0-8b46-79093949242d')))]", - "_analyticRulecontentProductId1": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','3edb7215-250b-40c0-8b46-79093949242d','-', '1.0.4')))]" + "_analyticRulecontentProductId1": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','3edb7215-250b-40c0-8b46-79093949242d','-', '3.2.1')))]" }, "analyticRuleObject2": { - "analyticRuleVersion2": "1.0.2", + "analyticRuleVersion2": "3.2.1", "_analyticRulecontentId2": "6116dc19-475a-4148-84b2-efe89c073e27", "analyticRuleId2": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', '6116dc19-475a-4148-84b2-efe89c073e27')]", "analyticRuleTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring('6116dc19-475a-4148-84b2-efe89c073e27')))]", - "_analyticRulecontentProductId2": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','6116dc19-475a-4148-84b2-efe89c073e27','-', '1.0.2')))]" + "_analyticRulecontentProductId2": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-','6116dc19-475a-4148-84b2-efe89c073e27','-', '3.2.1')))]" }, "QualysCustomConnector": "QualysCustomConnector", "_QualysCustomConnector": "[variables('QualysCustomConnector')]", @@ -360,7 +402,8 @@ } ] } - } + }, + "condition": "[parameters('DeployDataConnector')]" }, { "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', variables('_dataConnectorContentIdConnectorDefinition1')))]", @@ -559,7 +602,8 @@ "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", "version": "[variables('dataConnectorCCPVersion')]" - } + }, + "condition": "[parameters('DeployDataConnector')]" }, { "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',variables('_dataConnectorContentIdConnectorDefinition1'))]", @@ -767,7 +811,8 @@ } ] } - } + }, + "condition": "[parameters('DeployDataConnector')]" }, { "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', variables('_dataConnectorContentIdConnectorDefinition1')))]", @@ -802,7 +847,8 @@ } ] } - } + }, + "condition": "[parameters('DeployDataConnector')]" }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", @@ -970,7 +1016,8 @@ "packageId": "[variables('_solutionId')]", "contentSchemaVersion": "3.0.0", "version": "[variables('dataConnectorCCPVersion')]" - } + }, + "condition": "[parameters('DeployDataConnector')]" }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", @@ -981,7 +1028,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "QualysHostDetection Data Parser with template version 3.1.1", + "description": "QualysHostDetection Data Parser with template version 3.2.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('parserObject1').parserVersion1]", @@ -1051,7 +1098,8 @@ "contentProductId": "[concat(take(variables('_solutionId'),50),'-','pr','-', uniqueString(concat(variables('_solutionId'),'-','Parser','-',variables('parserObject1').parserContentId1,'-', '1.1.0')))]", "id": "[concat(take(variables('_solutionId'),50),'-','pr','-', uniqueString(concat(variables('_solutionId'),'-','Parser','-',variables('parserObject1').parserContentId1,'-', '1.1.0')))]", "version": "[variables('parserObject1').parserVersion1]" - } + }, + "condition": "[parameters('DeployParser')]" }, { "type": "Microsoft.OperationalInsights/workspaces/savedSearches", @@ -1072,7 +1120,8 @@ "value": "" } ] - } + }, + "condition": "[parameters('DeployParser')]" }, { "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", @@ -1102,7 +1151,8 @@ "tier": "Microsoft", "link": "https://support.microsoft.com" } - } + }, + "condition": "[parameters('DeployParser')]" }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", @@ -1113,7 +1163,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "QualysVMv2 Workbook with template version 3.1.1", + "description": "QualysVMv2 Workbook with template version 3.2.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('workbookVersion1')]", @@ -1190,7 +1240,8 @@ "contentProductId": "[variables('_workbookcontentProductId1')]", "id": "[variables('_workbookcontentProductId1')]", "version": "[variables('workbookVersion1')]" - } + }, + "condition": "[parameters('DeployWorkbook')]" }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", @@ -1201,7 +1252,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "HighNumberofVulnDetectedV2_AnalyticalRules Analytics Rule with template version 3.1.1", + "description": "HighNumberofVulnDetectedV2_AnalyticalRules Analytics Rule with template version 3.2.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject1').analyticRuleVersion1]", @@ -1218,7 +1269,7 @@ "description": "This creates an incident when a host has a high number of urgent, severity 5, vulnerabilities detected.", "displayName": "High Number of Urgent Vulnerabilities Detected", "enabled": false, - "query": "let threshold = 10;\nQualysHostDetection\n| where Severity == \"5\"\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by NetBios, IPAddress\n| where count_ >= threshold\n", + "query": "let QualysHostDetection = view () {\nlet QualysHostDetection_view = view () {\n QualysHostDetection_CL\n | mv-expand todynamic(Detections_s)\n | extend\n Severity_s = tostring(Detections_s.Severity),\n Status_s = tostring(Detections_s.Status),\n QID_s = tostring(Detections_s.Results)\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id_s', ''),\n HostId = column_ifexists('HostId_s', ''),\n HostTags = column_ifexists('HostTags_s', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod_s', ''),\n OperatingSystem = column_ifexists('OperatingSystem_s', ''),\n DnsName = column_ifexists('DnsName_s', ''),\n NetBios = column_ifexists('NetBios_s', ''),\n QGHostId = column_ifexists('QGHostId_g', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime_t', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''),\n QID = column_ifexists('QID_s', ''),\n SSL = column_ifexists('SSL_s', ''),\n Status = column_ifexists('Status_s', ''),\n Ignored = column_ifexists('Ignored_s', ''),\n Severity = column_ifexists('Severity_s', ''),\n Disabled = column_ifexists('Disabled_s', ''),\n LastFixed = column_ifexists('LastFixed_t', ''),\n LastFound = column_ifexists('LastFound_t', ''),\n TimesFound = column_ifexists('TimesFound_s', ''),\n FirstFound = column_ifexists('FirstFound_t', ''),\n LastUpdate = column_ifexists('LastUpdate_t', datetime(null)),\n LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)),\n Result_column_count = tostring(column_ifexists('Result_column_count_d', '')),\n Results_0 = column_ifexists('Results_0_s', ''),\n Type = column_ifexists('Type', ''),\n _ResourceId = column_ifexists('_ResourceId', '')\n };\n let QualysHostDetectionV2_view = view () {\n QualysHostDetectionV2_CL\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id_s', ''),\n HostId = column_ifexists('HostId_s', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod_s', ''),\n OperatingSystem = column_ifexists('OperatingSystem_s', ''),\n DnsName = column_ifexists('DnsName_s', ''),\n NetBios = column_ifexists('NetBios_s', ''),\n QGHostId = column_ifexists('QGHostId_g', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime_t', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''),\n QID = column_ifexists('QID_s', ''),\n SSL = column_ifexists('SSL_s', ''),\n Status = column_ifexists('Status_s', ''),\n Ignored = column_ifexists('Ignored_s', ''),\n Severity = column_ifexists('Severity_s', ''),\n Disabled = column_ifexists('Disabled_s', ''),\n LastFixed = column_ifexists('LastFixed_t', ''),\n LastFound = column_ifexists('LastFound_t', ''),\n TimesFound = column_ifexists('TimesFound_s', ''),\n FirstFound = column_ifexists('FirstFound_t', ''),\n LastUpdate = column_ifexists('LastUpdate_t', datetime(null)),\n LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)),\n Result_column_count = tostring(column_ifexists('Result_column_count_d', '')),\n Results_0 = column_ifexists('Results_0_s', ''),\n Type = column_ifexists('Type', ''),\n _ResourceId = column_ifexists('_ResourceId', '')\n };\n let QualysHostDetectionV3_view = view () {\n QualysHostDetectionV3_CL\n | extend DetectionList = todynamic(DetectionList)\n | extend DetectionItems = iff(\n gettype(DetectionList.DETECTION) == \"array\",\n DetectionList.DETECTION,\n pack_array(DetectionList.DETECTION)\n )\n | mv-expand Detections = DetectionItems to typeof(dynamic)\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id', ''),\n HostId = column_ifexists('HostId', ''),\n HostTags = column_ifexists('HostTags', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod', ''),\n OperatingSystem = column_ifexists('OperatingSystem', ''),\n DnsName = column_ifexists('DnsName', ''),\n NetBios = column_ifexists('NetBios', ''),\n QGHostId = column_ifexists('QGHostId', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime', ''),\n QID = tostring(Detections.QID),\n SSL = tostring(Detections.SSL),\n Status = tostring(Detections.STATUS),\n Ignored = tostring(Detections.IS_IGNORED),\n Severity = tostring(Detections.SEVERITY),\n Disabled = tostring(Detections.IS_DISABLED),\n LastFixed = todatetime(Detections.LAST_FIXED_DATETIME),\n LastFound = todatetime(Detections.LAST_FOUND_DATETIME),\n TimesFound = tostring(Detections.TIMES_FOUND),\n FirstFound = todatetime(Detections.FIRST_FOUND_DATETIME),\n LastUpdate = todatetime(Detections.LAST_UPDATE_DATETIME),\n LastProcessedDateTime = coalesce(\n todatetime(Detections.LAST_PROCESSED_DATETIME),\n todatetime(Detections.LAST_PROCESSED_DATE)),\n Results_0 = tostring(coalesce(Detections.RESULTS['#cdata-section'], Detections.RESULTS)),\n Result_column_count = \"1\",\n Type = tostring(Detections.TYPE),\n _ResourceId = column_ifexists('_ResourceId', '')\n | extend _IngestionTime = todatetime(TimeGenerated)\n | summarize arg_max(_IngestionTime, *) by HostId, QID, Status, SSL, Ignored, Severity, Disabled, LastFixed, LastFound, TimesFound, FirstFound, Result_column_count, Results_0, Type\n | project-away _IngestionTime\n };\n union isfuzzy=true\n (QualysHostDetection_view),\n (QualysHostDetectionV2_view),\n (QualysHostDetectionV3_view)\n | project\n TenantId,\n SourceSystem,\n MG,\n ManagementGroupName,\n TimeGenerated,\n Computer,\n RawData,\n Id,\n HostId,\n HostTags,\n IPAddress,\n TrackingMethod,\n OperatingSystem,\n DnsName,\n NetBios,\n QGHostId,\n LastScanDateTime,\n LastVMScannedDateTime,\n LastVMAuthScannedDateTime,\n QID,\n SSL,\n Type,\n Status,\n Ignored,\n Severity,\n Disabled,\n LastFixed,\n LastFound,\n TimesFound,\n FirstFound,\n LastUpdate,\n LastProcessedDateTime,\n Result_column_count,\n Results_0,\n _ResourceId\n | extend\n HostId = tostring(HostId),\n QID = tostring(QID),\n Status = tostring(Status),\n LastProcessedDateTime = todatetime(LastProcessedDateTime),\n LastUpdate = todatetime(LastUpdate),\n LastFound = todatetime(LastFound)\n | extend DetectionTime = coalesce(LastProcessedDateTime, LastUpdate, LastFound, todatetime(TimeGenerated))\n};\nlet threshold = 10;\nQualysHostDetection\n| where Severity == \"5\"\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by NetBios, IPAddress\n| where count_ >= threshold\n", "queryFrequency": "PT1H", "queryPeriod": "PT1H", "severity": "Medium", @@ -1229,10 +1280,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "QualysVMLogsCCPDefinition", "dataTypes": [ "QualysHostDetection" - ] + ], + "connectorId": "QualysVMLogsCCPDefinition" } ], "tactics": [ @@ -1243,22 +1294,22 @@ ], "entityMappings": [ { - "entityType": "Host", "fieldMappings": [ { - "identifier": "FullName", + "identifier": "NetBiosName", "columnName": "NetBios" } - ] + ], + "entityType": "Host" }, { - "entityType": "IP", "fieldMappings": [ { "identifier": "Address", "columnName": "IPAddress" } - ] + ], + "entityType": "IP" } ] } @@ -1303,7 +1354,8 @@ "contentProductId": "[variables('analyticRuleObject1')._analyticRulecontentProductId1]", "id": "[variables('analyticRuleObject1')._analyticRulecontentProductId1]", "version": "[variables('analyticRuleObject1').analyticRuleVersion1]" - } + }, + "condition": "[parameters('DeployAnalyticsRule')]" }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", @@ -1314,7 +1366,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "NewHighSeverityVulnDetectedAcrossMulitpleHostsV2_AnalyticalRules Analytics Rule with template version 3.1.1", + "description": "NewHighSeverityVulnDetectedAcrossMulitpleHostsV2_AnalyticalRules Analytics Rule with template version 3.2.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('analyticRuleObject2').analyticRuleVersion2]", @@ -1331,7 +1383,7 @@ "description": "This creates an incident when a new high severity vulnerability is detected across multilple hosts", "displayName": "New High Severity Vulnerability Detected Across Multiple Hosts", "enabled": false, - "query": "let threshold = 10;\nQualysHostDetection\n| extend Status = tostring(Status), Vulnerability = tostring(QID), Severity = tostring(Severity)\n| where Status =~ \"New\" and Severity == \"5\"\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), dcount(NetBios) by tostring(QID)\n| where dcount_NetBios >= threshold\n| extend timestamp = StartTime\n", + "query": "let QualysHostDetection = view () {\nlet QualysHostDetection_view = view () {\n QualysHostDetection_CL\n | mv-expand todynamic(Detections_s)\n | extend\n Severity_s = tostring(Detections_s.Severity),\n Status_s = tostring(Detections_s.Status),\n QID_s = tostring(Detections_s.Results)\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id_s', ''),\n HostId = column_ifexists('HostId_s', ''),\n HostTags = column_ifexists('HostTags_s', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod_s', ''),\n OperatingSystem = column_ifexists('OperatingSystem_s', ''),\n DnsName = column_ifexists('DnsName_s', ''),\n NetBios = column_ifexists('NetBios_s', ''),\n QGHostId = column_ifexists('QGHostId_g', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime_t', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''),\n QID = column_ifexists('QID_s', ''),\n SSL = column_ifexists('SSL_s', ''),\n Status = column_ifexists('Status_s', ''),\n Ignored = column_ifexists('Ignored_s', ''),\n Severity = column_ifexists('Severity_s', ''),\n Disabled = column_ifexists('Disabled_s', ''),\n LastFixed = column_ifexists('LastFixed_t', ''),\n LastFound = column_ifexists('LastFound_t', ''),\n TimesFound = column_ifexists('TimesFound_s', ''),\n FirstFound = column_ifexists('FirstFound_t', ''),\n LastUpdate = column_ifexists('LastUpdate_t', datetime(null)),\n LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)),\n Result_column_count = tostring(column_ifexists('Result_column_count_d', '')),\n Results_0 = column_ifexists('Results_0_s', ''),\n Type = column_ifexists('Type', ''),\n _ResourceId = column_ifexists('_ResourceId', '')\n };\n let QualysHostDetectionV2_view = view () {\n QualysHostDetectionV2_CL\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id_s', ''),\n HostId = column_ifexists('HostId_s', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod_s', ''),\n OperatingSystem = column_ifexists('OperatingSystem_s', ''),\n DnsName = column_ifexists('DnsName_s', ''),\n NetBios = column_ifexists('NetBios_s', ''),\n QGHostId = column_ifexists('QGHostId_g', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime_t', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''),\n QID = column_ifexists('QID_s', ''),\n SSL = column_ifexists('SSL_s', ''),\n Status = column_ifexists('Status_s', ''),\n Ignored = column_ifexists('Ignored_s', ''),\n Severity = column_ifexists('Severity_s', ''),\n Disabled = column_ifexists('Disabled_s', ''),\n LastFixed = column_ifexists('LastFixed_t', ''),\n LastFound = column_ifexists('LastFound_t', ''),\n TimesFound = column_ifexists('TimesFound_s', ''),\n FirstFound = column_ifexists('FirstFound_t', ''),\n LastUpdate = column_ifexists('LastUpdate_t', datetime(null)),\n LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)),\n Result_column_count = tostring(column_ifexists('Result_column_count_d', '')),\n Results_0 = column_ifexists('Results_0_s', ''),\n Type = column_ifexists('Type', ''),\n _ResourceId = column_ifexists('_ResourceId', '')\n };\n let QualysHostDetectionV3_view = view () {\n QualysHostDetectionV3_CL\n | extend DetectionList = todynamic(DetectionList)\n | extend DetectionItems = iff(\n gettype(DetectionList.DETECTION) == \"array\",\n DetectionList.DETECTION,\n pack_array(DetectionList.DETECTION)\n )\n | mv-expand Detections = DetectionItems to typeof(dynamic)\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id', ''),\n HostId = column_ifexists('HostId', ''),\n HostTags = column_ifexists('HostTags', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod', ''),\n OperatingSystem = column_ifexists('OperatingSystem', ''),\n DnsName = column_ifexists('DnsName', ''),\n NetBios = column_ifexists('NetBios', ''),\n QGHostId = column_ifexists('QGHostId', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime', ''),\n QID = tostring(Detections.QID),\n SSL = tostring(Detections.SSL),\n Status = tostring(Detections.STATUS),\n Ignored = tostring(Detections.IS_IGNORED),\n Severity = tostring(Detections.SEVERITY),\n Disabled = tostring(Detections.IS_DISABLED),\n LastFixed = todatetime(Detections.LAST_FIXED_DATETIME),\n LastFound = todatetime(Detections.LAST_FOUND_DATETIME),\n TimesFound = tostring(Detections.TIMES_FOUND),\n FirstFound = todatetime(Detections.FIRST_FOUND_DATETIME),\n LastUpdate = todatetime(Detections.LAST_UPDATE_DATETIME),\n LastProcessedDateTime = coalesce(\n todatetime(Detections.LAST_PROCESSED_DATETIME),\n todatetime(Detections.LAST_PROCESSED_DATE)),\n Results_0 = tostring(coalesce(Detections.RESULTS['#cdata-section'], Detections.RESULTS)),\n Result_column_count = \"1\",\n Type = tostring(Detections.TYPE),\n _ResourceId = column_ifexists('_ResourceId', '')\n | extend _IngestionTime = todatetime(TimeGenerated)\n | summarize arg_max(_IngestionTime, *) by HostId, QID, Status, SSL, Ignored, Severity, Disabled, LastFixed, LastFound, TimesFound, FirstFound, Result_column_count, Results_0, Type\n | project-away _IngestionTime\n };\n union isfuzzy=true\n (QualysHostDetection_view),\n (QualysHostDetectionV2_view),\n (QualysHostDetectionV3_view)\n | project\n TenantId,\n SourceSystem,\n MG,\n ManagementGroupName,\n TimeGenerated,\n Computer,\n RawData,\n Id,\n HostId,\n HostTags,\n IPAddress,\n TrackingMethod,\n OperatingSystem,\n DnsName,\n NetBios,\n QGHostId,\n LastScanDateTime,\n LastVMScannedDateTime,\n LastVMAuthScannedDateTime,\n QID,\n SSL,\n Type,\n Status,\n Ignored,\n Severity,\n Disabled,\n LastFixed,\n LastFound,\n TimesFound,\n FirstFound,\n LastUpdate,\n LastProcessedDateTime,\n Result_column_count,\n Results_0,\n _ResourceId\n | extend\n HostId = tostring(HostId),\n QID = tostring(QID),\n Status = tostring(Status),\n LastProcessedDateTime = todatetime(LastProcessedDateTime),\n LastUpdate = todatetime(LastUpdate),\n LastFound = todatetime(LastFound)\n | extend DetectionTime = coalesce(LastProcessedDateTime, LastUpdate, LastFound, todatetime(TimeGenerated))\n};\nlet threshold = 10;\nQualysHostDetection\n| extend Status = tostring(Status), Vulnerability = tostring(QID), Severity = tostring(Severity)\n| where Status =~ \"New\" and Severity == \"5\"\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), dcount(NetBios), RepresentativeHost = take_anyif(NetBios, isnotempty(NetBios)) by tostring(QID)\n| where dcount_NetBios >= threshold\n| extend timestamp = StartTime\n", "queryFrequency": "PT1H", "queryPeriod": "PT1H", "severity": "Medium", @@ -1342,10 +1394,10 @@ "status": "Available", "requiredDataConnectors": [ { - "connectorId": "QualysVMLogsCCPDefinition", "dataTypes": [ "QualysHostDetection" - ] + ], + "connectorId": "QualysVMLogsCCPDefinition" } ], "tactics": [ @@ -1353,6 +1405,17 @@ ], "techniques": [ "T1190" + ], + "entityMappings": [ + { + "fieldMappings": [ + { + "identifier": "NetBiosName", + "columnName": "RepresentativeHost" + } + ], + "entityType": "Host" + } ] } }, @@ -1396,7 +1459,8 @@ "contentProductId": "[variables('analyticRuleObject2')._analyticRulecontentProductId2]", "id": "[variables('analyticRuleObject2')._analyticRulecontentProductId2]", "version": "[variables('analyticRuleObject2').analyticRuleVersion2]" - } + }, + "condition": "[parameters('DeployAnalyticsRule')]" }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", @@ -1407,7 +1471,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "QualysCustomConnector Playbook with template version 3.1.1", + "description": "QualysCustomConnector Playbook with template version 3.2.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('playbookVersion1')]", @@ -3046,7 +3110,8 @@ "contentProductId": "[variables('_playbookcontentProductId1')]", "id": "[variables('_playbookcontentProductId1')]", "version": "[variables('playbookVersion1')]" - } + }, + "condition": "[parameters('DeployPlaybook')]" }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", @@ -3057,7 +3122,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "QualysVM-GetAssetDetails Playbook with template version 3.1.1", + "description": "QualysVM-GetAssetDetails Playbook with template version 3.2.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('playbookVersion2')]", @@ -3559,7 +3624,8 @@ "contentProductId": "[variables('_playbookcontentProductId2')]", "id": "[variables('_playbookcontentProductId2')]", "version": "[variables('playbookVersion2')]" - } + }, + "condition": "[parameters('DeployPlaybook')]" }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", @@ -3570,7 +3636,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "QualysVM-GetAssets-ByCVEID Playbook with template version 3.1.1", + "description": "QualysVM-GetAssets-ByCVEID Playbook with template version 3.2.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('playbookVersion3')]", @@ -4643,7 +4709,8 @@ "contentProductId": "[variables('_playbookcontentProductId3')]", "id": "[variables('_playbookcontentProductId3')]", "version": "[variables('playbookVersion3')]" - } + }, + "condition": "[parameters('DeployPlaybook')]" }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", @@ -4654,7 +4721,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "QualysVM-GetAssets-ByOpenPort Playbook with template version 3.1.1", + "description": "QualysVM-GetAssets-ByOpenPort Playbook with template version 3.2.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('playbookVersion4')]", @@ -5602,7 +5669,8 @@ "contentProductId": "[variables('_playbookcontentProductId4')]", "id": "[variables('_playbookcontentProductId4')]", "version": "[variables('playbookVersion4')]" - } + }, + "condition": "[parameters('DeployPlaybook')]" }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", @@ -5613,7 +5681,7 @@ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "QualysVM-LaunchVMScan-GenerateReport Playbook with template version 3.1.1", + "description": "QualysVM-LaunchVMScan-GenerateReport Playbook with template version 3.2.2", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('playbookVersion5')]", @@ -8259,19 +8327,20 @@ "contentProductId": "[variables('_playbookcontentProductId5')]", "id": "[variables('_playbookcontentProductId5')]", "version": "[variables('playbookVersion5')]" - } + }, + "condition": "[parameters('DeployPlaybook')]" }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentPackages", "apiVersion": "2023-04-01-preview", "location": "[parameters('workspace-location')]", "properties": { - "version": "3.1.1", + "version": "3.2.2", "kind": "Solution", "contentSchemaVersion": "3.0.0", "displayName": "QualysVM", "publisherDisplayName": "Microsoft Sentinel, Microsoft Corporation", - "descriptionHtml": "

Note: Please refer to the following before installing the solution:

\n

• Review the solution Release Notes

\n

• There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The Qualys Vulnerability Management solution for Microsoft Sentinel enables you to ingest host vulnerability detection data into Microsoft Sentinel.

\n

Underlying Microsoft Technologies used:

\n

This solution takes a dependency on the following technologies, and some of these dependencies either may be in Preview state or might result in additional ingestion or operational costs:

\n

• Microsoft Sentinel Codeless Connector Framework

\n

Data Connectors: 1, Parsers: 1, Workbooks: 1, Analytic Rules: 2, Custom Azure Logic Apps Connectors: 1, Playbooks: 4

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", + "descriptionHtml": "

Note: Please refer to the following before installing the solution:

\n

• Review the solution Release Notes

\n

• There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The Qualys Vulnerability Management solution for Microsoft Sentinel enables you to ingest host vulnerability detection data into Microsoft Sentinel.

\n

Underlying Microsoft Technologies used:

\n

This solution takes a dependency on the following technologies, and some of these dependencies either may be in Preview state or might result in additional ingestion or operational costs:

\n

• Microsoft Sentinel Codeless Connector Framework

\n

Data Connectors: 1, Parsers: 1, Workbooks: 1, Analytic Rules: 2, XDR Detections: 2, Custom Azure Logic Apps Connectors: 1, Playbooks: 4

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", "contentKind": "Solution", "contentProductId": "[variables('_solutioncontentProductId')]", "id": "[variables('_solutioncontentProductId')]", @@ -8345,6 +8414,16 @@ "kind": "Playbook", "contentId": "[variables('_QualysVM-LaunchVMScan-GenerateReport')]", "version": "[variables('playbookVersion5')]" + }, + { + "kind": "CustomDetection", + "contentId": "xdr-3edb7215-250b-40c0-8b46-79093949242d", + "version": "3.2.1" + }, + { + "kind": "CustomDetection", + "contentId": "xdr-6116dc19-475a-4148-84b2-efe89c073e27", + "version": "3.2.1" } ] }, @@ -8361,6 +8440,401 @@ } }, "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('_solutionId'))]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "QualysVM-CD-xdr-3edb7215-250b-40c0-8b46-79093949242d", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "mode": "Incremental", + "expressionEvaluationOptions": { + "scope": "inner" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "imports": { + "MicrosoftSecurity": { + "provider": "MicrosoftSecurity", + "version": "1.0.0" + } + }, + "resources": { + "detectionRule": { + "import": "MicrosoftSecurity", + "type": "Microsoft.Security/detectionRules@2026-06-01-preview", + "properties": { + "id": "[if(true(), 'xdr-3edb7215-250b-40c0-8b46-79093949242d', 'xdr-3edb7215-250b-40c0-8b46-79093949242d')]", + "queryCondition": { + "queryText": "let __xdr_inline_QualysHostDetection = view () {\nlet QualysHostDetection_view = view () {\n QualysHostDetection_CL\n | mv-expand todynamic(Detections_s)\n | extend\n Severity_s = tostring(Detections_s.Severity),\n Status_s = tostring(Detections_s.Status),\n QID_s = tostring(Detections_s.Results)\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id_s', ''),\n HostId = column_ifexists('HostId_s', ''),\n HostTags = column_ifexists('HostTags_s', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod_s', ''),\n OperatingSystem = column_ifexists('OperatingSystem_s', ''),\n DnsName = column_ifexists('DnsName_s', ''),\n NetBios = column_ifexists('NetBios_s', ''),\n QGHostId = column_ifexists('QGHostId_g', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime_t', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''),\n QID = column_ifexists('QID_s', ''),\n SSL = column_ifexists('SSL_s', ''),\n Status = column_ifexists('Status_s', ''),\n Ignored = column_ifexists('Ignored_s', ''),\n Severity = column_ifexists('Severity_s', ''),\n Disabled = column_ifexists('Disabled_s', ''),\n LastFixed = column_ifexists('LastFixed_t', ''),\n LastFound = column_ifexists('LastFound_t', ''),\n TimesFound = column_ifexists('TimesFound_s', ''),\n FirstFound = column_ifexists('FirstFound_t', ''),\n LastUpdate = column_ifexists('LastUpdate_t', datetime(null)),\n LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)),\n Result_column_count = tostring(column_ifexists('Result_column_count_d', '')),\n Results_0 = column_ifexists('Results_0_s', ''),\n Type = column_ifexists('Type', ''),\n _ResourceId = column_ifexists('_ResourceId', '')\n };\n let QualysHostDetectionV2_view = view () {\n QualysHostDetectionV2_CL\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id_s', ''),\n HostId = column_ifexists('HostId_s', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod_s', ''),\n OperatingSystem = column_ifexists('OperatingSystem_s', ''),\n DnsName = column_ifexists('DnsName_s', ''),\n NetBios = column_ifexists('NetBios_s', ''),\n QGHostId = column_ifexists('QGHostId_g', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime_t', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''),\n QID = column_ifexists('QID_s', ''),\n SSL = column_ifexists('SSL_s', ''),\n Status = column_ifexists('Status_s', ''),\n Ignored = column_ifexists('Ignored_s', ''),\n Severity = column_ifexists('Severity_s', ''),\n Disabled = column_ifexists('Disabled_s', ''),\n LastFixed = column_ifexists('LastFixed_t', ''),\n LastFound = column_ifexists('LastFound_t', ''),\n TimesFound = column_ifexists('TimesFound_s', ''),\n FirstFound = column_ifexists('FirstFound_t', ''),\n LastUpdate = column_ifexists('LastUpdate_t', datetime(null)),\n LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)),\n Result_column_count = tostring(column_ifexists('Result_column_count_d', '')),\n Results_0 = column_ifexists('Results_0_s', ''),\n Type = column_ifexists('Type', ''),\n _ResourceId = column_ifexists('_ResourceId', '')\n };\n let QualysHostDetectionV3_view = view () {\n QualysHostDetectionV3_CL\n | extend DetectionList = todynamic(DetectionList)\n | extend DetectionItems = iff(\n gettype(DetectionList.DETECTION) == \"array\",\n DetectionList.DETECTION,\n pack_array(DetectionList.DETECTION)\n )\n | mv-expand Detections = DetectionItems to typeof(dynamic)\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id', ''),\n HostId = column_ifexists('HostId', ''),\n HostTags = column_ifexists('HostTags', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod', ''),\n OperatingSystem = column_ifexists('OperatingSystem', ''),\n DnsName = column_ifexists('DnsName', ''),\n NetBios = column_ifexists('NetBios', ''),\n QGHostId = column_ifexists('QGHostId', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime', ''),\n QID = tostring(Detections.QID),\n SSL = tostring(Detections.SSL),\n Status = tostring(Detections.STATUS),\n Ignored = tostring(Detections.IS_IGNORED),\n Severity = tostring(Detections.SEVERITY),\n Disabled = tostring(Detections.IS_DISABLED),\n LastFixed = todatetime(Detections.LAST_FIXED_DATETIME),\n LastFound = todatetime(Detections.LAST_FOUND_DATETIME),\n TimesFound = tostring(Detections.TIMES_FOUND),\n FirstFound = todatetime(Detections.FIRST_FOUND_DATETIME),\n LastUpdate = todatetime(Detections.LAST_UPDATE_DATETIME),\n LastProcessedDateTime = coalesce(\n todatetime(Detections.LAST_PROCESSED_DATETIME),\n todatetime(Detections.LAST_PROCESSED_DATE)),\n Results_0 = tostring(coalesce(Detections.RESULTS['#cdata-section'], Detections.RESULTS)),\n Result_column_count = \"1\",\n Type = tostring(Detections.TYPE),\n _ResourceId = column_ifexists('_ResourceId', '')\n | extend _IngestionTime = todatetime(TimeGenerated)\n | summarize arg_max(_IngestionTime, *) by HostId, QID, Status, SSL, Ignored, Severity, Disabled, LastFixed, LastFound, TimesFound, FirstFound, Result_column_count, Results_0, Type\n | project-away _IngestionTime\n };\n union isfuzzy=true\n (QualysHostDetection_view),\n (QualysHostDetectionV2_view),\n (QualysHostDetectionV3_view)\n | project\n TenantId,\n SourceSystem,\n MG,\n ManagementGroupName,\n TimeGenerated,\n Computer,\n RawData,\n Id,\n HostId,\n HostTags,\n IPAddress,\n TrackingMethod,\n OperatingSystem,\n DnsName,\n NetBios,\n QGHostId,\n LastScanDateTime,\n LastVMScannedDateTime,\n LastVMAuthScannedDateTime,\n QID,\n SSL,\n Type,\n Status,\n Ignored,\n Severity,\n Disabled,\n LastFixed,\n LastFound,\n TimesFound,\n FirstFound,\n LastUpdate,\n LastProcessedDateTime,\n Result_column_count,\n Results_0,\n _ResourceId\n | extend\n HostId = tostring(HostId),\n QID = tostring(QID),\n Status = tostring(Status),\n LastProcessedDateTime = todatetime(LastProcessedDateTime),\n LastUpdate = todatetime(LastUpdate),\n LastFound = todatetime(LastFound)\n | extend DetectionTime = coalesce(LastProcessedDateTime, LastUpdate, LastFound, todatetime(TimeGenerated))\n};\nlet threshold = 10;\n__xdr_inline_QualysHostDetection\n| where Severity == \"5\"\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by NetBios, IPAddress\n| where count_ >= threshold" + }, + "displayName": "High Number of Urgent Vulnerabilities Detected", + "detectionAction": { + "alertTemplate": { + "customDetails": { + "HostFullName": "NetBios" + }, + "severity": "medium", + "title": "High Number of Urgent Vulnerabilities Detected", + "entityMappings": { + "hosts": [ + { + "netBiosNameColumn": "NetBios", + "id": "[if(true(), 'host1', 'host1')]" + } + ], + "ips": [ + { + "addressColumn": "IPAddress", + "id": "[if(true(), 'ip1', 'ip1')]" + } + ] + }, + "tactics": [ + { + "tactic": "InitialAccess", + "techniques": [ + { + "technique": "T1190" + } + ] + } + ], + "description": "'This creates an incident when a host has a high number of urgent, severity 5, vulnerabilities detected.'" + } + }, + "status": "disabled", + "schedule": { + "frequency": "PT1H" + }, + "contentProvenance": { + "packageId": "azuresentinel.azure-sentinel-qualysvm", + "packageVersion": "3.2.2", + "contentId": "xdr-3edb7215-250b-40c0-8b46-79093949242d", + "contentVersion": "3.2.1" + } + } + } + } + } + }, + "condition": "[parameters('DeployCustomDetection')]" + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-cd-',uniquestring('xdr-3edb7215-250b-40c0-8b46-79093949242d')))]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'QualysVM-CD-xdr-3edb7215-250b-40c0-8b46-79093949242d')]", + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "'This creates an incident when a host has a high number of urgent, severity 5, vulnerabilities detected.'", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0", + "imports": { + "MicrosoftSecurity": { + "provider": "MicrosoftSecurity", + "version": "1.0.0" + } + }, + "resources": { + "detectionRule": { + "import": "MicrosoftSecurity", + "type": "Microsoft.Security/detectionRules@2026-06-01-preview", + "properties": { + "id": "[if(true(), 'xdr-3edb7215-250b-40c0-8b46-79093949242d', 'xdr-3edb7215-250b-40c0-8b46-79093949242d')]", + "queryCondition": { + "queryText": "let __xdr_inline_QualysHostDetection = view () {\nlet QualysHostDetection_view = view () {\n QualysHostDetection_CL\n | mv-expand todynamic(Detections_s)\n | extend\n Severity_s = tostring(Detections_s.Severity),\n Status_s = tostring(Detections_s.Status),\n QID_s = tostring(Detections_s.Results)\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id_s', ''),\n HostId = column_ifexists('HostId_s', ''),\n HostTags = column_ifexists('HostTags_s', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod_s', ''),\n OperatingSystem = column_ifexists('OperatingSystem_s', ''),\n DnsName = column_ifexists('DnsName_s', ''),\n NetBios = column_ifexists('NetBios_s', ''),\n QGHostId = column_ifexists('QGHostId_g', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime_t', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''),\n QID = column_ifexists('QID_s', ''),\n SSL = column_ifexists('SSL_s', ''),\n Status = column_ifexists('Status_s', ''),\n Ignored = column_ifexists('Ignored_s', ''),\n Severity = column_ifexists('Severity_s', ''),\n Disabled = column_ifexists('Disabled_s', ''),\n LastFixed = column_ifexists('LastFixed_t', ''),\n LastFound = column_ifexists('LastFound_t', ''),\n TimesFound = column_ifexists('TimesFound_s', ''),\n FirstFound = column_ifexists('FirstFound_t', ''),\n LastUpdate = column_ifexists('LastUpdate_t', datetime(null)),\n LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)),\n Result_column_count = tostring(column_ifexists('Result_column_count_d', '')),\n Results_0 = column_ifexists('Results_0_s', ''),\n Type = column_ifexists('Type', ''),\n _ResourceId = column_ifexists('_ResourceId', '')\n };\n let QualysHostDetectionV2_view = view () {\n QualysHostDetectionV2_CL\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id_s', ''),\n HostId = column_ifexists('HostId_s', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod_s', ''),\n OperatingSystem = column_ifexists('OperatingSystem_s', ''),\n DnsName = column_ifexists('DnsName_s', ''),\n NetBios = column_ifexists('NetBios_s', ''),\n QGHostId = column_ifexists('QGHostId_g', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime_t', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''),\n QID = column_ifexists('QID_s', ''),\n SSL = column_ifexists('SSL_s', ''),\n Status = column_ifexists('Status_s', ''),\n Ignored = column_ifexists('Ignored_s', ''),\n Severity = column_ifexists('Severity_s', ''),\n Disabled = column_ifexists('Disabled_s', ''),\n LastFixed = column_ifexists('LastFixed_t', ''),\n LastFound = column_ifexists('LastFound_t', ''),\n TimesFound = column_ifexists('TimesFound_s', ''),\n FirstFound = column_ifexists('FirstFound_t', ''),\n LastUpdate = column_ifexists('LastUpdate_t', datetime(null)),\n LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)),\n Result_column_count = tostring(column_ifexists('Result_column_count_d', '')),\n Results_0 = column_ifexists('Results_0_s', ''),\n Type = column_ifexists('Type', ''),\n _ResourceId = column_ifexists('_ResourceId', '')\n };\n let QualysHostDetectionV3_view = view () {\n QualysHostDetectionV3_CL\n | extend DetectionList = todynamic(DetectionList)\n | extend DetectionItems = iff(\n gettype(DetectionList.DETECTION) == \"array\",\n DetectionList.DETECTION,\n pack_array(DetectionList.DETECTION)\n )\n | mv-expand Detections = DetectionItems to typeof(dynamic)\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id', ''),\n HostId = column_ifexists('HostId', ''),\n HostTags = column_ifexists('HostTags', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod', ''),\n OperatingSystem = column_ifexists('OperatingSystem', ''),\n DnsName = column_ifexists('DnsName', ''),\n NetBios = column_ifexists('NetBios', ''),\n QGHostId = column_ifexists('QGHostId', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime', ''),\n QID = tostring(Detections.QID),\n SSL = tostring(Detections.SSL),\n Status = tostring(Detections.STATUS),\n Ignored = tostring(Detections.IS_IGNORED),\n Severity = tostring(Detections.SEVERITY),\n Disabled = tostring(Detections.IS_DISABLED),\n LastFixed = todatetime(Detections.LAST_FIXED_DATETIME),\n LastFound = todatetime(Detections.LAST_FOUND_DATETIME),\n TimesFound = tostring(Detections.TIMES_FOUND),\n FirstFound = todatetime(Detections.FIRST_FOUND_DATETIME),\n LastUpdate = todatetime(Detections.LAST_UPDATE_DATETIME),\n LastProcessedDateTime = coalesce(\n todatetime(Detections.LAST_PROCESSED_DATETIME),\n todatetime(Detections.LAST_PROCESSED_DATE)),\n Results_0 = tostring(coalesce(Detections.RESULTS['#cdata-section'], Detections.RESULTS)),\n Result_column_count = \"1\",\n Type = tostring(Detections.TYPE),\n _ResourceId = column_ifexists('_ResourceId', '')\n | extend _IngestionTime = todatetime(TimeGenerated)\n | summarize arg_max(_IngestionTime, *) by HostId, QID, Status, SSL, Ignored, Severity, Disabled, LastFixed, LastFound, TimesFound, FirstFound, Result_column_count, Results_0, Type\n | project-away _IngestionTime\n };\n union isfuzzy=true\n (QualysHostDetection_view),\n (QualysHostDetectionV2_view),\n (QualysHostDetectionV3_view)\n | project\n TenantId,\n SourceSystem,\n MG,\n ManagementGroupName,\n TimeGenerated,\n Computer,\n RawData,\n Id,\n HostId,\n HostTags,\n IPAddress,\n TrackingMethod,\n OperatingSystem,\n DnsName,\n NetBios,\n QGHostId,\n LastScanDateTime,\n LastVMScannedDateTime,\n LastVMAuthScannedDateTime,\n QID,\n SSL,\n Type,\n Status,\n Ignored,\n Severity,\n Disabled,\n LastFixed,\n LastFound,\n TimesFound,\n FirstFound,\n LastUpdate,\n LastProcessedDateTime,\n Result_column_count,\n Results_0,\n _ResourceId\n | extend\n HostId = tostring(HostId),\n QID = tostring(QID),\n Status = tostring(Status),\n LastProcessedDateTime = todatetime(LastProcessedDateTime),\n LastUpdate = todatetime(LastUpdate),\n LastFound = todatetime(LastFound)\n | extend DetectionTime = coalesce(LastProcessedDateTime, LastUpdate, LastFound, todatetime(TimeGenerated))\n};\nlet threshold = 10;\n__xdr_inline_QualysHostDetection\n| where Severity == \"5\"\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by NetBios, IPAddress\n| where count_ >= threshold" + }, + "displayName": "High Number of Urgent Vulnerabilities Detected", + "detectionAction": { + "alertTemplate": { + "customDetails": { + "HostFullName": "NetBios" + }, + "severity": "medium", + "title": "High Number of Urgent Vulnerabilities Detected", + "entityMappings": { + "hosts": [ + { + "netBiosNameColumn": "NetBios", + "id": "[if(true(), 'host1', 'host1')]" + } + ], + "ips": [ + { + "addressColumn": "IPAddress", + "id": "[if(true(), 'ip1', 'ip1')]" + } + ] + }, + "tactics": [ + { + "tactic": "InitialAccess", + "techniques": [ + { + "technique": "T1190" + } + ] + } + ], + "description": "'This creates an incident when a host has a high number of urgent, severity 5, vulnerabilities detected.'" + } + }, + "status": "disabled", + "schedule": { + "frequency": "PT1H" + }, + "contentProvenance": { + "packageId": "azuresentinel.azure-sentinel-qualysvm", + "packageVersion": "3.2.2", + "contentId": "xdr-3edb7215-250b-40c0-8b46-79093949242d", + "contentVersion": "3.2.1" + } + } + } + } + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "xdr-3edb7215-250b-40c0-8b46-79093949242d", + "contentKind": "CustomDetection", + "displayName": "High Number of Urgent Vulnerabilities Detected", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','cd','-', uniqueString(concat(variables('_solutionId'),'-','CustomDetection','-','xdr-3edb7215-250b-40c0-8b46-79093949242d','-','3.2.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','cd','-', uniqueString(concat(variables('_solutionId'),'-','CustomDetection','-','xdr-3edb7215-250b-40c0-8b46-79093949242d','-','3.2.1')))]", + "version": "3.2.1", + "contentProvenance": { + "packageId": "azuresentinel.azure-sentinel-qualysvm", + "packageVersion": "3.2.2", + "contentId": "xdr-3edb7215-250b-40c0-8b46-79093949242d", + "contentVersion": "3.2.1", + "requiredWorkloads": [ + { + "workload": "sentinel", + "dataTypes": [ + "QualysHostDetection_CL", + "QualysHostDetectionV2_CL", + "QualysHostDetectionV3_CL" + ] + } + ], + "requiredDataConnectors": [ + { + "dataTypes": [ + "QualysHostDetection" + ], + "connectorId": "QualysVMLogsCCPDefinition" + } + ] + } + }, + "condition": "[parameters('DeployCustomDetection')]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "QualysVM-CD-xdr-6116dc19-475a-4148-84b2-efe89c073e27", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "mode": "Incremental", + "expressionEvaluationOptions": { + "scope": "inner" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "imports": { + "MicrosoftSecurity": { + "provider": "MicrosoftSecurity", + "version": "1.0.0" + } + }, + "resources": { + "detectionRule": { + "import": "MicrosoftSecurity", + "type": "Microsoft.Security/detectionRules@2026-06-01-preview", + "properties": { + "id": "[if(true(), 'xdr-6116dc19-475a-4148-84b2-efe89c073e27', 'xdr-6116dc19-475a-4148-84b2-efe89c073e27')]", + "queryCondition": { + "queryText": "let __xdr_inline_QualysHostDetection = view () {\nlet QualysHostDetection_view = view () {\n QualysHostDetection_CL\n | mv-expand todynamic(Detections_s)\n | extend\n Severity_s = tostring(Detections_s.Severity),\n Status_s = tostring(Detections_s.Status),\n QID_s = tostring(Detections_s.Results)\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id_s', ''),\n HostId = column_ifexists('HostId_s', ''),\n HostTags = column_ifexists('HostTags_s', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod_s', ''),\n OperatingSystem = column_ifexists('OperatingSystem_s', ''),\n DnsName = column_ifexists('DnsName_s', ''),\n NetBios = column_ifexists('NetBios_s', ''),\n QGHostId = column_ifexists('QGHostId_g', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime_t', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''),\n QID = column_ifexists('QID_s', ''),\n SSL = column_ifexists('SSL_s', ''),\n Status = column_ifexists('Status_s', ''),\n Ignored = column_ifexists('Ignored_s', ''),\n Severity = column_ifexists('Severity_s', ''),\n Disabled = column_ifexists('Disabled_s', ''),\n LastFixed = column_ifexists('LastFixed_t', ''),\n LastFound = column_ifexists('LastFound_t', ''),\n TimesFound = column_ifexists('TimesFound_s', ''),\n FirstFound = column_ifexists('FirstFound_t', ''),\n LastUpdate = column_ifexists('LastUpdate_t', datetime(null)),\n LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)),\n Result_column_count = tostring(column_ifexists('Result_column_count_d', '')),\n Results_0 = column_ifexists('Results_0_s', ''),\n Type = column_ifexists('Type', ''),\n _ResourceId = column_ifexists('_ResourceId', '')\n };\n let QualysHostDetectionV2_view = view () {\n QualysHostDetectionV2_CL\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id_s', ''),\n HostId = column_ifexists('HostId_s', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod_s', ''),\n OperatingSystem = column_ifexists('OperatingSystem_s', ''),\n DnsName = column_ifexists('DnsName_s', ''),\n NetBios = column_ifexists('NetBios_s', ''),\n QGHostId = column_ifexists('QGHostId_g', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime_t', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''),\n QID = column_ifexists('QID_s', ''),\n SSL = column_ifexists('SSL_s', ''),\n Status = column_ifexists('Status_s', ''),\n Ignored = column_ifexists('Ignored_s', ''),\n Severity = column_ifexists('Severity_s', ''),\n Disabled = column_ifexists('Disabled_s', ''),\n LastFixed = column_ifexists('LastFixed_t', ''),\n LastFound = column_ifexists('LastFound_t', ''),\n TimesFound = column_ifexists('TimesFound_s', ''),\n FirstFound = column_ifexists('FirstFound_t', ''),\n LastUpdate = column_ifexists('LastUpdate_t', datetime(null)),\n LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)),\n Result_column_count = tostring(column_ifexists('Result_column_count_d', '')),\n Results_0 = column_ifexists('Results_0_s', ''),\n Type = column_ifexists('Type', ''),\n _ResourceId = column_ifexists('_ResourceId', '')\n };\n let QualysHostDetectionV3_view = view () {\n QualysHostDetectionV3_CL\n | extend DetectionList = todynamic(DetectionList)\n | extend DetectionItems = iff(\n gettype(DetectionList.DETECTION) == \"array\",\n DetectionList.DETECTION,\n pack_array(DetectionList.DETECTION)\n )\n | mv-expand Detections = DetectionItems to typeof(dynamic)\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id', ''),\n HostId = column_ifexists('HostId', ''),\n HostTags = column_ifexists('HostTags', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod', ''),\n OperatingSystem = column_ifexists('OperatingSystem', ''),\n DnsName = column_ifexists('DnsName', ''),\n NetBios = column_ifexists('NetBios', ''),\n QGHostId = column_ifexists('QGHostId', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime', ''),\n QID = tostring(Detections.QID),\n SSL = tostring(Detections.SSL),\n Status = tostring(Detections.STATUS),\n Ignored = tostring(Detections.IS_IGNORED),\n Severity = tostring(Detections.SEVERITY),\n Disabled = tostring(Detections.IS_DISABLED),\n LastFixed = todatetime(Detections.LAST_FIXED_DATETIME),\n LastFound = todatetime(Detections.LAST_FOUND_DATETIME),\n TimesFound = tostring(Detections.TIMES_FOUND),\n FirstFound = todatetime(Detections.FIRST_FOUND_DATETIME),\n LastUpdate = todatetime(Detections.LAST_UPDATE_DATETIME),\n LastProcessedDateTime = coalesce(\n todatetime(Detections.LAST_PROCESSED_DATETIME),\n todatetime(Detections.LAST_PROCESSED_DATE)),\n Results_0 = tostring(coalesce(Detections.RESULTS['#cdata-section'], Detections.RESULTS)),\n Result_column_count = \"1\",\n Type = tostring(Detections.TYPE),\n _ResourceId = column_ifexists('_ResourceId', '')\n | extend _IngestionTime = todatetime(TimeGenerated)\n | summarize arg_max(_IngestionTime, *) by HostId, QID, Status, SSL, Ignored, Severity, Disabled, LastFixed, LastFound, TimesFound, FirstFound, Result_column_count, Results_0, Type\n | project-away _IngestionTime\n };\n union isfuzzy=true\n (QualysHostDetection_view),\n (QualysHostDetectionV2_view),\n (QualysHostDetectionV3_view)\n | project\n TenantId,\n SourceSystem,\n MG,\n ManagementGroupName,\n TimeGenerated,\n Computer,\n RawData,\n Id,\n HostId,\n HostTags,\n IPAddress,\n TrackingMethod,\n OperatingSystem,\n DnsName,\n NetBios,\n QGHostId,\n LastScanDateTime,\n LastVMScannedDateTime,\n LastVMAuthScannedDateTime,\n QID,\n SSL,\n Type,\n Status,\n Ignored,\n Severity,\n Disabled,\n LastFixed,\n LastFound,\n TimesFound,\n FirstFound,\n LastUpdate,\n LastProcessedDateTime,\n Result_column_count,\n Results_0,\n _ResourceId\n | extend\n HostId = tostring(HostId),\n QID = tostring(QID),\n Status = tostring(Status),\n LastProcessedDateTime = todatetime(LastProcessedDateTime),\n LastUpdate = todatetime(LastUpdate),\n LastFound = todatetime(LastFound)\n | extend DetectionTime = coalesce(LastProcessedDateTime, LastUpdate, LastFound, todatetime(TimeGenerated))\n};\nlet threshold = 10;\n__xdr_inline_QualysHostDetection\n| extend Status = tostring(Status), Vulnerability = tostring(QID), Severity = tostring(Severity)\n| where Status =~ \"New\" and Severity == \"5\"\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), dcount(NetBios), RepresentativeHost = take_anyif(NetBios, isnotempty(NetBios)) by tostring(QID)\n| where dcount_NetBios >= threshold\n| extend timestamp = StartTime" + }, + "displayName": "New High Severity Vulnerability Detected Across Multiple Hosts", + "detectionAction": { + "alertTemplate": { + "severity": "medium", + "entityMappings": { + "hosts": [ + { + "netBiosNameColumn": "RepresentativeHost", + "id": "[if(true(), 'host1', 'host1')]" + } + ] + }, + "title": "New High Severity Vulnerability Detected Across Multiple Hosts", + "description": "'This creates an incident when a new high severity vulnerability is detected across multilple hosts'", + "tactics": [ + { + "tactic": "InitialAccess", + "techniques": [ + { + "technique": "T1190" + } + ] + } + ] + } + }, + "status": "disabled", + "schedule": { + "frequency": "PT1H" + }, + "contentProvenance": { + "packageId": "azuresentinel.azure-sentinel-qualysvm", + "packageVersion": "3.2.2", + "contentId": "xdr-6116dc19-475a-4148-84b2-efe89c073e27", + "contentVersion": "3.2.1" + } + } + } + } + } + }, + "condition": "[parameters('DeployCustomDetection')]" + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-cd-',uniquestring('xdr-6116dc19-475a-4148-84b2-efe89c073e27')))]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'QualysVM-CD-xdr-6116dc19-475a-4148-84b2-efe89c073e27')]", + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "'This creates an incident when a new high severity vulnerability is detected across multilple hosts'", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0", + "imports": { + "MicrosoftSecurity": { + "provider": "MicrosoftSecurity", + "version": "1.0.0" + } + }, + "resources": { + "detectionRule": { + "import": "MicrosoftSecurity", + "type": "Microsoft.Security/detectionRules@2026-06-01-preview", + "properties": { + "id": "[if(true(), 'xdr-6116dc19-475a-4148-84b2-efe89c073e27', 'xdr-6116dc19-475a-4148-84b2-efe89c073e27')]", + "queryCondition": { + "queryText": "let __xdr_inline_QualysHostDetection = view () {\nlet QualysHostDetection_view = view () {\n QualysHostDetection_CL\n | mv-expand todynamic(Detections_s)\n | extend\n Severity_s = tostring(Detections_s.Severity),\n Status_s = tostring(Detections_s.Status),\n QID_s = tostring(Detections_s.Results)\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id_s', ''),\n HostId = column_ifexists('HostId_s', ''),\n HostTags = column_ifexists('HostTags_s', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod_s', ''),\n OperatingSystem = column_ifexists('OperatingSystem_s', ''),\n DnsName = column_ifexists('DnsName_s', ''),\n NetBios = column_ifexists('NetBios_s', ''),\n QGHostId = column_ifexists('QGHostId_g', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime_t', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''),\n QID = column_ifexists('QID_s', ''),\n SSL = column_ifexists('SSL_s', ''),\n Status = column_ifexists('Status_s', ''),\n Ignored = column_ifexists('Ignored_s', ''),\n Severity = column_ifexists('Severity_s', ''),\n Disabled = column_ifexists('Disabled_s', ''),\n LastFixed = column_ifexists('LastFixed_t', ''),\n LastFound = column_ifexists('LastFound_t', ''),\n TimesFound = column_ifexists('TimesFound_s', ''),\n FirstFound = column_ifexists('FirstFound_t', ''),\n LastUpdate = column_ifexists('LastUpdate_t', datetime(null)),\n LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)),\n Result_column_count = tostring(column_ifexists('Result_column_count_d', '')),\n Results_0 = column_ifexists('Results_0_s', ''),\n Type = column_ifexists('Type', ''),\n _ResourceId = column_ifexists('_ResourceId', '')\n };\n let QualysHostDetectionV2_view = view () {\n QualysHostDetectionV2_CL\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id_s', ''),\n HostId = column_ifexists('HostId_s', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod_s', ''),\n OperatingSystem = column_ifexists('OperatingSystem_s', ''),\n DnsName = column_ifexists('DnsName_s', ''),\n NetBios = column_ifexists('NetBios_s', ''),\n QGHostId = column_ifexists('QGHostId_g', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime_t', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''),\n QID = column_ifexists('QID_s', ''),\n SSL = column_ifexists('SSL_s', ''),\n Status = column_ifexists('Status_s', ''),\n Ignored = column_ifexists('Ignored_s', ''),\n Severity = column_ifexists('Severity_s', ''),\n Disabled = column_ifexists('Disabled_s', ''),\n LastFixed = column_ifexists('LastFixed_t', ''),\n LastFound = column_ifexists('LastFound_t', ''),\n TimesFound = column_ifexists('TimesFound_s', ''),\n FirstFound = column_ifexists('FirstFound_t', ''),\n LastUpdate = column_ifexists('LastUpdate_t', datetime(null)),\n LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)),\n Result_column_count = tostring(column_ifexists('Result_column_count_d', '')),\n Results_0 = column_ifexists('Results_0_s', ''),\n Type = column_ifexists('Type', ''),\n _ResourceId = column_ifexists('_ResourceId', '')\n };\n let QualysHostDetectionV3_view = view () {\n QualysHostDetectionV3_CL\n | extend DetectionList = todynamic(DetectionList)\n | extend DetectionItems = iff(\n gettype(DetectionList.DETECTION) == \"array\",\n DetectionList.DETECTION,\n pack_array(DetectionList.DETECTION)\n )\n | mv-expand Detections = DetectionItems to typeof(dynamic)\n | extend\n TenantId = column_ifexists('TenantId', ''),\n SourceSystem = column_ifexists('SourceSystem', ''),\n MG = column_ifexists('MG', ''),\n ManagementGroupName = column_ifexists('ManagementGroupName', ''),\n TimeGenerated = column_ifexists('TimeGenerated', ''),\n Computer = column_ifexists('Computer', ''),\n RawData = column_ifexists('RawData', ''),\n Id = column_ifexists('Id', ''),\n HostId = column_ifexists('HostId', ''),\n HostTags = column_ifexists('HostTags', ''),\n IPAddress = column_ifexists('IPAddress', ''),\n TrackingMethod = column_ifexists('TrackingMethod', ''),\n OperatingSystem = column_ifexists('OperatingSystem', ''),\n DnsName = column_ifexists('DnsName', ''),\n NetBios = column_ifexists('NetBios', ''),\n QGHostId = column_ifexists('QGHostId', ''),\n LastScanDateTime = column_ifexists('LastScanDateTime', ''),\n LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime', ''),\n LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime', ''),\n QID = tostring(Detections.QID),\n SSL = tostring(Detections.SSL),\n Status = tostring(Detections.STATUS),\n Ignored = tostring(Detections.IS_IGNORED),\n Severity = tostring(Detections.SEVERITY),\n Disabled = tostring(Detections.IS_DISABLED),\n LastFixed = todatetime(Detections.LAST_FIXED_DATETIME),\n LastFound = todatetime(Detections.LAST_FOUND_DATETIME),\n TimesFound = tostring(Detections.TIMES_FOUND),\n FirstFound = todatetime(Detections.FIRST_FOUND_DATETIME),\n LastUpdate = todatetime(Detections.LAST_UPDATE_DATETIME),\n LastProcessedDateTime = coalesce(\n todatetime(Detections.LAST_PROCESSED_DATETIME),\n todatetime(Detections.LAST_PROCESSED_DATE)),\n Results_0 = tostring(coalesce(Detections.RESULTS['#cdata-section'], Detections.RESULTS)),\n Result_column_count = \"1\",\n Type = tostring(Detections.TYPE),\n _ResourceId = column_ifexists('_ResourceId', '')\n | extend _IngestionTime = todatetime(TimeGenerated)\n | summarize arg_max(_IngestionTime, *) by HostId, QID, Status, SSL, Ignored, Severity, Disabled, LastFixed, LastFound, TimesFound, FirstFound, Result_column_count, Results_0, Type\n | project-away _IngestionTime\n };\n union isfuzzy=true\n (QualysHostDetection_view),\n (QualysHostDetectionV2_view),\n (QualysHostDetectionV3_view)\n | project\n TenantId,\n SourceSystem,\n MG,\n ManagementGroupName,\n TimeGenerated,\n Computer,\n RawData,\n Id,\n HostId,\n HostTags,\n IPAddress,\n TrackingMethod,\n OperatingSystem,\n DnsName,\n NetBios,\n QGHostId,\n LastScanDateTime,\n LastVMScannedDateTime,\n LastVMAuthScannedDateTime,\n QID,\n SSL,\n Type,\n Status,\n Ignored,\n Severity,\n Disabled,\n LastFixed,\n LastFound,\n TimesFound,\n FirstFound,\n LastUpdate,\n LastProcessedDateTime,\n Result_column_count,\n Results_0,\n _ResourceId\n | extend\n HostId = tostring(HostId),\n QID = tostring(QID),\n Status = tostring(Status),\n LastProcessedDateTime = todatetime(LastProcessedDateTime),\n LastUpdate = todatetime(LastUpdate),\n LastFound = todatetime(LastFound)\n | extend DetectionTime = coalesce(LastProcessedDateTime, LastUpdate, LastFound, todatetime(TimeGenerated))\n};\nlet threshold = 10;\n__xdr_inline_QualysHostDetection\n| extend Status = tostring(Status), Vulnerability = tostring(QID), Severity = tostring(Severity)\n| where Status =~ \"New\" and Severity == \"5\"\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), dcount(NetBios), RepresentativeHost = take_anyif(NetBios, isnotempty(NetBios)) by tostring(QID)\n| where dcount_NetBios >= threshold\n| extend timestamp = StartTime" + }, + "displayName": "New High Severity Vulnerability Detected Across Multiple Hosts", + "detectionAction": { + "alertTemplate": { + "severity": "medium", + "entityMappings": { + "hosts": [ + { + "netBiosNameColumn": "RepresentativeHost", + "id": "[if(true(), 'host1', 'host1')]" + } + ] + }, + "title": "New High Severity Vulnerability Detected Across Multiple Hosts", + "description": "'This creates an incident when a new high severity vulnerability is detected across multilple hosts'", + "tactics": [ + { + "tactic": "InitialAccess", + "techniques": [ + { + "technique": "T1190" + } + ] + } + ] + } + }, + "status": "disabled", + "schedule": { + "frequency": "PT1H" + }, + "contentProvenance": { + "packageId": "azuresentinel.azure-sentinel-qualysvm", + "packageVersion": "3.2.2", + "contentId": "xdr-6116dc19-475a-4148-84b2-efe89c073e27", + "contentVersion": "3.2.1" + } + } + } + } + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "xdr-6116dc19-475a-4148-84b2-efe89c073e27", + "contentKind": "CustomDetection", + "displayName": "New High Severity Vulnerability Detected Across Multiple Hosts", + "contentProductId": "[concat(take(variables('_solutionId'),50),'-','cd','-', uniqueString(concat(variables('_solutionId'),'-','CustomDetection','-','xdr-6116dc19-475a-4148-84b2-efe89c073e27','-','3.2.1')))]", + "id": "[concat(take(variables('_solutionId'),50),'-','cd','-', uniqueString(concat(variables('_solutionId'),'-','CustomDetection','-','xdr-6116dc19-475a-4148-84b2-efe89c073e27','-','3.2.1')))]", + "version": "3.2.1", + "contentProvenance": { + "packageId": "azuresentinel.azure-sentinel-qualysvm", + "packageVersion": "3.2.2", + "contentId": "xdr-6116dc19-475a-4148-84b2-efe89c073e27", + "contentVersion": "3.2.1", + "requiredWorkloads": [ + { + "workload": "sentinel", + "dataTypes": [ + "QualysHostDetection_CL", + "QualysHostDetectionV2_CL", + "QualysHostDetectionV3_CL" + ] + } + ], + "requiredDataConnectors": [ + { + "dataTypes": [ + "QualysHostDetection" + ], + "connectorId": "QualysVMLogsCCPDefinition" + } + ] + } + }, + "condition": "[parameters('DeployCustomDetection')]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "pid-7d9ebe77-5cfa-4d99-ae0c-66196a716a93-partnercenter", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } } ], "outputs": {} diff --git a/Solutions/QualysVM/Package/testParameters.json b/Solutions/QualysVM/Package/testParameters.json index aed8fcc514f..8064873e210 100644 --- a/Solutions/QualysVM/Package/testParameters.json +++ b/Solutions/QualysVM/Package/testParameters.json @@ -42,5 +42,47 @@ "metadata": { "description": "Name for the workbook" } + }, + "DeployAnalyticsRule": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Deploy the Microsoft Sentinel analytic rules carried by this solution (ContentKind AnalyticsRule and its AnalyticsRuleTemplate registration face)." + } + }, + "DeployPlaybook": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Deploy the playbooks carried by this solution and the automation assets they depend on (PlaybookTemplate, AzureFunction, LogicAppsCustomConnector). These are never shipped independently of the playbook they serve." + } + }, + "DeployWorkbook": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Deploy the Microsoft Sentinel workbook content and its Content Hub registration carried by this solution (ContentKind Workbook and WorkbookTemplate)." + } + }, + "DeployDataConnector": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Deploy the data connectors carried by this solution, together with their metadata and catalog entries (ContentKind DataConnector, ResourceDataConnector and DataType)." + } + }, + "DeployCustomDetection": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Deploy the XDR-native custom detection resources and their Content Hub registrations (ContentKind CustomDetection and CustomDetectionTemplate). Independent of DeployAnalyticsRule: both kinds can be selected together. Existing analytic-rule instances are not disabled or removed. CD enabled or disabled status is controlled separately from this deployment switch." + } + }, + "DeployParser": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Deploy the parsers and their registrations carried by this solution." + } } } diff --git a/Solutions/QualysVM/ReleaseNotes.md b/Solutions/QualysVM/ReleaseNotes.md index 35615151108..8788424ff23 100644 --- a/Solutions/QualysVM/ReleaseNotes.md +++ b/Solutions/QualysVM/ReleaseNotes.md @@ -1,5 +1,6 @@ | **Version** | **Date Modified (DD-MM-YYYY)** | **Change History** | |-------------|--------------------------------|----------------------------------------------------------------| +| 3.2.2 | 20-10-2026 | Added two disabled Defender XDR Custom Detections alongside the existing Microsoft Sentinel content. Defender Workbook drafts remain excluded pending review. | | 3.1.1 | 22-09-2026 | Updated the Playbooks to fix the Azure deploy button issue for GOV and corrected the parameter value for Service Endpoint. | | 3.1.0 | 14-09-2026 | Updated the **QualysHostDetection Parser** to reliably parse both single-object and array detection responses from the CCF connector. | | 3.0.11 | 10-09-2026 | Updated the Qualys Host Detection Parser to deduplicate repeated V3 snapshots using stable vulnerability content and lifecycle fields, preserve ingestion TimeGenerated, and expose the source timestamp as DetectionTime. Updated the CCP Data Connector to use explicit table references for Defender portal query compatibility. | diff --git a/Solutions/QualysVM/SolutionMetadata.json b/Solutions/QualysVM/SolutionMetadata.json index 3e34321a2ab..ac3b6bcaaca 100644 --- a/Solutions/QualysVM/SolutionMetadata.json +++ b/Solutions/QualysVM/SolutionMetadata.json @@ -1,16 +1,23 @@ { + "trackingId": "pid-7d9ebe77-5cfa-4d99-ae0c-66196a716a93-partnercenter", "publisherId": "azuresentinel", "offerId": "azure-sentinel-qualysvm", "firstPublishDate": "2020-12-14", "lastPublishDate": "2025-11-18", - "providers": ["Qualys"], + "providers": [ + "Qualys" + ], "categories": { - "domains": ["Security - Vulnerability Management","Security - Automation (SOAR)"] + "domains": [ + "Security - Vulnerability Management", + "Security - Automation (SOAR)" + ] }, "support": { - "name": "Microsoft Corporation", - "email": "support@microsoft.com", - "tier": "Microsoft", - "link": "https://support.microsoft.com" - } -} \ No newline at end of file + "name": "Microsoft Corporation", + "email": "support@microsoft.com", + "tier": "Microsoft", + "link": "https://support.microsoft.com" + }, + "version": "3.2.2" +} diff --git a/Solutions/QualysVM/XDR Detections/HighNumberofVulnDetectedV2.yaml b/Solutions/QualysVM/XDR Detections/HighNumberofVulnDetectedV2.yaml new file mode 100644 index 00000000000..53d70dfbd59 --- /dev/null +++ b/Solutions/QualysVM/XDR Detections/HighNumberofVulnDetectedV2.yaml @@ -0,0 +1,299 @@ +schemaVersion: 1.0.0 +version: 3.2.1 +kind: CustomDetection +resourceType: Microsoft.Security/detectionRules +apiVersion: 2026-06-01-preview +requiredDataConnectors: +- connectorId: QualysVMLogsCCPDefinition + dataTypes: + - QualysHostDetection +contentProvenance: + requiredWorkloads: + - workload: sentinel + dataTypes: + - QualysHostDetection_CL + - QualysHostDetectionV2_CL + - QualysHostDetectionV3_CL + requiredDataConnectors: + - connectorId: QualysVMLogsCCPDefinition + dataTypes: + - QualysHostDetection + dependencyReviewRequired: false + source: + platform: Microsoft Sentinel + kind: AnalyticsRule + id: 3edb7215-250b-40c0-8b46-79093949242d + path: Analytic Rules/HighNumberofVulnDetectedV2.yaml + version: 3.2.1 + querySha256: aadc5cb60a259b492608ca7d2cdbcada738a16b81a3b6ff82e9ac8caf81f1b11 + schedule: + queryFrequency: 1h + queryPeriod: 1h + conversion: + tool: sentinel-to-xdr-migration + version: 0.1.0 + status: converted + reviewRequired: false + reviewReasons: [] + requiredWorkloads: + - sentinel + warnings: + - isfuzzy unions can still fail semantic binding in Advanced Hunting + - Renamed local parser binding QualysHostDetection to __xdr_inline_QualysHostDetection to avoid a saved parser collision + - 'Schedule assessment: source frequency=1h, lookback=1h; target frequency=PT1H, native lookback=unknown/unconfigured; + mixed, Sentinel-only, or unknown query surface; no custom lookback is configured in the payload. See https://learn.microsoft.com/en-us/defender-xdr/custom-detection-rules#lookback' + - customDetails 'HostFullName' column `NetBios` has an unproven output binding after a destructive projection; the explicit + binding is retained, but static analysis cannot establish its output and runtime output-schema verification is required + - Custom-detail values must remain within the documented combined 4 KB per-alert limit; runtime values are unverified + and the service drops all custom details if exceeded + informational: [] + errors: [] + originalTactics: + - InitialAccess + originalTechniques: + - T1190 + reviewScope: + sourceDocumentSha256: 9f0187880320bb3397193862d5ea19ed38de9fe076672f4600a2ae1b9515d8f0 + targetQuerySha256: 820b0c467ea4b5e97125942fc31eaa229dae373b41b963702ebc9589b31a13ef + reviewDecisions: + - reason: isfuzzy unions can still fail semantic binding in Advanced Hunting + decision: accepted + sourceDocumentSha256: 9f0187880320bb3397193862d5ea19ed38de9fe076672f4600a2ae1b9515d8f0 + targetQuerySha256: 820b0c467ea4b5e97125942fc31eaa229dae373b41b963702ebc9589b31a13ef + eventTimeWindow: + status: default-native-lookback + appliedDefault: hourly-1h-to-native-4h-lookback + queryFrequency: 1h + queryPeriod: 1h + targetFrequency: PT1H + effectiveLookback: PT4H + originalSentinelLookbackSeconds: 3600 + effectiveEventWindowSeconds: 14400 + xdrServiceWindowSeconds: 14400 + filterInjected: false + parityClaimed: false + hasAggregation: true + aggregationWindowDifference: true + timingDifference: Four hours (PT4H / 14400s) is the native XDR ingestion-based lookback window for PT1H execution frequency, + not the execution frequency; original Sentinel queryPeriod was 1h (3600s). + aggregationDifference: Query contains summarize/make-series aggregation; evaluating over the native 4-hour XDR lookback + window without an injected 1h event-time filter may aggregate up to 4 hours of events per hourly run instead of 1 + hour. + sourceDocumentSha256: 9f0187880320bb3397193862d5ea19ed38de9fe076672f4600a2ae1b9515d8f0 + targetQuerySha256: 820b0c467ea4b5e97125942fc31eaa229dae373b41b963702ebc9589b31a13ef + acceptedReviewReasons: + - isfuzzy unions can still fail semantic binding in Advanced Hunting +properties: + id: xdr-3edb7215-250b-40c0-8b46-79093949242d + displayName: High Number of Urgent Vulnerabilities Detected + status: disabled + queryCondition: + queryText: |- + let __xdr_inline_QualysHostDetection = view () { + let QualysHostDetection_view = view () { + QualysHostDetection_CL + | mv-expand todynamic(Detections_s) + | extend + Severity_s = tostring(Detections_s.Severity), + Status_s = tostring(Detections_s.Status), + QID_s = tostring(Detections_s.Results) + | extend + TenantId = column_ifexists('TenantId', ''), + SourceSystem = column_ifexists('SourceSystem', ''), + MG = column_ifexists('MG', ''), + ManagementGroupName = column_ifexists('ManagementGroupName', ''), + TimeGenerated = column_ifexists('TimeGenerated', ''), + Computer = column_ifexists('Computer', ''), + RawData = column_ifexists('RawData', ''), + Id = column_ifexists('Id_s', ''), + HostId = column_ifexists('HostId_s', ''), + HostTags = column_ifexists('HostTags_s', ''), + IPAddress = column_ifexists('IPAddress', ''), + TrackingMethod = column_ifexists('TrackingMethod_s', ''), + OperatingSystem = column_ifexists('OperatingSystem_s', ''), + DnsName = column_ifexists('DnsName_s', ''), + NetBios = column_ifexists('NetBios_s', ''), + QGHostId = column_ifexists('QGHostId_g', ''), + LastScanDateTime = column_ifexists('LastScanDateTime_t', ''), + LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''), + LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''), + QID = column_ifexists('QID_s', ''), + SSL = column_ifexists('SSL_s', ''), + Status = column_ifexists('Status_s', ''), + Ignored = column_ifexists('Ignored_s', ''), + Severity = column_ifexists('Severity_s', ''), + Disabled = column_ifexists('Disabled_s', ''), + LastFixed = column_ifexists('LastFixed_t', ''), + LastFound = column_ifexists('LastFound_t', ''), + TimesFound = column_ifexists('TimesFound_s', ''), + FirstFound = column_ifexists('FirstFound_t', ''), + LastUpdate = column_ifexists('LastUpdate_t', datetime(null)), + LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)), + Result_column_count = tostring(column_ifexists('Result_column_count_d', '')), + Results_0 = column_ifexists('Results_0_s', ''), + Type = column_ifexists('Type', ''), + _ResourceId = column_ifexists('_ResourceId', '') + }; + let QualysHostDetectionV2_view = view () { + QualysHostDetectionV2_CL + | extend + TenantId = column_ifexists('TenantId', ''), + SourceSystem = column_ifexists('SourceSystem', ''), + MG = column_ifexists('MG', ''), + ManagementGroupName = column_ifexists('ManagementGroupName', ''), + TimeGenerated = column_ifexists('TimeGenerated', ''), + Computer = column_ifexists('Computer', ''), + RawData = column_ifexists('RawData', ''), + Id = column_ifexists('Id_s', ''), + HostId = column_ifexists('HostId_s', ''), + IPAddress = column_ifexists('IPAddress', ''), + TrackingMethod = column_ifexists('TrackingMethod_s', ''), + OperatingSystem = column_ifexists('OperatingSystem_s', ''), + DnsName = column_ifexists('DnsName_s', ''), + NetBios = column_ifexists('NetBios_s', ''), + QGHostId = column_ifexists('QGHostId_g', ''), + LastScanDateTime = column_ifexists('LastScanDateTime_t', ''), + LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''), + LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''), + QID = column_ifexists('QID_s', ''), + SSL = column_ifexists('SSL_s', ''), + Status = column_ifexists('Status_s', ''), + Ignored = column_ifexists('Ignored_s', ''), + Severity = column_ifexists('Severity_s', ''), + Disabled = column_ifexists('Disabled_s', ''), + LastFixed = column_ifexists('LastFixed_t', ''), + LastFound = column_ifexists('LastFound_t', ''), + TimesFound = column_ifexists('TimesFound_s', ''), + FirstFound = column_ifexists('FirstFound_t', ''), + LastUpdate = column_ifexists('LastUpdate_t', datetime(null)), + LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)), + Result_column_count = tostring(column_ifexists('Result_column_count_d', '')), + Results_0 = column_ifexists('Results_0_s', ''), + Type = column_ifexists('Type', ''), + _ResourceId = column_ifexists('_ResourceId', '') + }; + let QualysHostDetectionV3_view = view () { + QualysHostDetectionV3_CL + | extend DetectionList = todynamic(DetectionList) + | extend DetectionItems = iff( + gettype(DetectionList.DETECTION) == "array", + DetectionList.DETECTION, + pack_array(DetectionList.DETECTION) + ) + | mv-expand Detections = DetectionItems to typeof(dynamic) + | extend + TenantId = column_ifexists('TenantId', ''), + SourceSystem = column_ifexists('SourceSystem', ''), + MG = column_ifexists('MG', ''), + ManagementGroupName = column_ifexists('ManagementGroupName', ''), + TimeGenerated = column_ifexists('TimeGenerated', ''), + Computer = column_ifexists('Computer', ''), + RawData = column_ifexists('RawData', ''), + Id = column_ifexists('Id', ''), + HostId = column_ifexists('HostId', ''), + HostTags = column_ifexists('HostTags', ''), + IPAddress = column_ifexists('IPAddress', ''), + TrackingMethod = column_ifexists('TrackingMethod', ''), + OperatingSystem = column_ifexists('OperatingSystem', ''), + DnsName = column_ifexists('DnsName', ''), + NetBios = column_ifexists('NetBios', ''), + QGHostId = column_ifexists('QGHostId', ''), + LastScanDateTime = column_ifexists('LastScanDateTime', ''), + LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime', ''), + LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime', ''), + QID = tostring(Detections.QID), + SSL = tostring(Detections.SSL), + Status = tostring(Detections.STATUS), + Ignored = tostring(Detections.IS_IGNORED), + Severity = tostring(Detections.SEVERITY), + Disabled = tostring(Detections.IS_DISABLED), + LastFixed = todatetime(Detections.LAST_FIXED_DATETIME), + LastFound = todatetime(Detections.LAST_FOUND_DATETIME), + TimesFound = tostring(Detections.TIMES_FOUND), + FirstFound = todatetime(Detections.FIRST_FOUND_DATETIME), + LastUpdate = todatetime(Detections.LAST_UPDATE_DATETIME), + LastProcessedDateTime = coalesce( + todatetime(Detections.LAST_PROCESSED_DATETIME), + todatetime(Detections.LAST_PROCESSED_DATE)), + Results_0 = tostring(coalesce(Detections.RESULTS['#cdata-section'], Detections.RESULTS)), + Result_column_count = "1", + Type = tostring(Detections.TYPE), + _ResourceId = column_ifexists('_ResourceId', '') + | extend _IngestionTime = todatetime(TimeGenerated) + | summarize arg_max(_IngestionTime, *) by HostId, QID, Status, SSL, Ignored, Severity, Disabled, LastFixed, LastFound, TimesFound, FirstFound, Result_column_count, Results_0, Type + | project-away _IngestionTime + }; + union isfuzzy=true + (QualysHostDetection_view), + (QualysHostDetectionV2_view), + (QualysHostDetectionV3_view) + | project + TenantId, + SourceSystem, + MG, + ManagementGroupName, + TimeGenerated, + Computer, + RawData, + Id, + HostId, + HostTags, + IPAddress, + TrackingMethod, + OperatingSystem, + DnsName, + NetBios, + QGHostId, + LastScanDateTime, + LastVMScannedDateTime, + LastVMAuthScannedDateTime, + QID, + SSL, + Type, + Status, + Ignored, + Severity, + Disabled, + LastFixed, + LastFound, + TimesFound, + FirstFound, + LastUpdate, + LastProcessedDateTime, + Result_column_count, + Results_0, + _ResourceId + | extend + HostId = tostring(HostId), + QID = tostring(QID), + Status = tostring(Status), + LastProcessedDateTime = todatetime(LastProcessedDateTime), + LastUpdate = todatetime(LastUpdate), + LastFound = todatetime(LastFound) + | extend DetectionTime = coalesce(LastProcessedDateTime, LastUpdate, LastFound, todatetime(TimeGenerated)) + }; + let threshold = 10; + __xdr_inline_QualysHostDetection + | where Severity == "5" + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by NetBios, IPAddress + | where count_ >= threshold + schedule: + frequency: PT1H + detectionAction: + alertTemplate: + title: High Number of Urgent Vulnerabilities Detected + description: '''This creates an incident when a host has a high number of urgent, severity 5, vulnerabilities detected.''' + severity: medium + entityMappings: + hosts: + - id: host1 + netBiosNameColumn: NetBios + ips: + - id: ip1 + addressColumn: IPAddress + tactics: + - tactic: InitialAccess + techniques: + - technique: T1190 + customDetails: + HostFullName: NetBios diff --git a/Solutions/QualysVM/XDR Detections/NewHighSeverityVulnDetectedAcrossMulitpleHostsV2.yaml b/Solutions/QualysVM/XDR Detections/NewHighSeverityVulnDetectedAcrossMulitpleHostsV2.yaml new file mode 100644 index 00000000000..57e06dec70c --- /dev/null +++ b/Solutions/QualysVM/XDR Detections/NewHighSeverityVulnDetectedAcrossMulitpleHostsV2.yaml @@ -0,0 +1,292 @@ +schemaVersion: 1.0.0 +version: 3.2.1 +kind: CustomDetection +resourceType: Microsoft.Security/detectionRules +apiVersion: 2026-06-01-preview +requiredDataConnectors: +- connectorId: QualysVMLogsCCPDefinition + dataTypes: + - QualysHostDetection +contentProvenance: + requiredWorkloads: + - workload: sentinel + dataTypes: + - QualysHostDetection_CL + - QualysHostDetectionV2_CL + - QualysHostDetectionV3_CL + requiredDataConnectors: + - connectorId: QualysVMLogsCCPDefinition + dataTypes: + - QualysHostDetection + dependencyReviewRequired: false + source: + platform: Microsoft Sentinel + kind: AnalyticsRule + id: 6116dc19-475a-4148-84b2-efe89c073e27 + path: Analytic Rules/NewHighSeverityVulnDetectedAcrossMulitpleHostsV2.yaml + version: 3.2.1 + querySha256: af7aa2c23e7e1acfb51633cd9b63b827663618d7f2205293ff9c20ecbbf530ca + schedule: + queryFrequency: 1h + queryPeriod: 1h + conversion: + tool: sentinel-to-xdr-migration + version: 0.1.0 + status: converted + reviewRequired: false + reviewReasons: [] + requiredWorkloads: + - sentinel + warnings: + - isfuzzy unions can still fail semantic binding in Advanced Hunting + - Renamed local parser binding QualysHostDetection to __xdr_inline_QualysHostDetection to avoid a saved parser collision + - 'Schedule assessment: source frequency=1h, lookback=1h; target frequency=PT1H, native lookback=unknown/unconfigured; + mixed, Sentinel-only, or unknown query surface; no custom lookback is configured in the payload. See https://learn.microsoft.com/en-us/defender-xdr/custom-detection-rules#lookback' + informational: [] + errors: [] + originalTactics: + - InitialAccess + originalTechniques: + - T1190 + reviewScope: + sourceDocumentSha256: 43da0904d914aacebc219bcb7b91325b8009bb00b3f35af32533c4f5e144189d + targetQuerySha256: d204642ad3286c7e0987b6877e2149d4bcdb4ede4b35878edfad0f0ec8b1b661 + reviewDecisions: + - reason: isfuzzy unions can still fail semantic binding in Advanced Hunting + decision: accepted + sourceDocumentSha256: 43da0904d914aacebc219bcb7b91325b8009bb00b3f35af32533c4f5e144189d + targetQuerySha256: d204642ad3286c7e0987b6877e2149d4bcdb4ede4b35878edfad0f0ec8b1b661 + eventTimeWindow: + status: default-native-lookback + appliedDefault: hourly-1h-to-native-4h-lookback + queryFrequency: 1h + queryPeriod: 1h + targetFrequency: PT1H + effectiveLookback: PT4H + originalSentinelLookbackSeconds: 3600 + effectiveEventWindowSeconds: 14400 + xdrServiceWindowSeconds: 14400 + filterInjected: false + parityClaimed: false + hasAggregation: true + aggregationWindowDifference: true + timingDifference: Four hours (PT4H / 14400s) is the native XDR ingestion-based lookback window for PT1H execution frequency, + not the execution frequency; original Sentinel queryPeriod was 1h (3600s). + aggregationDifference: Query contains summarize/make-series aggregation; evaluating over the native 4-hour XDR lookback + window without an injected 1h event-time filter may aggregate up to 4 hours of events per hourly run instead of 1 + hour. + sourceDocumentSha256: 43da0904d914aacebc219bcb7b91325b8009bb00b3f35af32533c4f5e144189d + targetQuerySha256: d204642ad3286c7e0987b6877e2149d4bcdb4ede4b35878edfad0f0ec8b1b661 + acceptedReviewReasons: + - isfuzzy unions can still fail semantic binding in Advanced Hunting +properties: + id: xdr-6116dc19-475a-4148-84b2-efe89c073e27 + displayName: New High Severity Vulnerability Detected Across Multiple Hosts + status: disabled + queryCondition: + queryText: |- + let __xdr_inline_QualysHostDetection = view () { + let QualysHostDetection_view = view () { + QualysHostDetection_CL + | mv-expand todynamic(Detections_s) + | extend + Severity_s = tostring(Detections_s.Severity), + Status_s = tostring(Detections_s.Status), + QID_s = tostring(Detections_s.Results) + | extend + TenantId = column_ifexists('TenantId', ''), + SourceSystem = column_ifexists('SourceSystem', ''), + MG = column_ifexists('MG', ''), + ManagementGroupName = column_ifexists('ManagementGroupName', ''), + TimeGenerated = column_ifexists('TimeGenerated', ''), + Computer = column_ifexists('Computer', ''), + RawData = column_ifexists('RawData', ''), + Id = column_ifexists('Id_s', ''), + HostId = column_ifexists('HostId_s', ''), + HostTags = column_ifexists('HostTags_s', ''), + IPAddress = column_ifexists('IPAddress', ''), + TrackingMethod = column_ifexists('TrackingMethod_s', ''), + OperatingSystem = column_ifexists('OperatingSystem_s', ''), + DnsName = column_ifexists('DnsName_s', ''), + NetBios = column_ifexists('NetBios_s', ''), + QGHostId = column_ifexists('QGHostId_g', ''), + LastScanDateTime = column_ifexists('LastScanDateTime_t', ''), + LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''), + LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''), + QID = column_ifexists('QID_s', ''), + SSL = column_ifexists('SSL_s', ''), + Status = column_ifexists('Status_s', ''), + Ignored = column_ifexists('Ignored_s', ''), + Severity = column_ifexists('Severity_s', ''), + Disabled = column_ifexists('Disabled_s', ''), + LastFixed = column_ifexists('LastFixed_t', ''), + LastFound = column_ifexists('LastFound_t', ''), + TimesFound = column_ifexists('TimesFound_s', ''), + FirstFound = column_ifexists('FirstFound_t', ''), + LastUpdate = column_ifexists('LastUpdate_t', datetime(null)), + LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)), + Result_column_count = tostring(column_ifexists('Result_column_count_d', '')), + Results_0 = column_ifexists('Results_0_s', ''), + Type = column_ifexists('Type', ''), + _ResourceId = column_ifexists('_ResourceId', '') + }; + let QualysHostDetectionV2_view = view () { + QualysHostDetectionV2_CL + | extend + TenantId = column_ifexists('TenantId', ''), + SourceSystem = column_ifexists('SourceSystem', ''), + MG = column_ifexists('MG', ''), + ManagementGroupName = column_ifexists('ManagementGroupName', ''), + TimeGenerated = column_ifexists('TimeGenerated', ''), + Computer = column_ifexists('Computer', ''), + RawData = column_ifexists('RawData', ''), + Id = column_ifexists('Id_s', ''), + HostId = column_ifexists('HostId_s', ''), + IPAddress = column_ifexists('IPAddress', ''), + TrackingMethod = column_ifexists('TrackingMethod_s', ''), + OperatingSystem = column_ifexists('OperatingSystem_s', ''), + DnsName = column_ifexists('DnsName_s', ''), + NetBios = column_ifexists('NetBios_s', ''), + QGHostId = column_ifexists('QGHostId_g', ''), + LastScanDateTime = column_ifexists('LastScanDateTime_t', ''), + LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime_t', ''), + LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime_t', ''), + QID = column_ifexists('QID_s', ''), + SSL = column_ifexists('SSL_s', ''), + Status = column_ifexists('Status_s', ''), + Ignored = column_ifexists('Ignored_s', ''), + Severity = column_ifexists('Severity_s', ''), + Disabled = column_ifexists('Disabled_s', ''), + LastFixed = column_ifexists('LastFixed_t', ''), + LastFound = column_ifexists('LastFound_t', ''), + TimesFound = column_ifexists('TimesFound_s', ''), + FirstFound = column_ifexists('FirstFound_t', ''), + LastUpdate = column_ifexists('LastUpdate_t', datetime(null)), + LastProcessedDateTime = column_ifexists('LastProcessed_t', datetime(null)), + Result_column_count = tostring(column_ifexists('Result_column_count_d', '')), + Results_0 = column_ifexists('Results_0_s', ''), + Type = column_ifexists('Type', ''), + _ResourceId = column_ifexists('_ResourceId', '') + }; + let QualysHostDetectionV3_view = view () { + QualysHostDetectionV3_CL + | extend DetectionList = todynamic(DetectionList) + | extend DetectionItems = iff( + gettype(DetectionList.DETECTION) == "array", + DetectionList.DETECTION, + pack_array(DetectionList.DETECTION) + ) + | mv-expand Detections = DetectionItems to typeof(dynamic) + | extend + TenantId = column_ifexists('TenantId', ''), + SourceSystem = column_ifexists('SourceSystem', ''), + MG = column_ifexists('MG', ''), + ManagementGroupName = column_ifexists('ManagementGroupName', ''), + TimeGenerated = column_ifexists('TimeGenerated', ''), + Computer = column_ifexists('Computer', ''), + RawData = column_ifexists('RawData', ''), + Id = column_ifexists('Id', ''), + HostId = column_ifexists('HostId', ''), + HostTags = column_ifexists('HostTags', ''), + IPAddress = column_ifexists('IPAddress', ''), + TrackingMethod = column_ifexists('TrackingMethod', ''), + OperatingSystem = column_ifexists('OperatingSystem', ''), + DnsName = column_ifexists('DnsName', ''), + NetBios = column_ifexists('NetBios', ''), + QGHostId = column_ifexists('QGHostId', ''), + LastScanDateTime = column_ifexists('LastScanDateTime', ''), + LastVMScannedDateTime = column_ifexists('LastVMScannedDateTime', ''), + LastVMAuthScannedDateTime = column_ifexists('LastVMAuthScannedDateTime', ''), + QID = tostring(Detections.QID), + SSL = tostring(Detections.SSL), + Status = tostring(Detections.STATUS), + Ignored = tostring(Detections.IS_IGNORED), + Severity = tostring(Detections.SEVERITY), + Disabled = tostring(Detections.IS_DISABLED), + LastFixed = todatetime(Detections.LAST_FIXED_DATETIME), + LastFound = todatetime(Detections.LAST_FOUND_DATETIME), + TimesFound = tostring(Detections.TIMES_FOUND), + FirstFound = todatetime(Detections.FIRST_FOUND_DATETIME), + LastUpdate = todatetime(Detections.LAST_UPDATE_DATETIME), + LastProcessedDateTime = coalesce( + todatetime(Detections.LAST_PROCESSED_DATETIME), + todatetime(Detections.LAST_PROCESSED_DATE)), + Results_0 = tostring(coalesce(Detections.RESULTS['#cdata-section'], Detections.RESULTS)), + Result_column_count = "1", + Type = tostring(Detections.TYPE), + _ResourceId = column_ifexists('_ResourceId', '') + | extend _IngestionTime = todatetime(TimeGenerated) + | summarize arg_max(_IngestionTime, *) by HostId, QID, Status, SSL, Ignored, Severity, Disabled, LastFixed, LastFound, TimesFound, FirstFound, Result_column_count, Results_0, Type + | project-away _IngestionTime + }; + union isfuzzy=true + (QualysHostDetection_view), + (QualysHostDetectionV2_view), + (QualysHostDetectionV3_view) + | project + TenantId, + SourceSystem, + MG, + ManagementGroupName, + TimeGenerated, + Computer, + RawData, + Id, + HostId, + HostTags, + IPAddress, + TrackingMethod, + OperatingSystem, + DnsName, + NetBios, + QGHostId, + LastScanDateTime, + LastVMScannedDateTime, + LastVMAuthScannedDateTime, + QID, + SSL, + Type, + Status, + Ignored, + Severity, + Disabled, + LastFixed, + LastFound, + TimesFound, + FirstFound, + LastUpdate, + LastProcessedDateTime, + Result_column_count, + Results_0, + _ResourceId + | extend + HostId = tostring(HostId), + QID = tostring(QID), + Status = tostring(Status), + LastProcessedDateTime = todatetime(LastProcessedDateTime), + LastUpdate = todatetime(LastUpdate), + LastFound = todatetime(LastFound) + | extend DetectionTime = coalesce(LastProcessedDateTime, LastUpdate, LastFound, todatetime(TimeGenerated)) + }; + let threshold = 10; + __xdr_inline_QualysHostDetection + | extend Status = tostring(Status), Vulnerability = tostring(QID), Severity = tostring(Severity) + | where Status =~ "New" and Severity == "5" + | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), dcount(NetBios), RepresentativeHost = take_anyif(NetBios, isnotempty(NetBios)) by tostring(QID) + | where dcount_NetBios >= threshold + | extend timestamp = StartTime + schedule: + frequency: PT1H + detectionAction: + alertTemplate: + title: New High Severity Vulnerability Detected Across Multiple Hosts + description: '''This creates an incident when a new high severity vulnerability is detected across multilple hosts''' + severity: medium + entityMappings: + hosts: + - id: host1 + netBiosNameColumn: RepresentativeHost + tactics: + - tactic: InitialAccess + techniques: + - technique: T1190 diff --git a/Solutions/QualysVM/data/Solution_QualysVM.json b/Solutions/QualysVM/data/Solution_QualysVM.json index 1ecaba34f9e..3c77c588764 100644 --- a/Solutions/QualysVM/data/Solution_QualysVM.json +++ b/Solutions/QualysVM/data/Solution_QualysVM.json @@ -25,7 +25,12 @@ ], "Metadata": "SolutionMetadata.json", "BasePath": "C:\\GitHub\\Azure-Sentinel\\Solutions\\QualysVM", - "Version": "3.1.1", "TemplateSpec": true, - "Is1PConnector": false + "Is1PConnector": false, + "XDR Workbooks": [], + "XDR Detections": [ + "XDR Detections/HighNumberofVulnDetectedV2.yaml", + "XDR Detections/NewHighSeverityVulnDetectedAcrossMulitpleHostsV2.yaml" + ], + "Version": "3.2.2" }