Skip to content

fix(config): gate production hardening on isProd predicate (env-gate defect class)#3874

Merged
PierreBrisorgueil merged 6 commits into
masterfrom
fix/prod-env-gate
Jun 15, 2026
Merged

fix(config): gate production hardening on isProd predicate (env-gate defect class)#3874
PierreBrisorgueil merged 6 commits into
masterfrom
fix/prod-env-gate

Conversation

@PierreBrisorgueil

@PierreBrisorgueil PierreBrisorgueil commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • What changed: Export isProd/isDevEnv predicates; gate error-object leak, rate-limiter defaults, swagger docs, and mongoose debug logging off these predicates instead of a literal NODE_ENV==='production' check.
  • Why: The hardening guards were keyed off NODE_ENV === 'production', but downstream projects run NODE_ENV={project} (e.g. NODE_ENV=trawl), so production.config.js never loads for them. The practical effect: full error objects were leaked to API consumers in production, rate-limiter profiles for api and billingPlans routes were silently no-ops, mongoose query logging was on, and /api/docs+/api/spec.json were exposed.
  • Related issues: Closes 🔒 Production env-gate: error leak + swagger/debug/rate-limiter defaults #3852. Part of the security audit epic 🎯 Security audit hardening — Node + Vue (2026-06 audit) #3848.

Scope

  • Module(s) impacted: lib/helpers/config.js, lib/helpers/responses.js, lib/services/express.js, lib/services/mongoose.js, config/defaults/production.config.js, lib/middlewares/, modules/billing/config/, modules/organizations/config/
  • Cross-module impact: yes — shared lib/ predicates consumed by express, mongoose, responses; rate-limiter config consumed by every route that mounts api/billingPlans profiles
  • Risk level: medium — changes observable defaults for any env that is not development/test/localhost

Validation

  • npm run lint
  • npm test — 2017 unit tests pass (--runInBand)
  • Manual checks done (if applicable)

