reputable 0.1.13 → 0.1.14

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: cb476f1f238df7751eac5e901cf1b9e7cf6907720f74b84a98f1509963459c52
4
- data.tar.gz: a39ce0abde8171b3915e34a1008dd72cd3e2834922a26959791c6fdb614f6466
3
+ metadata.gz: c71694602b9f9eb25537c91c895503e068d59f36b9fbafcefcb7104acacd945f
4
+ data.tar.gz: 95fabaa4a264c471b46957b781b49cef0cda4205ab35d312ebb85497d9a88657
5
5
  SHA512:
6
- metadata.gz: 961ade11852eb1ebabfac922c70935646f9bcb68837f5c72fa4c7056207644949ca5ca7cfbc17b04fa10bc85e66a418c376530a705ea09f72f911744af5cc82e
7
- data.tar.gz: ddfc988515d1ff11540bc4ac1b0b8f2d8e3cfd11bb601c092a031ea2182eb00f7602c0178d51c23122a19f0b8a087536618b3cf7b9d33353551f757c48ba2253
6
+ metadata.gz: 4a8f05f1bc68354e88b6591873de564f4dbc56531209d7b00781536d23303d969447f05f76bd040b8e078efc836c8697ef1894342168e93c1c5592eb3af62639
7
+ data.tar.gz: dcd56a2d35fb3d162ffe0813e336212ce61d3e57a6e64d94303f9198b62849211e24879a7302073d4f1e0d3fa90ad765edc0ce6e941951e38a9a67c4dfb6f5fe
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- reputable (0.1.13)
4
+ reputable (0.1.14)
5
5
  connection_pool (~> 2.2)
6
6
  redis (>= 4.0, < 6.0)
7
7
 
@@ -159,9 +159,19 @@ module Reputable
159
159
  )
160
160
  return if reputable_verified?(session_key: session_key)
161
161
 
162
- if params[:reputable_signature]
162
+ # Check for new format (reputable_s) or legacy format (reputable_signature)
163
+ if params[:reputable_s] || params[:reputable_signature]
163
164
  if Reputable.verify_redirect_return(params)
164
- if params[:reputable_status] == "pass"
165
+ # For new format, decode payload to get status
166
+ # For legacy format, use reputable_status directly
167
+ status = if params[:reputable_r]
168
+ decoded = Reputable.decode_reputable_response(params)
169
+ decoded&.dig("status")
170
+ else
171
+ params[:reputable_status]
172
+ end
173
+
174
+ if status == "pass"
165
175
  session[session_key] = Time.now.to_i
166
176
  return
167
177
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Reputable
4
- VERSION = "0.1.13"
4
+ VERSION = "0.1.14"
5
5
  end
data/lib/reputable.rb CHANGED
@@ -181,9 +181,77 @@ module Reputable
181
181
  end
182
182
 
183
183
  # Verify the signature of a redirect return
184
+ # Supports both new format (reputable_r + reputable_s) and legacy format
184
185
  # @param params [Hash] Request query parameters
185
- # @return [Boolean] true if valid logic and passed signature check
186
+ # @return [Boolean] true if valid signature check
186
187
  def verify_redirect_return(params)
188
+ keys = configuration.trusted_keys
189
+ if keys.nil? || keys.empty?
190
+ logger&.warn "Reputable: Missing trusted_keys, cannot verify redirect"
191
+ return false
192
+ end
193
+
194
+ # Detect format: new (reputable_r) vs legacy (reputable_status)
195
+ if params["reputable_r"] && params["reputable_s"]
196
+ verify_new_format(params, keys)
197
+ elsif params["reputable_status"] && params["reputable_signature"]
198
+ verify_legacy_format(params, keys)
199
+ else
200
+ false
201
+ end
202
+ end
203
+
204
+ # Decode the new reputable_r payload
205
+ # @param params [Hash] Request query parameters
206
+ # @return [Hash, nil] Decoded payload with normalized keys, or nil if invalid
207
+ def decode_reputable_response(params)
208
+ encoded = params["reputable_r"]
209
+ return nil unless encoded
210
+
211
+ begin
212
+ json = base64url_decode(encoded)
213
+ payload = JSON.parse(json)
214
+
215
+ # Return normalized hash with full key names
216
+ {
217
+ "version" => payload["v"],
218
+ "status" => payload["s"],
219
+ "session_id" => payload["sid"],
220
+ "outcome" => payload["o"],
221
+ "ignore_analytics" => payload["i"],
222
+ "country" => payload["c"],
223
+ "challenge_passed" => payload["cp"]
224
+ }.compact
225
+ rescue StandardError => e
226
+ logger&.warn "Reputable: Failed to decode response: #{e.message}"
227
+ nil
228
+ end
229
+ end
230
+
231
+ private
232
+
233
+ def verify_new_format(params, keys)
234
+ encoded_payload = params["reputable_r"]
235
+ signature = params["reputable_s"]
236
+
237
+ return false unless encoded_payload && signature
238
+
239
+ begin
240
+ # Decode to get the JSON string (this is what was signed)
241
+ json_payload = base64url_decode(encoded_payload)
242
+
243
+ # Verify signature against JSON string
244
+ keys.any? do |key|
245
+ expected_signature = OpenSSL::HMAC.hexdigest("SHA256", key, json_payload)
246
+ secure_compare(expected_signature, signature)
247
+ end
248
+ rescue StandardError => e
249
+ logger&.warn "Reputable: Failed to verify new format: #{e.message}"
250
+ false
251
+ end
252
+ end
253
+
254
+ def verify_legacy_format(params, keys)
187
255
  status = params["reputable_status"]
