MCPcopy Index your code
hub / github.com/coder/coder / readSecretInput

Function readSecretInput

cli/cliui/prompt.go:212–263  ·  view source on GitHub ↗

readSecretInput reads secret input from the terminal rune-by-rune, masking each character with an asterisk.

(f *os.File, w io.Writer)

Source from the content-addressed store, hash-verified

210// readSecretInput reads secret input from the terminal rune-by-rune,
211// masking each character with an asterisk.
212func readSecretInput(f *os.File, w io.Writer) (string, error) {
213 // Put terminal into raw mode (no echo, no line buffering).
214 oldState, err := pty.MakeInputRaw(f.Fd())
215 if err != nil {
216 return "", err
217 }
218 defer func() {
219 _ = pty.RestoreTerminal(f.Fd(), oldState)
220 }()
221
222 reader := bufio.NewReader(f)
223 var runes []rune
224
225 for {
226 r, _, err := reader.ReadRune()
227 if err != nil {
228 return "", err
229 }
230
231 switch {
232 case r == '\r' || r == '\n':
233 // Finish on Enter
234 if _, err := fmt.Fprint(w, "\r\n"); err != nil {
235 return "", err
236 }
237 return string(runes), nil
238
239 case r == 3:
240 // Ctrl+C
241 return "", ErrCanceled
242
243 case r == 127 || r == '\b':
244 // Backspace/Delete: remove last rune
245 if len(runes) > 0 {
246 // Erase the last '*' on the screen
247 if _, err := fmt.Fprint(w, "\b \b"); err != nil {
248 return "", err
249 }
250 runes = runes[:len(runes)-1]
251 }
252
253 default:
254 // Only mask printable, non-control runes
255 if !unicode.IsControl(r) {
256 runes = append(runes, r)
257 if _, err := fmt.Fprint(w, "*"); err != nil {
258 return "", err
259 }
260 }
261 }
262 }
263}

Callers 1

PromptFunction · 0.85

Calls 3

MakeInputRawFunction · 0.92
RestoreTerminalFunction · 0.92
ReadRuneMethod · 0.45

Tested by

no test coverage detected