diff --git a/artifacts/api-server/src/routes/scans.ts b/artifacts/api-server/src/routes/scans.ts index b5d380e..8279d4e 100644 --- a/artifacts/api-server/src/routes/scans.ts +++ b/artifacts/api-server/src/routes/scans.ts @@ -68,6 +68,7 @@ function serializeFile(f: ScanFile) { size: f.size, hash: f.hash, hasContent: f.content !== null, + content: f.content, }; } diff --git a/artifacts/skillguard/src/pages/scan-report.tsx b/artifacts/skillguard/src/pages/scan-report.tsx index 71e2414..41a2e61 100644 --- a/artifacts/skillguard/src/pages/scan-report.tsx +++ b/artifacts/skillguard/src/pages/scan-report.tsx @@ -17,6 +17,8 @@ import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Button } from "@/components/ui/button"; import { Progress } from "@/components/ui/progress"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog"; +import { ScrollArea } from "@/components/ui/scroll-area"; import { VerdictBadge, SeverityBadge, AxisBadge, CheckpointStatusBadge, CHECKPOINT_STATUS_LABELS, RelationBadge } from "@/components/ui-helpers"; import { formatDate } from "@/lib/format"; import { ShieldQuestion, ShieldAlert, AlertTriangle, Download, FileCode, CheckCircle2, Code, Shield, FileDown, ListChecks, Fingerprint, GitCompare, History, GitCommitVertical, Sparkles, Loader2, Folder, File as FileIcon, Copy, Check, ChevronRight, ChevronDown } from "lucide-react"; @@ -94,6 +96,7 @@ function FilesTree({ files }: { files: ScanReportFile[] }) { const [collapsed, setCollapsed] = useState>(() => new Set()); const rows = useMemo(() => flattenFileTree(tree, collapsed), [tree, collapsed]); const [copiedHash, setCopiedHash] = useState(null); + const [previewFile, setPreviewFile] = useState(null); const toggleFolder = (path: string) => { setCollapsed((prev) => { @@ -115,6 +118,7 @@ function FilesTree({ files }: { files: ScanReportFile[] }) { }; return ( + <>
@@ -159,10 +163,16 @@ function FilesTree({ files }: { files: ScanReportFile[] }) { ) : (
- + @@ -197,6 +207,25 @@ function FilesTree({ files }: { files: ScanReportFile[] }) {
+ !open && setPreviewFile(null)}> + + + {previewFile?.path} + + {previewFile?.language ? `${previewFile.language} · ` : ""} + {previewFile?.size} B + + + {previewFile?.hasContent && previewFile.content != null ? ( + +
{previewFile.content}
+
+ ) : ( +

Keine Vorschau verfügbar (Binärdatei).

+ )} +
+
+ ); } diff --git a/lib/api-client-react/src/generated/api.schemas.ts b/lib/api-client-react/src/generated/api.schemas.ts index 8423c20..f400723 100644 --- a/lib/api-client-react/src/generated/api.schemas.ts +++ b/lib/api-client-react/src/generated/api.schemas.ts @@ -218,6 +218,11 @@ export interface ScanFile { hash: string; /** Whether the text content was stored (false for binary files) */ hasContent: boolean; + /** + * The stored text content of the file, or null for binary files + * @nullable + */ + content?: string | null; } export type FindingAxis = typeof FindingAxis[keyof typeof FindingAxis]; diff --git a/lib/api-spec/openapi.yaml b/lib/api-spec/openapi.yaml index a2a2513..81d7b95 100644 --- a/lib/api-spec/openapi.yaml +++ b/lib/api-spec/openapi.yaml @@ -607,6 +607,9 @@ components: hasContent: type: boolean description: Whether the text content was stored (false for binary files) + content: + type: ["string", "null"] + description: The stored text content of the file, or null for binary files Finding: type: object diff --git a/lib/api-zod/src/generated/api.ts b/lib/api-zod/src/generated/api.ts index 30639d7..f998af5 100644 --- a/lib/api-zod/src/generated/api.ts +++ b/lib/api-zod/src/generated/api.ts @@ -232,7 +232,8 @@ export const GetScanResponse = zod.object({ "language": zod.string().nullish(), "size": zod.number(), "hash": zod.string().describe('SHA-256 hash of the file content'), - "hasContent": zod.boolean().describe('Whether the text content was stored (false for binary files)') + "hasContent": zod.boolean().describe('Whether the text content was stored (false for binary files)'), + "content": zod.string().nullish().describe('The stored text content of the file, or null for binary files') })), "findings": zod.array(zod.object({ "id": zod.number(), @@ -317,7 +318,8 @@ export const GenerateScanDescriptionResponse = zod.object({ "language": zod.string().nullish(), "size": zod.number(), "hash": zod.string().describe('SHA-256 hash of the file content'), - "hasContent": zod.boolean().describe('Whether the text content was stored (false for binary files)') + "hasContent": zod.boolean().describe('Whether the text content was stored (false for binary files)'), + "content": zod.string().nullish().describe('The stored text content of the file, or null for binary files') })), "findings": zod.array(zod.object({ "id": zod.number(), diff --git a/lib/api-zod/src/generated/types/scanFile.ts b/lib/api-zod/src/generated/types/scanFile.ts index feeb72a..379d980 100644 --- a/lib/api-zod/src/generated/types/scanFile.ts +++ b/lib/api-zod/src/generated/types/scanFile.ts @@ -17,4 +17,9 @@ export interface ScanFile { hash: string; /** Whether the text content was stored (false for binary files) */ hasContent: boolean; + /** + * The stored text content of the file, or null for binary files + * @nullable + */ + content?: string | null; }