ez_logs_agent 0.1.8 → 0.1.10

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: d6be395fe0a765025430e03a92081b53d9ae1925df029543cd0785f334bd41b7
4
- data.tar.gz: 6bcf0dc0e0a93731513463753031161adf6a96ba433d1647d20614162bf5a5ae
3
+ metadata.gz: 2a7479b12ee7dd0814929516f0474d7e05e393410d86a1c6d56117c854945809
4
+ data.tar.gz: 54af5890799ca5614373dae0e66d1b422b3abab3dee7d9d0f95c7f23039ffdd7
5
5
  SHA512:
6
- metadata.gz: 8addaf44c89ada3706be7711c763700338064a296c29d9011361d86ed087b87327662567ec803f8296d6df2fedbca626ee564bc8c8b2090dddb38cc0f6332b8e
7
- data.tar.gz: e6226450a0391438452c6d9811b76470ec431a4ed5c9046588e6608643c0c29cabd6168ada7ffa7d07f2bf0d211d6994ee712a10858c9372a0ecd71b8bc7d11a
6
+ metadata.gz: a68790e445b19ba92f2df2d0733fe6ae7e6d9c43f2ff854eed01fced091cb7d62aa1c0377429c44d3250abc515d5a1bb45056f1087eb0e2b8d491461fdd94843
7
+ data.tar.gz: f4208dc88f566f28e1ec24288533468fb3f99d268a77d4f3030da19fc4fc77fe8e36a39052516ddc94def977f03012d8bc157fccb319161a06d4889f52cd37e5
data/CHANGELOG.md CHANGED
@@ -2,6 +2,42 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.1.10] — 2026-06-05
6
+
7
+ ### Fixed
8
+ - `Sanitizer` no longer collapses ActiveJob keyword-argument hashes
9
+ (those tagged with `_aj_ruby2_keywords`) to `"[Object]"` at the
10
+ depth-3 cap. ActionMailer puts kwargs at two wrapper layers — an
11
+ outer `{"args" => [kwargs_hash], "_aj_ruby2_keywords" => ["args"]}`
12
+ payload and the kwargs hash itself, also marked. Each layer is
13
+ framework noise; the depth budget now skips them so real kwargs
14
+ survive the wire (e.g. `CompanyMailer.deleted(admin_email:, ...)`
15
+ now ships `admin_email`/`company_name`/`deleted_at` instead of a
16
+ single `"[Object]"`).
17
+
18
+ The carve-out is narrow: only hashes that actually carry the
19
+ `_aj_ruby2_keywords` marker are exempt. Customer-data hashes
20
+ without the marker still hit the depth cap unchanged, and
21
+ sensitive-key filtering (passwords, tokens, …) still runs on the
22
+ real kwargs entries — the wrapper is free to descend into, but
23
+ nothing inside it is exempt from masking. No wire-format change.
24
+
25
+ ## [0.1.9] — 2026-06-05
26
+
27
+ ### Fixed
28
+ - `Sanitizer` no longer collapses ActiveJob record references
29
+ (`{"_aj_globalid" => "gid://app/Model/id"}`) to `"[Object]"` at the
30
+ depth-3 cap. These one-key wrapper hashes pass through verbatim so
31
+ the server can display *which* record a job ran on
32
+ (e.g. ActionMailer's `Record 1` field now reads `User #42` instead
33
+ of `[Object]`).
34
+
35
+ The carve-out is narrow: only the exact `{"_aj_globalid" => "gid://..."}`
36
+ shape is exempt. Multi-key hashes, non-GID values, and any other
37
+ nested structure still hit the existing graph-protection rules
38
+ (depth cap, array truncation, non-primitive collapse) unchanged. No
39
+ wire-format change.
40
+
5
41
  ## [0.1.8] — 2026-05-29
6
42
 
7
43
  ### Changed
@@ -96,6 +96,23 @@ module EzLogsAgent
96
96
  private
97
97
 
98
98
  def sanitize_nested_object(hash, depth)
