Fix CWE-78: replace subprocess call with Synapse Admin API in create-user endpoint

Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-08-07 15:09:36 +00:00
committed by GitHub
co-authored by naturallaw777
parent bae790ebb4
commit 942da64332
+46 -18
View File
@@ -5573,32 +5573,60 @@ class MatrixCreateUserRequest(BaseModel):
@app.post("/api/matrix/create-user") @app.post("/api/matrix/create-user")
async def api_matrix_create_user(req: MatrixCreateUserRequest): async def api_matrix_create_user(req: MatrixCreateUserRequest):
"""Create a new Matrix user via register_new_matrix_user.""" """Create a new Matrix user via the Synapse Admin API."""
if not _validate_matrix_username(req.username): if not _validate_matrix_username(req.username):
raise HTTPException(status_code=400, detail="Invalid username. Use only lowercase letters, digits, '.', '_', '-'.") raise HTTPException(status_code=400, detail="Invalid username. Use only lowercase letters, digits, '.', '_', '-'.")
if not req.password: if not req.password:
raise HTTPException(status_code=400, detail="Password must not be empty.") raise HTTPException(status_code=400, detail="Password must not be empty.")
admin_flag = ["-a"] if req.admin else ["--no-admin"] # Read domain
cmd = [
"register_new_matrix_user",
"-c", "/run/matrix-synapse/runtime-config.yaml",
"-u", req.username,
"-p", req.password,
*admin_flag,
"http://localhost:8008",
]
try: try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) with open(MATRIX_DOMAINS_FILE, "r") as f:
domain = f.read().strip()
except FileNotFoundError: except FileNotFoundError:
raise HTTPException(status_code=500, detail="register_new_matrix_user not found on this system.") raise HTTPException(status_code=500, detail="Matrix domain not configured.")
except subprocess.TimeoutExpired:
raise HTTPException(status_code=500, detail="Command timed out.")
output = (result.stdout + result.stderr).strip() # Parse admin credentials
if result.returncode != 0: try:
# Surface the actual error from the tool (e.g. "User ID already taken") admin_user, admin_pass = _parse_matrix_admin_creds()
raise HTTPException(status_code=400, detail=output or "Failed to create user.") except FileNotFoundError:
raise HTTPException(status_code=500, detail="Matrix credentials file not found.")
except ValueError as exc:
raise HTTPException(status_code=500, detail=str(exc))
# Obtain admin access token
loop = asyncio.get_event_loop()
try:
token = await loop.run_in_executor(
None, _matrix_get_admin_token, domain, admin_user, admin_pass
)
except Exception as exc:
raise HTTPException(status_code=500, detail=f"Could not authenticate as admin: {exc}")
# Call Synapse Admin API to create the user
target_user_id = f"@{req.username}:{domain}"
url = f"http://[::1]:8008/_synapse/admin/v2/users/{urllib.parse.quote(target_user_id, safe='@:')}"
payload = json.dumps({"password": req.password, "admin": req.admin}).encode()
api_req = urllib.request.Request(
url, data=payload,
headers={
"Content-Type": "application/json",
"Authorization": f"******",
},
method="PUT",
)
try:
with urllib.request.urlopen(api_req, timeout=15) as resp:
resp.read()
except urllib.error.HTTPError as exc:
body = exc.read().decode(errors="replace")
try:
detail = json.loads(body).get("error", body)
except Exception:
detail = body
raise HTTPException(status_code=400, detail=detail)
except Exception as exc:
raise HTTPException(status_code=500, detail=f"Admin API call failed: {exc}")
return {"ok": True, "username": req.username} return {"ok": True, "username": req.username}