Root cause
Decoders dereference nil array items
The urlencoded, multipart and parseArray decoders dereference the nil Items field, so any request reaching an affected operation panics.
Provisional finding. Full causal and release verification remains incomplete.
kin-openapi's urlencoded, multipart and array decoders, written in 2019, assume every array schema has items. The AI-co-authored 0.136.0 changes accepted OpenAPI 3.1 arrays without items and enabled that mode by default, so a request to such an operation panics. 0.135.0 rejects the document; 0.143.0 fixes these decoders.
AI-assisted change
The human-written 2019 decoders already assumed every array had items; the AI-co-authored 0.136.0 change expanded 3.1 schema handling and enabled it by default, letting arrays without items reach them.
Root cause
The urlencoded, multipart and parseArray decoders dereference the nil Items field, so any request reaching an affected operation panics.
Fix
The repair returns errors from parseArray, UrlencodedBodyDecoder and MultipartBodyDecoder when an array schema has no items, so requests no longer panic.
Case facts
Cite this finding
Vibe Security Radar, "CVE-2026-97394 in getkin/kin-openapi," Georgia Tech SSLab, https://vibesecradar.com/cves/CVE-2026-97394/
@@ -873,6 +873,92 @@ func (schema Schema) JSONLookup(token string) (any, error) { return schema.MaxProps, nil case "discriminator": return schema.Discriminator, nil++ // OpenAPI 3.1 / JSON Schema 2020-12 fields+ case "const":+ return schema.Const, nil+ case "examples":+ return schema.Examples, nil+ case "prefixItems":+ return schema.PrefixItems, nil+ case "contains":+ if schema.Contains != nil {+ if schema.Contains.Ref != "" {+ return &Ref{Ref: schema.Contains.Ref}, nil+ }+ return schema.Contains.Value, nil+ }+ case "minContains":+ return schema.MinContains, nil+ case "maxContains":+ return schema.MaxContains, nil+ case "patternProperties":+ return schema.PatternProperties, nil+ case "dependentSchemas":+ return schema.DependentSchemas, nil+ case "propertyNames":+ if schema.PropertyNames != nil {+ if schema.PropertyNames.Ref != "" {+ return &Ref{Ref: schema.PropertyNames.Ref}, nil+ }+ return schema.PropertyNames.Value, nil+ }+ case "unevaluatedItems":+ if schema.UnevaluatedItems != nil {+ if schema.UnevaluatedItems.Ref != "" {+ return &Ref{Ref: schema.UnevaluatedItems.Ref}, nil+ }+ return schema.UnevaluatedItems.Value, nil+ }+ case "unevaluatedProperties":+ if schema.UnevaluatedProperties != nil {+ if schema.UnevaluatedProperties.Ref != "" {+ return &Ref{Ref: schema.UnevaluatedProperties.Ref}, nil+ }+ return schema.UnevaluatedProperties.Value, nil+ }+ case "if":+ if schema.If != nil {+ if schema.If.Ref != "" {+ return &Ref{Ref: schema.If.Ref}, nil+ }+ return schema.If.Value, nil+ }+ case "then":+ if schema.Then != nil {+ if schema.Then.Ref != "" {+ return &Ref{Ref: schema.Then.Ref}, nil+ }+ return schema.Then.Value, nil+ }+ case "else":+ if schema.Else != nil {+ if schema.Else.Ref != "" {+ return &Ref{Ref: schema.Else.Ref}, nil+ }+ return schema.Else.Value, nil+ }+ case "dependentRequired":+ return schema.DependentRequired, nil+ case "$id":+ return schema.SchemaID, nil+ case "$anchor":+ return schema.Anchor, nil+ case "$dynamicRef":+ return schema.DynamicRef, nil+ case "$dynamicAnchor":+ return schema.DynamicAnchor, nil+ case "contentMediaType":+ return schema.ContentMediaType, nil+ case "contentEncoding":+ return schema.ContentEncoding, nil+ case "contentSchema":+ if schema.ContentSchema != nil {+ if schema.ContentSchema.Ref != "" {+ return &Ref{Ref: schema.ContentSchema.Ref}, nil+ }+ return schema.ContentSchema.Value, nil+ }@@ -1124,6 +1124,9 @@ func pathFromKeys(kk []string) []any { // Every item is parsed as a primitive value. // The function returns an error when an error happened while parse array's items. func parseArray(raw []string, schemaRef *openapi3.SchemaRef) ([]any, error) {+ if schemaRef.Value.Items == nil || schemaRef.Value.Items.Value == nil {+ return nil, fmt.Errorf("array items schema is required for decoding")+ } var value []any for i, v := range raw { item, err := parsePrimitive(v, schemaRef.Value.Items)@@ -1418,6 +1421,9 @@ func UrlencodedBodyDecoder(body io.Reader, header http.Header, schema *openapi3. case propType.Is("object"): return nil, fmt.Errorf("unsupported schema of request body's property %q", propName) case propType.Is("array"):+ if propSchema.Value.Items == nil || propSchema.Value.Items.Value == nil {+ return nil, fmt.Errorf("unsupported schema of request body's property %q: array items required", propName)+ } items := propSchema.Value.Items.Value if !(items.Type.Is("string") || items.Type.Is("integer") || items.Type.Is("number") || items.Type.Is("boolean")) { return nil, fmt.Errorf("unsupported schema of request body's property %q", propName)@@ -1561,6 +1567,9 @@ func MultipartBodyDecoder(body io.Reader, header http.Header, schema *openapi3.S } } if valueSchema.Value.Type.Is("array") {+ if valueSchema.Value.Items == nil {+ return nil, fmt.Errorf("unsupported schema of multipart part %q: array items required", name)+ } valueSchema = valueSchema.Value.Items } }AI-assisted change d70a18c045bd95fa06454c779f7f94d067254c90c00f443c8c0de6e42791680d · Fix 9255d7797902660335791e71263a4deb12ff1e0fbd10d5bf40869c03b2b7d05d