Skip to content

Commit 5041f07

Browse files
committed
chore: better path checks
1 parent cad26ac commit 5041f07

File tree

8 files changed

+25
-14
lines changed

8 files changed

+25
-14
lines changed

adapter/outbound/ssh.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,11 @@ func NewSsh(option SshOption) (*Ssh, error) {
136136
if strings.Contains(option.PrivateKey, "PRIVATE KEY") {
137137
b = []byte(option.PrivateKey)
138138
} else {
139-
b, err = os.ReadFile(C.Path.Resolve(option.PrivateKey))
139+
path := C.Path.Resolve(option.PrivateKey)
140+
if !C.Path.IsSafePath(path) {
141+
return nil, fmt.Errorf("path is not subpath of home directory: %s", path)
142+
}
143+
b, err = os.ReadFile(path)
140144
if err != nil {
141145
return nil, err
142146
}

adapter/provider/parser.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717

1818
var (
1919
errVehicleType = errors.New("unsupport vehicle type")
20-
errSubPath = errors.New("path is not subpath of home directory")
2120
)
2221

2322
type healthCheckSchema struct {
@@ -115,7 +114,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide
115114
if schema.Path != "" {
116115
path = C.Path.Resolve(schema.Path)
117116
if !C.Path.IsSafePath(path) {
118-
return nil, fmt.Errorf("%w: %s", errSubPath, path)
117+
return nil, fmt.Errorf("path is not subpath of home directory: %s", path)
119118
}
120119
}
121120
vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, schema.Header, resource.DefaultHttpTimeout, schema.SizeLimit)

component/ca/config.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ func GetCertPool(customCA string, customCAString string) (*x509.CertPool, error)
8181
var certificate []byte
8282
var err error
8383
if len(customCA) > 0 {
84-
certificate, err = os.ReadFile(C.Path.Resolve(customCA))
84+
path := C.Path.Resolve(customCA)
85+
if !C.Path.IsSafePath(path) {
86+
return nil, fmt.Errorf("path is not subpath of home directory: %s", path)
87+
}
88+
certificate, err = os.ReadFile(path)
8589
if err != nil {
8690
return nil, fmt.Errorf("load ca error: %w", err)
8791
}

component/ca/keypair.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
type Path interface {
1818
Resolve(path string) string
19+
IsSafePath(path string) bool
1920
}
2021

2122
// LoadTLSKeyPair loads a TLS key pair from the provided certificate and private key data or file paths, supporting fallback resolution.
@@ -39,7 +40,13 @@ func LoadTLSKeyPair(certificate, privateKey string, path Path) (tls.Certificate,
3940
}
4041

4142
certificate = path.Resolve(certificate)
43+
if !path.IsSafePath(certificate) {
44+
return tls.Certificate{}, fmt.Errorf("path is not subpath of home directory: %s", certificate)
45+
}
4246
privateKey = path.Resolve(privateKey)
47+
if !path.IsSafePath(privateKey) {
48+
return tls.Certificate{}, fmt.Errorf("path is not subpath of home directory: %s", privateKey)
49+
}
4350
cert, loadErr := tls.LoadX509KeyPair(certificate, privateKey)
4451
if loadErr != nil {
4552
return tls.Certificate{}, fmt.Errorf("parse certificate failed, maybe format error:%s, or path error: %s", painTextErr.Error(), loadErr.Error())

config/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,9 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
754754
}
755755

756756
func parseController(cfg *RawConfig) (*Controller, error) {
757+
if path := cfg.ExternalUI; path != "" && !C.Path.IsSafePath(path) {
758+
return nil, fmt.Errorf("path is not subpath of home directory: %s", path)
759+
}
757760
return &Controller{
758761
ExternalController: cfg.ExternalController,
759762
ExternalUI: cfg.ExternalUI,

constant/path.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func (p *path) IsSafePath(path string) bool {
8484
return false
8585
}
8686

87-
return !strings.Contains(rel, "..")
87+
return filepath.IsLocal(rel)
8888
}
8989

9090
func (p *path) GetPathByHash(prefix, name string) string {

hub/route/configs.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -373,10 +373,9 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) {
373373
} else {
374374
if req.Path == "" {
375375
req.Path = C.Path.Config()
376-
}
377-
if !filepath.IsAbs(req.Path) {
376+
} else if !filepath.IsLocal(req.Path) {
378377
render.Status(r, http.StatusBadRequest)
379-
render.JSON(w, r, newError("path is not a absolute path"))
378+
render.JSON(w, r, newError("path is not a valid absolute path"))
380379
return
381380
}
382381

rules/provider/parse.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package provider
22

33
import (
4-
"errors"
54
"fmt"
65
"time"
76

@@ -12,10 +11,6 @@ import (
1211
"github.com/metacubex/mihomo/rules/common"
1312
)
1413

15-
var (
16-
errSubPath = errors.New("path is not subpath of home directory")
17-
)
18-
1914
type ruleProviderSchema struct {
2015
Type string `provider:"type"`
2116
Behavior string `provider:"behavior"`
@@ -53,7 +48,7 @@ func ParseRuleProvider(name string, mapping map[string]any, parse common.ParseRu
5348
if schema.Path != "" {
5449
path = C.Path.Resolve(schema.Path)
5550
if !C.Path.IsSafePath(path) {
56-
return nil, fmt.Errorf("%w: %s", errSubPath, path)
51+
return nil, fmt.Errorf("path is not subpath of home directory: %s", path)
5752
}
5853
}
5954
vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil, resource.DefaultHttpTimeout, schema.SizeLimit)

0 commit comments

Comments
 (0)