This document describes the implementation of request payload sanitization for the StellarFlow backend (issue #150). The middleware prevents injection attacks and enforces strict data type validation across all price-related endpoints.
Goal: Prevent injection and "mangled data" from entering the pipeline.
Key Requirements:
- Ensure
pricevalues are always valid i128-compatible strings - Validate
symbolmatches uppercase whitelist - Prevent SQL injection, XSS, and prototype pollution attacks
- Maintain data integrity through the entire pipeline
Uses Joi for schema-based validation with the following components:
- Pattern:
/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/ - Range: -170,141,183,460,469,231,731,687,303,715,884,105,728 to 170,141,183,460,469,231,731,687,303,715,884,105,727
- Function:
isValidI128String()- Validates Stellar i128 compatibility
["NGN", "GHS", "KES", "ZAR", "XLM"]priceSchema- Accepts numbers or i128-compatible stringscurrencySchema- 3-letter uppercase codes from whitelistsourceSchema- Alphanumeric, max 100 charactersmemoIdSchema- Format:SF-CURRENCY-TIMESTAMP-SEQUENCEpriceUpdateMultiSigRequestSchema- Full request object validationsignatureRequestSchema- Signature request validationmarketRateQuerySchema- Query parameter validation
Implements Express middleware functions that use the schemas:
createPayloadSanitizer(schema, name, stripUnknown)
- Generic middleware factory
- Validates request body against Joi schema
- Logs validation failures for security auditing
- Returns 400 with detailed error messages on failure
sanitizeMultiSigRequest
- Applied to:
POST /api/v1/price-updates/multi-sig/request - Validates: priceReviewId, currency, rate, source, memoId
- Rejects unknown fields
sanitizeSignatureRequest
- Applied to:
POST /api/v1/price-updates/sign - Validates: multiSigPriceId
- Prevents signature injection
sanitizeMarketRateQuery
- Applied to:
GET /api/v1/market-rates/rate/:currency - Validates URL parameters (converted to body-like format)
- Normalizes currency to uppercase
sanitizeGenericPayload
- Applied globally to all requests
- Removes dangerous fields:
__proto__,constructor,prototype,eval,exec,system,shell - Detects suspicious patterns:
<,>, script tags, etc.
validatePriceAndCurrency(priceField, currencyField)
- Middleware factory for endpoints accepting both price and currency
- Validates positive numeric price
- Validates 3-letter uppercase currency
- Normalizes data
logPayload
- Logs all incoming requests for audit trails
- Masks sensitive fields:
secret,password,token,apiKey
Routes updated with sanitization middleware:
router.post("/multi-sig/request", sanitizeMultiSigRequest, handler);
router.post("/sign", sanitizeSignatureRequest, handler);router.get("/rate/:currency", sanitizeMarketRateQuery, handler);| Attack Type | Prevention Method | Example |
|---|---|---|
| SQL Injection | Input whitelist + schema validation | "NGN'; DROP TABLE prices; --" → 400 Rejected |
| XSS/Script Injection | Alphanumeric + pattern validation | "<script>alert('xss')</script>" → 400 Rejected |
| Prototype Pollution | Explicit field rejection | __proto__: { admin: true } → 400 Rejected |
| Type Confusion | Explicit type validation | price: "invalid" → 400 Rejected |
| Out-of-Range Values | i128 BigInt validation | price: "999...overflow" → 400 Rejected |
| Unexpected Fields | unknown(false) in schema |
Extra fields → 400 Rejected |
- All validation failures logged with:
- Client IP address
- Route path
- Error details
- Timestamp
- Request context
- Currency codes automatically uppercased
- Decimal prices converted to string for precision
- Whitespace trimmed
- Invalid data rejected, not silently coerced
Comprehensive test suite at test/payloadSanitization.test.ts covers:
-
i128 Validation
- Valid:
123,123.45,1e5, edge cases - Invalid: overflow, non-numeric, malformed
- Valid:
-
Price Schema
- Valid: positive numbers and strings
- Invalid: negative, zero, null, non-numeric
-
Currency Schema
- Valid: supported codes (auto-uppercased)
- Invalid: unsupported, numbers, wrong length
-
Source Schema
- Valid: alphanumeric with limits
- Invalid: special characters, spaces
-
Memo ID Schema
- Valid: SF-XXX-1234567890-YYY format
- Invalid: malformed, wrong currency case
-
Multi-Sig Requests
- Valid: complete payload with all fields
- Invalid: missing fields, unknown fields
-
Injection Tests
- SQL injection attempts
- XSS/script payload attempts
- Prototype pollution attempts
Run tests:
npm run test # or
tsx test/payloadSanitization.test.tsnpm installThe package.json has been updated with:
{
"dependencies": {
"joi": "^17.11.0"
}
}Already done in:
src/routes/priceUpdates.tssrc/routes/marketRates.ts
npm run test:jest # or specific test file
tsx test/payloadSanitization.test.ts- No database migrations needed
- No configuration changes needed
- Middleware operates purely on HTTP request/response layer
Requests that previously succeeded but contained malformed data now return 400 Bad Request:
{
"success": false,
"error": "Invalid request payload: currency: \"Currency must be one of: NGN, GHS, KES, ZAR, XLM\"",
"details": "MultiSigRequest validation failed"
}- 400 Bad Request - Validation failed
- 500 Internal Server Error - Unexpected validation error (rare)
- Valid requests remain unchanged
- Only malformed/injection attempts are rejected
- Non-required fields with correct types pass through
Supported currencies whitelist (edit in src/middleware/validationSchemas.ts):
export const SUPPORTED_CURRENCIES = ["NGN", "GHS", "KES", "ZAR", "XLM"];To add new currencies:
- Add to
SUPPORTED_CURRENCIESarray - Ensure Prisma
Currencymodel has entry - Redeploy
All validation failures are logged to the audit trail. Monitor for:
- Spike in 400 errors - Possible injection attacks
- Failed multi-sig requests - Data quality issues
- Repeated failures from same IP - Potential attacker
- Negligible - Joi schemas are highly optimized
- Validation typically <1ms per request
- Errors short-circuit early
- Rate Limiting by Error Type - Throttle repeated injection attempts
- Geo-Blocking - Block requests from suspicious origins
- Signature Verification - Add HMAC validation for sensitive endpoints
- Machine Learning - Detect anomalous patterns in price data
- Custom Validators - Domain-specific validation rules
- Joi Documentation: https://joi.dev/
- Stellar i128: https://developers.stellar.org/docs/smart-contracts/soroban
- OWASP Injection Prevention: https://owasp.org/www-community/attacks/injection_attacks
- OWASP Prototype Pollution: https://owasp.org/www-community/attacks/Prototype_pollution
For questions or issues about payload sanitization:
- Check test suite in
test/payloadSanitization.test.ts - Review schema definitions in
src/middleware/validationSchemas.ts - Check middleware flow in
src/middleware/payloadSanitizer.ts - Review route-specific integration in
src/routes/