Skip to content

Conversation

@ReneWerner87
Copy link
Member

@ReneWerner87 ReneWerner87 commented Feb 9, 2026

fixes #383

Summary by CodeRabbit

  • Documentation: Updated partial include path guidance and examples with clearer direction on filesystem-relative partial resolution.
  • New Features: Partials now support searching multiple candidate paths for improved flexibility; added verbose logging and descriptive error messages when partials cannot be found.
  • Tests: Added test coverage for relative partial rendering functionality.

@ReneWerner87 ReneWerner87 requested a review from a team as a code owner February 9, 2026 18:41
@ReneWerner87 ReneWerner87 requested review from Copilot, efectn, gaby and sixcolors and removed request for a team February 9, 2026 18:41
@gemini-code-assist
Copy link

Summary of Changes

Hello @ReneWerner87, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors the partials resolution mechanism within the Mustache template engine to enable the use of relative paths for including template snippets. This change addresses a reported issue by making template organization more intuitive and flexible. Additionally, the error handling for cases where partials cannot be found has been significantly improved, offering clearer diagnostic information to developers.

Highlights

  • Relative Partial Paths: The Mustache template engine now supports resolving partials using paths relative to the engine's base directory, simplifying template organization and usage.
  • Enhanced Error Handling: Improved error messages for missing partials now provide a list of all attempted file paths, significantly aiding in debugging and troubleshooting.
  • Updated Documentation and Examples: The README.md and example templates have been updated to reflect the new relative partial path usage, ensuring consistency and clarity for users.
  • New Test Coverage: A dedicated test case has been added to validate the correct functionality of relative partial resolution, ensuring robustness.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • mustache/README.md
    • Updated example index.mustache to use relative partial paths (e.g., {{> partials/header }}).
    • Revised comments for mustache.NewFileSystem to clarify partial resolution behavior.
  • mustache/mustache.go
    • Introduced baseDir and verbose fields to fileSystemPartialProvider for flexible path resolution and logging.
    • Reworked fileSystemPartialProvider.Get to use a lookupCandidates strategy, attempting multiple paths for a partial.
    • Implemented lookupCandidates function to generate potential file paths for partials, supporting relative and base-directory-prefixed paths.
    • Enhanced error messages for missing partials to include all attempted candidate paths.
    • Integrated baseDir initialization in New and NewFileSystemPartials functions.
    • Enabled verbose logging for partial lookups based on the engine's Verbose setting.
  • mustache/mustache_test.go
    • Added Test_Render_RelativePartials to validate the new relative partial resolution.
    • Adjusted Test_FileSystem to align with the updated partials resolution logic by changing the partialsFS base directory.
  • mustache/views/index.mustache
    • Modified partial includes to use relative paths (e.g., {{> partials/header }}).
  • mustache/views/relative.mustache
    • Added a new template file to serve as a test case for relative partials.
Activity
  • No specific activity (comments, reviews, or progress updates) was provided in the context for this pull request.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@ReneWerner87
Copy link
Member Author

@Durandle46 can you check and test with your codebase

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the partial template resolution logic to support relative paths and improves error handling, which is a great enhancement for usability. However, a critical Path Traversal vulnerability has been introduced in mustache/mustache.go due to insufficient sanitization of partial names. This is particularly concerning when using the default mustache.New() constructor, which reads directly from the local filesystem without proper directory protections. Proper validation is needed to ensure partial names cannot escape the base directory.

Comment on lines +61 to 96
func (p fileSystemPartialProvider) lookupCandidates(partial string) []string {
addCandidate := func(candidates []string, candidate string) []string {
for _, existing := range candidates {
if existing == candidate {
return candidates
}
}
return append(candidates, candidate)
}

addExtension := func(raw string) string {
if strings.HasSuffix(raw, p.extension) {
return raw
}
return raw + p.extension
}

base := filepath.ToSlash(strings.TrimSpace(p.baseDir))
base = strings.TrimSuffix(base, "/")
if base == "." || base == "/" {
base = ""
}

clean := filepath.ToSlash(strings.TrimSpace(partial))
clean = strings.TrimPrefix(clean, "./")

candidates := make([]string, 0, 2)
if clean != "" {
candidates = addCandidate(candidates, addExtension(clean))
if base != "" {
candidates = addCandidate(candidates, addExtension(path.Join(base, clean)))
}
}

return candidates
}

Choose a reason for hiding this comment

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

security-high high

The lookupCandidates method does not sanitize the partial input, leading to a Path Traversal vulnerability. When the engine is initialized using mustache.New(), it uses the local filesystem directly (p.fileSystem is nil). A malicious template could use ../ sequences in a partial tag (e.g., {{> ../../../etc/passwd }}) to read arbitrary files from the server, escaping the intended template directory. More robust sanitization is required to prevent access outside the intended template directory. This involves cleaning the path and then checking for directory traversal attempts or absolute paths to ensure the resolved path remains within the baseDir.

@coderabbitai
Copy link

coderabbitai bot commented Feb 9, 2026

Walkthrough

This PR addresses a bug where Mustache template partials fail to load silently. The fix implements multi-candidate path resolution for partial lookup, allowing partials to be resolved relative to an engine's base directory while maintaining backward compatibility. Updated tests and documentation reflect the corrected partial path resolution behavior.

Changes

Cohort / File(s) Summary
Documentation
mustache/README.md
Updated partial include path examples from views/partials/* to partials/* and clarified that partials are resolved relative to the engine directory with full path compatibility.
Core Partial Resolution Logic
mustache/mustache.go
Reworked partial lookup to search multiple candidate file paths via lookupCandidates mechanism instead of single fixed path; added baseDir and verbose fields to fileSystemPartialProvider; enhanced error handling with aggregated candidate information; updated New() and NewFileSystemPartials() initialization to set baseDir appropriately; Load() now propagates verbose logging to partialsProvider; added path import.
Tests & Templates
mustache/mustache_test.go, mustache/views/index.mustache, mustache/views/relative.mustache
Added new test case Test_Render_RelativePartials to verify relative partial rendering; updated Test_FileSystem to use "./views" base directory; updated index template partial includes to use corrected paths; introduced new relative template for testing relative partial resolution.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • gaby
  • sixcolors
  • efectn

Poem

🐰 Hoppy hop, the partials now work fine,
No more silent fails, each path will align,
Candidates tried, 'til found with care,
Templates rendered, everywhere!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: refactoring partials resolution to support relative paths and improving error handling, which aligns with the core modifications across README, mustache.go, tests, and template files.
Linked Issues check ✅ Passed The PR successfully addresses issue #383 by implementing multiple candidate path lookup for partials, adding verbose error logging, and enabling partials to render correctly with proper error messages when partials are missing.
Out of Scope Changes check ✅ Passed All changes are directly related to resolving the partials issue: documentation updates, partials lookup refactoring, error handling improvements, test cases for relative partials, and template file updates are all in-scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mustache_update-partials-resolution

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
mustache/README.md (1)

57-62: ⚠️ Potential issue | 🟡 Minor

Pre-existing version mismatch in the example code.

Line 17 instructs users to install mustache/v4, but the Go example on line 61 imports mustache/v3. This wasn't introduced by this PR, but since you're updating this section, it might be worth fixing in the same pass.

🧹 Nitpick comments (1)
mustache/mustache.go (1)

78-82: Consider also stripping a leading ./ from base for consistency.

After filepath.ToSlash and TrimSuffix("/"), the code checks for "." and "/" but not "./". If someone passes "./" as the directory, base would remain "./" after these checks, then path.Join("./", clean) would clean it anyway. Not a bug, but trimming "./" here (like you do for clean on line 85) would make the logic symmetric.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b24b6b and f192436.

📒 Files selected for processing (5)
  • mustache/README.md
  • mustache/mustache.go
  • mustache/mustache_test.go
  • mustache/views/index.mustache
  • mustache/views/relative.mustache
🧰 Additional context used
🧬 Code graph analysis (1)
mustache/mustache_test.go (1)
mustache/mustache.go (2)
  • New (99-112)
  • NewFileSystemPartials (120-136)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Agent
  • GitHub Check: compare
🔇 Additional comments (9)
mustache/mustache.go (3)

34-59: Looks good overall — solid multi-candidate lookup with clear error reporting.

The fallback logic is well-structured: try each candidate, capture the first error, and surface a descriptive message listing all tried paths. The verbose logging will help users debug partial resolution issues — directly addressing the silent-failure bug in issue #383.


61-96: path.Join cleans away the leading ./ — verify this is intentional.

On line 91, path.Join(base, clean) normalizes the result, so when base is "./views", the candidate becomes "views/partials/header.mustache" (without the leading ./). This works because os.ReadFile resolves both identically relative to CWD, but it means the first candidate (partials/header.mustache) and the joined candidate (views/partials/header.mustache) will never be equal — so the dedup in addCandidate will never trigger for the common New("./views", ...) case. That's fine functionally, but worth noting.

One edge case: if partial is an empty or whitespace-only string, clean becomes "" and no candidates are generated, hitting the "no partial candidates generated" fallback in Get. This seems reasonable.


145-147: LGTM — propagating verbosity at load time.

Clean approach to wire engine-level Verbose into the partials provider during Load().

mustache/mustache_test.go (2)

51-65: Good test coverage for the new relative partials feature.

The test exercises the core bug fix — rendering partials via relative paths from the engine's base directory. Assertions are clear and consistent with the existing test patterns.


99-113: Test_FileSystem partials base updated to "./views" — aligns with new resolution logic.

Changing partialsFS from http.Dir(".") to http.Dir("./views") is consistent with the updated partial paths in index.mustache (now partials/header instead of views/partials/header). This makes the test accurately reflect the documented usage pattern.

mustache/views/relative.mustache (1)

1-3: LGTM — clean test fixture for relative partials.

Uses partials/header and partials/footer paths consistent with the updated resolution logic.

mustache/views/index.mustache (1)

1-3: Partial paths updated to be relative to engine directory — consistent with the new resolution behavior.

mustache/README.md (2)

24-28: Documentation examples updated to match new partial path convention — LGTM.


69-71: Helpful clarification on partial resolution behavior.

The comment clearly explains that partials resolve relative to the engine directory / filesystem root and that full paths remain compatible for backward compatibility.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Refactors Mustache partial resolution so partials can be referenced relative to the engine’s views directory (and improves observability when partial lookup fails), addressing gofiber/template issue #383.

Changes:

  • Updated Mustache partial lookup to try multiple candidate paths and return richer errors (plus optional verbose logs).
  • Adjusted example templates/docs to use {{> partials/...}} instead of {{> views/partials/...}}.
  • Added/updated tests and fixtures for the new partial path expectations (including filesystem-partials behavior).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
mustache/mustache.go Adds candidate-based partial lookup and improved error reporting/logging; wires provider into New(...) and filesystem constructors.
mustache/mustache_test.go Adds a relative-partials render test and updates filesystem-partials root to ./views.
mustache/views/index.mustache Updates partial includes to partials/... form.
mustache/views/relative.mustache Adds a template fixture using partials/... includes.
mustache/README.md Updates documentation examples to the new partial include style and clarifies resolution behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +89 to +92
candidates = addCandidate(candidates, addExtension(clean))
if base != "" {
candidates = addCandidate(candidates, addExtension(path.Join(base, clean)))
}
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

lookupCandidates currently tries the raw partial path before the engine baseDir candidate. That means a partials/header reference can resolve to a file relative to the process working directory even when an engine directory is configured, which is contrary to the README statement that partials resolve relative to the engine directory / filesystem root and can pick the wrong file if duplicates exist. Prefer trying baseDir + partial first (when baseDir is set), and fall back to the raw path for compatibility.

Suggested change
candidates = addCandidate(candidates, addExtension(clean))
if base != "" {
candidates = addCandidate(candidates, addExtension(path.Join(base, clean)))
}
if base != "" {
candidates = addCandidate(candidates, addExtension(path.Join(base, clean)))
}
candidates = addCandidate(candidates, addExtension(clean))

Copilot uses AI. Check for mistakes.
Comment on lines +55 to +59
if firstErr == nil {
firstErr = fmt.Errorf("no partial candidates generated")
}
return "", fmt.Errorf("render: partial %q does not exist (tried: %s): %w", partial, strings.Join(candidates, ", "), firstErr)
}
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

The new partial lookup/error-wrapping behavior isn’t covered by a failing-path test. Add a test that renders a template referencing a missing partial and assert that Render (or Load, depending on mustache behavior) returns an error containing the partial name and attempted candidates (and that it’s not silently ignored).

Copilot uses AI. Check for mistakes.
Comment on lines 68 to +71
// Or from an embedded system
// Note that with an embedded system the partials included from template files must be
// specified relative to the filesystem's root, not the current working directory
// engine := mustache.NewFileSystem(http.Dir("./views", ".mustache"), ".mustache")
// Partials are resolved relative to the engine directory / filesystem root.
// For compatibility, full paths also work when present in your templates.
// engine := mustache.NewFileSystem(http.Dir("./views"), ".mustache")
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

The README example shows http.Dir(...) in the (commented) embedded/filesystem snippet but the import block in the example doesn’t include net/http. Add the import so users can copy/paste and uncomment the snippet without compile errors.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 [Bug]: Mustache template engine; partials don't work

1 participant