mymx 0.3.6 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { EmailAddress, EmailReceivedEvent, ParsedDataComplete, ParsedDataFailed, ParsedError, RawContentDownloadOnly, RawContentInline, WEBHOOK_VERSION$1 as WEBHOOK_VERSION, WebhookAttachment } from "./types-DJTmrgCz.js";
1
+ import { EmailAddress, EmailReceivedEvent, ParsedDataComplete, ParsedDataFailed, ParsedError, RawContentDownloadOnly, RawContentInline, WEBHOOK_VERSION$1 as WEBHOOK_VERSION, WebhookAttachment } from "./types-C8JlcpcT.js";
2
2
  import { SignResult, signWebhookPayload$1 as signWebhookPayload } from "./signing-Ecohrukk.js";
3
3
 
4
4
  //#region src/contract.d.ts
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { EmailAddress, EmailReceivedEvent, KnownWebhookEvent, ParsedData, ParsedDataComplete, ParsedDataFailed, ParsedError, ParsedStatus, RawContent, RawContentDownloadOnly, RawContentInline, UnknownEvent, WEBHOOK_VERSION$1 as WEBHOOK_VERSION, WebhookAttachment, WebhookEvent } from "./types-DJTmrgCz.js";
1
+ import { AuthConfidence, AuthVerdict, DkimSignature, DkimSignatureResult, DmarcPolicy, DmarcResult, EmailAddress, EmailAuth, EmailReceivedEvent, KnownWebhookEvent, ParsedData, ParsedDataComplete, ParsedDataFailed, ParsedError, ParsedStatus, RawContent, RawContentDownloadOnly, RawContentInline, SpfResult, UnknownEvent, ValidateEmailAuthResult, WEBHOOK_VERSION$1 as WEBHOOK_VERSION, WebhookAttachment, WebhookEvent } from "./types-C8JlcpcT.js";
2
2
  import { MYMX_CONFIRMED_HEADER$1 as MYMX_CONFIRMED_HEADER, MYMX_SIGNATURE_HEADER$1 as MYMX_SIGNATURE_HEADER, VerifyOptions, verifyWebhookSignature$1 as verifyWebhookSignature } from "./signing-Ecohrukk.js";
3
3
  import { MyMXWebhookError$1 as MyMXWebhookError, PAYLOAD_ERRORS$1 as PAYLOAD_ERRORS, RAW_EMAIL_ERRORS$1 as RAW_EMAIL_ERRORS, RawEmailDecodeError$1 as RawEmailDecodeError, RawEmailDecodeErrorCode, VERIFICATION_ERRORS$1 as VERIFICATION_ERRORS, WebhookErrorCode, WebhookPayloadError$1 as WebhookPayloadError, WebhookPayloadErrorCode, WebhookValidationError$1 as WebhookValidationError, WebhookValidationErrorCode, WebhookVerificationError$1 as WebhookVerificationError, WebhookVerificationErrorCode } from "./errors-CSPHzZB_.js";
4
4
 
@@ -171,6 +171,10 @@ declare const emailReceivedEventJsonSchema: {
171
171
  readonly additionalProperties: false;
172
172
  readonly description: "Email analysis and classification results. May be absent if analysis was not performed.";
173
173
  };
174
+ readonly auth: {
175
+ readonly $ref: "#/definitions/EmailAuth";
176
+ readonly description: "Email authentication results (SPF, DKIM, DMARC). May be absent if authentication was not performed.";
177
+ };
174
178
  };
175
179
  readonly required: ["id", "received_at", "smtp", "headers", "content", "parsed"];
176
180
  readonly additionalProperties: false;
@@ -483,10 +487,170 @@ declare const emailReceivedEventJsonSchema: {
483
487
  readonly additionalProperties: false;
484
488
  readonly description: "Error details when email parsing fails.";
485
489
  };
490
+ readonly EmailAuth: {
491
+ readonly type: "object";
492
+ readonly properties: {
493
+ readonly spf: {
494
+ readonly $ref: "#/definitions/SpfResult";
495
+ readonly description: "SPF verification result.\n\nSPF checks if the sending IP is authorized by the envelope sender's domain. \"pass\" means the IP is authorized; \"fail\" means it's explicitly not allowed.";
496
+ };
497
+ readonly dmarc: {
498
+ readonly $ref: "#/definitions/DmarcResult";
499
+ readonly description: "DMARC verification result.\n\nDMARC passes if either SPF or DKIM passes AND aligns with the From: domain. \"pass\" means the email is authenticated according to the sender's policy.";
500
+ };
501
+ readonly dmarcPolicy: {
502
+ readonly $ref: "#/definitions/DmarcPolicy";
503
+ readonly description: "DMARC policy from the sender's DNS record.\n\n- `reject`: Domain wants receivers to reject failing emails\n- `quarantine`: Domain wants failing emails marked as suspicious\n- `none`: Domain is monitoring only (no action requested)\n- `null`: No DMARC record found for this domain";
504
+ };
505
+ readonly dmarcFromDomain: {
506
+ readonly type: ["string", "null"];
507
+ readonly description: "The organizational domain used for DMARC lookups.\n\nFor example, if the From: address is `user@mail.example.com`, the DMARC lookup checks `_dmarc.mail.example.com`, then falls back to `_dmarc.example.com`. This field shows which domain's policy was used.";
508
+ };
509
+ readonly dmarcSpfAligned: {
510
+ readonly type: "boolean";
511
+ readonly description: "Whether SPF aligned with the From: domain for DMARC purposes.\n\nTrue if the envelope sender domain matches the From: domain (per alignment mode).";
512
+ };
513
+ readonly dmarcDkimAligned: {
514
+ readonly type: "boolean";
515
+ readonly description: "Whether DKIM aligned with the From: domain for DMARC purposes.\n\nTrue if at least one DKIM signature's domain matches the From: domain.";
516
+ };
517
+ readonly dmarcSpfStrict: {
518
+ readonly type: ["boolean", "null"];
519
+ readonly description: "Whether DMARC SPF alignment mode is strict.\n\n- `true`: Strict alignment required (exact domain match)\n- `false`: Relaxed alignment allowed (organizational domain match)\n- `null`: No DMARC record found";
520
+ };
521
+ readonly dmarcDkimStrict: {
522
+ readonly type: ["boolean", "null"];
523
+ readonly description: "Whether DMARC DKIM alignment mode is strict.\n\n- `true`: Strict alignment required (exact domain match)\n- `false`: Relaxed alignment allowed (organizational domain match)\n- `null`: No DMARC record found";
524
+ };
525
+ readonly dkimSignatures: {
526
+ readonly type: "array";
527
+ readonly items: {
528
+ readonly $ref: "#/definitions/DkimSignature";
529
+ };
530
+ readonly description: "All DKIM signatures found in the email with their verification results.\n\nMay be empty if no DKIM signatures were present.";
531
+ };
532
+ };
533
+ readonly required: ["spf", "dmarc", "dmarcPolicy", "dmarcFromDomain", "dmarcSpfAligned", "dmarcDkimAligned", "dmarcSpfStrict", "dmarcDkimStrict", "dkimSignatures"];
534
+ readonly additionalProperties: false;
535
+ readonly description: "Email authentication results for SPF, DKIM, and DMARC.\n\nUse `validateEmailAuth()` to compute a verdict based on these results.";
536
+ };
537
+ readonly SpfResult: {
538
+ readonly type: "string";
539
+ readonly enum: ["pass", "fail", "softfail", "neutral", "none", "temperror", "permerror"];
540
+ readonly description: "SPF verification result.";
541
+ };
542
+ readonly DmarcResult: {
543
+ readonly type: "string";
544
+ readonly enum: ["pass", "fail", "none", "temperror", "permerror"];
545
+ readonly description: "DMARC verification result.";
546
+ };
547
+ readonly DmarcPolicy: {
548
+ readonly type: ["string", "null"];
549
+ readonly enum: ["reject", "quarantine", "none", null];
550
+ readonly description: "DMARC policy action specified in the domain's DMARC record.\n\n- `reject`: The domain owner requests that receivers reject failing emails\n- `quarantine`: The domain owner requests that failing emails be treated as suspicious\n- `none`: The domain owner is only monitoring (no action requested)\n- `null`: No DMARC policy was found for the domain";
551
+ };
552
+ readonly DkimSignature: {
553
+ readonly type: "object";
554
+ readonly properties: {
555
+ readonly domain: {
556
+ readonly type: "string";
557
+ readonly description: "The domain that signed this DKIM signature (d= tag). This may differ from the From: domain (that's what alignment checks).";
558
+ };
559
+ readonly selector: {
560
+ readonly type: "string";
561
+ readonly description: "The DKIM selector used to locate the public key (s= tag). Combined with the domain to form the DNS lookup: `selector._domainkey.domain`";
562
+ };
563
+ readonly result: {
564
+ readonly $ref: "#/definitions/DkimSignatureResult";
565
+ readonly description: "Verification result for this specific signature.";
566
+ };
567
+ readonly aligned: {
568
+ readonly type: "boolean";
569
+ readonly description: "Whether this signature's domain aligns with the From: domain (for DMARC).\n\nAlignment can be \"strict\" (exact match) or \"relaxed\" (organizational domain match). For example, if From: is `user@sub.example.com` and DKIM is signed by `example.com`:\n- Relaxed alignment: true (same organizational domain)\n- Strict alignment: false (not exact match)";
570
+ };
571
+ readonly keyBits: {
572
+ readonly type: ["number", "null"];
573
+ readonly description: "Key size in bits (e.g., 1024, 2048). Null if the key size couldn't be determined.";
574
+ };
575
+ readonly algo: {
576
+ readonly type: "string";
577
+ readonly description: "Signing algorithm (e.g., \"rsa-sha256\", \"ed25519-sha256\").";
578
+ };
579
+ };
580
+ readonly required: ["domain", "selector", "result", "aligned", "keyBits", "algo"];
581
+ readonly additionalProperties: false;
582
+ readonly description: "Details about a single DKIM signature found in the email.\n\nAn email may have multiple DKIM signatures (e.g., one from the sending domain and one from the ESP). Each signature is verified independently.";
583
+ };
584
+ readonly DkimSignatureResult: {
585
+ readonly type: "string";
586
+ readonly enum: ["pass", "fail", "temperror", "permerror"];
587
+ readonly description: "DKIM signature verification result for a single signature.";
588
+ };
486
589
  };
487
590
  }; //#endregion
488
- //#region src/index.d.ts
591
+ //#region src/auth.d.ts
489
592
 
593
+ /**
594
+ * Validate email authentication and compute a verdict.
595
+ *
596
+ * This function analyzes SPF, DKIM, and DMARC results to determine
597
+ * whether an email is likely authentic ("legit"), potentially spoofed
598
+ * ("suspicious"), or indeterminate ("unknown").
599
+ *
600
+ * ## Verdict Logic
601
+ *
602
+ * **Legit (high confidence):**
603
+ * - DMARC pass with DKIM alignment (cryptographic proof of authenticity)
604
+ *
605
+ * **Legit (medium confidence):**
606
+ * - DMARC pass with SPF alignment only (no DKIM)
607
+ * - Note: SPF can break through forwarding, but DMARC pass is still meaningful
608
+ *
609
+ * **Suspicious (high confidence):**
610
+ * - DMARC fail when domain has `reject` or `quarantine` policy
611
+ * - The domain owner explicitly says to distrust failing emails
612
+ * - SPF explicitly fails (IP not authorized by sender)
613
+ *
614
+ * **Suspicious (low confidence):**
615
+ * - DMARC fail when domain has `none` policy (monitoring mode)
616
+ * - No DMARC record but SPF/DKIM fail
617
+ *
618
+ * **Unknown:**
619
+ * - No DMARC record and no clear pass/fail
620
+ * - Temporary errors during authentication
621
+ * - No authentication data available
622
+ *
623
+ * @param auth - Email authentication results from the webhook
624
+ * @returns Verdict, confidence level, and explanatory reasons
625
+ *
626
+ * @example
627
+ * ```typescript
628
+ * const result = validateEmailAuth({
629
+ * spf: 'pass',
630
+ * dmarc: 'pass',
631
+ * dmarcPolicy: 'reject',
632
+ * dmarcFromDomain: 'example.com',
633
+ * dmarcSpfAligned: true,
634
+ * dmarcDkimAligned: true,
635
+ * dmarcSpfStrict: false,
636
+ * dmarcDkimStrict: false,
637
+ * dkimSignatures: [{
638
+ * domain: 'example.com',
639
+ * selector: 'default',
640
+ * result: 'pass',
641
+ * aligned: true,
642
+ * keyBits: 2048,
643
+ * algo: 'rsa-sha256',
644
+ * }],
645
+ * });
646
+ *
647
+ * // result.verdict === 'legit'
648
+ * // result.confidence === 'high'
649
+ * // result.reasons === ['DMARC passed with DKIM alignment']
650
+ * ```
651
+ */
652
+ declare function validateEmailAuth(auth: EmailAuth): ValidateEmailAuthResult; //#endregion
653
+ //#region src/index.d.ts
490
654
  /**
491
655
  * Parse a webhook payload, returning typed events for known types
492
656
  * and UnknownEvent for future event types.
@@ -760,5 +924,7 @@ declare function decodeRawEmail(event: EmailReceivedEvent, options?: DecodeRawEm
760
924
  * }
761
925
  * ```
762
926
  */
763
- declare function verifyRawEmailDownload(downloaded: Buffer | ArrayBuffer | Uint8Array, event: EmailReceivedEvent): Buffer; //#endregion
764
- export { DecodeRawEmailOptions, EmailAddress, EmailReceivedEvent, HandleWebhookOptions, KnownWebhookEvent, MYMX_CONFIRMED_HEADER, MYMX_SIGNATURE_HEADER, MyMXWebhookError, PAYLOAD_ERRORS, ParsedData, ParsedDataComplete, ParsedDataFailed, ParsedError, ParsedStatus, RAW_EMAIL_ERRORS, RawContent, RawContentDownloadOnly, RawContentInline, RawEmailDecodeError, RawEmailDecodeErrorCode, UnknownEvent, VERIFICATION_ERRORS, VerifyOptions, WEBHOOK_VERSION, WebhookAttachment, WebhookErrorCode, WebhookEvent, WebhookHeaders, WebhookPayloadError, WebhookPayloadErrorCode, WebhookValidationError, WebhookValidationErrorCode, WebhookVerificationError, WebhookVerificationErrorCode, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, getDownloadTimeRemaining, handleWebhook, isDownloadExpired, isEmailReceivedEvent, isRawIncluded, parseWebhookEvent, verifyRawEmailDownload, verifyWebhookSignature };
927
+ declare function verifyRawEmailDownload(downloaded: Buffer | ArrayBuffer | Uint8Array, event: EmailReceivedEvent): Buffer;
928
+
929
+ //#endregion
930
+ export { AuthConfidence, AuthVerdict, DecodeRawEmailOptions, DkimSignature, DkimSignatureResult, DmarcPolicy, DmarcResult, EmailAddress, EmailAuth, EmailReceivedEvent, HandleWebhookOptions, KnownWebhookEvent, MYMX_CONFIRMED_HEADER, MYMX_SIGNATURE_HEADER, MyMXWebhookError, PAYLOAD_ERRORS, ParsedData, ParsedDataComplete, ParsedDataFailed, ParsedError, ParsedStatus, RAW_EMAIL_ERRORS, RawContent, RawContentDownloadOnly, RawContentInline, RawEmailDecodeError, RawEmailDecodeErrorCode, SpfResult, UnknownEvent, VERIFICATION_ERRORS, ValidateEmailAuthResult, VerifyOptions, WEBHOOK_VERSION, WebhookAttachment, WebhookErrorCode, WebhookEvent, WebhookHeaders, WebhookPayloadError, WebhookPayloadErrorCode, WebhookValidationError, WebhookValidationErrorCode, WebhookVerificationError, WebhookVerificationErrorCode, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, getDownloadTimeRemaining, handleWebhook, isDownloadExpired, isEmailReceivedEvent, isRawIncluded, parseWebhookEvent, validateEmailAuth, verifyRawEmailDownload, verifyWebhookSignature };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { MyMXWebhookError, PAYLOAD_ERRORS, RAW_EMAIL_ERRORS, RawEmailDecodeError, VERIFICATION_ERRORS, WEBHOOK_VERSION, WebhookPayloadError, WebhookValidationError, WebhookVerificationError } from "./errors-2CwICC_t.js";
2
2
  import { MYMX_CONFIRMED_HEADER, MYMX_SIGNATURE_HEADER, bufferToString, verifyWebhookSignature } from "./signing-kfnmqAcK.js";
