diff --git a/bin/brakeman b/bin/brakeman index b4fe8de2663..ace1c9ba08a 100755 --- a/bin/brakeman +++ b/bin/brakeman @@ -1,27 +1,7 @@ #!/usr/bin/env ruby -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application 'brakeman' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) - -bundle_binstub = File.expand_path("bundle", __dir__) - -if File.file?(bundle_binstub) - if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") - load(bundle_binstub) - else - abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. -Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") - end -end - require "rubygems" require "bundler/setup" +ARGV.unshift("--ensure-latest") + load Gem.bin_path("brakeman", "brakeman") diff --git a/bin/rubocop b/bin/rubocop index 369a05bedb5..40330c0ff1c 100755 --- a/bin/rubocop +++ b/bin/rubocop @@ -1,27 +1,8 @@ #!/usr/bin/env ruby -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application 'rubocop' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) - -bundle_binstub = File.expand_path("bundle", __dir__) - -if File.file?(bundle_binstub) - if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") - load(bundle_binstub) - else - abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. -Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") - end -end - require "rubygems" require "bundler/setup" +# explicit rubocop config increases performance slightly while avoiding config confusion. +ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) + load Gem.bin_path("rubocop", "rubocop") diff --git a/bin/setup b/bin/setup index e2675753e1d..51afbda953b 100755 --- a/bin/setup +++ b/bin/setup @@ -1,8 +1,8 @@ #!/usr/bin/env ruby require "fileutils" -# path to your application root. APP_ROOT = File.expand_path("..", __dir__) +APP_NAME = "gemcutter" def system!(*args) system(*args, exception: true) @@ -36,4 +36,8 @@ FileUtils.chdir APP_ROOT do puts "\n== Restarting application server ==" system! "bin/rails restart" + + # puts "\n== Configuring puma-dev ==" + # system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}" + # system "curl -Is https://#{APP_NAME}.test/up | head -n 1" end diff --git a/config/environments/development.rb b/config/environments/development.rb index 42bbef4f7de..985853896bd 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,4 +1,3 @@ -require_relative "../../lib/gemcutter/middleware/hostess" require "active_support/core_ext/integer/time" Rails.application.configure do @@ -15,7 +14,7 @@ # Show full error reports. config.consider_all_requests_local = true - # Enable server timing + # Enable server timing. config.server_timing = true # Enable/disable caching. By default caching is disabled. @@ -26,9 +25,7 @@ config.cache_store = :mem_cache_store, { compress: true, compression_min_size: 524_288 } - config.public_file_server.headers = { - "Cache-Control" => "public, max-age=#{2.days.to_i}" - } + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false @@ -40,6 +37,8 @@ config.action_mailer.raise_delivery_errors = true + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. config.action_mailer.perform_caching = false config.action_mailer.default_url_options = { host: Gemcutter::HOST, @@ -75,13 +74,18 @@ # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true + require_relative "../../lib/gemcutter/middleware/hostess" config.middleware.use Gemcutter::Middleware::Hostess + # Annotate rendered view with file names. - # config.action_view.annotate_rendered_view_with_filenames = true + config.action_view.annotate_rendered_view_with_filenames = true - # Raise error when a before_action's only/except options reference missing actions + # Raise error when a before_action's only/except options reference missing actions. config.action_controller.raise_on_missing_callback_actions = true + # Apply autocorrection by RuboCop to files generated by `bin/rails generate`. + config.generators.apply_rubocop_autocorrect_after_generate! + # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. config.file_watcher = ActiveSupport::EventedFileUpdateChecker diff --git a/config/environments/production.rb b/config/environments/production.rb index 8d5862924f6..fce4d18fc9b 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,5 +1,3 @@ -require Rails.root.join("config", "secret") if Rails.root.join("config", "secret.rb").file? -require_relative "../../lib/gemcutter/middleware/redirector" require "active_support/core_ext/integer/time" Rails.application.configure do @@ -22,8 +20,7 @@ # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files). # config.require_master_key = true - # Disable serving static files from the `/public` folder by default since - # Apache or NGINX already handles this. + # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead. config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? config.public_file_server.headers = { 'Cache-Control' => 'max-age=315360000, public', @@ -36,7 +33,7 @@ # Compress CSS using a preprocessor. config.assets.css_compressor = :sass - # Do not fallback to assets pipeline if a precompiled asset is missed. + # Do not fall back to assets pipeline if a precompiled asset is missed. config.assets.compile = false # Enable serving of images, stylesheets, and JavaScripts from an asset server. @@ -62,7 +59,6 @@ # Include generic and useful information about system operation, but avoid logging too much # information to avoid inadvertent exposure of personally identifiable information (PII). $stdout.sync = true - config.log_level = :info config.rails_semantic_logger.format = :json config.rails_semantic_logger.semantic = true config.rails_semantic_logger.add_file_appender = false @@ -71,13 +67,20 @@ # Prepend all log lines with the following tags. # config.log_tags = [ :request_id ] + # "info" includes generic and useful information about system operation, but avoids logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). If you + # want to log everything, set the level to "debug". + config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") + # Use a different cache store in production. # config.cache_store = :mem_cache_store # Use a real queuing backend for Active Job (and separate queues per environment). - # config.active_job.queue_adapter = :resque + # config.active_job.queue_adapter = :resque # config.active_job.queue_name_prefix = "gemcutter_production" + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. config.action_mailer.perform_caching = false # Ignore bad email addresses and do not raise email delivery errors. @@ -122,5 +125,6 @@ value_max_bytes: 2_097_152 # 2MB } + require_relative "../../lib/gemcutter/middleware/redirector" config.middleware.use Gemcutter::Middleware::Redirector end diff --git a/config/environments/test.rb b/config/environments/test.rb index 73358c44122..a65ac8280f1 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,4 +1,3 @@ -require_relative "../../lib/gemcutter/middleware/redirector" require "active_support/core_ext/integer/time" # The test environment is used exclusively to run your application's @@ -35,12 +34,17 @@ # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. config.action_mailer.perform_caching = false # Tell Action Mailer not to deliver emails to the real world. # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test + + # Unlike controllers, the mailer instance doesn't have any context about the + # incoming request so you'll need to provide the :host parameter yourself. config.action_mailer.default_url_options = { host: Gemcutter::HOST, port: "31337", protocol: Gemcutter::PROTOCOL } @@ -48,7 +52,7 @@ # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr - require 'clearance_backdoor' + require_relative "../../lib/clearance_backdoor" config.middleware.use ClearanceBackdoor # Raise exceptions for disallowed deprecations. @@ -65,7 +69,7 @@ # 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 + # Raise error when a before_action's only/except options reference missing actions. config.action_controller.raise_on_missing_callback_actions = true BCrypt::Engine.cost = BCrypt::Engine::MIN_COST diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index f332248831b..616790254b5 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -1,49 +1,51 @@ # Be sure to restart your server when you modify this file. -# Define an application-wide content security policy -# For further information see the following documentation -# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy +# 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.config.content_security_policy do |policy| - policy.default_src :self - policy.font_src :self, "https://fonts.gstatic.com" - policy.img_src :self, "https://secure.gaug.es", "https://gravatar.com", "https://www.gravatar.com", "https://secure.gravatar.com", - "https://*.fastly-insights.com", "https://avatars.githubusercontent.com" - policy.object_src :none - # NOTE: This scirpt_src is overridden for all requests in ApplicationController - # This is the baseline in case the override is ever skipped - policy.script_src :self, "https://secure.gaug.es", "https://www.fastly-insights.com" - policy.style_src :self, "https://fonts.googleapis.com" - policy.connect_src :self, "https://s3-us-west-2.amazonaws.com/rubygems-dumps/", "https://*.fastly-insights.com", "https://fastly-insights.com", - "https://api.github.com", "http://localhost:*" - policy.form_action :self, "https://github.com/login/oauth/authorize" - policy.frame_ancestors :self - policy.base_uri :self +Rails.application.configure do + config.content_security_policy do |policy| + policy.default_src :self + policy.font_src :self, "https://fonts.gstatic.com" + policy.img_src :self, "https://secure.gaug.es", "https://gravatar.com", "https://www.gravatar.com", "https://secure.gravatar.com", + "https://*.fastly-insights.com", "https://avatars.githubusercontent.com" + policy.object_src :none + # NOTE: This scirpt_src is overridden for all requests in ApplicationController + # This is the baseline in case the override is ever skipped + policy.script_src :self, "https://secure.gaug.es", "https://www.fastly-insights.com" + policy.style_src :self, "https://fonts.googleapis.com" + policy.connect_src :self, "https://s3-us-west-2.amazonaws.com/rubygems-dumps/", "https://*.fastly-insights.com", "https://fastly-insights.com", + "https://api.github.com", "http://localhost:*" + policy.form_action :self, "https://github.com/login/oauth/authorize" + policy.frame_ancestors :self + policy.base_uri :self - # Specify URI for violation reports - policy.report_uri lambda { - dd_api_key = ENV['DATADOG_CSP_API_KEY'].presence - url = ActionDispatch::Http::URL.url_for( - protocol: 'https', - host: 'csp-report.browser-intake-datadoghq.com', - path: '/api/v2/logs', - params: { - "dd-api-key": dd_api_key, - "dd-evp-origin": 'content-security-policy', - ddsource: 'csp-report', - ddtags: { - service: "rubygems.org", - version: AppRevision.version, - env: Rails.env, - trace_id: Datadog::Tracing.correlation&.trace_id, - "gemcutter.user.id": (current_user.id if respond_to?(:signed_in?) && signed_in?) - }.compact.map { |k, v| "#{k}:#{v}" }.join(',') - } - ) - # ensure we compute the URL on development/test, - # but onlu return it if the API key is configures - url if dd_api_key - } + # Specify URI for violation reports + policy.report_uri lambda { + dd_api_key = ENV['DATADOG_CSP_API_KEY'].presence + url = ActionDispatch::Http::URL.url_for( + protocol: 'https', + host: 'csp-report.browser-intake-datadoghq.com', + path: '/api/v2/logs', + params: { + "dd-api-key": dd_api_key, + "dd-evp-origin": 'content-security-policy', + ddsource: 'csp-report', + ddtags: { + service: "rubygems.org", + version: AppRevision.version, + env: Rails.env, + trace_id: Datadog::Tracing.correlation&.trace_id, + "gemcutter.user.id": (current_user.id if respond_to?(:signed_in?) && signed_in?) + }.compact.map { |k, v| "#{k}:#{v}" }.join(',') + } + ) + # ensure we compute the URL on development/test, + # but onlu return it if the API key is configures + url if dd_api_key + } + end end # Generate session nonces for permitted importmap, inline scripts, and inline styles. @@ -54,7 +56,3 @@ request.session.id.to_s.presence || SecureRandom.base64(16) } Rails.application.config.content_security_policy_nonce_directives = %w[script-src style-src] - -# Report CSP violations to a specified URI. See: -# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only -# config.content_security_policy_report_only = truepoint diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index d997f9aa485..89c5d312f58 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -3,7 +3,7 @@ # 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 += %I[ - password passw secret token _key crypt salt certificate otp ssn api_key recovery_codes seed - jwt +Rails.application.config.filter_parameters += %i[ + passw email secret token _key crypt salt certificate otp ssn + api_key recovery_codes seed jwt password ] diff --git a/config/initializers/new_framework_defaults_7_2.rb b/config/initializers/new_framework_defaults_7_2.rb new file mode 100644 index 00000000000..b549c4a258a --- /dev/null +++ b/config/initializers/new_framework_defaults_7_2.rb @@ -0,0 +1,70 @@ +# Be sure to restart your server when you modify this file. +# +# This file eases your Rails 7.2 framework defaults upgrade. +# +# Uncomment each configuration one by one to switch to the new default. +# Once your application is ready to run with all new defaults, you can remove +# this file and set the `config.load_defaults` to `7.2`. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. +# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html + +### +# Controls whether Active Job's `#perform_later` and similar methods automatically defer +# the job queuing to after the current Active Record transaction is committed. +# +# Example: +# Topic.transaction do +# topic = Topic.create(...) +# NewTopicNotificationJob.perform_later(topic) +# end +# +# In this example, if the configuration is set to `:never`, the job will +# be enqueued immediately, even though the `Topic` hasn't been committed yet. +# Because of this, if the job is picked up almost immediately, or if the +# transaction doesn't succeed for some reason, the job will fail to find this +# topic in the database. +# +# If `enqueue_after_transaction_commit` is set to `:default`, the queue adapter +# will define the behaviour. +# +# Note: Active Job backends can disable this feature. This is generally done by +# backends that use the same database as Active Record as a queue, hence they +# don't need this feature. +#++ +# Rails.application.config.active_job.enqueue_after_transaction_commit = :default + +### +# Adds image/webp to the list of content types Active Storage considers as an image +# Prevents automatic conversion to a fallback PNG, and assumes clients support WebP, as they support gif, jpeg, and png. +# This is possible due to broad browser support for WebP, but older browsers and email clients may still not support +# WebP. Requires imagemagick/libvips built with WebP support. +#++ +# Rails.application.config.active_storage.web_image_content_types = %w[image/png image/jpeg image/gif image/webp] + +### +# Enable validation of migration timestamps. When set, an ActiveRecord::InvalidMigrationTimestampError +# will be raised if the timestamp prefix for a migration is more than a day ahead of the timestamp +# associated with the current time. This is done to prevent forward-dating of migration files, which can +# impact migration generation and other migration commands. +# +# Applications with existing timestamped migrations that do not adhere to the +# expected format can disable validation by setting this config to `false`. +#++ +# Rails.application.config.active_record.validate_migration_timestamps = true + +### +# Controls whether the PostgresqlAdapter should decode dates automatically with manual queries. +# +# Example: +# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date") #=> Date +# +# This query used to return a `String`. +#++ +# Rails.application.config.active_record.postgresql_adapter_decode_dates = true + +### +# Enables YJIT as of Ruby 3.3, to bring sizeable performance improvements. If you are +# deploying to a memory constrained environment you may want to set this to `false`. +#++ +# Rails.application.config.yjit = true diff --git a/config/puma.rb b/config/puma.rb index cbc576cdb8f..06bcbfdb6d8 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -2,20 +2,32 @@ # are invoked here are part of Puma's configuration DSL. For more information # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. -# Puma can serve each request in a thread from an internal thread pool. -# The `threads` method setting takes two numbers: a minimum and maximum. -# Any libraries that use thread pools should be configured to match -# the maximum value specified for Puma. Default is set to 5 threads for minimum -# and maximum; this matches the default thread size of Active Record. -max_threads_count = ENV.fetch("RAILS_MAX_THREADS", 5) -min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } -threads min_threads_count, max_threads_count - -require "concurrent" +# Puma starts a configurable number of processes (workers) and each process +# serves each request in a thread from an internal thread pool. +# +# The ideal number of threads per worker depends both on how much time the +# application spends waiting for IO operations and on how much you wish to +# to prioritize throughput over latency. +# +# As a rule of thumb, increasing the number of threads will increase how much +# traffic a given process can handle (throughput), but due to CRuby's +# Global VM Lock (GVL) it has diminishing returns and will degrade the +# response time (latency) of the application. +# +# The default is set to 3 threads as it's deemed a decent compromise between +# throughput and latency for the average Rails application. +# +# Any libraries that use a connection pool or another resource pool should +# be configured to provide at least as many connections as the number of +# threads. This includes Active Record's `pool` parameter in `database.yml`. +threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) +threads threads_count, threads_count rails_env = ENV.fetch("RAILS_ENV") { "development" } production_like = !%w[development test].include?(rails_env) # rubocop:disable Rails/NegateInclude +require "concurrent" + if production_like # Specifies that the worker count should equal the number of processors in production. worker_count = Integer(ENV.fetch("WEB_CONCURRENCY") { Concurrent.physical_processor_count }) @@ -39,8 +51,9 @@ # Specifies the `environment` that Puma will run in. environment rails_env -# Specifies the `pidfile` that Puma will use. -pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } +# Specify the PID file. Defaults to tmp/pids/server.pid in development. +# In other environments, only set the PID file if requested. +pidfile ENV["PIDFILE"] if ENV["PIDFILE"] before_fork do sleep 1 diff --git a/public/robots.txt b/public/robots.txt index 1fab05ef6e9..a1bc7a1368b 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,3 +1,4 @@ +# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file User-agent: * Disallow: /downloads/ Disallow: /gems?letter=*