Öffentliche Landingpage mit Erklärung & Prüfregelwerk (Task #29)
Baut die Einstiegsseite (/) zu einer öffentlichen Landingpage um und verschiebt
das bisherige Dashboard auf eine eigene Route (/dashboard).
Änderungen:
- Neues schlankes Public-Layout (components/public-layout.tsx): Kopfbereich mit
SkillGuard-Logo (Link -> /) und CTAs ("Zum Dashboard" -> /dashboard,
"Skill prüfen" -> /pruefen), Inhaltsbereich, Footer mit Impressum/
Haftungsausschluss-Links (aus Task #27 wiederverwendet).
- Neue Landingpage (pages/landing.tsx):
- Hero mit Wertaussage und primären CTAs.
- Abschnitt "Worin liegt das Risiko?" mit 6 verständlichen Problem-Karten
(nicht vertrauenswürdiger Code, versteckte Anweisungen, Prompt-Injektion,
Datenabfluss, Zugriff auf Geheimnisse, unkontrollierte Installation).
- Abschnitt "Das Prüfregelwerk": lädt aktive Regeln live via useListRules,
filtert enabled, rendert zwei getrennte Gruppen (Datenschutz / IT-Sicherheit)
mit Achsen-/Schweregrad-Badge, "Was geprüft wird" (Regelbeschreibung) und
"Warum das ein Risiko ist" (kuratierte Texte je ruleId mit Rückfall auf die
Regelbeschreibung). Lade-/Fehler-/Leerzustände abgedeckt.
- App.tsx: Routing aufgeteilt – "/" nutzt PublicLayout+Landing, alle übrigen
Routen bleiben im AppLayout (Sidebar); Dashboard nun unter /dashboard.
- layout.tsx: Sidebar-Logo verlinkt auf /, Dashboard-Link auf /dashboard mit
angepasster Aktiv-Logik (startsWith("/dashboard")).
Hinweise:
- Keine Backend-/Schema-Änderungen; kuratierte Risikotexte leben im Frontend.
- Vorhandene TS-Fehler in admin/scan-* Seiten stammen aus anderen offenen Tasks
(fehlende generierte API-Member) und sind nicht Teil dieser Änderung.
- Verifiziert via Screenshots (Landing + /dashboard) und tsc (eigene Dateien
fehlerfrei).
Replit-Task-Id: 83cee1ab-a1fc-4106-be3c-52e1cebcc838
This commit is contained in:
parent
9415e184dc
commit
fc4d0d9d28
4 changed files with 345 additions and 18 deletions
|
|
@ -3,8 +3,10 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { Toaster } from "@/components/ui/toaster";
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||||
import { AppLayout } from "@/components/layout";
|
import { AppLayout } from "@/components/layout";
|
||||||
|
import { PublicLayout } from "@/components/public-layout";
|
||||||
import NotFound from "@/pages/not-found";
|
import NotFound from "@/pages/not-found";
|
||||||
|
|
||||||
|
import Landing from "@/pages/landing";
|
||||||
import Dashboard from "@/pages/dashboard";
|
import Dashboard from "@/pages/dashboard";
|
||||||
import ScanForm from "@/pages/scan-form";
|
import ScanForm from "@/pages/scan-form";
|
||||||
import ScanReport from "@/pages/scan-report";
|
import ScanReport from "@/pages/scan-report";
|
||||||
|
|
@ -18,19 +20,28 @@ const queryClient = new QueryClient();
|
||||||
|
|
||||||
function Router() {
|
function Router() {
|
||||||
return (
|
return (
|
||||||
<AppLayout>
|
<Switch>
|
||||||
<Switch>
|
<Route path="/">
|
||||||
<Route path="/" component={Dashboard} />
|
<PublicLayout>
|
||||||
<Route path="/pruefen" component={ScanForm} />
|
<Landing />
|
||||||
<Route path="/berichte/:id" component={ScanReport} />
|
</PublicLayout>
|
||||||
<Route path="/vergleich/:id/:otherId" component={ScanCompare} />
|
</Route>
|
||||||
<Route path="/verlauf" component={ScanHistory} />
|
<Route>
|
||||||
<Route path="/admin" component={Admin} />
|
<AppLayout>
|
||||||
<Route path="/impressum" component={Impressum} />
|
<Switch>
|
||||||
<Route path="/haftungsausschluss" component={Haftungsausschluss} />
|
<Route path="/dashboard" component={Dashboard} />
|
||||||
<Route component={NotFound} />
|
<Route path="/pruefen" component={ScanForm} />
|
||||||
</Switch>
|
<Route path="/berichte/:id" component={ScanReport} />
|
||||||
</AppLayout>
|
<Route path="/vergleich/:id/:otherId" component={ScanCompare} />
|
||||||
|
<Route path="/verlauf" component={ScanHistory} />
|
||||||
|
<Route path="/admin" component={Admin} />
|
||||||
|
<Route path="/impressum" component={Impressum} />
|
||||||
|
<Route path="/haftungsausschluss" component={Haftungsausschluss} />
|
||||||
|
<Route component={NotFound} />
|
||||||
|
</Switch>
|
||||||
|
</AppLayout>
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,11 @@ export function AppLayout({ children }: { children: React.ReactNode }) {
|
||||||
<SidebarProvider>
|
<SidebarProvider>
|
||||||
<div className="flex min-h-screen w-full bg-background text-foreground">
|
<div className="flex min-h-screen w-full bg-background text-foreground">
|
||||||
<Sidebar className="border-r border-sidebar-border bg-sidebar text-sidebar-foreground">
|
<Sidebar className="border-r border-sidebar-border bg-sidebar text-sidebar-foreground">
|
||||||
<SidebarHeader className="p-4 flex flex-row items-center gap-2">
|
<SidebarHeader className="p-4">
|
||||||
<Shield className="w-6 h-6 text-sidebar-primary" />
|
<Link href="/" className="flex flex-row items-center gap-2">
|
||||||
<span className="font-bold text-lg tracking-tight">SkillGuard</span>
|
<Shield className="w-6 h-6 text-sidebar-primary" />
|
||||||
|
<span className="font-bold text-lg tracking-tight">SkillGuard</span>
|
||||||
|
</Link>
|
||||||
</SidebarHeader>
|
</SidebarHeader>
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
<SidebarGroup>
|
<SidebarGroup>
|
||||||
|
|
@ -19,8 +21,8 @@ export function AppLayout({ children }: { children: React.ReactNode }) {
|
||||||
<SidebarGroupContent>
|
<SidebarGroupContent>
|
||||||
<SidebarMenu>
|
<SidebarMenu>
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<SidebarMenuButton asChild isActive={location === "/"}>
|
<SidebarMenuButton asChild isActive={location.startsWith("/dashboard")}>
|
||||||
<Link href="/">
|
<Link href="/dashboard">
|
||||||
<LayoutDashboard className="w-4 h-4 mr-2" />
|
<LayoutDashboard className="w-4 h-4 mr-2" />
|
||||||
<span>Dashboard</span>
|
<span>Dashboard</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
49
artifacts/skillguard/src/components/public-layout.tsx
Normal file
49
artifacts/skillguard/src/components/public-layout.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { Link } from "wouter";
|
||||||
|
import { Shield, Search, LayoutDashboard } from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
export function PublicLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen w-full flex-col bg-background text-foreground">
|
||||||
|
<header className="sticky top-0 z-30 border-b border-border bg-background/80 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||||
|
<div className="mx-auto flex max-w-6xl items-center justify-between gap-4 px-4 py-3 sm:px-6">
|
||||||
|
<Link href="/" className="flex items-center gap-2">
|
||||||
|
<Shield className="h-6 w-6 text-sidebar-primary" />
|
||||||
|
<span className="text-lg font-bold tracking-tight">SkillGuard</span>
|
||||||
|
</Link>
|
||||||
|
<nav className="flex items-center gap-2">
|
||||||
|
<Button asChild variant="ghost" size="sm">
|
||||||
|
<Link href="/dashboard">
|
||||||
|
<LayoutDashboard className="mr-2 h-4 w-4" />
|
||||||
|
<span className="hidden sm:inline">Zum Dashboard</span>
|
||||||
|
<span className="sm:hidden">Dashboard</span>
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
<Button asChild size="sm">
|
||||||
|
<Link href="/pruefen">
|
||||||
|
<Search className="mr-2 h-4 w-4" />
|
||||||
|
Skill prüfen
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main className="flex-1">{children}</main>
|
||||||
|
|
||||||
|
<footer className="border-t border-border">
|
||||||
|
<div className="mx-auto flex max-w-6xl flex-col items-center justify-between gap-2 px-4 py-6 text-xs text-muted-foreground sm:flex-row sm:px-6">
|
||||||
|
<span>© 2026 avameo GmbH</span>
|
||||||
|
<nav className="flex items-center gap-4">
|
||||||
|
<Link href="/impressum" className="transition-colors hover:text-foreground">
|
||||||
|
Impressum
|
||||||
|
</Link>
|
||||||
|
<Link href="/haftungsausschluss" className="transition-colors hover:text-foreground">
|
||||||
|
Haftungsausschluss
|
||||||
|
</Link>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
265
artifacts/skillguard/src/pages/landing.tsx
Normal file
265
artifacts/skillguard/src/pages/landing.tsx
Normal file
|
|
@ -0,0 +1,265 @@
|
||||||
|
import { useListRules, type Rule } from "@workspace/api-client-react";
|
||||||
|
import { Link } from "wouter";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||||
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { AxisBadge, SeverityBadge } from "@/components/ui-helpers";
|
||||||
|
import {
|
||||||
|
Shield,
|
||||||
|
ShieldCheck,
|
||||||
|
Search,
|
||||||
|
LayoutDashboard,
|
||||||
|
EyeOff,
|
||||||
|
Syringe,
|
||||||
|
Upload,
|
||||||
|
KeyRound,
|
||||||
|
FileWarning,
|
||||||
|
Lock,
|
||||||
|
ShieldAlert,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
|
const RISK_EXPLANATIONS: Record<string, string> = {
|
||||||
|
"SEC-REVERSE-SHELL":
|
||||||
|
"Eine Reverse-Shell öffnet Angreifern eine Fernsteuerung Ihres Rechners – sie könnten dann beliebige Befehle ausführen, als säßen sie selbst davor.",
|
||||||
|
"SEC-REMOTE-EXEC":
|
||||||
|
"Wird Code direkt aus dem Netz ausgeführt, weiß niemand vorher, was wirklich läuft – schädlicher Fremdcode kann jederzeit unbemerkt nachgeladen werden.",
|
||||||
|
"SEC-DESTRUCTIVE":
|
||||||
|
"Solche Befehle können in Sekunden ganze Verzeichnisse, Festplatten oder Backups unwiderruflich löschen oder das System lahmlegen.",
|
||||||
|
"SEC-PRIV-ESC":
|
||||||
|
"Mit erhöhten Rechten kann ein Skill Schutzmechanismen aushebeln und tief ins System eingreifen, weit über das hinaus, was es eigentlich bräuchte.",
|
||||||
|
"SEC-PERSISTENCE":
|
||||||
|
"Dauerhafte Hintertüren sorgen dafür, dass Schadcode auch nach einem Neustart aktiv bleibt und sich kaum noch entfernen lässt.",
|
||||||
|
"SEC-OBFUSCATION":
|
||||||
|
"Verschleierter Code versteckt seine wahre Funktion absichtlich – das ist ein typisches Merkmal, um Schadhandlungen vor der Prüfung zu verbergen.",
|
||||||
|
"SEC-SUPPLY-CHAIN":
|
||||||
|
"Pakete aus unkontrollierten Quellen können manipuliert sein und Schadcode einschleusen, noch bevor das Skill überhaupt etwas tut.",
|
||||||
|
"SEC-NETWORK":
|
||||||
|
"Ausgehende Verbindungen sind nicht automatisch bösartig, können aber Daten nach außen tragen oder Befehle empfangen – sie gehören kontrolliert.",
|
||||||
|
"PRIV-SECRET-ACCESS":
|
||||||
|
"Greift ein Skill auf Passwörter, Schlüssel oder Zugangsdaten zu, können Angreifer damit Ihre Konten und Cloud-Dienste übernehmen.",
|
||||||
|
"PRIV-EXFILTRATION":
|
||||||
|
"Werden lokale Daten an fremde Server gesendet, verlassen vertrauliche Informationen unbemerkt Ihren Rechner – besonders gefährlich zusammen mit Zugriff auf Geheimnisse.",
|
||||||
|
"PRIV-PROMPT-INJECTION":
|
||||||
|
"Manipulative Anweisungen bringen den KI-Agenten dazu, Sicherheitsregeln zu ignorieren oder Sie zu täuschen – Sie verlieren die Kontrolle über sein Verhalten.",
|
||||||
|
"PRIV-HIDDEN-INSTRUCTIONS":
|
||||||
|
"Unsichtbare Zeichen oder versteckte Kommentare enthalten Anweisungen, die ein Mensch nie zu sehen bekommt, der KI-Agent aber sehr wohl befolgt.",
|
||||||
|
"PRIV-PII":
|
||||||
|
"Werden personenbezogene Daten erfasst, drohen DSGVO-Verstöße und der Missbrauch sensibler Informationen wie Ausweis-, Bank- oder Gesundheitsdaten.",
|
||||||
|
"PRIV-AGENT-TAMPERING":
|
||||||
|
"Verändert ein Skill den Agenten, dessen Gedächtnis oder andere Skills, kann es Schutzregeln dauerhaft aushebeln und sich selbst tarnen.",
|
||||||
|
"PRIV-OVERREACH":
|
||||||
|
"Wer mehr Rechte verlangt als nötig, schafft unnötige Angriffsfläche – im Schadensfall steht dem Skill dann viel zu viel offen.",
|
||||||
|
"AI-PROMPT-INJECTION":
|
||||||
|
"Subtile Manipulationsversuche umgehen oft die starren Mustererkennungen – die KI-Analyse erkennt auch verdeckte Angriffe auf das Agentenverhalten.",
|
||||||
|
"AI-MALICIOUS-INTENT":
|
||||||
|
"Schädliche Absicht ist nicht immer ein bekanntes Muster – die KI-Analyse bewertet den Sinn des Codes und findet getarnte Funktionen.",
|
||||||
|
"AI-DATA-PRIVACY":
|
||||||
|
"Datenschutzrisiken stecken oft im Kontext, nicht in einzelnen Schlüsselwörtern – die KI-Analyse erkennt möglichen Datenabfluss auch ohne klare Signatur.",
|
||||||
|
};
|
||||||
|
|
||||||
|
const PROBLEM_POINTS = [
|
||||||
|
{
|
||||||
|
icon: Shield,
|
||||||
|
title: "Nicht vertrauenswürdiger Code",
|
||||||
|
text: "Ein fremdes Skill kann beliebige Befehle auf Ihrem Rechner ausführen. Wer es installiert, vertraut blind dem, was darin steckt – oft ohne es je gelesen zu haben.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: EyeOff,
|
||||||
|
title: "Versteckte & unsichtbare Anweisungen",
|
||||||
|
text: "Anweisungen können in unsichtbaren Zeichen oder versteckten Kommentaren stecken. Für Menschen unsichtbar, vom KI-Agenten aber befolgt.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Syringe,
|
||||||
|
title: "Prompt-Injektion",
|
||||||
|
text: "Manipulative Texte bringen den KI-Agenten dazu, frühere Anweisungen zu ignorieren, Sicherheitsregeln zu umgehen oder Sie zu täuschen.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Upload,
|
||||||
|
title: "Datenabfluss",
|
||||||
|
text: "Vertrauliche Daten können unbemerkt an fremde Server gesendet werden – von Dateien über Zwischenergebnisse bis zu ganzen Verzeichnissen.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: KeyRound,
|
||||||
|
title: "Zugriff auf Geheimnisse",
|
||||||
|
text: "Passwörter, API-Schlüssel und Zugangsdaten liegen an bekannten Orten. Ein bösartiges Skill weiß genau, wo es danach suchen muss.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: FileWarning,
|
||||||
|
title: "Unkontrollierte Installation",
|
||||||
|
text: "Wird ein Skill ungeprüft eingebunden, fehlt jede Kontrolle darüber, was es darf und tut – ein erhebliches Sicherheits- und Datenschutzrisiko.",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function riskText(rule: Rule): string {
|
||||||
|
return RISK_EXPLANATIONS[rule.ruleId] ?? rule.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
function RuleCard({ rule }: { rule: Rule }) {
|
||||||
|
return (
|
||||||
|
<Card className="h-full">
|
||||||
|
<CardHeader className="pb-3">
|
||||||
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<AxisBadge axis={rule.axis} className="text-[10px] px-1.5 py-0" />
|
||||||
|
<SeverityBadge severity={rule.severity} className="text-[10px] px-1.5 py-0" />
|
||||||
|
</div>
|
||||||
|
<CardTitle className="pt-1 text-base">{rule.title}</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-3 text-sm">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">Was geprüft wird</p>
|
||||||
|
<p className="leading-relaxed text-foreground/90">{rule.description}</p>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1 rounded-md border border-border bg-muted/40 p-3">
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">Warum das ein Risiko ist</p>
|
||||||
|
<p className="leading-relaxed text-foreground/90">{riskText(rule)}</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RuleGroup({
|
||||||
|
rules,
|
||||||
|
axis,
|
||||||
|
icon: Icon,
|
||||||
|
title,
|
||||||
|
intro,
|
||||||
|
}: {
|
||||||
|
rules: Rule[];
|
||||||
|
axis: "security" | "privacy";
|
||||||
|
icon: typeof Shield;
|
||||||
|
title: string;
|
||||||
|
intro: string;
|
||||||
|
}) {
|
||||||
|
const items = rules.filter((r) => r.axis === axis);
|
||||||
|
if (items.length === 0) return null;
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Icon className="h-6 w-6 text-sidebar-primary" />
|
||||||
|
<h3 className="text-2xl font-bold tracking-tight">{title}</h3>
|
||||||
|
<AxisBadge axis={axis} />
|
||||||
|
</div>
|
||||||
|
<p className="max-w-3xl text-muted-foreground">{intro}</p>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{items.map((rule) => (
|
||||||
|
<RuleCard key={rule.id} rule={rule} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Landing() {
|
||||||
|
const { data, isLoading, error } = useListRules();
|
||||||
|
const activeRules = (data ?? []).filter((r) => r.enabled);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-6xl px-4 py-12 sm:px-6">
|
||||||
|
<section className="flex flex-col items-start gap-6 pb-16">
|
||||||
|
<div className="inline-flex items-center gap-2 rounded-full border border-border bg-muted/50 px-3 py-1 text-xs font-medium text-muted-foreground">
|
||||||
|
<ShieldCheck className="h-3.5 w-3.5 text-sidebar-primary" />
|
||||||
|
Sicherheits- & Datenschutzprüfung für KI-Agent-Skills
|
||||||
|
</div>
|
||||||
|
<h1 className="max-w-3xl text-4xl font-bold leading-tight tracking-tight sm:text-5xl">
|
||||||
|
Prüfen Sie fremde Skills, bevor Sie ihnen vertrauen.
|
||||||
|
</h1>
|
||||||
|
<p className="max-w-2xl text-lg leading-relaxed text-muted-foreground">
|
||||||
|
SkillGuard untersucht öffentliche und fremde KI-Agent-Skills auf versteckte Anweisungen, Prompt-Injektion,
|
||||||
|
Datenabfluss und gefährlichen Code – und erklärt verständlich, wo das Risiko liegt. So entscheiden Sie auf
|
||||||
|
einer fundierten Grundlage, statt blind zu vertrauen.
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
|
<Button asChild size="lg">
|
||||||
|
<Link href="/pruefen">
|
||||||
|
<Search className="mr-2 h-4 w-4" />
|
||||||
|
Skill prüfen
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
<Button asChild size="lg" variant="outline">
|
||||||
|
<Link href="/dashboard">
|
||||||
|
<LayoutDashboard className="mr-2 h-4 w-4" />
|
||||||
|
Zum Dashboard
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="space-y-6 pb-16">
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<ShieldAlert className="h-6 w-6 text-sidebar-primary" />
|
||||||
|
<h2 className="text-3xl font-bold tracking-tight">Worin liegt das Risiko?</h2>
|
||||||
|
</div>
|
||||||
|
<p className="max-w-3xl text-muted-foreground">
|
||||||
|
Ein Skill ist mehr als nur eine Anleitung: Es kann Code ausführen, Daten lesen und das Verhalten Ihres
|
||||||
|
KI-Agenten steuern. Ein unkontrolliert installiertes Skill aus fremder Quelle ist deshalb ein echtes
|
||||||
|
Sicherheits- und Datenschutzrisiko – hier die wichtigsten Gefahren in Alltagssprache.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{PROBLEM_POINTS.map((p) => (
|
||||||
|
<Card key={p.title} className="h-full">
|
||||||
|
<CardHeader className="pb-2">
|
||||||
|
<p.icon className="h-7 w-7 text-sidebar-primary" />
|
||||||
|
<CardTitle className="pt-2 text-lg">{p.title}</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="text-sm leading-relaxed text-muted-foreground">{p.text}</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="space-y-10">
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<h2 className="text-3xl font-bold tracking-tight">Das Prüfregelwerk</h2>
|
||||||
|
<p className="max-w-3xl text-muted-foreground">
|
||||||
|
Jeder geprüfte Skill wird gegen die folgenden Prüfpunkte gehalten – aufgeteilt nach Datenschutz und
|
||||||
|
IT-Sicherheit. Die Liste wird live aus dem System geladen und zeigt nur die aktuell aktiven Prüfpunkte.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{[1, 2, 3, 4, 5, 6].map((i) => (
|
||||||
|
<Skeleton key={i} className="h-56 w-full" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : error ? (
|
||||||
|
<Card>
|
||||||
|
<CardContent className="py-10 text-center text-muted-foreground">
|
||||||
|
Das Prüfregelwerk konnte gerade nicht geladen werden. Bitte versuchen Sie es später erneut.
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
) : activeRules.length === 0 ? (
|
||||||
|
<Card>
|
||||||
|
<CardContent className="py-10 text-center text-muted-foreground">
|
||||||
|
Aktuell sind keine Prüfpunkte aktiviert.
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-12">
|
||||||
|
<RuleGroup
|
||||||
|
rules={activeRules}
|
||||||
|
axis="privacy"
|
||||||
|
icon={Lock}
|
||||||
|
title="Datenschutz"
|
||||||
|
intro="Diese Prüfpunkte schützen Ihre Daten und die Kontrolle über den KI-Agenten: Sie erkennen Datenabfluss, Zugriff auf Geheimnisse, versteckte oder manipulative Anweisungen und den Umgang mit personenbezogenen Daten."
|
||||||
|
/>
|
||||||
|
<RuleGroup
|
||||||
|
rules={activeRules}
|
||||||
|
axis="security"
|
||||||
|
icon={Shield}
|
||||||
|
title="IT-Sicherheit"
|
||||||
|
intro="Diese Prüfpunkte schützen Ihr System vor schädlichem Code: Sie erkennen gefährliche Befehle, Rechteausweitung, Persistenz-Mechanismen, Verschleierung und unsichere Quellen."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue