devbench 0.8.0 → 0.8.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: 733817dd1561a76917c0426c041129aa4fefe9200c21ffb462bdaf60c6db801c
4
- data.tar.gz: 9f17ddc4d783f8c666737406df6ddc5c79ff776b299af8b25f2e6d31b43baa9d
3
+ metadata.gz: ec8b8b2c35e830db27e1c4136ea53cc039274d5e12c7947a38918d0ae8084591
4
+ data.tar.gz: '09b1b3a42c848c2fe6391913bcb1ed91592a2c522523bff4a807658ee0e56151'
5
5
  SHA512:
6
- metadata.gz: 82993e99341857d99a11475d9805b1ba7d9dda794e5415a8b27ed121c8eb2f4c8a0ca63df58dcb89b1824a094a8d8f84cd43a1bae7e1d8aaa019e91a44827fda
7
- data.tar.gz: 44038eba715ea44b2423beb8d0c763f7c20430daf272d5e82be7b5e6af1993fbc655160a4d854520d91a27c3e0da6d0185103a5b71fac2092b226cfd9886a839
6
+ metadata.gz: a51bedbbd9b518641a9956170b023dc713395b5da72b722fcb2545019c2697d4883be45cbcafea2d38bfc3dcf975c86a2b5dc94ba717be85fdd957b4ea9ae965
7
+ data.tar.gz: 5421b8ee58441f9b2f57f8c573e66494cd0b4fefd0f64ae6e30efbef08fd5882d1e413b0fbf15388cde8657cb5dc548300130324c3cccc22b19be2455424b954
data/README.md CHANGED
@@ -50,7 +50,7 @@ gem (upgrading the gem upgrades the sensor), with **only the public part**
50
50
  of `DEVBENCH_DSN`:
51
51
 
52
52
  ```html
53
- <script src="https://unpkg.com/devbench@0.8.0/dist/devbench.min.js"
53
+ <script src="https://unpkg.com/devbench@0.8.1/dist/devbench.min.js"
54
54
  data-dsn="https://<public>@adt-ingest.onrender.com" data-release="<release>" defer></script>
55
55
  ```
56
56
 
@@ -126,13 +126,12 @@ module Devbench
126
126
  @lock.synchronize { @window.transform_values { |c| c.n } }
127
127
  end
128
128
 
129
- # rake devbench:test. Sends one synthetic exception straight to ingest
130
- # (not via the background thread), uploads its evidence if asked, and
131
- # prints what happened. Returns true when ingest accepted it.
132
- # The self-test (Devbench.test!): asks ingest whether this DSN's key is
133
- # valid, and for which tenant and environment. Creates nothing — it used
134
- # to send a synthetic exception, which became a real issue on a new
135
- # customer's punch list.
129
+ # The self-test behind `rake devbench:test` (Devbench.test!): POSTs
130
+ # /v1/check straight to ingest (not via the background thread) to ask
131
+ # whether this DSN's key is valid, and for which tenant and environment,
132
+ # and prints the answer. Returns true when ingest accepted the key.
133
+ # Creates nothing — it used to send a synthetic exception, which became a
134
+ # real issue on a new customer's punch list.
136
135
  def self_test(io)
137
136
  io.puts "Dev Bench: checking the DSN against #{@dsn} (service #{@service.inspect})"
138
137
  response, error = attempt(:post, @check_uri, '{}', flush_headers, HTTP_TIMEOUT)
@@ -39,7 +39,9 @@ module Devbench
39
39
  [go_re('\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b', Regexp::IGNORECASE), '<uuid>'],
40
40
  [go_re('\b\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?'), '<ts>'],
41
41
  [go_re('\b\d{4}-\d{2}-\d{2}\b'), '<date>'],
42
- [go_re('\b[^\t\n\f\r <>@]+@[^\t\n\f\r <>@]+\.[A-Za-z]{2,}\b'), '<email>'],
42
+ # The local part stops at `=` and `:` (#148): `customer=sam@x.com`
43
+ # templates to `customer=<email>`. Same class as Scrub's email rule.
44
+ [go_re('\b[^\t\n\f\r <>@=:]+@[^\t\n\f\r <>@]+\.[A-Za-z]{2,}\b'), '<email>'],
43
45
  [go_re("\\b[a-z][a-z0-9+.-]*://#{GO_NOT_WS}+"), '<url>'],
