This commit implements Phase 3 of the Sacred Pipeline, focusing on enhanced error handling. It includes: - **Transaction Status Polling Endpoint**: A new edge function `check-transaction-status` allows clients to poll the status of moderation transactions using idempotency keys. - **Expanded Error Sanitizer Patterns**: The `src/lib/errorSanitizer.ts` file has been updated with more comprehensive patterns to remove sensitive information from error messages, making them safer for display and logging. User-friendly replacements for common errors are also included. - **Rate Limiting for Submission Creation**: Client-side rate limiting has been implemented in `src/lib/submissionRateLimiter.ts` and applied to key submission functions within `src/lib/entitySubmissionHelpers.ts` (e.g., `submitParkCreation`, `submitRideCreation`, `submitParkUpdate`, `submitRideUpdate`) to prevent abuse and accidental duplicate submissions.
8.7 KiB
Phase 3: Enhanced Error Handling - COMPLETE
Status: ✅ Fully Implemented Date: 2025-01-07
Overview
Phase 3 adds comprehensive error handling improvements to the Sacred Pipeline, including transaction status polling, enhanced error sanitization, and client-side rate limiting for submission creation.
Components Implemented
1. Transaction Status Polling Endpoint
Edge Function: check-transaction-status
Purpose: Allows clients to poll the status of moderation transactions using idempotency keys
Features:
- Query transaction status by idempotency key
- Returns detailed status information (pending, processing, completed, failed, expired)
- User authentication and authorization (users can only check their own transactions)
- Structured error responses
- Comprehensive logging
Usage:
const { data, error } = await supabase.functions.invoke('check-transaction-status', {
body: { idempotencyKey: 'approval_submission123_...' }
});
// Response includes:
// - status: 'pending' | 'processing' | 'completed' | 'failed' | 'expired' | 'not_found'
// - createdAt, updatedAt, expiresAt
// - attempts, lastError (if failed)
// - action, submissionId
API Endpoints:
POST /check-transaction-status- Check status by idempotency key- Requires: Authentication header
- Returns: StatusResponse with transaction details
2. Error Sanitizer
File: src/lib/errorSanitizer.ts
Purpose: Removes sensitive information from error messages before display or logging
Sensitive Patterns Detected:
- Authentication tokens (Bearer, JWT, API keys)
- Database connection strings (PostgreSQL, MySQL)
- Internal IP addresses
- Email addresses in error messages
- UUIDs (internal IDs)
- File paths (Unix & Windows)
- Stack traces with file paths
- SQL queries revealing schema
User-Friendly Replacements:
- Database constraint errors → "This item already exists", "Required field missing"
- Auth errors → "Session expired. Please log in again"
- Network errors → "Service temporarily unavailable"
- Rate limiting → "Rate limit exceeded. Please wait before trying again"
- Permission errors → "Access denied"
Functions:
sanitizeErrorMessage(error, context?)- Main sanitization functioncontainsSensitiveData(message)- Check if message has sensitive datasanitizeErrorForLogging(error)- Sanitize for external loggingcreateSafeErrorResponse(error, fallbackMessage?)- Create user-safe error response
Examples:
import { sanitizeErrorMessage } from '@/lib/errorSanitizer';
try {
// ... operation
} catch (error) {
const safeMessage = sanitizeErrorMessage(error, {
action: 'park_creation',
userId: user.id
});
toast({
title: 'Error',
description: safeMessage,
variant: 'destructive'
});
}
3. Submission Rate Limiting
File: src/lib/submissionRateLimiter.ts
Purpose: Client-side rate limiting to prevent submission abuse and accidental duplicates
Rate Limits:
- Per Minute: 5 submissions maximum
- Per Hour: 20 submissions maximum
- Cooldown: 60 seconds after exceeding limits
Features:
- In-memory rate limit tracking (per session)
- Automatic timestamp cleanup
- User-specific limits
- Cooldown period after limit exceeded
- Detailed logging
Integration: Applied to all submission functions in entitySubmissionHelpers.ts:
submitParkCreationsubmitParkUpdatesubmitRideCreationsubmitRideUpdate- Composite submissions
Functions:
checkSubmissionRateLimit(userId, config?)- Check if user can submitrecordSubmissionAttempt(userId)- Record a submission (called after success)getRateLimitStatus(userId)- Get current rate limit statusclearUserRateLimit(userId)- Clear limits (admin/testing)
Usage:
// In entitySubmissionHelpers.ts
function checkRateLimitOrThrow(userId: string, action: string): void {
const rateLimit = checkSubmissionRateLimit(userId);
if (!rateLimit.allowed) {
throw new Error(sanitizeErrorMessage(rateLimit.reason));
}
}
// Called at the start of every submission function
export async function submitParkCreation(data, userId) {
checkRateLimitOrThrow(userId, 'park_creation');
// ... rest of submission logic
}
Response Example:
{
allowed: false,
reason: 'Too many submissions in a short time. Please wait 60 seconds',
retryAfter: 60
}
Architecture Adherence
✅ No JSON/JSONB: Error sanitizer operates on strings, rate limiter uses in-memory storage
✅ Relational: Transaction status queries the idempotency_keys table
✅ Type Safety: Full TypeScript types for all interfaces
✅ Logging: Comprehensive structured logging for debugging
Security Benefits
- Sensitive Data Protection: Error messages no longer expose internal details
- Rate Limit Protection: Prevents submission flooding and abuse
- Transaction Visibility: Users can check their own transaction status safely
- Audit Trail: All rate limit events logged for security monitoring
Error Flow Integration
User Action
↓
Rate Limit Check ────→ Block if exceeded
↓
Submission Creation
↓
Error Occurs ────→ Sanitize Error Message
↓
Display to User (Safe Message)
↓
Log to System (Detailed, Sanitized)
Testing Checklist
- Edge function deploys successfully
- Transaction status polling works with valid keys
- Transaction status returns 404 for invalid keys
- Users cannot access other users' transaction status
- Error sanitizer removes sensitive patterns
- Error sanitizer provides user-friendly messages
- Rate limiter blocks after per-minute limit
- Rate limiter blocks after per-hour limit
- Rate limiter cooldown period works
- Rate limiting applied to all submission functions
- Sanitized errors logged correctly
Related Files
Core Implementation
supabase/functions/check-transaction-status/index.ts- Transaction polling endpointsrc/lib/errorSanitizer.ts- Error message sanitizationsrc/lib/submissionRateLimiter.ts- Client-side rate limitingsrc/lib/entitySubmissionHelpers.ts- Integrated rate limiting
Dependencies
src/lib/idempotencyLifecycle.ts- Idempotency key lifecycle managementsrc/lib/logger.ts- Structured loggingsupabase/functions/_shared/logger.ts- Edge function logging
Performance Considerations
- In-Memory Storage: Rate limiter uses Map for O(1) lookups
- Automatic Cleanup: Old timestamps removed on each check
- Minimal Overhead: Pattern matching optimized with pre-compiled regexes
- Database Queries: Transaction status uses indexed lookup on idempotency_keys.key
Future Enhancements
Potential improvements for future phases:
- Persistent Rate Limiting: Store rate limits in database for cross-session tracking
- Dynamic Rate Limits: Adjust limits based on user reputation/role
- Advanced Sanitization: Context-aware sanitization based on error types
- Error Pattern Learning: ML-based detection of new sensitive patterns
- Transaction Webhooks: Real-time notifications when transactions complete
- Rate Limit Dashboard: Admin UI to view and manage rate limits
API Reference
Check Transaction Status
Endpoint: POST /functions/v1/check-transaction-status
Request:
{
"idempotencyKey": "approval_submission_abc123_..."
}
Response (200 OK):
{
"status": "completed",
"createdAt": "2025-01-07T10:30:00Z",
"updatedAt": "2025-01-07T10:30:05Z",
"expiresAt": "2025-01-08T10:30:00Z",
"attempts": 1,
"action": "approval",
"submissionId": "abc123",
"completedAt": "2025-01-07T10:30:05Z"
}
Response (404 Not Found):
{
"status": "not_found",
"error": "Transaction not found. It may have expired or never existed."
}
Response (401/403):
{
"error": "Unauthorized",
"status": "not_found"
}
Migration Notes
No database migrations required for this phase. All functionality is:
- Edge function (auto-deployed)
- Client-side utilities (imported as needed)
- Integration into existing submission functions
Monitoring
Key metrics to monitor:
- Rate Limit Events: Track users hitting limits
- Sanitization Events: Count messages requiring sanitization
- Transaction Status Queries: Monitor polling frequency
- Error Patterns: Identify common sanitized error types
Query examples in admin dashboard:
-- Rate limit violations (from logs)
SELECT COUNT(*) FROM request_metadata
WHERE error_message LIKE '%Rate limit exceeded%'
GROUP BY DATE(created_at);
-- Transaction status queries
-- (Check edge function logs for check-transaction-status)
Phase 3 Status: ✅ Complete Next Phase: Phase 4 or additional enhancements as needed