paraxial 0.8.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a8acee8353bb32e2bebce880b05661b968904d02f9e0748b6041325d8f24491b
4
- data.tar.gz: 4ef0694246bc93ae69c1f88a439be89e5fdbd29bcd0ee53406de63d19a791257
3
+ metadata.gz: be68560342effbc4099f126d85e25acf0400e0b27dc636f4173192a9a634d3da
4
+ data.tar.gz: 489450bae67581a1738754843a8ce3b6128ad098aa88228d1823bf41ce0ebada
5
5
  SHA512:
6
- metadata.gz: 8127e330796dcea327b3b8b029831c4b851e548df33bf114a4a7b07039e34ed1f48687525747ce11ce08298771a28f95edc0d420bae07d776f08e5b812fdd566
7
- data.tar.gz: 40a7c37df1123e0f8dfc87d31e950f8fc35fbea1729f436405ae18c8af9d1f77d76217450e0e21cb44fd3a713afb6bf49d16bcfda8f5ad5ab00c87c42e492712
6
+ metadata.gz: edd5307bcc69fcff116b2873d5571b035808f76bf508447403da5a1064d7faea2d230ceb12f0e6318469815b18d23254a6b4adf914b6c0bca009ac807f35d8fd
7
+ data.tar.gz: f473320f53f772245dad48f68edeb47271ebe22e921b73b6cfe74506b6e5ccc8925ebf6621f2a4a46f0b6f9c2282c9580f498572f85dc6214fe21359d59dcc52
@@ -3,24 +3,61 @@ module Paraxial
3
3
  module Checker
4
4
  @allows = { 'v4' => Patricia.new, 'v6' => Patricia.new(:AF_INET6) }
5
5
  @bans = { 'v4' => Patricia.new, 'v6' => Patricia.new(:AF_INET6) }
6
-
6
+ @buffer = Queue.new
7
+ @mutex = Mutex.new
8
+ @headers = { 'Content-Type': 'application/json' }
7
9
 
8
10
  if Paraxial::Helpers.get_api_key
9
11
  @thread = Thread.new do
10
12
  loop do
11
13
  get_abr
12
14
  sleep(10)
15
+ flush_buffer
16
+ end
17
+ end
18
+ end
19
+
20
+ def self.req_to_buff(req_hash)
21
+ @buffer << req_hash
22
+ end
23
+
24
+ def self.flush_buffer
25
+ @mutex.synchronize do
26
+ requests = []
27
+ until @buffer.empty?
28
+ requests << @buffer.pop(true) rescue nil
29
+ end
30
+
31
+ send_async_request(requests) unless requests.empty?
32
+ end
33
+ end
34
+
35
+ def self.send_async_request(requests)
36
+ body =
37
+ {
38
+ http_requests: requests,
39
+ private_api_key: Paraxial::Helpers.get_api_key
40
+ }
41
+
42
+ # Do not send HTTP events when free tier is set to true
43
+ ft = Paraxial::FreeTier.is_free_tier
44
+ if ft
45
+ puts "[Paraxial] HTTP ingest not supported on free tier"
46
+ else
47
+ Thread.new do
48
+ uri = URI.parse(Paraxial::Helpers.get_ingest_url)
49
+ Net::HTTP.post(uri, body.to_json, @headers)
13
50
  end
14
51
  end
15
52
  end
16
53
 
54
+
17
55
  def self.get_abr
18
- uri = URI.parse(Paraxial::Helpers.get_paraxial_url + '/api/abr')
19
- headers = { 'Content-Type': 'application/json' }
56
+ uri = URI.parse(Paraxial::Helpers.get_abr_url)
20
57
 
21
58
  body = { api_key: Paraxial::Helpers.get_api_key }
22
59
  begin
23
- r = Net::HTTP.post(uri, body.to_json, headers)
60
+ r = Net::HTTP.post(uri, body.to_json, @headers)
24
61
  if r.code == '200'
25
62
  put_abr(JSON.parse(r.body))
26
63
  else
@@ -92,7 +129,53 @@ module Paraxial
92
129
  end
93
130
  end
94
131
 
132
+ def self.ban_ip_msg(ip, length, msg)
133
+ local_ban(ip)
134
+
135
+ uri = URI.parse(Paraxial::Helpers.get_ruby_ban_url)
136
+ body =
137
+ {
138
+ bad_ip: ip,
139
+ ban_length: length,
140
+ msg: msg,
141
+ api_key: Paraxial::Helpers.get_api_key
142
+ }
143
+ r = Net::HTTP.post(uri, body.to_json, @headers)
144
+ if r.code == '200'
145
+ :ok
146
+ else
147
+ :error
148
+ end
149
+ end
150
+
151
+ def self.honeypot_ban(ip, length)
152
+ local_ban(ip)
153
+
154
+ uri = URI.parse(Paraxial::Helpers.get_honeypot_url)
155
+
156
+ body = { api_key: Paraxial::Helpers.get_api_key, bad_ip: ip, ban_length: length }
157
+ r = Net::HTTP.post(uri, body.to_json, @headers)
158
+ if r.code == '200'
159
+ :ok
160
+ else
161
+ :error
162
+ end
163
+ end
164
+
95
165
  def self.ban_ip(ip)
