react_on_rails_pro 16.7.0.rc.3 → 17.0.0.rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a4266ac7cb6a9cdbb62755a17d95282222b6909e64d41ff7b7a7bcf897c2f0a
4
- data.tar.gz: 8372bde1ffd861b8f8026e6fa28f4a9f65130ec6c70a42cdfa09a33f6a449c5a
3
+ metadata.gz: 318109f164359903fe25daa4f3b63cbd4c6814e0f2cc34d4c91c21bb19ec9590
4
+ data.tar.gz: b45033f1471e37ee756825b4aa00238229416011efa4054151a58bf942ca9a03
5
5
  SHA512:
6
- metadata.gz: 40ebe444b6abb44184984519f44a61df910f69e9e0fa73841ae552de49ba799ed872e8fc3bd8d8b0af8ca27c5c88946fa8abc28278fe42ac0603ac3d5de8cfde
7
- data.tar.gz: fd3568d5daff73971afddf96575237d2e07a738c972c1f0067e6ac8dc32ed4923c3dbdcf41fbe8dc06cb9d446c48b8544fb30e7ede7e8a57f9606fa2ec25dcc7
6
+ metadata.gz: 900bc242f0c60afaa7201215e2d9306f1f41d5a5c2f22fa11c8bbcd0e49c3ed54bdfaa59568ddefea8736e3d2719e42a1bff591e9eb11084bb2b566e72013a15
7
+ data.tar.gz: e8055b9d34b0255730974669b6b1926924e34e14029d478bb62cc49830bdd0e9bfd261670b43f488bb7781a5c7c137a44208d0a20a81f1c7c97bbeef82662749
@@ -8,7 +8,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
8
8
 
9
9
  gem "react_on_rails", path: "../"
10
10
 
11
- gem "shakapacker", "9.6.1"
11
+ gem "shakapacker", "10.1.0"
12
12
  gem "bootsnap", require: false
13
13
  gem "rails", "~> 7.1"
14
14
  gem "puma", "~> 6"
data/Gemfile.lock CHANGED
@@ -9,7 +9,7 @@ GIT
9
9
  PATH
10
10
  remote: ..
11
11
  specs:
12
- react_on_rails (16.7.0.rc.3)
12
+ react_on_rails (17.0.0.rc.0)
13
13
  addressable
14
14
  connection_pool
15
15
  execjs (~> 2.5)
@@ -20,7 +20,7 @@ PATH
20
20
  PATH
21
21
  remote: .
22
22
  specs:
23
- react_on_rails_pro (16.7.0.rc.3)
23
+ react_on_rails_pro (17.0.0.rc.0)
24
24
  addressable
25
25
  async (>= 2.29)
26
26
  async-http (~> 0.95)
@@ -28,7 +28,7 @@ PATH
28
28
  io-endpoint (~> 0.17.0)
29
29
  jwt (>= 2.5, < 4)
30
30
  rainbow
31
- react_on_rails (= 16.7.0.rc.3)
31
+ react_on_rails (= 17.0.0.rc.0)
32
32
 
33
33
  GEM
34
34
  remote: https://rubygems.org/
@@ -296,7 +296,7 @@ GEM
296
296
  nio4r (~> 2.0)
297
297
  racc (1.8.1)
298
298
  rack (3.2.5)
299
- rack-proxy (0.7.7)
299
+ rack-proxy (0.8.2)
300
300
  rack
301
301
  rack-session (2.1.1)
302
302
  base64 (>= 0.1.0)
@@ -425,7 +425,7 @@ GEM
425
425
  rubyzip (>= 1.2.2, < 3.0)
426
426
  websocket (~> 1.0)
427
427
  semantic_range (3.1.1)
428
- shakapacker (9.6.1)
428
+ shakapacker (10.1.0)
429
429
  activesupport (>= 5.2)
430
430
  package_json
431
431
  rack-proxy (>= 0.6.1)
