Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 52 additions & 5 deletions ui/dashboard/src/hooks/useGroupingConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,62 @@ const useGroupingConfig = (panelName?: string) => {
const { panelsMap } = useDashboardState();
const panel = panelName ? panelsMap[panelName] : undefined;

// Grouping arrives from the URL, so it is untrusted input: shareable links
// get hand-edited, truncated by chat clients, and outlive the mod they were
// built for. Anything unusable is DROPPED so the panel falls back to its
// default - previously each of these replaced the whole dashboard with an
// error boundary, leaving no way back except editing the URL by hand:
//
// ?grouping={not-valid-json -> "Expected property name or '}' in JSON..."
// [{"type":"banana"}] -> "Unknown group type banana"
// {"panel":"nonsense"} -> "n.filter is not a function"
// [{"type":null}] -> "Unknown group type null"
//
// The sibling `where` (filter) parameter already tolerates junk; this brings
// grouping into line with it.
const allGroupings = useMemo(() => {
const rawGroupings = searchParams.get("grouping");
if (rawGroupings) {
let parsedGroups: KeyValuePairs<DisplayGroup[]>;
parsedGroups = JSON.parse(rawGroupings);
return parsedGroups;
} else {
if (!rawGroupings) {
return {};
}

let parsed: any;
try {
parsed = JSON.parse(rawGroupings);
} catch (e) {
return {};
}

if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
return {};
}

const knownTypes = new Set([
"benchmark",
"control",
"control_tag",
"detection",
"dimension",
"reason",
"resource",
"result",
"severity",
"status",
]);

const safe: KeyValuePairs<DisplayGroup[]> = {};
for (const [panelKey, groups] of Object.entries(parsed)) {
if (!Array.isArray(groups)) {
continue;
}
const usable = (groups as any[]).filter(
(g) => !!g && typeof g === "object" && knownTypes.has(g.type),
) as DisplayGroup[];
if (usable.length) {
safe[panelKey] = usable;
}
}
return safe;
}, [searchParams]);

const grouping = useMemo(() => {
Expand Down