shugoi 0.4.6 → 0.4.7
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/CHANGELOG.md +15 -0
- data/README.md +3 -2
- data/lib/shugoi/api_client.rb +11 -22
- data/lib/shugoi/bot_policy.rb +15 -0
- data/lib/shugoi/bot_verifier.rb +3 -12
- data/lib/shugoi/challenge_limiter.rb +38 -0
- data/lib/shugoi/challenge_path.rb +12 -0
- data/lib/shugoi/challenge_script.rb +24 -0
- data/lib/shugoi/challenge_url.rb +15 -0
- data/lib/shugoi/config.rb +3 -7
- data/lib/shugoi/config_cache.rb +29 -14
- data/lib/shugoi/constants.rb +2 -4
- data/lib/shugoi/core.rb +43 -186
- data/lib/shugoi/csp.rb +3 -21
- data/lib/shugoi/errors.rb +0 -2
- data/lib/shugoi/guard_cache.rb +1 -8
- data/lib/shugoi/guard_injector.rb +31 -0
- data/lib/shugoi/html_store.rb +1 -5
- data/lib/shugoi/key_validator.rb +36 -0
- data/lib/shugoi/locales.rb +15 -9
- data/lib/shugoi/notice.rb +59 -87
- data/lib/shugoi/pow.rb +3 -24
- data/lib/shugoi/rack/middleware.rb +11 -118
- data/lib/shugoi/rails/configuration.rb +4 -7
- data/lib/shugoi/rails/railtie.rb +0 -10
- data/lib/shugoi/rate_limit_formatter.rb +13 -0
- data/lib/shugoi/render_endpoint.rb +24 -0
- data/lib/shugoi/render_handler.rb +0 -22
- data/lib/shugoi/response_processor.rb +36 -0
- data/lib/shugoi/shield_page.rb +82 -0
- data/lib/shugoi/skeleton_generator.rb +25 -30
- data/lib/shugoi/token_signer.rb +0 -15
- data/lib/shugoi/utils.rb +0 -25
- data/lib/shugoi/version.rb +1 -3
- data/lib/shugoi.rb +12 -3
- metadata +44 -13
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Shugoi
|
|
2
|
+
class ShieldPage
|
|
3
|
+
Input = Struct.new(:title, :message, :badge, :host, :remaining_seconds, :locale, keyword_init: true)
|
|
4
|
+
|
|
5
|
+
def self.render(**attributes)
|
|
6
|
+
new(Input.new(**attributes)).render
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def initialize(input)
|
|
10
|
+
@input = input
|
|
11
|
+
@messages = Locales.messages(input.locale)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def render
|
|
15
|
+
title = escaped(@input.title, :blocked_title)
|
|
16
|
+
badge = escaped(@input.badge, :blocked_badge)
|
|
17
|
+
host = Utils.escape_html((@input.host.to_s.empty? ? "shugoi.com" : @input.host).slice(0, 120))
|
|
18
|
+
description = Utils.escape_html(description_text)
|
|
19
|
+
language = @input.locale == "fr" ? "fr" : "en"
|
|
20
|
+
<<~HTML
|
|
21
|
+
<!DOCTYPE html>
|
|
22
|
+
<html lang="#{language}">
|
|
23
|
+
<head>
|
|
24
|
+
<meta charset="UTF-8">
|
|
25
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
26
|
+
<style>#{styles}</style>
|
|
27
|
+
</head>
|
|
28
|
+
<body>
|
|
29
|
+
<div id="c">
|
|
30
|
+
<img src="https://shugoi.com/favicon.png" alt class="l">
|
|
31
|
+
<img src="https://shugoi.com/brand.png" alt class="b">
|
|
32
|
+
<div class="bdg">#{badge}</div>
|
|
33
|
+
<h2>#{title}</h2>
|
|
34
|
+
<p class="desc">#{description}</p>
|
|
35
|
+
<p class="ft">#{host} · Shugoi</p>
|
|
36
|
+
</div>
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
39
|
+
HTML
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def escaped(value, fallback)
|
|
45
|
+
Utils.escape_html(value.to_s.empty? ? @messages[fallback] : value)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def description_text
|
|
49
|
+
return @input.message.to_s unless @input.remaining_seconds.positive?
|
|
50
|
+
|
|
51
|
+
prefix = @input.message.to_s.sub(/Il reste \d+ seconde?s?.*$/, "").sub(/Retry in \d+s?.*$/, "").strip
|
|
52
|
+
"#{prefix} #{@messages[:retry_in_seconds].call(@input.remaining_seconds)}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def countdown_script
|
|
56
|
+
return "" unless @input.remaining_seconds.positive?
|
|
57
|
+
|
|
58
|
+
<<~JS.delete("\n").strip
|
|
59
|
+
<script>
|
|
60
|
+
var s=#{@input.remaining_seconds};
|
|
61
|
+
var i=setInterval(function(){
|
|
62
|
+
s--;
|
|
63
|
+
var e=document.getElementById("cd");
|
|
64
|
+
if(e){
|
|
65
|
+
if(s<=0){e.innerHTML="0s";clearInterval(i);setTimeout(function(){location.reload()},500)}
|
|
66
|
+
else{e.innerHTML=s+"s"}
|
|
67
|
+
}
|
|
68
|
+
},1000)
|
|
69
|
+
</script>
|
|
70
|
+
JS
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def styles
|
|
74
|
+
<<~CSS.delete("\n").strip
|
|
75
|
+
@font-face{font-family:'Alex Brush';src:url(https://shugoi.com/alex-brush.woff2?v=2) format('woff2');font-display:swap}
|
|
76
|
+
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
77
|
+
html,body{height:100%;background:#fcf9f5}
|
|
78
|
+
body{font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;display:flex;align-items:center;justify-content:center;padding:1.2rem}
|
|
79
|
+
CSS
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
1
|
module Shugoi
|
|
4
|
-
# Génère le skeleton HTML (bootcode unicode) injecté dans la page.
|
|
5
|
-
# Parité avec generateSkeleton (render.ts) — mêmes fragments JS, même encodage unicode.
|
|
6
2
|
class SkeletonGenerator
|
|
7
3
|
def initialize(config, guard_cache, config_cache, token_signer)
|
|
8
4
|
@config = config
|
|
@@ -11,7 +7,6 @@ module Shugoi
|
|
|
11
7
|
@token_signer = token_signer
|
|
12
8
|
end
|
|
13
9
|
|
|
14
|
-
# @return [String] HTML du skeleton (<script>…eval([...])…</script>)
|
|
15
10
|
def generate(site_key, token, base_url, render_url = "./__shugoi/render", locale = nil)
|
|
16
11
|
locale ||= Locales.resolve_locale(@config.locale, nil)
|
|
17
12
|
@guard_cache.ensure_ready(site_key, @config.signing_secret)
|
|
@@ -24,10 +19,11 @@ module Shugoi
|
|
|
24
19
|
fragments << "window.__sg_siteKey=#{json(site_key)}"
|
|
25
20
|
fragments << "window.__sg_baseUrl=#{json(base_url)}"
|
|
26
21
|
fragments << "window.__sg_config=#{json(flags)}"
|
|
27
|
-
# Mode debug (audit #8) : piloté UNIQUEMENT par le serveur. En production ce flag
|
|
28
|
-
# est toujours false → le guard n'active jamais ses traces via ?sg_probe_debug=1.
|
|
29
22
|
fragments << "window.__sg_diagEnabled=#{production? ? 'false' : 'true'}"
|
|
30
|
-
fragments << "try{if((location.search||'').indexOf('sg_proof=')>=0){
|
|
23
|
+
fragments << "try{if((location.search||'').indexOf('sg_proof=')>=0){" \
|
|
24
|
+
"var _qs=location.search.replace(/[?&]sg_proof=[^&]*/," \
|
|
25
|
+
"'');var _cu=location.pathname+(_qs?_qs:'')+location.hash;" \
|
|
26
|
+
"history.replaceState(null,'',_cu)}}catch(e){}"
|
|
31
27
|
|
|
32
28
|
pow = Pow.new(@config.signing_secret, @config.pow_difficulty, @config.pow_ttl_ms).challenge
|
|
33
29
|
fragments << "window.__sg_pow=#{json(pow)}"
|
|
@@ -37,30 +33,21 @@ module Shugoi
|
|
|
37
33
|
fragments << "window.__sg_clockts=#{now_ms}"
|
|
38
34
|
fragments << "window.__sg_disableRestrictedAccess=true" unless @config.restricted_access
|
|
39
35
|
fragments << "try{#{detect}}catch(e){window.__sg_blocked=true}" if detect
|
|
40
|
-
|
|
41
|
-
# __sg_showBlock (page de blocage néobrutaliste) — messages localisés interpolés.
|
|
42
36
|
fragments << show_block_fragment(msgs)
|
|
43
37
|
|
|
44
|
-
fragments << "var t
|
|
45
|
-
fragments << "window.__sg_token
|
|
46
|
-
fragments << "var k
|
|
47
|
-
fragments << "var b
|
|
48
|
-
fragments << "var r
|
|
49
|
-
|
|
50
|
-
# rd(p,n) : remplacement du document par le contenu rendu.
|
|
38
|
+
fragments << "var t=#{json(token)}"
|
|
39
|
+
fragments << "window.__sg_token=#{json(token)}"
|
|
40
|
+
fragments << "var k=#{json(site_key)}"
|
|
41
|
+
fragments << "var b=#{json(base_url)}"
|
|
42
|
+
fragments << "var r=#{json(render_url)}"
|
|
51
43
|
fragments << rd_fragment(msgs)
|
|
52
44
|
|
|
53
45
|
fragments << "_gw(function(){rd(r+\"?token=\"+t,0);setTimeout(_sgCl,1500)})"
|
|
54
46
|
fragments << cleanup_fragment
|
|
55
47
|
|
|
56
48
|
combined = fragments.join(";")
|
|
57
|
-
# Échappe `</script>` et `</style>` AVANT l'encodage unicode : sans ça, le HTML parser
|
|
58
|
-
# du navigateur coupe le <script> dès qu'il rencontre `</script>` dans le bootcode.
|
|
59
49
|
combined = combined.gsub(%r{</(script|style)}i, "<\\/$1")
|
|
60
|
-
|
|
61
|
-
decoded_call = "[...'#{enc}'].map(x=>String.fromCodePoint(x.codePointAt(0)-917504)).join('')"
|
|
62
|
-
# <meta charset=UTF-8> : garantit que le navigateur lit le bootcode unicode en UTF-8.
|
|
63
|
-
"<meta charset=\"UTF-8\"><script>eval(#{decoded_call})</script>"
|
|
50
|
+
"<meta charset=\"UTF-8\"><script>#{combined}</script>"
|
|
64
51
|
end
|
|
65
52
|
|
|
66
53
|
private
|
|
@@ -73,7 +60,6 @@ module Shugoi
|
|
|
73
60
|
ENV["NODE_ENV"] == "production" || ENV["RACK_ENV"] == "production" || ENV["RAILS_ENV"] == "production"
|
|
74
61
|
end
|
|
75
62
|
|
|
76
|
-
# jsStr : chaîne JSON sans les guillemets externes + échappe `<` (parité render.ts).
|
|
77
63
|
def js_str(s)
|
|
78
64
|
JSON.generate(s.to_s).slice(1..-2).gsub("<", "\\x3c")
|
|
79
65
|
end
|
|
@@ -81,23 +67,32 @@ module Shugoi
|
|
|
81
67
|
def show_block_fragment(msgs)
|
|
82
68
|
fb_badge = js_str(msgs[:blocked_badge])
|
|
83
69
|
fb_title = js_str(msgs[:blocked_title])
|
|
84
|
-
css = "
|
|
85
|
-
|
|
70
|
+
css = <<~CSS.delete("\\n").strip
|
|
71
|
+
html,body{height:100%;background:#fcf9f5}body{font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;display:flex;align-items:center;justify-content:center;padding:1.2rem}#c{max-width:460px;width:100%;background:#fff;border:4px solid #000;border-radius:28px 6px 32px 10px;box-shadow:12px 12px 0 #000;padding:3rem 2.4rem 2.8rem;text-align:center}
|
|
72
|
+
CSS
|
|
73
|
+
<<~JS.delete("\\n").strip
|
|
74
|
+
window.__sg_showBlock=function(msg,title,badge){var h="<head><meta charset=UTF-8><meta name=viewport content=width=device-width,initial-scale=1><style>@font-face{font-family:\\x27Alex Brush\\x27;src:url(https://shugoi.com/alex-brush.woff2?v=2) format(\\x27woff2\\x27);font-display:swap}*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}#{css}</style></head>
|
|
75
|
+
<body><div id=c><img src=https://shugoi.com/favicon-block.png class=l><img src=https://shugoi.com/brand-block.png class=b><div class=bdg>"+(badge||"#{fb_badge}")+"</div><h2>"+(title||"#{fb_title}")+"</h2><p class=desc>"+(msg||"")+"</p><p class=ft>"+location.hostname+" \\u00b7 Shugoi</p></div></body>";document.documentElement.innerHTML=h}
|
|
76
|
+
JS
|
|
86
77
|
end
|
|
87
78
|
|
|
88
79
|
def rd_fragment(msgs)
|
|
89
80
|
devtools = js_str(msgs[:devtools_body])
|
|
90
81
|
tamper_title = js_str(msgs[:tamper_title])
|
|
91
|
-
"var _gw=function(cb){if(window.__sg_guardsReady||window.__sg_blocked)cb();
|
|
82
|
+
"var _gw=function(cb){if(window.__sg_guardsReady||window.__sg_blocked)cb();" \
|
|
83
|
+
"else setTimeout(function(){_gw(cb)},100)};" \
|
|
92
84
|
"function rd(p,n){if(window.__sg_blocked)return;if(!document.body)return setTimeout(function(){rd(p,n)},50);" \
|
|
93
|
-
"if(n>6){if((window.__sg_config||{}).enableContentReplacementCheck===true)
|
|
85
|
+
"if(n>6){if((window.__sg_config||{}).enableContentReplacementCheck===true)" \
|
|
86
|
+
"window.__sg_showBlock&&window.__sg_showBlock(\"#{devtools}\",\"#{tamper_title}\");return}" \
|
|
94
87
|
"var _g=(window.__sg_grant||\"\");if(_g){p=p+(\"&grant=\"+encodeURIComponent(_g))}" \
|
|
95
88
|
"var _m=(window.__sg_detectMid||window.__sg_mid||\"\");if(_m){p=p+(\"&mid=\"+encodeURIComponent(_m))}" \
|
|
96
89
|
"fetch(p).then(function(x){return x.json()}).then(function(d){if(window.__sg_blocked)return;" \
|
|
97
|
-
"if(!document.body)return setTimeout(function(){rd(p,n+1)},
|
|
90
|
+
"if(!document.body)return setTimeout(function(){rd(p,n+1)}," \
|
|
91
|
+
"50);" \
|
|
98
92
|
"if(d.html){document.open(\"text/html\");document.write(d.html);document.close();window.scrollTo(0,0)}" \
|
|
99
93
|
"if(d.blocked){window.__sg_showBlock&&window.__sg_showBlock(d.message,d.title)}" \
|
|
100
|
-
"if(d.error){if((window.__sg_config||{}).enableContentReplacementCheck===true)
|
|
94
|
+
"if(d.error){if((window.__sg_config||{}).enableContentReplacementCheck===true)" \
|
|
95
|
+
"window.__sg_showBlock&&window.__sg_showBlock(\"#{devtools}\",\"#{tamper_title}\")}" \
|
|
101
96
|
"else if(!d.html&&!d.blocked){setTimeout(function(){rd(p,n+1)},300)}})" \
|
|
102
97
|
".catch(function(){setTimeout(function(){rd(p,n+1)},300)})}"
|
|
103
98
|
end
|
data/lib/shugoi/token_signer.rb
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
1
|
module Shugoi
|
|
4
|
-
# Signature des tokens render + vérification des render-grants.
|
|
5
|
-
# Parité exacte avec le module Node :
|
|
6
|
-
# token : siteKey:timestamp:nonce:sig (sig = HMAC(secret, "siteKey:ts:nonce"))
|
|
7
|
-
# grant : base36(ts):HMAC(secret, "render-grant:siteKey:mid:token:ip:ts")
|
|
8
2
|
class TokenSigner
|
|
9
|
-
# Parité module Node (render.ts) : TTL du render-grant réduit à 60 s (audit 2026-08-03).
|
|
10
3
|
GRANT_TTL_MS = 60_000
|
|
11
4
|
|
|
12
5
|
def initialize(secret)
|
|
13
6
|
@secret = secret.to_s
|
|
14
7
|
end
|
|
15
8
|
|
|
16
|
-
# @return [String] token signé, ou "" si pas de secret
|
|
17
9
|
def sign(site_key, timestamp = Utils.now_ms)
|
|
18
10
|
return "" if @secret.empty?
|
|
19
11
|
nonce = SecureRandom.hex(8)
|
|
@@ -22,12 +14,6 @@ module Shugoi
|
|
|
22
14
|
"#{payload}:#{sig}"
|
|
23
15
|
end
|
|
24
16
|
|
|
25
|
-
# Vérifie un render-grant émis par le wlc serveur.
|
|
26
|
-
# @param mid [String] machineId (64 hex)
|
|
27
|
-
# @param grant [String] grant brut "base36ts:sig"
|
|
28
|
-
# @param token [String] token render lié
|
|
29
|
-
# @param ip [String] IP du client
|
|
30
|
-
# @param expected_site_key [String] siteKey attendu (celui du middleware)
|
|
31
17
|
def verify_render_grant(mid, grant, token, ip, expected_site_key)
|
|
32
18
|
return true if @secret.empty? # fail-safe : pas de secret → pas de vérification
|
|
33
19
|
return false if grant.to_s.empty? || mid.to_s.empty?
|
|
@@ -46,7 +32,6 @@ module Shugoi
|
|
|
46
32
|
Utils.secure_equals(sig, expected)
|
|
47
33
|
end
|
|
48
34
|
|
|
49
|
-
# Vérifie un token render complet (siteKey:ts:nonce:sig).
|
|
50
35
|
def verify_token(token)
|
|
51
36
|
parts = token.to_s.split(":")
|
|
52
37
|
return false unless parts.length == 4 && parts[3].length == 64
|
data/lib/shugoi/utils.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
1
|
require "openssl"
|
|
4
2
|
require "securerandom"
|
|
5
3
|
require "json"
|
|
@@ -8,7 +6,6 @@ module Shugoi
|
|
|
8
6
|
module Utils
|
|
9
7
|
module_function
|
|
10
8
|
|
|
11
|
-
# Comparaison constant-time de deux hex strings.
|
|
12
9
|
def secure_equals(a, b)
|
|
13
10
|
return false unless a.is_a?(String) && b.is_a?(String)
|
|
14
11
|
return false unless a.bytesize == b.bytesize
|
|
@@ -39,11 +36,6 @@ module Shugoi
|
|
|
39
36
|
str.to_s.to_i(36)
|
|
40
37
|
end
|
|
41
38
|
|
|
42
|
-
# Nombre de bits à zéro en tête du digest hex (parité avec la fonction JS `bits`
|
|
43
|
-
# du module Node). Audit 2026-08-03 : comptage CORRIGÉ — l'ancienne version
|
|
44
|
-
# (nib.to_s(2).match(/^0*/)[0].length) sous-comptait les zéros internes du premier
|
|
45
|
-
# nibble non-nul (`3` → '11' → 0 au lieu de 2). Le comptage ci-dessous est exact et
|
|
46
|
-
# DOIT rester synchrone avec core.rb (isPowValid), le challenge JS et le guard.
|
|
47
39
|
def leading_zero_bits(hex_digest)
|
|
48
40
|
leading = 0
|
|
49
41
|
hex_digest.each_char do |c|
|
|
@@ -66,27 +58,10 @@ module Shugoi
|
|
|
66
58
|
Time.now.to_i
|
|
67
59
|
end
|
|
68
60
|
|
|
69
|
-
# Encodage unicode (parité Node). Le module Node encode avec `charCodeAt(i)` qui donne
|
|
70
|
-
# le CODE UNIT UTF-16 (0-65535) — pour les caractères non-BMP (émojis…), charCodeAt
|
|
71
|
-
# produit DEUX unités (surrogate pair). Ruby `each_codepoint` donnerait le code point
|
|
72
|
-
# complet (> 65535) → désynchronisation avec le décodage navigateur `codePointAt(0)`.
|
|
73
|
-
# On encode donc par code unit UTF-16, exactement comme JS charCodeAt.
|
|
74
|
-
# Le guard-detect (raw=1) peut être en ASCII-8BIT (bytes obfusqués > 0x7F) : on force
|
|
75
|
-
# une conversion UTF-8 avec remplacement, comme le fetch().text() du module Node.
|
|
76
|
-
def unicode_encode(code)
|
|
77
|
-
str = code.dup.force_encoding(Encoding::UTF_8)
|
|
78
|
-
str = str.encode(Encoding::UTF_16LE, invalid: :replace, undef: :replace, replace: "\uFFFD")
|
|
79
|
-
str.bytes.each_slice(2).map do |lo, hi|
|
|
80
|
-
unit = lo | (hi << 8)
|
|
81
|
-
(917504 + unit).chr(Encoding::UTF_8)
|
|
82
|
-
end.join
|
|
83
|
-
end
|
|
84
|
-
|
|
85
61
|
def escape_json_string(s)
|
|
86
62
|
JSON.generate(s.to_s)[1..-2]
|
|
87
63
|
end
|
|
88
64
|
|
|
89
|
-
# Échappement HTML (parité escapeHtml de core.ts).
|
|
90
65
|
def escape_html(s)
|
|
91
66
|
s.to_s
|
|
92
67
|
.gsub("&", "&")
|
data/lib/shugoi/version.rb
CHANGED
data/lib/shugoi.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
1
|
require_relative "shugoi/version"
|
|
4
2
|
require_relative "shugoi/errors"
|
|
5
3
|
require_relative "shugoi/constants"
|
|
@@ -13,6 +11,17 @@ require_relative "shugoi/bot_verifier"
|
|
|
13
11
|
require_relative "shugoi/api_client"
|
|
14
12
|
require_relative "shugoi/guard_cache"
|
|
15
13
|
require_relative "shugoi/config_cache"
|
|
14
|
+
require_relative "shugoi/rate_limit_formatter"
|
|
15
|
+
require_relative "shugoi/challenge_path"
|
|
16
|
+
require_relative "shugoi/challenge_limiter"
|
|
17
|
+
require_relative "shugoi/shield_page"
|
|
18
|
+
require_relative "shugoi/key_validator"
|
|
19
|
+
require_relative "shugoi/challenge_script"
|
|
20
|
+
require_relative "shugoi/bot_policy"
|
|
21
|
+
require_relative "shugoi/challenge_url"
|
|
22
|
+
require_relative "shugoi/render_endpoint"
|
|
23
|
+
require_relative "shugoi/guard_injector"
|
|
24
|
+
require_relative "shugoi/response_processor"
|
|
16
25
|
require_relative "shugoi/html_store"
|
|
17
26
|
require_relative "shugoi/notice"
|
|
18
27
|
require_relative "shugoi/skeleton_generator"
|
|
@@ -20,7 +29,7 @@ require_relative "shugoi/render_handler"
|
|
|
20
29
|
require_relative "shugoi/core"
|
|
21
30
|
require_relative "shugoi/rack/middleware"
|
|
22
31
|
require_relative "shugoi/rails/configuration"
|
|
23
|
-
require_relative "shugoi/rails/railtie" if defined?(
|
|
32
|
+
require_relative "shugoi/rails/railtie" if defined?(Rails::Railtie)
|
|
24
33
|
|
|
25
34
|
module Shugoi
|
|
26
35
|
def self.new(app, options = {})
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: shugoi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shugoi
|
|
@@ -10,59 +10,83 @@ cert_chain: []
|
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
|
-
name:
|
|
13
|
+
name: activesupport
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
18
|
+
version: '6.0'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '9'
|
|
19
22
|
type: :runtime
|
|
20
23
|
prerelease: false
|
|
21
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
25
|
requirements:
|
|
23
26
|
- - ">="
|
|
24
27
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '
|
|
28
|
+
version: '6.0'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '9'
|
|
26
32
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
33
|
+
name: concurrent-ruby
|
|
28
34
|
requirement: !ruby/object:Gem::Requirement
|
|
29
35
|
requirements:
|
|
30
36
|
- - ">="
|
|
31
37
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
38
|
+
version: '1.1'
|
|
39
|
+
- - "<"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '2'
|
|
33
42
|
type: :runtime
|
|
34
43
|
prerelease: false
|
|
35
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
45
|
requirements:
|
|
37
46
|
- - ">="
|
|
38
47
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
48
|
+
version: '1.1'
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '2'
|
|
40
52
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
53
|
+
name: rack
|
|
42
54
|
requirement: !ruby/object:Gem::Requirement
|
|
43
55
|
requirements:
|
|
44
56
|
- - ">="
|
|
45
57
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
58
|
+
version: '2.2'
|
|
59
|
+
- - "<"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '4'
|
|
47
62
|
type: :runtime
|
|
48
63
|
prerelease: false
|
|
49
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
65
|
requirements:
|
|
51
66
|
- - ">="
|
|
52
67
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
68
|
+
version: '2.2'
|
|
69
|
+
- - "<"
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: '4'
|
|
54
72
|
description: Rack middleware + Rails integration for Shugoi. Blocks bots, scrapers
|
|
55
|
-
and headless clients with a proof-of-work challenge,
|
|
56
|
-
|
|
73
|
+
and headless clients with a proof-of-work challenge, environment signals and split-render,
|
|
74
|
+
mirroring the Node.js module.
|
|
57
75
|
executables: []
|
|
58
76
|
extensions: []
|
|
59
77
|
extra_rdoc_files: []
|
|
60
78
|
files:
|
|
79
|
+
- CHANGELOG.md
|
|
61
80
|
- LICENSE
|
|
62
81
|
- README.md
|
|
63
82
|
- lib/shugoi.rb
|
|
64
83
|
- lib/shugoi/api_client.rb
|
|
84
|
+
- lib/shugoi/bot_policy.rb
|
|
65
85
|
- lib/shugoi/bot_verifier.rb
|
|
86
|
+
- lib/shugoi/challenge_limiter.rb
|
|
87
|
+
- lib/shugoi/challenge_path.rb
|
|
88
|
+
- lib/shugoi/challenge_script.rb
|
|
89
|
+
- lib/shugoi/challenge_url.rb
|
|
66
90
|
- lib/shugoi/config.rb
|
|
67
91
|
- lib/shugoi/config_cache.rb
|
|
68
92
|
- lib/shugoi/constants.rb
|
|
@@ -70,14 +94,20 @@ files:
|
|
|
70
94
|
- lib/shugoi/csp.rb
|
|
71
95
|
- lib/shugoi/errors.rb
|
|
72
96
|
- lib/shugoi/guard_cache.rb
|
|
97
|
+
- lib/shugoi/guard_injector.rb
|
|
73
98
|
- lib/shugoi/html_store.rb
|
|
99
|
+
- lib/shugoi/key_validator.rb
|
|
74
100
|
- lib/shugoi/locales.rb
|
|
75
101
|
- lib/shugoi/notice.rb
|
|
76
102
|
- lib/shugoi/pow.rb
|
|
77
103
|
- lib/shugoi/rack/middleware.rb
|
|
78
104
|
- lib/shugoi/rails/configuration.rb
|
|
79
105
|
- lib/shugoi/rails/railtie.rb
|
|
106
|
+
- lib/shugoi/rate_limit_formatter.rb
|
|
107
|
+
- lib/shugoi/render_endpoint.rb
|
|
80
108
|
- lib/shugoi/render_handler.rb
|
|
109
|
+
- lib/shugoi/response_processor.rb
|
|
110
|
+
- lib/shugoi/shield_page.rb
|
|
81
111
|
- lib/shugoi/skeleton_generator.rb
|
|
82
112
|
- lib/shugoi/token_signer.rb
|
|
83
113
|
- lib/shugoi/utils.rb
|
|
@@ -87,7 +117,8 @@ licenses:
|
|
|
87
117
|
- MIT
|
|
88
118
|
metadata:
|
|
89
119
|
homepage_uri: https://shugoi.com
|
|
90
|
-
source_code_uri: https://github.com/
|
|
120
|
+
source_code_uri: https://github.com/ShugoiTeam/shugoi-ruby
|
|
121
|
+
rubygems_mfa_required: 'true'
|
|
91
122
|
rdoc_options: []
|
|
92
123
|
require_paths:
|
|
93
124
|
- lib
|