aiwaf 0.1.9.0.6__tar.gz → 0.1.9.0.8__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.
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/PKG-INFO +1 -1
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/__init__.py +1 -1
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/commands/add_ipexemption.py +1 -1
- aiwaf-0.1.9.0.8/aiwaf/management/commands/debug_csv.py +155 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/commands/test_exemption.py +1 -1
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/middleware.py +4 -4
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/storage.py +11 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf.egg-info/PKG-INFO +1 -1
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf.egg-info/SOURCES.txt +1 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/pyproject.toml +1 -1
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/setup.py +1 -1
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/LICENSE +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/README.md +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/apps.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/blacklist_manager.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/decorators.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/__init__.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/commands/__init__.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/commands/add_exemption.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/commands/aiwaf_diagnose.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/commands/aiwaf_logging.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/commands/aiwaf_reset.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/commands/clear_cache.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/commands/detect_and_train.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/commands/diagnose_blocking.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/commands/regenerate_model.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/commands/setup_models.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/management/commands/test_exemption_fix.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/middleware_logger.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/models.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/resources/model.pkl +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/templatetags/__init__.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/templatetags/aiwaf_tags.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/trainer.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf/utils.py +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf.egg-info/dependency_links.txt +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf.egg-info/requires.txt +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/aiwaf.egg-info/top_level.txt +0 -0
- {aiwaf-0.1.9.0.6 → aiwaf-0.1.9.0.8}/setup.cfg +0 -0
|
@@ -17,7 +17,7 @@ class Command(BaseCommand):
|
|
|
17
17
|
if store.is_exempted(ip):
|
|
18
18
|
self.stdout.write(self.style.WARNING(f'IP {ip} is already exempted.'))
|
|
19
19
|
else:
|
|
20
|
-
store.
|
|
20
|
+
store.add_exemption(ip, reason)
|
|
21
21
|
self.stdout.write(self.style.SUCCESS(f'IP {ip} added to exemption list.'))
|
|
22
22
|
if reason:
|
|
23
23
|
self.stdout.write(self.style.SUCCESS(f'Reason: {reason}'))
|
|
@@ -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_exemption(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")
|
|
@@ -63,7 +63,7 @@ class Command(BaseCommand):
|
|
|
63
63
|
|
|
64
64
|
# Create the exemption
|
|
65
65
|
try:
|
|
66
|
-
exemption_store.
|
|
66
|
+
exemption_store.add_exemption(test_ip, "Test exemption from debug")
|
|
67
67
|
self.stdout.write(self.style.SUCCESS("✅ Created test exemption"))
|
|
68
68
|
except Exception as e:
|
|
69
69
|
self.stdout.write(self.style.ERROR(f"❌ Failed to create exemption: {e}"))
|
|
@@ -135,12 +135,12 @@ class IPAndKeywordBlockMiddleware:
|
|
|
135
135
|
|
|
136
136
|
|
|
137
137
|
class RateLimitMiddleware:
|
|
138
|
-
WINDOW = 10 # seconds
|
|
139
|
-
MAX = 20 # soft limit
|
|
140
|
-
FLOOD = 40 # hard limit
|
|
141
|
-
|
|
142
138
|
def __init__(self, get_response):
|
|
143
139
|
self.get_response = get_response
|
|
140
|
+
# Make rate limiting configurable via Django settings
|
|
141
|
+
self.WINDOW = getattr(settings, "AIWAF_RATE_WINDOW", 10) # seconds
|
|
142
|
+
self.MAX = getattr(settings, "AIWAF_RATE_MAX", 20) # soft limit
|
|
143
|
+
self.FLOOD = getattr(settings, "AIWAF_RATE_FLOOD", 40) # hard limit
|
|
144
144
|
|
|
145
145
|
def __call__(self, request):
|
|
146
146
|
if is_exempt(request):
|
|
@@ -171,6 +171,17 @@ class ModelExemptionStore:
|
|
|
171
171
|
except Exception:
|
|
172
172
|
return []
|
|
173
173
|
|
|
174
|
+
@staticmethod
|
|
175
|
+
def get_all():
|
|
176
|
+
"""Get all exempted IP entries as dictionaries"""
|
|
177
|
+
_import_models()
|
|
178
|
+
if IPExemption is None:
|
|
179
|
+
return []
|
|
180
|
+
try:
|
|
181
|
+
return list(IPExemption.objects.values('ip_address', 'reason', 'created_at'))
|
|
182
|
+
except Exception:
|
|
183
|
+
return []
|
|
184
|
+
|
|
174
185
|
class ModelKeywordStore:
|
|
175
186
|
@staticmethod
|
|
176
187
|
def add_keyword(keyword):
|
|
@@ -25,6 +25,7 @@ aiwaf/management/commands/aiwaf_diagnose.py
|
|
|
25
25
|
aiwaf/management/commands/aiwaf_logging.py
|
|
26
26
|
aiwaf/management/commands/aiwaf_reset.py
|
|
27
27
|
aiwaf/management/commands/clear_cache.py
|
|
28
|
+
aiwaf/management/commands/debug_csv.py
|
|
28
29
|
aiwaf/management/commands/detect_and_train.py
|
|
29
30
|
aiwaf/management/commands/diagnose_blocking.py
|
|
30
31
|
aiwaf/management/commands/regenerate_model.py
|
|
@@ -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.
|
|
12
|
+
version="0.1.9.0.8",
|
|
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
|
|
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
|
|
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
|
|
File without changes
|