module.exports = { friendlyName: 'View dashboard', description: 'Display "Dashboard" page.', exits: { success: { viewTemplatePath: 'pages/dashboard' } }, fn: async function () { // console.time(`Dashboard page view action`); let dataForGraphs = {}; // Get the most recently created Vulnerability and VulnerabilityInstall record. let latestVulnerabilityInstallRecord = await VulnerabilityInstall.find().sort('updatedAt DESC').limit(1); let latestVulnerabilityRecord = await Vulnerability.find().sort('updatedAt DESC').limit(1); // Get the most recent createdAt timestamp from these records, we'll use this timestamp to set the "Last updated:" on the dashboard page. let latestTimestamp = Math.max(latestVulnerabilityRecord[0].updatedAt, latestVulnerabilityInstallRecord[0].updatedAt); let dashboardDataLastUpdatedAt = latestTimestamp; dataForGraphs.lastUpdatedAt = dashboardDataLastUpdatedAt; // // ╦ ╦╦ ╦╦ ╔╗╔╔═╗╦═╗╔═╗╔╗ ╦╦ ╦╔╦╗╦ ╦ ╔═╗╔═╗╦ ╦╔╗╔╔╦╗╔═╗ // ╚╗╔╝║ ║║ ║║║║╣ ╠╦╝╠═╣╠╩╗║║ ║ ║ ╚╦╝ ║ ║ ║║ ║║║║ ║ ╚═╗ // ╚╝ ╚═╝╩═╝╝╚╝╚═╝╩╚═╩ ╩╚═╝╩╩═╝╩ ╩ ╩ ╚═╝╚═╝╚═╝╝╚╝ ╩ ╚═╝ // Note: the information gathered in this section is used in the unique vulnerability count tiles, as well as the "What percentage are critical?" chart. let nativeQueryToFindAllVulnerabilityIdsWithSeverity; if(sails.config.datastores.default.adapter === 'sails-postgresql') { nativeQueryToFindAllVulnerabilityIdsWithSeverity = ` SELECT vulnerability.id, vulnerability.severity FROM vulnerability WHERE EXISTS ( SELECT 1 FROM vulnerabilityinstall WHERE vulnerabilityinstall.vulnerability = vulnerability.id AND (vulnerabilityinstall."uninstalledAt" IS NULL OR vulnerabilityinstall."uninstalledAt" = 0) );`; } else { nativeQueryToFindAllVulnerabilityIdsWithSeverity = ` SELECT vulnerability.id, vulnerability.severity FROM vulnerability WHERE EXISTS ( SELECT 1 FROM vulnerabilityinstall WHERE vulnerabilityinstall.vulnerability = vulnerability.id AND (vulnerabilityinstall.uninstalledAt IS NULL OR vulnerabilityinstall.uninstalledAt = 0) );`; } // console.time('all vulns native query'); let vulnerabilityNativeQueryResult = await sails.sendNativeQuery(nativeQueryToFindAllVulnerabilityIdsWithSeverity); // console.timeEnd('all vulns native query'); let vulnerabilityIdsWithSeverity = vulnerabilityNativeQueryResult.rows; // Sort the vulnerabilities by severity, and add the counts to the totalUniqueCounts dictionary. let criticalSeverity = vulnerabilityIdsWithSeverity.filter((vuln)=>{ return vuln.severity >= 9; }); let highSeverity = vulnerabilityIdsWithSeverity.filter((vuln)=>{ return vuln.severity <= 8.9 && vuln.severity >= 7.0; }); let mediumSeverity = vulnerabilityIdsWithSeverity.filter((vuln)=>{ return vuln.severity <= 6.9 && vuln.severity >= 4.0; }); let lowSeverity = vulnerabilityIdsWithSeverity.filter((vuln)=>{ return vuln.severity >= 0 && vuln.severity <= 3.9; }); let totalUniqueCounts = { low: lowSeverity.length, medium: mediumSeverity.length, high: highSeverity.length, critical: criticalSeverity.length, }; dataForGraphs.totalUniqueCounts = totalUniqueCounts; // Now build native SQL Queries using the IDs of vulnerabilities to find vulnerabilityInstall records for each severity. let lowVulnerabilityInstallNativeQuery; let mediumVulnerabilityInstallNativeQuery; let highVulnerabilityInstallNativeQuery; let criticalVulnerabilityInstallNativeQuery; if(sails.config.datastores.default.adapter === 'sails-postgresql') { // If this app is configured to use a Postgres datastore, we'll need to put double quotes around the uninstalledAt column name. lowVulnerabilityInstallNativeQuery = ` SELECT * FROM vulnerabilityinstall WHERE "uninstalledAt" = 0 AND vulnerability IN (${_.pluck(lowSeverity,'id').join(',')});`; mediumVulnerabilityInstallNativeQuery = ` SELECT * FROM vulnerabilityinstall WHERE "uninstalledAt" = 0 AND vulnerability IN (${_.pluck(mediumSeverity,'id').join(',')})`; highVulnerabilityInstallNativeQuery = ` SELECT * FROM vulnerabilityinstall WHERE "uninstalledAt" = 0 AND vulnerability IN (${_.pluck(highSeverity,'id').join(',')})`; criticalVulnerabilityInstallNativeQuery = ` SELECT * FROM vulnerabilityinstall WHERE "uninstalledAt" = 0 AND vulnerability IN (${_.pluck(criticalSeverity,'id').join(',')})`; } else if(sails.config.datastores.default.adapter === 'sails-mysql') { lowVulnerabilityInstallNativeQuery = ` SELECT * FROM vulnerabilityinstall WHERE uninstalledAt = 0 AND vulnerability IN (${_.pluck(lowSeverity,'id').join(',')})`; mediumVulnerabilityInstallNativeQuery = ` SELECT * FROM vulnerabilityinstall WHERE uninstalledAt = 0 AND vulnerability IN (${_.pluck(mediumSeverity,'id').join(',')})`; highVulnerabilityInstallNativeQuery = ` SELECT * FROM vulnerabilityinstall WHERE uninstalledAt = 0 AND vulnerability IN (${_.pluck(highSeverity,'id').join(',')})`; criticalVulnerabilityInstallNativeQuery = ` SELECT * FROM vulnerabilityinstall WHERE uninstalledAt = 0 AND vulnerability IN (${_.pluck(criticalSeverity,'id').join(',')})`; } // console.time(`native queries to get total counts`); // Now send some queries. let criticalVulnInstallsResult = await sails.sendNativeQuery(criticalVulnerabilityInstallNativeQuery); let totalInstallsWithCricitalVulns = criticalVulnInstallsResult.rows; let highVulnInstallsResult = await sails.sendNativeQuery(highVulnerabilityInstallNativeQuery); let totalInstallsWithHighVulns = highVulnInstallsResult.rows; let mediumVulnInstallsResult = await sails.sendNativeQuery(mediumVulnerabilityInstallNativeQuery); let totalInstallsWithMediumVulns = mediumVulnInstallsResult.rows; let lowVulnInstallsResult = await sails.sendNativeQuery(lowVulnerabilityInstallNativeQuery); let totalInstallsWithLowVulns = lowVulnInstallsResult.rows; // console.timeEnd(`native queries to get total counts`); let totalNumberOfVulns = { low: totalInstallsWithLowVulns.length, medium: totalInstallsWithMediumVulns.length, high: totalInstallsWithHighVulns.length, critical: totalInstallsWithCricitalVulns.length }; dataForGraphs.totalNumberOfVulns = totalNumberOfVulns; // ╔╦╗╦ ╦╔═╗ ╦═╗╦╔═╗╦╔═╦╔═╗╔═╗╔╦╗ ╦ ╦╔═╗╔═╗╔╦╗╔═╗ ╦═╗╦╔═╗╦ ╦╔╦╗ ╔╗╔╔═╗╦ ╦ // ║ ╠═╣║╣ ╠╦╝║╚═╗╠╩╗║║╣ ╚═╗ ║ ╠═╣║ ║╚═╗ ║ ╚═╗ ╠╦╝║║ ╦╠═╣ ║ ║║║║ ║║║║ // ╩ ╩ ╩╚═╝ ╩╚═╩╚═╝╩ ╩╩╚═╝╚═╝ ╩ ╩ ╩╚═╝╚═╝ ╩ ╚═╝ ╩╚═╩╚═╝╩ ╩ ╩ ╝╚╝╚═╝╚╩╝ let nativeQueryToFindTopTenMostVulnerableHosts; if(sails.config.datastores.default.adapter === 'sails-postgresql') { nativeQueryToFindTopTenMostVulnerableHosts = ` WITH "installAggregates" AS ( SELECT vulnerabilityinstall.vulnerability AS "vulnerabilityId", COUNT(DISTINCT CASE WHEN vulnerabilityinstall."uninstalledAt" IS NULL OR vulnerabilityinstall."uninstalledAt" = 0 THEN vulnerabilityinstall.host END ) AS "currentAffectedHostCount", COUNT(DISTINCT vulnerabilityinstall.host) AS "totalAffectedHostCount" FROM vulnerabilityinstall GROUP BY vulnerabilityinstall.vulnerability ), "riskCalculation" AS ( SELECT vulnerability.id AS "vulnerabilityId", "installAggregates"."currentAffectedHostCount", "installAggregates"."totalAffectedHostCount", ( vulnerability.severity * CASE WHEN vulnerability."hasKnownExploit" THEN 2.0 WHEN vulnerability."probabilityOfExploit" > 0.5 THEN 1.5 ELSE 1.0 END ) AS "threatScore", LN(1 + COALESCE("installAggregates"."currentAffectedHostCount", 0)) AS "impactScore" FROM vulnerability LEFT JOIN "installAggregates" ON "installAggregates"."vulnerabilityId" = vulnerability.id ), "normalizedRisk" AS ( SELECT "vulnerabilityId", ("threatScore" * "impactScore") AS "rawRiskScore", MAX("threatScore" * "impactScore") OVER () AS "maxRawRisk" FROM "riskCalculation" ), "vulnerabilityPps" AS ( SELECT "vulnerabilityId", CASE WHEN "maxRawRisk" > 0 THEN ("rawRiskScore" / "maxRawRisk" * 9.9) + 0.1 ELSE 0.1 END AS "vulnerabilityPps" FROM "normalizedRisk" ), "hostVulnerabilitySet" AS ( SELECT DISTINCT vulnerabilityinstall.host AS "hostId", vulnerability.id AS "vulnerabilityId", vulnerability."hasKnownExploit" AS "hasKnownExploit", "vulnerabilityPps"."vulnerabilityPps" AS "vulnerabilityPps" FROM vulnerabilityinstall JOIN vulnerability ON vulnerability.id = vulnerabilityinstall.vulnerability JOIN "vulnerabilityPps" ON "vulnerabilityPps"."vulnerabilityId" = vulnerability.id WHERE vulnerabilityinstall."uninstalledAt" IS NULL OR vulnerabilityinstall."uninstalledAt" = 0 ), "hostPps" AS ( SELECT host."displayName", host."fleetApid", COUNT(DISTINCT "hostVulnerabilitySet"."vulnerabilityId") AS "vulnerabilityCount", COUNT(DISTINCT "hostVulnerabilitySet"."vulnerabilityId") FILTER (WHERE "hostVulnerabilitySet"."hasKnownExploit") AS "kevCount", MAX("hostVulnerabilitySet"."vulnerabilityPps") AS "maxVulnerabilityPps", ( MAX("hostVulnerabilitySet"."vulnerabilityPps") * (1 + LN(1 + COUNT(DISTINCT "hostVulnerabilitySet"."vulnerabilityId")) * 0.15) )::numeric(12,4) AS "hostPpsNumeric" FROM "hostVulnerabilitySet" JOIN host ON host.id = "hostVulnerabilitySet"."hostId" GROUP BY host."displayName", host."fleetApid" ), "hostPpsNormalized" AS ( SELECT "hostPps"."displayName", "hostPps"."fleetApid", "hostPps"."vulnerabilityCount", "hostPps"."kevCount", "hostPps"."maxVulnerabilityPps", "hostPps"."hostPpsNumeric", MAX("hostPps"."hostPpsNumeric") OVER () AS "maxHostPps" FROM "hostPps" ) SELECT "hostPpsNormalized"."displayName", "hostPpsNormalized"."fleetApid", "hostPpsNormalized"."vulnerabilityCount", "hostPpsNormalized"."kevCount", ROUND("hostPpsNormalized"."maxVulnerabilityPps"::numeric, 2) AS "worstVulnerabilityPps", CASE WHEN "hostPpsNormalized"."maxHostPps" > 0 THEN ROUND( ( "hostPpsNormalized"."hostPpsNumeric" / "hostPpsNormalized"."maxHostPps" ) * 9 + 1, 2) ELSE 1.00 END AS "hostPps" FROM "hostPpsNormalized" ORDER BY "hostPps" DESC, "hostPpsNormalized"."displayName" ASC LIMIT 10;`; } else { nativeQueryToFindTopTenMostVulnerableHosts = ` WITH installAggregates AS ( SELECT vi.vulnerability AS vulnerabilityId, COUNT(DISTINCT IF(vi.uninstalledAt IS NULL OR vi.uninstalledAt = 0, vi.host, NULL)) AS currentAffectedHostCount, COUNT(DISTINCT vi.host) AS totalAffectedHostCount FROM vulnerabilityinstall vi GROUP BY vi.vulnerability ), riskCalculation AS ( SELECT v.id AS vulnerabilityId, ia.currentAffectedHostCount, ia.totalAffectedHostCount, ( v.severity * CASE WHEN v.hasKnownExploit THEN 2.0 WHEN v.probabilityOfExploit > 0.5 THEN 1.5 ELSE 1.0 END ) AS threatScore, LN(1 + COALESCE(ia.currentAffectedHostCount, 0)) AS impactScore FROM vulnerability v LEFT JOIN installAggregates ia ON ia.vulnerabilityId = v.id ), normalizedRisk AS ( SELECT vulnerabilityId, (threatScore * impactScore) AS rawRiskScore, MAX(threatScore * impactScore) OVER () AS maxRawRisk FROM riskCalculation ), vulnerabilityPps AS ( SELECT vulnerabilityId, CASE WHEN maxRawRisk > 0 THEN (rawRiskScore / maxRawRisk * 9.9) + 0.1 ELSE 0.1 END AS vulnerabilityPps FROM normalizedRisk ), hostVulnerabilitySet AS ( SELECT DISTINCT vi.host AS hostId, v.id AS vulnerabilityId, v.hasKnownExploit AS hasKnownExploit, vp.vulnerabilityPps AS vulnerabilityPps FROM vulnerabilityinstall vi JOIN vulnerability v ON v.id = vi.vulnerability JOIN vulnerabilityPps vp ON vp.vulnerabilityId = v.id WHERE vi.uninstalledAt IS NULL OR vi.uninstalledAt = 0 ), hostPps AS ( SELECT h.displayName, h.fleetApid, COUNT(DISTINCT hvs.vulnerabilityId) AS vulnerabilityCount, COUNT(DISTINCT IF(hvs.hasKnownExploit, hvs.vulnerabilityId, NULL)) AS kevCount, MAX(hvs.vulnerabilityPps) AS maxVulnerabilityPps, CAST( MAX(hvs.vulnerabilityPps) * (1 + LN(1 + COUNT(DISTINCT hvs.vulnerabilityId)) * 0.15) AS DECIMAL(12,4) ) AS hostPpsNumeric FROM hostVulnerabilitySet hvs JOIN host h ON h.id = hvs.hostId GROUP BY h.displayName, h.fleetApid ), hostPpsNormalized AS ( SELECT hp.displayName, hp.fleetApid, hp.vulnerabilityCount, hp.kevCount, hp.maxVulnerabilityPps, hp.hostPpsNumeric, MAX(hp.hostPpsNumeric) OVER () AS maxHostPps FROM hostPps hp ) SELECT displayName, fleetApid, vulnerabilityCount, kevCount, ROUND(maxVulnerabilityPps, 2) AS worstVulnerabilityPps, CASE WHEN maxHostPps > 0 THEN ROUND(((hostPpsNumeric / maxHostPps) * 9) + 1, 2) ELSE 1.00 END AS hostPps FROM hostPpsNormalized ORDER BY hostPps DESC, displayName ASC LIMIT 10;`; } // console.time(`native query to find top 10 most vulnerable hosts`); let topTenMostVulnerableHosts = await sails.sendNativeQuery(nativeQueryToFindTopTenMostVulnerableHosts); // console.timeEnd(`native query to find top 10 most vulnerable hosts`); // console.log(topTenMostVulnerableHosts.rows); let mostVulnerableHosts = topTenMostVulnerableHosts.rows.map((host)=>{ return { displayName: host.displayName, fleetUrl: sails.config.custom.fleetBaseUrl + `/hosts/${host.fleetApid}`, numberOfCves: host.vulnerabilityCount, pps: host.hostPps, }; }); dataForGraphs.mostVulnerableHosts = mostVulnerableHosts; // ╔╦╗╦ ╦╔═╗ ╦ ╦╔═╗╦ ╦╔═╗╦ ╔═╗╦ ╦╔═╗╔═╗╔═╗╔═╗╔╦╗╔═╗ ╦╔╗╔ ╔═╗╔═╗╔═╗╔╦╗╦ ╦╔═╗╦═╗╔═╗ // ║ ╠═╣║╣ ║ ║╚═╗║ ║╠═╣║ ╚═╗║ ║╚═╗╠═╝║╣ ║ ║ ╚═╗ ║║║║ ╚═╗║ ║╠╣ ║ ║║║╠═╣╠╦╝║╣ // ╩ ╩ ╩╚═╝ ╚═╝╚═╝╚═╝╩ ╩╩═╝ ╚═╝╚═╝╚═╝╩ ╚═╝╚═╝ ╩ ╚═╝ ╩╝╚╝ ╚═╝╚═╝╚ ╩ ╚╩╝╩ ╩╩╚═╚═╝ let nativeQueryToFindTopTenMostVulnerableSoftwareItems; if(sails.config.datastores.default.adapter === 'sails-postgresql') { nativeQueryToFindTopTenMostVulnerableSoftwareItems = ` SELECT vi."softwareName", vi."versionName", MIN(vi."fleetApid") AS "fleetApid", COUNT(DISTINCT v.id) AS "numberOfVulns" FROM vulnerabilityinstall vi JOIN vulnerability v ON vi.vulnerability = v.id WHERE COALESCE(vi."uninstalledAt", 0) = 0 GROUP BY vi."softwareName", vi."versionName" ORDER BY "numberOfVulns" DESC LIMIT 10;`; } else { nativeQueryToFindTopTenMostVulnerableSoftwareItems = ` SELECT vi.softwareName, vi.versionName, MIN(vi.fleetApid) AS fleetApid, COUNT(DISTINCT v.id) AS numberOfVulns FROM vulnerabilityinstall vi JOIN vulnerability v ON vi.vulnerability = v.id WHERE vi.uninstalledAt IS NULL OR vi.uninstalledAt = 0 GROUP BY vi.softwareName, vi.versionName ORDER BY numberOfVulns DESC LIMIT 10;`; } // console.time(`top ten most vulenerable software query`); let topVulnerableSoftwareResult = await sails.sendNativeQuery(nativeQueryToFindTopTenMostVulnerableSoftwareItems); // console.timeEnd(`top ten most vulenerable software query`); let topTenMostVulnerableSoftwareItems = topVulnerableSoftwareResult.rows.map((software)=>{ return { softwareNameAndVersion: `${software.softwareName} - ${software.versionName}`, fleetUrl: sails.config.custom.fleetBaseUrl + `/software/versions/${software.fleetApid}`, numberOfVulns: software.numberOfVulns, }; }); dataForGraphs.mostVulnerableSoftware = topTenMostVulnerableSoftwareItems; // ╔═╗╦═╗╦╔╦╗╦╔═╗╔═╗╦ ╦ ╦ ╦ ╦═╗╔═╗╦═╗╔═╗ ╔═╗╔═╗╔═╗╔╦╗╦ ╦╔═╗╦═╗╔═╗ // ║ ╠╦╝║ ║ ║║ ╠═╣║ ║ ╚╦╝ ╠╦╝╠═╣╠╦╝║╣ ╚═╗║ ║╠╣ ║ ║║║╠═╣╠╦╝║╣ // ╚═╝╩╚═╩ ╩ ╩╚═╝╩ ╩╩═╝╩═╝╩ ╩╚═╩ ╩╩╚═╚═╝ ╚═╝╚═╝╚ ╩ ╚╩╝╩ ╩╩╚═╚═╝ // Software that is installed on <5 hosts with critical vulnerabilities. let queryToFindTopUniqueCriticalSoftwareWithMostVulnerabilities; if(sails.config.datastores.default.adapter === 'sails-postgresql') { queryToFindTopUniqueCriticalSoftwareWithMostVulnerabilities = ` SELECT vi."softwareName", STRING_AGG(DISTINCT h."displayName", ', ' ORDER BY h."displayName") AS "hostNames", STRING_AGG(DISTINCT v."cveId", ', ' ORDER BY v."cveId") AS cves, STRING_AGG(DISTINCT v."cveDescription", '; ' ORDER BY v."cveDescription") AS descriptions, COUNT(DISTINCT vi.host) AS host_install_count FROM vulnerabilityinstall vi JOIN vulnerability v ON vi.vulnerability = v.id JOIN host h ON vi.host = h.id WHERE (vi."uninstalledAt" IS NULL OR vi."uninstalledAt" = 0) AND v.severity >= 9.0 AND vi."softwareName" !~* '^(Python|perl|lib)' GROUP BY vi."softwareName" HAVING COUNT(DISTINCT vi.host) < 3 ORDER BY host_install_count ASC LIMIT 10;`; // queryToFindTopUniqueCriticalSoftwareWithMostVulnerabilities = ` // WITH "activeInstalls" AS ( // SELECT // vulnerabilityinstall.host, // vulnerabilityinstall."softwareName", // vulnerabilityinstall."versionName", // vulnerabilityinstall."fleetApid", // vulnerabilityinstall."softwareName" AS "softwareNameAndVersion" // FROM vulnerabilityinstall // WHERE vulnerabilityinstall."uninstalledAt" IS NULL OR vulnerabilityinstall."uninstalledAt" = 0 // ), // "softwareHostCounts" AS ( // SELECT // "activeInstalls"."softwareNameAndVersion", // COUNT(DISTINCT "activeInstalls".host) AS "hostInstallCount", // STRING_AGG(DISTINCT host."displayName", ', ' ORDER BY host."displayName") AS "hostNames", // STRING_AGG( // DISTINCT "activeInstalls"."fleetApid"::text, // ', ' ORDER BY "activeInstalls"."fleetApid"::text // ) AS "fleetApid" // FROM "activeInstalls" // JOIN host ON host.id = "activeInstalls".host // GROUP BY "activeInstalls"."softwareNameAndVersion" // ), // "criticalVulnerabilityAgg" AS ( // SELECT // "activeInstalls"."softwareNameAndVersion", // COUNT(DISTINCT vulnerability.id) AS "vulnerabilityCount", // STRING_AGG(DISTINCT vulnerability."cveId", ', ' ORDER BY vulnerability."cveId") AS "cveIds" // FROM "activeInstalls" // JOIN vulnerabilityinstall ON // vulnerabilityinstall.host = "activeInstalls".host // AND vulnerabilityinstall."softwareName" = "activeInstalls"."softwareName" // AND COALESCE(vulnerabilityinstall."versionName",'') = COALESCE("activeInstalls"."versionName",'') // AND (vulnerabilityinstall."uninstalledAt" IS NULL OR vulnerabilityinstall."uninstalledAt" = 0) // JOIN vulnerability ON vulnerability.id = vulnerabilityinstall.vulnerability // WHERE vulnerability.severity >= 9.0 // GROUP BY "activeInstalls"."softwareNameAndVersion" // ) // SELECT // "softwareHostCounts"."softwareNameAndVersion", // "softwareHostCounts"."hostInstallCount", // "criticalVulnerabilityAgg"."vulnerabilityCount", // "criticalVulnerabilityAgg"."cveIds", // "softwareHostCounts"."hostNames", // "softwareHostCounts"."fleetApid" // FROM "softwareHostCounts" // JOIN "criticalVulnerabilityAgg" // ON "criticalVulnerabilityAgg"."softwareNameAndVersion" = "softwareHostCounts"."softwareNameAndVersion" // WHERE "softwareHostCounts"."hostInstallCount" < 5 // ORDER BY // "criticalVulnerabilityAgg"."vulnerabilityCount" DESC, // "softwareHostCounts"."hostInstallCount" ASC, // "softwareHostCounts"."softwareNameAndVersion" ASC // LIMIT 10;`; } else { queryToFindTopUniqueCriticalSoftwareWithMostVulnerabilities = ` SELECT vi.softwareName, GROUP_CONCAT(DISTINCT h.displayName SEPARATOR ', ') AS hostNames, GROUP_CONCAT(DISTINCT v.cveId SEPARATOR ', ') AS cves, GROUP_CONCAT(DISTINCT v.cveDescription SEPARATOR '; ') AS descriptions, COUNT(DISTINCT vi.host) AS host_install_count FROM vulnerabilityinstall vi JOIN vulnerability v ON vi.vulnerability = v.id JOIN host h ON vi.host = h.id WHERE -- Consider only currently installed software (vi.uninstalledAt IS NULL OR vi.uninstalledAt = 0) -- Filter for software associated with 'Critical' vulnerabilities (CVSS 8.0-10.0) AND v.severity >= 8.0 -- Exclude software names starting with 'Python' or 'pert' AND vi.softwareName NOT REGEXP '^(Python|perl|lib)' GROUP BY vi.softwareName HAVING host_install_count < 3 ORDER BY -- Order by the number of hosts the software is installed on, ascending, -- to show the rarest software first. host_install_count ASC LIMIT 10`; // queryToFindTopUniqueCriticalSoftwareWithMostVulnerabilities = ` // WITH activeInstalls AS ( // SELECT // vulnerabilityinstall.host, // vulnerabilityinstall.softwareName, // vulnerabilityinstall.versionName, // vulnerabilityinstall.fleetApid, // CONCAT(vulnerabilityinstall.softwareName AS softwareNameAndVersion // FROM vulnerabilityinstall // WHERE vulnerabilityinstall.uninstalledAt IS NULL OR vulnerabilityinstall.uninstalledAt = 0 // ), // softwareHostCounts AS ( // SELECT // activeInstalls.softwareNameAndVersion, // COUNT(DISTINCT activeInstalls.host) AS hostInstallCount, // GROUP_CONCAT(DISTINCT host.displayName ORDER BY host.displayName SEPARATOR ', ') AS hostNames, // GROUP_CONCAT(DISTINCT CAST(activeInstalls.fleetApid AS CHAR) ORDER BY CAST(activeInstalls.fleetApid AS CHAR) SEPARATOR ', ') AS fleetApid // FROM activeInstalls // JOIN host ON host.id = activeInstalls.host // GROUP BY activeInstalls.softwareNameAndVersion // ), // criticalVulnerabilityAgg AS ( // SELECT // activeInstalls.softwareNameAndVersion, // COUNT(DISTINCT vulnerability.id) AS vulnerabilityCount, // GROUP_CONCAT(DISTINCT vulnerability.cveId ORDER BY vulnerability.cveId SEPARATOR ', ') AS cveIds // FROM activeInstalls // JOIN vulnerabilityinstall ON // vulnerabilityinstall.host = activeInstalls.host // AND vulnerabilityinstall.softwareName = activeInstalls.softwareName // AND COALESCE(vulnerabilityinstall.versionName,'') = COALESCE(activeInstalls.versionName,'') // AND (vulnerabilityinstall.uninstalledAt IS NULL OR vulnerabilityinstall.uninstalledAt = 0) // JOIN vulnerability ON vulnerability.id = vulnerabilityinstall.vulnerability // WHERE vulnerability.severity >= 9.0 // GROUP BY activeInstalls.softwareNameAndVersion // ) // SELECT // softwareHostCounts.softwareNameAndVersion, // softwareHostCounts.hostInstallCount, // criticalVulnerabilityAgg.vulnerabilityCount, // criticalVulnerabilityAgg.cveIds, // softwareHostCounts.hostNames, // softwareHostCounts.fleetApid // FROM softwareHostCounts // JOIN criticalVulnerabilityAgg // ON criticalVulnerabilityAgg.softwareNameAndVersion = softwareHostCounts.softwareNameAndVersion // WHERE softwareHostCounts.hostInstallCount < 5 // ORDER BY // criticalVulnerabilityAgg.vulnerabilityCount DESC, // softwareHostCounts.hostInstallCount ASC, // softwareHostCounts.softwareNameAndVersion ASC // LIMIT 10;`; } // console.time('critical unique software query'); let topUniqueCriticalVulnsResult = await sails.sendNativeQuery(queryToFindTopUniqueCriticalSoftwareWithMostVulnerabilities); // console.timeEnd('critical unique software query'); // console.log(topUniqueCriticalVulnsResult); let uniqueCriticalSoftware = topUniqueCriticalVulnsResult.rows.map((software)=>{ // Build an array of CVE objects, with each containing the CVE ID and a link to the Fleet page for the CVE. let cves = software.cves.split(', ').map((cveId)=>{ return { name: cveId, fleetUrl: `${sails.config.custom.fleetBaseUrl}/software/vulnerabilities/${cveId}` }; }); return { softwareNameAndVersion: software.softwareName, fleetUrl: `${sails.config.custom.fleetBaseUrl}/software/versions/${software.fleetApid}`, numberOfVulns: cves.length, cves, affectedHosts: software.hostNames.split(', '), }; }); dataForGraphs.uniqueCriticalSoftware = uniqueCriticalSoftware; // console.log(uniqueCriticalSoftware); // // ╦ ╦╔═╗╔═╗╔╦╗╔═╗ ╦ ╦╦╔╦╗╦ ╦ ╔╦╗╔═╗╔═╗╔╦╗ ╔═╗╦ ╦╔═╗╔═╗ // ╠═╣║ ║╚═╗ ║ ╚═╗ ║║║║ ║ ╠═╣ ║║║║ ║╚═╗ ║ ║ ╚╗╔╝║╣ ╚═╗ // ╩ ╩╚═╝╚═╝ ╩ ╚═╝ ╚╩╝╩ ╩ ╩ ╩ ╩ ╩╚═╝╚═╝ ╩ ╚═╝ ╚╝ ╚═╝╚═╝ let hostsWithMostVulnsQuery; if(sails.config.datastores.default.adapter === 'sails-postgresql') { hostsWithMostVulnsQuery = ` WITH "activeVulnerabilityInstalls" AS ( SELECT DISTINCT vulnerabilityinstall.host, vulnerabilityinstall.vulnerability FROM vulnerabilityinstall WHERE vulnerabilityinstall."uninstalledAt" IS NULL OR vulnerabilityinstall."uninstalledAt" = 0 ), "vulnerabilityCountsByHost" AS ( SELECT activeVulnerabilityInstalls.host, COUNT(DISTINCT activeVulnerabilityInstalls.vulnerability) AS "vulnerabilityCount", STRING_AGG(DISTINCT vulnerability."cveId", ', ' ORDER BY vulnerability."cveId") AS "cveIds" FROM "activeVulnerabilityInstalls" AS activeVulnerabilityInstalls JOIN vulnerability ON vulnerability.id = activeVulnerabilityInstalls.vulnerability GROUP BY activeVulnerabilityInstalls.host ) SELECT host."displayName" AS "displayName", host."fleetApid" AS "fleetApid", vulnerabilityCountsByHost."vulnerabilityCount", vulnerabilityCountsByHost."cveIds" FROM "vulnerabilityCountsByHost" AS vulnerabilityCountsByHost JOIN host ON host.id = vulnerabilityCountsByHost.host ORDER BY vulnerabilityCountsByHost."vulnerabilityCount" DESC, host."displayName" ASC LIMIT 10;`; } else { hostsWithMostVulnsQuery = ` WITH activeVulnerabilityInstalls AS ( SELECT DISTINCT vulnerabilityinstall.host, vulnerabilityinstall.vulnerability FROM vulnerabilityinstall WHERE vulnerabilityinstall.uninstalledAt IS NULL OR vulnerabilityinstall.uninstalledAt = 0 ), vulnerabilityCountsByHost AS ( SELECT activeVulnerabilityInstalls.host, COUNT(DISTINCT activeVulnerabilityInstalls.vulnerability) AS vulnerabilityCount, GROUP_CONCAT( DISTINCT vulnerability.cveId ORDER BY vulnerability.cveId SEPARATOR ', ' ) AS cveIds FROM activeVulnerabilityInstalls JOIN vulnerability ON vulnerability.id = activeVulnerabilityInstalls.vulnerability GROUP BY activeVulnerabilityInstalls.host ) SELECT host.displayName AS displayName, host.fleetApid AS fleetApid, vulnerabilityCountsByHost.vulnerabilityCount, vulnerabilityCountsByHost.cveIds FROM vulnerabilityCountsByHost JOIN host ON host.id = vulnerabilityCountsByHost.host ORDER BY vulnerabilityCountsByHost.vulnerabilityCount DESC, host.displayName ASC LIMIT 10;`; } // console.time('hosts with most vulnerabilities query'); let hostWithMostVulnsResult = await sails.sendNativeQuery(hostsWithMostVulnsQuery); // console.timeEnd('hosts with most vulnerabilities query'); let hostsWithMostVulns = hostWithMostVulnsResult.rows.map((host)=>{ let cves = host.cveIds.split(', ').map((cveId)=>{ return { name: cveId, fleetUrl: `${sails.config.custom.fleetBaseUrl}/software/vulnerabilities/${cveId}` }; }); return { displayName: host.displayName, fleetUrl: `${sails.config.custom.fleetBaseUrl}/hosts/${host.fleetApid}`, numberOfVulns: host.vulnerabilityCount, cves, }; }); dataForGraphs.hostsWithMostVulns = hostsWithMostVulns; // // ╔═╗╦ ╦╔═╗ ╔═╗╔═╗╔╦╗╦╦ ╦╦╔╦╗╦ ╦ ╔╦╗╦═╗╔═╗╔╗╔╔╦╗╔═╗ // ║ ╚╗╔╝║╣ ╠═╣║ ║ ║╚╗╔╝║ ║ ╚╦╝ ║ ╠╦╝║╣ ║║║ ║║╚═╗ // ╚═╝ ╚╝ ╚═╝ ╩ ╩╚═╝ ╩ ╩ ╚╝ ╩ ╩ ╩ ╩ ╩╚═╚═╝╝╚╝═╩╝╚═╝ let queryToFindRemediationVeloticy; if(sails.config.datastores.default.adapter === 'sails-postgresql') { queryToFindRemediationVeloticy = ` WITH "params" AS ( SELECT (3::bigint * 24 * 60 * 60 * 1000) AS "graceMs" ), "monthAnchors" AS ( SELECT date_trunc('month', (now() AT TIME ZONE 'UTC'))::timestamptz AS "thisMonthStartTs" ), "monthBoundaries" AS ( SELECT gs AS "monthStartTs", LEAD(gs) OVER (ORDER BY gs) AS "monthEndTs" FROM generate_series( (SELECT "thisMonthStartTs" - INTERVAL '4 months' FROM "monthAnchors"), (SELECT "thisMonthStartTs" FROM "monthAnchors"), INTERVAL '1 month' ) AS gs ), "months" AS ( SELECT (EXTRACT(EPOCH FROM "monthStartTs") * 1000)::bigint AS "monthStartMs", (EXTRACT(EPOCH FROM "monthEndTs") * 1000)::bigint AS "monthEndMs" FROM "monthBoundaries" WHERE "monthEndTs" IS NOT NULL ), "rowsWithLag" AS ( SELECT vulnerabilityinstall.host, vulnerabilityinstall."softwareName", vulnerabilityinstall."versionName", vulnerabilityinstall.vulnerability, vulnerabilityinstall."createdAt"::bigint AS "createdAtMs", vulnerabilityinstall."uninstalledAt"::bigint AS "uninstalledAtMs", LAG(vulnerabilityinstall."uninstalledAt"::bigint) OVER ( PARTITION BY vulnerabilityinstall.host, vulnerabilityinstall."softwareName", vulnerabilityinstall."versionName", vulnerabilityinstall.vulnerability ORDER BY vulnerabilityinstall."createdAt"::bigint ) AS "prevUninstalledMs" FROM vulnerabilityinstall ), "labeledEpisodes" AS ( SELECT "rowsWithLag".*, CASE WHEN "rowsWithLag"."prevUninstalledMs" IS NULL THEN 1 WHEN "rowsWithLag"."createdAtMs" > "rowsWithLag"."prevUninstalledMs" + (SELECT "graceMs" FROM "params") THEN 1 ELSE 0 END AS "isNewEpisode" FROM "rowsWithLag" ), "episodeIndex" AS ( SELECT host, "softwareName", "versionName", vulnerability, "createdAtMs", "uninstalledAtMs", SUM("isNewEpisode") OVER ( PARTITION BY host, "softwareName", "versionName", vulnerability ORDER BY "createdAtMs" ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS "episodeId" FROM "labeledEpisodes" ), "episodes" AS ( SELECT host, "softwareName", "versionName", vulnerability, MIN("createdAtMs") AS "episodeStartMs", NULLIF(MAX(COALESCE("uninstalledAtMs", 0)), 0) AS "episodeEndMs" FROM "episodeIndex" GROUP BY host, "softwareName", "versionName", vulnerability, "episodeId" ), "newCvesByMonth" AS ( SELECT "months"."monthStartMs", COUNT(*) AS "newCves" FROM "months" JOIN "episodes" ON "episodes"."episodeStartMs" >= "months"."monthStartMs" AND "episodes"."episodeStartMs" < "months"."monthEndMs" GROUP BY "months"."monthStartMs" ), "remediatedCvesByMonth" AS ( SELECT "months"."monthStartMs", COUNT(*) AS "remediatedCves" FROM "months" JOIN "episodes" ON "episodes"."episodeEndMs" IS NOT NULL AND "episodes"."episodeEndMs" >= "months"."monthStartMs" AND "episodes"."episodeEndMs" < "months"."monthEndMs" GROUP BY "months"."monthStartMs" ), "activeCveCountByMonth" AS ( SELECT "months"."monthStartMs", COUNT(*) AS "activeCveCount" FROM "months" JOIN "episodes" ON "episodes"."episodeStartMs" < "months"."monthEndMs" AND ( "episodes"."episodeEndMs" IS NULL OR "episodes"."episodeEndMs" >= "months"."monthEndMs" ) GROUP BY "months"."monthStartMs" ) SELECT "months"."monthStartMs", COALESCE("newCvesByMonth"."newCves", 0) AS "newCves", COALESCE("remediatedCvesByMonth"."remediatedCves", 0) AS "remediatedCves", COALESCE("activeCveCountByMonth"."activeCveCount", 0) AS "activeCveCount" FROM "months" LEFT JOIN "newCvesByMonth" ON "newCvesByMonth"."monthStartMs" = "months"."monthStartMs" LEFT JOIN "remediatedCvesByMonth" ON "remediatedCvesByMonth"."monthStartMs" = "months"."monthStartMs" LEFT JOIN "activeCveCountByMonth" ON "activeCveCountByMonth"."monthStartMs" = "months"."monthStartMs" ORDER BY "months"."monthStartMs" ASC;`; } else { queryToFindRemediationVeloticy = ` WITH params AS ( SELECT CAST(3*24*60*60*1000 AS SIGNED) AS graceMs ), monthAnchors AS ( SELECT TIMESTAMP(DATE_FORMAT(UTC_TIMESTAMP(), '%Y-%m-01 00:00:00')) AS thisMonthStartTs ), monthBoundaries AS ( WITH RECURSIVE m AS ( SELECT 0 AS n, DATE_SUB((SELECT thisMonthStartTs FROM monthAnchors), INTERVAL 4 MONTH) AS monthStartTs UNION ALL SELECT n + 1, DATE_ADD(monthStartTs, INTERVAL 1 MONTH) FROM m WHERE n < 3 ) SELECT monthStartTs FROM m ), months AS ( SELECT CAST(UNIX_TIMESTAMP(monthStartTs) * 1000 AS SIGNED) AS monthStartMs, CAST(UNIX_TIMESTAMP(DATE_ADD(monthStartTs, INTERVAL 1 MONTH)) * 1000 AS SIGNED) AS monthEndMs FROM monthBoundaries ), rowsWithLag AS ( SELECT vulnerabilityinstall.host, vulnerabilityinstall.softwareName, vulnerabilityinstall.versionName, vulnerabilityinstall.vulnerability, CAST(vulnerabilityinstall.createdAt AS SIGNED) AS createdAtMs, CAST(vulnerabilityinstall.uninstalledAt AS SIGNED) AS uninstalledAtMs, LAG(CAST(vulnerabilityinstall.uninstalledAt AS SIGNED)) OVER ( PARTITION BY vulnerabilityinstall.host, vulnerabilityinstall.softwareName, vulnerabilityinstall.versionName, vulnerabilityinstall.vulnerability ORDER BY CAST(vulnerabilityinstall.createdAt AS SIGNED) ) AS prevUninstalledMs FROM vulnerabilityinstall ), labeledEpisodes AS ( SELECT rowsWithLag.*, CASE WHEN rowsWithLag.prevUninstalledMs IS NULL THEN 1 WHEN rowsWithLag.createdAtMs > rowsWithLag.prevUninstalledMs + (SELECT graceMs FROM params) THEN 1 ELSE 0 END AS isNewEpisode FROM rowsWithLag ), episodeIndex AS ( SELECT host, softwareName, versionName, vulnerability, createdAtMs, uninstalledAtMs, SUM(isNewEpisode) OVER ( PARTITION BY host, softwareName, versionName, vulnerability ORDER BY createdAtMs ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS episodeId FROM labeledEpisodes ), episodes AS ( SELECT host, softwareName, versionName, vulnerability, MIN(createdAtMs) AS episodeStartMs, NULLIF(MAX(COALESCE(uninstalledAtMs, 0)), 0) AS episodeEndMs FROM episodeIndex GROUP BY host, softwareName, versionName, vulnerability, episodeId ), newCvesByMonth AS ( SELECT months.monthStartMs, COUNT(*) AS newCves FROM months JOIN episodes ON episodes.episodeStartMs >= months.monthStartMs AND episodes.episodeStartMs < months.monthEndMs GROUP BY months.monthStartMs ), remediatedCvesByMonth AS ( SELECT months.monthStartMs, COUNT(*) AS remediatedCves FROM months JOIN episodes ON episodes.episodeEndMs IS NOT NULL AND episodes.episodeEndMs >= months.monthStartMs AND episodes.episodeEndMs < months.monthEndMs GROUP BY months.monthStartMs ), activeCveCountByMonth AS ( SELECT months.monthStartMs, COUNT(*) AS activeCveCount FROM months JOIN episodes ON episodes.episodeStartMs < months.monthEndMs AND (episodes.episodeEndMs IS NULL OR episodes.episodeEndMs >= months.monthEndMs) GROUP BY months.monthStartMs ) SELECT months.monthStartMs, COALESCE(newCvesByMonth.newCves, 0) AS newCves, COALESCE(remediatedCvesByMonth.remediatedCves, 0) AS remediatedCves, COALESCE(activeCveCountByMonth.activeCveCount, 0) AS activeCveCount FROM months LEFT JOIN newCvesByMonth ON newCvesByMonth.monthStartMs = months.monthStartMs LEFT JOIN remediatedCvesByMonth ON remediatedCvesByMonth.monthStartMs = months.monthStartMs LEFT JOIN activeCveCountByMonth ON activeCveCountByMonth.monthStartMs = months.monthStartMs ORDER BY months.monthStartMs ASC;`; } // console.time('remediation velocity query'); let remediationTrendResult = await sails.sendNativeQuery(queryToFindRemediationVeloticy); // console.timeEnd('remediation velocity query'); let activityTrendsTimeline = remediationTrendResult.rows.map((month)=>{ return { monthStartedAt: month.monthStartMs, timelineLabel: new Date(Number(month.monthStartMs)).toLocaleString('en-US', { month: 'short', year: 'numeric' }), newCves: month.newCves, remediatedCves: month.remediatedCves, totalNumberOfCves: month.activeCveCount, }; }); dataForGraphs.activityTrendsTimeline = activityTrendsTimeline; // // ╔═╗╦ ╦╔═╗ ╦═╗╔═╗╔╦╗╔═╗╔╦╗╦╔═╗╔╦╗╦╔═╗╔╗╔ ╔═╗╦ ╦╔╦╗╔╦╗╔═╗╦═╗╦ ╦ // ║ ╚╗╔╝║╣ ╠╦╝║╣ ║║║║╣ ║║║╠═╣ ║ ║║ ║║║║ ╚═╗║ ║║║║║║║╠═╣╠╦╝╚╦╝ // ╚═╝ ╚╝ ╚═╝ ╩╚═╚═╝╩ ╩╚═╝═╩╝╩╩ ╩ ╩ ╩╚═╝╝╚╝ ╚═╝╚═╝╩ ╩╩ ╩╩ ╩╩╚═ ╩ let thirtyDaysInMs = (1000 * 60 * 60 * 24 * 30); let ninetyDaysInMs = (1000 * 60 * 60 * 24 * 90); let thirtyDaysAgoAt = Date.now() - thirtyDaysInMs; let ninetyDaysAgoAt = Date.now() - ninetyDaysInMs; // let numberOfNewCvesInThePastWeek = await Vulnerability.count({createdAt: {'>': oneWeekAgoAt}}); // console.log('numberOfNewCvesInThePastWeek:', numberOfNewCvesInThePastWeek); let numberOfNewAndActiveVulnInstallsInThePastThirtyDays = await VulnerabilityInstall.count({installedAt: {'>': thirtyDaysAgoAt}, uninstalledAt: 0}); // console.log('numberOfNewAndActiveVulnInstallsInThePastWeek:', numberOfNewAndActiveVulnInstallsInThePastWeek); // let numberOfNewCvesInThePastThirtyDays = await Vulnerability.count({createdAt: {'>': thirtyDaysAgoAt}}); let numberOfNewAndActiveVulnInstallsInThePastNinetyDays = await VulnerabilityInstall.count({installedAt: {'>': ninetyDaysAgoAt}, uninstalledAt: 0}); // console.log('numberOfNewCvesInThePastThirtyDays:', numberOfNewCvesInThePastThirtyDays): // console.log('numberOfNewAndActiveVulnInstallsInThePastThirtyDays:', numberOfNewAndActiveVulnInstallsInThePastThirtyDays); let remediationSummary = { newCvesThirtyDays: numberOfNewAndActiveVulnInstallsInThePastThirtyDays, newCvesNinetyDays: numberOfNewAndActiveVulnInstallsInThePastNinetyDays, }; // Send native queries to get number of remediated CVEs let nativeQueryToGetNumberOfRemediatedCves; if(sails.config.datastores.default.adapter === 'sails-postgresql') { nativeQueryToGetNumberOfRemediatedCves = ` WITH "timeBounds" AS ( SELECT (EXTRACT(EPOCH FROM (now() AT TIME ZONE 'UTC')) * 1000)::bigint AS "nowMs", (EXTRACT(EPOCH FROM ((now() AT TIME ZONE 'UTC') - INTERVAL '30 days')) * 1000)::bigint AS "thirtyDaysAgoMs", (EXTRACT(EPOCH FROM ((now() AT TIME ZONE 'UTC') - INTERVAL '90 days')) * 1000)::bigint AS "ninetyDaysAgoMs" ), "remediatedOnceLast90Days" AS ( SELECT vulnerabilityinstall.id, vulnerabilityinstall."uninstalledAt" AS "remediatedAtMs" FROM vulnerabilityinstall JOIN "timeBounds" ON TRUE WHERE vulnerabilityinstall."uninstalledAt" IS NOT NULL AND vulnerabilityinstall."uninstalledAt" > 0 AND vulnerabilityinstall."uninstalledAt" >= "timeBounds"."ninetyDaysAgoMs" AND vulnerabilityinstall."uninstalledAt" < "timeBounds"."nowMs" AND NOT EXISTS ( SELECT 1 FROM vulnerabilityinstall AS vi_reinstall WHERE vi_reinstall.host = vulnerabilityinstall.host AND vi_reinstall.vulnerability = vulnerabilityinstall.vulnerability AND vi_reinstall."softwareName" = vulnerabilityinstall."softwareName" AND COALESCE(vi_reinstall."versionName",'') = COALESCE(vulnerabilityinstall."versionName",'') AND vi_reinstall."createdAt" > vulnerabilityinstall."uninstalledAt" ) ) SELECT COUNT(*) FILTER (WHERE "remediatedAtMs" >= "timeBounds"."thirtyDaysAgoMs") AS "remediatedCveCountLast30Days", COUNT(*) AS "remediatedCveCountLast90Days" FROM "remediatedOnceLast90Days" CROSS JOIN "timeBounds";`; } else { nativeQueryToGetNumberOfRemediatedCves = ` WITH timeBounds AS ( SELECT CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP()) * 1000 AS SIGNED) AS nowMs, CAST(UNIX_TIMESTAMP(DATE_SUB(UTC_TIMESTAMP(), INTERVAL 7 DAY)) * 1000 AS SIGNED) AS thirtyDaysAgoMs, CAST(UNIX_TIMESTAMP(DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 DAY)) * 1000 AS SIGNED) AS ninetyDaysAgoMs ), remediatedOnceLast90Days AS ( SELECT vulnerabilityinstall.id, vulnerabilityinstall.uninstalledAt AS remediatedAtMs FROM vulnerabilityinstall JOIN timeBounds ON TRUE WHERE vulnerabilityinstall.uninstalledAt IS NOT NULL AND vulnerabilityinstall.uninstalledAt > 0 AND vulnerabilityinstall.uninstalledAt >= timeBounds.ninetyDaysAgoMs AND vulnerabilityinstall.uninstalledAt < timeBounds.nowMs AND NOT EXISTS ( SELECT 1 FROM vulnerabilityinstall AS vi_reinstall WHERE vi_reinstall.host = vulnerabilityinstall.host AND vi_reinstall.vulnerability = vulnerabilityinstall.vulnerability AND vi_reinstall.softwareName = vulnerabilityinstall.softwareName AND COALESCE(vi_reinstall.versionName,'') = COALESCE(vulnerabilityinstall.versionName,'') AND vi_reinstall.createdAt > vulnerabilityinstall.uninstalledAt ) ) SELECT SUM(CASE WHEN remediatedAtMs >= timeBounds.thirtyDaysAgoMs THEN 1 ELSE 0 END) AS remediatedCveCountLast30Days, COUNT(*) AS remediatedCveCountLast90Days FROM remediatedOnceLast90Days CROSS JOIN timeBounds;`; } // console.time('recent remediated cves query'); let remediatedCvesQueryResult = await sails.sendNativeQuery(nativeQueryToGetNumberOfRemediatedCves); // console.timeEnd('recent remediated cves query'); remediationSummary.remediatedCvesThirtyDays = remediatedCvesQueryResult.rows[0].remediatedCveCountLast30Days ? remediatedCvesQueryResult.rows[0].remediatedCveCountLast30Days : 0; remediationSummary.remediatedCvesNinetyDays = remediatedCvesQueryResult.rows[0].remediatedCveCountLast90Days ? remediatedCvesQueryResult.rows[0].remediatedCveCountLast90Days : 0; dataForGraphs.remediationSummary = remediationSummary; // // ╔═╗╦ ╔╦╗╔═╗╔═╗╔╦╗ ╔═╗╦ ╦╔═╗╔═╗ ╦ ╦╦╔╦╗╦ ╦ ╔═╗═╗ ╦╔═╗╦ ╔═╗╦╔╦╗╔═╗ // ║ ║║ ║║║╣ ╚═╗ ║ ║ ╚╗╔╝║╣ ╚═╗ ║║║║ ║ ╠═╣ ║╣ ╔╩╦╝╠═╝║ ║ ║║ ║ ╚═╗ // ╚═╝╩═╝═╩╝╚═╝╚═╝ ╩ ╚═╝ ╚╝ ╚═╝╚═╝ ╚╩╝╩ ╩ ╩ ╩ ╚═╝╩ ╚═╩ ╩═╝╚═╝╩ ╩ ╚═╝ let queryToFindOldestExploitableCves; if(sails.config.datastores.default.adapter === 'sails-postgresql') { queryToFindOldestExploitableCves = ` WITH oldestExploitable AS ( SELECT DISTINCT vulnerability."cveId", vulnerability."publishedAt" FROM vulnerability JOIN vulnerabilityinstall ON vulnerabilityinstall.vulnerability = vulnerability.id AND (vulnerabilityinstall."uninstalledAt" IS NULL OR vulnerabilityinstall."uninstalledAt" = 0) WHERE vulnerability."hasKnownExploit" = TRUE ) SELECT "cveId" FROM oldestExploitable ORDER BY "publishedAt" ASC LIMIT 5;`; } else { queryToFindOldestExploitableCves = ` WITH oldestExploitable AS ( SELECT DISTINCT vulnerability.cveId, vulnerability.publishedAt FROM vulnerability JOIN vulnerabilityinstall ON vulnerabilityinstall.vulnerability = vulnerability.id AND (vulnerabilityinstall.uninstalledAt IS NULL OR vulnerabilityinstall.uninstalledAt = 0) WHERE vulnerability.hasKnownExploit = TRUE ) SELECT cveId FROM oldestExploitable ORDER BY publishedAt ASC LIMIT 5;`; } // console.time('oldest exploitable cves query'); let oldestExploitableCvesResult = await sails.sendNativeQuery(queryToFindOldestExploitableCves); // console.timeEnd('oldest exploitable cves query'); let oldestExploitableCves = oldestExploitableCvesResult.rows.map((cve)=>{ return { cveId: cve.cveId, fleetUrl: `${sails.config.custom.fleetBaseUrl}/software/vulnerabilities/${cve.cveId}`, yearPublished: cve.cveId.split('-')[1], }; }); dataForGraphs.oldestExploitableCves = oldestExploitableCves; // // ╦ ╦╔═╗╔═╗╔╦╗╔═╗ ╦ ╦╦╔╦╗╦ ╦ ╔═╗╦ ╔╦╗╔═╗╔═╗╔╦╗ ╔═╗═╗ ╦╔═╗╦ ╔═╗╦╔╦╗╔═╗╔╗ ╦ ╔═╗ ╔═╗╦ ╦╔═╗╔═╗ // ╠═╣║ ║╚═╗ ║ ╚═╗ ║║║║ ║ ╠═╣ ║ ║║ ║║║╣ ╚═╗ ║ ║╣ ╔╩╦╝╠═╝║ ║ ║║ ║ ╠═╣╠╩╗║ ║╣ ║ ╚╗╔╝║╣ ╚═╗ // ╩ ╩╚═╝╚═╝ ╩ ╚═╝ ╚╩╝╩ ╩ ╩ ╩ ╚═╝╩═╝═╩╝╚═╝╚═╝ ╩ ╚═╝╩ ╚═╩ ╩═╝╚═╝╩ ╩ ╩ ╩╚═╝╩═╝╚═╝ ╚═╝ ╚╝ ╚═╝╚═╝ let queryToFindHostsWithOldestExploitableCves; if(sails.config.datastores.default.adapter === 'sails-postgresql') { queryToFindHostsWithOldestExploitableCves = ` WITH "hostOldestExploitable" AS ( SELECT host.id AS "hostId", host."displayName" AS "displayName", host."fleetApid" AS "fleetApid", vulnerability."cveId" AS "cveId", vulnerability."publishedAt" AS "publishedAtMs", vulnerabilityinstall."softwareName" AS "softwareName", vulnerabilityinstall."versionName" AS "versionName" FROM vulnerabilityinstall JOIN vulnerability ON vulnerability.id = vulnerabilityinstall.vulnerability AND vulnerability."hasKnownExploit" = TRUE JOIN host ON host.id = vulnerabilityinstall.host WHERE vulnerabilityinstall."uninstalledAt" IS NULL OR vulnerabilityinstall."uninstalledAt" = 0 ), "rankedOldestPerHost" AS ( SELECT "hostId", "displayName", "fleetApid", "cveId", "publishedAtMs", "softwareName", "versionName", ROW_NUMBER() OVER ( PARTITION BY "hostId" ORDER BY "publishedAtMs" ASC ) AS "rowNumber" FROM "hostOldestExploitable" ) SELECT "displayName", "fleetApid", "cveId", ("softwareName" || ' ' || COALESCE("versionName", '')) AS "softwareNameAndVersion", "publishedAtMs" FROM "rankedOldestPerHost" WHERE "rowNumber" = 1 ORDER BY "publishedAtMs" ASC LIMIT 5;`; } else { queryToFindHostsWithOldestExploitableCves = ` WITH hostOldestExploitable AS ( SELECT host.id AS hostId, host.displayName AS displayName, host.fleetApid AS fleetApid, vulnerability.cveId AS cveId, vulnerability.publishedAt AS publishedAtMs, vulnerabilityinstall.softwareName AS softwareName, vulnerabilityinstall.versionName AS versionName FROM vulnerabilityinstall JOIN vulnerability ON vulnerability.id = vulnerabilityinstall.vulnerability AND vulnerability.hasKnownExploit = TRUE JOIN host ON host.id = vulnerabilityinstall.host WHERE vulnerabilityinstall.uninstalledAt IS NULL OR vulnerabilityinstall.uninstalledAt = 0 ), rankedOldestPerHost AS ( SELECT hostId, displayName, fleetApid, cveId, publishedAtMs, softwareName, versionName, ROW_NUMBER() OVER ( PARTITION BY hostId ORDER BY publishedAtMs ASC ) AS rowNumber FROM hostOldestExploitable ) SELECT displayName, fleetApid, cveId, CONCAT(softwareName, ' ', COALESCE(versionName, '')) AS softwareNameAndVersion, publishedAtMs FROM rankedOldestPerHost WHERE rowNumber = 1 ORDER BY publishedAtMs ASC LIMIT 5;`; } // console.time('oldest exploitable hosts query'); let oldestExploitableHostsResult = await sails.sendNativeQuery(queryToFindHostsWithOldestExploitableCves); // console.timeEnd('oldest exploitable hosts query'); let oldestExploitableHosts = oldestExploitableHostsResult.rows.map((host)=>{ return { displayName: host.displayName, affectedSoftware: host.softwareNameAndVersion, fleetUrl: `${sails.config.custom.fleetBaseUrl}/hosts/${host.fleetApid}`, cveId: host.cveId, yearPublished: host.cveId.split('-')[1], }; }); dataForGraphs.oldestExploitableHosts = oldestExploitableHosts; // // ╔╗╔╔═╗╦ ╦╔═╗╔═╗╔╦╗ ╔═╗╦ ╦╔═╗╔═╗ ╦ ╦╦╔╦╗╦ ╦ ╔═╗═╗ ╦╔═╗╦ ╔═╗╦╔╦╗╔═╗ // ║║║║╣ ║║║║╣ ╚═╗ ║ ║ ╚╗╔╝║╣ ╚═╗ ║║║║ ║ ╠═╣ ║╣ ╔╩╦╝╠═╝║ ║ ║║ ║ ╚═╗ // ╝╚╝╚═╝╚╩╝╚═╝╚═╝ ╩ ╚═╝ ╚╝ ╚═╝╚═╝ ╚╩╝╩ ╩ ╩ ╩ ╚═╝╩ ╚═╩ ╩═╝╚═╝╩ ╩ ╚═╝ let queryToFindNewestExploitableCves; if(sails.config.datastores.default.adapter === 'sails-postgresql') { queryToFindNewestExploitableCves = ` WITH newestExploitable AS ( SELECT DISTINCT vulnerability."cveId", vulnerability."publishedAt" FROM vulnerability JOIN vulnerabilityinstall ON vulnerabilityinstall.vulnerability = vulnerability.id AND (vulnerabilityinstall."uninstalledAt" IS NULL OR vulnerabilityinstall."uninstalledAt" = 0) WHERE vulnerability."hasKnownExploit" = TRUE ) SELECT "cveId" FROM newestExploitable ORDER BY "publishedAt" DESC LIMIT 5;`; } else { queryToFindNewestExploitableCves = ` WITH newestExploitable AS ( SELECT DISTINCT vulnerability.cveId, vulnerability.publishedAt FROM vulnerability JOIN vulnerabilityinstall ON vulnerabilityinstall.vulnerability = vulnerability.id AND (vulnerabilityinstall.uninstalledAt IS NULL OR vulnerabilityinstall.uninstalledAt = 0) WHERE vulnerability.hasKnownExploit = TRUE ) SELECT cveId FROM newestExploitable ORDER BY publishedAt DESC LIMIT 5;`; } // console.time('newest exploitable cves'); let newestExploitableCvesResult = await sails.sendNativeQuery(queryToFindNewestExploitableCves); // console.timeEnd('newest exploitable cves'); // console.log(newestExploitableCvesResult); let newestExploitableCves = newestExploitableCvesResult.rows.map((cve)=>{ return { cveId: cve.cveId, fleetUrl: `${sails.config.custom.fleetBaseUrl}/software/vulnerabilities/${cve.cveId}`, yearPublished: cve.cveId.split('-')[1], }; }); dataForGraphs.newestExploitableCves = newestExploitableCves; // // ╦ ╦╔═╗╔═╗╔╦╗╔═╗ ╦ ╦╦╔╦╗╦ ╦ ╔╗╔╔═╗╦ ╦╔═╗╔═╗╔╦╗ ╔═╗═╗ ╦╔═╗╦ ╔═╗╦╔╦╗╔═╗╔╗ ╦ ╔═╗ ╔═╗╦ ╦╔═╗╔═╗ // ╠═╣║ ║╚═╗ ║ ╚═╗ ║║║║ ║ ╠═╣ ║║║║╣ ║║║║╣ ╚═╗ ║ ║╣ ╔╩╦╝╠═╝║ ║ ║║ ║ ╠═╣╠╩╗║ ║╣ ║ ╚╗╔╝║╣ ╚═╗ // ╩ ╩╚═╝╚═╝ ╩ ╚═╝ ╚╩╝╩ ╩ ╩ ╩ ╝╚╝╚═╝╚╩╝╚═╝╚═╝ ╩ ╚═╝╩ ╚═╩ ╩═╝╚═╝╩ ╩ ╩ ╩╚═╝╩═╝╚═╝ ╚═╝ ╚╝ ╚═╝╚═╝ let queryToFindHostsWithNewestExploitableCves; if(sails.config.datastores.default.adapter === 'sails-postgresql') { queryToFindHostsWithNewestExploitableCves = ` WITH "hostNewestExploitable" AS ( SELECT host.id AS "hostId", host."displayName" AS "displayName", host."fleetApid" AS "fleetApid", vulnerability."cveId" AS "cveId", vulnerability."publishedAt" AS "publishedAtMs", vulnerabilityinstall."softwareName" AS "softwareName", vulnerabilityinstall."versionName" AS "versionName" FROM vulnerabilityinstall JOIN vulnerability ON vulnerability.id = vulnerabilityinstall.vulnerability AND vulnerability."hasKnownExploit" = TRUE JOIN host ON host.id = vulnerabilityinstall.host WHERE vulnerabilityinstall."uninstalledAt" IS NULL OR vulnerabilityinstall."uninstalledAt" = 0 ), "rankedNewestPerHost" AS ( SELECT "hostId", "displayName", "fleetApid", "cveId", "publishedAtMs", "softwareName", "versionName", ROW_NUMBER() OVER ( PARTITION BY "hostId" ORDER BY "publishedAtMs" ASC ) AS "rowNumber" FROM "hostNewestExploitable" ) SELECT "displayName", "fleetApid", "cveId", ("softwareName" || ' ' || COALESCE("versionName", '')) AS "softwareNameAndVersion", "publishedAtMs" FROM "rankedNewestPerHost" WHERE "rowNumber" = 1 ORDER BY "publishedAtMs" DESC LIMIT 5;`; } else { queryToFindHostsWithNewestExploitableCves = ` WITH hostNewestExploitable AS ( SELECT host.id AS hostId, host.displayName AS displayName, host.fleetApid AS fleetApid, vulnerability.cveId AS cveId, vulnerability.publishedAt AS publishedAtMs, vulnerabilityinstall.softwareName AS softwareName, vulnerabilityinstall.versionName AS versionName FROM vulnerabilityinstall JOIN vulnerability ON vulnerability.id = vulnerabilityinstall.vulnerability AND vulnerability.hasKnownExploit = TRUE JOIN host ON host.id = vulnerabilityinstall.host WHERE vulnerabilityinstall.uninstalledAt IS NULL OR vulnerabilityinstall.uninstalledAt = 0 ), rankedNewestPerHost AS ( SELECT hostId, displayName, fleetApid, cveId, publishedAtMs, softwareName, versionName, ROW_NUMBER() OVER ( PARTITION BY hostId ORDER BY publishedAtMs ASC ) AS rowNumber FROM hostNewestExploitable ) SELECT displayName, fleetApid, cveId, CONCAT(softwareName, ' ', COALESCE(versionName, '')) AS softwareNameAndVersion, publishedAtMs FROM rankedNewestPerHost WHERE rowNumber = 1 ORDER BY publishedAtMs DESC LIMIT 5;`; } // console.time('hosts with newest exploitable cves query'); let newestExploitableHostsResult = await sails.sendNativeQuery(queryToFindHostsWithNewestExploitableCves); // console.timeEnd('hosts with newest exploitable cves query'); // console.log(newestExploitableHostsResult); let newestExploitableHosts = newestExploitableHostsResult.rows.map((host)=>{ return { displayName: host.displayName, affectedSoftware: host.softwareNameAndVersion, fleetUrl: `${sails.config.custom.fleetBaseUrl}/hosts/${host.fleetApid}`, cveId: host.cveId, yearPublished: host.cveId.split('-')[1], }; }); dataForGraphs.newestExploitableHosts = newestExploitableHosts; // console.timeEnd(`Dashboard page view action`); // Respond with view. return { ...dataForGraphs }; } };