aiwaf 0.1.2__tar.gz → 0.1.5__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 (30) hide show
  1. aiwaf-0.1.5/LICENSE +21 -0
  2. aiwaf-0.1.5/PKG-INFO +195 -0
  3. aiwaf-0.1.5/README.md +179 -0
  4. aiwaf-0.1.5/aiwaf/middleware.py +184 -0
  5. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf/models.py +8 -0
  6. aiwaf-0.1.5/aiwaf/trainer.py +151 -0
  7. aiwaf-0.1.5/aiwaf.egg-info/PKG-INFO +195 -0
  8. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf.egg-info/SOURCES.txt +1 -0
  9. {aiwaf-0.1.2 → aiwaf-0.1.5}/pyproject.toml +1 -1
  10. aiwaf-0.1.5/setup.py +43 -0
  11. aiwaf-0.1.2/PKG-INFO +0 -187
  12. aiwaf-0.1.2/README.md +0 -176
  13. aiwaf-0.1.2/aiwaf/middleware.py +0 -115
  14. aiwaf-0.1.2/aiwaf/trainer.py +0 -123
  15. aiwaf-0.1.2/aiwaf.egg-info/PKG-INFO +0 -187
  16. aiwaf-0.1.2/setup.py +0 -31
  17. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf/__init__.py +0 -0
  18. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf/apps.py +0 -0
  19. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf/blacklist_manager.py +0 -0
  20. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf/management/__init__.py +0 -0
  21. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf/management/commands/__init__.py +0 -0
  22. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf/management/commands/detect_and_train.py +0 -0
  23. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf/resources/model.pkl +0 -0
  24. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf/storage.py +0 -0
  25. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf/template_tags/__init__.py +0 -0
  26. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf/template_tags/aiwaf_tags.py +0 -0
  27. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf/utils.py +0 -0
  28. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf.egg-info/dependency_links.txt +0 -0
  29. {aiwaf-0.1.2 → aiwaf-0.1.5}/aiwaf.egg-info/top_level.txt +0 -0
  30. {aiwaf-0.1.2 → aiwaf-0.1.5}/setup.cfg +0 -0
aiwaf-0.1.5/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Aayush Gauba
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
aiwaf-0.1.5/PKG-INFO ADDED
@@ -0,0 +1,195 @@
1
+ Metadata-Version: 2.4
2
+ Name: aiwaf
3
+ Version: 0.1.5
4
+ Summary: AI-powered Web Application Firewall
5
+ Home-page: https://github.com/aayushgauba/aiwaf
6
+ Author: Aayush Gauba
7
+ Author-email: Aayush Gauba <gauba.aayush@gmail.com>
8
+ License: MIT
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Dynamic: author
13
+ Dynamic: home-page
14
+ Dynamic: license-file
15
+ Dynamic: requires-python
16
+
17
+
18
+ # AI‑WAF
19
+
20
+ > A self‑learning, Django‑friendly Web Application Firewall
21
+ > with rate‑limiting, anomaly detection, honeypots, UUID‑tamper protection, dynamic keyword extraction, file‑extension probing detection, and daily retraining.
22
+
23
+ ---
24
+
25
+ ## Package Structure
26
+
27
+ ```
28
+ aiwaf/
29
+ ├── __init__.py
30
+ ├── blacklist_manager.py
31
+ ├── middleware.py
32
+ ├── trainer.py # exposes train()
33
+ ├── utils.py
34
+ ├── template_tags/
35
+ │ └── aiwaf_tags.py
36
+ ├── resources/
37
+ │ ├── model.pkl # pre‑trained base model
38
+ │ └── dynamic_keywords.json # evolves daily
39
+ ├── management/
40
+ │ └── commands/
41
+ │ └── detect_and_train.py # `python manage.py detect_and_train`
42
+ └── LICENSE
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Features
48
+
49
+ - **IP Blocklist**
50
+ Instantly blocks suspicious IPs (supports CSV fallback or Django model).
51
+
52
+ - **Rate Limiting**
53
+ Sliding‑window blocks flooders (> `AIWAF_RATE_MAX` per `AIWAF_RATE_WINDOW`), then blacklists them.
54
+
55
+ - **AI Anomaly Detection**
56
+ IsolationForest on features:
57
+ - Path length
58
+ - Keyword hits (static + dynamic)
59
+ - Response time
60
+ - Status‑code index
61
+ - Burst count
62
+ - Total 404s
63
+
64
+ - **Dynamic Keyword Extraction**
65
+ Every retrain: top 10 most frequent “words” from 4xx/5xx paths are appended to your malicious keyword set.
66
+
67
+ - **File‑Extension Probing Detection**
68
+ Tracks repeated 404s on common web‑extensions (e.g. `.php`, `.asp`) and auto‑blocks after a burst.
69
+
70
+ - **Honeypot Field**
71
+ Hidden form field (via template tag) that bots fill → instant block.
72
+
73
+ - **UUID Tampering Protection**
74
+ Any `<uuid:…>` URL that doesn’t map to **any** model in its Django app gets blocked.
75
+
76
+ - **Daily Retraining**
77
+ Reads rotated/gzipped logs, auto‑blocks 404 floods (≥6), retrains the model, updates `model.pkl` + `dynamic_keywords.json`.
78
+
79
+ ---
80
+
81
+ ## Installation
82
+
83
+ ```bash
84
+ # From PyPI
85
+ pip install aiwaf
86
+
87
+ # Or for local development
88
+ git clone https://github.com/aayushgauba/aiwaf.git
89
+ cd aiwaf
90
+ pip install -e .
91
+ ```
92
+
93
+ ---
94
+
95
+ ## ⚙️ Configuration (`settings.py`)
96
+
97
+ ```python
98
+ INSTALLED_APPS += ["aiwaf"]
99
+
100
+ ### Database Setup
101
+
102
+ After adding `aiwaf` to your `INSTALLED_APPS`, create the necessary tables for the IP‐blacklist and dynamic‐keyword models:
103
+
104
+ ```bash
105
+ python manage.py makemigrations aiwaf
106
+ python manage.py migrate
107
+
108
+ # Required
109
+ AIWAF_ACCESS_LOG = "/var/log/nginx/access.log"
110
+
111
+ # Optional (defaults shown)
112
+ AIWAF_MODEL_PATH = BASE_DIR / "aiwaf" / "resources" / "model.pkl"
113
+ AIWAF_HONEYPOT_FIELD = "hp_field"
114
+ AIWAF_RATE_WINDOW = 10 # seconds
115
+ AIWAF_RATE_MAX = 20 # max reqs/window
116
+ AIWAF_RATE_FLOOD = 10 # flood threshold
117
+ AIWAF_WINDOW_SECONDS = 60 # anomaly window
118
+ AIWAF_FILE_EXTENSIONS = [".php", ".asp", ".jsp"] # 404‑burst tracked extensions
119
+ ```
120
+
121
+ > **Note:** You no longer need to define `AIWAF_MALICIOUS_KEYWORDS` or `AIWAF_STATUS_CODES` in your settings — they’re built in and evolve dynamically.
122
+
123
+ ---
124
+
125
+ ## Middleware Setup
126
+
127
+ Add in **this** order to your `MIDDLEWARE` list:
128
+
129
+ ```python
130
+ MIDDLEWARE = [
131
+ "aiwaf.middleware.IPBlockMiddleware",
132
+ "aiwaf.middleware.RateLimitMiddleware",
133
+ "aiwaf.middleware.AIAnomalyMiddleware",
134
+ "aiwaf.middleware.HoneypotMiddleware",
135
+ "aiwaf.middleware.UUIDTamperMiddleware",
136
+ # ... other middleware ...
137
+ ]
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Honeypot Field (in your template)
143
+
144
+ ```django
145
+ {% load aiwaf_tags %}
146
+
147
+ <form method="post">
148
+ {% csrf_token %}
149
+ {% honeypot_field %}
150
+ <!-- your real fields -->
151
+ </form>
152
+ ```
153
+
154
+ > Renders a hidden `<input name="hp_field" style="display:none">`.
155
+ > Any non‑empty submission → IP blacklisted.
156
+
157
+ ---
158
+
159
+ ## Running Detection & Training
160
+
161
+ ```bash
162
+ python manage.py detect_and_train
163
+ ```
164
+
165
+ **What happens:**
166
+ 1. Read access logs
167
+ 2. Auto‑block IPs with ≥ 6 total 404s
168
+ 3. Extract features & train IsolationForest
169
+ 4. Save `model.pkl`
170
+ 5. Extract top 10 dynamic keywords from 4xx/5xx
171
+
172
+ ---
173
+
174
+ ## How It Works
175
+
176
+ | Middleware | Purpose |
177
+ |------------------------------------|-----------------------------------------------------------------|
178
+ | IPAndKeywordBlockMiddleware | Blocks requests from known blacklisted IPs and Keywords |
179
+ | RateLimitMiddleware | Enforces burst & flood thresholds |
180
+ | AIAnomalyMiddleware | ML‑driven behavior analysis + block on anomaly |
181
+ | HoneypotMiddleware | Detects bots filling hidden inputs in forms |
182
+ | UUIDTamperMiddleware | Blocks guessed/nonexistent UUIDs across all models in an app |
183
+
184
+ ---
185
+
186
+ ## License
187
+
188
+ This project is licensed under the **MIT License**. See the [LICENSE](LICENSE) file for details.
189
+
190
+ ---
191
+
192
+ ## Credits
193
+
194
+ **AI‑WAF** by [Aayush Gauba](https://github.com/aayushgauba)
195
+ > “Let your firewall learn and evolve — keep your site a fortress.”
aiwaf-0.1.5/README.md ADDED
@@ -0,0 +1,179 @@
1
+
2
+ # AI‑WAF
3
+
4
+ > A self‑learning, Django‑friendly Web Application Firewall
5
+ > with rate‑limiting, anomaly detection, honeypots, UUID‑tamper protection, dynamic keyword extraction, file‑extension probing detection, and daily retraining.
6
+
7
+ ---
8
+
9
+ ## Package Structure
10
+
11
+ ```
12
+ aiwaf/
13
+ ├── __init__.py
14
+ ├── blacklist_manager.py
15
+ ├── middleware.py
16
+ ├── trainer.py # exposes train()
17
+ ├── utils.py
18
+ ├── template_tags/
19
+ │ └── aiwaf_tags.py
20
+ ├── resources/
21
+ │ ├── model.pkl # pre‑trained base model
22
+ │ └── dynamic_keywords.json # evolves daily
23
+ ├── management/
24
+ │ └── commands/
25
+ │ └── detect_and_train.py # `python manage.py detect_and_train`
26
+ └── LICENSE
27
+ ```
28
+
29
+ ---
30
+
31
+ ## Features
32
+
33
+ - **IP Blocklist**
34
+ Instantly blocks suspicious IPs (supports CSV fallback or Django model).
35
+
36
+ - **Rate Limiting**
37
+ Sliding‑window blocks flooders (> `AIWAF_RATE_MAX` per `AIWAF_RATE_WINDOW`), then blacklists them.
38
+
39
+ - **AI Anomaly Detection**
40
+ IsolationForest on features:
41
+ - Path length
42
+ - Keyword hits (static + dynamic)
43
+ - Response time
44
+ - Status‑code index
45
+ - Burst count
46
+ - Total 404s
47
+
48
+ - **Dynamic Keyword Extraction**
49
+ Every retrain: top 10 most frequent “words” from 4xx/5xx paths are appended to your malicious keyword set.
50
+
51
+ - **File‑Extension Probing Detection**
52
+ Tracks repeated 404s on common web‑extensions (e.g. `.php`, `.asp`) and auto‑blocks after a burst.
53
+
54
+ - **Honeypot Field**
55
+ Hidden form field (via template tag) that bots fill → instant block.
56
+
57
+ - **UUID Tampering Protection**
58
+ Any `<uuid:…>` URL that doesn’t map to **any** model in its Django app gets blocked.
59
+
60
+ - **Daily Retraining**
61
+ Reads rotated/gzipped logs, auto‑blocks 404 floods (≥6), retrains the model, updates `model.pkl` + `dynamic_keywords.json`.
62
+
63
+ ---
64
+
65
+ ## Installation
66
+
67
+ ```bash
68
+ # From PyPI
69
+ pip install aiwaf
70
+
71
+ # Or for local development
72
+ git clone https://github.com/aayushgauba/aiwaf.git
73
+ cd aiwaf
74
+ pip install -e .
75
+ ```
76
+
77
+ ---
78
+
79
+ ## ⚙️ Configuration (`settings.py`)
80
+
81
+ ```python
82
+ INSTALLED_APPS += ["aiwaf"]
83
+
84
+ ### Database Setup
85
+
86
+ After adding `aiwaf` to your `INSTALLED_APPS`, create the necessary tables for the IP‐blacklist and dynamic‐keyword models:
87
+
88
+ ```bash
89
+ python manage.py makemigrations aiwaf
90
+ python manage.py migrate
91
+
92
+ # Required
93
+ AIWAF_ACCESS_LOG = "/var/log/nginx/access.log"
94
+
95
+ # Optional (defaults shown)
96
+ AIWAF_MODEL_PATH = BASE_DIR / "aiwaf" / "resources" / "model.pkl"
97
+ AIWAF_HONEYPOT_FIELD = "hp_field"
98
+ AIWAF_RATE_WINDOW = 10 # seconds
99
+ AIWAF_RATE_MAX = 20 # max reqs/window
100
+ AIWAF_RATE_FLOOD = 10 # flood threshold
101
+ AIWAF_WINDOW_SECONDS = 60 # anomaly window
102
+ AIWAF_FILE_EXTENSIONS = [".php", ".asp", ".jsp"] # 404‑burst tracked extensions
103
+ ```
104
+
105
+ > **Note:** You no longer need to define `AIWAF_MALICIOUS_KEYWORDS` or `AIWAF_STATUS_CODES` in your settings — they’re built in and evolve dynamically.
106
+
107
+ ---
108
+
109
+ ## Middleware Setup
110
+
111
+ Add in **this** order to your `MIDDLEWARE` list:
112
+
113
+ ```python
114
+ MIDDLEWARE = [
115
+ "aiwaf.middleware.IPBlockMiddleware",
116
+ "aiwaf.middleware.RateLimitMiddleware",
117
+ "aiwaf.middleware.AIAnomalyMiddleware",
118
+ "aiwaf.middleware.HoneypotMiddleware",
119
+ "aiwaf.middleware.UUIDTamperMiddleware",
120
+ # ... other middleware ...
121
+ ]
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Honeypot Field (in your template)
127
+
128
+ ```django
129
+ {% load aiwaf_tags %}
130
+
131
+ <form method="post">
132
+ {% csrf_token %}
133
+ {% honeypot_field %}
134
+ <!-- your real fields -->
135
+ </form>
136
+ ```
137
+
138
+ > Renders a hidden `<input name="hp_field" style="display:none">`.
139
+ > Any non‑empty submission → IP blacklisted.
140
+
141
+ ---
142
+
143
+ ## Running Detection & Training
144
+
145
+ ```bash
146
+ python manage.py detect_and_train
147
+ ```
148
+
149
+ **What happens:**
150
+ 1. Read access logs
151
+ 2. Auto‑block IPs with ≥ 6 total 404s
152
+ 3. Extract features & train IsolationForest
153
+ 4. Save `model.pkl`
154
+ 5. Extract top 10 dynamic keywords from 4xx/5xx
155
+
156
+ ---
157
+
158
+ ## How It Works
159
+
160
+ | Middleware | Purpose |
161
+ |------------------------------------|-----------------------------------------------------------------|
162
+ | IPAndKeywordBlockMiddleware | Blocks requests from known blacklisted IPs and Keywords |
163
+ | RateLimitMiddleware | Enforces burst & flood thresholds |
164
+ | AIAnomalyMiddleware | ML‑driven behavior analysis + block on anomaly |
165
+ | HoneypotMiddleware | Detects bots filling hidden inputs in forms |
166
+ | UUIDTamperMiddleware | Blocks guessed/nonexistent UUIDs across all models in an app |
167
+
168
+ ---
169
+
170
+ ## License
171
+
172
+ This project is licensed under the **MIT License**. See the [LICENSE](LICENSE) file for details.
173
+
174
+ ---
175
+
176
+ ## Credits
177
+
178
+ **AI‑WAF** by [Aayush Gauba](https://github.com/aayushgauba)
179
+ > “Let your firewall learn and evolve — keep your site a fortress.”
@@ -0,0 +1,184 @@
1
+ # aiwaf/middleware.py
2
+
3
+ import time
4
+ import re
5
+ import os
6
+ import numpy as np
7
+ import joblib
8
+
9
+ from collections import defaultdict
10
+ from django.utils.deprecation import MiddlewareMixin
11
+ from django.http import JsonResponse
12
+ from django.conf import settings
13
+ from django.core.cache import cache
14
+ from django.db.models import F
15
+ from django.apps import apps
16
+ from django.urls import get_resolver
17
+
18
+ from .blacklist_manager import BlacklistManager
19
+ from .models import DynamicKeyword
20
+
21
+ MODEL_PATH = getattr(
22
+ settings,
23
+ "AIWAF_MODEL_PATH",
24
+ os.path.join(os.path.dirname(__file__), "resources", "model.pkl")
25
+ )
26
+ MODEL = joblib.load(MODEL_PATH)
27
+
28
+ STATIC_KW = getattr(
29
+ settings,
30
+ "AIWAF_MALICIOUS_KEYWORDS",
31
+ [
32
+ ".php", "xmlrpc", "wp-", ".env", ".git", ".bak",
33
+ "conflg", "shell", "filemanager"
34
+ ]
35
+ )
36
+
37
+ def get_ip(request):
38
+ xff = request.META.get("HTTP_X_FORWARDED_FOR")
39
+ if xff:
40
+ return xff.split(",")[0].strip()
41
+ return request.META.get("REMOTE_ADDR", "")
42
+
43
+ class IPAndKeywordBlockMiddleware:
44
+ def __init__(self, get_response):
45
+ self.get_response = get_response
46
+ self.url_patterns = self._collect_view_paths()
47
+
48
+ def _collect_view_paths(self):
49
+ resolver = get_resolver()
50
+ patterns = set()
51
+
52
+ def extract(patterns_list, prefix=""):
53
+ for p in patterns_list:
54
+ if hasattr(p, "url_patterns"):
55
+ extract(p.url_patterns, prefix + str(p.pattern))
56
+ else:
57
+ pat = (prefix + str(p.pattern)).strip("^$")
58
+ patterns.add(pat)
59
+ extract(resolver.url_patterns)
60
+ return patterns
61
+
62
+ def __call__(self, request):
63
+ ip = get_ip(request)
64
+ path = request.path.lower()
65
+ if BlacklistManager.is_blocked(ip):
66
+ return JsonResponse({"error": "blocked"}, status=403)
67
+ segments = [seg for seg in re.split(r"\W+", path) if len(seg) > 3]
68
+ for seg in segments:
69
+ obj, _ = DynamicKeyword.objects.get_or_create(keyword=seg)
70
+ DynamicKeyword.objects.filter(pk=obj.pk).update(count=F("count") + 1)
71
+ dynamic_top = list(
72
+ DynamicKeyword.objects
73
+ .order_by("-count")
74
+ .values_list("keyword", flat=True)[: getattr(settings, "AIWAF_DYNAMIC_TOP_N", 10)]
75
+ )
76
+ all_kw = set(STATIC_KW) | set(dynamic_top)
77
+ safe_kw = {kw for kw in all_kw if any(kw in pat for pat in self.url_patterns)}
78
+ suspicious_kw = all_kw - safe_kw
79
+ for seg in segments:
80
+ if seg in suspicious_kw:
81
+ BlacklistManager.block(ip, f"Keyword block: {seg}")
82
+ return JsonResponse({"error": "blocked"}, status=403)
83
+ return self.get_response(request)
84
+
85
+
86
+ class RateLimitMiddleware:
87
+ WINDOW = 10
88
+ MAX = 20
89
+ FLOOD = 10
90
+
91
+ def __init__(self, get_response):
92
+ self.get_response = get_response
93
+ self.logs = defaultdict(list)
94
+
95
+ def __call__(self, request):
96
+ ip = get_ip(request)
97
+ now = time.time()
98
+ recs = [t for t in self.logs[ip] if now - t < self.WINDOW]
99
+ recs.append(now)
100
+ self.logs[ip] = recs
101
+
102
+ if len(recs) > self.MAX:
103
+ return JsonResponse({"error": "too_many_requests"}, status=429)
104
+ if len(recs) > self.FLOOD:
105
+ BlacklistManager.block(ip, "Flood pattern")
106
+ return JsonResponse({"error": "blocked"}, status=403)
107
+
108
+ return self.get_response(request)
109
+
110
+
111
+ class AIAnomalyMiddleware(MiddlewareMixin):
112
+ WINDOW = getattr(settings, "AIWAF_WINDOW_SECONDS", 60)
113
+ TOP_N = getattr(settings, "AIWAF_DYNAMIC_TOP_N", 10)
114
+
115
+ def process_request(self, request):
116
+ ip = get_ip(request)
117
+ if BlacklistManager.is_blocked(ip):
118
+ return JsonResponse({"error": "blocked"}, status=403)
119
+
120
+ now = time.time()
121
+ key = f"aiwaf:{ip}"
122
+ data = cache.get(key, [])
123
+ # TODO: you may want to capture real status & response_time in process_response
124
+ data.append((now, request.path, 0, 0.0))
125
+ data = [d for d in data if now - d[0] < self.WINDOW]
126
+ cache.set(key, data, timeout=self.WINDOW)
127
+
128
+ # update dynamic‐keyword counts
129
+ for seg in re.split(r"\W+", request.path.lower()):
130
+ if len(seg) > 3:
131
+ obj, _ = DynamicKeyword.objects.get_or_create(keyword=seg)
132
+ DynamicKeyword.objects.filter(pk=obj.pk).update(count=F("count") + 1)
133
+
134
+ if len(data) < 5:
135
+ return None
136
+
137
+ # pull top‐N dynamic tokens
138
+ top_dynamic = list(
139
+ DynamicKeyword.objects
140
+ .order_by("-count")
141
+ .values_list("keyword", flat=True)[: self.TOP_N]
142
+ )
143
+ ALL_KW = set(STATIC_KW) | set(top_dynamic)
144
+
145
+ total = len(data)
146
+ ratio404 = sum(1 for (_, _, st, _) in data if st == 404) / total
147
+ hits = sum(any(kw in path.lower() for kw in ALL_KW) for (_, path, _, _) in data)
148
+ avg_rt = np.mean([rt for (_, _, _, rt) in data]) if data else 0.0
149
+ ivs = [data[i][0] - data[i - 1][0] for i in range(1, total)]
150
+ avg_iv = np.mean(ivs) if ivs else 0.0
151
+
152
+ X = np.array([[total, ratio404, hits, avg_rt, avg_iv]], dtype=float)
153
+ if MODEL.predict(X)[0] == -1:
154
+ BlacklistManager.block(ip, "AI anomaly")
155
+ return JsonResponse({"error": "blocked"}, status=403)
156
+
157
+ return None
158
+
159
+
160
+ class HoneypotMiddleware(MiddlewareMixin):
161
+ def process_view(self, request, view_func, view_args, view_kwargs):
162
+ trap = request.POST.get(getattr(settings, "AIWAF_HONEYPOT_FIELD", "hp_field"), "")
163
+ if trap:
164
+ ip = get_ip(request)
165
+ BlacklistManager.block(ip, "HONEYPOT triggered")
166
+ return JsonResponse({"error": "bot_detected"}, status=403)
167
+ return None
168
+
169
+
170
+ class UUIDTamperMiddleware(MiddlewareMixin):
171
+ def process_view(self, request, view_func, view_args, view_kwargs):
172
+ uid = view_kwargs.get("uuid")
173
+ if not uid:
174
+ return None
175
+
176
+ ip = get_ip(request)
177
+ app_label = view_func.__module__.split(".")[0]
178
+ app_cfg = apps.get_app_config(app_label)
179
+ for Model in app_cfg.get_models():
180
+ if Model.objects.filter(pk=uid).exists():
181
+ return None
182
+
183
+ BlacklistManager.block(ip, "UUID tampering")
184
+ return JsonResponse({"error": "blocked"}, status=403)
@@ -26,3 +26,11 @@ class BlacklistEntry(models.Model):
26
26
 
27
27
  def __str__(self):
28
28
  return f"{self.ip_address} ({self.reason})"
29
+
30
+ class DynamicKeyword(models.Model):
31
+ keyword = models.CharField(max_length=100, unique=True)
32
+ count = models.PositiveIntegerField(default=0)
33
+ last_updated = models.DateTimeField(auto_now=True)
34
+
35
+ class Meta:
36
+ ordering = ['-count']