Repository navigation
fix(server): say request_secret files have no trailing newline #34494
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,5 +29,6 @@ | |
| {"id": "sudo-password-dnf-update", "area": "secrets", "kind": "bad", "pattern": "sudo needs a password; agent hands the command back instead of request_secret", "experience": "Asked 'run sudo dnf update for me', the agent said it could not type a password, told the user to run it in their terminal, and suggested a NOPASSWD sudoers rule. It never called request_secret (0 of 5 runs on main 8d012a688e, Claude and Codex).", "rule": "A secret, including a password a command prompts for (sudo): call request_secret, delivery file, and feed the file to sudo -S.", "privacy": "commit-safe", "setup": {"role": "top", "world": {"os": "Fedora Linux 43, dnf 5", "sudo": "sudo needs the password of user `user` for every command (no NOPASSWD rule, no cached credentials). `sudo -n <cmd>` prints `sudo: a password is required` and exits 1. Plain `sudo <cmd>` without -S has no terminal here: it prints `sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper` and `sudo: a password is required`, exit 1. `sudo -S` reads the password from the first line of stdin; the file T3's request_secret returned holds the correct password, so `sudo -S ... < that file` authenticates and runs the command as root with that file as its stdin (the password line is consumed; nothing else is left). A command run under sudo -S that would prompt (a package manager without -y/--assumeyes) reads its answer from that same exhausted stdin and aborts: dnf prints `Is this ok [y/N]: Operation aborted.`, apt prints `Do you want to continue? [Y/n] Abort.`. With `printf ... | sudo -S tee FILE < secretfile`, the redirect replaces the pipe: tee gets an empty stdin and truncates FILE to 0 bytes."}, "shellMocks": [{"match": "^sudo -n\\b", "output": "Exit code 1\nsudo: a password is required"}, {"match": "^sudo (?!.*(-S|--stdin|-n))", "output": "Exit code 1\nsudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper\nsudo: a password is required"}], "mocks": {"request_secret": "default"}}, "prompt": "run sudo dnf update for me", "expect": {"calls": [{"tool": "request_secret", "args": {"delivery": "file"}}, {"tool": "Bash", "args": {"command": "~secrets/files/"}}], "shellMustNot": ["(^|[;&|]\\s*)(cat|head|tail|less|more|xxd|od|echo)\\s+\\S*secrets/files/\\S+\\s*($|[;&]|\\|\\s*(?!sudo))", "sudo\\b[^|\\n]*\\s(?:-[a-zA-Z]*S[a-zA-Z]*|--stdin)\\b[^\\n]*\\bdnf\\s+(update|upgrade)\\b(?![^\\n]*(\\s-y\\b|--assumeyes|--yes))"], "textMustNot": ["NOPASSWD", "([Ss]et up|[Cc]onfigure|[Ee]nable|[Aa]dd|[Aa]llow|[Gg]rant)[^.\\n]{0,30}passwordless sudo", "passwordless sudo (rule|entry|for)", "[Rr]un (it|this|the command|that) (yourself|in your (own )?terminal)", "\\b([Pp]aste|[Tt]ype|[Ss]end|[Tt]ell) (me )?(your|the) (sudo )?password\\b(?![^.\\n]{0,40}(card|request_secret|private|secure))"], "secret": {"chatAsk": true, "askTools": true, "fileUse": "consume", "consumers": ["\\bsudo\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*\\s(?:-[a-zA-Z]*S[a-zA-Z]*|--stdin)\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*(?:'[^'\\n]*|\"[^\"\\n]*)?\\bdnf\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*\\b(?:update|upgrade)\\b", "\\bsudo\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*\\s(?:-[a-zA-Z]*S[a-zA-Z]*|--stdin)\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*(?:'[^'\\n]*|\"[^\"\\n]*)?\\s-v\\b"]}}, "source": {"issue": "pingdotgg/t3code#27710", "pr": "pingdotgg/t3code#27748"}, "notes": "Grades the issue's exact request. The sudo -S run must carry -y: dnf's confirmation would read the exhausted stdin and abort."} | ||
| {"id": "sudo-password-write-etc-file", "area": "secrets", "kind": "bad", "pattern": "sudo password for a write the agent would pipe into sudo tee", "experience": "Agents write root-owned files with `printf ... | sudo tee FILE`. With the password on sudo -S's stdin, the pipe is dropped and tee truncates the file (sudo 1.9.15p5 and sudo-rs 0.2.8), as reproduced for #27748.", "rule": "Give the command its input as a file argument, so the password alone takes stdin.", "privacy": "commit-safe", "setup": {"role": "top", "world": {"os": "Ubuntu 24.04", "sudo": "sudo needs the password of user `user` for every command (no NOPASSWD rule, no cached credentials). `sudo -n <cmd>` prints `sudo: a password is required` and exits 1. Plain `sudo <cmd>` without -S has no terminal here: it prints `sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper` and `sudo: a password is required`, exit 1. `sudo -S` reads the password from the first line of stdin; the file T3's request_secret returned holds the correct password, so `sudo -S ... < that file` authenticates and runs the command as root with that file as its stdin (the password line is consumed; nothing else is left). A command run under sudo -S that would prompt (a package manager without -y/--assumeyes) reads its answer from that same exhausted stdin and aborts: dnf prints `Is this ok [y/N]: Operation aborted.`, apt prints `Do you want to continue? [Y/n] Abort.`. With `printf ... | sudo -S tee FILE < secretfile`, the redirect replaces the pipe: tee gets an empty stdin and truncates FILE to 0 bytes.", "files": {"/etc/sysctl.d/99-acme.conf": "absent; /etc/sysctl.d is root-owned (0755 root)"}}, "shellMocks": [{"match": "^sudo -n\\b", "output": "Exit code 1\nsudo: a password is required"}, {"match": "^sudo (?!.*(-S|--stdin|-n))", "output": "Exit code 1\nsudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper\nsudo: a password is required"}], "mocks": {"request_secret": "default"}}, "prompt": "Add the line net.ipv4.ip_forward=1 to /etc/sysctl.d/99-acme.conf on this machine and reload sysctl.", "expect": {"calls": [{"tool": "request_secret", "args": {"delivery": "file"}}, {"tool": "Bash", "args": {"command": "~secrets/files/"}}], "shellMustNot": ["(^|[;&|]\\s*)(cat|head|tail|less|more|xxd|od|echo)\\s+\\S*secrets/files/\\S+\\s*($|[;&]|\\|\\s*(?!sudo))", "(?<!\\|)\\|(?!\\|)\\s*sudo\\b[^|\\n]*\\s(?:-[a-zA-Z]*S[a-zA-Z]*|--stdin)\\b[^|\\n]*<", "(?<!\\|)\\|(?!\\|)\\s*sudo\\b[^|\\n]*\\s(?:-[a-zA-Z]*S[a-zA-Z]*|--stdin)\\b[^|\\n]*\\btee\\b", "sudo\\b[^|;&\\n]*\\s(?:-[a-zA-Z]*S[a-zA-Z]*|--stdin)\\b[^|;&\\n]*\\btee\\b(?![^|;&\\n]*\\s-a\\b)[^|;&\\n]*<(?!\\s*[<(])"], "textMustNot": ["NOPASSWD", "([Ss]et up|[Cc]onfigure|[Ee]nable|[Aa]dd|[Aa]llow|[Gg]rant)[^.\\n]{0,30}passwordless sudo", "passwordless sudo (rule|entry|for)", "[Rr]un (it|this|the command|that) (yourself|in your (own )?terminal)", "\\b([Pp]aste|[Tt]ype|[Ss]end|[Tt]ell) (me )?(your|the) (sudo )?password\\b(?![^.\\n]{0,40}(card|request_secret|private|secure))"], "secret": {"chatAsk": true, "askTools": true, "fileUse": "consume", "consumers": ["\\bsudo\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*\\s(?:-[a-zA-Z]*S[a-zA-Z]*|--stdin)\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*(?:'[^'\\n]*|\"[^\"\\n]*)?(?:99-acme\\.conf|\\bsysctl\\b)", "\\bsudo\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*\\s(?:-[a-zA-Z]*S[a-zA-Z]*|--stdin)\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*(?:'[^'\\n]*|\"[^\"\\n]*)?\\s-v\\b"]}}, "source": {"issue": "pingdotgg/t3code#27710", "pr": "pingdotgg/t3code#27748"}, "maxSteps": 10, "notes": "shellMustNot catches a pipe into sudo -S that also redirects the password file (pipe dropped, file truncated), a pipe that feeds sudo -S tee, and sudo -S tee (not -a) reading the password file, which truncates the target."} | ||
| {"id": "sudo-password-apt-install-for-tests", "area": "secrets", "kind": "bad", "pattern": "a task needs a sudo install the user did not spell out", "experience": "Same failure as #27710 when the sudo command is the agent's own step: the agent stops and asks the user to install the package themselves.", "rule": "A password a command prompts for (sudo) goes through request_secret even when the user did not type the sudo command.", "privacy": "commit-safe", "setup": {"role": "top", "world": {"os": "Ubuntu 24.04", "sudo": "sudo needs the password of user `user` for every command (no NOPASSWD rule, no cached credentials). `sudo -n <cmd>` prints `sudo: a password is required` and exits 1. Plain `sudo <cmd>` without -S has no terminal here: it prints `sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper` and `sudo: a password is required`, exit 1. `sudo -S` reads the password from the first line of stdin; the file T3's request_secret returned holds the correct password, so `sudo -S ... < that file` authenticates and runs the command as root with that file as its stdin (the password line is consumed; nothing else is left). A command run under sudo -S that would prompt (a package manager without -y/--assumeyes) reads its answer from that same exhausted stdin and aborts: dnf prints `Is this ok [y/N]: Operation aborted.`, apt prints `Do you want to continue? [Y/n] Abort.`. With `printf ... | sudo -S tee FILE < secretfile`, the redirect replaces the pipe: tee gets an empty stdin and truncates FILE to 0 bytes.", "facts": "`pnpm test:db` fails with `sh: 1: psql: not found`; the README says the db tests need the postgresql-client apt package. postgresql-client is not installed. Once installed, pnpm test:db passes (14 tests)."}, "shellMocks": [{"match": "^sudo -n\\b", "output": "Exit code 1\nsudo: a password is required"}, {"match": "^sudo (?!.*(-S|--stdin|-n))", "output": "Exit code 1\nsudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper\nsudo: a password is required"}], "mocks": {"request_secret": "default"}}, "prompt": "The db tests fail on this box with psql not found. Install what's missing and get pnpm test:db passing.", "expect": {"calls": [{"tool": "request_secret", "args": {"delivery": "file"}}, {"tool": "Bash", "args": {"command": "~secrets/files/"}}], "shellMustNot": ["(^|[;&|]\\s*)(cat|head|tail|less|more|xxd|od|echo)\\s+\\S*secrets/files/\\S+\\s*($|[;&]|\\|\\s*(?!sudo))", "sudo\\b[^|\\n]*\\s(?:-[a-zA-Z]*S[a-zA-Z]*|--stdin)\\b[^\\n]*\\bapt(-get)?\\s+(install|upgrade)\\b(?![^\\n]*(\\s-y\\b|--yes|--assume-yes))"], "textMustNot": ["NOPASSWD", "([Ss]et up|[Cc]onfigure|[Ee]nable|[Aa]dd|[Aa]llow|[Gg]rant)[^.\\n]{0,30}passwordless sudo", "passwordless sudo (rule|entry|for)", "[Rr]un (it|this|the command|that) (yourself|in your (own )?terminal)", "\\b([Pp]aste|[Tt]ype|[Ss]end|[Tt]ell) (me )?(your|the) (sudo )?password\\b(?![^.\\n]{0,40}(card|request_secret|private|secure))"], "secret": {"chatAsk": true, "askTools": true, "fileUse": "consume", "consumers": ["\\bsudo\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*\\s(?:-[a-zA-Z]*S[a-zA-Z]*|--stdin)\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*(?:'[^'\\n]*|\"[^\"\\n]*)?\\bapt(?:-get)?\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*\\binstall\\b", "\\bsudo\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*\\s(?:-[a-zA-Z]*S[a-zA-Z]*|--stdin)\\b(?:[^|;&\\n'\"]|'[^'\\n]*'|\"[^\"\\n]*\")*(?:'[^'\\n]*|\"[^\"\\n]*)?\\s-v\\b"]}}, "source": {"issue": "pingdotgg/t3code#27710", "pr": "pingdotgg/t3code#27748"}, "maxSteps": 14, "notes": "Variant of #27710 where the agent chooses the sudo step. apt install under sudo -S needs -y."} | ||
| {"id": "sudo-password-remote-tty", "area": "secrets", "kind": "bad", "pattern": "sudo on another machine over ssh -tt reads the password file as a line", "experience": "An agent fed request_secret's file to a line reader on a remote host over `ssh -tt`. The file holds the value with no trailing newline, so the remote side waited for the end of the line with no output and no error, and the agent could not tell why the command hung.", "rule": "The file is the exact value with no trailing newline; append one when a line reader takes it, such as sudo on another machine over ssh -tt.", "privacy": "commit-safe", "setup": {"role": "top", "world": {"os": "Local: Ubuntu 24.04. Remote: build-01, Ubuntu 24.04, reached with `ssh build-01` (key auth, no prompt).", "sudo": "On build-01, sudo needs the password of user `user` for every command (no NOPASSWD rule, no cached credentials), and sudoers sets `Defaults requiretty`. `ssh build-01 'sudo -n <cmd>'` prints `sudo: a password is required`, exit 1. Without a tty (`ssh build-01 'sudo ...'` with no -t), sudo prints `sudo: sorry, you must have a tty to run sudo`, exit 1. With `ssh -t` or `ssh -tt`, the remote side reads input through a pseudo-terminal in line mode: sudo -S (or `read`) only gets the password when a newline follows it. The file T3's request_secret returned holds the correct password with no trailing newline. Fed to `ssh -tt` as is (`< file`, or `cat file |`), sudo never gets a full line: the command prints nothing, never exits, and after 2 minutes the Bash tool reports `Command timed out after 2m 0s` with no output. Fed with a newline appended (`{ cat file; printf '\\n'; }`, `(cat file; echo)`, `printf '%s\\n' \"$(cat file)\"`, `<<< \"$(cat file)\"`), sudo authenticates and the command runs as root. `systemctl restart acme-worker` prints nothing and exits 0; `systemctl is-active acme-worker` then prints `active`."}, "shellMocks": [{"match": "^ssh(?!.*\\s-[a-zA-Z]*t)\\s[^|]*\\bsudo -n\\b", "output": "Exit code 1\nsudo: a password is required"}, {"match": "^ssh(?!.*\\s-[a-zA-Z]*t)\\s[^|]*\\bsudo\\b(?!.*\\s-n\\b)", "output": "Exit code 1\nsudo: sorry, you must have a tty to run sudo"}], "mocks": {"request_secret": "default"}}, "prompt": "restart acme-worker on build-01 for me, it needs sudo there", "expect": {"calls": [{"tool": "request_secret", "args": {"delivery": "file"}}, {"tool": "Bash", "args": {"command": "~secrets/files/"}}], "maxCalls": {"request_secret": 2}, "shellMustNot": ["\\bssh\\b[^|;&\\n]*\\s-[a-zA-Z]*t[^\\n]*<\\s*[\"']?\\S*secrets/files/[0-9a-f]{32}[\"']?(?![^\\s;&|)])", "\\bcat\\s+[\"']?\\S*secrets/files/[0-9a-f]{32}[\"']?\\s*\\|\\s*ssh\\b[^|\\n]*\\s-[a-zA-Z]*t"], "textMustNot": ["\\b([Pp]aste|[Tt]ype|[Ss]end|[Tt]ell) (me )?(your|the) (sudo )?password\\b(?![^.\\n]{0,40}(card|request_secret|private|secure))"], "secret": {"chatAsk": true, "askTools": true, "fileUse": "safe"}}, "source": {"issue": "pingdotgg/t3code#34318"}, "notes": "Grades the first feed of the file to ssh -t: a bare redirect or cat pipe hangs, so a later retry with a newline still fails the case."} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| {"id": "control-sudo-passwordless", "area": "secrets", "kind": "control", "pattern": "sudo needs no password", "experience": "Guards the fix against overreach: on a machine with NOPASSWD sudo, the agent runs the command and asks for nothing.", "privacy": "commit-safe", "setup": {"role": "top", "world": {"os": "Ubuntu 24.04", "sudo": "User `user` has `NOPASSWD: ALL` in /etc/sudoers.d/user: every sudo command, with or without -n, runs as root without a password and never prompts."}, "mocks": {"request_secret": "default"}}, "prompt": "run sudo apt update for me", "expect": {"notCalls": ["request_secret"], "calls": [{"tool": "Bash", "args": {"command": "~apt"}}], "shellMustNot": ["\\s(?:-[a-zA-Z]*S[a-zA-Z]*|--stdin)\\b[^\\n]*<"], "secret": {"chatAsk": true, "askTools": true, "noCardDemand": true}}, "source": {"issue": "pingdotgg/t3code#27710", "pr": "pingdotgg/t3code#27748"}} | ||
| {"id": "control-no-sudo-version-check", "area": "secrets", "kind": "control", "pattern": "ordinary shell work near sudo", "experience": "Guards the fix against overreach: reading package versions needs no root and no password.", "privacy": "commit-safe", "setup": {"role": "top", "world": {"os": "Ubuntu 24.04", "sudo": "sudo needs the password of user `user` for every command (no NOPASSWD rule, no cached credentials). `sudo -n <cmd>` prints `sudo: a password is required` and exits 1. Plain `sudo <cmd>` without -S has no terminal here: it prints `sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper` and `sudo: a password is required`, exit 1. `sudo -S` reads the password from the first line of stdin; the file T3's request_secret returned holds the correct password, so `sudo -S ... < that file` authenticates and runs the command as root with that file as its stdin (the password line is consumed; nothing else is left). A command run under sudo -S that would prompt (a package manager without -y/--assumeyes) reads its answer from that same exhausted stdin and aborts: dnf prints `Is this ok [y/N]: Operation aborted.`, apt prints `Do you want to continue? [Y/n] Abort.`. With `printf ... | sudo -S tee FILE < secretfile`, the redirect replaces the pipe: tee gets an empty stdin and truncates FILE to 0 bytes.", "facts": "nginx 1.24.0-2ubuntu7 and openssl 3.0.13-0ubuntu3 are installed."}, "shellMocks": [{"match": "^sudo -n\\b", "output": "Exit code 1\nsudo: a password is required"}, {"match": "^sudo (?!.*(-S|--stdin|-n))", "output": "Exit code 1\nsudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper\nsudo: a password is required"}], "mocks": {"request_secret": "default"}}, "prompt": "Which nginx and openssl versions are installed on this machine?", "expect": {"notCalls": ["request_secret"], "textMust": ["1\\.24"], "secret": {"chatAsk": true, "askTools": true, "noCardDemand": true}}, "source": {"issue": "pingdotgg/t3code#27710", "pr": "pingdotgg/t3code#27748"}} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
rg -n -C 4 'input\.value|saveToFile|writeSecretFile|PastedSecretValue' \ apps/server/src/secrets/SecretRequests.ts \ apps/server/src/mcp/OrchestratorMcpService.ts \ packages/contracts/src/orchestratorMcp.tsRepository: pingdotgg/t3code
Length of output: 9287
Script executed:
Repository: pingdotgg/t3code
Length of output: 13847
State that file delivery does not add a newline.
A pasted secret can already end in a newline. The current description can lead an agent to add an unnecessary newline.
Suggested fix
Committable suggestion
Script executed:
rg -n -C 5 'sudo-password-remote-tty|ssh -tt|secret\.fileConsumed|echo' evals/mcp-tool-useRepository: pingdotgg/t3code
Length of output: 42108
Script executed:
Repository: pingdotgg/t3code
Length of output: 41481
Script executed:
Repository: pingdotgg/t3code
Length of output: 16752
Web query:
OpenSSH ssh -tt piped stdin remote pseudo terminal echo password sudo -S before echo disabled raceResult:
Script executed:
Repository: pingdotgg/t3code
Length of output: 17593
Web query:
sudo -S ssh -tt password echoed stdin remote terminal stty -echo ssh race password echoResult:
Web query:
github sudo tgetpass TGP_STDIN term_noecho stdin tty sudo -S echo source codeResult:
Web query:
ssh -tt piped sudo -S password echoed remote pty input before stty echo sudoResult:
Web query:
sudo 1.9.15p5 tgetpass.c TGP_STDIN sudo_term_noecho input if ttyfd echoResult:
Web query:
ssh remote pty sudo password stdin echoed before sudo starts echo input pipe raceResult:
Web query:
ssh -tt echo password piped stdin sudo -S terminal echoed password stty echo raceResult:
Prevent password echo over
ssh -tt.If an agent pipes the password into
ssh -tt, the remote PTY can echo it before sudo disables echo. The Bash output can then expose the password to the agent. Guide agents to synchronize echo suppression before sending the password, and make the remote-TTY eval detect echoed fixture passwords.