aiwaf 0.1.9.0.2__py3-none-any.whl → 0.1.9.0.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/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  default_app_config = "aiwaf.apps.AiwafConfig"
2
2
 
3
- __version__ = "0.1.9.0.2"
3
+ __version__ = "0.1.9.0.4"
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
@@ -0,0 +1,155 @@
1
+ from django.core.management.base import BaseCommand
2
+ import os
3
+ import csv
4
+
5
+ class Command(BaseCommand):
6
+ help = 'Debug and fix AI-WAF CSV functionality'
7
+
8
+ def add_arguments(self, parser):
9
+ parser.add_argument(
10
+ '--test-ip',
11
+ type=str,
12
+ help='Test IP address to add to exemption list',
13
+ default='127.0.0.1'
14
+ )
15
+ parser.add_argument(
16
+ '--fix',
17
+ action='store_true',
18
+ help='Attempt to fix identified issues',
19
+ )
20
+
21
+ def handle(self, *args, **options):
22
+ self.stdout.write(self.style.HTTP_INFO("🔍 AI-WAF CSV Debug & Fix"))
23
+ self.stdout.write("")
24
+
25
+ # Check storage mode
26
+ from django.conf import settings
27
+ storage_mode = getattr(settings, 'AIWAF_STORAGE_MODE', 'models')
28
+ csv_dir = getattr(settings, 'AIWAF_CSV_DATA_DIR', 'aiwaf_data')
29
+
30
+ self.stdout.write(f"Storage Mode: {storage_mode}")
31
+ self.stdout.write(f"CSV Directory: {csv_dir}")
32
+ self.stdout.write("")
33
+
34
+ # Check middleware logging
35
+ middleware_logging = getattr(settings, 'AIWAF_MIDDLEWARE_LOGGING', False)
36
+ middleware_log = getattr(settings, 'AIWAF_MIDDLEWARE_LOG', 'aiwaf_requests.log')
37
+
38
+ self.stdout.write(f"Middleware Logging: {middleware_logging}")
39
+ self.stdout.write(f"Middleware Log File: {middleware_log}")
40
+ self.stdout.write("")
41
+
42
+ # Check if CSV directory exists
43
+ if os.path.exists(csv_dir):
44
+ self.stdout.write(self.style.SUCCESS(f"✅ CSV directory exists: {csv_dir}"))
45
+ else:
46
+ self.stdout.write(self.style.ERROR(f"❌ CSV directory missing: {csv_dir}"))
47
+ if options['fix']:
48
+ os.makedirs(csv_dir, exist_ok=True)
49
+ self.stdout.write(self.style.SUCCESS(f"✅ Created CSV directory: {csv_dir}"))
50
+
51
+ # Check CSV files
52
+ csv_files = ['blacklist.csv', 'exemptions.csv', 'keywords.csv']
53
+ for filename in csv_files:
54
+ filepath = os.path.join(csv_dir, filename)
55
+ if os.path.exists(filepath):
56
+ # Count entries
57
+ try:
58
+ with open(filepath, 'r', newline='', encoding='utf-8') as f:
59
+ reader = csv.reader(f)
60
+ rows = list(reader)
61
+ entry_count = len(rows) - 1 if rows else 0 # Subtract header
62
+ self.stdout.write(self.style.SUCCESS(f"✅ {filename}: {entry_count} entries"))
63
+ except Exception as e:
64
+ self.stdout.write(self.style.ERROR(f"❌ {filename}: Error reading - {e}"))
65
+ else:
66
+ self.stdout.write(self.style.WARNING(f"⚠️ {filename}: Not found"))
67
+
68
+ self.stdout.write("")
69
+
70
+ # Test storage functionality
71
+ self.stdout.write(self.style.HTTP_INFO("🧪 Testing Storage Functions"))
72
+
73
+ try:
74
+ from aiwaf.storage import get_exemption_store, get_blacklist_store, get_keyword_store
75
+
76
+ # Test exemption store
77
+ exemption_store = get_exemption_store()
78
+ self.stdout.write(f"Exemption Store: {exemption_store.__name__}")
79
+
80
+ # Test blacklist store
81
+ blacklist_store = get_blacklist_store()
82
+ self.stdout.write(f"Blacklist Store: {blacklist_store.__name__}")
83
+
84
+ # Test keyword store
85
+ keyword_store = get_keyword_store()
86
+ self.stdout.write(f"Keyword Store: {keyword_store.__name__}")
87
+
88
+ except Exception as e:
89
+ self.stdout.write(self.style.ERROR(f"❌ Storage import failed: {e}"))
90
+ return
91
+
92
+ self.stdout.write("")
93
+
94
+ # Test exemption functionality
95
+ test_ip = options['test_ip']
96
+ self.stdout.write(f"🧪 Testing exemption with IP: {test_ip}")
97
+
98
+ try:
99
+ # Check if already exempted
100
+ is_exempted_before = exemption_store.is_exempted(test_ip)
101
+ self.stdout.write(f"Before: IP {test_ip} exempted = {is_exempted_before}")
102
+
103
+ # Add to exemption
104
+ exemption_store.add_ip(test_ip, "Test exemption from debug command")
105
+ self.stdout.write(f"✅ Added {test_ip} to exemption list")
106
+
107
+ # Check if now exempted
108
+ is_exempted_after = exemption_store.is_exempted(test_ip)
109
+ self.stdout.write(f"After: IP {test_ip} exempted = {is_exempted_after}")
110
+
111
+ if is_exempted_after:
112
+ self.stdout.write(self.style.SUCCESS("✅ Exemption functionality working!"))
113
+ else:
114
+ self.stdout.write(self.style.ERROR("❌ Exemption functionality not working!"))
115
+
116
+ # List all exemptions
117
+ all_exemptions = exemption_store.get_all()
118
+ self.stdout.write(f"Total exemptions: {len(all_exemptions)}")
119
+
120
+ for exemption in all_exemptions:
121
+ self.stdout.write(f" - {exemption.get('ip_address', exemption)}")
122
+
123
+ except Exception as e:
124
+ self.stdout.write(self.style.ERROR(f"❌ Exemption test failed: {e}"))
125
+
126
+ self.stdout.write("")
127
+
128
+ # Check middleware logger file
129
+ csv_log_file = middleware_log.replace('.log', '.csv')
130
+ if os.path.exists(csv_log_file):
131
+ try:
132
+ with open(csv_log_file, 'r', newline='', encoding='utf-8') as f:
133
+ reader = csv.reader(f)
134
+ rows = list(reader)
135
+ entry_count = len(rows) - 1 if rows else 0
136
+ self.stdout.write(self.style.SUCCESS(f"✅ Middleware CSV log: {entry_count} entries"))
137
+ except Exception as e:
138
+ self.stdout.write(self.style.ERROR(f"❌ Middleware CSV log error: {e}"))
139
+ else:
140
+ self.stdout.write(self.style.WARNING(f"⚠️ Middleware CSV log not found: {csv_log_file}"))
141
+ self.stdout.write(" Make some requests to generate log entries")
142
+
143
+ # Recommendations
144
+ self.stdout.write("")
145
+ self.stdout.write(self.style.HTTP_INFO("💡 Recommendations:"))
146
+
147
+ if storage_mode != 'csv':
148
+ self.stdout.write("1. Set AIWAF_STORAGE_MODE = 'csv' in settings.py")
149
+
150
+ if not middleware_logging:
151
+ self.stdout.write("2. Set AIWAF_MIDDLEWARE_LOGGING = True in settings.py")
152
+
153
+ self.stdout.write("3. Add AIWAFLoggerMiddleware to MIDDLEWARE in settings.py")
154
+ self.stdout.write("4. Make some requests to generate log data")
155
+ self.stdout.write("5. Run 'python manage.py detect_and_train' to train with data")
@@ -0,0 +1,79 @@
1
+ from django.core.management.base import BaseCommand
2
+ import os
3
+ import warnings
4
+
5
+ class Command(BaseCommand):
6
+ help = 'Regenerate AI-WAF model with current scikit-learn version'
7
+
8
+ def add_arguments(self, parser):
9
+ parser.add_argument(
10
+ '--force',
11
+ action='store_true',
12
+ help='Force regeneration even if model exists',
13
+ )
14
+
15
+ def handle(self, *args, **options):
16
+ self.stdout.write(self.style.HTTP_INFO("🔄 AI-WAF Model Regeneration"))
17
+ self.stdout.write("")
18
+
19
+ # Check current sklearn version
20
+ try:
21
+ import sklearn
22
+ self.stdout.write(f"Current scikit-learn version: {sklearn.__version__}")
23
+ except ImportError:
24
+ self.stdout.write(self.style.ERROR("❌ scikit-learn not available"))
25
+ self.stdout.write("Install with: pip install scikit-learn")
26
+ return
27
+
28
+ # Check if model exists
29
+ from aiwaf.trainer import MODEL_PATH
30
+ model_exists = os.path.exists(MODEL_PATH)
31
+
32
+ if model_exists and not options['force']:
33
+ self.stdout.write(f"Model exists at: {MODEL_PATH}")
34
+
35
+ # Try to load and check version
36
+ try:
37
+ import joblib
38
+ with warnings.catch_warnings():
39
+ warnings.filterwarnings("ignore", category=UserWarning, module="sklearn.base")
40
+ model_data = joblib.load(MODEL_PATH)
41
+
42
+ if isinstance(model_data, dict) and 'sklearn_version' in model_data:
43
+ stored_version = model_data['sklearn_version']
44
+ if stored_version == sklearn.__version__:
45
+ self.stdout.write(self.style.SUCCESS("✅ Model is up-to-date"))
46
+ return
47
+ else:
48
+ self.stdout.write(f"⚠️ Model version mismatch:")
49
+ self.stdout.write(f" Stored: {stored_version}")
50
+ self.stdout.write(f" Current: {sklearn.__version__}")
51
+ else:
52
+ self.stdout.write("⚠️ Legacy model format detected")
53
+
54
+ except Exception as e:
55
+ self.stdout.write(f"⚠️ Could not check model: {e}")
56
+
57
+ self.stdout.write("")
58
+ self.stdout.write("Regenerating model to fix version compatibility...")
59
+
60
+ # Regenerate model
61
+ self.stdout.write("🚀 Starting model training...")
62
+
63
+ try:
64
+ from aiwaf.trainer import train
65
+ train()
66
+ self.stdout.write("")
67
+ self.stdout.write(self.style.SUCCESS("✅ Model regenerated successfully!"))
68
+ 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.")
71
+
72
+ except Exception as e:
73
+ self.stdout.write("")
74
+ self.stdout.write(self.style.ERROR(f"❌ Model regeneration failed: {e}"))
75
+ self.stdout.write("")
76
+ self.stdout.write("Possible solutions:")
77
+ self.stdout.write("1. Check that you have log data available")
78
+ self.stdout.write("2. Verify AIWAF_ACCESS_LOG setting")
79
+ self.stdout.write("3. Run 'python manage.py detect_and_train' for full training")
aiwaf/middleware.py CHANGED
@@ -25,7 +25,43 @@ MODEL_PATH = getattr(
25
25
  "AIWAF_MODEL_PATH",
26
26
  os.path.join(os.path.dirname(__file__), "resources", "model.pkl")
27
27
  )
28
- MODEL = joblib.load(MODEL_PATH)
28
+
29
+ def load_model_safely():
30
+ """Load the AI model with version compatibility checking."""
31
+ import warnings
32
+ import sklearn
33
+
34
+ try:
35
+ # Suppress sklearn version warnings temporarily
36
+ with warnings.catch_warnings():
37
+ warnings.filterwarnings("ignore", category=UserWarning, module="sklearn.base")
38
+ model_data = joblib.load(MODEL_PATH)
39
+
40
+ # Handle both old format (direct model) and new format (with metadata)
41
+ if isinstance(model_data, dict) and 'model' in model_data:
42
+ # New format with metadata
43
+ model = model_data['model']
44
+ stored_version = model_data.get('sklearn_version', 'unknown')
45
+ current_version = sklearn.__version__
46
+
47
+ if stored_version != current_version:
48
+ print(f"ℹ️ Model was trained with sklearn v{stored_version}, current v{current_version}")
49
+ print(" Run 'python manage.py detect_and_train' to update model if needed.")
50
+
51
+ return model
52
+ else:
53
+ # Old format - direct model object
54
+ print("ℹ️ Using legacy model format. Consider retraining for better compatibility.")
55
+ return model_data
56
+
57
+ except Exception as e:
58
+ print(f"Warning: Could not load AI model from {MODEL_PATH}: {e}")
59
+ print("AI anomaly detection will be disabled until model is retrained.")
60
+ print("Run 'python manage.py detect_and_train' to regenerate the model.")
61
+ return None
62
+
63
+ # Load model with safety checks
64
+ MODEL = load_model_safely()
29
65
 
