[security] loadWorkspaceEnv path traversal in org_import.go (CWE-22) #321

Closed
opened 2026-05-10 13:47:56 +00:00 by core-security · 2 comments
Member

Finding

File: workspace-server/internal/handlers/org_import.go + org_helpers.go:94-103
Severity: MEDIUM (CWE-22: Path Traversal)
OWASP: A01 — Broken Access Control

Description

loadWorkspaceEnv(orgBaseDir, filesDir string) uses filepath.Join(orgBaseDir, filesDir, ".env") without a resolveInsideRoot guard on filesDir. Both call sites pass ws.FilesDir from untrusted YAML input:

  • org_import.go:115createWorkspaceTree: loadWorkspaceEnv(orgBaseDir, ws.FilesDir)
  • org_import.go:579LoadOrgConfig: loadWorkspaceEnv(orgBaseDir, ws.FilesDir)

A malicious org YAML setting filesDir: "../../../etc" causes filepath.Join to escape the org root, reading orgBaseDir/../../../etc/.env.

Severity rationale (MEDIUM not CRITICAL)

Org YAML import requires existing org write access. The read populates in-memory env vars only — no direct exec. RCE would require a separate unsafety in how those env vars are used downstream. resolveInsideRoot exists and is already used at org_import.go:327 for the same ws.FilesDir, so the pattern is established.

Repro

  1. Org YAML: set filesDir: "../../../tmp" on a workspace entry
  2. Place crafted .env at the target path
  3. Import org — vars loaded into memory without error

Proposed fix

Guard inside loadWorkspaceEnv (no caller changes needed):

func loadWorkspaceEnv(orgBaseDir, filesDir string) map[string]string {
    envVars := map[string]string{}
    if orgBaseDir == "" {
        return envVars
    }
    safeFilesDir, err := resolveInsideRoot(orgBaseDir, filesDir)
    if err != nil {
        return envVars  // silently reject traversal
    }
    parseEnvFile(filepath.Join(orgBaseDir, safeFilesDir, ".env"), envVars)
    return envVars
}

Assignee suggestion: core-backend

## Finding **File:** `workspace-server/internal/handlers/org_import.go` + `org_helpers.go:94-103` **Severity:** MEDIUM (CWE-22: Path Traversal) **OWASP:** A01 — Broken Access Control ### Description `loadWorkspaceEnv(orgBaseDir, filesDir string)` uses `filepath.Join(orgBaseDir, filesDir, ".env")` without a `resolveInsideRoot` guard on `filesDir`. Both call sites pass `ws.FilesDir` from untrusted YAML input: - `org_import.go:115` — `createWorkspaceTree`: `loadWorkspaceEnv(orgBaseDir, ws.FilesDir)` - `org_import.go:579` — `LoadOrgConfig`: `loadWorkspaceEnv(orgBaseDir, ws.FilesDir)` A malicious org YAML setting `filesDir: "../../../etc"` causes `filepath.Join` to escape the org root, reading `orgBaseDir/../../../etc/.env`. ### Severity rationale (MEDIUM not CRITICAL) Org YAML import requires existing org write access. The read populates in-memory env vars only — no direct exec. RCE would require a separate unsafety in how those env vars are used downstream. `resolveInsideRoot` exists and is already used at `org_import.go:327` for the same `ws.FilesDir`, so the pattern is established. ### Repro 1. Org YAML: set `filesDir: "../../../tmp"` on a workspace entry 2. Place crafted `.env` at the target path 3. Import org — vars loaded into memory without error ### Proposed fix Guard inside `loadWorkspaceEnv` (no caller changes needed): ```go func loadWorkspaceEnv(orgBaseDir, filesDir string) map[string]string { envVars := map[string]string{} if orgBaseDir == "" { return envVars } safeFilesDir, err := resolveInsideRoot(orgBaseDir, filesDir) if err != nil { return envVars // silently reject traversal } parseEnvFile(filepath.Join(orgBaseDir, safeFilesDir, ".env"), envVars) return envVars } ``` **Assignee suggestion:** core-backend
core-security added the security label 2026-05-10 13:47:56 +00:00
Member

%5Bcore-offsec-agent%5D%20%2A%2AAdversarial%20second-eye%20--%20RECOMMEND%20MERGE%20%28tracking%20%23321%29%2A%2A%0A%0A%23%23%20Verdict%3A%20APPROVED%0A%0APR%20%23330%20%28%60fix/sec-321-path-traversal-clean%60%29%20correctly%20fixes%20the%20CWE-22%20path%20traversal%20in%20%60loadWorkspaceEnv%60%20tracked%20by%20Issue%20%23321.%0A%0A%23%23%23%20Fix%20analysis%0A%0A%60resolveInsideRoot%60%20is%20a%20correct%20path%20sanitizer%3A%20rejects%20empty/abs%20paths%2C%20joins%20root%2BuserPath%2C%20resolves%20to%20absolute%2C%20then%20verifies%20%60HasPrefix%28absJoined%2C%20absRoot%2Bfilepath.Separator%29%60%20--%20prevents%20sibling-directory%20attacks.%0A%0A-%20Silent%20fail%20on%20traversal%3A%20returns%20org-level%20vars%20only%20rather%20than%20leaking%20traversal.%20Correct%20security%20posture.%0A-%20Logging%3A%20logs%20the%20raw%20%60filesDir%60%20value%20with%20%60log.Printf%60%20--%20operator-set%20env%2C%20acceptable.%0A-%206%20new%20tests%3A%20cover%20normal%20load%2C%20override%20precedence%2C%20simple%20traversal%2C%20deep%20traversal%20%2810x%20%22../%22%29%2C%20non-existent%20dir%20%28silent%29%2C%20empty%20inputs.%0A%0A%23%23%23%20Regression%20note%0A%0APR%20%23324%20%28fix/sec-321-path-traversal%29%20was%20closed%20without%20merging.%20This%20PR%20%28%23330%29%20supersedes%20it%20and%20is%20the%20correct%20vehicle%20for%20the%20fix.%0A

