Skip to content

chore: upgrade Hugo to 0.164.0 and convert HTML content to markdown - #636

Merged
Timur Tukaev (tym83) merged 5 commits into
mainfrom
chore/hugo-upgrade
Aug 3, 2026
Merged

chore: upgrade Hugo to 0.164.0 and convert HTML content to markdown#636
Timur Tukaev (tym83) merged 5 commits into
mainfrom
chore/hugo-upgrade

Conversation

@tym83

@tym83 Timur Tukaev (tym83) commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

The pinned Hugo version could not move past 0.161 because the site carries content files served as text/html. Hugo 0.162.0 denies that media type by default, as the fix for an XSS vulnerability. This converts those files to markdown and lifts the pin to 0.164.0.

Why this matters

Hugo 0.162.0 fixed CVE-2026-50133 — XSS through text/html content files, affecting every release before it — and introduced the security.allowContent whitelist with text/html denied by default. Fourteen files here declared that media type: the five localized home pages and nine versioned roadmap pages. So the build only kept working by staying on a release that still carries the flaw.

Two more security fixes come along with the upgrade: Go moves to 1.26.3, which closes CVE-2026-39826 and CVE-2026-39823, both escaper bypasses in html/template.

What

  • Renamed the fourteen .html content files to .md. Goldmark already runs with unsafe: true, so the inline HTML in these pages renders exactly as before.
  • On the home pages, reduced body indentation inside the wrapper divs from five spaces to two. At four or more spaces markdown would read those lines as code blocks. Verified in the rendered output: no code blocks appear, and the community section renders in full across all five languages.
  • Raised HUGO_VERSION to 0.164.0 in netlify.toml and .github/workflows/hugo.yaml.
  • Added a security.node.permissions block widening Node read access.

The Node permissions change needs your eyes

Since 0.161.0 the Node asset tools run under Node's permission model with read access limited to the project directory. That default is not enough here: Docsy arrives as a Hugo module and its SCSS is served from the module cache outside the project tree, so PostCSS fails with Use --allow-fs-read to manage permissions.

I widened read access rather than pinning the exact set of paths, and I want to be straight about it: this is broader than necessary. I tried two narrower approaches first — relocating the module cache inside the project directory, and adding an explicit postcss.config.js to stop postcss-cli from searching upward — and neither fixed it. Writes stay denied, and addons, child processes and workers keep Hugo's defaults, so the exposure is read-only. Narrowing this to a concrete path list would be a good follow-up, and if one of you knows the right set offhand, I will happily take the correction.

Verification

Full local build on 0.164.0: 1705 pages, zero errors, all five languages render, roadmap pages render, home page intact.

Note that the two version pins are the only places the version is declared, so CI and Netlify previews both move together with this change.

Summary by CodeRabbit

  • New Features

    • Added roadmap pages across all supported documentation versions, including links to roadmap details, feedback channels, and community meetings.
  • Documentation

    • Standardized community section formatting across localized pages without changing displayed content.
    • Updated page metadata and source references.
  • Chores

    • Updated the documentation build environment to Hugo 0.164.0.
    • Improved asset processing permissions for successful site builds.

Hugo 0.162.0 fixed CVE-2026-50133, an XSS through content files served as
text/html, and denies that media type by default from then on. The site
carried fourteen such files — the five home pages and nine roadmap pages —
so it could only keep building by staying on a version with the flaw.

Converting them to markdown removes the reason to hold back. Goldmark
already runs with unsafe: true, so the inline HTML in these pages renders
as before. Body indentation inside the wrapper divs on the home pages was
reduced to two spaces so markdown does not read those lines as code blocks;
the rendered output has no code blocks and the community section is intact.

Node asset tools have run under Node's permission model since 0.161.0, which
the module-cached Docsy SCSS does not satisfy, so read access is widened
while writes stay denied.

Verified with a full local build on 0.164.0: 1705 pages, no errors, all five
languages and the roadmap pages render.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: tym83 <6355522@gmail.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@netlify

netlify Bot commented Aug 3, 2026

Copy link
Copy Markdown

Deploy Preview for cozystack ready!

Name Link
🔨 Latest commit d5c5577
🔍 Latest deploy log https://app.netlify.com/projects/cozystack/deploys/6a7065558f52d50008d778ad
😎 Deploy Preview https://deploy-preview-636--cozystack.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@IvanHunters, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 18 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 11ad6987-f441-4a12-a737-fe72eba890d9

📥 Commits

