chore: upgrade Hugo to 0.164.0 and convert HTML content to markdown - #636
Conversation
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>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
✅ Deploy Preview for cozystack ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Warning Review limit reached
Next review available in: 18 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughThe 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. ChangesSite update
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
ReviewThe 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)
Root cause: the check hashes the whole English source Fix (mechanical, no real re-translation needed since the content is equivalent): then commit the updated On
|
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>
There was a problem hiding this comment.
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
📒 Files selected for processing (17)
.github/workflows/hugo.yamlcontent/de/_index.mdcontent/en/_index.mdcontent/en/docs/next/roadmap.mdcontent/en/docs/v0/roadmap.mdcontent/en/docs/v1.0/roadmap.mdcontent/en/docs/v1.1/roadmap.mdcontent/en/docs/v1.2/roadmap.mdcontent/en/docs/v1.3/roadmap.mdcontent/en/docs/v1.4/roadmap.mdcontent/en/docs/v1.5/roadmap.mdcontent/en/docs/v1.6/roadmap.mdcontent/hi/_index.mdcontent/ru/_index.mdcontent/zh-cn/_index.mdhugo.yamlnetlify.toml
| # 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: | ||
| - '*' |
There was a problem hiding this comment.
🔒 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:
- 1: https://gohugo.io/configuration/security/
- 2: https://github.com/gohugoio/hugo/releases/tag/v0.161.0
- 3: GHSA-x597-9fr4-5857
- 4: gohugoio/hugo@a54c398
- 5: https://github.com/gohugoio/hugo/blob/96e06e1a/config/security/securityConfig.go
- 6: https://fossies.org/linux/hugo/docs/content/en/configuration/security.md
- 7: https://discourse.gohugo.io/t/security-error-with-the-new-0-161-0-security-for-postcss/57186
🌐 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:
- 1: https://github.com/gohugoio/hugo/releases/tag/v0.161.0
- 2: https://discourse.gohugo.io/t/hugo-v0-161-0-released/57060
- 3: https://gohugo.io/configuration/security/
- 4: GHSA-x597-9fr4-5857
- 5: gohugoio/hugo@a54c398
- 6: https://github.com/gohugoio/hugo/blob/96e06e1a/config/security/securityConfig.go
- 7: https://discourse.gohugo.io/t/security-error-with-the-new-0-161-0-security-for-postcss/57186
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>
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/htmlcontent files, affecting every release before it — and introduced thesecurity.allowContentwhitelist withtext/htmldenied 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
.htmlcontent files to.md. Goldmark already runs withunsafe: true, so the inline HTML in these pages renders exactly as before.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.HUGO_VERSIONto0.164.0innetlify.tomland.github/workflows/hugo.yaml.security.node.permissionsblock 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.jsto 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
Documentation
Chores