166
+ local_ban(ip)
167
+ uri = URI.parse(Paraxial::Helpers.get_ban_url)
168
+
169
+ body = { api_key: Paraxial::Helpers.get_api_key, ip_address: ip }
170
+ r = Net::HTTP.post(uri, body.to_json, @headers)
171
+ if r.code == '200'
172
+ :ok
173
+ else
174
+ :error
175
+ end
176
+ end
177
+
178
+ def self.local_ban(ip)
96
179
  if ip.include?('.')
97
180
  # IPv4
98
181
  current_t = @bans['v4']
@@ -104,17 +187,6 @@ module Paraxial
104
187
  current_t.add(ip)
105
188
  @bans['v6'] = current_t
106
189
  end
107
-
108
- uri = URI.parse(Paraxial::Helpers.get_ban_url)
109
- headers = { 'Content-Type': 'application/json' }
110
-
111
- body = { api_key: Paraxial::Helpers.get_api_key, ip_address: ip }
112
- r = Net::HTTP.post(uri, body.to_json, headers)
113
- if r.code == '200'
114
- :ok
115
- else
116
- :error
117
- end
118
190
  end
119
191
 
120
192
  def self.allow_ip?(ip)
@@ -0,0 +1,34 @@
1
+ require 'net/http'
2
+ require 'json'
3
+
4
+ module Paraxial
5
+ module FreeTier
6
+ class << self
7
+ attr_reader :is_free_tier
8
+
9
+ def initialize
10
+ @is_free_tier = true
11
+ check_free_tier_status
12
+ end
13
+
14
+ private
15
+
16
+ # Kicks off an async HTTP request
17
+ def check_free_tier_status
18
+ Thread.new do
19
+ uri = URI.parse(Paraxial::Helpers.get_free_tier_url())
20
+ headers = { 'Content-Type': 'application/json' }
21
+ body = { api_key: Paraxial::Helpers.get_api_key }
22
+ r = Net::HTTP.post(uri, body.to_json, headers)
23
+ result = JSON.parse(r.body)
24
+ # Assume the API response contains a field `free_tier` (true/false)
25
+ @is_free_tier = result['free_tier']
26
+ rescue StandardError => e
27
+ # Handle any errors (network issues, parsing errors, etc.)
28
+ puts "Error fetching free tier status: #{e.message}"
29
+ @is_free_tier = true
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -12,6 +12,26 @@ module Paraxial
12
12
  get_paraxial_url + '/api/exploit'
13
13
  end
14
14
 
15
+ def self.get_free_tier_url
16
+ get_paraxial_url + '/api/free_tier'
17
+ end
18
+
19
+ def self.get_abr_url
20
+ get_paraxial_url + '/api/abr'
21
+ end
22
+
23
+ def self.get_ingest_url
24
+ get_paraxial_url + '/api/ingest'
25
+ end
26
+
27
+ def self.get_honeypot_url
28
+ get_paraxial_url + '/api/honeypot_ban_x'
29
+ end
30
+
31
+ def self.get_ruby_ban_url
32
+ get_paraxial_url + '/api/ruby_ban_x'
33
+ end
34
+
15
35
  def self.get_api_key
16
36
  @paraxial_api_key ||= ENV['PARAXIAL_API_KEY']
17
37
  end
