Skip to content

fix: cross-env validation and docs for renderer password#3090

Merged
justin808 merged 3 commits intomainfrom
jg/2887-renderer-password-validation
Apr 12, 2026
Merged

fix: cross-env validation and docs for renderer password#3090
justin808 merged 3 commits intomainfrom
jg/2887-renderer-password-validation

Conversation

@justin808
Copy link
Copy Markdown
Member

@justin808 justin808 commented Apr 9, 2026

Summary

Addresses all three follow-up items from PR #2829:

  • Ruby-side NODE_ENV checking: validate_renderer_password_for_production now checks both RAILS_ENV and NODE_ENV, mirroring the Node-side runtimeEnvsAllowDevelopmentDefaults(). Surfaces misconfigurations (e.g. NODE_ENV=production + RAILS_ENV=development) at Rails boot time instead of at Node request time.
  • Clarifying comment: Added comment to defaultReplayServerAsyncOperationLogs() explaining the intentional asymmetry — it only checks NODE_ENV because async log replay is a JS debugging concern, not a security boundary.
  • Unconditional validator call: Moved validate_renderer_password_for_production from inside setup_renderer_password to setup_config_values, making enforcement unconditional and resilient to future refactors of the password resolution logic.

Closes #2887

Test plan

  • All 51 Ruby configuration specs pass (including 4 new cross-env tests)
  • All 31 Node renderer configBuilder tests pass
  • RuboCop clean
  • ESLint + Prettier clean
  • CI passes

🤖 Generated with Claude Code


Note

Medium Risk
Tightens production-like startup validation for the Node renderer password, which can cause previously misconfigured deployments to fail fast at Rails boot. Changes are localized to configuration/validation paths with expanded spec coverage.

Overview
Ensures RENDERER_PASSWORD enforcement is fail-closed and consistent across Ruby and Node by validating against both RAILS_ENV and NODE_ENV, including rejecting mixed-env setups (e.g. NODE_ENV=production + RAILS_ENV=development).

Moves validate_renderer_password_for_production to run unconditionally during Rails setup_config_values, updates the error message docs/matrix accordingly, and expands Ruby specs to cover the new cross-env scenarios.

Adds a clarifying comment in the Node configBuilder that defaultReplayServerAsyncOperationLogs() intentionally keys only off NODE_ENV (debugging behavior, not a security boundary).

Reviewed by Cursor Bugbot for commit 5a01f45. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened production validation for the Node renderer to prevent misconfiguration in mixed or production envs.
  • Documentation

    • Clarified default server operation logging behavior and updated environment-related messaging.
  • Tests

    • Expanded test coverage for environment-variable combinations to ensure consistent behavior across deployment and development scenarios.

)

1. Add NODE_ENV checking to Ruby-side validate_renderer_password_for_production,
   mirroring the Node-side runtimeEnvsAllowDevelopmentDefaults() behavior. This
   surfaces misconfigurations (e.g. NODE_ENV=production + RAILS_ENV=development)
   at Rails boot time rather than at Node request time.

2. Add clarifying comment to defaultReplayServerAsyncOperationLogs() explaining
   why it intentionally checks only NODE_ENV (JS debugging concern, not a
   security boundary).

3. Move validate_renderer_password_for_production call from setup_renderer_password
   to setup_config_values so enforcement is unconditional and resilient to future
   refactors of the password resolution logic.

Closes #2887

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 9, 2026

Walkthrough

Moved renderer-password validation into the main config setup, broadened the validation to consider both RAILS_ENV and NODE_ENV, clarified NODE_ENV-only behavior in Node renderer async-logging comments, and expanded tests for cross-env scenarios.

Changes

Cohort / File(s) Summary
Ruby Configuration
react_on_rails_pro/lib/react_on_rails_pro/configuration.rb, react_on_rails_pro/sig/react_on_rails_pro/configuration.rbs
Call to validate_renderer_password_for_production relocated to setup_config_values (unconditional validation); validation logic now considers both RAILS_ENV and NODE_ENV and only skips when all present envs are in {development,test}; added private method signature in RBS.
Node renderer docs/comments
packages/react-on-rails-pro-node-renderer/src/shared/configBuilder.ts
Clarified comment and documentation for defaultReplayServerAsyncOperationLogs() to state its gating is based solely on NODE_ENV (unchanged logic), distinguishing it from runtimeEnvsAllowDevelopmentDefaults().
Tests
react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb
Expanded ENV stubbing to use ENV.fetch including NODE_ENV; added test cases covering mixed RAILS_ENV/NODE_ENV combinations (raises and non-raises) to validate the new cross-env rules.

