Guard ManageSieve Command
ManageSieve command-line validator (RFC 5804 — "A Protocol for Remotely Managing Sieve Scripts"). Gates every verb the framework's ManageSieve listener accepts from peers — AUTHENTICATE / STARTTLS / LOGOUT / CAPABILITY / HAVESPACE / PUTSCRIPT / LISTSCRIPTS / SETACTIVE / GETSCRIPT / DELETESCRIPT / RENAMESCRIPT / NOOP.
ManageSieve is a line-oriented text protocol that mixes simple single-line commands with literal-syntax payloads ({N} / {N+}) carrying script bytes verbatim. Responses are OK ... / NO ... / BYE ... per RFC 5804 §1.2.
## Smuggling defense — bare-CR / bare-LF refusal
Same wire-protocol concern as SMTP / IMAP / POP3. Command lines MUST be canonical CRLF-terminated; bare-CR or bare-LF inside a command (outside the literal-payload window) is refused. The listener clears the receive buffer at STARTTLS upgrade to defend the same pre-handshake injection class that affected STARTTLS in Exim (CVE-2021-38371) / Dovecot (CVE-2021-33515) / Postfix (CVE-2011-0411).
## Cleartext-AUTH refusal under strict
RFC 5804 §1.1 + RFC 4954 §4 — AUTHENTICATE with credential- bearing mechanisms (PLAIN / LOGIN) over a cleartext channel exposes the password to passive observation. Strict + balanced refuse AUTHENTICATE PLAIN / AUTHENTICATE LOGIN pre-TLS; the listener composes this gate at the validate boundary AND re-checks at the dispatch boundary as defense-in-depth.
AUTHENTICATE EXTERNAL (RFC 4422 §4) is exempt — the credential is the TLS client certificate already presented, so cleartext is not the concern. AUTHENTICATE SCRAM-SHA-256 (RFC 7677) and AUTHENTICATE OAUTHBEARER (RFC 7628) are mechanism-side credential-protected and may run pre-TLS under permissive; strict still requires TLS (defense-in-depth + active-MITM resistance).
## Script-name shape (RFC 5804 §2.1)
Script names are UTF-8 strings of 1-512 octets containing no NUL (0x00), CR (0x0D), LF (0x0A), forward-slash (0x2F), backslash (0x5C), or double-quote (0x22). The forward-slash + backslash refusal blocks path-traversal-style storage-backend collisions; the NUL/CR/LF refusal blocks wire-protocol smuggling.
## Literal syntax (RFC 5804 §2.3 + RFC 7888 LITERAL+)
PUTSCRIPT name {N} / PUTSCRIPT name {N+} introduces an N-byte script payload. The bare {N} form is synchronizing (server replies with a continuation request before the client sends the payload); {N+} (RFC 7888) is non-synchronizing. The validator refuses N values above the per-profile script-byte cap (matching b.safeSieve's maxScriptBytes: 64 KiB strict / 256 KiB balanced / 1 MiB permissive).
## Per-verb shape
RFC 5804 §2.1-§2.10:
- AUTHENTICATE "STARTTLS — no args - LOGOUT — no args - CAPABILITY — no args - NOOP [string] — optional echo-tag arg - HAVESPACE "PUTSCRIPT "LISTSCRIPTS — no args - SETACTIVE "GETSCRIPT "DELETESCRIPT "RENAMESCRIPT "
## Caps
- Per-line cap (excluding the literal payload itself): 8 KiB strict / 16 KiB balanced / 64 KiB permissive. ManageSieve's command lines are LONGER than POP3/IMAP because script names may carry UTF-8 + the literal-payload announcement. - Script-byte cap (literal {N} value): same as b.safeSieve.PROFILES. — 64 KiB / 256 KiB / 1 MiB. - Script name: RFC 5804 §2.1 1-512 octets.
Throws GuardManageSieveCommandError on every refusal.
b.guardManageSieveCommand.validate(line, opts?) #
{
profile: "strict" | "balanced" | "permissive",
posture: "hipaa" | "pci-dss" | "gdpr" | "soc2",
tls: boolean, // when false + AUTHENTICATE PLAIN/LOGIN
under strict, refuse with
`guard-managesieve-command/cleartext-auth`
(RFC 4954 §4 + RFC 5804 §1.1)
}
Validate a single ManageSieve command line (without its CRLF terminator, and without the literal-script payload that may follow). Returns a shape describing the parsed verb + arguments + (when applicable) the trailing literal-byte count the listener must read from the wire. Throws GuardManageSieveCommandError on refusal.
var p = b.guardManageSieveCommand.validate('PUTSCRIPT "myscript" {52+}', { tls: true });
// → { verb: "PUTSCRIPT", args: ["myscript"], literalBytes: 52, literalPlus: true }
var c = b.guardManageSieveCommand.validate("CAPABILITY", { tls: true });
// → { verb: "CAPABILITY", args: [] }
b.guardManageSieveCommand.compliancePosture(name) #
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.guardManageSieveCommand.compliancePosture("hipaa"); // → "strict"
b.guardManageSieveCommand.compliancePosture("not-a-regime"); // → null
Last updated 2026-08-08T16:39:49.652Z by seeder.