Skip to content

Conversation

justin808
Copy link
Member

@justin808 justin808 commented Sep 23, 2025

before force pushing the branch for #1798


This change is Reviewable

Summary by CodeRabbit

  • New Features

    • Added configuration for server bundle output path and an option to enforce private server bundles.
    • Introduced an explicit public bundles path API with fallbacks for bundle resolution.
  • Security

    • Optional enforcement ensures server bundles load only from private directories, with path validation.
  • Bug Fixes

    • Improved compatibility when Shakapacker isn’t present.
    • More robust server bundle detection.
    • Fixed packs generator behavior when the server bundle file is empty.
  • Documentation

    • Updated configuration guide with security options, paths, and bundle organization examples; noted a deprecation.
  • Chores

    • Ignored SSR-generated bundles in version control.

Copy link
Contributor

coderabbitai bot commented Sep 23, 2025

Walkthrough

Adds configuration and path resolution for server-side bundles, including private SSR output support and enforcement, with docs and changelog updates. Updates generator templates, utils, and tests to handle new options, fallback logic, and SSR output in a private directory. Minor .gitignore and packs generator adjustments included.

Changes

Cohort / File(s) Summary
Ignore rules
.gitignore
Add ignore for ssr-generated bundles.
Generator: gitignore updates
lib/generators/react_on_rails/base_generator.rb
Build dynamic list of missing ignore entries; append header plus entries for **/generated/** and ssr-generated.
Config: new options + validation
lib/react_on_rails/configuration.rb
Add server_bundle_output_path and enforce_private_server_bundles with defaults, accessors, initialization, and validation to require private non-public path when enforced.
Initializer template
lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt
Expose new config options in generated initializer (defaults: server_bundle_output_path: "ssr-generated", enforce_private_server_bundles: true).
Webpack server config template
lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt
Output server bundles to ../../ssr-generated private path; remove publicPath.
Utils: path resolution & aliases
lib/react_on_rails/utils.rb
Add public_bundles_full_path; deprecate generated_assets_full_path as alias; enhance bundle path resolution with manifest fallbacks and private SSR handling; add helpers for enforcement and server bundle detection.
Packs generator behavior
lib/react_on_rails/packs_generator.rb
Treat blank server_bundle_js_file as unset (nil/empty/whitespace) when deciding to inject generated server bundle.
Documentation
CHANGELOG.md, docs/guides/configuration.md
Document new options, security enforcement, fallback resolution, deprecations, and examples for public vs private bundle organization; add version notes and bug fix entry.
Dummy app config
spec/dummy/config/initializers/react_on_rails.rb
Set server_bundle_output_path: nil and enforce_private_server_bundles: false for test environment.
Specs: configuration validation
spec/react_on_rails/configuration_spec.rb
Add tests for enforcement validation and packer usage stubs.
Specs: utils path resolution
spec/react_on_rails/utils_spec.rb
Expand tests for manifest fallbacks, private SSR paths, and enforcement behavior across server and RSC bundles.
Specs: packer reference
spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb
Switch packer reference to ReactOnRails::PackerUtils.packer.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor App as Rails App
  participant ROR as ReactOnRails::Configuration
  participant Utils as ReactOnRails::Utils
  participant FS as Filesystem

  App->>ROR: configure(server_bundle_output_path, enforce_private_server_bundles)
  ROR->>ROR: validate_enforce_private_server_bundles()
  alt enforcement ON
    ROR-->>App: error if path nil or under public
  else enforcement OFF
    ROR-->>App: accept any path (incl. nil/public)
  end

  App->>Utils: bundle_js_file_path(bundle_name)
  alt server bundle and server_bundle_output_path set
    Utils->>FS: resolve private SSR path
    Utils-->>App: return private path
  else not in manifest
    Utils->>FS: check packer public output
    alt found
      Utils-->>App: return packer public path
    else not found
      Utils->>FS: check public/packs
      alt found
        Utils-->>App: return public/packs path
      else not found
        Utils->>FS: check generated assets (deprecated alias)
        Utils-->>App: return fallback or missing
      end
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • Judahmeek
  • alexeyr-ci

Poem

In secret burrows, bundles hide,
SSR carrots tucked inside.
Paths resolved, with gentle care,
Public fields? We won’t go there.
Config knobs twirl—clickety-click—
A rabbit ships it, quick-quick-quick! 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title accurately reflects the main change—improving bundle path resolution—which aligns with the updates to utils, configuration, generators, docs, and tests; it does include an extraneous non‑technical prefix ("For abanoub") that adds noise but does not make the title misleading.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch for-abanoub-improve-bundle-path-resolution

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.

Copy link
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: 0

🧹 Nitpick comments (3)
docs/guides/configuration.md (2)

136-149: Clarify path semantics and align examples with implementation

Add that relative paths are resolved from Rails.root to reduce ambiguity.

Apply this diff:

-# Directory where server bundles will be output during build process.
+# Directory where server bundles will be output during build process.
 # This allows organizing server-side rendering assets separately from client assets.
 #
-# Default is nil, which uses standard fallback locations in this priority order:
+# Paths are resolved relative to Rails.root when given as relative paths.
+#
+# Default is nil, which uses standard fallback locations in this priority order:
   #   1. Environment-specific path (e.g., public/webpack/test)
   #   2. Standard Shakapacker location (public/packs)
   #   3. Generated assets path (for legacy setups)
 #
 # Example configurations:
-# config.server_bundle_output_path = "ssr-generated"     # Private directory (recommended)
+# config.server_bundle_output_path = "ssr-generated"     # Private directory at Rails.root/ssr-generated (recommended)
 # config.server_bundle_output_path = "app/assets/builds" # Custom private location
 # config.server_bundle_output_path = nil                 # Use fallback locations (default)
 config.server_bundle_output_path = nil

170-194: Fix directory structure example to match actual output location

The server webpack config outputs to Rails.root/ssr-generated (not app/ssr-generated). Update the example tree accordingly.

Apply this diff:

-# Example directory structure with recommended configuration:
-#   app/
-#   ├── ssr-generated/           # Private server bundles
-#   │   ├── server-bundle.js
-#   │   └── rsc-bundle.js
-#   └── public/
-#       └── webpack/development/ # Public client bundles
-#           ├── application.js
-#           ├── manifest.json
-#           └── styles.css
+# Example directory structure with recommended configuration:
+#   ssr-generated/               # Private server bundles (Rails.root/ssr-generated)
+#   │   ├── server-bundle.js
+#   │   └── rsc-bundle.js
+#   public/
+#     └── webpack/development/   # Public client bundles
+#         ├── application.js
+#         ├── manifest.json
+#         └── styles.css
lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt (1)

46-54: Correct template comment: gem default is nil, template sets recommended value

The comment says “Defaults to ssr-generated” but the gem default is nil; the template is setting a recommended default for new apps. Adjust the comment to avoid confusion.

Apply this diff:

-# Configure where server bundles are output. Defaults to "ssr-generated".
-# This should match your webpack configuration for server bundles.
+# Configure where server bundles are output. For new apps we set "ssr-generated" (recommended).
+# The gem default is nil, which uses fallback public locations. This should match your server webpack output.
 config.server_bundle_output_path = "ssr-generated"
 
 # Enforce that server bundles are only loaded from private (non-public) directories.
-# When true, server bundles will only be loaded from the configured server_bundle_output_path.
-# This is recommended for production to prevent server-side code from being exposed.
+# When true, server bundles will only be loaded from the configured server_bundle_output_path.
+# Recommended for production to prevent server-side code from being exposed.
 config.enforce_private_server_bundles = true
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f77bd54 and 870835f.

📒 Files selected for processing (14)
  • .gitignore (1 hunks)
  • CHANGELOG.md (1 hunks)
  • docs/guides/configuration.md (1 hunks)
  • lib/generators/react_on_rails/base_generator.rb (1 hunks)
  • lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt (1 hunks)
  • lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt (1 hunks)
  • lib/react_on_rails/configuration.rb (6 hunks)
  • lib/react_on_rails/packer_utils.rb (2 hunks)
  • lib/react_on_rails/packs_generator.rb (1 hunks)
  • lib/react_on_rails/utils.rb (5 hunks)
  • spec/dummy/config/initializers/react_on_rails.rb (1 hunks)
  • spec/dummy/spec/helpers/react_on_rails_helper_spec.rb (1 hunks)
  • spec/react_on_rails/configuration_spec.rb (5 hunks)
  • spec/react_on_rails/utils_spec.rb (12 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}

📄 CodeRabbit inference engine (CLAUDE.md)

{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}: Before every commit/push, run bundle exec rubocop and fix all violations in Ruby code
Let RuboCop handle all Ruby code formatting; never manually format Ruby files

Files:

  • spec/dummy/spec/helpers/react_on_rails_helper_spec.rb
  • lib/generators/react_on_rails/base_generator.rb
  • lib/react_on_rails/packs_generator.rb
  • spec/react_on_rails/configuration_spec.rb
  • lib/react_on_rails/packer_utils.rb
  • spec/react_on_rails/utils_spec.rb
  • spec/dummy/config/initializers/react_on_rails.rb
  • lib/react_on_rails/utils.rb
  • lib/react_on_rails/configuration.rb
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru,js,jsx,ts,tsx,json,yml,yaml,md,css,scss}}

📄 CodeRabbit inference engine (CLAUDE.md)

Ensure all committed files end with a trailing newline character

Files:

  • spec/dummy/spec/helpers/react_on_rails_helper_spec.rb
  • lib/generators/react_on_rails/base_generator.rb
  • lib/react_on_rails/packs_generator.rb
  • spec/react_on_rails/configuration_spec.rb
  • lib/react_on_rails/packer_utils.rb
  • CHANGELOG.md
  • spec/react_on_rails/utils_spec.rb
  • spec/dummy/config/initializers/react_on_rails.rb
  • lib/react_on_rails/utils.rb
  • lib/react_on_rails/configuration.rb
  • docs/guides/configuration.md
**/*.{js,jsx,ts,tsx,json,yml,yaml,md,css,scss}

📄 CodeRabbit inference engine (CLAUDE.md)

Let Prettier handle all non-Ruby file formatting; never manually format these files

Files:

  • CHANGELOG.md
  • docs/guides/configuration.md
🧠 Learnings (4)
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.

Applied to files:

  • spec/dummy/spec/helpers/react_on_rails_helper_spec.rb
  • spec/dummy/config/initializers/react_on_rails.rb
  • lib/react_on_rails/utils.rb
  • lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.

Applied to files:

  • lib/generators/react_on_rails/base_generator.rb
  • lib/react_on_rails/packs_generator.rb
  • lib/react_on_rails/utils.rb
  • lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.

Applied to files:

  • spec/react_on_rails/configuration_spec.rb
  • lib/react_on_rails/configuration.rb
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.

Applied to files:

  • lib/react_on_rails/utils.rb
  • lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt
🧬 Code graph analysis (5)
lib/react_on_rails/packs_generator.rb (1)
lib/react_on_rails/configuration.rb (1)
  • configuration (16-59)
spec/react_on_rails/configuration_spec.rb (1)
lib/react_on_rails/configuration.rb (1)
  • configure (6-9)
lib/react_on_rails/packer_utils.rb (1)
lib/react_on_rails/utils.rb (1)
  • gem_available? (249-259)
spec/react_on_rails/utils_spec.rb (2)
lib/react_on_rails/packer_utils.rb (3)
  • packer_public_output_path (105-107)
  • packer (16-21)
  • packer_type (12-14)
lib/react_on_rails/utils.rb (3)
  • bundle_js_file_path (73-108)
  • server_bundle_js_file_path (158-163)
  • rsc_bundle_js_file_path (165-170)
lib/react_on_rails/utils.rb (2)
lib/react_on_rails/configuration.rb (1)
  • configuration (16-59)
lib/react_on_rails/packer_utils.rb (2)
  • using_packer? (5-10)
  • packer_public_output_path (105-107)
🔇 Additional comments (21)
.gitignore (1)

64-66: Ignore private SSR bundles: LGTM

Adding ssr-generated to .gitignore aligns with the new private server bundle output.

lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt (1)

47-55: Private SSR output path: LGTM

Outputting server bundles to a private ssr-generated directory with no publicPath is correct for SSR.

Please confirm this path (Rails.root/ssr-generated) matches the default in the initializer template and the docs to avoid confusion.

lib/react_on_rails/packs_generator.rb (1)

165-169: Use of blank? for server bundle check: LGTM

Switching to blank? correctly handles nil and whitespace.

spec/dummy/config/initializers/react_on_rails.rb (1)

30-34: Dummy app keeps SSR bundles public-path based: LGTM

Explicitly setting server_bundle_output_path = nil and enforce_private_server_bundles = false for the dummy app makes sense.

lib/generators/react_on_rails/base_generator.rb (1)

140-151: Robust .gitignore updates: LGTM

Building a deduped additions list and appending with a clear header is solid.

spec/dummy/spec/helpers/react_on_rails_helper_spec.rb (1)

6-6: Simplified Packer constant: LGTM

Directly aliasing Packer = Shakapacker is clearer than dynamic resolution in tests.

CHANGELOG.md (1)

26-53: Comprehensive documentation of new server bundle security features

This documentation provides excellent coverage of the new server bundle security features. The three-tier fallback system for bundle path resolution is well explained, as are the new configuration options and their security implications.

spec/react_on_rails/configuration_spec.rb (1)

433-487: Comprehensive test coverage for private server bundle validation

Excellent test coverage for the new server bundle security validation. The tests properly cover both enforcement scenarios (enabled/disabled) and validate the path validation logic including edge cases like nil paths and public directory containment.

lib/react_on_rails/packer_utils.rb (2)

89-90: LGTM: Consistent usage of simplified detection method

The precompile method properly uses the simplified using_packer? method for consistency.


5-10: Shakapacker detection — tests remain valid

spec/react_on_rails/utils_spec.rb (shared_context "without packer enabled") mocks described_class.gem_available?("shakapacker"). PackerUtils now calls ReactOnRails::Utils.gem_available? directly, so the mock still intercepts the call and no test changes are required.

spec/react_on_rails/utils_spec.rb (3)

79-82: Enhanced mock configuration for server bundle testing

Good enhancement to include mocks for the new server_bundle_output_path and enforce_private_server_bundles configuration options. The default values align with the configuration defaults.


148-199: Comprehensive fallback testing for bundle path resolution

Excellent test coverage for the new fallback logic including:

  • Standard path fallback when environment-specific paths don't exist
  • Environment-specific path as final fallback when no bundles exist
  • Private server bundle enforcement that bypasses public fallbacks

The tests properly mock file existence and verify the correct fallback behavior.


246-312: Thorough testing of server bundle path resolution

Good test coverage for various server bundle scenarios:

  • Private SSR-generated path for server bundles
  • Configured path behavior that skips manifest lookup
  • Fallback to standard locations when configured path is nil
  • Environment-specific path as final fallback

The tests properly verify the early return behavior when server_bundle_output_path is configured.

lib/react_on_rails/configuration.rb (4)

56-57: New server bundle security configuration options

Good addition of the new configuration options with sensible defaults:

  • server_bundle_output_path: nil allows opt-in configuration
  • enforce_private_server_bundles: false maintains backward compatibility

73-74: Updated attribute accessors for new configuration options

The new attributes are properly added to the Configuration class accessors.


91-91: Proper initialization of new configuration parameters

The new configuration parameters are correctly added to the constructor and properly assigned in the initialization.

Also applies to: 136-137


154-154: Robust private server bundle validation

The validation method implements comprehensive security checks:

  1. Only validates when enforcement is enabled
  2. Checks for nil server_bundle_output_path
  3. Validates the path is outside the public directory
  4. Includes proper Rails.root availability checks for test environments

The error messages are clear and actionable for developers.

Also applies to: 203-225

lib/react_on_rails/utils.rb (4)

74-83: Early return optimization for configured server bundles

Excellent optimization that allows server bundles to bypass manifest lookup when server_bundle_output_path is configured. This improves performance and supports the private server bundle feature.


110-139: Comprehensive fallback logic for missing manifest entries

The handle_missing_manifest_entry method implements a robust 3-tier fallback system:

  1. Environment-specific path (when using packer)
  2. Standard Shakapacker location (public/packs)
  3. Generated assets path (legacy support)

The method properly respects private server bundle enforcement by returning private paths when enforcement is enabled.


141-156: Well-designed helper methods for server bundle handling

The private class methods provide clean abstractions:

  • server_bundle? correctly identifies both server and RSC bundles
  • enforce_private_server_bundles? provides clean access to configuration
  • server_bundle_private_path handles path construction with sensible defaults

236-247: Clear method naming and deprecation strategy

The introduction of public_bundles_full_path with deprecation of generated_assets_full_path provides better clarity about public vs private bundle paths. The deprecation approach maintains backward compatibility while guiding users toward the clearer API.

Copy link

claude bot commented Sep 23, 2025

Code Review for PR #1807: Improve Bundle Path Resolution

I have reviewed the changes in this PR and here is my feedback:

Strengths

  1. Good Security Enhancement: The addition of enforce_private_server_bundles configuration is a valuable security improvement that prevents server-side code from being accidentally exposed through public directories.

  2. Backward Compatibility: The changes maintain backward compatibility by defaulting enforce_private_server_bundles to false and server_bundle_output_path to nil.

  3. Comprehensive Documentation: The configuration guide has been well-documented with clear examples showing the separation between client and server bundles.

  4. Proper Validation: Good validation logic for ensuring server bundles are not placed in public directories when enforcement is enabled.

🔍 Areas for Improvement

1. Bundle Path Resolution Logic Complexity

The handle_missing_manifest_entry method in lib/react_on_rails/utils.rb has complex fallback logic that could be simplified. Consider extracting the fallback location building into a separate method for clarity and testability.

2. Potential Path Traversal Risk

In bundle_js_file_path, when using server_bundle_output_path, while File.expand_path helps, consider adding validation to ensure bundle_name does not contain path traversal sequences like "../".

3. Test Coverage Gaps

The tests should include:

  • Edge cases for bundle path resolution with missing files
  • Verification that server bundles are actually loaded from private directories when enforcement is enabled
  • Tests for the new public_bundles_full_path method

4. Generator Template Configuration

The generator template sets config.enforce_private_server_bundles = true by default. This might break existing applications on upgrade. Consider keeping it false by default in the template and providing migration documentation.

⚠️ Potential Issues

  1. Hard-coded Path in Webpack Config:
    The webpack template has a hard-coded path ../../ssr-generated which should use a configurable path or at least match the Ruby configuration default.

  2. Method Deprecation Without Warning:
    The generated_assets_full_path method is aliased but not properly deprecated with a deprecation warning. Consider adding an ActiveSupport::Deprecation.warn message.

  3. Missing Rubocop Disable:
    The validate_enforce_private_server_bundles method exceeds the standard method length. Consider adding a rubocop disable comment or refactoring into smaller methods.