Sequence Diagram(s)

sequenceDiagram
  participant Config as ReactOnRailsPro::Configuration
  participant ENV as Environment
  participant Validator as validate_renderer_password_for_production

  Config->>Config: setup_config_values()
  Config->>Config: setup_renderer_password()
  Config->>Validator: validate_renderer_password_for_production()
  Validator->>ENV: read RAILS_ENV, NODE_ENV
  alt any present env outside {development,test}
    Validator->>Config: raise error if no RENDERER_PASSWORD
  else all present envs in {development,test}
    Validator->>Config: no-op (skip validation)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰
I nibble configs, hop through envs so spry,
NODE and RAILS I check with twitching nose,
No mismatches pass, no secret left awry,
A careful guard where safer setup grows,
Hooray — secure boot, and off the rabbit goes! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: cross-env validation and docs for renderer password' clearly summarizes the main changes: adding cross-environment validation checks and documentation improvements for renderer password handling.
Linked Issues check ✅ Passed All three requirements from issue #2887 are addressed: Ruby-side NODE_ENV checking is implemented, documentation comment is added to defaultReplayServerAsyncOperationLogs(), and validator call is moved to setup_config_values.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the three objectives: validation logic refactoring, documentation clarity, and test updates covering the new cross-environment validation scenarios.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg/2887-renderer-password-validation

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.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 9, 2026

Greptile Summary

This PR hardens the RENDERER_PASSWORD validation by checking both RAILS_ENV and NODE_ENV (mirroring the Node-side runtimeEnvsAllowDevelopmentDefaults()), moves the validator call to setup_config_values so it runs unconditionally, and adds a clarifying comment to defaultReplayServerAsyncOperationLogs() explaining its intentional NODE_ENV-only check. All four new cross-env test cases are well-targeted and the logic correctly fails closed when both env vars are absent.

Confidence Score: 5/5

Safe to merge — logic is correct, fail-closed, and well-covered by tests.

All findings are P2 or better. The cross-env validation logic is correct: it fails closed when both envs are absent, catches the NODE_ENV=production + RAILS_ENV=development misconfiguration, and preserves the development/test bypass. Moving the validator call to setup_config_values is a clean improvement with no behavioral regressions. Test coverage is thorough for the new scenarios.

No files require special attention.

Vulnerabilities

No security concerns identified. The change strengthens the security posture by ensuring NODE_ENV=production + RAILS_ENV=development misconfigurations are caught at boot time rather than silently bypassing the renderer password requirement.

Important Files Changed

Filename Overview
react_on_rails_pro/lib/react_on_rails_pro/configuration.rb Validator moved to setup_config_values for unconditional enforcement; now checks both RAILS_ENV and NODE_ENV with correct fail-closed logic
react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb Tests updated to use ENV.fetch instead of ENV.[], with 4 new cross-env specs covering the key misconfiguration scenarios
packages/react-on-rails-pro-node-renderer/src/shared/configBuilder.ts Adds clarifying comment explaining why defaultReplayServerAsyncOperationLogs() intentionally checks only NODE_ENV
react_on_rails_pro/sig/react_on_rails_pro/configuration.rbs Adds missing RBS type signature for validate_renderer_password_for_production

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[setup_config_values] --> B[setup_renderer_password]
    B --> C{renderer_password present?}
    C -- Yes --> D[Skip URL/ENV extraction]
    C -- No --> E[Extract from renderer_url]
    E --> F{Password in URL?}
    F -- Yes --> G[Set from URL]
    F -- No --> H[Set from ENV RENDERER_PASSWORD]
    D & G & H --> I[validate_renderer_password_for_production]
    I --> J{renderer_password present?}
    J -- Yes --> K[✅ Skip validation]
    J -- No --> L{node_renderer?}
    L -- No --> K
    L -- Yes --> M[Build runtime_envs from RAILS_ENV + NODE_ENV]
    M --> N{runtime_envs.any? AND all dev/test?}
    N -- Yes --> K
    N -- No --> O[🚨 Raise Error: RENDERER_PASSWORD required]
Loading

Reviews (1): Last reviewed commit: "fix: cross-env validation and documentat..." | Re-trigger Greptile

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

size-limit report 📦

