aiwaf 0.1.9.2.1__tar.gz → 0.1.9.2.2__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 (42) hide show
  1. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/PKG-INFO +1 -1
  2. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/__init__.py +1 -1
  3. aiwaf-0.1.9.2.2/aiwaf/management/commands/aiwaf_list.py +81 -0
  4. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/trainer.py +23 -8
  5. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf.egg-info/PKG-INFO +1 -1
  6. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf.egg-info/SOURCES.txt +1 -0
  7. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/pyproject.toml +1 -1
  8. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/setup.py +1 -1
  9. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/LICENSE +0 -0
  10. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/README.md +0 -0
  11. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/apps.py +0 -0
  12. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/blacklist_manager.py +0 -0
  13. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/decorators.py +0 -0
  14. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/__init__.py +0 -0
  15. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/__init__.py +0 -0
  16. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/add_exemption.py +0 -0
  17. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/add_ipexemption.py +0 -0
  18. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/aiwaf_diagnose.py +0 -0
  19. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/aiwaf_logging.py +0 -0
  20. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/aiwaf_reset.py +0 -0
  21. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/check_dependencies.py +0 -0
  22. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/clear_blacklist.py +0 -0
  23. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/clear_cache.py +0 -0
  24. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/debug_csv.py +0 -0
  25. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/detect_and_train.py +0 -0
  26. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/diagnose_blocking.py +0 -0
  27. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/regenerate_model.py +0 -0
  28. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/setup_models.py +0 -0
  29. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/test_exemption.py +0 -0
  30. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/management/commands/test_exemption_fix.py +0 -0
  31. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/middleware.py +0 -0
  32. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/middleware_logger.py +0 -0
  33. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/models.py +0 -0
  34. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/resources/model.pkl +0 -0
  35. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/storage.py +0 -0
  36. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/templatetags/__init__.py +0 -0
  37. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/templatetags/aiwaf_tags.py +0 -0
  38. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf/utils.py +0 -0
  39. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf.egg-info/dependency_links.txt +0 -0
  40. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf.egg-info/requires.txt +0 -0
  41. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/aiwaf.egg-info/top_level.txt +0 -0
  42. {aiwaf-0.1.9.2.1 → aiwaf-0.1.9.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiwaf
3
- Version: 0.1.9.2.1
3
+ Version: 0.1.9.2.2
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.2.1"
3
+ __version__ = "0.1.9.2.2"
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,81 @@
1
+ from django.core.management.base import BaseCommand
2
+ from django.utils import timezone
3
+ from aiwaf.storage import get_blacklist_store, get_exemption_store, get_keyword_store
4
+ from datetime import timedelta
5
+ import json
6
+
7
+ def _sort(items, order):
8
+ reverse = (order == "newest")
9
+ return sorted(items, key=lambda x: x.get("created_at") or timezone.make_aware(timezone.datetime.min),
10
+ reverse=reverse)
11
+
12
+ def _filter_since(items, seconds):
13
+ if not seconds: return items
14
+ cutoff = timezone.now() - timedelta(seconds=seconds)
15
+ return [it for it in items if it.get("created_at") and it["created_at"] >= cutoff]
16
+
17
+ def _print_table(rows, headers):
18
+ widths = [len(h) for h in headers]
19
+ for r in rows:
20
+ for i, cell in enumerate(r):
21
+ widths[i] = max(widths[i], len(str(cell)))
22
+ print(" | ".join(h.ljust(widths[i]) for i, h in enumerate(headers)))
23
+ print("-+-".join("-" * w for w in widths))
24
+ for r in rows:
25
+ print(" | ".join(str(cell).ljust(widths[i]) for i, cell in enumerate(r)))
26
+
27
+ class Command(BaseCommand):
28
+ help = "Lister les données AIWAF (IPs bloquées, exemptions, mots-clés dynamiques)."
29
+
30
+ def add_arguments(self, parser):
31
+ grp = parser.add_mutually_exclusive_group()
32
+ grp.add_argument("--ips", action="store_true", help="Lister les IPs bloquées (défaut).")
33
+ grp.add_argument("--exemptions", action="store_true", help="Lister les IPs exemptées.")
34
+ grp.add_argument("--keywords", action="store_true", help="Lister les mots-clés dynamiques.")
35
+ grp.add_argument("--all", action="store_true", help="Tout lister.")
36
+ parser.add_argument("--format", choices=["table", "json"], default="table")
37
+ parser.add_argument("--limit", type=int, default=100)
38
+ parser.add_argument("--order", choices=["newest", "oldest"], default="newest")
39
+ parser.add_argument("--since", type=int, help="Fenêtre en secondes (ex: 86400 = 24h).")
40
+
41
+ def handle(self, *args, **o):
42
+ if not any([o["exemptions"], o["keywords"], o["all"]]): # défaut = ips
43
+ o["ips"] = True
44
+ payload = {}
45
+
46
+ if o["all"] or o["ips"]:
47
+ data = get_blacklist_store().get_all()
48
+ data = _filter_since(data, o.get("since"))
49
+ data = _sort(data, o["order"])[:o["limit"]]
50
+ payload["ips"] = data
51
+
52
+ if o["all"] or o["exemptions"]:
53
+ data = get_exemption_store().get_all()
54
+ data = _filter_since(data, o.get("since"))
55
+ data = _sort(data, o["order"])[:o["limit"]]
56
+ payload["exemptions"] = data
57
+
58
+ if o["all"] or o["keywords"]:
59
+ kws = get_keyword_store().get_top_keywords(o["limit"])
60
+ payload["keywords"] = [{"keyword": k} for k in kws]
61
+
62
+ if o["format"] == "json":
63
+ def _default(v):
64
+ try: return v.isoformat()
65
+ except Exception: return str(v)
66
+ self.stdout.write(json.dumps(payload, ensure_ascii=False, indent=2, default=_default))
67
+ else:
68
+ if "ips" in payload:
69
+ print("\n== IPs bloquées ==")
70
+ rows = [[r.get("ip_address",""), r.get("reason",""), r.get("created_at","")]
71
+ for r in payload["ips"]]
72
+ _print_table(rows, ["ip_address", "reason", "created_at"])
73
+ if "exemptions" in payload:
74
+ print("\n== Exemptions ==")
75
+ rows = [[r.get("ip_address",""), r.get("reason",""), r.get("created_at","")]
76
+ for r in payload["exemptions"]]
77
+ _print_table(rows, ["ip_address", "reason", "created_at"])
78
+ if "keywords" in payload:
79
+ print("\n== Mots-clés dynamiques ==")
80
+ rows = [[r["keyword"]] for r in payload["keywords"]]
81
+ _print_table(rows, ["keyword"])
@@ -34,19 +34,34 @@ def path_exists_in_django(path: str) -> bool:
34
34
  from django.urls import get_resolver
35
35
  from django.urls.resolvers import URLResolver
36
36
 
37
- candidate = path.split("?")[0].lstrip("/")
37
+ candidate = path.split("?")[0].strip("/") # Remove query params and normalize slashes
38
+
39
+ # Try exact resolution first - this is the most reliable method
38
40
  try:
39
41
  get_resolver().resolve(f"/{candidate}")
40
42
  return True
41
43
  except:
42
44
  pass
43
-
44
- root = get_resolver()
45
- for p in root.url_patterns:
46
- if isinstance(p, URLResolver):
47
- prefix = p.pattern.describe().strip("^/")
48
- if prefix and candidate.startswith(prefix):
49
- return True
45
+
46
+ # Also try with trailing slash if it doesn't have one
47
+ if not candidate.endswith("/"):
48
+ try:
49
+ get_resolver().resolve(f"/{candidate}/")
50
+ return True
51
+ except:
52
+ pass
53
+
54
+ # Try without trailing slash if it has one
55
+ if candidate.endswith("/"):
56
+ try:
57
+ get_resolver().resolve(f"/{candidate.rstrip('/')}")
58
+ return True
59
+ except:
60
+ pass
61
+
62
+ # If direct resolution fails, be conservative
63
+ # Only do basic prefix matching for known include patterns
64
+ # but don't assume sub-paths exist just because the prefix exists
50
65
  return False
51
66
 
52
67
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiwaf
3
- Version: 0.1.9.2.1
3
+ Version: 0.1.9.2.2
4
4
  Summary: AI-powered Web Application Firewall
5
5
  Home-page: https://github.com/aayushgauba/aiwaf
6
6
  Author: Aayush Gauba
@@ -22,6 +22,7 @@ aiwaf/management/commands/__init__.py
22
22
  aiwaf/management/commands/add_exemption.py
23
23
  aiwaf/management/commands/add_ipexemption.py
24
24
  aiwaf/management/commands/aiwaf_diagnose.py
25
+ aiwaf/management/commands/aiwaf_list.py
25
26
  aiwaf/management/commands/aiwaf_logging.py
26
27
  aiwaf/management/commands/aiwaf_reset.py
27
28
  aiwaf/management/commands/check_dependencies.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "aiwaf"
3
- version = "0.1.9.2.1"
3
+ version = "0.1.9.2.2"
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.2.1",
12
+ version="0.1.9.2.2",
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