30
66
  STATIC_KW = getattr(
31
67
  settings,
@@ -130,8 +166,8 @@ class AIAnomalyMiddleware(MiddlewareMixin):
130
166
 
131
167
  def __init__(self, get_response=None):
132
168
  super().__init__(get_response)
133
- model_path = os.path.join(os.path.dirname(__file__), "resources", "model.pkl")
134
- self.model = joblib.load(model_path)
169
+ # Use the safely loaded global MODEL instead of loading again
170
+ self.model = MODEL
135
171
 
136
172
  def process_request(self, request):
137
173
  if is_exempt(request):
@@ -164,7 +200,9 @@ class AIAnomalyMiddleware(MiddlewareMixin):
164
200
  total_404 = sum(1 for (_, _, st, _) in data if st == 404)
165
201
  feats = [path_len, kw_hits, resp_time, status_idx, burst_count, total_404]
166
202
  X = np.array(feats, dtype=float).reshape(1, -1)
167
- if self.model.predict(X)[0] == -1:
203
+
204
+ # Only use AI model if it's available
205
+ if self.model is not None and self.model.predict(X)[0] == -1:
168
206
  if not is_ip_exempted(ip):
169
207
  BlacklistManager.block(ip, "AI anomaly")
170
208
  return JsonResponse({"error": "blocked"}, status=403)
@@ -28,7 +28,11 @@ class AIWAFLoggerMiddleware(MiddlewareMixin):
28
28
  def _ensure_csv_header(self):
29
29
  """Ensure CSV file has proper header row"""
30
30
  if not os.path.exists(self.csv_file):
31
- os.makedirs(os.path.dirname(self.csv_file), exist_ok=True) if os.path.dirname(self.csv_file) else None
31
+ # Create directory if it doesn't exist
32
+ csv_dir = os.path.dirname(self.csv_file)
33
+ if csv_dir and not os.path.exists(csv_dir):
34
+ os.makedirs(csv_dir, exist_ok=True)
35
+
32
36
  with open(self.csv_file, 'w', newline='', encoding='utf-8') as f:
33
37
  writer = csv.writer(f)
34
38
  writer.writerow([
@@ -73,6 +77,11 @@ class AIWAFLoggerMiddleware(MiddlewareMixin):
73
77
  def _log_to_csv(self, data):
74
78
  """Write log entry to CSV file"""
75
79
  try:
80
+ # Ensure directory exists before writing
81
+ csv_dir = os.path.dirname(self.csv_file)
82
+ if csv_dir and not os.path.exists(csv_dir):
83
+ os.makedirs(csv_dir, exist_ok=True)
84
+
76
85
  with open(self.csv_file, 'a', newline='', encoding='utf-8') as f:
77
86
  writer = csv.writer(f)
78
87
  writer.writerow([
aiwaf/storage.py CHANGED
@@ -167,28 +167,39 @@ class CsvExemptionStore:
167
167
  @staticmethod
168
168
  def add_ip(ip_address, reason=""):
169
169
  ensure_csv_directory()
170
- # Check if IP already exists
170
+
171
+ # Check if IP already exists to avoid duplicates
171
172
  if CsvExemptionStore.is_exempted(ip_address):
172
173
  return
173
174
 
174
175
  # Add new entry
175
176
  new_file = not os.path.exists(EXEMPTION_CSV)
176
- with open(EXEMPTION_CSV, "a", newline="", encoding="utf-8") as f:
177
- writer = csv.writer(f)
178
- if new_file:
179
- writer.writerow(["ip_address", "reason", "created_at"])
180
- writer.writerow([ip_address, reason, timezone.now().isoformat()])
177
+ try:
178
+ with open(EXEMPTION_CSV, "a", newline="", encoding="utf-8") as f:
179
+ writer = csv.writer(f)
180
+ if new_file:
181
+ writer.writerow(["ip_address", "reason", "created_at"])
182
+ writer.writerow([ip_address, reason, timezone.now().isoformat()])
183
+ except Exception as e:
184
+ print(f"Error writing to exemption CSV: {e}")
185
+ print(f"File path: {EXEMPTION_CSV}")
186
+ print(f"Directory exists: {os.path.exists(CSV_DATA_DIR)}")
187
+ raise
181
188
 
182
189
  @staticmethod
183
190
  def is_exempted(ip_address):
184
191
  if not os.path.exists(EXEMPTION_CSV):
185
192
  return False
186
193
 
187
- with open(EXEMPTION_CSV, "r", newline="", encoding="utf-8") as f:
188
- reader = csv.DictReader(f)
189
- for row in reader:
190
- if row["ip_address"] == ip_address:
191
- return True
194
+ try:
195
+ with open(EXEMPTION_CSV, "r", newline="", encoding="utf-8") as f:
196
+ reader = csv.DictReader(f)
197
+ for row in reader:
198
+ if row["ip_address"] == ip_address:
199
+ return True
200
+ except Exception as e:
201
+ print(f"Error reading exemption CSV: {e}")
202
+ return False
192
203
  return False
193
204
 
194
205
  @staticmethod
aiwaf/trainer.py CHANGED
@@ -192,11 +192,28 @@ def train() -> None:
192
192
  contamination=getattr(settings, "AIWAF_AI_CONTAMINATION", 0.05),
193
193
  random_state=42
194
194
  )
195
- model.fit(X)
195
+
196
+ # Suppress sklearn warnings during training
197
+ import warnings
198
+ with warnings.catch_warnings():
199
+ warnings.filterwarnings("ignore", category=UserWarning, module="sklearn")
200
+ model.fit(X)
196
201
 
197
202
  os.makedirs(os.path.dirname(MODEL_PATH), exist_ok=True)
198
- joblib.dump(model, MODEL_PATH)
199
- print(f"Model trained on {len(X)} samples → {MODEL_PATH}")
203
+
204
+ # Save model with version metadata
205
+ import sklearn
206
+ from django.utils import timezone as django_timezone
207
+ model_data = {
208
+ 'model': model,
209
+ 'sklearn_version': sklearn.__version__,
210
+ 'created_at': str(django_timezone.now()),
211
+ 'feature_count': len(feature_cols),
212
+ 'samples_count': len(X)
213
+ }
214
+ joblib.dump(model_data, MODEL_PATH)
215
+ print(f"✅ Model trained on {len(X)} samples → {MODEL_PATH}")
216
+ print(f"📦 Created with scikit-learn v{sklearn.__version__}")
200
217
 
201
218
  # Check for anomalies and intelligently decide which IPs to block
202
219
  preds = model.predict(X)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiwaf
3
- Version: 0.1.9.0.2
3
+ Version: 0.1.9.0.4
4
4
  Summary: AI-powered Web Application Firewall
5
5
  Home-page: https://github.com/aayushgauba/aiwaf
6
6
  Author: Aayush Gauba
@@ -12,7 +12,7 @@ License-File: LICENSE
12
12
  Requires-Dist: Django>=3.2
13
13
  Requires-Dist: numpy>=1.21
14
14
  Requires-Dist: pandas>=1.3
15
- Requires-Dist: scikit-learn>=1.0
15
+ Requires-Dist: scikit-learn<2.0,>=1.0
16
16
  Requires-Dist: joblib>=1.1
17
17
  Dynamic: author
18
18
  Dynamic: home-page
@@ -371,6 +371,7 @@ MIDDLEWARE = [
371
371
 
372
372
  **Common Issues:**
373
373
  - **AppRegistryNotReady Error**: Fixed in v0.1.9.0.1 - update with `pip install --upgrade aiwaf`
374
+ - **Scikit-learn Version Warnings**: Fixed in v0.1.9.0.3 - regenerate model with `python manage.py regenerate_model`
374
375
  - Missing Django: `pip install Django`
375
376
  - Old AI-WAF version: `pip install --upgrade aiwaf`
376
377
  - Missing migrations: `python manage.py migrate`
@@ -388,6 +389,24 @@ python manage.py detect_and_train
388
389
  1. Read access logs (incl. rotated or gzipped) **OR** AI-WAF middleware CSV logs
389
390
  2. Auto‑block IPs with ≥ 6 total 404s
390
391
  3. Extract features & train IsolationForest
392
+ 4. Save `model.pkl` with current scikit-learn version
393
+
394
+ ### Model Regeneration
395
+
396
+ If you see scikit-learn version warnings, regenerate the model:
397
+
398
+ ```bash
399
+ # Quick model regeneration (recommended)
400
+ python manage.py regenerate_model
401
+
402
+ # Full retraining with fresh data
403
+ python manage.py detect_and_train
404
+ ```
405
+
406
+ **Benefits:**
407
+ - ✅ Eliminates version compatibility warnings
408
+ - ✅ Uses current scikit-learn optimizations
409
+ - ✅ Maintains same protection level
391
410
  4. Save `model.pkl`
392
411
  5. Extract top 10 dynamic keywords from 4xx/5xx
393
412
  6. Remove any keywords associated with newly exempt paths
@@ -1,12 +1,12 @@
1
- aiwaf/__init__.py,sha256=_efEtDv3HT5nHuYd3IpmPDtwhVdKcnrYo4EE9OC_rMU,220
1
+ aiwaf/__init__.py,sha256=w99RK2Yc4N5TICgJLmsEL_DOiNe7LoCy5GZMkf1t_hQ,220
2
2
  aiwaf/apps.py,sha256=nCez-Ptlv2kaEk5HenA8b1pATz1VfhrHP1344gwcY1A,142
3
3
  aiwaf/blacklist_manager.py,sha256=92ltIrFfv8WOC4CXwvNVZYfivkRZHGNg3E2QAbHQipQ,550
4
4
  aiwaf/decorators.py,sha256=IUKOdM_gdroffImRZep1g1wT6gNqD10zGwcp28hsJCs,825
5
- aiwaf/middleware.py,sha256=1JPrc0npI_a5bnB-thN0ME1ehfTbWBl1j9wTndZwRdQ,9505
6
- aiwaf/middleware_logger.py,sha256=uTYTvIc4Mv1pjY50aXaqQ5cWAO9qqquijAyVMs1KWlM,6517
5
+ aiwaf/middleware.py,sha256=fajFXsETb2kGQJzHi7cEV3bQA88yqHbZvRDPNR1y2OA,11188
6
+ aiwaf/middleware_logger.py,sha256=v5tsiFhfdFTDBRRsfw-vfmhW2sNJx7EYRem99pgMsP0,6858
7
7
  aiwaf/models.py,sha256=XaG1pd_oZu3y-fw66u4wblGlWcUY9gvsTNKGD0kQk7Y,1672
8
- aiwaf/storage.py,sha256=aaszMkskRO0RZPwD2vsgP35TthI_xQEs5IxsCsxjGcw,13734
9
- aiwaf/trainer.py,sha256=2-8sUbXTR07fmJ3INHM6LZP_tqqT4zbv5_jrYky7DSU,9262
8
+ aiwaf/storage.py,sha256=Gh92OCE52j9d8vjBQCoJbjA5-fsMQkdbYYypJMuZjtQ,14167
9
+ aiwaf/trainer.py,sha256=Cem-qi1KMjfw7MFDxyxjn7nAJGPrk-I8An4HHniiT00,9877
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
@@ -14,12 +14,14 @@ aiwaf/management/commands/add_ipexemption.py,sha256=srgdVPDJtF7G9GGIqaZ7L3qTuNhe
14
14
  aiwaf/management/commands/aiwaf_diagnose.py,sha256=nXFRhq66N4QC3e4scYJ2sUngJce-0yDxtBO3R2BllRM,6134
15
15
  aiwaf/management/commands/aiwaf_logging.py,sha256=FCIqULn2tii2vD9VxL7vk3PV4k4vr7kaA00KyaCExYY,7692
16
16
  aiwaf/management/commands/aiwaf_reset.py,sha256=0FIBqpZS8xgFFvAKJ-0zAC_-QNQwRkOHpXb8N-OdFr8,3740
17
+ aiwaf/management/commands/debug_csv.py,sha256=Vp55g1qWAuRUuAHoz56nuPxMrcccFCHKRGeYyui8Pxw,6794
17
18
  aiwaf/management/commands/detect_and_train.py,sha256=-o-LZ7QZ5GeJPCekryox1DGXKMmFEkwwrcDsiM166K0,269
19
+ aiwaf/management/commands/regenerate_model.py,sha256=SUy7TCTTDJy4kRZNAbTIVBxSmljUaAC6ms0JTfSO6BE,3445
18
20
  aiwaf/resources/model.pkl,sha256=5t6h9BX8yoh2xct85MXOO60jdlWyg1APskUOW0jZE1Y,1288265
19
21
  aiwaf/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
22
  aiwaf/templatetags/aiwaf_tags.py,sha256=XXfb7Tl4DjU3Sc40GbqdaqOEtKTUKELBEk58u83wBNw,357
21
- aiwaf-0.1.9.0.2.dist-info/licenses/LICENSE,sha256=Ir8PX4dxgAcdB0wqNPIkw84fzIIRKE75NoUil9RX0QU,1069
22
- aiwaf-0.1.9.0.2.dist-info/METADATA,sha256=N5kAt8giKmurCaiXuwBTqSXNvlT77uOTE0a90AnXRr0,13002
23
- aiwaf-0.1.9.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
- aiwaf-0.1.9.0.2.dist-info/top_level.txt,sha256=kU6EyjobT6UPCxuWpI_BvcHDG0I2tMgKaPlWzVxe2xI,6
25
- aiwaf-0.1.9.0.2.dist-info/RECORD,,
23
+ aiwaf-0.1.9.0.4.dist-info/licenses/LICENSE,sha256=Ir8PX4dxgAcdB0wqNPIkw84fzIIRKE75NoUil9RX0QU,1069
24
+ aiwaf-0.1.9.0.4.dist-info/METADATA,sha256=PtBnfHANxkOI4buUl8kQqglCgzXjGR2dtC6IZdJSEKc,13571
25
+ aiwaf-0.1.9.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
+ aiwaf-0.1.9.0.4.dist-info/top_level.txt,sha256=kU6EyjobT6UPCxuWpI_BvcHDG0I2tMgKaPlWzVxe2xI,6
27
+ aiwaf-0.1.9.0.4.dist-info/RECORD,,