[v14.0/forgejo] fix: remove template file from generated repo (#11722)

**Backport:** https://codeberg.org/forgejo/forgejo/pulls/11691

This PR fixes a bug where the previously-implemented functionality to delete the `.gitea/template` or `.forgejo/template` file when generating a repository from a template was not working. The issue happened because the code was using `util.Remove()` with a relative path, but this resolves against the process working directory instead of the temporary clone directory. The fix was to use `root.Remove()` which is based on an `os.OpenRoot()` anchored at `tmpDir`.

Updated integration tests and verified that they pass with this change and fail without it.

Co-authored-by: Brandon Rothweiler <bdr9@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11722
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
This commit is contained in:
forgejo-backport-action 2026-03-18 21:11:52 +01:00 committed by Gusted
parent a1bd21d45e
commit a2f9fb501f
3 changed files with 30 additions and 16 deletions

View file

@ -54,19 +54,19 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r
}
if gt != nil {
if err := util.Remove(gt.Path); err != nil {
// All file access should be done through `root` to avoid file traversal attacks, especially with symlinks
root, err := os.OpenRoot(tmpDir)
if err != nil {
return fmt.Errorf("open root: %w", err)
}
defer root.Close()
if err := root.Remove(gt.Path); err != nil {
return fmt.Errorf("remove .giteatemplate: %w", err)
}
// Avoid walking tree if there are no globs
if len(gt.Globs()) > 0 {
// All file access should be done through `root` to avoid file traversal attacks, especially with symlinks
root, err := os.OpenRoot(tmpDir)
if err != nil {
return fmt.Errorf("open root: %w", err)
}
defer root.Close()
tmpDirSlash := strings.TrimSuffix(filepath.ToSlash(tmpDir), "/") + "/"
if err := filepath.WalkDir(tmpDirSlash, func(path string, d os.DirEntry, walkErr error) error {
if walkErr != nil {

View file

@ -55,19 +55,19 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r
}
if gt != nil {
if err := util.Remove(gt.Path); err != nil {
// All file access should be done through `root` to avoid file traversal attacks, especially with symlinks
root, err := os.OpenRoot(tmpDir)
if err != nil {
return fmt.Errorf("open root: %w", err)
}
defer root.Close()
if err := root.Remove(gt.Path); err != nil {
return fmt.Errorf("remove .giteatemplate: %w", err)
}
// Avoid walking tree if there are no globs
if len(gt.Globs()) > 0 {
// All file access should be done through `root` to avoid file traversal attacks, especially with symlinks
root, err := os.OpenRoot(tmpDir)
if err != nil {
return fmt.Errorf("open root: %w", err)
}
defer root.Close()
tmpDirSlash := strings.TrimSuffix(filepath.ToSlash(tmpDir), "/") + "/"
if err := filepath.WalkDir(tmpDirSlash, func(path string, d os.DirEntry, walkErr error) error {
if walkErr != nil {

View file

@ -120,6 +120,10 @@ Clone URL: %s%s/%s.git`,
req = NewRequestf(t, "GET", "/%s/%s/raw/branch/master/%s.log", generateOwner.Name, generateRepoName, generateRepoName)
resp = session.MakeRequest(t, req, http.StatusOK)
assert.Equal(t, generateRepoName, resp.Body.String())
// The .gitea/template file should not be present in the generated repo
req = NewRequestf(t, "GET", "/%s/%s/raw/branch/master/.gitea/template", generateOwner.Name, generateRepoName)
session.MakeRequest(t, req, http.StatusNotFound)
}
// test form elements before and after POST error response
@ -291,6 +295,16 @@ func TestRepoGenerateTemplating(t *testing.T) {
user.Name,
generatedName)
assert.Equal(t, body, resp.Body.String())
// The .forgejo/template file should not be present in the generated repo
req = NewRequestf(
t,
"GET", "/%s/%s/raw/branch/%s/.forgejo/template",
user.Name,
generatedName,
template.DefaultBranch,
)
session.MakeRequest(t, req, http.StatusNotFound)
})
}