Skip to content

Bump kin-openapi to v0.144.0 (GHSA-mmfr-pmjx-hw9w) via upjet v2.4.1 - #734

Closed
Breee with Copilot wants to merge 2 commits into
mainfrom
copilot/fix-nil-pointer-panic
Closed

Bump kin-openapi to v0.144.0 (GHSA-mmfr-pmjx-hw9w) via upjet v2.4.1#734
Breee with Copilot wants to merge 2 commits into
mainfrom
copilot/fix-nil-pointer-panic

Conversation

Copilot AI commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

github.com/getkin/kin-openapi v0.133.0 is affected by GHSA-mmfr-pmjx-hw9w / CVE-2026-76905: a nil-pointer dereference in openapi3filter.ConvertErrors when rendering validation errors for a malformed multipart/form-data body. It reaches this repo only as an indirect dependency.

Changes

  • go.mod / go.sum (updated via go get + go mod tidy):
    • github.com/getkin/kin-openapi v0.133.0 → v0.144.0
    • github.com/crossplane/upjet/v2 v2.3.0 → v2.4.1
    • github.com/go-openapi/swag* v0.25.4 → v0.25.5
    • transitively: oasdiff v1.11.8 → v1.26.1, upbound/uptest → main pseudo-version

Why the upjet bump is required

The dependency path is configupjet/v2/pkg/configuptest/pkg/crdschemaoasdiffkin-openapi, and each hop is API-coupled:

  • bumping only kin-openapi breaks oasdiff v1.11.8Schema.ExclusiveMin/Max changed from bool to openapi3.ExclusiveBound in v0.141.0
  • bumping only oasdiff breaks uptest (utils.StringList, diff.Config.WithExcludeElements removed)
  • bumping only uptest breaks upjet v2.3.0 (TypeChangeDetails.Deleted.Is)

upjet v2.4.1 is the lowest release that pins the whole fixed chain, which is why the resulting version is v0.144.0 rather than the minimum patched v0.141.0. make generate produces no diff under v2.4.1, so there is no generated-code churn to review.

The go-openapi/swag* bump is unrelated to the CVE but required: go mod tidy fails at v0.25.4 because the test dependency github.com/go-openapi/testify/v2/assert/yaml no longer exists.

Reachability assessment

Not reachable — high confidence.

  • No file in this repository imports github.com/getkin/kin-openapi; it is indirect only.
  • The single entry point is uptest's CRD schema diffing, which uses openapi3 (and oasdiff/diff) at code-generation time. The vulnerable openapi3filter package is not in that call graph.
  • The provider is a Kubernetes controller and exposes no HTTP endpoint that validates multipart/form-data bodies through openapi3filter, so the advisory's unauthenticated-DoS scenario has no corresponding surface here.

The update primarily clears the scanner alert rather than remediating an active risk.

Original prompt

This section details the Dependabot vulnerability alert you should resolve

<alert_title>kin-openapi openai3filter: nil-pointer panic in ConvertErrors on malformed multipart/form-data body enables unauthenticated DoS</alert_title>
<alert_description>### Summary

A nil-pointer dereference in openapi3filter.ConvertErrors lets any unauthenticated client crash a server with a single HTTP request. When an application validates a multipart/form-data request body and renders the resulting validation error through the library-provided ValidationErrorEncoder / ConvertErrors helpers, a malformed scalar form field (e.g. a non-numeric value for an integer property) produces an error shape that convertParseError dereferences without a nil check. The handler goroutine panics, causing a denial of service. application/json request bodies are not affected — the bug is specific to multipart/form-data.

Details

The panic is in convertParseError, at openapi3filter/validation_error_encoder.go:119-120 (still present on master at the time of writing):

} else if innerErr.RootCause() != nil {
    if rootErr, ok := innerErr.Cause.(*ParseError); ok &&
        rootErr.Kind == KindInvalidFormat && e.Parameter.In == "query" {   // ❌ e.Parameter may be nil → panic

The comparison e.Parameter.In == "query" assumes e.Parameter is non-nil. It is reached whenever both of the following hold:

  1. e.Parameter == nil. A *RequestError carries either Parameter (parameter errors) or RequestBody (body errors), never both. ValidateRequestBody builds body errors with only RequestBody set, leaving Parameter nil — see validate_request.go:326-332.
  2. innerErr.Cause is itself a *ParseError (a ParseError nested inside a ParseError), so the type assertion on line 119 succeeds and execution reaches the e.Parameter.In dereference on line 120.

The only default code path that satisfies both conditions is the multipart body decoder, which wraps a failed part's *ParseError inside another *ParseError at req_resp_decoder.go:1549 and :1558:

if v, ok := err.(*ParseError); ok {
    return nil, &ParseError{path: []any{name}, Cause: v}   // v is a *ParseError → nested
}

Why other paths do not reach the dereference:

Body content type Failure mode RequestError.Err shape .Cause is *ParseError? e.Parameter Panics?
multipart/form-data scalar part fails primitive parse (age=notanumber) *ParseError wrapping a *ParseError yes nil YES
application/json malformed JSON syntax *ParseError whose .Cause is an encoding/json error no (assertion fails → safe fallback branch) nil no
application/json wrong type / schema violation *openapi3.SchemaError (routed to convertSchemaError, never reaches convertParseError) n/a nil no
styled query / path params invalid format *ParseError wrapping a *ParseError yes set (non-nil) no (guard/assignment succeeds)

Note that the sibling "path" branch two lines above (line 108) already guards correctly with e.Parameter != nil; the "query" branch simply omits the same guard.

Recommended fix. Add the missing nil guard to the condition:

 		if rootErr, ok := innerErr.Cause.(*ParseError); ok &&
-			rootErr.Kind == KindInvalidFormat && e.Parameter.In == "query" {
+			rootErr.Kind == KindInvalidFormat && e.Parameter != nil && e.Parameter.In == "query" {

When e.Parameter == nil the inner if is skipped and control falls through to the existing return &ValidationError{Status: http.StatusBadRequest, Title: innerErr.Reason} at line 127-130 — a correct 400 Bad Request. I verified that applying only this one-line guard stops the panic and returns *ValidationError{Status: 400}.

Minor follow-up worth including in the same change: for the multipart nested *ParseError, the outer ParseError.Reason is empty, so the fallback Title: innerErr.Reason yields a 400 with an empty Title. The descriptive text lives in innerErr.Error() (e.g. "path age: value notanumber: an invalid integer: invalid syntax"). Prefer a non-empty fallback:

title := innerErr.Reason
if title == "" {
    title = innerErr.Error()
}
return &ValidationError{Status: http.StatusBadRequest, Title...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Resolves crossplane-contrib/provider-keycloak alert #20

…mjx-hw9w)

Co-authored-by: Breee <11966385+Breee@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix nil-pointer panic in ConvertErrors for multipart/form-data Bump kin-openapi to v0.144.0 (GHSA-mmfr-pmjx-hw9w) via upjet v2.4.1 Aug 23, 2026
Copilot AI requested a review from Breee August 23, 2026 19:27
@Breee Breee closed this Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants