beskar 0.0.1 → 0.1.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +143 -0
  3. data/README.md +987 -21
  4. data/app/controllers/beskar/application_controller.rb +170 -0
  5. data/app/controllers/beskar/banned_ips_controller.rb +280 -0
  6. data/app/controllers/beskar/dashboard_controller.rb +70 -0
  7. data/app/controllers/beskar/security_events_controller.rb +182 -0
  8. data/app/controllers/concerns/beskar/controllers/security_tracking.rb +70 -0
  9. data/app/models/beskar/banned_ip.rb +193 -0
  10. data/app/models/beskar/security_event.rb +64 -0
  11. data/app/services/beskar/banned_ip_manager.rb +78 -0
  12. data/app/views/beskar/banned_ips/edit.html.erb +259 -0
  13. data/app/views/beskar/banned_ips/index.html.erb +361 -0
  14. data/app/views/beskar/banned_ips/new.html.erb +310 -0
  15. data/app/views/beskar/banned_ips/show.html.erb +310 -0
  16. data/app/views/beskar/dashboard/index.html.erb +280 -0
  17. data/app/views/beskar/security_events/index.html.erb +309 -0
  18. data/app/views/beskar/security_events/show.html.erb +307 -0
  19. data/app/views/layouts/beskar/application.html.erb +647 -5
  20. data/config/locales/en.yml +10 -0
  21. data/config/routes.rb +41 -0
  22. data/db/migrate/20251016000001_create_beskar_security_events.rb +25 -0
  23. data/db/migrate/20251016000002_create_beskar_banned_ips.rb +23 -0
  24. data/lib/beskar/configuration.rb +214 -0
  25. data/lib/beskar/engine.rb +105 -0
  26. data/lib/beskar/logger.rb +293 -0
  27. data/lib/beskar/middleware/request_analyzer.rb +305 -0
  28. data/lib/beskar/middleware.rb +4 -0
  29. data/lib/beskar/models/security_trackable.rb +25 -0
  30. data/lib/beskar/models/security_trackable_authenticable.rb +167 -0
  31. data/lib/beskar/models/security_trackable_devise.rb +82 -0
  32. data/lib/beskar/models/security_trackable_generic.rb +355 -0
  33. data/lib/beskar/services/account_locker.rb +263 -0
  34. data/lib/beskar/services/device_detector.rb +250 -0
  35. data/lib/beskar/services/geolocation_service.rb +392 -0
  36. data/lib/beskar/services/ip_whitelist.rb +113 -0
  37. data/lib/beskar/services/rate_limiter.rb +257 -0
  38. data/lib/beskar/services/waf.rb +551 -0
  39. data/lib/beskar/version.rb +1 -1
  40. data/lib/beskar.rb +32 -1
  41. data/lib/generators/beskar/install/install_generator.rb +158 -0
  42. data/lib/generators/beskar/install/templates/initializer.rb.tt +177 -0
  43. data/lib/tasks/beskar_tasks.rake +121 -4
  44. metadata +138 -5
data/README.md CHANGED
@@ -2,13 +2,45 @@
2
2
 
3
3
  **Beskar** is a comprehensive, Rails-native security engine designed to provide multi-layered, proactive protection for modern web applications. It defends against common threats, bot activity, and account takeovers without requiring external dependencies, integrating seamlessly into your application as a natural extension of the framework.
4
4
 
