Skip to content

feat: add oas-validator plugin#13344

Merged
AlinsRan merged 13 commits into
apache:masterfrom
AlinsRan:feat/oas-validator-plugin
May 11, 2026
Merged

feat: add oas-validator plugin#13344
AlinsRan merged 13 commits into
apache:masterfrom
AlinsRan:feat/oas-validator-plugin

Conversation

@AlinsRan

@AlinsRan AlinsRan commented May 9, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR introduces the oas-validator plugin, which validates inbound HTTP requests against an OpenAPI Specification (OAS) 3.x document before forwarding them to upstream services.

Description

The oas-validator plugin rejects requests that do not conform to the configured OpenAPI spec, returning a configurable HTTP error code.

Validation scope (each independently configurable):

  • Request method and path
  • Query parameters
  • Request headers
  • Request body

Spec delivery:

  • Inline: spec provided as a JSON string in the plugin config
  • Remote URL: spec fetched from a URL; cached for a configurable TTL (plugin metadata.spec_url_ttl, default 3600 s)

Use cases:

  • Enforce API contracts without modifying upstream services
  • Reject malformed requests early (shift-left validation)
  • Protect backends from unexpected inputs

Example

The following example validates all requests to /api/* against an inline OpenAPI 3.0 spec. Requests with an invalid body are rejected with HTTP 400.

curl http://127.0.0.1:9180/apisix/admin/routes/1 \
  -H "X-API-KEY: $admin_key" -X PUT -d '{
  "uri": "/api/*",
  "plugins": {
    "oas-validator": {
      "spec": "{\"openapi\":\"3.0.0\",\"info\":{\"title\":\"Pet API\",\"version\":\"1.0.0\"},\"paths\":{\"/api/pet\":{\"post\":{\"requestBody\":{\"required\":true,\"content\":{\"application/json\":{\"schema\":{\"type\":\"object\",\"required\":[\"name\"],\"properties\":{\"name\":{\"type\":\"string\"}}}}}},\"responses\":{\"200\":{\"description\":\"ok\"}}}}}}"
    }
  },
  "upstream": {
    "type": "roundrobin",
    "nodes": { "127.0.0.1:1980": 1 }
  }
}'

A request missing the required name field will be rejected:

# Returns HTTP 400: failed to validate request body
curl -X POST http://127.0.0.1:9080/api/pet \
  -H "Content-Type: application/json" \
  -d '{"age": 3}'

Alternatively, the spec can be served from a remote URL using spec_url instead of the inline spec field.

Dependencies

This plugin depends on the lua-resty-openapi-validator LuaRocks package (>= 1.0.5-1), added to apisix-master-0.rockspec.

https://luarocks.org/modules/membphis/lua-resty-openapi-validator

Files changed

  • apisix/plugins/oas-validator.lua — plugin implementation
  • t/plugin/oas-validator.t — basic test cases
  • t/plugin/oas-validator2.t — extended test cases (plugin metadata, OAS 3.1)
  • t/plugin/oas-validator3.t — test cases for spec_url feature
  • t/spec/spec.json, t/spec/spec31.json, t/spec/spec31-gaps.json — test spec fixtures
  • docs/en/latest/plugins/oas-validator.md — English documentation
  • docs/zh/latest/plugins/oas-validator.md — Chinese documentation
  • apisix-master-0.rockspec — add lua-resty-openapi-validator dependency
  • apisix/cli/config.lua — register plugin in default list
  • docs/en/latest/config.json / docs/zh/latest/config.json — add to sidebar

The oas-validator plugin validates inbound HTTP requests against an
OpenAPI Specification (OAS) 3.x document before forwarding them to
upstream services. Requests that fail validation are rejected with a
configurable HTTP error status code.

The OpenAPI spec can be provided as an inline JSON string or fetched
from a remote URL. Remote specs are cached for a configurable TTL
(configured via plugin metadata). Validation covers request method,
path, query parameters, headers, and request body, with per-category
skip flags for selective enforcement.

Dependencies:
- lua-resty-openapi-validator (>= 1.0.5-1)

Co-authored-by: Copilot <[email protected]>
@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. dependencies Pull requests that update a dependency file doc Documentation things enhancement New feature or request plugin labels May 9, 2026
AlinsRan and others added 8 commits May 9, 2026 09:13
- Add Apache license headers to oas-validator.lua and test files
- Remove trailing whitespace in t/plugin/oas-validator.t

Co-authored-by: Copilot <[email protected]>
- Add oas-validator to expected plugin list in t/admin/plugins.t
- Add OpenAPI spec fixture files (t/spec/*.json) required by tests

Co-authored-by: Copilot <[email protected]>
- Change priority from 510 to 512 to avoid conflict with mcp-bridge
- Move plugin registration position in config.lua accordingly
- Add fake upstream server on port 1971 in test preprocessor
- Replace unreachable port 6969/1980 with port 1971 in test upstreams
- Fix oas-validator2.t: replace 1980 with 1970 for TEST 22/24/26
- Remove trailing scheme/pass_host fields from updated upstream blocks

Co-authored-by: Copilot <[email protected]>
- Rename APISIX CRD tab to APISIX Ingress Controller
- Simplify admin_key note block

Co-authored-by: Copilot <[email protected]>
nic-6443
nic-6443 previously approved these changes May 9, 2026
@AlinsRan
AlinsRan requested a review from Baoyuantop May 9, 2026 07:58
membphis
membphis previously approved these changes May 9, 2026
@AlinsRan
AlinsRan dismissed stale reviews from membphis, shreemaan-abhishek, and nic-6443 via 633d26c May 9, 2026 09:38
nic-6443
nic-6443 previously approved these changes May 9, 2026
type = "string",
minLength = 1
},
spec_url = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both support http and https
only https is not enough

@AlinsRan AlinsRan May 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, it support http
https? match oneOf http of https.

ref: https://github.com/apache/apisix/pull/13344/changes#diff-ad598e1c79a6882d92774e7d1ed2b8687a038e121a1246740a8cab4076712e52R480

=== TEST 19: create route with spec_url for TTL test
--- config
    location /t {
        content_by_lua_block {
            local t = require("lib.test_admin")
            local code, body = t.test('/apisix/admin/routes/1',
                ngx.HTTP_PUT,
                [[{
                    "uri": "/*",
                    "plugins": {
                        "oas-validator": {
                            "spec_url": "http://127.0.0.1:1979/spec.json"
                        }
                    },
                    "upstream": {
                        "type": "roundrobin",
                        "nodes": {
                            "127.0.0.1:1970": 1
                        }
                    }
                }]]
            )
            if code >= 300 then
                ngx.status = code
            end
            ngx.say(body)
        }
    }
--- response_body
passed

Comment thread apisix/plugins/oas-validator.lua Outdated

local function get_validator(conf)
if conf.spec then
if not conf._validator then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use lrucache to store the validator?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

store item in conf is not a best practise
another way: use metadata way

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace withconf._meta.

We don't need an extra lrucache here.
Storing the data directly in conf already acts as caching, and its lifecycle is naturally bound to the conf object.

@AlinsRan
AlinsRan dismissed stale reviews from nic-6443 and shreemaan-abhishek via 1d6ad38 May 11, 2026 01:56
@AlinsRan
AlinsRan merged commit 026c0dc into apache:master May 11, 2026
23 of 24 checks passed
@AlinsRan
AlinsRan deleted the feat/oas-validator-plugin branch May 11, 2026 08:27
wistefan pushed a commit to wistefan/apisix that referenced this pull request Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file doc Documentation things enhancement New feature or request plugin size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants