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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/nemesis_shield.rb +18 -3
  3. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8e3261ce1ca6eb75be91cc58f82412fa5280a1ea99bab663c731d2febe643a8a
4
- data.tar.gz: 5b8028e72aa2836b34daa0da860ef8f9fd0a1cda55b107c7690173bb7012f454
3
+ metadata.gz: fe007575d89dca3c4c77078c517dd4acd58b19ae65368325e730cfbe977473a5
4
+ data.tar.gz: a16c094ba7f5977a224aedbd236af7411632f5a1f104ff4a6751797c8bf245c4
5
5
  SHA512:
6
- metadata.gz: 3a1454ba4d794b24b87500788acf506fed322a68fa717230cc6c9494b536e511d8443e439e4f18bb3b753c4d9fbeccfd3708d281b931def6ee32e6ecc738ddfe
7
- data.tar.gz: 120624a050c429c246b140f4c361d50ed7afb3ef2ccd8aa6fb94e5ea09aeafe6aeeb9b4c5ab1cc7a2ba005d9cc78ebb574d43e86355bb9c075280e260eb1b5aa
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 })
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nemesis-shield
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Autogon Inc.