188
256
  session_id = params["reputable_session_id"]
189
257
  signature = params["reputable_signature"]
@@ -194,14 +262,7 @@ module Reputable
194
262
 
195
263
  return false unless status && session_id && signature
196
264
 
197
- keys = configuration.trusted_keys
198
- if keys.nil? || keys.empty?
199
- logger&.warn "Reputable: Missing trusted_keys, cannot verify redirect"
200
- return false
201
- end
202
-
203
265
  # Reconstruct data string: status:sessionId:outcome:ignoreAnalytics:country:challengePassed
204
- # Note: optional params default to empty strings if missing in reconstruction logic on server
205
266
  data_parts = [
206
267
  status,
207
268
  session_id,
@@ -213,12 +274,21 @@ module Reputable
213
274
 
214
275
  data = data_parts.join(":")
215
276
 
216
- # Iterate through all trusted keys to find a match
217
277
  keys.any? do |key|
218
278
  expected_signature = OpenSSL::HMAC.hexdigest("SHA256", key, data)
219
279
  secure_compare(expected_signature, signature)
220
280
  end
221
281
  end
282
+
283
+ def base64url_encode(str)
284
+ Base64.urlsafe_encode64(str).gsub("=", "")
285
+ end
286
+
287
+ def base64url_decode(str)
288
+ # Add padding if needed
289
+ padded = str + "=" * ((4 - str.length % 4) % 4)
290
+ Base64.urlsafe_decode64(padded)
291
+ end
222
292
 
223
293
  private
224
294
 
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.13
4
+ version: 0.1.14
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-31 00:00:00.000000000 Z
11
+ date: 2026-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -137,7 +137,6 @@ files:
137
137
  - lib/reputable/reputation.rb
138
138
  - lib/reputable/tracker.rb
139
139
  - lib/reputable/version.rb
140
- - reputable.gemspec
141
140
  homepage: https://github.com/reputable-click/reputable-rb
142
141
  licenses:
143
142
  - MIT
data/reputable.gemspec DELETED
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/reputable/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "reputable"
7
- spec.version = Reputable::VERSION
8
- spec.authors = ["Reputable"]
9
- spec.email = ["support@reputable.click"]
10
-
11
- spec.summary = "Ruby client for Reputable - bot detection and reputation scoring"
12
- spec.description = "Track requests and manage IP reputation through Redis/Dragonfly integration with Reputable"
13
- spec.homepage = "https://github.com/reputable-click/reputable-rb"
14
- spec.license = "MIT"
15
- spec.required_ruby_version = ">= 2.7.0"
16
-
17
- spec.metadata["homepage_uri"] = spec.homepage
18
- spec.metadata["source_code_uri"] = spec.homepage
19
- spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
20
-
21
- spec.files = Dir.chdir(__dir__) do
22
- `git ls-files -z`.split("\x0").reject do |f|
23
- (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)}) || f.end_with?(".gem")
24
- end
25
- end
26
- spec.bindir = "exe"
27
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
- spec.require_paths = ["lib"]
29
-
30
- spec.add_dependency "redis", ">= 4.0", "< 6.0"
31
- spec.add_dependency "connection_pool", "~> 2.2"
32
-
33
- spec.add_development_dependency "bundler", "~> 2.0"
34
- spec.add_development_dependency "rake", "~> 13.0"
35
- spec.add_development_dependency "rspec", "~> 3.12"
36
- spec.add_development_dependency "rack", "~> 2.0"
37
- spec.add_development_dependency "rubocop", "~> 1.0"
38
- end