trackguard 0.28.0 → 0.29.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/assets/stylesheets/trackguard/admin.css +9 -0
- data/app/controllers/concerns/trackguard/page_tracker.rb +9 -2
- data/app/helpers/trackguard/application_helper.rb +13 -5
- data/app/jobs/trackguard/hub/submit_blocked_request_job.rb +39 -0
- data/app/jobs/trackguard/hub/submit_page_view_job.rb +39 -0
- data/app/jobs/trackguard/track_blocked_request_job.rb +1 -14
- data/app/services/trackguard/track_blocked_request.rb +32 -0
- data/app/views/trackguard/admin/_stats_panel.html.erb +1 -1
- data/config/importmap.rb +3 -1
- data/lib/trackguard/adapters/base.rb +3 -3
- data/lib/trackguard/adapters/hub.rb +28 -19
- data/lib/trackguard/adapters/local.rb +1 -1
- data/lib/trackguard/engine.rb +4 -1
- data/lib/trackguard/trace_id_middleware.rb +14 -0
- data/lib/trackguard/version.rb +1 -1
- data/lib/trackguard.rb +1 -0
- metadata +5 -2
- data/app/helpers/trackguard/hub_helper.rb +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1372d974514748b7cb553b4299b14de6345339c00947e24102be03ec7ac0be0d
|
|
4
|
+
data.tar.gz: 142f9a999003624989fa47ad7a422a5bc76c504dbb8666a8e676d49970490775
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e3f01bd73d5701b53f8e7a3ef37ab3c3a43b016dc72e9388c8b8f9db225412380889b8fd13ae1df8783231fcdb6fde828f7f6807854c6ef9fa0ee6a4924dc774
|
|
7
|
+
data.tar.gz: 9ff260dc894d2cbc70e69062bb878d394be1913faf4edd61dad5b9c10c41db2e68805985832ba8f4b7762af6a6483e0b6a0d9690447993d0c3f5ea542d8eb98c
|
|
@@ -240,6 +240,7 @@
|
|
|
240
240
|
/* ── Table ─────────────────────────────────────────────────────────── */
|
|
241
241
|
.tg-table {
|
|
242
242
|
width: 100%;
|
|
243
|
+
table-layout: fixed;
|
|
243
244
|
border-collapse: collapse;
|
|
244
245
|
font-size: 0.875rem;
|
|
245
246
|
margin-bottom: 2rem;
|
|
@@ -269,6 +270,14 @@
|
|
|
269
270
|
text-align: right;
|
|
270
271
|
color: #9ca3af;
|
|
271
272
|
font-weight: 400;
|
|
273
|
+
white-space: nowrap;
|
|
274
|
+
width: 3.5rem;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.tg-td--label {
|
|
278
|
+
overflow: hidden;
|
|
279
|
+
text-overflow: ellipsis;
|
|
280
|
+
white-space: nowrap;
|
|
272
281
|
}
|
|
273
282
|
|
|
274
283
|
.tg-td--break { word-break: break-all; }
|
|
@@ -15,7 +15,7 @@ module Trackguard
|
|
|
15
15
|
private
|
|
16
16
|
|
|
17
17
|
def set_trace_id
|
|
18
|
-
@trace_id =
|
|
18
|
+
@trace_id = request.env["trackguard.trace_id"]
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def track_page_view
|
|
@@ -31,10 +31,17 @@ module Trackguard
|
|
|
31
31
|
trace_id: @trace_id,
|
|
32
32
|
source: extract_source,
|
|
33
33
|
tracking_layer: "backend",
|
|
34
|
-
http_method: request.request_method
|
|
34
|
+
http_method: request.request_method,
|
|
35
|
+
prefetch: turbo_prefetch?
|
|
35
36
|
)
|
|
36
37
|
end
|
|
37
38
|
|
|
39
|
+
def turbo_prefetch?
|
|
40
|
+
request.headers["Purpose"] == "prefetch" ||
|
|
41
|
+
request.headers["Sec-Purpose"]&.start_with?("prefetch") ||
|
|
42
|
+
request.headers["X-Sec-Purpose"]&.start_with?("prefetch")
|
|
43
|
+
end
|
|
44
|
+
|
|
38
45
|
def extract_source
|
|
39
46
|
raw = params[:ref].presence || params[:utm_source].presence
|
|
40
47
|
raw && raw.strip.downcase.first(64)
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
module Trackguard
|
|
2
2
|
module ApplicationHelper
|
|
3
|
-
def
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
def trackguard_header_tags
|
|
4
|
+
tags = [ tag.meta(name: "trace-id", content: @trace_id) ]
|
|
5
|
+
|
|
6
|
+
case Trackguard.adapter
|
|
7
|
+
when Trackguard::Adapters::Local
|
|
8
|
+
tags << tag.meta(name: "trackguard-url", content: trackguard.page_views_path)
|
|
9
|
+
when Trackguard::Adapters::Hub
|
|
10
|
+
if Rails.env.production?
|
|
11
|
+
tags << tag.script(src: "#{Trackguard.hub_url}/track.js", data: { api_key: Trackguard.hub_api_key })
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
safe_join(tags, "\n")
|
|
8
16
|
end
|
|
9
17
|
|
|
10
18
|
def trackguard_nav_links
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
module Trackguard
|
|
7
|
+
module Hub
|
|
8
|
+
class SubmitBlockedRequestJob < ApplicationJob
|
|
9
|
+
queue_as :default
|
|
10
|
+
|
|
11
|
+
def perform(ip:, user_agent:, path:, http_method:, block_reason:)
|
|
12
|
+
uri = URI("#{Trackguard.hub_url}/api/backend/blocked_requests")
|
|
13
|
+
body = {
|
|
14
|
+
blocked_request: {
|
|
15
|
+
ip: ip, user_agent: user_agent, path: path,
|
|
16
|
+
http_method: http_method, block_reason: block_reason
|
|
17
|
+
}
|
|
18
|
+
}.to_json
|
|
19
|
+
|
|
20
|
+
req = Net::HTTP::Post.new(uri)
|
|
21
|
+
req["X-Api-Key"] = Trackguard.hub_api_key
|
|
22
|
+
req["Authorization"] = "Bearer #{Trackguard.hub_secret_key}"
|
|
23
|
+
req["Content-Type"] = "application/json"
|
|
24
|
+
req.body = body
|
|
25
|
+
|
|
26
|
+
opts = { use_ssl: uri.scheme == "https", open_timeout: 3, read_timeout: 5 }
|
|
27
|
+
response = Net::HTTP.start(uri.hostname, uri.port, **opts) { |http| http.request(req) }
|
|
28
|
+
|
|
29
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
30
|
+
Rails.logger.warn(
|
|
31
|
+
"[Trackguard::Hub::SubmitBlockedRequestJob] Unexpected response #{response.code} for path=#{path}"
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
rescue StandardError => e
|
|
35
|
+
Rails.logger.warn("[Trackguard::Hub::SubmitBlockedRequestJob] Failed to submit blocked request: #{e.message}")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
module Trackguard
|
|
7
|
+
module Hub
|
|
8
|
+
class SubmitPageViewJob < ApplicationJob
|
|
9
|
+
queue_as :default
|
|
10
|
+
|
|
11
|
+
def perform(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:, http_method:, **)
|
|
12
|
+
uri = URI("#{Trackguard.hub_url}/api/backend/page_views")
|
|
13
|
+
body = {
|
|
14
|
+
page_view: {
|
|
15
|
+
path: path, referer: referer, session_id: session_id, trace_id: trace_id,
|
|
16
|
+
source: source, http_method: http_method, ip: ip, user_agent: user_agent
|
|
17
|
+
}
|
|
18
|
+
}.to_json
|
|
19
|
+
|
|
20
|
+
req = Net::HTTP::Post.new(uri)
|
|
21
|
+
req["X-Api-Key"] = Trackguard.hub_api_key
|
|
22
|
+
req["Authorization"] = "Bearer #{Trackguard.hub_secret_key}"
|
|
23
|
+
req["Content-Type"] = "application/json"
|
|
24
|
+
req.body = body
|
|
25
|
+
|
|
26
|
+
opts = { use_ssl: uri.scheme == "https", open_timeout: 3, read_timeout: 5 }
|
|
27
|
+
response = Net::HTTP.start(uri.hostname, uri.port, **opts) { |http| http.request(req) }
|
|
28
|
+
|
|
29
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
30
|
+
Rails.logger.warn(
|
|
31
|
+
"[Trackguard::Hub::SubmitPageViewJob] Unexpected response #{response.code} for path=#{path}"
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
rescue StandardError => e
|
|
35
|
+
Rails.logger.warn("[Trackguard::Hub::SubmitPageViewJob] Failed to submit page view: #{e.message}")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -5,20 +5,7 @@ module Trackguard
|
|
|
5
5
|
queue_as :default
|
|
6
6
|
|
|
7
7
|
def perform(ip:, user_agent:, path:, http_method:, block_reason:)
|
|
8
|
-
|
|
9
|
-
v.user_agent = user_agent
|
|
10
|
-
v.first_seen_at = Time.current
|
|
11
|
-
v.last_seen_at = Time.current
|
|
12
|
-
end
|
|
13
|
-
visitor.update!(last_seen_at: Time.current, user_agent: user_agent)
|
|
14
|
-
|
|
15
|
-
BlockedRequest.create!(
|
|
16
|
-
path: path,
|
|
17
|
-
user_agent: user_agent,
|
|
18
|
-
http_method: http_method,
|
|
19
|
-
block_reason: block_reason,
|
|
20
|
-
visitor: visitor
|
|
21
|
-
)
|
|
8
|
+
TrackBlockedRequest.call(ip:, user_agent:, path:, http_method:, block_reason:)
|
|
22
9
|
end
|
|
23
10
|
end
|
|
24
11
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Trackguard
|
|
4
|
+
class TrackBlockedRequest < ApplicationService
|
|
5
|
+
def initialize(ip:, user_agent:, path:, http_method:, block_reason:, visitor_scope: {})
|
|
6
|
+
@ip = ip
|
|
7
|
+
@user_agent = user_agent
|
|
8
|
+
@path = path
|
|
9
|
+
@http_method = http_method
|
|
10
|
+
@block_reason = block_reason
|
|
11
|
+
@visitor_scope = visitor_scope
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
visitor = Visitor.find_or_create_by!(ip: @ip, **@visitor_scope) do |v|
|
|
16
|
+
v.user_agent = @user_agent
|
|
17
|
+
v.first_seen_at = Time.current
|
|
18
|
+
v.last_seen_at = Time.current
|
|
19
|
+
end
|
|
20
|
+
visitor.update!(last_seen_at: Time.current, user_agent: @user_agent)
|
|
21
|
+
|
|
22
|
+
BlockedRequest.create!(
|
|
23
|
+
path: @path,
|
|
24
|
+
user_agent: @user_agent,
|
|
25
|
+
http_method: @http_method,
|
|
26
|
+
block_reason: @block_reason,
|
|
27
|
+
visitor: visitor,
|
|
28
|
+
**@visitor_scope
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/config/importmap.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Trackguard
|
|
|
9
9
|
def flagged_visitor?(ip) = raise NotImplementedError, "#{self.class}#flagged_visitor?"
|
|
10
10
|
|
|
11
11
|
def track_page_view(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:, tracking_layer:,
|
|
12
|
-
http_method:)
|
|
12
|
+
http_method:, prefetch: false)
|
|
13
13
|
return if blocked_user_agent?(user_agent)
|
|
14
14
|
return if blocked_path?(path)
|
|
15
15
|
return if path.blank? || path.start_with?(Trackguard.admin_path)
|
|
@@ -17,7 +17,7 @@ module Trackguard
|
|
|
17
17
|
perform_track_page_view(
|
|
18
18
|
path: path, ip: ip, user_agent: user_agent, referer: referer,
|
|
19
19
|
session_id: session_id, trace_id: trace_id, source: source,
|
|
20
|
-
tracking_layer: tracking_layer, http_method: http_method
|
|
20
|
+
tracking_layer: tracking_layer, http_method: http_method, prefetch: prefetch
|
|
21
21
|
)
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -28,7 +28,7 @@ module Trackguard
|
|
|
28
28
|
protected
|
|
29
29
|
|
|
30
30
|
def perform_track_page_view(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:,
|
|
31
|
-
tracking_layer:, http_method:)
|
|
31
|
+
tracking_layer:, http_method:, prefetch: false)
|
|
32
32
|
raise NotImplementedError, "#{self.class}#perform_track_page_view"
|
|
33
33
|
end
|
|
34
34
|
end
|
|
@@ -31,14 +31,22 @@ module Trackguard
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
|
|
34
|
-
|
|
34
|
+
Trackguard::Hub::SubmitBlockedRequestJob.perform_later(
|
|
35
|
+
ip: ip, user_agent: user_agent, path: path, http_method: http_method, block_reason: block_reason
|
|
36
|
+
)
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
protected
|
|
38
40
|
|
|
39
41
|
def perform_track_page_view(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:,
|
|
40
|
-
tracking_layer:, http_method:)
|
|
41
|
-
|
|
42
|
+
tracking_layer:, http_method:, prefetch: false)
|
|
43
|
+
return if prefetch
|
|
44
|
+
|
|
45
|
+
Trackguard::Hub::SubmitPageViewJob.perform_later(
|
|
46
|
+
path: path, ip: ip, user_agent: user_agent, referer: referer,
|
|
47
|
+
session_id: session_id, trace_id: trace_id, source: source,
|
|
48
|
+
tracking_layer: tracking_layer, http_method: http_method
|
|
49
|
+
)
|
|
42
50
|
end
|
|
43
51
|
|
|
44
52
|
private
|
|
@@ -55,22 +63,8 @@ module Trackguard
|
|
|
55
63
|
end
|
|
56
64
|
|
|
57
65
|
def fetch_rules_from_hub
|
|
58
|
-
uri = URI("#{Trackguard.hub_url}/api/rules")
|
|
59
|
-
|
|
60
|
-
request["Authorization"] = "Bearer #{Trackguard.hub_secret_key}"
|
|
61
|
-
request["Accept"] = "application/json"
|
|
62
|
-
|
|
63
|
-
etag = Rails.cache.read(ETAG_KEY)
|
|
64
|
-
request["If-None-Match"] = etag if etag
|
|
65
|
-
|
|
66
|
-
response = Net::HTTP.start(
|
|
67
|
-
uri.hostname, uri.port,
|
|
68
|
-
use_ssl: uri.scheme == "https",
|
|
69
|
-
open_timeout: 3,
|
|
70
|
-
read_timeout: 5
|
|
71
|
-
) do |http|
|
|
72
|
-
http.request(request)
|
|
73
|
-
end
|
|
66
|
+
uri = URI("#{Trackguard.hub_url}/api/backend/rules")
|
|
67
|
+
response = execute_http(uri, build_request(uri))
|
|
74
68
|
|
|
75
69
|
return Rails.cache.read(STALE_KEY) || {} if response.is_a?(Net::HTTPNotModified)
|
|
76
70
|
|
|
@@ -80,6 +74,21 @@ module Trackguard
|
|
|
80
74
|
Rails.cache.write(ETAG_KEY, response["ETag"], expires_in: 24.hours) if response["ETag"]
|
|
81
75
|
fresh
|
|
82
76
|
end
|
|
77
|
+
|
|
78
|
+
def build_request(uri)
|
|
79
|
+
req = Net::HTTP::Get.new(uri)
|
|
80
|
+
req["Authorization"] = "Bearer #{Trackguard.hub_secret_key}"
|
|
81
|
+
req["X-Api-Key"] = Trackguard.hub_api_key
|
|
82
|
+
req["Accept"] = "application/json"
|
|
83
|
+
etag = Rails.cache.read(ETAG_KEY)
|
|
84
|
+
req["If-None-Match"] = etag if etag
|
|
85
|
+
req
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def execute_http(uri, request)
|
|
89
|
+
opts = { use_ssl: uri.scheme == "https", open_timeout: 3, read_timeout: 5 }
|
|
90
|
+
Net::HTTP.start(uri.hostname, uri.port, **opts) { |http| http.request(request) }
|
|
91
|
+
end
|
|
83
92
|
end
|
|
84
93
|
end
|
|
85
94
|
end
|
|
@@ -32,7 +32,7 @@ module Trackguard
|
|
|
32
32
|
protected
|
|
33
33
|
|
|
34
34
|
def perform_track_page_view(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:,
|
|
35
|
-
tracking_layer:, http_method:)
|
|
35
|
+
tracking_layer:, http_method:, prefetch: false)
|
|
36
36
|
TrackPageViewJob.perform_later(
|
|
37
37
|
path: path,
|
|
38
38
|
ip: ip,
|
data/lib/trackguard/engine.rb
CHANGED
|
@@ -6,7 +6,6 @@ module Trackguard
|
|
|
6
6
|
|
|
7
7
|
config.to_prepare do
|
|
8
8
|
ActionController::Base.helper Trackguard::ApplicationHelper
|
|
9
|
-
ActionController::Base.helper Trackguard::HubHelper
|
|
10
9
|
end
|
|
11
10
|
|
|
12
11
|
initializer "trackguard.assets" do |app|
|
|
@@ -17,6 +16,10 @@ module Trackguard
|
|
|
17
16
|
app.config.importmap.paths << root.join("config/importmap.rb") if app.config.respond_to?(:importmap)
|
|
18
17
|
end
|
|
19
18
|
|
|
19
|
+
initializer "trackguard.trace_id_middleware" do |app|
|
|
20
|
+
app.middleware.use Trackguard::TraceIdMiddleware
|
|
21
|
+
end
|
|
22
|
+
|
|
20
23
|
config.after_initialize do
|
|
21
24
|
Trackguard::RackAttack.configure
|
|
22
25
|
end
|
data/lib/trackguard/version.rb
CHANGED
data/lib/trackguard.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.29.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Krzysztof Rygielski
|
|
@@ -58,8 +58,9 @@ files:
|
|
|
58
58
|
- app/controllers/trackguard/admin/whitelisted_ips_controller.rb
|
|
59
59
|
- app/controllers/trackguard/page_views_controller.rb
|
|
60
60
|
- app/helpers/trackguard/application_helper.rb
|
|
61
|
-
- app/helpers/trackguard/hub_helper.rb
|
|
62
61
|
- app/jobs/trackguard/detect_suspicious_visitors_job.rb
|
|
62
|
+
- app/jobs/trackguard/hub/submit_blocked_request_job.rb
|
|
63
|
+
- app/jobs/trackguard/hub/submit_page_view_job.rb
|
|
63
64
|
- app/jobs/trackguard/track_blocked_request_job.rb
|
|
64
65
|
- app/jobs/trackguard/track_page_view_job.rb
|
|
65
66
|
- app/models/trackguard/blocked_path.rb
|
|
@@ -71,6 +72,7 @@ files:
|
|
|
71
72
|
- app/models/trackguard/whitelisted_ip.rb
|
|
72
73
|
- app/services/trackguard/analytics_query.rb
|
|
73
74
|
- app/services/trackguard/application_service.rb
|
|
75
|
+
- app/services/trackguard/track_blocked_request.rb
|
|
74
76
|
- app/services/trackguard/track_page_view.rb
|
|
75
77
|
- app/views/layouts/trackguard/admin.html.erb
|
|
76
78
|
- app/views/trackguard/admin/_nav.html.erb
|
|
@@ -98,6 +100,7 @@ files:
|
|
|
98
100
|
- lib/trackguard/adapters/local.rb
|
|
99
101
|
- lib/trackguard/engine.rb
|
|
100
102
|
- lib/trackguard/rack_attack.rb
|
|
103
|
+
- lib/trackguard/trace_id_middleware.rb
|
|
101
104
|
- lib/trackguard/version.rb
|
|
102
105
|
- trackguard.gemspec
|
|
103
106
|
homepage: https://github.com/riggy/trackguard
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Trackguard
|
|
4
|
-
module HubHelper
|
|
5
|
-
def trackguard_hub_js_tag
|
|
6
|
-
return unless Trackguard.adapter.is_a?(Trackguard::Adapters::Hub)
|
|
7
|
-
|
|
8
|
-
tag.script(src: "https://app.trackguard.dev/track.js", data: { api_key: Trackguard.hub_api_key })
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
end
|