@@ -539,7 +539,7 @@ DEPENDENCIES
539
539
  sass-rails
540
540
  scss_lint
541
541
  selenium-webdriver (= 4.9.0)
542
- shakapacker (= 9.6.1)
542
+ shakapacker (= 10.1.0)
543
543
  simplecov (~> 0.16.1)
544
544
  spring
545
545
  spring-watcher-listen
@@ -13,9 +13,11 @@ module ReactOnRailsPro
13
13
  # ReactOnRailsPro::RollingDeployAdapters::Http adapter on the next
14
14
  # deploy's build CI consumes both endpoints.
15
15
  #
16
- # Auto-mounted by the engine when `config.rolling_deploy_adapter` is the
17
- # Http adapter (or a subclass). Users who need a custom mount path or want
18
- # to layer their own auth middleware can mount manually:
16
+ # Mount this in your application's routes with `draw_routes` so the Http
17
+ # adapter on the next deploy can reach it. (Engine auto-mount keyed on
18
+ # `config.rolling_deploy_adapter` is planned for a follow-up but is not
19
+ # wired yet, so an explicit mount is currently required.) You can also use
20
+ # a custom mount path or layer your own auth middleware:
19
21
  #
20
22
  # # config/routes.rb
21
23
  # ReactOnRailsPro::RollingDeploy::BundlesController.draw_routes(
@@ -23,10 +25,10 @@ module ReactOnRailsPro
23
25
  # path: "/internal/rolling-deploy"
24
26
  # )
25
27
  #
26
- # Callers that need to mount the controller more than once (for example,
27
- # the engine auto-mount plus a user-controlled secondary path) must pass
28
- # a distinct `as_prefix:` per call so Rails' named-route registry
29
- # doesn't raise `ArgumentError: Invalid route name, already in use`.
28
+ # Callers that mount the controller more than once (for example, a future
29
+ # engine auto-mount plus a user-controlled secondary path) must pass a
30
+ # distinct `as_prefix:` per call so Rails' named-route registry doesn't
31
+ # raise `ArgumentError: Invalid route name, already in use`.
30
32
  #
31
33
  # Security:
32
34
  # * Bearer-token auth via `Authorization: Bearer <token>`, constant-time
@@ -56,9 +58,8 @@ module ReactOnRailsPro
56
58
  DEFAULT_ROUTE_PREFIX = "react_on_rails_pro_rolling_deploy"
57
59
 
58
60
  class << self
59
- # Helper for users who want to mount manually under a custom path. The
60
- # auto-mount path uses these same route definitions via the engine
61
- # initializer (see ReactOnRailsPro::Engine).
61
+ # Helper for mounting the controller in your application's routes. A
62
+ # planned engine auto-mount will reuse these same route definitions.
62
63
  #
63
64
  # `as_prefix:` controls the generated named-route helpers
64
65
  # (`<prefix>_manifest`, `<prefix>_bundle`). Callers that mount the
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "set"
4
+
3
5
  module ReactOnRailsPro
4
6
  def self.configure
5
7
  yield(configuration)
@@ -298,10 +300,13 @@ module ReactOnRailsPro
298
300
 
299
301
  def validate_url
300
302
  URI(renderer_url)
301
- rescue URI::InvalidURIError => e
302
- message = "Unparseable ReactOnRailsPro.config.renderer_url #{renderer_url} provided.\n" \
303
- "#{e.message}"
304
- raise ReactOnRailsPro::Error, message
303
+ rescue URI::InvalidURIError
304
+ # Deliberately do NOT echo renderer_url or the URI error message: a
305
+ # malformed renderer_url may embed credentials (https://:password@host),
306
+ # and reproducing it here would leak the password into logs/error reporters.
307
+ raise ReactOnRailsPro::Error,
308
+ "ReactOnRailsPro.config.renderer_url is not a parseable URI. Verify the value " \
309
+ "(it is not echoed here because it may contain credentials)."
305
310
  end
