reputable 0.1.9 → 0.1.10

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: e8d9854d3f0dfbf85029fa9411273ffdebad9288eaa11b91649a3aec538b6b2b
4
- data.tar.gz: 8d7fbfaf636c06c80a1ea291a773d35c1e478f5dd1261efd6e213a473f1d0362
3
+ metadata.gz: 10ace4d8c54f4fe4bcf4a1a2bbba66015dbff719b353ee9331e9f3ad56400c69
4
+ data.tar.gz: d3044684df204b097e1a739f29425947306ed9c5533d4a20c66b1064dafc8892
5
5
  SHA512:
6
- metadata.gz: a13fdb389753a6aed4bde2d9e6bc2304e184db0ded7a579adc6d7fef16d4e1e72a6032f11eec7e6e03e0e706aa2a135c07c17e3e607e227f7670864e84c91aea
7
- data.tar.gz: fb215dbd673959e3414eaa31c0a77a3c78366db796f34b1afbef12d066041b1308b0c6312a90dc7ee953a8b578083192509a535fb3bf9b53f265f9f3b9a22f80
6
+ metadata.gz: d3e578a48821efe6610ee60d0560c366fa2136e4fcbc93274822abda5cfb776e0aa924fe7877ff3c2164b177328fe630734fbf5d972103d741d5fc5f8db3f65b
7
+ data.tar.gz: 077b28a5af24557650e4e82759f63656532da2586f200b68a1dee2ebb6315c238efa44bf8dbb5a58634eb3cecb1819649772abeb65f78efcd064339453d842e3
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- reputable (0.1.9)
4
+ reputable (0.1.10)
5
5
  connection_pool (~> 2.2)
6
6
  redis (>= 4.0, < 6.0)
7
7
 
data/README.md CHANGED
@@ -129,7 +129,7 @@ Reputable.configure do |config|
129
129
  # Customize TTLs (in seconds, 0 = forever)