Path Size
react-on-rails/client bundled (gzip) 62.63 KB (0%)
react-on-rails/client bundled (gzip) (time) 62.63 KB (0%)
react-on-rails/client bundled (brotli) 53.7 KB (0%)
react-on-rails/client bundled (brotli) (time) 53.7 KB (0%)
react-on-rails-pro/client bundled (gzip) 63.56 KB (0%)
react-on-rails-pro/client bundled (gzip) (time) 63.56 KB (0%)
react-on-rails-pro/client bundled (brotli) 54.6 KB (0%)
react-on-rails-pro/client bundled (brotli) (time) 54.6 KB (0%)
registerServerComponent/client bundled (gzip) 127.39 KB (0%)
registerServerComponent/client bundled (gzip) (time) 127.39 KB (0%)
registerServerComponent/client bundled (brotli) 61.52 KB (0%)
registerServerComponent/client bundled (brotli) (time) 61.52 KB (0%)
wrapServerComponentRenderer/client bundled (gzip) 122.02 KB (0%)
wrapServerComponentRenderer/client bundled (gzip) (time) 122.02 KB (0%)
wrapServerComponentRenderer/client bundled (brotli) 56.77 KB (0%)
wrapServerComponentRenderer/client bundled (brotli) (time) 56.77 KB (+0.16% 🔺)

Comment thread react_on_rails_pro/lib/react_on_rails_pro/configuration.rb
Comment thread react_on_rails_pro/lib/react_on_rails_pro/configuration.rb
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 9, 2026

Review

The approach is sound. Moving validate_renderer_password_for_production out of setup_renderer_password and into setup_config_values is the right call — it makes enforcement unconditional regardless of how the password resolution path evolves.

Logic correctness — The new condition mirrors runtimeEnvsAllowDevelopmentDefaults() faithfully:

RAILS_ENV NODE_ENV Old behavior New behavior
development unset ✅ skip ✅ skip
production unset ❌ raise ❌ raise
unset unset ❌ raise ❌ raise
development production ✅ skip (bug) ❌ raise (fixed)
production development ❌ raise ❌ raise

The fourth row is the key fix — the old code only checked RAILS_ENV so a NODE_ENV=production + RAILS_ENV=development misconfiguration would have silently passed.

Orderingsetup_renderer_password runs before validate_renderer_password_for_production in setup_config_values, so the validator always sees a fully-resolved password. The validator's own early-return on renderer_password.present? is correct defense-in-depth.

Two small items left inline (one missing test, one style nit on .freeze). Neither blocks merge.

RBS signature — Correctly adds the private method. Looks good.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb (1)

338-360: Add the NODE_ENV-only development case to the matrix.

The new validator also allows RAILS_ENV unset + NODE_ENV=development/test, but that branch is still untested here. Adding one explicit example would lock down the Node/Ruby parity this PR is trying to preserve.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb` around
lines 338 - 360, Add a spec that covers the case where RAILS_ENV is unset and
NODE_ENV is "development" (and optionally "test") by stubbing
ENV.fetch("RAILS_ENV", nil) to return nil while stubbing ENV.fetch("NODE_ENV",
nil) to return "development", then run the existing ReactOnRailsPro.configure
block (setting config.server_renderer and config.renderer_url) and assert it
does not raise; update the test matrix near the existing examples that use
ReactOnRailsPro.configure and ENV.fetch to include this NODE_ENV-only
development case so the validator branch allowing RAILS_ENV unset +
NODE_ENV=development is exercised.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@react_on_rails_pro/lib/react_on_rails_pro/configuration.rb`:
- Around line 267-274: The diagnostic/error matrix text that describes
environment combinations must be updated to reflect the new dual-env rule
implemented by runtime_envs and allowed_envs: change any statement that
unconditionally treats "(RAILS_ENV unset)" as production-like to reflect that
RAILS_ENV can be unset when NODE_ENV is development or test (i.e., allowed by
runtime_envs.any? && runtime_envs.all? {...}). Locate the message or matrix text
that documents cross-env cases and adjust the entries so they correctly show
that only when neither RAILS_ENV nor NODE_ENV is development/test is the runtime
considered production-like; ensure the updated wording references the same
dual-check logic used by runtime_envs and allowed_envs.

---

