Files
prowler/ui/lib/tours/attack-paths.tour.ts
Alan Buscaglia 49309b43d3 feat(ui): UI onboarding system (#11430)
Co-authored-by: Pablo F.G <pablo.fernandez@prowler.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: alejandrobailo <alejandrobailo94@gmail.com>
Co-authored-by: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com>
2026-06-15 13:53:48 +02:00

99 lines
3.3 KiB
TypeScript

import type { AttackPathQuery, AttackPathScan } from "@/types/attack-paths";
import { ATTACK_PATH_QUERY_IDS } from "@/types/attack-paths";
import {
defineTour,
TOUR_STEP_ALIGNMENTS,
TOUR_STEP_SIDES,
} from "./tour-types";
export const attackPathsTour = defineTour({
id: "attack-paths",
version: 1,
coversFiles: [
"ui/app/(prowler)/attack-paths/**",
"ui/components/attack-paths/**",
],
steps: [
{
title: "Welcome to Attack Paths",
description:
"Attack Paths visualizes how a compromise in one resource could spread through your cloud. It's currently available for AWS accounts only.",
},
{
target: "intro",
side: TOUR_STEP_SIDES.BOTTOM,
align: TOUR_STEP_ALIGNMENTS.START,
title: "Start with a scan",
description:
"Attack Paths analyses are generated from your existing AWS scans. Each scan is a point-in-time snapshot of one account.",
},
{
target: "scan-list",
side: TOUR_STEP_SIDES.TOP,
align: TOUR_STEP_ALIGNMENTS.START,
title: "Pick a scan",
description:
"Each row is a scan. Click the radio button on the left to select one.",
},
{
target: "query-selector",
side: TOUR_STEP_SIDES.BOTTOM,
align: TOUR_STEP_ALIGNMENTS.START,
title: "Choose a query",
description:
"Predefined queries cover common risk patterns (privilege escalation, public exposure, lateral movement). You can also write your own query.",
},
{
target: "execute-button",
side: TOUR_STEP_SIDES.TOP,
align: TOUR_STEP_ALIGNMENTS.END,
// No Next button: clicking the highlighted Execute Query button runs the query
// and advances the tour (see handleExecuteQuery in attack-paths-page).
autoAdvance: true,
title: "Run it whenever you're ready",
description:
"Click Execute Query to see the graph with the possible attack paths.",
},
{
title: "You're all set",
description:
"Explore the attack paths and dig into anything that looks risky.",
},
],
});
export type AttackPathsTourTarget = NonNullable<
(typeof attackPathsTour.steps)[number]["target"]
>;
// Demo-pick logic lives here so page code never decides what to auto-run.
// Preferred demo query: well-known and usually returns findings.
const PREFERRED_DEMO_QUERY_ID = "aws-iam-statements-allow-all-actions";
const isReadyScan = (scan: AttackPathScan): boolean =>
scan.attributes.graph_data_ready;
// Prefer a ready AWS scan; fall back to any ready scan for non-AWS tenants.
export function pickDemoScan(
scans: readonly AttackPathScan[],
): AttackPathScan | undefined {
const preferredAws = scans.find(
(scan) => isReadyScan(scan) && scan.attributes.provider_type === "aws",
);
return preferredAws ?? scans.find(isReadyScan);
}
// Runnable = no required params and not Custom (needs manual Cypher).
export function pickDemoQuery(
queries: readonly AttackPathQuery[],
): AttackPathQuery | undefined {
const isRunnable = (query: AttackPathQuery): boolean =>
query.id !== ATTACK_PATH_QUERY_IDS.CUSTOM &&
query.attributes.parameters.length === 0;
const preferredIam = queries.find(
(query) => query.id === PREFERRED_DEMO_QUERY_ID && isRunnable(query),
);
return preferredIam ?? queries.find(isRunnable);
}