44
46
  [go_re('\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'), '<ip>'],
45
47
  [go_re('\b(?:0x)?[0-9a-fA-F]{8,}\b'), '<hex>'],
@@ -55,9 +57,18 @@ module Devbench
55
57
 
56
58
  DEPENDENCY_FRAGMENTS = [
57
59
  '/node_modules/', '/vendor/', '/gems/', '/ruby/gems/', '/usr/local/go/src/',
58
- '/go/pkg/mod/', '/.bundle/', '<anonymous>'
60
+ '/go/pkg/mod/', '/.bundle/', '<anonymous>',
61
+ # Python (#235): pip packages (site-packages, Debian's dist-packages)
62
+ # and the standard library (lib/python3.X, RHEL's lib64/python3.X);
63
+ # frozen stdlib modules report a file like "<frozen runpy>".
64
+ '/site-packages/', '/dist-packages/', '/lib/python3.', '/lib64/python3.', '<frozen '
59
65
  ].freeze
60
66
 
67
+ # .NET framework frames (#235) ship without PDBs: no file, only a
68
+ # namespace-qualified function. A prefix of the trimmed function, so
69
+ # `Shop.System.Clock.Now` stays in-app.
70
+ DEPENDENCY_FUNCTION_PREFIXES = ['System.', 'Microsoft.'].freeze
71
+
61
72
  ASSET_DIGEST = /\A(.+)[-.]([A-Za-z0-9_]{8,})((?:\.chunk|\.bundle)?\.(?:js|mjs|cjs|css)(?:\.map)?)\z/
62
73
 
63
74
  class << self
@@ -145,7 +156,10 @@ module Devbench
145
156
 
146
157
  def dependency?(file, function)
147
158
  hay = "#{file} #{function}"
148
- DEPENDENCY_FRAGMENTS.any? { |frag| hay.include?(frag) }
159
+ return true if DEPENDENCY_FRAGMENTS.any? { |frag| hay.include?(frag) }
160
+
161
+ fn = trim(function)
162
+ DEPENDENCY_FUNCTION_PREFIXES.any? { |prefix| fn.start_with?(prefix) }
149
163
  end
150
164
 
151
165
  def strip_asset_digest(name)
@@ -148,9 +148,9 @@ module Devbench
148
148
  false
149
149
  end
150
150
 
151
- # The `exception` report for an error, without sending it. Used by
152
- # the self-test (Devbench.test!), which sends one synthetic report
153
- # straight to ingest, built exactly as a real one would be.
151
+ # The `exception` report for an error, built exactly as a real one
152
+ # would be, without sending it. (The self-test no longer uses it: it
153
+ # POSTs /v1/check and sends no report.)
154
154
  def report_for(error, context:, handled:, symbol: nil)
155
155
  exception_payload(error, context, handled, symbol)
156
156
  end
@@ -58,7 +58,9 @@ module Devbench
58
58
  [go_re('\b(password|passwd|pwd|secret|api[_\-]?key|access[_\-]?token|refresh[_\-]?token|' \
59
59
  'client[_\-]?secret|private[_\-]?key|session[_\-]?id|csrf[_\-]?token)' \
60
60
  "(#{WS}*[:=]#{WS}*)(\"[^\"]*\"|'[^']*'|#{NOT_WS}+)", Regexp::IGNORECASE), '\1\2<redacted:secret>'],
61
- [go_re('\b[^\t\n\f\r <>@]+@[^\t\n\f\r <>@]+\.[A-Za-z]{2,}\b'), '<redacted:email>'],
61
+ # The local part stops at `=` and `:` (#148): `customer=sam@x.com`
62
+ # keeps its key. Same class as the template's email rule.
63
+ [go_re('\b[^\t\n\f\r <>@=:]+@[^\t\n\f\r <>@]+\.[A-Za-z]{2,}\b'), '<redacted:email>'],
62
64
  [go_re('\b\d{3}-\d{2}-\d{4}\b'), '<redacted:ssn>'],
63
65
  # The card and phone rules (#186) judge each candidate in the block.
64
66
  [CARD_CANDIDATE, ->(m) { card?(m[0]) ? CARD : m[0] }],
@@ -14,9 +14,9 @@ module Devbench
14
14
  # This must agree byte for byte with the Go SDK, because a token minted by a
15
15
  # Rails app is verified by ingest, which uses the Go SDK's VerifySession.
