Skip to content

Commit

Permalink
docs: DOC-1520 Security Bulletin Fix (#5055) (#5061)
Browse files Browse the repository at this point in the history
* chore: update object

* chore: DOC-1520 fixed affected products content logic

* chore: update limit

* chore: update table to easier scan

(cherry picked from commit ace8016)

Co-authored-by: Karl Cardenas <[email protected]>
  • Loading branch information
1 parent 4a08968 commit 70ce220
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 76 deletions.
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

0 comments on commit 70ce220

Please sign in to comment.