main: validate resolved config file extensions for run

This commit is contained in:
Meo597
2026-10-15 07:56:35 +08:00
parent 74456ca5e9
commit ecd8719d98
5 changed files with 127 additions and 4 deletions
+6 -2
View File
@@ -175,9 +175,13 @@ func init() {
switch v := input.(type) {
case cmdarg.Arg:
r, err := confloader.LoadConfig(v[0])
common.Must(err)
if err != nil {
return nil, err
}
data, err := buf.ReadAllToBytes(r)
common.Must(err)
if err != nil {
return nil, err
}
return loadProtobufConfig(data)
case io.Reader:
data, err := buf.ReadAllToBytes(v)
+2
View File
@@ -4,6 +4,8 @@ package base
type CommandEnvHolder struct {
// Executable name of current binary
Exec string
// Full name of the command being executed
CommandName string
// commands column width of current command
CommandsWidth int
}
+1
View File
@@ -61,6 +61,7 @@ BigCmdLoop:
}
buildCommandText(cmd)
CommandEnv.CommandName = cmdName
cmd.Run(cmd, args)
Exit()
return
+36 -2
View File
@@ -8,12 +8,14 @@ import (
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/utils"
"github.com/xtls/xray-core/main/commands/base"
"github.com/xtls/xray-core/main/confloader"
)
@@ -28,10 +30,15 @@ func ConfigLoader(arg string) (out io.Reader, err error) {
data, err = FetchHTTPContent(arg)
case arg == "stdin:":
data, err = io.ReadAll(os.Stdin)
data, err = readConfigFile(os.Stdin)
default:
data, err = os.ReadFile(arg)
file, openErr := os.Open(arg)
if openErr != nil {
return nil, openErr
}
defer file.Close()
data, err = readConfigFile(file)
}
if err != nil {
@@ -41,6 +48,33 @@ func ConfigLoader(arg string) (out io.Reader, err error) {
return
}
func readConfigFile(file *os.File) ([]byte, error) {
if base.CommandEnv.CommandName != "run" {
return io.ReadAll(file)
}
info, err := file.Stat()
if err != nil {
return nil, err
}
if info.Mode().IsRegular() {
path, err := filepath.Abs(file.Name())
if err != nil {
return nil, err
}
path, err = filepath.EvalSymlinks(path)
if err != nil {
return nil, err
}
switch strings.ToLower(filepath.Ext(path)) {
case ".jsonc", ".json", ".yml", ".yaml", ".toml", ".pb", ".protobuf":
default:
return nil, errors.New("unsupported config file extension: ", path)
}
}
return io.ReadAll(file)
}
// FetchHTTPContent issues an HTTP GET against either a regular HTTP(S) URL
// or a Unix socket HTTP endpoint.
//
+82
View File
@@ -0,0 +1,82 @@
package main
import (
"context"
"flag"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"time"
)
func TestRunConfigCommands(t *testing.T) {
if os.Getenv("XRAY_TEST_CONFIG_COMMAND") == "1" {
os.Args = append([]string{os.Args[0]}, flag.Args()...)
main()
return
}
dir := t.TempDir()
config := filepath.Join(dir, "config.yaml")
script := filepath.Join(dir, "config.lua")
binary := filepath.Join(dir, "config.pb")
message := filepath.Join(dir, "user.tmsg")
for path, body := range map[string]string{
config: "{}",
script: "{}",
binary: "",
message: `{"type":"xray.app.dispatcher.Config","value":""}`,
} {
if err := os.WriteFile(path, []byte(body), 0o600); err != nil {
t.Fatal(err)
}
}
file, err := os.Open(script)
if err != nil {
t.Fatal(err)
}
defer file.Close()
for _, tc := range []struct {
name string
args []string
code int
input io.Reader
}{
{"format override", []string{"run", "-test", "-format", "json", "-c", config}, 0, nil},
{"invalid extension", []string{"run", "-test", "-format", "json", "-c", script}, 23, nil},
{"default command", []string{"-test", "-format", "json", "-c", script}, 23, nil},
{"protobuf config", []string{"run", "-test", "-c", binary}, 0, nil},
{"protobuf error", []string{"run", "-test", "-format", "protobuf", "-c", script}, 23, nil},
{"merged source", []string{"run", "-test", "-format", "json", "-c", config, "-c", script}, 23, nil},
{"dump", []string{"run", "-dump", "-c", config}, 0, nil},
{"stdin pipe", []string{"run", "-test", "-c", "stdin:"}, 0, nil},
{"stdin file", []string{"run", "-test", "-c", "stdin:"}, 23, file},
{"convert json", []string{"convert", "json", message}, 0, nil},
{"convert pb", []string{"convert", "pb", "-debug", config}, 0, nil},
{"convert pb stdin", []string{"convert", "pb", "-debug", "stdin:"}, 0, file},
} {
t.Run(tc.name, func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
args := append([]string{"-test.run=^TestRunConfigCommands$", "--"}, tc.args...)
cmd := exec.CommandContext(ctx, os.Args[0], args...)
cmd.Env = append(os.Environ(), "XRAY_TEST_CONFIG_COMMAND=1")
cmd.Stdin = strings.NewReader("{}")
if tc.input != nil {
cmd.Stdin = tc.input
}
output, err := cmd.CombinedOutput()
if cmd.ProcessState == nil {
t.Fatal(err)
}
if code := cmd.ProcessState.ExitCode(); code != tc.code {
t.Fatalf("exit code = %d, want %d: %v\n%s", code, tc.code, err, output)
}
if tc.code == 23 && !strings.Contains(string(output), "failed to load config files") {
t.Fatalf("missing config error:\n%s", output)
}
})
}
}