3
- import { validateEmailReceivedEvent } from "./zod-Ced9Kav9.js";
3
+ import { validateEmailReceivedEvent } from "./zod-CalKEwR4.js";
4
4
  import { createHash } from "node:crypto";
5
5
 
6
6
  //#region src/schema.generated.ts
@@ -167,6 +167,10 @@ const emailReceivedEventJsonSchema = {
167
167
  "required": ["spamassassin"],
168
168
  "additionalProperties": false,
169
169
  "description": "Email analysis and classification results. May be absent if analysis was not performed."
170
+ },
171
+ "auth": {
172
+ "$ref": "#/definitions/EmailAuth",
173
+ "description": "Email authentication results (SPF, DKIM, DMARC). May be absent if authentication was not performed."
170
174
  }
171
175
  },
172
176
  "required": [
@@ -508,10 +512,345 @@ const emailReceivedEventJsonSchema = {
508
512
  ],
509
513
  "additionalProperties": false,
510
514
  "description": "Error details when email parsing fails."
515
+ },
516
+ "EmailAuth": {
517
+ "type": "object",
518
+ "properties": {
519
+ "spf": {
520
+ "$ref": "#/definitions/SpfResult",
521
+ "description": "SPF verification result.\n\nSPF checks if the sending IP is authorized by the envelope sender's domain. \"pass\" means the IP is authorized; \"fail\" means it's explicitly not allowed."
522
+ },
523
+ "dmarc": {
524
+ "$ref": "#/definitions/DmarcResult",
525
+ "description": "DMARC verification result.\n\nDMARC passes if either SPF or DKIM passes AND aligns with the From: domain. \"pass\" means the email is authenticated according to the sender's policy."
526
+ },
527
+ "dmarcPolicy": {
528
+ "$ref": "#/definitions/DmarcPolicy",
529
+ "description": "DMARC policy from the sender's DNS record.\n\n- `reject`: Domain wants receivers to reject failing emails\n- `quarantine`: Domain wants failing emails marked as suspicious\n- `none`: Domain is monitoring only (no action requested)\n- `null`: No DMARC record found for this domain"
530
+ },
531
+ "dmarcFromDomain": {
532
+ "type": ["string", "null"],
533
+ "description": "The organizational domain used for DMARC lookups.\n\nFor example, if the From: address is `user@mail.example.com`, the DMARC lookup checks `_dmarc.mail.example.com`, then falls back to `_dmarc.example.com`. This field shows which domain's policy was used."
534
+ },
535
+ "dmarcSpfAligned": {
536
+ "type": "boolean",
537
+ "description": "Whether SPF aligned with the From: domain for DMARC purposes.\n\nTrue if the envelope sender domain matches the From: domain (per alignment mode)."
538
+ },
539
+ "dmarcDkimAligned": {
540
+ "type": "boolean",
541
+ "description": "Whether DKIM aligned with the From: domain for DMARC purposes.\n\nTrue if at least one DKIM signature's domain matches the From: domain."
542
+ },
543
+ "dmarcSpfStrict": {
544
+ "type": ["boolean", "null"],
545
+ "description": "Whether DMARC SPF alignment mode is strict.\n\n- `true`: Strict alignment required (exact domain match)\n- `false`: Relaxed alignment allowed (organizational domain match)\n- `null`: No DMARC record found"
546
+ },
547
+ "dmarcDkimStrict": {
548
+ "type": ["boolean", "null"],
549
+ "description": "Whether DMARC DKIM alignment mode is strict.\n\n- `true`: Strict alignment required (exact domain match)\n- `false`: Relaxed alignment allowed (organizational domain match)\n- `null`: No DMARC record found"
550
+ },
551
+ "dkimSignatures": {
552
+ "type": "array",
553
+ "items": { "$ref": "#/definitions/DkimSignature" },
554
+ "description": "All DKIM signatures found in the email with their verification results.\n\nMay be empty if no DKIM signatures were present."
555
+ }
556
+ },
557
+ "required": [
558
+ "spf",
559
+ "dmarc",
560
+ "dmarcPolicy",
561
+ "dmarcFromDomain",
562
+ "dmarcSpfAligned",
563
+ "dmarcDkimAligned",
564
+ "dmarcSpfStrict",
565
+ "dmarcDkimStrict",
566
+ "dkimSignatures"
567
+ ],
568
+ "additionalProperties": false,
569
+ "description": "Email authentication results for SPF, DKIM, and DMARC.\n\nUse `validateEmailAuth()` to compute a verdict based on these results."
570
+ },
571
+ "SpfResult": {
572
+ "type": "string",
573
+ "enum": [
574
+ "pass",
575
+ "fail",
576
+ "softfail",
577
+ "neutral",
578
+ "none",
579
+ "temperror",
580
+ "permerror"
581
+ ],
582
+ "description": "SPF verification result."
583
+ },
584
+ "DmarcResult": {
585
+ "type": "string",
586
+ "enum": [
587
+ "pass",
588
+ "fail",
589
+ "none",
590
+ "temperror",
591
+ "permerror"
592
+ ],
593
+ "description": "DMARC verification result."
594
+ },
595
+ "DmarcPolicy": {
596
+ "type": ["string", "null"],
597
+ "enum": [
598
+ "reject",
599
+ "quarantine",
600
+ "none",
601
+ null
602
+ ],
603
+ "description": "DMARC policy action specified in the domain's DMARC record.\n\n- `reject`: The domain owner requests that receivers reject failing emails\n- `quarantine`: The domain owner requests that failing emails be treated as suspicious\n- `none`: The domain owner is only monitoring (no action requested)\n- `null`: No DMARC policy was found for the domain"
604
+ },
605
+ "DkimSignature": {
606
+ "type": "object",
607
+ "properties": {
608
+ "domain": {
609
+ "type": "string",
610
+ "description": "The domain that signed this DKIM signature (d= tag). This may differ from the From: domain (that's what alignment checks)."
611
+ },
612
+ "selector": {
613
+ "type": "string",
614
+ "description": "The DKIM selector used to locate the public key (s= tag). Combined with the domain to form the DNS lookup: `selector._domainkey.domain`"
615
+ },
616
+ "result": {
617
+ "$ref": "#/definitions/DkimSignatureResult",
618
+ "description": "Verification result for this specific signature."
619
+ },
620
+ "aligned": {
621
+ "type": "boolean",
622
+ "description": "Whether this signature's domain aligns with the From: domain (for DMARC).\n\nAlignment can be \"strict\" (exact match) or \"relaxed\" (organizational domain match). For example, if From: is `user@sub.example.com` and DKIM is signed by `example.com`:\n- Relaxed alignment: true (same organizational domain)\n- Strict alignment: false (not exact match)"
623
+ },
624
+ "keyBits": {
625
+ "type": ["number", "null"],
626
+ "description": "Key size in bits (e.g., 1024, 2048). Null if the key size couldn't be determined."
627
+ },
628
+ "algo": {
629
+ "type": "string",
630
+ "description": "Signing algorithm (e.g., \"rsa-sha256\", \"ed25519-sha256\")."
631
+ }
632
+ },
633
+ "required": [
634
+ "domain",
635
+ "selector",
636
+ "result",
637
+ "aligned",
638
+ "keyBits",
639
+ "algo"
640
+ ],
641
+ "additionalProperties": false,
642
+ "description": "Details about a single DKIM signature found in the email.\n\nAn email may have multiple DKIM signatures (e.g., one from the sending domain and one from the ESP). Each signature is verified independently."
643
+ },
644
+ "DkimSignatureResult": {
645
+ "type": "string",
646
+ "enum": [
647
+ "pass",
648
+ "fail",
649
+ "temperror",
650
+ "permerror"
651
+ ],
652
+ "description": "DKIM signature verification result for a single signature."
511
653
  }
512
654
  }
513
655
  };
514
656
 
657
+ //#endregion
658
+ //#region src/auth.ts
659
+ /**
660
+ * Minimum DKIM key size considered acceptable.
661
+ *
662
+ * 1024-bit RSA keys are cryptographically weak by modern standards (NIST
663
+ * deprecated them in 2013), but they remain extremely common in email due to:
664
+ * - DNS TXT record size limits (255 bytes per string)
665
+ * - Legacy infrastructure constraints
666
+ * - Major ESPs like Amazon SES and Resend still use 1024-bit keys
667
+ *
668
+ * We flag keys <1024 bits as weak (these are truly dangerous), while accepting
669
+ * ≥1024 bits to avoid false positives against legitimate senders. For maximum
670
+ * security, domain owners should use 2048+ bit keys where possible.
671
+ */
672
+ const MIN_SECURE_KEY_BITS = 1024;
673
+ /**
674
+ * Validate email authentication and compute a verdict.
675
+ *
676
+ * This function analyzes SPF, DKIM, and DMARC results to determine
677
+ * whether an email is likely authentic ("legit"), potentially spoofed
678
+ * ("suspicious"), or indeterminate ("unknown").
679
+ *
680
+ * ## Verdict Logic
681
+ *
682
+ * **Legit (high confidence):**
683
+ * - DMARC pass with DKIM alignment (cryptographic proof of authenticity)
684
+ *
685
+ * **Legit (medium confidence):**
686
+ * - DMARC pass with SPF alignment only (no DKIM)
687
+ * - Note: SPF can break through forwarding, but DMARC pass is still meaningful
688
+ *
689
+ * **Suspicious (high confidence):**
690
+ * - DMARC fail when domain has `reject` or `quarantine` policy
691
+ * - The domain owner explicitly says to distrust failing emails
692
+ * - SPF explicitly fails (IP not authorized by sender)
693
+ *
694
+ * **Suspicious (low confidence):**
695
+ * - DMARC fail when domain has `none` policy (monitoring mode)
696
+ * - No DMARC record but SPF/DKIM fail
697
+ *
698
+ * **Unknown:**
699
+ * - No DMARC record and no clear pass/fail
700
+ * - Temporary errors during authentication
701
+ * - No authentication data available
702
+ *
703
+ * @param auth - Email authentication results from the webhook
704
+ * @returns Verdict, confidence level, and explanatory reasons
705
+ *
706
+ * @example
707
+ * ```typescript
708
+ * const result = validateEmailAuth({
709
+ * spf: 'pass',
710
+ * dmarc: 'pass',
711
+ * dmarcPolicy: 'reject',
712
+ * dmarcFromDomain: 'example.com',
713
+ * dmarcSpfAligned: true,
714
+ * dmarcDkimAligned: true,
715
+ * dmarcSpfStrict: false,
716
+ * dmarcDkimStrict: false,
717
+ * dkimSignatures: [{
718
+ * domain: 'example.com',
719
+ * selector: 'default',
720
+ * result: 'pass',
721
+ * aligned: true,
722
+ * keyBits: 2048,
723
+ * algo: 'rsa-sha256',
724
+ * }],
725
+ * });
726
+ *
727
+ * // result.verdict === 'legit'
728
+ * // result.confidence === 'high'
729
+ * // result.reasons === ['DMARC passed with DKIM alignment']
730
+ * ```
731
+ */
732
+ function validateEmailAuth(auth) {
733
+ const reasons = [];
734
+ let verdict;
735
+ let confidence;
736
+ if (auth.dmarc === "temperror" || auth.dmarc === "permerror") return {
737
+ verdict: "unknown",
738
+ confidence: "low",
739
+ reasons: [`DMARC verification error (${auth.dmarc})`, "Cannot determine email authenticity due to DNS or policy errors"]
740
+ };
741
+ if (auth.spf === "temperror" || auth.spf === "permerror") reasons.push(`SPF verification error (${auth.spf})`);
742
+ const weakKeySignatures = auth.dkimSignatures.filter((sig) => sig.keyBits !== null && sig.keyBits < MIN_SECURE_KEY_BITS);
743
+ if (weakKeySignatures.length > 0) for (const sig of weakKeySignatures) reasons.push(`Weak DKIM key (${sig.keyBits} bits) for ${sig.domain} - minimum ${MIN_SECURE_KEY_BITS} bits recommended`);
744
+ if (auth.dmarc === "pass") {
745
+ const alignedSigs = auth.dkimSignatures.filter((sig) => sig.result === "pass" && sig.aligned);
746
+ if (auth.dmarcDkimAligned && alignedSigs.length > 0) {
747
+ const domains = alignedSigs.map((sig) => sig.domain).join(", ");
748
+ reasons.unshift(`DMARC passed with DKIM alignment (${domains})`);
749
+ verdict = "legit";
750
+ confidence = weakKeySignatures.length > 0 ? "medium" : "high";
751
+ return {
752
+ verdict,
753
+ confidence,
754
+ reasons
755
+ };
756
+ }
757
+ if (auth.dmarcSpfAligned && auth.spf === "pass") {
758
+ reasons.unshift("DMARC passed with SPF alignment");
759
+ reasons.push("No aligned DKIM signature (SPF can break through forwarding)");
760
+ return {
761
+ verdict: "legit",
762
+ confidence: "medium",
763
+ reasons
764
+ };
765
+ }
766
+ reasons.unshift("DMARC passed");
767
+ return {
768
+ verdict: "legit",
769
+ confidence: "medium",
770
+ reasons
771
+ };
772
+ }
773
+ if (auth.dmarc === "fail") {
774
+ if (auth.dmarcPolicy === "reject") {
775
+ reasons.unshift("DMARC failed and domain has reject policy");
776
+ reasons.push("The sender's domain explicitly rejects emails that fail authentication");
777
+ return {
778
+ verdict: "suspicious",
779
+ confidence: "high",
780
+ reasons
781
+ };
782
+ }
783
+ if (auth.dmarcPolicy === "quarantine") {
784
+ reasons.unshift("DMARC failed and domain has quarantine policy");
785
+ reasons.push("The sender's domain marks failing emails as suspicious");
786
+ return {
787
+ verdict: "suspicious",
788
+ confidence: "high",
789
+ reasons
790
+ };
791
+ }
792
+ reasons.unshift("DMARC failed (domain is in monitoring mode)");
793
+ if (auth.spf === "fail") {
794
+ reasons.push("SPF failed - sending IP not authorized");
795
+ return {
796
+ verdict: "suspicious",
797
+ confidence: "medium",
798
+ reasons
799
+ };
800
+ }
801
+ return {
802
+ verdict: "suspicious",
803
+ confidence: "low",
804
+ reasons
805
+ };
806
+ }
807
+ if (auth.dmarc === "none") {
808
+ if (auth.spf === "fail") {
809
+ reasons.push("No DMARC record for sender domain");
810
+ reasons.push("SPF failed - sending IP not authorized");
811
+ return {
812
+ verdict: "suspicious",
813
+ confidence: "medium",
814
+ reasons
815
+ };
816
+ }
817
+ const passingDkim = auth.dkimSignatures.filter((sig) => sig.result === "pass");
818
+ if (passingDkim.length > 0) {
819
+ const domains = passingDkim.map((sig) => sig.domain).join(", ");
820
+ reasons.push("No DMARC record for sender domain");
821
+ reasons.push(`DKIM verified for: ${domains}`);
822
+ if (auth.spf === "pass") reasons.push("SPF passed");
823
+ return {
824
+ verdict: "unknown",
825
+ confidence: "low",
826
+ reasons
827
+ };
828
+ }
829
+ if (auth.spf === "pass") {
830
+ reasons.push("No DMARC record for sender domain");
831
+ reasons.push("No DKIM signatures present");
832
+ reasons.push("SPF passed (but SPF alone is weak authentication)");
833
+ return {
834
+ verdict: "unknown",
835
+ confidence: "low",
836
+ reasons
837
+ };
838
+ }
839
+ reasons.push("No DMARC record for sender domain");
840
+ reasons.push("No valid authentication found");
841
+ return {
842
+ verdict: "unknown",
843
+ confidence: "low",
844
+ reasons
845
+ };
846
+ }
847
+ return {
848
+ verdict: "unknown",
849
+ confidence: "low",
850
+ reasons: ["Unable to determine email authenticity"]
851
+ };
852
+ }
853
+
515
854
  //#endregion
516
855
  //#region src/parsing.ts
