Skip to content

Commit

Permalink
Display CVEs and fixes in the advisory details page (#2735)
Browse files Browse the repository at this point in the history
* Display fixes in advisory details page

* Fix off-by-one bug in iteration

* Display CVEs, update and add tests

* Update CVE factory

* Make upgradable packages less flaky

The factory was using faker.company.buzzNoun() which I suspect has a
small pool of words to pick from. Using words.noun() makes is less flaky
in my experience.

* Accept suggested changes by Jamie and Alessio

Thanks for taking the time to review my PR

* Fix factories

---------

Co-authored-by: Alessio Biancalana <alessio.biancalana@suse.com>
  • Loading branch information
janvhs and dottorblaster authored Jul 4, 2024
1 parent 2423563 commit a7980e0
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 46 deletions.
68 changes: 49 additions & 19 deletions assets/js/lib/test-utils/factories/advisoryErrata.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,53 @@ import { faker } from '@faker-js/faker';
import { Factory } from 'fishery';
import { advisoryType } from './relevantPatches';

export const advisoryErrataFactory = Factory.define(() => ({
id: faker.number.int({ min: 1, max: 65536 }),
issue_date: faker.date.recent({ days: 30 }),
update_date: faker.date.recent({ days: 30 }),
last_modified_date: faker.date.recent({ days: 30 }),
synopsis: faker.lorem.sentence(),
release: faker.number.int({ min: 1, max: 256 }),
advisory_status: 'stable',
vendor_advisory: faker.lorem.word(),
type: faker.helpers.arrayElement(advisoryType),
product: faker.internet.userName(),
errata_from: faker.lorem.word(),
topic: faker.animal.cat(),
description: faker.lorem.sentence(),
references: faker.lorem.sentence(),
notes: faker.lorem.sentence(),
solution: faker.lorem.sentence(),
reboot_suggested: faker.datatype.boolean(),
restart_suggested: faker.datatype.boolean(),
const fixMapFactory = Factory.define(({ transientParams }) => {
const { length = 1 } = transientParams;

return Object.fromEntries(
new Array(length)
.fill(0)
.map(() => [
faker.number.int({ min: 1, max: 65536 }),
faker.lorem.sentence(),
])
);
});

export const cveFactory = Factory.define(
() =>
`CVE-${faker.number.int({ min: 1991, max: 2024 })}-${faker.number.int({
min: 0,
max: 9999,
})}`
);

export const advisoryErrataFactory = Factory.define(({ params }) => ({
fixes:
params.fixes ||
fixMapFactory.build(
{},
{ transient: { length: faker.number.int({ min: 1, max: 10 }) } }
),
cves: cveFactory.buildList(10),
errata_details: {
id: faker.number.int({ min: 1, max: 65536 }),
issue_date: faker.date.recent({ days: 30 }),
update_date: faker.date.recent({ days: 30 }),
last_modified_date: faker.date.recent({ days: 30 }),
synopsis: faker.lorem.sentence(),
release: faker.number.int({ min: 1, max: 256 }),
advisory_status: 'stable',
vendor_advisory: faker.lorem.word(),
type: faker.helpers.arrayElement(advisoryType),
product: faker.internet.userName(),
errata_from: faker.lorem.word(),
topic: faker.animal.cat(),
description: faker.lorem.sentence(),
references: faker.lorem.sentence(),
notes: faker.lorem.sentence(),
solution: faker.lorem.sentence(),
reboot_suggested: faker.datatype.boolean(),
restart_suggested: faker.datatype.boolean(),
},
}));
2 changes: 1 addition & 1 deletion assets/js/lib/test-utils/factories/upgradablePackage.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const releaseVersionFactory = () =>
export const upgradablePackageFactory = Factory.define(() => ({
from_epoch: faker.date.anytime(),
to_release: releaseVersionFactory(),
name: faker.company.buzzNoun(),
name: faker.word.noun(),
from_release: releaseVersionFactory(),
to_epoch: faker.date.anytime(),
arch: faker.airline.flightNumber(),
Expand Down
32 changes: 23 additions & 9 deletions assets/js/pages/AdvisoryDetails/AdvisoryDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ function EmptyData() {
function AdvisoryDetails({
advisoryName,
errata,
fixes,
cves,
packages,
affectsPackageMaintanaceStack,
}) {
Expand All @@ -25,7 +23,9 @@ function AdvisoryDetails({
type,
description,
reboot_suggested: rebootSuggested,
} = errata;
} = errata.errata_details;

const { fixes, cves } = errata;

return (
<div>
Expand Down Expand Up @@ -76,10 +76,17 @@ function AdvisoryDetails({
<div className="flex flex-col mb-4">
<h2 className="text-xl font-bold mb-2">Fixes</h2>
<div className="bg-white py-4 px-6 shadow shadow-md rounded-lg">
{fixes && fixes.length > 1 ? (
{fixes && Object.keys(fixes).length ? (
<ul>
{fixes.map((fix) => (
<li>{fix}</li>
{Object.entries(fixes).map(([id, fix]) => (
<li key={`bug-${id}`}>
<a
className="text-jungle-green-500 hover:opacity-75"
href={`https://bugzilla.suse.com/show_bug.cgi?id=${id}`}
>
{fix}
</a>
</li>
))}
</ul>
) : (
Expand All @@ -90,10 +97,17 @@ function AdvisoryDetails({
<div className="flex flex-col mb-4">
<h2 className="text-xl font-bold mb-2">CVEs</h2>
<div className="bg-white py-4 px-6 shadow shadow-md rounded-lg">
{cves && cves.length > 1 ? (
{cves && cves.length ? (
<ul>
{cves.map((cve) => (
<li>{cve}</li>
<li key={cve}>
<a
className="text-jungle-green-500 hover:opacity-75"
href={`https://cve.mitre.org/cgi-bin/cvename.cgi?name=${cve}`}
>
{cve}
</a>
</li>
))}
</ul>
) : (
Expand All @@ -104,7 +118,7 @@ function AdvisoryDetails({
<div className="flex flex-col mb-4">
<h2 className="text-xl font-bold mb-2">Affected Packages</h2>
<div className="bg-white py-4 px-6 shadow shadow-md rounded-lg">
{packages && packages.length > 1 ? (
{packages && packages.length ? (
<ul>
{packages.map((pkg) => (
<li>{pkg}</li>
Expand Down
2 changes: 0 additions & 2 deletions assets/js/pages/AdvisoryDetails/AdvisoryDetails.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ Yesterday, I left before the post arrived. Normally, the post just delivers my p
However, the post didn't come by today, and I am starting to wonder, if my Geekos ate my quiche. AITA? 😟`,
reboot_suggested: true,
},
fixes: undefined,
cves: undefined,
packages: undefined,
affectsPackageMaintanaceStack: false,
},
Expand Down
75 changes: 60 additions & 15 deletions assets/js/pages/AdvisoryDetails/AdvisoryDetails.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import '@testing-library/jest-dom';
import { faker } from '@faker-js/faker';

import { renderWithRouter as render } from '@lib/test-utils';
import { advisoryErrataFactory } from '@lib/test-utils/factories';
import { advisoryErrataFactory, cveFactory } from '@lib/test-utils/factories';

import AdvisoryDetails from './AdvisoryDetails';

describe('AdvisoryDetails', () => {
it('displays a message, when the CVE, packages or fixes section is empty', () => {
const errata = advisoryErrataFactory.build();
const errata = advisoryErrataFactory.build({ cves: [], fixes: {} });

render(
<AdvisoryDetails advisoryName={faker.lorem.word()} errata={errata} />
Expand All @@ -27,39 +27,84 @@ describe('AdvisoryDetails', () => {
render(<AdvisoryDetails advisoryName={advisoryName} errata={errata} />);

expect(screen.getByText(advisoryName)).toBeVisible();
expect(screen.getByText(errata.synopsis)).toBeVisible();
expect(screen.getByText(errata.advisory_status)).toBeVisible();
expect(screen.getByText(errata.description)).toBeVisible();
expect(screen.getByText(errata.errata_details.synopsis)).toBeVisible();
expect(
screen.getByText(errata.errata_details.advisory_status)
).toBeVisible();
expect(screen.getByText(errata.errata_details.description)).toBeVisible();
});

it('displays CVEs, packages and fixes', () => {
it('displays packages', () => {
const errata = advisoryErrataFactory.build();
const advisoryName = faker.lorem.word();

const fixes = faker.word.words(2).split(' ');
const packages = faker.word.words(2).split(' ');
const cves = faker.word.words(2).split(' ');

render(
<AdvisoryDetails
advisoryName={advisoryName}
errata={errata}
fixes={fixes}
cves={cves}
packages={packages}
/>
);

fixes.forEach((expectedWord) => {
packages.forEach((expectedWord) => {
expect(screen.getByText(expectedWord)).toBeVisible();
});
});

cves.forEach((expectedWord) => {
expect(screen.getByText(expectedWord)).toBeVisible();
it('displays fixes with a valid link', () => {
const errata = advisoryErrataFactory.build();
const advisoryName = faker.lorem.word();

render(<AdvisoryDetails advisoryName={advisoryName} errata={errata} />);

Object.entries(errata.fixes).forEach(([id, fixText]) => {
const el = screen.getByText(fixText);
expect(el.href.includes(id)).toBe(true);
expect(el).toBeVisible();
});
});

packages.forEach((expectedWord) => {
expect(screen.getByText(expectedWord)).toBeVisible();
it('displays CVEs with a valid link', () => {
const errata = advisoryErrataFactory.build({
cves: cveFactory.buildList(faker.number.int({ min: 2, max: 10 })),
});

const advisoryName = faker.lorem.word();

render(<AdvisoryDetails advisoryName={advisoryName} errata={errata} />);

errata.cves.forEach((cve) => {
const el = screen.getByText(cve);
expect(el.href.includes(cve)).toBe(true);
expect(el).toBeVisible();
});
});

it('displays a single fix with a valid link', () => {
const errata = advisoryErrataFactory.build();
const advisoryName = faker.lorem.word();

render(<AdvisoryDetails advisoryName={advisoryName} errata={errata} />);

Object.entries(errata.fixes).forEach(([id, fixText]) => {
const el = screen.getByText(fixText);
expect(el.href.includes(id)).toBe(true);
expect(el).toBeVisible();
});
});

it('displays a single CVE with a valid link', () => {
const errata = advisoryErrataFactory.build();
const advisoryName = faker.lorem.word();

render(<AdvisoryDetails advisoryName={advisoryName} errata={errata} />);

errata.cves.forEach((cve) => {
const el = screen.getByText(cve);
expect(el.href.includes(cve)).toBe(true);
expect(el).toBeVisible();
});
});
});

0 comments on commit a7980e0

Please sign in to comment.