SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
import { Router, type IRouter } from "express";
|
|
|
|
|
import { db } from "@workspace/db";
|
|
|
|
|
import {
|
|
|
|
|
scansTable,
|
|
|
|
|
scanFilesTable,
|
|
|
|
|
findingsTable,
|
|
|
|
|
type Scan,
|
|
|
|
|
type ScanFile,
|
|
|
|
|
type Finding,
|
|
|
|
|
} from "@workspace/db";
|
|
|
|
|
import { eq, desc } from "drizzle-orm";
|
|
|
|
|
import {
|
|
|
|
|
ListScansResponse,
|
|
|
|
|
CreateScanBody,
|
|
|
|
|
GetScanParams,
|
|
|
|
|
GetScanResponse,
|
|
|
|
|
DeleteScanParams,
|
|
|
|
|
} from "@workspace/api-zod";
|
|
|
|
|
import {
|
|
|
|
|
parseZip,
|
|
|
|
|
parseSingleFile,
|
|
|
|
|
parseText,
|
|
|
|
|
deriveScanName,
|
|
|
|
|
} from "../lib/skillParser";
|
2026-06-10 18:53:17 +00:00
|
|
|
import { analyzeSkill, type EngineResult } from "../lib/scanEngine";
|
|
|
|
|
import { STATIC_RULES, AI_RULES, type ParsedFile } from "../lib/ruleCatalog";
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
import { logger } from "../lib/logger";
|
|
|
|
|
|
|
|
|
|
const router: IRouter = Router();
|
|
|
|
|
|
2026-06-10 18:53:17 +00:00
|
|
|
type CreateScanInput = ReturnType<typeof CreateScanBody.parse>;
|
|
|
|
|
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
export function serializeScan(scan: Scan) {
|
|
|
|
|
return {
|
|
|
|
|
id: scan.id,
|
|
|
|
|
name: scan.name,
|
|
|
|
|
source: scan.source,
|
|
|
|
|
status: scan.status,
|
|
|
|
|
verdict: scan.verdict,
|
|
|
|
|
riskScore: scan.riskScore,
|
|
|
|
|
fileCount: scan.fileCount,
|
|
|
|
|
aiUsed: scan.aiUsed,
|
|
|
|
|
aiError: scan.aiError,
|
|
|
|
|
findingCounts: scan.findingCounts,
|
|
|
|
|
createdAt: scan.createdAt.toISOString(),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function serializeFile(f: ScanFile) {
|
|
|
|
|
return {
|
|
|
|
|
path: f.path,
|
|
|
|
|
kind: f.kind,
|
|
|
|
|
language: f.language,
|
|
|
|
|
size: f.size,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function serializeFinding(f: Finding) {
|
|
|
|
|
return {
|
|
|
|
|
id: f.id,
|
|
|
|
|
ruleId: f.ruleId,
|
|
|
|
|
axis: f.axis,
|
|
|
|
|
severity: f.severity,
|
|
|
|
|
title: f.title,
|
|
|
|
|
description: f.description,
|
|
|
|
|
remediation: f.remediation,
|
|
|
|
|
file: f.file,
|
|
|
|
|
line: f.line,
|
|
|
|
|
snippet: f.snippet,
|
|
|
|
|
detectedBy: f.detectedBy,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 18:53:17 +00:00
|
|
|
function serializeScanDetail(
|
|
|
|
|
scan: Scan,
|
|
|
|
|
files: ScanFile[],
|
|
|
|
|
findings: Finding[],
|
|
|
|
|
) {
|
|
|
|
|
return {
|
|
|
|
|
...serializeScan(scan),
|
|
|
|
|
checkpoints: scan.checkpoints ?? [],
|
|
|
|
|
files: files.map(serializeFile),
|
|
|
|
|
findings: [...findings].sort((a, b) => a.id - b.id).map(serializeFinding),
|
|
|
|
|
};
|
|
|
|
|
}
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
|
2026-06-10 18:53:17 +00:00
|
|
|
type ParseResult =
|
|
|
|
|
| { ok: true; files: ParsedFile[] }
|
|
|
|
|
| { ok: false; status: number; message: string };
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
|
2026-06-10 18:53:17 +00:00
|
|
|
function parseScanInput(input: CreateScanInput): ParseResult {
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
try {
|
2026-06-10 18:53:17 +00:00
|
|
|
let files: ParsedFile[];
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
if (input.source === "zip") {
|
|
|
|
|
if (!input.contentBase64)
|
2026-06-10 18:53:17 +00:00
|
|
|
return { ok: false, status: 400, message: "ZIP-Inhalt fehlt." };
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
files = parseZip(Buffer.from(input.contentBase64, "base64"));
|
|
|
|
|
} else if (input.source === "file") {
|
|
|
|
|
if (!input.contentBase64)
|
2026-06-10 18:53:17 +00:00
|
|
|
return { ok: false, status: 400, message: "Dateiinhalt fehlt." };
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
files = [
|
|
|
|
|
parseSingleFile(
|
|
|
|
|
input.filename ?? "datei",
|
|
|
|
|
Buffer.from(input.contentBase64, "base64"),
|
|
|
|
|
),
|
|
|
|
|
];
|
|
|
|
|
} else {
|
|
|
|
|
if (!input.text || !input.text.trim())
|
2026-06-10 18:53:17 +00:00
|
|
|
return { ok: false, status: 400, message: "Text fehlt." };
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
files = [parseText(input.text, input.name ?? "SKILL.md")];
|
|
|
|
|
}
|
2026-06-10 18:53:17 +00:00
|
|
|
if (files.length === 0)
|
|
|
|
|
return {
|
|
|
|
|
ok: false,
|
|
|
|
|
status: 400,
|
|
|
|
|
message: "Keine analysierbaren Dateien gefunden.",
|
|
|
|
|
};
|
|
|
|
|
return { ok: true, files };
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
} catch (err) {
|
|
|
|
|
logger.error({ err }, "Skill-Parsing fehlgeschlagen");
|
2026-06-10 18:53:17 +00:00
|
|
|
return {
|
|
|
|
|
ok: false,
|
|
|
|
|
status: 400,
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
message:
|
|
|
|
|
"Das Skill konnte nicht gelesen werden. Bitte prüfen Sie das Format (gültiges ZIP / Textdatei).",
|
2026-06-10 18:53:17 +00:00
|
|
|
};
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
}
|
2026-06-10 18:53:17 +00:00
|
|
|
}
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
|
2026-06-10 18:53:17 +00:00
|
|
|
async function persistScan(
|
|
|
|
|
input: CreateScanInput,
|
|
|
|
|
name: string,
|
|
|
|
|
files: ParsedFile[],
|
|
|
|
|
result: EngineResult,
|
|
|
|
|
): Promise<{ scan: Scan; files: ScanFile[]; findings: Finding[] }> {
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
const [scan] = await db
|
|
|
|
|
.insert(scansTable)
|
|
|
|
|
.values({
|
|
|
|
|
name,
|
|
|
|
|
source: input.source,
|
|
|
|
|
status: "completed",
|
|
|
|
|
verdict: result.verdict,
|
|
|
|
|
riskScore: result.riskScore,
|
|
|
|
|
fileCount: files.length,
|
|
|
|
|
aiUsed: result.aiUsed,
|
|
|
|
|
aiError: result.aiError,
|
|
|
|
|
findingCounts: result.counts,
|
2026-06-10 18:53:17 +00:00
|
|
|
checkpoints: result.checkpoints,
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
})
|
|
|
|
|
.returning();
|
|
|
|
|
|
|
|
|
|
let insertedFiles: ScanFile[] = [];
|
|
|
|
|
if (files.length > 0) {
|
|
|
|
|
insertedFiles = await db
|
|
|
|
|
.insert(scanFilesTable)
|
|
|
|
|
.values(
|
|
|
|
|
files.map((f) => ({
|
|
|
|
|
scanId: scan.id,
|
|
|
|
|
path: f.path,
|
|
|
|
|
kind: f.kind,
|
|
|
|
|
language: f.language,
|
|
|
|
|
size: f.size,
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
.returning();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let insertedFindings: Finding[] = [];
|
|
|
|
|
if (result.findings.length > 0) {
|
|
|
|
|
insertedFindings = await db
|
|
|
|
|
.insert(findingsTable)
|
|
|
|
|
.values(
|
|
|
|
|
result.findings.map((f) => ({
|
|
|
|
|
scanId: scan.id,
|
|
|
|
|
ruleId: f.ruleId,
|
|
|
|
|
axis: f.axis,
|
|
|
|
|
severity: f.severity,
|
|
|
|
|
title: f.title,
|
|
|
|
|
description: f.description,
|
|
|
|
|
remediation: f.remediation,
|
|
|
|
|
file: f.file,
|
|
|
|
|
line: f.line,
|
|
|
|
|
snippet: f.snippet,
|
|
|
|
|
detectedBy: f.detectedBy,
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
.returning();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 18:53:17 +00:00
|
|
|
return { scan, files: insertedFiles, findings: insertedFindings };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
router.get("/scans", async (_req, res) => {
|
|
|
|
|
const rows = await db
|
|
|
|
|
.select()
|
|
|
|
|
.from(scansTable)
|
|
|
|
|
.orderBy(desc(scansTable.createdAt));
|
|
|
|
|
res.json(ListScansResponse.parse(rows.map(serializeScan)));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.post("/scans", async (req, res) => {
|
|
|
|
|
const parsed = CreateScanBody.safeParse(req.body);
|
|
|
|
|
if (!parsed.success) {
|
|
|
|
|
return res
|
|
|
|
|
.status(400)
|
|
|
|
|
.json({ message: "Ungültige Eingabe", details: parsed.error.issues });
|
|
|
|
|
}
|
|
|
|
|
const input = parsed.data;
|
|
|
|
|
|
|
|
|
|
const parseResult = parseScanInput(input);
|
|
|
|
|
if (!parseResult.ok) {
|
|
|
|
|
return res.status(parseResult.status).json({ message: parseResult.message });
|
|
|
|
|
}
|
|
|
|
|
const files = parseResult.files;
|
|
|
|
|
|
|
|
|
|
const name = input.name?.trim() || deriveScanName(files, "Unbenanntes Skill");
|
|
|
|
|
const result = await analyzeSkill(files, input.useAi);
|
|
|
|
|
const { scan, files: insertedFiles, findings } = await persistScan(
|
|
|
|
|
input,
|
|
|
|
|
name,
|
|
|
|
|
files,
|
|
|
|
|
result,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return res
|
|
|
|
|
.status(201)
|
|
|
|
|
.json(GetScanResponse.parse(serializeScanDetail(scan, insertedFiles, findings)));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const STREAM_PACING_MS = 80;
|
|
|
|
|
const delay = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));
|
|
|
|
|
|
|
|
|
|
router.post("/scans/stream", async (req, res) => {
|
|
|
|
|
const parsed = CreateScanBody.safeParse(req.body);
|
|
|
|
|
if (!parsed.success) {
|
|
|
|
|
res
|
|
|
|
|
.status(400)
|
|
|
|
|
.json({ message: "Ungültige Eingabe", details: parsed.error.issues });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const input = parsed.data;
|
|
|
|
|
|
|
|
|
|
const parseResult = parseScanInput(input);
|
|
|
|
|
if (!parseResult.ok) {
|
|
|
|
|
res.status(parseResult.status).json({ message: parseResult.message });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const files = parseResult.files;
|
|
|
|
|
const name = input.name?.trim() || deriveScanName(files, "Unbenanntes Skill");
|
|
|
|
|
|
|
|
|
|
res.status(200);
|
|
|
|
|
res.setHeader("Content-Type", "application/x-ndjson; charset=utf-8");
|
|
|
|
|
res.setHeader("Cache-Control", "no-cache, no-transform");
|
|
|
|
|
res.setHeader("X-Accel-Buffering", "no");
|
|
|
|
|
res.setHeader("Connection", "keep-alive");
|
|
|
|
|
res.flushHeaders();
|
|
|
|
|
|
|
|
|
|
// Detect a genuine client disconnect. NOTE: do NOT use req.on("close") here —
|
|
|
|
|
// for a POST it fires as soon as the request body is consumed, not on abort.
|
|
|
|
|
// res "close" before writableFinished means the client went away.
|
|
|
|
|
let aborted = false;
|
|
|
|
|
res.on("close", () => {
|
|
|
|
|
if (!res.writableFinished) aborted = true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const write = (obj: unknown) => {
|
|
|
|
|
if (aborted || res.writableEnded) return;
|
|
|
|
|
res.write(JSON.stringify(obj) + "\n");
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
};
|
2026-06-10 18:53:17 +00:00
|
|
|
|
|
|
|
|
write({
|
|
|
|
|
type: "start",
|
|
|
|
|
name,
|
|
|
|
|
fileCount: files.length,
|
|
|
|
|
totalChecks: STATIC_RULES.length + (input.useAi ? AI_RULES.length : 0),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let cumulative = 0;
|
|
|
|
|
try {
|
|
|
|
|
const result = await analyzeSkill(files, input.useAi, async (event) => {
|
|
|
|
|
if (event.type === "ai-start") {
|
|
|
|
|
write({ type: "ai-start" });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
cumulative += event.checkpoint.scoreDelta;
|
|
|
|
|
write({
|
|
|
|
|
type: "checkpoint",
|
|
|
|
|
checkpoint: event.checkpoint,
|
|
|
|
|
runningScore: Math.min(100, cumulative),
|
|
|
|
|
});
|
|
|
|
|
if (!aborted) await delay(STREAM_PACING_MS);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const { scan } = await persistScan(input, name, files, result);
|
|
|
|
|
|
|
|
|
|
write({
|
|
|
|
|
type: "done",
|
|
|
|
|
scanId: scan.id,
|
|
|
|
|
riskScore: result.riskScore,
|
|
|
|
|
verdict: result.verdict,
|
|
|
|
|
findingCounts: result.counts,
|
|
|
|
|
aiUsed: result.aiUsed,
|
|
|
|
|
aiError: result.aiError,
|
|
|
|
|
});
|
|
|
|
|
if (!aborted && !res.writableEnded) res.end();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
logger.error({ err }, "Streaming-Scan fehlgeschlagen");
|
|
|
|
|
write({ type: "error", message: "Die Analyse ist fehlgeschlagen." });
|
|
|
|
|
if (!aborted && !res.writableEnded) res.end();
|
|
|
|
|
}
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.get("/scans/:id", async (req, res) => {
|
|
|
|
|
const params = GetScanParams.safeParse(req.params);
|
|
|
|
|
if (!params.success)
|
|
|
|
|
return res.status(400).json({ message: "Ungültige ID" });
|
|
|
|
|
|
|
|
|
|
const [scan] = await db
|
|
|
|
|
.select()
|
|
|
|
|
.from(scansTable)
|
|
|
|
|
.where(eq(scansTable.id, params.data.id));
|
|
|
|
|
if (!scan) return res.status(404).json({ message: "Scan nicht gefunden" });
|
|
|
|
|
|
|
|
|
|
const files = await db
|
|
|
|
|
.select()
|
|
|
|
|
.from(scanFilesTable)
|
|
|
|
|
.where(eq(scanFilesTable.scanId, scan.id));
|
|
|
|
|
const findings = await db
|
|
|
|
|
.select()
|
|
|
|
|
.from(findingsTable)
|
|
|
|
|
.where(eq(findingsTable.scanId, scan.id))
|
|
|
|
|
.orderBy(findingsTable.id);
|
|
|
|
|
|
2026-06-10 18:53:17 +00:00
|
|
|
return res.json(GetScanResponse.parse(serializeScanDetail(scan, files, findings)));
|
SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.delete("/scans/:id", async (req, res) => {
|
|
|
|
|
const params = DeleteScanParams.safeParse(req.params);
|
|
|
|
|
if (!params.success)
|
|
|
|
|
return res.status(400).json({ message: "Ungültige ID" });
|
|
|
|
|
await db.delete(scansTable).where(eq(scansTable.id, params.data.id));
|
|
|
|
|
return res.status(204).send();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export default router;
|