lima-vm/lima · error

ssh option %#q contains a line break

Error message

ssh option %#q contains a line break

What it means

Format() renders Lima's SSH options either as a command line (FormatCmd) or as an ssh_config block (FormatConfig). Before writing anything it scans every option string for CR/LF, because a line break inside an option value would inject extra ssh_config directives (e.g. a malicious ProxyCommand) into the generated config. It refuses the whole operation rather than produce a config vulnerable to injection.

Source

Thrown at pkg/sshutil/format.go:70

// Formats is the list of the supported formats.
var Formats = []FormatT{FormatCmd, FormatArgs, FormatOptions, FormatConfig}

func quoteOption(o string) string {
	// make sure the shell doesn't swallow quotes in option values
	if strings.ContainsRune(o, '"') {
		o = "'" + o + "'"
	}
	return o
}

// Format formats the ssh options.
func Format(w io.Writer, sshPath, instName string, format FormatT, opts []string) error {
	fakeHostname := hostname.FromInstName(instName) // TODO: support customization
	for _, o := range opts {
		// A line break in an option value (e.g. a crafted user.name) would inject
		// extra ssh_config directives such as ProxyCommand into the generated config.
		if strings.ContainsAny(o, "\r\n") {
			return fmt.Errorf("ssh option %#q contains a line break", o)
		}
	}
	switch format {
	case FormatCmd:
		args := []string{sshPath}
		for _, o := range opts {
			args = append(args, "-o", quoteOption(o))
		}
		args = append(args, fakeHostname)
		// the args are similar to `limactl shell` but not exactly same. (e.g., lacks -t)
		fmt.Fprintln(w, strings.Join(args, " ")) // no need to use shellescape.QuoteCommand
	case FormatArgs:
		var args []string
		for _, o := range opts {
			args = append(args, "-o", quoteOption(o))
		}
		fmt.Fprintln(w, strings.Join(args, " ")) // no need to use shellescape.QuoteCommand
	case FormatOptions:

View on GitHub (pinned to dd909d0973)

Solutions

  1. Find which option contains the line break (it is echoed in the error) and strip or reject it before calling Format: strings.ReplaceAll(o, "\n", " ").
  2. Validate upstream inputs (user.name, identities, extra ssh options in the instance config) for control characters at load time.
  3. If the value legitimately needs a newline, encode it differently (e.g. base64 or a config file reference) instead of passing it as an ssh option.

Example fix

// before
opts := []string{"IdentityFile=" + identity}
sshutil.Format(w, sshPath, instName, sshutil.FormatConfig, opts)
// after
identity = strings.ReplaceAll(identity, "\r", "")
identity = strings.ReplaceAll(identity, "\n", " ")
sshutil.Format(w, sshPath, instName, sshutil.FormatConfig, opts)
Defensive patterns

Strategy: validation

Validate before calling

func hasLineBreak(opts []string) bool {
    for _, o := range opts {
        if strings.ContainsAny(o, "\r\n") {
            return true
        }
    }
    return false
}
if hasLineBreak(opts) { /* sanitize or reject before Format */ }

Type guard

func safeOption(o string) bool { return !strings.ContainsAny(o, "\r\n") }

Try / catch

if err := sshutil.Format(w, sshPath, instName, format, opts); err != nil {
    if strings.Contains(err.Error(), "contains a line break") {
        // sanitize opts and retry
    }
}

Prevention

When it happens

Trigger: Calling sshutil.Format (directly or via showSSHAction / writeSSHConfigFile) with an opts slice where any element contains '\n' or '\r', e.g. a crafted user.name or other value sourced from user input or the instance YAML.

Common situations: User-supplied values with embedded newlines pulled from lima.yaml, environment data, or cloud-init fields being passed into SSH option generation; attempted config-injection payloads.

Related errors


AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01). Data as JSON: /api/errors/7cd9a81f120c2c53. Report an issue: GitHub.