517
856
  /**
@@ -844,4 +1183,4 @@ function verifyRawEmailDownload(downloaded, event) {
844
1183
  }
845
1184
 
846
1185
  //#endregion
847
- export { MYMX_CONFIRMED_HEADER, MYMX_SIGNATURE_HEADER, MyMXWebhookError, PAYLOAD_ERRORS, RAW_EMAIL_ERRORS, RawEmailDecodeError, VERIFICATION_ERRORS, WEBHOOK_VERSION, WebhookPayloadError, WebhookValidationError, WebhookVerificationError, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, getDownloadTimeRemaining, handleWebhook, isDownloadExpired, isEmailReceivedEvent, isRawIncluded, parseWebhookEvent, verifyRawEmailDownload, verifyWebhookSignature };
1186
+ export { MYMX_CONFIRMED_HEADER, MYMX_SIGNATURE_HEADER, MyMXWebhookError, PAYLOAD_ERRORS, RAW_EMAIL_ERRORS, RawEmailDecodeError, VERIFICATION_ERRORS, WEBHOOK_VERSION, WebhookPayloadError, WebhookValidationError, WebhookVerificationError, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, getDownloadTimeRemaining, handleWebhook, isDownloadExpired, isEmailReceivedEvent, isRawIncluded, parseWebhookEvent, validateEmailAuth, verifyRawEmailDownload, verifyWebhookSignature };
@@ -466,8 +466,218 @@ interface EmailReceivedEvent {
466
466
  score: number;
467
467
  };
468
468
  };
469
+ /**
470
+ * Email authentication results (SPF, DKIM, DMARC).
471
+ * May be absent if authentication was not performed.
472
+ */
473
+ auth?: EmailAuth;
469
474
  };
470
475
  }
476
+ /**
477
+ * SPF verification result.
478
+ *
479
+ * @see https://datatracker.ietf.org/doc/html/rfc7208
480
+ */
481
+ type SpfResult = "pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror";
482
+ /**
483
+ * DMARC verification result.
484
+ *
485
+ * @see https://datatracker.ietf.org/doc/html/rfc7489
486
+ */
487
+ type DmarcResult = "pass" | "fail" | "none" | "temperror" | "permerror";
488
+ /**
489
+ * DMARC policy action specified in the domain's DMARC record.
490
+ *
491
+ * - `reject`: The domain owner requests that receivers reject failing emails
492
+ * - `quarantine`: The domain owner requests that failing emails be treated as suspicious
493
+ * - `none`: The domain owner is only monitoring (no action requested)
494
+ * - `null`: No DMARC policy was found for the domain
495
+ */
496
+ type DmarcPolicy = "reject" | "quarantine" | "none" | null;
497
+ /**
498
+ * DKIM signature verification result for a single signature.
499
+ */
500
+ type DkimSignatureResult = "pass" | "fail" | "temperror" | "permerror";
501
+ /**
502
+ * Details about a single DKIM signature found in the email.
503
+ *
504
+ * An email may have multiple DKIM signatures (e.g., one from the sending domain
505
+ * and one from the ESP). Each signature is verified independently.
506
+ */
507
+ interface DkimSignature {
508
+ /**
509
+ * The domain that signed this DKIM signature (d= tag).
510
+ * This may differ from the From: domain (that's what alignment checks).
511
+ */
512
+ domain: string;
513
+ /**
514
+ * The DKIM selector used to locate the public key (s= tag).
515
+ * Combined with the domain to form the DNS lookup: `selector._domainkey.domain`
516
+ */
517
+ selector: string;
518
+ /**
519
+ * Verification result for this specific signature.
520
+ */
521
+ result: DkimSignatureResult;
522
+ /**
523
+ * Whether this signature's domain aligns with the From: domain (for DMARC).
524
+ *
525
+ * Alignment can be "strict" (exact match) or "relaxed" (organizational domain match).
526
+ * For example, if From: is `user@sub.example.com` and DKIM is signed by `example.com`:
527
+ * - Relaxed alignment: true (same organizational domain)
528
+ * - Strict alignment: false (not exact match)
529
+ */
530
+ aligned: boolean;
531
+ /**
532
+ * Key size in bits (e.g., 1024, 2048).
533
+ * Null if the key size couldn't be determined.
534
+ */
535
+ keyBits: number | null;
536
+ /**
537
+ * Signing algorithm (e.g., "rsa-sha256", "ed25519-sha256").
538
+ */
539
+ algo: string;
540
+ }
541
+ /**
542
+ * Email authentication results for SPF, DKIM, and DMARC.
543
+ *
544
+ * Use `validateEmailAuth()` to compute a verdict based on these results.
545
+ *
546
+ * @example
547
+ * ```typescript
548
+ * import { handleWebhook, validateEmailAuth } from 'mymx';
549
+ *
550
+ * const event = handleWebhook({ body, headers, secret });
551
+ *
552
+ * if (event.email.auth) {
553
+ * const result = validateEmailAuth(event.email.auth);
554
+ * if (result.verdict === 'legit') {
555
+ * // Email passed authentication
556
+ * }
557
+ * }
558
+ * ```
559
+ */
560
+ interface EmailAuth {
561
+ /**
562
+ * SPF verification result.
563
+ *
564
+ * SPF checks if the sending IP is authorized by the envelope sender's domain.
565
+ * "pass" means the IP is authorized; "fail" means it's explicitly not allowed.
566
+ */
567
+ spf: SpfResult;
568
+ /**
569
+ * DMARC verification result.
570
+ *
571
+ * DMARC passes if either SPF or DKIM passes AND aligns with the From: domain.
572
+ * "pass" means the email is authenticated according to the sender's policy.
573
+ */
574
+ dmarc: DmarcResult;
575
+ /**
576
+ * DMARC policy from the sender's DNS record.
577
+ *
578
+ * - `reject`: Domain wants receivers to reject failing emails
579
+ * - `quarantine`: Domain wants failing emails marked as suspicious
580
+ * - `none`: Domain is monitoring only (no action requested)
581
+ * - `null`: No DMARC record found for this domain
582
+ */
583
+ dmarcPolicy: DmarcPolicy;
584
+ /**
585
+ * The organizational domain used for DMARC lookups.
586
+ *
587
+ * For example, if the From: address is `user@mail.example.com`, the DMARC
588
+ * lookup checks `_dmarc.mail.example.com`, then falls back to `_dmarc.example.com`.
589
+ * This field shows which domain's policy was used.
590
+ */
591
+ dmarcFromDomain: string | null;
592
+ /**
593
+ * Whether SPF aligned with the From: domain for DMARC purposes.
594
+ *
595
+ * True if the envelope sender domain matches the From: domain (per alignment mode).
596
+ */
597
+ dmarcSpfAligned: boolean;
598
+ /**
599
+ * Whether DKIM aligned with the From: domain for DMARC purposes.
600
+ *
601
+ * True if at least one DKIM signature's domain matches the From: domain.
602
+ */
603
+ dmarcDkimAligned: boolean;
604
+ /**
605
+ * Whether DMARC SPF alignment mode is strict.
606
+ *
607
+ * - `true`: Strict alignment required (exact domain match)
608
+ * - `false`: Relaxed alignment allowed (organizational domain match)
609
+ * - `null`: No DMARC record found
610
+ */
611
+ dmarcSpfStrict: boolean | null;
612
+ /**
613
+ * Whether DMARC DKIM alignment mode is strict.
614
+ *
615
+ * - `true`: Strict alignment required (exact domain match)
616
+ * - `false`: Relaxed alignment allowed (organizational domain match)
617
+ * - `null`: No DMARC record found
618
+ */
619
+ dmarcDkimStrict: boolean | null;
620
+ /**
621
+ * All DKIM signatures found in the email with their verification results.
622
+ *
623
+ * May be empty if no DKIM signatures were present.
624
+ */
625
+ dkimSignatures: DkimSignature[];
626
+ }
627
+ /**
628
+ * Overall verdict from email authentication validation.
629
+ *
630
+ * - `legit`: Strong evidence the email is authentic
631
+ * - `suspicious`: Evidence suggests the email may be spoofed
632
+ * - `unknown`: Not enough information to determine authenticity
633
+ */
634
+ type AuthVerdict = "legit" | "suspicious" | "unknown";
635
+ /**
636
+ * Confidence level for the authentication verdict.
637
+ *
638
+ * - `high`: Strong cryptographic evidence (DKIM aligned + DMARC pass)
639
+ * - `medium`: Good evidence but with caveats (SPF-only alignment)
640
+ * - `low`: Weak evidence (missing authentication or unclear results)
641
+ */
642
+ type AuthConfidence = "high" | "medium" | "low";
643
+ /**
644
+ * Result of validating email authentication.
645
+ *
646
+ * @example
647
+ * ```typescript
648
+ * const result = validateEmailAuth(event.email.auth);
649
+ *
650
+ * switch (result.verdict) {
651
+ * case 'legit':
652
+ * console.log('Email is authenticated');
653
+ * break;
654
+ * case 'suspicious':
655
+ * console.log('Email may be spoofed:', result.reasons.join(', '));
656
+ * break;
657
+ * case 'unknown':
658
+ * console.log('Cannot verify authenticity');
659
+ * break;
660
+ * }
661
+ * ```
662
+ */
663
+ interface ValidateEmailAuthResult {
664
+ /**
665
+ * Overall authentication verdict.
666
+ */
667
+ verdict: AuthVerdict;
668
+ /**
669
+ * Confidence level for this verdict.
670
+ */
671
+ confidence: AuthConfidence;
672
+ /**
673
+ * Human-readable reasons explaining the verdict.
674
+ *
675
+ * For `legit`: what authentication checks passed
676
+ * For `suspicious`: what failed or is concerning
677
+ * For `unknown`: why we couldn't determine authenticity
678
+ */
679
+ reasons: string[];
680
+ }
471
681
  /**
472
682
  * Represents a webhook event type not yet supported by this SDK version.
473
683
  *
@@ -517,4 +727,4 @@ type WebhookEvent = KnownWebhookEvent | UnknownEvent;
517
727
  * Possible values for the `status` field in {@link ParsedData}.
518
728
  */
