From 1eafaefff8eb44ded2f8b404beae871bda89933b Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sun, 10 May 2026 03:04:58 +0700 Subject: [PATCH] fix: post-v0.2.0 review P1s + CI hygiene (v0.2.1) Security: - render-link: drop `| safeHTML` on .Text (self-XSS surface w/ Goldmark unsafe:true) - projects.html: safeURL + noreferrer + target=_blank on repo/demo - comments: require repo+repoId+categoryId in gate (prevent broken Giscus iframe) - htmltest-action: pin to commit SHA 31be84a (supply-chain) Fixed: - seo.html: nil-safe \$authorURL chain (no nil.url template error) - nav.html: relURL on Menu.URL (sub-path deploy correctness) - pages.yml: drop dead if-find Pagefind guard --- .github/workflows/pages.yml | 11 +++-------- CHANGELOG.md | 21 +++++++++++++++++++++ layouts/_markup/render-link.html | 2 +- layouts/_partials/comments.html | 2 +- layouts/_partials/head/seo.html | 5 ++++- layouts/_partials/home/projects.html | 4 ++-- layouts/_partials/nav.html | 2 +- 7 files changed, 33 insertions(+), 14 deletions(-) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 1c4d465..6f74071 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -45,12 +45,7 @@ jobs: run: hugo --gc --minify --baseURL "https://tiennm99.github.io/tsuki/" - name: Build Pagefind index - run: | - if find exampleSite/public -name "*.html" -type f | head -1 | grep -q .; then - npx pagefind --site exampleSite/public - else - echo "No HTML files found yet (layouts not implemented). Skipping Pagefind." - fi + run: npx pagefind --site exampleSite/public - name: Assert CSS bundle budget (≤ 4200 B gz) run: | @@ -70,8 +65,8 @@ jobs: run: ./scripts/smoke-tests.sh exampleSite/public - name: htmltest (broken internal links + HTML5 validation) - # TODO before v0.2.0 tag: pin @master to a commit SHA (supply-chain hygiene). - uses: wjdp/htmltest-action@master + # Pinned to master SHA (2026-05-10) for supply-chain hygiene. Refresh periodically. + uses: wjdp/htmltest-action@31be84a95c860a331e0cf9a99f71e3eb39d2f86b with: config: .htmltest.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 95efe60..a6a5f3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,27 @@ All notable changes to tsuki will be documented here. Format follows [Keep a Cha ## [Unreleased] +## [0.2.1] — 2026-05-10 + +Patch release. Closes 5 P1 correctness/security findings from the post-v0.2.0 review plus 2 CI hygiene items. No new features. + +### Security + +- **Render-link no longer trusts inline HTML in link text** — `layouts/_markup/render-link.html` dropped `| safeHTML` on `.Text`. With Goldmark `unsafe: true`, the prior pipeline allowed `[](url)` inner-text to execute. Side effect: italic/emphasis-in-link (`[*x*](url)`) now renders as plain text rather than ``. Restore by overriding the render hook if needed. Self-XSS in single-author themes; consistency fix vs `render-image.html`. +- **Project links sanitize URL + add `noreferrer`** — `layouts/_partials/home/projects.html` pipes `repo`/`demo` URLs through `safeURL` and emits `rel="noopener noreferrer" target="_blank"`. Closes inconsistency with the `render-link` policy and prevents referer leakage to project destinations. +- **Comments gate tightened** — `layouts/_partials/comments.html` now requires `repo`, `repoId`, AND `categoryId` in addition to `enable`. Prevents broken Giscus iframes when partially configured. +- **htmltest GitHub Action pinned to commit SHA** — `.github/workflows/pages.yml` no longer tracks `wjdp/htmltest-action@master`; pinned to `31be84a` for supply-chain hygiene. + +### Fixed + +- **`seo.html` `$authorURL` chain hardened** — refactored fragile `site.Params.profile.url | default ...` chain into nil-safe `with` blocks. Site builds no longer risk `nil.url` template error when consumers follow the documented `data/profile.yaml`-only configuration. +- **Header navigation respects sub-path deploys** — `layouts/_partials/nav.html` pipes `Menu.URL` through `relURL`. Sites deploying under a sub-path (e.g., GitHub Pages `/repo/`) with `url:`-form menu entries now route correctly. +- **CI Pagefind step simplified** — `.github/workflows/pages.yml` dropped the dead `if find ... | head -1 | grep -q .` guard. Layouts exist; the else branch never triggered. + +## [0.2.0] — 2026-05-09 + +SEO baseline, accessibility polish, author UX, discovery, distribution prep, CI hardening. See [v0.2.0 tag](https://github.com/tiennm99/tsuki/releases/tag/v0.2.0) for highlights. + ### Changed - **TOC gate consolidated** to `_partials/toc-enabled.html` — single source for the `params.toc.{enable,minWordCount}` + per-page `toc: false` predicate, called from `single.html` (TOC partial) and `_partials/footer.html` (toc-active.js loader). Was duplicated 6-line logic in two sites; now one partial. No behavior change. diff --git a/layouts/_markup/render-link.html b/layouts/_markup/render-link.html index 7b58237..db8b858 100644 --- a/layouts/_markup/render-link.html +++ b/layouts/_markup/render-link.html @@ -7,5 +7,5 @@ {{ .Text | safeHTML }} +>{{ .Text }} {{- /**/ -}} diff --git a/layouts/_partials/comments.html b/layouts/_partials/comments.html index 9e736ec..619a1ab 100644 --- a/layouts/_partials/comments.html +++ b/layouts/_partials/comments.html @@ -1,5 +1,5 @@ {{- $g := site.Params.comments.giscus -}} -{{- if and $g $g.enable -}} +{{- if and $g $g.enable $g.repo $g.repoId $g.categoryId -}} {{- if ne .Params.comments false -}}

