rack-ai 0.2.0 → 0.3.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/CHANGELOG.md +38 -1
- data/examples/rails_integration_advanced.rb +353 -0
- data/examples/sinatra_microservice.rb +431 -0
- data/lib/rack/ai/configuration.rb +35 -44
- data/lib/rack/ai/middleware.rb +1 -1
- data/lib/rack/ai/utils/enhanced_logger.rb +3 -3
- data/lib/rack/ai/utils/logger.rb +6 -21
- data/lib/rack/ai/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 70c4c59ceaa3a8fd55136f31d5935e0f701f83086a48c09f3ff7b8f012961d4b
|
4
|
+
data.tar.gz: a74d4964324bdec31e68fe9a9d73ce343091159817f8707a4cf99a871fe5ce03
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5272d46f9facd1245c5e953033bc5e1c2994ab4b528efc1ae69b6fbed599c836159d34970110bac8c0044e0a6bd576c9976da9dbe2d81df3ab20d73ca9d4ff33
|
7
|
+
data.tar.gz: e67d23491fc329a4eb251f33f22c7c541cbde6af5298c29289914801f4793372f6384dc0912df026b605f2fb0f837659bc45a8c67e411046809418421be10be5
|
data/CHANGELOG.md
CHANGED
@@ -27,7 +27,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
27
27
|
- Test suite stability and reliability
|
28
28
|
- Memory leaks in long-running applications
|
29
29
|
|
30
|
-
## [0.
|
30
|
+
## [0.3.0] - 2024-01-16
|
31
|
+
|
32
|
+
### Added
|
33
|
+
- **New Providers**: HuggingFace and Local provider implementations
|
34
|
+
- **New Features**:
|
35
|
+
- Caching with predictive analysis and Redis integration
|
36
|
+
- Smart routing based on AI classification results
|
37
|
+
- Enhanced logging with structured AI insights
|
38
|
+
- Content enhancement for SEO, readability, and accessibility
|
39
|
+
- Rate limiting with IP-based tracking and configurable windows
|
40
|
+
- Anomaly detection with baseline learning and risk scoring
|
41
|
+
- **Advanced Examples**:
|
42
|
+
- Rails integration with all features enabled
|
43
|
+
- Sinatra microservice with security-focused configuration
|
44
|
+
- Comprehensive example demonstrating all capabilities
|
45
|
+
- **Enhanced Configuration**: OpenStruct-based nested configuration for better usability
|
46
|
+
- **Improved Testing**: Complete test coverage for all providers and features
|
47
|
+
- **Better Error Handling**: Comprehensive error handling throughout the codebase
|
48
|
+
|
49
|
+
### Changed
|
50
|
+
- Configuration system now uses OpenStruct for nested settings
|
51
|
+
- Logger implementation simplified and warnings resolved
|
52
|
+
- Provider initialization improved with better validation
|
53
|
+
- Middleware now supports all implemented features
|
54
|
+
|
55
|
+
### Fixed
|
56
|
+
- Configuration hash access issues resolved
|
57
|
+
- Logger constant conflicts eliminated
|
58
|
+
- Test failures for provider implementations
|
59
|
+
- OStruct deprecation warnings addressed
|
60
|
+
|
61
|
+
### Security
|
62
|
+
- Enhanced injection detection capabilities
|
63
|
+
- Improved anomaly detection algorithms
|
64
|
+
- Better rate limiting implementation
|
65
|
+
- Comprehensive security analysis features
|
66
|
+
|
67
|
+
## [0.2.0] - 2024-01-15
|
31
68
|
|
32
69
|
### Added
|
33
70
|
- Initial release with core AI middleware functionality
|
@@ -0,0 +1,353 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Advanced Rails Integration Example for Rack::AI
|
4
|
+
# This example demonstrates how to integrate Rack::AI with a Rails application
|
5
|
+
# using all available features and advanced configuration options.
|
6
|
+
|
7
|
+
require 'rails'
|
8
|
+
require 'rack/ai'
|
9
|
+
|
10
|
+
class RailsAiIntegrationApp < Rails::Application
|
11
|
+
# Basic Rails configuration
|
12
|
+
config.load_defaults 7.0
|
13
|
+
config.eager_load = false
|
14
|
+
config.cache_classes = false
|
15
|
+
config.consider_all_requests_local = true
|
16
|
+
config.secret_key_base = 'test_secret_key_base_for_rack_ai_demo'
|
17
|
+
|
18
|
+
# Configure Rack::AI with all features enabled
|
19
|
+
config.middleware.use Rack::AI::Middleware,
|
20
|
+
provider: :openai,
|
21
|
+
api_key: ENV['OPENAI_API_KEY'] || 'your-openai-api-key-here',
|
22
|
+
features: [:classification, :security, :moderation, :caching, :routing, :logging, :enhancement, :rate_limiting, :anomaly_detection],
|
23
|
+
fail_safe: true,
|
24
|
+
async_processing: true,
|
25
|
+
sanitize_logs: true,
|
26
|
+
explain_decisions: true,
|
27
|
+
|
28
|
+
# Classification settings
|
29
|
+
classification: {
|
30
|
+
confidence_threshold: 0.8,
|
31
|
+
categories: [:human, :bot, :spam, :suspicious]
|
32
|
+
},
|
33
|
+
|
34
|
+
# Security settings
|
35
|
+
security: {
|
36
|
+
injection_detection: true,
|
37
|
+
anomaly_threshold: 0.7,
|
38
|
+
block_suspicious: true
|
39
|
+
},
|
40
|
+
|
41
|
+
# Moderation settings
|
42
|
+
moderation: {
|
43
|
+
toxicity_threshold: 0.6,
|
44
|
+
check_response: false,
|
45
|
+
block_on_violation: true
|
46
|
+
},
|
47
|
+
|
48
|
+
# Caching settings
|
49
|
+
caching: {
|
50
|
+
predictive_enabled: true,
|
51
|
+
prefetch_threshold: 0.9,
|
52
|
+
redis_url: ENV['REDIS_URL'] || 'redis://localhost:6379'
|
53
|
+
},
|
54
|
+
|
55
|
+
# Routing settings
|
56
|
+
routing: {
|
57
|
+
smart_routing_enabled: true,
|
58
|
+
suspicious_route: '/security/verify',
|
59
|
+
bot_route: '/api/bot'
|
60
|
+
},
|
61
|
+
|
62
|
+
# Rate limiting settings
|
63
|
+
rate_limiting: {
|
64
|
+
window_size: 3600, # 1 hour
|
65
|
+
max_requests: 1000,
|
66
|
+
block_duration: 3600,
|
67
|
+
cleanup_interval: 300
|
68
|
+
},
|
69
|
+
|
70
|
+
# Anomaly detection settings
|
71
|
+
anomaly_detection: {
|
72
|
+
baseline_window: 86400, # 24 hours
|
73
|
+
anomaly_threshold: 2.0, # z-score threshold
|
74
|
+
min_requests: 10,
|
75
|
+
learning_rate: 0.1
|
76
|
+
}
|
77
|
+
|
78
|
+
# Routes for demonstration
|
79
|
+
routes.draw do
|
80
|
+
# Main application routes
|
81
|
+
root 'home#index'
|
82
|
+
|
83
|
+
# API endpoints that will be analyzed by AI
|
84
|
+
namespace :api do
|
85
|
+
namespace :v1 do
|
86
|
+
resources :users, only: [:index, :show, :create, :update]
|
87
|
+
resources :posts, only: [:index, :show, :create, :update, :destroy]
|
88
|
+
resources :comments, only: [:create, :update, :destroy]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Admin routes (high security)
|
93
|
+
namespace :admin do
|
94
|
+
resources :users
|
95
|
+
resources :settings
|
96
|
+
get 'dashboard', to: 'dashboard#index'
|
97
|
+
end
|
98
|
+
|
99
|
+
# Security routes (used by AI routing)
|
100
|
+
namespace :security do
|
101
|
+
get 'verify', to: 'verification#show'
|
102
|
+
post 'verify', to: 'verification#create'
|
103
|
+
get 'blocked', to: 'blocked#show'
|
104
|
+
end
|
105
|
+
|
106
|
+
# Health check endpoint (skipped by AI processing)
|
107
|
+
get 'health', to: 'health#check'
|
108
|
+
|
109
|
+
# AI analysis endpoints
|
110
|
+
namespace :ai do
|
111
|
+
get 'status', to: 'status#show'
|
112
|
+
get 'metrics', to: 'metrics#index'
|
113
|
+
get 'logs', to: 'logs#index'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Controllers
|
119
|
+
class ApplicationController < ActionController::Base
|
120
|
+
protect_from_forgery with: :exception
|
121
|
+
|
122
|
+
# Access AI analysis results in controllers
|
123
|
+
before_action :check_ai_analysis
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def check_ai_analysis
|
128
|
+
@ai_results = request.env['rack.ai']
|
129
|
+
|
130
|
+
# Log AI decisions for monitoring
|
131
|
+
if @ai_results && @ai_results[:results]
|
132
|
+
Rails.logger.info "AI Analysis: #{@ai_results[:results].inspect}"
|
133
|
+
|
134
|
+
# Handle high-risk requests
|
135
|
+
if high_risk_request?
|
136
|
+
Rails.logger.warn "High risk request detected: #{request.path}"
|
137
|
+
# Additional security measures could be implemented here
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def high_risk_request?
|
143
|
+
return false unless @ai_results&.dig(:results)
|
144
|
+
|
145
|
+
classification = @ai_results[:results][:classification]
|
146
|
+
security = @ai_results[:results][:security]
|
147
|
+
|
148
|
+
(classification && classification[:classification] == :suspicious && classification[:confidence] > 0.8) ||
|
149
|
+
(security && security[:threat_level] == :high)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class HomeController < ApplicationController
|
154
|
+
def index
|
155
|
+
@ai_status = @ai_results ? 'Active' : 'Inactive'
|
156
|
+
@request_classification = @ai_results&.dig(:results, :classification, :classification)
|
157
|
+
@security_level = @ai_results&.dig(:results, :security, :threat_level)
|
158
|
+
|
159
|
+
render json: {
|
160
|
+
message: 'Welcome to Rails + Rack::AI Demo',
|
161
|
+
ai_status: @ai_status,
|
162
|
+
classification: @request_classification,
|
163
|
+
security_level: @security_level,
|
164
|
+
timestamp: Time.current.iso8601
|
165
|
+
}
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class Api::V1::BaseController < ApplicationController
|
170
|
+
# API-specific AI handling
|
171
|
+
before_action :validate_api_request
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def validate_api_request
|
176
|
+
if @ai_results&.dig(:results, :rate_limiting, :blocked)
|
177
|
+
render json: {
|
178
|
+
error: 'Rate limit exceeded',
|
179
|
+
retry_after: @ai_results[:results][:rate_limiting][:retry_after]
|
180
|
+
}, status: 429
|
181
|
+
return
|
182
|
+
end
|
183
|
+
|
184
|
+
if @ai_results&.dig(:results, :classification, :classification) == :spam
|
185
|
+
render json: { error: 'Request classified as spam' }, status: 403
|
186
|
+
return
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
class Api::V1::UsersController < Api::V1::BaseController
|
192
|
+
def index
|
193
|
+
# Simulate user data with AI enhancement
|
194
|
+
users = [
|
195
|
+
{ id: 1, name: 'John Doe', email: 'john@example.com' },
|
196
|
+
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
|
197
|
+
]
|
198
|
+
|
199
|
+
# AI enhancement could improve API responses
|
200
|
+
enhanced = @ai_results&.dig(:results, :enhancement, :enhancement_applied)
|
201
|
+
|
202
|
+
render json: {
|
203
|
+
users: users,
|
204
|
+
meta: {
|
205
|
+
total: users.length,
|
206
|
+
ai_enhanced: enhanced || false,
|
207
|
+
classification: @ai_results&.dig(:results, :classification, :classification)
|
208
|
+
}
|
209
|
+
}
|
210
|
+
end
|
211
|
+
|
212
|
+
def show
|
213
|
+
user_id = params[:id]
|
214
|
+
|
215
|
+
# Simulate anomaly detection for unusual access patterns
|
216
|
+
anomaly_detected = @ai_results&.dig(:results, :anomaly_detection, :risk_score)&.> 0.8
|
217
|
+
|
218
|
+
if anomaly_detected
|
219
|
+
Rails.logger.warn "Anomalous access pattern detected for user #{user_id}"
|
220
|
+
end
|
221
|
+
|
222
|
+
render json: {
|
223
|
+
id: user_id,
|
224
|
+
name: "User #{user_id}",
|
225
|
+
email: "user#{user_id}@example.com",
|
226
|
+
anomaly_detected: anomaly_detected
|
227
|
+
}
|
228
|
+
end
|
229
|
+
|
230
|
+
def create
|
231
|
+
# AI moderation for user-generated content
|
232
|
+
moderation_result = @ai_results&.dig(:results, :moderation)
|
233
|
+
|
234
|
+
if moderation_result&.dig(:flagged)
|
235
|
+
render json: {
|
236
|
+
error: 'Content violates community guidelines',
|
237
|
+
categories: moderation_result[:categories]
|
238
|
+
}, status: 422
|
239
|
+
return
|
240
|
+
end
|
241
|
+
|
242
|
+
render json: {
|
243
|
+
message: 'User created successfully',
|
244
|
+
moderation_passed: true
|
245
|
+
}, status: 201
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
class SecurityController < ApplicationController
|
250
|
+
def verify
|
251
|
+
render json: {
|
252
|
+
message: 'Security verification required',
|
253
|
+
reason: params[:reason] || 'suspicious_activity',
|
254
|
+
instructions: 'Please complete the verification process'
|
255
|
+
}
|
256
|
+
end
|
257
|
+
|
258
|
+
def blocked
|
259
|
+
render json: {
|
260
|
+
message: 'Access blocked',
|
261
|
+
reason: 'security_violation',
|
262
|
+
contact: 'security@example.com'
|
263
|
+
}, status: 403
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
class HealthController < ApplicationController
|
268
|
+
def check
|
269
|
+
render json: {
|
270
|
+
status: 'ok',
|
271
|
+
timestamp: Time.current.iso8601,
|
272
|
+
ai_middleware: 'active'
|
273
|
+
}
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
class Ai::StatusController < ApplicationController
|
278
|
+
def show
|
279
|
+
render json: {
|
280
|
+
ai_middleware: {
|
281
|
+
active: @ai_results.present?,
|
282
|
+
features: @ai_results&.dig(:results)&.keys || [],
|
283
|
+
processing_time: @ai_results&.dig(:processing_time),
|
284
|
+
provider: @ai_results&.dig(:provider)
|
285
|
+
},
|
286
|
+
request_analysis: @ai_results&.dig(:results) || {}
|
287
|
+
}
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
class Ai::MetricsController < ApplicationController
|
292
|
+
def index
|
293
|
+
# In a real application, this would fetch metrics from storage
|
294
|
+
render json: {
|
295
|
+
total_requests: 1500,
|
296
|
+
classifications: {
|
297
|
+
human: 1200,
|
298
|
+
bot: 200,
|
299
|
+
suspicious: 80,
|
300
|
+
spam: 20
|
301
|
+
},
|
302
|
+
security_threats: {
|
303
|
+
low: 1400,
|
304
|
+
medium: 80,
|
305
|
+
high: 20
|
306
|
+
},
|
307
|
+
rate_limiting: {
|
308
|
+
blocked_requests: 45,
|
309
|
+
current_limits: 1000
|
310
|
+
},
|
311
|
+
anomalies_detected: 12,
|
312
|
+
cache_hit_rate: 0.78
|
313
|
+
}
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
# Initialize and run the application
|
318
|
+
if __FILE__ == $0
|
319
|
+
# Set up environment
|
320
|
+
ENV['RAILS_ENV'] ||= 'development'
|
321
|
+
|
322
|
+
# Initialize Rails application
|
323
|
+
app = RailsAiIntegrationApp.new
|
324
|
+
app.initialize!
|
325
|
+
|
326
|
+
puts "🚀 Rails + Rack::AI Advanced Integration Demo"
|
327
|
+
puts "📊 Features enabled: Classification, Security, Moderation, Caching, Routing, Logging, Enhancement, Rate Limiting, Anomaly Detection"
|
328
|
+
puts "🔗 Available endpoints:"
|
329
|
+
puts " GET / - Home page with AI status"
|
330
|
+
puts " GET /api/v1/users - Users API (with AI analysis)"
|
331
|
+
puts " GET /api/v1/users/:id - User details (with anomaly detection)"
|
332
|
+
puts " POST /api/v1/users - Create user (with content moderation)"
|
333
|
+
puts " GET /admin/dashboard - Admin area (high security)"
|
334
|
+
puts " GET /security/verify - Security verification page"
|
335
|
+
puts " GET /health - Health check (AI processing skipped)"
|
336
|
+
puts " GET /ai/status - AI middleware status"
|
337
|
+
puts " GET /ai/metrics - AI analysis metrics"
|
338
|
+
puts ""
|
339
|
+
puts "🔧 Configuration:"
|
340
|
+
puts " - Set OPENAI_API_KEY environment variable for full functionality"
|
341
|
+
puts " - Set REDIS_URL for caching features (optional)"
|
342
|
+
puts " - All features are enabled with production-ready settings"
|
343
|
+
puts ""
|
344
|
+
puts "📝 Testing suggestions:"
|
345
|
+
puts " - Try different User-Agent strings to trigger bot detection"
|
346
|
+
puts " - Make rapid requests to test rate limiting"
|
347
|
+
puts " - Send suspicious payloads to test security features"
|
348
|
+
puts " - Access admin routes to see enhanced security analysis"
|
349
|
+
|
350
|
+
# Start the server
|
351
|
+
require 'rack'
|
352
|
+
Rack::Handler::WEBrick.run(app, Port: 3000, Host: '0.0.0.0')
|
353
|
+
end
|
@@ -0,0 +1,431 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Sinatra Microservice Example with Rack::AI
|
4
|
+
# This example demonstrates how to build a secure microservice using Sinatra and Rack::AI
|
5
|
+
# with comprehensive AI-powered security, monitoring, and optimization features.
|
6
|
+
|
7
|
+
require 'sinatra/base'
|
8
|
+
require 'json'
|
9
|
+
require 'rack/ai'
|
10
|
+
|
11
|
+
class SecureMicroservice < Sinatra::Base
|
12
|
+
# Configure Rack::AI middleware with security-focused settings
|
13
|
+
use Rack::AI::Middleware,
|
14
|
+
provider: :openai,
|
15
|
+
api_key: ENV['OPENAI_API_KEY'] || 'demo-key',
|
16
|
+
features: [:classification, :security, :rate_limiting, :anomaly_detection, :logging],
|
17
|
+
fail_safe: true,
|
18
|
+
async_processing: false, # Synchronous for microservice reliability
|
19
|
+
sanitize_logs: true,
|
20
|
+
explain_decisions: true,
|
21
|
+
|
22
|
+
# Strict security settings for microservice
|
23
|
+
classification: {
|
24
|
+
confidence_threshold: 0.7,
|
25
|
+
categories: [:human, :bot, :spam, :suspicious, :malicious]
|
26
|
+
},
|
27
|
+
|
28
|
+
security: {
|
29
|
+
injection_detection: true,
|
30
|
+
anomaly_threshold: 0.6,
|
31
|
+
block_suspicious: true
|
32
|
+
},
|
33
|
+
|
34
|
+
# Aggressive rate limiting for API protection
|
35
|
+
rate_limiting: {
|
36
|
+
window_size: 300, # 5 minutes
|
37
|
+
max_requests: 100,
|
38
|
+
block_duration: 900, # 15 minutes
|
39
|
+
cleanup_interval: 60
|
40
|
+
},
|
41
|
+
|
42
|
+
# Sensitive anomaly detection
|
43
|
+
anomaly_detection: {
|
44
|
+
baseline_window: 3600, # 1 hour
|
45
|
+
anomaly_threshold: 1.5, # Lower threshold for microservice
|
46
|
+
min_requests: 5,
|
47
|
+
learning_rate: 0.2
|
48
|
+
}
|
49
|
+
|
50
|
+
# Enable JSON parsing
|
51
|
+
set :protection, except: [:json_csrf]
|
52
|
+
|
53
|
+
# Helper methods for AI analysis
|
54
|
+
helpers do
|
55
|
+
def ai_results
|
56
|
+
@ai_results ||= request.env['rack.ai']
|
57
|
+
end
|
58
|
+
|
59
|
+
def ai_classification
|
60
|
+
ai_results&.dig(:results, :classification)
|
61
|
+
end
|
62
|
+
|
63
|
+
def ai_security
|
64
|
+
ai_results&.dig(:results, :security)
|
65
|
+
end
|
66
|
+
|
67
|
+
def ai_rate_limiting
|
68
|
+
ai_results&.dig(:results, :rate_limiting)
|
69
|
+
end
|
70
|
+
|
71
|
+
def ai_anomaly
|
72
|
+
ai_results&.dig(:results, :anomaly_detection)
|
73
|
+
end
|
74
|
+
|
75
|
+
def request_blocked?
|
76
|
+
ai_rate_limiting&.dig(:blocked) ||
|
77
|
+
ai_security&.dig(:threat_level) == :high ||
|
78
|
+
ai_classification&.dig(:classification) == :malicious
|
79
|
+
end
|
80
|
+
|
81
|
+
def suspicious_request?
|
82
|
+
ai_classification&.dig(:classification) == :suspicious ||
|
83
|
+
ai_security&.dig(:threat_level) == :medium ||
|
84
|
+
ai_anomaly&.dig(:risk_score)&.> 0.7
|
85
|
+
end
|
86
|
+
|
87
|
+
def log_request_analysis
|
88
|
+
return unless ai_results
|
89
|
+
|
90
|
+
logger.info "AI Analysis: #{ai_results[:results].keys.join(', ')}"
|
91
|
+
|
92
|
+
if request_blocked?
|
93
|
+
logger.warn "Request blocked: #{request.path_info}"
|
94
|
+
elsif suspicious_request?
|
95
|
+
logger.warn "Suspicious request: #{request.path_info}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def security_headers
|
100
|
+
{
|
101
|
+
'X-AI-Classification' => ai_classification&.dig(:classification)&.to_s,
|
102
|
+
'X-AI-Security-Level' => ai_security&.dig(:threat_level)&.to_s,
|
103
|
+
'X-AI-Risk-Score' => ai_anomaly&.dig(:risk_score)&.to_s,
|
104
|
+
'X-Content-Type-Options' => 'nosniff',
|
105
|
+
'X-Frame-Options' => 'DENY',
|
106
|
+
'X-XSS-Protection' => '1; mode=block'
|
107
|
+
}.compact
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Before filter for security checks
|
112
|
+
before do
|
113
|
+
content_type :json
|
114
|
+
log_request_analysis
|
115
|
+
|
116
|
+
# Block malicious requests immediately
|
117
|
+
if request_blocked?
|
118
|
+
halt 403, security_headers, {
|
119
|
+
error: 'Request blocked by AI security system',
|
120
|
+
reason: determine_block_reason,
|
121
|
+
timestamp: Time.now.iso8601
|
122
|
+
}.to_json
|
123
|
+
end
|
124
|
+
|
125
|
+
# Add security headers to all responses
|
126
|
+
headers security_headers
|
127
|
+
end
|
128
|
+
|
129
|
+
# Health check endpoint (bypasses AI processing)
|
130
|
+
get '/health' do
|
131
|
+
{
|
132
|
+
status: 'healthy',
|
133
|
+
service: 'secure-microservice',
|
134
|
+
timestamp: Time.now.iso8601,
|
135
|
+
ai_middleware: ai_results ? 'active' : 'inactive'
|
136
|
+
}.to_json
|
137
|
+
end
|
138
|
+
|
139
|
+
# Service status with AI metrics
|
140
|
+
get '/status' do
|
141
|
+
{
|
142
|
+
service: {
|
143
|
+
name: 'secure-microservice',
|
144
|
+
version: '1.0.0',
|
145
|
+
uptime: Process.clock_gettime(Process::CLOCK_MONOTONIC),
|
146
|
+
status: 'operational'
|
147
|
+
},
|
148
|
+
ai: {
|
149
|
+
active: ai_results.present?,
|
150
|
+
provider: ai_results&.dig(:provider),
|
151
|
+
features: ai_results&.dig(:results)&.keys || [],
|
152
|
+
processing_time: ai_results&.dig(:processing_time),
|
153
|
+
classification: ai_classification,
|
154
|
+
security_level: ai_security&.dig(:threat_level),
|
155
|
+
rate_limit_status: ai_rate_limiting,
|
156
|
+
anomaly_score: ai_anomaly&.dig(:risk_score)
|
157
|
+
},
|
158
|
+
timestamp: Time.now.iso8601
|
159
|
+
}.to_json
|
160
|
+
end
|
161
|
+
|
162
|
+
# User authentication endpoint
|
163
|
+
post '/auth/login' do
|
164
|
+
request.body.rewind
|
165
|
+
data = JSON.parse(request.body.read) rescue {}
|
166
|
+
|
167
|
+
# AI analysis helps detect credential stuffing attacks
|
168
|
+
if ai_anomaly&.dig(:risk_score)&.> 0.8
|
169
|
+
logger.warn "Potential credential stuffing attack detected"
|
170
|
+
|
171
|
+
halt 429, {
|
172
|
+
error: 'Too many authentication attempts',
|
173
|
+
retry_after: 300,
|
174
|
+
timestamp: Time.now.iso8601
|
175
|
+
}.to_json
|
176
|
+
end
|
177
|
+
|
178
|
+
# Simulate authentication logic
|
179
|
+
username = data['username']
|
180
|
+
password = data['password']
|
181
|
+
|
182
|
+
if username && password && username.length > 3 && password.length > 6
|
183
|
+
token = "secure_token_#{Time.now.to_i}_#{rand(1000)}"
|
184
|
+
|
185
|
+
{
|
186
|
+
success: true,
|
187
|
+
token: token,
|
188
|
+
expires_at: (Time.now + 3600).iso8601,
|
189
|
+
security_analysis: {
|
190
|
+
classification: ai_classification&.dig(:classification),
|
191
|
+
risk_score: ai_anomaly&.dig(:risk_score) || 0.0
|
192
|
+
}
|
193
|
+
}.to_json
|
194
|
+
else
|
195
|
+
halt 400, {
|
196
|
+
error: 'Invalid credentials format',
|
197
|
+
requirements: {
|
198
|
+
username: 'minimum 4 characters',
|
199
|
+
password: 'minimum 7 characters'
|
200
|
+
}
|
201
|
+
}.to_json
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Data processing endpoint with content validation
|
206
|
+
post '/data/process' do
|
207
|
+
request.body.rewind
|
208
|
+
raw_data = request.body.read
|
209
|
+
|
210
|
+
# Check for suspicious content patterns
|
211
|
+
if ai_security&.dig(:injection_detection, :sql_injection_detected)
|
212
|
+
logger.error "SQL injection attempt detected"
|
213
|
+
halt 400, {
|
214
|
+
error: 'Malicious content detected',
|
215
|
+
type: 'sql_injection',
|
216
|
+
timestamp: Time.now.iso8601
|
217
|
+
}.to_json
|
218
|
+
end
|
219
|
+
|
220
|
+
if ai_security&.dig(:injection_detection, :xss_detected)
|
221
|
+
logger.error "XSS attempt detected"
|
222
|
+
halt 400, {
|
223
|
+
error: 'Malicious content detected',
|
224
|
+
type: 'xss_injection',
|
225
|
+
timestamp: Time.now.iso8601
|
226
|
+
}.to_json
|
227
|
+
end
|
228
|
+
|
229
|
+
begin
|
230
|
+
data = JSON.parse(raw_data)
|
231
|
+
rescue JSON::ParserError
|
232
|
+
halt 400, {
|
233
|
+
error: 'Invalid JSON format',
|
234
|
+
timestamp: Time.now.iso8601
|
235
|
+
}.to_json
|
236
|
+
end
|
237
|
+
|
238
|
+
# Process data (simulation)
|
239
|
+
processed_data = {
|
240
|
+
original_size: raw_data.bytesize,
|
241
|
+
processed_at: Time.now.iso8601,
|
242
|
+
items_count: data.is_a?(Array) ? data.length : 1,
|
243
|
+
security_scan: {
|
244
|
+
threats_detected: ai_security&.dig(:injection_detection, :threats) || [],
|
245
|
+
risk_level: ai_security&.dig(:threat_level) || :low,
|
246
|
+
classification: ai_classification&.dig(:classification) || :unknown
|
247
|
+
}
|
248
|
+
}
|
249
|
+
|
250
|
+
{
|
251
|
+
success: true,
|
252
|
+
result: processed_data,
|
253
|
+
ai_analysis: {
|
254
|
+
processing_safe: ai_security&.dig(:threat_level) != :high,
|
255
|
+
confidence: ai_classification&.dig(:confidence) || 0.0
|
256
|
+
}
|
257
|
+
}.to_json
|
258
|
+
end
|
259
|
+
|
260
|
+
# Analytics endpoint with anomaly detection
|
261
|
+
get '/analytics/requests' do
|
262
|
+
# Simulate analytics data
|
263
|
+
analytics = {
|
264
|
+
total_requests: 1250,
|
265
|
+
requests_by_classification: {
|
266
|
+
human: 1000,
|
267
|
+
bot: 150,
|
268
|
+
suspicious: 80,
|
269
|
+
spam: 15,
|
270
|
+
malicious: 5
|
271
|
+
},
|
272
|
+
security_events: {
|
273
|
+
blocked_requests: 25,
|
274
|
+
sql_injection_attempts: 8,
|
275
|
+
xss_attempts: 12,
|
276
|
+
rate_limit_violations: 45
|
277
|
+
},
|
278
|
+
anomaly_detection: {
|
279
|
+
anomalies_detected: 18,
|
280
|
+
current_baseline: ai_anomaly&.dig(:baseline_metrics) || {},
|
281
|
+
risk_distribution: {
|
282
|
+
low: 1180,
|
283
|
+
medium: 55,
|
284
|
+
high: 15
|
285
|
+
}
|
286
|
+
},
|
287
|
+
performance: {
|
288
|
+
avg_response_time: 125,
|
289
|
+
ai_processing_time: ai_results&.dig(:processing_time) || 0,
|
290
|
+
cache_hit_rate: 0.72
|
291
|
+
},
|
292
|
+
timestamp: Time.now.iso8601
|
293
|
+
}
|
294
|
+
|
295
|
+
# Add current request analysis
|
296
|
+
if ai_results
|
297
|
+
analytics[:current_request] = {
|
298
|
+
classification: ai_classification,
|
299
|
+
security: ai_security,
|
300
|
+
anomaly_score: ai_anomaly&.dig(:risk_score)
|
301
|
+
}
|
302
|
+
end
|
303
|
+
|
304
|
+
analytics.to_json
|
305
|
+
end
|
306
|
+
|
307
|
+
# File upload endpoint with security scanning
|
308
|
+
post '/upload' do
|
309
|
+
unless params[:file] && params[:file][:tempfile]
|
310
|
+
halt 400, {
|
311
|
+
error: 'No file provided',
|
312
|
+
timestamp: Time.now.iso8601
|
313
|
+
}.to_json
|
314
|
+
end
|
315
|
+
|
316
|
+
file = params[:file]
|
317
|
+
filename = file[:filename]
|
318
|
+
content = file[:tempfile].read
|
319
|
+
|
320
|
+
# AI-powered content analysis
|
321
|
+
suspicious_patterns = []
|
322
|
+
|
323
|
+
if content.include?('<script')
|
324
|
+
suspicious_patterns << 'javascript_code'
|
325
|
+
end
|
326
|
+
|
327
|
+
if content.match?(/\b(SELECT|INSERT|UPDATE|DELETE|DROP)\b/i)
|
328
|
+
suspicious_patterns << 'sql_statements'
|
329
|
+
end
|
330
|
+
|
331
|
+
if ai_security&.dig(:threat_level) == :high
|
332
|
+
logger.error "High-risk file upload blocked: #{filename}"
|
333
|
+
halt 403, {
|
334
|
+
error: 'File upload blocked by security system',
|
335
|
+
filename: filename,
|
336
|
+
threats: ai_security[:injection_detection][:threats],
|
337
|
+
timestamp: Time.now.iso8601
|
338
|
+
}.to_json
|
339
|
+
end
|
340
|
+
|
341
|
+
# Simulate file processing
|
342
|
+
file_info = {
|
343
|
+
filename: filename,
|
344
|
+
size: content.bytesize,
|
345
|
+
content_type: file[:type],
|
346
|
+
uploaded_at: Time.now.iso8601,
|
347
|
+
security_scan: {
|
348
|
+
suspicious_patterns: suspicious_patterns,
|
349
|
+
threat_level: ai_security&.dig(:threat_level) || :low,
|
350
|
+
safe_to_process: suspicious_patterns.empty? && ai_security&.dig(:threat_level) != :high
|
351
|
+
},
|
352
|
+
ai_classification: ai_classification&.dig(:classification)
|
353
|
+
}
|
354
|
+
|
355
|
+
{
|
356
|
+
success: true,
|
357
|
+
file: file_info,
|
358
|
+
message: suspicious_patterns.empty? ? 'File uploaded successfully' : 'File uploaded with warnings'
|
359
|
+
}.to_json
|
360
|
+
end
|
361
|
+
|
362
|
+
# Error handlers
|
363
|
+
error 404 do
|
364
|
+
{
|
365
|
+
error: 'Endpoint not found',
|
366
|
+
path: request.path_info,
|
367
|
+
method: request.request_method,
|
368
|
+
timestamp: Time.now.iso8601,
|
369
|
+
ai_classification: ai_classification&.dig(:classification)
|
370
|
+
}.to_json
|
371
|
+
end
|
372
|
+
|
373
|
+
error 500 do
|
374
|
+
logger.error "Internal server error: #{env['sinatra.error']}"
|
375
|
+
|
376
|
+
{
|
377
|
+
error: 'Internal server error',
|
378
|
+
timestamp: Time.now.iso8601,
|
379
|
+
request_id: env['HTTP_X_REQUEST_ID'] || SecureRandom.hex(8)
|
380
|
+
}.to_json
|
381
|
+
end
|
382
|
+
|
383
|
+
private
|
384
|
+
|
385
|
+
def determine_block_reason
|
386
|
+
reasons = []
|
387
|
+
|
388
|
+
if ai_rate_limiting&.dig(:blocked)
|
389
|
+
reasons << "rate_limit_exceeded"
|
390
|
+
end
|
391
|
+
|
392
|
+
if ai_security&.dig(:threat_level) == :high
|
393
|
+
reasons << "high_security_threat"
|
394
|
+
end
|
395
|
+
|
396
|
+
if ai_classification&.dig(:classification) == :malicious
|
397
|
+
reasons << "malicious_classification"
|
398
|
+
end
|
399
|
+
|
400
|
+
reasons.join(', ')
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
# CLI runner
|
405
|
+
if __FILE__ == $0
|
406
|
+
require 'rack'
|
407
|
+
|
408
|
+
puts "🔒 Secure Microservice with Rack::AI"
|
409
|
+
puts "🛡️ Features: Classification, Security, Rate Limiting, Anomaly Detection, Logging"
|
410
|
+
puts "🔗 Available endpoints:"
|
411
|
+
puts " GET /health - Health check (AI processing bypassed)"
|
412
|
+
puts " GET /status - Service and AI status"
|
413
|
+
puts " POST /auth/login - User authentication with anomaly detection"
|
414
|
+
puts " POST /data/process - Data processing with injection detection"
|
415
|
+
puts " GET /analytics/requests - Request analytics and security metrics"
|
416
|
+
puts " POST /upload - File upload with security scanning"
|
417
|
+
puts ""
|
418
|
+
puts "🔧 Configuration:"
|
419
|
+
puts " - Set OPENAI_API_KEY for full AI functionality"
|
420
|
+
puts " - Aggressive security settings for microservice protection"
|
421
|
+
puts " - Real-time threat detection and blocking"
|
422
|
+
puts ""
|
423
|
+
puts "🧪 Testing commands:"
|
424
|
+
puts " curl http://localhost:4567/health"
|
425
|
+
puts " curl http://localhost:4567/status"
|
426
|
+
puts " curl -X POST http://localhost:4567/auth/login -d '{\"username\":\"test\",\"password\":\"password123\"}' -H 'Content-Type: application/json'"
|
427
|
+
puts " curl -X POST http://localhost:4567/data/process -d '[{\"id\":1,\"data\":\"test\"}]' -H 'Content-Type: application/json'"
|
428
|
+
puts ""
|
429
|
+
|
430
|
+
Rack::Handler::WEBrick.run(SecureMicroservice, Port: 4567, Host: '0.0.0.0')
|
431
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "dry
|
4
|
-
require "dry
|
3
|
+
require "dry-configurable"
|
4
|
+
require "dry-validation"
|
5
|
+
require "ostruct"
|
5
6
|
|
6
7
|
module Rack
|
7
8
|
module AI
|
@@ -105,45 +106,38 @@ module Rack
|
|
105
106
|
@metrics_enabled = true
|
106
107
|
@explain_decisions = false
|
107
108
|
|
108
|
-
#
|
109
|
-
@classification = {
|
110
|
-
confidence_threshold: 0.8,
|
111
|
-
categories: [:spam, :bot, :human, :suspicious]
|
112
|
-
}
|
113
|
-
|
114
|
-
@moderation = {
|
115
|
-
toxicity_threshold: 0.7,
|
116
|
-
check_response: false,
|
117
|
-
block_on_violation: true
|
118
|
-
}
|
119
|
-
|
120
|
-
@caching = {
|
121
|
-
predictive_enabled: true,
|
122
|
-
prefetch_threshold: 0.9,
|
123
|
-
redis_url: "redis://localhost:6379"
|
124
|
-
}
|
125
|
-
|
126
|
-
@routing = {
|
127
|
-
smart_routing_enabled: true,
|
128
|
-
suspicious_route: "/captcha",
|
129
|
-
bot_route: "/api/bot"
|
130
|
-
}
|
109
|
+
# Nested configuration objects with OpenStruct-like behavior
|
131
110
|
end
|
132
111
|
|
133
112
|
def classification
|
134
|
-
@classification
|
113
|
+
@classification ||= OpenStruct.new(
|
114
|
+
confidence_threshold: 0.8,
|
115
|
+
categories: [:spam, :bot, :human, :suspicious]
|
116
|
+
)
|
135
117
|
end
|
136
118
|
|
137
119
|
def moderation
|
138
|
-
@moderation
|
120
|
+
@moderation ||= OpenStruct.new(
|
121
|
+
toxicity_threshold: 0.7,
|
122
|
+
check_response: false,
|
123
|
+
block_on_violation: true
|
124
|
+
)
|
139
125
|
end
|
140
126
|
|
141
127
|
def caching
|
142
|
-
@caching
|
128
|
+
@caching ||= OpenStruct.new(
|
129
|
+
predictive_enabled: true,
|
130
|
+
prefetch_threshold: 0.9,
|
131
|
+
redis_url: "redis://localhost:6379"
|
132
|
+
)
|
143
133
|
end
|
144
134
|
|
145
135
|
def routing
|
146
|
-
@routing
|
136
|
+
@routing ||= OpenStruct.new(
|
137
|
+
smart_routing_enabled: true,
|
138
|
+
suspicious_route: "/captcha",
|
139
|
+
bot_route: "/api/bot"
|
140
|
+
)
|
147
141
|
end
|
148
142
|
|
149
143
|
# For compatibility with middleware
|
@@ -186,11 +180,11 @@ module Rack
|
|
186
180
|
|
187
181
|
def to_h
|
188
182
|
{
|
189
|
-
provider:
|
190
|
-
api_key:
|
191
|
-
api_url:
|
192
|
-
timeout:
|
193
|
-
retries:
|
183
|
+
provider: provider,
|
184
|
+
api_key: api_key,
|
185
|
+
api_url: api_url,
|
186
|
+
timeout: timeout,
|
187
|
+
retries: retries,
|
194
188
|
features: @features,
|
195
189
|
fail_safe: @fail_safe,
|
196
190
|
async_processing: @async_processing,
|
@@ -212,16 +206,13 @@ module Rack
|
|
212
206
|
end
|
213
207
|
|
214
208
|
def provider_config
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
else
|
223
|
-
{}
|
224
|
-
end
|
209
|
+
{
|
210
|
+
provider: provider,
|
211
|
+
api_key: api_key,
|
212
|
+
api_url: api_url,
|
213
|
+
timeout: timeout,
|
214
|
+
retries: retries
|
215
|
+
}
|
225
216
|
end
|
226
217
|
end
|
227
218
|
end
|
data/lib/rack/ai/middleware.rb
CHANGED
@@ -6,7 +6,7 @@ require 'json'
|
|
6
6
|
module Rack
|
7
7
|
module AI
|
8
8
|
module Utils
|
9
|
-
class
|
9
|
+
class AdvancedLogger
|
10
10
|
LOG_LEVELS = {
|
11
11
|
debug: ::Logger::DEBUG,
|
12
12
|
info: ::Logger::INFO,
|
@@ -123,8 +123,8 @@ module Rack
|
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
|
-
#
|
127
|
-
|
126
|
+
# Alias for backward compatibility
|
127
|
+
EnhancedLogger = AdvancedLogger
|
128
128
|
end
|
129
129
|
end
|
130
130
|
end
|
data/lib/rack/ai/utils/logger.rb
CHANGED
@@ -7,45 +7,29 @@ module Rack
|
|
7
7
|
module AI
|
8
8
|
module Utils
|
9
9
|
class Logger
|
10
|
-
def self.debug(message, context = {})
|
11
|
-
puts "[DEBUG] #{message} #{context}" if ENV['RACK_AI_DEBUG']
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.info(message, context = {})
|
15
|
-
puts "[INFO] #{message} #{context}"
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.warn(message, context = {})
|
19
|
-
puts "[WARN] #{message} #{context}"
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.error(message, context = {})
|
23
|
-
puts "[ERROR] #{message} #{context}"
|
24
|
-
end
|
25
|
-
|
26
10
|
class << self
|
27
11
|
def logger
|
28
12
|
@logger ||= build_logger
|
29
13
|
end
|
30
14
|
|
31
15
|
def debug(message, metadata = {})
|
32
|
-
|
16
|
+
puts "[DEBUG] #{message} #{metadata}" if ENV['RACK_AI_DEBUG']
|
33
17
|
end
|
34
18
|
|
35
19
|
def info(message, metadata = {})
|
36
|
-
|
20
|
+
puts "[INFO] #{message} #{metadata}"
|
37
21
|
end
|
38
22
|
|
39
23
|
def warn(message, metadata = {})
|
40
|
-
|
24
|
+
puts "[WARN] #{message} #{metadata}"
|
41
25
|
end
|
42
26
|
|
43
27
|
def error(message, metadata = {})
|
44
|
-
|
28
|
+
puts "[ERROR] #{message} #{metadata}"
|
45
29
|
end
|
46
30
|
|
47
31
|
def fatal(message, metadata = {})
|
48
|
-
|
32
|
+
puts "[FATAL] #{message} #{metadata}"
|
49
33
|
end
|
50
34
|
|
51
35
|
private
|
@@ -122,6 +106,7 @@ module Rack
|
|
122
106
|
end
|
123
107
|
end
|
124
108
|
end
|
109
|
+
|
125
110
|
end
|
126
111
|
end
|
127
112
|
end
|
data/lib/rack/ai/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-ai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ahmet KAHRAMAN
|
@@ -243,7 +243,9 @@ files:
|
|
243
243
|
- benchmarks/performance_benchmark.rb
|
244
244
|
- examples/comprehensive_example.rb
|
245
245
|
- examples/rails_integration.rb
|
246
|
+
- examples/rails_integration_advanced.rb
|
246
247
|
- examples/sinatra_integration.rb
|
248
|
+
- examples/sinatra_microservice.rb
|
247
249
|
- lib/rack/ai.rb
|
248
250
|
- lib/rack/ai/configuration.rb
|
249
251
|
- lib/rack/ai/features/anomaly_detection.rb
|