Skip to content

Commit 6dce6ad

Browse files
committed
Fix template repository expansion for non-regular files
1 parent ebd88af commit 6dce6ad

File tree

3 files changed

+74
-37
lines changed

3 files changed

+74
-37
lines changed

models/asymkey/ssh_key_authorized_keys.go

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,9 @@ import (
1717
"code.gitea.io/gitea/modules/log"
1818
"code.gitea.io/gitea/modules/setting"
1919
"code.gitea.io/gitea/modules/util"
20+
"golang.org/x/crypto/ssh"
2021
)
2122

22-
// _____ __ .__ .__ .___
23-
// / _ \ __ ___/ |_| |__ ___________|__|_______ ____ __| _/
24-
// / /_\ \| | \ __\ | \ / _ \_ __ \ \___ // __ \ / __ |
25-
// / | \ | /| | | Y ( <_> ) | \/ |/ /\ ___// /_/ |
26-
// \____|__ /____/ |__| |___| /\____/|__| |__/_____ \\___ >____ |
27-
// \/ \/ \/ \/ \/
28-
// ____ __.
29-
// | |/ _|____ ___.__. ______
30-
// | <_/ __ < | |/ ___/
31-
// | | \ ___/\___ |\___ \
32-
// |____|__ \___ > ____/____ >
33-
// \/ \/\/ \/
34-
//
3523
// This file contains functions for creating authorized_keys files
3624
//
3725
// There is a dependence on the database within RegeneratePublicKeys however most of these functions probably belong in a module
@@ -49,6 +37,23 @@ func WithSSHOpLocker(f func() error) error {
4937
return f()
5038
}
5139

40+
// removeSSHKeyComment removes the trailing comment from an SSH public key line.
41+
func removeSSHKeyComment(pubKeyLine string) (string, error) {
42+
pubKeyLine = strings.TrimSpace(pubKeyLine)
43+
if pubKeyLine == "" || strings.HasPrefix(pubKeyLine, "#") {
44+
return pubKeyLine, nil
45+
}
46+
47+
pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubKeyLine))
48+
if err != nil {
49+
return "", fmt.Errorf("invalid public key: %w", err)
50+
}
51+
52+
// MarshalAuthorizedKey returns "<type> <base64>\n"
53+
key := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(pubKey)))
54+
return key, nil
55+
}
56+
5257
// AuthorizedStringForKey creates the authorized keys string appropriate for the provided key
5358
func AuthorizedStringForKey(key *PublicKey) string {
5459
sb := &strings.Builder{}
@@ -60,7 +65,13 @@ func AuthorizedStringForKey(key *PublicKey) string {
6065
"Key": key,
6166
})
6267

63-
return fmt.Sprintf(tplPublicKey, util.ShellEscape(sb.String()), key.Content)
68+
content, err := removeSSHKeyComment(key.Content)
69+
if err != nil {
70+
log.Error("Failed to remove comment from SSH key ID %d: %v", key.ID, err)
71+
content = key.Content
72+
}
73+
74+
return fmt.Sprintf(tplPublicKey, util.ShellEscape(sb.String()), content)
6475
}
6576

6677
// appendAuthorizedKeysToFile appends new SSH keys' content to authorized_keys file.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package asymkey
5+
6+
import (
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func Test_removeSSHKeyComment(t *testing.T) {
13+
content, err := removeSSHKeyComment("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINna5Jd6FTG4d87pUHnd/uLBr/6zGOVVFEQmdTs6k21L user@hostname")
14+
assert.NoError(t, err)
15+
assert.Equal(t, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINna5Jd6FTG4d87pUHnd/uLBr/6zGOVVFEQmdTs6k21L", content)
16+
}

services/repository/generate.go

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -153,33 +153,43 @@ func processGiteaTemplateFile(ctx context.Context, tmpDir string, templateRepo,
153153
if d.IsDir() {
154154
return nil
155155
}
156+
fInfo, err := d.Info()
157+
if err != nil {
158+
return err
159+
}
160+
mode := fInfo.Mode()
161+
if !mode.IsRegular() { // for symlinks and other special files, just skip
162+
return nil
163+
}
156164

157165
base := strings.TrimPrefix(filepath.ToSlash(path), tmpDirSlash)
158166
for _, g := range giteaTemplateFile.Globs() {
159-
if g.Match(base) {
160-
content, err := os.ReadFile(path)
161-
if err != nil {
162-
return err
163-
}
164-
165-
generatedContent := []byte(generateExpansion(ctx, string(content), templateRepo, generateRepo, false))
166-
if err := os.WriteFile(path, generatedContent, 0o644); err != nil {
167-
return err
168-
}
169-
170-
substPath := filepath.FromSlash(filepath.Join(tmpDirSlash, generateExpansion(ctx, base, templateRepo, generateRepo, true)))
171-
172-
// Create parent subdirectories if needed or continue silently if it exists
173-
if err = os.MkdirAll(filepath.Dir(substPath), 0o755); err != nil {
174-
return err
175-
}
176-
177-
// Substitute filename variables
178-
if err = os.Rename(path, substPath); err != nil {
179-
return err
180-
}
181-
break
167+
if !g.Match(base) {
168+
continue
169+
}
170+
171+
content, err := os.ReadFile(path)
172+
if err != nil {
173+
return err
174+
}
175+
176+
generatedContent := []byte(generateExpansion(ctx, string(content), templateRepo, generateRepo, false))
177+
if err := os.WriteFile(path, generatedContent, 0o644); err != nil {
178+
return err
179+
}
180+
181+
substPath := filepath.FromSlash(filepath.Join(tmpDirSlash, generateExpansion(ctx, base, templateRepo, generateRepo, true)))
182+
183+
// Create parent subdirectories if needed or continue silently if it exists
184+
if err = os.MkdirAll(filepath.Dir(substPath), 0o755); err != nil {
185+
return err
186+
}
187+
188+
// Substitute filename variables
189+
if err = os.Rename(path, substPath); err != nil {
190+
return err
182191
}
192+
break
183193
}
184194
return nil
185195
}) // end: WalkDir

0 commit comments

Comments
 (0)