Skip to content

Commit e488e13

Browse files
committed
fix: enforce lower and upper limits on template max_ttl_ms
1 parent 253e6cb commit e488e13

File tree

3 files changed

+121
-0
lines changed

3 files changed

+121
-0
lines changed

coderd/authorize.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func (api *API) Authorize(r *http.Request, action rbac.Action, object rbac.Objec
4949
// This function will log appropriately, but the caller must return an
5050
// error to the api client.
5151
// Eg:
52+
//
5253
// if !h.Authorize(...) {
5354
// httpapi.Forbidden(rw)
5455
// return

coderd/templates.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,25 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
176176
if !ptr.NilOrZero(createTemplate.MaxTTLMillis) {
177177
maxTTL = time.Duration(*createTemplate.MaxTTLMillis) * time.Millisecond
178178
}
179+
if maxTTL < 0 {
180+
httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{
181+
Message: "Invalid create template request.",
182+
Validations: []codersdk.ValidationError{
183+
{Field: "max_ttl_ms", Detail: "Must be a positive integer."},
184+
},
185+
})
186+
return
187+
}
188+
189+
if maxTTL > maxTTLDefault {
190+
httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{
191+
Message: "Invalid create template request.",
192+
Validations: []codersdk.ValidationError{
193+
{Field: "max_ttl_ms", Detail: "Cannot be greater than " + maxTTLDefault.String()},
194+
},
195+
})
196+
return
197+
}
179198

180199
minAutostartInterval := minAutostartIntervalDefault
181200
if !ptr.NilOrZero(createTemplate.MinAutostartIntervalMillis) {
@@ -384,6 +403,15 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
384403
if req.MinAutostartIntervalMillis < 0 {
385404
validErrs = append(validErrs, codersdk.ValidationError{Field: "min_autostart_interval_ms", Detail: "Must be a positive integer."})
386405
}
406+
if req.MaxTTLMillis > maxTTLDefault.Milliseconds() {
407+
httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{
408+
Message: "Invalid create template request.",
409+
Validations: []codersdk.ValidationError{
410+
{Field: "max_ttl_ms", Detail: "Cannot be greater than " + maxTTLDefault.String()},
411+
},
412+
})
413+
return
414+
}
387415

388416
if len(validErrs) > 0 {
389417
httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{

coderd/templates_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,46 @@ func TestPostTemplateByOrganization(t *testing.T) {
108108
require.Equal(t, http.StatusConflict, apiErr.StatusCode())
109109
})
110110

111+
t.Run("MaxTTLTooLow", func(t *testing.T) {
112+
t.Parallel()
113+
client := coderdtest.New(t, nil)
114+
user := coderdtest.CreateFirstUser(t, client)
115+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
116+
117+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
118+
defer cancel()
119+
120+
_, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{
121+
Name: "testing",
122+
VersionID: version.ID,
123+
MaxTTLMillis: ptr.Ref(int64(-1)),
124+
})
125+
var apiErr *codersdk.Error
126+
require.ErrorAs(t, err, &apiErr)
127+
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
128+
require.Contains(t, err.Error(), "max_ttl_ms: Must be a positive integer")
129+
})
130+
131+
t.Run("MaxTTLTooHigh", func(t *testing.T) {
132+
t.Parallel()
133+
client := coderdtest.New(t, nil)
134+
user := coderdtest.CreateFirstUser(t, client)
135+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
136+
137+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
138+
defer cancel()
139+
140+
_, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{
141+
Name: "testing",
142+
VersionID: version.ID,
143+
MaxTTLMillis: ptr.Ref(365 * 24 * time.Hour.Milliseconds()),
144+
})
145+
var apiErr *codersdk.Error
146+
require.ErrorAs(t, err, &apiErr)
147+
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
148+
require.Contains(t, err.Error(), "max_ttl_ms: Cannot be greater than")
149+
})
150+
111151
t.Run("Unauthorized", func(t *testing.T) {
112152
t.Parallel()
113153
client := coderdtest.New(t, nil)
@@ -271,6 +311,58 @@ func TestPatchTemplateMeta(t *testing.T) {
271311
assert.Equal(t, req.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis)
272312
})
273313

314+
t.Run("MaxTTLTooLow", func(t *testing.T) {
315+
t.Parallel()
316+
317+
client := coderdtest.New(t, nil)
318+
user := coderdtest.CreateFirstUser(t, client)
319+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
320+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
321+
ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds())
322+
})
323+
req := codersdk.UpdateTemplateMeta{
324+
MaxTTLMillis: -1,
325+
}
326+
327+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
328+
defer cancel()
329+
330+
_, err := client.UpdateTemplateMeta(ctx, template.ID, req)
331+
require.ErrorContains(t, err, "max_ttl_ms: Must be a positive integer")
332+
333+
// Ensure no update occurred
334+
updated, err := client.Template(ctx, template.ID)
335+
require.NoError(t, err)
336+
assert.Equal(t, updated.UpdatedAt, template.UpdatedAt)
337+
assert.Equal(t, updated.MaxTTLMillis, template.MaxTTLMillis)
338+
})
339+
340+
t.Run("MaxTTLTooHigh", func(t *testing.T) {
341+
t.Parallel()
342+
343+
client := coderdtest.New(t, nil)
344+
user := coderdtest.CreateFirstUser(t, client)
345+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
346+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
347+
ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds())
348+
})
349+
req := codersdk.UpdateTemplateMeta{
350+
MaxTTLMillis: 365 * 24 * time.Hour.Milliseconds(),
351+
}
352+
353+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
354+
defer cancel()
355+
356+
_, err := client.UpdateTemplateMeta(ctx, template.ID, req)
357+
require.ErrorContains(t, err, "max_ttl_ms: Cannot be greater than")
358+
359+
// Ensure no update occurred
360+
updated, err := client.Template(ctx, template.ID)
361+
require.NoError(t, err)
362+
assert.Equal(t, updated.UpdatedAt, template.UpdatedAt)
363+
assert.Equal(t, updated.MaxTTLMillis, template.MaxTTLMillis)
364+
})
365+
274366
t.Run("NotModified", func(t *testing.T) {
275367
t.Parallel()
276368

0 commit comments

Comments
 (0)