aiwaf 0.1.8.2__py3-none-any.whl → 0.1.8.4__py3-none-any.whl

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/middleware.py CHANGED
@@ -22,10 +22,28 @@ def is_ip_exempted(ip):
22
22
 
23
23
  def is_exempt_path(path):
24
24
  path = path.lower()
25
+
26
+ # Default login paths that should always be exempt
27
+ default_login_paths = [
28
+ "/admin/login/",
29
+ "/admin/",
30
+ "/login/",
31
+ "/accounts/login/",
32
+ "/auth/login/",
33
+ "/signin/",
34
+ ]
35
+
36
+ # Check default login paths
37
+ for login_path in default_login_paths:
38
+ if path.startswith(login_path):
39
+ return True
40
+
41
+ # Check user-configured exempt paths
25
42
  exempt_paths = getattr(settings, "AIWAF_EXEMPT_PATHS", [])
26
43
  for exempt in exempt_paths:
27
44
  if path == exempt or path.startswith(exempt.rstrip("/") + "/"):
28
45
  return True
46
+
29
47
  return False
30
48
 
31
49
  MODEL_PATH = getattr(
aiwaf/trainer.py CHANGED
@@ -34,6 +34,23 @@ IPExemption = apps.get_model("aiwaf", "IPExemption")
34
34
 
35
35
  def is_exempt_path(path: str) -> bool:
36
36
  path = path.lower()
37
+
38
+ # Default login paths that should always be exempt
39
+ default_login_paths = [
40
+ "/admin/login/",
41
+ "/admin/",
42
+ "/login/",
43
+ "/accounts/login/",
44
+ "/auth/login/",
45
+ "/signin/",
46
+ ]
47
+
48
+ # Check default login paths
49
+ for login_path in default_login_paths:
50
+ if path.startswith(login_path):
51
+ return True
52
+
53
+ # Check user-configured exempt paths
37
54
  for exempt in getattr(settings, "AIWAF_EXEMPT_PATHS", []):
38
55
  if path == exempt or path.startswith(exempt.rstrip("/") + "/"):
39
56
  return True
@@ -116,6 +133,7 @@ def train() -> None:
116
133
 
117
134
  parsed = []
118
135
  ip_404 = defaultdict(int)
136
+ ip_404_login = defaultdict(int) # Track 404s on login paths separately
119
137
  ip_times = defaultdict(list)
120
138
 
121
139
  for line in raw_lines:
@@ -125,15 +143,24 @@ def train() -> None:
125
143
  parsed.append(rec)
126
144
  ip_times[rec["ip"]].append(rec["timestamp"])
127
145
  if rec["status"] == "404":
128
- ip_404[rec["ip"]] += 1
146
+ if is_exempt_path(rec["path"]):
147
+ ip_404_login[rec["ip"]] += 1 # Login path 404s
148
+ else:
149
+ ip_404[rec["ip"]] += 1 # Non-login path 404s
129
150
 
130
- # 3. Optional immediate 404‐flood blocking
151
+ # 3. Optional immediate 404‐flood blocking (only for non-login paths)
131
152
  for ip, count in ip_404.items():
132
153
  if count >= 6:
133
- BlacklistEntry.objects.get_or_create(
134
- ip_address=ip,
135
- defaults={"reason": "Excessive 404s (≥6)"}
136
- )
154
+ # Only block if they have significant non-login 404s
155
+ login_404s = ip_404_login.get(ip, 0)
156
+ total_404s = count + login_404s
157
+
158
+ # Don't block if majority of 404s are on login paths
159
+ if count > login_404s: # More non-login 404s than login 404s
160
+ BlacklistEntry.objects.get_or_create(
161
+ ip_address=ip,
162
+ defaults={"reason": f"Excessive 404s (≥6 non-login, {count}/{total_404s})"}
163
+ )
137
164
 
138
165
  feature_dicts = []
139
166
  for r in parsed:
@@ -167,7 +194,10 @@ def train() -> None:
167
194
  df = pd.DataFrame(feature_dicts)
168
195
  feature_cols = [c for c in df.columns if c != "ip"]
169
196
  X = df[feature_cols].astype(float).values
170
- model = IsolationForest(contamination=0.01, random_state=42)
197
+ model = IsolationForest(
198
+ contamination=getattr(settings, "AIWAF_AI_CONTAMINATION", 0.05),
199
+ random_state=42
200
+ )
171
201
  model.fit(X)
172
202
 
173
203
  os.makedirs(os.path.dirname(MODEL_PATH), exist_ok=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiwaf
3
- Version: 0.1.8.2
3
+ Version: 0.1.8.4
4
4
  Summary: AI-powered Web Application Firewall
5
5
  Home-page: https://github.com/aayushgauba/aiwaf
6
6
  Author: Aayush Gauba
@@ -89,7 +89,17 @@ aiwaf/
89
89
  **Exempt Path & IP Awareness**
90
90
 
91
91
  **Exempt Paths:**
92
- Set `AIWAF_EXEMPT_PATHS` in your Django `settings.py` (not in your code). Fully respects this setting across all modules exempt paths are:
92
+ 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`:
93
+
94
+ ```python
95
+ AIWAF_EXEMPT_PATHS = [
96
+ "/api/webhooks/",
97
+ "/health/",
98
+ "/special-endpoint/",
99
+ ]
100
+ ```
101
+
102
+ All exempt paths are:
93
103
  - Skipped from keyword learning
94
104
  - Immune to AI blocking
95
105
  - Ignored in log training
@@ -164,6 +174,7 @@ AIWAF_ACCESS_LOG = "/var/log/nginx/access.log"
164
174
  ```python
165
175
  AIWAF_MODEL_PATH = BASE_DIR / "aiwaf" / "resources" / "model.pkl"
166
176
  AIWAF_MIN_FORM_TIME = 1.0 # minimum seconds between GET and POST
177
+ AIWAF_AI_CONTAMINATION = 0.05 # AI anomaly detection sensitivity (5%)
167
178
  AIWAF_RATE_WINDOW = 10 # seconds
168
179
  AIWAF_RATE_MAX = 20 # max requests per window
169
180
  AIWAF_RATE_FLOOD = 10 # flood threshold
@@ -1,10 +1,10 @@
1
1
  aiwaf/__init__.py,sha256=nQFpJ1YpX48snzLjEQCf8zD2YNh8v0b_kPTrXx8uBYc,46
2
2
  aiwaf/apps.py,sha256=nCez-Ptlv2kaEk5HenA8b1pATz1VfhrHP1344gwcY1A,142
3
3
  aiwaf/blacklist_manager.py,sha256=sM6uTH7zD6MOPGb0kzqV2aFut2vxKgft_UVeRJr7klw,392
4
- aiwaf/middleware.py,sha256=0tmOTErZBy524O6gtKKo4liWYiovAVMgxEk91L-ngZk,9246
4
+ aiwaf/middleware.py,sha256=3zFW0hGPpNti2_6Vbamw8m8YiqcJ2jC5sygWNxJPsMU,9670
5
5
  aiwaf/models.py,sha256=XaG1pd_oZu3y-fw66u4wblGlWcUY9gvsTNKGD0kQk7Y,1672
6
6
  aiwaf/storage.py,sha256=bxCILzzvA1-q6nwclRE8WrfoRhe25H4VrsQDf0hl_lY,1903
7
- aiwaf/trainer.py,sha256=Xs_AuA7RCa1oyo5-lJYlnRYUiaq-HY2KXAviAdiGnzU,6217
7
+ aiwaf/trainer.py,sha256=IUPCGVe0RT6sRHecBeNZuKop2d6APzXlvv3nirgKQLI,7319
8
8
  aiwaf/utils.py,sha256=RkEUWhhHy6tOk7V0UYv3cN4xhOR_7aBy9bjhwuV2cdA,1436
9
9
  aiwaf/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  aiwaf/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -14,8 +14,8 @@ aiwaf/management/commands/detect_and_train.py,sha256=-o-LZ7QZ5GeJPCekryox1DGXKMm
14
14
  aiwaf/resources/model.pkl,sha256=5t6h9BX8yoh2xct85MXOO60jdlWyg1APskUOW0jZE1Y,1288265
15
15
  aiwaf/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  aiwaf/templatetags/aiwaf_tags.py,sha256=XXfb7Tl4DjU3Sc40GbqdaqOEtKTUKELBEk58u83wBNw,357
17
- aiwaf-0.1.8.2.dist-info/licenses/LICENSE,sha256=Ir8PX4dxgAcdB0wqNPIkw84fzIIRKE75NoUil9RX0QU,1069
18
- aiwaf-0.1.8.2.dist-info/METADATA,sha256=Q93RjqYRrVZ9PwQCHVF2LNgM5bvY1TZxnIgnbPV29-c,7178
19
- aiwaf-0.1.8.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
- aiwaf-0.1.8.2.dist-info/top_level.txt,sha256=kU6EyjobT6UPCxuWpI_BvcHDG0I2tMgKaPlWzVxe2xI,6
21
- aiwaf-0.1.8.2.dist-info/RECORD,,
17
+ aiwaf-0.1.8.4.dist-info/licenses/LICENSE,sha256=Ir8PX4dxgAcdB0wqNPIkw84fzIIRKE75NoUil9RX0QU,1069
18
+ aiwaf-0.1.8.4.dist-info/METADATA,sha256=abO7KBH2-WR5UDKt_bawhvXohcIToLXKQm_J33uwjRY,7435
19
+ aiwaf-0.1.8.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
+ aiwaf-0.1.8.4.dist-info/top_level.txt,sha256=kU6EyjobT6UPCxuWpI_BvcHDG0I2tMgKaPlWzVxe2xI,6
21
+ aiwaf-0.1.8.4.dist-info/RECORD,,