You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@dubbo.apache.org by "Skylm808 (via GitHub)" <gi...@apache.org> on 2026/05/26 14:11:23 UTC

[PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Skylm808 opened a new pull request, #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948

   **What this PR does**:
   
   Adds a dedicated `dgp.filter.http.openapi` HTTP filter for OpenAPI-driven request validation.
   
   The filter loads a local OpenAPI 3.0/3.1/3.2 document with `libopenapi` and validates incoming requests with `libopenapi-validator` before forwarding them upstream. It supports OpenAPI path/method matching, templated paths such as `/users/{id}`, path/query/header parameter validation, and JSON request body schema validation.
   
   This PR also removes the previous custom OpenAPI validation code from `apiconfig`, keeping `apiconfig` focused on Pixiu API route matching and metadata.
   
   **Which issue(s) this PR fixes**:
   
   Fixes #857
   
   **Special notes for your reviewer**:
   
   OpenAPI `security` validation is intentionally disabled in this filter. Authentication and authorization should continue to be handled by dedicated filters such as JWT, OPA, or SAML.
   
   If a request path and method are not declared in the OpenAPI document, this filter skips validation and lets the request continue.
   
   **Does this PR introduce a user-facing change?**:
   
   ```release-note
   Adds a dedicated OpenAPI request validation filter, `dgp.filter.http.openapi`, backed by libopenapi and libopenapi-validator.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Similarityoung (via GitHub)" <gi...@apache.org>.
Similarityoung commented on PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#issuecomment-4604294828

   fix ci please


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3342136729


##########
pkg/filter/http/apiconfig/config.go:
##########
@@ -23,10 +23,8 @@ import (
 
 // ApiConfigConfig the config for api_config filter
 type ApiConfigConfig struct {
-	APIMetaConfig           *model.APIMetaConfig `yaml:"api_meta_config" json:"api_meta_config,omitempty"`
-	Path                    string               `yaml:"path" json:"path,omitempty"`
-	Dynamic                 bool                 `yaml:"dynamic" json:"dynamic,omitempty"`
-	DynamicAdapter          string               `yaml:"dynamic_adapter" json:"dynamic_adapter,omitempty"`
-	OpenAPIPath             string               `yaml:"openapi_path" json:"openapi_path,omitempty"`
-	EnableOpenAPIValidation bool                 `yaml:"enable_openapi_validation" json:"enable_openapi_validation,omitempty"`
+	APIMetaConfig  *model.APIMetaConfig `yaml:"api_meta_config" json:"api_meta_config,omitempty"`
+	Path           string               `yaml:"path" json:"path,omitempty"`
+	Dynamic        bool                 `yaml:"dynamic" json:"dynamic,omitempty"`
+	DynamicAdapter string               `yaml:"dynamic_adapter" json:"dynamic_adapter,omitempty"`
 }

Review Comment:
   已处理。现在保留了 apiconfig 里的 openapi_path 和 enable_openapi_validation 旧字段,并在 Apply() 阶段 fail fast,提示用户迁移到独立的 dgp.filter.http.openapi filter。后续也补充了 key presence 检测,所以即使 openapi_path: "" 或 enable_openapi_validation: false 也会触发迁移提示。已补回归测试。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3342369239


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {
+		return nil, "", false
+	}
+	if !hasRequestOperation(req, pathItem) {
+		return nil, "", false
+	}
+	return pathItem, foundPath, true
+}
+
+func hasRequestOperation(req *http.Request, pathItem *v3.PathItem) bool {
+	switch req.Method {
+	case http.MethodGet:
+		return pathItem.Get != nil
+	case http.MethodPost:
+		return pathItem.Post != nil
+	case http.MethodPut:
+		return pathItem.Put != nil
+	case http.MethodDelete:
+		return pathItem.Delete != nil
+	case http.MethodOptions:
+		return pathItem.Options != nil
+	case http.MethodHead:
+		return pathItem.Head != nil || pathItem.Get != nil
+	case http.MethodPatch:
+		return pathItem.Patch != nil
+	case http.MethodTrace:
+		return pathItem.Trace != nil
+	default:
+		operations := pathItem.GetOperations()
+		return operations != nil && operations.GetOrZero(strings.ToLower(req.Method)) != nil
+	}
+}
+
+func loadValidatorFromFile(path string) (openapiValidator.Validator, *v3.Document, error) {
+	spec, err := os.ReadFile(path)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	doc, err := libopenapi.NewDocumentWithConfiguration(spec, &datamodel.DocumentConfiguration{
+		BasePath: filepath.Dir(path),
+	})
+	if err != nil {
+		return nil, nil, err
+	}
+
+	model, err := doc.BuildV3Model()
+	if err != nil {

Review Comment:
   同上,这个问题已在同一处修复:BuildV3Model() 返回 error 时会中止初始化,不再使用 partial model,并补了 invalid $ref 回归测试。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3369028350


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"bytes"
+	"io"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	validatorRadix "github.com/pb33f/libopenapi-validator/radix"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+
+	defaultMaxRequestBodyBytes = 1 << 20
+)
+
+var errOpenAPIRequestBodyTooLarge = errors.New("openapi request body too large")
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg               *Config
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Filter struct {
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+		// MaxRequestBodyBytes limits how much request body data OpenAPI validation may read.
+		// Zero uses the default limit.
+		MaxRequestBodyBytes int64 `yaml:"max_request_body_bytes" json:"max_request_body_bytes,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path, err := cleanOpenAPIPath(factory.cfg.Path)
+	if err != nil {
+		return err
+	}
+	factory.cfg.Path = path
+
+	maxRequestBody, err := factory.cfg.effectiveMaxRequestBodyBytes()
+	if err != nil {
+		return err
+	}
+
+	validator, model, validationOptions, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	factory.validationOptions = validationOptions
+	factory.maxRequestBody = maxRequestBody
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator:         factory.validator,
+		model:             factory.model,
+		validationOptions: factory.validationOptions,
+		maxRequestBody:    factory.maxRequestBody,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if err := f.prepareRequestBodyForValidation(req); err != nil {
+		if err == errOpenAPIRequestBodyTooLarge {
+			errResp := contexthttp.PayloadTooLarge.WithError(err)
+			ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+			logger.Debug(errResp.Error())
+			return filter.Stop
+		}
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request body read failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request body read failed: %v", err)
+		return filter.Stop
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		validationDetails := formatValidationErrors(validationErrs)
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request validation failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request validation failed: %s", validationDetails)
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, f.validationOptions)
+	if len(validationErrs) > 0 {

Review Comment:
   已处理。
   
   当前没有扩展 `FindPath` / path tree 对 OpenAPI 3.2 additional operations 的支持,而是按当前实现能力收窄文档说明:OpenAPI 3.2 只声明支持本 filter 已接入的标准 HTTP operation,不再宣称完整支持 3.2 additional operations。
   
   因此 `query` 这类 OpenAPI 3.2 additional operation 仍会被视为未声明 method 并跳过校验,文档已同步说明这个限制。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3342355738


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {

Review Comment:
   已处理。现在 FindPath 返回 validationErrs 时会记录 Debug 日志,包含请求 method/path 和具体错误内容,方便区分路径未声明、模板歧义或查找异常;pathItem 为空或 operation 未声明时仍按未声明路径跳过校验。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Similarityoung (via GitHub)" <gi...@apache.org>.
Similarityoung commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3367026652


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"bytes"
+	"io"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	validatorRadix "github.com/pb33f/libopenapi-validator/radix"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+
+	defaultMaxRequestBodyBytes = 1 << 20
+)
+
+var errOpenAPIRequestBodyTooLarge = errors.New("openapi request body too large")
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg               *Config
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Filter struct {
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+		// MaxRequestBodyBytes limits how much request body data OpenAPI validation may read.
+		// Zero uses the default limit.
+		MaxRequestBodyBytes int64 `yaml:"max_request_body_bytes" json:"max_request_body_bytes,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path, err := cleanOpenAPIPath(factory.cfg.Path)
+	if err != nil {
+		return err
+	}
+	factory.cfg.Path = path
+
+	maxRequestBody, err := factory.cfg.effectiveMaxRequestBodyBytes()
+	if err != nil {
+		return err
+	}
+
+	validator, model, validationOptions, err := loadValidatorFromFile(path)

Review Comment:
   [P1] 这里返回加载错误本身没问题,但当前集成链路不会真正让启动失败。`FilterManager.ReLoad()` 只记录 `Apply()` 的错误,随后仍会把返回的 nil factory 放进 `filtersArray`;后续创建 filter chain 时会对这个 nil factory 调 `PrepareFilterChain`,请求路径上可能直接 panic。建议让 HTTP filter 初始化错误向上返回,或者至少不要安装失败的 factory,并补一个非法 OpenAPI 配置的集成测试。



##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"bytes"
+	"io"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	validatorRadix "github.com/pb33f/libopenapi-validator/radix"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+
+	defaultMaxRequestBodyBytes = 1 << 20
+)
+
+var errOpenAPIRequestBodyTooLarge = errors.New("openapi request body too large")
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg               *Config
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Filter struct {
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+		// MaxRequestBodyBytes limits how much request body data OpenAPI validation may read.
+		// Zero uses the default limit.
+		MaxRequestBodyBytes int64 `yaml:"max_request_body_bytes" json:"max_request_body_bytes,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path, err := cleanOpenAPIPath(factory.cfg.Path)
+	if err != nil {
+		return err
+	}
+	factory.cfg.Path = path
+
+	maxRequestBody, err := factory.cfg.effectiveMaxRequestBodyBytes()
+	if err != nil {
+		return err
+	}
+
+	validator, model, validationOptions, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	factory.validationOptions = validationOptions
+	factory.maxRequestBody = maxRequestBody
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator:         factory.validator,
+		model:             factory.model,
+		validationOptions: factory.validationOptions,
+		maxRequestBody:    factory.maxRequestBody,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if err := f.prepareRequestBodyForValidation(req); err != nil {
+		if err == errOpenAPIRequestBodyTooLarge {
+			errResp := contexthttp.PayloadTooLarge.WithError(err)
+			ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+			logger.Debug(errResp.Error())
+			return filter.Stop
+		}
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request body read failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request body read failed: %v", err)
+		return filter.Stop
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		validationDetails := formatValidationErrors(validationErrs)
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request validation failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request validation failed: %s", validationDetails)
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, f.validationOptions)
+	if len(validationErrs) > 0 {
+		logger.Debugf("openapi path lookup errors for %s %s: %s", req.Method, req.URL.Path, formatValidationErrors(validationErrs))
+		return nil, "", false
+	}
+	if pathItem == nil {
+		return nil, "", false
+	}
+	if !hasRequestOperation(req, pathItem) {
+		return nil, "", false
+	}
+	return pathItem, foundPath, true
+}
+
+func hasRequestOperation(req *http.Request, pathItem *v3.PathItem) bool {
+	switch req.Method {
+	case http.MethodGet:
+		return pathItem.Get != nil
+	case http.MethodPost:
+		return pathItem.Post != nil
+	case http.MethodPut:
+		return pathItem.Put != nil
+	case http.MethodDelete:
+		return pathItem.Delete != nil
+	case http.MethodOptions:
+		return pathItem.Options != nil
+	case http.MethodHead:
+		return pathItem.Head != nil || pathItem.Get != nil
+	case http.MethodPatch:
+		return pathItem.Patch != nil
+	case http.MethodTrace:
+		return pathItem.Trace != nil
+	default:
+		operations := pathItem.GetOperations()
+		return operations != nil && operations.GetOrZero(strings.ToLower(req.Method)) != nil
+	}
+}
+
+func (f *Filter) prepareRequestBodyForValidation(req *http.Request) error {
+	if req.Body == nil || req.Body == http.NoBody {
+		return nil
+	}
+	if f.maxRequestBody <= 0 {
+		return nil
+	}
+	if req.ContentLength > f.maxRequestBody {
+		return errOpenAPIRequestBodyTooLarge
+	}
+
+	body, err := readRequestBodyWithinLimit(req.Body, f.maxRequestBody)
+	if err != nil {
+		return err
+	}
+	req.Body = io.NopCloser(bytes.NewReader(body))
+	req.ContentLength = int64(len(body))
+	return nil
+}
+
+func readRequestBodyWithinLimit(body io.ReadCloser, limit int64) ([]byte, error) {
+	// The original body is replaced by the caller after this bounded read.
+	defer body.Close()
+
+	data, err := io.ReadAll(io.LimitReader(body, limit+1))
+	if err != nil {
+		return nil, err
+	}
+	if int64(len(data)) > limit {
+		return nil, errOpenAPIRequestBodyTooLarge
+	}
+	return data, nil
+}
+
+func loadValidatorFromFile(path string) (openapiValidator.Validator, *v3.Document, *validatorConfig.ValidationOptions, error) {
+	spec, err := os.ReadFile(path)
+	if err != nil {
+		return nil, nil, nil, errors.Wrap(err, "read openapi file")
+	}
+
+	doc, err := libopenapi.NewDocumentWithConfiguration(spec, &datamodel.DocumentConfiguration{

Review Comment:
   [P1] 这里设置 `BasePath` 后,libopenapi 会解析本地外部 `$ref`,并会从 base path 递归索引文件。入口 `path` 已经做了相对路径、`..`、敏感目录和 symlink 检查,但 spec 内部的外部 `$ref` 还没有同等限制。建议用受限的 `LocalFS` / `FileFilter`,或者在构建模型前校验所有本地外部 `$ref`,避免绝对路径、`..` 或 symlink 跳出允许目录。



##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"bytes"
+	"io"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	validatorRadix "github.com/pb33f/libopenapi-validator/radix"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+
+	defaultMaxRequestBodyBytes = 1 << 20
+)
+
+var errOpenAPIRequestBodyTooLarge = errors.New("openapi request body too large")
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg               *Config
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Filter struct {
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+		// MaxRequestBodyBytes limits how much request body data OpenAPI validation may read.
+		// Zero uses the default limit.
+		MaxRequestBodyBytes int64 `yaml:"max_request_body_bytes" json:"max_request_body_bytes,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path, err := cleanOpenAPIPath(factory.cfg.Path)
+	if err != nil {
+		return err
+	}
+	factory.cfg.Path = path
+
+	maxRequestBody, err := factory.cfg.effectiveMaxRequestBodyBytes()
+	if err != nil {
+		return err
+	}
+
+	validator, model, validationOptions, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	factory.validationOptions = validationOptions
+	factory.maxRequestBody = maxRequestBody
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator:         factory.validator,
+		model:             factory.model,
+		validationOptions: factory.validationOptions,
+		maxRequestBody:    factory.maxRequestBody,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if err := f.prepareRequestBodyForValidation(req); err != nil {
+		if err == errOpenAPIRequestBodyTooLarge {
+			errResp := contexthttp.PayloadTooLarge.WithError(err)
+			ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+			logger.Debug(errResp.Error())
+			return filter.Stop
+		}
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request body read failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request body read failed: %v", err)
+		return filter.Stop
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		validationDetails := formatValidationErrors(validationErrs)
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request validation failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request validation failed: %s", validationDetails)
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, f.validationOptions)
+	if len(validationErrs) > 0 {
+		logger.Debugf("openapi path lookup errors for %s %s: %s", req.Method, req.URL.Path, formatValidationErrors(validationErrs))
+		return nil, "", false
+	}
+	if pathItem == nil {
+		return nil, "", false
+	}
+	if !hasRequestOperation(req, pathItem) {
+		return nil, "", false
+	}
+	return pathItem, foundPath, true
+}
+
+func hasRequestOperation(req *http.Request, pathItem *v3.PathItem) bool {
+	switch req.Method {
+	case http.MethodGet:
+		return pathItem.Get != nil
+	case http.MethodPost:
+		return pathItem.Post != nil
+	case http.MethodPut:
+		return pathItem.Put != nil
+	case http.MethodDelete:
+		return pathItem.Delete != nil
+	case http.MethodOptions:
+		return pathItem.Options != nil
+	case http.MethodHead:
+		return pathItem.Head != nil || pathItem.Get != nil

Review Comment:
   [P2] 这里会把只声明了 GET 的 HEAD 请求当成命中的 operation 继续交给 validator。现有 query/header 参数路径不会 fallback 到 GET,所以当前测试能通过;但 request body 提取会 fallback 到 GET,带 `requestBody` 的 GET operation 仍可能影响 HEAD。文档说 GET-only HEAD 应跳过校验,建议这里按文档只接受显式 `head` operation,或者补充 GET requestBody 场景并更新文档契约。



##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"bytes"
+	"io"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	validatorRadix "github.com/pb33f/libopenapi-validator/radix"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+
+	defaultMaxRequestBodyBytes = 1 << 20
+)
+
+var errOpenAPIRequestBodyTooLarge = errors.New("openapi request body too large")
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg               *Config
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Filter struct {
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+		// MaxRequestBodyBytes limits how much request body data OpenAPI validation may read.
+		// Zero uses the default limit.
+		MaxRequestBodyBytes int64 `yaml:"max_request_body_bytes" json:"max_request_body_bytes,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path, err := cleanOpenAPIPath(factory.cfg.Path)
+	if err != nil {
+		return err
+	}
+	factory.cfg.Path = path
+
+	maxRequestBody, err := factory.cfg.effectiveMaxRequestBodyBytes()
+	if err != nil {
+		return err
+	}
+
+	validator, model, validationOptions, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	factory.validationOptions = validationOptions
+	factory.maxRequestBody = maxRequestBody
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator:         factory.validator,
+		model:             factory.model,
+		validationOptions: factory.validationOptions,
+		maxRequestBody:    factory.maxRequestBody,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if err := f.prepareRequestBodyForValidation(req); err != nil {
+		if err == errOpenAPIRequestBodyTooLarge {
+			errResp := contexthttp.PayloadTooLarge.WithError(err)
+			ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+			logger.Debug(errResp.Error())
+			return filter.Stop
+		}
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request body read failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request body read failed: %v", err)
+		return filter.Stop
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		validationDetails := formatValidationErrors(validationErrs)
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request validation failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request validation failed: %s", validationDetails)
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, f.validationOptions)
+	if len(validationErrs) > 0 {

Review Comment:
   [P2] 这里一旦 `FindPath()` 返回 missing operation error 就直接跳过,下面 `hasRequestOperation()` 里基于 `GetOperations()` 的兜底基本走不到。对 OpenAPI 3.2 的 additional operations,比如 `query`,libopenapi 可以建模,但当前 `FindPath` / path tree 的 method 判断不会把它当作已声明方法,最后会被当成未声明 method 放行。若要宣称支持 OpenAPI 3.2 operation,建议调整这个分支并补 `QUERY /path` 这类测试;否则文档里需要收窄 3.2 支持范围。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3342039995


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path, err := cleanOpenAPIPath(factory.cfg.Path)
+	if err != nil {
+		return err
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		validationDetails := formatValidationErrors(validationErrs)
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request validation failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request validation failed: %s", validationDetails)
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)

Review Comment:
   已处理。现在 Apply() 阶段会显式构建并保存带 PathTree 的 ValidationOptions,创建 validator 和调用 FindPath() 时复用同一份 options,不再传 nil。这样预查 path/method 时可以走 SDK 的 PathTree 快路径。也补了对应测试。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3369027070


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"bytes"
+	"io"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	validatorRadix "github.com/pb33f/libopenapi-validator/radix"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+
+	defaultMaxRequestBodyBytes = 1 << 20
+)
+
+var errOpenAPIRequestBodyTooLarge = errors.New("openapi request body too large")
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg               *Config
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Filter struct {
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+		// MaxRequestBodyBytes limits how much request body data OpenAPI validation may read.
+		// Zero uses the default limit.
+		MaxRequestBodyBytes int64 `yaml:"max_request_body_bytes" json:"max_request_body_bytes,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path, err := cleanOpenAPIPath(factory.cfg.Path)
+	if err != nil {
+		return err
+	}
+	factory.cfg.Path = path
+
+	maxRequestBody, err := factory.cfg.effectiveMaxRequestBodyBytes()
+	if err != nil {
+		return err
+	}
+
+	validator, model, validationOptions, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	factory.validationOptions = validationOptions
+	factory.maxRequestBody = maxRequestBody
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator:         factory.validator,
+		model:             factory.model,
+		validationOptions: factory.validationOptions,
+		maxRequestBody:    factory.maxRequestBody,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if err := f.prepareRequestBodyForValidation(req); err != nil {
+		if err == errOpenAPIRequestBodyTooLarge {
+			errResp := contexthttp.PayloadTooLarge.WithError(err)
+			ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+			logger.Debug(errResp.Error())
+			return filter.Stop
+		}
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request body read failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request body read failed: %v", err)
+		return filter.Stop
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		validationDetails := formatValidationErrors(validationErrs)
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request validation failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request validation failed: %s", validationDetails)
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, f.validationOptions)
+	if len(validationErrs) > 0 {
+		logger.Debugf("openapi path lookup errors for %s %s: %s", req.Method, req.URL.Path, formatValidationErrors(validationErrs))
+		return nil, "", false
+	}
+	if pathItem == nil {
+		return nil, "", false
+	}
+	if !hasRequestOperation(req, pathItem) {
+		return nil, "", false
+	}
+	return pathItem, foundPath, true
+}
+
+func hasRequestOperation(req *http.Request, pathItem *v3.PathItem) bool {
+	switch req.Method {
+	case http.MethodGet:
+		return pathItem.Get != nil
+	case http.MethodPost:
+		return pathItem.Post != nil
+	case http.MethodPut:
+		return pathItem.Put != nil
+	case http.MethodDelete:
+		return pathItem.Delete != nil
+	case http.MethodOptions:
+		return pathItem.Options != nil
+	case http.MethodHead:
+		return pathItem.Head != nil || pathItem.Get != nil
+	case http.MethodPatch:
+		return pathItem.Patch != nil
+	case http.MethodTrace:
+		return pathItem.Trace != nil
+	default:
+		operations := pathItem.GetOperations()
+		return operations != nil && operations.GetOrZero(strings.ToLower(req.Method)) != nil
+	}
+}
+
+func (f *Filter) prepareRequestBodyForValidation(req *http.Request) error {
+	if req.Body == nil || req.Body == http.NoBody {
+		return nil
+	}
+	if f.maxRequestBody <= 0 {
+		return nil
+	}
+	if req.ContentLength > f.maxRequestBody {
+		return errOpenAPIRequestBodyTooLarge
+	}
+
+	body, err := readRequestBodyWithinLimit(req.Body, f.maxRequestBody)
+	if err != nil {
+		return err
+	}
+	req.Body = io.NopCloser(bytes.NewReader(body))
+	req.ContentLength = int64(len(body))
+	return nil
+}
+
+func readRequestBodyWithinLimit(body io.ReadCloser, limit int64) ([]byte, error) {
+	// The original body is replaced by the caller after this bounded read.
+	defer body.Close()
+
+	data, err := io.ReadAll(io.LimitReader(body, limit+1))
+	if err != nil {
+		return nil, err
+	}
+	if int64(len(data)) > limit {
+		return nil, errOpenAPIRequestBodyTooLarge
+	}
+	return data, nil
+}
+
+func loadValidatorFromFile(path string) (openapiValidator.Validator, *v3.Document, *validatorConfig.ValidationOptions, error) {
+	spec, err := os.ReadFile(path)
+	if err != nil {
+		return nil, nil, nil, errors.Wrap(err, "read openapi file")
+	}
+
+	doc, err := libopenapi.NewDocumentWithConfiguration(spec, &datamodel.DocumentConfiguration{

Review Comment:
   已修复。
   
   现在会在 `BuildV3Model` 之前递归预校验 spec 内部的本地外部 `$ref`,并拒绝:
   
   - 绝对路径
   - 远程 URL
   - 包含 `..` 的路径
   - symlink 解析后跳出 OpenAPI 根目录的引用
   
   同时补了回归测试,覆盖合法嵌套本地 `$ref`、绝对 `$ref`、`..`、远程 `$ref` 和嵌套 symlink escape。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "sonarqubecloud[bot] (via GitHub)" <gi...@apache.org>.
sonarqubecloud[bot] commented on PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#issuecomment-4641969966

   ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948) **Quality Gate passed**  
   Issues  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [5 New issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=ACCEPTED)
   
   Measures  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_coverage&view=list)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [3.9% Duplication on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_duplicated_lines_density&view=list)  
     
   <!-- sqra-placement-anchor -->
   [See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948)
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "AlexStocks (via GitHub)" <gi...@apache.org>.
AlexStocks commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3308066019


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {
+		return nil, "", false
+	}
+	if !hasRequestOperation(req, pathItem) {
+		return nil, "", false
+	}
+	return pathItem, foundPath, true
+}
+
+func hasRequestOperation(req *http.Request, pathItem *v3.PathItem) bool {
+	switch req.Method {
+	case http.MethodGet:
+		return pathItem.Get != nil
+	case http.MethodPost:
+		return pathItem.Post != nil
+	case http.MethodPut:
+		return pathItem.Put != nil
+	case http.MethodDelete:
+		return pathItem.Delete != nil
+	case http.MethodOptions:
+		return pathItem.Options != nil
+	case http.MethodHead:
+		return pathItem.Head != nil || pathItem.Get != nil
+	case http.MethodPatch:
+		return pathItem.Patch != nil
+	case http.MethodTrace:
+		return pathItem.Trace != nil
+	default:
+		operations := pathItem.GetOperations()
+		return operations != nil && operations.GetOrZero(strings.ToLower(req.Method)) != nil
+	}
+}
+
+func loadValidatorFromFile(path string) (openapiValidator.Validator, *v3.Document, error) {
+	spec, err := os.ReadFile(path)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	doc, err := libopenapi.NewDocumentWithConfiguration(spec, &datamodel.DocumentConfiguration{
+		BasePath: filepath.Dir(path),
+	})
+	if err != nil {
+		return nil, nil, err
+	}
+
+	model, err := doc.BuildV3Model()
+	if err != nil {

Review Comment:
   [P0] `doc.BuildV3Model()` 的错误被静默丢弃。
   
   `BuildV3Model()` 可能同时返回非 nil error 和部分 model。当前代码直接丢弃了 error:
   
   ```go
   model, err := doc.BuildV3Model()
   // err 被忽略
   ```
   
   spec 本身的问题(无效 `$ref`、schema 循环引用等)会被静默忽略,validator 基于残缺模型运行,可能导致本该拒绝的请求被放行,或本该放行的请求被误拦。
   
   修复:
   ```go
   model, err := doc.BuildV3Model()
   if err != nil {
       return nil, nil, fmt.Errorf("build openapi model: %w", err)
   }
   ```
   
   如果确实需要容忍部分错误,至少应该 `logger.Warn` 记录 model build 的 warnings。



##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {

Review Comment:
   [P1] `findRequestOperation` 把路径查找错误和「未声明」混为一谈。
   
   `validatorPaths.FindPath` 返回的 `validationErrs` 可能包含多种含义:路径确实不存在、路径模板匹配但存在歧义、内部错误。当前全部当成「未声明」处理并放行,调试时完全无法区分「真的没声明」和「查找过程出错」。
   
   建议至少在 `validationErrs` 非空时 `logger.Debug` 记录一下错误内容,方便排查。



##########
go.mod:
##########
@@ -73,6 +73,8 @@ require (
 	v.marlon.life/toolkit v0.0.0-20211025131614-e4a91730b4ab
 )
 
+replace github.com/go-openapi/testify/v2 => github.com/go-openapi/testify/v2 v2.0.2

Review Comment:
   [P1] `replace` directive 缺少注释说明原因。
   
   ```go
   replace github.com/go-openapi/testify/v2 => github.com/go-openapi/testify/v2 v2.0.2
   ```
   
   为什么需要 replace?是上游版本兼容问题还是临时 workaround?按照 Go module 惯例,每个 replace 都应该附带注释说明理由和预期移除条件。
   
   另外请确认 `go-openapi/testify/v2 v2.0.2` 是否已被 libopenapi v0.36.1 的传递依赖正确引入——如果是依赖冲突导致的 replace,建议在 PR description 中说明冲突链。



##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {
+		return nil, "", false
+	}
+	if !hasRequestOperation(req, pathItem) {
+		return nil, "", false
+	}
+	return pathItem, foundPath, true
+}
+
+func hasRequestOperation(req *http.Request, pathItem *v3.PathItem) bool {
+	switch req.Method {
+	case http.MethodGet:
+		return pathItem.Get != nil
+	case http.MethodPost:
+		return pathItem.Post != nil
+	case http.MethodPut:
+		return pathItem.Put != nil
+	case http.MethodDelete:
+		return pathItem.Delete != nil
+	case http.MethodOptions:
+		return pathItem.Options != nil
+	case http.MethodHead:
+		return pathItem.Head != nil || pathItem.Get != nil
+	case http.MethodPatch:
+		return pathItem.Patch != nil
+	case http.MethodTrace:
+		return pathItem.Trace != nil
+	default:
+		operations := pathItem.GetOperations()
+		return operations != nil && operations.GetOrZero(strings.ToLower(req.Method)) != nil
+	}
+}
+
+func loadValidatorFromFile(path string) (openapiValidator.Validator, *v3.Document, error) {
+	spec, err := os.ReadFile(path)

Review Comment:
   [P2] `os.ReadFile(path)` 对路径没有做任何校验。
   
   如果 `path` 来自远程配置(如 Nacos/Apollo),攻击者可以注入任意文件路径读取服务器上的敏感文件内容(虽然读取后只用于 OpenAPI 解析,不会直接返回给用户,但 spec 内容可能通过 error message 泄露)。
   
   建议:
   1. 如果 path 来自用户配置,至少校验不是绝对路径或不在敏感目录下
   2. 考虑限制 `BasePath` 不能是 `/` 或 `/etc` 等敏感路径
   3. 如果只支持相对路径,在 `Apply()` 阶段做 `filepath.IsAbs()` 检查
   
   当前场景如果 path 只来自本地 YAML 配置文件,风险较低,但作为防御性编程应该加上。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Similarityoung (via GitHub)" <gi...@apache.org>.
Similarityoung commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3339495803


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path, err := cleanOpenAPIPath(factory.cfg.Path)
+	if err != nil {
+		return err
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		validationDetails := formatValidationErrors(validationErrs)
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request validation failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request validation failed: %s", validationDetails)
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)

Review Comment:
   [P1] 这里传 `nil` 调用 `FindPath`,会绕过 `NewValidatorFromV3Model` 初始化时自动构建的 `PathTree`。结果是每个请求在预查 path/method 时仍可能走全量 path 扫描和正则匹配;OpenAPI 文档较大时这是网关热路径性能风险。建议显式构建并复用同一份 `ValidationOptions`/`PathTree`,或者改为调用 validator 的 request validation,再只把 missing path/operation 转成 `Continue`。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "sonarqubecloud[bot] (via GitHub)" <gi...@apache.org>.
sonarqubecloud[bot] commented on PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#issuecomment-4550604115

   ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948) **Quality Gate passed**  
   Issues  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=ACCEPTED)
   
   Measures  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_coverage&view=list)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [9.1% Duplication on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_duplicated_lines_density&view=list)  
     
   <!-- sqra-placement-anchor -->
   [See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948)
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3342246833


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {

Review Comment:
   已处理。现在 FindPath 返回 validationErrs 时会先记录 Debug 日志,包含请求 method/path 和错误内容,再跳过校验;pathItem 为空或 operation 未声明时仍按未声明路径放行。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "sonarqubecloud[bot] (via GitHub)" <gi...@apache.org>.
sonarqubecloud[bot] commented on PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#issuecomment-4611921454

   ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948) **Quality Gate passed**  
   Issues  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=ACCEPTED)
   
   Measures  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_coverage&view=list)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [5.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_duplicated_lines_density&view=list)  
     
   <!-- sqra-placement-anchor -->
   [See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948)
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "sonarqubecloud[bot] (via GitHub)" <gi...@apache.org>.
sonarqubecloud[bot] commented on PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#issuecomment-4608536998

   ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948) **Quality Gate passed**  
   Issues  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=ACCEPTED)
   
   Measures  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_coverage&view=list)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [5.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_duplicated_lines_density&view=list)  
     
   <!-- sqra-placement-anchor -->
   [See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948)
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3342361007


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {
+		return nil, "", false
+	}
+	if !hasRequestOperation(req, pathItem) {
+		return nil, "", false
+	}
+	return pathItem, foundPath, true
+}
+
+func hasRequestOperation(req *http.Request, pathItem *v3.PathItem) bool {
+	switch req.Method {
+	case http.MethodGet:
+		return pathItem.Get != nil
+	case http.MethodPost:
+		return pathItem.Post != nil
+	case http.MethodPut:
+		return pathItem.Put != nil
+	case http.MethodDelete:
+		return pathItem.Delete != nil
+	case http.MethodOptions:
+		return pathItem.Options != nil
+	case http.MethodHead:
+		return pathItem.Head != nil || pathItem.Get != nil
+	case http.MethodPatch:
+		return pathItem.Patch != nil
+	case http.MethodTrace:
+		return pathItem.Trace != nil
+	default:
+		operations := pathItem.GetOperations()
+		return operations != nil && operations.GetOrZero(strings.ToLower(req.Method)) != nil
+	}
+}
+
+func loadValidatorFromFile(path string) (openapiValidator.Validator, *v3.Document, error) {
+	spec, err := os.ReadFile(path)

Review Comment:
   已处理。现在 Apply() 阶段会先校验 OpenAPI path,只允许相对路径,并拒绝绝对路径、.. 跳转和敏感 base path。已补绝对路径和 .. 跳转的回归测试。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "sonarqubecloud[bot] (via GitHub)" <gi...@apache.org>.
sonarqubecloud[bot] commented on PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#issuecomment-4828245625

   ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948) **Quality Gate passed**  
   Issues  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [5 New issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=ACCEPTED)
   
   Measures  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_coverage&view=list)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [3.9% Duplication on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_duplicated_lines_density&view=list)  
     
   <!-- sqra-placement-anchor -->
   [See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948)
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "sonarqubecloud[bot] (via GitHub)" <gi...@apache.org>.
sonarqubecloud[bot] commented on PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#issuecomment-4555951030

   ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948) **Quality Gate passed**  
   Issues  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=ACCEPTED)
   
   Measures  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_coverage&view=list)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [7.3% Duplication on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_duplicated_lines_density&view=list)  
     
   <!-- sqra-placement-anchor -->
   [See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948)
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "AlexStocks (via GitHub)" <gi...@apache.org>.
AlexStocks commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3308060049


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {
+		return nil, "", false
+	}
+	if !hasRequestOperation(req, pathItem) {
+		return nil, "", false
+	}
+	return pathItem, foundPath, true
+}
+
+func hasRequestOperation(req *http.Request, pathItem *v3.PathItem) bool {
+	switch req.Method {
+	case http.MethodGet:
+		return pathItem.Get != nil
+	case http.MethodPost:
+		return pathItem.Post != nil
+	case http.MethodPut:
+		return pathItem.Put != nil
+	case http.MethodDelete:
+		return pathItem.Delete != nil
+	case http.MethodOptions:
+		return pathItem.Options != nil
+	case http.MethodHead:
+		return pathItem.Head != nil || pathItem.Get != nil
+	case http.MethodPatch:
+		return pathItem.Patch != nil
+	case http.MethodTrace:
+		return pathItem.Trace != nil
+	default:
+		operations := pathItem.GetOperations()
+		return operations != nil && operations.GetOrZero(strings.ToLower(req.Method)) != nil
+	}
+}
+
+func loadValidatorFromFile(path string) (openapiValidator.Validator, *v3.Document, error) {
+	spec, err := os.ReadFile(path)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	doc, err := libopenapi.NewDocumentWithConfiguration(spec, &datamodel.DocumentConfiguration{
+		BasePath: filepath.Dir(path),
+	})
+	if err != nil {
+		return nil, nil, err
+	}
+
+	model, err := doc.BuildV3Model()
+	if err != nil {

Review Comment:
   [P0]  的错误被静默丢弃。
   
   当 OpenAPI spec 存在部分校验错误时, 会同时返回非 nil error **和** 部分 model。当前代码只取了 model,error 被丢弃:
   
   
   
   这意味着 spec 本身的问题(如无效的 、schema 循环引用等)会被静默忽略,validator 基于残缺模型运行,可能导致:
   1. 本该拒绝的请求被放行
   2. 本该放行的请求被误拦
   
   修复建议:
   
   
   如果确实需要容忍部分错误,至少应该  记录 model build 的 warnings。



##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {

Review Comment:
   [P1]  把路径查找错误和「未声明」混为一谈。
   
   
   
    返回的  可能包含多种含义:
   - 路径确实不存在 → 应该 skip
   - 路径模板匹配但存在歧义 → 可能需要 log
   - 内部错误 → 应该 log
   
   当前全部当成「未声明」处理并放行,在调试时完全无法区分「真的没声明」和「查找过程出错」。
   
   建议至少在  非空时  记录一下错误内容,方便排查。



##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {
+		return nil, "", false
+	}
+	if !hasRequestOperation(req, pathItem) {
+		return nil, "", false
+	}
+	return pathItem, foundPath, true
+}
+
+func hasRequestOperation(req *http.Request, pathItem *v3.PathItem) bool {
+	switch req.Method {
+	case http.MethodGet:
+		return pathItem.Get != nil
+	case http.MethodPost:
+		return pathItem.Post != nil
+	case http.MethodPut:
+		return pathItem.Put != nil
+	case http.MethodDelete:
+		return pathItem.Delete != nil
+	case http.MethodOptions:
+		return pathItem.Options != nil
+	case http.MethodHead:
+		return pathItem.Head != nil || pathItem.Get != nil
+	case http.MethodPatch:
+		return pathItem.Patch != nil
+	case http.MethodTrace:
+		return pathItem.Trace != nil
+	default:
+		operations := pathItem.GetOperations()
+		return operations != nil && operations.GetOrZero(strings.ToLower(req.Method)) != nil
+	}
+}
+
+func loadValidatorFromFile(path string) (openapiValidator.Validator, *v3.Document, error) {
+	spec, err := os.ReadFile(path)

Review Comment:
   [P2]  对路径没有做任何校验。
   
   如果  来自远程配置(如 Nacos/Apollo),攻击者可以注入任意文件路径读取服务器上的敏感文件内容(虽然读取后只用于 OpenAPI 解析,不会直接返回给用户,但 spec 内容可能通过 error message 泄露)。
   
   建议:
   1. 如果 path 来自用户配置,至少校验不是绝对路径或不在敏感目录下
   2. 考虑限制  不能是  或  等敏感路径
   3. 如果只支持相对路径,在  阶段做  检查
   
   当前场景如果 path 只来自本地 YAML 配置文件,风险较低,但作为防御性编程应该加上。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3307899137


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {

Review Comment:
   This only checks whether the OpenAPI path exists, not whether the requested HTTP method is declared on that path. For a spec that declares `GET /users` but receives `POST /users`, `findRequestOperation` returns the path item and validation proceeds, which can return a 400 instead of the documented skip-when-path/method-is-not-declared behavior. Check the method-specific operation on the `PathItem` before calling the validator, and continue when it is nil.



##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"

Review Comment:
   The new third-party `github.com/pb33f/...` imports are grouped together with Pixiu internal imports. Other HTTP filters keep third-party packages in a separate import block from `github.com/apache/dubbo-go-pixiu/...` imports (for example, `pkg/filter/http/proxyrewrite/rewrite.go:24-33`), so this should be split to match the repository's import grouping convention.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Similarityoung (via GitHub)" <gi...@apache.org>.
Similarityoung commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3339494427


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path, err := cleanOpenAPIPath(factory.cfg.Path)
+	if err != nil {
+		return err
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {

Review Comment:
   [P1] 这里会把请求交给 `libopenapi-validator` 做 body schema 校验;它的 JSON request body 路径会通过 `io.ReadAll` 读取完整 body 并 reset `request.Body`。当前 filter 没有任何 body 大小上限,网关热路径上大 JSON 或 chunked body 会放大内存和延迟风险。建议在进入 validator 前检查 `ContentLength`,并用受限 reader 包装 `Body`;超限时直接返回项目约定的 413/400。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Similarityoung (via GitHub)" <gi...@apache.org>.
Similarityoung commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3339497130


##########
pkg/filter/http/apiconfig/api_config.go:
##########
@@ -74,8 +69,8 @@ func (factory *FilterFactory) Config() any {
 func (factory *FilterFactory) Apply() error {
 	factory.apiService = api.NewLocalMemoryAPIDiscoveryService()
 
-	if factory.cfg.Dynamic && (factory.cfg.EnableOpenAPIValidation || factory.cfg.OpenAPIPath != "") {
-		return errors.New("dynamic api config does not support openapi validation")
+	if factory.cfg.OpenAPIPath != "" || factory.cfg.EnableOpenAPIValidation {

Review Comment:
   [P1] 这里可以拦住 `openapi_path` 非空和 `enable_openapi_validation: true`,但仍无法识别 deprecated key 是否出现过。升级用户如果显式写了 `enable_openapi_validation: false` 或 `openapi_path: ""`,这段逻辑会静默接受,和“旧 OpenAPI 配置键已迁移,出现就应提示迁移”的目标不一致。建议把 deprecated 字段改成可检测 presence 的类型/自定义 unmarshal,只要 key 出现在 apiconfig 配置里就 fail fast,并补对应回归测试。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3369354582


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"bytes"
+	"io"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	validatorRadix "github.com/pb33f/libopenapi-validator/radix"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+
+	defaultMaxRequestBodyBytes = 1 << 20
+)
+
+var errOpenAPIRequestBodyTooLarge = errors.New("openapi request body too large")
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg               *Config
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Filter struct {
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+		// MaxRequestBodyBytes limits how much request body data OpenAPI validation may read.
+		// Zero uses the default limit.
+		MaxRequestBodyBytes int64 `yaml:"max_request_body_bytes" json:"max_request_body_bytes,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path, err := cleanOpenAPIPath(factory.cfg.Path)
+	if err != nil {
+		return err
+	}
+	factory.cfg.Path = path
+
+	maxRequestBody, err := factory.cfg.effectiveMaxRequestBodyBytes()
+	if err != nil {
+		return err
+	}
+
+	validator, model, validationOptions, err := loadValidatorFromFile(path)

Review Comment:
   根因在 `FilterManager.ReLoad()`(`filter_manager.go:84-89`):`Apply()` 失败后只记日志没有 `continue`,nil factory 被装进 `filtersArray`,后续 `CreateFilterChain()` 对 nil interface 调 `PrepareFilterChain` 会 panic,影响所有 `Apply()` 可能失败的 filter,建议单独修。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3369035830


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"bytes"
+	"io"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	validatorRadix "github.com/pb33f/libopenapi-validator/radix"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+
+	defaultMaxRequestBodyBytes = 1 << 20
+)
+
+var errOpenAPIRequestBodyTooLarge = errors.New("openapi request body too large")
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg               *Config
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Filter struct {
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+		// MaxRequestBodyBytes limits how much request body data OpenAPI validation may read.
+		// Zero uses the default limit.
+		MaxRequestBodyBytes int64 `yaml:"max_request_body_bytes" json:"max_request_body_bytes,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path, err := cleanOpenAPIPath(factory.cfg.Path)
+	if err != nil {
+		return err
+	}
+	factory.cfg.Path = path
+
+	maxRequestBody, err := factory.cfg.effectiveMaxRequestBodyBytes()
+	if err != nil {
+		return err
+	}
+
+	validator, model, validationOptions, err := loadValidatorFromFile(path)

Review Comment:
   确认这个风险存在,但根因在 `FilterManager.ReLoad()`,不是 OpenAPI filter。
   
   本次 OpenAPI `Apply()` 已经会正常返回错误;问题是 `ReLoad()` 吞掉 error 后仍把 nil factory 放进 `filtersArray`,后续对 nil factory 调 `PrepareFilterChain()` 可能 panic。这个行为会影响所有 `Apply()` 可能失败的 filter,不只 OpenAPI。
   
   建议作为单独 follow-up 修复 filter manager 的错误传播 / 失败 factory 安装逻辑。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "AlexStocks (via GitHub)" <gi...@apache.org>.
AlexStocks merged PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3342369239


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {
+		return nil, "", false
+	}
+	if !hasRequestOperation(req, pathItem) {
+		return nil, "", false
+	}
+	return pathItem, foundPath, true
+}
+
+func hasRequestOperation(req *http.Request, pathItem *v3.PathItem) bool {
+	switch req.Method {
+	case http.MethodGet:
+		return pathItem.Get != nil
+	case http.MethodPost:
+		return pathItem.Post != nil
+	case http.MethodPut:
+		return pathItem.Put != nil
+	case http.MethodDelete:
+		return pathItem.Delete != nil
+	case http.MethodOptions:
+		return pathItem.Options != nil
+	case http.MethodHead:
+		return pathItem.Head != nil || pathItem.Get != nil
+	case http.MethodPatch:
+		return pathItem.Patch != nil
+	case http.MethodTrace:
+		return pathItem.Trace != nil
+	default:
+		operations := pathItem.GetOperations()
+		return operations != nil && operations.GetOrZero(strings.ToLower(req.Method)) != nil
+	}
+}
+
+func loadValidatorFromFile(path string) (openapiValidator.Validator, *v3.Document, error) {
+	spec, err := os.ReadFile(path)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	doc, err := libopenapi.NewDocumentWithConfiguration(spec, &datamodel.DocumentConfiguration{
+		BasePath: filepath.Dir(path),
+	})
+	if err != nil {
+		return nil, nil, err
+	}
+
+	model, err := doc.BuildV3Model()
+	if err != nil {

Review Comment:
   已处理。现在 BuildV3Model() 返回 error 时会直接中止 OpenAPI filter 初始化,并带上 build openapi model 的错误上下文,不再使用部分构建出来的 model。已补无效 $ref 的回归测试。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3342151689


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {
+		return nil, "", false
+	}
+	if !hasRequestOperation(req, pathItem) {
+		return nil, "", false
+	}
+	return pathItem, foundPath, true
+}
+
+func hasRequestOperation(req *http.Request, pathItem *v3.PathItem) bool {
+	switch req.Method {
+	case http.MethodGet:
+		return pathItem.Get != nil
+	case http.MethodPost:
+		return pathItem.Post != nil
+	case http.MethodPut:
+		return pathItem.Put != nil
+	case http.MethodDelete:
+		return pathItem.Delete != nil
+	case http.MethodOptions:
+		return pathItem.Options != nil
+	case http.MethodHead:
+		return pathItem.Head != nil || pathItem.Get != nil
+	case http.MethodPatch:
+		return pathItem.Patch != nil
+	case http.MethodTrace:
+		return pathItem.Trace != nil
+	default:
+		operations := pathItem.GetOperations()
+		return operations != nil && operations.GetOrZero(strings.ToLower(req.Method)) != nil
+	}
+}
+
+func loadValidatorFromFile(path string) (openapiValidator.Validator, *v3.Document, error) {
+	spec, err := os.ReadFile(path)

Review Comment:
   已处理。现在 Apply() 阶段会校验 OpenAPI path,只允许相对路径,并拒绝绝对路径、.. 跳转和敏感 base path。已补绝对路径和 .. 跳转的回归测试。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "codecov-commenter (via GitHub)" <gi...@apache.org>.
codecov-commenter commented on PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#issuecomment-4551630036

   ## [Codecov](https://app.codecov.io/gh/apache/dubbo-go-pixiu/pull/948?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) Report
   :x: Patch coverage is `48.48485%` with `51 lines` in your changes missing coverage. Please review.
   :white_check_mark: Project coverage is 23.70%. Comparing base ([`9168831`](https://app.codecov.io/gh/apache/dubbo-go-pixiu/commit/91688317f74306b5117c2120fc8146def0bc4eea?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)) to head ([`f00be07`](https://app.codecov.io/gh/apache/dubbo-go-pixiu/commit/f00be070e33a22cf56d885ed3c715a894086cbe4?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)).
   :warning: Report is 2 commits behind head on develop.
   
   | [Files with missing lines](https://app.codecov.io/gh/apache/dubbo-go-pixiu/pull/948?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Patch % | Lines |
   |---|---|---|
   | [pkg/filter/http/openapi/openapi.go](https://app.codecov.io/gh/apache/dubbo-go-pixiu/pull/948?src=pr&el=tree&filepath=pkg%2Ffilter%2Fhttp%2Fopenapi%2Fopenapi.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-cGtnL2ZpbHRlci9odHRwL29wZW5hcGkvb3BlbmFwaS5nbw==) | 48.48% | [41 Missing and 10 partials :warning: ](https://app.codecov.io/gh/apache/dubbo-go-pixiu/pull/948?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
   
   <details><summary>Additional details and impacted files</summary>
   
   
   
   ```diff
   @@             Coverage Diff             @@
   ##           develop     #948      +/-   ##
   ===========================================
   - Coverage    24.05%   23.70%   -0.35%     
   ===========================================
     Files          273      271       -2     
     Lines        21114    20840     -274     
   ===========================================
   - Hits          5079     4941     -138     
   + Misses       15500    15398     -102     
   + Partials       535      501      -34     
   ```
   
   | [Flag](https://app.codecov.io/gh/apache/dubbo-go-pixiu/pull/948/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Coverage Δ | |
   |---|---|---|
   | [unittests](https://app.codecov.io/gh/apache/dubbo-go-pixiu/pull/948/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `23.70% <48.48%> (-0.35%)` | :arrow_down: |
   
   Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#carryforward-flags-in-the-pull-request-comment) to find out more.
   </details>
   
   [:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/apache/dubbo-go-pixiu/pull/948?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache).   
   :loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache).
   <details><summary> :rocket: New features to boost your workflow: </summary>
   
   - :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
   - :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
   </details>


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Alanxtl (via GitHub)" <gi...@apache.org>.
Alanxtl commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3308656556


##########
pkg/filter/http/apiconfig/config.go:
##########
@@ -23,10 +23,8 @@ import (
 
 // ApiConfigConfig the config for api_config filter
 type ApiConfigConfig struct {
-	APIMetaConfig           *model.APIMetaConfig `yaml:"api_meta_config" json:"api_meta_config,omitempty"`
-	Path                    string               `yaml:"path" json:"path,omitempty"`
-	Dynamic                 bool                 `yaml:"dynamic" json:"dynamic,omitempty"`
-	DynamicAdapter          string               `yaml:"dynamic_adapter" json:"dynamic_adapter,omitempty"`
-	OpenAPIPath             string               `yaml:"openapi_path" json:"openapi_path,omitempty"`
-	EnableOpenAPIValidation bool                 `yaml:"enable_openapi_validation" json:"enable_openapi_validation,omitempty"`
+	APIMetaConfig  *model.APIMetaConfig `yaml:"api_meta_config" json:"api_meta_config,omitempty"`
+	Path           string               `yaml:"path" json:"path,omitempty"`
+	Dynamic        bool                 `yaml:"dynamic" json:"dynamic,omitempty"`
+	DynamicAdapter string               `yaml:"dynamic_adapter" json:"dynamic_adapter,omitempty"`
 }

Review Comment:
   removing `openapi_path` and `enable_openapi_validation` silently disables validation for existing deployments. `ParseConfig` uses plain YAML unmarshal, so old keys are ignored rather than rejected, and `apiconfig.Apply()` no longer warns/errors. Users upgrading with the old config will think OpenAPI validation is still enabled, but requests will pass unvalidated unless they manually add the new `dgp.filter.http.openapi` filter. Keep deprecated fields and fail fast or warn with a migration message; add a regression test for legacy config keys.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3369027921


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"bytes"
+	"io"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	validatorRadix "github.com/pb33f/libopenapi-validator/radix"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+
+	defaultMaxRequestBodyBytes = 1 << 20
+)
+
+var errOpenAPIRequestBodyTooLarge = errors.New("openapi request body too large")
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg               *Config
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Filter struct {
+		validator         openapiValidator.Validator
+		model             *v3.Document
+		validationOptions *validatorConfig.ValidationOptions
+		maxRequestBody    int64
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+		// MaxRequestBodyBytes limits how much request body data OpenAPI validation may read.
+		// Zero uses the default limit.
+		MaxRequestBodyBytes int64 `yaml:"max_request_body_bytes" json:"max_request_body_bytes,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path, err := cleanOpenAPIPath(factory.cfg.Path)
+	if err != nil {
+		return err
+	}
+	factory.cfg.Path = path
+
+	maxRequestBody, err := factory.cfg.effectiveMaxRequestBodyBytes()
+	if err != nil {
+		return err
+	}
+
+	validator, model, validationOptions, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	factory.validationOptions = validationOptions
+	factory.maxRequestBody = maxRequestBody
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator:         factory.validator,
+		model:             factory.model,
+		validationOptions: factory.validationOptions,
+		maxRequestBody:    factory.maxRequestBody,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if err := f.prepareRequestBodyForValidation(req); err != nil {
+		if err == errOpenAPIRequestBodyTooLarge {
+			errResp := contexthttp.PayloadTooLarge.WithError(err)
+			ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+			logger.Debug(errResp.Error())
+			return filter.Stop
+		}
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request body read failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request body read failed: %v", err)
+		return filter.Stop
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		validationDetails := formatValidationErrors(validationErrs)
+		errResp := contexthttp.BadRequest.WithError(errors.New("openapi request validation failed"))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debugf("openapi request validation failed: %s", validationDetails)
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, f.validationOptions)
+	if len(validationErrs) > 0 {
+		logger.Debugf("openapi path lookup errors for %s %s: %s", req.Method, req.URL.Path, formatValidationErrors(validationErrs))
+		return nil, "", false
+	}
+	if pathItem == nil {
+		return nil, "", false
+	}
+	if !hasRequestOperation(req, pathItem) {
+		return nil, "", false
+	}
+	return pathItem, foundPath, true
+}
+
+func hasRequestOperation(req *http.Request, pathItem *v3.PathItem) bool {
+	switch req.Method {
+	case http.MethodGet:
+		return pathItem.Get != nil
+	case http.MethodPost:
+		return pathItem.Post != nil
+	case http.MethodPut:
+		return pathItem.Put != nil
+	case http.MethodDelete:
+		return pathItem.Delete != nil
+	case http.MethodOptions:
+		return pathItem.Options != nil
+	case http.MethodHead:
+		return pathItem.Head != nil || pathItem.Get != nil

Review Comment:
   已修复。
   
   `HEAD` 现在只接受显式声明的 `head` operation,不再 fallback 到 `GET`。这样只声明了 `GET` 的 `HEAD` 请求会按文档契约跳过校验,不会再被 GET 的 requestBody 或其它 operation 信息影响。
   
   同时补了回归测试,明确覆盖 `HEAD` 必须显式声明 `head` operation 的行为。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "sonarqubecloud[bot] (via GitHub)" <gi...@apache.org>.
sonarqubecloud[bot] commented on PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#issuecomment-4544890290

   ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948) **Quality Gate passed**  
   Issues  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=ACCEPTED)
   
   Measures  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_coverage&view=list)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [9.9% Duplication on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_duplicated_lines_density&view=list)  
     
   <!-- sqra-placement-anchor -->
   [See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948)
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#issuecomment-4608535037

   > fix ci please
   
   已修复


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3342055197


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path, err := cleanOpenAPIPath(factory.cfg.Path)
+	if err != nil {
+		return err
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {

Review Comment:
   已处理。现在进入 validator 前会先限制 request body 大小:Content-Length 已知且超限时直接返回 413;chunked 或未知长度 body 会先做受限读取并重置 request.Body,避免 validator 无限制读取。也补了 Content-Length 和 chunked body 超限的回归测试。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "sonarqubecloud[bot] (via GitHub)" <gi...@apache.org>.
sonarqubecloud[bot] commented on PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#issuecomment-4603471243

   ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948) **Quality Gate passed**  
   Issues  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=ACCEPTED)
   
   Measures  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=apache_dubbo-go-pixiu&pullRequest=948&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_coverage&view=list)  
   ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [5.5% Duplication on New Code](https://sonarcloud.io/component_measures?id=apache_dubbo-go-pixiu&pullRequest=948&metric=new_duplicated_lines_density&view=list)  
     
   <!-- sqra-placement-anchor -->
   [See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=948)
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3342085970


##########
pkg/filter/http/apiconfig/api_config.go:
##########
@@ -74,8 +69,8 @@ func (factory *FilterFactory) Config() any {
 func (factory *FilterFactory) Apply() error {
 	factory.apiService = api.NewLocalMemoryAPIDiscoveryService()
 
-	if factory.cfg.Dynamic && (factory.cfg.EnableOpenAPIValidation || factory.cfg.OpenAPIPath != "") {
-		return errors.New("dynamic api config does not support openapi validation")
+	if factory.cfg.OpenAPIPath != "" || factory.cfg.EnableOpenAPIValidation {

Review Comment:
   已处理。现在 apiconfig 会在 YAML 解析时记录 openapi_path 和 enable_openapi_validation 这两个旧 key 是否出现过,所以即使配置成 openapi_path: "" 或 enable_openapi_validation: false,也会 fail fast 提示迁移到 dgp.filter.http.openapi。对应场景的回归测试也补了。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3342351284


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {
+		return nil, "", false
+	}
+	if !hasRequestOperation(req, pathItem) {
+		return nil, "", false
+	}
+	return pathItem, foundPath, true
+}
+
+func hasRequestOperation(req *http.Request, pathItem *v3.PathItem) bool {
+	switch req.Method {
+	case http.MethodGet:
+		return pathItem.Get != nil
+	case http.MethodPost:
+		return pathItem.Post != nil
+	case http.MethodPut:
+		return pathItem.Put != nil
+	case http.MethodDelete:
+		return pathItem.Delete != nil
+	case http.MethodOptions:
+		return pathItem.Options != nil
+	case http.MethodHead:
+		return pathItem.Head != nil || pathItem.Get != nil
+	case http.MethodPatch:
+		return pathItem.Patch != nil
+	case http.MethodTrace:
+		return pathItem.Trace != nil
+	default:
+		operations := pathItem.GetOperations()
+		return operations != nil && operations.GetOrZero(strings.ToLower(req.Method)) != nil
+	}
+}
+
+func loadValidatorFromFile(path string) (openapiValidator.Validator, *v3.Document, error) {
+	spec, err := os.ReadFile(path)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	doc, err := libopenapi.NewDocumentWithConfiguration(spec, &datamodel.DocumentConfiguration{
+		BasePath: filepath.Dir(path),
+	})
+	if err != nil {
+		return nil, nil, err
+	}
+
+	model, err := doc.BuildV3Model()
+	if err != nil {

Review Comment:
   已处理。现在 BuildV3Model() 返回 error 时会直接中止 OpenAPI filter 初始化,并带上 build openapi model 的错误上下文,不再使用部分构建出来的 model。也补了无效 $ref 的回归测试。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org


Re: [PR] Feature/openapi sdk validator [dubbo-go-pixiu]

Posted by "Skylm808 (via GitHub)" <gi...@apache.org>.
Skylm808 commented on code in PR #948:
URL: https://github.com/apache/dubbo-go-pixiu/pull/948#discussion_r3342246833


##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {

Review Comment:
   同上,这个问题已在同一处修复:BuildV3Model() 返回 error 时会中止初始化,不再使用 partial model,并补了 invalid $ref 回归测试。



##########
pkg/filter/http/openapi/openapi.go:
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 openapi
+
+import (
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+import (
+	"github.com/pb33f/libopenapi"
+	openapiValidator "github.com/pb33f/libopenapi-validator"
+	validatorConfig "github.com/pb33f/libopenapi-validator/config"
+	validatorErrors "github.com/pb33f/libopenapi-validator/errors"
+	validatorPaths "github.com/pb33f/libopenapi-validator/paths"
+	"github.com/pb33f/libopenapi/datamodel"
+	v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
+
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
+	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+const (
+	// Kind is the kind of OpenAPI validation filter.
+	Kind = constant.HTTPOpenAPIFilter
+)
+
+func init() {
+	filter.RegisterHttpFilter(&Plugin{})
+}
+
+type (
+	Plugin struct {
+	}
+
+	FilterFactory struct {
+		cfg       *Config
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Filter struct {
+		validator openapiValidator.Validator
+		model     *v3.Document
+	}
+
+	Config struct {
+		Path string `yaml:"path" json:"path,omitempty"`
+	}
+)
+
+func (p *Plugin) Kind() string {
+	return Kind
+}
+
+func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
+	return &FilterFactory{cfg: &Config{}}, nil
+}
+
+func (factory *FilterFactory) Config() any {
+	return factory.cfg
+}
+
+func (factory *FilterFactory) Apply() error {
+	path := strings.TrimSpace(factory.cfg.Path)
+	if path == "" {
+		return errors.New("openapi path is required")
+	}
+	factory.cfg.Path = path
+
+	validator, model, err := loadValidatorFromFile(path)
+	if err != nil {
+		return err
+	}
+	factory.validator = validator
+	factory.model = model
+	return nil
+}
+
+func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
+	f := &Filter{
+		validator: factory.validator,
+		model:     factory.model,
+	}
+	chain.AppendDecodeFilters(f)
+	return nil
+}
+
+func (f *Filter) Decode(ctx *contexthttp.HttpContext) filter.FilterStatus {
+	if f.validator == nil || f.model == nil {
+		return filter.Continue
+	}
+
+	req := ctx.Request
+	pathItem, foundPath, ok := f.findRequestOperation(req)
+	if !ok {
+		return filter.Continue
+	}
+
+	if valid, validationErrs := f.validator.ValidateHttpRequestSyncWithPathItem(req, pathItem, foundPath); !valid {
+		errResp := contexthttp.BadRequest.WithError(errors.New(formatValidationErrors(validationErrs)))
+		ctx.SendLocalReply(errResp.Status, errResp.ToJSON())
+		logger.Debug(errResp.Error())
+		return filter.Stop
+	}
+	return filter.Continue
+}
+
+func (f *Filter) findRequestOperation(req *http.Request) (*v3.PathItem, string, bool) {
+	pathItem, validationErrs, foundPath := validatorPaths.FindPath(req, f.model, nil)
+	if len(validationErrs) > 0 || pathItem == nil {

Review Comment:
   已处理。现在 FindPath 返回 validationErrs 时会先记录 Debug 日志,包含请求 method/path 和错误内容,再跳过校验;pathItem 为空或 operation 未声明时仍按未声明路径放行。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org