Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions errors/parameter_errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,31 @@ func IncorrectCookieParamArrayNumber(
}
}

func QueryParameterCannotBeDecoded(param *v3.Parameter, val string, sch *base.Schema, pathTemplate string, operation string, renderedSchema string) *ValidationError {
keywordLocation := helpers.ConstructParameterJSONPointer(pathTemplate, operation, param.Name, "type")
specLine, specCol := paramSchemaTypeLineCol(param)

return &ValidationError{
ValidationType: helpers.ParameterValidation,
ValidationSubType: helpers.ParameterValidationQuery,
Message: fmt.Sprintf("Query parameter '%s' cannot be decoded", param.Name),
Reason: fmt.Sprintf("The query parameter '%s' is defined as an object, "+
"however the value '%s' cannot be decoded as an object", param.Name, val),
SpecLine: specLine,
SpecCol: specCol,
ParameterName: param.Name,
Context: sch,
HowToFix: HowToFixInvalidEncoding,
SchemaValidationErrors: []*SchemaValidationFailure{{
Reason: fmt.Sprintf("Query value '%s' cannot be decoded as object", val),
FieldName: param.Name,
InstancePath: []string{param.Name},
KeywordLocation: keywordLocation,
ReferenceSchema: renderedSchema,
}},
}
}

func IncorrectParamEncodingJSON(param *v3.Parameter, ef string, sch *base.Schema, pathTemplate string, operation string, renderedSchema string) *ValidationError {
escapedPath := strings.ReplaceAll(pathTemplate, "~", "~0")
escapedPath = strings.ReplaceAll(escapedPath, "/", "~1")
Expand Down
20 changes: 19 additions & 1 deletion parameters/query_parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,27 @@ doneLooking:
}
}

if encodedObj == nil {
Comment thread
SebTardif marked this conversation as resolved.
validationErrors = append(validationErrors,
errors.QueryParameterCannotBeDecoded(params[p], ef, sch, pathValue, operation, renderedSchema))
break skipValues
}
objVal, objExists := encodedObj[params[p].Name]
if !objExists || objVal == nil {
validationErrors = append(validationErrors,
errors.QueryParameterCannotBeDecoded(params[p], ef, sch, pathValue, operation, renderedSchema))
break skipValues
}
objMap, mapOk := objVal.(map[string]interface{})
if !mapOk {
validationErrors = append(validationErrors,
errors.QueryParameterCannotBeDecoded(params[p], ef, sch, pathValue, operation, renderedSchema))
break skipValues
}

numErrors := len(validationErrors)
validationErrors = append(validationErrors,
ValidateParameterSchema(sch, encodedObj[params[p].Name].(map[string]interface{}),
ValidateParameterSchema(sch, objMap,
ef,
"Query parameter",
"The query parameter",
Expand Down
81 changes: 81 additions & 0 deletions parameters/query_parameters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/pb33f/libopenapi-validator/config"
liberrors "github.com/pb33f/libopenapi-validator/errors"
"github.com/pb33f/libopenapi-validator/paths"
)

Expand Down Expand Up @@ -4140,3 +4141,83 @@ paths:
assert.True(t, valid, "issue #91: item_count=10 with type: string should not fail with 'expected string, but got number'")
assert.Empty(t, errors)
}

func TestQueryParamObjectMissingKey_NoPanic(t *testing.T) {
// A content-wrapped object parameter with a non-JSON content type cannot
// be decoded into a map. The validator must not panic AND must report a
// validation error (the parameter is present but unsatisfiable).
spec := `openapi: 3.1.0
paths:
/test:
get:
parameters:
- name: filter
in: query
required: false
content:
text/plain:
schema:
type: object
properties:
color:
type: string
operationId: testFilter
`

doc, err := libopenapi.NewDocument([]byte(spec))
require.NoError(t, err)

m, errs := doc.BuildV3Model()
require.NoError(t, errs)

v := NewParameterValidator(&m.Model)

request, _ := http.NewRequest(http.MethodGet, "https://example.com/test?filter=color,blue", nil)

var valid bool
var valErrors []*liberrors.ValidationError
assert.NotPanics(t, func() {
valid, valErrors = v.ValidateQueryParams(request)
})

assert.False(t, valid, "parameter present but not decodable as object should be invalid")
require.Len(t, valErrors, 1)
assert.Equal(t, "Query parameter 'filter' cannot be decoded", valErrors[0].Message)
}

func TestQueryParamObjectContentJSON_ValidObject(t *testing.T) {
// A content-wrapped JSON parameter with a valid JSON object should validate
// successfully against the schema.
spec := `openapi: 3.1.0
paths:
/test:
get:
parameters:
- name: filter
in: query
required: false
content:
application/json:
schema:
type: object
properties:
color:
type: string
operationId: testFilter
`

doc, err := libopenapi.NewDocument([]byte(spec))
require.NoError(t, err)

m, errs := doc.BuildV3Model()
require.NoError(t, errs)

v := NewParameterValidator(&m.Model)

request, _ := http.NewRequest(http.MethodGet, `https://example.com/test?filter={"color":"blue"}`, nil)

valid, valErrors := v.ValidateQueryParams(request)

assert.True(t, valid, "valid JSON object should pass validation")
assert.Empty(t, valErrors)
}
Loading