feat(vscode): add Daemon debug configuration

Add a new debug configuration for Daemon mode in VS Code launch settings
to facilitate debugging with specific environment variables and flags.

fix(config): use filepath.Dir for config path resolution

Change from assigning raw config path to using filepath.Dir to get the
directory of the config file, ensuring proper directory handling.

refactor(filehandler): improve file access security and directory handling

Improve security checks by using Lstat instead of Mode check for symlinks,
enhance directory handling when URL ends with "/" to prevent path traversal
errors, and refine path resolution logic to properly handle root directory
cases.

fix(config): disable SSL and update example root path

Disable SSL for teststatic server configuration and update root path
from relative "./example" to "../example" for proper example directory access.
```

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
kingecg 2026-07-11 08:50:00 +08:00
parent f4a2967f59
commit 6fd9f32f9c
4 changed files with 44 additions and 17 deletions

18
.vscode/launch.json vendored
View File

@ -21,6 +21,24 @@
"showLog": true,
"trace": "log",
"dlvFlags": ["--check-go-version=false"]
},
{
"name": "Daemon",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}",
"env": {
"GODEBUG": "cgocheck=0",
"_go_daemon": "g_daemon"
},
"args": [
"run",
"${file}"
],
"showLog": true,
"trace": "log",
"dlvFlags": ["--check-go-version=false"]
}
]
}

View File

@ -133,7 +133,7 @@ func LoadConfig() {
server := model.HttpServerConfig{}
err = decoder.Decode(&server)
if err == nil {
server.ConfPath = config
server.ConfPath = filepath.Dir(config)
normalizeServer(&server)
model.Config.Servers = append(model.Config.Servers, &server)
} else {

View File

@ -25,27 +25,34 @@ func (f FileHandler) String() string {
}
func (f FileHandler) Open(name string) (http.File, error) {
l := gologger.GetLogger("filehandler")
l.Debug("access:", name)
l.Debug("Open called: name=", name)
if strings.HasPrefix(name, "../") {
return nil, errors.New("not permitted")
}
relatedPath := strings.TrimPrefix(name, f.WPath)
rPath := filepath.Join(f.Root, strings.TrimPrefix(relatedPath, "/"))
l.Debug("access:", rPath)
// When URL ends with "/" (like "/") and WPath matches, we should return
// the directory and let http.FileServer handle the redirect to default file.
// This prevents "http: attempting to traverse a non-directory" error.
if relatedPath == "" && f.Default != "" {
relatedPath = "."
}
// If relatedPath becomes ".", use Root directly to represent the directory itself
var rPath string
if relatedPath == "." {
rPath = f.Root
} else {
rPath = filepath.Join(f.Root, strings.TrimPrefix(relatedPath, "/"))
}
// Resolve symlinks and clean path to prevent path traversal
realRoot, _ := filepath.EvalSymlinks(f.Root)
realPath, _ := filepath.EvalSymlinks(filepath.Dir(rPath))
if realPath != realRoot && !strings.HasPrefix(realPath, realRoot+string(filepath.Separator)) {
return nil, errors.New("not permitted")
}
realRPath, _ := filepath.EvalSymlinks(rPath)
if rPath == f.Root {
if f.Default == "" {
return nil, errors.New("not permit list dir")
}
rPath = filepath.Join(rPath, f.Default)
// Check if the resolved path is within root
if realRPath != realRoot && !strings.HasPrefix(realRPath, realRoot+string(filepath.Separator)) {
return nil, errors.New("not permitted")
}
fInfo, exists, err := FileExists(rPath)
@ -57,11 +64,13 @@ func (f FileHandler) Open(name string) (http.File, error) {
return nil, os.ErrNotExist
}
if fInfo.IsDir() && rPath != strings.TrimSuffix(f.Root, "/") {
// If it's a directory and not the root, don't allow listing
if fInfo.IsDir() && rPath != f.Root {
return nil, errors.New("not permit list dir")
}
if fInfo.Mode() == fs.ModeSymlink {
// Use Lstat to check if the path itself is a symlink (doesn't follow symlinks)
if lstatInfo, err := os.Lstat(rPath); err == nil && lstatInfo.Mode()&fs.ModeSymlink != 0 {
return nil, errors.New("not permit follow symbol link")
}

View File

@ -2,11 +2,11 @@
"name": "teststatic",
"server": ["www.teststatic.com"],
"port": 8088,
"enable_ssl":true,
"enable_ssl":false,
"paths": [
{
"path": "/",
"root": "./example",
"root": "../example",
"default": "index.html"
}
]