reputable 0.1.0 → 0.1.2

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: 022cbf56ba79954bcae6450e3fe7d31791498a1bbde2ee3d6add895124cde28b
4
- data.tar.gz: 6f3cddf2cbc0f69241d7affd69d9322a521a03eb0727a6c7e1695d6fe3194106
3
+ metadata.gz: 5cfe6bbd421f190d87e5f0be242142988e269d8df74186f85f66c8f5996f115f
4
+ data.tar.gz: 4db1343a290ef95d775b0694a50d84d48c861af293bf0d3e4d388e44c031c9d0
5
5
  SHA512:
6
- metadata.gz: 6a03a6f38827b0eb79ce4b8db2ed57640614822caf5f31165020da33324eaf39a6ded64a9d5a19e5b557a03e3b2e06348b02dcd78472a407f680cd18b4c4086d
7
- data.tar.gz: 0a52fc4e6a6fe20b799841e2dd4e475afc0a8ff4a20a48240812b343538e8f2a3b0e11863b6a2dea89748b9f8f8931f45889142d5d7f690144223958cde890b6
6
+ metadata.gz: 211cb949190c13bd0f3d8f6c4f0296a84c92653e64c2971d5866e0606cc777ebfe356a8a73d10e5a269f6701105372f9c4c8203f90bf75f279958b385874c3ad
7
+ data.tar.gz: 14dbcf3338f725de441618100e0b57e99314974e0ff89b787c45141dcec56fa6c230c9acaa69feaed42233a87cf10add2cc2ccca2e80883eddb656779821ace6
data/.DS_Store ADDED
Binary file
data/Gemfile.lock ADDED
@@ -0,0 +1,76 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ reputable (0.1.2)
5
+ connection_pool (~> 2.2)
6
+ redis (>= 4.0, < 6.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ast (2.4.3)
12
+ connection_pool (2.5.5)
13
+ diff-lcs (1.6.2)
14
+ json (2.18.0)
15
+ language_server-protocol (3.17.0.5)
16
+ lint_roller (1.1.0)
17
+ parallel (1.27.0)
18
+ parser (3.3.10.0)
19
+ ast (~> 2.4.1)
20
+ racc
21
+ prism (1.7.0)
22
+ racc (1.8.1)
23
+ rack (2.2.21)
24
+ rainbow (3.1.1)
25
+ rake (13.3.1)
26
+ redis (5.4.1)
27
+ redis-client (>= 0.22.0)
28
+ redis-client (0.26.2)
29
+ connection_pool
30
+ regexp_parser (2.11.3)
31
+ rspec (3.13.2)
32
+ rspec-core (~> 3.13.0)
33
+ rspec-expectations (~> 3.13.0)
34
+ rspec-mocks (~> 3.13.0)
35
+ rspec-core (3.13.6)
36
+ rspec-support (~> 3.13.0)
37
+ rspec-expectations (3.13.5)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.13.0)
40
+ rspec-mocks (3.13.7)
41
+ diff-lcs (>= 1.2.0, < 2.0)
42
+ rspec-support (~> 3.13.0)
43
+ rspec-support (3.13.6)
44
+ rubocop (1.82.0)
45
+ json (~> 2.3)
46
+ language_server-protocol (~> 3.17.0.2)
47
+ lint_roller (~> 1.1.0)
48
+ parallel (~> 1.10)
49
+ parser (>= 3.3.0.2)
50
+ rainbow (>= 2.2.2, < 4.0)
51
+ regexp_parser (>= 2.9.3, < 3.0)
52
+ rubocop-ast (>= 1.48.0, < 2.0)
53
+ ruby-progressbar (~> 1.7)
54
+ unicode-display_width (>= 2.4.0, < 4.0)
55
+ rubocop-ast (1.48.0)
56
+ parser (>= 3.3.7.2)
57
+ prism (~> 1.4)
58
+ ruby-progressbar (1.13.0)
59
+ unicode-display_width (3.2.0)
60
+ unicode-emoji (~> 4.1)
61
+ unicode-emoji (4.2.0)
62
+
63
+ PLATFORMS
64
+ arm64-darwin-25
65
+ ruby
66
+
67
+ DEPENDENCIES
68
+ bundler (~> 2.0)
69
+ rack (~> 2.0)
70
+ rake (~> 13.0)
71
+ reputable!
72
+ rspec (~> 3.0)
73
+ rubocop (~> 1.21)
74
+
75
+ BUNDLED WITH
76
+ 2.5.9
data/README.md CHANGED
@@ -28,9 +28,6 @@ Reputable.configure do |config|
28
28
  # Required: Redis/Dragonfly URL (TLS supported via rediss://)
