Skip to content

fix(v3.18.1): wave-5 operator feedback (CSRF, orphan cleanup)#104

Merged
vnykmshr merged 9 commits into
mainfrom
fix/v3.18.1
May 19, 2026
Merged

fix(v3.18.1): wave-5 operator feedback (CSRF, orphan cleanup)#104
vnykmshr merged 9 commits into
mainfrom
fix/v3.18.1

Conversation

@vnykmshr
Copy link
Copy Markdown
Collaborator

Summary

Four issues from the log.1mb.dev operator: CSRF middleware preserves a valid cookie across GETs (#101); banner uploads no longer orphan on swap or remove (#103) via per-action unlink + startup sweep with reference tracking; tag-cloud anchors emit data-count for CSS weighting (#100); autosave warning element contract pinned by test (#102 — symptom not reproducible from code).

Changes

  • CSRF cookie stabilitymiddleware/middleware.go reuses an existing 64-char hex cookie on GET/HEAD; rotation only on absence/malformed. SPA <main>-only swap no longer staled meta against a rotated cookie.
  • Banner per-action unlinkUpdateArticle returns the prev banner; compose.UnlinkOwnedUpload removes /uploads/<slug>/<filename>-shape files only, ContainSlugPath-protected, ENOENT-silent. External URLs, /static/..., and bare frontmatter filenames are operator-managed.
  • Startup orphan sweep — walks <UPLOAD_PATH>/<slug>/, removes files no article references via banner field or body content (markdown image, raw HTML img/src, plain link). URL-decoded, traversal-rejected. Async post-bind, 60s timeout, panic-recovered. ORPHAN_SWEEP_DISABLED=true opts out.
  • Tag-cloud data-count — one-line attribute on .tag-cloud-item.

Test Plan

  • CI green (lint, test Go 1.26, security, build)
  • Manual: /admin/drafts → Edit → Upload banner → 200 (validates Banner upload returns 403 after navigating from /admin/drafts → Edit (CSRF token rotation) #101)
  • Manual: swap banner A→B on an article, save, confirm A unlinked on disk
  • Manual: clear banner on an article, save, confirm file unlinked
  • Manual: restart with orphan PNGs on disk → log shows orphan sweep complete cleaned=N
  • Manual: /tags → inspect → data-count="N" on each .tag-cloud-item
  • Manual: non-compose POST (login, contact submit) still validates CSRF

Closes #100, #101, #102, #103.

vnykmshr added 9 commits May 19, 2026 17:48
SPA navigations swap <main> only; rotating the cookie on GET left the
<head> meta token stale, mismatching subsequent POSTs (banner upload 403).

Closes #101.
Server template renders #compose-save-warning with `hidden`
unconditionally; only compose.js's localStorage write-failure handler
flips it visible. Reporter's #102 symptom is not reproducible from the
code — autosave is purely client-side, no network path links it to #101.

Refs #102.
UpdateArticle now returns the previous banner value alongside its error.
HandleEdit passes that to UnlinkOwnedUpload, which removes the file only
when it matches /uploads/<slug>/<filename> shape and lives inside the
slug's upload dir. External URLs, /static/... paths, and bare filenames
are operator-managed and left alone.

Closes #103.
Walks <UPLOAD_PATH>/<slug>/ once per boot and removes files no article
references via banner field or body content (markdown image, raw HTML
img/src, plain link). URL-decoded, traversal-rejected. Bounded by 60s
timeout, runs async post-bind. Disable with ORPHAN_SWEEP_DISABLED=true.

Refs #103.
Lets forkers size or weight tags via CSS attribute selectors without
JS. Operator request, no semantics change for existing markup.

Closes #100.
Wave-5 operator feedback patch: #100, #101, #102, #103.
Best-effort cleanup must not crash the server. An unrecovered panic in
runOrphanSweep would propagate to the runtime and bring down the
process. Logged at Error level so it still surfaces in triage.
GetAllArticles() applies the v3.13.0 DedicatedRouteArticle predicate and
omits type:page content. Without GetPages() in the union, Page banner
uploads appeared unreferenced and were swept on every restart.
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.

tags: emit data-count on .tag-cloud-item for CSS-side weighting

1 participant