Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion BackEnd/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,9 @@ WS_PING_INTERVAL=25000
WS_PING_TIMEOUT=20000
WS_MESSAGE_HISTORY_LIMIT=200

# CORS Configuration
# CORS_ORIGINS is also used for WS allowed origins (comma-separated)
# CORS_ORIGINS=http://localhost:3000,http://localhost:3001
CORS_ORIGINS=http://localhost:3000,http://localhost:3001

# Stellar Network Configuration
STELLAR_NETWORK=testnet
Expand Down
8,986 changes: 5,499 additions & 3,487 deletions BackEnd/package-lock.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions BackEnd/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"@nestjs/platform-socket.io": "^11.1.17",
"@nestjs/schedule": "^6.1.0",
"@nestjs/swagger": "^11.2.5",
"@nestjs/terminus": "^9.1.2",
"@nestjs/terminus": "^11.0.0",
"@nestjs/throttler": "^6.5.0",
"@nestjs/typeorm": "^11.0.0",
"@nestjs/websockets": "^11.1.17",
Expand All @@ -65,8 +65,8 @@
"@opentelemetry/sdk-trace-node": "^2.7.0",
"@opentelemetry/semantic-conventions": "^1.40.0",
"@sendgrid/mail": "^8.1.4",
"@sentry/nestjs": "^8.53.0",
"@sentry/node": "^8.53.0",
"@sentry/nestjs": "^9.0.0",
"@sentry/node": "^9.0.0",
"@stellar/stellar-sdk": "^15.0.1",
"@types/crypto-js": "^4.2.2",
"@types/passport-github2": "^1.2.9",
Expand Down Expand Up @@ -104,8 +104,8 @@
"devDependencies": {
"@eslint/eslintrc": "^0.1.0",
"@eslint/js": "^9.18.0",
"@nestjs/cli": "^7.6.0",
"@nestjs/schematics": "^7.3.1",
"@nestjs/cli": "^11.0.0",
"@nestjs/schematics": "^11.0.0",
"@nestjs/testing": "^11.0.1",
"@types/bcrypt": "^6.0.0",
"@types/express": "^5.0.0",
Expand Down
82 changes: 82 additions & 0 deletions BackEnd/src/common/dto/response-wrapper.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { ApiProperty } from '@nestjs/swagger';

/**
* Standardized response wrapper for all API responses
* This matches the structure applied by ResponseFormatInterceptor
*/
export class ApiResponseDto<T> {
@ApiProperty({
description: 'Response data payload',
example: {},
})
data: T;

@ApiProperty({
description: 'Response metadata including timestamp',
type: 'object',
example: {
timestamp: 1700000000000,
},
})
meta: {
timestamp: number;
};
}

/**
* Generic metadata object for responses
*/
export class ResponseMetaDto {
@ApiProperty({
description: 'Unix timestamp in milliseconds',
example: 1700000000000,
})
timestamp: number;
}

/**
* Paginated response wrapper with metadata
*/
export class PaginatedResponseDto<T> {
@ApiProperty({
description: 'Array of items',
type: Array,
})
data: T[];

@ApiProperty({
description: 'Pagination metadata',
type: 'object',
})
meta: {
timestamp: number;
total: number;
page: number;
limit: number;
totalPages: number;
};
}

/**
* Success response wrapper
*/
export class SuccessResponseDto {
@ApiProperty({
description: 'Success indicator',
example: true,
})
success: boolean;

@ApiProperty({
description: 'Response message',
example: 'Operation completed successfully',
required: false,
})
message?: string;

@ApiProperty({
description: 'Response data payload',
required: false,
})
data?: any;
}
8 changes: 4 additions & 4 deletions BackEnd/src/config/cors.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.int
*/
export const getCorsConfig = (): CorsOptions => {
// Get allowed origins from environment variable
const allowedOrigins = process.env.CORS_ORIGINS
? process.env.CORS_ORIGINS.split(',').map(origin => origin.trim())
: ['http://localhost:3000', 'http://localhost:3001'];
const allowedOrigins = process.env.CORS_ORIGINS
? process.env.CORS_ORIGINS.split(',').map((origin) => origin.trim())
: [];

return {
// Specify allowed origins
Expand Down Expand Up @@ -68,4 +68,4 @@ export const getCorsConfig = (): CorsOptions => {
// Return status 204 for successful preflight
optionsSuccessStatus: 204,
};
};
};
46 changes: 35 additions & 11 deletions BackEnd/src/config/security.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ export const getApplicationSecurityConfig = (
configService: ConfigService,
): ApplicationSecurityConfig => {
const environment = configService.get<string>('NODE_ENV', 'development');
const jsonBodyLimit = configService.get<string>('SECURITY_JSON_BODY_LIMIT', '1mb');
const jsonBodyLimit = configService.get<string>(
'SECURITY_JSON_BODY_LIMIT',
'1mb',
);
const urlencodedBodyLimit = configService.get<string>(
'SECURITY_URLENCODED_BODY_LIMIT',
'256kb',
Expand All @@ -101,12 +104,18 @@ export const getApplicationSecurityConfig = (
configService.get<string>('JWT_SECRET') ||
'local-dev-request-signature-secret',
},
allowedOrigins: toList(configService.get<string>('CORS_ORIGIN')).map((origin) =>
origin.replace(/\/$/, ''),
allowedOrigins: toList(configService.get<string>('CORS_ORIGINS')).map(
(origin) => origin.replace(/\/$/, ''),
),
headers: {
csrfCookieName: configService.get<string>('CSRF_COOKIE_NAME', '__Host-csrf-token'),
csrfHeaderName: configService.get<string>('CSRF_HEADER_NAME', 'x-csrf-token'),
csrfCookieName: configService.get<string>(
'CSRF_COOKIE_NAME',
'__Host-csrf-token',
),
csrfHeaderName: configService.get<string>(
'CSRF_HEADER_NAME',
'x-csrf-token',
),
signatureHeaderName: configService.get<string>(
'REQUEST_SIGNATURE_HEADER',
'x-request-signature',
Expand All @@ -119,7 +128,10 @@ export const getApplicationSecurityConfig = (
'REQUEST_SIGNATURE_NONCE_HEADER',
'x-signature-nonce',
),
auditHeaderName: configService.get<string>('SECURITY_AUDIT_HEADER', 'x-security-audit-id'),
auditHeaderName: configService.get<string>(
'SECURITY_AUDIT_HEADER',
'x-security-audit-id',
),
},
limits: {
jsonBodyLimit,
Expand All @@ -130,7 +142,10 @@ export const getApplicationSecurityConfig = (
),
maxHeaderCount: configService.get<number>('SECURITY_MAX_HEADERS', 80),
maxUrlLength: configService.get<number>('SECURITY_MAX_URL_LENGTH', 2048),
maxParameterDepth: configService.get<number>('SECURITY_MAX_PARAM_DEPTH', 8),
maxParameterDepth: configService.get<number>(
'SECURITY_MAX_PARAM_DEPTH',
8,
),
},
detection: {
blockSuspiciousRequests: configService.get<boolean>(
Expand All @@ -154,7 +169,10 @@ export const getApplicationSecurityConfig = (
'REQUEST_SIGNATURE_TOLERANCE_MS',
5 * 60 * 1000,
),
maxAuditBodyLength: configService.get<number>('SECURITY_AUDIT_BODY_MAX_LENGTH', 1024),
maxAuditBodyLength: configService.get<number>(
'SECURITY_AUDIT_BODY_MAX_LENGTH',
1024,
),
},
reputation: {
blockedIps: toList(configService.get<string>('SECURITY_BLOCKED_IPS')),
Expand All @@ -171,16 +189,22 @@ export const getApplicationSecurityConfig = (
/**
* Security configuration for Helmet middleware.
*/
export const getSecurityConfig = (configService: ConfigService): HelmetOptions => {
export const getSecurityConfig = (
configService: ConfigService,
): HelmetOptions => {
const appSecurity = getApplicationSecurityConfig(configService);

const cspDirectives: Record<string, any> = {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'],
scriptSrc: ["'self'"],
imgSrc: ["'self'", 'data:', 'https:'],
fontSrc: ["'self'", 'https://fonts.gstatic.com'],
connectSrc: ["'self'", 'https://api.stellar.org', ...appSecurity.allowedOrigins],
connectSrc: [
"'self'",
'https://api.stellar.org',
...appSecurity.allowedOrigins,
],
objectSrc: ["'none'"],
mediaSrc: ["'none'"],
frameSrc: ["'none'"],
Expand Down
Loading
Loading