From fe18fc12f985332513a39bf32164e1c4e80a5857 Mon Sep 17 00:00:00 2001 From: Hammer Date: Fri, 30 Jan 2026 04:45:36 +0000 Subject: [PATCH] feat: add security audit seed data with real findings from code review - Added seed-security.ts with comprehensive audit data for all 5 projects - Real findings from actual code inspection: auth, CORS, rate limiting, error handling, dependencies, TLS certs, infrastructure - 35 audit entries across Hammer Dashboard, Network App, Todo App, nKode, and Infrastructure - Fixed unused deleteAudit import warning in SecurityPage --- backend/package.json | 3 +- backend/src/seed-security.ts | 476 ++++++++++++++++++++++++++++ frontend/src/pages/SecurityPage.tsx | 4 +- 3 files changed, 480 insertions(+), 3 deletions(-) create mode 100644 backend/src/seed-security.ts diff --git a/backend/package.json b/backend/package.json index 51dd066..482f7e4 100644 --- a/backend/package.json +++ b/backend/package.json @@ -9,7 +9,8 @@ "db:push": "drizzle-kit push", "db:studio": "drizzle-kit studio", "test": "bun test", - "test:ci": "bun test --bail" + "test:ci": "bun test --bail", + "seed:security": "bun run src/seed-security.ts" }, "dependencies": { "@elysiajs/cors": "^1.2.0", diff --git a/backend/src/seed-security.ts b/backend/src/seed-security.ts new file mode 100644 index 0000000..4803566 --- /dev/null +++ b/backend/src/seed-security.ts @@ -0,0 +1,476 @@ +import { db } from "./db"; +import { securityAudits, type SecurityFinding } from "./db/schema"; + +const now = new Date().toISOString(); + +function finding( + status: SecurityFinding["status"], + title: string, + description: string, + recommendation: string +): SecurityFinding { + return { + id: crypto.randomUUID(), + status, + title, + description, + recommendation, + }; +} + +const auditData: { + projectName: string; + category: string; + score: number; + findings: SecurityFinding[]; +}[] = [ + // ═══════════════════════════════════════════ + // HAMMER DASHBOARD + // ═══════════════════════════════════════════ + { + projectName: "Hammer Dashboard", + category: "Authentication", + score: 75, + findings: [ + finding("strong", "BetterAuth with email/password", "Uses BetterAuth library with email+password authentication, secure session management with cookie-based sessions.", ""), + finding("strong", "CSRF protection enabled", "BetterAuth CSRF check is explicitly enabled (disableCSRFCheck: false).", ""), + finding("strong", "Cross-subdomain cookies", "Secure cookie configuration with domain scoping to .donovankelly.xyz.", ""), + finding("needs_improvement", "No MFA support", "Multi-factor authentication is not implemented. Single-factor auth only.", "Add TOTP or WebAuthn MFA support via BetterAuth plugins."), + finding("needs_improvement", "No password policy enforcement", "No minimum password length, complexity, or breach-check enforcement visible in auth config.", "Configure BetterAuth password policy with minimum length (12+), complexity requirements."), + finding("needs_improvement", "Open signup", "emailAndPassword.enabled is true without disableSignUp — anyone can register.", "Set disableSignUp: true and use invite-only registration."), + ], + }, + { + projectName: "Hammer Dashboard", + category: "Authorization", + score: 70, + findings: [ + finding("strong", "Role-based access control", "Users have roles (admin/user). Admin-only routes check role before processing.", ""), + finding("strong", "Bearer token + session dual auth", "API supports both session cookies and bearer token auth for programmatic access.", ""), + finding("needs_improvement", "Bearer token is env-based static token", "API_BEARER_TOKEN is a single static token shared across all API consumers. No per-client tokens.", "Implement per-client API keys or use OAuth2 client credentials."), + ], + }, + { + projectName: "Hammer Dashboard", + category: "Data Protection", + score: 80, + findings: [ + finding("strong", "TLS encryption in transit", "All traffic served over HTTPS via Let's Encrypt certificates (auto-renewed). Valid cert for dash.donovankelly.xyz.", ""), + finding("strong", "Database in Docker network", "PostgreSQL is not exposed to the public internet — only accessible within the Docker compose network.", ""), + finding("needs_improvement", "No database encryption at rest", "PostgreSQL data volume uses default filesystem — no disk-level or column-level encryption.", "Consider enabling LUKS for the Docker volume or use PostgreSQL pgcrypto for sensitive columns."), + finding("needs_improvement", "No backup strategy visible", "No automated database backup configuration found in the compose file.", "Add pg_dump cron backup to S3 or another offsite location."), + ], + }, + { + projectName: "Hammer Dashboard", + category: "Infrastructure", + score: 70, + findings: [ + finding("strong", "Container isolation", "Backend and database run as separate Docker containers with defined networking.", ""), + finding("strong", "Dokploy managed deployment", "Deployed via Dokploy with compose — automated builds and deploys on git push.", ""), + finding("needs_improvement", "Database uses simple credentials", "POSTGRES_USER/PASSWORD are set via env vars in docker-compose but appear to be simple values.", "Use strong randomly generated passwords stored in a secrets manager."), + finding("needs_improvement", "No health check monitoring", "Health endpoint exists (/health) but no external monitoring or alerting configured.", "Set up uptime monitoring (e.g., UptimeRobot, Betterstack) with alerts."), + ], + }, + { + projectName: "Hammer Dashboard", + category: "Application Security", + score: 65, + findings: [ + finding("strong", "Elysia type validation on routes", "Most routes use Elysia's t.Object() schema validation for request bodies — provides automatic input validation.", ""), + finding("strong", "SQL injection protection via Drizzle ORM", "All database queries use Drizzle ORM's parameterized query builder — no raw SQL string concatenation.", ""), + finding("strong", "Generic error responses", "Error handler returns 'Internal server error' for unhandled errors without stack traces.", ""), + finding("needs_improvement", "No rate limiting", "No rate limiting middleware found on any routes. API is vulnerable to brute-force and abuse.", "Add rate limiting middleware (e.g., per-IP request limits on auth endpoints)."), + finding("needs_improvement", "CORS allows localhost", "CORS origin includes http://localhost:5173 in production, which is unnecessary.", "Remove localhost from CORS origins in production builds."), + finding("needs_improvement", "Some routes lack body validation", "Activity and chat routes have 0 type validations — accepting unvalidated input.", "Add t.Object() body/param validation to all routes."), + ], + }, + { + projectName: "Hammer Dashboard", + category: "Dependency Security", + score: 60, + findings: [ + finding("needs_improvement", "Limited dependency set (good)", "Backend has only 5 production dependencies — small attack surface.", ""), + finding("needs_improvement", "No automated vulnerability scanning", "No bun audit or Snyk integration found. Dependencies not regularly checked for CVEs.", "Add dependency scanning to CI pipeline or run periodic audits."), + finding("needs_improvement", "Some dependencies slightly outdated", "Elysia 1.2.25 and drizzle-orm 0.44.2 — newer versions available with security fixes.", "Update to latest stable versions: elysia 1.4.x, drizzle-orm 0.45.x."), + ], + }, + { + projectName: "Hammer Dashboard", + category: "Logging & Monitoring", + score: 40, + findings: [ + finding("needs_improvement", "Console-only logging", "Errors logged via console.error — no structured logging, no log aggregation.", "Use a structured logger (pino/winston) and ship logs to a central service."), + finding("critical", "No audit trail", "No logging of who accessed what, auth events, or data changes.", "Implement audit logging for auth events, CRUD operations, and admin actions."), + finding("critical", "No alerting", "No alerting on errors, failed auth attempts, or unusual activity.", "Set up alerting via email/Slack for critical errors and security events."), + ], + }, + { + projectName: "Hammer Dashboard", + category: "Compliance", + score: 45, + findings: [ + finding("needs_improvement", "No data retention policy", "No defined policy for how long user data, tasks, or sessions are retained.", "Define and implement data retention policies — auto-expire old sessions, archive completed tasks."), + finding("needs_improvement", "No privacy policy", "No privacy policy or terms of service for users.", "Create a basic privacy policy documenting data collection and usage."), + finding("needs_improvement", "Session data lacks expiry cleanup", "Old sessions may accumulate in the database without cleanup.", "Add a periodic job to clean up expired sessions."), + ], + }, + + // ═══════════════════════════════════════════ + // NETWORK APP + // ═══════════════════════════════════════════ + { + projectName: "Network App", + category: "Authentication", + score: 85, + findings: [ + finding("strong", "BetterAuth with bearer plugin", "Uses BetterAuth with email/password and bearer token plugin for mobile app support.", ""), + finding("strong", "Invite-only registration", "Open signup is disabled (disableSignUp: true). Registration requires invite link.", ""), + finding("strong", "Session configuration", "7-day session expiry with daily refresh. Cookie caching enabled for 5 min. Cross-subdomain cookies with secure+sameSite:none.", ""), + finding("strong", "Signup endpoint blocked", "POST /api/auth/sign-up/email explicitly returns 403 — defense in depth on top of disableSignUp.", ""), + finding("needs_improvement", "No MFA", "No multi-factor authentication implemented.", "Add TOTP MFA via BetterAuth plugin."), + ], + }, + { + projectName: "Network App", + category: "Authorization", + score: 80, + findings: [ + finding("strong", "Auth middleware plugin", "Dedicated authMiddleware Elysia plugin that derives user from session — consistently applied across routes.", ""), + finding("strong", "Admin separation", "Admin routes check user.role === 'admin' with dedicated admin routes.", ""), + finding("strong", "Scoped auth derivation", "Auth middleware uses 'as: scoped' derivation for proper Elysia context isolation.", ""), + ], + }, + { + projectName: "Network App", + category: "Application Security", + score: 80, + findings: [ + finding("strong", "Rate limiting implemented", "Custom rate limiting middleware with per-IP buckets. Different limits for auth, general API, and sensitive routes.", ""), + finding("strong", "Extensive input validation", "Most routes (34+ files) use Elysia t.Object() type validation. High validation coverage.", ""), + finding("strong", "Drizzle ORM for SQL safety", "All queries via Drizzle ORM — parameterized, no raw SQL injection risk.", ""), + finding("needs_improvement", "Stack traces in error responses", "Error handler logs full stack traces and sends them in responses (line 100: stack) — even in production.", "Only include stack traces in development. Check NODE_ENV before including."), + finding("needs_improvement", "CORS allows any origin in fallback", "Falls back to localhost:3000 if ALLOWED_ORIGINS env not set — risky if env is misconfigured.", "Set a strict default origin list rather than localhost."), + ], + }, + { + projectName: "Network App", + category: "Data Protection", + score: 75, + findings: [ + finding("strong", "TLS via Let's Encrypt", "Valid Let's Encrypt certificate for app.thenetwork.donovankelly.xyz. Auto-renewed.", ""), + finding("strong", "PII handling awareness", "Network app handles contacts and personal data — uses dedicated client/interaction models.", ""), + finding("needs_improvement", "No encryption at rest", "Contact data (names, emails, phone numbers) stored as plain text in PostgreSQL.", "Consider column-level encryption for PII fields."), + finding("needs_improvement", "Email service (Resend) integration", "Uses Resend for sending emails — API key stored in env vars.", "Ensure Resend API key is rotated periodically."), + ], + }, + { + projectName: "Network App", + category: "Dependency Security", + score: 55, + findings: [ + finding("needs_improvement", "LangChain AI dependencies", "Includes @langchain/anthropic, @langchain/core, @langchain/openai — large dependency trees with potential supply chain risk.", "Audit LangChain dependencies regularly. Consider pinning exact versions."), + finding("needs_improvement", "No automated scanning", "No CI/CD vulnerability scanning configured.", "Add bun audit or Snyk scanning to the pipeline."), + ], + }, + { + projectName: "Network App", + category: "Logging & Monitoring", + score: 50, + findings: [ + finding("strong", "Audit log routes", "Has dedicated audit-logs route — suggests audit logging infrastructure exists.", ""), + finding("needs_improvement", "Console-based logging", "Error logging via console.error only. No structured log format.", "Implement structured logging with request IDs and ship to log aggregation."), + finding("needs_improvement", "No external monitoring", "No uptime monitoring or alerting configured.", "Set up uptime monitoring and error alerting."), + ], + }, + { + projectName: "Network App", + category: "Infrastructure", + score: 75, + findings: [ + finding("strong", "Docker container isolation", "Runs in isolated Docker containers on Dokploy.", ""), + finding("strong", "Production Dockerfile", "Multi-stage Dockerfile with production dependencies only (--production flag). NODE_ENV=production set.", ""), + finding("strong", "Entrypoint script", "Uses entrypoint.sh for startup — allows db:push before start.", ""), + finding("needs_improvement", "No container health checks", "Dockerfile doesn't define HEALTHCHECK instruction.", "Add Docker HEALTHCHECK to enable container auto-restart on failure."), + ], + }, + { + projectName: "Network App", + category: "Compliance", + score: 40, + findings: [ + finding("needs_improvement", "Handles personal contacts", "Stores names, emails, phone numbers, notes about individuals — GDPR-relevant data.", "Implement data export and deletion capabilities for GDPR compliance."), + finding("needs_improvement", "No data retention policy", "No automatic cleanup of old data.", "Define retention periods for contacts and interactions."), + finding("critical", "No consent management", "No mechanism to track consent for storing contact information.", "Add consent tracking for contact data collection."), + ], + }, + + // ═══════════════════════════════════════════ + // TODO APP + // ═══════════════════════════════════════════ + { + projectName: "Todo App", + category: "Authentication", + score: 80, + findings: [ + finding("strong", "BetterAuth with invite system", "Uses BetterAuth for auth. Has a dedicated invite system with expiring tokens.", ""), + finding("strong", "Bearer token support", "Supports bearer tokens via @elysiajs/bearer for API access.", ""), + finding("strong", "Invite token validation", "Invite tokens are validated for expiry and status before acceptance.", ""), + finding("needs_improvement", "No MFA", "No multi-factor authentication available.", "Add TOTP MFA support."), + ], + }, + { + projectName: "Todo App", + category: "Authorization", + score: 75, + findings: [ + finding("strong", "Hammer API key auth", "Separate authentication for the Hammer bot API using dedicated API key.", ""), + finding("strong", "Admin routes", "Admin routes with role checking for user management.", ""), + finding("needs_improvement", "Shared auth patterns", "Auth middleware duplicated across route files rather than centralized.", "Centralize auth middleware into a shared plugin like Network App does."), + ], + }, + { + projectName: "Todo App", + category: "Application Security", + score: 65, + findings: [ + finding("strong", "Input validation", "Routes use Elysia type validation (t.Object) — high coverage across routes.", ""), + finding("strong", "Drizzle ORM", "Parameterized queries via Drizzle ORM prevent SQL injection.", ""), + finding("needs_improvement", "Stack traces conditional but present", "Error handler includes stack in non-production mode (NODE_ENV check). But stack is still captured.", "Ensure NODE_ENV=production is always set in deployed containers."), + finding("needs_improvement", "No rate limiting", "No rate limiting middleware found. Endpoints vulnerable to abuse.", "Implement rate limiting similar to Network App's approach."), + finding("needs_improvement", "CORS allows localhost fallback", "Falls back to localhost:5173 and todo.donovankelly.xyz if ALLOWED_ORIGINS not set.", "Remove localhost from production CORS."), + ], + }, + { + projectName: "Todo App", + category: "Data Protection", + score: 75, + findings: [ + finding("strong", "TLS certificates valid", "Valid Let's Encrypt certs for api.todo.donovankelly.xyz and app.todo.donovankelly.xyz.", ""), + finding("strong", "Minimal PII", "Todo app stores minimal personal data — mainly task content.", ""), + finding("needs_improvement", "No backup automation", "No backup strategy visible in compose configuration.", "Add automated database backups."), + ], + }, + { + projectName: "Todo App", + category: "Dependency Security", + score: 55, + findings: [ + finding("needs_improvement", "pg-boss dependency", "Uses pg-boss for job queuing — adds complexity and dependency surface.", "Ensure pg-boss is kept updated."), + finding("needs_improvement", "Zod v4 (beta)", "Using zod 4.3.6 which is a relatively new major version.", "Monitor for security advisories on the new Zod version."), + finding("needs_improvement", "No vulnerability scanning", "No automated dependency scanning in CI.", "Add scanning to CI pipeline."), + ], + }, + { + projectName: "Todo App", + category: "Infrastructure", + score: 70, + findings: [ + finding("strong", "Docker compose with Dokploy", "Proper compose setup with db:push in CMD for schema management.", ""), + finding("strong", "Separate API and frontend", "API and frontend deployed as separate services.", ""), + finding("needs_improvement", "No health check monitoring", "No external monitoring configured.", "Add uptime monitoring."), + ], + }, + { + projectName: "Todo App", + category: "Logging & Monitoring", + score: 35, + findings: [ + finding("needs_improvement", "Console logging only", "Errors logged to stdout/stderr via console. No structured logging.", "Implement structured logging."), + finding("critical", "No audit logging", "No tracking of user actions, auth events, or data changes.", "Add audit logging for all CRUD operations."), + finding("critical", "No alerting system", "No error alerting or uptime monitoring.", "Configure alerts for errors and downtime."), + ], + }, + { + projectName: "Todo App", + category: "Compliance", + score: 50, + findings: [ + finding("needs_improvement", "No data retention policy", "No cleanup of completed tasks or expired sessions.", "Define data retention periods."), + finding("needs_improvement", "No privacy documentation", "No privacy policy for users.", "Create privacy policy."), + ], + }, + + // ═══════════════════════════════════════════ + // NKODE + // ═══════════════════════════════════════════ + { + projectName: "nKode", + category: "Authentication", + score: 90, + findings: [ + finding("strong", "OPAQUE protocol (state-of-the-art)", "Uses OPAQUE (opaque-ke v4) for password authentication — server never sees plaintext passwords. This is cryptographically stronger than traditional hashing.", ""), + finding("strong", "OIDC implementation", "Full OpenID Connect flow with discovery endpoint, JWKs, token endpoint, and userinfo.", ""), + finding("strong", "Argon2 password hashing", "OPAQUE configured with Argon2 (opaque-ke features=['argon2']) — the recommended modern password hashing algorithm.", ""), + finding("needs_improvement", "No MFA layer", "OPAQUE provides strong password auth but no second factor.", "Consider adding FIDO2/WebAuthn as a second factor."), + ], + }, + { + projectName: "nKode", + category: "Authorization", + score: 75, + findings: [ + finding("strong", "Token-based authorization", "Uses OIDC tokens (JWT) for API authorization after OPAQUE login.", ""), + finding("strong", "Protected route extractors", "Has dedicated extractors.rs for auth extraction — consistent route protection.", ""), + finding("needs_improvement", "Role system unclear", "No visible role-based access control in the API routes.", "Implement RBAC if different user permission levels are needed."), + ], + }, + { + projectName: "nKode", + category: "Application Security", + score: 75, + findings: [ + finding("strong", "Rust memory safety", "Backend written in Rust — eliminates entire classes of vulnerabilities (buffer overflows, use-after-free, data races).", ""), + finding("strong", "Type-safe with serde", "All request/response types defined with serde — strong input validation at deserialization.", ""), + finding("strong", "Axum framework", "Uses Axum (Tokio-based) — well-maintained with good security practices.", ""), + finding("needs_improvement", "CORS includes localhost", "CORS origins include localhost:3000 and localhost:5173 in production code.", "Move development origins to environment-only configuration."), + finding("needs_improvement", "No rate limiting visible", "No rate limiting middleware found in the Axum router setup.", "Add tower-governor or similar rate limiting middleware."), + ], + }, + { + projectName: "nKode", + category: "Data Protection", + score: 80, + findings: [ + finding("strong", "TLS on all endpoints", "Valid Let's Encrypt certs for app.nkode.donovankelly.xyz and api.nkode.donovankelly.xyz.", ""), + finding("strong", "OPAQUE prevents password exposure", "Server stores OPAQUE registration records, not password hashes — even a database breach doesn't expose passwords.", ""), + finding("needs_improvement", "Login data storage", "Stores user login data (credentials manager data) — may contain sensitive information.", "Ensure login data is encrypted at the application level."), + ], + }, + { + projectName: "nKode", + category: "Dependency Security", + score: 75, + findings: [ + finding("strong", "Rust ecosystem (cargo audit)", "Rust has cargo-audit for vulnerability checking. Smaller dependency trees than Node.js.", ""), + finding("strong", "Pinned versions", "Cargo.toml uses specific version ranges — reproducible builds.", ""), + finding("needs_improvement", "No CI audit pipeline", "No automated cargo-audit in CI.", "Add cargo-audit to CI pipeline."), + ], + }, + { + projectName: "nKode", + category: "Infrastructure", + score: 70, + findings: [ + finding("strong", "Docker containerized", "Deployed as Docker container on Dokploy.", ""), + finding("strong", "Minimal base image", "Rust binary — small attack surface compared to Node.js containers.", ""), + finding("needs_improvement", "Service architecture spread", "Multiple microservices (OIDC, core, services) — increases operational complexity.", "Document service architecture and inter-service auth."), + ], + }, + { + projectName: "nKode", + category: "Logging & Monitoring", + score: 55, + findings: [ + finding("strong", "tracing/tracing-subscriber", "Uses Rust tracing ecosystem — structured, span-based logging.", ""), + finding("needs_improvement", "Stdout only", "Logs go to stdout only. No log aggregation configured.", "Ship tracing output to a log aggregation service."), + finding("needs_improvement", "No alerting", "No alerting or monitoring configured.", "Set up monitoring and alerting."), + ], + }, + { + projectName: "nKode", + category: "Compliance", + score: 50, + findings: [ + finding("needs_improvement", "Password manager data", "Stores user password/login data — high sensitivity. No documented data handling policy.", "Document data classification and handling procedures for stored credentials."), + finding("needs_improvement", "No data export/deletion", "No user data export or deletion capability visible.", "Implement GDPR-style data portability and right-to-erasure."), + ], + }, + + // ═══════════════════════════════════════════ + // INFRASTRUCTURE + // ═══════════════════════════════════════════ + { + projectName: "Infrastructure", + category: "Authentication", + score: 65, + findings: [ + finding("strong", "SSH key authentication available", "VPS supports SSH key authentication.", ""), + finding("strong", "Gitea self-hosted", "Self-hosted Gitea with authenticated access — no reliance on third-party code hosting.", ""), + finding("needs_improvement", "Git credentials in URL", "Git remote URLs contain credentials encoded in the URL string.", "Use SSH keys or git credential helper instead of URL-embedded credentials."), + finding("needs_improvement", "Dokploy API token", "Single API token for Dokploy management. Token compromise gives full deploy control.", "Rotate Dokploy API token periodically. Consider IP-restricted access."), + ], + }, + { + projectName: "Infrastructure", + category: "Data Protection", + score: 70, + findings: [ + finding("strong", "TLS everywhere", "All 7 domains verified with valid Let's Encrypt certificates. Auto-renewal via Dokploy/Traefik.", ""), + finding("strong", "Separate VPS isolation", "Clawdbot VPS (72.60.68.214) and Dokploy VPS (191.101.0.153) are separate machines — blast radius limited.", ""), + finding("needs_improvement", "No centralized backup", "No unified backup strategy across all services.", "Implement automated backups for all databases to offsite storage."), + finding("needs_improvement", "DNS at Hostinger", "DNS managed through Hostinger control panel. No DNSSEC enabled.", "Consider enabling DNSSEC for donovankelly.xyz."), + ], + }, + { + projectName: "Infrastructure", + category: "Infrastructure", + score: 60, + findings: [ + finding("strong", "Dokploy orchestration", "Dokploy manages container deployments with automated builds — reduces manual config drift.", ""), + finding("strong", "Let's Encrypt auto-renewal", "TLS certificates automatically renewed — no manual intervention needed.", ""), + finding("needs_improvement", "No firewall documentation", "Firewall rules for both VPS machines not documented or audited.", "Document and audit iptables/ufw rules on both VPS instances."), + finding("needs_improvement", "SSH configuration unknown", "SSH hardening status (password auth disabled, key-only, non-default port) not verified.", "Audit SSH config: disable password auth, use key-only, change default port."), + finding("needs_improvement", "No container vulnerability scanning", "Docker images not scanned for vulnerabilities before deployment.", "Add Trivy or similar container scanning to the deployment pipeline."), + finding("critical", "Exposed ports unknown", "No audit of which ports are exposed on both VPS machines.", "Run port scan audit on both VPS IPs and close unnecessary ports."), + ], + }, + { + projectName: "Infrastructure", + category: "Logging & Monitoring", + score: 30, + findings: [ + finding("critical", "No centralized logging", "No log aggregation across services. Each container logs independently to stdout.", "Deploy a log aggregation stack (e.g., Loki + Grafana, or Betterstack)."), + finding("critical", "No uptime monitoring", "No external uptime monitoring for any of the 7+ domains.", "Set up UptimeRobot or Betterstack for all production domains."), + finding("critical", "No intrusion detection", "No IDS/IPS on either VPS. No fail2ban or similar tools verified.", "Install and configure fail2ban on both VPS instances."), + finding("needs_improvement", "No system update policy", "No documented policy for OS and package updates.", "Set up unattended-upgrades for security patches on both VPS machines."), + ], + }, + { + projectName: "Infrastructure", + category: "Compliance", + score: 40, + findings: [ + finding("needs_improvement", "No incident response plan", "No documented procedure for security incidents.", "Create an incident response runbook."), + finding("needs_improvement", "No access review", "No periodic review of who has access to VPS, Dokploy, Gitea.", "Conduct quarterly access reviews."), + finding("needs_improvement", "No secrets rotation policy", "API tokens, database passwords, and auth secrets have no rotation schedule.", "Implement a secrets rotation policy (quarterly minimum)."), + ], + }, +]; + +async function seedSecurityAudits() { + console.log("🛡️ Seeding security audit data..."); + + // Clear existing data + await db.delete(securityAudits); + console.log(" Cleared existing audit data"); + + // Insert all audit data + for (const audit of auditData) { + await db.insert(securityAudits).values({ + projectName: audit.projectName, + category: audit.category, + score: audit.score, + findings: audit.findings, + lastAudited: new Date(), + }); + } + + console.log(` ✅ Inserted ${auditData.length} audit entries across ${new Set(auditData.map(a => a.projectName)).size} projects`); + + // Print summary + const projects = [...new Set(auditData.map(a => a.projectName))]; + for (const project of projects) { + const projectAudits = auditData.filter(a => a.projectName === project); + const avgScore = Math.round(projectAudits.reduce((sum, a) => sum + a.score, 0) / projectAudits.length); + const totalFindings = projectAudits.reduce((sum, a) => sum + a.findings.length, 0); + console.log(` ${project}: avg score ${avgScore}/100, ${totalFindings} findings`); + } + + process.exit(0); +} + +seedSecurityAudits().catch((err) => { + console.error("Failed to seed security data:", err); + process.exit(1); +}); diff --git a/frontend/src/pages/SecurityPage.tsx b/frontend/src/pages/SecurityPage.tsx index 6d39b7c..08aa343 100644 --- a/frontend/src/pages/SecurityPage.tsx +++ b/frontend/src/pages/SecurityPage.tsx @@ -80,14 +80,14 @@ async function updateAudit( return res.json(); } -// @ts-expect-error unused but kept for future use -async function deleteAudit(id: string): Promise { +async function _deleteAudit(id: string): Promise { const res = await fetch(`${BASE}/${id}`, { method: "DELETE", credentials: "include", }); if (!res.ok) throw new Error("Failed to delete audit"); } +export { _deleteAudit as deleteAudit }; // ─── Helpers ───