130
130
  config.default_ttls = {
131
131
  trusted_verified: 0, # Forever
132
- trusted_behavior: 30 * 24 * 3600, # 30 days
132
+ trusted_behavior: 365 * 24 * 3600, # 1 year
133
133
  untrusted_challenge: 7 * 24 * 3600,
134
134
  untrusted_block: 7 * 24 * 3600,
135
135
  untrusted_ignore: 7 * 24 * 3600
@@ -416,8 +416,17 @@ Reputable.track_request_async(
416
416
  ### Reputation Management
417
417
 
418
418
  ```ruby
419
- # Trust IP forever (after payment, verification, etc.)
420
- Reputable.trust_ip(request.ip, reason: 'payment_completed', order_id: order.id)
419
+ # Trust IP (behavioral by default, uses default TTL)
420
+ Reputable.trust_ip(request.ip, reason: 'behavior_trust', order_id: order.id)
421
+
422
+ # Trust IP as verified (forever, explicitly)
423
+ Reputable.trust_ip(
424
+ request.ip,
425
+ reason: 'payment_completed',
426
+ status: :trusted_verified,
427
+ ttl: 0,
428
+ order_id: order.id
429
+ )
421
430
 
422
431
  # Challenge (require CAPTCHA, etc.)
423
432
  Reputable.challenge_ip(request.ip, reason: 'unusual_activity')
data/RELEASING.md ADDED
@@ -0,0 +1,26 @@
1
+ # Releasing the Ruby Gem
2
+
3
+ ## Update version + changelog
4
+
5
+ 1. Bump the version in `clients/ruby/lib/reputable/version.rb`.
6
+ 2. Add a matching entry in `CHANGELOG.md` (repo root).
7
+
8
+ ## Build the gem (optional local check)
9
+
10
+ ```bash
11
+ cd clients/ruby
12
+ bundle exec rake build
13
+ ```
14
+
15
+ ## Build + push from repo root (npm script)
16
+
17
+ ```bash
18
+ npm run release:gem
19
+ ```
20
+
21
+ ## Publish (if applicable)
22
+
23
+ ```bash
24
+ cd clients/ruby
25
+ gem push pkg/reputable-<version>.gem
26
+ ```
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reputable
4
+ # Simple HTML blocked page renderer for Rack/Rails usage.
5
+ module BlockedPage
6
+ class << self
7
+ def response(
8
+ site_name: nil,
9
+ support_email: nil,
10
+ support_url: nil,
11
+ client_ip: nil,
12
+ heading: "Access blocked",
13
+ message: nil,
14
+ status: 403,
15
+ show_ip: true
16
+ )
17
+ html = build_html(
18
+ site_name: site_name,
19
+ support_email: support_email,
20
+ support_url: support_url,
21
+ client_ip: client_ip,
22
+ heading: heading,
23
+ message: message,
24
+ show_ip: show_ip
25
+ )
26
+
27
+ headers = {
28
+ "Content-Type" => "text/html; charset=utf-8",
29
+ "Cache-Control" => "no-store"
30
+ }
31
+
32
+ [status, headers, [html]]
33
+ end
34
+
35
+ def html(**options)
36
+ build_html(**options)
37
+ end
38
+
39
+ private
40
+
41
+ def build_html(site_name:, support_email:, support_url:, client_ip:, heading:, message:, show_ip:)
42
+ display_name = escape_html(site_name || "this site")
43
+ safe_support_email = support_email ? escape_html(support_email) : nil
44
+ safe_support_url = support_url ? escape_html(support_url) : nil
45
+ safe_heading = escape_html(heading || "Access blocked")
46
+ safe_message = escape_html(
47
+ message || "We cannot allow this request for #{display_name}. Your connection was blocked by a security policy."
48
+ )
49
+
50
+ contact_line = if safe_support_email || safe_support_url
51
+ parts = []
52
+ parts << if safe_support_email
53
+ %(at <a href="mailto:#{safe_support_email}">#{safe_support_email}</a>)
54
+ else
55
+ nil
56
+ end
57
+ parts << if safe_support_url
58
+ %(via <a href="#{safe_support_url}">support</a>)
59
+ else
60
+ nil
61
+ end
62
+ parts.compact.join(" or ")
63
+ else
64
+ "by contacting the site owner"
65
+ end
66
+
67
+ ip_line = if show_ip && client_ip && !client_ip.to_s.empty?
68
+ %(<div class="ip-text">Your IP: #{escape_html(client_ip.to_s)}</div>)
69
+ else
70
+ ""
71
+ end
72
+
73
+ <<~HTML
74
+ <!DOCTYPE html>
75
+ <html lang="en">
76
+ <head>
77
+ <meta charset="UTF-8">
78
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
79
+ <title>Access blocked</title>
80
+ <style>
81
+ :root {
82
+ --bg-color: #fafafa;
83
+ --card-bg: #ffffff;
84
+ --text-color: #333333;
85
+ --text-muted: #6b7280;
86
+ --border-color: #e5e7eb;
87
+ --link-color: #2563eb;
88
+ }
89
+ @media (prefers-color-scheme: dark) {
90
+ :root {
91
+ --bg-color: #0f0f0f;
92
+ --card-bg: #1a1a1a;
93
+ --text-color: #f3f4f6;
94
+ --text-muted: #9ca3af;
95
+ --border-color: #374151;
96
+ --link-color: #60a5fa;
97
+ }
98
+ }
99
+ * { box-sizing: border-box; }
100
+ body {
101
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
102
+ background: var(--bg-color);
103
+ color: var(--text-color);
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ min-height: 100vh;
108
+ margin: 0;
109
+ padding: 20px;
110
+ }
111
+ .card {
112
+ max-width: 520px;
113
+ width: 100%;
114
+ padding: 40px;
115
+ background: var(--card-bg);
116
+ border: 1px solid var(--border-color);
117
+ border-radius: 12px;
118
+ text-align: center;
119
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
120
+ }
121
+ h1 { margin: 0 0 12px; font-size: 22px; font-weight: 600; }
122
+ p { margin: 0 0 20px; line-height: 1.5; color: var(--text-muted); }
123
+ a { color: var(--link-color); text-decoration: none; }
124
+ a:hover { text-decoration: underline; }
125
+ .muted { font-size: 13px; color: var(--text-muted); }
126
+ .branding {
127
+ display: flex;
128
+ align-items: center;
129
+ justify-content: center;
130
+ gap: 8px;
131
+ margin-top: 24px;
132
+ padding-top: 20px;
133
+ border-top: 1px solid var(--border-color);
134
+ }
135
+ .branding-text {
136
+ font-size: 12px;
137
+ color: var(--text-muted);
138
+ }
139
+ .ip-text {
140
+ margin-top: 12px;
141
+ font-size: 11px;
142
+ color: var(--text-muted);
143
+ }
144
+ </style>
145
+ </head>
146
+ <body>
147
+ <div class="card">
148
+ <h1>#{safe_heading}</h1>
149
+ <p>#{safe_message}</p>
150
+ <p class="muted">If you believe this is a mistake, you can reach #{display_name} #{contact_line}.</p>
151
+ <div class="branding">
152
+ <span class="branding-text">Protected by Reputable</span>
153
+ </div>
154
+ #{ip_line}
155
+ </div>
156
+ </body>
157
+ </html>
158
+ HTML
159
+ end
160
+
161
+ def escape_html(text)
162
+ text.to_s
163
+ .gsub("&", "&amp;")
164
+ .gsub("<", "&lt;")
165
+ .gsub(">", "&gt;")
166
+ .gsub('"', "&quot;")
167
+ .gsub("'", "&#39;")
168
+ end
169
+ end
170
+ end
171
+ end
@@ -24,7 +24,7 @@ module Reputable
24
24
  # Default TTLs in seconds (0 = forever)
25
25
  DEFAULT_TTLS = {
26
26
  trusted_verified: 0, # Forever
27
- trusted_behavior: 30 * 24 * 3600, # 30 days
27
+ trusted_behavior: 365 * 24 * 3600, # 1 year
28
28
  untrusted_challenge: 7 * 24 * 3600, # 7 days
29
29
  untrusted_block: 7 * 24 * 3600, # 7 days
30
30
  untrusted_ignore: 7 * 24 * 3600 # 7 days
@@ -25,11 +25,13 @@ module Reputable
25
25
  )
26
26
  end
27
27
 
28
- # Trust the current user's IP (e.g., after payment)
29
- def trust_current_ip(reason:, **metadata)
28
+ # Trust the current user's IP (behavioral by default, verified optional)
29
+ def trust_current_ip(reason:, status: :trusted_behavior, ttl: nil, **metadata)
30
30
  Reputable::Reputation.trust_ip(
31
31
  request.remote_ip,
32
32
  reason: reason,
33
+ status: status,
34
+ ttl: ttl,
33
35
  **metadata
34
36
  )
35
37
  end
@@ -62,14 +62,16 @@ module Reputable
62
62
  false
63
63
  end
64
64
 
65
- # Convenience method: Trust an IP (verified status, forever TTL)
66
- def trust_ip(ip, reason: "manual_trust", **metadata)
65
+ # Convenience method: Trust an IP (behavioral by default)
66
+ # @param status [Symbol] :trusted_behavior or :trusted_verified
67
+ # @param ttl [Integer, nil] TTL in seconds (0 = forever, nil = use default)
68
+ def trust_ip(ip, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **metadata)
67
69
  apply(
68
70
  entity_type: :ip,
69
71
  entity_id: ip,
70
- status: :trusted_verified,
72
+ status: status,
71
73
  reason: reason,
72
- ttl: 0,
74
+ ttl: ttl,
73
75
  metadata: metadata
74
76
  )
75
77
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Reputable
4
- VERSION = "0.1.9"
4
+ VERSION = "0.1.10"
5
5
  end
data/lib/reputable.rb CHANGED
@@ -34,8 +34,8 @@ end
34
34
  # path: "/products/123"
35
35
  # )
36
36
  #
37
- # @example Apply reputation after payment
38
- # Reputable.trust_ip("203.0.113.45", reason: "payment_completed")
37
+ # @example Apply verified reputation after payment
38
+ # Reputable.trust_ip("203.0.113.45", reason: "payment_completed", status: :trusted_verified, ttl: 0)
39
39
  #
40
40
  # @example Disable completely via ENV
41
41
  # # In your environment: REPUTABLE_ENABLED=false
@@ -89,8 +89,8 @@ module Reputable
89
89
  Reputation.apply(entity_type: entity_type, entity_id: entity_id, status: status, **options)
90
90
  end
91
91
 
92
- def trust_ip(ip, reason: "manual_trust", **metadata)
93
- Reputation.trust_ip(ip, reason: reason, **metadata)
92
+ def trust_ip(ip, reason: "manual_trust", status: :trusted_behavior, ttl: nil, **metadata)
93
+ Reputation.trust_ip(ip, reason: reason, status: status, ttl: ttl, **metadata)
94
94
  end
95
95
 
96
96
  def block_ip(ip, reason: "manual_block", **metadata)
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.9
4
+ version: 0.1.10
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-29 00:00:00.000000000 Z
11
+ date: 2025-12-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -126,8 +126,10 @@ files:
126
126
  - Gemfile
127
127
  - Gemfile.lock
128
128
  - README.md
129
+ - RELEASING.md
129
130
  - Rakefile
130
131
  - lib/reputable.rb
132
+ - lib/reputable/blocked_page.rb
131
133
  - lib/reputable/configuration.rb
132
134
  - lib/reputable/connection.rb
133
135
  - lib/reputable/middleware.rb