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
This commit is contained in:
@@ -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",
|
||||
|
||||
476
backend/src/seed-security.ts
Normal file
476
backend/src/seed-security.ts
Normal file
@@ -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);
|
||||
});
|
||||
Reference in New Issue
Block a user