wafaHell 0.2.0__py3-none-any.whl → 1.1.0__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.
wafaHell/app.py ADDED
@@ -0,0 +1,31 @@
1
+ from middleware import Wafahell
2
+ from flask import Flask, render_template, request
3
+ from werkzeug.middleware.proxy_fix import ProxyFix
4
+
5
+ app = Flask(__name__)
6
+
7
+
8
+ app.wsgi_app = ProxyFix(
9
+ app.wsgi_app,
10
+ x_for=1,
11
+ x_proto=1,
12
+ x_host=1,
13
+ x_port=1
14
+ )
15
+
16
+ @app.route('/', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])
17
+ def home():
18
+ return "<h1>Bem-vindo à minha aplicação Flask!</h1><p>Acesse /hello?nome=test</p>"
19
+
20
+ @app.route('/hello', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])
21
+ def hello():
22
+ nome = request.args.get('nome', 'Visitante')
23
+ return f"<h1>Olá, {nome}!</h1>"
24
+
25
+ @app.route('/admin/dashboard')
26
+ def dashboard():
27
+ return "<h1>Dashboard Personalizado</h1><p>Este é o painel de controle personalizado.</p>"
28
+
29
+ if __name__ == '__main__':
30
+ Wafahell(app=app, dashboard_path='/hell/dashboard', block_durantion=15, rate_limit=True, block_ip=False)
31
+ app.run(debug=True, host='0.0.0.0', port=5001)
wafaHell/globals.py ADDED
@@ -0,0 +1,5 @@
1
+ import os
2
+ from diskcache import Cache
3
+
4
+ cache_path = '/dev/shm/waf_cache' if os.path.exists('/dev/shm') else './waf_cache_temp'
5
+ waf_cache = Cache(cache_path)
wafaHell/logger.py CHANGED
@@ -1,34 +1,122 @@
1
+ from .model import WafLog, get_session
2
+ from datetime import datetime
1
3
  import logging
4
+ import re
5
+ # Handler customizado para salvar no SQLite via SQLAlchemy
6
+ class SQLAlchemyHandler(logging.Handler):
7
+ def __init__(self):
8
+ super().__init__()
9
+ self.session = get_session()
10
+ # Regex para extrair dados da string formatada pelo parse_req
11
+ self.attr_pattern = re.compile(
12
+ r"Attack_type: (?P<type>.*?), IP: (?P<ip>.*?), .*?Path: (?P<path>.*?), Method: (?P<method>.*?), Payload: (?P<payload>.*?), attack_local: (?P<local>.*)"
13
+ )
14
+
15
+ def emit(self, record):
16
+ session = self.session # Certifique-se de instanciar a sessão
17
+ try:
18
+ msg = record.getMessage()
19
+
20
+ # Valores padrão
21
+ ip, path, method, payload, local, attack_type = (None, None, None, msg, None, "Info")
22
+
23
+ # Expandimos a condição para incluir [BLOCKED]
24
+ if any(tag in msg for tag in ["[ATTACK]", "[RATE LIMIT]", "[BLOCKED]"]):
25
+
26
+ def extract(key, text):
27
+ # Regex ajustada para capturar até a vírgula ou fim da tag, permitindo espaços internos
28
+ match = re.search(rf"{key}:?\s*([^,\]]+)", text)
29
+ return match.group(1).strip() if match else None
30
+
31
+ ip = extract("IP", msg)
32
+
33
+ if "[BLOCKED]" in msg:
34
+ attack_type = "IP BLOCK"
35
+ ua = extract("UA", msg)
36
+ payload = f"IP Bloqueado. User-Agent: {ua}" if ua else "IP Bloqueado"
37
+ path = "---"
38
+ method = "---"
39
+ elif "[RATE LIMIT]" in msg:
40
+ attack_type = "RATE LIMIT"
41
+ payload = "Exceeded request limit"
42
+ path = extract("Path", msg)
43
+ method = extract("Method", msg)
44
+
45
+ bucket = datetime.utcnow().strftime('%Y-%m-%d %H:%M')
46
+ log_entry = WafLog(
47
+ level=record.levelname,
48
+ attack_type=attack_type,
49
+ ip=ip,
50
+ path=path,
51
+ method=method,
52
+ payload=payload,
53
+ attack_local=local,
54
+ time_bucket=bucket
55
+ )
56
+
57
+ session.add(log_entry)
58
+ session.commit()
59
+ else:
60
+ # Lógica para [ATTACK]
61
+ attack_type = extract("Attack_type", msg) or "Unknown"
62
+ path = extract("Path", msg)
63
+ method = extract("Method", msg)
64
+
65
+ # Captura o local completo (ex: HEADER 'User-Agent')
66
+ local_match = re.search(r"attack_local:\s*(.+)$", msg)
67
+ local = local_match.group(1).strip() if local_match else extract("attack_local", msg)
68
+
69
+ # Regex específica para payloads que podem conter vírgulas
70
+ payload_match = re.search(r"Payload: (.*?), attack_local:", msg)
71
+ payload = payload_match.group(1) if payload_match else extract("Payload", msg)
72
+
73
+ if not "[RATE LIMIT]" in msg:
74
+ log_entry = WafLog(
75
+ level=record.levelname,
76
+ attack_type=attack_type,
77
+ ip=ip,
78
+ path=path,
79
+ method=method,
80
+ payload=payload,
81
+ attack_local=local,
82
+ # time_bucket fica None aqui para não filtrar ataques normais no banco
83
+ )
84
+
85
+ session.add(log_entry)
86
+ session.commit()
87
+
88
+ except Exception as e:
89
+ session.rollback()
90
+ finally:
91
+ session.close()
2
92
 
3
93
  class Logger:
4
94
  def __init__(self, name="WAF", log_file="waf.log", level=logging.INFO):
5
- # Cria logger com nome
6
95
  self.logger = logging.getLogger(name)
7
96
  self.logger.setLevel(level)
97
+ self.logger.propagate = False
8
98
 
9
- # Evita handlers duplicados ao reinicializar
10
99
  if not self.logger.handlers:
11
- # Handler para console
12
- console_handler = logging.StreamHandler()
13
- console_handler.setLevel(level)
14
-
15
- # Handler para arquivo
16
- file_handler = logging.FileHandler(log_file)
17
- file_handler.setLevel(level)
18
-
19
- # Formato
20
100
  formatter = logging.Formatter(
21
101
  "[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s",
22
102
  datefmt="%H:%M:%S - %d/%m/%Y"
23
103
  )
24
104
 
105
+ # Handler 1: Console
106
+ console_handler = logging.StreamHandler()
25
107
  console_handler.setFormatter(formatter)
26
- file_handler.setFormatter(formatter)
27
-
28
- # Adiciona handlers
29
108
  self.logger.addHandler(console_handler)
109
+
110
+ # Handler 2: Arquivo
111
+ file_handler = logging.FileHandler(log_file)
112
+ file_handler.setFormatter(formatter)
30
113
  self.logger.addHandler(file_handler)
31
114
 
115
+ # # Handler 3: Banco de Dados (A Mágica acontece aqui)
116
+ # db_handler = SQLAlchemyHandler()
117
+ # db_handler.setLevel(logging.INFO) # Salva INFO, WARNING e acima no DB
118
+ # self.logger.addHandler(db_handler)
119
+
32
120
  def info(self, msg):
33
121
  self.logger.info(msg)
34
122