Guard List-Id
RFC 2919 List-Id header validator. Companion to b.guardListUnsubscribe; gates the outbound submission path so mailing-list mail carries a well-formed list identifier that downstream mail-client filters + bulk-sender pipelines can reliably route on.
## RFC 2919 §2 ABNF
list-id = list-label "." list-id-namespace
list-label = dot-atom-text (RFC 5322)
list-id-namespace = domain-name / "localhost"
Headers MAY surround the identifier in angle brackets and prepend a phrase + comment:
List-Id: My Newsletter <my-newsletter.example.com>
List-Id: (Comment text) <list-12345.example.com>
This validator parses both bare-identifier and bracketed forms, refusing the address-list-injection class.
## Defenses
- **Length cap** — RFC 2919 §3 caps the list identifier at 255 octets. Total header value capped at 998 bytes per RFC 5322 §2.1.1 line cap. - **CRLF + control-char refusal** — header-injection defense (CVE-2026-32178 — .NET CWE-138 header-injection spoofing, the System.Net.Mail vector per MSRC, on the wire-protocol surface; this primitive's job is the SEMANTIC shape). - **Phrase-injection refusal** — Operator-supplied display phrase mustn't carry CRLF / < / > outside the angle brackets (a separate Bcc/Cc header smuggled into the phrase fails the parse). - **Domain shape** — dot-atom-text per RFC 5322 §3.2.3; LDH labels per RFC 5321 §2.3.5; at least one . separator (rejects bare mylist claims). - **localhost namespace** — RFC 2919 §3 permits, but operator MUST also carry the recommended 32-hex random component when using localhost. Strict refuses unmanaged identifiers missing the randomness suffix (SHOULD semantics).
## CVE / threat model
- **List-Id forging** — RFC 2919 §8 explicitly notes the identifier is NOT an authentication signal; this primitive refuses the SHAPE-injection class (mailing-list pipelines that crash or mis-route on malformed List-Id). Operators wanting authentication compose b.mail.auth.dmarc.evaluate / b.mail.auth.arc.verify on top. - **Bulk-sender bucket-drop** — Gmail's 2024 bulk-sender requirements key on List-Id presence for messages with Precedence: list or 5000+ daily sends; malformed List-Id drops the message into spam. This primitive surfaces the refuse-at-submit verdict so operators see the issue at send-time, not at delivery.
b.guardListId.validate(headerValue, opts?) #
{
profile: "strict" | "balanced" | "permissive",
posture: "hipaa" | "pci-dss" | "gdpr" | "soc2",
}
Validate an RFC 2919 List-Id header value. Accepts:
(bracketed bare-identifier form)My Newsletter(phrase + bracketed)my-list.example.com(bare, no brackets — RFC 2919 allows)
Returns { action, listId, namespace, phrase?, reason }. Action one of "accept" / "refuse".
var v = b.guardListId.validate("My Newsletter ");
if (v.action === "accept") emit("List-Id: " + v.raw);
b.guardListId.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.guardListId.compliancePosture("hipaa"); // → "strict"
b.guardListId.compliancePosture("not-a-regime"); // → null
Last updated 2026-08-08T16:39:49.652Z by seeder.