diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ceb8614..bae199e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,4 +49,7 @@ jobs: bundler-cache: true - name: Lint with rubocop run: | - bundle exec rubocop -c .rubocop.yml + bundle exec rubocop -c .rubocop.yml -f github + - name: Check with brakeman + run: | + bundle exec brakeman --skip-files repos/ --no-pager diff --git a/Gemfile b/Gemfile index ef91fbd..397d2da 100644 --- a/Gemfile +++ b/Gemfile @@ -58,9 +58,10 @@ group :development, :test do end group :development do - gem 'annotate', '~> 3.2' - gem 'rubocop-minitest', '~> 0.35.1' - gem 'rubocop-rails', '~> 2.25' + gem 'annotate', '~> 3.2', require: false + gem 'brakeman', require: false + gem 'rubocop-minitest', '~> 0.35.1', require: false + gem 'rubocop-rails', '~> 2.25', require: false gem 'web-console' end diff --git a/Gemfile.lock b/Gemfile.lock index 7b1fb40..9e53168 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -82,6 +82,8 @@ GEM bindex (0.8.1) bootsnap (1.18.3) msgpack (~> 1.2) + brakeman (6.1.2) + racc builder (3.3.0) capybara (3.40.0) addressable @@ -329,6 +331,7 @@ PLATFORMS DEPENDENCIES annotate (~> 3.2) bootsnap + brakeman capybara cssbundling-rails debug diff --git a/bin/brakeman b/bin/brakeman new file mode 100755 index 0000000..ace1c9b --- /dev/null +++ b/bin/brakeman @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +ARGV.unshift("--ensure-latest") + +load Gem.bin_path("brakeman", "brakeman") diff --git a/bin/rubocop b/bin/rubocop new file mode 100755 index 0000000..40330c0 --- /dev/null +++ b/bin/rubocop @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +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 3cd5a9d..b8c9f38 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 = "gamification2" def system!(*args) system(*args, exception: true) @@ -30,4 +30,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/application.rb b/config/application.rb index 2615891..101aaf9 100644 --- a/config/application.rb +++ b/config/application.rb @@ -20,7 +20,7 @@ module Gamification2 class Application < Rails::Application - config.load_defaults 7.1 + config.load_defaults 7.2 config.active_support.cache_format_version = 7.1 config.autoload_lib(ignore: %w[assets tasks]) config.active_job.queue_adapter = :delayed_job diff --git a/config/brakeman.ignore b/config/brakeman.ignore new file mode 100644 index 0000000..88f74ca --- /dev/null +++ b/config/brakeman.ignore @@ -0,0 +1,74 @@ +{ + "ignored_warnings": [ + { + "warning_type": "Cross-Site Scripting", + "warning_code": 4, + "fingerprint": "102da98da3b38d5f1a50de0fcbf26279c5319c676c67fca0e464a40c98b0ae9c", + "check_name": "LinkToHref", + "message": "Potentially unsafe model attribute in `link_to` href", + "file": "app/views/coders/show.html.erb", + "line": 13, + "link": "https://brakemanscanner.org/docs/warning_types/link_to_href", + "code": "link_to(image_tag(\"github.png\"), Coder.in_organisation.extending(CommitStats).with_commit_stats.with_repository_count.find(params[:id]).github_url, :title => \"View profile on Github\")", + "render_path": [ + { + "type": "controller", + "class": "CodersController", + "method": "show", + "line": 18, + "file": "app/controllers/coders_controller.rb", + "rendered": { + "name": "coders/show", + "file": "app/views/coders/show.html.erb" + } + } + ], + "location": { + "type": "template", + "template": "coders/show" + }, + "user_input": "Coder.in_organisation.extending(CommitStats).with_commit_stats.with_repository_count.find(params[:id]).github_url", + "confidence": "Weak", + "cwe_id": [ + 79 + ], + "note": "This URL comes from GitHub, not from user input" + }, + { + "warning_type": "Cross-Site Scripting", + "warning_code": 4, + "fingerprint": "68764d8c652eaa58cf9d314b8f0f4cbc2a7d14b82dc6c1c854fc81bca648bbad", + "check_name": "LinkToHref", + "message": "Potentially unsafe model attribute in `link_to` href", + "file": "app/views/repositories/show.html.erb", + "line": 4, + "link": "https://brakemanscanner.org/docs/warning_types/link_to_href", + "code": "link_to(image_tag(\"github.png\"), Repository.find(params[:id]).github_url, :title => \"View repository on Github\")", + "render_path": [ + { + "type": "controller", + "class": "RepositoriesController", + "method": "show", + "line": 14, + "file": "app/controllers/repositories_controller.rb", + "rendered": { + "name": "repositories/show", + "file": "app/views/repositories/show.html.erb" + } + } + ], + "location": { + "type": "template", + "template": "repositories/show" + }, + "user_input": "Repository.find(params[:id]).github_url", + "confidence": "Weak", + "cwe_id": [ + 79 + ], + "note": "This URL comes from GitHub, not from user input" + } + ], + "updated": "2024-08-10 13:16:38 +0200", + "brakeman_version": "6.1.2" +} diff --git a/config/environments/development.rb b/config/environments/development.rb index 7459c23..19c96c1 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -14,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. @@ -24,9 +24,7 @@ config.action_controller.enable_fragment_cache_logging = true config.cache_store = :memory_store - 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 @@ -58,8 +56,11 @@ # config.i18n.raise_on_missing_translations = true # 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! end diff --git a/config/environments/production.rb b/config/environments/production.rb index 56cf56b..dea1579 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -13,20 +13,20 @@ config.eager_load = true # Full error reports are disabled and caching is turned on. - config.consider_all_requests_local = false + config.consider_all_requests_local = false config.action_controller.perform_caching = true # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files). # config.require_master_key = true - # Enable static file serving from the `/public` folder (turn off if using NGINX/Apache for it). - config.public_file_server.enabled = true + # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead. + # config.public_file_server.enabled = false # 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. @@ -41,7 +41,10 @@ # config.assume_ssl = true # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - # config.force_ssl = true + 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 by default config.logger = ActiveSupport::Logger.new($stdout) @@ -51,7 +54,7 @@ # Prepend all log lines with the following tags. config.log_tags = [:request_id] - # Info include generic and useful information about system operation, but avoids logging too much + # "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') @@ -60,7 +63,7 @@ # 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 = "gamification2_production" # Enable locale fallbacks for I18n (makes lookups for any locale fall back to diff --git a/config/environments/test.rb b/config/environments/test.rb index 554712e..a7e2373 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -18,17 +18,14 @@ config.eager_load = ENV['CI'].present? # Configure public file server for tests with Cache-Control for performance. - config.public_file_server.enabled = true - config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{1.hour.to_i}" - } + config.public_file_server.headers = { 'Cache-Control' => "public, max-age=#{1.hour.to_i}" } # Show full error reports and disable caching. - config.consider_all_requests_local = true + config.consider_all_requests_local = true config.action_controller.perform_caching = false config.cache_store = :null_store - # Raise exceptions instead of rendering exception templates. + # Render exception templates for rescuable exceptions and raise for other exceptions. config.action_dispatch.show_exceptions = :rescuable # Disable request forgery protection in test environment. @@ -49,6 +46,6 @@ # 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 end diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 262e862..58277c1 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -4,5 +4,5 @@ # Use this to limit dissemination of sensitive information. # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. Rails.application.config.filter_parameters += %i[ - passw secret token _key crypt salt certificate otp ssn + passw email secret token _key crypt salt certificate otp ssn ] diff --git a/config/puma.rb b/config/puma.rb index 0593712..632d820 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,43 +1,33 @@ -# 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 +# This configuration file will be evaluated by Puma. The top-level methods that +# 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. -# Specifies the `worker_timeout` threshold that Puma will use to wait before -# terminating a worker in development environments. +# Puma starts a configurable number of processes (workers) and each process +# serves each request in a thread from an internal thread pool. # -worker_timeout 3600 if ENV.fetch('RAILS_ENV', 'development') == 'development' - -# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +# 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. # -port ENV.fetch('PORT', 3000) - -# Specifies the `environment` that Puma will run in. +# 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. # -environment ENV.fetch('RAILS_ENV') { 'development' } - -# Specifies the `pidfile` that Puma will use. -pidfile ENV.fetch('PIDFILE') { 'tmp/pids/server.pid' } - -# Specifies the number of `workers` to boot in clustered mode. -# Workers are forked web server processes. If using threads and workers together -# the concurrency of the application would be max `threads` * `workers`. -# Workers do not work on JRuby or Windows (both of which do not support -# processes). +# The default is set to 3 threads as it's deemed a decent compromise between +# throughput and latency for the average Rails application. # -# workers ENV.fetch("WEB_CONCURRENCY") { 2 } +# 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 -# Use the `preload_app!` method when specifying a `workers` number. -# This directive tells Puma to first boot the application and load code -# before forking the application. This takes advantage of Copy On Write -# process behavior so workers use less memory. -# -# preload_app! +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +port ENV.fetch('PORT', 3000) # Allow puma to be restarted by `bin/rails restart` command. plugin :tmp_restart + +# Only use a pidfile when requested +pidfile ENV['PIDFILE'] if ENV['PIDFILE'] diff --git a/public/406-unsupported-browser.html b/public/406-unsupported-browser.html new file mode 100644 index 0000000..7cf1e16 --- /dev/null +++ b/public/406-unsupported-browser.html @@ -0,0 +1,66 @@ + + + + Your browser is not supported (406) + + + + + + +
+
+

Your browser is not supported.

+

Please upgrade your browser to continue.

+
+
+ + diff --git a/public/icon.png b/public/icon.png new file mode 100644 index 0000000..f3b5abc Binary files /dev/null and b/public/icon.png differ diff --git a/public/icon.svg b/public/icon.svg new file mode 100644 index 0000000..78307cc --- /dev/null +++ b/public/icon.svg @@ -0,0 +1,3 @@ + + +