Bound HTTP metrics route-label cardinality to prevent a metric-explosion DoS
Description
MetricsService.trackHttpRequest in src/observability/metrics-service.ts labels http_requests_total and http_request_duration_seconds with route = extractRoute(req). If extractRoute ever falls back to a raw URL/path (rather than the matched Express route template), then paths embedding identifiers — /contracts/abc, /contracts/def, … — each create a new Prometheus time series. An attacker hitting many distinct paths can blow up label cardinality, exhausting memory in the process and in the scraping Prometheus. This is distinct from existing header-redaction and per-provider-gauge work; it is specifically about the unbounded route label.
Requirements and context
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Ensure
extractRoute resolves to the matched route template (e.g. req.route?.path joined with baseUrl) and never the concrete path with embedded ids; collapse unmatched requests to a single unmatched bucket.
- Add a hard cap on distinct
route label values; once exceeded, attribute further routes to an other bucket so cardinality cannot grow without bound.
- Keep
method and status_code labels intact; do not change metric names.
- Make the cap configurable via validated config in
src/config/env.schema.ts.
Suggested execution
- Fork the repo and create a branch
git checkout -b security/metrics-route-cardinality-cap
- Implement changes
- Write code in:
src/observability/metrics-service.ts.
- Write comprehensive tests in: create
src/observability/metrics-service.test.ts — fire many distinct concrete paths and assert the number of route label values stays bounded and unmatched paths collapse.
- Add documentation: note the cardinality guard in the observability/health section of
README.md.
- Add TSDoc to
extractRoute and the cap logic.
- Validate security: distinct user-controlled paths cannot create unbounded series.
- Test and commit
Test and commit
- Run
npm run lint and npm test.
- Cover edge cases: matched template, unmatched 404, cap boundary, high-cardinality flood.
- Include the full
npm test output and a security notes section in the PR.
Example commit message
fix(observability): bound HTTP metrics route-label cardinality
Guidelines
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
Community & contribution rewards
- 💬 Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- ⭐ This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project — if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward.
Bound HTTP metrics route-label cardinality to prevent a metric-explosion DoS
Description
MetricsService.trackHttpRequestinsrc/observability/metrics-service.tslabelshttp_requests_totalandhttp_request_duration_secondswithroute = extractRoute(req). IfextractRouteever falls back to a raw URL/path (rather than the matched Express route template), then paths embedding identifiers —/contracts/abc,/contracts/def, … — each create a new Prometheus time series. An attacker hitting many distinct paths can blow up label cardinality, exhausting memory in the process and in the scraping Prometheus. This is distinct from existing header-redaction and per-provider-gauge work; it is specifically about the unboundedroutelabel.Requirements and context
extractRouteresolves to the matched route template (e.g.req.route?.pathjoined withbaseUrl) and never the concrete path with embedded ids; collapse unmatched requests to a singleunmatchedbucket.routelabel values; once exceeded, attribute further routes to anotherbucket so cardinality cannot grow without bound.methodandstatus_codelabels intact; do not change metric names.src/config/env.schema.ts.Suggested execution
git checkout -b security/metrics-route-cardinality-capsrc/observability/metrics-service.ts.src/observability/metrics-service.test.ts— fire many distinct concrete paths and assert the number ofroutelabel values stays bounded and unmatched paths collapse.README.md.extractRouteand the cap logic.Test and commit
npm run lintandnpm test.npm testoutput and a security notes section in the PR.Example commit message
fix(observability): bound HTTP metrics route-label cardinalityGuidelines
Community & contribution rewards