99
+ # ActiveJob serializes record refs as `{"_aj_globalid" => "gid://..."}`.
100
+ # Preserve these even past the depth limit so a record reference
101
+ # survives the [Object] collapse and the server can display WHICH
102
+ # record the job ran on. Display formatting (gid → "User #42") is
103
+ # the server's job; the agent's job is to keep the data on the wire.
104
+ return hash if global_id_hash?(hash)
105
+
106
+ # ActiveJob wraps keyword args at TWO layers: the outer mailer
107
+ # payload `{"args" => [kwargs_hash], "_aj_ruby2_keywords" => ["args"]}`
108
+ # and the inner kwargs hash itself, also tagged `_aj_ruby2_keywords`.
109
+ # Each wrapper layer is framework noise — recursing into it without
110
+ # spending depth budget keeps the real kwargs from collapsing to
111
+ # "[Object]" at depth 3. Sensitive-key filtering still runs on the
112
+ # real entries (passwords don't leak; the wrapper just doesn't cost
113
+ # a depth level).
114
+ return sanitize_ruby2_keywords_wrapper(hash, depth) if ruby2_keywords_wrapper?(hash)
115
+
99
116
  return "[Object]" if depth >= MAX_NESTING_DEPTH
100
117
  return {} if hash.empty?
101
118
 
@@ -104,6 +121,24 @@ module EzLogsAgent
104
121
  end
105
122
  end
106
123
 
124
+ # Sanitize the entries of an _aj_ruby2_keywords wrapper at the SAME
125
+ # depth as the wrapper itself, then re-attach the marker. This is what
126
+ # lets kwargs survive past the depth-3 cap when they're wrapped at
127
+ # depth 2-3 by the outer mailer payload.
128
+ def sanitize_ruby2_keywords_wrapper(hash, depth)
129
+ result = {}
130
+ hash.each do |key, value|
131
+ if ruby2_keywords_marker_key?(key)
132
+ # Preserve the marker verbatim — it's used to identify the
133
+ # wrapper on the receiving side, not to display.
134
+ result[key] = value
135
+ else
136
+ result[key] = sanitize_value(key, value, depth)
137
+ end
138
+ end
139
+ result
140
+ end
141
+
107
142
  def sanitize_array_value(array, depth)
108
143
  return [] if array.empty?
109
144
 
@@ -145,6 +180,31 @@ module EzLogsAgent
145
180
  value.is_a?(TrueClass) ||
146
181
  value.is_a?(FalseClass)
147
182
  end
183
+
184
+ # True iff `hash` is the exact one-key shape ActiveJob uses to serialize
185
+ # an ActiveRecord (or any GlobalID::Identification) argument:
186
+ # `{"_aj_globalid" => "gid://..."}`. Used to exempt these hashes from
187
+ # the depth-limit collapse so the wire still carries the record ref.
188
+ def global_id_hash?(hash)
189
+ return false unless hash.is_a?(Hash) && hash.size == 1
190
+ gid = hash["_aj_globalid"] || hash[:_aj_globalid]
191
+ gid.is_a?(String) && gid.start_with?("gid://")
192
+ end
193
+
194
+ # True iff `hash` carries the `_aj_ruby2_keywords` marker. ActiveJob
195
+ # uses this marker on any hash that originated as a keyword-argument
196
+ # splat (so the framework can re-splat on deserialize). Used to skip
197
+ # the depth penalty for these framework wrappers — the marker's
198
+ # presence is the unambiguous signal we're inside an ActiveJob
199
+ # serialization layer, not customer data.
200
+ def ruby2_keywords_wrapper?(hash)
201
+ return false unless hash.is_a?(Hash)
202
+ hash.key?("_aj_ruby2_keywords") || hash.key?(:_aj_ruby2_keywords)
203
+ end
204
+
205
+ def ruby2_keywords_marker_key?(key)
206
+ key.to_s == "_aj_ruby2_keywords"
207
+ end
148
208
  end
149
209
  end
150
210
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EzLogsAgent
4
- VERSION = "0.1.8"
4
+ VERSION = "0.1.10"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ez_logs_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - dezsirazvan
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-05-29 00:00:00.000000000 Z
10
+ date: 2026-06-05 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: request_store