306
311
 
307
312
  def validate_remote_bundle_cache_adapter
@@ -362,7 +367,7 @@ module ReactOnRailsPro
362
367
  "config.rolling_deploy_token is required when using the built-in " \
363
368
  "ReactOnRailsPro::RollingDeployAdapters::Http adapter. Generate one " \
364
369
  "with SecureRandom.hex(32) and set it on both Rails and your build CI. " \
365
- "See docs/pro/rolling-deploy-http-adapter.md."
370
+ "See docs/pro/rolling-deploy-adapters.md."
366
371
  end
367
372
  # Compare on `bytesize` (not `length`) so the validator matches the
368
373
  # byte-level constant-time check in `BundlesController#authenticate_rolling_deploy_request`.
@@ -376,7 +381,7 @@ module ReactOnRailsPro
376
381
  "config.rolling_deploy_token must be at least " \
377
382
  "#{ROLLING_DEPLOY_TOKEN_MIN_LENGTH} bytes (got #{token.bytesize}). " \
378
383
  "Generate a stronger token with SecureRandom.hex(32). " \
379
- "See docs/pro/rolling-deploy-http-adapter.md."
384
+ "See docs/pro/rolling-deploy-adapters.md."
380
385
  end
381
386
 
382
387
  def validate_rolling_deploy_upload_signature
@@ -450,67 +455,108 @@ module ReactOnRailsPro
450
455
  end
451
456
 
452
457
  def setup_renderer_password
458
+ resolve_renderer_password
459
+ # The password is sent to the Node Renderer in the request body, never via
460
+ # the URL (the HTTP client connects to scheme://host:port and the renderer
461
+ # authenticates on the body field). A password embedded in renderer_url is
462
+ # purely a Rails-side config convenience — once resolved above, strip it
463
+ # from the stored URL so the credential can never leak through any log line
464
+ # or error message that interpolates renderer_url downstream.
465
+ strip_renderer_url_userinfo
466
+ end
467
+
468
+ def resolve_renderer_password
453
469
  # Explicit passwords, including values loaded from ENV in the initializer, skip URL extraction.
454
470
  # Blank values (nil or "") fall through so URL extraction and ENV fallback still apply.
455
471
  return if renderer_password.present?
456
472
 
457
- uri = URI(renderer_url)
458
- self.renderer_password = uri.password
473
+ self.renderer_password = URI(renderer_url).password
459
474
 
460
475
  # Mirror Node-side defaults: if Rails config and URL are both missing a password,
461
476
  # use RENDERER_PASSWORD from env.
462
477
  self.renderer_password = ENV.fetch("RENDERER_PASSWORD", nil) if renderer_password.blank?
463
478
  end
464
479
 
480
+ def strip_renderer_url_userinfo
481
+ return if renderer_url.blank?
482
+
483
+ uri = URI(renderer_url)
484
+ return if uri.userinfo.nil?
485
+
486
+ # Order matters: URI rejects a password without a user, so clear password first.
487
+ uri.password = nil
488
+ uri.user = nil
489
+ self.renderer_url = uri.to_s
490
+ end
491
+
492
+ KNOWN_WEAK_RENDERER_PASSWORDS = %w[
493
+ devPassword myPassword1 password changeme admin secret test renderer
494
+ ].to_set(&:downcase).freeze
495
+
496
+ MIN_RENDERER_PASSWORD_LENGTH = 16
497
+
465
498
  def validate_renderer_password_for_production
466
- # Defense-in-depth: skip validation when a password is already configured (e.g. extracted
467
- # from the renderer URL by setup_renderer_password, or set directly in the initializer).
468
- return if renderer_password.present?
469
499
  return unless node_renderer?
470
500
 