519
729
  type ParsedStatus = "complete" | "failed"; //#endregion
520
- export { EmailAddress, EmailReceivedEvent, KnownWebhookEvent, ParsedData, ParsedDataComplete, ParsedDataFailed, ParsedError, ParsedStatus, RawContent, RawContentDownloadOnly, RawContentInline, UnknownEvent, WEBHOOK_VERSION as WEBHOOK_VERSION$1, WebhookAttachment, WebhookEvent };
730
+ export { AuthConfidence, AuthVerdict, DkimSignature, DkimSignatureResult, DmarcPolicy, DmarcResult, EmailAddress, EmailAuth, EmailReceivedEvent, KnownWebhookEvent, ParsedData, ParsedDataComplete, ParsedDataFailed, ParsedError, ParsedStatus, RawContent, RawContentDownloadOnly, RawContentInline, SpfResult, UnknownEvent, ValidateEmailAuthResult, WEBHOOK_VERSION as WEBHOOK_VERSION$1, WebhookAttachment, WebhookEvent };
@@ -73,6 +73,52 @@ const rawContentDownloadOnlySchema = z.object({
73
73
  sha256: sha256Schema
74
74
  });
75
75
  const rawContentSchema = z.discriminatedUnion("included", [rawContentInlineSchema, rawContentDownloadOnlySchema]);
76
+ const spfResultSchema = z.enum([
77
+ "pass",
78
+ "fail",
79
+ "softfail",
80
+ "neutral",
81
+ "none",
82
+ "temperror",
83
+ "permerror"
84
+ ]);
85
+ const dmarcResultSchema = z.enum([
86
+ "pass",
87
+ "fail",
88
+ "none",
89
+ "temperror",
90
+ "permerror"
91
+ ]);
92
+ const dmarcPolicySchema = z.enum([
93
+ "reject",
94
+ "quarantine",
95
+ "none"
96
+ ]).nullable();
97
+ const dkimSignatureResultSchema = z.enum([
98
+ "pass",
99
+ "fail",
100
+ "temperror",
101
+ "permerror"
102
+ ]);
103
+ const dkimSignatureSchema = z.object({
104
+ domain: z.string(),
105
+ selector: z.string(),
106
+ result: dkimSignatureResultSchema,
107
+ aligned: z.boolean(),
108
+ keyBits: z.number().int().positive().max(16384).nullable(),
109
+ algo: z.string()
110
+ });
111
+ const emailAuthSchema = z.object({
112
+ spf: spfResultSchema,
113
+ dmarc: dmarcResultSchema,
114
+ dmarcPolicy: dmarcPolicySchema,
115
+ dmarcFromDomain: z.string().nullable(),
116
+ dmarcSpfAligned: z.boolean(),
117
+ dmarcDkimAligned: z.boolean(),
118
+ dmarcSpfStrict: z.boolean().nullable(),
119
+ dmarcDkimStrict: z.boolean().nullable(),
120
+ dkimSignatures: z.array(dkimSignatureSchema)
121
+ });
76
122
  const emailReceivedEventSchema = z.object({
77
123
  id: z.string(),
78
124
  event: z.literal("email.received"),
@@ -105,7 +151,8 @@ const emailReceivedEventSchema = z.object({
105
151
  })
106
152
  }),
107
153
  parsed: parsedDataSchema,
108
- analysis: z.object({ spamassassin: z.object({ score: z.number() }) }).optional()
154
+ analysis: z.object({ spamassassin: z.object({ score: z.number() }) }).optional(),
155
+ auth: emailAuthSchema.optional()
109
156
  })
110
157
  });
