fix(02): resolve symlinks before fixture and vars path checks (WR-07)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
18
tide/flow.go
18
tide/flow.go
@@ -184,7 +184,20 @@ func materializeSidecar(base string, resp *Response) error {
|
||||
if base != "" {
|
||||
path = filepath.Join(base, resp.BodyFile)
|
||||
}
|
||||
raw, err := os.ReadFile(path)
|
||||
resolved, err := resolvePath(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tide: read body_file %s: %w", resp.BodyFile, err)
|
||||
}
|
||||
if base != "" {
|
||||
root, err := resolvePath(base)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tide: fixture dir: %w", err)
|
||||
}
|
||||
if resolved != root && !strings.HasPrefix(resolved, root+string(os.PathSeparator)) {
|
||||
return fmt.Errorf("tide: body_file %q escapes the fixture directory", resp.BodyFile)
|
||||
}
|
||||
}
|
||||
raw, err := os.ReadFile(resolved)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tide: read body_file %s: %w", resp.BodyFile, err)
|
||||
}
|
||||
@@ -208,6 +221,9 @@ func validateSidecar(path string) error {
|
||||
if clean == ".." || strings.HasPrefix(clean, "../") {
|
||||
return fmt.Errorf("body_file %q escapes the fixture directory", path)
|
||||
}
|
||||
if _, err := resolvePath(path); err != nil {
|
||||
return fmt.Errorf("body_file %q: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -192,6 +192,9 @@ func validateFixturePath(p string) error {
|
||||
if clean == ".." || strings.HasPrefix(clean, "../") {
|
||||
return fmt.Errorf("fixture %q escapes the fixture directory", p)
|
||||
}
|
||||
if _, err := resolvePath(p); err != nil {
|
||||
return fmt.Errorf("fixture %q: %w", p, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -253,6 +253,23 @@ func TestProxyRejectsNonLoopbackOverflowAndCredentials(t *testing.T) {
|
||||
}); err == nil || !strings.Contains(err.Error(), "outside") {
|
||||
t.Fatalf("vars inside fixtures: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(inside, []byte("{}\n"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
linkDir := t.TempDir()
|
||||
link := filepath.Join(linkDir, "vars-link.yaml")
|
||||
if err := os.Symlink(inside, link); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := NewProxy(ProxyConfig{
|
||||
Listen: "127.0.0.1:0",
|
||||
Upstream: upstream.URL,
|
||||
Fixtures: fixtures,
|
||||
VarsPath: link,
|
||||
Rules: mustParseRules(t, testRulesYAML()),
|
||||
}); err == nil || !strings.Contains(err.Error(), "outside") {
|
||||
t.Fatalf("symlink vars into fixtures: %v", err)
|
||||
}
|
||||
outside := filepath.Join(t.TempDir(), "vars.yaml")
|
||||
p, err := NewProxy(ProxyConfig{
|
||||
Listen: "127.0.0.1:0",
|
||||
|
||||
@@ -686,11 +686,11 @@ func varsOutsideFixtures(varsPath, fixtures string) error {
|
||||
if varsPath == "" || fixtures == "" {
|
||||
return nil
|
||||
}
|
||||
absVars, err := filepath.Abs(varsPath)
|
||||
absVars, err := resolvePath(varsPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tide: vars path: %w", err)
|
||||
}
|
||||
absFix, err := filepath.Abs(fixtures)
|
||||
absFix, err := resolvePath(fixtures)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tide: fixtures path: %w", err)
|
||||
}
|
||||
@@ -700,6 +700,24 @@ func varsOutsideFixtures(varsPath, fixtures string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolvePath(path string) (string, error) {
|
||||
abs, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if eval, err := filepath.EvalSymlinks(abs); err == nil {
|
||||
return eval, nil
|
||||
}
|
||||
parentEval, err := filepath.EvalSymlinks(filepath.Dir(abs))
|
||||
if err != nil {
|
||||
if _, statErr := os.Lstat(abs); statErr == nil {
|
||||
return "", fmt.Errorf("eval symlinks %s: %w", path, err)
|
||||
}
|
||||
return filepath.Clean(abs), nil
|
||||
}
|
||||
return filepath.Join(parentEval, filepath.Base(abs)), nil
|
||||
}
|
||||
|
||||
func mergeRouteCaptures(step *Step, rules Rules) {
|
||||
if step == nil || len(step.Capture) > 0 {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user