29
29
  config.redis_url = ENV['REPUTABLE_REDIS_URL']
30
30
 
31
- # Required: Your tenant ID
32
- config.tenant_id = ENV['REPUTABLE_TENANT_ID']
33
-
34
31
  # Optional: Enable logging (logs at debug level)
35
32
  Reputable.logger = Rails.logger
36
33
  end
@@ -82,7 +79,6 @@ All configuration can be set via environment variables:
82
79
  ```bash
83
80
  # Required
84
81
  REPUTABLE_REDIS_URL=rediss://user:password@your-dragonfly.example.com:6379
85
- REPUTABLE_TENANT_ID=your-tenant-id
86
82
 
87
83
  # Optional: Disable entirely (useful for test environments)
88
84
  REPUTABLE_ENABLED=false
@@ -105,9 +101,6 @@ Reputable.configure do |config|
105
101
  # Redis connection (supports redis:// and rediss:// for TLS)
106
102
  config.redis_url = ENV['REPUTABLE_REDIS_URL']
107
103
 
108
- # Your tenant identifier
109
- config.tenant_id = ENV['REPUTABLE_TENANT_ID']
110
-
111
104
  # Connection pool settings
112
105
  config.pool_size = 5 # Number of Redis connections
113
106
  config.pool_timeout = 1.0 # Max wait for connection (seconds)
@@ -284,8 +277,6 @@ track_reputable_request(tags: ['custom:tag'])
284
277
 
285
278
  # Trust methods (after successful actions)
286
279
  trust_current_ip(reason: 'payment_completed', order_id: '123')
287
- trust_current_user(reason: 'email_verified')
288
- trust_current_session(reason: 'captcha_passed')
289
280
 
290
281
  # Challenge/Block methods
291
282
  challenge_current_ip(reason: 'suspicious_activity')
@@ -319,11 +310,9 @@ Reputable.track_request(
319
310
  path: request.path,
320
311
  query: request.query_string,
321
312
  method: request.request_method,
322
- session_id: session.id,
323
- session_present: true,
324
313
  user_agent: request.user_agent,
325
314
  referer: request.referer,
326
- tags: ['ctx:page:product']
315
+ tags: ['view:page:product']
327
316
  )
328
317
 
329
318
  # Asynchronous (fire-and-forget, recommended)
@@ -339,12 +328,6 @@ Reputable.track_request_async(
339
328
  # Trust IP forever (after payment, verification, etc.)
340
329
  Reputable.trust_ip(request.ip, reason: 'payment_completed', order_id: order.id)
341
330
 
342
- # Trust a user
343
- Reputable.trust_user(current_user.id, reason: 'email_verified')
344
-
345
- # Trust a session (default: 24 hour TTL)
346
- Reputable.trust_session(session.id, reason: 'captcha_passed')
347
-
348
331
  # Challenge (require CAPTCHA, etc.)
349
332
  Reputable.challenge_ip(request.ip, reason: 'unusual_activity')
350
333
 
@@ -372,10 +355,6 @@ Reputable.lookup_reputation(:ip, request.ip)
372
355
  # => { status: "trusted_verified", reason: "payment_completed",
373
356
  # source: "app_server", updated_at: 1703123456789,
374
357
  # expires_at: 0, metadata: { order_id: "123" } }
375
-
376
- # User/Session lookups
377
- Reputable.trusted_user?(current_user.id)
378
- Reputable.trusted_session?(session.id)
379
358
  ```
380
359
 
381
360
  ---
@@ -438,10 +417,10 @@ Use tags to classify requests for behavioral analysis:
438
417
 
439
418
  ```ruby
440
419
  # Page context
441
- tags: ['ctx:page:product']
442
- tags: ['ctx:page:checkout']
443
- tags: ['ctx:page:cart']
444
- tags: ['ctx:page:login']
420
+ tags: ['view:page:product']
421
+ tags: ['view:page:checkout']
422
+ tags: ['view:page:cart']
423
+ tags: ['view:page:login']
445
424
 
446
425
  # Traffic source
447
426
  tags: ['trust:channel:email']
@@ -493,7 +472,6 @@ expect(Reputable.lookup_ip('1.2.3.4')).to be_nil
493
472
  ### What's Available from Rails
494
473
 
495
474
  - IP reputation and history
496
- - Session tracking
497
475
  - Request classification
498
476
  - UA churn detection
499
477
  - Cross-request pattern analysis
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -9,12 +9,12 @@ module Reputable
9
9
  # Supports TLS connections with automatic SSL error handling.
10
10
  # All SSL/connection errors are caught and logged, never breaking your app.
11
11
  class Configuration
12
- attr_accessor :redis_url, :redis_options, :tenant_id, :buffer_prefix,
12
+ attr_accessor :redis_url, :redis_options, :buffer_prefix,
13
13
  :request_buffer_key, :reputation_buffer_key,
14
14
  :default_ttls, :pool_size, :pool_timeout,
15
15
  :connect_timeout, :read_timeout, :write_timeout,
16
16
  :ssl_params, :trusted_proxies, :ip_header_priority,
17
- :on_error
17
+ :on_error, :secret_key, :verification_base_url
18
18
 
19
19
  # Default TTLs in seconds (0 = forever)
20
20
  DEFAULT_TTLS = {
@@ -52,7 +52,6 @@ module Reputable
52
52
  def initialize
53
53
  @redis_url = ENV.fetch("REPUTABLE_REDIS_URL", "redis://127.0.0.1:6379")
54
54
  @redis_options = {}
55
- @tenant_id = ENV.fetch("REPUTABLE_TENANT_ID", "default")
56
55
  @buffer_prefix = "reputable:buffer"
57
56
  @pool_size = Integer(ENV.fetch("REPUTABLE_POOL_SIZE", "5"))
58
57
  @pool_timeout = Float(ENV.fetch("REPUTABLE_POOL_TIMEOUT", "1.0"))
@@ -64,6 +63,8 @@ module Reputable
64
63
  @trusted_proxies = nil # Additional trusted proxy IPs/ranges
65
64
  @ip_header_priority = DEFAULT_IP_HEADERS.dup
66
65
  @on_error = nil # Optional error callback: ->(error, context) { ... }
66
+ @secret_key = ENV["REPUTABLE_SECRET_KEY"]
67
+ @verification_base_url = ENV.fetch("REPUTABLE_VERIFY_URL", "https://api.reputable.click/_reputable/verify")
67
68
  end
68
69
 
69
70
  # Check if Reputable is enabled (can be disabled via ENV)
@@ -75,11 +76,11 @@ module Reputable
75
76
  end
76
77
 
77
78
  def request_buffer_key
78
- @request_buffer_key || "#{buffer_prefix}:requests:#{tenant_id}"
79
+ @request_buffer_key || "#{buffer_prefix}:requests"
79
80
  end
80
81
 
81
82
  def reputation_buffer_key
82
- @reputation_buffer_key || "#{buffer_prefix}:reputation:#{tenant_id}"
83
+ @reputation_buffer_key || "#{buffer_prefix}:reputation"
83
84
  end
84
85
 
85
86
  def default_ttl_for(status)
@@ -45,6 +45,9 @@ module Reputable
45
45
  end
46
46
 
47
47
  def call(env)
48
+ # Check for verification return parameters and verify signature if present
49
+ handle_verification_return(env)
50
+
48
51
  # ALWAYS process the request first - tracking must never block
49
52
  status, headers, response = @app.call(env)
50
53
 
@@ -70,6 +73,26 @@ module Reputable
70
73
  Reputable.logger&.debug("Reputable middleware: #{e.class} - #{e.message}")
71
74
  end
72
75
 
76
+ def handle_verification_return(env)
77
+ request = Rack::Request.new(env)
78
+ # Quick check to avoid overhead
79
+ return unless request.query_string.include?("reputable_status")
80
+
81
+ return unless request.params["reputable_status"] == "pass"
82
+
83
+ if Reputable.verify_redirect_return(request.params)
84
+ env["reputable.verified"] = true
85
+
86
+ # Store in session if available
87
+ if env["rack.session"]
88
+ env["rack.session"]["reputable_verified"] = true
89
+ env["rack.session"]["reputable_verified_at"] = Time.now.to_i
90
+ end
91
+ end
92
+ rescue StandardError => e
93
+ Reputable.logger&.debug("Reputable verification error: #{e.message}")
94
+ end
95
+
73
96
  def skip_request?(env)
74
97
  return true if @skip_if&.call(env)
75
98
 
@@ -109,8 +132,6 @@ module Reputable
109
132
  path: request.path,
110
133
  query: request.query_string.empty? ? nil : request.query_string,
111
134
  method: request.request_method,
112
- session_id: extract_session_id(env),
113
- session_present: session_present?(env),
114
135
  user_agent: env["HTTP_USER_AGENT"],
115
136
  referer: env["HTTP_REFERER"],
116
137
  tags: build_tags(env)
@@ -190,29 +211,6 @@ module Reputable
190
211
  nil
191
212
  end
192
213
 
193
- def extract_session_id(env)
194
- # Try rack.session.id first
195
- return env["rack.session.id"] if env["rack.session.id"]
196
-
197
- # Try to get from rack.session
198
- session = env["rack.session"]
199
- return session.id.to_s if session.respond_to?(:id)
200
-
201
- nil
202
- rescue StandardError
203
- nil
204
- end
205
-
206
- def session_present?(env)
207
- return true if env["rack.session.id"]
208
- return true if env["rack.session"]&.any?
209
- return true if env["HTTP_COOKIE"]&.include?("_session")
210
-
211
- false
212
- rescue StandardError
213
- false
214
- end
215
-
216
214
  def build_tags(env)
217
215
  tags = []
218
216
 
@@ -3,30 +3,6 @@
3
3
  module Reputable
4
4
  # Rails-specific integration
5
5
  module Rails
6
- # Railtie for automatic Rails integration
7
- class Railtie < ::Rails::Railtie
8
- initializer "reputable.configure_rails_initialization" do
9
- # Auto-configure from Rails credentials or ENV
10
- Reputable.configure do |config|
11
- # Try Rails credentials first
12
- if defined?(::Rails.application.credentials)
13
- creds = ::Rails.application.credentials.reputable rescue nil
14
- if creds
15
- config.redis_url = creds[:redis_url] if creds[:redis_url]
16
- config.tenant_id = creds[:tenant_id] if creds[:tenant_id]
17
- end
18
- end
19
- end
20
- end
21
-
22
- initializer "reputable.insert_middleware" do |app|
23
- # Only insert middleware if explicitly enabled
24
- if ENV["REPUTABLE_AUTO_MIDDLEWARE"] == "true"
25
- app.middleware.use Reputable::Middleware
26
- end
27
- end
28
- end
29
-
30
6
  # Helper methods for controllers
31
7
  module ControllerHelpers
32
8
  extend ActiveSupport::Concern
@@ -38,8 +14,6 @@ module Reputable
38
14
  path: request.path,
39
15
  query: request.query_string,
40
16
  method: request.method,
41
- session_id: session.id.to_s,
42
- session_present: session.any?,
43
17
  user_agent: request.user_agent,
44
18
  referer: request.referer,
45
19
  tags: tags,
@@ -56,26 +30,6 @@ module Reputable
56
30
  )
57
31
  end
58
32
 
59
- # Trust the current user
60
- def trust_current_user(reason:, **metadata)
61
- return false unless respond_to?(:current_user) && current_user
62
-
63
- Reputable::Reputation.trust_user(
64
- current_user.id,
65
- reason: reason,
66
- **metadata
67
- )
68
- end
69
-
70
- # Trust the current session
71
- def trust_current_session(reason:, **metadata)
72
- Reputable::Reputation.trust_session(
73
- session.id.to_s,
74
- reason: reason,
75
- **metadata
76
- )
77
- end
78
-
79
33
  # Challenge the current IP
80
34
  def challenge_current_ip(reason:, **metadata)
81
35
  Reputable::Reputation.challenge_ip(
@@ -127,25 +81,31 @@ module Reputable
127
81
  def current_ip_status
128
82
  Reputable::Reputation.lookup_ip(request.remote_ip)
129
83
  end
84
+ end
130
85
 
131
- # Check if current user is trusted
132
- # @return [Boolean]
133
- def current_user_trusted?
134
- return false unless respond_to?(:current_user) && current_user
135
-
136
- Reputable::Reputation.trusted_user?(current_user.id)
137
- end
86
+ # Railtie for automatic Rails integration (only defined when Rails is present)
87
+ if defined?(::Rails::Railtie)
88
+ class Railtie < ::Rails::Railtie
89
+ initializer "reputable.configure_rails_initialization" do
90
+ # Auto-configure from Rails credentials or ENV
91
+ Reputable.configure do |config|
92
+ # Try Rails credentials first
93
+ if defined?(::Rails.application.credentials)
94
+ creds = ::Rails.application.credentials.reputable rescue nil
95
+ if creds
96
+ config.redis_url = creds[:redis_url] if creds[:redis_url]
97
+ end
98
+ end
99
+ end
100
+ end
138
101
 
139
- # Check if current session is trusted
140
- # @return [Boolean]
141
- def current_session_trusted?
142
- Reputable::Reputation.trusted_session?(session.id.to_s)
102
+ initializer "reputable.insert_middleware" do |app|
103
+ # Only insert middleware if explicitly enabled
104
+ if ENV["REPUTABLE_AUTO_MIDDLEWARE"] == "true"
105
+ app.middleware.use Reputable::Middleware
106
+ end
107
+ end
143
108
  end
144
109
  end
145
110
  end
146
111
  end
147
-
148
- # Auto-load Railtie if Rails is present
149
- if defined?(::Rails::Railtie)
150
- require_relative "rails/railtie"
151
- end
@@ -19,12 +19,12 @@ module Reputable
19
19
  untrusted_ignore
20
20
  ].freeze
21
21
 
22
- VALID_ENTITY_TYPES = %i[ip asn ja4 session user].freeze
22
+ VALID_ENTITY_TYPES = %i[ip asn ja4].freeze
23
23
 
24
24
  class << self
25
25
  # Apply a reputation status to an entity
26
26
  #
27
- # @param entity_type [Symbol] Type of entity (:ip, :asn, :ja4, :session, :user)
27
+ # @param entity_type [Symbol] Type of entity (:ip, :asn, :ja4)
28
28
  # @param entity_id [String] The entity identifier (IP address, ASN, etc.)
29
29
  # @param status [Symbol] Reputation status to apply
30
30
  # @param options [Hash] Additional options
@@ -110,30 +110,6 @@ module Reputable
110
110
  )
111
111
  end
112
112
 
113
- # Trust a user (by internal user ID)
114
- def trust_user(user_id, reason: "verified_user", **metadata)
115
- apply(
116
- entity_type: :user,
117
- entity_id: user_id.to_s,
118
- status: :trusted_verified,
119
- reason: reason,
120
- ttl: 0,
121
- metadata: metadata
122
- )
123
- end
124
-
125
- # Trust a session
126
- def trust_session(session_id, reason: "verified_session", ttl: nil, **metadata)
127
- apply(
128
- entity_type: :session,
129
- entity_id: session_id,
130
- status: :trusted_verified,
131
- reason: reason,
132
- ttl: ttl || (24 * 3600), # Default 24 hours for sessions
133
- metadata: metadata
134
- )
135
- end
136
-
137
113
  # ========================================================================
138
114
  # LOOKUP METHODS (O(1) Redis hash lookups)
139
115
  # ========================================================================
@@ -141,7 +117,7 @@ module Reputable
141
117
  # Lookup the current reputation status for an entity
142
118
  # This is an O(1) Redis HGETALL operation
143
119
  #
144
- # @param entity_type [Symbol] Type of entity (:ip, :asn, :ja4, :session, :user)
120
+ # @param entity_type [Symbol] Type of entity (:ip, :asn, :ja4)
145
121
  # @param entity_id [String] The entity identifier
146
122
  # @return [Hash, nil] Reputation data or nil if not found/expired/error
147
123
  #
@@ -198,7 +174,7 @@ module Reputable
198
174
  # Check if an IP is trusted (any trusted_* status)
199
175
  #
200
176
  # @param ip [String] IP address
201
- # @return [Boolean]
177
+ # @return [Boolean] Returns false if disabled, nil lookup, or not trusted
202
178
  #
203
179
  # @example
204
180
  # if Reputable::Reputation.trusted_ip?("203.0.113.45")
@@ -206,7 +182,9 @@ module Reputable
206
182
  # end
207
183
  def trusted_ip?(ip)
208
184
  status = lookup_ip(ip)
209
- status&.start_with?("trusted")
185
+ return false if status.nil?
186
+
187
+ status.start_with?("trusted")
210
188
  end
211
189
 
212
190
  # Check if an IP should be blocked
@@ -225,42 +203,6 @@ module Reputable
225
203
  lookup_ip(ip) == "untrusted_challenge"
226
204
  end
227
205
 
228
- # Lookup user reputation
229
- #
230
- # @param user_id [String, Integer] User ID
231
- # @return [String, nil] Status string or nil
232
- def lookup_user(user_id)
233
- result = lookup(:user, user_id.to_s)
234
- result&.dig(:status)
235
- end
236
-
237
- # Check if a user is trusted
238
- #
239
- # @param user_id [String, Integer] User ID
240
- # @return [Boolean]
241
- def trusted_user?(user_id)
242
- status = lookup_user(user_id)
243
- status&.start_with?("trusted")
244
- end
245
-
246
- # Lookup session reputation
247
- #
248
- # @param session_id [String] Session ID
249
- # @return [String, nil] Status string or nil
250
- def lookup_session(session_id)
251
- result = lookup(:session, session_id)
252
- result&.dig(:status)
253
- end
254
-
255
- # Check if a session is trusted
256
- #
257
- # @param session_id [String] Session ID
258
- # @return [Boolean]
259
- def trusted_session?(session_id)
260
- status = lookup_session(session_id)
261
- status&.start_with?("trusted")
262
- end
263
-
264
206
  private
265
207
 
266
208
  def valid_entity_type?(entity_type)
@@ -18,9 +18,6 @@ module Reputable
18
18
  # @param options [Hash] Additional options
19
19
  # @option options [String] :query Query string
20
20
  # @option options [String] :method HTTP method (GET, POST, etc.)
21
- # @option options [String] :session_id Session identifier
22
- # @option options [Boolean] :session_present Whether a session cookie exists
23
- # @option options [String] :user_id Internal user ID (for logged-in users)
24
21
  # @option options [String] :fingerprint Browser fingerprint hash
25
22
  # @option options [String] :user_agent User-Agent header
26
23
  # @option options [String] :referer Referer header
@@ -41,11 +38,9 @@ module Reputable
41
38
  # path: request.path,
42
39
  # query: request.query_string,
43
40
  # method: request.request_method,
44
- # session_id: session.id,
45
- # session_present: true,
46
41
  # user_agent: request.user_agent,
47
42
  # referer: request.referer,
48
- # tags: ["ctx:page:product", "trust:channel:organic"]
43
+ # tags: ["view:page:product", "trust:channel:organic"]
49
44
  # )
50
45
  def track_request(ip:, path:, **options)
51
46
  return false unless Reputable.enabled?
@@ -80,9 +75,6 @@ module Reputable
80
75
  path: path,
81
76
  query: options[:query],
82
77
  method: options[:method] || "GET",
83
- session_id: options[:session_id],
84
- session_present: options[:session_present],
85
- user_id: options[:user_id],
86
78
  fingerprint: options[:fingerprint],
87
79
  user_agent: options[:user_agent],
88
80
  referer: options[:referer],
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Reputable
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
data/lib/reputable.rb CHANGED
@@ -1,17 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "reputable/version"
4
+ require "json"
5
+ require "base64"
4
6
  require_relative "reputable/configuration"
5
7
  require_relative "reputable/connection"
6
8
  require_relative "reputable/tracker"
7
9
  require_relative "reputable/reputation"
8
10
  require_relative "reputable/middleware"
9
11
 
10
- # Optional Rails integration
11
- begin
12
+ # Optional Rails integration (only load if Rails is defined)
13
+ if defined?(::Rails)
12
14
  require_relative "reputable/rails"
13
- rescue LoadError
14
- # Rails not available, skip
15
15
  end
16
16
 
17
17
  # Reputable - Bot detection and reputation scoring client
@@ -25,14 +25,12 @@ end
25
25
  # @example Basic setup
26
26
  # Reputable.configure do |config|
27
27
  # config.redis_url = "rediss://user:pass@dragonfly.example.com:6379"
28
- # config.tenant_id = "my-tenant"
29
28
  # end
30
29
  #
31
30
  # @example Track a request
32
31
  # Reputable.track_request(
33
32
  # ip: "203.0.113.45",
34
- # path: "/products/123",
35
- # session_present: true
33
+ # path: "/products/123"
36
34
  # )
37
35
  #
38
36
  # @example Apply reputation after payment
@@ -106,14 +104,6 @@ module Reputable
106
104
  Reputation.ignore_ip(ip, reason: reason, **metadata)
107
105
  end
108
106
 
109
- def trust_user(user_id, reason: "verified_user", **metadata)
110
- Reputation.trust_user(user_id, reason: reason, **metadata)
111
- end
112
-
113
- def trust_session(session_id, reason: "verified_session", **metadata)
114
- Reputation.trust_session(session_id, reason: reason, **metadata)
115
- end
116
-
117
107
  # Delegate lookup methods to Reputation module (O(1) Redis lookups)
118
108
  def lookup_reputation(entity_type, entity_id)
119
109
  Reputation.lookup(entity_type, entity_id)
@@ -135,20 +125,73 @@ module Reputable
135
125
  Reputation.challenged_ip?(ip)
136
126
  end
137
127
 
138
- def lookup_user(user_id)
139
- Reputation.lookup_user(user_id)
140
- end
141
-
142
- def trusted_user?(user_id)
143
- Reputation.trusted_user?(user_id)
144
- end
145
-
146
- def lookup_session(session_id)
147
- Reputation.lookup_session(session_id)
148
- end
149
-
150
- def trusted_session?(session_id)
151
- Reputation.trusted_session?(session_id)
128
+ # Generate a signed verification URL
129
+ def verification_url(return_url:, failure_url: nil, session_id: nil)
130
+ secret = configuration.secret_key
131
+ unless secret
132
+ logger&.warn "Reputable: Missing secret_key, cannot generate verification URL"
133
+ return return_url
134
+ end
135
+
136
+ base_url = configuration.verification_base_url
137
+
138
+ # JWT Header
139
+ header = { alg: "HS256", typ: "JWT" }
140
+ encoded_header = base64url_encode(JSON.generate(header))
141
+
142
+ # JWT Payload
143
+ payload = {
144
+ returnUrl: return_url,
145
+ failureUrl: failure_url,
146
+ sessionId: session_id,
147
+ iat: Time.now.to_i
148
+ }
149
+ encoded_payload = base64url_encode(JSON.generate(payload))
150
+
151
+ # Signature
152
+ data = "#{encoded_header}.#{encoded_payload}"
153
+ signature = OpenSSL::HMAC.digest("SHA256", secret, data)
154
+ encoded_signature = base64url_encode(signature)
155
+
156
+ token = "#{data}.#{encoded_signature}"
157
+
158
+ "#{base_url}?token=#{token}"
159
+ end
160
+
161
+ # Verify the signature of a redirect return
162
+ # @param params [Hash] Request query parameters
163
+ # @return [Boolean] true if valid logic and passed signature check
164
+ def verify_redirect_return(params)
165
+ status = params["reputable_status"]
166
+ session_id = params["reputable_session_id"]
167
+ signature = params["reputable_signature"]
168
+
169
+ return false unless status && session_id && signature
170
+
171
+ secret = configuration.secret_key
172
+ unless secret
173
+ logger&.warn "Reputable: Missing secret_key, cannot verify redirect"
174
+ return false
175
+ end
176
+
177
+ data = "#{status}:#{session_id}"
178
+ expected_signature = OpenSSL::HMAC.hexdigest("SHA256", secret, data)
179
+
180
+ secure_compare(expected_signature, signature)
181
+ end
182
+
183
+ private
184
+
185
+ def base64url_encode(str)
186
+ Base64.urlsafe_encode64(str).gsub("=", "")
187
+ end
188
+
189
+ def secure_compare(a, b)
190
+ return false unless a.bytesize == b.bytesize
191
+ l = a.unpack "C#{a.bytesize}"
192
+ res = 0
193
+ b.each_byte { |byte| res |= byte ^ l.shift }
194
+ res == 0
152
195
  end
153
196
  end
154
197
  end
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.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reputable
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-12-20 00:00:00.000000000 Z
11
+ date: 2025-12-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -78,14 +78,28 @@ dependencies:
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: '3.0'
81
+ version: '3.12'
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: '3.0'
88
+ version: '3.12'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rack
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '2.0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '2.0'
89
103
  - !ruby/object:Gem::Dependency
90
104
  name: rubocop
91
105
  requirement: !ruby/object:Gem::Requirement
@@ -108,8 +122,11 @@ executables: []
108
122
  extensions: []
109
123
  extra_rdoc_files: []
110
124
  files:
125
+ - ".DS_Store"
111
126
  - Gemfile
127
+ - Gemfile.lock
112
128
  - README.md
129
+ - Rakefile
113
130
  - lib/reputable.rb
114
131
  - lib/reputable/configuration.rb
115
132
  - lib/reputable/connection.rb