mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
Changes: - Updated the query to find critically vulnerable rare software for MySQL databases to return affected host names as `hostNames`
1475 lines
62 KiB
JavaScript
1475 lines
62 KiB
JavaScript
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
|
|
};
|
|
|
|
}
|
|
|
|
|
|
};
|