From d4319e3fa6a816cc6bbe6517efa9e53e4f9af29e Mon Sep 17 00:00:00 2001 From: Casey Marshall Date: Fri, 21 Jan 2022 12:53:43 -0600 Subject: [PATCH] fix: disallow requesting API version dates in the future Respond 400 when a version date is requested from the future. --- versionware/export_test.go | 6 ++++++ versionware/validator.go | 13 +++++++++++++ versionware/validator_test.go | 15 +++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/versionware/export_test.go b/versionware/export_test.go index ad4fe6e1..49914ccb 100644 --- a/versionware/export_test.go +++ b/versionware/export_test.go @@ -1,3 +1,9 @@ package versionware +import "time" + var DefaultValidatorConfig = defaultValidatorConfig + +func (v *Validator) SetToday(today func() time.Time) { + v.today = today +} diff --git a/versionware/validator.go b/versionware/validator.go index 0e80d10f..100ffee5 100644 --- a/versionware/validator.go +++ b/versionware/validator.go @@ -5,6 +5,7 @@ import ( "net/http" "net/url" "sort" + "time" "github.com/getkin/kin-openapi/openapi3" "github.com/getkin/kin-openapi/openapi3filter" @@ -19,6 +20,7 @@ type Validator struct { versions vervet.VersionSlice validators []*openapi3filter.Validator errFunc VersionErrorHandler + today func() time.Time } // ValidatorConfig defines how a new Validator may be configured. @@ -54,6 +56,11 @@ var defaultValidatorConfig = ValidatorConfig{ }, } +func today() time.Time { + now := time.Now().UTC() + return time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC) +} + // NewValidator returns a new validation middleware, which validates versioned // requests according to the given OpenAPI spec versions. For configuration // defaults, a nil config may be used. @@ -84,6 +91,7 @@ func NewValidator(config *ValidatorConfig, docs ...*openapi3.T) (*Validator, err versions: make([]vervet.Version, len(docs)), validators: make([]*openapi3filter.Validator, len(docs)), errFunc: config.VersionError, + today: today, } validatorVersions := map[string]*openapi3filter.Validator{} for i := range docs { @@ -130,6 +138,11 @@ func (v *Validator) Middleware(h http.Handler) http.Handler { v.errFunc(w, req, http.StatusBadRequest, err) return } + if t := v.today(); requested.Date.After(t) { + v.errFunc(w, req, http.StatusBadRequest, + fmt.Errorf("requested version newer than present date %s", t)) + return + } resolvedIndex, err := v.versions.ResolveIndex(*requested) if err != nil { v.errFunc(w, req, http.StatusNotFound, err) diff --git a/versionware/validator_test.go b/versionware/validator_test.go index 2da19b26..b86dd102 100644 --- a/versionware/validator_test.go +++ b/versionware/validator_test.go @@ -10,6 +10,7 @@ import ( "net/http/httptest" "regexp" "testing" + "time" qt "github.com/frankban/quicktest" "github.com/getkin/kin-openapi/openapi3" @@ -414,6 +415,17 @@ func TestValidator(t *testing.T) { body: `{"id": "42", "contents": {"name": "foo", "expected": 9, "actual": 10}, "extra": true}`, }, strict: false, + }, { + name: "invalid GET for API in the future", + handler: validatorTestHandler{}.withDefaults(), + request: testRequest{ + method: "GET", + path: "/test/42?version=2023-09-17", + }, + response: testResponse{ + 400, "Bad Request\n", + }, + strict: true, }} for i, test := range tests { c.Run(fmt.Sprintf("%d %s", i, test.name), func(c *qt.C) { @@ -429,6 +441,9 @@ func TestValidator(t *testing.T) { config.Options = append(config.Options, append(test.options, openapi3filter.Strict(test.strict))...) v, err := versionware.NewValidator(&config, docs...) c.Assert(err, qt.IsNil) + v.SetToday(func() time.Time { + return time.Date(2022, time.January, 21, 0, 0, 0, 0, time.UTC) + }) h = v.Middleware(&test.handler) // Test: make a client request