aiwaf 0.1.9.1.9__tar.gz → 0.1.9.2.0__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.9.1.9 → aiwaf-0.1.9.2.0}/PKG-INFO +121 -12
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/README.md +120 -11
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/__init__.py +1 -1
- aiwaf-0.1.9.2.0/aiwaf/management/commands/aiwaf_reset.py +183 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/middleware.py +210 -42
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/storage.py +13 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/trainer.py +90 -9
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf.egg-info/PKG-INFO +121 -12
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/pyproject.toml +1 -1
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/setup.py +1 -1
- aiwaf-0.1.9.1.9/aiwaf/management/commands/aiwaf_reset.py +0 -136
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/LICENSE +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/apps.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/blacklist_manager.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/decorators.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/__init__.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/__init__.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/add_exemption.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/add_ipexemption.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/aiwaf_diagnose.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/aiwaf_logging.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/check_dependencies.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/clear_blacklist.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/clear_cache.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/debug_csv.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/detect_and_train.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/diagnose_blocking.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/regenerate_model.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/setup_models.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/test_exemption.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/management/commands/test_exemption_fix.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/middleware_logger.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/models.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/resources/model.pkl +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/templatetags/__init__.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/templatetags/aiwaf_tags.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf/utils.py +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf.egg-info/SOURCES.txt +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf.egg-info/dependency_links.txt +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf.egg-info/requires.txt +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/aiwaf.egg-info/top_level.txt +0 -0
- {aiwaf-0.1.9.1.9 → aiwaf-0.1.9.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aiwaf
|
|
3
|
-
Version: 0.1.9.
|
|
3
|
+
Version: 0.1.9.2.0
|
|
4
4
|
Summary: AI-powered Web Application Firewall
|
|
5
5
|
Home-page: https://github.com/aayushgauba/aiwaf
|
|
6
6
|
Author: Aayush Gauba
|
|
@@ -25,7 +25,13 @@ Dynamic: requires-python
|
|
|
25
25
|
# AI‑WAF
|
|
26
26
|
|
|
27
27
|
> A self‑learning, Django‑friendly Web Application Firewall
|
|
28
|
-
> with rate‑limiting, anomaly detection, honeypots, UUID‑tamper protection,
|
|
28
|
+
> with **enhanced context-aware protection**, rate‑limiting, anomaly detection, honeypots, UUID‑tamper protection, **smart keyword learning**, file‑extension probing detection, exempt path awareness, and daily retraining.
|
|
29
|
+
|
|
30
|
+
**🆕 Latest Enhancements:**
|
|
31
|
+
- ✅ **Smart Keyword Filtering** - Prevents blocking legitimate pages like `/profile/`
|
|
32
|
+
- ✅ **Granular Reset Commands** - Clear specific data types (`--blacklist`, `--keywords`, `--exemptions`)
|
|
33
|
+
- ✅ **Context-Aware Learning** - Only learns from suspicious requests, not legitimate site functionality
|
|
34
|
+
- ✅ **Enhanced Configuration** - `AIWAF_ALLOWED_PATH_KEYWORDS` and `AIWAF_EXEMPT_KEYWORDS`
|
|
29
35
|
|
|
30
36
|
---
|
|
31
37
|
|
|
@@ -88,9 +94,14 @@ aiwaf/
|
|
|
88
94
|
- Burst count
|
|
89
95
|
- Total 404s
|
|
90
96
|
|
|
91
|
-
- **Dynamic Keyword
|
|
92
|
-
-
|
|
93
|
-
- **
|
|
97
|
+
- **Enhanced Dynamic Keyword Learning**
|
|
98
|
+
- **Smart Context-Aware Learning**: Only learns keywords from suspicious requests on non-existent paths
|
|
99
|
+
- **Legitimate Path Protection**: Automatically excludes keywords from valid Django URLs (like `/profile/`, `/admin/`)
|
|
100
|
+
- **Configuration Options**:
|
|
101
|
+
- `AIWAF_ALLOWED_PATH_KEYWORDS` - Explicitly allow certain keywords in legitimate paths
|
|
102
|
+
- `AIWAF_EXEMPT_KEYWORDS` - Keywords that should never trigger blocking
|
|
103
|
+
- **Automatic Cleanup**: Keywords from `AIWAF_EXEMPT_PATHS` are automatically removed from the database
|
|
104
|
+
- **False Positive Prevention**: Stops learning legitimate site functionality as "malicious"
|
|
94
105
|
|
|
95
106
|
- **File‑Extension Probing Detection**
|
|
96
107
|
Tracks repeated 404s on common extensions (e.g. `.php`, `.asp`) and blocks IPs.
|
|
@@ -196,20 +207,44 @@ python manage.py add_ipexemption <ip-address> --reason "optional reason"
|
|
|
196
207
|
|
|
197
208
|
### Resetting AI-WAF
|
|
198
209
|
|
|
199
|
-
|
|
210
|
+
The `aiwaf_reset` command provides **granular control** for clearing different types of data:
|
|
200
211
|
|
|
201
212
|
```bash
|
|
202
|
-
# Clear everything (
|
|
213
|
+
# Clear everything (default - backward compatible)
|
|
203
214
|
python manage.py aiwaf_reset
|
|
204
215
|
|
|
205
|
-
# Clear everything without confirmation
|
|
216
|
+
# Clear everything without confirmation prompt
|
|
206
217
|
python manage.py aiwaf_reset --confirm
|
|
207
218
|
|
|
208
|
-
# Clear
|
|
209
|
-
python manage.py aiwaf_reset --blacklist
|
|
219
|
+
# 🆕 GRANULAR CONTROL - Clear specific data types
|
|
220
|
+
python manage.py aiwaf_reset --blacklist # Clear only blocked IPs
|
|
221
|
+
python manage.py aiwaf_reset --exemptions # Clear only exempted IPs
|
|
222
|
+
python manage.py aiwaf_reset --keywords # Clear only learned keywords
|
|
223
|
+
|
|
224
|
+
# 🔧 COMBINE OPTIONS - Mix and match as needed
|
|
225
|
+
python manage.py aiwaf_reset --blacklist --keywords # Keep exemptions
|
|
226
|
+
python manage.py aiwaf_reset --exemptions --keywords # Keep blacklist
|
|
227
|
+
python manage.py aiwaf_reset --blacklist --exemptions # Keep keywords
|
|
228
|
+
|
|
229
|
+
# 🚀 COMMON USE CASES
|
|
230
|
+
# Fix false positive keywords (like "profile" blocking legitimate pages)
|
|
231
|
+
python manage.py aiwaf_reset --keywords --confirm
|
|
232
|
+
python manage.py detect_and_train # Retrain with enhanced filtering
|
|
233
|
+
|
|
234
|
+
# Clear blocked IPs but preserve exemptions and learning
|
|
235
|
+
python manage.py aiwaf_reset --blacklist --confirm
|
|
236
|
+
|
|
237
|
+
# Legacy support (still works for backward compatibility)
|
|
238
|
+
python manage.py aiwaf_reset --blacklist-only # Legacy: blacklist only
|
|
239
|
+
python manage.py aiwaf_reset --exemptions-only # Legacy: exemptions only
|
|
240
|
+
```
|
|
210
241
|
|
|
211
|
-
|
|
212
|
-
|
|
242
|
+
**Enhanced Feedback:**
|
|
243
|
+
```bash
|
|
244
|
+
$ python manage.py aiwaf_reset --keywords
|
|
245
|
+
🔧 AI-WAF Reset: Clear 15 learned keywords
|
|
246
|
+
Are you sure you want to proceed? [y/N]: y
|
|
247
|
+
✅ Reset complete: Deleted 15 learned keywords
|
|
213
248
|
```
|
|
214
249
|
|
|
215
250
|
### Checking Dependencies
|
|
@@ -482,6 +517,21 @@ AIWAF_EXEMPT_PATHS = [ # optional but highly recommended
|
|
|
482
517
|
"/media/",
|
|
483
518
|
"/health/",
|
|
484
519
|
]
|
|
520
|
+
|
|
521
|
+
# 🆕 ENHANCED KEYWORD FILTERING OPTIONS
|
|
522
|
+
AIWAF_ALLOWED_PATH_KEYWORDS = [ # Keywords allowed in legitimate paths
|
|
523
|
+
"profile", "user", "account", "settings", "dashboard",
|
|
524
|
+
"admin", "api", "auth", "search", "contact", "about",
|
|
525
|
+
# Add your site-specific legitimate keywords
|
|
526
|
+
"buddycraft", "sc2", "starcraft", # Example: gaming site keywords
|
|
527
|
+
]
|
|
528
|
+
|
|
529
|
+
AIWAF_EXEMPT_KEYWORDS = [ # Keywords that never trigger blocking
|
|
530
|
+
"api", "webhook", "health", "static", "media",
|
|
531
|
+
"upload", "download", "backup", "profile"
|
|
532
|
+
]
|
|
533
|
+
|
|
534
|
+
AIWAF_DYNAMIC_TOP_N = 10 # Number of dynamic keywords to learn (default: 10)
|
|
485
535
|
```
|
|
486
536
|
|
|
487
537
|
> **Note:** You no longer need to define `AIWAF_MALICIOUS_KEYWORDS` or `AIWAF_STATUS_CODES` — they evolve dynamically.
|
|
@@ -680,6 +730,65 @@ python manage.py detect_and_train
|
|
|
680
730
|
|
|
681
731
|
---
|
|
682
732
|
|
|
733
|
+
## 🔧 Troubleshooting
|
|
734
|
+
|
|
735
|
+
### Legitimate Pages Being Blocked
|
|
736
|
+
|
|
737
|
+
**Problem**: Users can't access legitimate pages like `/en/profile/` due to keyword blocking.
|
|
738
|
+
|
|
739
|
+
**Cause**: AIWAF learned legitimate keywords (like "profile") as suspicious from previous traffic.
|
|
740
|
+
|
|
741
|
+
**Solution**:
|
|
742
|
+
```bash
|
|
743
|
+
# 1. Clear problematic learned keywords
|
|
744
|
+
python manage.py aiwaf_reset --keywords --confirm
|
|
745
|
+
|
|
746
|
+
# 2. Add legitimate keywords to settings
|
|
747
|
+
# In settings.py:
|
|
748
|
+
AIWAF_ALLOWED_PATH_KEYWORDS = [
|
|
749
|
+
"profile", "user", "account", "dashboard",
|
|
750
|
+
# Add your site-specific keywords
|
|
751
|
+
]
|
|
752
|
+
|
|
753
|
+
# 3. Retrain with enhanced filtering (won't learn legitimate keywords)
|
|
754
|
+
python manage.py detect_and_train
|
|
755
|
+
|
|
756
|
+
# 4. Test - legitimate pages should now work!
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
### Preventing Future False Positives
|
|
760
|
+
|
|
761
|
+
Configure AIWAF to recognize your site's legitimate keywords:
|
|
762
|
+
|
|
763
|
+
```python
|
|
764
|
+
# settings.py
|
|
765
|
+
AIWAF_ALLOWED_PATH_KEYWORDS = [
|
|
766
|
+
# Common legitimate keywords
|
|
767
|
+
"profile", "user", "account", "settings", "dashboard",
|
|
768
|
+
"admin", "search", "contact", "about", "help",
|
|
769
|
+
|
|
770
|
+
# Your site-specific keywords
|
|
771
|
+
"buddycraft", "sc2", "starcraft", # Gaming site example
|
|
772
|
+
"shop", "cart", "checkout", # E-commerce example
|
|
773
|
+
"blog", "article", "news", # Content site example
|
|
774
|
+
]
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
### Reset Command Options
|
|
778
|
+
|
|
779
|
+
```bash
|
|
780
|
+
# Clear everything (safest for troubleshooting)
|
|
781
|
+
python manage.py aiwaf_reset --confirm
|
|
782
|
+
|
|
783
|
+
# Clear only problematic keywords
|
|
784
|
+
python manage.py aiwaf_reset --keywords --confirm
|
|
785
|
+
|
|
786
|
+
# Clear blocked IPs but keep exemptions
|
|
787
|
+
python manage.py aiwaf_reset --blacklist --confirm
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
---
|
|
791
|
+
|
|
683
792
|
## 🧠 How It Works
|
|
684
793
|
|
|
685
794
|
| Middleware | Purpose |
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
# AI‑WAF
|
|
3
3
|
|
|
4
4
|
> A self‑learning, Django‑friendly Web Application Firewall
|
|
5
|
-
> with rate‑limiting, anomaly detection, honeypots, UUID‑tamper protection,
|
|
5
|
+
> with **enhanced context-aware protection**, rate‑limiting, anomaly detection, honeypots, UUID‑tamper protection, **smart keyword learning**, file‑extension probing detection, exempt path awareness, and daily retraining.
|
|
6
|
+
|
|
7
|
+
**🆕 Latest Enhancements:**
|
|
8
|
+
- ✅ **Smart Keyword Filtering** - Prevents blocking legitimate pages like `/profile/`
|
|
9
|
+
- ✅ **Granular Reset Commands** - Clear specific data types (`--blacklist`, `--keywords`, `--exemptions`)
|
|
10
|
+
- ✅ **Context-Aware Learning** - Only learns from suspicious requests, not legitimate site functionality
|
|
11
|
+
- ✅ **Enhanced Configuration** - `AIWAF_ALLOWED_PATH_KEYWORDS` and `AIWAF_EXEMPT_KEYWORDS`
|
|
6
12
|
|
|
7
13
|
---
|
|
8
14
|
|
|
@@ -65,9 +71,14 @@ aiwaf/
|
|
|
65
71
|
- Burst count
|
|
66
72
|
- Total 404s
|
|
67
73
|
|
|
68
|
-
- **Dynamic Keyword
|
|
69
|
-
-
|
|
70
|
-
- **
|
|
74
|
+
- **Enhanced Dynamic Keyword Learning**
|
|
75
|
+
- **Smart Context-Aware Learning**: Only learns keywords from suspicious requests on non-existent paths
|
|
76
|
+
- **Legitimate Path Protection**: Automatically excludes keywords from valid Django URLs (like `/profile/`, `/admin/`)
|
|
77
|
+
- **Configuration Options**:
|
|
78
|
+
- `AIWAF_ALLOWED_PATH_KEYWORDS` - Explicitly allow certain keywords in legitimate paths
|
|
79
|
+
- `AIWAF_EXEMPT_KEYWORDS` - Keywords that should never trigger blocking
|
|
80
|
+
- **Automatic Cleanup**: Keywords from `AIWAF_EXEMPT_PATHS` are automatically removed from the database
|
|
81
|
+
- **False Positive Prevention**: Stops learning legitimate site functionality as "malicious"
|
|
71
82
|
|
|
72
83
|
- **File‑Extension Probing Detection**
|
|
73
84
|
Tracks repeated 404s on common extensions (e.g. `.php`, `.asp`) and blocks IPs.
|
|
@@ -173,20 +184,44 @@ python manage.py add_ipexemption <ip-address> --reason "optional reason"
|
|
|
173
184
|
|
|
174
185
|
### Resetting AI-WAF
|
|
175
186
|
|
|
176
|
-
|
|
187
|
+
The `aiwaf_reset` command provides **granular control** for clearing different types of data:
|
|
177
188
|
|
|
178
189
|
```bash
|
|
179
|
-
# Clear everything (
|
|
190
|
+
# Clear everything (default - backward compatible)
|
|
180
191
|
python manage.py aiwaf_reset
|
|
181
192
|
|
|
182
|
-
# Clear everything without confirmation
|
|
193
|
+
# Clear everything without confirmation prompt
|
|
183
194
|
python manage.py aiwaf_reset --confirm
|
|
184
195
|
|
|
185
|
-
# Clear
|
|
186
|
-
python manage.py aiwaf_reset --blacklist
|
|
196
|
+
# 🆕 GRANULAR CONTROL - Clear specific data types
|
|
197
|
+
python manage.py aiwaf_reset --blacklist # Clear only blocked IPs
|
|
198
|
+
python manage.py aiwaf_reset --exemptions # Clear only exempted IPs
|
|
199
|
+
python manage.py aiwaf_reset --keywords # Clear only learned keywords
|
|
200
|
+
|
|
201
|
+
# 🔧 COMBINE OPTIONS - Mix and match as needed
|
|
202
|
+
python manage.py aiwaf_reset --blacklist --keywords # Keep exemptions
|
|
203
|
+
python manage.py aiwaf_reset --exemptions --keywords # Keep blacklist
|
|
204
|
+
python manage.py aiwaf_reset --blacklist --exemptions # Keep keywords
|
|
205
|
+
|
|
206
|
+
# 🚀 COMMON USE CASES
|
|
207
|
+
# Fix false positive keywords (like "profile" blocking legitimate pages)
|
|
208
|
+
python manage.py aiwaf_reset --keywords --confirm
|
|
209
|
+
python manage.py detect_and_train # Retrain with enhanced filtering
|
|
210
|
+
|
|
211
|
+
# Clear blocked IPs but preserve exemptions and learning
|
|
212
|
+
python manage.py aiwaf_reset --blacklist --confirm
|
|
213
|
+
|
|
214
|
+
# Legacy support (still works for backward compatibility)
|
|
215
|
+
python manage.py aiwaf_reset --blacklist-only # Legacy: blacklist only
|
|
216
|
+
python manage.py aiwaf_reset --exemptions-only # Legacy: exemptions only
|
|
217
|
+
```
|
|
187
218
|
|
|
188
|
-
|
|
189
|
-
|
|
219
|
+
**Enhanced Feedback:**
|
|
220
|
+
```bash
|
|
221
|
+
$ python manage.py aiwaf_reset --keywords
|
|
222
|
+
🔧 AI-WAF Reset: Clear 15 learned keywords
|
|
223
|
+
Are you sure you want to proceed? [y/N]: y
|
|
224
|
+
✅ Reset complete: Deleted 15 learned keywords
|
|
190
225
|
```
|
|
191
226
|
|
|
192
227
|
### Checking Dependencies
|
|
@@ -459,6 +494,21 @@ AIWAF_EXEMPT_PATHS = [ # optional but highly recommended
|
|
|
459
494
|
"/media/",
|
|
460
495
|
"/health/",
|
|
461
496
|
]
|
|
497
|
+
|
|
498
|
+
# 🆕 ENHANCED KEYWORD FILTERING OPTIONS
|
|
499
|
+
AIWAF_ALLOWED_PATH_KEYWORDS = [ # Keywords allowed in legitimate paths
|
|
500
|
+
"profile", "user", "account", "settings", "dashboard",
|
|
501
|
+
"admin", "api", "auth", "search", "contact", "about",
|
|
502
|
+
# Add your site-specific legitimate keywords
|
|
503
|
+
"buddycraft", "sc2", "starcraft", # Example: gaming site keywords
|
|
504
|
+
]
|
|
505
|
+
|
|
506
|
+
AIWAF_EXEMPT_KEYWORDS = [ # Keywords that never trigger blocking
|
|
507
|
+
"api", "webhook", "health", "static", "media",
|
|
508
|
+
"upload", "download", "backup", "profile"
|
|
509
|
+
]
|
|
510
|
+
|
|
511
|
+
AIWAF_DYNAMIC_TOP_N = 10 # Number of dynamic keywords to learn (default: 10)
|
|
462
512
|
```
|
|
463
513
|
|
|
464
514
|
> **Note:** You no longer need to define `AIWAF_MALICIOUS_KEYWORDS` or `AIWAF_STATUS_CODES` — they evolve dynamically.
|
|
@@ -657,6 +707,65 @@ python manage.py detect_and_train
|
|
|
657
707
|
|
|
658
708
|
---
|
|
659
709
|
|
|
710
|
+
## 🔧 Troubleshooting
|
|
711
|
+
|
|
712
|
+
### Legitimate Pages Being Blocked
|
|
713
|
+
|
|
714
|
+
**Problem**: Users can't access legitimate pages like `/en/profile/` due to keyword blocking.
|
|
715
|
+
|
|
716
|
+
**Cause**: AIWAF learned legitimate keywords (like "profile") as suspicious from previous traffic.
|
|
717
|
+
|
|
718
|
+
**Solution**:
|
|
719
|
+
```bash
|
|
720
|
+
# 1. Clear problematic learned keywords
|
|
721
|
+
python manage.py aiwaf_reset --keywords --confirm
|
|
722
|
+
|
|
723
|
+
# 2. Add legitimate keywords to settings
|
|
724
|
+
# In settings.py:
|
|
725
|
+
AIWAF_ALLOWED_PATH_KEYWORDS = [
|
|
726
|
+
"profile", "user", "account", "dashboard",
|
|
727
|
+
# Add your site-specific keywords
|
|
728
|
+
]
|
|
729
|
+
|
|
730
|
+
# 3. Retrain with enhanced filtering (won't learn legitimate keywords)
|
|
731
|
+
python manage.py detect_and_train
|
|
732
|
+
|
|
733
|
+
# 4. Test - legitimate pages should now work!
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
### Preventing Future False Positives
|
|
737
|
+
|
|
738
|
+
Configure AIWAF to recognize your site's legitimate keywords:
|
|
739
|
+
|
|
740
|
+
```python
|
|
741
|
+
# settings.py
|
|
742
|
+
AIWAF_ALLOWED_PATH_KEYWORDS = [
|
|
743
|
+
# Common legitimate keywords
|
|
744
|
+
"profile", "user", "account", "settings", "dashboard",
|
|
745
|
+
"admin", "search", "contact", "about", "help",
|
|
746
|
+
|
|
747
|
+
# Your site-specific keywords
|
|
748
|
+
"buddycraft", "sc2", "starcraft", # Gaming site example
|
|
749
|
+
"shop", "cart", "checkout", # E-commerce example
|
|
750
|
+
"blog", "article", "news", # Content site example
|
|
751
|
+
]
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
### Reset Command Options
|
|
755
|
+
|
|
756
|
+
```bash
|
|
757
|
+
# Clear everything (safest for troubleshooting)
|
|
758
|
+
python manage.py aiwaf_reset --confirm
|
|
759
|
+
|
|
760
|
+
# Clear only problematic keywords
|
|
761
|
+
python manage.py aiwaf_reset --keywords --confirm
|
|
762
|
+
|
|
763
|
+
# Clear blocked IPs but keep exemptions
|
|
764
|
+
python manage.py aiwaf_reset --blacklist --confirm
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
---
|
|
768
|
+
|
|
660
769
|
## 🧠 How It Works
|
|
661
770
|
|
|
662
771
|
| Middleware | Purpose |
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
from django.core.management.base import BaseCommand
|
|
2
|
+
from aiwaf.storage import get_blacklist_store, get_exemption_store, get_keyword_store
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
class Command(BaseCommand):
|
|
6
|
+
help = 'Reset AI-WAF by clearing blacklist, exemption, and/or keyword entries'
|
|
7
|
+
|
|
8
|
+
def add_arguments(self, parser):
|
|
9
|
+
parser.add_argument(
|
|
10
|
+
'--blacklist',
|
|
11
|
+
action='store_true',
|
|
12
|
+
help='Clear blacklist entries (default: all)'
|
|
13
|
+
)
|
|
14
|
+
parser.add_argument(
|
|
15
|
+
'--exemptions',
|
|
16
|
+
action='store_true',
|
|
17
|
+
help='Clear exemption entries (default: all)'
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
'--keywords',
|
|
21
|
+
action='store_true',
|
|
22
|
+
help='Clear learned dynamic keywords (default: all)'
|
|
23
|
+
)
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
'--confirm',
|
|
26
|
+
action='store_true',
|
|
27
|
+
help='Skip confirmation prompt'
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Legacy flags for backward compatibility
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
'--blacklist-only',
|
|
33
|
+
action='store_true',
|
|
34
|
+
help='(Legacy) Clear only blacklist entries'
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
'--exemptions-only',
|
|
38
|
+
action='store_true',
|
|
39
|
+
help='(Legacy) Clear only exemption entries'
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def handle(self, *args, **options):
|
|
43
|
+
# Parse arguments
|
|
44
|
+
blacklist_flag = options.get('blacklist', False)
|
|
45
|
+
exemptions_flag = options.get('exemptions', False)
|
|
46
|
+
keywords_flag = options.get('keywords', False)
|
|
47
|
+
confirm = options.get('confirm', False)
|
|
48
|
+
|
|
49
|
+
# Legacy support
|
|
50
|
+
blacklist_only = options.get('blacklist_only', False)
|
|
51
|
+
exemptions_only = options.get('exemptions_only', False)
|
|
52
|
+
|
|
53
|
+
# Handle legacy flags
|
|
54
|
+
if blacklist_only:
|
|
55
|
+
blacklist_flag = True
|
|
56
|
+
exemptions_flag = False
|
|
57
|
+
keywords_flag = False
|
|
58
|
+
elif exemptions_only:
|
|
59
|
+
blacklist_flag = False
|
|
60
|
+
exemptions_flag = True
|
|
61
|
+
keywords_flag = False
|
|
62
|
+
|
|
63
|
+
# If no specific flags, clear everything
|
|
64
|
+
if not (blacklist_flag or exemptions_flag or keywords_flag):
|
|
65
|
+
blacklist_flag = exemptions_flag = keywords_flag = True
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
blacklist_store = get_blacklist_store()
|
|
69
|
+
exemption_store = get_exemption_store()
|
|
70
|
+
keyword_store = get_keyword_store()
|
|
71
|
+
except Exception as e:
|
|
72
|
+
self.stdout.write(self.style.ERROR(f'Error initializing stores: {e}'))
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
# Count current entries safely
|
|
76
|
+
counts = {'blacklist': 0, 'exemptions': 0, 'keywords': 0}
|
|
77
|
+
entries = {'blacklist': [], 'exemptions': [], 'keywords': []}
|
|
78
|
+
|
|
79
|
+
if blacklist_flag:
|
|
80
|
+
try:
|
|
81
|
+
entries['blacklist'] = blacklist_store.get_all()
|
|
82
|
+
counts['blacklist'] = len(entries['blacklist'])
|
|
83
|
+
except Exception as e:
|
|
84
|
+
self.stdout.write(self.style.WARNING(f'Warning: Could not count blacklist entries: {e}'))
|
|
85
|
+
|
|
86
|
+
if exemptions_flag:
|
|
87
|
+
try:
|
|
88
|
+
entries['exemptions'] = exemption_store.get_all()
|
|
89
|
+
counts['exemptions'] = len(entries['exemptions'])
|
|
90
|
+
except Exception as e:
|
|
91
|
+
self.stdout.write(self.style.WARNING(f'Warning: Could not count exemption entries: {e}'))
|
|
92
|
+
|
|
93
|
+
if keywords_flag:
|
|
94
|
+
try:
|
|
95
|
+
entries['keywords'] = keyword_store.get_all_keywords()
|
|
96
|
+
counts['keywords'] = len(entries['keywords'])
|
|
97
|
+
except Exception as e:
|
|
98
|
+
self.stdout.write(self.style.WARNING(f'Warning: Could not count keyword entries: {e}'))
|
|
99
|
+
|
|
100
|
+
# Build action description
|
|
101
|
+
actions = []
|
|
102
|
+
if blacklist_flag:
|
|
103
|
+
actions.append(f"{counts['blacklist']} blacklist entries")
|
|
104
|
+
if exemptions_flag:
|
|
105
|
+
actions.append(f"{counts['exemptions']} exemption entries")
|
|
106
|
+
if keywords_flag:
|
|
107
|
+
actions.append(f"{counts['keywords']} learned keywords")
|
|
108
|
+
|
|
109
|
+
action = "Clear " + ", ".join(actions)
|
|
110
|
+
|
|
111
|
+
# Show what will be cleared
|
|
112
|
+
self.stdout.write(f"🔧 AI-WAF Reset: {action}")
|
|
113
|
+
|
|
114
|
+
if not confirm:
|
|
115
|
+
try:
|
|
116
|
+
response = input("Are you sure you want to proceed? [y/N]: ")
|
|
117
|
+
if response.lower() not in ['y', 'yes']:
|
|
118
|
+
self.stdout.write(self.style.WARNING('Operation cancelled'))
|
|
119
|
+
return
|
|
120
|
+
except (EOFError, KeyboardInterrupt):
|
|
121
|
+
self.stdout.write(self.style.WARNING('\nOperation cancelled'))
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
# Perform the reset
|
|
125
|
+
deleted_counts = {'blacklist': 0, 'exemptions': 0, 'keywords': 0, 'errors': []}
|
|
126
|
+
|
|
127
|
+
if blacklist_flag:
|
|
128
|
+
# Clear blacklist entries
|
|
129
|
+
try:
|
|
130
|
+
for entry in entries['blacklist']:
|
|
131
|
+
try:
|
|
132
|
+
blacklist_store.remove_ip(entry['ip_address'])
|
|
133
|
+
deleted_counts['blacklist'] += 1
|
|
134
|
+
except Exception as e:
|
|
135
|
+
deleted_counts['errors'].append(f"Error removing blacklist IP {entry.get('ip_address', 'unknown')}: {e}")
|
|
136
|
+
except Exception as e:
|
|
137
|
+
deleted_counts['errors'].append(f"Error clearing blacklist: {e}")
|
|
138
|
+
|
|
139
|
+
if exemptions_flag:
|
|
140
|
+
# Clear exemption entries
|
|
141
|
+
try:
|
|
142
|
+
for entry in entries['exemptions']:
|
|
143
|
+
try:
|
|
144
|
+
exemption_store.remove_ip(entry['ip_address'])
|
|
145
|
+
deleted_counts['exemptions'] += 1
|
|
146
|
+
except Exception as e:
|
|
147
|
+
deleted_counts['errors'].append(f"Error removing exemption IP {entry.get('ip_address', 'unknown')}: {e}")
|
|
148
|
+
except Exception as e:
|
|
149
|
+
deleted_counts['errors'].append(f"Error clearing exemptions: {e}")
|
|
150
|
+
|
|
151
|
+
if keywords_flag:
|
|
152
|
+
# Clear keyword entries
|
|
153
|
+
try:
|
|
154
|
+
for keyword in entries['keywords']:
|
|
155
|
+
try:
|
|
156
|
+
keyword_store.remove_keyword(keyword)
|
|
157
|
+
deleted_counts['keywords'] += 1
|
|
158
|
+
except Exception as e:
|
|
159
|
+
deleted_counts['errors'].append(f"Error removing keyword '{keyword}': {e}")
|
|
160
|
+
except Exception as e:
|
|
161
|
+
deleted_counts['errors'].append(f"Error clearing keywords: {e}")
|
|
162
|
+
|
|
163
|
+
# Report results
|
|
164
|
+
if deleted_counts['errors']:
|
|
165
|
+
for error in deleted_counts['errors']:
|
|
166
|
+
self.stdout.write(self.style.WARNING(f"⚠️ {error}"))
|
|
167
|
+
|
|
168
|
+
# Build success message
|
|
169
|
+
success_parts = []
|
|
170
|
+
if blacklist_flag:
|
|
171
|
+
success_parts.append(f"{deleted_counts['blacklist']} blacklist entries")
|
|
172
|
+
if exemptions_flag:
|
|
173
|
+
success_parts.append(f"{deleted_counts['exemptions']} exemption entries")
|
|
174
|
+
if keywords_flag:
|
|
175
|
+
success_parts.append(f"{deleted_counts['keywords']} learned keywords")
|
|
176
|
+
|
|
177
|
+
success_message = "✅ Reset complete: Deleted " + ", ".join(success_parts)
|
|
178
|
+
self.stdout.write(self.style.SUCCESS(success_message))
|
|
179
|
+
|
|
180
|
+
if deleted_counts['errors']:
|
|
181
|
+
self.stdout.write(
|
|
182
|
+
self.style.WARNING(f"⚠️ Completed with {len(deleted_counts['errors'])} errors (see above)")
|
|
183
|
+
)
|