aiwaf 0.1.9.0.3__tar.gz → 0.1.9.0.4__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 (33) hide show
  1. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/PKG-INFO +1 -1
  2. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/__init__.py +1 -1
  3. aiwaf-0.1.9.0.4/aiwaf/management/commands/debug_csv.py +155 -0
  4. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/middleware_logger.py +10 -1
  5. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/storage.py +22 -11
  6. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf.egg-info/PKG-INFO +1 -1
  7. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf.egg-info/SOURCES.txt +1 -0
  8. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/pyproject.toml +1 -1
  9. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/setup.py +1 -1
  10. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/LICENSE +0 -0
  11. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/README.md +0 -0
  12. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/apps.py +0 -0
  13. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/blacklist_manager.py +0 -0
  14. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/decorators.py +0 -0
  15. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/management/__init__.py +0 -0
  16. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/management/commands/__init__.py +0 -0
  17. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/management/commands/add_ipexemption.py +0 -0
  18. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/management/commands/aiwaf_diagnose.py +0 -0
  19. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/management/commands/aiwaf_logging.py +0 -0
  20. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/management/commands/aiwaf_reset.py +0 -0
  21. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/management/commands/detect_and_train.py +0 -0
  22. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/management/commands/regenerate_model.py +0 -0
  23. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/middleware.py +0 -0
  24. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/models.py +0 -0
  25. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/resources/model.pkl +0 -0
  26. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/templatetags/__init__.py +0 -0
  27. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/templatetags/aiwaf_tags.py +0 -0
  28. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/trainer.py +0 -0
  29. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf/utils.py +0 -0
  30. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf.egg-info/dependency_links.txt +0 -0
  31. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf.egg-info/requires.txt +0 -0
  32. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/aiwaf.egg-info/top_level.txt +0 -0
  33. {aiwaf-0.1.9.0.3 → aiwaf-0.1.9.0.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiwaf
3
- Version: 0.1.9.0.3
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
@@ -1,6 +1,6 @@
1
1
  default_app_config = "aiwaf.apps.AiwafConfig"
2
2
 
3
- __version__ = "0.1.9.0.3"
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")
@@ -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([
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiwaf
3
- Version: 0.1.9.0.3
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
@@ -23,6 +23,7 @@ aiwaf/management/commands/add_ipexemption.py
23
23
  aiwaf/management/commands/aiwaf_diagnose.py
24
24
  aiwaf/management/commands/aiwaf_logging.py
25
25
  aiwaf/management/commands/aiwaf_reset.py
26
+ aiwaf/management/commands/debug_csv.py
26
27
  aiwaf/management/commands/detect_and_train.py
27
28
  aiwaf/management/commands/regenerate_model.py
28
29
  aiwaf/resources/model.pkl
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "aiwaf"
3
- version = "0.1.9.0.3"
3
+ version = "0.1.9.0.4"
4
4
  description = "AI-powered Web Application Firewall"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.8"
@@ -9,7 +9,7 @@ long_description = (HERE / "README.md").read_text(encoding="utf-8")
9
9
 
10
10
  setup(
11
11
  name="aiwaf",
12
- version="0.1.9.0.3",
12
+ version="0.1.9.0.4",
13
13
  description="AI‑driven, self‑learning Web Application Firewall for Django",
14
14
  long_description=long_description,
15
15
  long_description_content_type="text/markdown",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes