Skip to content

Commit

Permalink
feat: Crosstable displays only in CVEs mentioned products
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas Junk committed Aug 31, 2023
1 parent bdcebbf commit 2d1a2cb
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 60 deletions.
12 changes: 8 additions & 4 deletions src/lib/singleview/components/ProductVulnerabilities.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@

<script lang="ts">
import { appStore } from "$lib/store";
import { CSAFDocProps, DocumentCategory } from "../docmodel/docmodeltypes";
import { ProductStatusSymbol } from "../productvulnerabilities/productvulnerabilitiestypes";
let headerColumns: any = [];
let headerColumns: string[] = [];
let productLines: string[][];
$: if ($appStore.doc) {
const vulnerabilities = [...$appStore.doc.vulnerabilities];
headerColumns = vulnerabilities.shift();
const vulnerabilities = [...$appStore.doc.productVulnerabilities];
headerColumns = vulnerabilities.shift()!;
productLines = vulnerabilities;
}
$: isDocumentASecurityAdvisory =
$appStore.doc &&
$appStore.doc[CSAFDocProps.CATEGORY] === DocumentCategory.CSAF_SECURITY_ADVISORY;
</script>

{#if $appStore.doc}
{#if isDocumentASecurityAdvisory}
<div>
<h2>Vulnerabilities overview</h2>
{#if productLines.length > 0}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/singleview/docmodel/docmodel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ const convertToDocModel = (csafDoc: any): DocModel => {
trackingVersion: getTrackingVersion(csafDoc),
revisionHistory: [],
lastUpdate: getLastUpdate(csafDoc),
vulnerabilities: [],
productVulnerabilities: [],
isDocPresent: checkDocumentPresent(csafDoc),
isTrackingPresent: checkTrackingPresent(csafDoc),
isDistributionPresent: checkDistributionPresent(csafDoc),
Expand Down
8 changes: 7 additions & 1 deletion src/lib/singleview/docmodel/docmodeltypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ export const Status = {
} as const;
export type StatusKeys = (typeof Status)[keyof typeof Status];

export const DocumentCategory = {
CSAF_SECURITY_ADVISORY: "csaf_security_advisory",
CSAF_BASE: "csaf_base",
CSAF_VEX: "csaf_vex"
} as const;

export type Publisher = {
category: string;
name: string;
Expand All @@ -73,7 +79,7 @@ export type DocModel = {
publisher: Publisher;
trackingVersion: string;
revisionHistory: RevisionHistoryEntry[];
vulnerabilities: Array<Array<string>>;
productVulnerabilities: Array<Array<string>>;
isDocPresent: boolean;
isTrackingPresent: boolean;
isDistributionPresent: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
extractVulnerabilities,
generateProductVulnerabilities
} from "./productvulnerabilities";
import { ProductStatusSymbol } from "./productvulnerabilitiestypes";
import { ProductStatusSymbol, type Vulnerability } from "./productvulnerabilitiestypes";

const emptyObject = {};

Expand Down Expand Up @@ -264,48 +264,48 @@ describe("Productvulnerabilities test", () => {

describe("Productvulnerabilities test", () => {
it("Vulnerability: parses empty object", () => {
const result = extractVulnerabilities(emptyObject);
expect(result.length).toBe(0);
const { vulnerabilities } = extractVulnerabilities(emptyObject);
expect(vulnerabilities.length).toBe(0);
});
});

describe("Productvulnerabilities test", () => {
it("Vulnerability: parses no vulnerabilities", () => {
const result = extractVulnerabilities(noVulnerabilities);
expect(result.length).toBe(0);
const { vulnerabilities } = extractVulnerabilities(noVulnerabilities);
expect(vulnerabilities.length).toBe(0);
});
});

describe("Productvulnerabilities test", () => {
it("Vulnerability: parses vulnerability without cve", () => {
const result = extractVulnerabilities(vulnerability_wo_CVE);
expect(result.length).toBe(1);
const { vulnerabilities } = extractVulnerabilities(vulnerability_wo_CVE);
expect(vulnerabilities.length).toBe(0);
});
});

describe("Productvulnerabilities test", () => {
it("Vulnerability: parses vulnerability with empty product_status", () => {
const result = extractVulnerabilities(vulnerability_empty_product_status);
expect(result.length).toBe(1);
const { vulnerabilities } = extractVulnerabilities(vulnerability_empty_product_status);
expect(vulnerabilities.length).toBe(1);
});
});

describe("Productvulnerabilities test", () => {
it("Vulnerability: parses vulnerability with empty known_affected", () => {
const result = extractVulnerabilities(vulnerability_known_affected_empty);
expect(result.length).toBe(1);
expect(Object.keys(result[0].known_affected).length).toBe(0);
const { vulnerabilities } = extractVulnerabilities(vulnerability_known_affected_empty);
expect(vulnerabilities.length).toBe(1);
expect(Object.keys(vulnerabilities[0].known_affected!).length).toBe(0);
});
});

describe("Productvulnerabilities test", () => {
it("Vulnerability: parses vulnerability with filled known_affected", () => {
const result = extractVulnerabilities(vulnerability_known_affected_filled);
const value = result[0];
expect(result.length).toBe(1);
expect(Object.keys(value.known_affected).length).toBe(2);
expect(value.known_affected["123"]).toBe("123");
expect(value.known_affected["456"]).toBe("456");
const { vulnerabilities } = extractVulnerabilities(vulnerability_known_affected_filled);
const value = vulnerabilities[0];
expect(vulnerabilities.length).toBe(1);
expect(Object.keys(value.known_affected!).length).toBe(2);
expect(value.known_affected!["123"]).toBe("123");
expect(value.known_affected!["456"]).toBe("456");
});
});

Expand Down
90 changes: 54 additions & 36 deletions src/lib/singleview/productvulnerabilities/productvulnerabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ import {
type Product,
type Relationship,
type ProductStatus_t,
type ProductStatus_t_Key
type ProductStatus_t_Key,
type VulnerabilitesExtractionResult
} from "./productvulnerabilitiestypes";

const generateProductVulnerabilities = (jsonDocument: any) => {
const products = extractProducts(jsonDocument);
let vulnerabilities = extractVulnerabilities(jsonDocument);
let products = extractProducts(jsonDocument);
let { vulnerabilities, relevantProducts } = extractVulnerabilities(jsonDocument);
products = products.filter((product: Product) => {
return relevantProducts[product.product_id];
});
vulnerabilities.sort((vuln1: Vulnerability, vuln2: Vulnerability) => {
if (vuln1.cve < vuln2.cve) return -1;
if (vuln1.cve > vuln2.cve) return 1;
Expand Down Expand Up @@ -119,43 +123,57 @@ const generateDictFrom = (productStatus: ProductStatus_t, section: ProductStatus
}, {});
};

const extractVulnerabilities = (jsonDocument: any): Vulnerability[] => {
const extractVulnerabilities = (jsonDocument: any): VulnerabilitesExtractionResult => {
const extractionResult: VulnerabilitesExtractionResult = {
vulnerabilities: [],
relevantProducts: {}
};
if (!jsonDocument.vulnerabilities) {
return [];
return extractionResult;
}
return jsonDocument.vulnerabilities.reduce((acc: Vulnerability[], vulnerability: any) => {
if (!vulnerability.cve) {
return acc;
}
const result: Vulnerability = {
cve: vulnerability.cve
};
if (vulnerability.product_status) {
if (vulnerability.product_status.known_affected) {
result.known_affected = generateDictFrom(vulnerability.product_status, "known_affected");
const vulnerabilities = jsonDocument.vulnerabilities.reduce(
(acc: Vulnerability[], vulnerability: any) => {
if (!vulnerability.cve) {
return acc;
}
if (vulnerability.product_status.fixed) {
result.fixed = generateDictFrom(vulnerability.product_status, "fixed");
const result: Vulnerability = {
cve: vulnerability.cve
};
if (vulnerability.product_status) {
if (vulnerability.product_status.known_affected) {
result.known_affected = generateDictFrom(vulnerability.product_status, "known_affected");
Object.assign(extractionResult.relevantProducts, result.known_affected);
}
if (vulnerability.product_status.fixed) {
result.fixed = generateDictFrom(vulnerability.product_status, "fixed");
Object.assign(extractionResult.relevantProducts, result.fixed);
}
if (vulnerability.product_status.under_investigation) {
result.under_investigation = generateDictFrom(
vulnerability.product_status,
"under_investigation"
);
Object.assign(extractionResult.relevantProducts, result.under_investigation);
}
if (vulnerability.product_status.known_not_affected) {
result.known_not_affected = generateDictFrom(
vulnerability.product_status,
"known_not_affected"
);
Object.assign(extractionResult.relevantProducts, result.known_not_affected);
}
if (vulnerability.product_status.recommended) {
result.recommended = generateDictFrom(vulnerability.product_status, "recommended");
Object.assign(extractionResult.relevantProducts, result.recommended);
}
}
if (vulnerability.product_status.under_investigation) {
result.under_investigation = generateDictFrom(
vulnerability.product_status,
"under_investigation"
);
}
if (vulnerability.product_status.known_not_affected) {
result.known_not_affected = generateDictFrom(
vulnerability.product_status,
"known_not_affected"
);
}
if (vulnerability.product_status.recommended) {
result.recommended = generateDictFrom(vulnerability.product_status, "recommended");
}
}
acc.push(result);
return acc;
}, []);
acc.push(result);
return acc;
},
[]
);
extractionResult.vulnerabilities = vulnerabilities;
return extractionResult;
};

export { extractProducts, extractVulnerabilities, generateProductVulnerabilities };
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,8 @@ export type Vulnerability = {
known_not_affected?: StringObject;
recommended?: StringObject;
};

export type VulnerabilitesExtractionResult = {
vulnerabilities: Vulnerability[];
relevantProducts: StringObject;
};

0 comments on commit 2d1a2cb

Please sign in to comment.