Nitpick comments:
In `@react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb`:
- Around line 338-360: Add a spec that covers the case where RAILS_ENV is unset
and NODE_ENV is "development" (and optionally "test") by stubbing
ENV.fetch("RAILS_ENV", nil) to return nil while stubbing ENV.fetch("NODE_ENV",
nil) to return "development", then run the existing ReactOnRailsPro.configure
block (setting config.server_renderer and config.renderer_url) and assert it
does not raise; update the test matrix near the existing examples that use
ReactOnRailsPro.configure and ENV.fetch to include this NODE_ENV-only
development case so the validator branch allowing RAILS_ENV unset +
NODE_ENV=development is exercised.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1eaa261d-f855-45ec-85dc-411fdec4990e

📥 Commits

Reviewing files that changed from the base of the PR and between 16ac897 and c0d9631.

📒 Files selected for processing (4)
  • packages/react-on-rails-pro-node-renderer/src/shared/configBuilder.ts
  • react_on_rails_pro/lib/react_on_rails_pro/configuration.rb
  • react_on_rails_pro/sig/react_on_rails_pro/configuration.rbs
  • react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb

Comment thread react_on_rails_pro/lib/react_on_rails_pro/configuration.rb
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON, but it could not run because on-demand usage is turned off. To enable Bugbot Autofix, turn on on-demand usage and set a spend limit in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c0d9631. Configure here.

Comment thread react_on_rails_pro/lib/react_on_rails_pro/configuration.rb Outdated
- Use .compact_blank instead of .compact so empty-string env vars are
  filtered like Node's Boolean() filter (cursor[bot])
- Update error matrix to reflect dual-env rule — RAILS_ENV unset is
  not always production-like when NODE_ENV is development/test (coderabbitai[bot])
- Add test for RAILS_ENV=nil + NODE_ENV=development (claude[bot])

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@justin808
Copy link
Copy Markdown
Member Author

Review feedback addressed in 35bfb99

# Reviewer Issue Resolution
1 @cursor[bot] .compact doesn't filter empty strings like Node's Boolean() Changed to .compact_blank for parity
2 @coderabbitai[bot] Error matrix says "(RAILS_ENV unset)" is always production-like Updated matrix to reflect dual-env rule
3 @claude[bot] Missing test for RAILS_ENV=nil, NODE_ENV=development Added test case
4 @claude[bot] .freeze on local var is a no-op Declined — local var was extracted for RuboCop Performance/CollectionLiteralInLoop; inlining would re-trigger the cop

All 4 review threads resolved. 52 specs passing, RuboCop clean.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 12, 2026

Tip

For best results, initiate chat on the files or code changes.

@justin808 Thanks for the detailed summary! Great to see all the feedback addressed so thoroughly. A few notes on the resolutions:

  1. .compact_blank fix — Good call. .compact only removes nil, while .compact_blank also strips empty strings "", which correctly mirrors Node's Boolean() filtering behavior. This is an important behavioral difference in Ruby.
  2. Error matrix update — Glad the dual-env rule is now accurately reflected in the docs.
  3. RAILS_ENV=nil, NODE_ENV=development test — Important edge case, good that it's covered.
  4. .freeze decline — Completely reasonable; avoiding a RuboCop Performance/CollectionLiteralInLoop violation is a valid justification for keeping the extracted local variable.

52 specs passing and RuboCop clean is a solid state. 🎉


✏️ Learnings added
Learnt from: justin808
URL: https://github.com/shakacode/react_on_rails/pull/3090

Timestamp: 2026-04-12T07:04:58.059Z
Learning: In `react_on_rails_pro/lib/react_on_rails_pro/configuration.rb`, use `.compact_blank` (not `.compact`) when filtering environment variable collections, because `.compact` only removes `nil` while `.compact_blank` also removes empty strings `""`, matching Node's `Boolean()` filtering behavior for env vars.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: react_on_rails_pro/CLAUDE.md:0-0
Timestamp: 2026-02-24T16:52:47.694Z
Learning: Applies to react_on_rails_pro/spec/execjs-compatible-dummy/spec/**/*.rb : ExecJS dummy tests should be run with: `cd react_on_rails_pro/spec/execjs-compatible-dummy && bundle exec rspec`

