Guard IMAP Command

IMAP command-line validator (RFC 9051 IMAP4rev2; obsoletes RFC 3501). Gates every command-line the framework's inbound IMAP listener accepts from peers — CAPABILITY / NOOP / LOGOUT / STARTTLS / AUTHENTICATE / LOGIN / ENABLE / SELECT / EXAMINE / CREATE / DELETE / RENAME / SUBSCRIBE / UNSUBSCRIBE / LIST / NAMESPACE / STATUS / APPEND / IDLE / CHECK / CLOSE / UNSELECT / EXPUNGE / SEARCH / FETCH / STORE / COPY / MOVE / UID / GETQUOTA / SETQUOTA / GETQUOTAROOT / ID.

## Smuggling defense — bare-CR / bare-LF refusal

Same wire-protocol smuggling class as SMTP: implementations that accept bare-CR or bare-LF in a command line let a hostile peer inject a second command past a per-line filter. RFC 9051 §2.2.1 requires CRLF only; this validator refuses every bare CR / bare LF / NUL / C0 / DEL byte outside of explicit literal blocks (which the wire-protocol reader has already framed before handing the line to this validator).

## Literal-injection defense

IMAP carries inline length-prefixed literals: {n}. Per RFC 9051 §2.2.2 the literal opener {n} MUST appear at the end of a command line, with the n bytes following on subsequent line(s). RFC 7888 LITERAL+ relaxes the round-trip but is only honored post-AUTH. The validator detects literal openers as either:

Per-literal byte cap defaults to 64 MiB (operator opts down via maxLiteralBytes); the LISTENER then enforces the post-literal read against this cap.

## Mailbox-name traversal

Mailbox names per RFC 9051 §5.1 — UTF-8 hierarchy with the server-chosen delimiter (typically / or .). Refuses path- traversal (..), NUL bytes, control chars, leading/trailing slash, overlong UTF-8 sequences, and (under strict) modified- UTF7 (RFC 3501 §5.1.3 legacy encoding — operators with legacy MUAs opt in via allowLegacyMUtf7).

## Per-verb shape

Each command verb has a fixed argument shape per RFC 9051 §6. LOGIN user pass takes exactly two atoms or strings. SELECT takes one mailbox name. FETCH takes a sequence-set + a parts list. Refusals under strict use guard-imap-command/bad-shape.

## Caps

- Command line (tag + verb + arguments excluding literal payload) capped at 8 KiB. RFC 9051 does not mandate a line cap but most servers limit at 8 KiB or 16 KiB to bound memory; operators on permissive can extend. - Mailbox name capped at 1 KiB. - Sequence set element count capped at 10,000 per command. - SEARCH expression nesting (AND/OR/NOT) capped at 32 levels. - Per-literal byte cap (64 MiB default).

Throws GuardImapCommandError on every refusal. Pure-functional — no I/O, no state. The IMAP listener composes one instance per accepted connection.

b.guardImapCommand.validate(line, opts?) #

stable0.9.49
{
  profile:   "strict" | "balanced" | "permissive",
  posture:   "hipaa" | "pci-dss" | "gdpr" | "soc2",
  authenticated: boolean,    // when true, LITERAL+ (RFC 7888) is honored under
                               strict; pre-AUTH literal+ is refused per RFC 7888 §1
}

Validate a single IMAP command line (without its CRLF terminator — the listener strips that before calling this). Returns { tag, verb, args, literalSize, literalNonSync } on success; throws GuardImapCommandError on any refusal. literalSize is the pending-literal byte count when the line ends in {n}; null otherwise. literalNonSync is true for RFC 7888 LITERAL+ ({n+}).

var parsed = b.guardImapCommand.validate("A001 LOGIN alice secret");
// → { tag: "A001", verb: "LOGIN", args: ["alice", "secret"], literalSize: null, literalNonSync: false }

var pending = b.guardImapCommand.validate("A002 APPEND INBOX {1024}");
// → { tag: "A002", verb: "APPEND", args: ["INBOX"], literalSize: 1024, literalNonSync: false }

b.guardImapCommand.detectLiteralSmuggling(line) #

stable0.9.49

Return true when the input line contains a literal opener {n} or {n+} that is NOT at the end of the line — the smuggling-shape per RFC 9051 §2.2.2.

b.guardImapCommand.detectLiteralSmuggling("A001 APPEND INBOX {10} hostile");  // → true
b.guardImapCommand.detectLiteralSmuggling("A001 APPEND INBOX {10}");          // → false (well-formed)

b.guardImapCommand.compliancePosture(name) #

stable0.9.49hipaapci-dssgdprsoc2

Return the effective profile NAME for a compliance posture, or null for a name this parser does not map. Unlike the content-guard variant this returns the resolved profile string (every line-protocol parser composes gateContract.ALL_STRICT_POSTURES, so "hipaa" / "pci-dss" / "gdpr" / "soc2" all resolve to "strict") and never throws — the parser shape carries no overlay-clone, no buildProfile, and no loadRulePack. Wired by gateContract.defineParser.

b.guardImapCommand.compliancePosture("hipaa");                   // → "strict"
b.guardImapCommand.compliancePosture("not-a-regime");            // → null

Last updated 2026-08-08T16:39:49.652Z by seeder.