Guardrails check

  • No secrets or credentials introduced (.env*, secrets/**, keys, tokens)
  • No risky rename/move of core stack paths
  • Changes remain merge-friendly for downstream projects
  • Tests added or updated when behavior changed

Infra/Stack alignment details

Before vs After (key changes only)

Area Before After Notes
Error object leak NODE_ENV==='production' guard !isProd() via DEV_ENVS set Correct for downstream envs
api+billingPlans rate limiters Only in production.config.js → inactive downstream Base layer dev-config with lenient caps Same pattern auth already used
Swagger /api/docs+/api/spec.json Always exposed Gated off dev envs See tradeoff note below
Mongoose query debug Always on for non-production Gated off dev envs
isProd / isDevEnv No shared predicate Exported from lib/helpers/config.js DEV_ENVS = new Set(['development','test','localhost'])

Part breakdown

(A) isProd/isDevEnv predicates (lib/helpers/config.js): isDevEnv() returns true for development, test, localhost; isProd() = !isDevEnv(). Both exported.

(B) Error-object leak (lib/helpers/responses.js + express terminal handler): stack traces and raw error objects are now stripped for any env that is not dev/test/localhost — i.e. !isDevEnv() — matching what downstream projects need.

(C) Rate-limiter base layer (lib/middlewares/, modules/billing/config/, modules/organizations/config/): api and billingPlans profiles moved into the dev-config layer with permissive base caps (presence-driven via a Proxy); production.config.js still overrides with strict caps. This aligns api/billingPlans with how auth already behaved.

(D) Swagger + mongoose debug (lib/services/express.js, lib/services/mongoose.js): /api/docs and /api/spec.json are now only served when isDevEnv() is true; mongoose query debug is only enabled in dev envs.

Deliberate tradeoffs

  1. Swagger opt-out-by-default: In any non-dev env (including downstream staging/prod), /api/docs and /api/spec.json are no longer served. A downstream that intentionally publishes production API docs has no escape hatch yet. A future config.swagger.enableInProd flag would allow opting back in — not added here to keep scope tight.

  2. api/billingPlans rate limiters now active at base caps downstream: These were previously silently inactive for all non-production envs. They now apply at lenient base-layer caps to every downstream env, with strict caps binding only under NODE_ENV=production. This is the correct security posture — consistent with how auth already behaved — but downstream configs can tighten the base caps via their own dev-config overrides if needed.

Notes for reviewers

  • Security considerations: this is a defect-class fix — the guards existed but were unreachable for any downstream project name. No new attack surface.
  • Mergeability considerations: downstream projects picking this up via /update-stack need to ensure their dev-config layers define api+billingPlans rate-limiter entries if they want project-specific caps (the base layer defaults are permissive, so it is safe to not define them).
  • Follow-up tasks: swagger.enableInProd escape-hatch flag (🔒 Production env-gate: error leak + swagger/debug/rate-limiter defaults #3852 tracks).

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Prevented internal error details from leaking in production responses
    • Disabled API documentation exposure in production environments
    • Fixed Mongoose debug logging inappropriately enabled in non-development environments
  • New Features

    • Added rate limiting for authenticated API endpoints and billing service endpoints
  • Tests

    • Added comprehensive test coverage for environment-based security gates and error handling behavior

Capture NODE_ENV in beforeEach/afterEach (safe restore even if a sibling
test mutates it before the file loads). Document the inline DEV_ENVS set
in express.docsEnvGate must stay in sync with lib/helpers/config.js.
Add local-env positive cases to docsEnvGate (mounts docs) and
debugGate (debug true) — mirrors the existing development cases.
@PierreBrisorgueil PierreBrisorgueil added the Fix A bug fix label Jun 15, 2026
@PierreBrisorgueil PierreBrisorgueil self-assigned this Jun 15, 2026
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@PierreBrisorgueil, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 50 minutes and 49 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 733a1852-8f87-450b-b520-b60672edce31

📥 Commits

Reviewing files that changed from the base of the PR and between eeaeab3 and 0c83b9b.

📒 Files selected for processing (3)
  • lib/helpers/config.js
  • lib/middlewares/tests/rateLimiter.baseLayer.unit.tests.js
  • lib/services/tests/express.docsEnvGate.unit.tests.js

Walkthrough

Introduces isDevEnv and isProd named predicates in configHelper backed by the existing DEV_ENVS allow-list, then replaces all NODE_ENV === 'production' literal guards in error responses, Swagger mounting, and Mongoose debug logging with these predicates. Rate-limiter config profiles are moved to base-layer configs, and comprehensive unit tests are added for each gate.

Changes

Production env-gate hardening

Layer / File(s) Summary
isDevEnv/isProd predicates and production defaults
lib/helpers/config.js, config/defaults/production.config.js
Defines isDevEnv (allow-list check over DEV_ENVS) and isProd (its inverse) in configHelper, wires isDevEnv into validateJwtSecret, exports both, and adds swagger.enable: false to the production defaults config.
Error response leak gating
lib/helpers/responses.js, lib/helpers/tests/responses.errorLeak.unit.tests.js
Replaces NODE_ENV !== 'production' literal check with !configHelper.isProd() to suppress serialized error payloads for all non-dev deployment environments; new test suite covers both dev-include and prod-exclude behavior with arbitrary env labels.
Express Swagger mount and error handler gating
lib/services/express.js, lib/services/tests/express.docs.unit.tests.js, lib/services/tests/express.docsEnvGate.unit.tests.js, lib/services/tests/express.errorroutes.unit.tests.js
Adds !configHelper.isProd() as a Swagger mount guard in initSwagger and replaces the literal NODE_ENV === 'production' check in initErrorRoutes with configHelper.isProd(); existing Swagger tests mock configHelper as dev-grade; new docsEnvGate and error-routes tests assert correct behavior across dev, prod, and arbitrary env strings.
Mongoose resolveDebug env-gate
lib/services/mongoose.js, lib/services/tests/mongoose.debugGate.unit.tests.js
Adds resolveDebug(cfg) helper that enables Mongoose query logging only when cfg.db.debug is truthy and configHelper.isDevEnv() returns true; connect uses it instead of the raw config flag; new test suite validates the gate across all env label categories.
Rate-limiter base-layer config profiles
modules/organizations/config/organizations.development.config.js, modules/billing/config/billing.development.config.js, lib/middlewares/tests/rateLimiter.baseLayer.unit.tests.js
Moves rateLimit.api into the organizations dev config and stripe.rateLimit.billingPlans into the billing dev config so profiles are always present after config merging regardless of NODE_ENV; regression-guard test suite validates shape of each profile.
isDevEnv/isProd predicate unit tests
lib/helpers/tests/config.envPredicate.unit.tests.js
New Jest test file exercises isDevEnv and isProd for all dev-like labels, production, and arbitrary env strings, including default-argument and unset-NODE_ENV behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant expressService as Express (initErrorRoutes)
  participant configHelper as configHelper.isProd()
  participant responsesHelper as responses.error

  Client->>expressService: HTTP request triggers error
  expressService->>configHelper: isProd()
  configHelper-->>expressService: true (non-dev env) / false (dev env)
  alt isProd() === true
    expressService-->>Client: { message: "Internal Server Error" }
  else isProd() === false
    expressService->>responsesHelper: include serialized error detail
    responsesHelper-->>Client: { message, error: "<serialized details>" }
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • pierreb-devkit/Node#3790: Modifies the same initErrorRoutes error handler in lib/services/express.js and its unit tests to suppress err.message/err.code in production-grade environments, which is the exact same defect area this PR hardens via configHelper.isProd().
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main change: gating production hardening on the isProd predicate to fix an env-gate defect class affecting downstream projects.
Description check ✅ Passed The PR description follows the template structure, covering summary, scope, validation, guardrails, and infra details with clear before/after comparisons and tradeoffs documented.
Linked Issues check ✅ Passed The PR successfully addresses all four specific defects identified in issue #3852: error object leak via isProd/isDevEnv predicates, silent rate-limiter failures via base-layer config, mongoose debug logging gating, and swagger endpoint exposure control.
Out of Scope Changes check ✅ Passed All changes align with the stated objectives: exporting environment predicates, gating error responses, moving rate-limiter configs to base layer, controlling swagger/mongoose debug. No unrelated or extraneous modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/prod-env-gate

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@PierreBrisorgueil PierreBrisorgueil marked this pull request as ready for review June 15, 2026 08:06
Copilot AI review requested due to automatic review settings June 15, 2026 08:06
@codecov

codecov Bot commented Jun 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.50%. Comparing base (351ed34) to head (0c83b9b).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #3874   +/-   ##
=======================================
  Coverage   92.49%   92.50%           
=======================================
  Files         165      165           
  Lines        5397     5400    +3     
  Branches     1737     1740    +3     
=======================================
+ Hits         4992     4995    +3     
  Misses        325      325           
  Partials       80       80           
Flag Coverage Δ
integration 60.09% <87.50%> (+0.02%) ⬆️
unit 73.66% <87.50%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 351ed34...0c83b9b. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a production-hardening env-gating defect by introducing shared environment predicates (isDevEnv / isProd) and using them to correctly apply “production-grade” behavior when downstream deployments use NODE_ENV=<project> instead of the literal production.

Changes:

  • Add isDevEnv / isProd predicates in lib/helpers/config.js and switch production hardening checks to use them.
  • Secure-by-default gates: disable swagger docs mounting and mongoose query debug logging outside dev-grade envs; prevent detailed Express error responses outside dev-grade envs.
  • Ensure rate-limiter profiles (api, billingPlans) exist in the always-merged base config layer, with production overrides, and add regression tests for all gates.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
modules/organizations/config/organizations.development.config.js Adds base-layer rateLimit.api profile so limiter is present under all envs.
modules/billing/config/billing.development.config.js Adds base-layer rateLimit.billingPlans profile so limiter is present under all envs.
lib/services/mongoose.js Gates mongoose debug via isDevEnv() and exports resolveDebug.
lib/services/express.js Gates swagger mounting by env predicate and hardens terminal error handler for non-dev envs.
lib/helpers/config.js Introduces and exports isDevEnv / isProd predicates; reuses predicate for JWT-secret validation.
lib/helpers/responses.js Gates raw serialized error-object exposure using the env predicate.
config/defaults/production.config.js Explicitly disables swagger via production defaults.
lib/services/tests/mongoose.debugGate.unit.tests.js Adds unit coverage for mongoose debug env gating behavior.
lib/services/tests/express.errorroutes.unit.tests.js Adds regression test ensuring non-dev project envs don’t leak error details.
lib/services/tests/express.docsEnvGate.unit.tests.js Adds tests ensuring docs routes mount only in dev-grade envs.
lib/services/tests/express.docs.unit.tests.js Updates existing swagger tests to mock new env predicate dependency.
lib/middlewares/tests/rateLimiter.baseLayer.unit.tests.js Adds regression guard asserting base-layer limiter profile presence/shape.
lib/helpers/tests/responses.errorLeak.unit.tests.js Adds tests for error-object exposure gating outside dev envs.
lib/helpers/tests/config.envPredicate.unit.tests.js Adds unit tests for isDevEnv / isProd semantics and defaults.

Comment thread lib/helpers/responses.js
Comment thread lib/helpers/config.js Outdated
Comment thread lib/helpers/config.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/middlewares/tests/rateLimiter.baseLayer.unit.tests.js`:
- Around line 28-32: The JSDoc comment for the expectUsableProfile helper
function is missing the required `@returns` tag. Add `@returns` {void} to the JSDoc
block above the expectUsableProfile function declaration to satisfy the JS
function JSDoc rule that requires both `@param` and `@returns` documentation for all
named helper functions in test files.

In `@lib/services/tests/express.docsEnvGate.unit.tests.js`:
- Around line 30-33: The buildMockApp helper function is missing the required
JSDoc header. Add a JSDoc comment above the buildMockApp function definition
that includes a brief description of what the function does and a `@returns` tag
documenting that it returns a mock app object with a get method and _routes
property. Since the function takes no parameters, no `@param` tags are needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 98f6925a-3aff-42bb-969f-c6db49dd61e5

📥 Commits

Reviewing files that changed from the base of the PR and between 351ed34 and eeaeab3.

📒 Files selected for processing (14)
  • config/defaults/production.config.js
  • lib/helpers/config.js
  • lib/helpers/responses.js
  • lib/helpers/tests/config.envPredicate.unit.tests.js
  • lib/helpers/tests/responses.errorLeak.unit.tests.js
  • lib/middlewares/tests/rateLimiter.baseLayer.unit.tests.js
  • lib/services/express.js
  • lib/services/mongoose.js
  • lib/services/tests/express.docs.unit.tests.js
  • lib/services/tests/express.docsEnvGate.unit.tests.js
  • lib/services/tests/express.errorroutes.unit.tests.js
  • lib/services/tests/mongoose.debugGate.unit.tests.js
  • modules/billing/config/billing.development.config.js
  • modules/organizations/config/organizations.development.config.js

Comment thread lib/middlewares/tests/rateLimiter.baseLayer.unit.tests.js
Comment thread lib/services/tests/express.docsEnvGate.unit.tests.js
… pass-1)

- Clarify that isDevEnv/isProd default to 'development' when NODE_ENV is unset
- Add @returns {void} to expectUsableProfile in rateLimiter.baseLayer tests
- Add JSDoc to buildMockApp helper in express.docsEnvGate tests
@PierreBrisorgueil PierreBrisorgueil merged commit edf5b93 into master Jun 15, 2026
8 checks passed
@PierreBrisorgueil PierreBrisorgueil deleted the fix/prod-env-gate branch June 15, 2026 08:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Fix A bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🔒 Production env-gate: error leak + swagger/debug/rate-limiter defaults

2 participants