Learnt from: ihabadham
Repo: shakacode/react_on_rails PR: 2284
File: react_on_rails/lib/generators/react_on_rails/templates/pro/base/config/initializers/react_on_rails_pro.rb.tt:10-10
Timestamp: 2026-02-12T20:43:26.843Z
Learning: In React on Rails Pro initializer templates (e.g., `react_on_rails_pro.rb.tt`), the `config.renderer_password` uses `ENV.fetch("RENDERER_PASSWORD", "devPassword")` as a development starter configuration. This pattern is intentional: the node renderer requires explicit production deployment setup, and adding a production-only raise would break generator execution since initializers are loaded during `rails generate`. The pattern is consistent with existing manual setup documentation.

Learnt from: justin808
Repo: shakacode/react_on_rails PR: 2857
File: README.md:0-0
Timestamp: 2026-03-26T08:32:31.881Z
Learning: In `README.md` (and related docs) for `shakacode/react_on_rails`, the Ruby minimum version (`>= 3.0`) similarly reflects the actual package validator/gemspec floor, while `CI tested: 3.2 - 3.4` describes coverage. This distinction is intentional and should not be flagged as a docs/CI mismatch.

Learnt from: CR
Repo: shakacode/react_on_rails PR: 0
File: react_on_rails_pro/CLAUDE.md:0-0
Timestamp: 2026-02-24T16:52:47.694Z
Learning: The Node renderer uses JWT-based authentication with password from Rails initializer

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 12, 2026

Review: cross-env validation and renderer password docs

Overall: solid change. The logic is correct, the Node-side mirror is precise, and moving the validator call out of setup_renderer_password is a resilience improvement. A few observations below.

Logic correctness

The Ruby implementation of validate_renderer_password_for_production now exactly mirrors runtimeEnvsAllowDevelopmentDefaults() in the Node renderer:

  • Both unset → runtime_envs.any? is false → raises (fail-closed) ✓
  • One dev/test, one nil → all elements allowed → returns ✓
  • Mixed (e.g. staging + development) → not all allowed → raises ✓
  • Both dev/test → all allowed → returns ✓

Moving the validator call

Previously, if renderer_password.present? was already true on entry to setup_renderer_password, the method returned early and the validator was never called. With the unconditional call in setup_config_values, the validator always runs (and still short-circuits immediately via return if renderer_password.present?). This closes a future-refactor trap where changes to setup_renderer_password could inadvertently skip enforcement.

Minor observations

  1. .freeze on a local variable (configuration.rb line 278): allowed_envs = %w[development test].freeze — freezing a local that is never mutated or passed elsewhere is a style quirk. Could be a module constant or just an unfrozen literal. Not a bug.

  2. Missing mixed-env test for the other direction: The new cross-env tests cover NODE_ENV=production + RAILS_ENV=development but not RAILS_ENV=staging + NODE_ENV=development. The logic handles it correctly, but an explicit test would document the intent for that combination (staging is a real deployment environment where this misconfiguration could occur).

  3. compact_blank vs compact: Using compact_blank is the right choice — it correctly treats an empty-string env var as unset, which preserves fail-closed behavior even if RAILS_ENV="" is set.

Tests

The new cross-env specs (4 production-like + 4 dev/test cases) give thorough coverage of the documented matrix, and the migration from ENV.[] stubs to ENV.fetch stubs correctly aligns with the implementation change.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb (1)

338-365: Add one blank-string env case to lock in .compact_blank.

The new matrix only covers nil and named envs. If runtime_envs in react_on_rails_pro/lib/react_on_rails_pro/configuration.rb:270-271 regressed from .compact_blank back to .compact, these specs would still pass while reintroducing the empty-string mismatch with Node. An example with RAILS_ENV="" and NODE_ENV="development" would catch that.

💡 Suggested example
+        it "does not raise when RAILS_ENV is blank and NODE_ENV is development" do
+          allow(ENV).to receive(:fetch).with("RAILS_ENV", nil).and_return("")
+          allow(ENV).to receive(:fetch).with("NODE_ENV", nil).and_return("development")
+
+          expect do
+            ReactOnRailsPro.configure do |config|
+              config.server_renderer = "NodeRenderer"
+              config.renderer_url = "https://localhost:3800"
+            end
+          end.not_to raise_error
+        end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb` around
lines 338 - 365, Add a spec that covers the empty-string env case to ensure
runtime_envs uses .compact_blank: create a new example in configuration_spec
(near the other env/matrix tests) that stubs ENV.fetch to return "" for
"RAILS_ENV" and "development" for "NODE_ENV", then run the same
ReactOnRailsPro.configure block and expect no error; this will catch regressions
if runtime_envs in react_on_rails_pro/lib/react_on_rails_pro/configuration.rb
(the runtime_envs method around lines referencing .compact_blank) were changed
back to .compact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb`:
- Around line 310-314: The shared before block must also stub
ENV.fetch("RENDERER_PASSWORD", nil) to return nil so tests exercise the "no
password" path; update the setup in the spec to stub ENV.fetch for
"RENDERER_PASSWORD" to nil (so setup_renderer_password in
react_on_rails_pro/lib/react_on_rails_pro/configuration.rb does not populate
renderer_password) ensuring the validator runs through the dev/test env matrix
rather than returning early.

