diff --git a/README.md b/README.md index 2eeade0..cfee2d1 100644 --- a/README.md +++ b/README.md @@ -27,4 +27,27 @@ services: image: postgres:15 environment: POSTGRES_DB: tradeflow - \ No newline at end of file +``` + +## 🔒 Security & CORS + +The API is configured with strict Cross-Origin Resource Sharing (CORS) policies to ensure secure communication: + +- **Allowed Origins**: + - `http://localhost:3000` (Local Development) + - `https://tradeflow-web.vercel.app` (Production) +- **Allowed Methods**: `GET`, `POST`, `PUT`, `PATCH` + +### Verifying CORS +To verify the CORS configuration, ensure dependencies are installed and the server is running, then execute the test script: + +```bash +# 1. Install dependencies +npm install + +# 2. Start the server +npm run start + +# 3. Run the CORS verification script (in a new terminal) +node test-cors.js +``` diff --git a/src/main.ts b/src/main.ts index 8a9998d..5f2d972 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,15 @@ import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; async function bootstrap() { const app = await NestFactory.create(AppModule); - app.enableCors(); // Vital for Frontend connection + // Configure CORS for secure cross-origin communication + app.enableCors({ + origin: [ + 'http://localhost:3000', // Local development environment + 'https://tradeflow-web.vercel.app', // Production environment + ], + methods: ['GET', 'POST', 'PUT', 'PATCH'], + credentials: true, + }); const config = new DocumentBuilder() .setTitle('TradeFlow API') diff --git a/test-cors.js b/test-cors.js new file mode 100644 index 0000000..2697692 --- /dev/null +++ b/test-cors.js @@ -0,0 +1,63 @@ +const http = require('http'); + +const options = { + hostname: 'localhost', + port: 3000, + path: '/health', + method: 'GET', + headers: { + 'Origin': 'http://localhost:3000' + } +}; + +function checkCors(origin, expectedStatus, expectedAllowOrigin) { + return new Promise((resolve, reject) => { + const reqOptions = { ...options, headers: { 'Origin': origin } }; + const req = http.request(reqOptions, (res) => { + const allowOrigin = res.headers['access-control-allow-origin']; + console.log(`Origin: ${origin}, Status: ${res.statusCode}, Access-Control-Allow-Origin: ${allowOrigin}`); + + if (expectedAllowOrigin) { + if (allowOrigin === expectedAllowOrigin) { + resolve(true); + } else { + reject(new Error(`Expected ${expectedAllowOrigin}, got ${allowOrigin}`)); + } + } else { + if (!allowOrigin) { + resolve(true); + } else { + reject(new Error(`Expected no Access-Control-Allow-Origin, got ${allowOrigin}`)); + } + } + }); + + req.on('error', (e) => { + reject(e); + }); + + req.end(); + }); +} + +async function runTests() { + try { + console.log('Testing Allowed Origin 1: http://localhost:3000'); + await checkCors('http://localhost:3000', 200, 'http://localhost:3000'); + + console.log('Testing Allowed Origin 2: https://tradeflow-web.vercel.app'); + await checkCors('https://tradeflow-web.vercel.app', 200, 'https://tradeflow-web.vercel.app'); + + console.log('Testing Disallowed Origin: http://evil.com'); + await checkCors('http://evil.com', 200, undefined); // Should not return the header + + console.log('All CORS tests passed!'); + } catch (error) { + console.error('CORS Test Failed:', error.message); + process.exit(1); + } +} + +// Give server a moment to start if needed, but this script assumes it's running. +// If run via a command that starts server then runs this, we need a wait. +runTests();