Merge commit from fork

User-controlled template sources were rendered with the unrestricted
jinja2.Template, allowing authenticated users to achieve remote code
execution. All rendering now goes through a single shared
RenderSandboxedTemplate() helper backed by jinja2.sandbox, which denies
unsafe attribute access (e.g. __globals__, __class__) while keeping the
intended templating features working:

- POST /api/email/preview: Body and Subject (GHSA-8q4m-52pw-hfmw)
- POST /api/email/send: Body and Subject (same sink class)
- POST /api/updateWireguardConfigurationInfo (OverridePeerSettings):
  values were rendered before validation, so the payload executed
  before __validateOverridePeerSettings could reject it
- Peer.downloadPeer(): rendered configuration file includes raw
  OverridePeerSettings values; also rendered for dashboard clients
  via GetAssignedPeers

Verified: lipsum.__globals__, __class__.__mro__, TemplateReference,
cycler/joiner/namespace escape chains all raise SecurityError; normal
templates ({{ configuration.* }}, {{ peer.* }}, filters, loops) render
unchanged.

Co-authored-by: DaanSelen <80752476+DaanSelen@users.noreply.github.com>
This commit is contained in:
Randark
2026-10-26 10:29:51 +01:00
committed by GitHub
co-authored by DaanSelen
parent 4ee6b462dc
commit dded6a86df
4 changed files with 28 additions and 15 deletions
+3 -3
View File
@@ -3,7 +3,6 @@ WireGuard Configuration
"""
from typing import Any
import jinja2
import sqlalchemy, random, shutil, configparser, ipaddress, os, subprocess, time, re, uuid, psutil, traceback
from zipfile import ZipFile
from datetime import datetime, timedelta
@@ -21,7 +20,8 @@ from .Utilities import StringToBoolean, \
ValidateDNSAddress, \
ValidateEndpointAllowedIPs, \
CheckAddress, \
CheckPeerKey
CheckPeerKey, \
RenderSandboxedTemplate
from .WireguardConfigurationInfo import WireguardConfigurationInfo, PeerGroupsClass
from .DashboardWebHooks import DashboardWebHooks
@@ -1242,7 +1242,7 @@ class WireguardConfiguration:
elif key == "OverridePeerSettings":
for (key, val) in value.items():
try:
status, msg = self.__validateOverridePeerSettings(key, jinja2.Template(val).render(configuration=self.toJson()))
status, msg = self.__validateOverridePeerSettings(key, RenderSandboxedTemplate(val, configuration=self.toJson()))
if not status:
return False, msg, key
except Exception as e: