Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Security Bulletin Fix #5055

Merged
merged 5 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 3 additions & 42 deletions utils/cves/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ const { escapeMDXSpecialChars } = require("../helpers/string");
const { generateMarkdownTable } = require("../helpers/affected-table");
const { generateRevisionHistory } = require("../helpers/revision-history");
const { generateCVEOfficialDetailsUrl } = require("../helpers/urls");
const { generateCVEMap } = require("../helpers/cveHelpers");

async function getSecurityBulletins(payload) {
const limit = 100;
const limit = 300;
const maxIterations = 1000;
let results = [];

Expand Down Expand Up @@ -210,47 +211,7 @@ async function generateMarkdownForCVEs(GlobalCVEData) {

// To generate the Impact Product & Versions table we need to track all the instances of the same CVE
// The following hashmap will store the data for each CVE and aggregate the impact data for each product
const cveImpactMap = {};

for (const item of allCVEs) {
// Let's add the CVE to the map if it doesn't exist
// We can take all of the values from the first instance of the CVE
// Future instances will update the values if they are true
if (!cveImpactMap[item.metadata.cve]) {
cveImpactMap[item.metadata.cve] = {
versions: item.spec.impact.impactedVersions,
impactsPaletteEnterprise: item.spec.impact.impactedProducts.palette,
impactsPaletteEnterpriseAirgap: item.spec.impact.impactedDeployments.airgap,
impactsVerteX: item.spec.impact.impactedProducts.vertex,
impactsVerteXAirgap: item.spec.impact.impactedDeployments.airgap,
};
}

// If the CVE already exists in the map, we need to update the values
// But only if the value is true. If the value is false, we don't need to update it.
if (cveImpactMap[item.metadata.cve]) {
cveImpactMap[item.metadata.cve].versions = [
...cveImpactMap[item.metadata.cve].versions,
...item.spec.impact.impactedVersions,
];

if (item.spec.impact.impactedProducts.palette) {
cveImpactMap[item.metadata.cve].impactsPaletteEnterprise = true;
}

if (item.spec.impact.impactedDeployments.airgap) {
cveImpactMap[item.metadata.cve].impactsPaletteEnterpriseAirgap = true;
}

if (item.spec.impact.impactedProducts.vertex) {
cveImpactMap[item.metadata.cve].impactsVerteX = true;
}

if (item.spec.impact.impactedDeployments.airgap) {
cveImpactMap[item.metadata.cve].impactsVerteXAirgap = true;
}
}
}
const cveImpactMap = generateCVEMap(allCVEs);

const markdownPromises = allCVEs.map((item) =>
createCveMarkdown(item, cveImpactMap[item.metadata.cve], "docs/docs-content/security-bulletins/reports/")
Expand Down
51 changes: 34 additions & 17 deletions utils/helpers/affected-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,60 @@ function generateMarkdownTable(cveImpactMap) {
throw new Error("Invalid input: cveImpactMap must be an object.");
}

// Extract impact data and ensure consistency
const impactData = {
"Palette Enterprise": cveImpactMap.impactsPaletteEnterprise,
"Palette Enterprise Airgap": cveImpactMap.impactsPaletteEnterpriseAirgap,
VerteX: cveImpactMap.impactsVerteX,
"VerteX Airgap": cveImpactMap.impactsVerteXAirgap,
"Palette Enterprise": cveImpactMap.palette,
"Palette Enterprise Airgap": cveImpactMap.paletteAirgap,
VerteX: cveImpactMap.vertex,
"VerteX Airgap": cveImpactMap.vertexAirgap,
};

const allProductsFalse = Object.values(impactData).every((value) => value === false);
// Check if all products are not impacted
const allProductsFalse = Object.values(impactData).every((product) => product.impacts === false);
if (allProductsFalse) {
return "Investigation is ongoing to determine how this vulnerability affects our products";
return "Investigation is ongoing to determine how this vulnerability affects our products.";
}

const anyProductTrue = Object.values(impactData).some((value) => value === true);
if (anyProductTrue && (!cveImpactMap.versions || cveImpactMap.versions.length === 0)) {
// Check for any product impacted but no versions provided
const anyProductTrueNoVersions = Object.values(impactData).some(
(product) => product.impacts && (!product.versions || product.versions.length === 0)
);
if (anyProductTrueNoVersions) {
throw new Error("Error: Data inconsistency - Products impacted but no versions provided.");
}

// Create the header row with the specified order
// Collect all unique versions across all products
const allVersions = Object.values(impactData)
.flatMap((product) => product.versions || [])
.filter((version) => semver.valid(version));
const uniqueVersions = Array.from(new Set(allVersions)).sort(semver.rcompare);

// Create the header row
const header = `| Version | Palette Enterprise | Palette Enterprise Airgap | VerteX | VerteX Airgap |\n`;
const separator = `| - | -------- | -------- | -------- | -------- |\n`;

// const uniqueVersions = Array.from(new Set(cveImpactMap.versions)).sort((a, b) => b.localeCompare(a));
const uniqueVersions = Array.from(new Set(cveImpactMap.versions)).sort(semver.rcompare);

// Create rows for each version
const rows = uniqueVersions
.map((version) => {
const row = [
`| ${version}`,
impactData["Palette Enterprise"] ? "Impacted" : "No Impact",
impactData["Palette Enterprise Airgap"] ? "Impacted" : "No Impact",
impactData["VerteX"] ? "Impacted" : "No Impact",
impactData["VerteX Airgap"] ? "Impacted" : "No Impact",
impactData["Palette Enterprise"].impacts && impactData["Palette Enterprise"].versions.includes(version)
? "⚠️ Impacted"
: "✅ No Impact",
impactData["Palette Enterprise Airgap"].impacts &&
impactData["Palette Enterprise Airgap"].versions.includes(version)
? "⚠️ Impacted"
: "✅ No Impact",
impactData["VerteX"].impacts && impactData["VerteX"].versions.includes(version)
? "⚠️ Impacted"
: "✅ No Impact",
impactData["VerteX Airgap"].impacts && impactData["VerteX Airgap"].versions.includes(version)
? "⚠️ Impacted"
: "✅ No Impact",
].join(" | ");
return row + " |";
})
.join("\n");

return header + separator + rows;
}

Expand Down
31 changes: 14 additions & 17 deletions utils/helpers/affected-table.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ const { generateMarkdownTable } = require("./affected-table");
describe("generateMarkdownTable", () => {
it("should generate a markdown table for two products with mixed impact", () => {
const cveImpactMap = {
versions: ["4.4.20", "4.5.3"],
impactsPaletteEnterprise: true,
impactsPaletteEnterpriseAirgap: false,
impactsVerteX: false,
impactsVerteXAirgap: false,
palette: { impacts: true, versions: ["4.4.20", "4.5.3"] },
paletteAirgap: { impacts: false, versions: [] },
vertex: { impacts: false, versions: [] },
vertexAirgap: { impacts: false, versions: [] },
};

const expectedTable = `| Version | Palette Enterprise | Palette Enterprise Airgap | VerteX | VerteX Airgap |
|-|--------|--------|--------|--------|
| - | -------- | -------- | -------- | -------- |
| 4.5.3 | Impacted | No Impact | No Impact | No Impact |
| 4.4.20 | Impacted | No Impact | No Impact | No Impact |`;

Expand All @@ -20,24 +19,22 @@ describe("generateMarkdownTable", () => {

it("should return investigation message when all products are not impacted", () => {
const cveImpactMap = {
versions: ["4.4.20", "4.5.3"],
impactsPaletteEnterprise: false,
impactsPaletteEnterpriseAirgap: false,
impactsVerteX: false,
impactsVerteXAirgap: false,
palette: { impacts: false, versions: [] },
paletteAirgap: { impacts: false, versions: [] },
vertex: { impacts: false, versions: [] },
vertexAirgap: { impacts: false, versions: [] },
};

const expectedMessage = "Investigation is ongoing to determine how this vulnerability affects our products";
const expectedMessage = "Investigation is ongoing to determine how this vulnerability affects our products.";
expect(generateMarkdownTable(cveImpactMap)).toBe(expectedMessage);
});

it("should throw an error when products are impacted but no versions are provided", () => {
const cveImpactMap = {
versions: [],
impactsPaletteEnterprise: true,
impactsPaletteEnterpriseAirgap: false,
impactsVerteX: false,
impactsVerteXAirgap: false,
palette: { impacts: true, versions: [] },
paletteAirgap: { impacts: false, versions: [] },
vertex: { impacts: false, versions: [] },
vertexAirgap: { impacts: false, versions: [] },
};

expect(() => generateMarkdownTable(cveImpactMap)).toThrow(
Expand Down
57 changes: 57 additions & 0 deletions utils/helpers/cveHelpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// generates a map of CVEs. Each CVE entry contains information about the impact of the CVE on different products and versions.
function generateCVEMap(cveData) {
const cveImpactMap = {};

for (const item of cveData) {
// Let's create a CVE entry in the map if it doesn't exist.
// By default, let's initailize all values to false or empty array.
if (!cveImpactMap[item.metadata.cve]) {
cveImpactMap[item.metadata.cve] = {
palette: {
impacts: false,
versions: [],
},
paletteAirgap: {
impacts: false,
versions: [],
},
vertex: {
impacts: false,
versions: [],
},
vertexAirgap: {
impacts: false,
versions: [],
},
};
}

// Palette Enterprise logic
if (item.spec.impact.impactedProducts.palette && !item.spec.impact.impactedDeployments.airgap) {
cveImpactMap[item.metadata.cve].palette.impacts = true;
cveImpactMap[item.metadata.cve].palette.versions = item.spec.impact.impactedVersions;
}

// Palette Enterprise Airgap logic
if (item.spec.impact.impactedProducts.palette && item.spec.impact.impactedDeployments.airgap) {
cveImpactMap[item.metadata.cve].paletteAirgap.impacts = true;
cveImpactMap[item.metadata.cve].paletteAirgap.versions = item.spec.impact.impactedVersions;
}

// Palette VerteX logic
if (item.spec.impact.impactedProducts.vertex && !item.spec.impact.impactedDeployments.airgap) {
cveImpactMap[item.metadata.cve].vertex.impacts = true;
cveImpactMap[item.metadata.cve].vertex.versions = item.spec.impact.impactedVersions;
}

// Palette VerteX Airgap logic
if (item.spec.impact.impactedProducts.vertex && item.spec.impact.impactedDeployments.airgap) {
cveImpactMap[item.metadata.cve].vertexAirgap.impacts = true;
cveImpactMap[item.metadata.cve].vertexAirgap.versions = item.spec.impact.impactedVersions;
}
}

return cveImpactMap;
}

module.exports = { generateCVEMap };
Loading
Loading