diff --git a/libs/cypress/fixtures/index.ts b/libs/cypress/fixtures/index.ts index e40a07e79f..6aeb52aa4f 100644 --- a/libs/cypress/fixtures/index.ts +++ b/libs/cypress/fixtures/index.ts @@ -2,3 +2,4 @@ export * from './fleets'; export * from './devices'; export * from './enrollmentRequests'; export * from './repositories'; +export * from './vulnerabilities'; diff --git a/libs/cypress/fixtures/vulnerabilities/constants.ts b/libs/cypress/fixtures/vulnerabilities/constants.ts new file mode 100644 index 0000000000..1ec3f314bb --- /dev/null +++ b/libs/cypress/fixtures/vulnerabilities/constants.ts @@ -0,0 +1,7 @@ +import { ApiVersion } from '@flightctl/types/alpha'; + +export const VULNERABILITY_API_VERSION = ApiVersion.ApiVersionFlightctlIoV1alpha1; + +export const MOCK_IMAGE_REF = 'quay.io/solar-farms/ai-inverter:1.5.0'; +export const MOCK_IMAGE_DIGEST_A = 'sha256:4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b'; +export const MOCK_IMAGE_DIGEST_B = 'sha256:1f2e3d4c5b6a79808f9e0d1c2b3a4958675849302110fedcba9876543210abcdef'; diff --git a/libs/cypress/fixtures/vulnerabilities/index.ts b/libs/cypress/fixtures/vulnerabilities/index.ts new file mode 100644 index 0000000000..8afb20777a --- /dev/null +++ b/libs/cypress/fixtures/vulnerabilities/index.ts @@ -0,0 +1,3 @@ +export * from './constants'; +export * from './mockVulnerabilityData'; +export * from './paginateMockVulnerabilityList'; diff --git a/libs/cypress/fixtures/vulnerabilities/mockVulnerabilityData.ts b/libs/cypress/fixtures/vulnerabilities/mockVulnerabilityData.ts new file mode 100644 index 0000000000..1546626401 --- /dev/null +++ b/libs/cypress/fixtures/vulnerabilities/mockVulnerabilityData.ts @@ -0,0 +1,354 @@ +import { + Vulnerability, + type VulnerabilityGroup, + type VulnerabilityGroupItem, + type VulnerabilityGroupList, + type VulnerabilityImpact, + type VulnerabilityList, + type VulnerabilitySummaryResponse, +} from '@flightctl/types/alpha'; + +import { MOCK_IMAGE_DIGEST_A, MOCK_IMAGE_DIGEST_B, MOCK_IMAGE_REF, VULNERABILITY_API_VERSION } from './constants'; + +type MockVulnerabilitySeed = { + cveId: string; + severity: Vulnerability.severity; + cvssScore?: number; + advisoryId?: string; + issuer?: string; + link?: string; + description: string; + publishedAt: string; + imageDigest: string; + affectedDevices: number; +}; + +const mockVulnerabilitySeeds: MockVulnerabilitySeed[] = [ + { + cveId: 'CVE-2025-1086', + severity: Vulnerability.severity.CRITICAL, + cvssScore: 9.8, + advisoryId: 'RHSA-2025:1234', + issuer: 'Red Hat', + link: 'https://access.redhat.com/security/cve/CVE-2025-1086', + description: + 'A use-after-free vulnerability in the netfilter subsystem may allow a local attacker to escalate privileges.', + publishedAt: '2025-02-01T12:00:00Z', + imageDigest: MOCK_IMAGE_DIGEST_A, + affectedDevices: 8, + }, + { + cveId: 'CVE-2025-4711', + severity: Vulnerability.severity.CRITICAL, + cvssScore: 9.6, + advisoryId: 'RHSA-2025:2100', + issuer: 'Red Hat', + link: 'https://access.redhat.com/security/cve/CVE-2025-4711', + description: + 'Heap buffer overflow in the kernel USB subsystem allows local privilege escalation on edge gateway hosts.', + publishedAt: '2025-05-18T07:45:00Z', + imageDigest: MOCK_IMAGE_DIGEST_B, + affectedDevices: 12, + }, + { + cveId: 'CVE-2025-6220', + severity: Vulnerability.severity.CRITICAL, + cvssScore: 9.1, + advisoryId: 'RHSA-2025:3188', + issuer: 'Red Hat', + link: 'https://access.redhat.com/security/cve/CVE-2025-6220', + description: + 'Remote code execution in glibc iconv conversion routines affects containerized inverter control services.', + publishedAt: '2025-09-12T16:20:00Z', + imageDigest: MOCK_IMAGE_DIGEST_A, + affectedDevices: 6, + }, + { + cveId: 'CVE-2026-1102', + severity: Vulnerability.severity.CRITICAL, + cvssScore: 9.4, + advisoryId: 'RHSA-2026:0142', + issuer: 'Red Hat', + link: 'https://access.redhat.com/security/cve/CVE-2026-1102', + description: 'Authentication bypass in systemd-resolved enables DNS cache poisoning on disconnected edge nodes.', + publishedAt: '2026-03-04T10:30:00Z', + imageDigest: MOCK_IMAGE_DIGEST_B, + affectedDevices: 9, + }, + { + cveId: 'CVE-2025-21413', + severity: Vulnerability.severity.HIGH, + cvssScore: 8.1, + advisoryId: 'RHSA-2025:5678', + issuer: 'Red Hat', + link: 'https://access.redhat.com/security/cve/CVE-2025-21413', + description: + 'Microsoft Outlook remote code execution vulnerability affecting embedded components in the edge image.', + publishedAt: '2025-03-15T09:30:00Z', + imageDigest: MOCK_IMAGE_DIGEST_A, + affectedDevices: 5, + }, + { + cveId: 'CVE-2025-3847', + severity: Vulnerability.severity.HIGH, + cvssScore: 7.8, + advisoryId: 'RHSA-2025:4021', + issuer: 'Red Hat', + link: 'https://access.redhat.com/security/cve/CVE-2025-3847', + description: 'OpenSSL TLS handshake flaw may allow man-in-the-middle decryption of agent-to-control-plane traffic.', + publishedAt: '2025-07-22T13:10:00Z', + imageDigest: MOCK_IMAGE_DIGEST_B, + affectedDevices: 7, + }, + { + cveId: 'CVE-2025-9103', + severity: Vulnerability.severity.HIGH, + cvssScore: 8.4, + advisoryId: 'RHSA-2025:5510', + issuer: 'Red Hat', + link: 'https://access.redhat.com/security/cve/CVE-2025-9103', + description: + 'SQL injection in embedded telemetry collector permits unauthorized reads from local configuration stores.', + publishedAt: '2025-11-03T08:55:00Z', + imageDigest: MOCK_IMAGE_DIGEST_A, + affectedDevices: 4, + }, + { + cveId: 'CVE-2026-2874', + severity: Vulnerability.severity.HIGH, + cvssScore: 7.5, + issuer: 'NVD', + link: 'https://nvd.nist.gov/vuln/detail/CVE-2026-2874', + description: + 'Path traversal in the firmware update helper allows writing files outside the intended staging directory.', + publishedAt: '2026-06-19T15:40:00Z', + imageDigest: MOCK_IMAGE_DIGEST_B, + affectedDevices: 3, + }, + { + cveId: 'CVE-2025-3094', + severity: Vulnerability.severity.MEDIUM, + cvssScore: 5.5, + issuer: 'NVD', + link: 'https://nvd.nist.gov/vuln/detail/CVE-2025-3094', + description: 'Malicious code was discovered in upstream xz-utils packages bundled in the container image.', + publishedAt: '2025-03-29T18:45:00Z', + imageDigest: MOCK_IMAGE_DIGEST_B, + affectedDevices: 3, + }, + { + cveId: 'CVE-2025-4412', + severity: Vulnerability.severity.MEDIUM, + cvssScore: 6.2, + advisoryId: 'RHSA-2025:2890', + issuer: 'Red Hat', + link: 'https://access.redhat.com/security/cve/CVE-2025-4412', + description: 'Denial of service via excessive memory allocation in the journald log forwarding component.', + publishedAt: '2025-06-08T11:25:00Z', + imageDigest: MOCK_IMAGE_DIGEST_A, + affectedDevices: 6, + }, + { + cveId: 'CVE-2025-7731', + severity: Vulnerability.severity.MEDIUM, + cvssScore: 5.9, + issuer: 'NVD', + link: 'https://nvd.nist.gov/vuln/detail/CVE-2025-7731', + description: 'Cross-site scripting in the local diagnostics web UI when rendering unsanitized device label values.', + publishedAt: '2025-10-14T09:00:00Z', + imageDigest: MOCK_IMAGE_DIGEST_B, + affectedDevices: 2, + }, + { + cveId: 'CVE-2026-0538', + severity: Vulnerability.severity.MEDIUM, + cvssScore: 6.5, + advisoryId: 'RHSA-2026:0298', + issuer: 'Red Hat', + link: 'https://access.redhat.com/security/cve/CVE-2026-0538', + description: 'Race condition in podman storage driver may corrupt image layers during concurrent pull operations.', + publishedAt: '2026-02-27T17:15:00Z', + imageDigest: MOCK_IMAGE_DIGEST_A, + affectedDevices: 5, + }, + { + cveId: 'CVE-2025-48795', + severity: Vulnerability.severity.LOW, + cvssScore: 3.7, + issuer: 'NVD', + link: 'https://nvd.nist.gov/vuln/detail/CVE-2025-48795', + description: + 'The Terrapin attack breaks SSH channel integrity by truncating extension negotiation in legacy clients.', + publishedAt: '2025-01-18T11:20:00Z', + imageDigest: MOCK_IMAGE_DIGEST_B, + affectedDevices: 2, + }, + { + cveId: 'CVE-2025-6014', + severity: Vulnerability.severity.LOW, + cvssScore: 2.8, + issuer: 'NVD', + link: 'https://nvd.nist.gov/vuln/detail/CVE-2025-6014', + description: 'Information disclosure via verbose error messages in the local metrics exporter HTTP endpoint.', + publishedAt: '2025-04-25T14:50:00Z', + imageDigest: MOCK_IMAGE_DIGEST_A, + affectedDevices: 4, + }, + { + cveId: 'CVE-2025-8450', + severity: Vulnerability.severity.LOW, + cvssScore: 3.1, + advisoryId: 'RHSA-2025:6122', + issuer: 'Red Hat', + link: 'https://access.redhat.com/security/cve/CVE-2025-8450', + description: 'Timing side channel in password comparison routine for the edge enrollment bootstrap utility.', + publishedAt: '2025-08-30T06:35:00Z', + imageDigest: MOCK_IMAGE_DIGEST_B, + affectedDevices: 1, + }, + { + cveId: 'CVE-2026-4190', + severity: Vulnerability.severity.LOW, + cvssScore: 2.5, + issuer: 'NVD', + link: 'https://nvd.nist.gov/vuln/detail/CVE-2026-4190', + description: + 'Log injection in the device heartbeat reporter allows forging benign status entries in local log files.', + publishedAt: '2026-07-11T12:05:00Z', + imageDigest: MOCK_IMAGE_DIGEST_A, + affectedDevices: 2, + }, + { + cveId: 'CVE-2025-0005', + severity: Vulnerability.severity.NONE, + cvssScore: 0, + issuer: 'NVD', + link: 'https://nvd.nist.gov/vuln/detail/CVE-2025-0005', + description: 'Informational finding with no exploitable impact on deployed edge workloads.', + publishedAt: '2025-01-10T08:00:00Z', + imageDigest: MOCK_IMAGE_DIGEST_A, + affectedDevices: 1, + }, + { + cveId: 'CVE-2025-9999', + severity: Vulnerability.severity.UNKNOWN, + issuer: 'NVD', + link: 'https://nvd.nist.gov/vuln/detail/CVE-2025-9999', + description: 'Vendor advisory pending; severity has not been scored yet.', + publishedAt: '2025-04-02T14:15:00Z', + imageDigest: MOCK_IMAGE_DIGEST_B, + affectedDevices: 1, + }, +]; + +const toGroupItemSeverity = (severity: Vulnerability.severity): VulnerabilityGroupItem.severity => severity; + +const buildGroupFinding = (seed: MockVulnerabilitySeed): VulnerabilityGroupItem => ({ + imageDigest: seed.imageDigest, + imageRefs: [MOCK_IMAGE_REF], + severity: toGroupItemSeverity(seed.severity), + cvssScore: seed.cvssScore, + advisoryId: seed.advisoryId, + issuer: seed.issuer, + link: seed.link, + description: seed.description, + publishedAt: seed.publishedAt, + affectedDevices: seed.affectedDevices, + firstSeenAt: '2025-05-01T10:00:00Z', +}); + +const buildVulnerabilityGroup = (seed: MockVulnerabilitySeed): VulnerabilityGroup => ({ + apiVersion: VULNERABILITY_API_VERSION, + kind: 'VulnerabilityGroup', + cveId: seed.cveId, + severity: seed.severity, + maxCvssScore: seed.cvssScore, + maxPublishedAt: seed.publishedAt, + affectedDevices: seed.affectedDevices, + findings: [buildGroupFinding(seed)], +}); + +const buildDeviceVulnerability = (seed: MockVulnerabilitySeed): Vulnerability => ({ + apiVersion: VULNERABILITY_API_VERSION, + kind: 'Vulnerability', + cveId: seed.cveId, + advisoryId: seed.advisoryId, + issuer: seed.issuer, + link: seed.link, + severity: seed.severity, + cvssScore: seed.cvssScore, + description: seed.description, + publishedAt: seed.publishedAt, + image: MOCK_IMAGE_REF, + imageDigest: seed.imageDigest, + affectedDevices: 1, +}); + +export const mockVulnerabilityGroups: VulnerabilityGroup[] = mockVulnerabilitySeeds.map(buildVulnerabilityGroup); + +export const mockDeviceVulnerabilities: Vulnerability[] = mockVulnerabilitySeeds.map(buildDeviceVulnerability); + +export const mockVulnerabilityGroupList: VulnerabilityGroupList = { + apiVersion: VULNERABILITY_API_VERSION, + kind: 'VulnerabilityGroupList', + metadata: { + remainingItemCount: 0, + }, + items: mockVulnerabilityGroups, +}; + +export const mockDeviceVulnerabilityList: VulnerabilityList = { + apiVersion: VULNERABILITY_API_VERSION, + kind: 'VulnerabilityList', + metadata: { + remainingItemCount: 0, + }, + items: mockDeviceVulnerabilities, +}; + +const countSeedsBySeverity = (severity: Vulnerability.severity): number => + mockVulnerabilitySeeds.filter((seed) => seed.severity === severity).length; + +export const mockVulnerabilitySummary: VulnerabilitySummaryResponse = { + apiVersion: VULNERABILITY_API_VERSION, + kind: 'VulnerabilitySummary', + cvesBySeverity: { + total: mockVulnerabilitySeeds.length, + critical: countSeedsBySeverity(Vulnerability.severity.CRITICAL), + high: countSeedsBySeverity(Vulnerability.severity.HIGH), + medium: countSeedsBySeverity(Vulnerability.severity.MEDIUM), + low: countSeedsBySeverity(Vulnerability.severity.LOW), + none: countSeedsBySeverity(Vulnerability.severity.NONE), + unknown: countSeedsBySeverity(Vulnerability.severity.UNKNOWN), + }, +}; + +const buildVulnerabilityImpact = (seed: MockVulnerabilitySeed): VulnerabilityImpact => ({ + apiVersion: VULNERABILITY_API_VERSION, + kind: 'VulnerabilityImpact', + metadata: {}, + cveId: seed.cveId, + issuer: seed.issuer, + link: seed.link, + severity: seed.severity, + maxCvssScore: seed.cvssScore, + maxPublishedAt: seed.publishedAt, + items: [ + { + fleetName: 'eu-east-prod-001', + fleetless: false, + affectedDevices: seed.affectedDevices, + findings: [buildGroupFinding(seed)], + }, + { + fleetName: '', + fleetless: true, + affectedDevices: 1, + findings: [buildGroupFinding({ ...seed, affectedDevices: 1, imageDigest: MOCK_IMAGE_DIGEST_B })], + }, + ], +}); + +export const mockVulnerabilityImpactByCve: Record = Object.fromEntries( + mockVulnerabilitySeeds.map((seed) => [seed.cveId, buildVulnerabilityImpact(seed)]), +); diff --git a/libs/cypress/fixtures/vulnerabilities/paginateMockVulnerabilityList.ts b/libs/cypress/fixtures/vulnerabilities/paginateMockVulnerabilityList.ts new file mode 100644 index 0000000000..cf04ec5347 --- /dev/null +++ b/libs/cypress/fixtures/vulnerabilities/paginateMockVulnerabilityList.ts @@ -0,0 +1,142 @@ +import { + Vulnerability, + type VulnerabilityGroup, + type VulnerabilityGroupList, + type VulnerabilityList, +} from '@flightctl/types/alpha'; + +import { VULNERABILITY_API_VERSION } from './constants'; +import { mockDeviceVulnerabilities, mockVulnerabilityGroups } from './mockVulnerabilityData'; + +const DEFAULT_PAGE_SIZE = 15; + +const SEVERITY_ORDER: Vulnerability.severity[] = [ + Vulnerability.severity.CRITICAL, + Vulnerability.severity.HIGH, + Vulnerability.severity.MEDIUM, + Vulnerability.severity.LOW, + Vulnerability.severity.NONE, + Vulnerability.severity.UNKNOWN, +]; + +type MockVulnerabilityItem = VulnerabilityGroup | (typeof mockDeviceVulnerabilities)[number]; + +const getSeverityRank = (severity: Vulnerability.severity): number => SEVERITY_ORDER.indexOf(severity); + +const parseListQuery = (endpointWithQuery: string) => { + const queryIndex = endpointWithQuery.indexOf('?'); + const searchParams = new URLSearchParams(queryIndex >= 0 ? endpointWithQuery.slice(queryIndex + 1) : ''); + + return { + limit: Number(searchParams.get('limit')) || DEFAULT_PAGE_SIZE, + continue: searchParams.get('continue') || '', + sortBy: searchParams.get('sortBy') || 'severity', + order: searchParams.get('order') || 'desc', + fieldSelector: searchParams.get('fieldSelector') || '', + }; +}; + +const parseContinueOffset = (continueToken: string): number => { + if (!continueToken) { + return 0; + } + + const offset = Number(continueToken); + return Number.isFinite(offset) && offset >= 0 ? offset : 0; +}; + +const filterItems = (items: T[], fieldSelector: string): T[] => { + if (!fieldSelector) { + return items; + } + + let filtered = items; + const selectors = fieldSelector.split(',').map((selector) => selector.trim()); + + for (const selector of selectors) { + if (selector.startsWith('cveId contains ')) { + const cveIdSearch = selector.slice('cveId contains '.length).toUpperCase(); + filtered = filtered.filter((item) => item.cveId.toUpperCase().includes(cveIdSearch)); + continue; + } + + if (selector.startsWith('severity in (')) { + const severities = selector + .slice('severity in ('.length, -1) + .split(',') + .map((severity) => severity.trim()) as Vulnerability.severity[]; + filtered = filtered.filter((item) => severities.includes(item.severity)); + continue; + } + + if (selector.startsWith('severity=')) { + const severity = selector.slice('severity='.length) as Vulnerability.severity; + filtered = filtered.filter((item) => item.severity === severity); + } + } + + return filtered; +}; + +const sortItems = (items: T[], sortBy: string, order: string): T[] => { + const sorted = [...items]; + const direction = order === 'asc' ? 1 : -1; + + sorted.sort((left, right) => { + if (sortBy === 'cveId') { + return direction * left.cveId.localeCompare(right.cveId); + } + + const rankDiff = getSeverityRank(left.severity) - getSeverityRank(right.severity); + if (rankDiff !== 0) { + // Lower rank index = higher severity, so desc/asc direction is inverted vs cveId sorting. + const severityDirection = order === 'asc' ? -1 : 1; + return severityDirection * rankDiff; + } + + return left.cveId.localeCompare(right.cveId); + }); + + return sorted; +}; + +const buildPaginatedListResponse = ( + allItems: T[], + endpointWithQuery: string, + listKind: 'VulnerabilityGroupList' | 'VulnerabilityList', +): VulnerabilityGroupList | VulnerabilityList => { + const { limit, continue: continueToken, sortBy, order, fieldSelector } = parseListQuery(endpointWithQuery); + const offset = parseContinueOffset(continueToken); + + const filtered = filterItems(allItems, fieldSelector); + const sorted = sortItems(filtered, sortBy, order); + const pageItems = sorted.slice(offset, offset + limit); + const remaining = sorted.length - offset - pageItems.length; + const nextContinue = remaining > 0 ? String(offset + pageItems.length) : undefined; + + return { + apiVersion: VULNERABILITY_API_VERSION, + kind: listKind, + metadata: { + ...(nextContinue ? { continue: nextContinue } : {}), + ...(remaining > 0 ? { remainingItemCount: remaining } : {}), + }, + items: pageItems, + } as VulnerabilityGroupList | VulnerabilityList; +}; + +const toEndpointWithQuery = (urlOrEndpoint: string): string => { + const vulnerabilitiesIndex = urlOrEndpoint.indexOf('/vulnerabilities'); + return vulnerabilitiesIndex >= 0 ? urlOrEndpoint.slice(vulnerabilitiesIndex) : urlOrEndpoint; +}; + +export const paginateMockVulnerabilityList = (urlOrEndpoint: string): VulnerabilityGroupList | VulnerabilityList => { + const endpointWithQuery = toEndpointWithQuery(urlOrEndpoint); + const baseEndpoint = endpointWithQuery.split('?')[0]; + + if (baseEndpoint.startsWith('vulnerabilities/devices/')) { + return buildPaginatedListResponse(mockDeviceVulnerabilities, endpointWithQuery, 'VulnerabilityList'); + } + + return buildPaginatedListResponse(mockVulnerabilityGroups, endpointWithQuery, 'VulnerabilityGroupList'); +}; diff --git a/libs/cypress/support/interceptors.ts b/libs/cypress/support/interceptors.ts index a99fd9beee..423b8f8674 100644 --- a/libs/cypress/support/interceptors.ts +++ b/libs/cypress/support/interceptors.ts @@ -5,10 +5,12 @@ import { loadInterceptors as loadDeviceInterceptors } from './interceptors/devic import { loadInterceptors as loadRepositoryInterceptors } from './interceptors/repositories'; import { loadInterceptors as loadAuthInterceptors } from './interceptors/auth'; import { loadInterceptors as loadGeneralInterceptors } from './interceptors/general'; +import { loadInterceptors as loadVulnerabilityInterceptors } from './interceptors/vulnerabilities'; const loadApiInterceptors = () => { loadAuthInterceptors(); loadGeneralInterceptors(); + loadVulnerabilityInterceptors(); loadResourceSyncsInterceptors(); loadFleetInterceptors(); loadDeviceInterceptors(); diff --git a/libs/cypress/support/interceptors/vulnerabilities.ts b/libs/cypress/support/interceptors/vulnerabilities.ts new file mode 100644 index 0000000000..be16f8e8d8 --- /dev/null +++ b/libs/cypress/support/interceptors/vulnerabilities.ts @@ -0,0 +1,56 @@ +import { + mockVulnerabilityImpactByCve, + mockVulnerabilitySummary, + paginateMockVulnerabilityList, +} from '../../fixtures/vulnerabilities'; + +const API_BASE_PATH = '/api/flightctl/api/v1'; + +const vulnerabilityListMatcher = new RegExp(`^${API_BASE_PATH}/vulnerabilities(\\?.*)?$`); +const vulnerabilitySummaryMatcher = new RegExp(`^${API_BASE_PATH}/vulnerabilities/summary(\\?.*)?$`); +const deviceVulnerabilitiesMatcher = new RegExp(`^${API_BASE_PATH}/vulnerabilities/devices/([^/?]+)(\\?.*)?$`); +const fleetVulnerabilitiesMatcher = new RegExp(`^${API_BASE_PATH}/vulnerabilities/fleets/([^/?]+)(\\?.*)?$`); +const vulnerabilityImpactMatcher = new RegExp(`^${API_BASE_PATH}/vulnerabilities/cves/([^/?]+)/impact(\\?.*)?$`); + +const extractCveId = (url: string): string | undefined => { + const match = url.match(/\/vulnerabilities\/cves\/([^/?]+)\/impact/); + return match?.[1] ? decodeURIComponent(match[1]) : undefined; +}; + +const loadInterceptors = () => { + cy.intercept('GET', vulnerabilitySummaryMatcher, { + body: mockVulnerabilitySummary, + }).as('vulnerability-summary'); + + cy.intercept('GET', vulnerabilityListMatcher, (req) => { + req.reply({ + body: paginateMockVulnerabilityList(req.url), + }); + }).as('vulnerabilities'); + + cy.intercept('GET', deviceVulnerabilitiesMatcher, (req) => { + req.reply({ + body: paginateMockVulnerabilityList(req.url), + }); + }).as('device-vulnerabilities'); + + cy.intercept('GET', fleetVulnerabilitiesMatcher, (req) => { + req.reply({ + body: paginateMockVulnerabilityList(req.url), + }); + }).as('fleet-vulnerabilities'); + + cy.intercept('GET', vulnerabilityImpactMatcher, (req) => { + const cveId = extractCveId(req.url); + const impact = cveId ? mockVulnerabilityImpactByCve[cveId] : undefined; + + if (impact) { + req.reply({ body: impact }); + return; + } + + req.reply({ statusCode: 404 }); + }).as('vulnerability-impact'); +}; + +export { loadInterceptors }; diff --git a/libs/cypress/tsconfig.json b/libs/cypress/tsconfig.json index 3539c38385..1650eed077 100644 --- a/libs/cypress/tsconfig.json +++ b/libs/cypress/tsconfig.json @@ -10,6 +10,7 @@ "types": ["cypress"], "paths": { "@flightctl/types": ["../types"], + "@flightctl/types/alpha": ["../types/alpha"], }, }, }