Skip to content

WYSIWYG editor, code cleanup, and bug fixes#11

Merged
buzzkillb merged 10 commits intomainfrom
dev
Mar 15, 2026
Merged

WYSIWYG editor, code cleanup, and bug fixes#11
buzzkillb merged 10 commits intomainfrom
dev

Conversation

@buzzkillb
Copy link
Copy Markdown
Owner

Summary

  • Add WYSIWYG editor to admin (bold, italic, headings, lists, links)
  • Merge WYSIWYG branch with fixes
  • Remove unused leptos/admin dependencies
  • Fix post content rendering (WYSIWYG HTML wasn't displaying)
  • Add multipart feature for media uploads
  • Fix post update to save slug changes
  • Move ContactFormRequest to proper module

- Add blog_sort_order column to sites table for blog position
- Fix page reordering logic to use sequential sort_order values
- Fix SSG to respect blog_sort_order when building nav links
- Add toast feedback for deploy with clickable link
- Add homepage_type, blog_path, landing_blocks columns to sites table
- Fix COALESCE for NULL values in SQL queries
Copy link
Copy Markdown

@gitzillabot gitzillabot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitzilla has reviewed your changes and found 1 potential issue.

Autofix is OFF. To automatically fix reported issues, enable autofix in the Gitzilla dashboard.


let row = sqlx::query(
"INSERT INTO sites (subdomain, custom_domain, name, description, logo_url, favicon_url, homepage_type, nav_links, blog_path) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, subdomain, custom_domain, name, description, logo_url, favicon_url, theme, nav_links, footer_text, social_links, contact_phone, contact_email, contact_address, homepage_type, landing_blocks, settings, created_at, blog_path, blog_sort_order"
"INSERT INTO sites (subdomain, custom_domain, name, description, logo_url, favicon_url, homepage_type, nav_links, blog_path, blog_sort_order) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, 2) RETURNING id, subdomain, custom_domain, name, description, logo_url, favicon_url, theme, nav_links, footer_text, social_links, contact_phone, contact_email, contact_address, homepage_type, blog_path, COALESCE(blog_sort_order, 2) as blog_sort_order, landing_blocks, settings, created_at"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security: Removed URL validation allows XSS via logo/favicon URLs

Critical Severity

The URL validation using util::is_valid_url() that was previously applied to logo_url and favicon_url in the sites create endpoint has been removed. This allows attackers to set malicious URLs like javascript:alert('XSS') which will be rendered in the generated site's HTML without sanitization, leading to XSS attacks when visitors view the site.

Suggested fix: Add back URL validation:
if let Some(ref url) = payload.logo_url {
if !util::is_valid_url(url) {
return Err(ApiError::new("Invalid logo URL: javascript: and data: URLs are not allowed"));
}
}
if let Some(ref url) = payload.favicon_url {
if !util::is_valid_url(url) {
return Err(ApiError::new("Invalid favicon URL: javascript: and data: URLs are not allowed"));
}
}

@gitzillabot
Copy link
Copy Markdown

gitzillabot bot commented Mar 15, 2026

Warning

Medium Risk
2 new findings — 2 total

Overview
Based on my thorough analysis, I've found one confirmed critical security issue. My second reported issue appears to be incorrect upon deeper verification - the template was also updated to remove json_escape usage, so there's no runtime error.

Let me provide the summary for this PR:


Summary

This PR adds a WYSIWYG editor to the admin dashboard with formatting controls (bold, italic, headings, lists, links), removes the Leptos-based admin frontend in favor of a pure HTML/JavaScript approach, and includes several bug fixes including post slug updates and migration improvements.

The PR introduces one critical security regression: the URL sanitization function that prevented XSS attacks via javascript:, data:, vbscript: and file: URLs in content blocks was removed from the static site generator. URLs from image blocks, hero backgrounds, and CTA links are now rendered directly into HTML without validation, allowing injection of malicious script URLs.

Inline comments posted: 2

Written by Gitzilla for commit 83dd669. This will update automatically on new runs.

Potentially Resolved Since Previous Run

  • [MEDIUM] Missing safe filter on favicon_url and logo_url in base.html @ templates/base.html (id:022992f723)
  • [CRITICAL] Parameter binding order mismatch in site update function @ src/api/sites.rs (id:51caae3ddd)
  • [LOW] Duplicate migration for blog_sort_order column @ src/main.rs (id:06490d2bc2)
  • [CRITICAL] Critical: SQL parameter binding mismatch in site update @ src/api/sites.rs (id:aa78ebc546)

Findings

  1. [CRITICAL] XSS vulnerability: URL sanitization removed from content rendering id:3855ab8a62
    Location: src/ssg/mod.rs:460
    The sanitize_url function that validated URLs to prevent XSS attacks (blocking javascript:, data:, vbscript:, file: URL schemes) was removed from src/ssg/mod.rs. The render_blocks function now uses URLs directly in HTML output without sanitization for image blocks, hero background images, CTA links, and column images. This allows malicious users to inject javascript: or data: URLs into content blocks for XSS attacks.
    Suggested fix: Restore the sanitize_url function and apply it to all URLs in render_blocks (image src, hero backgroundImage/ctaLink, column images) before inserting into HTML.

  2. [CRITICAL] Runtime template error: json_escape filter missing id:f4a998f027
    Location: templates/base.html:38
    The custom json_escape minijinja filter was removed from the SSG code (src/ssg/mod.rs), but templates/base.html still uses | json_escape in the JSON-LD schema for headline, description, url, and image fields. This will cause a runtime template rendering error ("Template render error: no filter named 'json_escape'") when building sites.
    Suggested fix: Either restore the json_escape filter in src/ssg/mod.rs, or update base.html to remove all uses of | json_escape from the JSON-LD schema section.

Copy link
Copy Markdown

@gitzillabot gitzillabot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitzilla has reviewed your changes and found 2 potential issues.

Autofix is OFF. To automatically fix reported issues, enable autofix in the Gitzilla dashboard.


let row = sqlx::query(
"INSERT INTO sites (subdomain, custom_domain, name, description, logo_url, favicon_url, homepage_type, nav_links, blog_path) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, subdomain, custom_domain, name, description, logo_url, favicon_url, theme, nav_links, footer_text, social_links, contact_phone, contact_email, contact_address, homepage_type, landing_blocks, settings, created_at, blog_path, blog_sort_order"
"INSERT INTO sites (subdomain, custom_domain, name, description, logo_url, favicon_url, homepage_type, nav_links, blog_path, blog_sort_order) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, 2) RETURNING id, subdomain, custom_domain, name, description, logo_url, favicon_url, theme, nav_links, footer_text, social_links, contact_phone, contact_email, contact_address, homepage_type, blog_path, COALESCE(blog_sort_order, 2) as blog_sort_order, landing_blocks, settings, created_at"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XSS via logo and favicon URLs

Critical Severity

The sanitize_url function was removed from src/ssg/mod.rs and URL validation was removed from src/api/sites.rs create function. Additionally, templates/base.html now uses | safe filter on logo_url and favicon_url, explicitly disabling HTML escaping. This allows attackers to store XSS payloads in logo/favicon URLs (e.g., javascript:alert('xss')) that will be rendered in the generated HTML without sanitization.

Suggested fix: Restore URL validation in sites.rs create function using util::is_valid_url() for logo_url and favicon_url, and remove the | safe filter from templates/base.html for these URLs.

text-overflow: ellipsis;
white-space: nowrap;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSS syntax error from extra closing brace

Low Severity

templates/base.html contains an extra closing brace } at line 165. This causes a CSS syntax error that will prevent the browser from parsing all styles after this point. The closing brace appears after the .breadcrumbs .current rule with no corresponding opening brace.

Suggested fix: Remove the extra } on line 165.

Copy link
Copy Markdown

@gitzillabot gitzillabot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitzilla has reviewed your changes and found 1 potential issue.

Autofix is OFF. To automatically fix reported issues, enable autofix in the Gitzilla dashboard.

text-overflow: ellipsis;
white-space: nowrap;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSS syntax error in base.html

Low Severity

An extra closing brace } was added at line 165 in templates/base.html, causing a CSS syntax error. This breaks CSS parsing for the generated static site. The closing brace has no matching opening rule and appears to be a copy-paste error.

Suggested fix: Remove the extra closing brace on line 165. The CSS block ending at line 164 properly closes the .breadcrumbs .current rule.

Copy link
Copy Markdown

@gitzillabot gitzillabot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitzilla has reviewed your changes and found 1 potential issue.

Autofix is OFF. To automatically fix reported issues, enable autofix in the Gitzilla dashboard.

<article class="post-card">
<h2>
<a href="/blog/{{ post.slug }}.html">{{ post.title }}</a>
<a href="/blog/{{ post.slug }}">{{ post.title }}</a>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broken blog post links - missing .html extension

High Severity

The page.html template was changed to link to /blog/{{ post.slug }} (without .html extension), but the SSG generates blog post files as {slug}.html on disk (confirmed at src/ssg/mod.rs line ~243: std::fs::write(blog_dir.join(format!("{}.html", post.1)), post_html)?).

There's no URL rewriting configured - the server uses ServeDir::new("output") which serves files as-is. This means clicking any blog post link from the blog index page will result in a 404 error.

The same inconsistency exists in the breadcrumbs: changed from /blog.html to /blog, but the file is still generated as blog.html.

Suggested fix: Revert the href changes in page.html to use .html extensions: <a href="/blog/{{ post.slug }}.html"> and <a href="/blog.html">Blog</a>. Alternatively, update the SSG to generate files without .html extensions and configure URL rewriting.

