glib-web 5.0.5 → 5.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f17a1a4daae7e40a97af1832e97b2d8c9dd67310f5c633cbdeb4ab3dd5d5d46a
4
- data.tar.gz: 669a156ca671210d00fb34383fc99bdfe9089e69470709d733b4a686922b4291
3
+ metadata.gz: 2e9d0b6eb1a02d147f08a0166a4d67075ebbf903af3601e78ab592c5f9a11808
4
+ data.tar.gz: ee0d8980dfa1158fa65126b8d29dad29e2b788f1b51e4fb062a2de306a1d789a
5
5
  SHA512:
6
- metadata.gz: e707bdf53ababa1afe4b53fa4a8d6d0f6c7bf4db2730a18fc576cf58fd7c7d16e3081a98a488c18a5c8d6ee953edaa44c4a8c2f4e53840ae367f44d735435e2d
7
- data.tar.gz: f88d6a7ddec4a1c364396579a3b56eaaf82e98dd32a16a5d2f0595c0ea0d79dbfa63ab94df4ccefefd47aa6a4b12b601793a67aed5d51d22ee8c2670ed8993c0
6
+ metadata.gz: 21960f43365ee50457f2ad2856d5bf5e6cf6c1a5014332006e9fe43e7f2e6cec9633a35cb5678a34388310d88e69222ab4641d1267125e87e01d822d00fb7e4f
7
+ data.tar.gz: cf920f5f56177c092fc2bd84bacf4b096d1b41ae4364b47bcfe3beba1b58bb8afa76883a33210a9f9ad709085c7dc33644933128cddbe93b4ff2251e6f2367b5
@@ -38,7 +38,16 @@ module Glib::Analytics
38
38
  referrer_url = request.headers['referer'] # Notice that the HTTP header uses one "r"
39
39
  if group.nil? && action.nil? && !referrer_url.nil?
40
40
  current_host = request.host
41
- referrer_host = URI.parse(referrer_url).host
41
+ # The Referer header is attacker-controlled and may not be a valid URI
42
+ # (scanners send junk like "() { ignored; }; cat /etc/passwd"). Treat an
43
+ # unparseable referer as "no referer" rather than letting URI::InvalidURIError
44
+ # bubble up mid-render, where the 500 rescue then double-renders.
45
+ referrer_host =
46
+ begin
47
+ URI.parse(referrer_url).host
48
+ rescue URI::InvalidURIError
49
+ nil
50
+ end
42
51
 
43
52
  # Replace the subdomain portion with regex so it will only match the non-subdomain part.
44
53
  # This will allow cross-subdomain referral, but it will not work if the host is a bare domain,
@@ -249,6 +249,13 @@ module Glib::Json::Libs
249
249
  Rollbar.error(exception, use_exception_level_filters: true)
250
250
  end
251
251
 
252
+ # The exception may have been raised AFTER a response was already rendered
253
+ # (e.g. from an after_action injection). Rendering the error page on top of that
254
+ # would raise AbstractController::DoubleRenderError, which masks the genuine
255
+ # exception reported above. The real cause is already logged, so leave the
256
+ # already-rendered response in place rather than double-rendering.
257
+ return if performed?
258
+
252
259
  render file: Rails.root.join('public', '500.html'), status: :internal_server_error
253
260
  else
254
261
  raise exception
@@ -261,6 +268,12 @@ module Glib::Json::Libs
261
268
  def glib_json_handle_404(exception, preview_mode)
262
269
  if json_ui_activated?
263
270
  if Rails.env.production? || preview_mode
271
+ # As with glib_json_handle_500: if a response was already rendered (e.g. an
272
+ # after_action raised RecordNotFound), rendering the 404 page on top would raise
273
+ # AbstractController::DoubleRenderError and mask the real exception. Keep the
274
+ # already-rendered response instead of double-rendering.
275
+ return if performed?
276
+
264
277
  render file: Rails.root.join('public', '404.html'), status: :not_found
265
278
  else
266
279
  raise exception
@@ -8,11 +8,7 @@ module Glib
8
8
 
9
9
  module ClassMethods
10
10
  def generate_preview_tests
11
- project_root = Rails.root
12
- paths = Dir.glob(project_root + 'test/mailers/previews/*')
13
- paths.each do |file|
14
- require file
15
- end
11
+ Glib::MailerTester.load_preview_files
16
12
 
17
13
  ActionMailer::Preview.subclasses.each do |preview_class|
18
14
  preview = preview_class.new
@@ -45,6 +41,95 @@ module Glib
45
41
  end
46
42
  end
47
43
  end
44
+
45
+ # Opt-in companion to `generate_preview_tests`. That method only snapshot-tests
46
+ # the previews that EXIST; this asserts every mailer ACTION has a matching
47
+ # preview, so a new mailer/action can't silently ship with no preview.
48
+ #
49
+ # `except:` allowlists "Mailer#action" strings that intentionally have no
50
+ # preview — document the reason at the call site.
51
+ def assert_all_mailer_actions_have_previews(except: [])
52
+ test 'every mailer action has a preview' do
53
+ missing = Glib::MailerTester.missing_previews(except: except)
54
+ assert missing.empty?, Glib::MailerTester.missing_previews_message(missing)
55
+ end
56
+ end
57
+ end
58
+
59
+ # --- Preview coverage (companion to generate_preview_tests) ---------------
60
+
61
+ # Loads every preview file (idempotent — `require` no-ops if already loaded).
62
+ def self.load_preview_files
63
+ Dir.glob(Rails.root + 'test/mailers/previews/*').each { |file| require file }
64
+ end
65
+
66
+ # Sorted Array<String> of "Mailer#action" entries that have no preview.
67
+ def self.missing_previews(except: [])
68
+ load_preview_files
69
+ compute_missing_previews(
70
+ mailer_actions: reflect_mailer_actions,
71
+ previewed_actions: ->(mailer) { reflect_preview_methods(mailer) },
72
+ except: except
73
+ )
74
+ end
75
+
76
+ # Pure (no Rails reflection) so it is unit-testable.
77
+ # mailer_actions: { "FooMailer" => ["welcome", "goodbye"] }
78
+ # previewed_actions: ->(mailer_name) { ["welcome"] }
79
+ # except: ["FooMailer#goodbye"]
80
+ def self.compute_missing_previews(mailer_actions:, previewed_actions:, except: [])
81
+ excluded = except.map(&:to_s).to_set
82
+ mailer_actions.flat_map do |mailer, actions|
83
+ previewed = Array(previewed_actions.call(mailer)).map(&:to_s).to_set
84
+ actions.map(&:to_s)
85
+ .reject { |action| previewed.include?(action) }
86
+ .map { |action| "#{mailer}##{action}" }
87
+ end.reject { |id| excluded.include?(id) }.sort
88
+ end
89
+
90
+ def self.missing_previews_message(missing)
91
+ <<~MSG
92
+ Every mailer action must have a preview so `generate_preview_tests` snapshot-tests it.
93
+ These actions have none:
94
+
95
+ #{missing.join("\n ")}
96
+
97
+ Add a method named after the action to the matching `<Mailer>Preview` class under
98
+ test/mailers/previews/, returning `<Mailer>.<action>(...)`. If an action genuinely
99
+ cannot be previewed, allowlist it with a reason:
100
+
101
+ assert_all_mailer_actions_have_previews(except: ['FooMailer#some_action']) # why
102
+ MSG
103
+ end
104
+
105
+ # Reflection glue (needs a loaded Rails app). Scopes to app mailers under
106
+ # `ApplicationMailer` so third-party mailers (Devise, etc.) are not flagged.
107
+ def self.reflect_mailer_actions
108
+ load_mailer_files
109
+ base = '::ApplicationMailer'.safe_constantize || ActionMailer::Base
110
+ base.descendants.each_with_object({}) do |mailer, acc|
111
+ actions = mailer.action_methods.to_a
112
+ acc[mailer.name] = actions unless actions.empty?
113
+ end
114
+ end
115
+
116
+ # Loads only `app/mailers` (so every mailer subclass is defined) rather than
117
+ # eager-loading the whole app — faster, and avoids unrelated load failures.
118
+ def self.load_mailer_files
119
+ dir = Rails.root.join('app/mailers')
120
+ return unless dir.exist?
121
+
122
+ loader = Rails.respond_to?(:autoloaders) ? Rails.autoloaders.main : nil
123
+ if loader.respond_to?(:eager_load_dir)
124
+ loader.eager_load_dir(dir.to_s)
125
+ else
126
+ Rails.application.eager_load!
127
+ end
128
+ end
129
+
130
+ def self.reflect_preview_methods(mailer_name)
131
+ preview = "#{mailer_name}Preview".safe_constantize
132
+ preview ? preview.instance_methods(false).map(&:to_s) : []
48
133
  end
49
134
 
50
135
  def log_root_dir
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glib-web
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.5
4
+ version: 5.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''