feat: add request-validation plugin (#700)

Signed-off-by: sjcsjc123 <1401189096@qq.com>
This commit is contained in:
SJC
2024-02-02 16:52:43 +08:00
committed by GitHub
parent e43f5d106f
commit c63cdb62ea
7 changed files with 895 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
# 功能说明
`request-validation`插件用于提前验证向上游服务转发的请求。该插件使用`JSON Schema`机制进行数据验证可以验证请求的body及header数据。
# 配置字段
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| -------- | -------- | -------- |-----| -------- |
|header_schema|object|选填| - |配置用于验证请求header的JSON Schema|
|body_schema|object|选填| - |配置用于验证请求body的JSON Schema|
|rejected_code|number|选填| 403 |配置请求被拒绝时返回的HTTP状态码|
|rejected_msg|string|选填| - |配置请求被拒绝时返回的HTTP应答Body|
|enable_swagger|bool|选填| false |配置是否开启swagger文档验证|
|enable_oas3|bool|选填| false |配置是否开启OAS3文档验证|
**校验规则对header和body是一样的下面以body为例说明**
# 配置示例
## 枚举Enum验证
```yaml
body_schema:
type: object
required:
- enum_payload
properties:
enum_payload:
type: string
enum:
- "enum_string_1"
- "enum_string_2"
default: "enum_string_1"
```
## 布尔Boolean验证
```yaml
body_schema:
type: object
required:
- boolean_payload
properties:
boolean_payload:
type: boolean
default: true
```
## 数字范围Number or Integer验证
```yaml
body_schema:
type: object
required:
- integer_payload
properties:
integer_payload:
type: integer
minimum: 1
maximum: 10
```
## 字符串长度String验证
```yaml
body_schema:
type: object
required:
- string_payload
properties:
string_payload:
type: string
minLength: 1
maxLength: 10
```
## 正则表达式Regex验证
```yaml
body_schema:
type: object
required:
- regex_payload
properties:
regex_payload:
type: string
minLength: 1
maxLength: 10
pattern: "^[a-zA-Z0-9_]+$"
```
## 数组Array验证
```yaml
body_schema:
type: object
required:
- array_payload
properties:
array_payload:
type: array
minItems: 1
items:
type: integer
minimum: 1
maximum: 10
uniqueItems: true
default: [1, 2, 3]
```
## 多字段组合Combined验证
```yaml
body_schema:
type: object
required:
- boolean_payload
- array_payload
- regex_payload
properties:
boolean_payload:
type: boolean
array_payload:
type: array
minItems: 1
items:
type: integer
minimum: 1
maximum: 10
uniqueItems: true
default: [1, 2, 3]
regex_payload:
type: string
minLength: 1
maxLength: 10
pattern: "^[a-zA-Z0-9_]+$"
```
## 自定义拒绝信息
```yaml
body_schema:
type: object
required:
- boolean_payload
properties:
boolean_payload:
type: boolean
rejected_code: 403
rejected_msg: "请求被拒绝"
```
# 本地调试
参考[使用 GO 语言开发 WASM 插件](https://higress.io/zh-cn/docs/user/wasm-go#%E4%B8%89%E6%9C%AC%E5%9C%B0%E8%B0%83%E8%AF%95)

View File

@@ -0,0 +1 @@
1.0.0

View File

@@ -0,0 +1,18 @@
module github.com/alibaba/higress/plugins/wasm-go/extensions/request-validation
go 1.19
require (
github.com/alibaba/higress/plugins/wasm-go v1.3.1
github.com/santhosh-tekuri/jsonschema v1.2.4
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0
github.com/tidwall/gjson v1.17.0
)
require (
github.com/google/uuid v1.3.0 // indirect
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 // indirect
github.com/magefile/mage v1.14.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
)

View File

@@ -0,0 +1,22 @@
github.com/alibaba/higress/plugins/wasm-go v1.3.1 h1:d+t4W2NyqmqUz6DPZENflODfkLgdVlTfyso+nq0fSkg=
github.com/alibaba/higress/plugins/wasm-go v1.3.1/go.mod h1:WZ/68vwe8qWhusa6C4/gMwUqas0jvHWSOa1bp8iK8F4=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbGQ2DTIXHBHxWfqCYQW1fKjyJ/I7W1pMyUDeEA=
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis=
github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0 h1:kS7BvMKN+FiptV4pfwiNX8e3q14evxAWkhYbxt8EI1M=
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0/go.mod h1:qkW5MBz2jch2u8bS59wws65WC+Gtx3x0aPUX5JL7CXI=
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -0,0 +1,199 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
"github.com/santhosh-tekuri/jsonschema"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
"github.com/tidwall/gjson"
)
const (
defaultHeaderSchema = "header"
defaultBodySchema = "body"
defaultRejectedCode = 403
)
func main() {
wrapper.SetCtx(
"request-validation",
wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
wrapper.ProcessRequestBodyBy(onHttpRequestBody),
wrapper.ParseConfigBy(parseConfig),
)
}
// Config is the config for request validation.
type Config struct {
// compiler is the compiler for json schema.
compiler *jsonschema.Compiler
// rejectedCode is the code for rejected request.
rejectedCode uint32
// rejectedMsg is the message for rejected request.
rejectedMsg string
// draft is the draft version of json schema.
draft *jsonschema.Draft
// enableBodySchema is the flag for enable body schema.
enableBodySchema bool
// enableHeaderSchema is the flag for enable header schema.
enableHeaderSchema bool
}
func parseConfig(result gjson.Result, config *Config, log wrapper.Log) error {
headerSchema := result.Get("header_schema").String()
bodySchema := result.Get("body_schema").String()
enableSwagger := result.Get("enable_swagger").Bool()
enableOas3 := result.Get("enable_oas3").Bool()
code := result.Get("rejected_code").Int()
msg := result.Get("rejected_msg").String()
// set config default value
config.enableBodySchema = false
config.enableHeaderSchema = false
// check enable_swagger and enable_oas3
if enableSwagger && enableOas3 {
return fmt.Errorf("enable_swagger and enable_oas3 can not be true at the same time")
}
// set draft version
if enableSwagger {
config.draft = jsonschema.Draft4
}
if enableOas3 {
config.draft = jsonschema.Draft7
}
if !enableSwagger && !enableOas3 {
config.draft = jsonschema.Draft7
}
// create compiler
compiler := jsonschema.NewCompiler()
compiler.Draft = config.draft
config.compiler = compiler
// add header schema to compiler
if headerSchema != "" {
err := config.compiler.AddResource(defaultHeaderSchema, strings.NewReader(headerSchema))
if err != nil {
return err
}
config.enableHeaderSchema = true
}
// add body schema to compiler
if bodySchema != "" {
err := config.compiler.AddResource(defaultBodySchema, strings.NewReader(bodySchema))
if err != nil {
return err
}
config.enableBodySchema = true
}
// check rejected_code is valid
if code != 0 && code > 100 && code < 600 {
config.rejectedCode = uint32(code)
} else {
config.rejectedCode = defaultRejectedCode
}
config.rejectedMsg = msg
return nil
}
func onHttpRequestHeaders(ctx wrapper.HttpContext, config Config, log wrapper.Log) types.Action {
if !config.enableHeaderSchema {
return types.ActionContinue
}
// get headers
headers, err := proxywasm.GetHttpRequestHeaders()
if err != nil {
log.Errorf("get request headers failed: %v", err)
return types.ActionContinue
}
// covert to schema
schema := make(map[string]interface{})
for _, header := range headers {
schema[header[0]] = header[1]
}
// convert to json string
schemaBytes, err := json.Marshal(schema)
if err != nil {
log.Errorf("marshal schema failed: %v", err)
return types.ActionContinue
}
// validate
document := strings.NewReader(string(schemaBytes))
compile, err := config.compiler.Compile(defaultHeaderSchema)
if err != nil {
log.Errorf("compile schema failed: %v", err)
return types.ActionContinue
}
err = compile.Validate(document)
if err != nil {
log.Errorf("validate request headers failed: %v", err)
proxywasm.SendHttpResponse(config.rejectedCode, nil, []byte(config.rejectedMsg), -1)
return types.ActionPause
}
return types.ActionContinue
}
func onHttpRequestBody(ctx wrapper.HttpContext, config Config, body []byte, log wrapper.Log) types.Action {
if !config.enableBodySchema {
return types.ActionContinue
}
// covert to schema
schema := make(map[string]interface{})
err := json.Unmarshal(body, &schema)
if err != nil {
log.Errorf("unmarshal body failed: %v", err)
return types.ActionContinue
}
// convert to json string
schemaBytes, err := json.Marshal(schema)
if err != nil {
log.Errorf("marshal schema failed: %v", err)
return types.ActionContinue
}
// validate
document := strings.NewReader(string(schemaBytes))
compile, err := config.compiler.Compile(defaultBodySchema)
if err != nil {
log.Errorf("compile schema failed: %v", err)
return types.ActionContinue
}
err = compile.Validate(document)
if err != nil {
log.Errorf("validate request body failed: %v", err)
proxywasm.SendHttpResponse(config.rejectedCode, nil, []byte(config.rejectedMsg), -1)
return types.ActionPause
}
return types.ActionContinue
}

View File

@@ -0,0 +1,412 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tests
import (
"testing"
"github.com/alibaba/higress/test/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
)
func init() {
Register(WasmPluginsRequestValidation)
}
var WasmPluginsRequestValidation = suite.ConformanceTest{
ShortName: "WasmPluginsRequestValidation",
Description: "The Ingress in the higress-conformance-infra namespace test the request-validation wasmplugins.",
Manifests: []string{"tests/go-wasm-request-validation.yaml"},
Features: []suite.SupportedFeature{suite.WASMGoConformanceFeature},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testCases := []http.Assertion{
{
Meta: http.AssertionMeta{
TestCaseName: "request validation pass",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Headers: map[string]string{
"enum_payload": "enum_string_1",
},
Body: []byte(`
{
"enum_payload": "enum_string_1",
"bool_payload": true,
"integer_payload": 100,
"string_payload": "abc",
"regex_payload": "abc123",
"array_payload": [200, 302]
}
`),
ContentType: http.ContentTypeApplicationJson,
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "header lack of require parameter",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
CompareTarget: http.CompareTargetResponse,
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Body: []byte(`
{
"enum_payload": "enum_string_1",
"bool_payload": true,
"integer_payload": 100,
"string_payload": "abc",
"regex_payload": "abc123",
"array_payload": [200, 302]
}
`),
ContentType: http.ContentTypeApplicationJson,
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 403,
Body: []byte(`customize reject message`),
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "body lack of require parameter",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
CompareTarget: http.CompareTargetResponse,
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Headers: map[string]string{
"enum_payload": "enum_string_1",
},
Body: []byte(`
{
"bool_payload": true,
"integer_payload": 100,
"string_payload": "abc",
"regex_payload": "abc123",
"array_payload": [200, 302]
}
`),
ContentType: http.ContentTypeApplicationJson,
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 403,
Body: []byte(`customize reject message`),
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "body enum payload not in enum list",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
CompareTarget: http.CompareTargetResponse,
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Headers: map[string]string{
"enum_payload": "enum_string_1",
},
Body: []byte(`
{
"enum_payload": "enum_string_3",
"bool_payload": true,
"integer_payload": 100,
"string_payload": "abc",
"regex_payload": "abc123",
"array_payload": [200, 302]
}
`),
ContentType: http.ContentTypeApplicationJson,
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 403,
Body: []byte(`customize reject message`),
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "body bool payload not bool type",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
CompareTarget: http.CompareTargetResponse,
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Headers: map[string]string{
"enum_payload": "enum_string_1",
},
Body: []byte(`
{
"enum_payload": "enum_string_1",
"bool_payload": "string",
"integer_payload": 100,
"string_payload": "abc",
"regex_payload": "abc123",
"array_payload": [200, 302]
}
`),
ContentType: http.ContentTypeApplicationJson,
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 403,
Body: []byte(`customize reject message`),
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "body integer payload not in range",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
CompareTarget: http.CompareTargetResponse,
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Headers: map[string]string{
"enum_payload": "enum_string_1",
},
Body: []byte(`
{
"enum_payload": "enum_string_1",
"bool_payload": true,
"integer_payload": 70000,
"string_payload": "abc",
"regex_payload": "abc123",
"array_payload": [200, 302]
}
`),
ContentType: http.ContentTypeApplicationJson,
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 403,
Body: []byte(`customize reject message`),
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "body string payload length not in range",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
CompareTarget: http.CompareTargetResponse,
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Headers: map[string]string{
"enum_payload": "enum_string_1",
},
Body: []byte(`
{
"enum_payload": "enum_string_1",
"bool_payload": true,
"integer_payload": 100,
"string_payload": "a",
"regex_payload": "abc123",
"array_payload": [200, 302]
}
`),
ContentType: http.ContentTypeApplicationJson,
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 403,
Body: []byte(`customize reject message`),
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "body regex payload not match regex pattern",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
CompareTarget: http.CompareTargetResponse,
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Headers: map[string]string{
"enum_payload": "enum_string_1",
},
Body: []byte(`
{
"enum_payload": "enum_string_1",
"bool_payload": true,
"integer_payload": 100,
"string_payload": "abc",
"regex_payload": "abc@123",
"array_payload": [200, 302]
}
`),
ContentType: http.ContentTypeApplicationJson,
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 403,
Body: []byte(`customize reject message`),
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "body array payload not in array range",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
CompareTarget: http.CompareTargetResponse,
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Headers: map[string]string{
"enum_payload": "enum_string_1",
},
Body: []byte(`
{
"enum_payload": "enum_string_1",
"bool_payload": true,
"integer_payload": 100,
"string_payload": "abc",
"regex_payload": "abc123",
"array_payload": [150, 302]
}
`),
ContentType: http.ContentTypeApplicationJson,
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 403,
Body: []byte(`customize reject message`),
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "body array payload not unique array items",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
CompareTarget: http.CompareTargetResponse,
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Headers: map[string]string{
"enum_payload": "enum_string_1",
},
Body: []byte(`
{
"enum_payload": "enum_string_1",
"bool_payload": true,
"integer_payload": 100,
"string_payload": "abc",
"regex_payload": "abc123",
"array_payload": [302, 302]
}
`),
ContentType: http.ContentTypeApplicationJson,
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 403,
Body: []byte(`customize reject message`),
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "body array payload length not in range",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
CompareTarget: http.CompareTargetResponse,
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Headers: map[string]string{
"enum_payload": "enum_string_1",
},
Body: []byte(`
{
"enum_payload": "enum_string_1",
"bool_payload": true,
"integer_payload": 100,
"string_payload": "abc",
"regex_payload": "abc123",
"array_payload": [302]
}
`),
ContentType: http.ContentTypeApplicationJson,
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 403,
Body: []byte(`customize reject message`),
},
},
},
}
t.Run("WasmPlugins request-validation", func(t *testing.T) {
for _, testcase := range testCases {
t.Logf("Running test case: %s", testcase.Meta.TestCaseName)
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,97 @@
# Copyright (c) 2022 Alibaba Group Holding Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httproute-app-root
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Prefix
path: "/foo"
backend:
service:
name: infra-backend-v1
port:
number: 8080
---
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: request-validation
namespace: higress-system
spec:
defaultConfig:
header_schema:
type: object
required:
- enum_payload
properties:
enum_payload:
type: string
enum:
- "enum_string_1"
- "enum_string_2"
default: "enum_string_1"
body_schema:
type: object
required:
- enum_payload
- bool_payload
- integer_payload
- string_payload
- regex_payload
- array_payload
properties:
enum_payload:
type: string
enum:
- "enum_string_1"
- "enum_string_2"
default: "enum_string_1"
bool_payload:
type: boolean
default: true
integer_payload:
type: integer
minimum: 1
maximum: 65535
string_payload:
type: string
minLength: 2
maxLength: 32
regex_payload:
type: string
minLength: 1
maxLength: 32
pattern: "^[a-zA-Z0-9]+$"
array_payload:
type: array
minItems: 2
items:
type: integer
minimum: 200
maximum: 599
uniqueItems: true
default: [200, 302]
rejected_code: 403
rejected_msg: "customize reject message"
enable_swagger: true
enable_oas3: false
url: file:///opt/plugins/wasm-go/extensions/request-validation/plugin.wasm