471
- # Fail closed: only skip validation when every present runtime env is explicitly
472
- # development or test. This mirrors the Node-side runtimeEnvsAllowDevelopmentDefaults()
473
- # which checks both NODE_ENV and RAILS_ENV. Checking NODE_ENV here surfaces
474
- # misconfigurations (e.g. NODE_ENV=production + RAILS_ENV=development) at Rails boot
475
- # time rather than waiting for the Node renderer to reject the request.
476
501
  runtime_envs = [ENV.fetch("RAILS_ENV", nil), ENV.fetch("NODE_ENV", nil)].compact_blank.map(&:downcase)
477
502
  allowed_envs = %w[development test].freeze
478
- return if runtime_envs.any? && runtime_envs.all? { |e| allowed_envs.include?(e) }
503
+ is_production_like = !(runtime_envs.any? && runtime_envs.all? { |e| allowed_envs.include?(e) })
479
504
 
480
- raise ReactOnRailsPro::Error, <<~MSG
481
- RENDERER_PASSWORD must be set in production-like environments (staging, production, etc.)
482
- when using the NodeRenderer.
505
+ if renderer_password.blank?
506
+ return unless is_production_like
483
507
 
484
- In development and test environments, the renderer password is optional and no authentication
485
- is required. In all other environments, you must explicitly configure a password to secure
486
- communication between Rails and the Node Renderer.
508
+ raise ReactOnRailsPro::Error, <<~MSG
509
+ RENDERER_PASSWORD must be set in production-like environments (staging, production, etc.)
510
+ when using the NodeRenderer.
487
511
 
488
- To fix this, set the RENDERER_PASSWORD environment variable:
512
+ In development and test environments, the renderer password is optional and no authentication
513
+ is required. In all other environments, you must explicitly configure a password to secure
514
+ communication between Rails and the Node Renderer.
489
515
 
490
- export RENDERER_PASSWORD="your-secure-password"
516
+ To fix this, set the RENDERER_PASSWORD environment variable:
491
517
 
492
- Rails reads it automatically. If you prefer to make it explicit in your initializer:
518
+ export RENDERER_PASSWORD="your-secure-password"
493
519
 
494
- # config/initializers/react_on_rails_pro.rb
495
- ReactOnRailsPro.configure do |config|
496
- config.renderer_password = ENV.fetch("RENDERER_PASSWORD")
497
- end
520
+ Rails reads it automatically. If you prefer to make it explicit in your initializer:
498
521
 
499
- Set the same password for the Node Renderer via the RENDERER_PASSWORD environment variable.
500
- Rails resolves the password in this order:
501
- 1) config.renderer_password (blank values fall through to the next step)
502
- 2) Password embedded in config.renderer_url (for example, https://:password@host:3800)
503
- 3) ENV["RENDERER_PASSWORD"]
522
+ # config/initializers/react_on_rails_pro.rb
523
+ ReactOnRailsPro.configure do |config|
524
+ config.renderer_password = ENV.fetch("RENDERER_PASSWORD")
525
+ end
504
526
 
505
- If Rails and the Node Renderer disagree about startup behavior, verify both RAILS_ENV and NODE_ENV.
527
+ Set the same password for the Node Renderer via the RENDERER_PASSWORD environment variable.
528
+ Rails resolves the password in this order:
529
+ 1) config.renderer_password (blank values fall through to the next step)
530
+ 2) Password embedded in config.renderer_url (for example, https://:password@host:3800)
531
+ 3) ENV["RENDERER_PASSWORD"]
506
532
 
