Files
Infrastruktur-Monitoring-Sy…/frontend/e2e/vuln-scopes.spec.ts
Dennis Nemec aba2c69416 Split the vulnerability view into host and container categories
The findings of the Debian host (OS packages and applications found in the
root filesystem) and of the container images in the cluster were mixed in
one flat table. They are now two prominent categories: a card each with its
own severity split and target count, acting as the primary selector, and a
table that adapts to the selection. Host findings show the package source
reported by the scanner (debian, gobinary, node-pkg …) instead of the
target, container findings show the image.

Backend: findings carry the scanner's package source, the list endpoint
takes ?scope=host|container, and the targets endpoint reports each target
with its category and open count.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 19:47:47 +02:00

46 lines
2.4 KiB
TypeScript

import { test, expect, type Page } from '@playwright/test'
async function scan(page: Page) {
await page.goto('/login')
await page.getByLabel('Email').fill('admin@example.com')
await page.getByLabel('Password').fill('admin-password-123')
await page.getByRole('button', { name: 'Sign in' }).click()
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible()
await page.goto('/vulnerabilities')
await page.getByRole('button', { name: 'Scan now' }).click()
await expect(page.getByTestId('scope-host-critical')).not.toHaveText('0', { timeout: 20_000 })
}
test('host and container findings are separated into two categories', async ({ page }) => {
await scan(page)
// both categories are visible with their own numbers
await expect(page.getByTestId('scope-host')).toContainText('OS, packages and applications')
await expect(page.getByTestId('scope-container')).toContainText('images')
await expect(page.getByTestId('scope-container-critical')).not.toHaveText('0')
// host is selected first and shows only host findings, with the package source
await expect(page.getByTestId('scope-host')).toHaveAttribute('aria-pressed', 'true')
await expect(page.getByRole('row', { name: /CVE-2023-45853/ })).toBeVisible()
await expect(page.getByRole('row', { name: /CVE-2024-24790/ })).toHaveCount(0)
await expect(page.getByTestId('findings-scroll')).toContainText('Source')
// switching to containers swaps the table
await page.getByTestId('scope-container').click()
await expect(page.getByTestId('scope-container')).toHaveAttribute('aria-pressed', 'true')
await expect(page.getByRole('row', { name: /CVE-2024-24790/ })).toBeVisible()
await expect(page.getByRole('row', { name: /CVE-2023-45853/ })).toHaveCount(0)
await expect(page.getByTestId('findings-scroll')).toContainText('Image')
await expect(page.getByRole('row', { name: /gitea\/gitea/ }).first()).toBeVisible()
// the filter is labelled per category and only offers targets of that category
const images = await page.getByLabel('Image', { exact: true }).locator('option').allTextContents()
expect(images.some((o) => o.includes('gitea'))).toBe(true)
expect(images.some((o) => o.startsWith('os'))).toBe(false)
await page.getByTestId('scope-host').click()
const hosts = await page.getByLabel('Target', { exact: true }).locator('option').allTextContents()
expect(hosts.some((o) => o.startsWith('os'))).toBe(true)
expect(hosts.some((o) => o.includes('gitea'))).toBe(false)
})