📝 Minor Suggestions

  1. CHANGELOG Entry: The CHANGELOG is comprehensive but could be clearer about the migration path for existing applications.

  2. Error Messages: Consider making error messages more actionable by providing examples of correct configuration.

  3. Comment Clarity: Some inline comments could be more descriptive about the "why" rather than the "what".

Performance Considerations

The fallback path checking using File.exist? in a loop is fine for a small number of paths, but consider caching the result during development if this method is called frequently.

🔒 Security Review

  • ✅ Validation prevents server bundles in public directories
  • ✅ Uses File.expand_path to resolve paths
  • ⚠️ Consider adding input sanitization for bundle_name parameter
  • ⚠️ Document security implications in upgrade guide

📊 Overall Assessment

This is a well-thought-out improvement that addresses real security concerns while maintaining backward compatibility. The main areas for improvement are around code organization, test coverage, and ensuring the webpack configuration template matches the Ruby defaults. With the suggested improvements, this will be a solid enhancement to the library.

Recommendation: Address the potential issues mentioned above, particularly the webpack config hard-coded path and the deprecation warning, before merging.

Copy link

claude bot commented Sep 23, 2025

Code Review for PR #1807: Improve Bundle Path Resolution

Overall Assessment

This PR significantly improves the security and organization of server bundles by introducing configurable output paths and enforcement of private server bundle loading. The changes address important security concerns while maintaining backward compatibility.

Strengths ✅

  1. Security Enhancement: The introduction of enforce_private_server_bundles configuration prevents potential security risks by ensuring server-side code isn't exposed through public directories.
  2. Backward Compatibility: The default configuration maintains existing behavior, preventing breaking changes for current users.
  3. Comprehensive Testing: Excellent test coverage with new specs for configuration validation and bundle path resolution.
  4. Clear Documentation: Thorough documentation updates in configuration.md with helpful examples and security warnings.
  5. Improved Error Messages: Clear validation errors guide users to correct configuration.

Issues to Address 🔧

1. Potential Path Traversal in handle_missing_manifest_entry

In lib/react_on_rails/utils.rb:110-137, the fallback logic doesn't validate that resolved paths stay within expected boundaries. The File.expand_path and File.exist? checks could potentially be exploited.
Recommendation: Add path validation to ensure resolved paths are within Rails.root.

2. File Existence Check May Leak Information

The File.exist? check in production could potentially reveal filesystem structure.
Recommendation: Consider logging warnings instead of checking file existence in production mode.

3. Thread Safety Concern

The instance variable caching pattern (@server_bundle_path) in server_bundle_js_file_path may have thread safety issues in multi-threaded environments.
Recommendation: Consider using thread-safe caching mechanisms.

Performance Considerations 🚀

  1. Multiple File System Checks: The fallback mechanism performs multiple File.exist? checks which could impact performance in high-traffic scenarios.

    • Consider caching the results or checking only in development/test environments.
  2. Repeated Configuration Access: Multiple calls to ReactOnRails.configuration in hot paths.

    • Consider caching configuration values within the method scope.

Best Practices Suggestions 💡

  1. Extract Constants: Magic strings like "ssr-generated" should be extracted as constants.

  2. Deprecation Notice: The generated_assets_full_path alias should include a proper deprecation warning using ActiveSupport::Deprecation.

  3. Configuration Validation Order: The validation in validate_enforce_private_server_bundles could be more efficient by checking simpler conditions first.

Minor Issues 📝

  1. Gitignore Updates: The generator now adds both /generated/ and ssr-generated entries. Consider consolidating under a single comment block for clarity.

  2. Test Coverage: While specs are comprehensive, consider adding integration tests for the fallback path resolution logic with actual file system operations.

  3. Method Naming: The private method server_bundle? could be more descriptive, perhaps is_server_side_bundle?.

Security Recommendation 🔒

Consider adding a configuration option to disable fallback path resolution entirely in production for maximum security:

  • config.strict_bundle_path_resolution = true # No fallbacks in production

Summary

This is a well-thought-out PR that addresses important security concerns while maintaining backward compatibility. The code is clean and well-tested. With the suggested improvements, particularly around path validation and thread safety, this will be a valuable addition to React on Rails.

Recommendation: Approve with minor revisions to address the path traversal validation and thread safety concerns.

Copy link

claude bot commented Sep 23, 2025

Code Review for PR #1807: Improve Bundle Path Resolution

I have completed a thorough review of this pull request. Here is my comprehensive feedback:

Strengths

  1. Well-Structured Architecture: The dual package structure (Ruby gem + NPM package) maintains clean separation between server and client concerns.

  2. Comprehensive Configuration System: The configuration in lib/react_on_rails/configuration.rb provides robust validation with sensible defaults.

  3. Security Best Practices:

    • Proper input sanitization using ReactOnRails::JsonOutput.escape
    • XSS protection through controlled html_safe usage
    • Path validation to prevent directory traversal
    • Secure shell command handling
  4. Performance Optimizations:

    • Server rendering pool with configurable sizing
    • Connection pooling for ExecJS contexts
    • React 18 streaming support
    • Deferred component loading strategies

🔍 Areas Requiring Attention

  1. Shell Command Execution Risk (lib/generators/react_on_rails/generator_helper.rb:80):

    • Using backticks for shell commands with interpolated variables poses injection risk
    • Fix: Replace with File.symlink or FileUtils.ln_s
  2. Node Detection Using Shell (lib/generators/react_on_rails/install_generator.rb):

    • Backticks used for node detection and version checking
    • Fix: Use Open3.capture2 for safer system calls
  3. Error Message Information Disclosure:

    • Some error messages expose internal paths
    • Fix: Sanitize error messages in production

📊 Test Coverage Analysis

  • 34 spec files provide solid core functionality coverage
  • Test helpers and utilities are well-tested
  • Recommendation: Add integration tests for security-critical paths

🚀 Performance Suggestions

  1. Implement caching for frequently rendered components
  2. Add performance metrics for server rendering pool
  3. Consider lazy loading for infrequently used components

📝 Code Quality Improvements

  1. Configuration class (381 lines) could benefit from modularization
  2. Some methods exceed 50 lines and should be extracted
  3. Complex methods need additional inline documentation

🔒 Security Enhancements

  1. Implement rate limiting for SSR to prevent DoS
  2. Add CSP headers for rendered components
  3. Include CSRF token validation for component props

Compliance Check

  • Follows Rails conventions ✓
  • Proper use of concerns and modules ✓
  • Good separation of concerns ✓
  • Comprehensive error handling ✓

📋 Required Changes

Before Merge:

  • Replace backtick command execution
  • Add path/filename input validation

Post-Merge Improvements:

  • Refactor large classes
  • Add security integration tests
  • Implement SSR rate limiting

Final Assessment

The codebase demonstrates solid engineering practices with good security posture. The main concern is shell command execution which must be addressed before production deployment.

Review Status: Approved with required changes - Please address the shell command execution issues before merging.

Excellent work on the React/Rails integration architecture! The bundle path resolution improvements look good overall.

Copy link

claude bot commented Sep 23, 2025

Code Review for PR #1807: Improve Bundle Path Resolution

Overall Assessment

This PR introduces significant improvements to bundle path resolution and adds security-focused configuration options for server bundles. The changes are well-structured and maintain backward compatibility while addressing important architectural concerns.

Strengths

  1. Security-First Approach: Excellent addition of enforce_private_server_bundles option to prevent server-side code from being exposed in public directories. The default to false ensures backward compatibility.

  2. Comprehensive Fallback Logic: The improved bundle path resolution with multiple fallback locations is robust and handles edge cases well:

    • Environment-specific paths
    • Standard Shakapacker locations
    • Legacy generated assets paths
  3. Clean Configuration API: The new configuration options (server_bundle_output_path and enforce_private_server_bundles) are well-documented and follow Rails conventions.

  4. Excellent Test Coverage: The PR includes thorough test cases covering:

    • Standard path resolution
    • Fallback scenarios
    • Server vs client bundle differentiation
    • Configuration option behavior
  5. Method Naming Improvement: The addition of public_bundles_full_path clarifies the distinction between public and private assets, though the deprecation of generated_assets_full_path should be handled carefully.

⚠️ Areas for Consideration

  1. Performance: The fallback logic checks multiple file paths with File.exist? which could impact performance in scenarios where manifest lookups frequently fail. Consider caching the results or adding monitoring to track how often fallbacks are triggered.

  2. Default Value Choice: Setting server_bundle_output_path to nil by default is good for backward compatibility, but consider documenting a migration path for existing users to adopt the more secure "ssr-generated" configuration.

  3. Error Handling: When enforce_private_server_bundles is enabled and no private path exists, the code returns the path anyway (line 111 in utils.rb). Consider whether this should throw an error instead to fail fast in production.

  4. Code Complexity: The handle_missing_manifest_entry method (lines 109-136) has grown complex. Consider extracting the fallback location building into a separate method for better maintainability.

🔒 Security Considerations

✅ The security improvements are solid:

  • Server bundles can now be kept outside public directories
  • Enforcement option prevents accidental exposure
  • Validation ensures configuration consistency

Consider adding a warning log when server bundles are found in public directories but enforcement is disabled, to help users identify potential security improvements.

🐛 Potential Issues

  1. Rails.root Handling: Line 78 in utils.rb uses Rails.root || "." which could cause issues in non-Rails contexts. Consider using defined?(Rails) && Rails.root for safer fallback.

  2. Missing Constants: The PR references Shakapacker::Manifest::MissingEntryError directly but should handle cases where Shakapacker might not be loaded (though the gemspec requires it).

📝 Documentation & Migration

