rockstart 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +100 -0
  4. data/Rakefile +19 -0
  5. data/lib/generators/rockstart/USAGE +13 -0
  6. data/lib/generators/rockstart/devise/USAGE +9 -0
  7. data/lib/generators/rockstart/devise/devise_generator.rb +258 -0
  8. data/lib/generators/rockstart/devise/templates/controllers/passwords_controller.rb +56 -0
  9. data/lib/generators/rockstart/devise/templates/controllers/registrations_controller.rb +88 -0
  10. data/lib/generators/rockstart/devise/templates/controllers/sessions_controller.rb +32 -0
  11. data/lib/generators/rockstart/devise/templates/create_user_migration.rb.tt +11 -0
  12. data/lib/generators/rockstart/devise/templates/models/user.rb +42 -0
  13. data/lib/generators/rockstart/devise/templates/spec/factories/users.rb +17 -0
  14. data/lib/generators/rockstart/devise/templates/spec/models/user_spec.rb +64 -0
  15. data/lib/generators/rockstart/devise/templates/spec/requests/users/passwords_spec.rb +202 -0
  16. data/lib/generators/rockstart/devise/templates/spec/requests/users/registrations_spec.rb +445 -0
  17. data/lib/generators/rockstart/devise/templates/spec/requests/users/sessions_spec.rb +171 -0
  18. data/lib/generators/rockstart/devise/templates/spec/support/devise_request_spec_helper.rb +29 -0
  19. data/lib/generators/rockstart/devise/templates/translations.en.yml +4 -0
  20. data/lib/generators/rockstart/docker/USAGE +10 -0
  21. data/lib/generators/rockstart/docker/docker_generator.rb +86 -0
  22. data/lib/generators/rockstart/docker/templates/app/Dockerfile-app +47 -0
  23. data/lib/generators/rockstart/docker/templates/docker-compose.test.yml +29 -0
  24. data/lib/generators/rockstart/docker/templates/docker-compose.yml +47 -0
  25. data/lib/generators/rockstart/docker/templates/dockerignore +16 -0
  26. data/lib/generators/rockstart/docker/templates/dotenv.docker.tt +4 -0
  27. data/lib/generators/rockstart/docker/templates/localhost_domains.ext.tt +7 -0
  28. data/lib/generators/rockstart/docker/templates/setup-localhost.tt +27 -0
  29. data/lib/generators/rockstart/docker/templates/web/Dockerfile-web +15 -0
  30. data/lib/generators/rockstart/docker/templates/web/nginx.conf +62 -0
  31. data/lib/generators/rockstart/frontend_helpers/USAGE +8 -0
  32. data/lib/generators/rockstart/frontend_helpers/frontend_helpers_generator.rb +65 -0
  33. data/lib/generators/rockstart/frontend_helpers/templates/application_urls.rb +26 -0
  34. data/lib/generators/rockstart/frontend_helpers/templates/application_urls_helper.rb +20 -0
  35. data/lib/generators/rockstart/frontend_helpers/templates/titles.en.yml.tt +5 -0
  36. data/lib/generators/rockstart/logging/USAGE +8 -0
  37. data/lib/generators/rockstart/logging/logging_generator.rb +12 -0
  38. data/lib/generators/rockstart/logging/templates/rockstart/lograge_initializer.rb +50 -0
  39. data/lib/generators/rockstart/postgres/USAGE +8 -0
  40. data/lib/generators/rockstart/postgres/postgres_generator.rb +32 -0
  41. data/lib/generators/rockstart/postgres/templates/config/database.yml.tt +18 -0
  42. data/lib/generators/rockstart/postgres/templates/migration.rb.tt +7 -0
  43. data/lib/generators/rockstart/pundit/USAGE +8 -0
  44. data/lib/generators/rockstart/pundit/pundit_generator.rb +32 -0
  45. data/lib/generators/rockstart/pundit/templates/app/controllers/concerns/pundit_error_handling.rb +29 -0
  46. data/lib/generators/rockstart/pundit/templates/app/policies/application_policy.rb +71 -0
  47. data/lib/generators/rockstart/pundit/templates/app/policies/user_policy.rb +47 -0
  48. data/lib/generators/rockstart/pundit/templates/config/locales/pundit.en.yml +6 -0
  49. data/lib/generators/rockstart/pundit/templates/lib/templates/pundit/policy/policy.rb +36 -0
  50. data/lib/generators/rockstart/pundit/templates/lib/templates/rspec/policy/policy_spec.rb +58 -0
  51. data/lib/generators/rockstart/pundit/templates/spec/policies/user_policy_spec.rb +95 -0
  52. data/lib/generators/rockstart/pundit/templates/spec/support/pundit_matchers.rb +7 -0
  53. data/lib/generators/rockstart/quality/USAGE +10 -0
  54. data/lib/generators/rockstart/quality/quality_generator.rb +28 -0
  55. data/lib/generators/rockstart/quality/templates/quality.rake +4 -0
  56. data/lib/generators/rockstart/quality/templates/rubocop.rake +4 -0
  57. data/lib/generators/rockstart/quality/templates/rubocop.yml +45 -0
  58. data/lib/generators/rockstart/rockstart_generator.rb +77 -0
  59. data/lib/generators/rockstart/rspec/USAGE +8 -0
  60. data/lib/generators/rockstart/rspec/rspec_generator.rb +70 -0
  61. data/lib/generators/rockstart/rspec/templates/dotenv.development +1 -0
  62. data/lib/generators/rockstart/rspec/templates/dotenv.test +1 -0
  63. data/lib/generators/rockstart/rspec/templates/rspec_templates/model/model_spec.rb +13 -0
  64. data/lib/generators/rockstart/rspec/templates/support/factory_bot.rb +6 -0
  65. data/lib/generators/rockstart/rspec/templates/support/shoulda_matchers.rb +9 -0
  66. data/lib/generators/rockstart/rspec/templates/support/test_helpers.rb +9 -0
  67. data/lib/generators/rockstart/scaffold_templates/USAGE +8 -0
  68. data/lib/generators/rockstart/scaffold_templates/scaffold_templates_generator.rb +39 -0
  69. data/lib/generators/rockstart/scaffold_templates/templates/api_controller.rb.tt +96 -0
  70. data/lib/generators/rockstart/scaffold_templates/templates/controller.rb.tt +126 -0
  71. data/lib/generators/rockstart/scaffold_templates/templates/rspec/scaffold/api_request_spec.rb +139 -0
  72. data/lib/generators/rockstart/scaffold_templates/templates/rspec/scaffold/request_spec.rb +408 -0
  73. data/lib/generators/rockstart/security/USAGE +13 -0
  74. data/lib/generators/rockstart/security/security_generator.rb +108 -0
  75. data/lib/generators/rockstart/security/templates/brakeman.rake +6 -0
  76. data/lib/generators/rockstart/security/templates/bundler_audit.rake +4 -0
  77. data/lib/generators/rockstart/security/templates/cache_support.rb +18 -0
  78. data/lib/generators/rockstart/security/templates/content_security_policy_initializer.rb.tt +56 -0
  79. data/lib/generators/rockstart/security/templates/content_security_spec.rb.tt +83 -0
  80. data/lib/generators/rockstart/security/templates/csp_violations_controller.rb +39 -0
  81. data/lib/generators/rockstart/security/templates/rack_attack.rb +98 -0
  82. data/lib/generators/rockstart/security/templates/security.rake +9 -0
  83. data/lib/generators/rockstart/security/templates/session_store_initializer.rb.tt +7 -0
  84. data/lib/generators/rockstart/smtp_mailer/USAGE +8 -0
  85. data/lib/generators/rockstart/smtp_mailer/smtp_mailer_generator.rb +30 -0
  86. data/lib/generators/rockstart/smtp_mailer/templates/config/initializers/action_mailer.rb +10 -0
  87. data/lib/generators/rockstart/tailwindcss/USAGE +8 -0
  88. data/lib/generators/rockstart/tailwindcss/tailwindcss_generator.rb +30 -0
  89. data/lib/generators/rockstart/tailwindcss/templates/application.css +3 -0
  90. data/lib/generators/rockstart/tailwindcss/templates/postcss.config.js +32 -0
  91. data/lib/rockstart/base_generator.rb +32 -0
  92. data/lib/rockstart/env.rb +16 -0
  93. data/lib/rockstart/railtie.rb +6 -0
  94. data/lib/rockstart/version.rb +5 -0
  95. data/lib/rockstart.rb +9 -0
  96. data/lib/tasks/rockstart_tasks.rake +5 -0
  97. metadata +187 -0
