trackguard 0.16.0 → 0.17.0
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/app/controllers/trackguard/admin/visitors_controller.rb +4 -1
- data/app/jobs/trackguard/detect_suspicious_visitors_job.rb +14 -7
- data/app/models/trackguard/blocked_user_agent.rb +7 -0
- data/app/views/trackguard/admin/dashboards/show.html.erb +5 -0
- data/app/views/trackguard/admin/visits/index.html.erb +5 -0
- data/db/migrate/20260505191009_add_name_to_trackguard_visitors.rb +5 -0
- data/lib/generators/trackguard/install_generator.rb +0 -4
- data/lib/generators/trackguard/templates/add_visitor_name.rb +7 -0
- data/lib/generators/trackguard/templates/create_trackguard_tables.rb +12 -7
- data/lib/generators/trackguard/upgrade_generator.rb +27 -0
- data/lib/trackguard/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 79ebac148a49b2b6df9f280c3a8e8e624fa82910ccf0e3d4c608a04ac7f73ced
|
|
4
|
+
data.tar.gz: a0a0648dacb35df737e19413603d658b1c015f6e5fa60d0a6adc72da497082da
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 59d2f2a7dde041da4cc0acf29249c77d9f911a76c498f9381293c838a29bd6ac5fec3c875645c7be60b686d77cb99e4731032b45eb7a2a93464dadaac81b04cb
|
|
7
|
+
data.tar.gz: 5c1bf529774f1af5e8a7e83154e7e21b94e468c95702ac00bed7f0b133b85c092359e544e7f2f615d4e364d904e4512d16ad42d42e3c712d8f8dcb4b60cb81ae
|
|
@@ -11,11 +11,13 @@ module Trackguard
|
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
# rubocop:disable Metrics/AbcSize
|
|
14
15
|
def flag
|
|
15
16
|
if @visitor.update(
|
|
16
17
|
flagged_at: Time.current,
|
|
17
18
|
flag_reason: params[:flag_reason].presence,
|
|
18
|
-
flagged_by: params[:flagged_by].presence || Visitor::FLAGGED_BY.first
|
|
19
|
+
flagged_by: params[:flagged_by].presence || Visitor::FLAGGED_BY.first,
|
|
20
|
+
name: params[:name].presence || BlockedUserAgent.matching_pattern(@visitor.user_agent)
|
|
19
21
|
)
|
|
20
22
|
respond_to do |format|
|
|
21
23
|
format.html { redirect_back_or_to dashboard_path }
|
|
@@ -28,6 +30,7 @@ module Trackguard
|
|
|
28
30
|
end
|
|
29
31
|
end
|
|
30
32
|
end
|
|
33
|
+
# rubocop:enable Metrics/AbcSize
|
|
31
34
|
|
|
32
35
|
def unflag
|
|
33
36
|
@visitor.update!(flagged_at: nil, flag_reason: nil, flagged_by: nil)
|
|
@@ -43,13 +43,15 @@ module Trackguard
|
|
|
43
43
|
return if count.zero?
|
|
44
44
|
return if visitor.whitelisted_ip&.active?
|
|
45
45
|
|
|
46
|
+
name = name_from_ua(visitor.user_agent)
|
|
47
|
+
|
|
46
48
|
if count >= HARD_FLAG_THRESHOLD
|
|
47
|
-
flag!(visitor, "#{count} page views in 24h (hard flag threshold)")
|
|
49
|
+
flag!(visitor, "#{count} page views in 24h (hard flag threshold)", name: name)
|
|
48
50
|
return
|
|
49
51
|
end
|
|
50
52
|
|
|
51
53
|
if (reason = ua_flag_reason(visitor.user_agent))
|
|
52
|
-
flag!(visitor, reason)
|
|
54
|
+
flag!(visitor, reason, name: name)
|
|
53
55
|
return
|
|
54
56
|
end
|
|
55
57
|
|
|
@@ -58,7 +60,7 @@ module Trackguard
|
|
|
58
60
|
return if count < MIN_VIEWS
|
|
59
61
|
|
|
60
62
|
if views.all? { |pv| pv.session_id.nil? && pv.referer.nil? && pv.path == "/" }
|
|
61
|
-
flag!(visitor, "no session, no referrer, single root hit")
|
|
63
|
+
flag!(visitor, "no session, no referrer, single root hit", name: name)
|
|
62
64
|
return
|
|
63
65
|
end
|
|
64
66
|
|
|
@@ -85,7 +87,7 @@ module Trackguard
|
|
|
85
87
|
|
|
86
88
|
return if score < FLAG_SCORE_THRESHOLD
|
|
87
89
|
|
|
88
|
-
flag!(visitor, reasons.join("; "))
|
|
90
|
+
flag!(visitor, reasons.join("; "), name: name)
|
|
89
91
|
end
|
|
90
92
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
91
93
|
|
|
@@ -107,7 +109,8 @@ module Trackguard
|
|
|
107
109
|
.where("wi.id IS NULL OR wi.expires_at <= ?", Time.current)
|
|
108
110
|
.distinct
|
|
109
111
|
.each do |visitor|
|
|
110
|
-
flag!(visitor, "trace_id shared across multiple visitors (cross-visitor bot detected)"
|
|
112
|
+
flag!(visitor, "trace_id shared across multiple visitors (cross-visitor bot detected)",
|
|
113
|
+
name: name_from_ua(visitor.user_agent))
|
|
111
114
|
end
|
|
112
115
|
end
|
|
113
116
|
|
|
@@ -115,8 +118,12 @@ module Trackguard
|
|
|
115
118
|
"blank or minimal user-agent" if user_agent.blank? || user_agent.to_s.length < 10
|
|
116
119
|
end
|
|
117
120
|
|
|
118
|
-
def flag!(visitor, reason)
|
|
119
|
-
visitor.update!(flagged_at: Time.current, flag_reason: reason, flagged_by: "claw:auto")
|
|
121
|
+
def flag!(visitor, reason, name: nil)
|
|
122
|
+
visitor.update!(flagged_at: Time.current, flag_reason: reason, flagged_by: "claw:auto", name: name)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def name_from_ua(user_agent)
|
|
126
|
+
BlockedUserAgent.matching_pattern(user_agent)
|
|
120
127
|
end
|
|
121
128
|
|
|
122
129
|
def blank_ratio(views, attr)
|
|
@@ -12,5 +12,12 @@ module Trackguard
|
|
|
12
12
|
end
|
|
13
13
|
patterns.any? { |p| user_agent.to_s.downcase.include?(p.downcase) }
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
def self.matching_pattern(user_agent)
|
|
17
|
+
patterns = Rails.cache.fetch(CACHE_KEY, expires_in: 10.minutes) do
|
|
18
|
+
pluck(:pattern)
|
|
19
|
+
end
|
|
20
|
+
patterns.find { |p| user_agent.to_s.downcase.include?(p.downcase) }
|
|
21
|
+
end
|
|
15
22
|
end
|
|
16
23
|
end
|
|
@@ -151,6 +151,7 @@
|
|
|
151
151
|
<%= hidden_field_tag :id, visitor.id %>
|
|
152
152
|
<%= f.submit "Flag", class: "tg-btn tg-btn--danger" %>
|
|
153
153
|
<%= f.text_field :flag_reason, placeholder: "Flag reason (optional)", class: "tg-input", autocomplete: "off" %>
|
|
154
|
+
<%= f.text_field :name, placeholder: "Name (optional, auto-detected if blank)", class: "tg-input", autocomplete: "off" %>
|
|
154
155
|
<% end %>
|
|
155
156
|
<% end %>
|
|
156
157
|
</div>
|
|
@@ -175,6 +176,10 @@
|
|
|
175
176
|
<span class="tg-dl__term">User agent</span>
|
|
176
177
|
<span class="tg-dl__def tg-dl__def--break"><%= visitor&.user_agent.presence || "—" %></span>
|
|
177
178
|
</div>
|
|
179
|
+
<div class="tg-dl__row">
|
|
180
|
+
<span class="tg-dl__term">Name</span>
|
|
181
|
+
<span class="tg-dl__def"><%= visitor&.name.presence || "—" %></span>
|
|
182
|
+
</div>
|
|
178
183
|
<div class="tg-dl__row">
|
|
179
184
|
<span class="tg-dl__term">Flag status</span>
|
|
180
185
|
<% if flagged %>
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
<%= hidden_field_tag :id, visitor.id %>
|
|
64
64
|
<%= f.submit "Flag", class: "tg-btn tg-btn--danger" %>
|
|
65
65
|
<%= f.text_field :flag_reason, placeholder: "Flag reason (optional)", class: "tg-input", autocomplete: "off" %>
|
|
66
|
+
<%= f.text_field :name, placeholder: "Name (optional, auto-detected if blank)", class: "tg-input", autocomplete: "off" %>
|
|
66
67
|
<% end %>
|
|
67
68
|
<% end %>
|
|
68
69
|
</div>
|
|
@@ -87,6 +88,10 @@
|
|
|
87
88
|
<span class="tg-dl__term">User agent</span>
|
|
88
89
|
<span class="tg-dl__def tg-dl__def--break"><%= visitor&.user_agent.presence || "—" %></span>
|
|
89
90
|
</div>
|
|
91
|
+
<div class="tg-dl__row">
|
|
92
|
+
<span class="tg-dl__term">Name</span>
|
|
93
|
+
<span class="tg-dl__def"><%= visitor&.name.presence || "—" %></span>
|
|
94
|
+
</div>
|
|
90
95
|
<div class="tg-dl__row">
|
|
91
96
|
<span class="tg-dl__term">Flag status</span>
|
|
92
97
|
<% if flagged %>
|
|
@@ -15,10 +15,6 @@ module Trackguard
|
|
|
15
15
|
migration_template "create_trackguard_tables.rb", "db/migrate/create_trackguard_tables.rb"
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def create_visits_migration_file
|
|
19
|
-
migration_template "add_trackguard_visits.rb", "db/migrate/add_trackguard_visits.rb"
|
|
20
|
-
end
|
|
21
|
-
|
|
22
18
|
def print_next_steps
|
|
23
19
|
say "\nNext steps:", :green
|
|
24
20
|
say " 1. rails db:migrate"
|
|
@@ -13,20 +13,25 @@ class CreateTrackguardTables < ActiveRecord::Migration[<%= ActiveRecord::Migrati
|
|
|
13
13
|
|
|
14
14
|
add_index :trackguard_visitors, :ip, unique: true
|
|
15
15
|
|
|
16
|
-
create_table :
|
|
17
|
-
t.string :
|
|
16
|
+
create_table :trackguard_visits do |t|
|
|
17
|
+
t.string :type
|
|
18
|
+
t.string :path, null: false
|
|
18
19
|
t.string :user_agent
|
|
19
20
|
t.string :referer
|
|
20
21
|
t.string :session_id
|
|
21
22
|
t.string :trace_id
|
|
22
23
|
t.string :source
|
|
23
|
-
t.
|
|
24
|
-
t.
|
|
24
|
+
t.string :block_reason
|
|
25
|
+
t.string :http_method
|
|
26
|
+
t.references :visitor, null: false, foreign_key: { to_table: :trackguard_visitors }
|
|
27
|
+
t.datetime :created_at, null: false
|
|
25
28
|
end
|
|
26
29
|
|
|
27
|
-
add_index :
|
|
28
|
-
add_index :
|
|
29
|
-
add_index :
|
|
30
|
+
add_index :trackguard_visits, :type
|
|
31
|
+
add_index :trackguard_visits, :path
|
|
32
|
+
add_index :trackguard_visits, :created_at
|
|
33
|
+
add_index :trackguard_visits, :source
|
|
34
|
+
add_index :trackguard_visits, :block_reason
|
|
30
35
|
|
|
31
36
|
create_table :trackguard_whitelisted_ips do |t|
|
|
32
37
|
t.string :ip, null: false
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
require "rails/generators/active_record"
|
|
3
|
+
|
|
4
|
+
module Trackguard
|
|
5
|
+
class UpgradeGenerator < Rails::Generators::Base
|
|
6
|
+
include Rails::Generators::Migration
|
|
7
|
+
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
|
|
10
|
+
def self.next_migration_number(dirname)
|
|
11
|
+
ActiveRecord::Generators::Base.next_migration_number(dirname)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create_visits_migration_file
|
|
15
|
+
migration_template "add_trackguard_visits.rb", "db/migrate/add_trackguard_visits.rb"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create_visitor_name_migration_file
|
|
19
|
+
migration_template "add_visitor_name.rb", "db/migrate/add_visitor_name.rb"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def print_next_steps
|
|
23
|
+
say "\nNext steps:", :green
|
|
24
|
+
say " 1. rails db:migrate"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/trackguard/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: trackguard
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.17.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Krzysztof Rygielski
|
|
@@ -72,9 +72,12 @@ files:
|
|
|
72
72
|
- app/views/trackguard/admin/visits/index.html.erb
|
|
73
73
|
- config/importmap.rb
|
|
74
74
|
- config/routes.rb
|
|
75
|
+
- db/migrate/20260505191009_add_name_to_trackguard_visitors.rb
|
|
75
76
|
- lib/generators/trackguard/install_generator.rb
|
|
76
77
|
- lib/generators/trackguard/templates/add_trackguard_visits.rb
|
|
78
|
+
- lib/generators/trackguard/templates/add_visitor_name.rb
|
|
77
79
|
- lib/generators/trackguard/templates/create_trackguard_tables.rb
|
|
80
|
+
- lib/generators/trackguard/upgrade_generator.rb
|
|
78
81
|
- lib/tasks/trackguard.rake
|
|
79
82
|
- lib/trackguard.rb
|
|
80
83
|
- lib/trackguard/engine.rb
|