nemesis-shield 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 +4 -4
- data/nemesis_shield.rb +18 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fe007575d89dca3c4c77078c517dd4acd58b19ae65368325e730cfbe977473a5
|
|
4
|
+
data.tar.gz: a16c094ba7f5977a224aedbd236af7411632f5a1f104ff4a6751797c8bf245c4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5bf4a5413932c7e3f0df3e16a0ad9b6b958b6e4d77f0a756f800c83db06eef88973bd29fe99e05a9a0c3dede8388ee33ce0b61cc64175691e6f9c74e0090f7cf
|
|
7
|
+
data.tar.gz: 3383a5c44388a9ce77181c6d20524740329acfd7b52ff2281c72f159fa516da797e16b0437e5b9fe0804de74d1e534c0b65b17db3b73b33af956e4edcae4505e
|
data/nemesis_shield.rb
CHANGED
|
@@ -27,6 +27,8 @@ module NemesisShield
|
|
|
27
27
|
B64 = /\A[A-Za-z0-9+\/]+={0,2}\z/
|
|
28
28
|
ALPHA = /\A[A-Za-z]+\z/
|
|
29
29
|
ALNUM = /\A[A-Za-z0-9]+\z/
|
|
30
|
+
# A run of 7+ letters => reads like a word / route name, not an opaque id/token.
|
|
31
|
+
WORD = /[A-Za-z]{7,}/
|
|
30
32
|
|
|
31
33
|
module_function
|
|
32
34
|
|
|
@@ -40,7 +42,7 @@ module NemesisShield
|
|
|
40
42
|
when "uuid" then "{uuid}"
|
|
41
43
|
when "hex" then "{hex}"
|
|
42
44
|
when "base64" then "{token}"
|
|
43
|
-
when "alnum" then seg.length >= 12 ? "{id}" : seg
|
|
45
|
+
when "alnum" then (seg.length >= 12 && seg !~ WORD) ? "{id}" : seg
|
|
44
46
|
else seg end
|
|
45
47
|
end.join("/")
|
|
46
48
|
out.empty? ? "/" : out
|
|
@@ -61,7 +63,7 @@ module NemesisShield
|
|
|
61
63
|
return "int" if s =~ INT
|
|
62
64
|
return "float" if s =~ FLOATRE
|
|
63
65
|
return "hex" if s.length >= 16 && s =~ HEX
|
|
64
|
-
return "base64" if s.length >= 16 && s =~ B64
|
|
66
|
+
return "base64" if s.length >= 16 && s =~ B64 && s !~ WORD
|
|
65
67
|
return "alpha" if s =~ ALPHA
|
|
66
68
|
return "alnum" if s =~ ALNUM
|
|
67
69
|
"string"
|
|
@@ -74,9 +76,22 @@ module NemesisShield
|
|
|
74
76
|
end
|
|
75
77
|
|
|
76
78
|
# Request signature (method + route + query param kinds + auth). Excludes status by design.
|
|
79
|
+
# Analytics / click-tracking query params carry no application logic and are the main cause of shape
|
|
80
|
+
# explosion (every ad/campaign link adds different ones). Stripped from the signature so a UTM'd
|
|
81
|
+
# request matches the bare route, while real params (and any attack in them) stay modeled. Shared,
|
|
82
|
+
# identical across every Nemesis Shield SDK so the shape hash matches everywhere.
|
|
83
|
+
TRACKING_PREFIXES = %w[utm_ mtm_ pk_ hsa_ matomo_ piwik_ ga_].freeze
|
|
84
|
+
TRACKING_EXACT = %w[gclid gbraid wbraid dclid gclsrc fbclid msclkid twclid ttclid yclid igshid scid wickedid _ga _gl _hsenc _hsmi mc_cid mc_eid vero_id vero_conv oly_anon_id oly_enc_id _openstat rb_clickid s_cid epik sccid].freeze
|
|
85
|
+
|
|
86
|
+
def tracking_param?(name)
|
|
87
|
+
n = name.to_s.downcase
|
|
88
|
+
TRACKING_PREFIXES.any? { |p| n.start_with?(p) } || TRACKING_EXACT.include?(n)
|
|
89
|
+
end
|
|
90
|
+
module_function :tracking_param?
|
|
91
|
+
|
|
77
92
|
def build_sketch(method:, path:, query: {}, authed: false, status: 0)
|
|
78
93
|
route = normalize_path(path)
|
|
79
|
-
params = (query || {}).keys.sort.map { |k| v = query[k]; { name: k.to_s, kind: kind_of(v.is_a?(Array) ? v.first : v), nested: v.is_a?(Array) } }
|
|
94
|
+
params = (query || {}).keys.reject { |k| tracking_param?(k) }.sort.map { |k| v = query[k]; { name: k.to_s, kind: kind_of(v.is_a?(Array) ? v.first : v), nested: v.is_a?(Array) } }
|
|
80
95
|
# canonical shape input: keys sorted (auth, method, params, route); params are [name, kind, nested];
|
|
81
96
|
# status is intentionally excluded so enforcement can decide BEFORE the response exists.
|
|
82
97
|
canon = JSON.generate({ auth: authed ? 1 : 0, method: method.to_s.upcase, params: params.map { |p| [p[:name], p[:kind], p[:nested] ? 1 : 0] }, route: route })
|