{"record":{"id":"ac5590584d73787f","repo":"Tencent/WeKnora","slug":"sandbox-work-dir-q-is-outside-allowed-roots-s","errorCode":null,"errorMessage":"sandbox: work dir %q is outside allowed roots (%s)","messagePattern":"sandbox: work dir %q is outside allowed roots \\((.+?)\\)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sandbox/session_manager.go","lineNumber":1066,"sourceCode":"// allowSkillsRoot widens it to the skills image root for install/maintenance\n// sessions, so the installer agent can set work_dir to the skill directory and\n// run ordinary relative commands instead of composing long absolute paths. It\n// is a widening, not a removal: everything outside these two roots is still\n// refused.\nfunc cleanSessionWorkDir(workDir string, allowSkillsRoot bool) (string, error) {\n\tclean := path.Clean(strings.TrimSpace(workDir))\n\tif clean == SessionWorkspaceRoot || strings.HasPrefix(clean, SessionWorkspaceRoot+\"/\") {\n\t\treturn clean, nil\n\t}\n\tif allowSkillsRoot &&\n\t\t(clean == SkillsImageRoot || strings.HasPrefix(clean, SkillsImageRoot+\"/\")) {\n\t\treturn clean, nil\n\t}\n\tallowed := SessionWorkspaceRoot\n\tif allowSkillsRoot {\n\t\tallowed = SessionWorkspaceRoot + \", \" + SkillsImageRoot\n\t}\n\treturn \"\", fmt.Errorf(\n\t\t\"sandbox: work dir %q is outside allowed roots (%s)\",\n\t\tworkDir, allowed,\n\t)\n}\n\n// buildSessionCreateRequest projects Config into a provider-neutral remote\n// create request. The metadata block is populated per-session by the\n// lifecycle coordinator; env vars propagate as-is.\n//\n// The provider parameter (derived from RemoteSandboxClient.Provider()) is the\n// authoritative source of identity — it selects the correct Config fields so\n// Cube and E2B never read each other's templates or TTLs.\nfunc buildSessionCreateRequest(provider RemoteProvider, cfg *Config) (RemoteCreateRequest, error) {\n\tenvVars := withWorkspaceEnvDefaults(cloneMetadata(cfg.EnvVars))\n\n\tswitch provider {\n\tcase SandboxTypeCube:\n\t\tttl := cfg.CubeSandboxTTL","sourceCodeStart":1048,"sourceCodeEnd":1084,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/sandbox/session_manager.go#L1048-L1084","documentation":"cleanSessionWorkDir keeps shell_exec inside directories the sandbox is willing to let the agent work in. By default only /workspace is allowed; with the skills-install mode the skills image root is added. It throws this when the requested working directory is not under one of those roots.","triggerScenarios":"ExecShellCommandWithOptions invoked with WorkDir set to anything outside /workspace (and, when allowSkillsRoot is set, outside the skills image root) — e.g. \"/\", \"/home/user\", \"/opt/tool\". Also reachable via ValidatedSessionOutputDir and the two cleanSessionWorkDir tests.","commonSituations":"Trying to cd into an installed tool's directory outside the workspace; reusing host-style working directories in remote sessions; assuming skills directories are executable workspaces in ordinary sessions.","solutions":["Set the command's work dir to a path under /workspace.","If the work must happen in the skills image root, run in skills-install mode (allowSkillsRoot) so that root is permitted.","Create the needed directory under /workspace and cd there in the command instead."],"exampleFix":"// before\nopts.WorkDir = \"/opt/tools\"\n// after\nopts.WorkDir = \"/workspace/tools\"","handlingStrategy":"validation","validationCode":"func allowedWorkDir(dir string, allowSkills bool) bool {\n    c := filepath.Clean(dir)\n    ok := strings.HasPrefix(c, \"/workspace/\")\n    if allowSkills {\n        ok = ok || strings.HasPrefix(c, skillsImageRoot+\"/\")\n    }\n    return ok\n}","typeGuard":"func inRoot(dir, root string) bool {\n    c := filepath.Clean(dir)\n    return strings.HasPrefix(c, root+\"/\")\n}","tryCatchPattern":"err := exec.ExecShellCommandWithOptions(ctx, s, opts)\nif err != nil && strings.Contains(err.Error(), \"outside allowed roots\") {\n    opts.WorkDir = \"/workspace\"\n    err = exec.ExecShellCommandWithOptions(ctx, s, opts)\n}","preventionTips":["Default WorkDir to \"/workspace\" and require justification for anything else.","Clean all work-dir values with filepath.Clean before use.","Only enable the skills root in install-mode sessions."],"tags":["sandbox","path-validation","shell-exec"],"backgroundTag":"path-outside-sandbox-root","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}