The documentation updates are comprehensive. Consider adding:

  1. A migration guide for users wanting to adopt the secure server bundle configuration
  2. Examples showing the directory structure with the new configuration
  3. Performance implications of the fallback chain

Recommendation

This is a well-crafted PR that addresses important architectural and security concerns while maintaining backward compatibility. The code quality is high, tests are comprehensive, and the implementation is thoughtful.

I recommend approval after addressing the minor concerns above, particularly:

  1. Adding better error handling when enforcement is enabled
  2. Considering performance monitoring for the fallback chain
  3. Improving the Rails.root handling for edge cases

Excellent work on improving the bundle path resolution and security posture of React on Rails! 🎉

Copy link

claude bot commented Sep 23, 2025

Code Review

I have reviewed PR #1807 "For abanoub improve bundle path resolution" and here is my feedback:

🎯 Overall Assessment

This PR adds valuable security and organization improvements for server bundle handling. The implementation is well-thought-out with good backward compatibility.

✅ Strengths

  1. Excellent Security Enhancement: The enforce_private_server_bundles option provides important security by preventing server-side code exposure
  2. Clear Separation of Concerns: New public_bundles_full_path method better distinguishes between public webpack bundles and private server bundles
  3. Backward Compatibility: Defaults maintain existing behavior while providing upgrade path
  4. Comprehensive Documentation: Configuration guide clearly explains the new options with helpful examples
  5. Good Test Coverage: New tests validate the security enforcement logic

🔍 Issues & Suggestions

1. Potential Path Traversal Risk (Security)

In lib/react_on_rails/utils.rb:

  • The server_bundle_private_path method directly uses config.server_bundle_output_path without sanitization
  • Consider adding path validation to prevent directory traversal attacks

2. Incomplete Validation (Bug)

In lib/react_on_rails/configuration.rb:validate_enforce_private_server_bundles:

  • Validation only runs when Rails.root exists
  • Could silently pass in non-Rails contexts where validation is still important
  • Consider raising an error or warning when validation cannot be performed

3. Missing File Existence Check (Performance)

In handle_missing_manifest_entry:

  • The fallback locations are checked with File.exist?, but server_bundle_private_path does not verify file existence
  • This could lead to returning non-existent paths for server bundles

4. Template Defaults Inconsistency

  • The template sets enforce_private_server_bundles = true by default
  • But the configuration defaults to false
  • New projects get stricter security, existing ones do not - this is good but should be highlighted in upgrade docs

💡 Recommendations

  1. Add Path Sanitization: Sanitize bundle_name using File.basename to prevent path traversal

  2. Improve Validation Robustness: Add validation for path traversal patterns (..) and absolute paths even when Rails.root is not available

  3. Add Upgrade Documentation: Consider adding a migration guide for existing apps wanting to adopt the new security features

✔️ Code Quality

  • Follows project conventions and style guidelines per CLAUDE.md
  • Changes are well-structured and maintainable
  • Good use of private class methods for internal logic
  • Proper use of RuboCop conventions

🚀 Performance Considerations

  • The multiple fallback location checks could add overhead in production
  • Consider caching the resolved paths in production environment

✅ Testing

  • Good test coverage for the new validation logic
  • Tests properly mock Rails.root for path validation
  • Consider adding tests for path traversal prevention scenarios

Conclusion

This is a solid PR that adds important security features. With the suggested improvements around path sanitization and validation robustness, it will be production-ready. The backward compatibility approach is commendable.

Recommendation: Approve with minor changes suggested above.

Great work on improving the security posture of server-side rendering!

Copy link

claude bot commented Sep 23, 2025

Pull Request Review

Thank you for this PR improving bundle path resolution! I have conducted a thorough review and have the following feedback:

✅ Strengths

  1. Enhanced Security: The addition of enforce_private_server_bundles and server_bundle_output_path configuration options provides excellent security improvements by preventing server-side code from being exposed through public directories.

  2. Better Fallback Logic: The improved bundle path resolution with multiple fallback locations makes the system more robust when manifest lookups fail.

  3. Comprehensive Documentation: The configuration guide updates clearly explain the new features with helpful examples.

  4. Strong Test Coverage: The PR includes thorough test coverage for all new functionality including edge cases.

  5. Backward Compatibility: Default values maintain backward compatibility while allowing users to opt-in to enhanced security.

🔍 Areas for Improvement

1. Code Quality Issues

a. Simplified Packer Utils
The simplification of PackerUtils.using_packer? removes version checking but doesn't explain why. This could break compatibility with older Shakapacker versions.

b. Use of Dynamic Constant Resolution
Line 99 in utils.rb still uses:

rescue Object.const_get(ReactOnRails::PackerUtils.packer_type.capitalize)::Manifest::MissingEntryError

This should be simplified to Shakapacker::Manifest::MissingEntryError since Shakapacker is now required.

2. Potential Bugs

a. Race Condition Risk
The server_bundle? check happens before the manifest lookup, which could cause issues if bundle names change during runtime or in development mode with hot reloading.

b. Path Expansion Inconsistency
In handle_missing_manifest_entry, some paths use File.expand_path while others don't, which could lead to path comparison issues.

3. Performance Considerations

a. Redundant File System Checks
The fallback logic performs multiple File.exist? calls sequentially. Consider checking all paths in parallel or caching results for frequently accessed bundles.

b. Configuration Access
Multiple calls to ReactOnRails.configuration could be optimized by storing it in a local variable.

4. Security Concerns

a. Directory Traversal Risk
The server_bundle_output_path validation only checks if it starts with the public directory. Consider also validating against directory traversal patterns (e.g., ../).

b. Symlink Handling
The path validation doesn't account for symlinks that could point from a private directory to a public one.

5. Test Coverage Gaps

While test coverage is generally good, consider adding tests for:

  • Symlink scenarios
  • Directory traversal attempts
  • Concurrent access patterns
  • Bundle name changes during runtime

📝 Specific Recommendations

  1. Add Version Compatibility Check: If removing Shakapacker version checking was intentional, add a comment explaining the minimum required version.

  2. Improve Error Messages: When enforce_private_server_bundles validation fails, include the actual resolved path in the error message for better debugging.

  3. Add Configuration Validation Helper: Create a dedicated method to validate all server bundle configurations together rather than spread across multiple methods.

  4. Consider Adding Logging: Add debug logging for bundle path resolution to help troubleshoot issues in production.

  5. Update CHANGELOG: Consider adding a migration guide section for users upgrading from older versions.

🎯 Minor Issues

  • Line endings: Ensure all files have consistent line endings (the PR seems fine here based on CLAUDE.md requirements)
  • Generated files: The gitignore correctly adds ssr-generated
  • Template consistency: The webpack server config template properly uses the new private directory

✅ CI/Linting Compliance

Based on the repository's CLAUDE.md requirements:

  • Files appear to end with newlines ✅
  • RuboCop compliance needs verification (please run bundle exec rubocop)
  • The code follows the formatting requirements

Verdict

This is a valuable improvement to React on Rails' security and reliability. With the suggested improvements addressed, particularly the security validations and performance optimizations, this will be a great addition to the codebase.

Recommendation: Approve with requested changes focused on security validation improvements and the const_get simplification.

justin808 and others added 21 commits September 23, 2025 14:07
- Fix fallback logic when manifest lookup fails to check multiple locations
- Try environment-specific path, standard location (public/packs), then generated assets path
- Return first path where bundle file actually exists
- Maintains backward compatibility with original behavior as final fallback
- Add comprehensive test cases to prevent regression

Fixes issue where server rendering failed in test environments when bundles
were in standard Shakapacker location but manifest lookup pointed to
environment-specific directory.

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

Co-Authored-By: Claude <[email protected]>
Major improvements to bundle_js_file_path logic:

**Security & Architecture:**
- Server bundles (SSR/RSC) now try secure non-public locations first:
  - ssr-generated/ (primary)
  - generated/server-bundles/ (secondary)
- Client bundles continue using manifest lookup as primary approach
- Prevents exposure of server-side logic in public directories

**Priority Order:**
- SERVER BUNDLES: secure locations → manifest → legacy public locations
- CLIENT BUNDLES: manifest → fallback locations (original behavior)
- Fixed priority order (normal case first, edge cases second)

**Code Quality:**
- Extracted complex method into smaller, focused private methods
- Reduced cyclomatic complexity and improved maintainability
- Added comprehensive test coverage for all scenarios
- Added ssr-generated/ to .gitignore

**Backwards Compatibility:**
- Legacy public locations still work as fallbacks
- Existing client bundle behavior unchanged
- Gradual migration path for server bundles

This addresses the core architectural issue where server bundles were
unnecessarily exposed in public directories while maintaining full
compatibility with existing setups.

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

Co-Authored-By: Claude <[email protected]>
Introduces two new configuration options to enhance server bundle path
resolution and security:

- server_bundle_output_path: Configurable directory for server bundle output
  (defaults to "ssr-generated")
- enforce_secure_server_bundles: Optional security enforcement for server
  bundle loading (defaults to false for backward compatibility)

These options work in conjunction with the existing bundle path resolution
improvements to provide better organization and security for server-side
rendering assets.

Features:
- Secure server bundle location configuration
- Backward compatibility maintained with sensible defaults
- Comprehensive documentation added to configuration guide
- Full parameter support in Configuration class initialization

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

Co-Authored-By: Claude <[email protected]>
- Changes default from "ssr-generated" to nil to avoid breaking changes
- Updates documentation to reflect nil default and show example usage
- Feature remains opt-in for backward compatibility
- Can be changed to a sensible default in next major release

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