{{ i18n "comments" | default "Bình luận" }}

diff --git a/layouts/_partials/head/seo.html b/layouts/_partials/head/seo.html index 97741ed..0259a70 100644 --- a/layouts/_partials/head/seo.html +++ b/layouts/_partials/head/seo.html @@ -61,7 +61,10 @@ {{- /* JSON-LD Article — only on single posts (IsPage + Kind page). */ -}} {{- if and .IsPage (eq .Kind "page") -}} {{- $authorName := site.Params.author | default (and site.Data.profile site.Data.profile.name) | default site.Title -}} -{{- $authorURL := site.Params.profile.url | default (and site.Data.profile site.Data.profile.url) | default site.Home.Permalink -}} +{{- $authorURL := "" -}} +{{- with site.Params.profile -}}{{- $authorURL = .url -}}{{- end -}} +{{- if not $authorURL -}}{{- with site.Data.profile -}}{{- $authorURL = .url -}}{{- end -}}{{- end -}} +{{- if not $authorURL -}}{{- $authorURL = site.Home.Permalink -}}{{- end -}} {{- $keywords := slice -}} {{- range .GetTerms "tags" -}}{{- $keywords = $keywords | append .LinkTitle -}}{{- end -}} {{- $publisher := dict "@type" "Organization" "name" site.Title -}} diff --git a/layouts/_partials/home/projects.html b/layouts/_partials/home/projects.html index 90b9fb1..2da9e8d 100644 --- a/layouts/_partials/home/projects.html +++ b/layouts/_partials/home/projects.html @@ -27,10 +27,10 @@ diff --git a/layouts/_partials/nav.html b/layouts/_partials/nav.html index a46b7b9..a054968 100644 --- a/layouts/_partials/nav.html +++ b/layouts/_partials/nav.html @@ -3,7 +3,7 @@ {{- range site.Menus.main }} {{- $current := or ($.IsMenuCurrent "main" .) ($.HasMenuCurrent "main" .) }}
  • - {{ .Name }} + {{ .Name }}
  • {{- end }}