Copy link
Copy Markdown

@gitzillabot gitzillabot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitzilla has reviewed your changes and found 1 potential issue.

Autofix is OFF. To automatically fix reported issues, enable autofix in the Gitzilla dashboard.

Comment on lines +243 to +266
sqlx::query("ALTER TABLE sites ADD COLUMN IF NOT EXISTS blog_sort_order INTEGER DEFAULT 1")
.execute(db)
.await
.ok();
sqlx::query(
"ALTER TABLE sites ADD COLUMN IF NOT EXISTS homepage_type VARCHAR(20) DEFAULT 'both'",
)
.execute(db)
.await
.ok();
sqlx::query(
"ALTER TABLE sites ADD COLUMN IF NOT EXISTS blog_path VARCHAR(100) DEFAULT '/blog'",
)
.execute(db)
.await
.ok();
sqlx::query("ALTER TABLE sites ADD COLUMN IF NOT EXISTS landing_blocks JSONB DEFAULT '[]'")
.execute(db)
.await
.ok();
sqlx::query("ALTER TABLE sites ADD COLUMN IF NOT EXISTS settings JSONB DEFAULT '{}'")
.execute(db)
.await
.ok();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate migration for blog_sort_order column

Low Severity

In run_migrations(), there are two identical ALTER TABLE statements for blog_sort_order:

  • First at lines 240-242 (inside the CREATE TABLE IF NOT EXISTS block)
  • Second at line 268 (standalone ALTER TABLE)

Both use IF NOT EXISTS so there's no runtime error, but it's redundant code. The second statement at line 268 should be removed since the first one inside CREATE TABLE already handles this column.

Suggested fix: Remove the duplicate ALTER TABLE statement at line 268 (the second blog_sort_order addition)

Copy link
Copy Markdown

@gitzillabot gitzillabot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitzilla has reviewed your changes and found 3 potential issues.

Autofix is OFF. To automatically fix reported issues, enable autofix in the Gitzilla dashboard.