Co-Authored-By: Claude <[email protected]>
- Restores using_packer? check to handle test scenarios where packer is disabled
- Combines manifest.json check with packer availability in cleaner conditional
- Reverts one inline Packer assignment that breaks when Shakapacker isn't loaded
- Maintains original functionality while keeping code improvements

All failing tests now pass:
- webpack_assets_status_checker_spec.rb (all scenarios)
- utils_spec.rb "without a packer enabled" scenarios

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

Co-Authored-By: Claude <[email protected]>
… handling

Since Shakapacker is required by gemspec, the using_packer? check in
bundle_js_file_path is unnecessary for production code. However, tests
mock this scenario for validation.

Changes:
- Remove using_packer? check from main bundle_js_file_path logic
- Add guard check in bundle_js_file_path_with_packer for test scenarios
- Maintains clean production code while handling test mocking properly
- All tests pass including "without packer" scenarios

This is the correct approach: main logic assumes Shakapacker is available
(as guaranteed by gemspec), while method implementation handles edge cases
for comprehensive test coverage.

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

Co-Authored-By: Claude <[email protected]>
Major Features:
- Implement enforce_secure_server_bundles configuration logic
- When enabled, server bundles MUST be found in private locations
- Skip manifest fallback for server bundles when enforcement is active
- Use configured server_bundle_output_path as additional private location

Code Quality Improvements:
- Extract duplicated file existence checking into find_first_existing_path helper
- Use inline private_class_method declarations for better readability
- Rename "secure" to "private" locations for clearer terminology
- Handle Rails.root being nil in test environments
- Use File.join consistently instead of Rails.root.join for compatibility

Test Infrastructure:
- Add comprehensive mocking for new configuration options
- Fix test contexts that override mock_bundle_configs helper
- Ensure all test scenarios properly mock new configuration keys
- All previously failing tests now pass

Security & Performance:
- Private server bundle locations checked first (ssr-generated, generated/server-bundles)
- Configurable server_bundle_output_path included in private location search
- Enforcement prevents fallback to public manifest locations when enabled
- Maintains backward compatibility with enforcement disabled by default

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

Co-Authored-By: Claude <[email protected]>
- Set 'ssr-generated' as default value for server_bundle_output_path
- Remove hard-coded paths from try_private_server_locations
- Update handle_missing_manifest_entry to respect configuration
- Maintain backwards compatibility with 'generated/server-bundles' fallback
- Honor enforce_secure_server_bundles flag to prevent public fallbacks

Co-authored-by: Abanoub Ghadban <[email protected]>
- Remove enforce_secure_server_bundles from bundle resolution logic
- Simplify utils.rb by removing hard-coded paths
- Use server_bundle_output_path configuration directly
- Add validation to ensure enforce_secure_server_bundles and server_bundle_output_path are compatible
- Set server_bundle_output_path default to 'ssr-generated'
- Update generator templates to use secure server bundle configuration
- Update tests to match new implementation

Co-authored-by: Abanoub Ghadban <[email protected]>
- Fix RuboCop violations:
  - Use string interpolation instead of concatenation in base_generator.rb
  - Apply guard clause pattern in configuration.rb
  - Remove extra blank line in utils.rb

- Fix configuration validation tests:
  - Add Rails.root availability check to prevent nil errors in tests
  - Mock Rails.root in test specs for path validation

- Fix utils tests:
  - Use default 'ssr-generated' path instead of nil in mock_bundle_configs
  - Update test expectations to match new server bundle path behavior
  - Remove outdated test expecting server bundles in public/packs

Co-authored-by: Abanoub Ghadban <[email protected]>
Set server_bundle_output_path to nil and enforce_secure_server_bundles to false
in the dummy app's React on Rails configuration. This ensures the dummy app
uses the standard webpack output location (public/webpack/test) where bundles
are actually built during tests, resolving the CI failures.

Co-authored-by: Abanoub Ghadban <[email protected]>
…t existence check

When server_bundle_output_path is configured, the bundle path resolution now returns
the configured path immediately without checking if the file exists. This ensures
predictable behavior and allows the server rendering process to handle missing files
appropriately.

Updates:
- Modified bundle_js_file_path_with_packer to return configured path directly
- Removed try_server_bundle_output_path method (no longer needed)
- Updated tests to reflect new behavior without File.exist? checks

Co-authored-by: Abanoub Ghadban <[email protected]>
- Fix RuboCop Layout/EmptyLines violation in lib/react_on_rails/utils.rb
- Fix 6 failing tests that expect manifest paths for server bundles
- Tests now properly clear server_bundle_output_path when testing manifest fallback behavior

Co-authored-by: Abanoub Ghadban <[email protected]>
- Renamed enforce_secure_server_bundles to enforce_private_server_bundles
- Updated validation method name to validate_enforce_private_server_bundles
- Updated all comments, test descriptions, and documentation to use 'private' instead of 'secure'
- Updated configuration files, templates, and specs to reflect new terminology

This change provides clearer terminology - 'private' better describes
non-public directories vs 'secure' which has broader security implications

Co-authored-by: Justin Gordon <[email protected]>
- Fix fallback logic when manifest lookup fails to check multiple locations
- Try environment-specific path, standard location (public/packs), then generated assets path
- Return first path where bundle file actually exists
- Maintains backward compatibility with original behavior as final fallback
- Add comprehensive test cases to prevent regression

Fixes issue where server rendering failed in test environments when bundles
were in standard Shakapacker location but manifest lookup pointed to
environment-specific directory.

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

Co-Authored-By: Claude <[email protected]>
- Fix duplicate and incomplete test cases in utils_spec.rb
- Update configuration.md to reflect correct default for server_bundle_output_path
- Add clarification about enforce_private_server_bundles enforcement

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

Co-Authored-By: Claude <[email protected]>
- Add back server bundle logic that respects server_bundle_output_path configuration
- Server bundles with configured output path skip manifest lookup for better performance
- Server bundles without configured path use fallback logic like client bundles
- Fix test mocking to properly test fallback behavior when server_bundle_output_path is nil
- All utils tests now pass (53/53)

This integrates the simpler remote approach while preserving the essential
server bundle functionality that the configuration and tests expect.

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

Co-Authored-By: Claude <[email protected]>
… default to nil

Address code review feedback:
- Change server_bundle_output_path default from 'ssr-generated' to nil for backwards compatibility
- Update documentation to reflect the nil default and explain opt-in behavior
- Existing applications continue to work without modification
- New applications can opt-in by setting server_bundle_output_path = 'ssr-generated'

The server bundle logic is confirmed working:
- server_bundle_js_file_path() → bundle_js_file_path() → server bundle logic
- When configured: skips manifest lookup, returns configured path directly
- When nil (default): uses standard fallback logic like client bundles
- All 53 tests pass, confirming correct implementation

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

Co-Authored-By: Claude <[email protected]>
…g fallback locations

Address critical bug in handle_missing_manifest_entry:
- Previously unconditionally called packer_public_output_path causing NoMethodError in test/non-packer environments
- Now conditionally adds packer path only when ReactOnRails::PackerUtils.using_packer? is true
- Build fallback_locations array step by step instead of in one static declaration
- Maintains same fallback behavior when packer is available
- Prevents crashes in environments where packer is not available or mocked as unavailable

This fixes a potential runtime error that could occur in test environments or
setups where Shakapacker is not available or properly initialized.

All 53 tests pass, including specific non-packer environment tests.

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

Co-Authored-By: Claude <[email protected]>
justin808 and others added 5 commits September 23, 2025 14:20
…prehensive test

Add security enforcement logic:
- When enforce_private_server_bundles is enabled, server bundles skip public path fallbacks
- Server bundles return private paths even if they don't exist (preventing public fallback)
- Add server_bundle_private_path helper that respects server_bundle_output_path configuration
- Add enforce_private_server_bundles? helper for clean configuration access

Add comprehensive test coverage:
- Test that enforcement prevents fallback to public paths when enabled
- Mock File.exist? to verify private path is returned even when public path exists
- Update mock_bundle_configs to include enforce_private_server_bundles default (false)
- All 54 tests pass, including new enforcement test

Security benefit:
- Prevents accidental serving of server bundles from public directories
- Ensures server-side code remains private even when deployment scripts fail
- Opt-in feature (defaults to false) for backwards compatibility

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

Co-Authored-By: Claude <[email protected]>
- Add proper mocks for ReactOnRails::PackerUtils.using_packer? in configuration tests
- Ensures shakapacker_precompile validation logic triggers correctly in tests
- Fixes CI failures where precompile? method was returning false due to missing mocks
- All configuration tests now pass (43 tests)

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

Co-Authored-By: Claude <[email protected]>
…ets_full_path

- Add new `public_assets_full_path` method to be explicit about public asset paths
- Keep `generated_assets_full_path` as backwards-compatible deprecated alias
- Update internal references to use clearer `public_assets_full_path` method
- Addresses naming ambiguity concern about whether paths are public or private
- Prepares for future evolution toward simplified public-only asset resolution

This improvement clarifies the distinction between:
- Public asset paths (client bundles, manifests) → public_assets_full_path
- Private asset paths (server bundles with enforcement) → server_bundle_private_path

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

Co-Authored-By: Claude <[email protected]>
…_assets_full_path

- Rename public_assets_full_path → public_bundles_full_path for specificity
- "public_bundles" is clearer than "public_assets" in Rails context
- Avoids confusion with general Rails public assets (images, stylesheets, etc.)
- Method specifically handles webpack bundles, not general assets
- Update all internal references to use the more specific naming
- Maintain backwards compatibility through generated_assets_full_path alias

This creates clear semantic distinction:
- public_bundles_full_path → webpack bundles in public directories
- server_bundle_private_path → server bundles in private directories

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

