fix(security): CWE path-injection — resolveInsideRoot for Restart + ReadFile template paths (PR #1261)
workspace_restart.go:127-133 accepted body.Template (attacker-controlled) via raw filepath.Join(h.configsDir, template), allowing path traversal (e.g. "../../../etc") to escape configsDir. Fix: replace raw filepath.Join with resolveInsideRoot, same pattern as workspace.go:102 (already fixed) and workspace.go:249 (already fixed). Both the explicit template path and the findTemplateByName fallback are safe — findTemplateByName returns a directory name from os.ReadDir which is inherently bounded and cannot contain "/". On resolve error the template is cleared so findTemplateByName fallback still fires (preserves existing restart behaviour when template is invalid). Closes: #1043 Co-authored-by: Molecule AI Core-BE <core-be@agents.moleculesai.app> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
52709718ec
commit
0bd2bf2b7f
@ -295,6 +295,13 @@ func (h *TemplatesHandler) ReadFile(c *gin.Context) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "file not found (container offline, no template)"})
|
||||
return
|
||||
}
|
||||
// validateRelPath is already called above (line 260) for the container path,
|
||||
// but the fallback below uses filePath directly in filepath.Join without
|
||||
// any sanitization. Re-validate before the host-side read to close the gap.
|
||||
if err := validateRelPath(filePath); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid path"})
|
||||
return
|
||||
}
|
||||
fullPath := filepath.Join(templateDir, filePath)
|
||||
data, err := os.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
|
||||
@ -125,10 +125,15 @@ func (h *WorkspaceHandler) Restart(c *gin.Context) {
|
||||
template = findTemplateByName(h.configsDir, wsName)
|
||||
}
|
||||
if template != "" {
|
||||
candidatePath := filepath.Join(h.configsDir, template)
|
||||
if _, err := os.Stat(candidatePath); err == nil {
|
||||
candidatePath, resolveErr := resolveInsideRoot(h.configsDir, template)
|
||||
if resolveErr != nil {
|
||||
log.Printf("Restart: invalid template %q: %v — proceeding without it", template, resolveErr)
|
||||
template = "" // clear so findTemplateByName fallback fires
|
||||
} else if _, err := os.Stat(candidatePath); err == nil {
|
||||
templatePath = candidatePath
|
||||
configLabel = template
|
||||
} else {
|
||||
log.Printf("Restart: template %q dir not found — proceeding without it", template)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user