507
- Environment matrix (both RAILS_ENV and NODE_ENV are checked):
508
- development/test — password optional when every set env is development or test
509
- (both unset) — treated as production-like; RENDERER_PASSWORD required
510
- staging RENDERER_PASSWORD required
511
- production — RENDERER_PASSWORD required
512
- (mixed envs) — RENDERER_PASSWORD required (e.g. NODE_ENV=production + RAILS_ENV=development)
513
- MSG
533
+ If Rails and the Node Renderer disagree about startup behavior, verify both RAILS_ENV and NODE_ENV.
534
+
535
+ Environment matrix (both RAILS_ENV and NODE_ENV are checked):
536
+ development/test password optional when every set env is development or test
537
+ (both unset) treated as production-like; RENDERER_PASSWORD required
538
+ staging — RENDERER_PASSWORD required
539
+ production — RENDERER_PASSWORD required
540
+ (mixed envs) — RENDERER_PASSWORD required (e.g. NODE_ENV=production + RAILS_ENV=development)
541
+ MSG
542
+ end
543
+
544
+ warn_if_renderer_password_weak
545
+ end
546
+
547
+ def warn_if_renderer_password_weak
548
+ if KNOWN_WEAK_RENDERER_PASSWORDS.include?(renderer_password.downcase)
549
+ # Don't log the literal value — even a known-default value is the
550
+ # user's *current* live credential until they rotate it.
551
+ Rails.logger.warn "[react_on_rails_pro] renderer_password matches a known-default value. " \
552
+ "Set RENDERER_PASSWORD to a random value of at least " \
553
+ "#{MIN_RENDERER_PASSWORD_LENGTH} characters."
554
+ elsif renderer_password.length < MIN_RENDERER_PASSWORD_LENGTH
555
+ Rails.logger.warn "[react_on_rails_pro] renderer_password is shorter than " \
556
+ "#{MIN_RENDERER_PASSWORD_LENGTH} characters " \
557
+ "(current length: #{renderer_password.length}). " \
558
+ "Consider using a stronger password."
559
+ end
514
560
  end
515
561
  end
516
562
  end
@@ -21,7 +21,7 @@ module ReactOnRailsPro
21
21
  # The currently-deployed Rails server already has the bundles + companion
22
22
  # assets sitting on disk; this adapter pulls them via authenticated HTTP.
23
23
  #
24
- # Configuration (see docs/pro/rolling-deploy-http-adapter.md):
24
+ # Configuration (see docs/pro/rolling-deploy-adapters.md):
25
25
  #
26
26
  # ReactOnRailsPro.configure do |config|
27
27
  # config.rolling_deploy_adapter = ReactOnRailsPro::RollingDeployAdapters::Http
@@ -113,7 +113,7 @@ module ReactOnRailsPro
113
113
  # Intentional no-op. The running Rails server IS the artifact store —
114
114
  # bundle + companion assets are already on local disk where the
115
115
  # mountable BundlesController will serve them on the next deploy's
116
- # build CI. Documented in docs/pro/rolling-deploy-http-adapter.md.
116
+ # build CI. Documented in docs/pro/rolling-deploy-adapters.md.
117
117
  def upload(_bundle_hash, bundle:, assets:)
118
118
  # See class doc above.
119
119
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ReactOnRailsPro
4
- VERSION = "16.7.0.rc.3"
4
+ VERSION = "17.0.0.rc.0"
5
5
  PROTOCOL_VERSION = "2.0.0"
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: react_on_rails_pro
3
3
  version: !ruby/object:Gem::Version
4
- version: 16.7.0.rc.3
4
+ version: 17.0.0.rc.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Gordon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-27 00:00:00.000000000 Z
11
+ date: 2026-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -120,14 +120,14 @@ dependencies:
120
120
  requirements:
121
121
  - - '='
122
122
  - !ruby/object:Gem::Version
123
- version: 16.7.0.rc.3
123
+ version: 17.0.0.rc.0
124
124
  type: :runtime
125
125
  prerelease: false
126
126
  version_requirements: !ruby/object:Gem::Requirement
127
127
  requirements:
128
128
  - - '='
129
129
  - !ruby/object:Gem::Version
130
- version: 16.7.0.rc.3
130
+ version: 17.0.0.rc.0
131
131
  - !ruby/object:Gem::Dependency
132
132
  name: bundler
133
133
  requirement: !ruby/object:Gem::Requirement