mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2025-05-25 11:28:03 +08:00
chore: the updateConfigs api also adds a check for SAFE_PATHS
This commit is contained in:
parent
a4fcd3af07
commit
2116640886
@ -138,7 +138,7 @@ func NewSsh(option SshOption) (*Ssh, error) {
|
|||||||
} else {
|
} else {
|
||||||
path := C.Path.Resolve(option.PrivateKey)
|
path := C.Path.Resolve(option.PrivateKey)
|
||||||
if !C.Path.IsSafePath(path) {
|
if !C.Path.IsSafePath(path) {
|
||||||
return nil, fmt.Errorf("path is not subpath of home directory: %s", path)
|
return nil, C.Path.ErrNotSafePath(path)
|
||||||
}
|
}
|
||||||
b, err = os.ReadFile(path)
|
b, err = os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
errVehicleType = errors.New("unsupport vehicle type")
|
errVehicleType = errors.New("unsupport vehicle type")
|
||||||
errSubPath = errors.New("path is not subpath of home directory")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type healthCheckSchema struct {
|
type healthCheckSchema struct {
|
||||||
@ -115,7 +114,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide
|
|||||||
if schema.Path != "" {
|
if schema.Path != "" {
|
||||||
path = C.Path.Resolve(schema.Path)
|
path = C.Path.Resolve(schema.Path)
|
||||||
if !C.Path.IsSafePath(path) {
|
if !C.Path.IsSafePath(path) {
|
||||||
return nil, fmt.Errorf("%w: %s", errSubPath, path)
|
return nil, C.Path.ErrNotSafePath(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, schema.Header, resource.DefaultHttpTimeout, schema.SizeLimit)
|
vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, schema.Header, resource.DefaultHttpTimeout, schema.SizeLimit)
|
||||||
|
@ -83,7 +83,7 @@ func GetCertPool(customCA string, customCAString string) (*x509.CertPool, error)
|
|||||||
if len(customCA) > 0 {
|
if len(customCA) > 0 {
|
||||||
path := C.Path.Resolve(customCA)
|
path := C.Path.Resolve(customCA)
|
||||||
if !C.Path.IsSafePath(path) {
|
if !C.Path.IsSafePath(path) {
|
||||||
return nil, fmt.Errorf("path is not subpath of home directory: %s", path)
|
return nil, C.Path.ErrNotSafePath(path)
|
||||||
}
|
}
|
||||||
certificate, err = os.ReadFile(path)
|
certificate, err = os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
type Path interface {
|
type Path interface {
|
||||||
Resolve(path string) string
|
Resolve(path string) string
|
||||||
IsSafePath(path string) bool
|
IsSafePath(path string) bool
|
||||||
|
ErrNotSafePath(path string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadTLSKeyPair loads a TLS key pair from the provided certificate and private key data or file paths, supporting fallback resolution.
|
// LoadTLSKeyPair loads a TLS key pair from the provided certificate and private key data or file paths, supporting fallback resolution.
|
||||||
@ -42,10 +43,12 @@ func LoadTLSKeyPair(certificate, privateKey string, path Path) (tls.Certificate,
|
|||||||
certificate = path.Resolve(certificate)
|
certificate = path.Resolve(certificate)
|
||||||
privateKey = path.Resolve(privateKey)
|
privateKey = path.Resolve(privateKey)
|
||||||
var loadErr error
|
var loadErr error
|
||||||
if path.IsSafePath(certificate) && path.IsSafePath(privateKey) {
|
if !path.IsSafePath(certificate) {
|
||||||
cert, loadErr = tls.LoadX509KeyPair(certificate, privateKey)
|
loadErr = path.ErrNotSafePath(certificate)
|
||||||
|
} else if !path.IsSafePath(privateKey) {
|
||||||
|
loadErr = path.ErrNotSafePath(privateKey)
|
||||||
} else {
|
} else {
|
||||||
loadErr = fmt.Errorf("path is not subpath of home directory")
|
cert, loadErr = tls.LoadX509KeyPair(certificate, privateKey)
|
||||||
}
|
}
|
||||||
if loadErr != nil {
|
if loadErr != nil {
|
||||||
return tls.Certificate{}, fmt.Errorf("parse certificate failed, maybe format error:%s, or path error: %s", painTextErr.Error(), loadErr.Error())
|
return tls.Certificate{}, fmt.Errorf("parse certificate failed, maybe format error:%s, or path error: %s", painTextErr.Error(), loadErr.Error())
|
||||||
|
@ -755,7 +755,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
|||||||
|
|
||||||
func parseController(cfg *RawConfig) (*Controller, error) {
|
func parseController(cfg *RawConfig) (*Controller, error) {
|
||||||
if path := cfg.ExternalUI; path != "" && !C.Path.IsSafePath(path) {
|
if path := cfg.ExternalUI; path != "" && !C.Path.IsSafePath(path) {
|
||||||
return nil, fmt.Errorf("path is not subpath of home directory: %s", path)
|
return nil, C.Path.ErrNotSafePath(path)
|
||||||
}
|
}
|
||||||
return &Controller{
|
return &Controller{
|
||||||
ExternalController: cfg.ExternalController,
|
ExternalController: cfg.ExternalController,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package constant
|
package constant
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
P "path"
|
P "path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -87,10 +88,8 @@ func (p *path) IsSafePath(path string) bool {
|
|||||||
if p.allowUnsafePath || features.CMFA {
|
if p.allowUnsafePath || features.CMFA {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
homedir := p.HomeDir()
|
|
||||||
path = p.Resolve(path)
|
path = p.Resolve(path)
|
||||||
safePaths := append([]string{homedir}, p.safePaths...) // add homedir to safePaths
|
for _, safePath := range p.SafePaths() {
|
||||||
for _, safePath := range safePaths {
|
|
||||||
if rel, err := filepath.Rel(safePath, path); err == nil {
|
if rel, err := filepath.Rel(safePath, path); err == nil {
|
||||||
if filepath.IsLocal(rel) {
|
if filepath.IsLocal(rel) {
|
||||||
return true
|
return true
|
||||||
@ -100,6 +99,23 @@ func (p *path) IsSafePath(path string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *path) SafePaths() []string {
|
||||||
|
return append([]string{p.homeDir}, p.safePaths...) // add homedir to safePaths
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *path) ErrNotSafePath(path string) error {
|
||||||
|
return ErrNotSafePath{Path: path, SafePaths: p.SafePaths()}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrNotSafePath struct {
|
||||||
|
Path string
|
||||||
|
SafePaths []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrNotSafePath) Error() string {
|
||||||
|
return fmt.Sprintf("path is not subpath of home directory or SAFE_PATHS: %s \n allowed paths: %s", e.Path, e.SafePaths)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *path) GetPathByHash(prefix, name string) string {
|
func (p *path) GetPathByHash(prefix, name string) string {
|
||||||
hash := utils.MakeHash([]byte(name))
|
hash := utils.MakeHash([]byte(name))
|
||||||
filename := hash.String()
|
filename := hash.String()
|
||||||
|
@ -371,14 +371,22 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if req.Path == "" {
|
if req.Path == "" { // default path unneeded any safe check
|
||||||
req.Path = C.Path.Config()
|
req.Path = C.Path.Config()
|
||||||
} else if !filepath.IsAbs(req.Path) {
|
} else {
|
||||||
|
if !filepath.IsAbs(req.Path) {
|
||||||
render.Status(r, http.StatusBadRequest)
|
render.Status(r, http.StatusBadRequest)
|
||||||
render.JSON(w, r, newError("path is not a absolute path"))
|
render.JSON(w, r, newError("path is not a absolute path"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !C.Path.IsSafePath(req.Path) {
|
||||||
|
render.Status(r, http.StatusBadRequest)
|
||||||
|
render.JSON(w, r, newError(C.Path.ErrNotSafePath(req.Path).Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cfg, err = executor.ParseWithPath(req.Path)
|
cfg, err = executor.ParseWithPath(req.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Status(r, http.StatusBadRequest)
|
render.Status(r, http.StatusBadRequest)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package provider
|
package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -12,10 +11,6 @@ import (
|
|||||||
"github.com/metacubex/mihomo/rules/common"
|
"github.com/metacubex/mihomo/rules/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
errSubPath = errors.New("path is not subpath of home directory")
|
|
||||||
)
|
|
||||||
|
|
||||||
type ruleProviderSchema struct {
|
type ruleProviderSchema struct {
|
||||||
Type string `provider:"type"`
|
Type string `provider:"type"`
|
||||||
Behavior string `provider:"behavior"`
|
Behavior string `provider:"behavior"`
|
||||||
@ -53,7 +48,7 @@ func ParseRuleProvider(name string, mapping map[string]any, parse common.ParseRu
|
|||||||
if schema.Path != "" {
|
if schema.Path != "" {
|
||||||
path = C.Path.Resolve(schema.Path)
|
path = C.Path.Resolve(schema.Path)
|
||||||
if !C.Path.IsSafePath(path) {
|
if !C.Path.IsSafePath(path) {
|
||||||
return nil, fmt.Errorf("%w: %s", errSubPath, path)
|
return nil, C.Path.ErrNotSafePath(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil, resource.DefaultHttpTimeout, schema.SizeLimit)
|
vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil, resource.DefaultHttpTimeout, schema.SizeLimit)
|
||||||
|
Loading…
Reference in New Issue
Block a user