ed08ff84ff
Two findings from the GHSA-pwx6-5pqc-c5xq scan bundle, both verified against
the code and reproduced before fixing.
**Unauthenticated denial of service via password strength (csf_495d53fa).**
POST /api/auth/password-strength takes `body('password').notEmpty()` with no
upper bound, sits behind express.json({ limit: '50mb' }), and hands the string
to zxcvbn, whose matching is superlinear and runs synchronously on the event
loop. Measured on this codebase, in ms of blocked loop: 128 -> 41, 512 -> 1367,
1000 -> 5097, 5000 -> did not return in two minutes. One unauthenticated
request of about a kilobyte stops the whole process for five seconds; a few
kilobytes stops it indefinitely. The /api/auth rate limit does not help when a
single request is already enough.
The cap lives in validatePassword() so it covers every caller, present and
future; the route validator is defence in depth. 128 keeps the worst case at
the cost of an ordinary request while staying far above any real password --
bcrypt consumes only the first 72 bytes, so length past that adds no entropy to
the stored hash anyway. This is the only unauthenticated reach into zxcvbn:
setup is token-gated and self-closing, and acceptInvite/adminAuth use the
regex-only validator in passwordGenerator.
**The /photos and /thumbnails static mounts (csf_9aa6afe6, csf_559cd5cc,
csf_b14d462e, csf_547d26fa, and the gallery half of csf_34e420af).**
They served the raw originals and thumbnail trees behind photoAuth, which
authorises on a slug match. A static file server cannot apply per-photo rules,
so everything the gallery API decides was absent: allow_downloads, per-category
allow_downloads, watermarking, the resolution cap, reveal-mode windows,
visibility='hidden', download logging, and the customer-assignment re-check
that makes revocation immediate. photoAuth also bcrypt-compares an
x-gallery-password header per request with no limiter -- both rate-limit gates
return early for non-/api paths -- so the mount was an unmetered password
oracle. The filenames needed to drive all of this are handed to every guest in
the photos listing.
Nothing builds these URLs: no reference in frontend/src, none in the email
templates, and the only backend mentions are the /api/admin/photos/... API
routes and a maintenance-mode prefix list. The equivalent authorised routes are
/api/gallery/:slug/photo/:id and /thumbnail/:id. nginx still proxies the two
locations; they now 404, which is the intent.
**The /uploads mount (csf_1fc92f57).** It exposed the whole uploads/ root with
no auth middleware at all, and that root also holds signed contract PDFs
(uploads/contracts/signed) and client transfer files (uploads/transfers/<id>),
reachable by anyone who learned or guessed a filename. Narrowed to the two
public asset trees it exists for; contracts and transfers keep their own
authorised routes.
Removing the mounts leaves src/middleware/photoAuth.js unreferenced by
application code. Left in place deliberately -- deleting it and its tests is a
separate cleanup, and a smaller diff backports more safely.
Backend suite: 2742 passed.
(cherry picked from commit 14cd5eacb3)
Enhanced Backup System Test Suite
This directory contains comprehensive tests for the enhanced backup system with S3 support.
Test Structure
Unit Tests
services/backupService.enhanced.test.js- Unit tests for the enhanced backup service- Configuration management
- S3 backup functionality
- Manifest generation
- Error handling and recovery
- Backward compatibility (local and rsync)
- Service lifecycle management
Integration Tests
integration/backup-s3.test.js- Integration tests for S3 backups- Real S3/MinIO connection tests
- Full backup process with actual files
- Incremental backup verification
- Manifest storage and retrieval
- Error recovery scenarios
Manual Integration Test Script
../scripts/test-backup-integration.js- Comprehensive manual testing script- Can test against MinIO, AWS S3, or any S3-compatible service
- Tests all backup types (S3, local, rsync)
- Performance testing with large files
- Detailed progress reporting
Running Tests
Prerequisites
-
For Unit Tests: No special setup required, all dependencies are mocked.
-
For Integration Tests: Requires a running S3-compatible service (MinIO recommended)
# Start MinIO using Docker docker run -d \ -p 9000:9000 \ -p 9001:9001 \ --name minio-test \ -e MINIO_ROOT_USER=minioadmin \ -e MINIO_ROOT_PASSWORD=minioadmin \ minio/minio server /data --console-address ":9001" -
Environment Variables (for integration tests):
# Optional - defaults work with local MinIO export TEST_S3_ENDPOINT=http://localhost:9000 export TEST_S3_ACCESS_KEY=minioadmin export TEST_S3_SECRET_KEY=minioadmin # Skip S3 tests if no S3 service available export SKIP_S3_TESTS=true
Running Unit Tests
# Run all backup service tests
npm test -- __tests__/services/backupService.enhanced.test.js
# Run specific test suite
npm test -- __tests__/services/backupService.enhanced.test.js -t "S3 Backup Functionality"
# Run with coverage
npm test -- --coverage __tests__/services/backupService.enhanced.test.js
Running Integration Tests
# Ensure MinIO is running first!
# Run S3 integration tests
npm test -- __tests__/integration/backup-s3.test.js
# Run with verbose output
npm test -- __tests__/integration/backup-s3.test.js --verbose
# Skip S3 tests if needed
SKIP_S3_TESTS=true npm test -- __tests__/integration/backup-s3.test.js
Running Manual Integration Tests
# Test with local MinIO (default)
node scripts/test-backup-integration.js
# Test with AWS S3
node scripts/test-backup-integration.js \
--endpoint https://s3.amazonaws.com \
--access-key YOUR_ACCESS_KEY \
--secret-key YOUR_SECRET_KEY \
--bucket your-test-bucket
# Test local backup
node scripts/test-backup-integration.js --type local
# Test with cleanup after completion
node scripts/test-backup-integration.js --cleanup
# Verbose output
node scripts/test-backup-integration.js --verbose
Test Coverage
The test suite covers:
Configuration
- ✅ Database configuration retrieval
- ✅ JSON parsing and error handling
- ✅ Configuration validation
- ✅ Required field validation
S3 Functionality
- ✅ S3 client initialization
- ✅ Connection testing
- ✅ File upload with progress tracking
- ✅ Large file handling (multipart upload)
- ✅ Metadata and custom headers
- ✅ Error handling and retries
Backup Process
- ✅ Full backup execution
- ✅ Incremental backup (changed files only)
- ✅ File checksum calculation and comparison
- ✅ Database backup inclusion
- ✅ Archive inclusion toggle
- ✅ File size limits
Manifest Generation
- ✅ Full manifest generation
- ✅ Incremental manifest with parent reference
- ✅ JSON and YAML format support
- ✅ Manifest validation
- ✅ S3 manifest storage and retrieval
- ✅ Checksum verification
Error Handling
- ✅ S3 connection failures
- ✅ File read errors
- ✅ Individual file failure recovery
- ✅ Retry logic with exponential backoff
- ✅ Email notifications on failure
- ✅ Concurrent backup prevention
Backward Compatibility
- ✅ Local directory backup
- ✅ Rsync backup
- ✅ Existing manifest format support
Service Management
- ✅ Cron job scheduling
- ✅ Service start/stop
- ✅ Manual backup triggering
- ✅ Backup history and status
Mock Setup
The unit tests use comprehensive mocking:
// Database mocking
jest.mock('../../src/database/db');
// S3 client mocking
jest.mock('../../src/services/storage/s3Storage');
// File system mocking
const mockFs = require('mock-fs');
// Cron job mocking
jest.mock('node-cron');
CI/CD Integration
To run tests in CI/CD pipeline:
# Example GitHub Actions
- name: Run Unit Tests
run: npm test -- __tests__/services/backupService.enhanced.test.js
- name: Start MinIO
run: |
docker run -d \
-p 9000:9000 \
--name minio-test \
-e MINIO_ROOT_USER=minioadmin \
-e MINIO_ROOT_PASSWORD=minioadmin \
minio/minio server /data
- name: Run Integration Tests
run: npm test -- __tests__/integration/backup-s3.test.js
Debugging Tests
# Run tests in debug mode
node --inspect-brk ./node_modules/.bin/jest __tests__/services/backupService.enhanced.test.js
# Run single test with console output
npm test -- __tests__/services/backupService.enhanced.test.js -t "should perform S3 backup" --verbose
Performance Considerations
- Integration tests create real files and S3 objects
- Each test run creates a unique S3 bucket to avoid conflicts
- Cleanup is automatic but can be disabled for debugging
- Large file tests (10MB+) are included but can be slow
Adding New Tests
When adding new backup features:
- Add unit tests to
backupService.enhanced.test.js - Add integration tests to
backup-s3.test.jsif S3-specific - Update manual test script for comprehensive testing
- Ensure mocks are properly configured
- Document any new environment requirements