fix(cache): address race condition, resource leak, and path traversal

- Fix race condition in cache.Store: hold lock during persist to ensure
  snapshot consistency with disk writes
- Fix resource leak in syncDir: use defer to ensure directory fd is closed
- Add path traversal validation in teamloader: prevent cache paths from
  escaping parent directory using filepath.Clean and prefix check
- Add test for path traversal protection

Assisted-By: docker-agent
This commit is contained in:
David Gageot committed 2026-04-28 09:21:01 +02:00
1 parent 7b898074c2
commit 2dea44efee
3 files changed
+35 -7

No files matched your search

+3 -7
View File
@@ -121,14 +121,10 @@ func (c *cache) Store(question, response string) {
key := c.normalize(question)
c.mu.Lock()
defer c.mu.Unlock()
c.entries[key] = response
var snapshot map[string]string
if c.persist != nil {
snapshot = maps.Clone(c.entries)
}
c.mu.Unlock()
if c.persist != nil {
snapshot := maps.Clone(c.entries)
c.persist(snapshot)
}
}
@@ -220,6 +216,6 @@ func syncDir(dir string) {
if err != nil {
return
}
defer d.Close()
_ = d.Sync()
_ = d.Close()
}
+6
View File
@@ -174,6 +174,12 @@ func LoadWithConfig(ctx context.Context, agentSource config.Source, runConfig *c
cachePath := agentConfig.Cache.Path
if cachePath != "" && !filepath.IsAbs(cachePath) {
cachePath = filepath.Join(parentDir, cachePath)
cachePath = filepath.Clean(cachePath)
// Ensure the resolved path is within parentDir to prevent path traversal
cleanParent := filepath.Clean(parentDir) + string(filepath.Separator)
if !strings.HasPrefix(cachePath+string(filepath.Separator), cleanParent) {
return nil, fmt.Errorf("agent %q: cache path %q escapes parent directory", agentConfig.Name, agentConfig.Cache.Path)
}
}
c, err := cache.New(cache.Config{
Enabled: true,
+26
View File
@@ -483,3 +483,29 @@ func TestExternalDepthContext(t *testing.T) {
ctx = contextWithExternalDepth(ctx, 7)
assert.Equal(t, 7, externalDepthFromContext(ctx))
}
func TestLoadWithConfig_CachePathTraversal(t *testing.T) {
tmpDir := t.TempDir()
// Create a config file with a path traversal attempt
configPath := filepath.Join(tmpDir, "agent.yaml")
configContent := `
agents:
root:
model: openai/gpt-4o
description: Test agent
instruction: You are a test agent.
cache:
enabled: true
path: ../../../../etc/passwd
`
require.NoError(t, os.WriteFile(configPath, []byte(configContent), 0o600))
source := config.NewFileSource(configPath)
runConfig := &config.RuntimeConfig{}
runConfig.WorkingDir = tmpDir
_, err := LoadWithConfig(t.Context(), source, runConfig)
require.Error(t, err)
assert.Contains(t, err.Error(), "escapes parent directory")
}