skillguard/artifacts/api-server/src/routes/rules.ts
Replit Agent a70b0d580a 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

56 lines
1.6 KiB
TypeScript

import { Router, type IRouter } from "express";
import { db } from "@workspace/db";
import { rulesTable, type Rule } from "@workspace/db";
import { eq } from "drizzle-orm";
import {
ListRulesResponse,
UpdateRuleParams,
UpdateRuleBody,
UpdateRuleResponse,
} from "@workspace/api-zod";
const router: IRouter = Router();
function serializeRule(r: Rule) {
return {
id: r.id,
ruleId: r.ruleId,
axis: r.axis,
category: r.category,
title: r.title,
description: r.description,
severity: r.severity,
detectionType: r.detectionType,
enabled: r.enabled,
};
}
router.get("/rules", async (_req, res) => {
const rows = await db.select().from(rulesTable).orderBy(rulesTable.id);
res.json(ListRulesResponse.parse(rows.map(serializeRule)));
});
router.patch("/rules/:id", async (req, res) => {
const params = UpdateRuleParams.safeParse(req.params);
if (!params.success) return res.status(400).json({ message: "Ungültige ID" });
const parsed = UpdateRuleBody.safeParse(req.body);
if (!parsed.success)
return res
.status(400)
.json({ message: "Ungültige Eingabe", details: parsed.error.issues });
const d = parsed.data;
const update: Partial<typeof rulesTable.$inferInsert> = {};
if (d.severity !== undefined) update.severity = d.severity;
if (d.enabled !== undefined) update.enabled = d.enabled;
const [updated] = await db
.update(rulesTable)
.set(update)
.where(eq(rulesTable.id, params.data.id))
.returning();
if (!updated) return res.status(404).json({ message: "Regel nicht gefunden" });
return res.json(UpdateRuleResponse.parse(serializeRule(updated)));
});
export default router;