larksuite/cli · error

emlbuilder: header value contains dangerous Unicode characte

Error message

emlbuilder: header value contains dangerous Unicode character: %q

What it means

The EML builder rejected a header value because it contains a Unicode code point deemed dangerous for RFC 5322 headers (e.g. bidirectional-override or zero-width characters used in spoofing). validateHeaderValue scans every rune of the value and fails fast so a hostile or copy-pasted header can never reach the serialized .eml output. The mail command layer wraps this into a typed ValidationError.

Source

Thrown at shortcuts/mail/emlbuilder/builder.go:143

	isOtherPart bool   // true = no Content-Disposition (AddOtherPart); false = Content-Disposition: inline
}

// New returns an empty Builder.
func New() Builder {
	return Builder{}
}

// validateHeaderValue rejects strings that contain characters unsafe in MIME
// header values: C0 control chars (except \t for folded headers), DEL (0x7F),
// and dangerous Unicode (Bidi overrides, zero-width chars) that enable
// visual-spoofing attacks.
func validateHeaderValue(v string) error {
	for _, r := range v {
		if r != '\t' && (r < 0x20 || r == 0x7f) {
			return fmt.Errorf("emlbuilder: header value contains control character: %q", v) //nolint:forbidigo // intermediate EML builder error; mail command layer wraps into typed ValidationError.
		}
		if isHeaderDangerousUnicode(r) {
			return fmt.Errorf("emlbuilder: header value contains dangerous Unicode character: %q", v) //nolint:forbidigo // intermediate EML builder error; mail command layer wraps into typed ValidationError.
		}
	}
	return nil
}

// isHeaderDangerousUnicode identifies Unicode code points used for visual
// spoofing: Bidi overrides that reverse display order, and zero-width characters
// that hide content.  These must not appear in email header values.
func isHeaderDangerousUnicode(r rune) bool {
	switch {
	case r >= 0x200B && r <= 0x200D: // zero-width space/non-joiner/joiner
		return true
	case r == 0xFEFF: // BOM / zero-width no-break space
		return true
	case r >= 0x202A && r <= 0x202E: // Bidi: LRE/RLE/PDF/LRO/RLO
		return true
	case r >= 0x2028 && r <= 0x2029: // line/paragraph separator
		return true

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Inspect the header value and remove dangerous Unicode characters (RTL overrides U+202A-U+202E, zero-width U+200B-U+200F, etc.).
  2. Sanitize user-supplied header text before passing it to the builder, stripping or replacing dangerous code points.
  3. If RTL display is genuinely needed, use explicit language/HTML body formatting instead of Unicode control characters in the header value.

Example fix

// before
b.Subject("‮lname@bank.com‬ payment")
// after
b.Subject("payment reminder") // dangerous bidi-override characters removed
Defensive patterns

Strategy: validation

Validate before calling

var dangerousUnicode = regexp.MustCompile(`[\x{202A}-\x{202E}\x{2066}-\x{2069}\x{200B}-\x{200F}\x{FEFF}]`)
func safeHeaderValue(v string) bool { return !dangerousUnicode.MatchString(v) }
// call before b.Subject(v) / b.MessageID(v) etc.

Try / catch

var raw []byte
err := b.Subject(userSubject)
if err != nil {
    var verr *ValidationError
    if errors.As(err, &verr) {
        return fmt.Errorf("subject rejected: %w", verr)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Subject, MessageID, InReplyTo, References, DispositionNotificationTo, or LMSReplyToMessageID with a string containing a dangerous Unicode rune (checked by isHeaderDangerousUnicode), e.g. U+202E RIGHT-TO-LEFT OVERRIDE or zero-width characters.

Common situations: Pasting a subject copied from a chat or document that contains invisible/zero-width characters; building localized subjects with RTL overrides for Arabic/Hebrew text; user-supplied subject text from a web form containing spoofing characters.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/27a96c9042ca41ae. Report an issue: GitHub.