Skip to content

Prevent process crashes from malformed query parameters #40

@tim-rohrer

Description

@tim-rohrer

Problem

The API currently crashes and restarts when receiving malformed query parameters like ?limit=abc. This causes parseInt("abc") → NaN which propagates to MongoDB as { $limit: NaN }, triggering a MongoServerError that becomes an unhandled rejection and terminates the Node process.

Current Impact

  • Production returns nginx 502 errors for these requests
  • All in-flight requests on the instance are dropped during crash
  • Service recovers via supervisor restart, but symptom is observable as intermittent 502s
  • At 25-30k visitors/day with no auth/rate limiting, this is trivially exploitable

Affected Paths

  1. ?limit=abc (and other non-numeric limit values)
  2. ?limit=10&limit=20 (repeated query keys - Express returns array, not handled)
  3. ?hours=999 (currently unbounded, can cause timeouts)
  4. ?start=garbage (silent failure, produces empty results)

Proposed Solution

Two changes:

1. Add unhandled rejection handler

In src/index.ts:

process.on("unhandledRejection", (reason) => {
  Logger.error(`Unhandled rejection: ${reason instanceof Error ? reason.stack : reason}`)
  // do not exit — log and continue
})

This is a Node hygiene measure independent of the specific bugs. Node's default behaviour of terminating on unhandled rejection was designed to surface bugs. In a production service, surfacing the bug should be a log line, not a process death. This single handler stops the crash even before the underlying bug is fixed.

2. Add input coercion with bounds

In src/meetings.controller.ts, add a helper function:

const parseIntOrDefault = (raw: unknown, fallback: number, min: number, max: number): number => {
  const s = Array.isArray(raw) ? raw[0] : raw
  const n = typeof s === "string" ? Number.parseInt(s, 10) : NaN
  if (!Number.isInteger(n) || n < min || n > max) return fallback
  return n
}

Apply to query params:

  • limit: use parseIntOrDefault(req.query.limit, 1000, 1, 1000)
  • hours: use parseIntOrDefault(req.query.hours, 24, 1, 168) (168 = 1 week max)

Acceptance Criteria

  • Process does not terminate when receiving malformed query parameters
  • Malformed inputs return 400 status with problem-document body
  • Tests verify each problematic URL pattern:
    • ?limit=abc
    • ?limit=10&limit=20
    • ?hours=999
    • ?start=garbage
  • Tests run in CI before deploy
  • Query parameter bounds documented in code comments

Notes

At 25-30k visitors/day with no auth/rate limiting, anyone can send ?limit=abc repeatedly and hold the service in a perpetual restart loop. Bots, buggy frontends, or debugging sessions will trigger this. The system needs to not die when it happens.

~30 lines of code total, plus tests.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions