mirror of
https://github.com/fatedier/frp.git
synced 2025-07-27 07:35:07 +00:00
Improve the strict configuration validation (#3809)
This commit is contained in:
@@ -110,8 +110,11 @@ func LoadConfigureFromFile(path string, c any, strict bool) error {
|
||||
|
||||
// LoadConfigure loads configuration from bytes and unmarshal into c.
|
||||
// Now it supports json, yaml and toml format.
|
||||
// TODO(fatedier): strict is not valide for ProxyConfigurer/VisitorConfigurer/ClientPluginOptions.
|
||||
func LoadConfigure(b []byte, c any, strict bool) error {
|
||||
v1.DisallowUnknownFieldsMu.Lock()
|
||||
defer v1.DisallowUnknownFieldsMu.Unlock()
|
||||
v1.DisallowUnknownFields = strict
|
||||
|
||||
var tomlObj interface{}
|
||||
// Try to unmarshal as TOML first; swallow errors from that (assume it's not valid TOML).
|
||||
if err := toml.Unmarshal(b, &tomlObj); err == nil {
|
||||
|
@@ -111,3 +111,56 @@ func TestLoadServerConfigStrictMode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomStructStrictMode(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
proxyStr := `
|
||||
serverPort = 7000
|
||||
|
||||
[[proxies]]
|
||||
name = "test"
|
||||
type = "tcp"
|
||||
remotePort = 6000
|
||||
`
|
||||
clientCfg := v1.ClientConfig{}
|
||||
err := LoadConfigure([]byte(proxyStr), &clientCfg, true)
|
||||
require.NoError(err)
|
||||
|
||||
proxyStr += `unknown = "unknown"`
|
||||
err = LoadConfigure([]byte(proxyStr), &clientCfg, true)
|
||||
require.Error(err)
|
||||
|
||||
visitorStr := `
|
||||
serverPort = 7000
|
||||
|
||||
[[visitors]]
|
||||
name = "test"
|
||||
type = "stcp"
|
||||
bindPort = 6000
|
||||
serverName = "server"
|
||||
`
|
||||
err = LoadConfigure([]byte(visitorStr), &clientCfg, true)
|
||||
require.NoError(err)
|
||||
|
||||
visitorStr += `unknown = "unknown"`
|
||||
err = LoadConfigure([]byte(visitorStr), &clientCfg, true)
|
||||
require.Error(err)
|
||||
|
||||
pluginStr := `
|
||||
serverPort = 7000
|
||||
|
||||
[[proxies]]
|
||||
name = "test"
|
||||
type = "tcp"
|
||||
remotePort = 6000
|
||||
[proxies.plugin]
|
||||
type = "unix_domain_socket"
|
||||
unixPath = "/tmp/uds.sock"
|
||||
`
|
||||
err = LoadConfigure([]byte(pluginStr), &clientCfg, true)
|
||||
require.NoError(err)
|
||||
pluginStr += `unknown = "unknown"`
|
||||
err = LoadConfigure([]byte(pluginStr), &clientCfg, true)
|
||||
require.Error(err)
|
||||
}
|
||||
|
@@ -15,9 +15,23 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/fatedier/frp/pkg/util/util"
|
||||
)
|
||||
|
||||
// TODO(fatedier): Due to the current implementation issue of the go json library, the UnmarshalJSON method
|
||||
// of a custom struct cannot access the DisallowUnknownFields parameter of the parent decoder.
|
||||
// Here, a global variable is temporarily used to control whether unknown fields are allowed.
|
||||
// Once the v2 version is implemented by the community, we can switch to a standardized approach.
|
||||
//
|
||||
// https://github.com/golang/go/issues/41144
|
||||
// https://github.com/golang/go/discussions/63397
|
||||
var (
|
||||
DisallowUnknownFields = false
|
||||
DisallowUnknownFieldsMu sync.Mutex
|
||||
)
|
||||
|
||||
type AuthScope string
|
||||
|
||||
const (
|
||||
|
@@ -15,6 +15,7 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
@@ -49,7 +50,13 @@ func (c *TypedClientPluginOptions) UnmarshalJSON(b []byte) error {
|
||||
return fmt.Errorf("unknown plugin type: %s", typeStruct.Type)
|
||||
}
|
||||
options := reflect.New(v).Interface().(ClientPluginOptions)
|
||||
if err := json.Unmarshal(b, options); err != nil {
|
||||
|
||||
decoder := json.NewDecoder(bytes.NewBuffer(b))
|
||||
if DisallowUnknownFields {
|
||||
decoder.DisallowUnknownFields()
|
||||
}
|
||||
|
||||
if err := decoder.Decode(options); err != nil {
|
||||
return err
|
||||
}
|
||||
c.ClientPluginOptions = options
|
||||
@@ -77,17 +84,20 @@ var clientPluginOptionsTypeMap = map[string]reflect.Type{
|
||||
}
|
||||
|
||||
type HTTP2HTTPSPluginOptions struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
LocalAddr string `json:"localAddr,omitempty"`
|
||||
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
|
||||
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
|
||||
}
|
||||
|
||||
type HTTPProxyPluginOptions struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
HTTPUser string `json:"httpUser,omitempty"`
|
||||
HTTPPassword string `json:"httpPassword,omitempty"`
|
||||
}
|
||||
|
||||
type HTTPS2HTTPPluginOptions struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
LocalAddr string `json:"localAddr,omitempty"`
|
||||
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
|
||||
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
|
||||
@@ -96,6 +106,7 @@ type HTTPS2HTTPPluginOptions struct {
|
||||
}
|
||||
|
||||
type HTTPS2HTTPSPluginOptions struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
LocalAddr string `json:"localAddr,omitempty"`
|
||||
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
|
||||
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
|
||||
@@ -104,11 +115,13 @@ type HTTPS2HTTPSPluginOptions struct {
|
||||
}
|
||||
|
||||
type Socks5PluginOptions struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
type StaticFilePluginOptions struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
LocalPath string `json:"localPath,omitempty"`
|
||||
StripPrefix string `json:"stripPrefix,omitempty"`
|
||||
HTTPUser string `json:"httpUser,omitempty"`
|
||||
@@ -116,5 +129,6 @@ type StaticFilePluginOptions struct {
|
||||
}
|
||||
|
||||
type UnixDomainSocketPluginOptions struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
UnixPath string `json:"unixPath,omitempty"`
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -177,7 +178,11 @@ func (c *TypedProxyConfig) UnmarshalJSON(b []byte) error {
|
||||
if configurer == nil {
|
||||
return fmt.Errorf("unknown proxy type: %s", typeStruct.Type)
|
||||
}
|
||||
if err := json.Unmarshal(b, configurer); err != nil {
|
||||
decoder := json.NewDecoder(bytes.NewBuffer(b))
|
||||
if DisallowUnknownFields {
|
||||
decoder.DisallowUnknownFields()
|
||||
}
|
||||
if err := decoder.Decode(configurer); err != nil {
|
||||
return err
|
||||
}
|
||||
c.ProxyConfigurer = configurer
|
||||
|
@@ -15,6 +15,7 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -108,7 +109,11 @@ func (c *TypedVisitorConfig) UnmarshalJSON(b []byte) error {
|
||||
if configurer == nil {
|
||||
return fmt.Errorf("unknown visitor type: %s", typeStruct.Type)
|
||||
}
|
||||
if err := json.Unmarshal(b, configurer); err != nil {
|
||||
decoder := json.NewDecoder(bytes.NewBuffer(b))
|
||||
if DisallowUnknownFields {
|
||||
decoder.DisallowUnknownFields()
|
||||
}
|
||||
if err := decoder.Decode(configurer); err != nil {
|
||||
return err
|
||||
}
|
||||
c.VisitorConfigurer = configurer
|
||||
@@ -120,7 +125,9 @@ func NewVisitorConfigurerByType(t VisitorType) VisitorConfigurer {
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return reflect.New(v).Interface().(VisitorConfigurer)
|
||||
vc := reflect.New(v).Interface().(VisitorConfigurer)
|
||||
vc.GetBaseConfig().Type = string(t)
|
||||
return vc
|
||||
}
|
||||
|
||||
var _ VisitorConfigurer = &STCPVisitorConfig{}
|
||||
|
Reference in New Issue
Block a user