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 +4 -4
- data/Gemfile.development_dependencies +1 -1
- data/Gemfile.lock +6 -6
- data/app/controllers/react_on_rails_pro/rolling_deploy/bundles_controller.rb +11 -10
- data/lib/react_on_rails_pro/configuration.rb +89 -43
- data/lib/react_on_rails_pro/rolling_deploy_adapters/http.rb +2 -2
- data/lib/react_on_rails_pro/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 318109f164359903fe25daa4f3b63cbd4c6814e0f2cc34d4c91c21bb19ec9590
|
|
4
|
+
data.tar.gz: b45033f1471e37ee756825b4aa00238229416011efa4054151a58bf942ca9a03
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 900bc242f0c60afaa7201215e2d9306f1f41d5a5c2f22fa11c8bbcd0e49c3ed54bdfaa59568ddefea8736e3d2719e42a1bff591e9eb11084bb2b566e72013a15
|
|
7
|
+
data.tar.gz: e8055b9d34b0255730974669b6b1926924e34e14029d478bb62cc49830bdd0e9bfd261670b43f488bb7781a5c7c137a44208d0a20a81f1c7c97bbeef82662749
|
data/Gemfile.lock
CHANGED
|
@@ -9,7 +9,7 @@ GIT
|
|
|
9
9
|
PATH
|
|
10
10
|
remote: ..
|
|
11
11
|
specs:
|
|
12
|
-
react_on_rails (
|
|
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 (
|
|
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 (=
|
|
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.
|
|
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 (
|
|
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 (=
|
|
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
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
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
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
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
|
|
60
|
-
# auto-mount
|
|
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
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
503
|
+
is_production_like = !(runtime_envs.any? && runtime_envs.all? { |e| allowed_envs.include?(e) })
|
|
479
504
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
when using the NodeRenderer.
|
|
505
|
+
if renderer_password.blank?
|
|
506
|
+
return unless is_production_like
|
|
483
507
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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
|
-
|
|
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
|
-
|
|
516
|
+
To fix this, set the RENDERER_PASSWORD environment variable:
|
|
491
517
|
|
|
492
|
-
|
|
518
|
+
export RENDERER_PASSWORD="your-secure-password"
|
|
493
519
|
|
|
494
|
-
|
|
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
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
-
|
|
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
|
-
|
|
508
|
-
|
|
509
|
-
(both
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
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-
|
|
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-
|
|
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
|
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:
|
|
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-
|
|
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:
|
|
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:
|
|
130
|
+
version: 17.0.0.rc.0
|
|
131
131
|
- !ruby/object:Gem::Dependency
|
|
132
132
|
name: bundler
|
|
133
133
|
requirement: !ruby/object:Gem::Requirement
|