{"record":{"id":"3d95490ea573110a","repo":"chenhg5/cc-connect","slug":"project-q-target-user-q-can-run-passwordless-su","errorCode":null,"errorMessage":"project %q: target user %q can run passwordless sudo. The run_as_user sandbox provides no isolation if the spawned agent can escalate non-interactively. Remove NOPASSWD sudo access for this user before starting cc-connect.","messagePattern":"project %q: target user %q can run passwordless sudo\\. The run_as_user sandbox provides no isolation if the spawned agent can escalate non-interactively\\. Remove NOPASSWD sudo access for this user before starting cc-connect\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"core/runas_check.go","lineNumber":114,"sourceCode":"\t\tresult.Fatal = append(result.Fatal, fmt.Errorf(\n\t\t\t\"project %q: passwordless sudo to user %q is not configured. Add a sudoers rule such as:\\n  %s ALL=(%s) NOPASSWD: ALL\\nthen restart cc-connect. Underlying error: %w\",\n\t\t\tcfg.Project, cfg.RunAsUser, currentUsernameOr(\"<supervisor>\"), cfg.RunAsUser, err))\n\t\treturn result // subsequent checks are pointless\n\t}\n\n\tif _, err := cfg.Runner.Run(ctx, \"-n\", \"-iu\", cfg.RunAsUser, \"--\", \"sudo\", \"-n\", \"/usr/bin/true\"); err == nil {\n\t\t// Escalation succeeded — collect sudo -l from the target's\n\t\t// context to help the operator find the offending rule.\n\t\tif out, listErr := cfg.Runner.Run(ctx, \"-n\", \"-iu\", cfg.RunAsUser, \"--\", \"sudo\", \"-n\", \"-l\"); listErr == nil {\n\t\t\tresult.SudoListOutput = strings.TrimSpace(string(out))\n\t\t}\n\t\tmsg := fmt.Sprintf(\n\t\t\t\"project %q: target user %q can run passwordless sudo. The run_as_user sandbox provides no isolation if the spawned agent can escalate non-interactively. Remove NOPASSWD sudo access for this user before starting cc-connect.\",\n\t\t\tcfg.Project, cfg.RunAsUser)\n\t\tif result.SudoListOutput != \"\" {\n\t\t\tmsg += \"\\n\\n`sudo -n -l` as \" + cfg.RunAsUser + \":\\n\" + indent(result.SudoListOutput, \"  \")\n\t\t}\n\t\tresult.Fatal = append(result.Fatal, errors.New(msg))\n\t\t// Don't return — still run check 3 so the operator gets all\n\t\t// the bad news in a single startup attempt.\n\t}\n\n\tif cfg.WorkDir == \"\" {\n\t\tresult.Warnings = append(result.Warnings, fmt.Sprintf(\n\t\t\t\"project %q: no work_dir configured; skipping filesystem access checks\", cfg.Project))\n\t} else {\n\t\tabsWorkDir := cfg.WorkDir\n\t\tif abs, err := filepath.Abs(absWorkDir); err == nil {\n\t\t\tabsWorkDir = abs\n\t\t}\n\t\tif _, err := cfg.Runner.Run(ctx, \"-n\", \"-iu\", cfg.RunAsUser, \"--\", \"test\", \"-r\", absWorkDir, \"-a\", \"-w\", absWorkDir); err != nil {\n\t\t\tresult.Fatal = append(result.Fatal, fmt.Errorf(\n\t\t\t\t\"project %q: target user %q cannot read AND write work_dir %q. Agents will fail with EACCES at runtime. Fix ownership/permissions on this directory (chown/chmod or an ACL granting the target user rwx) before starting cc-connect.\",\n\t\t\t\tcfg.Project, cfg.RunAsUser, absWorkDir))\n\t\t} else {\n\t\t\twarn := scanDescendants(ctx, cfg.Runner, cfg.RunAsUser, absWorkDir, cfg.ScanConfig)","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/runas_check.go#L96-L132","documentation":"A fatal preflight error raised when the configured run_as_user has passwordless (NOPASSWD) sudo rights. If the sandboxed agent can escalate to root non-interactively, the run_as_user sandbox provides no real isolation, so cc-connect refuses to start. The message embeds the project name, target user, and the full `sudo -n -l` output when available.","triggerScenarios":"During PreflightRunAsUser (core/runas_check.go:114), check 2 runs `sudo -n -l` as the target user and it succeeds, proving NOPASSWD escalation is possible for cfg.RunAsUser in project cfg.Project.","commonSituations":"Operator added the service user to sudoers with NOPASSWD:ALL for convenience; a cloud VM image grants the default user passwordless sudo; the sandbox user was reused from an admin role.","solutions":["Remove NOPASSWD entries for the run_as_user from /etc/sudoers (use visudo) so `sudo -n -l` fails for that user.","Restrict the user's sudoers rules to specific non-escalating commands, or remove the user from the sudo/wheel group.","Re-run `cc-connect doctor user-isolation` / restart to confirm preflight passes."],"exampleFix":"// before (/etc/sudoers)\nagent ALL=(ALL) NOPASSWD: ALL\n// after (/etc/sudoers via visudo — no entry for the sandbox user, or restricted)\n# agent user must not have NOPASSWD sudo\nagent ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp-only","handlingStrategy":"validation","validationCode":"out, err := exec.Command(\"sudo\", \"-n\", \"-l\", \"-U\", runAsUser).Output()\nif err == nil {\n    return fmt.Errorf(\"user %s still has passwordless sudo\", runAsUser)\n}","typeGuard":null,"tryCatchPattern":"result := core.PreflightRunAsUser(ctx, cfg)\nif len(result.Fatal) > 0 {\n    log.Fatalf(\"isolation misconfigured, refusing to start: %v\", result.Fatal)\n}","preventionTips":["Audit sudoers regularly: `sudo -n -l -U <user>` must fail for the sandbox user.","Provision sandbox users via automation that never grants NOPASSWD.","Run `cc-connect doctor user-isolation` as a deployment gate."],"tags":["security","sudo","isolation","privilege-escalation","preflight"],"backgroundTag":"insufficient-permissions","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}