aiwaf 0.1.9.0.4__tar.gz → 0.1.9.0.6__tar.gz

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.

Potentially problematic release.


This version of aiwaf might be problematic. Click here for more details.

Files changed (42) hide show
  1. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/PKG-INFO +30 -27
  2. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/README.md +29 -26
  3. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/__init__.py +1 -1
  4. aiwaf-0.1.9.0.6/aiwaf/blacklist_manager.py +37 -0
  5. aiwaf-0.1.9.0.6/aiwaf/management/commands/add_exemption.py +30 -0
  6. aiwaf-0.1.9.0.6/aiwaf/management/commands/clear_cache.py +18 -0
  7. aiwaf-0.1.9.0.6/aiwaf/management/commands/diagnose_blocking.py +96 -0
  8. aiwaf-0.1.9.0.6/aiwaf/management/commands/setup_models.py +35 -0
  9. aiwaf-0.1.9.0.6/aiwaf/management/commands/test_exemption.py +120 -0
  10. aiwaf-0.1.9.0.6/aiwaf/management/commands/test_exemption_fix.py +54 -0
  11. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/middleware.py +28 -16
  12. aiwaf-0.1.9.0.6/aiwaf/middleware_logger.py +129 -0
  13. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/models.py +28 -1
  14. aiwaf-0.1.9.0.6/aiwaf/storage.py +229 -0
  15. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/trainer.py +0 -12
  16. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf.egg-info/PKG-INFO +30 -27
  17. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf.egg-info/SOURCES.txt +6 -1
  18. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/pyproject.toml +1 -1
  19. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/setup.py +1 -1
  20. aiwaf-0.1.9.0.4/aiwaf/blacklist_manager.py +0 -24
  21. aiwaf-0.1.9.0.4/aiwaf/management/commands/debug_csv.py +0 -155
  22. aiwaf-0.1.9.0.4/aiwaf/middleware_logger.py +0 -169
  23. aiwaf-0.1.9.0.4/aiwaf/storage.py +0 -423
  24. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/LICENSE +0 -0
  25. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/apps.py +0 -0
  26. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/decorators.py +0 -0
  27. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/management/__init__.py +0 -0
  28. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/management/commands/__init__.py +0 -0
  29. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/management/commands/add_ipexemption.py +0 -0
  30. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/management/commands/aiwaf_diagnose.py +0 -0
  31. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/management/commands/aiwaf_logging.py +0 -0
  32. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/management/commands/aiwaf_reset.py +0 -0
  33. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/management/commands/detect_and_train.py +0 -0
  34. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/management/commands/regenerate_model.py +0 -0
  35. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/resources/model.pkl +0 -0
  36. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/templatetags/__init__.py +0 -0
  37. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/templatetags/aiwaf_tags.py +0 -0
  38. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf/utils.py +0 -0
  39. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf.egg-info/dependency_links.txt +0 -0
  40. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf.egg-info/requires.txt +0 -0
  41. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/aiwaf.egg-info/top_level.txt +0 -0
  42. {aiwaf-0.1.9.0.4 → aiwaf-0.1.9.0.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiwaf
3
- Version: 0.1.9.0.4
3
+ Version: 0.1.9.0.6
4
4
  Summary: AI-powered Web Application Firewall
5
5
  Home-page: https://github.com/aayushgauba/aiwaf
6
6
  Author: Aayush Gauba
@@ -68,7 +68,7 @@ aiwaf/
68
68
  ## 🚀 Features
69
69
 
70
70
  - **IP Blocklist**
71
- Instantly blocks suspicious IPs (supports CSV fallback or Django model).
71
+ Instantly blocks suspicious IPs using Django models with real-time performance.
72
72
 
73
73
  - **Rate Limiting**
74
74
  Sliding‑window blocks flooders (> `AIWAF_RATE_MAX` per `AIWAF_RATE_WINDOW`), then blacklists them.
@@ -98,9 +98,9 @@ aiwaf/
98
98
  Blocks guessed or invalid UUIDs that don't resolve to real models.
99
99
 
100
100
  - **Built-in Request Logger**
101
- Optional middleware logger that captures requests to CSV:
101
+ Optional middleware logger that captures requests to Django models:
102
102
  - **Automatic fallback** when main access logs unavailable
103
- - **CSV format** for easy analysis and training
103
+ - **Real-time storage** in database for instant access
104
104
  - **Captures response times** for better anomaly detection
105
105
  - **Zero configuration** - works out of the box
106
106
 
@@ -221,28 +221,33 @@ AIWAF_ACCESS_LOG = "/var/log/nginx/access.log"
221
221
 
222
222
  ---
223
223
 
224
- ### Storage Configuration
224
+ ### Database Models
225
225
 
226
- **Choose storage backend:**
226
+ AI-WAF uses Django models for real-time, high-performance storage:
227
227
 
228
228
  ```python
229
- # Use Django models (default) - requires database tables
230
- AIWAF_STORAGE_MODE = "models"
231
-
232
- # OR use CSV files - no database required
233
- AIWAF_STORAGE_MODE = "csv"
234
- AIWAF_CSV_DATA_DIR = "aiwaf_data" # Directory for CSV files
229
+ # All data is stored in Django models - no configuration needed
230
+ # Tables created automatically with migrations:
231
+ # - aiwaf_blacklistentry # Blocked IP addresses
232
+ # - aiwaf_ipexemption # Exempt IP addresses
233
+ # - aiwaf_dynamickeyword # Dynamic keywords with counts
234
+ # - aiwaf_featuresample # Feature samples for ML training
235
+ # - aiwaf_requestlog # Request logs (if middleware logging enabled)
235
236
  ```
236
237
 
237
- **CSV Mode Features:**
238
- - No database migrations required
239
- - Files stored in `aiwaf_data/` directory:
240
- - `blacklist.csv` - Blocked IP addresses
241
- - `exemptions.csv` - Exempt IP addresses
242
- - `keywords.csv` - Dynamic keywords
243
- - `access_samples.csv` - Feature samples for ML training
244
- - Perfect for lightweight deployments or when you prefer file-based storage
245
- - Management commands work identically in both modes
238
+ **Benefits of Django Models:**
239
+ - ⚡ **Real-time performance** - No file I/O bottlenecks
240
+ - 🔄 **Instant updates** - Changes visible immediately across all processes
241
+ - 🚀 **Better concurrency** - No file locking issues
242
+ - 📊 **Rich querying** - Use Django ORM for complex operations
243
+ - 🔍 **Admin integration** - View/manage data through Django admin
244
+
245
+ **Database Setup:**
246
+ ```bash
247
+ # Create and apply migrations
248
+ python manage.py makemigrations aiwaf
249
+ python manage.py migrate aiwaf
250
+ ```
246
251
 
247
252
  ---
248
253
 
@@ -253,8 +258,6 @@ Enable AI-WAF's built-in request logger as a fallback when main access logs aren
253
258
  ```python
254
259
  # Enable middleware logging
255
260
  AIWAF_MIDDLEWARE_LOGGING = True # Enable/disable logging
256
- AIWAF_MIDDLEWARE_LOG = "aiwaf_requests.log" # Log file path
257
- AIWAF_MIDDLEWARE_CSV = True # Use CSV format (recommended)
258
261
  ```
259
262
 
260
263
  **Then add middleware to MIDDLEWARE list:**
@@ -276,8 +279,8 @@ python manage.py aiwaf_logging --clear # Clear log files
276
279
 
277
280
  **Benefits:**
278
281
  - **Automatic fallback** when `AIWAF_ACCESS_LOG` unavailable
279
- - **CSV format** with precise timestamps and response times
280
- - **Zero configuration** - trainer automatically detects and uses CSV logs
282
+ - **Database storage** with precise timestamps and response times
283
+ - **Zero configuration** - trainer automatically detects and uses model logs
281
284
  - **Lightweight** - fails silently to avoid breaking your application
282
285
 
283
286
  ---
@@ -386,7 +389,7 @@ python manage.py detect_and_train
386
389
  ```
387
390
 
388
391
  ### What happens:
389
- 1. Read access logs (incl. rotated or gzipped) **OR** AI-WAF middleware CSV logs
392
+ 1. Read access logs (incl. rotated or gzipped) **OR** AI-WAF middleware model logs
390
393
  2. Auto‑block IPs with ≥ 6 total 404s
391
394
  3. Extract features & train IsolationForest
392
395
  4. Save `model.pkl` with current scikit-learn version
@@ -411,7 +414,7 @@ python manage.py detect_and_train
411
414
  5. Extract top 10 dynamic keywords from 4xx/5xx
412
415
  6. Remove any keywords associated with newly exempt paths
413
416
 
414
- **Note:** If main access log (`AIWAF_ACCESS_LOG`) is unavailable, trainer automatically falls back to AI-WAF middleware CSV logs.
417
+ **Note:** If main access log (`AIWAF_ACCESS_LOG`) is unavailable, trainer automatically falls back to AI-WAF middleware model logs.
415
418
 
416
419
  ---
417
420
 
@@ -47,7 +47,7 @@ aiwaf/
47
47
  ## 🚀 Features
48
48
 
49
49
  - **IP Blocklist**
50
- Instantly blocks suspicious IPs (supports CSV fallback or Django model).
50
+ Instantly blocks suspicious IPs using Django models with real-time performance.
51
51
 
52
52
  - **Rate Limiting**
53
53
  Sliding‑window blocks flooders (> `AIWAF_RATE_MAX` per `AIWAF_RATE_WINDOW`), then blacklists them.
@@ -77,9 +77,9 @@ aiwaf/
77
77
  Blocks guessed or invalid UUIDs that don't resolve to real models.
78
78
 
79
79
  - **Built-in Request Logger**
80
- Optional middleware logger that captures requests to CSV:
80
+ Optional middleware logger that captures requests to Django models:
81
81
  - **Automatic fallback** when main access logs unavailable
82
- - **CSV format** for easy analysis and training
82
+ - **Real-time storage** in database for instant access
83
83
  - **Captures response times** for better anomaly detection
84
84
  - **Zero configuration** - works out of the box
85
85
 
@@ -200,28 +200,33 @@ AIWAF_ACCESS_LOG = "/var/log/nginx/access.log"
200
200
 
201
201
  ---
202
202
 
203
- ### Storage Configuration
203
+ ### Database Models
204
204
 
205
- **Choose storage backend:**
205
+ AI-WAF uses Django models for real-time, high-performance storage:
206
206
 
207
207
  ```python
208
- # Use Django models (default) - requires database tables
209
- AIWAF_STORAGE_MODE = "models"
210
-
211
- # OR use CSV files - no database required
212
- AIWAF_STORAGE_MODE = "csv"
213
- AIWAF_CSV_DATA_DIR = "aiwaf_data" # Directory for CSV files
208
+ # All data is stored in Django models - no configuration needed
209
+ # Tables created automatically with migrations:
210
+ # - aiwaf_blacklistentry # Blocked IP addresses
211
+ # - aiwaf_ipexemption # Exempt IP addresses
212
+ # - aiwaf_dynamickeyword # Dynamic keywords with counts
213
+ # - aiwaf_featuresample # Feature samples for ML training
214
+ # - aiwaf_requestlog # Request logs (if middleware logging enabled)
214
215
  ```
215
216
 
216
- **CSV Mode Features:**
217
- - No database migrations required
218
- - Files stored in `aiwaf_data/` directory:
219
- - `blacklist.csv` - Blocked IP addresses
220
- - `exemptions.csv` - Exempt IP addresses
221
- - `keywords.csv` - Dynamic keywords
222
- - `access_samples.csv` - Feature samples for ML training
223
- - Perfect for lightweight deployments or when you prefer file-based storage
224
- - Management commands work identically in both modes
217
+ **Benefits of Django Models:**
218
+ - ⚡ **Real-time performance** - No file I/O bottlenecks
219
+ - 🔄 **Instant updates** - Changes visible immediately across all processes
220
+ - 🚀 **Better concurrency** - No file locking issues
221
+ - 📊 **Rich querying** - Use Django ORM for complex operations
222
+ - 🔍 **Admin integration** - View/manage data through Django admin
223
+
224
+ **Database Setup:**
225
+ ```bash
226
+ # Create and apply migrations
227
+ python manage.py makemigrations aiwaf
228
+ python manage.py migrate aiwaf
229
+ ```
225
230
 
226
231
  ---
227
232
 
@@ -232,8 +237,6 @@ Enable AI-WAF's built-in request logger as a fallback when main access logs aren
232
237
  ```python
233
238
  # Enable middleware logging
234
239
  AIWAF_MIDDLEWARE_LOGGING = True # Enable/disable logging
235
- AIWAF_MIDDLEWARE_LOG = "aiwaf_requests.log" # Log file path
236
- AIWAF_MIDDLEWARE_CSV = True # Use CSV format (recommended)
237
240
  ```
238
241
 
239
242
  **Then add middleware to MIDDLEWARE list:**
@@ -255,8 +258,8 @@ python manage.py aiwaf_logging --clear # Clear log files
255
258
 
256
259
  **Benefits:**
257
260
  - **Automatic fallback** when `AIWAF_ACCESS_LOG` unavailable
258
- - **CSV format** with precise timestamps and response times
259
- - **Zero configuration** - trainer automatically detects and uses CSV logs
261
+ - **Database storage** with precise timestamps and response times
262
+ - **Zero configuration** - trainer automatically detects and uses model logs
260
263
  - **Lightweight** - fails silently to avoid breaking your application
261
264
 
262
265
  ---
@@ -365,7 +368,7 @@ python manage.py detect_and_train
365
368
  ```
366
369
 
367
370
  ### What happens:
368
- 1. Read access logs (incl. rotated or gzipped) **OR** AI-WAF middleware CSV logs
371
+ 1. Read access logs (incl. rotated or gzipped) **OR** AI-WAF middleware model logs
369
372
  2. Auto‑block IPs with ≥ 6 total 404s
370
373
  3. Extract features & train IsolationForest
371
374
  4. Save `model.pkl` with current scikit-learn version
@@ -390,7 +393,7 @@ python manage.py detect_and_train
390
393
  5. Extract top 10 dynamic keywords from 4xx/5xx
391
394
  6. Remove any keywords associated with newly exempt paths
392
395
 
393
- **Note:** If main access log (`AIWAF_ACCESS_LOG`) is unavailable, trainer automatically falls back to AI-WAF middleware CSV logs.
396
+ **Note:** If main access log (`AIWAF_ACCESS_LOG`) is unavailable, trainer automatically falls back to AI-WAF middleware model logs.
394
397
 
395
398
  ---
396
399
 
@@ -1,6 +1,6 @@
1
1
  default_app_config = "aiwaf.apps.AiwafConfig"
2
2
 
3
- __version__ = "0.1.9.0.4"
3
+ __version__ = "0.1.9.0.6"
4
4
 
5
5
  # Note: Middleware classes are available from aiwaf.middleware
6
6
  # Import them only when needed to avoid circular imports during Django app loading
@@ -0,0 +1,37 @@
1
+ # aiwaf/blacklist_manager.py
2
+
3
+ from .storage import get_blacklist_store, get_exemption_store
4
+
5
+ class BlacklistManager:
6
+ @staticmethod
7
+ def block(ip, reason):
8
+ """Add IP to blacklist, but only if it's not exempted"""
9
+ # Check if IP is exempted before blocking
10
+ exemption_store = get_exemption_store()
11
+ if exemption_store.is_exempted(ip):
12
+ return # Don't block exempted IPs
13
+
14
+ store = get_blacklist_store()
15
+ store.block_ip(ip, reason)
16
+
17
+ @staticmethod
18
+ def is_blocked(ip):
19
+ """Check if IP is blocked, but respect exemptions"""
20
+ # First check if IP is exempted - exemptions override blacklist
21
+ exemption_store = get_exemption_store()
22
+ if exemption_store.is_exempted(ip):
23
+ return False # Exempted IPs are never considered blocked
24
+
25
+ # If not exempted, check blacklist
26
+ store = get_blacklist_store()
27
+ return store.is_blocked(ip)
28
+
29
+ @staticmethod
30
+ def all_blocked():
31
+ store = get_blacklist_store()
32
+ return store.get_all_blocked_ips()
33
+
34
+ @staticmethod
35
+ def unblock(ip):
36
+ store = get_blacklist_store()
37
+ store.unblock_ip(ip)
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from django.core.management.base import BaseCommand
4
+ from aiwaf.storage import get_exemption_store
5
+
6
+ class Command(BaseCommand):
7
+ help = 'Add IP to exemption list using Django models'
8
+
9
+ def add_arguments(self, parser):
10
+ parser.add_argument('ip', help='IP address to exempt')
11
+ parser.add_argument('--reason', default='Manual exemption', help='Reason for exemption')
12
+
13
+ def handle(self, *args, **options):
14
+ ip = options['ip']
15
+ reason = options['reason']
16
+
17
+ self.stdout.write(f"Adding IP {ip} to exemption list...")
18
+
19
+ exemption_store = get_exemption_store()
20
+ exemption_store.add_exemption(ip, reason)
21
+
22
+ # Verify it was added
23
+ if exemption_store.is_exempted(ip):
24
+ self.stdout.write(self.style.SUCCESS(f"✅ Successfully exempted IP: {ip}"))
25
+ else:
26
+ self.stdout.write(self.style.ERROR(f"❌ Failed to exempt IP: {ip}"))
27
+
28
+ # Show all exempted IPs
29
+ all_exempted = exemption_store.get_all_exempted_ips()
30
+ self.stdout.write(f"\nAll exempted IPs: {all_exempted}")
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from django.core.management.base import BaseCommand
4
+ from django.core.cache import cache
5
+
6
+ class Command(BaseCommand):
7
+ help = 'Clear Django cache'
8
+
9
+ def handle(self, *args, **options):
10
+ cache.clear()
11
+ self.stdout.write(self.style.SUCCESS("✅ Django cache cleared successfully!"))
12
+
13
+ # Also show what was cleared
14
+ self.stdout.write("🧹 Cleared all cached data including:")
15
+ self.stdout.write(" - Rate limiting data")
16
+ self.stdout.write(" - Blacklist cache")
17
+ self.stdout.write(" - AI anomaly data")
18
+ self.stdout.write(" - Honeypot timing data")
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from django.core.management.base import BaseCommand
4
+ from django.core.cache import cache
5
+ from aiwaf.blacklist_manager import BlacklistManager
6
+ from aiwaf.storage import get_exemption_store, get_blacklist_store
7
+ from aiwaf.utils import get_ip
8
+ from django.test import RequestFactory
9
+
10
+ class Command(BaseCommand):
11
+ help = 'Comprehensive diagnosis of blocking issues'
12
+
13
+ def add_arguments(self, parser):
14
+ parser.add_argument('--ip', default='97.187.30.95', help='IP address to test')
15
+ parser.add_argument('--clear-cache', action='store_true', help='Clear Django cache')
16
+
17
+ def handle(self, *args, **options):
18
+ test_ip = options['ip']
19
+
20
+ self.stdout.write(f"\n🔍 Comprehensive Blocking Diagnosis for IP: {test_ip}")
21
+ self.stdout.write("=" * 60)
22
+
23
+ if options['clear_cache']:
24
+ cache.clear()
25
+ self.stdout.write("🧹 Cleared Django cache")
26
+
27
+ # 1. Check exemption status
28
+ exemption_store = get_exemption_store()
29
+ is_exempted = exemption_store.is_exempted(test_ip)
30
+ self.stdout.write(f"1. ✅ IP exempted in storage: {is_exempted}")
31
+
32
+ # 2. Check blacklist status
33
+ blacklist_store = get_blacklist_store()
34
+ is_in_blacklist = blacklist_store.is_blocked(test_ip)
35
+ self.stdout.write(f"2. 🚫 IP in blacklist storage: {is_in_blacklist}")
36
+
37
+ # 3. Check BlacklistManager final decision
38
+ manager_blocked = BlacklistManager.is_blocked(test_ip)
39
+ self.stdout.write(f"3. 🎯 BlacklistManager says blocked: {manager_blocked}")
40
+
41
+ # 4. Check Django cache for blacklist entries
42
+ cache_key = f"blacklist:{test_ip}"
43
+ cached_value = cache.get(cache_key)
44
+ self.stdout.write(f"4. 💾 Cache value for blacklist:{test_ip}: {cached_value}")
45
+
46
+ # 5. Test what IP would be detected from a request
47
+ factory = RequestFactory()
48
+
49
+ # Test different scenarios
50
+ scenarios = [
51
+ ("Direct IP", {'REMOTE_ADDR': test_ip}),
52
+ ("X-Forwarded-For", {'HTTP_X_FORWARDED_FOR': test_ip}),
53
+ ("X-Real-IP", {'HTTP_X_REAL_IP': test_ip}),
54
+ ("CloudFlare", {'HTTP_CF_CONNECTING_IP': test_ip}),
55
+ ]
56
+
57
+ self.stdout.write(f"\n5. 🌐 IP Detection Tests:")
58
+ for name, meta in scenarios:
59
+ request = factory.get('/', **meta)
60
+ detected_ip = get_ip(request)
61
+ self.stdout.write(f" {name}: {detected_ip}")
62
+ if detected_ip == test_ip:
63
+ self.stdout.write(f" ✅ Match!")
64
+
65
+ # 6. Check rate limiting cache entries
66
+ self.stdout.write(f"\n6. 🚦 Rate Limiting Cache Entries:")
67
+ rate_keys = [
68
+ f"ratelimit:{test_ip}",
69
+ f"aiwaf:{test_ip}",
70
+ f"honeypot_get:{test_ip}"
71
+ ]
72
+
73
+ for key in rate_keys:
74
+ value = cache.get(key)
75
+ if value:
76
+ self.stdout.write(f" {key}: {value}")
77
+ else:
78
+ self.stdout.write(f" {key}: None")
79
+
80
+ # 7. Summary
81
+ self.stdout.write(f"\n📋 SUMMARY:")
82
+ if is_exempted and not manager_blocked:
83
+ self.stdout.write(self.style.SUCCESS("✅ IP should NOT be blocked"))
84
+ if options.get('still_blocked'):
85
+ self.stdout.write(self.style.WARNING("⚠️ If still blocked, check:"))
86
+ self.stdout.write(" - Web server logs (nginx, apache)")
87
+ self.stdout.write(" - Other middleware or security software")
88
+ self.stdout.write(" - Browser cache/cookies")
89
+ elif not is_exempted:
90
+ self.stdout.write(self.style.WARNING(f"⚠️ IP {test_ip} is NOT exempted"))
91
+ elif manager_blocked:
92
+ self.stdout.write(self.style.ERROR(f"❌ IP is being blocked despite exemption"))
93
+
94
+ self.stdout.write(f"\n💡 To clear all caches and reset:")
95
+ self.stdout.write(f" python manage.py shell -c \"from django.core.cache import cache; cache.clear()\"")
96
+ self.stdout.write(f"=" * 60)
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from django.core.management.base import BaseCommand
4
+ from django.db import transaction
5
+
6
+ class Command(BaseCommand):
7
+ help = 'Create Django database migrations for AI-WAF models (after removing CSV support)'
8
+
9
+ def handle(self, *args, **options):
10
+ self.stdout.write("🔄 Creating AI-WAF database migrations...")
11
+
12
+ try:
13
+ # Import the management command functions
14
+ from django.core.management import call_command
15
+
16
+ # Create migrations for aiwaf app
17
+ self.stdout.write("Creating migrations for aiwaf models...")
18
+ call_command('makemigrations', 'aiwaf', verbosity=2)
19
+
20
+ # Apply migrations
21
+ self.stdout.write("Applying migrations...")
22
+ call_command('migrate', 'aiwaf', verbosity=2)
23
+
24
+ self.stdout.write(self.style.SUCCESS("✅ Successfully created and applied AI-WAF migrations!"))
25
+ self.stdout.write("")
26
+ self.stdout.write("🎯 Next steps:")
27
+ self.stdout.write("1. Add your IP to exemptions: python manage.py add_exemption YOUR_IP")
28
+ self.stdout.write("2. Test the system: python manage.py diagnose_blocking --ip YOUR_IP")
29
+ self.stdout.write("3. Clear any old cache: python manage.py clear_cache")
30
+
31
+ except Exception as e:
32
+ self.stdout.write(self.style.ERROR(f"❌ Error during migration: {e}"))
33
+ self.stdout.write("You may need to run these commands manually:")
34
+ self.stdout.write(" python manage.py makemigrations aiwaf")
35
+ self.stdout.write(" python manage.py migrate aiwaf")
@@ -0,0 +1,120 @@
1
+ from django.core.management.base import BaseCommand
2
+ import os
3
+
4
+ class Command(BaseCommand):
5
+ help = 'Test AI-WAF exemption functionality step by step'
6
+
7
+ def add_arguments(self, parser):
8
+ parser.add_argument(
9
+ 'test_ip',
10
+ type=str,
11
+ help='IP address to test exemption for'
12
+ )
13
+
14
+ def handle(self, *args, **options):
15
+ test_ip = options['test_ip']
16
+
17
+ self.stdout.write(self.style.HTTP_INFO(f"🧪 Testing Exemption for IP: {test_ip}"))
18
+ self.stdout.write("=" * 50)
19
+
20
+ # Step 1: Check settings
21
+ from django.conf import settings
22
+ storage_mode = getattr(settings, 'AIWAF_STORAGE_MODE', 'models')
23
+ csv_dir = getattr(settings, 'AIWAF_CSV_DATA_DIR', 'aiwaf_data')
24
+
25
+ self.stdout.write(f"Storage Mode: {storage_mode}")
26
+ self.stdout.write(f"CSV Directory: {csv_dir}")
27
+ self.stdout.write("")
28
+
29
+ # Step 2: Check storage factory
30
+ try:
31
+ from aiwaf.storage import get_exemption_store, EXEMPTION_CSV, CSV_DATA_DIR, STORAGE_MODE
32
+ exemption_store = get_exemption_store()
33
+
34
+ self.stdout.write(f"Exemption Store Class: {exemption_store.__name__}")
35
+ self.stdout.write(f"Expected CSV File: {EXEMPTION_CSV}")
36
+ self.stdout.write(f"CSV Directory: {CSV_DATA_DIR}")
37
+ self.stdout.write(f"Storage Mode from storage.py: {STORAGE_MODE}")
38
+ self.stdout.write("")
39
+
40
+ except Exception as e:
41
+ self.stdout.write(self.style.ERROR(f"❌ Storage import failed: {e}"))
42
+ return
43
+
44
+ # Step 3: Check file existence
45
+ if os.path.exists(EXEMPTION_CSV):
46
+ self.stdout.write(self.style.SUCCESS(f"✅ Exemption CSV exists: {EXEMPTION_CSV}"))
47
+
48
+ # Read and display file contents
49
+ try:
50
+ with open(EXEMPTION_CSV, 'r', encoding='utf-8') as f:
51
+ content = f.read().strip()
52
+ if content:
53
+ self.stdout.write(f"📄 File contents:\n{content}")
54
+ self.stdout.write("")
55
+ else:
56
+ self.stdout.write("📄 File is empty")
57
+
58
+ except Exception as e:
59
+ self.stdout.write(self.style.ERROR(f"❌ Could not read file: {e}"))
60
+ else:
61
+ self.stdout.write(self.style.ERROR(f"❌ Exemption CSV not found: {EXEMPTION_CSV}"))
62
+ self.stdout.write("Creating test exemption...")
63
+
64
+ # Create the exemption
65
+ try:
66
+ exemption_store.add_ip(test_ip, "Test exemption from debug")
67
+ self.stdout.write(self.style.SUCCESS("✅ Created test exemption"))
68
+ except Exception as e:
69
+ self.stdout.write(self.style.ERROR(f"❌ Failed to create exemption: {e}"))
70
+ return
71
+
72
+ # Step 4: Test exemption check via storage
73
+ try:
74
+ is_exempted_storage = exemption_store.is_exempted(test_ip)
75
+ self.stdout.write(f"Direct storage check: {test_ip} exempted = {is_exempted_storage}")
76
+ except Exception as e:
77
+ self.stdout.write(self.style.ERROR(f"❌ Storage exemption check failed: {e}"))
78
+
79
+ # Step 5: Test exemption check via utils function
80
+ try:
81
+ from aiwaf.utils import is_ip_exempted
82
+ is_exempted_utils = is_ip_exempted(test_ip)
83
+ self.stdout.write(f"Utils function check: {test_ip} exempted = {is_exempted_utils}")
84
+ except Exception as e:
85
+ self.stdout.write(self.style.ERROR(f"❌ Utils exemption check failed: {e}"))
86
+
87
+ # Step 6: Test middleware import
88
+ try:
89
+ from aiwaf.middleware import IPAndKeywordBlockMiddleware
90
+ self.stdout.write("✅ Middleware import successful")
91
+ except Exception as e:
92
+ self.stdout.write(self.style.ERROR(f"❌ Middleware import failed: {e}"))
93
+
94
+ # Step 7: Test CSV reading manually
95
+ if os.path.exists(EXEMPTION_CSV):
96
+ try:
97
+ import csv
98
+ self.stdout.write("\n📋 Manual CSV parsing:")
99
+ with open(EXEMPTION_CSV, 'r', newline='', encoding='utf-8') as f:
100
+ reader = csv.DictReader(f)
101
+ found = False
102
+ for i, row in enumerate(reader):
103
+ ip_in_row = row.get('ip_address', 'N/A')
104
+ self.stdout.write(f" Row {i}: ip_address = '{ip_in_row}'")
105
+ if ip_in_row == test_ip:
106
+ found = True
107
+ self.stdout.write(f" ✅ Found match for {test_ip}")
108
+
109
+ if not found:
110
+ self.stdout.write(f" ❌ No match found for {test_ip}")
111
+
112
+ except Exception as e:
113
+ self.stdout.write(self.style.ERROR(f"❌ Manual CSV parsing failed: {e}"))
114
+
115
+ self.stdout.write("")
116
+ self.stdout.write(self.style.HTTP_INFO("💡 Debugging Tips:"))
117
+ self.stdout.write("1. Check that AIWAF_STORAGE_MODE = 'csv' in settings.py")
118
+ self.stdout.write("2. Ensure the CSV file has proper headers: ip_address,reason,created_at")
119
+ self.stdout.write("3. Check file permissions on the CSV directory")
120
+ self.stdout.write("4. Verify no trailing/leading spaces in IP addresses")
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from django.core.management.base import BaseCommand
4
+ from aiwaf.blacklist_manager import BlacklistManager
5
+ from aiwaf.storage import get_exemption_store
6
+
7
+ class Command(BaseCommand):
8
+ help = 'Test that exempted IPs are properly honored by BlacklistManager'
9
+
10
+ def add_arguments(self, parser):
11
+ parser.add_argument('--ip', default='97.187.30.95', help='IP address to test')
12
+
13
+ def handle(self, *args, **options):
14
+ test_ip = options['ip']
15
+
16
+ self.stdout.write(f"\n=== Testing Exemption Fix for IP: {test_ip} ===")
17
+
18
+ # Check exemption store
19
+ exemption_store = get_exemption_store()
20
+ is_exempted = exemption_store.is_exempted(test_ip)
21
+ self.stdout.write(f"1. Is IP exempted in storage? {is_exempted}")
22
+
23
+ # Test BlacklistManager.block() - should not block exempted IPs
24
+ self.stdout.write(f"\n2. Testing BlacklistManager.block() on exempted IP...")
25
+ BlacklistManager.block(test_ip, "Test block attempt")
26
+
27
+ # Check if actually blocked
28
+ is_blocked = BlacklistManager.is_blocked(test_ip)
29
+ self.stdout.write(f"3. Is IP blocked after block attempt? {is_blocked}")
30
+
31
+ if is_exempted and not is_blocked:
32
+ self.stdout.write(self.style.SUCCESS("✅ PASS: Exempted IP was NOT blocked"))
33
+ elif is_exempted and is_blocked:
34
+ self.stdout.write(self.style.ERROR("❌ FAIL: Exempted IP was blocked (this should not happen)"))
35
+ elif not is_exempted:
36
+ self.stdout.write(self.style.WARNING("⚠️ IP is not exempted, blocking behavior is normal"))
37
+
38
+ # Test with a non-exempted IP to verify blocking still works
39
+ test_non_exempted = "1.2.3.4"
40
+ self.stdout.write(f"\n4. Testing with non-exempted IP: {test_non_exempted}")
41
+
42
+ is_exempted_2 = exemption_store.is_exempted(test_non_exempted)
43
+ self.stdout.write(f" Is non-exempted IP exempted? {is_exempted_2}")
44
+
45
+ BlacklistManager.block(test_non_exempted, "Test block non-exempted")
46
+ is_blocked_2 = BlacklistManager.is_blocked(test_non_exempted)
47
+ self.stdout.write(f" Is non-exempted IP blocked? {is_blocked_2}")
48
+
49
+ if not is_exempted_2 and is_blocked_2:
50
+ self.stdout.write(self.style.SUCCESS("✅ PASS: Non-exempted IP was properly blocked"))
51
+ else:
52
+ self.stdout.write(self.style.ERROR("❌ FAIL: Non-exempted IP blocking failed"))
53
+
54
+ self.stdout.write(f"\n=== Test Complete ===")