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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +12 -3
- data/RELEASING.md +26 -0
- data/lib/reputable/blocked_page.rb +171 -0
- data/lib/reputable/configuration.rb +1 -1
- data/lib/reputable/rails.rb +4 -2
- data/lib/reputable/reputation.rb +6 -4
- data/lib/reputable/version.rb +1 -1
- data/lib/reputable.rb +4 -4
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 10ace4d8c54f4fe4bcf4a1a2bbba66015dbff719b353ee9331e9f3ad56400c69
|
|
4
|
+
data.tar.gz: d3044684df204b097e1a739f29425947306ed9c5533d4a20c66b1064dafc8892
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d3e578a48821efe6610ee60d0560c366fa2136e4fcbc93274822abda5cfb776e0aa924fe7877ff3c2164b177328fe630734fbf5d972103d741d5fc5f8db3f65b
|
|
7
|
+
data.tar.gz: 077b28a5af24557650e4e82759f63656532da2586f200b68a1dee2ebb6315c238efa44bf8dbb5a58634eb3cecb1819649772abeb65f78efcd064339453d842e3
|
data/Gemfile.lock
CHANGED
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:
|
|
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
|
|
420
|
-
Reputable.trust_ip(request.ip, reason: '
|
|
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("&", "&")
|
|
164
|
+
.gsub("<", "<")
|
|
165
|
+
.gsub(">", ">")
|
|
166
|
+
.gsub('"', """)
|
|
167
|
+
.gsub("'", "'")
|
|
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:
|
|
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
|
data/lib/reputable/rails.rb
CHANGED
|
@@ -25,11 +25,13 @@ module Reputable
|
|
|
25
25
|
)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
# Trust the current user's IP (
|
|
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
|
data/lib/reputable/reputation.rb
CHANGED
|
@@ -62,14 +62,16 @@ module Reputable
|
|
|
62
62
|
false
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
# Convenience method: Trust an IP (
|
|
66
|
-
|
|
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:
|
|
72
|
+
status: status,
|
|
71
73
|
reason: reason,
|
|
72
|
-
ttl:
|
|
74
|
+
ttl: ttl,
|
|
73
75
|
metadata: metadata
|
|
74
76
|
)
|
|
75
77
|
end
|
data/lib/reputable/version.rb
CHANGED
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.
|
|
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-
|
|
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
|