Query Details
let query_period = 120d;
SecurityNestedRecommendation
| where TimeGenerated > ago(query_period) and ParentRecommendationId in ("c609cf0f-71ab-41e9-a3c6-9a1f7fe1b8d5", "682b2595-d045-4cff-b5aa-46624eb2dd8f")
| summarize hint.strategy=shuffle
StartSubAssessmentTimeGeneration = min(SubAssessmentTimeGeneration),
EndSubAssessmentTimeGeneration = arg_max(SubAssessmentTimeGeneration, *)
by Id
| extend
ClusterLocation = case(
ParentRecommendationId == "c609cf0f-71ab-41e9-a3c6-9a1f7fe1b8d5", "Azure",
ParentRecommendationId == "682b2595-d045-4cff-b5aa-46624eb2dd8f", "AWS",
""),
Scanner = "MDVM - Microsoft Defender Vulnerability Management"
// "c609cf0f-71ab-41e9-a3c6-9a1f7fe1b8d5" "MDVM" "AzureRuntimeImageVulnerability"
// "41503391-efa5-47ee-9282-4eff6131462c" "Qualys" "GeneralVulnerability"
// "682b2595-d045-4cff-b5aa-46624eb2dd8f" "MDVM" "AwsRuntimeImageVulnerability"|"AzureRuntimeImageVulnerability"
| extend UpperCamelCase = isnotempty(AdditionalData["AssessedResourceType"])
| extend Keys = iff(UpperCamelCase, dynamic(null), set_union(extract_all(@'(\"[^\"]+\"\:)', tostring(AdditionalData)), dynamic(null)))
| extend Initials = iff(UpperCamelCase, dynamic(null), extract_all(@'(\".)[^\"]*\"\:', tostring(Keys)))
| extend AdditionalData = iff(UpperCamelCase,
AdditionalData,
todynamic(replace_strings(
tostring(AdditionalData),
Keys,
todynamic(replace_strings(
tostring(Keys),
Initials,
todynamic(toupper(Initials))
))
))
)
| join hint.remote=local kind=leftouter (
arg("").ResourceContainers
| where type == "microsoft.resources/subscriptions"
| project RecommendationSubscriptionId = subscriptionId, RecommendationSubscriptionName = name
) on RecommendationSubscriptionId
| project
//TimeGenerated,
StartSubAssessmentTimeGeneration,
EndSubAssessmentTimeGeneration,
Assessment_Age = bin(EndSubAssessmentTimeGeneration - StartSubAssessmentTimeGeneration, 1d)/1d,
//IsSnapshot,
//ResourceProviderType,
Scanner,
//ParentRecommendationId,
//NestedRecommendationId,
RecommendationState,
Cause,
RecommendationSeverity,
RecommendationSubscriptionId = coalesce(RecommendationSubscriptionName, RecommendationSubscriptionId),
ResourceGroup,
AssessedResourceId,
RecommendationName,
VulnerabilityId,
//Category, // Qualys
Description,
//Impact, // Qualys
RemediationDescription,
//ResourceDetails,
//Id,
//AdditionalData,
//AdditionalDataKeys = array_sort_asc(bag_keys(AdditionalData)),
CveId = AdditionalData["VulnerabilityDetails"]["CveId"],
Vulnerability_Severity = AdditionalData["VulnerabilityDetails"]["Severity"],
Vulnerability_PublishedDate = todatetime(AdditionalData["VulnerabilityDetails"]["PublishedDate"]),
Vulnerability_Age = bin(now() - todatetime(AdditionalData["VulnerabilityDetails"]["PublishedDate"]), 1d)/1d,
Vulnerability_Cvss = case(
isnotempty(AdditionalData["VulnerabilityDetails"]["Cvss"]["3.0"]), strcat(AdditionalData["VulnerabilityDetails"]["Cvss"]["3.0"]["Base"], " - ", AdditionalData["VulnerabilityDetails"]["Cvss"]["3.0"]["CvssVectorString"]),
isnotempty(AdditionalData["VulnerabilityDetails"]["Cvss"]["2.0"]), strcat(AdditionalData["VulnerabilityDetails"]["Cvss"]["2.0"]["Base"], " - ", AdditionalData["VulnerabilityDetails"]["Cvss"]["2.0"]["CvssVectorString"]),
""),
Exploit_IsInExploitKit = tobool(AdditionalData["VulnerabilityDetails"]["ExploitabilityAssessment"]["IsInExploitKit"]),
Exploit_IsPubliclyDisclosed = tobool(AdditionalData["VulnerabilityDetails"]["ExploitabilityAssessment"]["IsPubliclyDisclosed"]),
Exploit_IsVerified = tobool(AdditionalData["VulnerabilityDetails"]["ExploitabilityAssessment"]["IsVerified"]),
SoftwareName = AdditionalData["SoftwareDetails"]["PackageName"],
SoftwareCurrentVersion = AdditionalData["SoftwareDetails"]["Version"],
SoftwareFixedVersion = AdditionalData["SoftwareDetails"]["FixedVersion"],
//AssessedResourceType = AdditionalData["AssessedResourceType"],
//ArtifactType = AdditionalData["ArtifactDetails"]["ArtifactType"], // == "ContainerImage"
RegistryHost = AdditionalData["ArtifactDetails"]["RegistryHost"],
RepositoryName = AdditionalData["ArtifactDetails"]["RepositoryName"],
ImageDigest = AdditionalData["ArtifactDetails"]["Digest"],
ImageTags = array_sort_asc(AdditionalData["ArtifactDetails"]["Tags"]),
ClusterLocation,
ClusterResourceId = AdditionalData["ClusterDetails"]["ClusterResourceId"],
KubernetesContext = AdditionalData["KubernetesContext"]
The query retrieves data from the SecurityNestedRecommendation table and filters it based on the time period and specific recommendation IDs. It then summarizes the data by grouping it by the "Id" field and calculates the minimum and maximum values of the "SubAssessmentTimeGeneration" field.
The query also extends the data by adding additional columns such as "ClusterLocation" and "Scanner" based on the values of the "ParentRecommendationId" field. It further extends the data by performing string manipulations and joins with another table to retrieve additional information.
Finally, the query selects specific columns from the data and renames them, and performs calculations on some columns such as calculating the age of an assessment and a vulnerability.

Jose Sebastián Canós
Released: February 2, 2024
Tables
Keywords
Operators