@@ -6,7 +6,7 @@ unless Rails.env.test? || File.basename($0) == 'rake' || defined?(Rails::Generat
6
6
  def load(source, proc = nil)
7
7
  exg = Paraxial.configuration&.exploit_guard || nil
8
8
  if [:monitor, :block].include?(exg)
9
- if source.is_a?(String) && source.match?(/ActionView|Net::BufferedIO|ERB|ActiveSupport/)
9
+ if source.is_a?(String) && source.match?(/ActionView|Net::BufferedIO|ERB/)
10
10
  puts "[Paraxial] Exploit Guard triggered, malicious input to Marshal.load"
11
11
  puts source
12
12
 
@@ -3,6 +3,7 @@ require 'paraxial'
3
3
  require 'rpatricia'
4
4
  require_relative '../helpers'
5
5
  require_relative '../checker'
6
+ require_relative '../free_tier'
6
7
 
7
8
  Bundler.setup
8
9
 
@@ -20,6 +21,7 @@ unless Rails.env.test? || File.basename($0) == 'rake' || defined?(Rails::Generat
20
21
  puts '[Paraxial] API key detected, agent starting'
21
22
 
22
23
  Paraxial.check_exploit_guard
24
+ Paraxial::FreeTier.initialize
23
25
 
24
26
  deps_and_licenses = []
25
27
  Bundler.load.specs.each do |spec|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Paraxial
4
- VERSION = '0.8.0'
4
+ VERSION = '0.9.1'
5
5
  end
data/lib/paraxial.rb CHANGED
@@ -44,7 +44,65 @@ module Paraxial
44
44
  utc_time.strftime('%Y-%m-%d %H:%M:%S.%6N') + 'Z'
45
45
  end
46
46
 
47
+ def self.record(request, status)
48
+ return if Paraxial::Helpers.get_api_key.nil?
49
+
50
+ req_hash =
51
+ {
52
+ ip_address: request.remote_ip,
53
+ http_method: request.request_method,
54
+ path: request.path,
55
+ user_agent: request.user_agent,
56
+ allowed: !request.env['paraxial.deny'],
57
+ status_code: status,
58
+ inserted_at: get_timestamp,
59
+ cloud_ip: request.env['paraxial.cloud_ip'],
60
+ host: request.host
61
+ }
62
+ Paraxial::Checker.req_to_buff(req_hash)
63
+ end
64
+
65
+ # routes = ['/login', '/users/:id']
66
+ def self.block_cloud_ip(request, routes)
67
+ return if Paraxial::Helpers.get_api_key.nil?
68
+
69
+ ip = request.remote_ip
70
+ cloud_provider = get_cloud_provider(ip)
71
+
72
+ if cloud_provider
73
+ request.env['paraxial.cloud_ip'] = cloud_provider
74
+ else
75
+ request.env['paraxial.cloud_ip'] = nil
76
+ end
77
+
78
+ route_patterns = routes.map do |route|
79
+ Regexp.new("^" + route.gsub(/:\w+/, '\d+') + "$")
80
+ end
81
+
82
+ match = route_patterns.any? { |pattern| pattern.match?(request.path) }
83
+
84
+ if match and cloud_provider
85
+ request.env['paraxial.deny'] = true
86
+ end
87
+ end
88
+
89
+ def self.req_allowed?(request)
90
+ return if Paraxial::Helpers.get_api_key.nil?
91
+
92
+ if request.env['paraxial.deny'] == true
93
+ false
94
+ elsif Paraxial::Checker.allow_ip?(request.remote_ip) == true
95
+ request.env['paraxial.deny'] = false
96
+ true
97
+ else
98
+ request.env['paraxial.deny'] = true
99
+ false
100
+ end
101
+ end
102
+
47
103
  def self.cloud_ip?(ip)
104
+ return if Paraxial::Helpers.get_api_key.nil?
105
+
48
106
  if ip.include?('.')
49
107
  !!PARAXIAL_IPV4.search_best(ip)
50
108
  else
@@ -52,11 +110,37 @@ module Paraxial
52
110
  end
53
111
  end
54
112
 
113
+ def self.get_cloud_provider(ip)
114
+ return if Paraxial::Helpers.get_api_key.nil?
115
+
116
+ if ip.include?('.')
117
+ PARAXIAL_IPV4.search_best(ip)&.data
118
+ else
119
+ PARAXIAL_IPV6.search_best(ip)&.data
120
+ end
121
+ end
122
+
55
123
  def self.ban_ip(ip)
124
+ return if Paraxial::Helpers.get_api_key.nil?
125
+
56
126
  Paraxial::Checker.ban_ip(ip)
57
127
  end
58
128
 
129
+ def self.ban_ip_msg(ip, length, msg)
130
+ return if Paraxial::Helpers.get_api_key.nil?
131
+
132
+ Paraxial::Checker.ban_ip_msg(ip, length, msg)
133
+ end
134
+
135
+ def self.honeypot_ban(ip, length = :week)
136
+ return if Paraxial::Helpers.get_api_key.nil?
137
+
138
+ Paraxial::Checker.honeypot_ban(ip, length)
139
+ end
140
+
59
141
  def self.allow_ip?(ip)
142
+ return if Paraxial::Helpers.get_api_key.nil?
143
+
60
144
  Paraxial::Checker.allow_ip?(ip)
61
145
  end
62
146
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paraxial
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Lubas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-09-17 00:00:00.000000000 Z
11
+ date: 2024-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -97,6 +97,7 @@ files:
97
97
  - lib/paraxial/checker.rb
98
98
  - lib/paraxial/cli.rb
99
99
  - lib/paraxial/engine.rb
100
+ - lib/paraxial/free_tier.rb
100
101
  - lib/paraxial/helpers.rb
101
102
  - lib/paraxial/initializers/marshal_patch.rb
102
103
  - lib/paraxial/initializers/startup.rb