feat: add go backend skeleton
This commit is contained in:
22
internal/app/config.go
Normal file
22
internal/app/config.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
CodexHome string
|
||||
HTTPAddr string
|
||||
}
|
||||
|
||||
func DefaultConfig() Config {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
home = "."
|
||||
}
|
||||
return Config{
|
||||
CodexHome: filepath.Join(home, ".codex"),
|
||||
HTTPAddr: "127.0.0.1:18083",
|
||||
}
|
||||
}
|
||||
55
internal/codexhome/bounds.go
Normal file
55
internal/codexhome/bounds.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package codexhome
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var ErrOutsideCodexHome = errors.New("路径超出 Codex home")
|
||||
var ErrForbiddenPath = errors.New("禁止访问敏感路径")
|
||||
|
||||
func ResolveInside(home string, rel string) (string, error) {
|
||||
if filepath.IsAbs(rel) {
|
||||
return "", ErrOutsideCodexHome
|
||||
}
|
||||
cleanHome, err := filepath.Abs(home)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
candidate, err := filepath.Abs(filepath.Join(cleanHome, rel))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
relative, err := filepath.Rel(cleanHome, candidate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if relative == ".." || strings.HasPrefix(relative, ".."+string(filepath.Separator)) {
|
||||
return "", ErrOutsideCodexHome
|
||||
}
|
||||
if IsForbidden(candidate, cleanHome) {
|
||||
return "", ErrForbiddenPath
|
||||
}
|
||||
return candidate, nil
|
||||
}
|
||||
|
||||
func IsForbidden(path string, home string) bool {
|
||||
cleanHome, err := filepath.Abs(home)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
cleanPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
rel, err := filepath.Rel(cleanHome, cleanPath)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
rel = filepath.ToSlash(rel)
|
||||
forbidden := map[string]bool{
|
||||
"auth.json": true,
|
||||
}
|
||||
return forbidden[rel]
|
||||
}
|
||||
34
internal/codexhome/bounds_test.go
Normal file
34
internal/codexhome/bounds_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package codexhome
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResolveInsideCodexHomeAllowsAgentsToml(t *testing.T) {
|
||||
home := filepath.Join(t.TempDir(), ".codex")
|
||||
got, err := ResolveInside(home, "agents/product-manager.toml")
|
||||
if err != nil {
|
||||
t.Fatalf("ResolveInside returned error: %v", err)
|
||||
}
|
||||
want := filepath.Join(home, "agents", "product-manager.toml")
|
||||
if got != want {
|
||||
t.Fatalf("path mismatch: got %q want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveInsideCodexHomeRejectsTraversal(t *testing.T) {
|
||||
home := filepath.Join(t.TempDir(), ".codex")
|
||||
_, err := ResolveInside(home, "../auth.json")
|
||||
if err == nil {
|
||||
t.Fatal("expected traversal to be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsForbiddenPathBlocksAuthJSON(t *testing.T) {
|
||||
home := filepath.Join(t.TempDir(), ".codex")
|
||||
path := filepath.Join(home, "auth.json")
|
||||
if !IsForbidden(path, home) {
|
||||
t.Fatal("auth.json must be forbidden")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user