aiwaf 0.1.9.2.7__py3-none-any.whl → 0.1.9.2.9__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/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  default_app_config = "aiwaf.apps.AiwafConfig"
2
2
 
3
- __version__ = "0.1.9.2.7"
3
+ __version__ = "0.1.9.2.9"
4
4
 
5
5
  # Note: Middleware classes are available from aiwaf.middleware
6
6
  # Import them only when needed to avoid circular imports during Django app loading
@@ -4,7 +4,19 @@ from aiwaf.trainer import train
4
4
  class Command(BaseCommand):
5
5
  help = "Run AI‑WAF detect & retrain"
6
6
 
7
+ def add_arguments(self, parser):
8
+ parser.add_argument(
9
+ '--disable-ai',
10
+ action='store_true',
11
+ help='Disable AI model training, only perform keyword learning'
12
+ )
13
+
7
14
  def handle(self, *args, **options):
8
- train()
15
+ disable_ai = options.get('disable_ai', False)
16
+
17
+ if disable_ai:
18
+ self.stdout.write(self.style.WARNING("AI model training disabled - keyword learning only"))
19
+
20
+ train(disable_ai=disable_ai)
9
21
  self.stdout.write(self.style.SUCCESS("Done."))
10
22
 
@@ -11,6 +11,11 @@ class Command(BaseCommand):
11
11
  action='store_true',
12
12
  help='Force regeneration even if model exists',
13
13
  )
14
+ parser.add_argument(
15
+ '--disable-ai',
16
+ action='store_true',
17
+ help='Disable AI model training, only perform keyword learning'
18
+ )
14
19
 
15
20
  def handle(self, *args, **options):
16
21
  self.stdout.write(self.style.HTTP_INFO("🔄 AI-WAF Model Regeneration"))
@@ -58,16 +63,28 @@ class Command(BaseCommand):
58
63
  self.stdout.write("Regenerating model to fix version compatibility...")
59
64
 
60
65
  # Regenerate model
61
- self.stdout.write("🚀 Starting model training...")
66
+ disable_ai = options.get('disable_ai', False)
67
+
68
+ if disable_ai:
69
+ self.stdout.write("� AI model training disabled - keyword learning only")
70
+ self.stdout.write("🚀 Starting keyword training...")
71
+ else:
72
+ self.stdout.write("�🚀 Starting model training...")
62
73
 
63
74
  try:
64
75
  from aiwaf.trainer import train
65
- train()
66
- self.stdout.write("")
67
- self.stdout.write(self.style.SUCCESS("✅ Model regenerated successfully!"))
76
+ train(disable_ai=disable_ai)
68
77
  self.stdout.write("")
69
- self.stdout.write("The model is now compatible with your current scikit-learn version.")
70
- self.stdout.write("Version warnings should no longer appear.")
78
+
79
+ if disable_ai:
80
+ self.stdout.write(self.style.SUCCESS("✅ Keyword training completed successfully!"))
81
+ self.stdout.write("")
82
+ self.stdout.write("Keyword-based protection is now active.")
83
+ else:
84
+ self.stdout.write(self.style.SUCCESS("✅ Model regenerated successfully!"))
85
+ self.stdout.write("")
86
+ self.stdout.write("The model is now compatible with your current scikit-learn version.")
87
+ self.stdout.write("Version warnings should no longer appear.")
71
88
 
72
89
  except Exception as e:
73
90
  self.stdout.write("")
aiwaf/middleware.py CHANGED
@@ -32,6 +32,12 @@ def load_model_safely():
32
32
  import warnings
33
33
  import sklearn
34
34
 
35
+ # Check if AI is disabled globally
36
+ ai_disabled = getattr(settings, "AIWAF_DISABLE_AI", False)
37
+ if ai_disabled:
38
+ print("AI functionality disabled via AIWAF_DISABLE_AI setting")
39
+ return None
40
+
35
41
  try:
36
42
  # Suppress sklearn version warnings temporarily
37
43
  with warnings.catch_warnings():
@@ -46,13 +52,13 @@ def load_model_safely():
46
52
  current_version = sklearn.__version__
47
53
 