---

Nitpick comments:
In `@react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb`:
- Around line 338-365: Add a spec that covers the empty-string env case to
ensure runtime_envs uses .compact_blank: create a new example in
configuration_spec (near the other env/matrix tests) that stubs ENV.fetch to
return "" for "RAILS_ENV" and "development" for "NODE_ENV", then run the same
ReactOnRailsPro.configure block and expect no error; this will catch regressions
if runtime_envs in react_on_rails_pro/lib/react_on_rails_pro/configuration.rb
(the runtime_envs method around lines referencing .compact_blank) were changed
back to .compact.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7bed4bd1-babd-49de-9243-f5c78e985ad1

📥 Commits

Reviewing files that changed from the base of the PR and between c0d9631 and 35bfb99.

📒 Files selected for processing (2)
  • react_on_rails_pro/lib/react_on_rails_pro/configuration.rb
  • react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb
🚧 Files skipped from review as they are similar to previous changes (1)
  • react_on_rails_pro/lib/react_on_rails_pro/configuration.rb

Comment thread react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb
Ensures the dev/test environment matrix tests actually exercise the
env-checking logic by stubbing RENDERER_PASSWORD to nil. Without this,
a RENDERER_PASSWORD set in the local or CI environment would cause the
validator to return early, making the tests pass for the wrong reason.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@justin808
Copy link
Copy Markdown
Member Author

Review comments addressed (round 2)

Fixed (1):

  • configuration_spec.rb:310-314 — Stubbed RENDERER_PASSWORD to nil in the dev/test shared before block so tests exercise the env matrix logic even when the env var is set locally or in CI. Matches the production-like context setup (line 182). → 5a01f45

Already resolved (4):

  • Error matrix updated for dual-env rule (coderabbitai) ✅
  • .compact.compact_blank for empty-string filtering (cursor) ✅
  • Test for RAILS_ENV=nil, NODE_ENV=development added (claude) ✅
  • .freeze on local var justified by RuboCop cop (claude) ✅

All review threads are now resolved.


it "does not raise when RAILS_ENV is test and NODE_ENV is development" do
allow(ENV).to receive(:fetch).with("RAILS_ENV", nil).and_return("test")
allow(ENV).to receive(:fetch).with("NODE_ENV", nil).and_return("development")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The new tests cover NODE_ENV=development + RAILS_ENV=nil but not the symmetric NODE_ENV=test + RAILS_ENV=nil case. Since test is also in allowed_envs, a NODE_ENV-only test env should likewise skip validation. Consider adding this case for full parity:

it "does not raise when RAILS_ENV is unset and NODE_ENV is test" do
  allow(ENV).to receive(:fetch).with("RAILS_ENV", nil).and_return(nil)
  allow(ENV).to receive(:fetch).with("NODE_ENV", nil).and_return("test")

  expect do
    ReactOnRailsPro.configure do |config|
      config.server_renderer = "NodeRenderer"
      config.renderer_url = "https://localhost:3800"
    end
  end.not_to raise_error
end

(both unset) — treated as production-like; RENDERER_PASSWORD required
staging — RENDERER_PASSWORD required
production — RENDERER_PASSWORD required
(mixed envs) — RENDERER_PASSWORD required (e.g. NODE_ENV=production + RAILS_ENV=development)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The updated matrix removed the old (any other) — RENDERER_PASSWORD required catch-all row without adding an equivalent. Cases like NODE_ENV=staging + RAILS_ENV=nil (a single non-dev/test env value) are still enforced correctly by the logic, but they're no longer represented in the matrix. Consider adding one more row to make the table exhaustive:

(any other value) — RENDERER_PASSWORD required (e.g. NODE_ENV=staging alone, custom env names)

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 12, 2026

Review: fix: cross-env validation and docs for renderer password