111
158
  /**
@@ -160,6 +207,22 @@ const FIELD_DESCRIPTIONS = {
160
207
  "email.analysis": "email analysis results",
161
208
  "email.analysis.spamassassin": "SpamAssassin analysis",
162
209
  "email.analysis.spamassassin.score": "spam score",
210
+ "email.auth": "email authentication results",
211
+ "email.auth.spf": "SPF verification result",
212
+ "email.auth.dmarc": "DMARC verification result",
213
+ "email.auth.dmarcPolicy": "DMARC policy",
214
+ "email.auth.dmarcFromDomain": "DMARC organizational domain",
215
+ "email.auth.dmarcSpfAligned": "SPF DMARC alignment",
216
+ "email.auth.dmarcDkimAligned": "DKIM DMARC alignment",
217
+ "email.auth.dmarcSpfStrict": "SPF alignment mode",
218
+ "email.auth.dmarcDkimStrict": "DKIM alignment mode",
219
+ "email.auth.dkimSignatures": "DKIM signature list",
220
+ "email.auth.dkimSignatures.*.domain": "DKIM signing domain",
221
+ "email.auth.dkimSignatures.*.selector": "DKIM selector",
222
+ "email.auth.dkimSignatures.*.result": "DKIM verification result",
223
+ "email.auth.dkimSignatures.*.aligned": "DKIM alignment status",
224
+ "email.auth.dkimSignatures.*.keyBits": "DKIM key size",
225
+ "email.auth.dkimSignatures.*.algo": "DKIM signing algorithm",
163
226
  "email.parsed.attachments.*.filename": "attachment filename",
164
227
  "email.parsed.attachments.*.content_type": "attachment content type",
165
228
  "email.parsed.attachments.*.size_bytes": "attachment size",
@@ -309,4 +372,4 @@ function safeValidateEmailReceivedEvent(input) {
309
372
  }
310
373
 
311
374
  //#endregion
312
- export { emailAddressSchema, emailReceivedEventSchema, parsedDataCompleteSchema, parsedDataFailedSchema, parsedDataSchema, parsedErrorSchema, rawContentDownloadOnlySchema, rawContentInlineSchema, rawContentSchema, safeValidateEmailReceivedEvent, validateEmailReceivedEvent, webhookAttachmentSchema };
375
+ export { dkimSignatureResultSchema, dkimSignatureSchema, dmarcPolicySchema, dmarcResultSchema, emailAddressSchema, emailAuthSchema, emailReceivedEventSchema, parsedDataCompleteSchema, parsedDataFailedSchema, parsedDataSchema, parsedErrorSchema, rawContentDownloadOnlySchema, rawContentInlineSchema, rawContentSchema, safeValidateEmailReceivedEvent, spfResultSchema, validateEmailReceivedEvent, webhookAttachmentSchema };
package/dist/zod.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { EmailReceivedEvent } from "./types-DJTmrgCz.js";
1
+ import { EmailReceivedEvent } from "./types-C8JlcpcT.js";
2
2
  import { WebhookValidationError$1 as WebhookValidationError, WebhookValidationErrorCode } from "./errors-CSPHzZB_.js";
3
3
  import { z } from "zod";
4
4
 
@@ -547,6 +547,98 @@ declare const rawContentSchema: z.ZodDiscriminatedUnion<"included", [z.ZodObject
547
547
  max_inline_bytes: number;
548
548
  reason_code: "size_exceeded";
549
549
  }>]>;
550
+ declare const spfResultSchema: z.ZodEnum<["pass", "fail", "softfail", "neutral", "none", "temperror", "permerror"]>;
551
+ declare const dmarcResultSchema: z.ZodEnum<["pass", "fail", "none", "temperror", "permerror"]>;
552
+ declare const dmarcPolicySchema: z.ZodNullable<z.ZodEnum<["reject", "quarantine", "none"]>>;
553
+ declare const dkimSignatureResultSchema: z.ZodEnum<["pass", "fail", "temperror", "permerror"]>;
554
+ declare const dkimSignatureSchema: z.ZodObject<{
555
+ domain: z.ZodString;
556
+ selector: z.ZodString;
557
+ result: z.ZodEnum<["pass", "fail", "temperror", "permerror"]>;
558
+ aligned: z.ZodBoolean;
559
+ keyBits: z.ZodNullable<z.ZodNumber>;
560
+ algo: z.ZodString;
561
+ }, "strip", z.ZodTypeAny, {
562
+ domain: string;
563
+ selector: string;
564
+ result: "pass" | "fail" | "temperror" | "permerror";
565
+ aligned: boolean;
566
+ keyBits: number | null;
567
+ algo: string;
568
+ }, {
569
+ domain: string;
570
+ selector: string;
571
+ result: "pass" | "fail" | "temperror" | "permerror";
572
+ aligned: boolean;
573
+ keyBits: number | null;
574
+ algo: string;
575
+ }>;
576
+ declare const emailAuthSchema: z.ZodObject<{
577
+ spf: z.ZodEnum<["pass", "fail", "softfail", "neutral", "none", "temperror", "permerror"]>;
578
+ dmarc: z.ZodEnum<["pass", "fail", "none", "temperror", "permerror"]>;
579
+ dmarcPolicy: z.ZodNullable<z.ZodEnum<["reject", "quarantine", "none"]>>;
580
+ dmarcFromDomain: z.ZodNullable<z.ZodString>;
581
+ dmarcSpfAligned: z.ZodBoolean;
582
+ dmarcDkimAligned: z.ZodBoolean;
583
+ dmarcSpfStrict: z.ZodNullable<z.ZodBoolean>;
584
+ dmarcDkimStrict: z.ZodNullable<z.ZodBoolean>;
585
+ dkimSignatures: z.ZodArray<z.ZodObject<{
586
+ domain: z.ZodString;
587
+ selector: z.ZodString;
588
+ result: z.ZodEnum<["pass", "fail", "temperror", "permerror"]>;
589
+ aligned: z.ZodBoolean;
590
+ keyBits: z.ZodNullable<z.ZodNumber>;
591
+ algo: z.ZodString;
592
+ }, "strip", z.ZodTypeAny, {
593
+ domain: string;
594
+ selector: string;
595
+ result: "pass" | "fail" | "temperror" | "permerror";
596
+ aligned: boolean;
597
+ keyBits: number | null;
598
+ algo: string;
599
+ }, {
600
+ domain: string;
601
+ selector: string;
602
+ result: "pass" | "fail" | "temperror" | "permerror";
603
+ aligned: boolean;
604
+ keyBits: number | null;
605
+ algo: string;
606
+ }>, "many">;
607
+ }, "strip", z.ZodTypeAny, {
608
+ spf: "pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror";
609
+ dmarc: "pass" | "fail" | "none" | "temperror" | "permerror";
610
+ dmarcPolicy: "none" | "reject" | "quarantine" | null;
611
+ dmarcFromDomain: string | null;
612
+ dmarcSpfAligned: boolean;
613
+ dmarcDkimAligned: boolean;
614
+ dmarcSpfStrict: boolean | null;
615
+ dmarcDkimStrict: boolean | null;
616
+ dkimSignatures: {
617
+ domain: string;
618
+ selector: string;
619
+ result: "pass" | "fail" | "temperror" | "permerror";
620
+ aligned: boolean;
621
+ keyBits: number | null;
622
+ algo: string;
623
+ }[];
624
+ }, {
625
+ spf: "pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror";
626
+ dmarc: "pass" | "fail" | "none" | "temperror" | "permerror";
627
+ dmarcPolicy: "none" | "reject" | "quarantine" | null;
628
+ dmarcFromDomain: string | null;
629
+ dmarcSpfAligned: boolean;
630
+ dmarcDkimAligned: boolean;
631
+ dmarcSpfStrict: boolean | null;
632
+ dmarcDkimStrict: boolean | null;
633
+ dkimSignatures: {
634
+ domain: string;
635
+ selector: string;
636
+ result: "pass" | "fail" | "temperror" | "permerror";
637
+ aligned: boolean;
638
+ keyBits: number | null;
639
+ algo: string;
640
+ }[];
641
+ }>;
550
642
  declare const emailReceivedEventSchema: z.ZodObject<{
551
643
  id: z.ZodString;
552
644
  event: z.ZodLiteral<"email.received">;
@@ -914,6 +1006,72 @@ declare const emailReceivedEventSchema: z.ZodObject<{
914
1006
  score: number;
915
1007
  };
916
1008
  }>>;
1009
+ auth: z.ZodOptional<z.ZodObject<{
1010
+ spf: z.ZodEnum<["pass", "fail", "softfail", "neutral", "none", "temperror", "permerror"]>;
1011
+ dmarc: z.ZodEnum<["pass", "fail", "none", "temperror", "permerror"]>;
1012
+ dmarcPolicy: z.ZodNullable<z.ZodEnum<["reject", "quarantine", "none"]>>;
1013
+ dmarcFromDomain: z.ZodNullable<z.ZodString>;
1014
+ dmarcSpfAligned: z.ZodBoolean;
1015
+ dmarcDkimAligned: z.ZodBoolean;
1016
+ dmarcSpfStrict: z.ZodNullable<z.ZodBoolean>;
1017
+ dmarcDkimStrict: z.ZodNullable<z.ZodBoolean>;
1018
+ dkimSignatures: z.ZodArray<z.ZodObject<{
1019
+ domain: z.ZodString;
1020
+ selector: z.ZodString;
1021
+ result: z.ZodEnum<["pass", "fail", "temperror", "permerror"]>;
1022
+ aligned: z.ZodBoolean;
1023
+ keyBits: z.ZodNullable<z.ZodNumber>;
1024
+ algo: z.ZodString;
1025
+ }, "strip", z.ZodTypeAny, {
1026
+ domain: string;
1027
+ selector: string;
1028
+ result: "pass" | "fail" | "temperror" | "permerror";
1029
+ aligned: boolean;
1030
+ keyBits: number | null;
1031
+ algo: string;
1032
+ }, {
1033
+ domain: string;
1034
+ selector: string;
1035
+ result: "pass" | "fail" | "temperror" | "permerror";
1036
+ aligned: boolean;
1037
+ keyBits: number | null;
1038
+ algo: string;
1039
+ }>, "many">;
1040
+ }, "strip", z.ZodTypeAny, {
1041
+ spf: "pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror";
1042
+ dmarc: "pass" | "fail" | "none" | "temperror" | "permerror";
1043
+ dmarcPolicy: "none" | "reject" | "quarantine" | null;
1044
+ dmarcFromDomain: string | null;
1045
+ dmarcSpfAligned: boolean;
1046
+ dmarcDkimAligned: boolean;
1047
+ dmarcSpfStrict: boolean | null;
1048
+ dmarcDkimStrict: boolean | null;
1049
+ dkimSignatures: {
1050
+ domain: string;
1051
+ selector: string;
1052
+ result: "pass" | "fail" | "temperror" | "permerror";
1053
+ aligned: boolean;
1054
+ keyBits: number | null;
1055
+ algo: string;
1056
+ }[];
1057
+ }, {
1058
+ spf: "pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror";
1059
+ dmarc: "pass" | "fail" | "none" | "temperror" | "permerror";
1060
+ dmarcPolicy: "none" | "reject" | "quarantine" | null;
1061
+ dmarcFromDomain: string | null;
1062
+ dmarcSpfAligned: boolean;
1063
+ dmarcDkimAligned: boolean;
1064
+ dmarcSpfStrict: boolean | null;
1065
+ dmarcDkimStrict: boolean | null;
1066
+ dkimSignatures: {
1067
+ domain: string;
1068
+ selector: string;
1069
+ result: "pass" | "fail" | "temperror" | "permerror";
1070
+ aligned: boolean;
1071
+ keyBits: number | null;
1072
+ algo: string;
1073
+ }[];
1074
+ }>>;
917
1075
  }, "strip", z.ZodTypeAny, {
918
1076
  id: string;
919
1077
  received_at: string;
@@ -1006,6 +1164,24 @@ declare const emailReceivedEventSchema: z.ZodObject<{
1006
1164
  score: number;
1007
1165
  };
1008
1166
  } | undefined;
1167
+ auth?: {
1168
+ spf: "pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror";
1169
+ dmarc: "pass" | "fail" | "none" | "temperror" | "permerror";
1170
+ dmarcPolicy: "none" | "reject" | "quarantine" | null;
1171
+ dmarcFromDomain: string | null;
1172
+ dmarcSpfAligned: boolean;
1173
+ dmarcDkimAligned: boolean;
1174
+ dmarcSpfStrict: boolean | null;
1175
+ dmarcDkimStrict: boolean | null;
1176
+ dkimSignatures: {
1177
+ domain: string;
1178
+ selector: string;
1179
+ result: "pass" | "fail" | "temperror" | "permerror";
1180
+ aligned: boolean;
1181
+ keyBits: number | null;
1182
+ algo: string;
1183
+ }[];
1184
+ } | undefined;
1009
1185
  }, {
1010
1186
  id: string;
1011
1187
  received_at: string;
@@ -1098,6 +1274,24 @@ declare const emailReceivedEventSchema: z.ZodObject<{
1098
1274
  score: number;
1099
1275
  };
1100
1276
  } | undefined;
1277
+ auth?: {
1278
+ spf: "pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror";
1279
+ dmarc: "pass" | "fail" | "none" | "temperror" | "permerror";
1280
+ dmarcPolicy: "none" | "reject" | "quarantine" | null;
1281
+ dmarcFromDomain: string | null;
1282
+ dmarcSpfAligned: boolean;
1283
+ dmarcDkimAligned: boolean;
1284
+ dmarcSpfStrict: boolean | null;
1285
+ dmarcDkimStrict: boolean | null;
1286
+ dkimSignatures: {
1287
+ domain: string;
1288
+ selector: string;
1289
+ result: "pass" | "fail" | "temperror" | "permerror";
1290
+ aligned: boolean;
1291
+ keyBits: number | null;
1292
+ algo: string;
1293
+ }[];
1294
+ } | undefined;
1101
1295
  }>;
1102
1296
  }, "strip", z.ZodTypeAny, {
1103
1297
  id: string;
@@ -1200,6 +1394,24 @@ declare const emailReceivedEventSchema: z.ZodObject<{
1200
1394
  score: number;
1201
1395
  };
1202
1396
  } | undefined;
1397
+ auth?: {
1398
+ spf: "pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror";
1399
+ dmarc: "pass" | "fail" | "none" | "temperror" | "permerror";
1400
+ dmarcPolicy: "none" | "reject" | "quarantine" | null;
1401
+ dmarcFromDomain: string | null;
1402
+ dmarcSpfAligned: boolean;
1403
+ dmarcDkimAligned: boolean;
1404
+ dmarcSpfStrict: boolean | null;
1405
+ dmarcDkimStrict: boolean | null;
1406
+ dkimSignatures: {
1407
+ domain: string;
1408
+ selector: string;
1409
+ result: "pass" | "fail" | "temperror" | "permerror";
1410
+ aligned: boolean;
1411
+ keyBits: number | null;
1412
+ algo: string;
1413
+ }[];
1414
+ } | undefined;
1203
1415
  };
1204
1416
  }, {
1205
1417
  id: string;
@@ -1302,6 +1514,24 @@ declare const emailReceivedEventSchema: z.ZodObject<{
1302
1514
  score: number;
1303
1515
  };
1304
1516
  } | undefined;
1517
+ auth?: {
1518
+ spf: "pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror";
1519
+ dmarc: "pass" | "fail" | "none" | "temperror" | "permerror";
1520
+ dmarcPolicy: "none" | "reject" | "quarantine" | null;
1521
+ dmarcFromDomain: string | null;
1522
+ dmarcSpfAligned: boolean;
1523
+ dmarcDkimAligned: boolean;
1524
+ dmarcSpfStrict: boolean | null;
1525
+ dmarcDkimStrict: boolean | null;
1526
+ dkimSignatures: {
1527
+ domain: string;
1528
+ selector: string;
1529
+ result: "pass" | "fail" | "temperror" | "permerror";
1530
+ aligned: boolean;
1531
+ keyBits: number | null;
1532
+ algo: string;
1533
+ }[];
1534
+ } | undefined;
1305
1535
  };
1306
1536
  }>;
1307
1537
  /**
@@ -1353,4 +1583,4 @@ declare function validateEmailReceivedEvent(input: unknown): EmailReceivedEvent;
1353
1583
  * ```
1354
1584
  */