48
54
  if stored_version != current_version:
49
- print(f"ℹ️ Model was trained with sklearn v{stored_version}, current v{current_version}")
55
+ print(f"Model was trained with sklearn v{stored_version}, current v{current_version}")
50
56
  print(" Run 'python manage.py detect_and_train' to update model if needed.")
51
57
 
52
58
  return model
53
59
  else:
54
60
  # Old format - direct model object
55
- print("ℹ️ Using legacy model format. Consider retraining for better compatibility.")
61
+ print("Using legacy model format. Consider retraining for better compatibility.")
56
62
  return model_data
57
63
 
58
64
  except Exception as e:
aiwaf/storage.py CHANGED
@@ -2,22 +2,43 @@ import numpy as np
2
2
  import pandas as pd
3
3
  from django.conf import settings
4
4
  from django.utils import timezone
5
+ import os
6
+ import json
7
+ from collections import defaultdict
5
8
 
6
9
  # Defer model imports to avoid AppRegistryNotReady during Django app loading
7
10
  FeatureSample = BlacklistEntry = IPExemption = DynamicKeyword = None
8
11
 
12
+ # Fallback storage for when Django models are unavailable
13
+ _fallback_keywords = defaultdict(int)
14
+ _fallback_storage_path = os.path.join(os.path.dirname(__file__), 'fallback_keywords.json')
15
+
9
16
  def _import_models():
10
17
  """Import Django models only when needed and apps are ready."""
11
18
  global FeatureSample, BlacklistEntry, IPExemption, DynamicKeyword
12
19
 
13
20
  if FeatureSample is not None:
14
21
  return # Already imported
15
-
16
22
  try:
17
23
  from django.apps import apps
18
- if apps.ready and apps.is_installed('aiwaf'):
19
- from .models import FeatureSample, BlacklistEntry, IPExemption, DynamicKeyword
20
- except (ImportError, RuntimeError, Exception):
24
+ if apps.ready:
25
+ # Try multiple ways to import models
26
+ try:
27
+ # First try: direct import (most reliable)
28
+ from .models import FeatureSample, BlacklistEntry, IPExemption, DynamicKeyword
29
+ except ImportError:
30
+ # Second try: check if aiwaf app is installed under different name
31
+ for app_config in apps.get_app_configs():
32
+ if 'aiwaf' in app_config.name.lower() or 'aiwaf' in app_config.label.lower():
33
+ try:
34
+ from .models import FeatureSample, BlacklistEntry, IPExemption, DynamicKeyword
35
+ break
36
+ except ImportError:
37
+ continue
38
+ except (ImportError, RuntimeError, Exception) as e:
39
+ # Log the error for debugging but don't fail silently
40
+ import sys
41
+ print(f"Warning: Could not import AIWAF models: {e}", file=sys.stderr)
21
42
  # Keep models as None if can't import
22
43
  pass
23
44
 
@@ -62,7 +83,6 @@ class ModelFeatureStore:
62
83
  if df.empty:
63
84
  return df
64
85
 
65
- # Ensure proper column order and types
66
86
  feature_cols = ['path_len', 'kw_hits', 'resp_time', 'status_idx', 'burst_count', 'total_404']
67
87
  for col in feature_cols:
68
88
  if col in df.columns:
@@ -242,11 +262,40 @@ class ModelExemptionStore:
242
262
  return 0
243
263
 
244
264
  class ModelKeywordStore:
265
+ @staticmethod
266
+ def _load_fallback_keywords():
267
+ """Load keywords from fallback JSON file"""
268
+ global _fallback_keywords
269
+ try:
270
+ if os.path.exists(_fallback_storage_path):
271
+ with open(_fallback_storage_path, 'r') as f:
272
+ data = json.load(f)
273
+ _fallback_keywords = defaultdict(int, data)
274
+ except Exception as e:
275
+ import sys
276
+ print(f"Warning: Could not load fallback keywords: {e}", file=sys.stderr)
277
+
278
+ @staticmethod
279
+ def _save_fallback_keywords():
280
+ """Save keywords to fallback JSON file"""
281
+ try:
282
+ with open(_fallback_storage_path, 'w') as f:
283
+ json.dump(dict(_fallback_keywords), f, indent=2)
284
+ except Exception as e:
285
+ import sys
286
+ print(f"Warning: Could not save fallback keywords: {e}", file=sys.stderr)
287
+
245
288
  @staticmethod
246
289
  def add_keyword(keyword, count=1):
247
290
  """Add a keyword to the dynamic keyword list"""
248
291
  _import_models()
249
292
  if DynamicKeyword is None:
293
+ # Use fallback storage when Django models not available
294
+ ModelKeywordStore._load_fallback_keywords()
295
+ _fallback_keywords[keyword] += count
296
+ ModelKeywordStore._save_fallback_keywords()
297
+ import sys
298
+ print(f"Info: Using fallback storage for keyword '{keyword}' - Django models not available.", file=sys.stderr)
250
299
  return
251
300
  try:
252
301
  obj, created = DynamicKeyword.objects.get_or_create(keyword=keyword)
@@ -257,7 +306,12 @@ class ModelKeywordStore:
257
306
  obj.count = count
258
307
  obj.save()
259
308
  except Exception as e:
260
- print(f"Error adding keyword {keyword}: {e}")
309
+ # Fallback to file storage on database error
310
+ ModelKeywordStore._load_fallback_keywords()
311
+ _fallback_keywords[keyword] += count
312
+ ModelKeywordStore._save_fallback_keywords()
313
+ import sys
314
+ print(f"Database error adding keyword {keyword}, using fallback storage: {e}", file=sys.stderr)
261
315
 
262
316
  @staticmethod
263
317
  def remove_keyword(keyword):
@@ -275,14 +329,22 @@ class ModelKeywordStore:
275
329
  """Get top N keywords by count"""
276
330
  _import_models()
277
331
  if DynamicKeyword is None:
278
- return []
332
+ # Use fallback storage
333
+ ModelKeywordStore._load_fallback_keywords()
334
+ sorted_keywords = sorted(_fallback_keywords.items(), key=lambda x: x[1], reverse=True)
335
+ return [keyword for keyword, count in sorted_keywords[:n]]
279
336
  try:
280
337
  return list(
281
338
  DynamicKeyword.objects.order_by('-count')[:n]
282
339
  .values_list('keyword', flat=True)
283
340
  )
284
- except Exception:
285
- return []
341
+ except Exception as e:
342
+ # Fallback to file storage on database error
343
+ ModelKeywordStore._load_fallback_keywords()
344
+ sorted_keywords = sorted(_fallback_keywords.items(), key=lambda x: x[1], reverse=True)
345
+ import sys
346
+ print(f"Database error getting top keywords, using fallback storage: {e}", file=sys.stderr)
347
+ return [keyword for keyword, count in sorted_keywords[:n]]
286
348
 
287
349
  @staticmethod
288
350
  def get_all_keywords():
@@ -308,6 +370,18 @@ class ModelKeywordStore:
308
370
  except Exception as e:
309
371
  print(f"Error resetting keywords: {e}")
310
372
 
373
+ def add_keyword_for_route(self, route, keyword, count=1):
374
+ """Add a keyword for a specific route (fallback method)"""
375
+ # For now, just use the general add_keyword method
376
+ # In a full implementation, this would handle route-specific storage
377
+ return ModelKeywordStore.add_keyword(keyword, count)
378
+
379
+ def get_keywords_for_route(self, route):
380
+ """Get keywords for a specific route (fallback method)"""
381
+ # For now, return all keywords
382
+ # In a full implementation, this would return route-specific keywords
383
+ return ModelKeywordStore.get_all_keywords()
384
+
311
385
  # Factory functions that only return Django model stores
312
386
  def get_feature_store():
313
387
  """Get the feature store (Django models only)"""
aiwaf/trainer.py CHANGED
@@ -3,13 +3,10 @@ import glob
3
3
  import gzip
4
4
  import re
5
5
  import joblib
6
-
7
6
  from datetime import datetime
8
7
  from collections import defaultdict, Counter
9
-
10
8
  import pandas as pd
11
9
  from sklearn.ensemble import IsolationForest
12
-
13
10
  from django.conf import settings
14
11
  from django.apps import apps
15
12
  from django.db.models import F
@@ -409,9 +406,16 @@ def _is_malicious_context_trainer(path: str, keyword: str, status: str = "404")
409
406
  return any(malicious_indicators)
410
407
 
411
408
 
412
- def train() -> None:
413
- """Enhanced training with improved keyword filtering and exemption handling"""
414
- print("🚀 Starting AIWAF enhanced training...")
409
+ def train(disable_ai=False) -> None:
410
+ """Enhanced training with improved keyword filtering and exemption handling
411
+
412
+ Args:
413
+ disable_ai (bool): If True, skip AI model training and only do keyword learning
414
+ """
415
+ print("Starting AIWAF enhanced training...")
416
+
417
+ if disable_ai:
418
+ print("AI model training disabled - keyword learning only")
415
419
 
416
420
  # Remove exempt keywords first
417
421
  remove_exempt_keywords()
@@ -421,7 +425,7 @@ def train() -> None:
421
425
 
422
426
  exempted_ips = [entry['ip_address'] for entry in exemption_store.get_all()]
423
427
  if exempted_ips:
424
- print(f"🛡️ Found {len(exempted_ips)} exempted IPs - clearing from blacklist")
428
+ print(f"Found {len(exempted_ips)} exempted IPs - clearing from blacklist")
425
429
  for ip in exempted_ips:
426
430
  BlacklistManager.unblock(ip)
427
431
 
@@ -484,85 +488,100 @@ def train() -> None:
484
488
  })
485
489
 
486
490
  if not feature_dicts:
487
- print("⚠️ Nothing to train on – no valid log entries.")
491
+ print(" Nothing to train on – no valid log entries.")
488
492
  return
489
493
 
490
- df = pd.DataFrame(feature_dicts)
491
- feature_cols = [c for c in df.columns if c != "ip"]
492
- X = df[feature_cols].astype(float).values
493
- model = IsolationForest(
494
- contamination=getattr(settings, "AIWAF_AI_CONTAMINATION", 0.05),
495
- random_state=42
496
- )
497
-
498
- # Suppress sklearn warnings during training
499
- import warnings
500
- with warnings.catch_warnings():
501
- warnings.filterwarnings("ignore", category=UserWarning, module="sklearn")
502
- model.fit(X)
503
-
504
- os.makedirs(os.path.dirname(MODEL_PATH), exist_ok=True)
505
-
506
- # Save model with version metadata
507
- import sklearn
508
- from django.utils import timezone as django_timezone
509
- model_data = {
510
- 'model': model,
511
- 'sklearn_version': sklearn.__version__,
512
- 'created_at': str(django_timezone.now()),
513
- 'feature_count': len(feature_cols),
514
- 'samples_count': len(X)
515
- }
516
- joblib.dump(model_data, MODEL_PATH)
517
- print(f"✅ Model trained on {len(X)} samples → {MODEL_PATH}")
518
- print(f"📦 Created with scikit-learn v{sklearn.__version__}")
519
-
520
- # Check for anomalies and intelligently decide which IPs to block
521
- preds = model.predict(X)
522
- anomalous_ips = set(df.loc[preds == -1, "ip"])
523
-
524
- if anomalous_ips:
525
- print(f"⚠️ Detected {len(anomalous_ips)} potentially anomalous IPs during training")
526
-
527
- exemption_store = get_exemption_store()
528
- blacklist_store = get_blacklist_store()
529
- blocked_count = 0
494
+ # AI Model Training (optional)
495
+ blocked_count = 0
496
+ if not disable_ai:
497
+ print(" Training AI anomaly detection model...")
530
498
 
531
- for ip in anomalous_ips:
532
- # Skip if IP is exempted
533
- if exemption_store.is_exempted(ip):
534
- continue
499
+ try:
500
+ df = pd.DataFrame(feature_dicts)
501
+ feature_cols = [c for c in df.columns if c != "ip"]
502
+ X = df[feature_cols].astype(float).values
503
+ model = IsolationForest(
504
+ contamination=getattr(settings, "AIWAF_AI_CONTAMINATION", 0.05),
505
+ random_state=42
506
+ )
535
507
 
536
- # Get this IP's behavior from the data
537
- ip_data = df[df["ip"] == ip]
508
+ # Suppress sklearn warnings during training
509
+ import warnings
510
+ with warnings.catch_warnings():
511
+ warnings.filterwarnings("ignore", category=UserWarning, module="sklearn")
512
+ model.fit(X)
513
+
514
+ os.makedirs(os.path.dirname(MODEL_PATH), exist_ok=True)
538
515
 
539
- # Criteria to determine if this is likely a legitimate user vs threat:
540
- avg_kw_hits = ip_data["kw_hits"].mean()
541
- max_404s = ip_data["total_404"].max()
542
- avg_burst = ip_data["burst_count"].mean()
543
- total_requests = len(ip_data)
516
+ # Save model with version metadata
517
+ import sklearn
518
+ from django.utils import timezone as django_timezone
519
+ model_data = {
520
+ 'model': model,
521
+ 'sklearn_version': sklearn.__version__,
522
+ 'created_at': str(django_timezone.now()),
523
+ 'feature_count': len(feature_cols),
524
+ 'samples_count': len(X)
525
+ }
526
+ joblib.dump(model_data, MODEL_PATH)
527
+ print(f"Model trained on {len(X)} samples → {MODEL_PATH}")
528
+ print(f"Created with scikit-learn v{sklearn.__version__}")
544
529
 
545
- # Don't block if it looks like legitimate behavior:
546
- if (
547
- avg_kw_hits < 2 and # Not hitting many malicious keywords
548
- max_404s < 10 and # Not excessive 404s
549
- avg_burst < 15 and # Not excessive burst activity
550
- total_requests < 100 # Not excessive total requests
551
- ):
552
- print(f" - {ip}: Anomalous but looks legitimate (kw:{avg_kw_hits:.1f}, 404s:{max_404s}, burst:{avg_burst:.1f}) - NOT blocking")
553
- continue
530
+ # Check for anomalies and intelligently decide which IPs to block
531
+ preds = model.predict(X)
532
+ anomalous_ips = set(df.loc[preds == -1, "ip"])
554
533
 
555
- # Block if it shows clear signs of malicious behavior
556
- BlacklistManager.block(ip, f"AI anomaly + suspicious patterns (kw:{avg_kw_hits:.1f}, 404s:{max_404s}, burst:{avg_burst:.1f})")
557
- blocked_count += 1
558
- print(f" - {ip}: Blocked for suspicious behavior (kw:{avg_kw_hits:.1f}, 404s:{max_404s}, burst:{avg_burst:.1f})")
534
+ if anomalous_ips:
535
+ print(f"Detected {len(anomalous_ips)} potentially anomalous IPs during training")
536
+
537
+ exemption_store = get_exemption_store()
538
+ blacklist_store = get_blacklist_store()
539
+
540
+ for ip in anomalous_ips:
541
+ # Skip if IP is exempted
542
+ if exemption_store.is_exempted(ip):
543
+ continue
544
+
545
+ # Get this IP's behavior from the data
546
+ ip_data = df[df["ip"] == ip]
547
+
548
+ # Criteria to determine if this is likely a legitimate user vs threat:
549
+ avg_kw_hits = ip_data["kw_hits"].mean()
550
+ max_404s = ip_data["total_404"].max()
551
+ avg_burst = ip_data["burst_count"].mean()
552
+ total_requests = len(ip_data)
553
+
554
+ # Don't block if it looks like legitimate behavior:
555
+ if (
556
+ avg_kw_hits < 2 and # Not hitting many malicious keywords
557
+ max_404s < 10 and # Not excessive 404s
558
+ avg_burst < 15 and # Not excessive burst activity
559
+ total_requests < 100 # Not excessive total requests
560
+ ):
561
+ print(f" - {ip}: Anomalous but looks legitimate (kw:{avg_kw_hits:.1f}, 404s:{max_404s}, burst:{avg_burst:.1f}) - NOT blocking")
562
+ continue
563
+
564
+ # Block if it shows clear signs of malicious behavior
565
+ BlacklistManager.block(ip, f"AI anomaly + suspicious patterns (kw:{avg_kw_hits:.1f}, 404s:{max_404s}, burst:{avg_burst:.1f})")
566
+ blocked_count += 1
567
+ print(f" - {ip}: Blocked for suspicious behavior (kw:{avg_kw_hits:.1f}, 404s:{max_404s}, burst:{avg_burst:.1f})")
568
+
569
+ print(f" → Blocked {blocked_count}/{len(anomalous_ips)} anomalous IPs (others looked legitimate)")
559
570
 