Overall: Solid, well-reasoned PR. The logic correctly mirrors Node-side runtimeEnvsAllowDevelopmentDefaults() and the move to call validate_renderer_password_for_production unconditionally from setup_config_values is the right architectural fix.

What works well

  • Logic parity: The Ruby runtime_envs.any? && runtime_envs.all? { ... } pattern is a faithful translation of the TypeScript runtimeEnvs.length > 0 && runtimeEnvs.every(...) idiom — both fail-closed when both envs are unset, both pass when every present value is development or test.
  • Unconditional validator call: Previously, if renderer_password was already present when setup_renderer_password ran, the early return silently skipped validate_renderer_password_for_production. The new placement makes enforcement independent of password-resolution flow — good resilience against future refactors.
  • compact_blank vs compact: Using compact_blank correctly treats empty-string RAILS_ENV="" the same as unset — fail-closed. Appropriate for a security boundary.
  • Clarifying comment on defaultReplayServerAsyncOperationLogs: The comment explaining the intentional asymmetry (JS debugging concern, not a security boundary) prevents future maintainers from "fixing" it to also check RAILS_ENV.

Behavioral change worth noting in CHANGELOG

Previously: RAILS_ENV=nil + NODE_ENV=developmentraised (only RAILS_ENV was checked; nil → production-like)
Now: RAILS_ENV=nil + NODE_ENV=developmentdoes not raise (mirrors Node-side behavior)

This is intentional and correct, but it changes existing startup behavior for anyone running with RAILS_ENV unset and NODE_ENV=development. The test "does not raise when RAILS_ENV is unset and NODE_ENV is development" captures this, but a CHANGELOG entry would help operators know to expect the change.

Minor

Two specific notes left as inline comments:

  1. Missing test: NODE_ENV=test + RAILS_ENV=nil — symmetric to the added NODE_ENV=development + RAILS_ENV=nil test, but not covered.
  2. Error matrix: The old (any other) — RENDERER_PASSWORD required catch-all was dropped; adding it back (or an equivalent) makes the table exhaustive for cases like NODE_ENV=staging alone.

@justin808 justin808 merged commit 0610126 into main Apr 12, 2026
38 checks passed
@justin808 justin808 deleted the jg/2887-renderer-password-validation branch April 12, 2026 20:18
justin808 added a commit that referenced this pull request Apr 12, 2026
## Summary

Addresses all three follow-up items from PR #2829:

- **Ruby-side NODE_ENV checking**:
`validate_renderer_password_for_production` now checks both `RAILS_ENV`
and `NODE_ENV`, mirroring the Node-side
`runtimeEnvsAllowDevelopmentDefaults()`. Surfaces misconfigurations
(e.g. `NODE_ENV=production` + `RAILS_ENV=development`) at Rails boot
time instead of at Node request time.
- **Clarifying comment**: Added comment to
`defaultReplayServerAsyncOperationLogs()` explaining the intentional
asymmetry — it only checks `NODE_ENV` because async log replay is a JS
debugging concern, not a security boundary.
- **Unconditional validator call**: Moved
`validate_renderer_password_for_production` from inside
`setup_renderer_password` to `setup_config_values`, making enforcement
unconditional and resilient to future refactors of the password
resolution logic.

Closes #2887

## Test plan

- [x] All 51 Ruby configuration specs pass (including 4 new cross-env
tests)
- [x] All 31 Node renderer configBuilder tests pass
- [x] RuboCop clean
- [x] ESLint + Prettier clean
- [ ] CI passes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Tightens production-like startup validation for the Node renderer
password, which can cause previously misconfigured deployments to fail
fast at Rails boot. Changes are localized to configuration/validation
paths with expanded spec coverage.
> 
> **Overview**
> Ensures `RENDERER_PASSWORD` enforcement is **fail-closed and
consistent across Ruby and Node** by validating against both `RAILS_ENV`
and `NODE_ENV`, including rejecting mixed-env setups (e.g.
`NODE_ENV=production` + `RAILS_ENV=development`).
> 
> Moves `validate_renderer_password_for_production` to run
unconditionally during Rails `setup_config_values`, updates the error
message docs/matrix accordingly, and expands Ruby specs to cover the new
cross-env scenarios.
> 
> Adds a clarifying comment in the Node `configBuilder` that
`defaultReplayServerAsyncOperationLogs()` intentionally keys only off
`NODE_ENV` (debugging behavior, not a security boundary).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5a01f45. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Strengthened production validation for the Node renderer to prevent
misconfiguration in mixed or production envs.

