reputable 0.1.22 → 0.1.23
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/Gemfile.lock +1 -1
- data/lib/reputable/middleware.rb +25 -81
- data/lib/reputable/reputation.rb +2 -3
- data/lib/reputable/version.rb +1 -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: 1f667ca542120eb294a6c61a0650a1c1deb82b5383c87e8d744c98c9fba7548a
|
|
4
|
+
data.tar.gz: d061fbe002a8ae3c96bbbf45b7470f5df4bdac89f49bcb36f42ab1e676ee5699
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0f47bb9c880009d89a36d71ef0021edd07de2b87fb38ab5625625b89d7ad0240391fbc5ff4e5630e12bc7fafc4290a9649aec9685c25cfca5c28546e8e9699f6
|
|
7
|
+
data.tar.gz: 7223bdf677132850ccb4ef226c8540dad07fba22853c9461873a696b3c16f52a65fc06eba1757aeb121607470baea291e027db7364a19ee9c505a92bbec1b49d
|
data/Gemfile.lock
CHANGED
data/lib/reputable/middleware.rb
CHANGED
|
@@ -71,11 +71,6 @@ module Reputable
|
|
|
71
71
|
env["reputable.ip"] = ip
|
|
72
72
|
path = env["PATH_INFO"] || "/"
|
|
73
73
|
|
|
74
|
-
if Reputable.verbose?
|
|
75
|
-
Reputable.logger&.info("[Reputable] request: #{env['REQUEST_METHOD']} #{path} ip=#{ip} rid=#{env['reputable.request_id']}")
|
|
76
|
-
Reputable.logger&.info("[Reputable] state: enabled=#{Reputable.enabled?} operational=#{Reputable.operational?} gate=#{@reputation_gate} track=#{track_request_enabled?}")
|
|
77
|
-
end
|
|
78
|
-
|
|
79
74
|
# Check for verification return parameters and verify signature if present
|
|
80
75
|
handle_verification_return(env)
|
|
81
76
|
|
|
@@ -99,28 +94,25 @@ module Reputable
|
|
|
99
94
|
# All tracking is wrapped in rescue to ensure it never fails
|
|
100
95
|
safe_track_request(env)
|
|
101
96
|
|
|
97
|
+
# Single summary line for requests that passed through without intervention
|
|
98
|
+
if Reputable.verbose?
|
|
99
|
+
rep_status = env["reputable.reputation_status"]
|
|
100
|
+
Reputable.logger&.info("[Reputable] #{env['REQUEST_METHOD']} #{path} ip=#{ip} status=#{rep_status || 'unknown'} gate=#{@reputation_gate} result=pass")
|
|
101
|
+
end
|
|
102
|
+
|
|
102
103
|
[status, headers, response]
|
|
103
104
|
end
|
|
104
105
|
|
|
105
106
|
private
|
|
106
107
|
|
|
107
108
|
def safe_reputation_gate(env)
|
|
108
|
-
unless @reputation_gate
|
|
109
|
-
|
|
110
|
-
return nil
|
|
111
|
-
end
|
|
112
|
-
unless Reputable.enabled?
|
|
113
|
-
Reputable.logger&.info("[Reputable] gate: skipped (disabled)") if Reputable.verbose?
|
|
114
|
-
return nil
|
|
115
|
-
end
|
|
109
|
+
return nil unless @reputation_gate
|
|
110
|
+
return nil unless Reputable.enabled?
|
|
116
111
|
unless Reputable.operational?
|
|
117
|
-
Reputable.logger&.
|
|
118
|
-
return nil
|
|
119
|
-
end
|
|
120
|
-
if skip_request?(env)
|
|
121
|
-
Reputable.logger&.info("[Reputable] gate: skipped (path excluded)") if Reputable.verbose?
|
|
112
|
+
Reputable.logger&.warn("[Reputable] gate: skipped (not operational, circuit_breaker=#{Connection.circuit_open? ? 'open' : 'closed'})") if Reputable.verbose?
|
|
122
113
|
return nil
|
|
123
114
|
end
|
|
115
|
+
return nil if skip_request?(env)
|
|
124
116
|
|
|
125
117
|
enforce_reputation_gate(env)
|
|
126
118
|
rescue StandardError => e
|
|
@@ -133,25 +125,19 @@ module Reputable
|
|
|
133
125
|
status = reputation_status(env)
|
|
134
126
|
ip = env["reputable.ip"] || extract_ip(env)
|
|
135
127
|
|
|
136
|
-
if Reputable.verbose?
|
|
137
|
-
Reputable.logger&.info("[Reputable] gate: ip=#{ip} status=#{status.inspect}")
|
|
138
|
-
end
|
|
139
|
-
|
|
140
128
|
return nil if status.nil?
|
|
141
129
|
|
|
142
130
|
case status
|
|
143
131
|
when "untrusted_block"
|
|
144
|
-
Reputable.logger&.info("[Reputable] gate: BLOCKING ip=#{ip} action=#{@block_action}")
|
|
132
|
+
Reputable.logger&.info("[Reputable] gate: BLOCKING ip=#{ip} action=#{@block_action}")
|
|
145
133
|
handle_block_action(env, ip)
|
|
146
134
|
when "untrusted_challenge"
|
|
147
135
|
if verified_session?(env)
|
|
148
|
-
Reputable.logger&.info("[Reputable] gate: ip=#{ip} has untrusted_challenge but session is verified, passing through") if Reputable.verbose?
|
|
149
136
|
return nil
|
|
150
137
|
end
|
|
151
|
-
Reputable.logger&.info("[Reputable] gate: CHALLENGING ip=#{ip} action=#{@challenge_action}")
|
|
138
|
+
Reputable.logger&.info("[Reputable] gate: CHALLENGING ip=#{ip} action=#{@challenge_action}")
|
|
152
139
|
handle_challenge_action(env)
|
|
153
140
|
else
|
|
154
|
-
Reputable.logger&.info("[Reputable] gate: ip=#{ip} status=#{status} — allowing") if Reputable.verbose?
|
|
155
141
|
nil
|
|
156
142
|
end
|
|
157
143
|
end
|
|
@@ -189,17 +175,12 @@ module Reputable
|
|
|
189
175
|
query = request.query_string
|
|
190
176
|
return unless query.include?("reputable_r") || query.include?("reputable_status")
|
|
191
177
|
|
|
192
|
-
Reputable.logger&.info("[Reputable] verification return detected in query string") if Reputable.verbose?
|
|
193
|
-
|
|
194
178
|
params = request.params
|
|
195
179
|
|
|
196
180
|
# Determine status from new format or legacy format
|
|
197
181
|
status = if params["reputable_r"]
|
|
198
|
-
|
|
199
|
-
Reputable.logger&.info("[Reputable] verification: new format decoded=#{decoded.inspect}") if Reputable.verbose?
|
|
200
|
-
decoded&.dig("status")
|
|
182
|
+
Reputable.decode_reputable_response(params)&.dig("status")
|
|
201
183
|
else
|
|
202
|
-
Reputable.logger&.info("[Reputable] verification: legacy format status=#{params['reputable_status']}") if Reputable.verbose?
|
|
203
184
|
params["reputable_status"]
|
|
204
185
|
end
|
|
205
186
|
|
|
@@ -209,7 +190,6 @@ module Reputable
|
|
|
209
190
|
end
|
|
210
191
|
|
|
211
192
|
sig_valid = Reputable.verify_redirect_return(params)
|
|
212
|
-
Reputable.logger&.info("[Reputable] verification: signature_valid=#{sig_valid}") if Reputable.verbose?
|
|
213
193
|
|
|
214
194
|
if sig_valid
|
|
215
195
|
env["reputable.verified"] = true
|
|
@@ -230,10 +210,9 @@ module Reputable
|
|
|
230
210
|
if env["rack.session"]
|
|
231
211
|
env["rack.session"]["reputable_verified"] = true
|
|
232
212
|
env["rack.session"]["reputable_verified_at"] = Time.now.to_i
|
|
233
|
-
Reputable.logger&.info("[Reputable] verification: stored in session, verified=true") if Reputable.verbose?
|
|
234
|
-
else
|
|
235
|
-
Reputable.logger&.info("[Reputable] verification: no rack.session available, cannot persist") if Reputable.verbose?
|
|
236
213
|
end
|
|
214
|
+
|
|
215
|
+
Reputable.logger&.info("[Reputable] verification: pass, stored in session") if Reputable.verbose?
|
|
237
216
|
else
|
|
238
217
|
Reputable.logger&.warn("[Reputable] verification: INVALID signature, rejecting")
|
|
239
218
|
end
|
|
@@ -258,20 +237,16 @@ module Reputable
|
|
|
258
237
|
def handle_block_action(env, ip)
|
|
259
238
|
case @block_action
|
|
260
239
|
when :blocked_page
|
|
261
|
-
Reputable.logger&.info("[Reputable] block: rendering inline blocked page for ip=#{ip}") if Reputable.verbose?
|
|
262
240
|
build_blocked_page_response(ip)
|
|
263
241
|
when :blocked_page_remote
|
|
264
242
|
url = resolve_blocked_redirect_url(env) || Reputable.blocked_page_url
|
|
265
|
-
Reputable.logger&.info("[Reputable] block: REDIRECTING #{@blocked_redirect_status} → #{url}")
|
|
243
|
+
Reputable.logger&.info("[Reputable] block: REDIRECTING #{@blocked_redirect_status} → #{url}")
|
|
266
244
|
redirect_response(url, @blocked_redirect_status)
|
|
267
245
|
when :forbidden
|
|
268
|
-
Reputable.logger&.info("[Reputable] block: returning 403 for ip=#{ip}") if Reputable.verbose?
|
|
269
246
|
[403, { "Content-Type" => "text/plain; charset=utf-8" }, ["Forbidden"]]
|
|
270
247
|
when Proc
|
|
271
|
-
Reputable.logger&.info("[Reputable] block: calling custom proc for ip=#{ip}") if Reputable.verbose?
|
|
272
248
|
@block_action.call(env, ip)
|
|
273
249
|
else
|
|
274
|
-
Reputable.logger&.info("[Reputable] block: rendering default blocked page for ip=#{ip}") if Reputable.verbose?
|
|
275
250
|
build_blocked_page_response(ip)
|
|
276
251
|
end
|
|
277
252
|
end
|
|
@@ -281,7 +256,7 @@ module Reputable
|
|
|
281
256
|
when :verify
|
|
282
257
|
keys = Reputable.configuration.trusted_keys
|
|
283
258
|
if keys.nil? || keys.empty?
|
|
284
|
-
Reputable.logger&.
|
|
259
|
+
Reputable.logger&.warn("[Reputable] challenge: no trusted_keys configured, cannot redirect to verification")
|
|
285
260
|
return nil
|
|
286
261
|
end
|
|
287
262
|
|
|
@@ -290,10 +265,6 @@ module Reputable
|
|
|
290
265
|
failure_url = build_verification_failure_url(request)
|
|
291
266
|
session_id = resolve_session_id(env)
|
|
292
267
|
|
|
293
|
-
if Reputable.verbose?
|
|
294
|
-
Reputable.logger&.info("[Reputable] challenge: building verification URL return_url=#{return_url} failure_url=#{failure_url} session_id=#{session_id}")
|
|
295
|
-
end
|
|
296
|
-
|
|
297
268
|
redirect_url = Reputable.verification_url(
|
|
298
269
|
return_url: return_url,
|
|
299
270
|
failure_url: failure_url,
|
|
@@ -301,15 +272,11 @@ module Reputable
|
|
|
301
272
|
force_challenge: @verification_force_challenge
|
|
302
273
|
)
|
|
303
274
|
|
|
304
|
-
if redirect_url == return_url
|
|
305
|
-
Reputable.logger&.info("[Reputable] challenge: verification_url == return_url, skipping redirect") if Reputable.verbose?
|
|
306
|
-
return nil
|
|
307
|
-
end
|
|
275
|
+
return nil if redirect_url == return_url
|
|
308
276
|
|
|
309
|
-
Reputable.logger&.info("[Reputable] challenge: REDIRECTING #{@challenge_redirect_status} → #{redirect_url}")
|
|
277
|
+
Reputable.logger&.info("[Reputable] challenge: REDIRECTING #{@challenge_redirect_status} → #{redirect_url}")
|
|
310
278
|
redirect_response(redirect_url, @challenge_redirect_status)
|
|
311
279
|
when Proc
|
|
312
|
-
Reputable.logger&.info("[Reputable] challenge: calling custom proc") if Reputable.verbose?
|
|
313
280
|
@challenge_action.call(env)
|
|
314
281
|
else
|
|
315
282
|
nil
|
|
@@ -353,27 +320,14 @@ module Reputable
|
|
|
353
320
|
end
|
|
354
321
|
|
|
355
322
|
def verified_session?(env)
|
|
356
|
-
if env["reputable.verified"]
|
|
357
|
-
Reputable.logger&.info("[Reputable] verified_session?: true (env flag set this request)") if Reputable.verbose?
|
|
358
|
-
return true
|
|
359
|
-
end
|
|
323
|
+
return true if env["reputable.verified"]
|
|
360
324
|
|
|
361
325
|
session = env["rack.session"]
|
|
362
|
-
unless session
|
|
363
|
-
Reputable.logger&.info("[Reputable] verified_session?: false (no rack.session)") if Reputable.verbose?
|
|
364
|
-
return false
|
|
365
|
-
end
|
|
326
|
+
return false unless session
|
|
366
327
|
|
|
367
|
-
|
|
328
|
+
@verified_session_keys.any? do |key|
|
|
368
329
|
session[key] || session[key.to_s]
|
|
369
330
|
end
|
|
370
|
-
|
|
371
|
-
if Reputable.verbose?
|
|
372
|
-
keys_state = @verified_session_keys.map { |k| "#{k}=#{session[k] || session[k.to_s]}" }.join(" ")
|
|
373
|
-
Reputable.logger&.info("[Reputable] verified_session?: #{found} (#{keys_state})")
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
found
|
|
377
331
|
rescue StandardError
|
|
378
332
|
false
|
|
379
333
|
end
|
|
@@ -496,27 +450,17 @@ module Reputable
|
|
|
496
450
|
# X-Forwarded-For and Forwarded can contain multiple IPs
|
|
497
451
|
if header == "HTTP_X_FORWARDED_FOR"
|
|
498
452
|
ip = extract_from_xff(value)
|
|
499
|
-
if ip
|
|
500
|
-
Reputable.logger&.info("[Reputable] extract_ip: #{ip} (from #{header}: #{value})") if Reputable.verbose?
|
|
501
|
-
return ip
|
|
502
|
-
end
|
|
453
|
+
return ip if ip
|
|
503
454
|
elsif header == "HTTP_FORWARDED"
|
|
504
455
|
ip = extract_from_forwarded(value)
|
|
505
|
-
if ip
|
|
506
|
-
Reputable.logger&.info("[Reputable] extract_ip: #{ip} (from #{header}: #{value})") if Reputable.verbose?
|
|
507
|
-
return ip
|
|
508
|
-
end
|
|
456
|
+
return ip if ip
|
|
509
457
|
else
|
|
510
458
|
# Single IP headers
|
|
511
459
|
ip = value.to_s.strip
|
|
512
|
-
unless ip.empty?
|
|
513
|
-
Reputable.logger&.info("[Reputable] extract_ip: #{ip} (from #{header})") if Reputable.verbose?
|
|
514
|
-
return ip
|
|
515
|
-
end
|
|
460
|
+
return ip unless ip.empty?
|
|
516
461
|
end
|
|
517
462
|
end
|
|
518
463
|
|
|
519
|
-
Reputable.logger&.info("[Reputable] extract_ip: 0.0.0.0 (no headers matched)") if Reputable.verbose?
|
|
520
464
|
"0.0.0.0"
|
|
521
465
|
rescue StandardError
|
|
522
466
|
"0.0.0.0"
|
data/lib/reputable/reputation.rb
CHANGED
|
@@ -133,21 +133,20 @@ module Reputable
|
|
|
133
133
|
return nil unless valid_entity_type?(entity_type)
|
|
134
134
|
|
|
135
135
|
key = "reputation:#{entity_type}:#{entity_id}"
|
|
136
|
-
Reputable.logger&.info("[Reputable] lookup: key=#{key}") if Reputable.verbose?
|
|
137
136
|
|
|
138
137
|
data = Connection.safe_with(default: nil, context: "reputation_lookup") do |redis|
|
|
139
138
|
redis.hgetall(key)
|
|
140
139
|
end
|
|
141
140
|
|
|
142
141
|
if data.nil? || data.empty?
|
|
143
|
-
Reputable.logger&.
|
|
142
|
+
Reputable.logger&.debug("[Reputable] lookup: #{key} → (none)") if Reputable.verbose?
|
|
144
143
|
return nil
|
|
145
144
|
end
|
|
146
145
|
|
|
147
146
|
# Check if expired (Redis TTL should handle this, but double-check)
|
|
148
147
|
expires_at = data["expires_at"].to_i
|
|
149
148
|
if expires_at > 0 && expires_at < (Time.now.to_f * 1000).to_i
|
|
150
|
-
Reputable.logger&.
|
|
149
|
+
Reputable.logger&.debug("[Reputable] lookup: #{key} → expired") if Reputable.verbose?
|
|
151
150
|
return nil
|
|
152
151
|
end
|
|
153
152
|
|
data/lib/reputable/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: reputable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.23
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Reputable
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-02-
|
|
11
|
+
date: 2026-02-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: redis
|