5
+ ## Table of Contents
6
+
7
+ - [Features](#features)
8
+ - [Installation](#installation)
9
+ - [Quick Start](#quick-start)
10
+ - [Dashboard Authentication (REQUIRED)](#dashboard-authentication-required)
11
+ - [Add to Your User Model](#add-to-your-user-model)
12
+ - [Configuration](#configuration)
13
+ - [Usage](#usage)
14
+ - [Risk-Based Account Locking](#risk-based-account-locking-with-devise-lockable)
15
+ - [Rate Limiting](#rate-limiting)
16
+ - [IP Whitelisting](#ip-whitelisting)
17
+ - [Web Application Firewall (WAF)](#web-application-firewall-waf)
18
+ - [IP Blocking and Banning](#ip-blocking-and-banning)
19
+ - [Security Events](#security-events)
20
+ - [Middleware Integration](#middleware-integration)
21
+ - [WAF Pattern Reference](#waf-pattern-reference)
22
+ - [Security Best Practices](#security-best-practices)
23
+ - [Troubleshooting](#troubleshooting)
24
+ - [Development](#development)
25
+ - [Contributing](#contributing)
26
+ - [License](#license)
27
+
5
28
  ## Features
6
29
 
7
- - **Web Application Firewall (WAF):** Real-time protection against common attack vectors like SQL Injection (SQLi) and Cross-Site Scripting (XSS).
30
+ - **Devise Integration:** Seamless integration with Devise authentication for automatic login tracking and security analysis.
31
+ - **Risk-Based Account Locking:** Automatically locks accounts when authentication risk scores exceed configurable thresholds, preventing compromised account access.
32
+ - **Smart Rate Limiting:** Distributed rate limiting using Rails.cache with IP-based and account-based throttling with exponential backoff.
33
+ - **Brute Force Detection:** Advanced pattern recognition to detect single account attacks vs credential stuffing attempts, with automatic IP banning.
34
+ - **IP Whitelisting:** Allow trusted IPs (office networks, partners, security scanners) to bypass blocking while maintaining full audit logs. Supports individual IPs and CIDR notation.
35
+ - **Persistent IP Blocking:** Hybrid cache + database blocking system that survives application restarts. Auto-bans IPs after authentication abuse or excessive rate limiting violations.
36
+ - **Web Application Firewall (WAF):** Real-time detection and blocking of vulnerability scanning attempts across 12 attack categories including Rails exception analysis (WordPress scans, WordPress static files, PHP admin panels, config files, path traversal, framework debug, CMS detection, common exploits, UnknownFormat, IP spoofing, InvalidType, RecordNotFound enumeration). Includes escalating ban durations, monitor-only mode, and configurable exclusion patterns.
37
+ - **Security Event Tracking:** Comprehensive logging of authentication events with risk scoring and metadata extraction.
38
+ - **IP Geolocation:** MaxMind GeoLite2-City database integration for country/city location, coordinates, timezone, and enhanced risk scoring (configurable, database not included due to licensing).
39
+ - **Geographic Anomaly Detection:** Haversine-based impossible travel detection and location-based risk assessment.
8
40
  - **Advanced Bot Detection:** Multi-layered defense using JavaScript challenges and invisible honeypots to filter out malicious bots while allowing legitimate ones.
9
- - **Account Takeover (ATO) Prevention:** Actively monitors and blocks brute-force attacks, credential stuffing, and impossible travel anomalies.
41
+ - **Modular Architecture:** Devise-specific code is isolated in separate services for maintainability and extensibility.
10
42
  - **Rails-Native Architecture:** Built as a mountable `Rails::Engine`, it leverages `ActiveJob` and `Rails.cache` for high performance and low overhead.
11
- - **Real-Time Dashboard (Coming Soon):** A mountable dashboard to visualize security events and monitor threats as they happen.
43
+ - **Security Dashboard:** A mountable web interface for monitoring security events, managing IP bans, and viewing statistics. Features configurable authentication, real-time filtering, and export capabilities. See [Dashboard Authentication](#dashboard-authentication) section below.
12
44
 
13
45
  ## Installation
14
46
 
@@ -16,52 +48,987 @@ Add this line to your application's Gemfile:
16
48
 
17
49
  ```ruby
18
50
  gem 'beskar'
19
- ````
51
+ ```
20
52
 
21
53
  And then execute:
22
54
 
23
55
  ```bash
24
- $ bundle install
56
+ bundle install
25
57
  ```
26
58
 
27
- Next, run the installation generator. This will copy the necessary migrations and create an initializer file.
59
+ Run the installation task to set up Beskar:
28
60
 
29
61
  ```bash
30
- $ rails g beskar:install
62
+ bin/rails beskar:install
31
63
  ```
32
64
 
33
- Finally, run the database migrations:
65
+ This will:
66
+ - Copy all necessary migrations to your application
67
+ - Create `config/initializers/beskar.rb` with sensible defaults
68
+ - Display next steps for completing the setup
69
+
70
+ Then run the database migrations:
34
71
 
35
72
  ```bash
36
- $ rails db:migrate
73
+ bin/rails db:migrate
74
+ ```
75
+
76
+ ### Quick Start
77
+
78
+ **1. Configure Dashboard Authentication (Required)**
79
+
80
+ Before using Beskar, you must configure authentication for the dashboard. See the [Dashboard Authentication](#dashboard-authentication) section below for details and examples.
81
+
82
+ **2. Enable WAF Monitoring**
83
+
84
+ By default, Beskar enables the **Web Application Firewall (WAF) in monitor-only mode**. This means:
85
+ - ✅ Vulnerability scans are detected and logged
86
+ - ✅ Security events are created for analysis
87
+ - ⚠️ No requests are blocked yet (safe to enable in production)
88
+
89
+ After monitoring for 24-48 hours, review the logs and disable monitor-only mode to enable active blocking:
90
+
91
+ ```ruby
92
+ # config/initializers/beskar.rb
93
+ Beskar.configure do |config|
94
+ config.monitor_only = true # Change this to false to enable blocking
95
+ config.waf[:enabled] = true
96
+ # ... rest of configuration
97
+ end
98
+ ```
99
+
100
+ ### Dashboard Authentication (REQUIRED)
101
+
102
+ **⚠️ IMPORTANT: Dashboard authentication must be configured for all environments.**
103
+
104
+ The Beskar dashboard requires authentication to prevent unauthorized access. You must configure how users authenticate to access the dashboard by setting up the `authenticate_admin` callback:
105
+
106
+ ```ruby
107
+ # config/initializers/beskar.rb
108
+ Beskar.configure do |config|
109
+ # REQUIRED: Configure dashboard authentication
110
+ # The block is executed in the controller context and receives the request object.
111
+ # You have access to all controller methods (cookies, session, etc.) and helpers.
112
+ config.authenticate_admin = ->(request) do
113
+ # Return truthy to allow access, falsey to deny
114
+
115
+ # Example 1: Devise with admin role (recommended for production)
116
+ user = request.env['warden']&.authenticate(scope: :user)
117
+ user&.admin?
118
+ end
119
+ end
120
+ ```
121
+
122
+ **Why this is required:** Previous versions allowed unauthenticated access in development/test environments, which could lead to production security issues. Now, authentication must be explicitly configured for all environments to prevent accidental exposure.
123
+
124
+ **Other Authentication Strategies:**
125
+
126
+ ```ruby
127
+ # Token-based authentication
128
+ config.authenticate_admin = ->(request) do
129
+ request.headers['Authorization'] == "Bearer #{ENV['BESKAR_ADMIN_TOKEN']}"
130
+ end
131
+
132
+ # HTTP Basic Auth (uses controller method)
133
+ config.authenticate_admin = ->(request) do
134
+ authenticate_or_request_with_http_basic do |username, password|
135
+ username == ENV['BESKAR_USERNAME'] && password == ENV['BESKAR_PASSWORD']
136
+ end
137
+ end
138
+
139
+ # Cookie-based authentication (uses controller cookies)
140
+ config.authenticate_admin = ->(request) do
141
+ cookies.signed[:admin_token] == ENV['BESKAR_ADMIN_TOKEN']
142
+ end
143
+
144
+ # Development/Testing bypass (use with caution!)
145
+ config.authenticate_admin = ->(request) do
146
+ Rails.env.development? || Rails.env.test?
147
+ end
148
+ ```
149
+
150
+ **Accessing the Dashboard:**
151
+
152
+ After configuring authentication, mount the engine in your routes:
153
+
154
+ ```ruby
155
+ # config/routes.rb
156
+ Rails.application.routes.draw do
157
+ mount Beskar::Engine => "/beskar"
158
+ end
159
+ ```
160
+
161
+ Then visit `http://localhost:3000/beskar` to access the dashboard.
162
+
163
+ **Dashboard Features:**
164
+ - 📊 Security event monitoring with filtering and search
165
+ - 🚫 IP ban management (view, extend, unban)
166
+ - 📈 Statistics and risk distribution analysis
167
+ - 📥 Export capabilities (CSV/JSON)
168
+ - 🔒 CSRF protection and secure by default
169
+
170
+ ### Add to Your User Model
171
+
172
+ Include the `SecurityTrackable` concern in your Devise user model:
173
+
174
+ ```ruby
175
+ # app/models/user.rb
176
+ class User < ApplicationRecord
177
+ include Beskar::Models::SecurityTrackable
178
+
179
+ devise :database_authenticatable, :registerable,
180
+ :recoverable, :rememberable, :validatable
181
+ # ... other Devise modules
182
+ end
37
183
  ```
38
184
 
39
185
  ## Configuration
40
186
 
41
- You can configure Rails Security Shield in the initializer file created by the installer:
187
+ You can configure Beskar in the initializer file created by the installer.
188
+
189
+ > **Note:** Dashboard authentication setup is covered in the [Dashboard Authentication](#dashboard-authentication-required) section above.
42
190
 
43
191
  ```ruby
44
192
  # config/initializers/beskar.rb
45
- RailsSecurityShield.configure do |config|
193
+ Beskar.configure do |config|
194
+ # === Dashboard Authentication (REQUIRED) ===
195
+ # See "Dashboard Authentication" section above for examples and details
196
+ config.authenticate_admin = ->(request) do
197
+ user = request.env['warden']&.authenticate(scope: :user)
198
+ user&.admin?
199
+ end
200
+
201
+ # === Security Tracking ===
202
+ # Controls what security events are tracked and analyzed
203
+ config.security_tracking = {
204
+ enabled: true, # Master switch - disables all tracking when false
205
+ track_successful_logins: true, # Track successful authentication events
206
+ track_failed_logins: true, # Track failed authentication attempts
207
+ auto_analyze_patterns: true # Enable automatic pattern analysis for threats
208
+ }
209
+
210
+ # === Rate Limiting ===
211
+ config.rate_limiting = {
212
+ ip_attempts: {
213
+ limit: 10, # Max attempts per IP
214
+ period: 1.hour, # Time window
215
+ exponential_backoff: true # Enable exponential backoff
216
+ },
217
+ account_attempts: {
218
+ limit: 5, # Max attempts per account
219
+ period: 15.minutes, # Time window
220
+ exponential_backoff: true
221
+ },
222
+ global_attempts: {
223
+ limit: 100, # System-wide limit
224
+ period: 1.minute,
225
+ exponential_backoff: false
226
+ }
227
+ }
228
+
229
+ # === IP Whitelisting ===
230
+ # See "IP Whitelisting" section below for detailed examples
231
+ config.ip_whitelist = [] # Add trusted IPs here (supports CIDR notation)
232
+
46
233
  # === Web Application Firewall (WAF) ===
47
- # Enable or disable the WAF middleware. Defaults to false.
48
- config.enable_waf = true
234
+ # See "Web Application Firewall" section below for production examples
235
+ # Defaults shown here - use [:key] syntax to preserve other defaults
236
+ config.waf[:enabled] = true # Master switch for WAF
237
+ # config.waf[:auto_block] = true # Default: true
238
+ # config.waf[:score_threshold] = 150 # Default: 150 (cumulative risk score before blocking)
239
+ # config.waf[:violation_window] = 6.hours # Default: 6 hours (max time to track violations)
240
+ # config.waf[:block_durations] = [1.hour, 6.hours, 24.hours, 7.days] # Escalating bans
241
+ # config.waf[:permanent_block_after] = 500 # Permanent after cumulative score reaches 500
242
+ # config.waf[:create_security_events] = true # Log to SecurityEvent table
243
+ # config.waf[:record_not_found_exclusions] = [] # Regex patterns for false positives
244
+ # config.waf[:decay_enabled] = true # Enable exponential decay of violation scores
245
+ # config.waf[:decay_rates] = { # Decay rates by severity (half-life in minutes)
246
+ # critical: 360, # 6 hour half-life
247
+ # high: 120, # 2 hour half-life
248
+ # medium: 45, # 45 minute half-life
249
+ # low: 15 # 15 minute half-life
250
+ # }
251
+ # config.waf[:max_violations_tracked] = 50 # Maximum violations to track per IP
49
252
 
50
- # === Account Protection ===
51
- # Set the class name of your user model.
52
- # This is used for tracking security events related to users.
53
- config.user_class = 'User'
253
+ # === Risk-Based Account Locking ===
254
+ # Automatically lock accounts when authentication risk score exceeds threshold
255
+ config.risk_based_locking = {
256
+ enabled: false, # Master switch for risk-based locking
257
+ risk_threshold: 75, # Lock account if risk score >= this value (0-100)
258
+ lock_strategy: :devise_lockable, # Strategy: :devise_lockable, :custom, :none
259
+ auto_unlock_time: 1.hour, # Time until automatic unlock (if supported)
260
+ notify_user: true, # Send notification on lock (future feature)
261
+ log_lock_events: true # Create security event for locks
262
+ }
54
263
 
55
- # More configuration options will be available here.
264
+ # === IP Geolocation ===
265
+ # Configure IP geolocation for enhanced risk assessment
266
+ # Note: You must provide your own MaxMind GeoLite2-City database due to licensing
267
+ # Download from: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data
268
+ config.geolocation = {
269
+ provider: :maxmind, # Provider: :maxmind or :mock (for testing)
270
+ maxmind_city_db_path: Rails.root.join('db', 'geoip', 'GeoLite2-City.mmdb').to_s,
271
+ cache_ttl: 4.hours # How long to cache geolocation results
272
+ }
56
273
  end
274
+
57
275
  ```
58
276
 
59
277
  ## Usage
60
278
 
61
- Once installed and configured, Rails Security Shield works automatically. Its middleware is injected into the Rails request stack to analyze incoming traffic and block threats before they reach your application.
279
+ > **Note:** If you haven't already, see the [Add to Your User Model](#add-to-your-user-model) section in Quick Start for setting up `SecurityTrackable`.
280
+
281
+ ### Risk-Based Account Locking (with Devise Lockable)
282
+
283
+ Beskar can automatically lock user accounts when the calculated risk score exceeds a configured threshold. This prevents compromised accounts from being accessed even after successful authentication.
284
+
285
+ **Setup with Devise Lockable:**
286
+
287
+ 1. Enable the `:lockable` module in your User model:
288
+
289
+ ```ruby
290
+ class User < ApplicationRecord
291
+ devise :database_authenticatable, :registerable,
292
+ :recoverable, :rememberable, :validatable,
293
+ :lockable # Add this for risk-based locking
294
+
295
+ include Beskar::Models::SecurityTrackable
296
+ end
297
+ ```
298
+
299
+ 2. Generate and run the migration to add lockable columns:
300
+
301
+ ```bash
302
+ rails generate devise User # This will add lockable columns if not present
303
+ # Or manually add:
304
+ # - failed_attempts (integer)
305
+ # - unlock_token (string)
306
+ # - locked_at (datetime)
307
+ rails db:migrate
308
+ ```
309
+
310
+ 3. Enable risk-based locking in your initializer:
311
+
312
+ ```ruby
313
+ # config/initializers/beskar.rb
314
+ Beskar.configure do |config|
315
+ config.risk_based_locking = {
316
+ enabled: true, # Enable the feature
317
+ risk_threshold: 75, # Lock when risk >= 75
318
+ lock_strategy: :devise_lockable, # Use Devise's lockable module
319
+ auto_unlock_time: 1.hour, # Automatic unlock after 1 hour
320
+ notify_user: true, # Log notification intent
321
+ log_lock_events: true # Create security events
322
+ }
323
+ end
324
+ ```
325
+
326
+ **How it works:**
327
+
328
+ - After each successful authentication, Beskar calculates a risk score (0-100) based on:
329
+ - Geographic anomalies (impossible travel, country changes)
330
+ - Device fingerprints (suspicious user agents, bot signatures)
331
+ - Login patterns (velocity, time of day, recent failures)
332
+ - IP reputation and geolocation risk
333
+
334
+ - **Adaptive Learning:** The system learns from user behavior:
335
+ - After 2+ successful logins from an IP, that location becomes "established"
336
+ - If a user unlocks and logs in successfully, that pattern is trusted
337
+ - Risk scores are reduced to 30% for established patterns (capped at 25)
338
+ - This prevents repeated locks after users validate their login context
339
+
340
+ - If the risk score meets or exceeds the configured threshold, the account is automatically locked
341
+ - The user session is terminated immediately to prevent access
342
+ - A security event is logged with the lock reason and risk details (always logged for audit trail)
343
+ - The account remains locked until manually unlocked or the auto-unlock time expires (if supported)
344
+
345
+ **Example Adaptive Flow:**
346
+ 1. User travels to new location → High risk (85) → Account locked
347
+ 2. User unlocks account → Validates legitimacy
348
+ 3. User logs in from same location → Pattern established → Risk reduced to 25 → Login succeeds ✅
349
+ 4. Future logins from this location → Normal risk → No more locks
350
+
351
+ See `ADAPTIVE_LEARNING.md` for detailed documentation.
352
+
353
+ **Lock Reasons:**
354
+
355
+ The system identifies specific reasons for locking:
356
+ - `:impossible_travel` - Login from location requiring impossible travel speed
357
+ - `:suspicious_device` - Bot signature or suspicious user agent detected
358
+ - `:geographic_anomaly` - Country change or high-risk location
359
+ - `:high_risk_authentication` - General high-risk authentication pattern
360
+
361
+ **Manual Lock/Unlock Operations:**
362
+
363
+ ```ruby
364
+ # Manually lock an account based on risk
365
+ locker = Beskar::Services::AccountLocker.new(
366
+ user,
367
+ risk_score: 85,
368
+ reason: :suspicious_device,
369
+ metadata: { ip_address: request.ip }
370
+ )
371
+
372
+ if locker.should_lock?
373
+ locker.lock! # Lock the account
374
+ end
375
+
376
+ # Check if account is locked
377
+ locker.locked? # => true/false
378
+
379
+ # Manually unlock
380
+ locker.unlock!
381
+ ```
382
+
383
+ ### Security Event Tracking
384
+
385
+ Beskar automatically tracks login attempts and creates security events with rich metadata:
386
+
387
+ ```ruby
388
+ # Check recent failed attempts for a user
389
+ user.recent_failed_attempts(within: 1.hour)
390
+
391
+ # Check if user has suspicious login patterns
392
+ user.suspicious_login_pattern?
393
+
394
+ # Get recent successful logins
395
+ user.recent_successful_logins(within: 24.hours)
396
+
397
+ # Access security events
398
+ user.security_events.login_failures.recent
399
+ ```
400
+
401
+ ### Rate Limiting
402
+
403
+ Check if a request should be rate limited:
404
+
405
+ ```ruby
406
+ # In a controller or middleware
407
+ if Beskar.rate_limited?(request, current_user)
408
+ render json: { error: 'Rate limit exceeded' }, status: 429
409
+ return
410
+ end
411
+
412
+ # Manual rate limiting check
413
+ rate_limiter = Beskar::Services::RateLimiter.new(request.ip, current_user)
414
+ unless rate_limiter.allowed?
415
+ # Handle rate limiting
416
+ time_until_reset = rate_limiter.time_until_reset
417
+ end
418
+ ```
419
+
420
+ ### Security Events Analysis
421
+
422
+ Security events are automatically created and include:
423
+
424
+ - **Event Type**: `login_success`, `login_failure`
425
+ - **IP Address**: Client IP with proxy detection
426
+ - **User Agent**: Browser and device information
427
+ - **Risk Score**: 0-100 based on various factors
428
+ - **Metadata**: Device info, geolocation, timestamps
429
+ - **Attack Patterns**: Detection of brute force, credential stuffing, etc.
430
+
431
+ ### Attack Pattern Detection
432
+
433
+ Beskar can identify different types of attacks:
434
+
435
+ ```ruby
436
+ rate_limiter = Beskar::Services::RateLimiter.new(ip_address, user)
437
+ attack_type = rate_limiter.attack_pattern_type
438
+
439
+ case attack_type
440
+ when :brute_force_single_account
441
+ # Single IP attacking one account
442
+ when :distributed_single_account
443
+ # Multiple IPs attacking one account
444
+ when :single_ip_multiple_accounts
445
+ # One IP attacking multiple accounts (credential stuffing)
446
+ when :mixed_attack_pattern
447
+ # Complex attack pattern
448
+ end
449
+ ```
450
+
451
+ ### IP Whitelisting
452
+
453
+ Whitelist trusted IPs to bypass all security blocking while maintaining full audit logs:
454
+
455
+ ```ruby
456
+ # In config/initializers/beskar.rb
457
+ Beskar.configure do |config|
458
+ config.ip_whitelist = [
459
+ "203.0.113.0/24", # Office network (CIDR notation)
460
+ "198.51.100.50", # VPN gateway (single IP)
461
+ "2001:db8::1" # IPv6 address
462
+ ]
463
+ end
464
+ ```
465
+
466
+ **How it works:**
467
+ - Whitelisted IPs bypass **all blocking** (banned IPs, rate limits, WAF violations)
468
+ - All requests from whitelisted IPs are **still logged** for audit purposes
469
+ - Supports individual IPs and CIDR notation (IPv4 and IPv6)
470
+ - Configuration is validated on startup
471
+ - Efficient caching for high-performance checks
472
+
473
+ **Check if an IP is whitelisted:**
474
+ ```ruby
475
+ if Beskar::Services::IpWhitelist.whitelisted?(request.ip)
476
+ # IP is trusted - allow but log activity
477
+ end
478
+
479
+ # Clear whitelist cache after config changes
480
+ Beskar::Services::IpWhitelist.clear_cache!
481
+ ```
482
+
483
+ ### Web Application Firewall (WAF)
484
+
485
+ Beskar's WAF uses a **score-based blocking system with exponential decay** to intelligently detect and block vulnerability scanning attempts across 12 attack categories:
486
+
487
+ **Attack Categories Detected:**
488
+ 1. **WordPress Scans** (High: 80 points) - `/wp-admin`, `/wp-login.php`, `/wp-content/*.php`, `/xmlrpc.php`
489
+ 2. **WordPress Static Files** (Low: 30 points) - `/wp-content/*.css`, `/wp-content/*.js`, `/wp-content/*.jpg` (broken links, not attacks)
490
+ 3. **PHP Admin Panels** (High: 80 points) - `/phpmyadmin`, `/admin.php`, `/phpinfo.php`
491
+ 4. **Config Files** (Critical: 95 points) - `/.env`, `/.git`, `/database.yml`
492
+ 5. **Path Traversal** (Critical: 95 points) - `/../../../etc/passwd`, URL encoded variants
493
+ 6. **Framework Debug** (Medium: 60 points) - `/rails/info/routes`, `/__debug__`, `/telescope`
494
+ 7. **CMS Detection** (Medium: 60 points) - `/joomla`, `/drupal`, `/magento`
495
+ 8. **Common Exploits** (Critical: 95 points) - `/shell.php`, `/c99.php`, `/webshell`
496
+ 9. **ActionController::UnknownFormat** (Medium: 60 points) - Detects requests for unusual formats like `/users/1.exe`, `/api/data.bat` that trigger Rails format exceptions, indicating potential scanning
497
+ 10. **ActionDispatch::RemoteIp::IpSpoofAttackError** (Critical: 95 points) - Detects IP spoofing attempts when conflicting IP headers are present
498
+ 11. **ActionDispatch::Http::MimeNegotiation::InvalidType** (Medium: 60 points) - Detects invalid MIME type requests like `GET "../../../../../../../../etc/passwd{{"` that indicate scanner activity
499
+ 12. **ActiveRecord::RecordNotFound** (Low: 30 points) - Detects potential record enumeration scans like `/admin/users/999999`, with configurable exclusions to prevent false positives
500
+
501
+ **How Score-Based Blocking Works:**
502
+
503
+ Instead of counting violations (1, 2, 3...), Beskar tracks a **cumulative risk score** that decays over time:
504
+
505
+ - Each violation adds points based on severity (Critical=95, High=80, Medium=60, Low=30)
506
+ - Violations **decay exponentially** based on severity (critical threats persist longer)
507
+ - IP is blocked when cumulative score reaches threshold (default: 150 points)
508
+ - Lower-severity violations decay faster, reducing false positives from legitimate 404s
509
+
510
+ **Example Scenarios:**
511
+ ```ruby
512
+ # Scenario 1: Legitimate user hitting 404s
513
+ 10 × RecordNotFound (30 points each) = 300 cumulative
514
+ BUT: Low severity decays with 15-minute half-life
515
+ → Score drops quickly, no ban triggered
516
+
517
+ # Scenario 2: Attacker scanning config files
518
+ 2 × /.env access (95 points each) = 190 points
519
+ → Exceeds threshold (150) → Immediate ban
520
+ → Critical severity persists for 6 hours
521
+
522
+ # Scenario 3: Mixed attack pattern
523
+ 1 × WordPress scan (80) + 1 × Config access (95) = 175
524
+ → Exceeds threshold → Ban triggered
525
+ → Different decay rates for each violation type
526
+ ```
527
+
528
+ **Configuration Profiles:**
529
+
530
+ ```ruby
531
+ # 🔥 STRICT - High-security environment (financial, healthcare)
532
+ Beskar.configure do |config|
533
+ config.waf[:enabled] = true
534
+ config.waf[:auto_block] = true
535
+ config.waf[:score_threshold] = 100 # Lower threshold = faster blocking
536
+ config.waf[:violation_window] = 12.hours # Longer memory
537
+ config.waf[:permanent_block_after] = 300 # Permanent ban at 300 cumulative score
538
+ config.waf[:block_durations] = [6.hours, 24.hours, 7.days, 30.days]
539
+
540
+ # Slower decay = violations persist longer
541
+ config.waf[:decay_rates] = {
542
+ critical: 720, # 12 hour half-life (very persistent)
543
+ high: 360, # 6 hour half-life
544
+ medium: 120, # 2 hour half-life
545
+ low: 30 # 30 minute half-life
546
+ }
547
+
548
+ # Exclude legitimate 404-prone paths
549
+ config.waf[:record_not_found_exclusions] = [
550
+ %r{/posts/.*}, %r{/articles/\d+}, %r{/public/.*}
551
+ ]
552
+ end
553
+
554
+ # ⚖️ BALANCED - Default production (recommended for most apps)
555
+ Beskar.configure do |config|
556
+ config.waf[:enabled] = true
557
+ config.waf[:auto_block] = true
558
+ config.waf[:score_threshold] = 150 # Default threshold
559
+ config.waf[:violation_window] = 6.hours # Standard window
560
+ config.waf[:permanent_block_after] = 500 # Permanent at 500 cumulative
561
+ config.waf[:decay_enabled] = true
562
+ # Uses default decay rates (critical: 360, high: 120, medium: 45, low: 15)
563
+
564
+ config.waf[:record_not_found_exclusions] = [
565
+ %r{/posts/.*}, %r{/products/[\\w-]+}
566
+ ]
567
+ end
568
+
569
+ # 🧪 PERMISSIVE - High-traffic public site with many 404s
570
+ Beskar.configure do |config|
571
+ config.waf[:enabled] = true
572
+ config.waf[:auto_block] = true
573
+ config.waf[:score_threshold] = 200 # Higher tolerance
574
+ config.waf[:violation_window] = 3.hours # Shorter memory
575
+ config.waf[:permanent_block_after] = 800 # Rare permanent bans
576
+
577
+ # Faster decay = violations forgotten quickly
578
+ config.waf[:decay_rates] = {
579
+ critical: 180, # 3 hour half-life
580
+ high: 60, # 1 hour half-life
581
+ medium: 20, # 20 minute half-life
582
+ low: 5 # 5 minute half-life (very forgiving)
583
+ }
584
+
585
+ # Extensive exclusions for public content
586
+ config.waf[:record_not_found_exclusions] = [
587
+ %r{/posts/.*}, %r{/articles/.*}, %r{/tags/.*},
588
+ %r{/search/.*}, %r{/public/.*}, %r{/assets/.*}
589
+ ]
590
+ end
591
+
592
+ # 🔍 MONITOR ONLY - Testing/staging (recommended before going live)
593
+ Beskar.configure do |config|
594
+ config.monitor_only = true # Log violations but NEVER block
595
+ config.waf[:enabled] = true
596
+ config.waf[:create_security_events] = true
597
+ config.ip_whitelist = ["127.0.0.1", "::1"] # Whitelist localhost
598
+ end
599
+ ```
600
+
601
+ **Blocking Behavior:**
602
+
603
+ With **default settings** (score_threshold: 150):
604
+ - **Violations accumulate**: Each violation adds points based on severity
605
+ - **Score threshold reached**: IP automatically banned when cumulative score ≥ 150
606
+ - **Exponential decay**: Violations lose impact over time based on severity
607
+ - **Ban duration escalates**: Based on total score accumulated:
608
+ - 150-300 points → 1 hour ban
609
+ - 300-450 points → 6 hour ban
610
+ - 450-600 points → 24 hour ban
611
+ - 600+ points → 7 day ban
612
+ - 500+ cumulative score → **permanent ban**
613
+
614
+ **Key Advantages:**
615
+ - **Fewer false positives**: Low-severity violations (404s) decay quickly
616
+ - **Faster response to serious threats**: Critical violations persist longer
617
+ - **Adaptive blocking**: Mixed attack patterns properly weighted
618
+ - **Monitor mode compatible**: Set `config.monitor_only = true` to log without blocking
619
+
620
+ > **Production Tip:** Start with monitor mode for 24-48 hours to observe your traffic patterns, then adjust thresholds and exclusions before enabling blocking.
621
+
622
+ **Check WAF status:**
623
+ ```ruby
624
+ # Get current risk score (with decay applied)
625
+ current_score = Beskar::Services::Waf.get_current_score(ip_address)
626
+ # => 145.3 (below threshold, not blocked)
627
+
628
+ # Get number of violations tracked
629
+ violation_count = Beskar::Services::Waf.get_violation_count(ip_address)
630
+ # => 3 (number of violations being tracked)
631
+
632
+ # Get detailed violation records
633
+ violations = Beskar::Services::Waf.get_violations(ip_address)
634
+ # => [{timestamp: ..., score: 95, severity: :critical, category: :config_files}, ...]
635
+
636
+ # Reset violations (admin action)
637
+ Beskar::Services::Waf.reset_violations(ip_address)
638
+
639
+ # Analyze a request without blocking
640
+ waf_analysis = Beskar::Services::Waf.analyze_request(request)
641
+ if waf_analysis
642
+ puts "Detected: #{waf_analysis[:patterns].map { |p| p[:description] }}"
643
+ puts "Severity: #{waf_analysis[:highest_severity]}"
644
+ puts "Risk Score: #{waf_analysis[:risk_score]}"
645
+ end
646
+ ```
647
+
648
+ ### IP Blocking and Banning
649
+
650
+ Beskar uses a hybrid cache + database blocking system that persists across application restarts.
651
+
652
+ **Automatic IP Banning Thresholds:**
653
+
654
+ | Trigger | Threshold | Time Window | Ban Duration | Configurable |
655
+ |---------|-----------|-------------|--------------|--------------|
656
+ | **Failed Authentication** | 10 attempts | 1 hour | 1 hour (escalating) | Via rate_limiting config |
657
+ | **Rate Limit Violations** | 5 violations | 1 hour | 1 hour (escalating) | Via rate_limiting config |
658
+ | **WAF Violations** | 3 violations | 1 hour | 1 hour (escalating) | Via waf[:block_threshold] |
659
+
660
+ > **Note:** All ban durations escalate on repeat offenses: 1h → 6h → 24h → 7d → permanent
661
+
662
+ **Manual IP Management:**
663
+
664
+ ```ruby
665
+ # Ban an IP address
666
+ Beskar::BannedIp.ban!(
667
+ "203.0.113.50",
668
+ reason: "manual_block",
669
+ duration: 24.hours,
670
+ details: "Suspicious activity reported by admin",
671
+ metadata: { reporter: "admin@example.com", ticket: "#12345" }
672
+ )
673
+
674
+ # Permanent ban
675
+ Beskar::BannedIp.ban!(
676
+ "203.0.113.51",
677
+ reason: "confirmed_attack",
678
+ permanent: true,
679
+ details: "Confirmed malicious actor"
680
+ )
681
+
682
+ # Check if IP is banned
683
+ Beskar::BannedIp.banned?("203.0.113.50") # => true
684
+
685
+ # Unban an IP
686
+ Beskar::BannedIp.unban!("203.0.113.50")
687
+
688
+ # Extend existing ban
689
+ ban = Beskar::BannedIp.find_by(ip_address: "203.0.113.50")
690
+ ban.extend_ban!(12.hours) # Add 12 hours to current expiry
691
+ ```
692
+
693
+ **Query banned IPs:**
694
+
695
+ ```ruby
696
+ # Get all active bans
697
+ Beskar::BannedIp.active
698
+
699
+ # Get permanent bans
700
+ Beskar::BannedIp.permanent
701
+
702
+ # Get expired bans (not enforced but in database)
703
+ Beskar::BannedIp.expired
704
+
705
+ # Find bans by reason
706
+ Beskar::BannedIp.where(reason: 'waf_violation')
707
+ Beskar::BannedIp.where(reason: 'authentication_abuse')
708
+ Beskar::BannedIp.where(reason: 'rate_limit_abuse')
709
+
710
+ # Cleanup expired bans from database
711
+ removed_count = Beskar::BannedIp.cleanup_expired!
712
+ ```
713
+
714
+ **Preload cache on startup:**
715
+
716
+ The cache is automatically preloaded when your app starts, but you can manually trigger it:
717
+
718
+ ```ruby
719
+ # In config/initializers/beskar.rb (optional - happens automatically)
720
+ Rails.application.config.after_initialize do
721
+ Beskar::BannedIp.preload_cache!
722
+ end
723
+ ```
724
+
725
+ ### Security Events and Monitoring
726
+
727
+ **Query WAF violations:**
728
+
729
+ ```ruby
730
+ # Recent WAF violations
731
+ waf_events = Beskar::SecurityEvent
732
+ .where(event_type: 'waf_violation')
733
+ .where('created_at > ?', 24.hours.ago)
734
+ .order(created_at: :desc)
735
+
736
+ # High-risk WAF events
737
+ high_risk = Beskar::SecurityEvent
738
+ .where(event_type: 'waf_violation')
739
+ .where('risk_score >= ?', 80)
740
+ .includes(:user)
741
+
742
+ # Group by IP to find repeat offenders
743
+ repeat_offenders = Beskar::SecurityEvent
744
+ .where(event_type: 'waf_violation')
745
+ .where('created_at > ?', 7.days.ago)
746
+ .group(:ip_address)
747
+ .having('COUNT(*) >= ?', 5)
748
+ .count
749
+
750
+ # WAF violations by pattern type
751
+ waf_events.each do |event|
752
+ patterns = event.metadata['waf_analysis']['patterns']
753
+ patterns.each do |pattern|
754
+ puts "#{event.ip_address}: #{pattern['category']} - #{pattern['description']}"
755
+ end
756
+ end
757
+ ```
758
+
759
+ **Scheduled maintenance:**
760
+
761
+ ```ruby
762
+ # In a background job (e.g., daily)
763
+ class SecurityCleanupJob < ApplicationJob
764
+ def perform
765
+ # Remove expired bans from database
766
+ removed = Beskar::BannedIp.cleanup_expired!
767
+ Rails.logger.info "Cleaned up #{removed} expired IP bans"
768
+
769
+ # Archive old security events (optional)
770
+ Beskar::SecurityEvent.where('created_at < ?', 90.days.ago).delete_all
771
+
772
+ # Generate security report (example)
773
+ report = {
774
+ active_bans: Beskar::BannedIp.active.count,
775
+ permanent_bans: Beskar::BannedIp.permanent.count,
776
+ waf_violations_today: Beskar::SecurityEvent.where(
777
+ event_type: 'waf_violation',
778
+ created_at: 24.hours.ago..Time.current
779
+ ).count
780
+ }
781
+
782
+ # Send to monitoring service
783
+ Rails.logger.info "Security Report: #{report}"
784
+ end
785
+ end
786
+ ```
787
+
788
+ ### Middleware Integration
789
+
790
+ Beskar automatically injects its middleware (`Beskar::Middleware::RequestAnalyzer`) into the Rails stack to provide comprehensive request-level protection.
791
+
792
+ **Request Processing Order:**
793
+
794
+ Every request passes through these security checks in order:
795
+
796
+ 1. **Whitelist Check** - Determine if IP is whitelisted (bypasses blocking but still logs)
797
+ 2. **Banned IP Check** - Block immediately if IP is banned (403 Forbidden)
798
+ 3. **Rate Limiting** - Check rate limits (429 Too Many Requests if exceeded)
799
+ 4. **WAF Analysis** - Scan for vulnerability patterns (403 Forbidden if detected and threshold met)
800
+ 5. **Request Processing** - Continue to application if all checks pass
801
+
802
+ **Features:**
803
+ - **Early exit** - Banned IPs are blocked immediately for performance
804
+ - **Whitelist bypass** - Trusted IPs bypass all blocking but activity is logged
805
+ - **Auto-blocking** - See [Automatic IP Banning Thresholds](#ip-blocking-and-banning) section for details
806
+ - **Custom error pages** - Returns helpful 403/429 error responses
807
+ - **Response headers** - Adds `X-Beskar-Blocked` and `X-Beskar-Rate-Limited` headers
808
+ - **Graceful degradation** - Continues working if cache is unavailable
809
+
810
+ **Middleware Logs:**
811
+
812
+ The middleware generates structured log messages for monitoring:
813
+
814
+ ```
815
+ [Beskar::Middleware] Blocked request from banned IP: 203.0.113.50
816
+ [Beskar::Middleware] Rate limit exceeded for IP: 203.0.113.51
817
+ [Beskar::Middleware] WAF violation from whitelisted IP 192.168.1.100 (not blocking): WordPress vulnerability scan
818
+ [Beskar::Middleware] 🔒 Auto-blocked IP 203.0.113.52 after 3 WAF violations (duration: 1 hours)
819
+ [Beskar::Middleware] 🔒 Auto-blocked IP 203.0.113.53 for authentication brute force (15 failures)
820
+ ```
62
821
 
63
822
  Security events are logged to the `beskar_security_events` table for analysis and will be visualized in the forthcoming security dashboard.
64
823
 
824
+ ## WAF Pattern Reference
825
+
826
+ | Category | Severity | Example Patterns | Risk Score |
827
+ |----------|----------|------------------|------------|
828
+ | WordPress Scans | High | `/wp-admin`, `/wp-login.php`, `/wp-content/*.php` | 80 |
829
+ | WordPress Static Files | Low | `/wp-content/*.css`, `/wp-content/*.jpg` | 30 |
830
+ | PHP Admin Panels | High | `/phpmyadmin`, `/admin.php`, `/phpinfo.php` | 80 |
831
+ | Config Files | **Critical** | `/.env`, `/.git`, `/database.yml`, `/config.php` | **95** |
832
+ | Path Traversal | **Critical** | `/../../../etc/passwd`, `%2e%2e/` | **95** |
833
+ | Framework Debug | Medium | `/rails/info/routes`, `/__debug__`, `/telescope` | 60 |
834
+ | CMS Detection | Medium | `/joomla`, `/drupal`, `/magento` | 60 |
835
+ | Common Exploits | **Critical** | `/shell.php`, `/c99.php`, `/webshell` | **95** |
836
+ | UnknownFormat Exception | Medium | `/users/1.exe`, `/api/data.bat` | 60 |
837
+ | IP Spoofing Exception | **Critical** | Conflicting IP headers | **95** |
838
+ | Invalid MIME Type Exception | Medium | `GET "../../../../etc/passwd{{"` | 60 |
839
+ | RecordNotFound Exception | Low | `/admin/users/999999` | 30 |
840
+
841
+ **Pattern matching is:**
842
+ - Case-insensitive
843
+ - Works on full path including query strings
844
+ - Detects URL-encoded variants
845
+ - Can match multiple patterns per request
846
+
847
+ ## Security Best Practices
848
+
849
+ ### 1. Start with Monitor Mode
850
+
851
+ When first enabling WAF, use monitor-only mode to tune thresholds:
852
+
853
+ ```ruby
854
+ config.monitor_only = true # Log but don't block
855
+ config.waf[:enabled] = true
856
+ config.waf[:create_security_events] = true
857
+ ```
858
+
859
+ After reviewing logs for false positives, enable blocking:
860
+
861
+ ```ruby
862
+ config.monitor_only = false
863
+ config.waf[:enabled] = true
864
+ config.waf[:auto_block] = true
865
+ ```
866
+
867
+ ### 2. Whitelist Carefully
868
+
869
+ Only whitelist truly trusted IPs:
870
+
871
+ ```ruby
872
+ # ✅ Good - Documented, legitimate sources
873
+ config.ip_whitelist = [
874
+ "203.0.113.0/24", # Office network - IT approved
875
+ "198.51.100.50" # VPN gateway - documented in wiki
876
+ ]
877
+
878
+ # ❌ Bad - Whitelisting unknown IPs
879
+ config.ip_whitelist = ["0.0.0.0/0"] # Never do this!
880
+ ```
881
+
882
+ ### 3. Regular Maintenance
883
+
884
+ Set up a scheduled job to clean up old data:
885
+
886
+ ```ruby
887
+ # Schedule daily via cron or Sidekiq
888
+ SecurityCleanupJob.perform_later
889
+
890
+ # Or in initializer for quick cleanup on restart
891
+ Rails.application.config.after_initialize do
892
+ Beskar::BannedIp.cleanup_expired! if Rails.env.production?
893
+ end
894
+ ```
895
+
896
+ ### 4. Monitor Security Events
897
+
898
+ Set up alerts for high-risk events:
899
+
900
+ ```ruby
901
+ # Example monitoring
902
+ high_risk_count = Beskar::SecurityEvent
903
+ .where('created_at > ?', 1.hour.ago)
904
+ .where('risk_score >= ?', 80)
905
+ .count
906
+
907
+ alert_service.notify if high_risk_count > 10
908
+ ```
909
+
910
+ ### 5. Document Whitelist Changes
911
+
912
+ Keep a record of why each IP is whitelisted:
913
+
914
+ ```ruby
915
+ # config/initializers/beskar.rb
916
+ config.ip_whitelist = [
917
+ "203.0.113.0/24", # Office HQ network (added 2024-01-15, ticket #1234)
918
+ "198.51.100.50", # Partner API server (added 2024-02-01, contract #5678)
919
+ "192.0.2.10" # Security scanner (added 2024-03-01, vendor: SecurityCo)
920
+ ]
921
+ ```
922
+
923
+ ### 6. Test WAF in Staging
924
+
925
+ Before deploying to production, test WAF rules in staging to catch false positives.
926
+
927
+ ### 7. Review Ban Reasons
928
+
929
+ Periodically review banned IPs to ensure blocking is working correctly:
930
+
931
+ ```ruby
932
+ # Check recent auto-bans
933
+ recent_bans = Beskar::BannedIp
934
+ .where('created_at > ?', 7.days.ago)
935
+ .group(:reason)
936
+ .count
937
+
938
+ # Review specific ban details
939
+ waf_bans = Beskar::BannedIp.where(reason: 'waf_violation')
940
+ waf_bans.each do |ban|
941
+ puts "#{ban.ip_address}: #{ban.details} (violations: #{ban.violation_count})"
942
+ end
943
+ ```
944
+
945
+ ## Troubleshooting
946
+
947
+ ### Issue: Legitimate users being blocked
948
+
949
+ **Solution:** Add their IP to whitelist or reduce WAF `block_threshold`:
950
+
951
+ ```ruby
952
+ config.waf[:block_threshold] = 5 # Increase from default 3
953
+ ```
954
+
955
+ Or whitelist specific IPs:
956
+ ```ruby
957
+ config.ip_whitelist = ["user.ip.address.here"]
958
+ ```
959
+
960
+ ### Issue: Too many false positives
961
+
962
+ **Solution:** Enable monitor-only mode and review patterns:
963
+
964
+ ```ruby
965
+ config.monitor_only = true # This is a global setting, not WAF-specific
966
+
967
+ # Review what's being flagged
968
+ Beskar::SecurityEvent.where(event_type: 'waf_violation').last(20).each do |event|
969
+ puts "Path: #{event.metadata['request_path']}"
970
+ puts "Patterns: #{event.metadata['waf_analysis']['patterns']}"
971
+ end
972
+ ```
973
+
974
+ ### Issue: Banned IPs persist after restart
975
+
976
+ **Solution:** This is intentional (database persistence). To unban:
977
+
978
+ ```ruby
979
+ Beskar::BannedIp.unban!("ip.address.here")
980
+
981
+ # Or unban all expired
982
+ Beskar::BannedIp.cleanup_expired!
983
+ ```
984
+
985
+ ### Issue: Performance concerns
986
+
987
+ **Solution:** Beskar uses cache-first architecture. Ensure cache is configured:
988
+
989
+ ```ruby
990
+ # config/environments/production.rb
991
+ config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] }
992
+ ```
993
+
994
+ Check cache health:
995
+ ```ruby
996
+ Rails.cache.read("test_key") # Should work
997
+ Beskar::BannedIp.preload_cache! # Reload from database if needed
998
+ ```
999
+
1000
+ ## Migration from Previous Versions
1001
+
1002
+ If upgrading from a version without WAF/IP blocking features:
1003
+
1004
+ ```bash
1005
+ # Run new migrations
1006
+ rails db:migrate
1007
+
1008
+ # Preload cache with existing bans (if any)
1009
+ rails runner "Beskar::BannedIp.preload_cache!"
1010
+
1011
+ # Test in development first
1012
+ RAILS_ENV=development rails server
1013
+
1014
+ # Review logs for any issues
1015
+ tail -f log/development.log | grep Beskar
1016
+ ```
1017
+
1018
+ ## Performance Characteristics
1019
+
1020
+ - **Whitelist check**: O(n) where n = whitelist size, cached, < 1ms
1021
+ - **Banned IP check**: O(1) cache lookup, < 1ms
1022
+ - **Rate limit check**: O(1) cache lookup, < 1ms
1023
+ - **WAF analysis**: O(m) where m = number of patterns, < 5ms
1024
+ - **Total middleware overhead**: Typically < 10ms per request
1025
+
1026
+ **Scalability:**
1027
+ - Handles 1000s of requests/second
1028
+ - Cache-first architecture minimizes database queries
1029
+ - Efficient pattern matching with compiled regexes
1030
+ - Parallel test execution: 352 tests run in < 3 seconds
1031
+
65
1032
  ## Development
66
1033
 
67
1034
  After checking out the repo, run `bundle install` to install dependencies. The gem contains a dummy Rails application in `test/dummy` for development and testing.
@@ -75,7 +1042,7 @@ $ bin/rails test
75
1042
 
76
1043
  ## Contributing
77
1044
 
78
- Bug reports and pull requests are welcome on GitHub at [https://github.com/prograis/beskar](https://github.com/prograils/beskar).
1045
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/prograis/beskar](https://github.com/prograils/beskar).
79
1046
 
80
1047
  ## License
81
1048
 
@@ -84,4 +1051,3 @@ The gem is available as open source under the terms of the [MIT License](https:/
84
1051
  ## Code of Conduct
85
1052
 
86
1053
  Just be nice to each other.
87
-