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.
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/PKG-INFO +111 -3
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/README.md +110 -2
- aiwaf-0.1.8.8/aiwaf/blacklist_manager.py +24 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/management/commands/add_ipexemption.py +8 -7
- aiwaf-0.1.8.8/aiwaf/management/commands/aiwaf_logging.py +166 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/management/commands/aiwaf_reset.py +16 -5
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/middleware.py +11 -10
- aiwaf-0.1.8.8/aiwaf/middleware_logger.py +160 -0
- aiwaf-0.1.8.8/aiwaf/storage.py +351 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/trainer.py +51 -31
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/utils.py +3 -2
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf.egg-info/PKG-INFO +111 -3
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf.egg-info/SOURCES.txt +2 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/pyproject.toml +1 -1
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/setup.py +1 -1
- aiwaf-0.1.8.6/aiwaf/blacklist_manager.py +0 -14
- aiwaf-0.1.8.6/aiwaf/storage.py +0 -61
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/LICENSE +0 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/__init__.py +0 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/apps.py +0 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/decorators.py +0 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/management/__init__.py +0 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/management/commands/__init__.py +0 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/management/commands/detect_and_train.py +0 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/models.py +0 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/resources/model.pkl +0 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/templatetags/__init__.py +0 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf/templatetags/aiwaf_tags.py +0 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf.egg-info/dependency_links.txt +0 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf.egg-info/requires.txt +0 -0
- {aiwaf-0.1.8.6 → aiwaf-0.1.8.8}/aiwaf.egg-info/top_level.txt +0 -0
- {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.
|
|
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 don
|
|
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 don
|
|
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.
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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.
|
|
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 =
|
|
31
|
-
exemption_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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
dynamic_top =
|
|
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
|
-
|
|
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
|
|