Module: Reputable::Reputation
- Defined in:
- lib/reputable/reputation.rb
Overview
Reputation management functionality
RESILIENCE: All reputation methods are designed to fail silently.
- Write operations return false on any failure
- Read operations return nil on any failure
- Boolean checks (trusted_ip?, blocked_ip?) return false on failure
- Never raises exceptions during normal operation
Constant Summary collapse
- VALID_STATUSES =
%i[ trusted_verified trusted_behavior untrusted_challenge untrusted_block untrusted_ignore ].freeze
- VALID_ENTITY_TYPES =
%i[ip asn ja4].freeze
Class Method Summary collapse
-
.apply(entity_type:, entity_id:, status:, **options) ⇒ Boolean
Apply a reputation status to an entity.
-
.block_asn(asn, reason: "manual_block", ttl: nil, **metadata) ⇒ Object
Convenience method: Block an ASN.
-
.block_ip(ip, reason: "manual_block", ttl: nil, **metadata) ⇒ Object
Convenience method: Block an IP.
-
.blocked_asn?(asn) ⇒ Boolean
Check if an ASN should be blocked.
-
.blocked_ip?(ip) ⇒ Boolean
Check if an IP should be blocked.
-
.challenge_asn(asn, reason: "suspicious_traffic", ttl: nil, **metadata) ⇒ Object
Convenience method: Challenge an ASN (CAPTCHA, etc.).
-
.challenge_ip(ip, reason: "suspicious_activity", ttl: nil, **metadata) ⇒ Object
Convenience method: Challenge an IP (CAPTCHA, etc.).
-
.challenged_asn?(asn) ⇒ Boolean
Check if an ASN should be challenged.
-
.challenged_ip?(ip) ⇒ Boolean
Check if an IP should be challenged.
-
.ignore_asn(asn, reason: "datacenter_traffic", ttl: nil, **metadata) ⇒ Object
Convenience method: Mark ASN to be ignored in analytics.
-
.ignore_ip(ip, reason: "internal_traffic", ttl: nil, **metadata) ⇒ Object
Convenience method: Mark IP to be ignored in analytics.
-
.lookup(entity_type, entity_id) ⇒ Hash?
Lookup the current reputation status for an entity This is an O(1) Redis HGETALL operation.
-
.lookup_asn(asn) ⇒ String?
Quick lookup for ASN reputation status Returns just the status string (or nil) ASN is normalized (strips "AS" prefix if present).
-
.lookup_ip(ip) ⇒ String?
Quick lookup for IP reputation status Returns just the status string (or nil).
-
.trust_asn(asn, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **metadata) ⇒ Object
Convenience method: Trust an ASN (behavioral by default).
-
.trust_ip(ip, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **metadata) ⇒ Object
Convenience method: Trust an IP (behavioral by default).
-
.trusted_asn?(asn) ⇒ Boolean
Check if an ASN is trusted (any trusted_* status).
-
.trusted_ip?(ip) ⇒ Boolean
Check if an IP is trusted (any trusted_* status).
Class Method Details
.apply(entity_type:, entity_id:, status:, **options) ⇒ Boolean
Apply a reputation status to an entity
53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/reputable/reputation.rb', line 53 def apply(entity_type:, entity_id:, status:, **) return false unless Reputable.enabled? return false unless valid_entity_type?(entity_type) return false unless valid_status?(status) entry = build_reputation_entry(entity_type, entity_id, status, ) push_to_buffer(entry) rescue StandardError => e Reputable.logger&.debug("Reputable apply error: #{e.class} - #{e.}") false end |
.block_asn(asn, reason: "manual_block", ttl: nil, **metadata) ⇒ Object
Convenience method: Block an ASN
278 279 280 281 282 283 284 285 286 287 |
# File 'lib/reputable/reputation.rb', line 278 def block_asn(asn, reason: "manual_block", ttl: nil, **) apply( entity_type: :asn, entity_id: normalize_asn(asn), status: :untrusted_block, reason: reason, ttl: ttl, metadata: ) end |
.block_ip(ip, reason: "manual_block", ttl: nil, **metadata) ⇒ Object
Convenience method: Block an IP
80 81 82 83 84 85 86 87 88 89 |
# File 'lib/reputable/reputation.rb', line 80 def block_ip(ip, reason: "manual_block", ttl: nil, **) apply( entity_type: :ip, entity_id: ip, status: :untrusted_block, reason: reason, ttl: ttl, metadata: ) end |
.blocked_asn?(asn) ⇒ Boolean
Check if an ASN should be blocked
249 250 251 |
# File 'lib/reputable/reputation.rb', line 249 def blocked_asn?(asn) lookup_asn(asn) == "untrusted_block" end |
.blocked_ip?(ip) ⇒ Boolean
Check if an IP should be blocked
203 204 205 |
# File 'lib/reputable/reputation.rb', line 203 def blocked_ip?(ip) lookup_ip(ip) == "untrusted_block" end |
.challenge_asn(asn, reason: "suspicious_traffic", ttl: nil, **metadata) ⇒ Object
Convenience method: Challenge an ASN (CAPTCHA, etc.)
290 291 292 293 294 295 296 297 298 299 |
# File 'lib/reputable/reputation.rb', line 290 def challenge_asn(asn, reason: "suspicious_traffic", ttl: nil, **) apply( entity_type: :asn, entity_id: normalize_asn(asn), status: :untrusted_challenge, reason: reason, ttl: ttl, metadata: ) end |
.challenge_ip(ip, reason: "suspicious_activity", ttl: nil, **metadata) ⇒ Object
Convenience method: Challenge an IP (CAPTCHA, etc.)
92 93 94 95 96 97 98 99 100 101 |
# File 'lib/reputable/reputation.rb', line 92 def challenge_ip(ip, reason: "suspicious_activity", ttl: nil, **) apply( entity_type: :ip, entity_id: ip, status: :untrusted_challenge, reason: reason, ttl: ttl, metadata: ) end |
.challenged_asn?(asn) ⇒ Boolean
Check if an ASN should be challenged
257 258 259 |
# File 'lib/reputable/reputation.rb', line 257 def challenged_asn?(asn) lookup_asn(asn) == "untrusted_challenge" end |
.challenged_ip?(ip) ⇒ Boolean
Check if an IP should be challenged
211 212 213 |
# File 'lib/reputable/reputation.rb', line 211 def challenged_ip?(ip) lookup_ip(ip) == "untrusted_challenge" end |
.ignore_asn(asn, reason: "datacenter_traffic", ttl: nil, **metadata) ⇒ Object
Convenience method: Mark ASN to be ignored in analytics
302 303 304 305 306 307 308 309 310 311 |
# File 'lib/reputable/reputation.rb', line 302 def ignore_asn(asn, reason: "datacenter_traffic", ttl: nil, **) apply( entity_type: :asn, entity_id: normalize_asn(asn), status: :untrusted_ignore, reason: reason, ttl: ttl, metadata: ) end |
.ignore_ip(ip, reason: "internal_traffic", ttl: nil, **metadata) ⇒ Object
Convenience method: Mark IP to be ignored in analytics
104 105 106 107 108 109 110 111 112 113 |
# File 'lib/reputable/reputation.rb', line 104 def ignore_ip(ip, reason: "internal_traffic", ttl: nil, **) apply( entity_type: :ip, entity_id: ip, status: :untrusted_ignore, reason: reason, ttl: ttl, metadata: ) end |
.lookup(entity_type, entity_id) ⇒ Hash?
Lookup the current reputation status for an entity This is an O(1) Redis HGETALL operation
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/reputable/reputation.rb', line 131 def lookup(entity_type, entity_id) return nil unless Reputable.enabled? return nil unless valid_entity_type?(entity_type) key = "reputation:#{entity_type}:#{entity_id}" data = Connection.safe_with(default: nil, context: "reputation_lookup") do |redis| redis.hgetall(key) end if data.nil? || data.empty? Reputable.logger&.debug("[Reputable] lookup: #{key} → (none)") if Reputable.verbose? return nil end # Check if expired (Redis TTL should handle this, but double-check) expires_at = data["expires_at"].to_i if expires_at > 0 && expires_at < (Time.now.to_f * 1000).to_i Reputable.logger&.debug("[Reputable] lookup: #{key} → expired") if Reputable.verbose? return nil end result = { status: data["status"], reason: data["reason"], source: data["source"], updated_at: data["updated_at"].to_i, expires_at: expires_at, metadata: safe_parse_json(data["metadata"]) } Reputable.logger&.info("[Reputable] lookup: #{key} → status=#{result[:status]} reason=#{result[:reason]} source=#{result[:source]}") if Reputable.verbose? result rescue StandardError => e Reputable.logger&.warn("[Reputable] lookup: exception #{e.class} - #{e.}") nil end |
.lookup_asn(asn) ⇒ String?
Quick lookup for ASN reputation status Returns just the status string (or nil) ASN is normalized (strips "AS" prefix if present)
229 230 231 232 |
# File 'lib/reputable/reputation.rb', line 229 def lookup_asn(asn) result = lookup(:asn, normalize_asn(asn)) result&.dig(:status) end |
.lookup_ip(ip) ⇒ String?
Quick lookup for IP reputation status Returns just the status string (or nil)
178 179 180 181 |
# File 'lib/reputable/reputation.rb', line 178 def lookup_ip(ip) result = lookup(:ip, ip) result&.dig(:status) end |
.trust_asn(asn, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **metadata) ⇒ Object
Convenience method: Trust an ASN (behavioral by default)
266 267 268 269 270 271 272 273 274 275 |
# File 'lib/reputable/reputation.rb', line 266 def trust_asn(asn, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **) apply( entity_type: :asn, entity_id: normalize_asn(asn), status: status, reason: reason, ttl: ttl, metadata: ) end |
.trust_ip(ip, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **metadata) ⇒ Object
Convenience method: Trust an IP (behavioral by default)
68 69 70 71 72 73 74 75 76 77 |
# File 'lib/reputable/reputation.rb', line 68 def trust_ip(ip, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **) apply( entity_type: :ip, entity_id: ip, status: status, reason: reason, ttl: ttl, metadata: ) end |
.trusted_asn?(asn) ⇒ Boolean
Check if an ASN is trusted (any trusted_* status)
238 239 240 241 242 243 |
# File 'lib/reputable/reputation.rb', line 238 def trusted_asn?(asn) status = lookup_asn(asn) return false if status.nil? status.start_with?("trusted") end |
.trusted_ip?(ip) ⇒ Boolean
Check if an IP is trusted (any trusted_* status)
192 193 194 195 196 197 |
# File 'lib/reputable/reputation.rb', line 192 def trusted_ip?(ip) status = lookup_ip(ip) return false if status.nil? status.start_with?("trusted") end |