Files
hackerspace.sh/docs/PRODUCTION_AUDIT.md

16 KiB

Hackerspace.sh - Production Readiness Audit

Audit Date: 2026-03-10
Auditor: v0 Comprehensive Analysis
Status: PRE-PRODUCTION - Action Required


Executive Summary

Hackerspace.sh is a well-architected member management platform with a solid foundation. However, several critical issues must be addressed before production deployment. This audit identifies 47 issues across security, functionality, performance, and code quality.

Severity Breakdown

Severity Count Description
CRITICAL 5 Security vulnerabilities, data loss risk
HIGH 12 Broken functionality, UX blockers
MEDIUM 18 Missing features, technical debt
LOW 12 Nice-to-have improvements

1. CRITICAL Issues (Must Fix Before Production)

1.1 Missing Input Validation [CRITICAL]

Location: lib/actions.ts (all server actions)

Issue: Server actions accept user input without validation. Attackers could inject malicious data or cause database errors.

Current Code:

export async function createTask(formData: {
  title: string
  // ... accepts any string
}) {
  const { data, error } = await supabase.from('tasks').insert({
    title: formData.title,  // Raw input!
    // ...
  })
}

Fix Required:

import { z } from 'zod'

const createTaskSchema = z.object({
  title: z.string().min(1).max(200).trim(),
  description: z.string().max(2000).optional(),
  type: z.enum(['task', 'chore']),
  area: z.string().max(50).optional(),
  recurrence: z.enum(['none', 'daily', 'weekly', 'biweekly', 'monthly']).default('none'),
  due_date: z.string().datetime().optional(),
})

export async function createTask(formData: unknown) {
  const parsed = createTaskSchema.safeParse(formData)
  if (!parsed.success) {
    return { error: 'Invalid input', details: parsed.error.flatten() }
  }
  // Use parsed.data
}

Files to Update:

  • lib/actions.ts - All 30+ actions
  • Add zod to dependencies

1.2 Secrets Not Encrypted [CRITICAL]

Location: secrets table, integrations.config

Issue: Sensitive credentials stored as plain text in database. Compromised database = compromised credentials.

Current State:

  • secrets.value stores plain text
  • integrations.config stores API keys in JSONB

Fix Required:

  1. Add encryption layer using @vercel/kv or custom AES-256-GCM
  2. Encrypt on write, decrypt on read
  3. Store encryption key in environment variable
import crypto from 'crypto'

const ENCRYPTION_KEY = process.env.SECRETS_ENCRYPTION_KEY! // 32 bytes

function encrypt(text: string): string {
  const iv = crypto.randomBytes(16)
  const cipher = crypto.createCipheriv('aes-256-gcm', Buffer.from(ENCRYPTION_KEY, 'hex'), iv)
  let encrypted = cipher.update(text, 'utf8', 'hex')
  encrypted += cipher.final('hex')
  const authTag = cipher.getAuthTag().toString('hex')
  return `${iv.toString('hex')}:${authTag}:${encrypted}`
}

function decrypt(encrypted: string): string {
  const [ivHex, authTagHex, encryptedText] = encrypted.split(':')
  const decipher = crypto.createDecipheriv('aes-256-gcm', Buffer.from(ENCRYPTION_KEY, 'hex'), Buffer.from(ivHex, 'hex'))
  decipher.setAuthTag(Buffer.from(authTagHex, 'hex'))
  let decrypted = decipher.update(encryptedText, 'hex', 'utf8')
  decrypted += decipher.final('utf8')
  return decrypted
}

1.3 Rate Limiting Missing [CRITICAL]

Location: All server actions, middleware

Issue: No rate limiting on auth endpoints or API calls. Vulnerable to:

  • Brute force password attacks
  • Denial of service
  • Resource exhaustion

Fix Required:

// middleware.ts
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(10, '10 s'), // 10 requests per 10 seconds
})

export async function middleware(request: NextRequest) {
  const ip = request.ip ?? '127.0.0.1'
  const { success, limit, reset, remaining } = await ratelimit.limit(ip)
  
  if (!success) {
    return new NextResponse('Too Many Requests', {
      status: 429,
      headers: {
        'X-RateLimit-Limit': limit.toString(),
        'X-RateLimit-Remaining': remaining.toString(),
        'X-RateLimit-Reset': reset.toString(),
      },
    })
  }
  // Continue...
}

1.4 CSRF Protection Missing [CRITICAL]

Location: All forms

Issue: Forms don't include CSRF tokens. Attackers could trick users into performing actions.

Current State: Server actions bypass traditional CSRF but still vulnerable in some scenarios.

Fix Required:

  1. Verify Origin header in server actions
  2. Add CSRF token for sensitive operations
// In server actions
export async function deleteSecret(secretId: string) {
  const headersList = headers()
  const origin = headersList.get('origin')
  const host = headersList.get('host')
  
  if (origin && !origin.includes(host!)) {
    return { error: 'Invalid origin' }
  }
  // Continue...
}

1.5 Admin Client Exposure Risk [CRITICAL]

Location: lib/supabase/admin.ts

Issue: Service role key used in several places. Any mistake could expose it to client.

Current Usage:

  • lib/auth-actions.ts - createSpace, joinSpace
  • Signup trigger uses SECURITY DEFINER (safe)

Fix Required:

  1. Audit all admin client usage
  2. Move admin operations to dedicated API routes
  3. Add ESLint rule to prevent client-side imports
// .eslintrc.js
module.exports = {
  rules: {
    'no-restricted-imports': [
      'error',
      {
        paths: [{
          name: '@/lib/supabase/admin',
          message: 'Admin client should only be used in API routes, not client components.',
        }],
      },
    ],
  },
}

2. HIGH Priority Issues

2.1 Social Auth Buttons Non-Functional [HIGH]

Location: app/login/page.tsx

Issue: GitHub and Google buttons are rendered but don't work.

Fix:

async function handleGitHubLogin() {
  const supabase = createClient()
  const { error } = await supabase.auth.signInWithOAuth({
    provider: 'github',
    options: {
      redirectTo: `${window.location.origin}/auth/callback`,
    },
  })
  if (error) setError(error.message)
}

Also Required:

  • Configure OAuth providers in Supabase Dashboard
  • Add provider credentials

2.2 Import Feature Non-Functional [HIGH]

Location: app/(app)/import/page.tsx

Issue: File upload and database connector UI exists but doesn't process files.

Fix Required:

  1. Add file input handler
  2. Implement CSV parser (use papaparse)
  3. Add column mapping logic
  4. Implement batch insert
'use client'
import Papa from 'papaparse'
import { importMembers } from '@/lib/actions'

function handleFileUpload(event: React.ChangeEvent<HTMLInputElement>) {
  const file = event.target.files?.[0]
  if (!file) return

  Papa.parse(file, {
    header: true,
    complete: async (results) => {
      const mappedRows = results.data.map(row => ({
        display_name: row['Full Name'],
        email: row['Email'],
        // ... map other fields
      }))
      const result = await importMembers(mappedRows)
      // Handle result
    },
    error: (error) => setError(error.message),
  })
}

2.3 Payment Platform Integrations [HIGH]

Location: app/(app)/settings/settings-client.tsx

Issue: PayPal, Zeffy, Venmo integrations are UI-only. No actual API connections.

Status:

  • Integration config storage works
  • No OAuth flows
  • No transaction sync
  • No webhooks

Required Work:

  1. PayPal: Implement OAuth 2.0, transaction API
  2. Zeffy: Implement API key auth, webhook handler
  3. Venmo: Research Business API availability

2.4 Webhook Endpoint Missing [HIGH]

Location: Settings shows webhook URL but no endpoint exists

Issue: URL https://api.hackerspace.sh/{slug}/members is displayed but endpoint doesn't exist.

Fix Required:

// app/api/[slug]/members/route.ts
import { createAdminClient } from '@/lib/supabase/admin'
import { headers } from 'next/headers'
import crypto from 'crypto'

export async function POST(
  request: Request,
  { params }: { params: { slug: string } }
) {
  const headersList = headers()
  const signature = headersList.get('x-webhook-signature')
  
  const admin = createAdminClient()
  const { data: space } = await admin
    .from('spaces')
    .select('id, webhook_secret')
    .eq('slug', params.slug)
    .single()

  if (!space || !space.webhook_secret) {
    return Response.json({ error: 'Invalid space' }, { status: 404 })
  }

  // Verify signature
  const body = await request.text()
  const expectedSignature = crypto
    .createHmac('sha256', space.webhook_secret)
    .update(body)
    .digest('hex')

  if (signature !== expectedSignature) {
    return Response.json({ error: 'Invalid signature' }, { status: 401 })
  }

  // Process webhook payload
  const data = JSON.parse(body)
  // ...

  return Response.json({ success: true })
}

2.5 Error Boundaries Missing [HIGH]

Location: All pages

Issue: No error boundaries. Unhandled errors crash entire app.

Fix Required:

// app/(app)/error.tsx
'use client'

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <div className="min-h-screen flex items-center justify-center">
      <div className="text-center">
        <h2 className="text-xl font-semibold mb-2">Something went wrong</h2>
        <p className="text-muted-foreground mb-4">{error.message}</p>
        <button onClick={reset} className="bg-primary text-white px-4 py-2 rounded">
          Try again
        </button>
      </div>
    </div>
  )
}

2.6 Loading States Inconsistent [HIGH]

Location: Multiple pages

Issue: Some pages have loading states, others show blank while data loads.

Fix Required: Add loading.tsx to all route groups:

// app/(app)/loading.tsx
export default function Loading() {
  return (
    <div className="min-h-screen flex items-center justify-center">
      <Spinner className="w-8 h-8" />
    </div>
  )
}

2.7 Session Refresh Issues [HIGH]

Location: Long-running sessions

Issue: Session may expire during use without proper refresh.

Current: Middleware calls updateSession but client components don't handle expiry.

Fix Required:

// Add to layout or hook
useEffect(() => {
  const { data: { subscription } } = supabase.auth.onAuthStateChange((event) => {
    if (event === 'SIGNED_OUT' || event === 'TOKEN_REFRESHED') {
      router.refresh()
    }
  })
  return () => subscription.unsubscribe()
}, [])

2.8-2.12 Additional HIGH Issues

Issue Location Description
No email notifications - Members not notified of approvals, assignments
Task recurrence not auto-resetting Tasks Recurring tasks don't auto-create new instances
Project task linking missing Projects Can't link tasks to projects
Area leads not editable Ops UI shows leads but no edit functionality
Knowledge base detail page missing /ops/[id] Links go to non-existent page

3. MEDIUM Priority Issues

3.1 TypeScript Strict Mode Disabled

Location: tsconfig.json

Issue: Not using strict mode reduces type safety.

Fix: Enable strict mode and fix resulting errors.


3.2 No Test Coverage

Issue: Zero tests exist.

Required:

  • Unit tests for all server actions (Vitest)
  • Component tests (React Testing Library)
  • E2E tests for critical flows (Playwright)

3.3 No Logging/Monitoring

Issue: No structured logging or error tracking.

Fix: Add Sentry, LogRocket, or similar.


3.4 Database Queries Not Optimized

Issue: Some pages make sequential queries that could be parallel.

Example Fix:

// Before: Sequential
const { data: tasks } = await supabase.from('tasks').select('*')
const { data: projects } = await supabase.from('projects').select('*')

// After: Parallel
const [{ data: tasks }, { data: projects }] = await Promise.all([
  supabase.from('tasks').select('*'),
  supabase.from('projects').select('*'),
])

3.5-3.18 Additional MEDIUM Issues

Issue Description
No pagination Lists load all data, no infinite scroll
Search is client-only No full-text search in database
No data export Can import but not export
Mobile responsive issues Some pages not fully mobile-optimized
No keyboard shortcuts Missing accessibility features
Duplicate code in actions Membership check repeated everywhere
No optimistic updates UI waits for server response
Channel creation missing Can't create new chat channels
Message editing/deletion No edit/delete for messages
File attachments missing No file upload in messages or KB
Member search in modals Hard to find members in large spaces
No undo for destructive actions Delete is immediate
Date handling inconsistent Mix of string and Date objects
No timezone support All times in server timezone

4. LOW Priority Issues

Issue Description
Console warnings Some React key warnings
Unused code Dead imports in some files
Inconsistent styling Mix of Tailwind approaches
Missing meta tags Some pages lack proper SEO
No favicon variants Only basic favicon
No PWA support Could work offline
No dark/light toggle Theme locked to dark
No i18n English only
No accessibility audit WCAG compliance unknown
No performance profiling Bundle size not optimized
No CI/CD pipeline Manual deployments
No staging environment Direct to production

5. Security Checklist

Check Status Notes
RLS enabled on all tables PASS All 13 tables protected
Service role key protected PARTIAL Used in server actions, needs audit
Input validation FAIL No Zod schemas
Rate limiting FAIL Not implemented
CSRF protection PARTIAL Origin check needed
XSS prevention PASS React escapes by default
SQL injection PASS Supabase client parameterizes
Secrets encryption FAIL Plain text storage
Session management PASS Supabase handles
Password requirements PASS Min 8 chars enforced
Audit logging PASS activity_log table exists
Error message exposure PARTIAL Some errors too detailed

6. Performance Checklist

Check Status Notes
Server Components PASS Pages are RSC
Code splitting PASS Next.js automatic
Image optimization PASS No large images
Database indexes PARTIAL Basic indexes only
Query optimization PARTIAL Some N+1 issues
Caching PARTIAL revalidatePath used
Bundle size UNKNOWN Not audited
Core Web Vitals UNKNOWN Not measured

Phase 1: Security (Week 1)

  1. Add Zod validation to all server actions
  2. Implement rate limiting with Upstash
  3. Add secrets encryption
  4. Audit admin client usage
  5. Add CSRF token verification

Phase 2: Critical Functionality (Week 2)

  1. Implement social auth (GitHub, Google)
  2. Build CSV import functionality
  3. Add error boundaries
  4. Create loading states
  5. Fix session refresh

Phase 3: Features (Week 3-4)

  1. Add email notifications
  2. Implement webhook endpoint
  3. Build payment platform integrations
  4. Add task recurrence automation
  5. Create KB detail pages

Phase 4: Quality (Week 5)

  1. Write test suite (80%+ coverage target)
  2. Add logging/monitoring
  3. Performance optimization
  4. Accessibility audit
  5. Security penetration test

Phase 5: Polish (Week 6)

  1. Mobile optimization
  2. Keyboard shortcuts
  3. Export functionality
  4. Documentation completion
  5. CI/CD pipeline

8. Sign-Off Requirements

Before production launch, the following must be verified:

  • All CRITICAL issues resolved
  • All HIGH issues resolved or documented workarounds
  • Security penetration test completed
  • Load testing completed (target: 100 concurrent users)
  • Backup and recovery tested
  • Monitoring and alerting configured
  • Documentation complete
  • Support procedures documented
  • Legal review of ToS/Privacy Policy
  • GDPR/data handling compliance verified