16
16
  # Divergence produces a request that succeeds locally and is rejected in
17
- # production, with nothing to say why. There are no shared testdata/ vectors
18
- # for session tokens yet: test/session_test.rb pins known-answer tokens the Go
19
- # SDK minted, and sdk/server/go/session_kat_test.go pins the same table.
17
+ # production, with nothing to say why. Both SDKs are held to the shared
18
+ # vectors in testdata/session_vectors.json (#181, #563): test/session_test.rb
19
+ # here, sdk/server/go/session_kat_test.go there.
20
20
  module Session
21
21
  HEADER = 'x-adt-session'
22
22
  META_NAME = 'adt-session'
@@ -63,7 +63,9 @@ module Devbench
63
63
  raise Invalid if secret.nil? || secret.strip.empty?
64
64
  raise Invalid unless token.is_a?(String)
65
65
 
66
- parts = token.strip.split('.')
66
+ # -1 keeps trailing empty fields, as Go's strings.Split does: without
67
+ # it "adts1.<payload>.<sig>." is three parts here and four in ingest.
68
+ parts = token.strip.split('.', -1)
67
69
  raise Invalid unless parts.length == 3 && parts[0] == PREFIX
68
70
 
69
71
  # Constant time: a signature check that leaks timing can be brute-forced
@@ -111,10 +113,16 @@ module Devbench
111
113
  Base64.urlsafe_encode64(bytes, padding: false)
112
114
  end
113
115
 
116
+ # Decodes as Go's base64.RawURLEncoding does, and refuses what it refuses:
117
+ # padding (=), the standard alphabet's + and /, whitespace, and a length
118
+ # that leaves one character over. (Base64.urlsafe_decode64 accepts the
119
+ # first two, so a padded payload verified here but not in ingest, #563.)
120
+ # Like Go, non-zero bits left over in the last character are ignored.
114
121
  def self.unb64(text)
115
- Base64.urlsafe_decode64(text)
116
- rescue ArgumentError
117
- nil
122
+ return nil unless text.match?(/\A[A-Za-z0-9_-]*\z/) && text.length % 4 != 1
123
+
124
+ std = text.tr('-_', '+/')
125
+ (std + ('=' * ((4 - (std.length % 4)) % 4))).unpack1('m')
118
126
  end
119
127
  end
120
128
  end
@@ -133,10 +133,10 @@ module Devbench
133
133
  false
134
134
  end
135
135
 
136
- # Sends one synthetic exception straight to Dev Bench and prints the
137
- # result, so an operator can check the DSN without waiting for a flush
138
- # (`rake devbench:test` under Rails). Returns true when ingest accepted
139
- # it. Never raises.
136
+ # Asks ingest whether the DSN's key is valid (`POST /v1/check`) and prints
137
+ # the answer, so an operator can check the DSN without waiting for a
138
+ # flush (`rake devbench:test` under Rails). Creates nothing: no event,
139
+ # no issue. Returns true when ingest accepted the key. Never raises.
140
140
  def test!(io: $stdout)
141
141
  cfg = config
142
142
  unless cfg.enabled?
@@ -4,7 +4,7 @@ module Devbench
4
4
  # The gem's release version. Each SDK has its own version and release tag;
5
5
  # the Ruby release (tag ruby-vX.Y.Z in the public repo) checks this equals
6
6
  # the tag. The wire format is PROTOCOL_VERSION below.
7
- VERSION = '0.8.0'
7
+ VERSION = '0.8.1'
8
8
 
9
9
  # This SDK's name in every flush's `sdk` field: the key
10
10
  # testdata/protocol_vectors.json `sdk.names` gives sdk/server/ruby
@@ -12,7 +12,7 @@ module Devbench
12
12
  #
13
13
  # renders
14
14
  #
15
- # <script src="https://unpkg.com/devbench@0.8.0/dist/devbench.min.js"
15
+ # <script src="https://unpkg.com/devbench@0.8.1/dist/devbench.min.js"
16
16
  # data-dsn="https://<public>@<host>" data-release="<release>" defer></script>
17
17
  #
18
18
  # The DSN is the public part of DEVBENCH_DSN only (Devbench.browser_dsn),
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devbench
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dev Bench
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-13 00:00:00.000000000 Z
11
+ date: 2026-10-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Reports unhandled exceptions, Rails.error reports, failed ActiveJob and