Co-Authored-By: Claude <[email protected]>
CHANGELOG.md:
- Document all new server bundle security features
- Explain enhanced bundle path resolution with fallback logic
- Document public_bundles_full_path method naming improvement
- Clear categorization: New Features, API Improvements, Security Enhancements, Bug Fixes

docs/guides/configuration.md:
- Add comprehensive SERVER BUNDLE SECURITY AND ORGANIZATION section
- Document server_bundle_output_path with clear examples and defaults
- Explain enforce_private_server_bundles with security implications
- Add BUNDLE ORGANIZATION EXAMPLES section with:
  * Clear client vs server bundle separation
  * Directory structure examples
  * API method references (public_bundles_full_path vs server_bundle_js_file_path)

This documentation makes the new features crystal clear for users upgrading
or configuring server bundle security for the first time.

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

Co-Authored-By: Claude <[email protected]>
@AbanoubGhadban AbanoubGhadban force-pushed the for-abanoub-improve-bundle-path-resolution branch from 870835f to 8d419c9 Compare September 23, 2025 12:01
Copy link
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: 0

🧹 Nitpick comments (4)
spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb (1)

65-76: Avoid leaking a global constant; reference the packer error constant directly.

Defining Packer in a before block leaks a global constant and can trigger constant redefinition warnings. You can drop the RuboCop disables and inline the constant reference.

Apply this diff:

-          Packer = ReactOnRails::PackerUtils.packer # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
           allow(ReactOnRails::PackerUtils).to receive_messages(
             manifest_exists?: true,
             packer_public_output_path: generated_assets_full_path
           )
           allow(ReactOnRails.configuration).to receive(:server_bundle_js_file).and_return("server-bundle.js")
           allow(ReactOnRails::PackerUtils).to receive(:bundle_js_uri_from_packer)
             .with("manifest.json")
             .and_return(File.join(generated_assets_full_path, "manifest.json"))
           allow(ReactOnRails::PackerUtils).to receive(:bundle_js_uri_from_packer)
             .with("server-bundle.js")
-            .and_raise(Packer::Manifest::MissingEntryError)
+            .and_raise(ReactOnRails::PackerUtils.packer::Manifest::MissingEntryError)
           touch_files_in_dir(generated_assets_full_path)

Please run bundle exec rubocop and the spec suite to confirm no warnings or failures.

CHANGELOG.md (1)

1-1843: Fix Prettier formatting issue

The pipeline failure indicates that this file needs Prettier formatting applied.

Run the following command to fix the formatting:

#!/bin/bash
# Apply Prettier formatting to CHANGELOG.md
npx prettier --write CHANGELOG.md
lib/react_on_rails/utils.rb (1)

227-234: Consider adding migration guide for deprecated method

The deprecation of generated_assets_full_path is handled correctly, but users should be informed about how to migrate.

Consider adding a deprecation warning to help users migrate:

 # DEPRECATED: Use public_bundles_full_path for clarity about public vs private bundle paths
 def self.generated_assets_full_path
+  Rails.logger.warn "[DEPRECATION] `generated_assets_full_path` is deprecated. " \
+                   "Please use `public_bundles_full_path` instead." if Rails.env.development?
   public_bundles_full_path
 end
spec/react_on_rails/utils_spec.rb (1)

95-102: Consider extracting test bundle names to constants

The hardcoded bundle names could be extracted to constants for better maintainability.

+      TEST_SERVER_BUNDLE = "server-bundle.js"
+      TEST_RSC_BUNDLE = "rsc-bundle.js"
+      TEST_CLIENT_BUNDLE = "webpack-bundle.js"
+      
       before do
         # Mock configuration calls to avoid server bundle detection for regular client bundles
         allow(ReactOnRails).to receive_message_chain("configuration.server_bundle_js_file")
-          .and_return("server-bundle.js")
+          .and_return(TEST_SERVER_BUNDLE)
         allow(ReactOnRails).to receive_message_chain("configuration.rsc_bundle_js_file")
-          .and_return("rsc-bundle.js")
+          .and_return(TEST_RSC_BUNDLE)
       end
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 870835f and 8d419c9.

📒 Files selected for processing (13)
  • .gitignore (1 hunks)
  • CHANGELOG.md (1 hunks)
  • docs/guides/configuration.md (1 hunks)
  • lib/generators/react_on_rails/base_generator.rb (1 hunks)
  • lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt (1 hunks)
  • lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt (1 hunks)
  • lib/react_on_rails/configuration.rb (6 hunks)
  • lib/react_on_rails/packs_generator.rb (1 hunks)
  • lib/react_on_rails/utils.rb (4 hunks)
  • spec/dummy/config/initializers/react_on_rails.rb (1 hunks)
  • spec/react_on_rails/configuration_spec.rb (5 hunks)
  • spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb (1 hunks)
  • spec/react_on_rails/utils_spec.rb (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
  • lib/react_on_rails/packs_generator.rb
  • lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt
  • .gitignore
  • lib/react_on_rails/configuration.rb
  • spec/react_on_rails/configuration_spec.rb
  • lib/generators/react_on_rails/base_generator.rb
  • docs/guides/configuration.md
  • spec/dummy/config/initializers/react_on_rails.rb
  • lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt
🧰 Additional context used
📓 Path-based instructions (3)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru,js,jsx,ts,tsx,json,yml,yaml,md,css,scss}}

📄 CodeRabbit inference engine (CLAUDE.md)

Ensure all committed files end with a trailing newline character

Files:

  • CHANGELOG.md
  • lib/react_on_rails/utils.rb
  • spec/react_on_rails/utils_spec.rb
  • spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb
**/*.{js,jsx,ts,tsx,json,yml,yaml,md,css,scss}

📄 CodeRabbit inference engine (CLAUDE.md)

Let Prettier handle all non-Ruby file formatting; never manually format these files

Files:

  • CHANGELOG.md
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}

📄 CodeRabbit inference engine (CLAUDE.md)

{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}: Before every commit/push, run bundle exec rubocop and fix all violations in Ruby code
Let RuboCop handle all Ruby code formatting; never manually format Ruby files

Files:

  • lib/react_on_rails/utils.rb
  • spec/react_on_rails/utils_spec.rb
  • spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb
🧠 Learnings (3)
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.

Applied to files:

  • lib/react_on_rails/utils.rb
  • spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.

Applied to files:

  • lib/react_on_rails/utils.rb
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.

Applied to files:

  • lib/react_on_rails/utils.rb
🧬 Code graph analysis (2)
lib/react_on_rails/utils.rb (2)
lib/react_on_rails/configuration.rb (1)
  • configuration (16-59)
lib/react_on_rails/packer_utils.rb (2)
  • bundle_js_uri_from_packer (51-67)
  • packer_public_output_path (100-102)
spec/react_on_rails/utils_spec.rb (2)
lib/react_on_rails/packer_utils.rb (1)
  • packer_public_output_path (100-102)
lib/react_on_rails/utils.rb (3)
  • bundle_js_file_path (74-109)
  • server_bundle_js_file_path (159-164)
  • rsc_bundle_js_file_path (166-171)
🪛 GitHub Actions: Lint JS and Ruby
CHANGELOG.md

[error] 1-1: Prettier formatting check failed on CHANGELOG.md. Run 'prettier --write' to fix.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: dummy-app-integration-tests (3.4, 22)
  • GitHub Check: dummy-app-integration-tests (3.2, 20)
  • GitHub Check: rspec-package-tests (3.2, latest)
  • GitHub Check: rspec-package-tests (3.4, minimum)
  • GitHub Check: rspec-package-tests (3.4, latest)
  • GitHub Check: rspec-package-tests (3.2, minimum)
  • GitHub Check: claude-review
  • GitHub Check: examples (3.4, latest)
  • GitHub Check: examples (3.2, minimum)
🔇 Additional comments (8)
lib/react_on_rails/utils.rb (4)

74-109: Well-implemented server bundle path resolution with proper fallback hierarchy

The early return for server bundles with configured output paths is a good optimization, bypassing unnecessary manifest lookups. The fallback logic for non-server bundles maintains backward compatibility well.


142-157: Clear server bundle detection and enforcement logic

The private helper methods are well-structured and follow the single responsibility principle. The enforcement mechanism for private server bundles provides good security boundaries.


183-192: Good update to use public_bundles_full_path

The change correctly uses the new public_bundles_full_path method for server manifest resolution.


111-140: Robust fallback mechanism with file existence checks — verified

Found consistent references to packer_public_output_path and public/packs in lib/react_on_rails/{utils, packer_utils, configuration} and corresponding specs; fallback paths are exercised and no changes required. Approving changes.

spec/react_on_rails/utils_spec.rb (4)

64-68: Comprehensive test mocking for new configuration options

Good addition of mocks for server_bundle_output_path and enforce_private_server_bundles to support the new functionality.


133-157: Excellent test coverage for fallback scenarios

The tests thoroughly cover both the standard path and environment-specific path fallback scenarios, including file existence checks.


159-184: Strong security enforcement testing

Excellent test for the private server bundle enforcement feature. The test correctly verifies that when enforcement is enabled, the system returns the private path even when it doesn't exist, preventing accidental fallback to public paths.


224-291: Thorough server bundle path testing with multiple scenarios

The tests comprehensively cover:

  • Private SSR-generated paths for server bundles
  • Configured server_bundle_output_path behavior
  • Standard location fallbacks
  • Final fallback to environment-specific paths

Great coverage of edge cases!

Copy link

claude bot commented Sep 23, 2025

Code Review for PR #1807: Improve Bundle Path Resolution

I have completed a comprehensive review of this PR. Here is my feedback:

✅ Strengths

  1. Security Enhancement: The addition of enforce_private_server_bundles configuration with validation is a solid security improvement that prevents server-side code from being exposed in public directories.

  2. Backward Compatibility: The changes maintain backward compatibility by defaulting new options to existing behavior.

  3. Clear Documentation: The configuration documentation clearly explains the new options with helpful examples.

  4. Robust Fallback Logic: The improved bundle path resolution with multiple fallback locations enhances compatibility across different setups.

🔍 Areas for Improvement

1. Critical: Missing RuboCop Compliance

Per CLAUDE.md requirements, ALL commits must pass bundle exec rubocop with zero violations. Please run:

bundle exec rubocop

And fix any violations before merging.

2. Edge Case in Path Validation

In configuration.rb:220, consider handling symlinks properly:

# Current code might fail with symlinked paths
server_output_path = File.expand_path(server_bundle_output_path, Rails.root.to_s)

# Consider using File.realpath to resolve symlinks:
server_output_path = File.realpath(File.expand_path(server_bundle_output_path, Rails.root.to_s))

3. Test Coverage Concerns

  • Missing tests for symlinked paths in validate_enforce_private_server_bundles
  • No integration tests for the new fallback path resolution logic
  • Consider adding tests for Windows path handling (backslashes vs forward slashes)

4. Performance Consideration

In utils.rb:131, the File.exist? checks in the loop could be optimized by caching results for frequently accessed paths, especially in production:

# Consider memoizing for production
@bundle_path_cache ||= {}
fallback_locations.each do |path|
  expanded_path = File.expand_path(path)
  @bundle_path_cache[expanded_path] ||= File.exist?(expanded_path)
  return expanded_path if @bundle_path_cache[expanded_path]
end

5. Potential Race Condition

The File.exist? check in fallback location detection could have TOCTOU (Time-of-Check-Time-of-Use) issues in concurrent environments. Consider handling Errno::ENOENT when actually loading the bundle.

6. Method Deprecation

Good job deprecating generated_assets_full_path, but consider adding a deprecation warning:

def self.generated_assets_full_path
  ActiveSupport::Deprecation.warn(
    "generated_assets_full_path is deprecated. Use public_bundles_full_path instead.",
    caller
  )
  public_bundles_full_path
end

🐛 Potential Bugs

  1. Nil Safety Issue: In utils.rb:102, server_bundle? might fail if configuration values are nil:
# Add nil safety
bundle_name == config.server_bundle_js_file ||
bundle_name == config.rsc_bundle_js_file
# Should be:
bundle_name == config.server_bundle_js_file.presence ||
bundle_name == config.rsc_bundle_js_file.presence
  1. Missing Directory Creation: The webpack config outputs to ssr-generated but does not ensure the directory exists. Consider adding directory creation in the webpack config or documenting this requirement.

🔒 Security Observations

  • Good validation of server_bundle_output_path to prevent directory traversal
  • Consider adding a warning log when falling back to public paths for server bundles in production

📝 Documentation Suggestions

  1. Add migration guide for users moving from public to private server bundles
  2. Document the ssr-generated directory requirement in the webpack configuration template
  3. Consider adding troubleshooting section for common path resolution issues

✅ Recommendation

This PR makes valuable improvements to bundle path resolution and security. After addressing the RuboCop violations (mandatory per CLAUDE.md) and considering the suggestions above, especially the edge cases and test coverage, this will be ready to merge.

The security enhancements alone make this a worthwhile addition to React on Rails.

Copy link

claude bot commented Sep 23, 2025

Code Review for PR #1807: Improve Bundle Path Resolution

Summary

This PR introduces significant improvements to bundle path resolution and adds security features for server bundles. The changes are well-structured and address important security concerns while maintaining backward compatibility.

Strengths 👍

  1. Security Enhancement: The addition of enforce_private_server_bundles and server_bundle_output_path configuration options provides excellent security controls for server-side bundles, preventing potential exposure of server-side code.

  2. Backward Compatibility: The default values (nil and false) ensure existing setups continue working without changes.

  3. Improved Error Handling: The PR properly handles the NoMethodError issue when Shakapacker is not available by using dynamic constant resolution.

  4. Better Fallback Logic: The multi-tier fallback strategy for finding bundles is robust and handles various deployment scenarios.

  5. Clear Documentation: The configuration guide updates are comprehensive with excellent examples.

Issues & Suggestions 🔍

1. Bug Fix in packs_generator.rb

Good catch using .blank? instead of .empty?:

- return if ReactOnRails.configuration.server_bundle_js_file.empty?
+ return if ReactOnRails.configuration.server_bundle_js_file.blank?

This prevents errors when the config is nil.

2. Potential Race Condition

In bundle_js_file_path, when checking for server bundles with configured output path, there is an early return that bypasses manifest lookup. Consider adding a comment explaining why this is safe for server bundles.

3. Method Deprecation Handling

The deprecation of generated_assets_full_path is handled well with an alias, but consider adding a deprecation warning to help users migrate.

4. Path Validation Edge Case

The validation in validate_enforce_private_server_bundles correctly checks if the path is inside public, but consider also validating that the directory is writable.

5. Test Coverage Enhancement

The tests are comprehensive, but consider adding:

  • Test for the fallback behavior when multiple locations are checked
  • Test for server bundle detection with RSC bundles
  • Integration test verifying the full flow with enforce_private_server_bundles enabled

Performance Considerations 🚀

The handle_missing_manifest_entry method performs file existence checks which could be optimized by caching results for the duration of a request, especially in production where bundle locations do not change.

Security Review 🔒

✅ The security enhancements are solid:

  • Server bundles can be isolated from public directories
  • Path validation prevents misconfiguration
  • Clear documentation about security implications

Minor Suggestions 📝

  1. Gitignore Updates: Good addition of ssr-generated to gitignore
  2. Template Updates: The webpack config template correctly outputs to the private directory
  3. Changelog: Well-documented changes with clear categories

Overall Assessment ✅

This is a high-quality PR that improves both security and reliability of the bundle resolution system. The code is well-tested, maintains backward compatibility, and includes comprehensive documentation. The changes follow the project's coding standards and properly handle edge cases.

Recommendation: Approve with minor suggestions for consideration.

Compliance with CLAUDE.md

  • ✅ Files end with newline characters
  • ✅ No manual formatting (letting tools handle it)
  • ✅ Following existing code conventions
  • ✅ No hardcoded secrets or sensitive information

Copy link

claude bot commented Sep 23, 2025

Code Review for PR #1807: Bundle Path Resolution Improvements

🎯 Overall Assessment

This PR makes significant improvements to bundle path resolution and adds valuable security features for server bundle isolation. The changes are well-structured and backwards compatible.

✅ Strengths

  1. Security Enhancement: The new enforce_private_server_bundles option adds an important security layer by ensuring server-side code isn't exposed through public directories.

  2. Backwards Compatibility: Default values maintain existing behavior, making this a non-breaking change.

  3. Clear Documentation: The configuration guide updates are comprehensive with good examples.

  4. Robust Validation: Proper validation for the enforce_private_server_bundles configuration with clear error messages.

  5. Improved Path Resolution: Better fallback logic when manifest lookup fails, trying multiple locations intelligently.

🐛 Potential Issues

  1. Private Class Method Definition: The code uses 'private_class_method def self.method_name' syntax which may not work correctly in all Ruby versions. Consider using the traditional approach:

    def self.server_bundle?(bundle_name)
      # implementation
    end
    private_class_method :server_bundle?
  2. Blank vs Empty Check: In packs_generator.rb:169, using .blank? is correct, but ensure ActiveSupport is available in this context.

  3. Path Expansion Order: The fallback path resolution could benefit from checking file existence before expanding paths to avoid unnecessary file system operations.

⚡ Performance Considerations

  1. File Existence Checks: The fallback mechanism performs multiple File.exist? checks. Consider caching results for frequently accessed bundles during a request lifecycle.

  2. Path Construction: Multiple string operations for path construction could be optimized by pre-computing common paths.

🔒 Security Review

  1. Path Traversal Protection: The validation for server_bundle_output_path is good, but consider adding protection against path traversal attempts (e.g., paths containing '../').

  2. Symlink Consideration: The path validation should use File.realpath to resolve symlinks before checking if the path is inside the public directory.

🧪 Test Coverage

  1. Good Coverage: The configuration validation tests are comprehensive.

  2. Missing Edge Cases: Consider adding tests for:

    • Symlinked directories
    • Path traversal attempts
    • Bundle resolution when files don't exist
    • Behavior when Rails.root is nil (non-Rails environments)

💡 Suggestions for Improvement

  1. Deprecation Warning: Since generated_assets_full_path is being deprecated, add a deprecation warning:

    def self.generated_assets_full_path
      ActiveSupport::Deprecation.warn('generated_assets_full_path is deprecated. Use public_bundles_full_path instead.')
      public_bundles_full_path
    end
  2. Configuration DSL: Consider adding a DSL for bundle path configuration to make it more intuitive:

    config.bundle_paths do |paths|
      paths.server_output = 'ssr-generated'
      paths.enforce_private = true
    end
  3. Logging: Add debug logging when falling back to different bundle locations to help troubleshoot path resolution issues.

📝 Minor Issues

  1. Comment Consistency: Some comments use "e.g." while others use "for example" - standardize for consistency.

  2. Whitespace: Ensure all files end with a newline as per CLAUDE.md requirements.

✅ Conclusion

This is a solid PR that adds valuable functionality while maintaining backwards compatibility. The security enhancements are particularly welcome. With the minor adjustments suggested above (especially the private class method syntax and symlink handling), this will be ready to merge.

Great work on improving the bundle path resolution system! 👍

Copy link

claude bot commented Sep 23, 2025

Code Review for PR #1807: Bundle Path Resolution Improvements

Summary

This PR introduces significant improvements to bundle path resolution, particularly for server-side rendering bundles, with a focus on security and organizational separation between client and server assets.

✅ Strengths

  1. Security Enhancement: The introduction of private bundle paths for server-side bundles is an excellent security improvement, preventing potentially sensitive server-side code from being exposed in public directories.

  2. Backward Compatibility: The changes maintain backward compatibility well with the default configuration (server_bundle_output_path: nil), allowing for gradual migration.

  3. Comprehensive Test Coverage: The test suite has been properly updated with extensive coverage for various scenarios including:

    • Secure location testing for server bundles
    • Fallback behavior when bundles don't exist
    • Client vs server bundle distinction
    • RSC bundle handling
  4. Clean Code Organization: The refactoring with private methods improves readability and maintainability.

  5. Proper Deprecation: The deprecation of generated_assets_full_path in favor of public_bundles_full_path provides clearer semantic meaning.

🔍 Observations & Suggestions

  1. Configuration Naming: The configuration option enforce_private_server_bundles might be more accurately named as enforce_secure_server_bundles to align with the security-focused intent and avoid confusion about "private" vs "public" in the Rails asset pipeline context.

  2. Path Validation: When enforce_private_server_bundles is enabled, the validation ensures bundles load from designated paths. Consider adding a warning log when this enforcement blocks a bundle load attempt to aid debugging.

  3. Documentation: The configuration guide properly documents the new options. Consider adding a migration guide section for users wanting to move from public to private server bundle locations.

  4. Performance: The file existence checks (File.exist?) are performed sequentially. While this is unlikely to be a bottleneck, the approach is reasonable for path resolution that happens during initialization.

  5. Error Messages: When path validation fails with enforce_private_server_bundles enabled, the error messages are clear and helpful.

🐛 Potential Issues

  1. Rails.root Fallback: The fallback to "." when Rails.root is nil (line 91, 114 in utils.rb) might cause unexpected behavior in non-Rails contexts. Consider a more explicit error or clearer default.

  2. Symlink Handling: File.expand_path will resolve symlinks, which is generally desirable for security, but worth noting in documentation if users rely on symlinked bundle directories.

🔒 Security Considerations

  1. Path Traversal: The use of File.expand_path properly prevents path traversal attacks.

  2. Default Security: The opt-in nature of the security features (defaults to nil/false) maintains compatibility but means users need to explicitly enable enhanced security. Consider documenting security best practices prominently.

  3. Public Path Fallback: Even with server_bundle_output_path configured, the code still tries public paths as fallbacks. This is good for migration but could be tightened with enforce_private_server_bundles.

✅ Overall Assessment

This is a well-implemented feature that addresses real architectural and security concerns. The code quality is good, tests are comprehensive, and backward compatibility is maintained. The changes follow the CLAUDE.md guidelines for the project.

Recommendation: Approve with minor suggestions for improvement mentioned above.

The PR successfully achieves its goals of improving bundle path resolution while maintaining backward compatibility and adding valuable security options for production deployments.

Copy link

claude bot commented Sep 23, 2025

Code Review for PR #1807: Improve Bundle Path Resolution

🎯 Overall Assessment

This PR significantly improves bundle path resolution and adds important security features for server-side rendering bundles. The implementation is thoughtful and well-structured with good backward compatibility.

✅ Code Quality and Best Practices

  • Excellent separation of concerns: The distinction between public client bundles and private server bundles is well-designed
  • Good configuration API: The new server_bundle_output_path and enforce_private_server_bundles options are intuitive
  • Backward compatibility: Defaults preserve existing behavior while enabling new security features
  • Clear method naming: Renaming to public_bundles_full_path eliminates confusion vs generic "generated assets"

🐛 Potential Issues

  1. Missing validation in server_bundle_js_file_path:

    • The method uses server_bundle_path_is_http? but doesn't validate if the HTTP URL is actually accessible
    • Consider adding a health check or validation when using HTTP paths
  2. Possible edge case in handle_missing_manifest_entry:

    • When all fallback locations are checked but none exist, the method returns File.expand_path(fallback_locations.first)
    • This could be nil if ReactOnRails::PackerUtils.using_packer? returns false and causes the array to be empty
    • Recommend adding a nil check or ensuring at least one fallback exists
  3. Generator template inconsistency:

    • The generator template sets server_bundle_output_path = "ssr-generated" and enforce_private_server_bundles = true
    • But the configuration defaults have server_bundle_output_path = nil and enforce_private_server_bundles = false
    • This could confuse users about the recommended settings

⚡ Performance Considerations

  • File system checks optimization: The handle_missing_manifest_entry method performs multiple File.exist? checks sequentially
    • Consider caching results or using a single directory scan if checking many bundles
  • Path expansion overhead: Multiple calls to File.expand_path in hot paths could be optimized by memoization

🔒 Security Analysis

  • Excellent security enhancement: The enforce_private_server_bundles option properly prevents server-side code exposure
  • Path traversal protection: The validation in validate_enforce_private_server_bundles correctly checks for public directory inclusion
  • Consider adding: Path sanitization for server_bundle_output_path to prevent directory traversal attacks (e.g., paths containing ../)

🧪 Test Coverage

  • Comprehensive configuration tests: Good coverage of the new validation logic
  • Bundle path resolution tests: Well-tested fallback scenarios
  • Missing test cases:
    • No tests for HTTP server bundle paths
    • No tests for the edge case when all fallback locations fail
    • No integration tests showing the full flow with actual file system

💡 Suggestions for Improvement

  1. Add path sanitization:

    def validate_enforce_private_server_bundles
      # Add this check
      if server_bundle_output_path&.include?("..") 
        raise ReactOnRails::Error, "server_bundle_output_path cannot contain '..' for security"
      end
      # ... rest of validation
    end
  2. Improve error messages: When enforce_private_server_bundles validation fails, include the resolved path in the error message for easier debugging

  3. Documentation enhancement: The configuration guide is excellent, but consider adding a migration guide for users moving from public to private server bundles

  4. Consider adding logging: When falling back through multiple bundle locations, debug logging would help troubleshoot deployment issues

📝 Minor Issues

  • Typo in spec: Line 97 in configuration_spec.rb - "doesn't fail" could be "does not fail" for consistency
  • CHANGELOG formatting: The version header [16.0.1-rc.2] should probably be [16.0.1] - rc.2 or similar to match semantic versioning

✨ Positive Highlights

  • Excellent documentation in configuration.md with clear examples
  • Thoughtful deprecation of generated_assets_full_path with backward compatibility
  • Good use of private class methods for internal logic
  • Well-structured specs with clear contexts

Great work on this PR! The security improvements and clearer API will benefit many users. The suggested improvements are mostly edge cases and optimizations rather than critical issues.

Copy link

claude bot commented Sep 23, 2025

Code Review for PR #1807: Bundle Path Resolution Improvements

Thank you for this comprehensive enhancement to bundle path resolution and server bundle security! I've reviewed the changes and have the following feedback:

✅ Strengths

1. Excellent Security Enhancement

  • The introduction of server_bundle_output_path and enforce_private_server_bundles provides a robust solution for isolating server-side code from public directories
  • Path validation at configuration time prevents security misconfigurations early
  • Clear separation between client and server bundles reduces the risk of accidentally exposing server-side logic

2. Thoughtful Backward Compatibility

  • All new features default to backward-compatible values
  • Existing applications continue working without any changes
  • The deprecation of generated_assets_full_path is handled gracefully with an alias

3. Comprehensive Test Coverage

  • Excellent test coverage for configuration validation
  • Thorough testing of fallback path resolution logic
  • Edge cases like missing manifest entries and non-existent files are well covered

4. Clear Documentation

  • The configuration guide additions are detailed and helpful
  • Inline documentation explains the security implications clearly
  • Examples show recommended directory structures

🔍 Observations & Suggestions

1. Performance Consideration

The fallback logic in handle_missing_manifest_entry performs multiple file existence checks. Consider caching these results in production environments to avoid repeated filesystem checks.

2. Path Traversal Security

While the path validation uses File.expand_path and start_with? checks, consider adding additional validation to ensure no path traversal attempts with '..' sequences.

3. Linting Compliance

Please run 'bundle exec rubocop' to ensure all Ruby code follows the project's style guidelines. The CLAUDE.md specifically requires this before every commit.

4. Minor Enhancement Opportunity

The error message for enforce_private_server_bundles validation could include an example of a correct configuration, e.g., suggesting 'ssr-generated' as a valid path.

✅ Security Review

The security implications have been well considered:

  • Server bundles can be properly isolated from public access
  • Configuration validation prevents common misconfigurations
  • New applications get secure defaults automatically
  • The implementation doesn't introduce new attack vectors

📋 Test Coverage Assessment

The test coverage is comprehensive:

  • ✅ Configuration validation scenarios
  • ✅ Bundle path resolution with various configurations
  • ✅ Fallback logic with missing manifests
  • ✅ Server vs client bundle differentiation
  • ✅ File existence checking

🎯 Overall Assessment

This is a well-designed feature that significantly improves both the reliability and security of bundle handling in React on Rails. The implementation demonstrates careful consideration of backward compatibility while providing clear benefits for new and existing applications.

Recommendation: Approve with minor suggestions (especially the rubocop compliance check)

Great work on this important enhancement! The careful balance between security improvements and backward compatibility is particularly well done. 🚀

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.

2 participants