Reviewing files that changed from the base of the PR and between 1ffe4d7 and d5c5577.

📒 Files selected for processing (9)
  • .github/workflows/hugo.yaml
  • CLAUDE.md
  • CONTRIBUTING.md
  • content/de/_index.md
  • content/en/_index.md
  • content/hi/_index.md
  • content/ru/_index.md
  • content/zh-cn/_index.md
  • netlify.toml
📝 Walkthrough

Walkthrough

The site build now uses Hugo Extended 0.164.0 and permits unrestricted Node filesystem reads. Roadmap pages were added for the next and v0–v1.6 documentation versions. Localized homepages received digest updates and community-section indentation normalization.

Changes

Site update

Layer / File(s) Summary
Hugo build settings
.github/workflows/hugo.yaml, hugo.yaml, netlify.toml
The build uses Hugo Extended 0.164.0. Hugo Node permissions allow unrestricted filesystem reads.
Roadmap documentation pages
content/en/docs/{next,v0,v1.0,v1.1,v1.2,v1.3,v1.4,v1.5,v1.6}/roadmap.md
Roadmap pages include metadata and links for the project roadmap, feedback, and community meetings.
Localized homepage normalization
content/{de,en,hi,ru,zh-cn}/_index.md
Source digests were updated where required. Community-section indentation was normalized without changing displayed content or component structure.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: kvaps, lllamnyp, myasnikovdaniil

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the Hugo upgrade and the HTML-to-Markdown conversion, which are the main changes in the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/hugo-upgrade

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@IvanHunters

Copy link
Copy Markdown
Contributor

Review

The Hugo upgrade and the reasoning behind it are sound, and the HTML-to-markdown conversion is safe. There is one blocking issue that a Hugo build alone does not catch, plus notes on the item you flagged.

Blocker: i18n-lint is red (stale translation digests)

hack/check-i18n.sh fails on content/{de,hi,ru,zh-cn}/_index.md.

Root cause: the check hashes the whole English source content/en/_index.md (sha256) and compares it to the source_digest: front-matter field recorded in each translation. Reducing the body indentation in en/_index.md from five spaces to two changed the file bytes, so its digest is now 9fe03c36... instead of the recorded 348771fb.... The four translations still carry the old digest, so they are reported as stale. Note this is triggered by the whitespace edit, not by the rename: a pure git mv would keep the bytes and the digest would still match.

Fix (mechanical, no real re-translation needed since the content is equivalent):

hack/check-i18n.sh update-digests

then commit the updated content/{de,hi,ru,zh-cn}/_index.md (only the source_digest line changes). Worth adding hack/check-i18n.sh to the local pre-push routine, since the Hugo build does not run it and the "1705 pages, zero errors" check misses this class of problem.

On allowRead: ['*'] (not blocking)

Relative to current main this is a net tightening, not a loosening: on 0.160.1 there is no Node permission model at all, so the tools have unrestricted access today. After this PR read is open but write, addons, child processes and workers stay denied. So there is no security regression here, the opposite.

Residual risk of '*': a compromised build-time Node dependency (e.g. a PostCSS plugin) could read arbitrary files on the build host and exfiltrate them over the network, since network access is not gated by fs permissions. Low risk for a public docs site in an ephemeral CI/Netlify sandbox, but it does reduce defense-in-depth. Accepting it now and narrowing later, as the description proposes, is reasonable.

On narrowing to a concrete path set: the mounted assets/scss/docsy is a virtual mount; Node reads the real file in the Hugo module cache (${HUGO_CACHEDIR}/modules/filecache/...), and that real path is exactly what Node prints in the Use --allow-fs-read error. The reliable method is to narrow allowRead back to the project default, reproduce the failure, read the exact denied path from the error, and allowlist that directory's glob. Do this both locally and on a Netlify deploy-preview and union the globs, because the module cache root differs per environment. The earlier "relocate the cache into the project" attempt most likely set HUGO_CACHEDIR to a project-relative dir without adding the matching allowRead glob; retrying with HUGO_CACHEDIR=./_cache in both pins (netlify.toml and the workflow) plus an explicit allowRead: ['./_cache/**'] is worth another shot.

Minor

  • The html/template fixes ship inside the Hugo 0.164.0 binary (compiled with Go 1.26.3), not through Netlify's GO_VERSION. Leaving GO_VERSION = "1.23" in netlify.toml is correct and consistent, since it governs Go tooling in the site build rather than Hugo itself. Flagging so nobody "fixes" it by bumping GO_VERSION.

Verified and clean

  • All 14 conversions are safe. The nine roadmap files are pure renames (100% similarity) with trivial prose plus inline <a>/<br/>, no markdown-active constructs. The five home pages have the body indentation reduced to two spaces in every language (zero body lines with 4+ leading spaces in any of them), so no code-block artifacts appear. The 4-space and - lines are YAML front matter, which Goldmark does not render.
  • goldmark.renderer.unsafe: true is present, so the inline HTML renders as before.
  • Single security: key, no duplicate.
  • Version bumped consistently in both pins (netlify.toml and the workflow).
  • Netlify deploy-preview is green.

Only the red i18n-lint blocks the merge; it is a one-command fix plus committing four files.

The home page indentation fix changed the byte content of the English
source content/en/_index.md, so its sha256 no longer matched the
source_digest recorded in the de, hi, ru and zh-cn translations. That
made hack/check-i18n.sh report the four translations as stale and turned
the i18n-lint check red.

The English change is whitespace only (five-space to two-space body
indentation to avoid markdown code blocks), so the translations remain
semantically current. Refresh their recorded source_digest to match;
no re-translation is required.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@hugo.yaml`:
- Around line 16-22: Replace the unrestricted wildcard under
security.node.permissions.allowRead with explicit module-cache and required
asset paths needed by Hugo’s Node asset tools. Preserve the rationale comment;
if the necessary paths cannot be identified, remove allowRead and document a
time-bounded security exception confirming both build entrypoints were checked.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d7cbada2-eb9d-4bfe-a223-567cdaecbaff

📥 Commits

Reviewing files that changed from the base of the PR and between b119c66 and 1ffe4d7.

📒 Files selected for processing (17)
  • .github/workflows/hugo.yaml
  • content/de/_index.md
  • content/en/_index.md
  • content/en/docs/next/roadmap.md
  • content/en/docs/v0/roadmap.md
  • content/en/docs/v1.0/roadmap.md
  • content/en/docs/v1.1/roadmap.md
  • content/en/docs/v1.2/roadmap.md
  • content/en/docs/v1.3/roadmap.md
  • content/en/docs/v1.4/roadmap.md
  • content/en/docs/v1.5/roadmap.md
  • content/en/docs/v1.6/roadmap.md
  • content/hi/_index.md
  • content/ru/_index.md
  • content/zh-cn/_index.md
  • hugo.yaml
  • netlify.toml

Comment thread hugo.yaml
Comment on lines +16 to +22
# This is deliberately broader than necessary — the minimal set of readable
# paths has not been pinned down, and narrowing it would be a welcome follow-up.
security:
node:
permissions:
allowRead:
- '*'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | sed -n '1,120p'

echo
echo "== hugo.yaml =="
if [ -f hugo.yaml ]; then
  cat -n hugo.yaml
fi

echo
echo "== config/production/hugo.yaml =="
if [ -f config/production/hugo.yaml ]; then
  cat -n config/production/hugo.yaml
fi

echo
echo "== package manifests =="
for f in package.json lockfile.yaml pnpm-lock.yaml package-lock.json yarn.lock bun.lockb; do
  [ -e "$f" ] && echo "--- $f ---" && { head -200 "$f" || true; }
done

echo
echo "== security allowRead occurrences =="
rg -n "allowRead|node:.|permissions|security:" -S .

Repository: cozystack/website

Length of output: 50373


🌐 Web query:

Hugo 0.161.0 node permissions allowRead security.node.permissions.allowRead

💡 Result:

In Hugo 0.161.0, the security.node.permissions.allowRead configuration setting was introduced as part of a new security hardening measure [1][2][3]. When running Node-based asset pipelines (such as PostCSS, Babel, or TailwindCSS), Hugo now executes these tools using Node.js's native --permission flag [1][4]. This restricts the file system and resource access of these tools based on the defined permissions in your Hugo configuration [1][2]. Key details regarding security.node.permissions.allowRead: * Purpose: It defines a slice of file system paths that Node.js tools are explicitly permitted to read, mapping to the --allow-fs-read flag in Node.js [1][5]. * Configuration: Paths provided in this list are relative to the working directory [1][5]. * Default Behavior: By default, Hugo restricts access to the project directory [4][3]. You can use "." to represent the working directory itself or "*" to allow access to all paths [1][6]. * Troubleshooting: If a build fails due to these new restrictions (for example, if a tool like PostCSS needs to look up configuration files outside the immediate working directory, such as browserslist-stats.json), you may need to explicitly add those paths to the allowRead list in your hugo.toml or hugo.yaml file [7]. * Disabling: If necessary, you can disable the entire Node.js permission model by setting security.node.permissions.disable = true [1][7]. These changes were implemented to address security vulnerabilities that previously allowed Node-based tools to access files outside the project's working directory [3].

Citations:


🌐 Web query:

Hugo 0.161.0 Node asset tools PostCSS security node permissions allowRead

💡 Result:

In Hugo v0.161.0, a significant security hardening change was introduced regarding the execution of Node.js-based asset tools (PostCSS, Babel, and TailwindCSS) [1][2]. To prevent these tools from accessing arbitrary files on the system, Hugo now executes them by default using Node's native permission model via the --permission flag [3][4][5]. Key aspects of this change and how to manage the allowRead permissions include: The Default Security Model By default, Hugo restricts these Node tools so that they have no write access and only have read access to the project's working directory (represented as ".") [3][4][6]. This is managed under the security.node.permissions configuration block [3][6]. Configuring allowRead If your build fails with "Access to this API has been denied" or similar file system errors, you may need to explicitly grant additional read access [7]. You can configure this in your Hugo configuration file (e.g., hugo.toml or hugo.yaml): [security] [security.node.permissions] allowRead = ['.', '/path/to/additional/needed/dir'] In this configuration, "." refers to the project's working directory [3][6]. You can add other specific paths as needed, or use "*" to allow access to all paths, though the latter is less secure [3]. Troubleshooting and Alternatives 1. Paths are relative to the working directory [3]. 2. If you are experiencing issues with complex dependencies (like browserslist or specific plugins), ensure your configuration explicitly includes those directories if they reside outside the standard project root [7]. 3. For debugging, you can temporarily disable the entire permission model by setting disable = true within [security.node.permissions], though this is not recommended for production environments [3][7]. 4. Requirements: This feature requires Node.js version 22 or higher [1]. These changes were implemented to address security vulnerabilities where Node-based tools could be exploited to read or write files outside the intended project boundaries [4]. [1][3][4]

Citations:


Don’t release with unrestricted Node filesystem reads.

allowRead: ['*'] lets Hugo’s Node asset tools read every file visible to the build process. Keep the current rationale, but replace the wildcard with explicit module-cache or required asset paths before release. If narrowed paths are not possible, remove allowRead temporarily and document a time-bounded security exception that both build entrypoints were checked.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hugo.yaml` around lines 16 - 22, Replace the unrestricted wildcard under
security.node.permissions.allowRead with explicit module-cache and required
asset paths needed by Hugo’s Node asset tools. Preserve the rationale comment;
if the necessary paths cannot be identified, remove allowRead and document a
time-bounded security exception confirming both build entrypoints were checked.

Hugo 0.164 runs Node asset tools (PostCSS for the Docsy theme) as
node --permission ...; the flag is hardcoded in common/hexec/exec.go
nodePermissionArgs with no Node-version detection. The --permission
flag exists only since Node 22.13.0, where the permission model was
promoted to stable; Node 20 knows only --experimental-permission and
aborts with "bad option: --permission".

netlify.toml pinned NODE_VERSION=20, which fails the production build
the first time PostCSS actually runs (Hugo caches its output, so warm
build previews pass and hide the break until a cold-cache deploy).
The Pages workflow left Node unpinned and relied on the runner default,
which is equally fragile.

Pin Node 22 in both build paths.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
After the .html to .md conversion, the two adjacent block shortcodes
home/benefits and home/features sit in a single markdown paragraph.
Hugo only strips the wrapping <p> when a paragraph holds exactly one
shortcode, so the pair rendered as invalid <p><div>...</div></p> with an
extra empty paragraph margin on all five home pages. Put a blank line
between them so each is its own paragraph and gets unwrapped.

Refresh source_digest in the four translations, since the English source
content/en/_index.md changed.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
The Hugo upgrade also raises the minimum local toolchain: Hugo 0.164.0
(matching the pins) and Node 22 (needed for the node permission model).
Update the required-tools lines in CONTRIBUTING.md and CLAUDE.md, which
still referenced Hugo 0.160.1 and Node 20.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
@tym83
Timur Tukaev (tym83) merged commit 76f9f0b into main Aug 3, 2026
7 checks passed
@tym83
Timur Tukaev (tym83) deleted the chore/hugo-upgrade branch August 3, 2026 15:59
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.

2 participants