Skip to content

Latest commit

 

History

History
703 lines (541 loc) · 23.1 KB

File metadata and controls

703 lines (541 loc) · 23.1 KB

Backend Compliance & Production-Readiness Audit

Date: May 28, 2026
Audit Scope: /backend/src
Assessment Level: Active Development → Production Ready


Executive Summary

The backend codebase demonstrates strong foundational practices with good architecture patterns and security awareness. However, there are critical gaps in production readiness that must be resolved before handling financial transactions at scale:

Area Status Risk Level Comments
Error Handling ✅ Good Low Comprehensive error types, proper propagation
Input Validation ⚠️ Incomplete Critical Validation logic broken, inconsistent schema usage
Database Transactions ❌ Missing Critical No transaction wrapping, no rollback safety
Logging ⚠️ Partial Medium Mixed console/winston, sensitive data exposure
Rate Limiting ✅ Good Low Layered approach, configurable per endpoint
Access Control ✅ Good Low Stellar address verification, project ownership checks
Configuration ✅ Good Low Zod validation, environment safety
Response Validation ⚠️ Partial Medium Middleware in place but incomplete coverage
Migration Safety ✅ Good Low Reversible migrations with proper structure
Test Coverage ⚠️ Incomplete Medium Core happy paths covered, gaps in error scenarios

1. ERROR HANDLING

✅ Strengths

  • Structured error types with ErrorType enum (CONTRACT, AUTH, VALIDATION, ACCOUNT_STATE, RPC, INTERNAL)
  • Detailed error codes mapping contract errors to user-friendly messages with remediation hints
  • Centralized error handler in middleware/error.ts
  • Error propagation pattern: All route handlers catch and forward via next(error)
  • Error logging in handler includes request ID, error type, code, message, details

❌ Gaps

Gap 1: Incomplete Error Handling in validate.ts

File: middleware/validate.ts
Severity: CRITICAL
Issue: Response body is incomplete/broken - has syntax errors

// BROKEN CODE:
res.status().json({
  error: ,          // ← missing value
  message: ,        // ← missing value
  requestId,
  details:          // ← missing value
});

Impact: Any route using validateRequest middleware will crash on validation error
Fix: Complete the response object:

res.status(400).json({
  error: "validation_error",
  message: "Invalid request payload.",
  requestId,
  details: buildValidationDetails(error)
});

Gap 2: console.error vs logger

Files:

Severity: MEDIUM
Issue: Error handler uses console.error() instead of logger.error()

  • Not captured by log rotation
  • Not sent to error.log file
  • Inconsistent with rest of codebase

Impact: Production errors won't appear in centralized logs
Count: 3 console.error calls


2. INPUT VALIDATION

✅ Strengths

  • Zod schemas for all major request payloads
  • Stellar address validation with regex and Address.fromString() checks
  • Custom validators for business logic (basisPoints sum to 10,000, no duplicate collaborators)
  • Schema composition for reusable primitives

❌ Gaps

Gap 1: Validation Middleware is Broken (Critical)

File: middleware/validate.ts
Severity: CRITICAL
Issue: The validateRequest middleware has a syntax error and is not being used consistently

// validateRequest is defined but:
// 1. Not imported in any routes
// 2. Response handler broken (see Gap 1.1)
// 3. Routes validate manually with .safeParse() instead

Current pattern (routes do it directly):

// From routes/users.ts
const parsed = userRegistrationSchema.safeParse(req.body);
if (!parsed.success) {
  throw new AppError(..., parsed.error.flatten());
}

Impact: Validation is scattered, not DRY, harder to audit
Recommendation: Fix and use middleware consistently

Gap 2: Missing Validation in Some Routes

File: routes/transactions.ts

// GET /transactions/:txHash lacks path parameter validation
const { txHash } = req.params;
if (!txHash || txHash.length === 0) {
  throw new AppError(..., "Transaction hash is required.");
}
// ← Basic string check only, no format validation

Issue: Should validate Stellar transaction hash format (64 hex chars or XDR format)
Risk: Bad input can cause RPC failures or crash downstream parsing

Gap 3: Inconsistent Wallet Address Validation

Multiple files define stellarAddressSchema:

Issue: Duplicate code, inconsistent validation depth
Impact: Some endpoints accept malformed addresses, others reject valid ones

Gap 4: POST /users/login Missing

File: routes/users.ts

Issue: Index.ts registers auth limiter for "/users/login" but route doesn't exist

// index.ts line 73
app.use("/users/login", authLimiter);
// but no route handler for POST /users/login

Risk: Orphaned middleware, unauthenticated socket connection possible


3. DATABASE TRANSACTIONS

❌ Critical Gap: No Transaction Wrapping

