aiwaf 0.1.9.2.1__py3-none-any.whl → 0.1.9.2.2__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 +1 -1
- aiwaf/management/commands/aiwaf_list.py +81 -0
- aiwaf/trainer.py +23 -8
- {aiwaf-0.1.9.2.1.dist-info → aiwaf-0.1.9.2.2.dist-info}/METADATA +1 -1
- {aiwaf-0.1.9.2.1.dist-info → aiwaf-0.1.9.2.2.dist-info}/RECORD +8 -7
- {aiwaf-0.1.9.2.1.dist-info → aiwaf-0.1.9.2.2.dist-info}/WHEEL +0 -0
- {aiwaf-0.1.9.2.1.dist-info → aiwaf-0.1.9.2.2.dist-info}/licenses/LICENSE +0 -0
- {aiwaf-0.1.9.2.1.dist-info → aiwaf-0.1.9.2.2.dist-info}/top_level.txt +0 -0
aiwaf/__init__.py
CHANGED
|
@@ -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"])
|
aiwaf/trainer.py
CHANGED
|
@@ -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].
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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,4 +1,4 @@
|
|
|
1
|
-
aiwaf/__init__.py,sha256=
|
|
1
|
+
aiwaf/__init__.py,sha256=EwzM3mRDf7i5IVX0-pTMpVFRf51lpIIbh6ZzkzOw10M,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
|
|
@@ -6,13 +6,14 @@ aiwaf/middleware.py,sha256=8EC4AKfUjHhmVSKpquimkMUebBekr92pqyVF97wlbx0,27408
|
|
|
6
6
|
aiwaf/middleware_logger.py,sha256=LWZVDAnjh6CGESirA8eMbhGgJKB7lVDGRQqVroH95Lo,4742
|
|
7
7
|
aiwaf/models.py,sha256=vQxgY19BDVMjoO903UNrTZC1pNoLltMU6wbyWPoAEns,2719
|
|
8
8
|
aiwaf/storage.py,sha256=5ImrZMRn3u7HNsPH0fDjWhDrD2tgG2IHVnOXtLz0fk4,10253
|
|
9
|
-
aiwaf/trainer.py,sha256=
|
|
9
|
+
aiwaf/trainer.py,sha256=U-X79nFhSTEbVexFHo3IXFf1HgvXrFnQ__WqTar0o4M,19118
|
|
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
|
|
13
13
|
aiwaf/management/commands/add_exemption.py,sha256=U_ByfJw1EstAZ8DaSoRb97IGwYzXs0DBJkVAqeN4Wak,1128
|
|
14
14
|
aiwaf/management/commands/add_ipexemption.py,sha256=sSf3d9hGK9RqqlBYkCrnrd8KZWGT-derSpoWnEY4H60,952
|
|
15
15
|
aiwaf/management/commands/aiwaf_diagnose.py,sha256=nXFRhq66N4QC3e4scYJ2sUngJce-0yDxtBO3R2BllRM,6134
|
|
16
|
+
aiwaf/management/commands/aiwaf_list.py,sha256=tZK3FugApmPxxvmoB4-nLY9fpZJgiRtD137Bre5hEp8,3839
|
|
16
17
|
aiwaf/management/commands/aiwaf_logging.py,sha256=FCIqULn2tii2vD9VxL7vk3PV4k4vr7kaA00KyaCExYY,7692
|
|
17
18
|
aiwaf/management/commands/aiwaf_reset.py,sha256=pcF0zOYDSqjpCwDtk2HYJZLgr76td8OFRENtl20c1dQ,7472
|
|
18
19
|
aiwaf/management/commands/check_dependencies.py,sha256=GOZl00pDwW2cJjDvIaCeB3yWxmeYcJDRTIpmOTLvy2c,37204
|
|
@@ -28,8 +29,8 @@ aiwaf/management/commands/test_exemption_fix.py,sha256=ngyGaHUCmQQ6y--6j4q1viZJt
|
|
|
28
29
|
aiwaf/resources/model.pkl,sha256=5t6h9BX8yoh2xct85MXOO60jdlWyg1APskUOW0jZE1Y,1288265
|
|
29
30
|
aiwaf/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
31
|
aiwaf/templatetags/aiwaf_tags.py,sha256=XXfb7Tl4DjU3Sc40GbqdaqOEtKTUKELBEk58u83wBNw,357
|
|
31
|
-
aiwaf-0.1.9.2.
|
|
32
|
-
aiwaf-0.1.9.2.
|
|
33
|
-
aiwaf-0.1.9.2.
|
|
34
|
-
aiwaf-0.1.9.2.
|
|
35
|
-
aiwaf-0.1.9.2.
|
|
32
|
+
aiwaf-0.1.9.2.2.dist-info/licenses/LICENSE,sha256=Ir8PX4dxgAcdB0wqNPIkw84fzIIRKE75NoUil9RX0QU,1069
|
|
33
|
+
aiwaf-0.1.9.2.2.dist-info/METADATA,sha256=NFu9QZWsGPcmAJaHeJroSXZL_PstDr33NbSffV94bLQ,26824
|
|
34
|
+
aiwaf-0.1.9.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
35
|
+
aiwaf-0.1.9.2.2.dist-info/top_level.txt,sha256=kU6EyjobT6UPCxuWpI_BvcHDG0I2tMgKaPlWzVxe2xI,6
|
|
36
|
+
aiwaf-0.1.9.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|