oopsie_exceptions 1.1.0 → 1.1.1

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: a3c1bf7176b541c4c19f09ca56724e8c873e1bae149ac9ff9963a4f4fb4f1156
4
- data.tar.gz: 692fd55d89e3235279b1ba98764f1fbeaeab5e11204c3733674f7a8958956262
3
+ metadata.gz: eeb40f85518ab7f9d4d43e33bb9605206de73f6e3d8fc24979039c39c2c11c02
4
+ data.tar.gz: e075b4c269916b53dfecd103d7a10ec9ca0b6ce99de3b82c37b391fc386d56cf
5
5
  SHA512:
6
- metadata.gz: 0c8a3bfd9907819f35ecd979d114e9784daf71ff7c5b2264246782458463c15271a71319208fe5798de1c11178c7bf643e8bcda693124ed74e7f2a50cc25ef17
7
- data.tar.gz: 9d4f5168d31b516127abc0afa2e894ec02c28cdf076e82f96d555f81c1e38fb3884d3fbca46307572ce2f9749798cf1ace9c4e2fa6d98a6263908c4a695a14a1
6
+ metadata.gz: 71d6c2ce97bae33d43f3d0ad1fee09ede56ca47576c11303a98e663e2046f6719fbd309d1429f6ce375956cff33fefa404a61ddbacf51c82df377b5296929820
7
+ data.tar.gz: 064032a2ec6f0d414bcb24935932a2775a8dbe691ff0b6a1252681bf798b68e347f9e5de8d14fa897d29bca8d22caca262751723343730c62c98eb6c99e15d5d
data/README.md CHANGED
@@ -90,6 +90,8 @@ end
90
90
 
91
91
  Each captured exception is enriched with request URL/method/IP/params/headers, the current user (via `context_builder` or `set_context`), server hostname/PID/Ruby version, and a UTC timestamp.
92
92
 
93
+ Request context capture is best-effort. If Rack rejects a malformed or truncated request body while OopsieExceptions is collecting params, the exception report is still allowed to continue with `request.params` omitted. Payloads include omission metadata such as `params_omitted` / `params_error_class` or `body_omitted` / `body_error_class` when request enrichment fails.
94
+
93
95
  ## Adding context per-request
94
96
 
95
97
  If you don't want to use `context_builder`, you can set context from a controller:
@@ -189,6 +191,14 @@ Return `nil` to drop the notification entirely.
189
191
 
190
192
  ## Upgrading from earlier versions
191
193
 
194
+ ### Malformed request body handling
195
+
196
+ Apps that added a local guard around OopsieExceptions request context collection for malformed multipart or truncated request bodies can remove that workaround after upgrading to a gem release that includes best-effort request params/body capture. Until the fixed gem version is deployed in the app, keep the app-local guard in place.
197
+
198
+ This gem change only prevents OopsieExceptions from turning context enrichment into a new request failure. Production exception groups for the affected app should still be resolved from that app's deploy verification, not from the gem release alone.
199
+
200
+ ### Legacy delivery job cleanup
201
+
192
202
  If you're coming from an older version of the gem that generated `app/jobs/oopsie_exceptions/delivery_job.rb` in your app, **delete that file**. The gem now ships its own `OopsieExceptions::WebhookJob` and the host-app file is obsolete.
193
203
 
194
204
  ```bash
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rack"
3
+ require "rack/request"
4
4
 
5
5
  module OopsieExceptions
6
6
  module Context
@@ -27,38 +27,63 @@ module OopsieExceptions
27
27
  request = Rack::Request.new(env)
28
28
  config = OopsieExceptions.configuration
29
29
 
30
- ctx = {
31
- request: {
32
- url: request.url,
33
- method: request.request_method,
34
- ip: request.ip,
35
- user_agent: env["HTTP_USER_AGENT"],
36
- referer: env["HTTP_REFERER"],
37
- request_id: env["action_dispatch.request_id"] || env["HTTP_X_REQUEST_ID"],
38
- params: sanitize_params(request.params, config),
39
- headers: extract_headers(env, config)
40
- }
30
+ request_context = {
31
+ url: request.url,
32
+ method: request.request_method,
33
+ ip: request.ip,
34
+ user_agent: env["HTTP_USER_AGENT"],
35
+ referer: env["HTTP_REFERER"],
36
+ request_id: env["action_dispatch.request_id"] || env["HTTP_X_REQUEST_ID"],
37
+ headers: extract_headers(env, config)
41
38
  }
42
39
 
43
- if config.capture_request_body && request.content_type&.include?("application/json")
44
- body = request.body.read
45
- request.body.rewind
46
- ctx[:request][:body] = body[0, 10_000] if body && !body.empty?
47
- end
40
+ request_context.merge!(request_params_context(request, config))
41
+ request_context.merge!(request_body_context(request, config))
42
+
43
+ ctx = {
44
+ request: request_context
45
+ }
48
46
 
49
47
  ctx
50
48
  end
51
49
 
52
50
  private
53
51
 
52
+ def request_params_context(request, config)
53
+ { params: sanitize_params(request.params, config) }
54
+ rescue StandardError => error
55
+ rewind_body(request.body)
56
+ {
57
+ params: {},
58
+ params_omitted: true,
59
+ params_error_class: error.class.name
60
+ }
61
+ end
62
+
63
+ def request_body_context(request, config)
64
+ return {} unless config.capture_request_body
65
+ return {} unless request.content_type&.include?("application/json")
66
+
67
+ body_io = request.body
68
+ body = body_io.read
69
+ return {} if body.nil? || body.empty?
70
+
71
+ { body: body[0, 10_000] }
72
+ rescue StandardError => error
73
+ {
74
+ body_omitted: true,
75
+ body_error_class: error.class.name
76
+ }
77
+ ensure
78
+ rewind_body(body_io)
79
+ end
80
+
54
81
  def sanitize_params(params, config)
55
82
  filtered = params.reject { |k, _| k == "controller" || k == "action" }
56
83
  filter_keys = config.filter_parameters
57
84
  filtered.each_with_object({}) do |(k, v), hash|
58
- hash[k] = filter_keys.any? { |f| k.to_s.include?(f) } ? "[FILTERED]" : v
85
+ hash[k] = filter_keys.any? { |f| k.to_s.include?(f.to_s) } ? "[FILTERED]" : v
59
86
  end
60
- rescue
61
- {}
62
87
  end
63
88
 
64
89
  def extract_headers(env, config)
@@ -66,11 +91,17 @@ module OopsieExceptions
66
91
  env.each do |key, value|
67
92
  next unless key.start_with?("HTTP_")
68
93
  header_name = key.sub("HTTP_", "").split("_").map(&:capitalize).join("-")
69
- next if config.filter_headers.any? { |h| h.casecmp(header_name) == 0 }
94
+ next if config.filter_headers.any? { |h| h.to_s.casecmp(header_name) == 0 }
70
95
  headers[header_name] = value
71
96
  end
72
97
  headers
73
98
  end
99
+
100
+ def rewind_body(body_io)
101
+ body_io.rewind if body_io&.respond_to?(:rewind)
102
+ rescue StandardError
103
+ nil
104
+ end
74
105
  end
75
106
  end
76
107
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OopsieExceptions
4
- VERSION = "1.1.0"
4
+ VERSION = "1.1.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oopsie_exceptions
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Troy
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-04-06 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rack
@@ -24,11 +23,24 @@ dependencies:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
25
  version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: minitest
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '5.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '5.0'
27
40
  description: Captures unhandled exceptions from web requests and background jobs,
28
41
  enriches them with request/user/server context, and delivers structured JSON payloads
29
42
  to configurable webhook endpoints. Works with any Rack-based framework; optional
30
43
  Rails integration included.
31
- email:
32
44
  executables: []
33
45
  extensions: []
34
46
  extra_rdoc_files: []
@@ -55,7 +67,6 @@ metadata:
55
67
  source_code_uri: https://github.com/theinventor/oopsie_exceptions
56
68
  changelog_uri: https://github.com/theinventor/oopsie_exceptions/releases
57
69
  rubygems_mfa_required: 'true'
58
- post_install_message:
59
70
  rdoc_options: []
60
71
  require_paths:
61
72
  - lib
@@ -70,8 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
81
  - !ruby/object:Gem::Version
71
82
  version: '0'
72
83
  requirements: []
73
- rubygems_version: 3.5.3
74
- signing_key:
84
+ rubygems_version: 3.6.7
75
85
  specification_version: 4
76
86
  summary: Lightweight exception capture and webhook delivery for Ruby (framework-agnostic)
77
87
  test_files: []