mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 20:42:02 +00:00
559 lines
14 KiB
TypeScript
559 lines
14 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { compileLighthouseContext } from "./compiler";
|
|
import { lighthouseContextEnvelopeSchema } from "./schema";
|
|
|
|
describe("lighthouseContextEnvelopeSchema", () => {
|
|
describe("when validating an inline page context", () => {
|
|
it("should accept a valid version 1 envelope", () => {
|
|
// Given
|
|
const envelope = {
|
|
schemaVersion: 1,
|
|
transport: "inline",
|
|
items: [
|
|
{
|
|
kind: "page",
|
|
id: "findings",
|
|
source: "automatic",
|
|
scopeKey: "findings:/findings",
|
|
label: "Findings",
|
|
path: "/findings",
|
|
filters: { severity: ["critical"] },
|
|
},
|
|
],
|
|
};
|
|
|
|
// When
|
|
const result = lighthouseContextEnvelopeSchema.safeParse(envelope);
|
|
|
|
// Then
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it("should accept every supported contextual item kind", () => {
|
|
// Given
|
|
const envelope = {
|
|
schemaVersion: 1,
|
|
transport: "inline",
|
|
items: [
|
|
{
|
|
kind: "finding",
|
|
id: "finding-1",
|
|
source: "selection",
|
|
scopeKey: "findings:/findings",
|
|
label: "Selected finding",
|
|
findingId: "finding-1",
|
|
checkId: "check-1",
|
|
severity: "critical",
|
|
},
|
|
{
|
|
kind: "resource",
|
|
id: "resource-1",
|
|
source: "selection",
|
|
scopeKey: "resources:/resources",
|
|
label: "Selected resource",
|
|
resourceId: "resource-1",
|
|
service: "s3",
|
|
failedFindingsCount: 4,
|
|
},
|
|
{
|
|
kind: "compliance",
|
|
id: "cis-1.5",
|
|
source: "automatic",
|
|
scopeKey: "compliance:/compliance",
|
|
label: "CIS 1.5",
|
|
framework: "cis_1.5_aws",
|
|
score: 82,
|
|
totals: { passed: 82, failed: 18, total: 100 },
|
|
},
|
|
{
|
|
kind: "attack_path",
|
|
id: "query-1",
|
|
source: "automatic",
|
|
scopeKey: "attack-paths:/attack-paths/query-builder",
|
|
label: "Attack path query",
|
|
scanId: "scan-1",
|
|
queryId: "query-1",
|
|
parameters: { region: "eu-west-1", limit: 10 },
|
|
nodeCount: 12,
|
|
edgeCount: 11,
|
|
},
|
|
{
|
|
kind: "scan",
|
|
id: "scans-summary",
|
|
source: "automatic",
|
|
scopeKey: "scans:/scans",
|
|
label: "Visible scans",
|
|
total: 25,
|
|
},
|
|
{
|
|
kind: "provider",
|
|
id: "providers-summary",
|
|
source: "automatic",
|
|
scopeKey: "providers:/providers",
|
|
label: "Visible providers",
|
|
total: 7,
|
|
},
|
|
],
|
|
};
|
|
|
|
// When
|
|
const result = lighthouseContextEnvelopeSchema.safeParse(envelope);
|
|
|
|
// Then
|
|
expect(result.success).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("when context exceeds transport limits", () => {
|
|
it("should reject filters containing more than 20 values", () => {
|
|
// Given
|
|
const envelope = {
|
|
schemaVersion: 1,
|
|
transport: "inline",
|
|
items: [
|
|
{
|
|
kind: "page",
|
|
id: "findings",
|
|
source: "automatic",
|
|
scopeKey: "findings:/findings",
|
|
label: "Findings",
|
|
path: "/findings",
|
|
filters: {
|
|
severity: Array.from({ length: 21 }, (_, index) => `${index}`),
|
|
},
|
|
},
|
|
],
|
|
};
|
|
|
|
// When
|
|
const result = lighthouseContextEnvelopeSchema.safeParse(envelope);
|
|
|
|
// Then
|
|
expect(result.success).toBe(false);
|
|
});
|
|
|
|
it("should reject more than eight context items", () => {
|
|
// Given
|
|
const item = {
|
|
kind: "page",
|
|
id: "findings",
|
|
source: "automatic",
|
|
scopeKey: "findings:/findings",
|
|
label: "Findings",
|
|
path: "/findings",
|
|
};
|
|
|
|
// When
|
|
const result = lighthouseContextEnvelopeSchema.safeParse({
|
|
schemaVersion: 1,
|
|
transport: "inline",
|
|
items: Array.from({ length: 9 }, (_, index) => ({
|
|
...item,
|
|
id: `page-${index}`,
|
|
})),
|
|
});
|
|
|
|
// Then
|
|
expect(result.success).toBe(false);
|
|
});
|
|
|
|
it("should reject strings longer than 256 characters", () => {
|
|
// Given / When
|
|
const result = lighthouseContextEnvelopeSchema.safeParse({
|
|
schemaVersion: 1,
|
|
transport: "inline",
|
|
items: [
|
|
{
|
|
kind: "page",
|
|
id: "findings",
|
|
source: "automatic",
|
|
scopeKey: "findings:/findings",
|
|
label: "x".repeat(257),
|
|
path: "/findings",
|
|
},
|
|
],
|
|
});
|
|
|
|
// Then
|
|
expect(result.success).toBe(false);
|
|
});
|
|
|
|
it("should reject unknown item kinds", () => {
|
|
// Given / When
|
|
const result = lighthouseContextEnvelopeSchema.safeParse({
|
|
schemaVersion: 1,
|
|
transport: "inline",
|
|
items: [
|
|
{
|
|
kind: "secret",
|
|
id: "credentials",
|
|
source: "automatic",
|
|
scopeKey: "findings:/findings",
|
|
label: "Credentials",
|
|
},
|
|
],
|
|
});
|
|
|
|
// Then
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("compileLighthouseContext", () => {
|
|
describe("when multiple contributors describe the same entity", () => {
|
|
it("should deduplicate items by kind and id", () => {
|
|
// Given
|
|
const scopeKey = "findings:/findings";
|
|
const items = [
|
|
{
|
|
kind: "page",
|
|
id: "findings",
|
|
source: "automatic",
|
|
scopeKey,
|
|
label: "Findings",
|
|
path: "/findings",
|
|
},
|
|
{
|
|
kind: "finding",
|
|
id: "finding-1",
|
|
source: "selection",
|
|
scopeKey,
|
|
label: "Selected finding",
|
|
findingId: "finding-1",
|
|
severity: "critical",
|
|
},
|
|
{
|
|
kind: "finding",
|
|
id: "finding-1",
|
|
source: "selection",
|
|
scopeKey,
|
|
label: "Duplicate finding",
|
|
findingId: "finding-1",
|
|
severity: "critical",
|
|
},
|
|
];
|
|
|
|
// When
|
|
const context = compileLighthouseContext(items, scopeKey);
|
|
|
|
// Then
|
|
expect(context?.items.map((item) => `${item.kind}:${item.id}`)).toEqual([
|
|
"page:findings",
|
|
"finding:finding-1",
|
|
]);
|
|
});
|
|
|
|
it("should retain the highest-priority duplicate", () => {
|
|
// Given
|
|
const scopeKey = "findings:/findings";
|
|
const items = [
|
|
{
|
|
kind: "page",
|
|
id: "findings",
|
|
source: "automatic",
|
|
scopeKey,
|
|
label: "Findings",
|
|
path: "/findings",
|
|
},
|
|
{
|
|
kind: "finding",
|
|
id: "finding-1",
|
|
source: "selection",
|
|
scopeKey,
|
|
label: "Selected finding",
|
|
findingId: "finding-1",
|
|
},
|
|
{
|
|
kind: "finding",
|
|
id: "finding-1",
|
|
source: "focused",
|
|
scopeKey,
|
|
label: "Focused finding",
|
|
findingId: "finding-1",
|
|
},
|
|
];
|
|
|
|
// When
|
|
const context = compileLighthouseContext(items, scopeKey);
|
|
|
|
// Then
|
|
expect(context?.items[1]).toMatchObject({
|
|
id: "finding-1",
|
|
source: "focused",
|
|
label: "Focused finding",
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("when contributors arrive in render order", () => {
|
|
it("should order page, focused, selection, and summary items deterministically", () => {
|
|
// Given
|
|
const scopeKey = "findings:/findings";
|
|
const items = [
|
|
{
|
|
kind: "finding",
|
|
id: "findings-summary",
|
|
source: "automatic",
|
|
scopeKey,
|
|
label: "Visible findings",
|
|
findingId: "summary",
|
|
total: 42,
|
|
},
|
|
{
|
|
kind: "finding",
|
|
id: "finding-1",
|
|
source: "selection",
|
|
scopeKey,
|
|
label: "Selected finding",
|
|
findingId: "finding-1",
|
|
},
|
|
{
|
|
kind: "finding",
|
|
id: "finding-focused",
|
|
source: "focused",
|
|
scopeKey,
|
|
label: "Focused finding",
|
|
findingId: "finding-focused",
|
|
},
|
|
{
|
|
kind: "page",
|
|
id: "findings",
|
|
source: "automatic",
|
|
scopeKey,
|
|
label: "Findings",
|
|
path: "/findings",
|
|
},
|
|
];
|
|
|
|
// When
|
|
const context = compileLighthouseContext(items, scopeKey);
|
|
|
|
// Then
|
|
expect(context?.items.map((item) => item.id)).toEqual([
|
|
"findings",
|
|
"finding-focused",
|
|
"finding-1",
|
|
"findings-summary",
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("when serialized context exceeds 2 KiB", () => {
|
|
it("should drop lowest-priority items until the context fits", () => {
|
|
// Given
|
|
const scopeKey = "findings:/findings";
|
|
const page = {
|
|
kind: "page",
|
|
id: "findings",
|
|
source: "automatic",
|
|
scopeKey,
|
|
label: "Findings",
|
|
path: "/findings",
|
|
};
|
|
const selection = {
|
|
kind: "finding",
|
|
id: "finding-1",
|
|
source: "selection",
|
|
scopeKey,
|
|
label: "Selected finding",
|
|
findingId: "finding-1",
|
|
};
|
|
const summaries = Array.from({ length: 6 }, (_, index) => ({
|
|
kind: "finding",
|
|
id: `summary-${index}`,
|
|
source: "automatic",
|
|
scopeKey,
|
|
label: `Summary ${index} ${"x".repeat(240)}`,
|
|
findingId: `summary-${index}`,
|
|
checkId: `check-${index}-${"y".repeat(240)}`,
|
|
providerUid: `provider-${index}-${"z".repeat(237)}`,
|
|
total: index,
|
|
}));
|
|
|
|
// When
|
|
const context = compileLighthouseContext(
|
|
[page, selection, ...summaries],
|
|
scopeKey,
|
|
);
|
|
|
|
// Then
|
|
expect(context?.items.map((item) => item.id)).toEqual([
|
|
"findings",
|
|
"finding-1",
|
|
"summary-0",
|
|
]);
|
|
});
|
|
|
|
it("should preserve only the page when selection data is still too large", () => {
|
|
// Given
|
|
const scopeKey = "findings:/findings";
|
|
const page = {
|
|
kind: "page",
|
|
id: "findings",
|
|
source: "automatic",
|
|
scopeKey,
|
|
label: "Findings",
|
|
path: "/findings",
|
|
};
|
|
const selection = {
|
|
kind: "finding",
|
|
id: "finding-1",
|
|
source: "selection",
|
|
scopeKey,
|
|
label: "x".repeat(256),
|
|
findingId: "y".repeat(256),
|
|
checkId: "z".repeat(256),
|
|
severity: "s".repeat(256),
|
|
status: "t".repeat(256),
|
|
providerUid: "p".repeat(256),
|
|
resourceUid: "r".repeat(256),
|
|
region: "g".repeat(256),
|
|
};
|
|
|
|
// When
|
|
const context = compileLighthouseContext([selection, page], scopeKey);
|
|
|
|
// Then
|
|
expect(context?.items.map((item) => item.id)).toEqual(["findings"]);
|
|
});
|
|
});
|
|
|
|
describe("when context exceeds the eight-item limit", () => {
|
|
it("should progressively drop only the lowest-priority items", () => {
|
|
// Given
|
|
const scopeKey = "findings:/findings";
|
|
const page = {
|
|
kind: "page",
|
|
id: "findings",
|
|
source: "automatic",
|
|
scopeKey,
|
|
label: "Findings",
|
|
path: "/findings",
|
|
};
|
|
const focused = {
|
|
kind: "finding",
|
|
id: "focused",
|
|
source: "focused",
|
|
scopeKey,
|
|
label: "Focused finding",
|
|
findingId: "focused",
|
|
};
|
|
const selections = Array.from({ length: 2 }, (_, index) => ({
|
|
kind: "finding",
|
|
id: `selection-${index}`,
|
|
source: "selection",
|
|
scopeKey,
|
|
label: `Selected finding ${index}`,
|
|
findingId: `selection-${index}`,
|
|
}));
|
|
const summaries = Array.from({ length: 6 }, (_, index) => ({
|
|
kind: "finding",
|
|
id: `summary-${index}`,
|
|
source: "automatic",
|
|
scopeKey,
|
|
label: `Summary ${index}`,
|
|
findingId: `summary-${index}`,
|
|
total: index,
|
|
}));
|
|
|
|
// When
|
|
const context = compileLighthouseContext(
|
|
[page, ...summaries, ...selections, focused],
|
|
scopeKey,
|
|
);
|
|
|
|
// Then
|
|
expect(context?.items.map((item) => item.id)).toEqual([
|
|
"findings",
|
|
"focused",
|
|
"selection-0",
|
|
"selection-1",
|
|
"summary-0",
|
|
"summary-1",
|
|
"summary-2",
|
|
"summary-3",
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("when contributors belong to another page", () => {
|
|
it("should ignore stale scoped data", () => {
|
|
// Given / When
|
|
const context = compileLighthouseContext(
|
|
[
|
|
{
|
|
kind: "page",
|
|
id: "resources",
|
|
source: "automatic",
|
|
scopeKey: "resources:/resources",
|
|
label: "Resources",
|
|
path: "/resources",
|
|
},
|
|
{
|
|
kind: "finding",
|
|
id: "finding-1",
|
|
source: "selection",
|
|
scopeKey: "findings:/findings",
|
|
label: "Old finding",
|
|
findingId: "finding-1",
|
|
},
|
|
],
|
|
"resources:/resources",
|
|
);
|
|
|
|
// Then
|
|
expect(context?.items.map((item) => item.id)).toEqual(["resources"]);
|
|
});
|
|
});
|
|
|
|
describe("when current context is invalid", () => {
|
|
it("should return no context so sending remains available", () => {
|
|
// Given / When
|
|
const context = compileLighthouseContext(
|
|
[
|
|
{
|
|
kind: "finding",
|
|
id: "finding-1",
|
|
source: "selection",
|
|
scopeKey: "findings:/findings",
|
|
label: "Invalid finding without findingId",
|
|
},
|
|
],
|
|
"findings:/findings",
|
|
);
|
|
|
|
// Then
|
|
expect(context).toBeUndefined();
|
|
});
|
|
|
|
it("should discard valid items together with an invalid same-scope item", () => {
|
|
// Given / When
|
|
const context = compileLighthouseContext(
|
|
[
|
|
{
|
|
kind: "page",
|
|
id: "findings",
|
|
source: "automatic",
|
|
scopeKey: "findings:/findings",
|
|
label: "Findings",
|
|
path: "/findings",
|
|
},
|
|
{
|
|
kind: "finding",
|
|
id: "finding-1",
|
|
source: "selection",
|
|
scopeKey: "findings:/findings",
|
|
label: "Invalid finding without findingId",
|
|
},
|
|
],
|
|
"findings:/findings",
|
|
);
|
|
|
|
// Then
|
|
expect(context).toBeUndefined();
|
|
});
|
|
});
|
|
});
|