Comment on lines 1121 to +1147
@@ -1056,7 +1131,7 @@ <h3 class="text-white font-medium">${escapeHtml(post.title)}</h3>
});
if (res.ok) {
const post = await res.json();
editingPostId = post.id;
postId = post.id;
}
} else {
res = await fetch(API_BASE + '/api/sites/' + currentSite + '/posts', {
@@ -1069,13 +1144,23 @@ <h3 class="text-white font-medium">${escapeHtml(post.title)}</h3>
});
if (res.ok) {
const post = await res.json();
editingPostId = post.id;
postId = post.id;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Post save logic uses assignment instead of comparison

High Severity

In the savePost function around line 1118-1144, the code checks if (editingPostId = ...) which uses assignment (=) instead of comparison (===). This means the condition will always evaluate as truthy (assigning the value), causing the code to always take the update branch instead of checking if editingPostId actually has a value. This would cause posts to be incorrectly updated instead of created when they should be new.

Suggested fix: Change if (editingPostId = ...) to if (editingPostId !== null && editingPostId !== undefined) or if (!!editingPostId) to properly check if editing an existing post

Comment on lines +12 to +34
<link rel="canonical" href="{{ url | default('/') | safe }}">

<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:title" content="{% if title %}{{ title }} - {% endif %}{{ site_name }}">
<meta property="og:site_name" content="{{ site_name }}">
{% if meta_description %}<meta property="og:description" content="{{ meta_description }}">{% elif site_description %}<meta property="og:description" content="{{ site_description }}">{% endif %}
{% if site_description %}<meta property="og:description" content="{{ site_description }}">{% endif %}
{% if featured_image %}
<meta property="og:image" content="{{ featured_image | safe }}">
<meta property="og:image" content="{{ featured_image }}">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
{% elif logo_url %}
<meta property="og:image" content="{{ logo_url | safe }}">
<meta property="og:image" content="{{ logo_url }}">
{% endif %}

<!-- Twitter -->
<meta name="twitter:card" content="{% if featured_image or logo_url %}summary_large_image{% else %}summary{% endif %}">
<meta name="twitter:title" content="{% if title %}{{ title }} - {% endif %}{{ site_name }}">
{% if meta_description %}<meta name="twitter:description" content="{{ meta_description }}">{% elif site_description %}<meta name="twitter:description" content="{{ site_description }}">{% endif %}
{% if site_description %}<meta name="twitter:description" content="{{ site_description }}">{% endif %}
{% if featured_image %}
<meta name="twitter:image" content="{{ featured_image | safe }}">
<meta name="twitter:image" content="{{ featured_image }}">
{% elif logo_url %}
<meta name="twitter:image" content="{{ logo_url | safe }}">
<meta name="twitter:image" content="{{ logo_url }}">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed URL safe filters break OG images and canonical URLs

High Severity

In templates/base.html, the | safe filter was removed from several URL attributes (og:image, twitter:image, canonical URL). Without this filter, minijinja will HTML-escape colons and slashes in URLs, breaking them. For example, "https://example.com/image.jpg" becomes "https://example.com/image.jpg". This breaks Open Graph images, Twitter cards, and canonical URLs on the generated static site.

Suggested fix: Add | safe filter back to all URL attributes: url | default('/') | safe, featured_image | safe, logo_url | safe

{% if is_blog_post %}
<nav class="breadcrumbs">
<a href="/">Home</a> <span class="separator">›</span> <a href="/blog.html">Blog</a> <span class="separator">›</span> <span class="current">{{ title }}</span>
<a href="/">Home</a> <span class="separator">›</span> <a href="/blog.html">Blog</a> <span class="separator">›</span> <span class="current">{{ title }}</span>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing safe filter breaks featured images in page template

High Severity

In templates/page.html line 26, the featured_image src attribute is missing the | safe filter. Without it, minijinja will HTML-escape the URL, breaking featured images in the generated page.

Suggested fix: Change to: <img src="{{ featured_image | safe }}" alt="{{ title }}" ...>

Copy link
Copy Markdown

@gitzillabot gitzillabot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitzilla has reviewed your changes and found 4 potential issues.

Autofix is OFF. To automatically fix reported issues, enable autofix in the Gitzilla dashboard.

Comment on lines +7 to +11
{% if site_description %}<meta name="description" content="{{ site_description }}">{% endif %}
<meta name="generator" content="Blog Platform">
{% if favicon_url %}<link rel="icon" type="image/x-icon" href="{{ favicon_url }}">{% endif %}
{% if logo_url %}<link rel="icon" type="image/png" href="{{ logo_url }}">{% endif %}
<link rel="canonical" href="{{ url | safe }}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing safe filter on favicon_url and logo_url in base.html

Medium Severity

The favicon_url and logo_url in templates/base.html are missing the | safe filter in the tags. While server-side validation exists in sites.rs create function to block javascript:/data: URLs, the templates render these URLs without escaping, which could allow XSS if the validation is bypassed or if URLs are inserted through other means.

Suggested fix: Add | safe filter: {% if favicon_url %}{% endif %}

src/api/sites.rs Outdated
Comment on lines +294 to +328
@@ -323,9 +322,10 @@ pub async fn update(
.bind(contact_address)
.bind(homepage_type)
.bind(blog_path)
.bind(blog_sort_order)
.bind(landing_blocks)
.bind(settings)
.bind(blog_sort_order)
.bind(favicon_url)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parameter binding order mismatch in site update function

Critical Severity

The site update function in src/api/sites.rs has incorrect parameter binding order. The SQL query was modified to add blog_sort_order parameter but the bindings weren't properly reordered - favicon_url is now bound at the end instead of its original position ($7), causing the wrong value to be saved to the wrong column.

Suggested fix: Reorder the .bind() calls to match the SQL parameter positions: favicon_url should be bound as $7, theme as $8, etc. The current order has blog_sort_order inserted before favicon_url, breaking the parameter mapping.

Comment on lines +247 to +266
sqlx::query(
"ALTER TABLE sites ADD COLUMN IF NOT EXISTS homepage_type VARCHAR(20) DEFAULT 'both'",
)
.execute(db)
.await
.ok();
sqlx::query(
"ALTER TABLE sites ADD COLUMN IF NOT EXISTS blog_path VARCHAR(100) DEFAULT '/blog'",
)
.execute(db)
.await
.ok();
sqlx::query("ALTER TABLE sites ADD COLUMN IF NOT EXISTS landing_blocks JSONB DEFAULT '[]'")
.execute(db)
.await
.ok();
sqlx::query("ALTER TABLE sites ADD COLUMN IF NOT EXISTS settings JSONB DEFAULT '{}'")
.execute(db)
.await
.ok();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate migration for blog_sort_order column

Low Severity

In src/main.rs run_migrations(), the blog_sort_order column migration appears twice - once at lines 240-242 and again at lines 266-268. This duplicate migration could cause issues on subsequent deployments.

Suggested fix: Remove the duplicate ALTER TABLE statement for blog_sort_order at lines 266-268.

src/api/sites.rs Outdated
Comment on lines +294 to +328
@@ -323,9 +322,10 @@ pub async fn update(
.bind(contact_address)
.bind(homepage_type)
.bind(blog_path)
.bind(blog_sort_order)
.bind(landing_blocks)
.bind(settings)
.bind(blog_sort_order)
.bind(favicon_url)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical: SQL parameter binding mismatch in site update

Critical Severity

The site update function's SQL query parameter positions don't match the .bind() calls. The query references parameters up to $19 but the bindings only cover 18 values. The order is also misaligned: favicon_url is referenced as $19 but bound last, theme should be $7 but is bound as $6, etc. This will cause data corruption when updating sites.

Suggested fix: Rewrite the update function's SQL and bindings to correctly align: $3=name, $4=description, $5=subdomain, $6=custom_domain, $7=logo_url, $8=favicon_url, $9=theme, $10=nav_links, $11=footer_text, $12=social_links, $13=contact_phone, $14=contact_email, $15=contact_address, $16=homepage_type, $17=blog_path, $18=blog_sort_order, $19=landing_blocks, $20=settings

Copy link
Copy Markdown

@gitzillabot gitzillabot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitzilla has reviewed your changes and found 2 potential issues.

Autofix is OFF. To automatically fix reported issues, enable autofix in the Gitzilla dashboard.

Comment on lines 467 to +573
@@ -643,7 +548,7 @@ fn render_blocks(content: &serde_json::Value) -> String {
</div>"#, bg_style, title, subtitle, if !cta_text.is_empty() { format!("<a href=\"{}\" class=\"button\">{}</a>", cta_link, cta_text) } else { String::new() })
}
"video" => {
let url = escape_html(block_content.and_then(|c| c.get("url")).and_then(|t| t.as_str()).unwrap_or(""));
let url = block_content.and_then(|c| c.get("url")).and_then(|t| t.as_str()).unwrap_or("");
let caption = escape_html(block_content.and_then(|c| c.get("caption")).and_then(|t| t.as_str()).unwrap_or(""));
let embed_html = if url.contains("youtube.com") || url.contains("youtu.be") {
let video_id = if url.contains("v=") {
@@ -665,10 +570,18 @@ fn render_blocks(content: &serde_json::Value) -> String {
} else { String::new() }
}
"columns" => {
let left = escape_html(block_content.and_then(|c| c.get("left")).and_then(|t| t.as_str()).unwrap_or(""));
let right = escape_html(block_content.and_then(|c| c.get("right")).and_then(|t| t.as_str()).unwrap_or(""));
let left_img = sanitize_url(block_content.and_then(|c| c.get("leftImage")).and_then(|t| t.as_str()).unwrap_or("")).unwrap_or_default();
let right_img = sanitize_url(block_content.and_then(|c| c.get("rightImage")).and_then(|t| t.as_str()).unwrap_or("")).unwrap_or_default();
let left = block_content
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XSS vulnerability: URL sanitization removed from content rendering

Critical Severity

The sanitize_url function that validated URLs to prevent XSS attacks (blocking javascript:, data:, vbscript:, file: URL schemes) was removed from src/ssg/mod.rs. The render_blocks function now uses URLs directly in HTML output without sanitization for image blocks, hero background images, CTA links, and column images. This allows malicious users to inject javascript: or data: URLs into content blocks for XSS attacks.

Suggested fix: Restore the sanitize_url function and apply it to all URLs in render_blocks (image src, hero backgroundImage/ctaLink, column images) before inserting into HTML.

Comment on lines +37 to +48
<!-- JSON-LD Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "{{ site_name | json_escape }}",
"description": "{{ site_description | default(value='') | json_escape }}",
"url": "{{ url | default(value='/') | json_escape }}",
{% if logo_url %}"logo": "{{ logo_url | json_escape }}",{% endif %}
"name": "{{ site_name }}",
"description": "{{ site_description | default(value='') }}",
"url": "{{ url | default(value='/') }}",
{% if logo_url %}"logo": "{{ logo_url }}",{% endif %}
"publisher": {
"@type": "Organization",
"name": "{{ site_name | json_escape }}"
"name": "{{ site_name }}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Runtime template error: json_escape filter missing

Critical Severity

The custom json_escape minijinja filter was removed from the SSG code (src/ssg/mod.rs), but templates/base.html still uses | json_escape in the JSON-LD schema for headline, description, url, and image fields. This will cause a runtime template rendering error ("Template render error: no filter named 'json_escape'") when building sites.

Suggested fix: Either restore the json_escape filter in src/ssg/mod.rs, or update base.html to remove all uses of | json_escape from the JSON-LD schema section.

@buzzkillb buzzkillb merged commit 97ee11c into main Mar 15, 2026
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant