My opinionated ruby on rails template
at main 132 lines 4.4 kB view raw
1# frozen_string_literal: true 2 3say 'Setting up security gems...', :green 4 5gem 'lockbox' 6gem 'blind_index' 7gem 'invisible_captcha' 8gem 'strong_migrations' 9 10after_bundle do 11 say ' Running Lockbox audits generator...', :cyan 12 rails_command 'generate lockbox:audits' 13 14 say ' Running Strong Migrations installer...', :cyan 15 rails_command 'generate strong_migrations:install' 16end 17 18say ' Creating Lockbox initializer...', :cyan 19initializer 'lockbox.rb', <<~RUBY 20 # frozen_string_literal: true 21 22 # Lockbox - Field-level encryption 23 # https://github.com/ankane/lockbox 24 # 25 # Generate key with: Lockbox.generate_key 26 27 if Rails.application.credentials.lockbox&.key?(:master_key) 28 Lockbox.master_key = Rails.application.credentials.lockbox[:master_key] 29 elsif ENV['LOCKBOX_MASTER_KEY'].present? 30 Lockbox.master_key = ENV['LOCKBOX_MASTER_KEY'] 31 elsif Rails.env.production? 32 raise 'Lockbox master_key not configured! Run: rails credentials:edit' 33 else 34 Rails.logger.warn 'Lockbox master_key not found in credentials. Add it with: rails credentials:edit' 35 end 36RUBY 37 38say ' Creating BlindIndex initializer...', :cyan 39initializer 'blind_index.rb', <<~RUBY 40 # frozen_string_literal: true 41 42 # Blind Index - Searchable Encryption 43 # https://github.com/ankane/blind_index 44 # 45 # Allows searching encrypted columns without decrypting them 46 # Generate key with: BlindIndex.generate_key 47 48 if Rails.application.credentials.blind_index&.key?(:master_key) 49 BlindIndex.master_key = Rails.application.credentials.blind_index[:master_key] 50 elsif ENV['BLIND_INDEX_MASTER_KEY'].present? 51 BlindIndex.master_key = ENV['BLIND_INDEX_MASTER_KEY'] 52 elsif Rails.env.production? 53 raise 'BlindIndex master_key not configured! Run: rails credentials:edit' 54 else 55 Rails.logger.warn 'BlindIndex master_key not found in credentials. Add it with: rails credentials:edit' 56 end 57 58 # Default options 59 BlindIndex.default_options[:algorithm] = :argon2id # Most secure, recommended 60 BlindIndex.default_options[:insecure_key] = false # Require secure keys 61RUBY 62 63say ' Creating InvisibleCaptcha initializer...', :cyan 64file 'config/initializers/invisible_captcha.rb', <<~RUBY 65 # frozen_string_literal: true 66 67 InvisibleCaptcha.setup do |config| 68 # Minimum time (in seconds) for a human to fill out a form 69 config.timestamp_threshold = 2 70 71 # Custom honeypot field name (randomized per form by default) 72 # config.honeypots = ['foo', 'bar'] 73 74 # Flash message when spam is detected 75 config.timestamp_error_message = 'Something went wrong. Please try again.' 76 77 # Enable visual mode for debugging in development 78 config.visual_honeypots = Rails.env.development? 79 end 80RUBY 81 82say ' Creating Encryptable concern...', :cyan 83file 'app/models/concerns/encryptable.rb', <<~RUBY 84 # frozen_string_literal: true 85 86 # Include this concern and use the DSL to encrypt sensitive fields 87 # 88 # Example: 89 # class User < ApplicationRecord 90 # include Encryptable 91 # 92 # encrypts_field :ssn 93 # encrypts_field :phone, searchable: true 94 # end 95 # 96 # Migration for encrypted fields: 97 # add_column :users, :ssn_ciphertext, :text 98 # add_column :users, :phone_ciphertext, :text 99 # add_column :users, :phone_bidx, :string # for searchable fields 100 # add_index :users, :phone_bidx 101 # 102 module Encryptable 103 extend ActiveSupport::Concern 104 105 class_methods do 106 def encrypts_field(field_name, searchable: false) 107 encrypts field_name 108 109 if searchable 110 blind_index field_name 111 end 112 end 113 end 114 end 115RUBY 116 117say ' Adding invisible_captcha to ApplicationController...', :cyan 118inject_into_class 'app/controllers/application_controller.rb', 'ApplicationController', <<~RUBY 119 # Invisible captcha is available in forms via: invisible_captcha 120 # Add to specific controllers with: invisible_captcha only: [:create], on_spam: :spam_detected 121 # 122 # private 123 # def spam_detected 124 # redirect_to root_path, alert: 'Spam detected.' 125 # end 126RUBY 127 128say 'Security gems configured!', :green 129say ' - Lockbox: Use `encrypts :field_name` in models', :cyan 130say ' - BlindIndex: Use `blind_index :field_name` for searchable encrypted fields', :cyan 131say ' - InvisibleCaptcha: Use `invisible_captcha` helper in forms', :cyan 132say ' Generate production keys with: Lockbox.generate_key / BlindIndex.generate_key', :yellow