fixed some minor security issues

This commit is contained in:
Gamosoft
2026-08-25 14:59:33 +02:00
parent 4daa9b11e6
commit 9d3ff3ced1
3 changed files with 35 additions and 12 deletions
+13 -2
View File
@@ -10,6 +10,7 @@ replaced with placeholder HTML since they would make exports too large.
import base64
import logging
import re
from html import escape
from pathlib import Path
from typing import Optional, Tuple
import mimetypes
@@ -372,7 +373,15 @@ def convert_wikilinks_to_html(markdown_content: str) -> str:
def replace_wikilink(match):
target = match.group(1).strip()
display = match.group(2).strip() if match.group(2) else target
return f'<a href="#" class="wikilink" title="{target}" style="color: var(--accent-primary, #0366d6); text-decoration: none; border-bottom: 1px dashed currentColor;">{display}</a>'
# User-controlled note text lands in HTML attributes/body on /share and
# export pages — escape so a crafted wikilink cannot break out of the tag.
safe_target = escape(target, quote=True)
safe_display = escape(display)
return (
f'<a href="#" class="wikilink" title="{safe_target}" '
f'style="color: var(--accent-primary, #0366d6); text-decoration: none; '
f'border-bottom: 1px dashed currentColor;">{safe_display}</a>'
)
code_blocks: list = []
@@ -419,6 +428,8 @@ def generate_export_html(
.replace('$', '\\$')
.replace('</', '<\\/') # Prevent </script> breaking
)
# Filename stems are user-controlled; keep </title> / attribute breakouts out of <head>.
safe_title = escape(title, quote=True)
highlight_theme = 'github-dark' if is_dark else 'github'
mermaid_theme = 'dark' if is_dark else 'default'
@@ -464,7 +475,7 @@ def generate_export_html(
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{title}</title>
<title>{safe_title}</title>
<!-- Highlight.js for code syntax highlighting -->
<link rel="stylesheet" href="{hljs_css}">
+11 -1
View File
@@ -718,8 +718,18 @@ async def login(request: Request, password: str = Form(...)):
@app.get("/logout", include_in_schema=False)
async def logout_get_not_allowed():
"""Reject GET so the SPA catch-all cannot serve the app here, and so
<img src="/logout"> cannot clear the session (use POST instead)."""
return Response(status_code=405, headers={"Allow": "POST"})
@app.post("/logout", include_in_schema=False)
async def logout(request: Request):
"""Log out the current user"""
"""Log out the current user.
POST-only so a third-party page cannot force logout via a GET (e.g. <img src>).
"""
request.session.clear()
return RedirectResponse(url="/login", status_code=303)
+11 -9
View File
@@ -2382,15 +2382,17 @@
<!-- Logout (if auth enabled) - uses authEnabled from main app state -->
<div x-show="authEnabled" class="mb-4">
<label class="block text-xs font-medium mb-2" style="color: var(--text-secondary);" x-text="t('settings.account')"></label>
<a
href="/logout"
class="flex items-center justify-center gap-2 px-3 py-2 text-sm rounded transition-colors"
style="background-color: var(--bg-primary); color: var(--text-primary); border: 1px solid var(--border-primary);"
onmouseover="this.style.backgroundColor='var(--bg-hover)'"
onmouseout="this.style.backgroundColor='var(--bg-primary)'"
x-text="'🔒 ' + t('settings.logout')"
>
</a>
<form method="POST" action="/logout">
<button
type="submit"
class="w-full flex items-center justify-center gap-2 px-3 py-2 text-sm rounded transition-colors cursor-pointer"
style="background-color: var(--bg-primary); color: var(--text-primary); border: 1px solid var(--border-primary);"
onmouseover="this.style.backgroundColor='var(--bg-hover)'"
onmouseout="this.style.backgroundColor='var(--bg-primary)'"
x-text="'🔒 ' + t('settings.logout')"
>
</button>
</form>
</div>
</div>