diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 8b7a1fad0..8cbeb0286 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -73,6 +73,7 @@ jobs: 'rails71_rack22', 'rails72_rack22', 'rails80_rack22', + 'rails81_rack22', ] jruby_version: [ '9.3.15.0', '9.4.15.0', '10.0.6.0', '10.1.1.0' ] java_version: [ '8', '11', '17', '21', '25' ] @@ -99,6 +100,10 @@ jobs: jruby_version: '9.3.15.0' - appraisal: 'rails80_rack22' # Requires Ruby 3.4 compatibility, which JRuby 9.4 does not support jruby_version: '9.4.15.0' + - appraisal: 'rails81_rack22' # Requires Ruby 3.4 compatibility, which JRuby 9.3 does not support + jruby_version: '9.3.15.0' + - appraisal: 'rails81_rack22' # Requires Ruby 3.4 compatibility, which JRuby 9.4 does not support + jruby_version: '9.4.15.0' fail-fast: false env: diff --git a/Appraisals b/Appraisals index 70781847c..da0fa58af 100644 --- a/Appraisals +++ b/Appraisals @@ -11,6 +11,7 @@ version_spec = ->(prefix, desc) { "~> #{major_minor.call(prefix, desc)}.0" } "rails71" => {racks: %w[rack22]}, "rails72" => {racks: %w[rack22]}, "rails80" => {racks: %w[rack22]}, + "rails81" => {racks: %w[rack22]} }.each do |rails_desc, c| c[:racks].each do |rack_desc| diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a9b46b42..ffccccdf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - fix: JRuby::Rack::Input#read(0) should return an empty string - fix: close the original body when ShowStatus replaces it - fix: detect Transfer-Encoding/Content-Length headers case-insensitively +- chore: revert `rack.version` value to be Rack 2.2 spec conformant +- fix: do not mutate (potentially frozen) response header values when writing ## 1.2.8 diff --git a/Gemfile.lock b/Gemfile.lock index e5c533f78..fde0e3c76 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -57,4 +57,4 @@ CHECKSUMS thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73 BUNDLED WITH - 2.7.2 + 2.6.3 diff --git a/README.md b/README.md index f0110749a..e3f43f12a 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ For more information on Rack, visit http://rack.github.io/. | JRuby-Rack Series | Status | Rack | JRuby | Java | Rails | Servlet API (min → mostly supported) | Notes | |----------------------------------------------------------------|---------------|-----------|------------|------|-----------|--------------------------------------|-------| -| [**1.2**](https://github.com/jruby/jruby-rack/tree/1.2-stable) | EOL @ 2026-09 | 2.2 | 9.3 → 10.1 | 8+ | 5.0 → 8.0 | 3.0 → 4.0 (Java EE 6 → 7) | | +| [**1.2**](https://github.com/jruby/jruby-rack/tree/1.2-stable) | EOL @ 2026-09 | 2.2 | 9.3 → 10.1 | 8+ | 5.0 → 8.1 | 3.0 → 4.0 (Java EE 6 → 7) | | | [**1.1**](https://github.com/jruby/jruby-rack/tree/1.1-stable) | EOL @ 2024-05 | 1.x → 2.2 | 1.6 → 9.4 | 6+ | 2.1 → 5.2 | 2.5 → 4.0 (Java EE 5 → 7) | | | [**1.0**](https://github.com/jruby/jruby-rack/tree/1.0.10) | EOL @ 2011-11 | 0.9 → 1.x | 1.1 → 1.9 | 5+ | 2.1 → 3.x | 2.5 (Java EE 5) | | diff --git a/gemfiles/rails50_rack22.gemfile b/gemfiles/rails50_rack22.gemfile index 5ebe0f45f..4a99d46e8 100644 --- a/gemfiles/rails50_rack22.gemfile +++ b/gemfiles/rails50_rack22.gemfile @@ -13,6 +13,7 @@ end group :development do gem "appraisal", require: nil + gem "rexml" end group :test do diff --git a/gemfiles/rails52_rack22.gemfile b/gemfiles/rails52_rack22.gemfile index 242654abf..8242bba4e 100644 --- a/gemfiles/rails52_rack22.gemfile +++ b/gemfiles/rails52_rack22.gemfile @@ -12,6 +12,7 @@ end group :development do gem "appraisal", require: nil + gem "rexml" end group :test do diff --git a/gemfiles/rails60_rack22.gemfile b/gemfiles/rails60_rack22.gemfile index b7d7b5c1d..e26305589 100644 --- a/gemfiles/rails60_rack22.gemfile +++ b/gemfiles/rails60_rack22.gemfile @@ -12,6 +12,7 @@ end group :development do gem "appraisal", require: nil + gem "rexml" end group :test do diff --git a/gemfiles/rails61_rack22.gemfile b/gemfiles/rails61_rack22.gemfile index 8009c3653..600306ab5 100644 --- a/gemfiles/rails61_rack22.gemfile +++ b/gemfiles/rails61_rack22.gemfile @@ -12,6 +12,7 @@ end group :development do gem "appraisal", require: nil + gem "rexml" end group :test do diff --git a/gemfiles/rails70_rack22.gemfile b/gemfiles/rails70_rack22.gemfile index b7b1c5dcd..27cf5e876 100644 --- a/gemfiles/rails70_rack22.gemfile +++ b/gemfiles/rails70_rack22.gemfile @@ -5,11 +5,11 @@ source "https://rubygems.org" group :default do gem "rack", "~> 2.2.0" gem "rails", "~> 7.0.0" - gem "rdoc", "!= 8.0.0" end group :development do gem "appraisal", require: nil + gem "rexml" end group :test do diff --git a/gemfiles/rails71_rack22.gemfile b/gemfiles/rails71_rack22.gemfile index d13b8451c..f9dce538e 100644 --- a/gemfiles/rails71_rack22.gemfile +++ b/gemfiles/rails71_rack22.gemfile @@ -5,11 +5,11 @@ source "https://rubygems.org" group :default do gem "rack", "~> 2.2.0" gem "rails", "~> 7.1.0" - gem "rdoc", "!= 8.0.0" end group :development do gem "appraisal", require: nil + gem "rexml" end group :test do diff --git a/gemfiles/rails72_rack22.gemfile b/gemfiles/rails72_rack22.gemfile index d70ca9e0d..5f93aaec8 100644 --- a/gemfiles/rails72_rack22.gemfile +++ b/gemfiles/rails72_rack22.gemfile @@ -9,6 +9,7 @@ end group :development do gem "appraisal", require: nil + gem "rexml" end group :test do diff --git a/gemfiles/rails80_rack22.gemfile b/gemfiles/rails80_rack22.gemfile index ce87d96c5..23603faa4 100644 --- a/gemfiles/rails80_rack22.gemfile +++ b/gemfiles/rails80_rack22.gemfile @@ -9,6 +9,7 @@ end group :development do gem "appraisal", require: nil + gem "rexml" end group :test do diff --git a/gemfiles/rails81_rack22.gemfile b/gemfiles/rails81_rack22.gemfile new file mode 100644 index 000000000..9549db803 --- /dev/null +++ b/gemfiles/rails81_rack22.gemfile @@ -0,0 +1,19 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +group :default do + gem "rack", "~> 2.2.0" + gem "rails", "~> 8.1.0" +end + +group :development do + gem "appraisal", require: nil + gem "rexml" +end + +group :test do + gem "rake", "~> 13.4", require: nil + gem "rspec" + gem "logger" +end diff --git a/src/main/java/org/jruby/rack/ext/Response.java b/src/main/java/org/jruby/rack/ext/Response.java index 8c9bee154..c7aaed8c1 100644 --- a/src/main/java/org/jruby/rack/ext/Response.java +++ b/src/main/java/org/jruby/rack/ext/Response.java @@ -356,8 +356,7 @@ protected void writeStatus(final RackResponseEnvironment response) { } @JRubyMethod(name = "write_headers") - public IRubyObject write_headers(final ThreadContext context, final IRubyObject response) - throws IOException { + public IRubyObject write_headers(final ThreadContext context, final IRubyObject response) { writeHeaders(response.toJava(RackResponseEnvironment.class)); return context.nil; } @@ -406,7 +405,7 @@ public IRubyObject yield(ThreadContext context, IRubyObject[] args) { @Override public IRubyObject yield(ThreadContext context, IRubyObject value) { - value.callMethod(context, "chomp!", newLine); + value = value.callMethod(context, "chomp", newLine); response.addHeader(name, value.toString()); return value; } @@ -439,10 +438,10 @@ public IRubyObject write_body(final ThreadContext context, final IRubyObject res } protected void writeBody(final RackResponseEnvironment response) throws IOException { + final ThreadContext context = currentContext(); Channel bodyChannel = null; IRubyObject body = this.body; try { if ( body.respondsTo("call") && ! body.respondsTo("each") ) { - final ThreadContext context = currentContext(); final IRubyObject outputStream = JavaUtil.convertJavaToRuby(context.runtime, response.getOutputStream()); this.body.callMethod(context, "call", outputStream); @@ -450,7 +449,6 @@ protected void writeBody(final RackResponseEnvironment response) throws IOExcept } if ( body.respondsTo("to_path") ) { // send_file - final ThreadContext context = currentContext(); final IRubyObject path = body.callMethod(context, "to_path"); callMethod("send_file", path, JavaUtil.convertJavaToRuby(context.runtime, response)); return; @@ -465,7 +463,6 @@ protected void writeBody(final RackResponseEnvironment response) throws IOExcept bodyChannel = ((RubyIO) body).getChannel(); } else { - final ThreadContext context = currentContext(); final IRubyObject channel = body.callMethod(context, "to_channel"); bodyChannel = channel.toJava(Channel.class); } @@ -480,8 +477,6 @@ protected void writeBody(final RackResponseEnvironment response) throws IOExcept // NOTE: we no longer handle "to_inputstream" since in 1.7 "to_channel" covers those ... final OutputStream output = response.getOutputStream(); - final ThreadContext context = currentContext(); - IOException error = null; if ( doDechunk() ) { final IRubyObject output_stream = JavaUtil.convertJavaToRuby(context.runtime, output); callMethod(context, "write_body_dechunked", output_stream); @@ -706,6 +701,7 @@ private void transferChannel(final ReadableByteChannel channel, final OutputStre ThreadContext currentContext() { return getRuntime().getCurrentContext(); } + @SuppressWarnings("UnusedReturnValue") static IRubyObject invoke( final ThreadContext context, final IRubyObject self, final String method, final BlockBody body) { diff --git a/src/main/java/org/jruby/rack/util/IOHelpers.java b/src/main/java/org/jruby/rack/util/IOHelpers.java index eec8f4d9d..2db8d1072 100644 --- a/src/main/java/org/jruby/rack/util/IOHelpers.java +++ b/src/main/java/org/jruby/rack/util/IOHelpers.java @@ -73,24 +73,6 @@ public static String inputStreamToString(final InputStream stream) return str.toString(); } - public static ByteList readURL(final Ruby runtime, final URL url) - throws IOException { - if ( url == null ) return null; - - final int chunk = 256; - - final InputStream stream = url.openStream(); - final ByteList bytes = new ByteList(chunk); - - try { - while ( true ) bytes.append(stream, chunk); - } - catch (EOFException e) { /* read whole stream */ } - finally { stream.close(); } - - return bytes; - } - public static String rubyMagicCommentValue(final String script, final String prefix) throws IOException { if ( script == null ) return null; @@ -100,7 +82,7 @@ public static String rubyMagicCommentValue(final String script, final String pre String line, comment = null; Pattern pattern = null; while ( (line = reader.readLine()) != null ) { // we only support (magic) comments at the beginning : - if ( line.length() == 0 || line.charAt(0) != '#' ) break; + if (line.isEmpty() || line.charAt(0) != '#' ) break; if (pattern == null) { pattern = Pattern.compile(prefix + "\\s*(\\S+)"); diff --git a/src/spec/ruby/jruby/rack/integration_spec.rb b/src/spec/ruby/jruby/rack/integration_spec.rb index c30982598..9f91fc939 100644 --- a/src/spec/ruby/jruby/rack/integration_spec.rb +++ b/src/spec/ruby/jruby/rack/integration_spec.rb @@ -225,6 +225,10 @@ it_should_behave_like 'a rails app' end + describe 'rails 8.1', lib: :rails81 do + it_should_behave_like 'a rails app' + end + def expect_to_have_monkey_patched_chunked @runtime.evalScriptlet "require 'rack/chunked'" script = %{ diff --git a/src/spec/ruby/jruby/rack/response_spec.rb b/src/spec/ruby/jruby/rack/response_spec.rb index eb44b8731..3310ac9f1 100644 --- a/src/spec/ruby/jruby/rack/response_spec.rb +++ b/src/spec/ruby/jruby/rack/response_spec.rb @@ -86,6 +86,13 @@ class << value response.write_headers(response_environment) end + it "writes frozen header values containing newlines without raising" do + response.to_java.getHeaders.update({ "Set-Cookie" => "cookie1\ncookie2".freeze }) + expect(servlet_response).to receive(:addHeader).with("Set-Cookie", "cookie1") + expect(servlet_response).to receive(:addHeader).with("Set-Cookie", "cookie2") + response.write_headers(response_environment) + end + it "adds an int header when values is a fixnum" do update_response_headers "Expires" => 0 expect(response_environment).to receive(:addIntHeader).with("Expires", 0) diff --git a/src/spec/ruby/rack/handler/servlet_lint_spec.rb b/src/spec/ruby/rack/handler/servlet_lint_spec.rb new file mode 100644 index 000000000..b3394f2ec --- /dev/null +++ b/src/spec/ruby/rack/handler/servlet_lint_spec.rb @@ -0,0 +1,75 @@ +#-- +# This source code is available under the MIT license. +# See the file LICENSE.txt for details. +#++ + +require File.expand_path('../../spec_helper', File.dirname(__FILE__)) + +require 'rack' +require 'rack/lint' +require 'uri' +require 'rack/handler/servlet' + +# End-to-end conformance check: the env built from the servlet request and the +# handling of the (Lint wrapped) response must satisfy the loaded Rack version's +# SPEC - Rack::Lint raises when either side of the contract is broken. +describe 'Rack::Handler::Servlet (Rack::Lint)' do + + before :each do + @servlet_context = mock_servlet_context + allow(@rack_context).to receive(:getServerInfo).and_return 'Mock-Container/1.0' + @servlet_request = org.springframework.mock.web.MockHttpServletRequest.new(@servlet_context) + @servlet_response = org.springframework.mock.web.MockHttpServletResponse.new + @servlet_env = org.jruby.rack.servlet.ServletRackEnvironment.new( + @servlet_request, @servlet_response, @rack_context + ) + + @servlet_request.setMethod('GET') + @servlet_request.setContextPath('') + @servlet_request.setRequestURI('/some/path') + @servlet_request.setQueryString('a=1&b=2') + @servlet_request.addParameter('a', '1') + @servlet_request.addParameter('b', '2') + @servlet_request.setServerName('lint.example.com') + @servlet_request.setServerPort(8080) + @servlet_request.setProtocol('HTTP/1.1') + @servlet_request.setRemoteAddr('127.0.0.1') + @servlet_request.setContent(''.to_java_bytes) + @servlet_request.addHeader('Accept', 'text/plain') + end + + let(:inner_app) do + lambda do |env| + env['rack.input'].read # exercises the Lint wrapped input contract + [ 200, { 'Content-Type' => 'text/plain', 'Content-Length' => '2' }, [ 'OK' ] ] + end + end + + let(:servlet) { Rack::Handler::Servlet.new Rack::Lint.new(inner_app) } + + shared_examples 'lint-compatible env' do + + it "creates a SPEC compatible env and writes the response" do + response = servlet.call(@servlet_env) + expect(response.to_java.getStatus).to eql 200 + + response_env = org.jruby.rack.servlet.ServletRackResponseEnvironment.new(@servlet_response) + response.to_java.respond(response_env) + expect(@servlet_response.getStatus).to eql 200 + expect(@servlet_response.getContentAsString).to eql 'OK' + end + + end + + describe 'with (default) env' do + it_behaves_like 'lint-compatible env' + end + + describe 'with servlet env' do + before { Rack::Handler::Servlet.env = :servlet } + after { Rack::Handler::Servlet.env = nil } + + it_behaves_like 'lint-compatible env' + end + +end diff --git a/src/spec/stub/rails81/app/controllers/application_controller.rb b/src/spec/stub/rails81/app/controllers/application_controller.rb new file mode 100644 index 000000000..0d95db22b --- /dev/null +++ b/src/spec/stub/rails81/app/controllers/application_controller.rb @@ -0,0 +1,4 @@ +class ApplicationController < ActionController::Base + # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. + allow_browser versions: :modern +end diff --git a/src/spec/stub/rails81/app/helpers/application_helper.rb b/src/spec/stub/rails81/app/helpers/application_helper.rb new file mode 100644 index 000000000..de6be7945 --- /dev/null +++ b/src/spec/stub/rails81/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/src/spec/stub/rails81/config/application.rb b/src/spec/stub/rails81/config/application.rb new file mode 100644 index 000000000..bba537d7f --- /dev/null +++ b/src/spec/stub/rails81/config/application.rb @@ -0,0 +1,42 @@ +require_relative "boot" + +require "rails" +# Pick the frameworks you want: +require "active_model/railtie" +# require "active_job/railtie" +# require "active_record/railtie" +# require "active_storage/engine" +require "action_controller/railtie" +# require "action_mailer/railtie" +# require "action_mailbox/engine" +# require "action_text/engine" +require "action_view/railtie" +# require "action_cable/engine" +# require "rails/test_unit/railtie" + +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) + +module Rails81 + class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 8.1 + + # Please, add to the `ignore` list any other `lib` subdirectories that do + # not contain `.rb` files, or that should not be reloaded or eager loaded. + # Common ones are `templates`, `generators`, or `middleware`, for example. + config.autoload_lib(ignore: %w[assets tasks]) + + # Configuration for the application, engines, and railties goes here. + # + # These settings can be overridden in specific environments using the files + # in config/environments, which are processed later. + # + # config.time_zone = "Central Time (US & Canada)" + # config.eager_load_paths << Rails.root.join("extras") + + # Don't generate system test files. + config.generators.system_tests = nil + end +end diff --git a/src/spec/stub/rails81/config/boot.rb b/src/spec/stub/rails81/config/boot.rb new file mode 100644 index 000000000..282011619 --- /dev/null +++ b/src/spec/stub/rails81/config/boot.rb @@ -0,0 +1,3 @@ +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "bundler/setup" # Set up gems listed in the Gemfile. diff --git a/src/spec/stub/rails81/config/credentials.yml.enc b/src/spec/stub/rails81/config/credentials.yml.enc new file mode 100644 index 000000000..e67e3468f --- /dev/null +++ b/src/spec/stub/rails81/config/credentials.yml.enc @@ -0,0 +1 @@ +82NrCVblpkSw3wDVYHqQFkuTm4oS5bQt+rYDUmlei6JtoWvmkz/K1A81ysT3RXIojUAgbe2wo3zrl3dygSEmZCSV78wfDXJpBJi5fkVC84HQBJEBg0/8yFhZtDjvWG53X532RYnSVtAaPtPeqKS9F0uHNT7/G8Gkhfgu8JEg1oT1mlT4nT3VLExtH4QrXGBrvpWbFN6VzRIqFIO7Bk/tR92v6VCoAbl44j61pqEhW6SyDLb2PNGtW3o+Lq6RTTnsS9MOXXh/eNr4PZv97ghxbLSxcgzqXh6qBKsfZTTh30oxHRmv1yf1ur7hlopok1g4DcX5yK3Cul+ttkjYUoKdiSZ8cpZojQEqiMaRtvjVBGspHquqi9OuMtBlQaVxU1z3lGCdYnx1hqQDk6RnkBTE+Y7wbTGrjyQ1urKYXjw4bvu5WEh2n/UEHJsxrmg0v7yZZaOGESnfn+pbyWlbWfbA6fhH8ZguyqgzNiMh1TGU4Tqzvmgf/FAPQVNx--Jp0qmDUTnghki/m1--9Dp76qUD7YblOugS/mioww== \ No newline at end of file diff --git a/src/spec/stub/rails81/config/environment.rb b/src/spec/stub/rails81/config/environment.rb new file mode 100644 index 000000000..cac531577 --- /dev/null +++ b/src/spec/stub/rails81/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative "application" + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/src/spec/stub/rails81/config/environments/development.rb b/src/spec/stub/rails81/config/environments/development.rb new file mode 100644 index 000000000..f68dd3575 --- /dev/null +++ b/src/spec/stub/rails81/config/environments/development.rb @@ -0,0 +1,42 @@ +require "active_support/core_ext/integer/time" + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Make code changes take effect immediately without server restart. + config.enable_reloading = true + + # Do not eager load code on boot. + config.eager_load = false + + # Show full error reports. + config.consider_all_requests_local = true + + # Enable server timing. + config.server_timing = true + + # Enable/disable Action Controller caching. By default Action Controller caching is disabled. + # Run rails dev:cache to toggle Action Controller caching. + if Rails.root.join("tmp/caching-dev.txt").exist? + config.action_controller.perform_caching = true + config.action_controller.enable_fragment_cache_logging = true + config.public_file_server.headers = { "cache-control" => "public, max-age=#{2.days.to_i}" } + else + config.action_controller.perform_caching = false + end + + # Change to :null_store to avoid any caching. + config.cache_store = :memory_store + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + config.action_view.annotate_rendered_view_with_filenames = true + + # Raise error when a before_action's only/except options reference missing actions. + config.action_controller.raise_on_missing_callback_actions = true +end diff --git a/src/spec/stub/rails81/config/environments/production.rb b/src/spec/stub/rails81/config/environments/production.rb new file mode 100644 index 000000000..49ed6ab53 --- /dev/null +++ b/src/spec/stub/rails81/config/environments/production.rb @@ -0,0 +1,73 @@ +require "active_support/core_ext/integer/time" + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.enable_reloading = false + + # Eager load code on boot for better performance and memory savings (ignored by Rake tasks). + config.eager_load = true + + # Full error reports are disabled. + config.consider_all_requests_local = false + + # Turn on fragment caching in view templates. + config.action_controller.perform_caching = true + + # Cache digest stamped assets for far-future expiry. + # Short cache for others: robots.txt, sitemap.xml, 404.html, etc. + config.public_file_server.headers = { + "cache-control" => lambda do |path, _| + if path.start_with?("/assets/") + # Files in /assets/ are expected to be fully immutable. + # If the content change the URL too. + "public, immutable, max-age=#{1.year.to_i}" + else + # For anything else we cache for 1 minute. + "public, max-age=#{1.minute.to_i}, stale-while-revalidate=#{5.minutes.to_i}" + end + end + } + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.asset_host = "http://assets.example.com" + + # Assume all access to the app is happening through a SSL-terminating reverse proxy. + config.assume_ssl = true + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + config.force_ssl = true + + # Skip http-to-https redirect for the default health check endpoint. + # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } + + # Log to STDOUT with the current request id as a default log tag. + config.log_tags = [ :request_id ] + # config.logger = ActiveSupport::TaggedLogging.logger(STDOUT) + + # Change to "debug" to log everything (including potentially personally-identifiable information!). + config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") + + # Prevent health checks from clogging up the logs. + config.silence_healthcheck_path = "/up" + + # Don't log any deprecations. + config.active_support.report_deprecations = false + + # Replace the default in-process memory cache store with a durable alternative. + # config.cache_store = :mem_cache_store + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Enable DNS rebinding protection and other `Host` header attacks. + # config.hosts = [ + # "example.com", # Allow requests from example.com + # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com` + # ] + # + # Skip DNS rebinding protection for the default health check endpoint. + # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } +end diff --git a/src/spec/stub/rails81/config/environments/test.rb b/src/spec/stub/rails81/config/environments/test.rb new file mode 100644 index 000000000..14bc29e06 --- /dev/null +++ b/src/spec/stub/rails81/config/environments/test.rb @@ -0,0 +1,42 @@ +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # While tests run files are not watched, reloading is not necessary. + config.enable_reloading = false + + # Eager loading loads your entire application. When running a single test locally, + # this is usually not necessary, and can slow down your test suite. However, it's + # recommended that you enable it in continuous integration systems to ensure eager + # loading is working properly before deploying your code. + config.eager_load = ENV["CI"].present? + + # Configure public file server for tests with cache-control for performance. + config.public_file_server.headers = { "cache-control" => "public, max-age=3600" } + + # Show full error reports. + config.consider_all_requests_local = true + config.cache_store = :null_store + + # Render exception templates for rescuable exceptions and raise for other exceptions. + config.action_dispatch.show_exceptions = :rescuable + + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true + + # Raise error when a before_action's only/except options reference missing actions. + config.action_controller.raise_on_missing_callback_actions = true +end diff --git a/src/spec/stub/rails81/config/initializers/content_security_policy.rb b/src/spec/stub/rails81/config/initializers/content_security_policy.rb new file mode 100644 index 000000000..d51d71397 --- /dev/null +++ b/src/spec/stub/rails81/config/initializers/content_security_policy.rb @@ -0,0 +1,29 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide content security policy. +# See the Securing Rails Applications Guide for more information: +# https://guides.rubyonrails.org/security.html#content-security-policy-header + +# Rails.application.configure do +# config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end +# +# # Generate session nonces for permitted importmap, inline scripts, and inline styles. +# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } +# config.content_security_policy_nonce_directives = %w(script-src style-src) +# +# # Automatically add `nonce` to `javascript_tag`, `javascript_include_tag`, and `stylesheet_link_tag` +# # if the corresponding directives are specified in `content_security_policy_nonce_directives`. +# # config.content_security_policy_nonce_auto = true +# +# # Report violations without enforcing the policy. +# # config.content_security_policy_report_only = true +# end diff --git a/src/spec/stub/rails81/config/initializers/filter_parameter_logging.rb b/src/spec/stub/rails81/config/initializers/filter_parameter_logging.rb new file mode 100644 index 000000000..c0b717f7e --- /dev/null +++ b/src/spec/stub/rails81/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. +# Use this to limit dissemination of sensitive information. +# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. +Rails.application.config.filter_parameters += [ + :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc +] diff --git a/src/spec/stub/rails81/config/initializers/inflections.rb b/src/spec/stub/rails81/config/initializers/inflections.rb new file mode 100644 index 000000000..3860f659e --- /dev/null +++ b/src/spec/stub/rails81/config/initializers/inflections.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, "\\1en" +# inflect.singular /^(ox)en/i, "\\1" +# inflect.irregular "person", "people" +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym "RESTful" +# end diff --git a/src/spec/stub/rails81/config/locales/en.yml b/src/spec/stub/rails81/config/locales/en.yml new file mode 100644 index 000000000..6c349ae5e --- /dev/null +++ b/src/spec/stub/rails81/config/locales/en.yml @@ -0,0 +1,31 @@ +# Files in the config/locales directory are used for internationalization and +# are automatically loaded by Rails. If you want to use locales other than +# English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t "hello" +# +# In views, this is aliased to just `t`: +# +# <%= t("hello") %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# To learn more about the API, please read the Rails Internationalization guide +# at https://guides.rubyonrails.org/i18n.html. +# +# Be aware that YAML interprets the following case-insensitive strings as +# booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings +# must be quoted to be interpreted as strings. For example: +# +# en: +# "yes": yup +# enabled: "ON" + +en: + hello: "Hello world" diff --git a/src/spec/stub/rails81/config/master.key b/src/spec/stub/rails81/config/master.key new file mode 100644 index 000000000..e7883e7b5 --- /dev/null +++ b/src/spec/stub/rails81/config/master.key @@ -0,0 +1 @@ +c8efa0d2b5305bf19054185dc011f0f7 \ No newline at end of file diff --git a/src/spec/stub/rails81/config/routes.rb b/src/spec/stub/rails81/config/routes.rb new file mode 100644 index 000000000..48254e88e --- /dev/null +++ b/src/spec/stub/rails81/config/routes.rb @@ -0,0 +1,14 @@ +Rails.application.routes.draw do + # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html + + # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. + # Can be used by load balancers and uptime monitors to verify that the app is live. + get "up" => "rails/health#show", as: :rails_health_check + + # Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb) + # get "manifest" => "rails/pwa#manifest", as: :pwa_manifest + # get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker + + # Defines the root path route ("/") + # root "posts#index" +end diff --git a/src/spec/stub/rails81/public/robots.txt b/src/spec/stub/rails81/public/robots.txt new file mode 100644 index 000000000..c19f78ab6 --- /dev/null +++ b/src/spec/stub/rails81/public/robots.txt @@ -0,0 +1 @@ +# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file