{"record":{"id":"7061b5f1dd74b21b","repo":"chenhg5/cc-connect","slug":"preflightrunasuser-runasuser-is-empty","errorCode":null,"errorMessage":"PreflightRunAsUser: RunAsUser is empty","messagePattern":"PreflightRunAsUser: RunAsUser is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/runas_check.go","lineNumber":85,"sourceCode":"\n// PreflightRunAsUser runs all three startup safety checks for a single\n// project. It never panics and never returns nil; instead all problems are\n// accumulated into the returned PreflightResult for the caller to aggregate\n// and log.\n//\n// Checks:\n//\n//  1. Passwordless sudo -iu <target> is configured (fatal if missing).\n//  2. Target user has no passwordless sudo (fatal if they can escalate);\n//     on failure, captures `sudo -n -iu target -- sudo -n -l` output to\n//     help the operator find the offending rule.\n//  3. Target user can read AND write the work_dir root (fatal if not),\n//     plus a best-effort descendant walk producing warnings for paths\n//     the target user cannot access.\nfunc PreflightRunAsUser(ctx context.Context, cfg PreflightConfig) PreflightResult {\n\tresult := PreflightResult{Project: cfg.Project, RunAsUser: cfg.RunAsUser}\n\tif cfg.RunAsUser == \"\" {\n\t\tresult.Fatal = append(result.Fatal, errors.New(\"PreflightRunAsUser: RunAsUser is empty\"))\n\t\treturn result\n\t}\n\tif cfg.Runner == nil {\n\t\tcfg.Runner = ExecSudoRunner{}\n\t}\n\tif cfg.ScanConfig.MaxReport == 0 {\n\t\tcfg.ScanConfig = DefaultDescendantScanConfig\n\t}\n\n\tif _, err := cfg.Runner.Run(ctx, \"-n\", \"-iu\", cfg.RunAsUser, \"--\", \"/usr/bin/true\"); err != nil {\n\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","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/runas_check.go#L67-L103","documentation":"PreflightRunAsUser runs startup safety checks for the run_as_user sandbox and returns a PreflightResult. When cfg.RunAsUser is empty it appends this error to result.Fatal and returns immediately — the sandbox cannot be validated without a target user, so startup must be treated as failed. Unlike the plain probe, the error is carried inside the result's Fatal list rather than returned directly.","triggerScenarios":"PreflightRunAsUser invoked (by runDoctorOne or tests TestPreflightRunAsUser_*) with PreflightConfig.RunAsUser == \"\" — the empty check is the first gate at core/runas_check.go:85.","commonSituations":"Starting cc-connect with a project that lacks run_as_user; empty string left in config after commenting out the setting; test harnesses constructing PreflightConfig without the field.","solutions":["Set a non-empty run_as_user in the project's config.toml and restart.","Bypass preflight for projects that intentionally do not use run_as_user isolation.","Inspect result.Fatal after calling PreflightRunAsUser and abort startup when it is non-empty."],"exampleFix":"// before\nresult := core.PreflightRunAsUser(ctx, cfg)\nreturn nil\n// after\nresult := core.PreflightRunAsUser(ctx, cfg)\nif len(result.Fatal) > 0 {\n    return fmt.Errorf(\"preflight failed: %v\", result.Fatal)\n}\nreturn nil","handlingStrategy":"validation","validationCode":"if cfg.RunAsUser == \"\" {\n    return PreflightResult{Fatal: []error{errors.New(\"run_as_user not configured\")}}\n}","typeGuard":null,"tryCatchPattern":"result := core.PreflightRunAsUser(ctx, cfg)\nfor _, f := range result.Fatal {\n    slog.Error(\"preflight fatal\", \"err\", f)\n}\nif len(result.Fatal) > 0 { os.Exit(1) }","preventionTips":["Always inspect result.Fatal, not just an error return — preflight errors live in the result.","Run preflight in CI or at deploy time, not only at startup.","Keep run_as_user mandatory in project config schemas that promise isolation."],"tags":["config","sudo","isolation","preflight","startup"],"backgroundTag":"missing-required-config-field","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}