@@ -0,0 +1,408 @@
1
+ <%- resource_path = name.underscore.pluralize -%>
2
+ <%- permitted_params = attributes.map { |a| ":#{a.name}" }.join(", ") -%>
3
+ # frozen_string_literal: true
4
+
5
+ require "rails_helper"
6
+
7
+ <% module_namespacing do -%>
8
+ RSpec.describe "<%= controller_class_name %>", <%= type_metatag(:request) %> do
9
+ <% unless options[:singleton] -%>
10
+ describe "GET /<%= resource_path %>" do
11
+ context "with an authenticated admin" do
12
+ let(:authenticated_admin) { create(:user, :admin) }
13
+
14
+ before do
15
+ sign_in(authenticated_admin)
16
+ end
17
+
18
+ it "renders a successful response" do
19
+ create(:<%= file_name %>)
20
+ get <%= index_helper %>_url
21
+ expect(response).to be_successful
22
+ end
23
+ end
24
+
25
+ context "with an authenticated user" do
26
+ let(:authenticated_user) { create(:user) }
27
+
28
+ before do
29
+ sign_in(authenticated_user)
30
+ end
31
+
32
+ it "renders a successful response" do
33
+ create(:<%= file_name %>)
34
+ get <%= index_helper %>_url
35
+ expect(response).to be_successful
36
+ end
37
+ end
38
+
39
+ context "with an unauthorized user" do
40
+ let(:unauthorized_user) do
41
+ skip("Provide a user where <%= class_name %>Policy.index? is not granted")
42
+ end
43
+
44
+ before do
45
+ sign_in(unauthorized_user)
46
+ end
47
+
48
+ it "forbids access" do
49
+ create(:<%= file_name %>)
50
+ get <%= index_helper %>_url
51
+ expect(response).to redirect_to(url_for_user_dashboard)
52
+
53
+ follow_redirect!
54
+ expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.index?", default: t("pundit.default")))
55
+ end
56
+ end
57
+
58
+ it "does not allow access to guests" do
59
+ create(:<%= file_name %>)
60
+ get <%= index_helper %>_url
61
+ expect(response).to redirect_to(url_for_authentication)
62
+ end
63
+ end
64
+ <% end -%>
65
+
66
+ describe "GET /<%= resource_path %>/:id" do
67
+ context "with an authenticated admin" do
68
+ let(:authenticated_admin) { create(:user, :admin) }
69
+
70
+ before do
71
+ sign_in(authenticated_admin)
72
+ end
73
+
74
+ it "renders a successful response" do
75
+ <%= file_name %> = create(:<%= file_name %>)
76
+ get <%= show_helper.tr('@', '') %>
77
+ expect(response).to be_successful
78
+ end
79
+ end
80
+
81
+ context "with an authenticated user" do
82
+ let(:authenticated_user) { create(:user) }
83
+
84
+ before do
85
+ sign_in(authenticated_user)
86
+ end
87
+
88
+ it "renders a successful response" do
89
+ <%= file_name %> = create(:<%= file_name %>)
90
+ get <%= show_helper.tr('@', '') %>
91
+ expect(response).to be_successful
92
+ end
93
+ end
94
+
95
+ context "with an unauthorized user" do
96
+ let(:unauthorized_user) do
97
+ skip("Provide a user where <%= class_name %>Policy.show? is not granted")
98
+ end
99
+
100
+ before do
101
+ sign_in(unauthorized_user)
102
+ end
103
+
104
+ it "forbids access" do
105
+ <%= file_name %> = create(:<%= file_name %>)
106
+ get <%= show_helper.tr('@', '') %>
107
+ expect(response).to redirect_to(<%= index_helper %>_url)
108
+
109
+ follow_redirect!
110
+ expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.show?", default: t("pundit.default")))
111
+ end
112
+ end
113
+
114
+ it "does not allow access to guests" do
115
+ <%= file_name %> = create(:<%= file_name %>)
116
+ get <%= show_helper.tr('@', '') %>
117
+ expect(response).to redirect_to(url_for_authentication)
118
+ end
119
+ end
120
+
121
+ describe "GET /<%= resource_path %>/new" do
122
+ context "with an authenticated admin" do
123
+ let(:authenticated_admin) { create(:user, :admin) }
124
+
125
+ before do
126
+ sign_in(authenticated_admin)
127
+ end
128
+
129
+ it "renders a successful response" do
130
+ get <%= new_helper %>
131
+ expect(response).to be_successful
132
+ end
133
+ end
134
+
135
+ context "with an authenticated user" do
136
+ let(:authenticated_user) { create(:user) }
137
+
138
+ before do
139
+ sign_in(authenticated_user)
140
+ end
141
+
142
+ it "forbids access" do
143
+ get <%= new_helper %>
144
+ expect(response).to redirect_to(<%= index_helper %>_url)
145
+
146
+ follow_redirect!
147
+ expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.new?", default: t("pundit.default")))
148
+ end
149
+ end
150
+
151
+ it "does not allow access to guests" do
152
+ get <%= new_helper %>
153
+ expect(response).to redirect_to(url_for_authentication)
154
+ end
155
+ end
156
+
157
+ describe "GET /<%= resource_path %>/:id/edit" do
158
+ context "with an authenticated admin" do
159
+ let(:authenticated_admin) { create(:user, :admin) }
160
+
161
+ before do
162
+ sign_in(authenticated_admin)
163
+ end
164
+
165
+ it "render a successful response" do
166
+ <%= file_name %> = create(:<%= file_name %>)
167
+ get <%= edit_helper.tr('@','') %>
168
+ expect(response).to be_successful
169
+ end
170
+ end
171
+
172
+ context "with an authenticated user" do
173
+ let(:authenticated_user) { create(:user) }
174
+
175
+ before do
176
+ sign_in(authenticated_user)
177
+ end
178
+
179
+ it "forbids access" do
180
+ <%= file_name %> = create(:<%= file_name %>)
181
+ get <%= edit_helper.tr('@','') %>
182
+ expect(response).to redirect_to(<%= show_helper.tr('@', '') %>)
183
+
184
+ follow_redirect!
185
+ expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.edit?", default: t("pundit.default")))
186
+ end
187
+ end
188
+
189
+ it "does not allow access to guests" do
190
+ <%= file_name %> = create(:<%= file_name %>)
191
+ get <%= edit_helper.tr('@','') %>
192
+ expect(response).to redirect_to(url_for_authentication)
193
+ end
194
+ end
195
+
196
+ describe "POST /<%= resource_path %>" do
197
+ context "with valid parameters" do
198
+ let(:valid_attributes) do
199
+ <%- if attributes.any? -%>
200
+ attributes_for(:<%= ns_file_name %>).slice(<%= permitted_params %>)
201
+ <%- else -%>
202
+ skip("Add a hash of attributes valid for your model")
203
+ <%- end -%>
204
+ end
205
+
206
+ context "with an authenticated admin" do
207
+ let(:authenticated_admin) { create(:user, :admin) }
208
+
209
+ before do
210
+ sign_in(authenticated_admin)
211
+ end
212
+
213
+ it "creates a new <%= class_name %>" do
214
+ expect do
215
+ post <%= index_helper %>_url, params: { <%= ns_file_name %>: valid_attributes }
216
+ end.to change(<%= class_name %>, :count).by(1)
217
+
218
+ <%= file_name %> = <%= class_name %>.last
219
+ <%- if attributes.any? -%>
220
+ <%- attributes.each do |attribute| -%>
221
+ expect(<%= file_name %>.<%= attribute.name %>).to eq(valid_attributes[:<%= attribute.name %>])
222
+ <%- end -%>
223
+ <%- else -%>
224
+ skip("Add assertions for created state")
225
+ <%- end -%>
226
+ end
227
+
228
+ it "redirects to the created <%= ns_file_name %>" do
229
+ post <%= index_helper %>_url, params: { <%= ns_file_name %>: valid_attributes }
230
+ expect(response).to redirect_to(<%= show_helper.gsub("\@#{file_name}", class_name+".last") %>)
231
+ end
232
+ end
233
+
234
+ context "with an authenticated user" do
235
+ let(:authenticated_user) { create(:user) }
236
+
237
+ before do
238
+ sign_in(authenticated_user)
239
+ end
240
+
241
+ it "forbids access" do
242
+ post <%= index_helper %>_url, params: { <%= ns_file_name %>: valid_attributes }
243
+ expect(response).to redirect_to(<%= index_helper %>_url)
244
+
245
+ follow_redirect!
246
+ expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.create?", default: t("pundit.default")))
247
+ end
248
+ end
249
+
250
+ it "does not allow access to guests" do
251
+ post <%= index_helper %>_url, params: { <%= ns_file_name %>: valid_attributes }
252
+ expect(response).to redirect_to(url_for_authentication)
253
+ end
254
+ end
255
+
256
+ context "with invalid parameters" do
257
+ let(:authenticated_admin) { create(:user, :admin) }
258
+
259
+ let(:invalid_attributes) do
260
+ skip("Add a hash of attributes invalid for your model")
261
+ end
262
+
263
+ before do
264
+ sign_in(authenticated_admin)
265
+ end
266
+
267
+ it "does not create a new <%= class_name %>" do
268
+ expect do
269
+ post <%= index_helper %>_url, params: { <%= ns_file_name %>: invalid_attributes }
270
+ end.to change(<%= class_name %>, :count).by(0)
271
+ end
272
+
273
+ it "renders a successful response (i.e. to display the 'new' template)" do
274
+ post <%= index_helper %>_url, params: { <%= ns_file_name %>: invalid_attributes }
275
+ expect(response).to be_successful
276
+ end
277
+ end
278
+ end
279
+
280
+ describe "PATCH /<%= resource_path %>/:id" do
281
+ context "with valid parameters" do
282
+ let(:new_attributes) do
283
+ <%- if attributes.any? -%>
284
+ attributes_for(:<%= ns_file_name %>).slice(<%= permitted_params %>)
285
+ <%- else -%>
286
+ skip("Add a hash of attributes valid for your model")
287
+ <%- end -%>
288
+ end
289
+
290
+ context "with an authenticated admin" do
291
+ let(:authenticated_admin) { create(:user, :admin) }
292
+
293
+ before do
294
+ sign_in(authenticated_admin)
295
+ end
296
+
297
+ it "updates the requested <%= ns_file_name %>" do
298
+ <%= file_name %> = create(:<%= file_name %>)
299
+ patch <%= show_helper.tr('@', '') %>, params: { <%= singular_table_name %>: new_attributes }
300
+
301
+ <%= file_name %>.reload
302
+ <%- if attributes.any? -%>
303
+ <%- attributes.each do |attribute| -%>
304
+ expect(<%= file_name %>.<%= attribute.name %>).to eq(new_attributes[:<%= attribute.name %>])
305
+ <%- end -%>
306
+ <%- else -%>
307
+ skip("Add assertions for updated state")
308
+ <%- end -%>
309
+ end
310
+
311
+ it "redirects to the <%= ns_file_name %>" do
312
+ <%= file_name %> = create(:<%= file_name %>)
313
+ patch <%= show_helper.tr('@', '') %>, params: { <%= singular_table_name %>: new_attributes }
314
+ <%= file_name %>.reload
315
+ expect(response).to redirect_to(<%= singular_table_name %>_url(<%= file_name %>))
316
+ end
317
+ end
318
+
319
+ context "with an authenticated user" do
320
+ let(:authenticated_user) { create(:user) }
321
+
322
+ before do
323
+ sign_in(authenticated_user)
324
+ end
325
+
326
+ it "forbids access" do
327
+ <%= file_name %> = create(:<%= file_name %>)
328
+ patch <%= show_helper.tr('@', '') %>, params: { <%= singular_table_name %>: new_attributes }
329
+ expect(response).to redirect_to(<%= show_helper.tr('@', '') %>)
330
+
331
+ follow_redirect!
332
+ expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.update?", default: t("pundit.default")))
333
+ end
334
+ end
335
+
336
+ it "does not allow access to guests" do
337
+ <%= file_name %> = create(:<%= file_name %>)
338
+ patch <%= show_helper.tr('@', '') %>, params: { <%= singular_table_name %>: new_attributes }
339
+ expect(response).to redirect_to(url_for_authentication)
340
+ end
341
+ end
342
+
343
+ context "with invalid parameters" do
344
+ let(:authenticated_admin) { create(:user, :admin) }
345
+
346
+ let(:invalid_attributes) do
347
+ skip("Add a hash of attributes invalid for your model")
348
+ end
349
+
350
+ before do
351
+ sign_in(authenticated_admin)
352
+ end
353
+
354
+ it "renders a successful response (i.e. to display the 'edit' template)" do
355
+ <%= file_name %> = create(:<%= file_name %>)
356
+ patch <%= show_helper.tr('@', '') %>, params: { <%= singular_table_name %>: invalid_attributes }
357
+ expect(response).to be_successful
358
+ end
359
+ end
360
+ end
361
+
362
+ describe "DELETE /<%= resource_path %>/:id" do
363
+ context "with an authenticated admin" do
364
+ let(:authenticated_admin) { create(:user, :admin) }
365
+
366
+ before do
367
+ sign_in(authenticated_admin)
368
+ end
369
+
370
+ it "destroys the requested <%= ns_file_name %>" do
371
+ <%= file_name %> = create(:<%= file_name %>)
372
+ expect do
373
+ delete <%= show_helper.tr('@', '') %>
374
+ end.to change(<%= class_name %>, :count).by(-1)
375
+ end
376
+
377
+ it "redirects to the <%= table_name %> list" do
378
+ <%= file_name %> = create(:<%= file_name %>)
379
+ delete <%= show_helper.tr('@', '') %>
380
+ expect(response).to redirect_to(<%= index_helper %>_url)
381
+ end
382
+ end
383
+
384
+ context "with an authenticated user" do
385
+ let(:authenticated_user) { create(:user) }
386
+
387
+ before do
388
+ sign_in(authenticated_user)
389
+ end
390
+
391
+ it "forbids access" do
392
+ <%= file_name %> = create(:<%= file_name %>)
393
+ delete <%= show_helper.tr('@', '') %>
394
+ expect(response).to redirect_to(<%= show_helper.tr('@', '') %>)
395
+
396
+ follow_redirect!
397
+ expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.destroy?", default: t("pundit.default")))
398
+ end
399
+ end
400
+
401
+ it "does not allow access to guests" do
402
+ <%= file_name %> = create(:<%= file_name %>)
403
+ delete <%= show_helper.tr('@', '') %>
404
+ expect(response).to redirect_to(url_for_authentication)
405
+ end
406
+ end
407
+ end
408
+ <% end -%>
@@ -0,0 +1,13 @@
1
+ Description:
2
+ Adds security checks and middleware to a Rails app
3
+
4
+ Example:
5
+ rails generate rockstart:security
6
+
7
+ This will create:
8
+ Installs bundler-audit to safely check gems
9
+ Installs brakeman to check for any potential exploits
10
+ Adds rack_attack with a simple policy to block most attacks
11
+ Configures a minimalist Content Security Policy with supporting test coverage
12
+ Ensures test (and unconfigured) environments are using an in-memory cache
13
+ Enforces secure HTTPS connections by defaults
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Rockstart::SecurityGenerator < Rails::Generators::Base
4
+ include Rails::Generators::AppName
5
+ source_root File.expand_path("templates", __dir__)
6
+
7
+ class_option :font_hosts, type: :array,
8
+ desc: "Known third-party hosts for Fonts",
9
+ default: []
10
+
11
+ class_option :image_hosts, type: :array,
12
+ desc: "Known third-party hosts for Images",
13
+ default: []
14
+
15
+ class_option :script_hosts, type: :array,
16
+ desc: "Known third-party hosts for (Java)Scripts",
17
+ default: []
18
+
19
+ class_option :style_hosts, type: :array,
20
+ desc: "Known third-party hosts for Stylesheets",
21
+ default: []
22
+
23
+ class_option :session_name, type: :string,
24
+ desc: "Name used for Rails Sessions",
25
+ default: Rockstart::Env.default_session_name
26
+
27
+ def install_bundler_audit
28
+ gem "bundler-audit", github: "rubysec/bundler-audit"
29
+
30
+ Bundler.clean_system("bundle install --quiet")
31
+
32
+ copy_file "bundler_audit.rake", "lib/tasks/bundler_audit.rake"
33
+ end
34
+
35
+ def install_brakeman
36
+ gem "brakeman", group: %i[development test]
37
+
38
+ Bundler.clean_system("bundle install --quiet")
39
+
40
+ copy_file "brakeman.rake", "lib/tasks/brakeman.rake"
41
+
42
+ append_to_file ".gitignore", "brakeman\n"
43
+ end
44
+
45
+ def add_security_rake_tasks
46
+ copy_file "security.rake", "lib/tasks/security.rake"
47
+ end
48
+
49
+ def install_rack_attack
50
+ gem "rack-attack"
51
+
52
+ Bundler.clean_system("bundle install --quiet")
53
+
54
+ copy_file "rack_attack.rb", "config/initializers/rack_attack.rb"
55
+ copy_file "cache_support.rb", "spec/support/cache.rb"
56
+
57
+ application do
58
+ <<~CACHE
59
+ # Use memory_store cache for testing and default configurations
60
+ config.cache_store = :memory_store
61
+ CACHE
62
+ end
63
+ comment_lines "config/environments/test.rb", "config.cache_store = "
64
+ end
65
+
66
+ def add_session_initializer
67
+ template "session_store_initializer.rb.tt", "config/initializers/session_store.rb"
68
+ end
69
+
70
+ def add_content_security_policy
71
+ template "content_security_policy_initializer.rb.tt",
72
+ "config/initializers/content_security_policy.rb"
73
+
74
+ copy_file "csp_violations_controller.rb", "app/controllers/csp_violations_controller.rb"
75
+ route "resources :csp_violations, only: [:create]"
76
+
77
+ template "content_security_spec.rb.tt", "spec/requests/content_security_spec.rb"
78
+ end
79
+
80
+ def enforce_ssl
81
+ gsub_file "config/environments/production.rb",
82
+ /config.force_ssl = .+$/,
83
+ 'config.force_ssl = ENV["ALLOW_INSECURE_HTTP"].to_i != 1'
84
+ uncomment_lines "config/environments/production.rb", /config.force_ssl =/
85
+ end
86
+
87
+ private
88
+
89
+ def font_hosts
90
+ options[:font_hosts] || []
91
+ end
92
+
93
+ def image_hosts
94
+ options[:image_hosts] || []
95
+ end
96
+
97
+ def script_hosts
98
+ options[:script_hosts] || []
99
+ end
100
+
101
+ def style_hosts
102
+ options[:style_hosts] || []
103
+ end
104
+
105
+ def session_name
106
+ options[:session_name]
107
+ end
108
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc "Run brakeman check on your codebase"
4
+ task :brakeman do
5
+ system "bundle exec brakeman -w 2 -o brakeman"
6
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/audit/task"
4
+ Bundler::Audit::Task.new
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Support for tests that depend on Rails.cache
4
+ module CacheSupport
5
+ def clear_rails_cache
6
+ Rails.cache.clear
7
+ end
8
+ end
9
+
10
+ RSpec.configure do |config|
11
+ config.include CacheSupport
12
+
13
+ config.around(cache_testing: true) do |example|
14
+ clear_rails_cache
15
+ example.run
16
+ clear_rails_cache
17
+ end
18
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Be sure to restart your server when you modify this file.
4
+
5
+ # Define an application-wide content security policy
6
+ # For further information see the following documentation
7
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
8
+
9
+ # Configure any external domains used to host assets here
10
+ csp_connect_sources = []
11
+ csp_font_sources = <%= font_hosts.inspect %>
12
+ csp_image_sources = <%= image_hosts.inspect %>
13
+ csp_script_sources = <%= script_hosts.inspect %>
14
+ csp_style_sources = <%= style_hosts.inspect %>
15
+
16
+ # Allow the asset host to serve assets
17
+ if (asset_host = Rails.application.config.action_controller.asset_host.presence)
18
+ csp_font_sources.append(asset_host)
19
+ csp_image_sources.append(asset_host)
20
+ csp_script_sources.append(asset_host)
21
+ csp_style_sources.append(asset_host)
22
+ end
23
+
24
+ # Allow webpacker to render styles inline
25
+ if Rails.env.development?
26
+ csp_connect_sources += %w[http://localhost:3035 ws://localhost:3035]
27
+ csp_style_sources.append(:unsafe_inline)
28
+ end
29
+
30
+ Rails.application.config.content_security_policy do |policy|
31
+ policy.default_src :none
32
+ policy.connect_src :self, *csp_connect_sources
33
+ policy.font_src :self, *csp_font_sources
34
+ policy.img_src :self, :data, *csp_image_sources
35
+ policy.object_src :none
36
+ policy.script_src :self, *csp_script_sources
37
+ policy.style_src :self, *csp_style_sources
38
+
39
+ # Prevent non secure resources from being loaded
40
+ policy.block_all_mixed_content
41
+ policy.upgrade_insecure_requests
42
+
43
+ # Specify URI for violation reports
44
+ policy.report_uri "/csp_violations"
45
+ end
46
+
47
+ # If you are using UJS then enable automatic nonce generation
48
+ # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
49
+
50
+ # Set the nonce only to specific directives
51
+ # Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
52
+
53
+ # Report CSP violations to a specified URI
54
+ # For further information see the following documentation:
55
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
56
+ # Rails.application.config.content_security_policy_report_only = true