Files
prowler/ui/lib/lighthouse/context/pages.test.ts

206 lines
6.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { LighthousePageSuggestions } from "@/types/lighthouse-context";
import { LIGHTHOUSE_CONTEXT_LIMIT } from "./constants";
import {
buildLighthousePageContext,
getLighthouseScopeKey,
resolveLighthousePage,
} from "./pages";
describe("resolveLighthousePage", () => {
it.each([
["/", "overview"],
["/findings", "findings"],
["/resources", "resources"],
["/compliance", "compliance"],
["/compliance/cis-1.5-aws", "compliance-detail"],
["/attack-paths/query-builder", "attack-paths"],
["/scans", "scans"],
["/providers", "providers"],
["/alerts", "alerts"],
["/services", "services"],
["/workloads", "workloads"],
["/mutelist", "mutelist"],
])("should resolve %s as %s", (pathname, expectedPageId) => {
// Given / When
const page = resolveLighthousePage(pathname);
// Then
expect(page.id).toBe(expectedPageId);
expectValidSuggestions(page.suggestions);
});
it("should create a labeled fallback for other application pages", () => {
// Given / When
const page = resolveLighthousePage("/integrations/");
// Then
expect(page.id).toBe("other");
expect(page.label).toBe("Integrations");
expectValidSuggestions(page.suggestions);
});
it("should resolve encoded and decoded dynamic paths to the same scope", () => {
expect(getLighthouseScopeKey("/compliance/CSA%20CCM")).toBe(
getLighthouseScopeKey("/compliance/CSA CCM"),
);
});
it("should keep dynamic route scope keys inside the context schema limit", () => {
// Given
const pathname = `/compliance/${"a".repeat(300)}`;
// When
const context = buildLighthousePageContext(pathname, new URLSearchParams());
// Then
expect(context.scopeKey).toBe(getLighthouseScopeKey(pathname));
expect(context.scopeKey.length).toBeLessThanOrEqual(
LIGHTHOUSE_CONTEXT_LIMIT.STRING_LENGTH,
);
});
});
function expectValidSuggestions(suggestions: LighthousePageSuggestions) {
expect(suggestions).toHaveLength(4);
expect(new Set(suggestions.map(({ prompt }) => prompt)).size).toBe(
suggestions.length,
);
for (const suggestion of suggestions) {
expect(suggestion).toEqual(
expect.objectContaining({
label: expect.any(String),
prompt: expect.any(String),
}),
);
expect(suggestion.label.trim()).not.toBe("");
expect(suggestion.prompt.trim()).not.toBe("");
}
}
describe("buildLighthousePageContext", () => {
it("should include only declared search parameters with semantic filter keys", () => {
// Given
const searchParams = new URLSearchParams();
searchParams.append("filter[severity__in]", "critical,high");
searchParams.append("filter[status__in]", "FAIL");
searchParams.append("sort", "-severity");
searchParams.append("email", "security@example.com");
searchParams.append("filter[unknown_future_key]", "secret");
// When
const context = buildLighthousePageContext("/findings/", searchParams);
// Then
expect(context).toEqual({
kind: "page",
id: "findings",
source: "automatic",
scopeKey: "findings:/findings",
label: "Findings",
path: "/findings",
filters: {
severity: ["critical", "high"],
sort: ["-severity"],
status: ["FAIL"],
},
});
});
it("should preserve whitelisted compliance detail identifiers", () => {
const context = buildLighthousePageContext(
"/compliance/cis-aws",
new URLSearchParams({
complianceId: "cis_aws_1.5",
version: "1.5",
scanId: "scan-1",
mode: "per-scan",
"filter[cis_profile_level]": "Level 1",
}),
);
expect(context.filters).toEqual({
cis_profile_level: ["Level 1"],
complianceId: ["cis_aws_1.5"],
mode: ["per-scan"],
scanId: ["scan-1"],
version: ["1.5"],
});
});
it("should preserve the selected scan identifier on the scans page", () => {
const context = buildLighthousePageContext(
"/scans",
new URLSearchParams({ scanId: "scan-1", tab: "completed" }),
);
expect(context.filters).toEqual({
scanId: ["scan-1"],
tab: ["completed"],
});
});
it("should preserve the filter names emitted by the alerts page", () => {
const context = buildLighthousePageContext(
"/alerts",
new URLSearchParams({
"filter[enabled]": "true",
"filter[trigger]": "new_failing_findings",
"filter[search]": "s3",
edit: "alert-1",
}),
);
expect(context.filters).toEqual({
enabled: ["true"],
search: ["s3"],
trigger: ["new_failing_findings"],
});
});
it("should preserve the filter names emitted by list-page controls", () => {
const findings = buildLighthousePageContext(
"/findings",
new URLSearchParams({
"filter[search]": "public bucket",
"filter[scan__in]": "scan-1",
"filter[inserted_at]": "2026-07-01,2026-07-21",
}),
);
const providers = buildLighthousePageContext(
"/providers",
new URLSearchParams({ "filter[connected]": "true" }),
);
expect(findings.filters).toEqual({
inserted_at: ["2026-07-01", "2026-07-21"],
scan: ["scan-1"],
search: ["public bucket"],
});
expect(providers.filters).toEqual({ connected: ["true"] });
});
it("should discard sensitive values from allowed search parameters", () => {
// Given
const searchParams = new URLSearchParams();
searchParams.append("filter[search]", "security@example.com");
searchParams.append("filter[search]", "10.0.0.1");
searchParams.append("filter[search]", "2001:db8::1");
searchParams.append("filter[search]", "Bearer sensitive-value");
searchParams.append("filter[search]", "arn:aws:s3:::example");
searchParams.append("filter[search]", "12:30:00");
searchParams.append("filter[search]", "public bucket");
// When
const context = buildLighthousePageContext("/findings", searchParams);
// Then
expect(context.filters).toEqual({
search: ["arn:aws:s3:::example", "12:30:00", "public bucket"],
});
});
});