1355
1585
  declare function safeValidateEmailReceivedEvent(input: unknown): z.SafeParseReturnType<unknown, EmailReceivedEvent>; //#endregion
1356
- export { WebhookValidationError, WebhookValidationErrorCode, emailAddressSchema, emailReceivedEventSchema, parsedDataCompleteSchema, parsedDataFailedSchema, parsedDataSchema, parsedErrorSchema, rawContentDownloadOnlySchema, rawContentInlineSchema, rawContentSchema, safeValidateEmailReceivedEvent, validateEmailReceivedEvent, webhookAttachmentSchema };
1586
+ export { WebhookValidationError, WebhookValidationErrorCode, dkimSignatureResultSchema, dkimSignatureSchema, dmarcPolicySchema, dmarcResultSchema, emailAddressSchema, emailAuthSchema, emailReceivedEventSchema, parsedDataCompleteSchema, parsedDataFailedSchema, parsedDataSchema, parsedErrorSchema, rawContentDownloadOnlySchema, rawContentInlineSchema, rawContentSchema, safeValidateEmailReceivedEvent, spfResultSchema, validateEmailReceivedEvent, webhookAttachmentSchema };
package/dist/zod.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { WebhookValidationError } from "./errors-2CwICC_t.js";
2
- import { emailAddressSchema, emailReceivedEventSchema, parsedDataCompleteSchema, parsedDataFailedSchema, parsedDataSchema, parsedErrorSchema, rawContentDownloadOnlySchema, rawContentInlineSchema, rawContentSchema, safeValidateEmailReceivedEvent, validateEmailReceivedEvent, webhookAttachmentSchema } from "./zod-Ced9Kav9.js";
2
+ import { dkimSignatureResultSchema, dkimSignatureSchema, dmarcPolicySchema, dmarcResultSchema, emailAddressSchema, emailAuthSchema, emailReceivedEventSchema, parsedDataCompleteSchema, parsedDataFailedSchema, parsedDataSchema, parsedErrorSchema, rawContentDownloadOnlySchema, rawContentInlineSchema, rawContentSchema, safeValidateEmailReceivedEvent, spfResultSchema, validateEmailReceivedEvent, webhookAttachmentSchema } from "./zod-CalKEwR4.js";
3
3
 
4
- export { WebhookValidationError, emailAddressSchema, emailReceivedEventSchema, parsedDataCompleteSchema, parsedDataFailedSchema, parsedDataSchema, parsedErrorSchema, rawContentDownloadOnlySchema, rawContentInlineSchema, rawContentSchema, safeValidateEmailReceivedEvent, validateEmailReceivedEvent, webhookAttachmentSchema };
4
+ export { WebhookValidationError, dkimSignatureResultSchema, dkimSignatureSchema, dmarcPolicySchema, dmarcResultSchema, emailAddressSchema, emailAuthSchema, emailReceivedEventSchema, parsedDataCompleteSchema, parsedDataFailedSchema, parsedDataSchema, parsedErrorSchema, rawContentDownloadOnlySchema, rawContentInlineSchema, rawContentSchema, safeValidateEmailReceivedEvent, spfResultSchema, validateEmailReceivedEvent, webhookAttachmentSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mymx",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "Official MyMX Node.js SDK for webhook signature verification",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",