aiwaf 0.1.8.6__tar.gz → 0.1.8.8__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 (32) hide show
  1. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/PKG-INFO +111 -3
  2. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/README.md +110 -2
  3. aiwaf-0.1.8.8/aiwaf/blacklist_manager.py +24 -0
  4. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/management/commands/add_ipexemption.py +8 -7
  5. aiwaf-0.1.8.8/aiwaf/management/commands/aiwaf_logging.py +166 -0
  6. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/management/commands/aiwaf_reset.py +16 -5
  7. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/middleware.py +11 -10
  8. aiwaf-0.1.8.8/aiwaf/middleware_logger.py +160 -0
  9. aiwaf-0.1.8.8/aiwaf/storage.py +351 -0
  10. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/trainer.py +51 -31
  11. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/utils.py +3 -2
  12. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf.egg-info/PKG-INFO +111 -3
  13. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf.egg-info/SOURCES.txt +2 -0
  14. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/pyproject.toml +1 -1
  15. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/setup.py +1 -1
  16. aiwaf-0.1.8.6/aiwaf/blacklist_manager.py +0 -14
  17. aiwaf-0.1.8.6/aiwaf/storage.py +0 -61
  18. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/LICENSE +0 -0
  19. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/__init__.py +0 -0
  20. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/apps.py +0 -0
  21. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/decorators.py +0 -0
  22. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/management/__init__.py +0 -0
  23. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/management/commands/__init__.py +0 -0
  24. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/management/commands/detect_and_train.py +0 -0
  25. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/models.py +0 -0
  26. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/resources/model.pkl +0 -0
  27. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/templatetags/__init__.py +0 -0
  28. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/templatetags/aiwaf_tags.py +0 -0
  29. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf.egg-info/dependency_links.txt +0 -0
  30. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf.egg-info/requires.txt +0 -0
  31. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf.egg-info/top_level.txt +0 -0
  32. {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiwaf
3
- Version: 0.1.8.6
3
+ Version: 0.1.8.8
4
4
  Summary: AI-powered Web Application Firewall
5
5
  Home-page: https://github.com/aayushgauba/aiwaf
6
6
  Author: Aayush Gauba
@@ -83,7 +83,28 @@ aiwaf/
83
83
  - Submit forms faster than `AIWAF_MIN_FORM_TIME` seconds (default: 1 second)
84
84
 
85
85
  - **UUID Tampering Protection**
86
- Blocks guessed or invalid UUIDs that dont resolve to real models.
86
+ Blocks guessed or invalid UUIDs that don't resolve to real models.
87
+
88
+ - **Built-in Request Logger**
89
+ Optional middleware logger that captures requests to CSV:
90
+ - **Automatic fallback** when main access logs unavailable
91
+ - **CSV format** for easy analysis and training
92
+ - **Captures response times** for better anomaly detection
93
+ - **Zero configuration** - works out of the box
94
+
95
+
96
+ **Exempt Path & IP Awareness**
97
+
98
+ **Exempt Paths:**
99
+ AI‑WAF automatically exempts common login paths (`/admin/`, `/login/`, `/accounts/login/`, etc.) from all blocking mechanisms. You can add additional exempt paths in your Django `settings.py`:
100
+
101
+ ```python
102
+ AIWAF_EXEMPT_PATHS = [
103
+ "/api/webhooks/",
104
+ "/health/",
105
+ "/special-endpoint/",
106
+ ]
107
+ ```
87
108
 
88
109
 
89
110
  **Exempt Path & IP Awareness**
@@ -188,6 +209,67 @@ AIWAF_ACCESS_LOG = "/var/log/nginx/access.log"
188
209
 
189
210
  ---
190
211
 
212
+ ### Storage Configuration
213
+
214
+ **Choose storage backend:**
215
+
216
+ ```python
217
+ # Use Django models (default) - requires database tables
218
+ AIWAF_STORAGE_MODE = "models"
219
+
220
+ # OR use CSV files - no database required
221
+ AIWAF_STORAGE_MODE = "csv"
222
+ AIWAF_CSV_DATA_DIR = "aiwaf_data" # Directory for CSV files
223
+ ```
224
+
225
+ **CSV Mode Features:**
226
+ - No database migrations required
227
+ - Files stored in `aiwaf_data/` directory:
228
+ - `blacklist.csv` - Blocked IP addresses
229
+ - `exemptions.csv` - Exempt IP addresses
230
+ - `keywords.csv` - Dynamic keywords
231
+ - `access_samples.csv` - Feature samples for ML training
232
+ - Perfect for lightweight deployments or when you prefer file-based storage
233
+ - Management commands work identically in both modes
234
+
235
+ ---
236
+
237
+ ### Built-in Request Logger (Optional)
238
+
239
+ Enable AI-WAF's built-in request logger as a fallback when main access logs aren't available:
240
+
241
+ ```python
242
+ # Enable middleware logging
243
+ AIWAF_MIDDLEWARE_LOGGING = True # Enable/disable logging
244
+ AIWAF_MIDDLEWARE_LOG = "aiwaf_requests.log" # Log file path
245
+ AIWAF_MIDDLEWARE_CSV = True # Use CSV format (recommended)
246
+ ```
247
+
248
+ **Then add middleware to MIDDLEWARE list:**
249
+
250
+ ```python
251
+ MIDDLEWARE = [
252
+ # ... your existing middleware ...
253
+ 'aiwaf.middleware_logger.AIWAFLoggerMiddleware', # Add near the end
254
+ ]
255
+ ```
256
+
257
+ **Manage middleware logging:**
258
+
259
+ ```bash
260
+ python manage.py aiwaf_logging --status # Check logging status
261
+ python manage.py aiwaf_logging --enable # Show setup instructions
262
+ python manage.py aiwaf_logging --clear # Clear log files
263
+ ```
264
+
265
+ **Benefits:**
266
+ - **Automatic fallback** when `AIWAF_ACCESS_LOG` unavailable
267
+ - **CSV format** with precise timestamps and response times
268
+ - **Zero configuration** - trainer automatically detects and uses CSV logs
269
+ - **Lightweight** - fails silently to avoid breaking your application
270
+
271
+ ---
272
+
191
273
  ### Optional (defaults shown)
192
274
 
193
275
  ```python
@@ -219,14 +301,40 @@ Add in **this** order to your `MIDDLEWARE` list:
219
301
  ```python
220
302
  MIDDLEWARE = [
221
303
  "aiwaf.middleware.IPAndKeywordBlockMiddleware",
222
- "aiwaf.middleware.RateLimitMiddleware",
304
+ "aiwaf.middleware.RateLimitMiddleware",
223
305
  "aiwaf.middleware.AIAnomalyMiddleware",
224
306
  "aiwaf.middleware.HoneypotTimingMiddleware",
225
307
  "aiwaf.middleware.UUIDTamperMiddleware",
226
308
  # ... other middleware ...
309
+ "aiwaf.middleware_logger.AIWAFLoggerMiddleware", # Optional: Add if using built-in logger
227
310
  ]
228
311
  ```
229
312
 
313
+ > **⚠️ Order matters!** AI-WAF protection middleware should come early. The logger middleware should come near the end to capture final response data.
314
+
315
+ ---
316
+
317
+ ## Running Detection & Training
318
+
319
+ ```bash
320
+ python manage.py detect_and_train
321
+ ```
322
+
323
+ ### What happens:
324
+ 1. Read access logs (incl. rotated or gzipped) **OR** AI-WAF middleware CSV logs
325
+ 2. Auto‑block IPs with ≥ 6 total 404s
326
+ 3. Extract features & train IsolationForest
327
+ 4. Save `model.pkl`
328
+ 5. Extract top 10 dynamic keywords from 4xx/5xx
329
+ 6. Remove any keywords associated with newly exempt paths
330
+
331
+ **Note:** If main access log (`AIWAF_ACCESS_LOG`) is unavailable, trainer automatically falls back to AI-WAF middleware CSV logs.
332
+
333
+ ---
334
+
335
+ ## 🧠 How It Works
336
+ ```
337
+
230
338
  ---
231
339
 
232
340
  ## Running Detection & Training
@@ -62,7 +62,28 @@ aiwaf/
62
62
  - Submit forms faster than `AIWAF_MIN_FORM_TIME` seconds (default: 1 second)
63
63
 
64
64
  - **UUID Tampering Protection**
65
- Blocks guessed or invalid UUIDs that dont resolve to real models.
65
+ Blocks guessed or invalid UUIDs that don't resolve to real models.
66
+
67
+ - **Built-in Request Logger**
68
+ Optional middleware logger that captures requests to CSV:
69
+ - **Automatic fallback** when main access logs unavailable
70
+ - **CSV format** for easy analysis and training
71
+ - **Captures response times** for better anomaly detection
72
+ - **Zero configuration** - works out of the box
73
+
74
+
75
+ **Exempt Path & IP Awareness**
76
+
77
+ **Exempt Paths:**
78
+ AI‑WAF automatically exempts common login paths (`/admin/`, `/login/`, `/accounts/login/`, etc.) from all blocking mechanisms. You can add additional exempt paths in your Django `settings.py`:
79
+
80
+ ```python
81
+ AIWAF_EXEMPT_PATHS = [
82
+ "/api/webhooks/",
83
+ "/health/",
84
+ "/special-endpoint/",
85
+ ]
86
+ ```
66
87
 
67
88
 
68
89
  **Exempt Path & IP Awareness**
@@ -167,6 +188,67 @@ AIWAF_ACCESS_LOG = "/var/log/nginx/access.log"
167
188
 
168
189
  ---
169
190
 
191
+ ### Storage Configuration
192
+
193
+ **Choose storage backend:**
194
+
195
+ ```python
196
+ # Use Django models (default) - requires database tables
197
+ AIWAF_STORAGE_MODE = "models"
198
+
199
+ # OR use CSV files - no database required
200
+ AIWAF_STORAGE_MODE = "csv"
201
+ AIWAF_CSV_DATA_DIR = "aiwaf_data" # Directory for CSV files
202
+ ```
203
+
204
+ **CSV Mode Features:**
205
+ - No database migrations required
206
+ - Files stored in `aiwaf_data/` directory:
207
+ - `blacklist.csv` - Blocked IP addresses
208
+ - `exemptions.csv` - Exempt IP addresses
209
+ - `keywords.csv` - Dynamic keywords
210
+ - `access_samples.csv` - Feature samples for ML training
211
+ - Perfect for lightweight deployments or when you prefer file-based storage
212
+ - Management commands work identically in both modes
213
+
214
+ ---
215
+
216
+ ### Built-in Request Logger (Optional)
217
+
218
+ Enable AI-WAF's built-in request logger as a fallback when main access logs aren't available:
219
+
220
+ ```python
221
+ # Enable middleware logging
222
+ AIWAF_MIDDLEWARE_LOGGING = True # Enable/disable logging
223
+ AIWAF_MIDDLEWARE_LOG = "aiwaf_requests.log" # Log file path
224
+ AIWAF_MIDDLEWARE_CSV = True # Use CSV format (recommended)
225
+ ```
226
+
227
+ **Then add middleware to MIDDLEWARE list:**
228
+
229
+ ```python
230
+ MIDDLEWARE = [
231
+ # ... your existing middleware ...
232
+ 'aiwaf.middleware_logger.AIWAFLoggerMiddleware', # Add near the end
233
+ ]
234
+ ```
235
+
236
+ **Manage middleware logging:**
237
+
238
+ ```bash
239
+ python manage.py aiwaf_logging --status # Check logging status
240
+ python manage.py aiwaf_logging --enable # Show setup instructions
241
+ python manage.py aiwaf_logging --clear # Clear log files
242
+ ```
243
+
244
+ **Benefits:**
245
+ - **Automatic fallback** when `AIWAF_ACCESS_LOG` unavailable
246
+ - **CSV format** with precise timestamps and response times
247
+ - **Zero configuration** - trainer automatically detects and uses CSV logs
248
+ - **Lightweight** - fails silently to avoid breaking your application
249
+
250
+ ---
251
+
170
252
  ### Optional (defaults shown)
171
253
 
172
254
  ```python
@@ -198,14 +280,40 @@ Add in **this** order to your `MIDDLEWARE` list:
198
280
  ```python
199
281
  MIDDLEWARE = [
200
282
  "aiwaf.middleware.IPAndKeywordBlockMiddleware",
201
- "aiwaf.middleware.RateLimitMiddleware",
283
+ "aiwaf.middleware.RateLimitMiddleware",
202
284
  "aiwaf.middleware.AIAnomalyMiddleware",
203
285
  "aiwaf.middleware.HoneypotTimingMiddleware",
204
286
  "aiwaf.middleware.UUIDTamperMiddleware",
205
287
  # ... other middleware ...
288
+ "aiwaf.middleware_logger.AIWAFLoggerMiddleware", # Optional: Add if using built-in logger
206
289
  ]
207
290
  ```
208
291
 
292
+ > **⚠️ Order matters!** AI-WAF protection middleware should come early. The logger middleware should come near the end to capture final response data.
293
+
294
+ ---
295
+
296
+ ## Running Detection & Training
297
+
298
+ ```bash
299
+ python manage.py detect_and_train
300
+ ```
301
+
302
+ ### What happens:
303
+ 1. Read access logs (incl. rotated or gzipped) **OR** AI-WAF middleware CSV logs
304
+ 2. Auto‑block IPs with ≥ 6 total 404s
305
+ 3. Extract features & train IsolationForest
306
+ 4. Save `model.pkl`
307
+ 5. Extract top 10 dynamic keywords from 4xx/5xx
308
+ 6. Remove any keywords associated with newly exempt paths
309
+
310
+ **Note:** If main access log (`AIWAF_ACCESS_LOG`) is unavailable, trainer automatically falls back to AI-WAF middleware CSV logs.
311
+
312
+ ---
313
+
314
+ ## 🧠 How It Works
315
+ ```
316
+
209
317
  ---
210
318
 
211
319
  ## Running Detection & Training
@@ -0,0 +1,24 @@
1
+ # aiwaf/blacklist_manager.py
2
+
3
+ from .storage import get_blacklist_store
4
+
5
+ class BlacklistManager:
6
+ @staticmethod
7
+ def block(ip, reason):
8
+ store = get_blacklist_store()
9
+ store.add_ip(ip, reason)
10
+
11
+ @staticmethod
12
+ def is_blocked(ip):
13
+ store = get_blacklist_store()
14
+ return store.is_blocked(ip)
15
+
16
+ @staticmethod
17
+ def all_blocked():
18
+ store = get_blacklist_store()
19
+ return store.get_all()
20
+
21
+ @staticmethod
22
+ def unblock(ip):
23
+ store = get_blacklist_store()
24
+ store.remove_ip(ip)
@@ -1,5 +1,5 @@
1
1
  from django.core.management.base import BaseCommand, CommandError
2
- from aiwaf.models import IPExemption
2
+ from aiwaf.storage import get_exemption_store
3
3
 
4
4
  class Command(BaseCommand):
5
5
  help = 'Add an IP address to the IPExemption list (prevents blacklisting)'
@@ -11,12 +11,13 @@ class Command(BaseCommand):
11
11
  def handle(self, *args, **options):
12
12
  ip = options['ip']
13
13
  reason = options['reason']
14
- obj, created = IPExemption.objects.get_or_create(ip_address=ip, defaults={'reason': reason})
15
- if not created:
14
+
15
+ store = get_exemption_store()
16
+
17
+ if store.is_exempted(ip):
16
18
  self.stdout.write(self.style.WARNING(f'IP {ip} is already exempted.'))
17
19
  else:
20
+ store.add_ip(ip, reason)
18
21
  self.stdout.write(self.style.SUCCESS(f'IP {ip} added to exemption list.'))
19
- if reason:
20
- obj.reason = reason
21
- obj.save()
22
- self.stdout.write(self.style.SUCCESS(f'Reason set to: {reason}'))
22
+ if reason:
23
+ self.stdout.write(self.style.SUCCESS(f'Reason: {reason}'))
@@ -0,0 +1,166 @@
1
+ from django.core.management.base import BaseCommand
2
+ from django.conf import settings
3
+ import os
4
+
5
+ class Command(BaseCommand):
6
+ help = 'Manage AI-WAF middleware logging settings and view log status'
7
+
8
+ def add_arguments(self, parser):
9
+ parser.add_argument(
10
+ '--enable',
11
+ action='store_true',
12
+ help='Enable middleware logging (shows settings to add)'
13
+ )
14
+ parser.add_argument(
15
+ '--disable',
16
+ action='store_true',
17
+ help='Disable middleware logging (shows settings to remove)'
18
+ )
19
+ parser.add_argument(
20
+ '--status',
21
+ action='store_true',
22
+ help='Show current middleware logging status'
23
+ )
24
+ parser.add_argument(
25
+ '--clear',
26
+ action='store_true',
27
+ help='Clear/delete middleware log files'
28
+ )
29
+
30
+ def handle(self, *args, **options):
31
+ if options['enable']:
32
+ self._show_enable_instructions()
33
+ elif options['disable']:
34
+ self._show_disable_instructions()
35
+ elif options['clear']:
36
+ self._clear_logs()
37
+ else:
38
+ self._show_status()
39
+
40
+ def _show_status(self):
41
+ """Show current middleware logging configuration"""
42
+ self.stdout.write(self.style.HTTP_INFO("🔍 AI-WAF Middleware Logging Status"))
43
+ self.stdout.write("")
44
+
45
+ # Check settings
46
+ logging_enabled = getattr(settings, 'AIWAF_MIDDLEWARE_LOGGING', False)
47
+ log_file = getattr(settings, 'AIWAF_MIDDLEWARE_LOG', 'aiwaf_requests.log')
48
+ csv_format = getattr(settings, 'AIWAF_MIDDLEWARE_CSV', True)
49
+ csv_file = log_file.replace('.log', '.csv') if csv_format else None
50
+
51
+ # Status
52
+ status_color = self.style.SUCCESS if logging_enabled else self.style.WARNING
53
+ self.stdout.write(f"Status: {status_color('ENABLED' if logging_enabled else 'DISABLED')}")
54
+ self.stdout.write(f"Log File: {log_file}")
55
+ if csv_format:
56
+ self.stdout.write(f"CSV File: {csv_file}")
57
+ self.stdout.write(f"Format: {'CSV' if csv_format else 'Text'}")
58
+ self.stdout.write("")
59
+
60
+ # File existence and sizes
61
+ if logging_enabled:
62
+ self.stdout.write("📁 Log Files:")
63
+
64
+ if csv_format and csv_file:
65
+ if os.path.exists(csv_file):
66
+ size = os.path.getsize(csv_file)
67
+ lines = self._count_csv_lines(csv_file)
68
+ self.stdout.write(f" ✅ {csv_file} ({size:,} bytes, {lines:,} entries)")
69
+ else:
70
+ self.stdout.write(f" ❌ {csv_file} (not found)")
71
+
72
+ if os.path.exists(log_file):
73
+ size = os.path.getsize(log_file)
74
+ self.stdout.write(f" ✅ {log_file} ({size:,} bytes)")
75
+ else:
76
+ self.stdout.write(f" ❌ {log_file} (not found)")
77
+
78
+ # Middleware check
79
+ middleware_list = getattr(settings, 'MIDDLEWARE', [])
80
+ middleware_installed = 'aiwaf.middleware_logger.AIWAFLoggerMiddleware' in middleware_list
81
+
82
+ self.stdout.write("")
83
+ middleware_color = self.style.SUCCESS if middleware_installed else self.style.ERROR
84
+ self.stdout.write(f"Middleware: {middleware_color('INSTALLED' if middleware_installed else 'NOT INSTALLED')}")
85
+
86
+ if logging_enabled and not middleware_installed:
87
+ self.stdout.write(self.style.WARNING("⚠️ Logging is enabled but middleware is not installed!"))
88
+
89
+ def _show_enable_instructions(self):
90
+ """Show instructions for enabling middleware logging"""
91
+ self.stdout.write(self.style.SUCCESS("🚀 Enable AI-WAF Middleware Logging"))
92
+ self.stdout.write("")
93
+ self.stdout.write("Add these settings to your Django settings.py:")
94
+ self.stdout.write("")
95
+ self.stdout.write(self.style.HTTP_INFO("# Enable AI-WAF middleware logging"))
96
+ self.stdout.write(self.style.HTTP_INFO("AIWAF_MIDDLEWARE_LOGGING = True"))
97
+ self.stdout.write(self.style.HTTP_INFO("AIWAF_MIDDLEWARE_LOG = 'aiwaf_requests.log' # Optional"))
98
+ self.stdout.write(self.style.HTTP_INFO("AIWAF_MIDDLEWARE_CSV = True # Optional (default: True)"))
99
+ self.stdout.write("")
100
+ self.stdout.write("Add middleware to MIDDLEWARE list (preferably near the end):")
101
+ self.stdout.write("")
102
+ self.stdout.write(self.style.HTTP_INFO("MIDDLEWARE = ["))
103
+ self.stdout.write(self.style.HTTP_INFO(" # ... your existing middleware ..."))
104
+ self.stdout.write(self.style.HTTP_INFO(" 'aiwaf.middleware_logger.AIWAFLoggerMiddleware',"))
105
+ self.stdout.write(self.style.HTTP_INFO("]"))
106
+ self.stdout.write("")
107
+ self.stdout.write("Benefits:")
108
+ self.stdout.write(" ✅ Fallback when main access logs unavailable")
109
+ self.stdout.write(" ✅ CSV format for easy analysis")
110
+ self.stdout.write(" ✅ Automatic integration with AI-WAF trainer")
111
+ self.stdout.write(" ✅ Captures response times for better detection")
112
+
113
+ def _show_disable_instructions(self):
114
+ """Show instructions for disabling middleware logging"""
115
+ self.stdout.write(self.style.WARNING("⏹️ Disable AI-WAF Middleware Logging"))
116
+ self.stdout.write("")
117
+ self.stdout.write("To disable, update your Django settings.py:")
118
+ self.stdout.write("")
119
+ self.stdout.write(self.style.HTTP_INFO("# Disable AI-WAF middleware logging"))
120
+ self.stdout.write(self.style.HTTP_INFO("AIWAF_MIDDLEWARE_LOGGING = False"))
121
+ self.stdout.write("")
122
+ self.stdout.write("And remove from MIDDLEWARE list:")
123
+ self.stdout.write("")
124
+ self.stdout.write(self.style.HTTP_INFO("MIDDLEWARE = ["))
125
+ self.stdout.write(self.style.HTTP_INFO(" # ... your existing middleware ..."))
126
+ self.stdout.write(self.style.HTTP_INFO(" # 'aiwaf.middleware_logger.AIWAFLoggerMiddleware', # Remove this line"))
127
+ self.stdout.write(self.style.HTTP_INFO("]"))
128
+
129
+ def _clear_logs(self):
130
+ """Clear/delete middleware log files"""
131
+ log_file = getattr(settings, 'AIWAF_MIDDLEWARE_LOG', 'aiwaf_requests.log')
132
+ csv_format = getattr(settings, 'AIWAF_MIDDLEWARE_CSV', True)
133
+ csv_file = log_file.replace('.log', '.csv') if csv_format else None
134
+
135
+ files_deleted = 0
136
+
137
+ # Delete CSV file
138
+ if csv_file and os.path.exists(csv_file):
139
+ try:
140
+ os.remove(csv_file)
141
+ self.stdout.write(self.style.SUCCESS(f"✅ Deleted {csv_file}"))
142
+ files_deleted += 1
143
+ except Exception as e:
144
+ self.stdout.write(self.style.ERROR(f"❌ Failed to delete {csv_file}: {e}"))
145
+
146
+ # Delete text log file
147
+ if os.path.exists(log_file):
148
+ try:
149
+ os.remove(log_file)
150
+ self.stdout.write(self.style.SUCCESS(f"✅ Deleted {log_file}"))
151
+ files_deleted += 1
152
+ except Exception as e:
153
+ self.stdout.write(self.style.ERROR(f"❌ Failed to delete {log_file}: {e}"))
154
+
155
+ if files_deleted == 0:
156
+ self.stdout.write(self.style.WARNING("ℹ️ No log files found to delete"))
157
+ else:
158
+ self.stdout.write(self.style.SUCCESS(f"🗑️ Deleted {files_deleted} log file(s)"))
159
+
160
+ def _count_csv_lines(self, csv_file):
161
+ """Count lines in CSV file (excluding header)"""
162
+ try:
163
+ with open(csv_file, 'r', encoding='utf-8') as f:
164
+ return sum(1 for line in f) - 1 # Subtract header
165
+ except:
166
+ return 0
@@ -1,5 +1,5 @@
1
1
  from django.core.management.base import BaseCommand
2
- from aiwaf.models import BlacklistEntry, IPExemption
2
+ from aiwaf.storage import get_blacklist_store, get_exemption_store
3
3
 
4
4
  class Command(BaseCommand):
5
5
  help = 'Reset AI-WAF by clearing all blacklist and exemption (whitelist) entries'
@@ -26,9 +26,12 @@ class Command(BaseCommand):
26
26
  exemptions_only = options['exemptions_only']
27
27
  confirm = options['confirm']
28
28
 
29
+ blacklist_store = get_blacklist_store()
30
+ exemption_store = get_exemption_store()
31
+
29
32
  # Count current entries
30
- blacklist_count = BlacklistEntry.objects.count()
31
- exemption_count = IPExemption.objects.count()
33
+ blacklist_count = len(blacklist_store.get_all())
34
+ exemption_count = len(exemption_store.get_all())
32
35
 
33
36
  if blacklist_only and exemptions_only:
34
37
  self.stdout.write(self.style.ERROR('Cannot use both --blacklist-only and --exemptions-only flags'))
@@ -61,10 +64,18 @@ class Command(BaseCommand):
61
64
  deleted_counts = {'blacklist': 0, 'exemptions': 0}
62
65
 
63
66
  if clear_blacklist:
64
- deleted_counts['blacklist'], _ = BlacklistEntry.objects.all().delete()
67
+ # Clear blacklist entries
68
+ blacklist_entries = blacklist_store.get_all()
69
+ for entry in blacklist_entries:
70
+ blacklist_store.remove_ip(entry['ip_address'])
71
+ deleted_counts['blacklist'] = len(blacklist_entries)
65
72
 
66
73
  if clear_exemptions:
67
- deleted_counts['exemptions'], _ = IPExemption.objects.all().delete()
74
+ # Clear exemption entries
75
+ exemption_entries = exemption_store.get_all()
76
+ for entry in exemption_entries:
77
+ exemption_store.remove_ip(entry['ip_address'])
78
+ deleted_counts['exemptions'] = len(exemption_entries)
68
79
 
69
80
  # Report results
70
81
  if clear_blacklist and clear_exemptions:
@@ -16,8 +16,9 @@ from django.apps import apps
16
16
  from django.urls import get_resolver
17
17
  from .trainer import STATIC_KW, STATUS_IDX, path_exists_in_django
18
18
  from .blacklist_manager import BlacklistManager
19
- from .models import DynamicKeyword, IPExemption
19
+ from .models import IPExemption
20
20
  from .utils import is_exempt, get_ip, is_ip_exempted
21
+ from .storage import get_keyword_store
21
22
 
22
23
  MODEL_PATH = getattr(
23
24
  settings,
@@ -74,15 +75,14 @@ class IPAndKeywordBlockMiddleware:
74
75
  return self.get_response(request)
75
76
  if BlacklistManager.is_blocked(ip):
76
77
  return JsonResponse({"error": "blocked"}, status=403)
78
+
79
+ keyword_store = get_keyword_store()
77
80
  segments = [seg for seg in re.split(r"\W+", path) if len(seg) > 3]
81
+
78
82
  for seg in segments:
79
- obj, _ = DynamicKeyword.objects.get_or_create(keyword=seg)
80
- DynamicKeyword.objects.filter(pk=obj.pk).update(count=F("count") + 1)
81
- dynamic_top = list(
82
- DynamicKeyword.objects
83
- .order_by("-count")
84
- .values_list("keyword", flat=True)[: getattr(settings, "AIWAF_DYNAMIC_TOP_N", 10)]
85
- )
83
+ keyword_store.add_keyword(seg)
84
+
85
+ dynamic_top = keyword_store.get_top_keywords(getattr(settings, "AIWAF_DYNAMIC_TOP_N", 10))
86
86
  all_kw = set(STATIC_KW) | set(dynamic_top)
87
87
  suspicious_kw = {
88
88
  kw for kw in all_kw
@@ -172,10 +172,11 @@ class AIAnomalyMiddleware(MiddlewareMixin):
172
172
  data.append((now, request.path, response.status_code, resp_time))
173
173
  data = [d for d in data if now - d[0] < self.WINDOW]
174
174
  cache.set(key, data, timeout=self.WINDOW)
175
+
176
+ keyword_store = get_keyword_store()
175
177
  for seg in re.split(r"\W+", request.path.lower()):
176
178
  if len(seg) > 3:
177
- obj, _ = DynamicKeyword.objects.get_or_create(keyword=seg)
178
- DynamicKeyword.objects.filter(pk=obj.pk).update(count=F("count") + 1)
179
+ keyword_store.add_keyword(seg)
179
180
 
180
181
  return response
181
182