shadow1ng/fscan · error

credential contains line break

Error message

credential contains line break

What it means

A defensive injection guard: text-protocol credentials (STOMP, IMAP-adjacent login helpers) are scanned for CR/LF and rejected because a line break would let an attacker inject extra protocol commands (CRLF injection). rejectLineBreaks returns this plain (non-i18n) error if any supplied value contains \r or \n.

Source

Thrown at plugins/services/text_protocol.go:18

//go:build !plugin_selective || plugin_activemq || plugin_imap || plugin_pop3 || plugin_redis

package services

import (
	"fmt"
	"strconv"
	"strings"
)

func hasLineBreak(s string) bool {
	return strings.ContainsAny(s, "\r\n")
}

func rejectLineBreaks(values ...string) error {
	for _, value := range values {
		if hasLineBreak(value) {
			return fmt.Errorf("credential contains line break")
		}
	}
	return nil
}

func imapQuotedString(s string) (string, error) {
	if hasLineBreak(s) {
		return "", fmt.Errorf("imap credential contains line break")
	}
	return strconv.Quote(s), nil
}

func buildIMAPLoginCommand(tag, username, password string) (string, error) {
	quotedUser, err := imapQuotedString(username)
	if err != nil {
		return "", err
	}
	quotedPass, err := imapQuotedString(password)

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Trim CR/LF from credential values before calling the authenticate functions.
  2. Validate credentials at load time and reject any containing line breaks.
  3. Fix the source (config parser/file reader) so values are read without trailing newlines.
  4. Keep the guard in place — do not bypass it, since it prevents protocol injection.

Example fix

// before
password := strings.TrimSpace(string(data)) // may still embed internal \n
err := tryLogin(conn, user, password)
// after
if strings.ContainsAny(password, "\r\n") {
    return fmt.Errorf("credential contains line break")
}
err := tryLogin(conn, user, password)
Defensive patterns

Strategy: validation

Validate before calling

if strings.ContainsAny(username, "\r\n") || strings.ContainsAny(password, "\r\n") {
    return errors.New("credential contains line break")
}

Type guard

func safeCredential(s string) bool { return s != "" && !strings.ContainsAny(s, "\r\n") }

Try / catch

if err := rejectLineBreaks(user, pass); err != nil {
    // sanitize or reject the credential before sending to the protocol
}

Prevention

When it happens

Trigger: Calling authenticateSTOMP or tryLogin with a username or password containing '\n' or '\r', typically from untrusted input such as a config file, environment variable, or scan target list.

Common situations: Credentials read from files without trimming trailing newlines (e.g. password read via scanner including '\n'); YAML/JSON configs with embedded newlines; intentionally malicious inputs during testing.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/2cf4e1d6d9c125c1. Report an issue: GitHub.