fix: sanitise trip slug on input, escape single quotes in YAML frontmatter
Fix D: apply _sanitise_slug() to grav_trip_slug in POST /select before storing in TripState, preventing path traversal via ../sequences. Fix E: add _yaml_str() helper that doubles single quotes; apply to title, location_city, and location_country in both run_export and overwrite_export frontmatter blocks, preventing invalid YAML for values like Xi'an. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from flask import Blueprint, current_app, redirect, render_template, request
|
from flask import Blueprint, current_app, redirect, render_template, request
|
||||||
@@ -7,6 +8,12 @@ from app.state import TripState, Photo, load_state, save_state
|
|||||||
bp = Blueprint("albums", __name__)
|
bp = Blueprint("albums", __name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _sanitise_slug(s: str) -> str:
|
||||||
|
s = s.strip().lower()
|
||||||
|
s = re.sub(r'[^a-z0-9-]+', '-', s)
|
||||||
|
return s.strip('-')
|
||||||
|
|
||||||
|
|
||||||
def _client():
|
def _client():
|
||||||
return ImmichClient(current_app.config["IMMICH_URL"],
|
return ImmichClient(current_app.config["IMMICH_URL"],
|
||||||
current_app.config["IMMICH_API_KEY"])
|
current_app.config["IMMICH_API_KEY"])
|
||||||
@@ -31,7 +38,7 @@ def index():
|
|||||||
@bp.post("/select")
|
@bp.post("/select")
|
||||||
def select():
|
def select():
|
||||||
album_ids = request.form.getlist("album_ids[]")
|
album_ids = request.form.getlist("album_ids[]")
|
||||||
grav_trip_slug = request.form["grav_trip_slug"].strip()
|
grav_trip_slug = _sanitise_slug(request.form["grav_trip_slug"])
|
||||||
start_over = request.form.get("start_over") == "1"
|
start_over = request.form.get("start_over") == "1"
|
||||||
|
|
||||||
if len(album_ids) == 1:
|
if len(album_ids) == 1:
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ def slugify(text: str) -> str:
|
|||||||
return re.sub(r"[\s_-]+", "-", text).strip("-")
|
return re.sub(r"[\s_-]+", "-", text).strip("-")
|
||||||
|
|
||||||
|
|
||||||
|
def _yaml_str(s: str) -> str:
|
||||||
|
return s.replace("'", "''")
|
||||||
|
|
||||||
|
|
||||||
def _client():
|
def _client():
|
||||||
return ImmichClient(
|
return ImmichClient(
|
||||||
current_app.config["IMMICH_URL"],
|
current_app.config["IMMICH_URL"],
|
||||||
@@ -98,19 +102,19 @@ def run_export():
|
|||||||
if group.entry_type == "journal":
|
if group.entry_type == "journal":
|
||||||
frontmatter = (
|
frontmatter = (
|
||||||
f"---\n"
|
f"---\n"
|
||||||
f"title: '{group.title}'\n"
|
f"title: '{_yaml_str(group.title)}'\n"
|
||||||
f"date: '{date_str}'\n"
|
f"date: '{date_str}'\n"
|
||||||
f"template: {template}\n"
|
f"template: {template}\n"
|
||||||
f"published: true\n"
|
f"published: true\n"
|
||||||
f"location_city: '{group.location_city}'\n"
|
f"location_city: '{_yaml_str(group.location_city)}'\n"
|
||||||
f"location_country: '{group.location_country}'\n"
|
f"location_country: '{_yaml_str(group.location_country)}'\n"
|
||||||
f"hero_image: {hero_filename or ''}\n"
|
f"hero_image: {hero_filename or ''}\n"
|
||||||
f"---\n"
|
f"---\n"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
frontmatter = (
|
frontmatter = (
|
||||||
f"---\n"
|
f"---\n"
|
||||||
f"title: '{group.title}'\n"
|
f"title: '{_yaml_str(group.title)}'\n"
|
||||||
f"date: '{date_str}'\n"
|
f"date: '{date_str}'\n"
|
||||||
f"template: {template}\n"
|
f"template: {template}\n"
|
||||||
f"published: true\n"
|
f"published: true\n"
|
||||||
@@ -192,19 +196,19 @@ def overwrite_export():
|
|||||||
if group.entry_type == "journal":
|
if group.entry_type == "journal":
|
||||||
frontmatter = (
|
frontmatter = (
|
||||||
f"---\n"
|
f"---\n"
|
||||||
f"title: '{group.title}'\n"
|
f"title: '{_yaml_str(group.title)}'\n"
|
||||||
f"date: '{date_str}'\n"
|
f"date: '{date_str}'\n"
|
||||||
f"template: {template}\n"
|
f"template: {template}\n"
|
||||||
f"published: true\n"
|
f"published: true\n"
|
||||||
f"location_city: '{group.location_city}'\n"
|
f"location_city: '{_yaml_str(group.location_city)}'\n"
|
||||||
f"location_country: '{group.location_country}'\n"
|
f"location_country: '{_yaml_str(group.location_country)}'\n"
|
||||||
f"hero_image: {hero_filename or ''}\n"
|
f"hero_image: {hero_filename or ''}\n"
|
||||||
f"---\n"
|
f"---\n"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
frontmatter = (
|
frontmatter = (
|
||||||
f"---\n"
|
f"---\n"
|
||||||
f"title: '{group.title}'\n"
|
f"title: '{_yaml_str(group.title)}'\n"
|
||||||
f"date: '{date_str}'\n"
|
f"date: '{date_str}'\n"
|
||||||
f"template: {template}\n"
|
f"template: {template}\n"
|
||||||
f"published: true\n"
|
f"published: true\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user