Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4ee772e
Fix server bundle path resolution in test environments (#1797)
justin808 Sep 21, 2025
9e1465a
Improve bundle path resolution with secure server bundle locations
justin808 Sep 21, 2025
7aff6da
Apply suggestions from code review
justin808 Sep 21, 2025
b0c4963
Update Gemfile.lock and CHANGELOG.md
justin808 Sep 21, 2025
61a1254
Add configuration options for secure server bundle management
justin808 Sep 21, 2025
d974d82
Fix breaking change: Set server_bundle_output_path default to nil
justin808 Sep 22, 2025
60b50d8
Fix CI failures by restoring packer availability check
justin808 Sep 22, 2025
6a2035f
Clean solution: Remove redundant using_packer? check with proper test…
justin808 Sep 22, 2025
afd06ed
Implement enforce_secure_server_bundles with comprehensive improvements
justin808 Sep 22, 2025
ed37a2b
refactor: Use configuration-based approach for server bundle output path
github-actions[bot] Sep 22, 2025
fa07c57
refactor: Use configuration-based approach for server bundle output path
github-actions[bot] Sep 22, 2025
55a30c0
Fix CI failures: RuboCop violations and test failures
github-actions[bot] Sep 22, 2025
8936a77
Fix dummy app configuration for test environment
github-actions[bot] Sep 22, 2025
88212b8
refactor: Return configured server_bundle_output_path directly withou…
github-actions[bot] Sep 22, 2025
d917bda
Fix CI failures: RuboCop violations and test failures
github-actions[bot] Sep 22, 2025
b478d61
refactor: Rename 'secure' to 'private' for server bundles terminology
github-actions[bot] Sep 22, 2025
3cb7cdc
fix: Remove obsolete using_packer? method references
github-actions[bot] Sep 23, 2025
60d498d
update changelog.md
AbanoubGhadban Sep 23, 2025
3d5c7ea
Clarify method naming: rename generated_assets_full_path β†’ public_ass…
justin808 Sep 23, 2025
4252368
Improve method naming: use public_bundles_full_path instead of public…
justin808 Sep 23, 2025
6c38c6f
Refactor: Update test mocks to use public_bundles_full_path
AbanoubGhadban Sep 23, 2025
a9aff90
Enhance configuration documentation and clarify directory structure f…
AbanoubGhadban Sep 23, 2025
92b58ea
Update changelog and configuration to reflect breaking changes and en…
AbanoubGhadban Sep 23, 2025
ffa58ac
Don't fall back to public directory if enforce_private_server_bundles…
AbanoubGhadban Sep 23, 2025
369f5a7
Enhance server bundle path resolution logic
AbanoubGhadban Sep 23, 2025
b36413d
Update server bundle path resolution to utilize public output path
AbanoubGhadban Sep 23, 2025
a0e9656
Fix remaining test failures: update all packer method references
justin808 Sep 23, 2025
1c9e45e
Fix CI test failures: update test mocks for enforce_private_server_bu…
justin808 Sep 23, 2025
57a3ab9
Correct test expectations for new fallback behavior
justin808 Sep 23, 2025
85100ab
Enhancement: Add documentation for shakapacker.yml integration
justin808 Sep 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,8 @@ yalc.lock
# Generated by ROR FS-based Registry
generated

# Server-side rendering generated bundles
ssr-generated

# Claude Code local settings
.claude/settings.local.json
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,45 @@ After a release, please make sure to run `bundle exec rake update_changelog`. Th

Changes since the last non-beta release.

#### Breaking Changes

- **Removed `generated_assets_dirs` configuration**: The legacy `config.generated_assets_dirs` option is no longer supported and will raise an error if used. Since Shakapacker is now required, asset paths are automatically determined from `shakapacker.yml` configuration. Remove any `config.generated_assets_dirs` from your `config/initializers/react_on_rails.rb` file. Use `public_output_path` in `config/shakapacker.yml` to customize asset output location instead. [PR 1798](https://github.com/shakacode/react_on_rails/pull/1798)

#### New Features

- **Server Bundle Security**: Added new configuration options for enhanced server bundle security and organization:

- `server_bundle_output_path`: Configurable directory (relative to the Rails root) for server bundle output (default: "ssr-generated"). If set to `nil`, the server bundle will be loaded from the same public directory as client bundles.
- `enforce_private_server_bundles`: When enabled, ensures server bundles are only loaded from private directories outside the public folder (default: false for backward compatibility)

- **Improved Bundle Path Resolution**: Bundle path resolution for server bundles now works as follows:
- If `server_bundle_output_path` is set, the server bundle is loaded from that directory.
- If `server_bundle_output_path` is not set, the server bundle falls back to the client bundle directory (typically the public output path).
- If `enforce_private_server_bundles` is enabled:
- The server bundle will only be loaded from the private directory specified by `server_bundle_output_path`.
- If the bundle is not found there, it will _not_ fall back to the public directory.
- If `enforce_private_server_bundles` is not enabled and the bundle is not found in the private directory, it will fall back to the public directory.
- This logic ensures that, when strict enforcement is enabled, server bundles are never loaded from public directories, improving security and clarity of bundle resolution.

#### API Improvements

- **Method Naming Clarification**: Added `public_bundles_full_path` method to clarify bundle path handling:
- `public_bundles_full_path`: New method specifically for webpack bundles in public directories
- `generated_assets_full_path`: Now deprecated (backwards-compatible alias)
- This eliminates confusion between webpack bundles and general Rails public assets

#### Security Enhancements

- **Private Server Bundle Enforcement**: When `enforce_private_server_bundles` is enabled, server bundles bypass public directory fallbacks and are only loaded from designated private locations
- **Path Validation**: Added validation to ensure `server_bundle_output_path` points to private directories when enforcement is enabled

#### Bug Fixes

- **Non-Packer Environment Compatibility**: Fixed potential NoMethodError when using bundle path resolution in environments without Shakapacker
- **Shakapacker version requirements**: Fixed inconsistent version requirements between basic pack generation (6.5.1+) and advanced auto-bundling features (7.0.0+). Added backward compatibility for users on Shakapacker 6.5.1-6.9.x while providing clear upgrade guidance for advanced features. Added new constants `MINIMUM_SHAKAPACKER_VERSION_FOR_AUTO_BUNDLING` and improved version checking performance with caching. [PR 1798](https://github.com/shakacode/react_on_rails/pull/1798)

### [16.0.1-rc.2] - 2025-09-20

#### Bug Fixes

- **Packs generator**: Fixed error when `server_bundle_js_file` configuration is empty (default). Added safety check to prevent attempting operations on invalid file paths when server-side rendering is not configured. [PR 1802](https://github.com/shakacode/react_on_rails/pull/1802)
Expand Down
45 changes: 45 additions & 0 deletions docs/guides/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,51 @@ ReactOnRails.configure do |config|
# This manifest file is automatically generated by the React Server Components Webpack plugin. Only set this if you've configured the plugin to use a different filename.
config.react_server_client_manifest_file = "react-server-client-manifest.json"

################################################################################
# SERVER BUNDLE SECURITY AND ORGANIZATION
################################################################################

# This configures the directory (relative to the Rails root) where the server bundle will be output.
# By default, this is "ssr-generated". If set to nil, the server bundle will be loaded from the same
# public directory as client bundles. For enhanced security, use this option in conjunction with
# `enforce_private_server_bundles` to ensure server bundles are only loaded from private directories
# config.server_bundle_output_path = "ssr-generated"

# When set to true, React on Rails will only load server bundles from private, explicitly configured directories (such as `ssr-generated`), and will raise an error if a server bundle is found in a public or untrusted location. This helps prevent accidental or malicious execution of untrusted JavaScript on the server, and is strongly recommended for production environments. And prevent leakage of server-side code to the client (Especially in the case of RSC).
# Default is false for backward compatibility, but enabling this option is a best practice for security.
config.enforce_private_server_bundles = false

################################################################################
# BUNDLE ORGANIZATION EXAMPLES
################################################################################
#
# This configuration creates a clear separation between client and server assets:
#
# CLIENT BUNDLES (Public, Web-Accessible):
# Location: public/webpack/[environment]/ or public/packs/ (According to your shakapacker.yml configuration)
# Files: application.js, manifest.json, CSS files
# Served by: Web server directly
# Access: ReactOnRails::Utils.public_bundles_full_path
#
# SERVER BUNDLES (Private, Server-Only):
# Location: ssr-generated/ (when server_bundle_output_path configured)
# Files: server-bundle.js, rsc-bundle.js
# Served by: Never served to browsers
# Access: ReactOnRails::Utils.server_bundle_js_file_path
#
# 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
#
################################################################################

# `prerender` means server-side rendering
# default is false. This is an option for view helpers `render_component` and `render_component_hash`.
# Set to true to change the default value to true.
Expand Down
17 changes: 10 additions & 7 deletions lib/generators/react_on_rails/base_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def add_hello_world_route
end

def create_react_directories
# Create auto-registration directory structure for non-Redux components only
# Create auto-bundling directory structure for non-Redux components only
# Redux components handle their own directory structure
return if options.redux?

Expand Down Expand Up @@ -136,14 +136,17 @@ def update_gitignore_for_auto_registration
return unless File.exist?(gitignore_path)

gitignore_content = File.read(gitignore_path)
return if gitignore_content.include?("**/generated/**")

append_to_file ".gitignore" do
<<~GITIGNORE
additions = []
additions << "**/generated/**" unless gitignore_content.include?("**/generated/**")
additions << "ssr-generated" unless gitignore_content.include?("ssr-generated")

return if additions.empty?

# Generated React on Rails packs
**/generated/**
GITIGNORE
append_to_file ".gitignore" do
lines = ["\n# Generated React on Rails packs"]
lines.concat(additions)
"#{lines.join("\n")}\n"
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/generators/react_on_rails/react_with_redux_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ReactWithReduxGenerator < Rails::Generators::Base
aliases: "-T"

def create_redux_directories
# Create auto-registration directory structure for Redux
# Create auto-bundling directory structure for Redux
empty_directory("app/javascript/src/HelloWorldApp/ror_components")

# Create Redux support directories within the component directory
Expand All @@ -31,7 +31,7 @@ def copy_base_files
base_js_path = "redux/base"
ext = component_extension(options)

# Copy Redux-connected component to auto-registration structure
# Copy Redux-connected component to auto-bundling structure
copy_file("#{base_js_path}/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.client.#{ext}",
"app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.client.#{ext}")
copy_file("#{base_js_path}/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.server.#{ext}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ ReactOnRails.configure do |config|
#
config.server_bundle_js_file = "server-bundle.js"

# The location of the server bundle file defaults to the location of your client bundle file.
# However, you can change this location in your config/shakapacker.yml file, using Shakapacker v8.5.0 or greater
# If you are not using Shakapacker v8.5.0 or greater, then you can set the value here. v17 will probably remove configuration
# of the server_bundle_output_path.

# Configure where server bundles are output. Defaults to "ssr-generated".
# This should match your webpack configuration for server bundles.
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.
config.enforce_private_server_bundles = true

################################################################################
################################################################################
# FILE SYSTEM BASED COMPONENT REGISTRY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@ const configureServer = () => {

// Custom output for the server-bundle that matches the config in
// config/initializers/react_on_rails.rb
// Server bundles are output to a private directory (not public) for security
serverWebpackConfig.output = {
filename: 'server-bundle.js',
globalObject: 'this',
// If using the React on Rails Pro node server renderer, uncomment the next line
// libraryTarget: 'commonjs2',
path: config.outputPath,
publicPath: config.publicPath,
path: require('path').resolve(__dirname, '../../ssr-generated'),
// No publicPath needed since server bundles are not served via web
// https://webpack.js.org/configuration/output/#outputglobalobject
};

Expand Down
Loading
Loading