Module: Reputable
- Defined in:
- lib/reputable.rb,
lib/reputable/rails.rb,
lib/reputable/verify.rb,
lib/reputable/signals.rb,
lib/reputable/tracker.rb,
lib/reputable/version.rb,
lib/reputable/connection.rb,
lib/reputable/middleware.rb,
lib/reputable/reputation.rb,
lib/reputable/measurement.rb,
lib/reputable/blocked_page.rb,
lib/reputable/configuration.rb
Overview
Reputable - Bot detection and reputation scoring client
RESILIENCE: This gem is designed to fail silently and never break your app.
- All methods return safe defaults (false/nil) on any error
- Can be disabled via REPUTABLE_ENABLED=false
- Circuit breaker prevents connection storms
- All Redis timeouts are configurable via ENV
Defined Under Namespace
Modules: BlockedPage, Measurement, Rails, Reputation, Signals, Tracker, Verify
Classes: Configuration, ConfigurationError, Connection, ConnectionError, Error, Middleware
Constant Summary
collapse
- VERSION =
"0.1.26"
Class Attribute Summary collapse
Class Method Summary
collapse
-
.apply_reputation(entity_type:, entity_id:, status:, **options) ⇒ Object
Delegate reputation methods to Reputation module.
-
.block_asn(asn, reason: "manual_block", ttl: nil, **metadata) ⇒ Object
-
.block_ip(ip, reason: "manual_block", **metadata) ⇒ Object
-
.blocked_asn?(asn) ⇒ Boolean
-
.blocked_ip?(ip) ⇒ Boolean
-
.blocked_page_url ⇒ String
Build the hosted blocked page URL.
-
.challenge_asn(asn, reason: "suspicious_traffic", ttl: nil, **metadata) ⇒ Object
-
.challenge_ip(ip, reason: "suspicious_activity", **metadata) ⇒ Object
-
.challenged_asn?(asn) ⇒ Boolean
-
.challenged_ip?(ip) ⇒ Boolean
-
.configuration ⇒ Object
-
.configure {|configuration| ... } ⇒ Object
-
.decode_reputable_response(params) ⇒ Hash?
Decode the new reputable_r payload.
-
.disabled? ⇒ Boolean
Check if Reputable is disabled.
-
.enabled? ⇒ Boolean
Check if Reputable is enabled Can be disabled via REPUTABLE_ENABLED=false environment variable.
-
.ignore_asn(asn, reason: "datacenter_traffic", ttl: nil, **metadata) ⇒ Object
-
.ignore_ip(ip, reason: "internal_traffic", **metadata) ⇒ Object
-
.lookup_asn(asn) ⇒ Object
Delegate ASN reputation methods to Reputation module.
-
.lookup_ip(ip) ⇒ Object
-
.lookup_reputation(entity_type, entity_id) ⇒ Object
Delegate lookup methods to Reputation module (O(1) Redis lookups).
-
.measurement_collect_url(token) ⇒ Object
-
.measurement_redirect_url(token) ⇒ Object
-
.measurement_token(**kwargs) ⇒ Object
-
.operational? ⇒ Boolean
Check if Reputable is operational (enabled AND circuit breaker is closed) Use this to guard redirect/gating decisions — when the circuit is open, the Reputable server is likely unreachable and redirects would fail.
-
.proof_challenge_passed?(proof, **kwargs) ⇒ Boolean
Legacy alias from the proof era.
-
.reset! ⇒ Object
-
.signals_check(**kwargs) ⇒ Object
Bot Signals server API: signal report for an end user's request.
-
.signals_redirect_url(**kwargs) ⇒ Object
Bot Signals redirect-flow URL (publishable key + registered return URL).
-
.status ⇒ Hash
Get a detailed status report for monitoring/debugging.
-
.ticket_challenge_passed?(ticket, **kwargs) ⇒ Boolean
Did the ticket clear a CAPTCHA challenge? Convenience predicate.
-
.track_request(ip:, path:, **options) ⇒ Object
Delegate tracking methods to Tracker.
-
.track_request_async(ip:, path:, **options) ⇒ Object
-
.track_security_event(**kwargs) ⇒ Object
-
.track_security_event_async(**kwargs) ⇒ Object
-
.trust_asn(asn, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **metadata) ⇒ Object
-
.trust_ip(ip, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **metadata) ⇒ Object
-
.trusted_asn?(asn) ⇒ Boolean
-
.trusted_ip?(ip) ⇒ Boolean
-
.verbose? ⇒ Boolean
Check if verbose logging is enabled.
-
.verification_url(return_url:, failure_url: nil, session_id: nil, force_challenge: false) ⇒ String
Generate a signed verification URL.
-
.verify_measurement_result(token, **kwargs) ⇒ Object
-
.verify_measurement_result_token(token, **kwargs) ⇒ Object
-
.verify_proof(proof, **kwargs) ⇒ Object
Legacy alias from the proof era.
-
.verify_redirect_return(params) ⇒ Boolean
Verify the signature of a redirect return Supports both new format (reputable_r + reputable_s) and legacy format.
-
.verify_ticket(ticket, **kwargs) ⇒ Object
Bot Signals: verify the signed ticket submitted by the widget.
-
.verify_ticket_claims(ticket, **kwargs) ⇒ Object
Bot Signals: verify and return all signed ticket claims.
Class Attribute Details
.logger ⇒ Object
Returns the value of attribute logger.
52
53
54
|
# File 'lib/reputable.rb', line 52
def logger
@logger
end
|
Class Method Details
.apply_reputation(entity_type:, entity_id:, status:, **options) ⇒ Object
Delegate reputation methods to Reputation module
127
128
129
|
# File 'lib/reputable.rb', line 127
def apply_reputation(entity_type:, entity_id:, status:, **options)
Reputation.apply(entity_type: entity_type, entity_id: entity_id, status: status, **options)
end
|
.block_asn(asn, reason: "manual_block", ttl: nil, **metadata) ⇒ Object
189
190
191
|
# File 'lib/reputable.rb', line 189
def block_asn(asn, reason: "manual_block", ttl: nil, **metadata)
Reputation.block_asn(asn, reason: reason, ttl: ttl, **metadata)
end
|
.block_ip(ip, reason: "manual_block", **metadata) ⇒ Object
135
136
137
|
# File 'lib/reputable.rb', line 135
def block_ip(ip, reason: "manual_block", **metadata)
Reputation.block_ip(ip, reason: reason, **metadata)
end
|
.blocked_asn?(asn) ⇒ Boolean
.blocked_ip?(ip) ⇒ Boolean
.blocked_page_url ⇒ String
Build the hosted blocked page URL
307
308
309
310
311
|
# File 'lib/reputable.rb', line 307
def blocked_page_url
base_url = configuration.base_url
base_url = base_url.chomp("/")
"#{base_url}/_reputable/verify/blocked"
end
|
.challenge_asn(asn, reason: "suspicious_traffic", ttl: nil, **metadata) ⇒ Object
193
194
195
|
# File 'lib/reputable.rb', line 193
def challenge_asn(asn, reason: "suspicious_traffic", ttl: nil, **metadata)
Reputation.challenge_asn(asn, reason: reason, ttl: ttl, **metadata)
end
|
.challenge_ip(ip, reason: "suspicious_activity", **metadata) ⇒ Object
139
140
141
|
# File 'lib/reputable.rb', line 139
def challenge_ip(ip, reason: "suspicious_activity", **metadata)
Reputation.challenge_ip(ip, reason: reason, **metadata)
end
|
.challenged_asn?(asn) ⇒ Boolean
.challenged_ip?(ip) ⇒ Boolean
.configuration ⇒ Object
54
55
56
|
# File 'lib/reputable.rb', line 54
def configuration
@configuration ||= Configuration.new
end
|
58
59
60
61
|
# File 'lib/reputable.rb', line 58
def configure
yield(configuration)
Connection.reset! end
|
.decode_reputable_response(params) ⇒ Hash?
Decode the new reputable_r payload
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
|
# File 'lib/reputable.rb', line 337
def decode_reputable_response(params)
encoded = params["reputable_r"]
return nil unless encoded
begin
json = base64url_decode(encoded)
payload = JSON.parse(json)
{
"version" => payload["v"],
"status" => payload["s"],
"session_id" => payload["sid"],
"outcome" => payload["o"],
"ignore_analytics" => payload["i"],
"country" => payload["c"],
"challenge_passed" => payload["cp"]
}.compact
rescue StandardError => e
logger&.warn "Reputable: Failed to decode response: #{e.message}"
nil
end
end
|
.disabled? ⇒ Boolean
Check if Reputable is disabled
83
84
85
|
# File 'lib/reputable.rb', line 83
def disabled?
!enabled?
end
|
.enabled? ⇒ Boolean
Check if Reputable is enabled
Can be disabled via REPUTABLE_ENABLED=false environment variable
71
72
73
|
# File 'lib/reputable.rb', line 71
def enabled?
configuration.enabled?
end
|
.ignore_asn(asn, reason: "datacenter_traffic", ttl: nil, **metadata) ⇒ Object
197
198
199
|
# File 'lib/reputable.rb', line 197
def ignore_asn(asn, reason: "datacenter_traffic", ttl: nil, **metadata)
Reputation.ignore_asn(asn, reason: reason, ttl: ttl, **metadata)
end
|
.ignore_ip(ip, reason: "internal_traffic", **metadata) ⇒ Object
143
144
145
|
# File 'lib/reputable.rb', line 143
def ignore_ip(ip, reason: "internal_traffic", **metadata)
Reputation.ignore_ip(ip, reason: reason, **metadata)
end
|
.lookup_asn(asn) ⇒ Object
Delegate ASN reputation methods to Reputation module
.lookup_reputation(entity_type, entity_id) ⇒ Object
Delegate lookup methods to Reputation module (O(1) Redis lookups)
148
149
150
|
# File 'lib/reputable.rb', line 148
def lookup_reputation(entity_type, entity_id)
Reputation.lookup(entity_type, entity_id)
end
|
.measurement_collect_url(token) ⇒ Object
205
206
207
|
# File 'lib/reputable.rb', line 205
def measurement_collect_url(token)
Measurement.collect_url(token)
end
|
.measurement_redirect_url(token) ⇒ Object
209
210
211
|
# File 'lib/reputable.rb', line 209
def measurement_redirect_url(token)
Measurement.redirect_url(token)
end
|
.measurement_token(**kwargs) ⇒ Object
.operational? ⇒ Boolean
Check if Reputable is operational (enabled AND circuit breaker is closed)
Use this to guard redirect/gating decisions — when the circuit is open,
the Reputable server is likely unreachable and redirects would fail.
91
92
93
94
95
|
# File 'lib/reputable.rb', line 91
def operational?
return false unless enabled?
return false if Connection.circuit_open?
true
end
|
.proof_challenge_passed?(proof, **kwargs) ⇒ Boolean
Legacy alias from the proof era.
245
246
247
|
# File 'lib/reputable.rb', line 245
def proof_challenge_passed?(proof, **kwargs)
Verify.challenge_passed?(proof, **kwargs)
end
|
.reset! ⇒ Object
63
64
65
66
|
# File 'lib/reputable.rb', line 63
def reset!
@configuration = nil
Connection.reset!
end
|
.signals_check(**kwargs) ⇒ Object
Bot Signals server API: signal report for an end user's request.
250
251
252
|
# File 'lib/reputable.rb', line 250
def signals_check(**kwargs)
Signals.check(**kwargs)
end
|
.signals_redirect_url(**kwargs) ⇒ Object
Bot Signals redirect-flow URL (publishable key + registered return URL).
255
256
257
|
# File 'lib/reputable.rb', line 255
def signals_redirect_url(**kwargs)
Signals.redirect_url(**kwargs)
end
|
.status ⇒ Hash
Get a detailed status report for monitoring/debugging
.ticket_challenge_passed?(ticket, **kwargs) ⇒ Boolean
Did the ticket clear a CAPTCHA challenge? Convenience predicate.
240
241
242
|
# File 'lib/reputable.rb', line 240
def ticket_challenge_passed?(ticket, **kwargs)
Verify.challenge_passed?(ticket, **kwargs)
end
|
.track_request(ip:, path:, **options) ⇒ Object
Delegate tracking methods to Tracker
110
111
112
|
# File 'lib/reputable.rb', line 110
def track_request(ip:, path:, **options)
Tracker.track_request(ip: ip, path: path, **options)
end
|
.track_request_async(ip:, path:, **options) ⇒ Object
114
115
116
|
# File 'lib/reputable.rb', line 114
def track_request_async(ip:, path:, **options)
Tracker.track_request_async(ip: ip, path: path, **options)
end
|
.track_security_event(**kwargs) ⇒ Object
.track_security_event_async(**kwargs) ⇒ Object
.trust_asn(asn, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **metadata) ⇒ Object
185
186
187
|
# File 'lib/reputable.rb', line 185
def trust_asn(asn, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **metadata)
Reputation.trust_asn(asn, reason: reason, status: status, ttl: ttl, **metadata)
end
|
.trust_ip(ip, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **metadata) ⇒ Object
131
132
133
|
# File 'lib/reputable.rb', line 131
def trust_ip(ip, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **metadata)
Reputation.trust_ip(ip, reason: reason, status: status, ttl: ttl, **metadata)
end
|
.trusted_asn?(asn) ⇒ Boolean
.trusted_ip?(ip) ⇒ Boolean
.verbose? ⇒ Boolean
Check if verbose logging is enabled
77
78
79
|
# File 'lib/reputable.rb', line 77
def verbose?
configuration.verbose
end
|
.verification_url(return_url:, failure_url: nil, session_id: nil, force_challenge: false) ⇒ String
Generate a signed verification URL
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
|
# File 'lib/reputable.rb', line 265
def verification_url(return_url:, failure_url: nil, session_id: nil, force_challenge: false)
keys = configuration.trusted_keys
if keys.nil? || keys.empty?
logger&.warn "Reputable: Missing trusted_keys, cannot generate verification URL"
return return_url
end
secret = keys.first
base_url = configuration.base_url
base_url = base_url.chomp("/")
verify_url = "#{base_url}/_reputable/verify"
failure_url ||= "#{base_url}/_reputable/verify/failure"
= { alg: "HS256", typ: "JWT" }
= base64url_encode(JSON.generate())
payload = {
returnUrl: return_url,
failureUrl: failure_url,
sessionId: session_id,
forceChallenge: force_challenge,
iat: Time.now.to_i
}
encoded_payload = base64url_encode(JSON.generate(payload))
data = "#{}.#{encoded_payload}"
signature = OpenSSL::HMAC.digest("SHA256", secret, data)
encoded_signature = base64url_encode(signature)
token = "#{data}.#{encoded_signature}"
"#{verify_url}?token=#{token}"
end
|
.verify_measurement_result(token, **kwargs) ⇒ Object
217
218
219
|
# File 'lib/reputable.rb', line 217
def verify_measurement_result(token, **kwargs)
Measurement.verify_result(token, **kwargs)
end
|
.verify_measurement_result_token(token, **kwargs) ⇒ Object
213
214
215
|
# File 'lib/reputable.rb', line 213
def verify_measurement_result_token(token, **kwargs)
Measurement.verify_result_token(token, **kwargs)
end
|
.verify_proof(proof, **kwargs) ⇒ Object
Legacy alias from the proof era.
235
236
237
|
# File 'lib/reputable.rb', line 235
def verify_proof(proof, **kwargs)
Verify.verify_ticket(proof, **kwargs)
end
|
.verify_redirect_return(params) ⇒ Boolean
Verify the signature of a redirect return
Supports both new format (reputable_r + reputable_s) and legacy format
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
|
# File 'lib/reputable.rb', line 317
def verify_redirect_return(params)
keys = configuration.trusted_keys
if keys.nil? || keys.empty?
logger&.warn "Reputable: Missing trusted_keys, cannot verify redirect"
return false
end
if params["reputable_r"] && params["reputable_s"]
verify_new_format(params, keys)
elsif params["reputable_status"] && params["reputable_signature"]
verify_legacy_format(params, keys)
else
false
end
end
|
.verify_ticket(ticket, **kwargs) ⇒ Object
Bot Signals: verify the signed ticket submitted by the widget. Returns
the signal report hash (incl. "outcome" and "challengePassed") or nil.
223
224
225
|
# File 'lib/reputable.rb', line 223
def verify_ticket(ticket, **kwargs)
Verify.verify_ticket(ticket, **kwargs)
end
|
.verify_ticket_claims(ticket, **kwargs) ⇒ Object
Bot Signals: verify and return all signed ticket claims. This is useful
when an application must bind a route pass to key, mode, session, or
return origin rather than reading only the report.
230
231
232
|
# File 'lib/reputable.rb', line 230
def verify_ticket_claims(ticket, **kwargs)
Verify.verify_ticket_claims(ticket, **kwargs)
end
|