realuptime-errors 0.1.0 → 0.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 +4 -4
- data/README.md +8 -3
- data/lib/realuptime/errors/rails.rb +1 -1
- data/lib/realuptime/errors/scrub.rb +46 -9
- data/lib/realuptime/errors/version.rb +6 -1
- data/lib/realuptime/errors.rb +2 -2
- data/scrub-vectors.json +185 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 288a63586cd55e7e41a173f368ab8321080037f6d91e7e67075088121d4b59bd
|
|
4
|
+
data.tar.gz: 1e2e73b36ea00e394abe1b1111249e130da6f9ef6a17fbf1715a9426bfb1278c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5efbe30a69ed3b6b0a895bfe281d24cd4bb62ae251d591b7d931eb54069f5e1057e2b49f92da398b0684dd4ba356ac3e8ae4b30bb2a198d9e2ed5dfcab456691
|
|
7
|
+
data.tar.gz: 3f9a1392ae7b296ff85149dcc1e175cb8689d1e2460e37e1206b33c330040b840b759897131cc7feae4890533f55b52f3242dea2a4c2f758a28003748d4672ef
|
data/README.md
CHANGED
|
@@ -11,10 +11,15 @@ they merge upstream.
|
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Installable from the package registry (primary) or straight from this mirror's git history (e.g. to pin an exact commit).
|
|
15
15
|
|
|
16
16
|
```ruby
|
|
17
17
|
# Gemfile
|
|
18
|
+
gem "realuptime-errors"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
# or, straight from this mirror
|
|
18
23
|
gem "realuptime-errors", git: "https://github.com/RealUptimeHQ/realuptime-errors-ruby"
|
|
19
24
|
```
|
|
20
25
|
|
|
@@ -32,7 +37,7 @@ Realuptime::Errors.init(
|
|
|
32
37
|
Realuptime::Errors.capture_exception(some_error)
|
|
33
38
|
```
|
|
34
39
|
|
|
35
|
-
Rails: `gem "realuptime-errors",
|
|
40
|
+
Rails: `gem "realuptime-errors", require: "realuptime/errors/rails"`,
|
|
36
41
|
then set `Rails.application.config.realuptime_errors.dsn` (or
|
|
37
42
|
`ENV["REALUPTIME_ERRORS_DSN"]`); the Railtie installs the Rack middleware
|
|
38
43
|
and the ActiveJob hook.
|
|
@@ -86,7 +91,7 @@ ruby -Ilib -Itest -e 'Dir["test/*_test.rb"].each { |f| require "./#{f}" }'
|
|
|
86
91
|
|
|
87
92
|
## Version
|
|
88
93
|
|
|
89
|
-
This mirror tracks SDK_VERSION `0.1.
|
|
94
|
+
This mirror tracks SDK_VERSION `0.1.1` in `lib/realuptime/errors/version.rb`,
|
|
90
95
|
the string every event actually carries on the wire.
|
|
91
96
|
|
|
92
97
|
## License
|
|
@@ -13,7 +13,7 @@ module Realuptime
|
|
|
13
13
|
# gem "realuptime-errors", require: "realuptime/errors/rails"
|
|
14
14
|
#
|
|
15
15
|
# # config/initializers/realuptime_errors.rb (or ENV REALUPTIME_ERRORS_DSN)
|
|
16
|
-
# Rails.application.config.realuptime_errors.dsn = "https://realuptime.io/api/errors/v1/ingest/rue_..."
|
|
16
|
+
# Rails.application.config.realuptime_errors.dsn = "https://ingest.realuptime.io/api/errors/v1/ingest/rue_..."
|
|
17
17
|
# Rails.application.config.realuptime_errors.release = ENV["GIT_SHA"]
|
|
18
18
|
#
|
|
19
19
|
# What the Railtie installs, all through the ONE capture/scrub/transport
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Realuptime
|
|
4
4
|
module Errors
|
|
5
|
-
# PII scrub-by-default, client-side (docs
|
|
5
|
+
# PII scrub-by-default, client-side (the internal-docs repo's errors-plan.md, "PII
|
|
6
6
|
# scrub-by-default, specified"). A byte-for-byte port of
|
|
7
7
|
# packages/errors-js/scrub.ts and packages/errors-py/realuptime_errors.py's
|
|
8
8
|
# scrub half; the shared vectors (scrub-vectors.json, vendored into this
|
|
@@ -30,13 +30,41 @@ module Realuptime
|
|
|
30
30
|
REMOVED_USER_FIELDS = %w[user.email user.username].freeze
|
|
31
31
|
|
|
32
32
|
JWT_RE = /\beyJ[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}\b/.freeze
|
|
33
|
-
PREFIXED_KEY_RE = /\b(?:sk|pk|rk|ghp|gho|ghs|xox[a-z]|rua|rue|ru_live)_[A-Za-z0-9_-]{8,}/.freeze
|
|
33
|
+
PREFIXED_KEY_RE = /\b(?:sk|pk|rk|ghp|gho|ghs|xox[a-z]|rua|rue|ru_live|whsec)_[A-Za-z0-9_-]{8,}/.freeze
|
|
34
|
+
AWS_KEY_RE = /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/.freeze
|
|
34
35
|
CARD_RE = /(?<!\d)(?:\d[ -]?){12,18}\d(?!\d)/.freeze
|
|
35
36
|
HEX_RE = /\b[0-9a-fA-F]{32,}\b/.freeze
|
|
36
37
|
BASE64_RE = /(?<![A-Za-z0-9+_=-])[A-Za-z0-9+_-]{40,}={0,2}/.freeze
|
|
38
|
+
URL_CREDS_RE = %r{\b([a-zA-Z][a-zA-Z0-9+.-]*)://[^\s/@]+:[^\s/@]+@}.freeze
|
|
39
|
+
|
|
40
|
+
# Key-name denylist (REA-474): see packages/errors-js/scrub.ts's module
|
|
41
|
+
# doc for the full rationale. Matched against whole camelCase/
|
|
42
|
+
# snake_case segments (and contiguous joins of them) so "tokenizer"
|
|
43
|
+
# and "authentic" never match "token" / "auth".
|
|
44
|
+
KEY_DENYLIST = %w[
|
|
45
|
+
password passwd secret token apikey auth authorization
|
|
46
|
+
credential credentials privatekey accesskey session
|
|
47
|
+
].freeze
|
|
37
48
|
|
|
38
49
|
module_function
|
|
39
50
|
|
|
51
|
+
def key_segments(key)
|
|
52
|
+
spaced = key.to_s.gsub(/([a-z0-9])([A-Z])/, '\1_\2')
|
|
53
|
+
spaced.split(/[^a-zA-Z0-9]+/).reject(&:empty?).map(&:downcase)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def key_denylisted?(key)
|
|
57
|
+
segments = key_segments(key)
|
|
58
|
+
segments.each_index do |i|
|
|
59
|
+
joined = +""
|
|
60
|
+
(i...segments.length).each do |j|
|
|
61
|
+
joined << segments[j]
|
|
62
|
+
return true if KEY_DENYLIST.include?(joined)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
false
|
|
66
|
+
end
|
|
67
|
+
|
|
40
68
|
def luhn_valid?(digits)
|
|
41
69
|
total = 0
|
|
42
70
|
double = false
|
|
@@ -52,10 +80,12 @@ module Realuptime
|
|
|
52
80
|
(total % 10).zero?
|
|
53
81
|
end
|
|
54
82
|
|
|
55
|
-
# The
|
|
83
|
+
# The pattern rules over one string, in the shared order. Pure.
|
|
56
84
|
def scrub_string(value)
|
|
57
|
-
out = value.gsub(
|
|
85
|
+
out = value.gsub(URL_CREDS_RE) { "#{Regexp.last_match(1)}://#{SCRUBBED}@" }
|
|
86
|
+
out = out.gsub(JWT_RE, SCRUBBED)
|
|
58
87
|
out = out.gsub(PREFIXED_KEY_RE, SCRUBBED)
|
|
88
|
+
out = out.gsub(AWS_KEY_RE, SCRUBBED)
|
|
59
89
|
out = out.gsub(CARD_RE) do |run|
|
|
60
90
|
digits = run.delete(" -")
|
|
61
91
|
digits.length >= 13 && digits.length <= 19 && luhn_valid?(digits) ? SCRUBBED : run
|
|
@@ -64,14 +94,21 @@ module Realuptime
|
|
|
64
94
|
out.gsub(BASE64_RE, SCRUBBED)
|
|
65
95
|
end
|
|
66
96
|
|
|
67
|
-
# A flat string map (tags, custom context, device, frame locals):
|
|
68
|
-
#
|
|
69
|
-
# the
|
|
97
|
+
# A flat string map (tags, custom context, device, frame locals): a
|
|
98
|
+
# key denylisted by name (REA-474) replaces the value whole,
|
|
99
|
+
# regardless of shape; otherwise the value passes the pattern rules.
|
|
100
|
+
# Keys themselves are always left alone.
|
|
70
101
|
def scrub_string_map(value)
|
|
71
102
|
return value unless value.is_a?(Hash)
|
|
72
103
|
|
|
73
104
|
value.each_with_object({}) do |(name, entry), out|
|
|
74
|
-
out[name] = entry.is_a?(String)
|
|
105
|
+
out[name] = if !entry.is_a?(String)
|
|
106
|
+
entry
|
|
107
|
+
elsif key_denylisted?(name)
|
|
108
|
+
SCRUBBED
|
|
109
|
+
else
|
|
110
|
+
scrub_string(entry)
|
|
111
|
+
end
|
|
75
112
|
end
|
|
76
113
|
end
|
|
77
114
|
|
|
@@ -134,7 +171,7 @@ module Realuptime
|
|
|
134
171
|
if headers.is_a?(Hash)
|
|
135
172
|
request["headers"] = headers.each_with_object({}) do |(name, value), scrubbed|
|
|
136
173
|
lower = name.to_s.downcase
|
|
137
|
-
scrubbed[name] = if REMOVED_HEADERS.include?(lower) && !allowed.include?(lower)
|
|
174
|
+
scrubbed[name] = if (REMOVED_HEADERS.include?(lower) || key_denylisted?(lower)) && !allowed.include?(lower)
|
|
138
175
|
SCRUBBED
|
|
139
176
|
else
|
|
140
177
|
value.is_a?(String) ? scrub_string(value) : value
|
|
@@ -5,6 +5,11 @@ module Realuptime
|
|
|
5
5
|
SDK_NAME = "realuptime-errors-ruby"
|
|
6
6
|
# The string every batch carries on the wire ("sdk" field); the gemspec
|
|
7
7
|
# and the public mirror's version both read this constant.
|
|
8
|
-
|
|
8
|
+
# REA-567: bumped from 0.1.0 (the version currently live on RubyGems)
|
|
9
|
+
# so a republish carries the key-name redaction pass (REA-474) and the
|
|
10
|
+
# ingest.realuptime.io host change (REA-473) that landed after 0.1.0
|
|
11
|
+
# shipped. No feature was added, so this is a patch bump, not a minor
|
|
12
|
+
# one.
|
|
13
|
+
SDK_VERSION = "0.1.1"
|
|
9
14
|
end
|
|
10
15
|
end
|
data/lib/realuptime/errors.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# realuptime-errors: zero-dependency Ruby error tracking SDK for RealUptime
|
|
4
|
-
# Errors (docs
|
|
4
|
+
# Errors (the internal-docs repo's errors-plan.md, Linear REA-255).
|
|
5
5
|
#
|
|
6
6
|
# One wire contract with the JS and Python SDKs (packages/errors-js/types.ts);
|
|
7
7
|
# the shared scrub vectors (scrub-vectors.json, vendored into this gem and
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
# Usage:
|
|
19
19
|
#
|
|
20
20
|
# require "realuptime/errors"
|
|
21
|
-
# Realuptime::Errors.init(dsn: "https://realuptime.io/api/errors/v1/ingest/rue_...",
|
|
21
|
+
# Realuptime::Errors.init(dsn: "https://ingest.realuptime.io/api/errors/v1/ingest/rue_...",
|
|
22
22
|
# release: "v2.4.1", environment: "production")
|
|
23
23
|
# ...
|
|
24
24
|
# Realuptime::Errors.capture_exception(error)
|
data/scrub-vectors.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"comment": "Shared PII-scrub test vectors for the Errors SDKs and the server-side second net (docs
|
|
2
|
+
"comment": "Shared PII-scrub test vectors for the Errors SDKs and the server-side second net (the internal-docs repo's errors-plan.md, 'The SDKs: two languages, one contract'). Consumed by packages/errors-js/scrub.test.ts, packages/errors-py/tests/test_scrub.py, and packages/db/error-scrub.test.ts. All three implementations must produce byte-identical output for every vector; a semantic change here is a wire-contract change and needs all three updated in one PR. The server's second net applies the PATTERN rules only: FIELD REMOVAL (removed headers, and v2's user.email / user.username) and allowFields are client-side duties -- a field that reaches the server was either never in the removal set or was deliberately opted back in, and the server cannot tell which. Vectors where the server's output therefore differs carry expectedServer; absent it, the server expects `expected`. Vectors named 'v2 ...' cover the REA-182 additions (identity, tags, custom context, device metadata, matched route, frame locals); every v1 vector above them is unchanged, which is itself part of the additive guarantee.",
|
|
3
3
|
"version": 2,
|
|
4
4
|
"vectors": [
|
|
5
5
|
{
|
|
@@ -501,6 +501,190 @@
|
|
|
501
501
|
"request": { "method": "GET", "path": "/orders/8842", "route": "/orders/:id", "status": 500 },
|
|
502
502
|
"device": { "runtime": "node", "runtimeVersion": "22.4.0" }
|
|
503
503
|
}
|
|
504
|
+
},
|
|
505
|
+
|
|
506
|
+
{
|
|
507
|
+
"name": "REA-474 key-name denylist: context password key redacted regardless of value shape",
|
|
508
|
+
"event": {
|
|
509
|
+
"message": "boom",
|
|
510
|
+
"context": { "db_password": "hunter2", "cartTotal": "49.99" }
|
|
511
|
+
},
|
|
512
|
+
"expected": {
|
|
513
|
+
"message": "boom",
|
|
514
|
+
"context": { "db_password": "[scrubbed]", "cartTotal": "49.99" }
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
"name": "REA-474 key-name denylist: tag named secret redacted regardless of value shape",
|
|
519
|
+
"event": {
|
|
520
|
+
"message": "boom",
|
|
521
|
+
"tags": { "client_secret": "not-shaped-like-anything", "plan": "growth" }
|
|
522
|
+
},
|
|
523
|
+
"expected": {
|
|
524
|
+
"message": "boom",
|
|
525
|
+
"tags": { "client_secret": "[scrubbed]", "plan": "growth" }
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
"name": "REA-474 key-name denylist: camelCase apiKey in frame vars redacted",
|
|
530
|
+
"event": {
|
|
531
|
+
"message": "boom",
|
|
532
|
+
"frames": [
|
|
533
|
+
{
|
|
534
|
+
"file": "app/billing.py",
|
|
535
|
+
"function": "charge",
|
|
536
|
+
"line": 12,
|
|
537
|
+
"inApp": true,
|
|
538
|
+
"vars": { "apiKey": "not-shaped-like-anything", "attempt": "3" }
|
|
539
|
+
}
|
|
540
|
+
]
|
|
541
|
+
},
|
|
542
|
+
"expected": {
|
|
543
|
+
"message": "boom",
|
|
544
|
+
"frames": [
|
|
545
|
+
{
|
|
546
|
+
"file": "app/billing.py",
|
|
547
|
+
"function": "charge",
|
|
548
|
+
"line": 12,
|
|
549
|
+
"inApp": true,
|
|
550
|
+
"vars": { "apiKey": "[scrubbed]", "attempt": "3" }
|
|
551
|
+
}
|
|
552
|
+
]
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
"name": "REA-474 key-name denylist: private_key and access_key in custom context redacted (device is a closed shape in some SDKs, so this uses context instead)",
|
|
557
|
+
"event": {
|
|
558
|
+
"message": "boom",
|
|
559
|
+
"context": { "private_key": "not-shaped", "access_key": "not-shaped", "cartTotal": "49.99" }
|
|
560
|
+
},
|
|
561
|
+
"expected": {
|
|
562
|
+
"message": "boom",
|
|
563
|
+
"context": { "private_key": "[scrubbed]", "access_key": "[scrubbed]", "cartTotal": "49.99" }
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
"name": "REA-474 key-name denylist: breadcrumb data session key redacted",
|
|
568
|
+
"event": {
|
|
569
|
+
"message": "boom",
|
|
570
|
+
"breadcrumbs": [
|
|
571
|
+
{
|
|
572
|
+
"timestamp": null,
|
|
573
|
+
"category": "auth",
|
|
574
|
+
"message": "login attempt",
|
|
575
|
+
"data": { "session": "not-shaped-like-anything", "outcome": "ok" }
|
|
576
|
+
}
|
|
577
|
+
]
|
|
578
|
+
},
|
|
579
|
+
"expected": {
|
|
580
|
+
"message": "boom",
|
|
581
|
+
"breadcrumbs": [
|
|
582
|
+
{
|
|
583
|
+
"timestamp": null,
|
|
584
|
+
"category": "auth",
|
|
585
|
+
"message": "login attempt",
|
|
586
|
+
"data": { "session": "[scrubbed]", "outcome": "ok" }
|
|
587
|
+
}
|
|
588
|
+
]
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
"name": "REA-474 key-name denylist: near-miss key names are NOT redacted (tokenizer, authentic)",
|
|
593
|
+
"event": {
|
|
594
|
+
"message": "boom",
|
|
595
|
+
"context": { "tokenizer": "gpt2", "authentic": "yes" }
|
|
596
|
+
},
|
|
597
|
+
"expected": {
|
|
598
|
+
"message": "boom",
|
|
599
|
+
"context": { "tokenizer": "gpt2", "authentic": "yes" }
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
"name": "REA-474 key-name denylist: a header named by key alone (x-auth-token) is removed client-side; the server has no name-based removal so it stays (value fails the pattern rules)",
|
|
604
|
+
"event": {
|
|
605
|
+
"message": "boom",
|
|
606
|
+
"request": {
|
|
607
|
+
"method": "GET",
|
|
608
|
+
"path": "/v1/charge",
|
|
609
|
+
"status": 500,
|
|
610
|
+
"headers": { "x-auth-token": "not-shaped-like-anything", "x-request-id": "req-1" }
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
"expected": {
|
|
614
|
+
"message": "boom",
|
|
615
|
+
"request": {
|
|
616
|
+
"method": "GET",
|
|
617
|
+
"path": "/v1/charge",
|
|
618
|
+
"status": 500,
|
|
619
|
+
"headers": { "x-auth-token": "[scrubbed]", "x-request-id": "req-1" }
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
"expectedServer": {
|
|
623
|
+
"message": "boom",
|
|
624
|
+
"request": {
|
|
625
|
+
"method": "GET",
|
|
626
|
+
"path": "/v1/charge",
|
|
627
|
+
"status": 500,
|
|
628
|
+
"headers": { "x-auth-token": "not-shaped-like-anything", "x-request-id": "req-1" }
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
"name": "REA-474 key-name denylist: allowFields opts a denylisted header name back in, same as an explicitly removed header",
|
|
634
|
+
"allowFields": ["x-auth-token"],
|
|
635
|
+
"event": {
|
|
636
|
+
"message": "boom",
|
|
637
|
+
"request": {
|
|
638
|
+
"method": "GET",
|
|
639
|
+
"path": "/v1/charge",
|
|
640
|
+
"status": 500,
|
|
641
|
+
"headers": { "x-auth-token": "not-shaped-like-anything" }
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
"expected": {
|
|
645
|
+
"message": "boom",
|
|
646
|
+
"request": {
|
|
647
|
+
"method": "GET",
|
|
648
|
+
"path": "/v1/charge",
|
|
649
|
+
"status": 500,
|
|
650
|
+
"headers": { "x-auth-token": "not-shaped-like-anything" }
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
"name": "REA-474 value shape: AWS access key id (AKIA) is scrubbed",
|
|
656
|
+
"event": { "message": "leaked creds AKIAIOSFODNN7EXAMPLE in log" },
|
|
657
|
+
"expected": { "message": "leaked creds [scrubbed] in log" }
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
"name": "REA-474 value shape: AWS temporary access key id (ASIA) is scrubbed",
|
|
661
|
+
"event": { "message": "leaked creds ASIAIOSFODNN7EXAMPLE in log" },
|
|
662
|
+
"expected": { "message": "leaked creds [scrubbed] in log" }
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
"name": "REA-474 value shape: a short AKIA-prefixed run stays (not the 16-char shape)",
|
|
666
|
+
"event": { "message": "AKIA123 is not a real key" },
|
|
667
|
+
"expected": { "message": "AKIA123 is not a real key" }
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
"name": "REA-474 value shape: Stripe webhook signing secret (whsec_) is scrubbed",
|
|
671
|
+
"event": { "message": "signature check failed for whsec_abcdefgh12345678" },
|
|
672
|
+
"expected": { "message": "signature check failed for [scrubbed]" }
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
"name": "REA-474 value shape: a postgres connection string's userinfo is scrubbed; host stays readable",
|
|
676
|
+
"event": { "message": "connect failed: postgres://dbuser:hunter2@db.internal:5432/app" },
|
|
677
|
+
"expected": { "message": "connect failed: postgres://[scrubbed]@db.internal:5432/app" }
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
"name": "REA-474 value shape: an https URL with userinfo credentials is scrubbed; host and path stay",
|
|
681
|
+
"event": { "message": "fetch failed for https://svc:s3cr3t-pass@api.example.com/v1/orders" },
|
|
682
|
+
"expected": { "message": "fetch failed for https://[scrubbed]@api.example.com/v1/orders" }
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
"name": "REA-474 value shape: a plain URL with no userinfo is left alone",
|
|
686
|
+
"event": { "message": "fetch failed for https://api.example.com/v1/orders" },
|
|
687
|
+
"expected": { "message": "fetch failed for https://api.example.com/v1/orders" }
|
|
504
688
|
}
|
|
505
689
|
]
|
|
506
690
|
}
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: realuptime-errors
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- RealUptime
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Captures exceptions with scrub-by-default PII handling, bounded buffering
|
|
14
14
|
with honest drop counters, and framework integrations for Rails, Rack and Sidekiq.
|