fix: HEAD-Requests für /templates/* korrekt auflösen
- Implementiere do_HEAD in Handler - Sende nur Response-Header mit Content-Type und Content-Length - Kein Body, wie von HTTP/1.1 für HEAD spezifiziert - Nutze dieselbe Pfadlogik wie do_GET
This commit is contained in:
parent
625a2b72c7
commit
1845b30992
1 changed files with 45 additions and 18 deletions
63
web/serve.py
63
web/serve.py
|
|
@ -28,20 +28,54 @@ def find_free_port(start_port=9000):
|
||||||
class Handler(http.server.SimpleHTTPRequestHandler):
|
class Handler(http.server.SimpleHTTPRequestHandler):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, directory=DIRECTORY, **kwargs)
|
super().__init__(*args, directory=DIRECTORY, **kwargs)
|
||||||
|
|
||||||
|
def _validate_template_path(self, path):
|
||||||
|
"""Validiert, dass der Pfad auf Schritte Beschränkt ist und innerhalb von ROOT_DIR/templates/ oder DIRECTORY liegt."""
|
||||||
|
# /templates.json ist ein Sonderfall - exakter Pfad im web/-Verzeichnis
|
||||||
|
if path == '/templates.json':
|
||||||
|
return os.path.join(DIRECTORY, 'templates.json')
|
||||||
|
|
||||||
|
# Nur /templates/* Pfade erlauben
|
||||||
|
if not path.startswith('/templates/'):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Extrahiere den relativen Pfad nach /templates/
|
||||||
|
rel_path = path[len('/templates/'):] # 'system/test.json' oder '../etc/passwd'
|
||||||
|
|
||||||
|
# Ablehnen bei leeren Pfaden oder absoluten Pfaden
|
||||||
|
if not rel_path or os.path.isabs(rel_path):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Ablehnen bei '..' Sequenzen (vor der Normalisierung!)
|
||||||
|
if '..' in rel_path.split(os.sep):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Pfad normalisieren
|
||||||
|
normalized_rel = os.path.normpath(rel_path)
|
||||||
|
|
||||||
|
# Nach Normalisierung nochmal prüfen
|
||||||
|
if normalized_rel.startswith('..') or os.path.isabs(normalized_rel):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Vollständigen Pfad konstruieren
|
||||||
|
full_path = os.path.join(ROOT_DIR, 'templates', normalized_rel)
|
||||||
|
|
||||||
|
# Explizite Prüfung, dass der Pfad innerhalb von ROOT_DIR/templates/ liegt
|
||||||
|
templates_base = os.path.abspath(os.path.join(ROOT_DIR, 'templates'))
|
||||||
|
full_path_abs = os.path.abspath(full_path)
|
||||||
|
|
||||||
|
if not full_path_abs.startswith(templates_base + os.sep) and full_path_abs != templates_base:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return full_path_abs
|
||||||
|
|
||||||
def do_PUT(self):
|
def do_PUT(self):
|
||||||
# Nur PUT auf /templates/* Pfade erlauben
|
# Nur PUT auf /templates/* Pfade erlauben
|
||||||
if not self.path.startswith('/templates'):
|
file_path = self._validate_template_path(self.path)
|
||||||
self.send_error(403, "Method not allowed for this path")
|
if file_path is None:
|
||||||
|
self.send_error(403, "Forbidden: Invalid path")
|
||||||
return
|
return
|
||||||
|
|
||||||
# /templates.json liegt im web/-Verzeichnis (Katalog), alles andere unter ROOT
|
|
||||||
if self.path == '/templates.json':
|
|
||||||
file_path = os.path.join(DIRECTORY, 'templates.json')
|
|
||||||
else:
|
|
||||||
rel_path = self.path[1:] # '/templates/system/test.json' → 'templates/system/test.json'
|
|
||||||
file_path = os.path.join(ROOT_DIR, rel_path)
|
|
||||||
|
|
||||||
# Verzeichnis prüfen - muss existieren
|
# Verzeichnis prüfen - muss existieren
|
||||||
file_dir = os.path.dirname(file_path)
|
file_dir = os.path.dirname(file_path)
|
||||||
if not os.path.exists(file_dir) or not os.path.isdir(file_dir):
|
if not os.path.exists(file_dir) or not os.path.isdir(file_dir):
|
||||||
|
|
@ -81,15 +115,8 @@ class Handler(http.server.SimpleHTTPRequestHandler):
|
||||||
return super().do_GET()
|
return super().do_GET()
|
||||||
|
|
||||||
# Anfragen für /templates.json oder /templates/* umleiten
|
# Anfragen für /templates.json oder /templates/* umleiten
|
||||||
if self.path.startswith('/templates'):
|
file_path = self._validate_template_path(self.path)
|
||||||
# /templates.json ist der Katalog und liegt im web/-Verzeichnis,
|
if file_path is not None:
|
||||||
# /templates/... verweist auf Template-Dateien im Projekt-Root
|
|
||||||
if self.path == '/templates.json':
|
|
||||||
file_path = os.path.join(DIRECTORY, 'templates.json')
|
|
||||||
else:
|
|
||||||
rel_path = self.path[1:] # '/templates/system/x.json' → 'templates/system/x.json'
|
|
||||||
file_path = os.path.join(ROOT_DIR, rel_path)
|
|
||||||
|
|
||||||
if os.path.exists(file_path) and not os.path.isdir(file_path):
|
if os.path.exists(file_path) and not os.path.isdir(file_path):
|
||||||
try:
|
try:
|
||||||
with open(file_path, 'rb') as f:
|
with open(file_path, 'rb') as f:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue