@blamejs/core 0.14.27 → 0.15.0

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.
Files changed (134) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +2 -2
  3. package/index.js +4 -0
  4. package/lib/ai-content-detect.js +9 -10
  5. package/lib/api-key.js +107 -74
  6. package/lib/atomic-file.js +29 -1
  7. package/lib/audit-chain.js +47 -11
  8. package/lib/audit-sign.js +77 -2
  9. package/lib/audit-tools.js +79 -51
  10. package/lib/audit.js +218 -100
  11. package/lib/backup/index.js +13 -10
  12. package/lib/break-glass.js +202 -144
  13. package/lib/cache.js +174 -105
  14. package/lib/chain-writer.js +38 -16
  15. package/lib/cli.js +19 -14
  16. package/lib/cluster-provider-db.js +130 -104
  17. package/lib/cluster-storage.js +119 -22
  18. package/lib/cluster.js +119 -71
  19. package/lib/compliance.js +22 -0
  20. package/lib/consent.js +73 -24
  21. package/lib/constants.js +16 -11
  22. package/lib/crypto-field.js +387 -91
  23. package/lib/db-declare-row-policy.js +35 -22
  24. package/lib/db-file-lifecycle.js +3 -2
  25. package/lib/db-query.js +497 -255
  26. package/lib/db-schema.js +209 -44
  27. package/lib/db.js +176 -95
  28. package/lib/external-db-migrate.js +229 -139
  29. package/lib/external-db.js +25 -15
  30. package/lib/framework-error.js +11 -0
  31. package/lib/framework-files.js +73 -0
  32. package/lib/framework-schema.js +695 -394
  33. package/lib/gate-contract.js +596 -1
  34. package/lib/guard-agent-registry.js +26 -44
  35. package/lib/guard-all.js +1 -0
  36. package/lib/guard-auth.js +42 -112
  37. package/lib/guard-cidr.js +33 -154
  38. package/lib/guard-csv.js +46 -113
  39. package/lib/guard-domain.js +34 -157
  40. package/lib/guard-dsn.js +27 -43
  41. package/lib/guard-email.js +47 -69
  42. package/lib/guard-envelope.js +19 -32
  43. package/lib/guard-event-bus-payload.js +24 -42
  44. package/lib/guard-event-bus-topic.js +25 -43
  45. package/lib/guard-filename.js +42 -106
  46. package/lib/guard-graphql.js +42 -123
  47. package/lib/guard-html.js +53 -108
  48. package/lib/guard-idempotency-key.js +24 -42
  49. package/lib/guard-image.js +46 -103
  50. package/lib/guard-imap-command.js +18 -32
  51. package/lib/guard-jmap.js +16 -30
  52. package/lib/guard-json.js +38 -108
  53. package/lib/guard-jsonpath.js +38 -171
  54. package/lib/guard-jwt.js +49 -179
  55. package/lib/guard-list-id.js +25 -41
  56. package/lib/guard-list-unsubscribe.js +27 -43
  57. package/lib/guard-mail-compose.js +24 -42
  58. package/lib/guard-mail-move.js +26 -44
  59. package/lib/guard-mail-query.js +28 -46
  60. package/lib/guard-mail-reply.js +24 -42
  61. package/lib/guard-mail-sieve.js +24 -42
  62. package/lib/guard-managesieve-command.js +17 -31
  63. package/lib/guard-markdown.js +37 -104
  64. package/lib/guard-message-id.js +26 -45
  65. package/lib/guard-mime.js +39 -151
  66. package/lib/guard-oauth.js +54 -135
  67. package/lib/guard-pdf.js +45 -101
  68. package/lib/guard-pop3-command.js +21 -31
  69. package/lib/guard-posture-chain.js +24 -42
  70. package/lib/guard-regex.js +33 -107
  71. package/lib/guard-saga-config.js +24 -42
  72. package/lib/guard-shell.js +42 -172
  73. package/lib/guard-smtp-command.js +48 -54
  74. package/lib/guard-snapshot-envelope.js +24 -42
  75. package/lib/guard-sql.js +1491 -0
  76. package/lib/guard-stream-args.js +24 -43
  77. package/lib/guard-svg.js +47 -65
  78. package/lib/guard-template.js +35 -172
  79. package/lib/guard-tenant-id.js +26 -45
  80. package/lib/guard-time.js +32 -154
  81. package/lib/guard-trace-context.js +25 -44
  82. package/lib/guard-uuid.js +32 -153
  83. package/lib/guard-xml.js +38 -113
  84. package/lib/guard-yaml.js +51 -163
  85. package/lib/http-client.js +14 -0
  86. package/lib/inbox.js +120 -107
  87. package/lib/legal-hold.js +107 -50
  88. package/lib/log-stream-cloudwatch.js +47 -31
  89. package/lib/log-stream-otlp.js +32 -18
  90. package/lib/mail-crypto-smime.js +2 -6
  91. package/lib/mail-greylist.js +2 -6
  92. package/lib/mail-helo.js +2 -6
  93. package/lib/mail-journal.js +85 -64
  94. package/lib/mail-rbl.js +2 -6
  95. package/lib/mail-scan.js +2 -6
  96. package/lib/mail-spam-score.js +2 -6
  97. package/lib/mail-store.js +287 -154
  98. package/lib/middleware/fetch-metadata.js +17 -7
  99. package/lib/middleware/idempotency-key.js +54 -38
  100. package/lib/middleware/rate-limit.js +102 -32
  101. package/lib/middleware/security-headers.js +21 -5
  102. package/lib/migrations.js +108 -66
  103. package/lib/network-heartbeat.js +7 -0
  104. package/lib/nonce-store.js +31 -9
  105. package/lib/object-store/azure-blob-bucket-ops.js +9 -4
  106. package/lib/object-store/azure-blob.js +31 -3
  107. package/lib/object-store/sigv4.js +10 -0
  108. package/lib/outbox.js +136 -82
  109. package/lib/pqc-agent.js +44 -0
  110. package/lib/pubsub-cluster.js +42 -20
  111. package/lib/queue-local.js +202 -139
  112. package/lib/queue-redis.js +9 -1
  113. package/lib/queue-sqs.js +6 -0
  114. package/lib/retention.js +82 -39
  115. package/lib/safe-dns.js +29 -45
  116. package/lib/safe-ical.js +18 -33
  117. package/lib/safe-icap.js +27 -43
  118. package/lib/safe-sieve.js +21 -40
  119. package/lib/safe-sql.js +124 -3
  120. package/lib/safe-vcard.js +18 -33
  121. package/lib/scheduler.js +35 -12
  122. package/lib/seeders.js +122 -74
  123. package/lib/session-stores.js +42 -14
  124. package/lib/session.js +109 -72
  125. package/lib/sql.js +3885 -0
  126. package/lib/static.js +45 -7
  127. package/lib/subject.js +55 -17
  128. package/lib/vault/index.js +3 -2
  129. package/lib/vault/passphrase-ops.js +3 -2
  130. package/lib/vault/rotate.js +104 -64
  131. package/lib/vendor-data.js +2 -0
  132. package/lib/websocket.js +16 -0
  133. package/package.json +1 -1
  134. package/sbom.cdx.json +6 -6
@@ -648,167 +648,44 @@ function sanitize(input, opts) {
648
648
  }
649
649
  // Critical refuses can't be repaired.
650
650
  var issues = _detectIssues(input, opts);
651
- for (var i = 0; i < issues.length; i += 1) {
652
- if (issues[i].severity === "critical" || issues[i].severity === "high") {
653
- throw _err(issues[i].ruleId || "domain.refused",
654
- "guardDomain.sanitize: " + issues[i].snippet);
655
- }
656
- }
651
+ gateContract.throwOnRefusalSeverity(issues, { errorClass: GuardDomainError, codePrefix: "domain" });
657
652
  // Safe transforms: lowercase ASCII, strip trailing dot.
658
653
  var out = input.toLowerCase();
659
654
  if (out.charAt(out.length - 1) === ".") out = out.slice(0, -1);
660
655
  return out;
661
656
  }
662
657
 
663
- /**
664
- * @primitive b.guardDomain.gate
665
- * @signature b.guardDomain.gate(opts?)
666
- * @since 0.7.41
667
- * @status stable
668
- * @compliance hipaa, pci-dss, gdpr, soc2
669
- * @related b.guardDomain.validate, b.guardDomain.sanitize
670
- *
671
- * Build a `b.gateContract` gate that consumes `ctx.identifier` (or
672
- * `ctx.domain`) and dispatches `serve` (no input or clean)
673
- * `audit-only` (warn-only issues) → `refuse` (any critical or high
674
- * issue). No `sanitize` action — domain canonicalization is
675
- * caller-driven via `b.guardDomain.sanitize` so an allowlist gate
676
- * never silently rewrites the operator's stored allowlist key.
677
- *
678
- * @opts
679
- * profile: "strict"|"balanced"|"permissive",
680
- * compliancePosture: "hipaa"|"pci-dss"|"gdpr"|"soc2",
681
- * name: string, // gate identity for audit / observability
682
- *
683
- * @example
684
- * var domGate = b.guardDomain.gate({ profile: "strict" });
685
- * var verdict = await domGate.check({ identifier: "myhost.localhost" });
686
- * verdict.action; // → "refuse"
687
- */
688
- function gate(opts) {
689
- opts = _resolveOpts(opts);
690
- return gateContract.buildGuardGate(
691
- opts.name || "guardDomain:" + (opts.profile || "default"),
692
- opts,
693
- async function (ctx) {
694
- // Identifier-shape ctx — operator passes via ctx.identifier or
695
- // ctx.domain.
696
- var identifier = ctx && (ctx.identifier || ctx.domain || "");
697
- if (!identifier) return { ok: true, action: "serve" };
698
- var rv = validate(identifier, opts);
699
- if (rv.issues.length === 0) return { ok: true, action: "serve" };
700
- var hasCritical = rv.issues.some(function (i) {
701
- return i.severity === "critical";
702
- });
703
- var hasHigh = rv.issues.some(function (i) {
704
- return i.severity === "high";
705
- });
706
- if (!hasCritical && !hasHigh) {
707
- return { ok: true, action: "audit-only", issues: rv.issues };
708
- }
709
- return { ok: false, action: "refuse", issues: rv.issues };
710
- });
711
- }
712
-
713
- /**
714
- * @primitive b.guardDomain.buildProfile
715
- * @signature b.guardDomain.buildProfile(opts)
716
- * @since 0.7.41
717
- * @status stable
718
- * @related b.guardDomain.gate, b.guardDomain.compliancePosture
719
- *
720
- * Compose a derived profile from one or more named bases plus inline
721
- * overrides. `opts.extends` is a profile name (`"strict"` /
722
- * `"balanced"` / `"permissive"`) or an array of names; later entries
723
- * shadow earlier ones. Inline `opts` keys win last.
724
- *
725
- * @opts
726
- * extends: string|string[], // base profile name(s) to compose
727
- *
728
- * @example
729
- * var custom = b.guardDomain.buildProfile({
730
- * extends: "balanced",
731
- * allowedScripts: ["latin"],
732
- * punycodePolicy: "reject",
733
- * });
734
- * custom.punycodePolicy; // → "reject"
735
- * custom.bidiPolicy; // → "reject"
736
- */
737
- var buildProfile = gateContract.makeProfileBuilder(PROFILES);
738
-
739
- /**
740
- * @primitive b.guardDomain.compliancePosture
741
- * @signature b.guardDomain.compliancePosture(name)
742
- * @since 0.7.41
743
- * @status stable
744
- * @compliance hipaa, pci-dss, gdpr, soc2
745
- * @related b.guardDomain.gate, b.guardDomain.buildProfile
746
- *
747
- * Look up a compliance-posture overlay by name (`"hipaa"` /
748
- * `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of the
749
- * posture object — the caller may mutate freely. Throws
750
- * `GuardDomainError("domain.bad-posture")` on unknown name.
751
- *
752
- * @example
753
- * var posture = b.guardDomain.compliancePosture("hipaa");
754
- * posture.specialUsePolicy; // → "reject"
755
- * posture.forensicSnippetBytes; // → 256
756
- */
757
- function compliancePosture(name) {
758
- return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES,
759
- _err, "domain");
760
- }
658
+ // gate / buildProfile / compliancePosture / loadRulePack are assembled by
659
+ // gateContract.defineGuard below; their wiki sections render from the
660
+ // single-sourced @abiTemplate (defineGuard) blocks in gate-contract.js,
661
+ // instantiated per guard by the page generator.
662
+
663
+ var INTEGRATION_FIXTURES = Object.freeze({
664
+ kind: "identifier",
665
+ benignBytes: Buffer.from("example.com", "utf8"),
666
+ // Hostile: dotted-decimal IPv4 (CVE-2021-22931 class) every
667
+ // profile refuses (allowlist-bypass via DNS rebinding).
668
+ hostileBytes: Buffer.from("192.168.1.1", "utf8"),
669
+ benignIdentifier: "example.com",
670
+ hostileIdentifier: "192.168.1.1",
671
+ });
761
672
 
762
- var _domainRulePacks = gateContract.makeRulePackLoader(GuardDomainError, "domain");
763
- /**
764
- * @primitive b.guardDomain.loadRulePack
765
- * @signature b.guardDomain.loadRulePack(pack)
766
- * @since 0.7.41
767
- * @status stable
768
- * @related b.guardDomain.gate
769
- *
770
- * Register an operator-supplied rule pack with the guard-domain
771
- * registry. The pack is identified by `pack.id` (non-empty string)
772
- * and stored for later inspection / dispatch by gates that opt in
773
- * via `opts.rulePackId`. Returns the pack object unchanged on
774
- * success; throws `GuardDomainError("domain.bad-opt")` when `pack`
775
- * is missing or `pack.id` is not a non-empty string.
776
- *
777
- * @example
778
- * var pack = b.guardDomain.loadRulePack({
779
- * id: "tenant-corp-only",
780
- * rules: [
781
- * { id: "tenant-suffix", severity: "high",
782
- * detect: function (d) { return !/\.example\.com$/i.test(d); },
783
- * reason: "tenant policy: only example.com suffixes permitted" },
784
- * ],
785
- * });
786
- * pack.id; // → "tenant-corp-only"
787
- */
788
- var loadRulePack = _domainRulePacks.load;
789
-
790
- module.exports = {
791
- // ---- guard-* family registry exports ----
792
- NAME: "domain",
793
- KIND: "identifier",
794
- INTEGRATION_FIXTURES: Object.freeze({
795
- kind: "identifier",
796
- benignBytes: Buffer.from("example.com", "utf8"),
797
- // Hostile: dotted-decimal IPv4 (CVE-2021-22931 class) — every
798
- // profile refuses (allowlist-bypass via DNS rebinding).
799
- hostileBytes: Buffer.from("192.168.1.1", "utf8"),
800
- benignIdentifier: "example.com",
801
- hostileIdentifier: "192.168.1.1",
802
- }),
803
- // ---- primitive surface ----
804
- validate: validate,
805
- sanitize: sanitize,
806
- gate: gate,
807
- buildProfile: buildProfile,
808
- compliancePosture: compliancePosture,
809
- loadRulePack: loadRulePack,
810
- PROFILES: PROFILES,
811
- DEFAULTS: DEFAULTS,
812
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
813
- GuardDomainError: GuardDomainError,
814
- };
673
+ // Assembled from the gate-contract guard factory: error class, registry
674
+ // exports (NAME / KIND / INTEGRATION_FIXTURES), buildProfile /
675
+ // compliancePosture / loadRulePack wiring, plus the per-guard inspection
676
+ // surface (validate / sanitize). The gate is the factory default — the
677
+ // standard serve -> audit-only -> refuse chain — reading ctx.identifier ||
678
+ // ctx.domain via ctxFields. No sanitize action: an allowlist gate never
679
+ // rewrites the operator's stored allowlist key.
680
+ module.exports = gateContract.defineGuard({
681
+ name: "domain",
682
+ kind: "identifier",
683
+ errorClass: GuardDomainError,
684
+ profiles: PROFILES,
685
+ defaults: DEFAULTS,
686
+ postures: COMPLIANCE_POSTURES,
687
+ integrationFixtures: INTEGRATION_FIXTURES,
688
+ validate: validate,
689
+ sanitize: sanitize,
690
+ ctxFields: ["identifier", "domain"],
691
+ });
package/lib/guard-dsn.js CHANGED
@@ -101,6 +101,7 @@
101
101
 
102
102
  var C = require("./constants");
103
103
  var { defineClass } = require("./framework-error");
104
+ var gateContract = require("./gate-contract");
104
105
 
105
106
  var GuardDsnError = defineClass("GuardDsnError", { alwaysPermanent: true });
106
107
 
@@ -112,12 +113,7 @@ var PROFILES = Object.freeze({
112
113
  permissive: { maxBytes: C.BYTES.mib(4), maxRecipients: 4096, maxHeaderLine: 998 }, // RFC 5322 §2.1.1 line cap; large-blast bounce class
113
114
  });
114
115
 
115
- var COMPLIANCE_POSTURES = Object.freeze({
116
- hipaa: "strict",
117
- "pci-dss": "strict",
118
- gdpr: "strict",
119
- soc2: "strict",
120
- });
116
+ var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
121
117
 
122
118
  var KNOWN_ACTIONS = Object.freeze({
123
119
  failed: true,
@@ -263,22 +259,6 @@ function parse(deliveryStatusBody, opts) {
263
259
  };
264
260
  }
265
261
 
266
- /**
267
- * @primitive b.guardDsn.compliancePosture
268
- * @signature b.guardDsn.compliancePosture(posture)
269
- * @since 0.9.37
270
- * @status stable
271
- *
272
- * Return the effective profile name for a compliance posture, or
273
- * `null` for unknown posture names.
274
- *
275
- * @example
276
- * b.guardDsn.compliancePosture("hipaa"); // → "strict"
277
- */
278
- function compliancePosture(posture) {
279
- return COMPLIANCE_POSTURES[posture] || null;
280
- }
281
-
282
262
  function _splitBlocks(text) {
283
263
  // RFC 3464 §2.1.1: block separator is `CRLF CRLF` only — a "blank
284
264
  // line" in message-syntax terms. `\n\s*\n` admits `\v` / `\f` /
@@ -359,25 +339,29 @@ function _statusClass(firstDigit) {
359
339
  return "unknown";
360
340
  }
361
341
 
362
- function _resolveProfile(opts) {
363
- if (opts.posture && COMPLIANCE_POSTURES[opts.posture]) {
364
- return PROFILES[COMPLIANCE_POSTURES[opts.posture]];
365
- }
366
- var p = opts.profile || DEFAULT_PROFILE;
367
- if (!PROFILES[p]) {
368
- throw new GuardDsnError("guard-dsn/bad-profile",
369
- "guardDsn: unknown profile '" + p + "'");
370
- }
371
- return PROFILES[p];
372
- }
342
+ var _resolveProfile = gateContract.makeProfileResolver({
343
+ profiles: PROFILES,
344
+ postures: COMPLIANCE_POSTURES,
345
+ defaults: DEFAULT_PROFILE,
346
+ errorClass: GuardDsnError,
347
+ codePrefix: "guard-dsn",
348
+ byObject: true,
349
+ });
373
350
 
374
- module.exports = {
375
- parse: parse,
376
- compliancePosture: compliancePosture,
377
- PROFILES: PROFILES,
378
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
379
- KNOWN_ACTIONS: KNOWN_ACTIONS,
380
- GuardDsnError: GuardDsnError,
381
- NAME: "dsn",
382
- KIND: "delivery-status",
383
- };
351
+ // compliancePosture is assembled by gateContract.defineParser below; its
352
+ // wiki section renders from the single-sourced @abiTemplate (defineParser)
353
+ // block in gate-contract.js, instantiated for this guard by the page
354
+ // generator.
355
+ module.exports = gateContract.defineParser({
356
+ name: "dsn",
357
+ entry: parse,
358
+ entryName: "parse",
359
+ errorClass: GuardDsnError,
360
+ profiles: PROFILES,
361
+ postures: COMPLIANCE_POSTURES,
362
+ extra: {
363
+ KNOWN_ACTIONS: KNOWN_ACTIONS,
364
+ NAME: "dsn",
365
+ KIND: "delivery-status",
366
+ },
367
+ });
@@ -857,12 +857,8 @@ function sanitize(input, opts) {
857
857
  // Critical shapes have no safe sanitization in email — throw on
858
858
  // smuggling / CRLF injection / multi-@ / mixed-script.
859
859
  var issues = _detectMessageIssues(input, opts);
860
- for (var i = 0; i < issues.length; i += 1) {
861
- if (issues[i].severity === "critical") {
862
- throw _err(issues[i].ruleId || "email.refused",
863
- "guardEmail.sanitize: " + issues[i].snippet);
864
- }
865
- }
860
+ gateContract.throwOnRefusalSeverity(issues,
861
+ { errorClass: GuardEmailError, codePrefix: "email", severities: ["critical"] });
866
862
  return codepointClass.applyCharStripPolicies(input, opts);
867
863
  }
868
864
 
@@ -919,67 +915,49 @@ function gate(opts) {
919
915
  });
920
916
  }
921
917
 
922
- var buildProfile = gateContract.makeProfileBuilder(PROFILES);
923
-
924
- /**
925
- * @primitive b.guardEmail.compliancePosture
926
- * @signature b.guardEmail.compliancePosture(name)
927
- * @since 0.7.17
928
- * @status stable
929
- * @compliance hipaa, pci-dss, gdpr, soc2
930
- * @related b.guardEmail.gate, b.guardEmail.validateMessage
931
- *
932
- * Look up a compliance posture by name and return its frozen opts
933
- * bundle. Throws `GuardEmailError` (`email.unknown-posture`) for
934
- * names outside `hipaa` / `pci-dss` / `gdpr` / `soc2`. The returned
935
- * opts can be merged into a `gate()` / `validateMessage()` call to
936
- * apply the posture's defaults (forensic-snippet length included).
937
- *
938
- * @example
939
- * var guardEmail = require("./lib/guard-email");
940
- * var posture = guardEmail.compliancePosture("hipaa");
941
- * posture.bareCrPolicy; // → "reject"
942
- * posture.forensicSnippetBytes; // → 256
943
- */
944
- function compliancePosture(name) {
945
- return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES, _err, "email");
946
- }
918
+ // buildProfile / compliancePosture / loadRulePack are assembled by
919
+ // gateContract.defineGuard below; their wiki sections render from the
920
+ // single-sourced @abiTemplate (defineGuard) blocks in gate-contract.js,
921
+ // instantiated per guard by the page generator.
922
+
923
+ var INTEGRATION_FIXTURES = Object.freeze({
924
+ kind: "content",
925
+ contentType: "message/rfc822",
926
+ extension: ".eml",
927
+ benignBytes: Buffer.from(
928
+ "From: alice@example.com\r\nTo: bob@example.com\r\n" +
929
+ "Subject: hello\r\nDate: Mon, 5 May 2026 10:00:00 +0000\r\n\r\n" +
930
+ "Hello.\r\n", "utf8"),
931
+ // Hostile: SMTP-smuggling pattern bare LF followed by SMTP verb
932
+ // (CVE-2023-51764 / 51765 / 51766 class).
933
+ hostileBytes: Buffer.from(
934
+ "From: alice@example.com\r\nTo: bob@example.com\r\n" +
935
+ "Subject: hi\r\n\r\n" +
936
+ "body line 1\n.\nMAIL FROM: <evil@attacker>\r\n", "utf8"),
937
+ });
947
938
 
948
- var _emailRulePacks = gateContract.makeRulePackLoader(GuardEmailError, "email");
949
- var loadRulePack = _emailRulePacks.load;
950
-
951
- module.exports = {
952
- // ---- guard-* family registry exports ----
953
- NAME: "email",
954
- KIND: "content",
955
- MIME_TYPES: Object.freeze(["message/rfc822", "message/global"]),
956
- EXTENSIONS: Object.freeze([".eml", ".mbox", ".msg"]),
957
- INTEGRATION_FIXTURES: Object.freeze({
958
- kind: "content",
959
- contentType: "message/rfc822",
960
- extension: ".eml",
961
- benignBytes: Buffer.from(
962
- "From: alice@example.com\r\nTo: bob@example.com\r\n" +
963
- "Subject: hello\r\nDate: Mon, 5 May 2026 10:00:00 +0000\r\n\r\n" +
964
- "Hello.\r\n", "utf8"),
965
- // Hostile: SMTP-smuggling pattern — bare LF followed by SMTP verb
966
- // (CVE-2023-51764 / 51765 / 51766 class).
967
- hostileBytes: Buffer.from(
968
- "From: alice@example.com\r\nTo: bob@example.com\r\n" +
969
- "Subject: hi\r\n\r\n" +
970
- "body line 1\n.\nMAIL FROM: <evil@attacker>\r\n", "utf8"),
971
- }),
972
- // ---- primitive surface ----
973
- validate: validate,
974
- validateAddress: validateAddress,
975
- validateMessage: validateMessage,
976
- sanitize: sanitize,
977
- gate: gate,
978
- buildProfile: buildProfile,
979
- compliancePosture: compliancePosture,
980
- loadRulePack: loadRulePack,
981
- PROFILES: PROFILES,
982
- DEFAULTS: DEFAULTS,
983
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
984
- GuardEmailError: GuardEmailError,
985
- };
939
+ // Assembled from the gate-contract guard factory: error class, registry
940
+ // exports (NAME / KIND / MIME_TYPES / EXTENSIONS / INTEGRATION_FIXTURES),
941
+ // buildProfile / compliancePosture / loadRulePack wiring, plus the
942
+ // per-guard inspection surface (validate / sanitize / bespoke gate) and
943
+ // the address/message entries (validateAddress / validateMessage) passed
944
+ // through verbatim. The bespoke `gate` validates via validateMessage and
945
+ // carries the serve->audit-only->refuse chain unchanged.
946
+ module.exports = gateContract.defineGuard({
947
+ name: "email",
948
+ kind: "content",
949
+ errorClass: GuardEmailError,
950
+ profiles: PROFILES,
951
+ defaults: DEFAULTS,
952
+ postures: COMPLIANCE_POSTURES,
953
+ mimeTypes: ["message/rfc822", "message/global"],
954
+ extensions: [".eml", ".mbox", ".msg"],
955
+ integrationFixtures: INTEGRATION_FIXTURES,
956
+ validate: validate,
957
+ sanitize: sanitize,
958
+ gate: gate,
959
+ extra: {
960
+ validateAddress: validateAddress,
961
+ validateMessage: validateMessage,
962
+ },
963
+ });
@@ -97,6 +97,7 @@
97
97
  var { defineClass } = require("./framework-error");
98
98
  var lazyRequire = require("./lazy-require");
99
99
  var publicSuffix = require("./public-suffix");
100
+ var gateContract = require("./gate-contract");
100
101
 
101
102
  var audit = lazyRequire(function () { return require("./audit"); });
102
103
 
@@ -116,12 +117,7 @@ var PROFILES = Object.freeze({
116
117
  permissive: { gateOnFailure: false, defaultMode: "relaxed" },
117
118
  });
118
119
 
119
- var COMPLIANCE_POSTURES = Object.freeze({
120
- hipaa: "strict",
121
- "pci-dss": "strict",
122
- gdpr: "strict",
123
- soc2: "strict",
124
- });
120
+ var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
125
121
 
126
122
  /**
127
123
  * @primitive b.guardEnvelope.check
@@ -206,22 +202,6 @@ function check(ctx, opts) {
206
202
  };
207
203
  }
208
204
 
209
- /**
210
- * @primitive b.guardEnvelope.compliancePosture
211
- * @signature b.guardEnvelope.compliancePosture(posture)
212
- * @since 0.9.36
213
- * @status stable
214
- *
215
- * Return the effective profile name for a compliance posture, or
216
- * `null` for unknown posture names.
217
- *
218
- * @example
219
- * b.guardEnvelope.compliancePosture("hipaa"); // → "strict"
220
- */
221
- function compliancePosture(posture) {
222
- return COMPLIANCE_POSTURES[posture] || null;
223
- }
224
-
225
205
  function _spfVerdict(spfResult, fromDomain, mode) {
226
206
  var verdict = {
227
207
  aligned: false,
@@ -282,13 +262,20 @@ function _emitAudit(auditImpl, action, metadata) {
282
262
  } catch (_e) { /* drop-silent — audit failure must not block accept loop */ }
283
263
  }
284
264
 
285
- module.exports = {
286
- check: check,
287
- compliancePosture: compliancePosture,
288
- PROFILES: PROFILES,
289
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
290
- GuardEnvelopeError: GuardEnvelopeError,
291
- NAME: "envelope",
292
- KIND: "envelope-alignment",
293
- _domainAligned: _domainAligned,
294
- };
265
+ // compliancePosture is assembled by gateContract.defineParser below; its
266
+ // wiki section renders from the single-sourced @abiTemplate (defineParser)
267
+ // block in gate-contract.js, instantiated for this guard by the page
268
+ // generator.
269
+ module.exports = gateContract.defineParser({
270
+ name: "envelope",
271
+ entry: check,
272
+ entryName: "check",
273
+ errorClass: GuardEnvelopeError,
274
+ profiles: PROFILES,
275
+ postures: COMPLIANCE_POSTURES,
276
+ extra: {
277
+ NAME: "envelope",
278
+ KIND: "envelope-alignment",
279
+ _domainAligned: _domainAligned,
280
+ },
281
+ });
@@ -40,6 +40,7 @@
40
40
  */
41
41
 
42
42
  var { defineClass } = require("./framework-error");
43
+ var gateContract = require("./gate-contract");
43
44
 
44
45
  var GuardEventBusPayloadError = defineClass("GuardEventBusPayloadError", { alwaysPermanent: true });
45
46
 
@@ -51,12 +52,7 @@ var PROFILES = Object.freeze({
51
52
  permissive: { maxBytes: 1048576 }, // 1 MiB
52
53
  });
53
54
 
54
- var COMPLIANCE_POSTURES = Object.freeze({
55
- hipaa: "strict",
56
- "pci-dss": "strict",
57
- gdpr: "strict",
58
- soc2: "strict",
59
- });
55
+ var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
60
56
 
61
57
  var ISO_DATETIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})$/; // allow:regex-no-length-cap — value length-bounded by maxBytes payload cap
62
58
 
@@ -135,22 +131,10 @@ function validate(payload, schema, opts) {
135
131
  return payload;
136
132
  }
137
133
 
138
- /**
139
- * @primitive b.guardEventBusPayload.compliancePosture
140
- * @signature b.guardEventBusPayload.compliancePosture(posture)
141
- * @since 0.9.25
142
- * @status stable
143
- *
144
- * Return the effective profile for a given compliance posture name.
145
- * Returns `null` for unknown posture names so operator typos surface
146
- * here instead of silently falling through to the default profile.
147
- *
148
- * @example
149
- * b.guardEventBusPayload.compliancePosture("hipaa"); // → "strict"
150
- */
151
- function compliancePosture(posture) {
152
- return COMPLIANCE_POSTURES[posture] || null;
153
- }
134
+ // compliancePosture is assembled by gateContract.defineParser below; its
135
+ // wiki section renders from the single-sourced @abiTemplate (defineParser)
136
+ // block in gate-contract.js, instantiated for this guard by the page
137
+ // generator.
154
138
 
155
139
  function _checkType(value, type, fieldName) {
156
140
  if (type === "string" && typeof value !== "string") {
@@ -194,24 +178,22 @@ function _checkType(value, type, fieldName) {
194
178
  }
195
179
  }
196
180
 
197
- function _resolveProfile(opts) {
198
- if (opts.posture && COMPLIANCE_POSTURES[opts.posture]) {
199
- return COMPLIANCE_POSTURES[opts.posture];
200
- }
201
- var p = opts.profile || DEFAULT_PROFILE;
202
- if (!PROFILES[p]) {
203
- throw new GuardEventBusPayloadError("event-bus-payload/bad-profile",
204
- "guardEventBusPayload: unknown profile '" + p + "'");
205
- }
206
- return p;
207
- }
181
+ var _resolveProfile = gateContract.makeProfileResolver({
182
+ profiles: PROFILES,
183
+ postures: COMPLIANCE_POSTURES,
184
+ defaults: DEFAULT_PROFILE,
185
+ errorClass: GuardEventBusPayloadError,
186
+ codePrefix: "event-bus-payload",
187
+ });
208
188
 
209
- module.exports = {
210
- validate: validate,
211
- compliancePosture: compliancePosture,
212
- PROFILES: PROFILES,
213
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
214
- GuardEventBusPayloadError: GuardEventBusPayloadError,
215
- NAME: "eventBusPayload",
216
- KIND: "event-bus-payload",
217
- };
189
+ module.exports = gateContract.defineParser({
190
+ name: "event-bus-payload",
191
+ entry: validate,
192
+ errorClass: GuardEventBusPayloadError,
193
+ profiles: PROFILES,
194
+ postures: COMPLIANCE_POSTURES,
195
+ extra: {
196
+ NAME: "eventBusPayload",
197
+ KIND: "event-bus-payload",
198
+ },
199
+ });