560
- print(f" → Blocked {blocked_count}/{len(anomalous_ips)} anomalous IPs (others looked legitimate)")
571
+ except ImportError as e:
572
+ print(f"AI model training failed - missing dependencies: {e}")
573
+ print(" Continuing with keyword learning only...")
574
+ except Exception as e:
575
+ print(f"AI model training failed: {e}")
576
+ print(" Continuing with keyword learning only...")
577
+ else:
578
+ print("AI model training skipped (disabled)")
579
+ df = pd.DataFrame(feature_dicts) # Still need df for some operations
561
580
 
562
581
  tokens = Counter()
563
582
  legitimate_keywords = get_legitimate_keywords()
564
583
 
565
- print(f"🔍 Learning keywords from {len(parsed)} parsed requests...")
584
+ print(f"Learning keywords from {len(parsed)} parsed requests...")
566
585
 
567
586
  for r in parsed:
568
587
  # Only learn from suspicious requests (errors on non-existent paths)
@@ -603,22 +622,35 @@ def train() -> None:
603
622
  learned_from_paths.extend(example_paths[:2]) # Track first 2 example paths
604
623
 
605
624
  if filtered_tokens:
606
- print(f"📝 Added {len(filtered_tokens)} suspicious keywords: {[kw for kw, _ in filtered_tokens]}")
607
- print(f"🎯 Example malicious paths learned from: {learned_from_paths[:5]}") # Show first 5
625
+ print(f"Added {len(filtered_tokens)} suspicious keywords: {[kw for kw, _ in filtered_tokens]}")
626
+ print(f"Example malicious paths learned from: {learned_from_paths[:5]}") # Show first 5
608
627
  else:
609
- print("No new suspicious keywords learned (good sign!)")
628
+ print("No new suspicious keywords learned (good sign!)")
610
629
 
611
- print(f"🎯 Smart keyword learning complete. Excluded {len(legitimate_keywords)} legitimate keywords.")
612
- print(f"🔒 Used malicious context analysis to filter out false positives.")
630
+ print(f"Smart keyword learning complete. Excluded {len(legitimate_keywords)} legitimate keywords.")
631
+ print(f"Used malicious context analysis to filter out false positives.")
613
632
 
614
633
  # Training summary
615
634
  print("\n" + "="*60)
616
- print("🎉 AIWAF ENHANCED TRAINING COMPLETE")
635
+ if disable_ai:
636
+ print("AIWAF KEYWORD-ONLY TRAINING COMPLETE")
637
+ else:
638
+ print("AIWAF ENHANCED TRAINING COMPLETE")
617
639
  print("="*60)
618
- print(f"📊 Training Data: {len(parsed)} log entries processed")
619
- print(f"🤖 AI Model: Trained with {len(feature_cols)} features")
620
- print(f"🚫 Blocked IPs: {blocked_count if 'blocked_count' in locals() else 0} suspicious IPs blocked")
621
- print(f"🔑 Keywords: {len(filtered_tokens)} new suspicious keywords learned")
622
- print(f"🛡️ Exemptions: {len(exempted_ips)} IPs protected from blocking")
623
- print(f"✅ Enhanced protection now active with context-aware filtering!")
640
+ print(f"Training Data: {len(parsed)} log entries processed")
641
+
642
+ if not disable_ai:
643
+ print(f"AI Model: Trained with {len(feature_cols) if 'feature_cols' in locals() else 'N/A'} features")
644
+ print(f"Blocked IPs: {blocked_count} suspicious IPs blocked")
645
+ else:
646
+ print(f"AI Model: Disabled (keyword learning only)")
647
+ print(f"Blocked IPs: 0 (AI blocking disabled)")
648
+
649
+ print(f"Keywords: {len(filtered_tokens)} new suspicious keywords learned")
650
+ print(f"Exemptions: {len(exempted_ips)} IPs protected from blocking")
651
+
652
+ if disable_ai:
653
+ print(f"Keyword-based protection now active with context-aware filtering!")
654
+ else:
655
+ print(f"Enhanced protection now active with context-aware filtering!")
624
656
  print("="*60)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiwaf
3
- Version: 0.1.9.2.7
3
+ Version: 0.1.9.2.9
4
4
  Summary: AI-powered Web Application Firewall
5
5
  Home-page: https://github.com/aayushgauba/aiwaf
6
6
  Author: Aayush Gauba
@@ -806,13 +806,25 @@ python manage.py aiwaf_reset --blacklist --confirm
806
806
 
807
807
  ---
808
808
 
809
- ## 📄 License
809
+ ## Sponsors
810
+
811
+ This project is proudly supported by:
812
+
813
+ <a href="https://www.digitalocean.com/">
814
+ <img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
815
+ </a>
816
+
817
+ [DigitalOcean](https://www.digitalocean.com/) provides the cloud infrastructure that powers AIWAF development.
818
+
819
+ ---
820
+
821
+ ## License
810
822
 
811
823
  This project is licensed under the **MIT License**. See the [LICENSE](LICENSE) file for details.
812
824
 
813
825
  ---
814
826
 
815
- ## 👤 Credits
827
+ ## Credits
816
828
 
817
829
  **AI‑WAF** by [Aayush Gauba](https://github.com/aayushgauba)
818
- > Let your firewall learn and evolve — keep your site a fortress.”
830
+ > "Let your firewall learn and evolve — keep your site a fortress." your Django `INSTALLED_APPS` to avoid setup errors.
@@ -1,12 +1,12 @@
1
- aiwaf/__init__.py,sha256=TCY7zgvfazGCir552LTRiAfLBZcCfJGYpVD1eAuN9Pk,220
1
+ aiwaf/__init__.py,sha256=WYEirEwV6DFp3jOUp4bh0HowpXax-5yA30mS_cQb1Tk,220
2
2
  aiwaf/apps.py,sha256=nCez-Ptlv2kaEk5HenA8b1pATz1VfhrHP1344gwcY1A,142
3
3
  aiwaf/blacklist_manager.py,sha256=LYCeKFB-7e_C6Bg2WeFJWFIIQlrfRMPuGp30ivrnhQY,1196
4
4
  aiwaf/decorators.py,sha256=IUKOdM_gdroffImRZep1g1wT6gNqD10zGwcp28hsJCs,825
5
- aiwaf/middleware.py,sha256=ThR0Bxw_VbGcOsRMIXMmQ5OMOIAy19z4SulsK7rZ_NM,32053
5
+ aiwaf/middleware.py,sha256=Bo8xcrRugp9audWNKp2nkrfYC34E4523tgWarEdlb2A,32256
6
6
  aiwaf/middleware_logger.py,sha256=LWZVDAnjh6CGESirA8eMbhGgJKB7lVDGRQqVroH95Lo,4742
7
7
  aiwaf/models.py,sha256=vQxgY19BDVMjoO903UNrTZC1pNoLltMU6wbyWPoAEns,2719
8
- aiwaf/storage.py,sha256=5ImrZMRn3u7HNsPH0fDjWhDrD2tgG2IHVnOXtLz0fk4,10253
9
- aiwaf/trainer.py,sha256=hswkopOnEQHNkSn79AIk-OWw6zatP2EDhsBBEHQeFYw,26410
8
+ aiwaf/storage.py,sha256=pUXE3bm7aRrABh_B6jTOBUQOYK67oQmHaR9EqyOasis,14038
9
+ aiwaf/trainer.py,sha256=RYtSEnRKKzfwzFRX_XODbWgIqvedf4Y271DlDh5qsJE,28060
10
10
  aiwaf/utils.py,sha256=BJk5vJCYdGPl_4QQiknjhCbkzv5HZCXgFcBJDMJpHok,3390
11
11
  aiwaf/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  aiwaf/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -20,17 +20,17 @@ aiwaf/management/commands/check_dependencies.py,sha256=GOZl00pDwW2cJjDvIaCeB3yWx
20
20
  aiwaf/management/commands/clear_blacklist.py,sha256=Tisedg0EVlc3E01mA3hBZQorwMzc5j1cns-oYshja0g,2770
21
21
  aiwaf/management/commands/clear_cache.py,sha256=cdnuTgxkhKLqT_6k6yTcEBlREovNRQxAE51ceXlGYMA,647
22
22
  aiwaf/management/commands/debug_csv.py,sha256=Lddqp37mIn0zdvHf4GbuNTWYyJ5h8bumDcGmFSAioi0,6801
23
- aiwaf/management/commands/detect_and_train.py,sha256=-o-LZ7QZ5GeJPCekryox1DGXKMmFEkwwrcDsiM166K0,269
23
+ aiwaf/management/commands/detect_and_train.py,sha256=ai8mUq-n9SW4_SX59Q9hE6eUvmBgb6i-IiTkCvaBe84,703
24
24
  aiwaf/management/commands/diagnose_blocking.py,sha256=HKb_FdN4b6QdyqNDf54B08I5jyWfrv9Mh-SFBrr3LbU,4140
25
- aiwaf/management/commands/regenerate_model.py,sha256=SUy7TCTTDJy4kRZNAbTIVBxSmljUaAC6ms0JTfSO6BE,3445
25
+ aiwaf/management/commands/regenerate_model.py,sha256=teguV17TlkE5IiefIDOx-ow4KfKvh35vYdhsdcfMpwg,4195
26
26
  aiwaf/management/commands/setup_models.py,sha256=JzuxwAqO3e-8L4PdFlXkyEQmOA8EGCXBfaOwfCNv1Gg,1678
27
27
  aiwaf/management/commands/test_exemption.py,sha256=ENmWFMJE8iQyzJGAPdw_5PUPknLajE8JjwHgH8DCFsE,5518
28
28
  aiwaf/management/commands/test_exemption_fix.py,sha256=ngyGaHUCmQQ6y--6j4q1viZJtR-RvI526yDqvEaEXPs,2553
29
29
  aiwaf/resources/model.pkl,sha256=5t6h9BX8yoh2xct85MXOO60jdlWyg1APskUOW0jZE1Y,1288265
30
30
  aiwaf/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
31
  aiwaf/templatetags/aiwaf_tags.py,sha256=XXfb7Tl4DjU3Sc40GbqdaqOEtKTUKELBEk58u83wBNw,357
32
- aiwaf-0.1.9.2.7.dist-info/licenses/LICENSE,sha256=Ir8PX4dxgAcdB0wqNPIkw84fzIIRKE75NoUil9RX0QU,1069
33
- aiwaf-0.1.9.2.7.dist-info/METADATA,sha256=tT2a7bhytbNJ5nvi1X7R6-9Xef7liJYQL-G8RMM3Oa8,26824
34
- aiwaf-0.1.9.2.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
35
- aiwaf-0.1.9.2.7.dist-info/top_level.txt,sha256=kU6EyjobT6UPCxuWpI_BvcHDG0I2tMgKaPlWzVxe2xI,6
36
- aiwaf-0.1.9.2.7.dist-info/RECORD,,
32
+ aiwaf-0.1.9.2.9.dist-info/licenses/LICENSE,sha256=Ir8PX4dxgAcdB0wqNPIkw84fzIIRKE75NoUil9RX0QU,1069
33
+ aiwaf-0.1.9.2.9.dist-info/METADATA,sha256=nUd5bp6VMsOJcDRK6qXQy6cO5eYGJ_arEdZhrrHft6M,27208
34
+ aiwaf-0.1.9.2.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
35
+ aiwaf-0.1.9.2.9.dist-info/top_level.txt,sha256=kU6EyjobT6UPCxuWpI_BvcHDG0I2tMgKaPlWzVxe2xI,6
36
+ aiwaf-0.1.9.2.9.dist-info/RECORD,,