* **Documentation**
* Clarified default server operation logging behavior and updated
environment-related messaging.

* **Tests**
* Expanded test coverage for environment-variable combinations to ensure
consistent behavior across deployment and development scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
justin808 added a commit that referenced this pull request Apr 12, 2026
## Summary

Addresses all three follow-up items from PR #2829:

- **Ruby-side NODE_ENV checking**:
`validate_renderer_password_for_production` now checks both `RAILS_ENV`
and `NODE_ENV`, mirroring the Node-side
`runtimeEnvsAllowDevelopmentDefaults()`. Surfaces misconfigurations
(e.g. `NODE_ENV=production` + `RAILS_ENV=development`) at Rails boot
time instead of at Node request time.
- **Clarifying comment**: Added comment to
`defaultReplayServerAsyncOperationLogs()` explaining the intentional
asymmetry — it only checks `NODE_ENV` because async log replay is a JS
debugging concern, not a security boundary.
- **Unconditional validator call**: Moved
`validate_renderer_password_for_production` from inside
`setup_renderer_password` to `setup_config_values`, making enforcement
unconditional and resilient to future refactors of the password
resolution logic.

Closes #2887

## Test plan

- [x] All 51 Ruby configuration specs pass (including 4 new cross-env
tests)
- [x] All 31 Node renderer configBuilder tests pass
- [x] RuboCop clean
- [x] ESLint + Prettier clean
- [ ] CI passes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Tightens production-like startup validation for the Node renderer
password, which can cause previously misconfigured deployments to fail
fast at Rails boot. Changes are localized to configuration/validation
paths with expanded spec coverage.
> 
> **Overview**
> Ensures `RENDERER_PASSWORD` enforcement is **fail-closed and
consistent across Ruby and Node** by validating against both `RAILS_ENV`
and `NODE_ENV`, including rejecting mixed-env setups (e.g.
`NODE_ENV=production` + `RAILS_ENV=development`).
> 
> Moves `validate_renderer_password_for_production` to run
unconditionally during Rails `setup_config_values`, updates the error
message docs/matrix accordingly, and expands Ruby specs to cover the new
cross-env scenarios.
> 
> Adds a clarifying comment in the Node `configBuilder` that
`defaultReplayServerAsyncOperationLogs()` intentionally keys only off
`NODE_ENV` (debugging behavior, not a security boundary).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5a01f45. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Strengthened production validation for the Node renderer to prevent
misconfiguration in mixed or production envs.

* **Documentation**
* Clarified default server operation logging behavior and updated
environment-related messaging.

* **Tests**
* Expanded test coverage for environment-variable combinations to ensure
consistent behavior across deployment and development scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
justin808 added a commit that referenced this pull request Apr 12, 2026
…ages

* origin/main: (44 commits)
  Consolidate CSP nonce sanitization into shared module (#2828)
  Add comprehensive --rsc-pro generator tests (#3098)
  fix: cross-env validation and docs for renderer password (#3090)
  Improve package metadata and Pro upgrade CTAs (#3112)
  docs: standardize warning syntax to GFM alert format (#3115)
  docs: improve react-intl documentation for React Server Components (#3085)
  Fix generator CI SSR regression on main (#3110)
  Refocus GitHub README on docs navigation (#3113)
  Add manual dev environment testing checklist for coding agents (#3074)
  Bump version to 16.6.0
  Update CHANGELOG.md for 16.6.0 (#3078)
  fix: node-renderer diagnostic improvements (#3086)
  fix: pin third-party npm deps in generator to prevent peer dep conflicts (#3083)
  chore(deps): bump lodash from 4.17.23 to 4.18.1 in the npm-security group across 1 directory (#2920)
  fix: refactor formatExceptionMessage to accept generic request context (#2877)
  Bump version to 16.6.0.rc.1
  Update CHANGELOG.md for 16.6.0.rc.1 (#3079)
  Update CHANGELOG.md unreleased section (#3077)
  Fix Content-Length mismatch and null renderingRequest errors in node renderer (#3069)
  Improve memory debugging docs with simpler heap snapshot approach (#3072)
  ...

# Conflicts:
#	docs/pro/home-pro.md
#	docs/pro/react-on-rails-pro.md
#	docs/sidebars.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Follow-up: Cross-env validation and documentation clarity for renderer password

1 participant