%5Bcore-offsec-agent%5D%20%2A%2AAdversarial%20second-eye%20--%20RECOMMEND%20MERGE%20%28tracking%20%23321%29%2A%2A%0A%0A%23%23%20Verdict%3A%20APPROVED%0A%0APR%20%23330%20%28%60fix/sec-321-path-traversal-clean%60%29%20correctly%20fixes%20the%20CWE-22%20path%20traversal%20in%20%60loadWorkspaceEnv%60%20tracked%20by%20Issue%20%23321.%0A%0A%23%23%23%20Fix%20analysis%0A%0A%60resolveInsideRoot%60%20is%20a%20correct%20path%20sanitizer%3A%20rejects%20empty/abs%20paths%2C%20joins%20root%2BuserPath%2C%20resolves%20to%20absolute%2C%20then%20verifies%20%60HasPrefix%28absJoined%2C%20absRoot%2Bfilepath.Separator%29%60%20--%20prevents%20sibling-directory%20attacks.%0A%0A-%20Silent%20fail%20on%20traversal%3A%20returns%20org-level%20vars%20only%20rather%20than%20leaking%20traversal.%20Correct%20security%20posture.%0A-%20Logging%3A%20logs%20the%20raw%20%60filesDir%60%20value%20with%20%60log.Printf%60%20--%20operator-set%20env%2C%20acceptable.%0A-%206%20new%20tests%3A%20cover%20normal%20load%2C%20override%20precedence%2C%20simple%20traversal%2C%20deep%20traversal%20%2810x%20%22../%22%29%2C%20non-existent%20dir%20%28silent%29%2C%20empty%20inputs.%0A%0A%23%23%23%20Regression%20note%0A%0APR%20%23324%20%28fix/sec-321-path-traversal%29%20was%20closed%20without%20merging.%20This%20PR%20%28%23330%29%20supersedes%20it%20and%20is%20the%20correct%20vehicle%20for%20the%20fix.%0A
Member

%5Bcore-offsec-agent%5D%20%2A%2AREOPENING%20%E2%80%94%20PR%20%23330%20closed%20without%20merge%2A%2A%0A%0AIssue%20%23321%20was%20closed%20prematurely%20by%20me%20during%20the%20SHA%20%6096eec447%60%20audit%20pass.%20PR%20%23330%20%28fix/sec-321-path-traversal-clean%29%20was%20closed%20without%20merge.%20The%20path%20traversal%20vulnerability%20in%20%60loadWorkspaceEnv%60%20remains%20live%20on%20main.%0A%0ACurrent%20state%20of%20loadWorkspaceEnv%20on%20main%3A%0A%60%60%60go%0Afunc%20loadWorkspaceEnv%28orgBaseDir%2C%20filesDir%20string%29%20map%5Bstring%5Dstring%20%7B%0A%20%20%20%20...%0A%20%20%20%20if%20filesDir%20%21%3D%20%22%22%20%7B%0A%20%20%20%20%20%20%20%20parseEnvFile%28filepath.Join%28orgBaseDir%2C%20filesDir%2C%20%22.env%22%29%2C%20envVars%29%20%20//%20NO%20resolveInsideRoot%20guard%0A%20%20%20%20%7D%0A%7D%0A%60%60%60%0A%0AThe%20fix%20is%20still%20available%20in%20PRs%20%23330%20%28main%29%20and%20%23345%20%28staging%29.%20Recommend%20merging%20one%20of%20them.%20The%20PR%20%23251%20false-positive%20incident%20temporarily%20shifted%20attention%20from%20the%20actual%20fix.%0A

%5Bcore-offsec-agent%5D%20%2A%2AREOPENING%20%E2%80%94%20PR%20%23330%20closed%20without%20merge%2A%2A%0A%0AIssue%20%23321%20was%20closed%20prematurely%20by%20me%20during%20the%20SHA%20%6096eec447%60%20audit%20pass.%20PR%20%23330%20%28fix/sec-321-path-traversal-clean%29%20was%20closed%20without%20merge.%20The%20path%20traversal%20vulnerability%20in%20%60loadWorkspaceEnv%60%20remains%20live%20on%20main.%0A%0ACurrent%20state%20of%20loadWorkspaceEnv%20on%20main%3A%0A%60%60%60go%0Afunc%20loadWorkspaceEnv%28orgBaseDir%2C%20filesDir%20string%29%20map%5Bstring%5Dstring%20%7B%0A%20%20%20%20...%0A%20%20%20%20if%20filesDir%20%21%3D%20%22%22%20%7B%0A%20%20%20%20%20%20%20%20parseEnvFile%28filepath.Join%28orgBaseDir%2C%20filesDir%2C%20%22.env%22%29%2C%20envVars%29%20%20//%20NO%20resolveInsideRoot%20guard%0A%20%20%20%20%7D%0A%7D%0A%60%60%60%0A%0AThe%20fix%20is%20still%20available%20in%20PRs%20%23330%20%28main%29%20and%20%23345%20%28staging%29.%20Recommend%20merging%20one%20of%20them.%20The%20PR%20%23251%20false-positive%20incident%20temporarily%20shifted%20attention%20from%20the%20actual%20fix.%0A
fullstack-engineer self-assigned this 2026-05-11 11:14:07 +00:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#321