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
|
|
@ -29,19 +29,53 @@ class Handler(http.server.SimpleHTTPRequestHandler):
|
|||
def __init__(self, *args, **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):
|
||||
# Nur PUT auf /templates/* Pfade erlauben
|
||||
if not self.path.startswith('/templates'):
|
||||
self.send_error(403, "Method not allowed for this path")
|
||||
file_path = self._validate_template_path(self.path)
|
||||
if file_path is None:
|
||||
self.send_error(403, "Forbidden: Invalid path")
|
||||
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
|
||||
file_dir = os.path.dirname(file_path)
|
||||
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()
|
||||
|
||||
# Anfragen für /templates.json oder /templates/* umleiten
|
||||
if self.path.startswith('/templates'):
|
||||
# /templates.json ist der Katalog und liegt im web/-Verzeichnis,
|
||||
# /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)
|
||||
|
||||
file_path = self._validate_template_path(self.path)
|
||||
if file_path is not None:
|
||||
if os.path.exists(file_path) and not os.path.isdir(file_path):
|
||||
try:
|
||||
with open(file_path, 'rb') as f:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue