Skip to content

Commit b120247

Browse files
authored
fix: extend regex for template version name (coder#6876)
1 parent 563c3ad commit b120247

File tree

3 files changed

+80
-1
lines changed

3 files changed

+80
-1
lines changed

coderd/httpapi/httpapi.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func init() {
4444
valid := NameValid(str)
4545
return valid == nil
4646
}
47-
for _, tag := range []string{"username", "template_name", "workspace_name", "template_version_name"} {
47+
for _, tag := range []string{"username", "template_name", "workspace_name"} {
4848
err := Validate.RegisterValidation(tag, nameValidator)
4949
if err != nil {
5050
panic(err)
@@ -64,6 +64,20 @@ func init() {
6464
if err != nil {
6565
panic(err)
6666
}
67+
68+
templateVersionNameValidator := func(fl validator.FieldLevel) bool {
69+
f := fl.Field().Interface()
70+
str, ok := f.(string)
71+
if !ok {
72+
return false
73+
}
74+
valid := TemplateVersionNameValid(str)
75+
return valid == nil
76+
}
77+
err = Validate.RegisterValidation("template_version_name", templateVersionNameValidator)
78+
if err != nil {
79+
panic(err)
80+
}
6781
}
6882

6983
// Convenience error functions don't take contexts since their responses are

coderd/httpapi/name.go

+13
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ var (
1212
UsernameValidRegex = regexp.MustCompile("^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*$")
1313
usernameReplace = regexp.MustCompile("[^a-zA-Z0-9-]*")
1414

15+
templateVersionName = regexp.MustCompile(`^[a-zA-Z0-9]+(?:[_.-]{1}[a-zA-Z0-9]+)*$`)
1516
templateDisplayName = regexp.MustCompile(`^[^\s](.*[^\s])?$`)
1617
)
1718

@@ -52,6 +53,18 @@ func NameValid(str string) error {
5253
return nil
5354
}
5455

56+
// TemplateVersionNameValid returns whether the input string is a valid template version name.
57+
func TemplateVersionNameValid(str string) error {
58+
if len(str) > 64 {
59+
return xerrors.New("must be <= 64 characters")
60+
}
61+
matched := templateVersionName.MatchString(str)
62+
if !matched {
63+
return xerrors.New("must be alphanumeric with underscores and dots")
64+
}
65+
return nil
66+
}
67+
5568
// TemplateDisplayNameValid returns whether the input string is a valid template display name.
5669
func TemplateDisplayNameValid(str string) error {
5770
if len(str) == 0 {

coderd/httpapi/name_test.go

+52
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package httpapi_test
33
import (
44
"testing"
55

6+
"github.com/moby/moby/pkg/namesgenerator"
67
"github.com/stretchr/testify/require"
78

89
"github.com/coder/coder/coderd/httpapi"
@@ -120,6 +121,57 @@ func TestTemplateDisplayNameValid(t *testing.T) {
120121
}
121122
}
122123

124+
func TestTemplateVersionNameValid(t *testing.T) {
125+
t.Parallel()
126+
127+
testCases := []struct {
128+
Name string
129+
Valid bool
130+
}{
131+
{"1", true},
132+
{"12", true},
133+
{"1_2", true},
134+
{"1-2", true},
135+
{"cray", true},
136+
{"123_456", true},
137+
{"123-456", true},
138+
{"1234_678901234567890", true},
139+
{"1234-678901234567890", true},
140+
{"S", true},
141+
{"a1", true},
142+
{"a1K2", true},
143+
{"fuzzy_bear3", true},
144+
{"fuzzy-bear3", true},
145+
{"v1.0.0", true},
146+
{"heuristic_cray2", true},
147+
148+
{"", false},
149+
{".v1", false},
150+
{"v1..0", false},
151+
{"4--4", false},
152+
{"<b> </b>", false},
153+
{"!!!!1 ?????", false},
154+
}
155+
for _, testCase := range testCases {
156+
testCase := testCase
157+
t.Run(testCase.Name, func(t *testing.T) {
158+
t.Parallel()
159+
valid := httpapi.TemplateVersionNameValid(testCase.Name)
160+
require.Equal(t, testCase.Valid, valid == nil)
161+
})
162+
}
163+
}
164+
165+
func TestGeneratedTemplateVersionNameValid(t *testing.T) {
166+
t.Parallel()
167+
168+
for i := 0; i < 1000; i++ {
169+
name := namesgenerator.GetRandomName(1)
170+
err := httpapi.TemplateVersionNameValid(name)
171+
require.NoError(t, err, "invalid template version name: %s", name)
172+
}
173+
}
174+
123175
func TestFrom(t *testing.T) {
124176
t.Parallel()
125177
testCases := []struct {

0 commit comments

Comments
 (0)