Status: NOT IMPLEMENTED

Issue: Financial operations must be wrapped in database transactions for consistency. Current code doesn't use transactions at all.

Examples:

users.ts - User registration:

// Lines 44-63
const newUser = userRepository.create({...});
const savedUser = await userRepository.save(newUser);  // ← NO TRANSACTION

Missing concern: If an error occurs between queries, data becomes inconsistent.

What's missing:

  1. No use of QueryRunner for transaction management
  2. No rollback pattern
  3. No isolation level specification
  4. No deadlock retry logic

High-Risk Operations That Need Transactions:

  1. User registration - if wallet address unique constraint violated mid-save
  2. Transaction recording - if recording fails after blockchain confirmation
  3. Payout history - if file write fails during cache update

Code Pattern Needed:

const queryRunner = dataSource.createQueryRunner();
await queryRunner.startTransaction();
try {
  // Do work
  await queryRunner.commitTransaction();
} catch (err) {
  await queryRunner.rollbackTransaction();
  throw err;
} finally {
  await queryRunner.release();
}

Severity: CRITICAL - Data corruption risk in production


4. LOGGING

✅ Strengths

  • Winston logger configured with file rotation (error.log, combined.log)
  • Structured logging with metadata objects: logger.info("event", { userId, walletAddress, requestId })
  • Request ID tracking via middleware/request-id.ts - every log includes requestId

⚠️ Issues

Issue 1: Mixed console and logger

Files:

Impact:

  • Not captured by file rotation
  • CI logs mixed with stderr
  • Harder to parse/aggregate logs

Count: 13 console calls that should be logger calls

Issue 2: Sensitive Data Exposure

File: middleware/project-access.ts

logger.warn("Unauthorized project access attempt blocked", {
  requester,        // ← Stellar address - OK for audit trails
  projectId,        // ← OK
  requestId,        // ← OK
  ip: req.ip,       // ← IP address - consider privacy implications
});

Concern: IP addresses logged unredacted could violate GDPR/privacy laws
Risk: Low for blockchain context but should be hashed in production

Issue 3: Insufficient Error Context

File: middleware/error.ts

// Only logs error details, not request context
console.error({
  requestId,
  type: err.type,
  code: err.code,
  message: err.message,
  details: err.details
});
// Missing: err.stack, original request path/method

Impact: Hard to debug production issues without full context

Issue 4: No Log Levels for Cache Operations

File: services/stellar.ts

console.debug(`[cache] MISS (expired) key=${key}`);
console.debug(`[cache] HIT key=${key}`);
// Should use logger.debug() with consistent format

Impact: Cache performance debugging not integrated into Winston logs


5. RATE LIMITING

✅ Excellent Implementation

Strengths:

  • Layered strategy with 5 tiers:

    • globalLimiter: 500 req/15min (safety net for all routes)
    • readLimiter: 100 req/15min (GET endpoints)
    • writeLimiter: 30 req/15min (POST/PUT/DELETE)
    • adminLimiter: 20 req/15min (admin endpoints)
    • authLimiter: 10 req/5min (registration/login - strict burst protection)
  • Environment-configurable: All limits can be tuned via process.env:

    • RATE_LIMIT_WINDOW_MS
    • RATE_LIMIT_GLOBAL_MAX
    • RATE_LIMIT_WRITE_MAX
    • RATE_LIMIT_ADMIN_MAX
    • RATE_LIMIT_AUTH_WINDOW_MS
  • Proper headers: Uses standard rate-limit headers (Retry-After)

  • Applied consistently: Every route has rate limiting

  • Skip logic: Health checks bypass global limiter

File: middleware/rate-limit.ts

Note: Health endpoint properly skipped to avoid false alerting from monitors


6. USER ACCESS CONTROL

✅ Strong Implementation

Strengths:

  • Stellar address verification via middleware/project-access.ts

    • Validates X-Stellar-Address header format
    • Ensures only valid Stellar public keys accepted
    • Requires signature proof (enforced at wallet level with Freighter)
  • Project ownership checks - requireProjectAccess() middleware:

    • Verifies requester is either project owner OR collaborator
    • Prevents unauthorized enumeration of private project data
    • Logs unauthorized attempts with context
  • Role-based access in User entity:

    @Column({ type: "varchar", length: 32, default: "user" })
    role!: string;

    (Though role enforcement not fully implemented yet)

File: middleware/project-access.ts

⚠️ Minor Gaps

Gap 1: Role Enforcement Missing

Issue: User entity has role field but no middleware enforces it

  • No admin role checks on /splits/admin endpoint
  • No permission matrix for role → action

Recommendation: Implement requireRole() middleware

Gap 2: No Session/Token Management

Issue: No JWT or session tokens for repeated auth

  • Users must provide X-Stellar-Address header on every request
  • No logout mechanism
  • No token expiration

Note: This aligns with wallet-first design but limits features like "stay logged in"


7. CONFIGURATION

✅ Strong Implementation

Strengths:

  • Zod schema validation for all environment variables

  • Type-safe config with BackendEnv interface

  • Detailed error messages if vars are missing:

    [env] Server cannot start - fix the following environment variable issues:
      x  DATABASE_URL: DATABASE_URL must be a valid PostgreSQL connection string
    
  • Caching to avoid repeated validation via getEnv()

  • Production-specific handling:

    • Automatic SSL for non-localhost databases
    • CSP headers relaxed only for /docs route

Files:

✅ No Critical Gaps

All sensitive config (DATABASE_URL, RPC URLs, contract IDs) properly required and validated.


8. RESPONSE VALIDATION

⚠️ Partial Implementation

Strengths:

  • Middleware exists: middleware/validateResponse.ts
  • Schema validation: Responses validated before sending
  • Non-breaking: In production, validates but still sends response (soft fail)
  • Development mode: Sends validation error details for debugging

Code:

export function withResponseValidation<T>(
  schema: ZodSchema<T>,
  handler: RouteHandler,
): RouteHandler {
  // Validates res.json() output against schema
  // In production: logs drift but sends response anyway
  // In development: returns validation error
}

❌ Gaps

Gap 1: Not Applied to All Routes

Usage: Only found in /api/openapi.json handler

  • User registration responses: Not validated
  • Transaction history responses: Not validated
  • Split project list responses: Not validated

Files affected:

Impact: Response shape changes won't be caught until frontend breaks

Gap 2: Response Schemas Incomplete

Files:

Recommendation: Define schemas for every response type and apply middleware consistently:

splitsRouter.post("/", 
  withResponseValidation(ProjectListResponseSchema, async (req, res, next) => {
    // handler
  })
);

9. MIGRATION SAFETY

✅ Strong Implementation

Strengths:

  • Reversible migrations with up() and down() methods
  • Proper structure - creates extensions, tables, indexes, enums
  • Unique constraints to prevent duplicates (walletAddress, txHash)
  • Composite indexes for query optimization:
    • txHash (unique, for lookups)
    • roundId (for batch operations)
    • recipient (for user queries)
    • timestamp (for range queries)

File: migrations/1760000000000-InitialUserAndTransactionRecord.ts

down() method properly drops:

DROP INDEX IDX_transactions_timestamp;
DROP INDEX IDX_transactions_recipient;
DROP INDEX IDX_transactions_round_id;
DROP INDEX IDX_transactions_tx_hash;
DROP TABLE transactions;
DROP TYPE transactions_status_enum;
DROP TABLE users;

⚠️ Minor Improvements Needed

Issue 1: EXTENSION IF NOT EXISTS

Good practice but could be safer:

await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`);

Better: Fail explicitly if uuid-ossp not available (PostgreSQL may be too old)

Issue 2: Enum Creation Hardcoded

File: migrations/1760000000000-InitialUserAndTransactionRecord.ts

CREATE TYPE "public"."transactions_status_enum" 
  AS ENUM('pending', 'completed', 'failed')

Issue: Hardcoded status values; if entity changes, migration doesn't auto-update
Note: This is a TypeORM limitation, not a code bug

Issue 3: No Data Transformation Migrations

Issue: No migrations for future schema changes (adding columns, renaming, etc.)
Status: Not applicable yet - first migration only


10. TEST COVERAGE

⚠️ Partial Coverage

Test Files: 8 files in tests/

  1. e2e-happy-path.test.ts - Full core flow
  2. routes.test.ts - Route integration tests
  3. users.test.ts - User registration happy path
  4. history.test.ts - Split history filtering
  5. rpc-retries.test.ts - Retry logic
  6. transactions.test.ts - Transaction queries
  7. auth-middleware.test.ts - Stellar auth
  8. contract-interface-artifact.test.ts - Contract validation

✅ Strengths

  • Happy path coverage: Core flows (create split → deposit → distribute) tested
  • Error simulation: Mock Stellar RPC to test failure scenarios
  • Retry logic: Explicit tests for exponential backoff
  • Auth verification: Stellar address validation tested
  • Transaction filtering: History query parameters tested

❌ Gaps

Gap 1: No Error Handler Tests

Missing: Unit tests for middleware/error.ts

// No test coverage for:
// - AppError formatting
// - Non-AppError error handling
// - RpcError handling
// - Fallback error response

Impact: Error path bugs won't be caught

Gap 2: No Database Transaction Tests

Missing: Tests for:

  • User registration with duplicate wallet address (concurrency)
  • Transaction recording failures and rollback
  • Database connection loss

Critical for financial app reliability

Gap 3: No Validation Middleware Tests

Missing: Tests for middleware/validate.ts (it's broken anyway)

Impact: Can't verify validation error responses without fixing the middleware

Gap 4: Incomplete Rate Limit Testing

Missing:

  • Tests that verify rate limit headers are set correctly
  • Tests that verify different endpoints have different limits
  • Tests that verify IP-based rate limiting works

Gap 5: No Response Validation Tests

Missing: Tests for middleware/validateResponse.ts

Gap 6: No Access Control Tests

Missing:

  • Tests that verify non-owner can't access private project data
  • Tests that verify collaborators can access their projects
  • Tests that verify unauthorized attempts are logged

Example: No test for requireProjectAccess() returning 403 Unauthorized

Gap 7: No Configuration Tests

Missing: Tests for config/env.ts

// No tests for:
// - validateEnv() with missing required vars
// - validateEnv() with invalid port
// - getEnvDiagnostics() with various issues

Gap 8: Critical Gaps Summary

Test Category Covered Missing Risk
Happy path ✅ Core flow - Low
Error handling ⚠️ Some scenarios Comprehensive error tests High
Database ❌ Not tested Transactions, concurrency, rollback Critical
Validation ❌ Broken middleware All validation tests Critical
Rate limiting ❌ No tests Limit verification, headers Medium
Access control ❌ No tests Authorization checks High
Configuration ❌ No tests Env validation, diagnostics Medium
Response validation ❌ No tests Response shape contracts Medium

10 Critical/High-Priority Fixes

🔴 CRITICAL (Must Fix Before Production)

  1. Fix validate.ts middleware middleware/validate.ts

    • Broken response body syntax
    • Estimated effort: 15 min
  2. Implement database transactions

    • Wrap user registration, transaction recording in transactions
    • Add rollback error handling
    • Estimated effort: 3-4 hours
  3. Add database transaction tests

    • Concurrency scenarios, rollback verification
    • Estimated effort: 2-3 hours
  4. Implement response validation middleware usage

    • Apply to all routes with withResponseValidation()
    • Create comprehensive response schemas
    • Estimated effort: 2-3 hours

🟠 HIGH (Must Fix Before Scale)

  1. Replace console logging with logger

    • Migrate 13 console calls to winston logger
    • Estimated effort: 1 hour
  2. Complete error handling tests

    • Test all error paths, edge cases
    • Estimated effort: 2 hours
  3. Implement access control tests

    • Verify authorization checks work
    • Test logged attempts
    • Estimated effort: 1-2 hours
  4. Add validation tests

    • Test all Zod schemas, edge cases
    • Test malformed input rejection
    • Estimated effort: 2 hours
  5. Implement admin role enforcement

    • Create requireRole() middleware
    • Protect /splits/admin endpoint
    • Estimated effort: 1 hour
  6. Document deployment/config requirements

    • .env.example file
    • Migration runbook
    • Rollback procedures
    • Estimated effort: 1-2 hours

Detailed Remediation Plan

Phase 1: Critical Fixes (1-2 days)

  • Fix validate.ts middleware (15 min)
  • Implement database transactions (3-4 hours)
  • Apply response validation middleware (2-3 hours)
  • Replace console with logger (1 hour)
  • Add critical test coverage (4-5 hours)

Phase 2: High-Priority Fixes (2-3 days)

  • Complete error handling tests
  • Implement access control tests
  • Add validation tests
  • Admin role enforcement
  • Documentation

Phase 3: Hardening (3-5 days)

  • Performance testing under load
  • Security audit of RPC integration
  • Database backup/recovery procedures
  • Monitoring and alerting setup
  • Load testing rate limiters

Compliance Checklist

Before Production Deployment ✓

Security:

  • All console logging replaced with logger
  • No sensitive data (private keys, secrets) in logs
  • Rate limiting enforced on all endpoints
  • Access control verified with tests
  • Input validation complete and tested
  • Response shapes validated

Reliability:

  • Database transactions wrapping critical operations
  • Error handling comprehensive with retry logic
  • Graceful shutdown implemented ✓
  • Health checks in place ✓
  • Migrations reversible ✓

Operations:

  • All environment variables documented
  • Configuration validated at startup ✓
  • Logs rotated and archived
  • Database backups scheduled
  • Monitoring configured
  • Runbooks for common issues

Quality:

  • Critical path tests passing
  • Error scenarios tested
  • Rate limiting tested
  • Access control tested
  • Configuration tested
  • Code coverage > 80% for critical paths

Reference Files

Audit Performed On:


Audit Completed: May 28, 2026
Recommend Review With: Tech Lead, DevOps, QA before production cutover