@blamejs/core 0.14.26 → 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.
- package/CHANGELOG.md +6 -0
- package/README.md +2 -2
- package/index.js +4 -0
- package/lib/agent-envelope-mac.js +104 -0
- package/lib/agent-event-bus.js +105 -4
- package/lib/agent-posture-chain.js +8 -42
- package/lib/ai-content-detect.js +9 -10
- package/lib/api-key.js +107 -74
- package/lib/atomic-file.js +62 -4
- package/lib/audit-chain.js +47 -11
- package/lib/audit-sign.js +77 -2
- package/lib/audit-tools.js +79 -51
- package/lib/audit.js +249 -123
- package/lib/auth/openid-federation.js +108 -47
- package/lib/backup/index.js +13 -10
- package/lib/break-glass.js +202 -144
- package/lib/cache.js +174 -105
- package/lib/chain-writer.js +38 -16
- package/lib/cli.js +19 -14
- package/lib/cluster-provider-db.js +130 -104
- package/lib/cluster-storage.js +119 -22
- package/lib/cluster.js +119 -71
- package/lib/compliance.js +169 -4
- package/lib/consent.js +73 -24
- package/lib/constants.js +16 -11
- package/lib/crypto-field.js +474 -92
- package/lib/db-declare-row-policy.js +35 -22
- package/lib/db-file-lifecycle.js +3 -2
- package/lib/db-query.js +497 -255
- package/lib/db-schema.js +209 -44
- package/lib/db.js +176 -95
- package/lib/error-page.js +14 -1
- package/lib/external-db-migrate.js +229 -139
- package/lib/external-db.js +25 -15
- package/lib/file-upload.js +52 -7
- package/lib/framework-error.js +14 -1
- package/lib/framework-files.js +73 -0
- package/lib/framework-schema.js +695 -394
- package/lib/gate-contract.js +649 -1
- package/lib/guard-agent-registry.js +26 -44
- package/lib/guard-all.js +1 -0
- package/lib/guard-auth.js +42 -112
- package/lib/guard-cidr.js +33 -154
- package/lib/guard-csv.js +46 -113
- package/lib/guard-domain.js +34 -157
- package/lib/guard-dsn.js +27 -43
- package/lib/guard-email.js +47 -69
- package/lib/guard-envelope.js +19 -32
- package/lib/guard-event-bus-payload.js +24 -42
- package/lib/guard-event-bus-topic.js +25 -43
- package/lib/guard-filename.js +42 -106
- package/lib/guard-graphql.js +42 -123
- package/lib/guard-html.js +53 -108
- package/lib/guard-idempotency-key.js +24 -42
- package/lib/guard-image.js +46 -103
- package/lib/guard-imap-command.js +18 -32
- package/lib/guard-jmap.js +16 -30
- package/lib/guard-json.js +38 -108
- package/lib/guard-jsonpath.js +38 -171
- package/lib/guard-jwt.js +49 -179
- package/lib/guard-list-id.js +25 -41
- package/lib/guard-list-unsubscribe.js +27 -43
- package/lib/guard-mail-compose.js +24 -42
- package/lib/guard-mail-move.js +26 -44
- package/lib/guard-mail-query.js +28 -46
- package/lib/guard-mail-reply.js +24 -42
- package/lib/guard-mail-sieve.js +24 -42
- package/lib/guard-managesieve-command.js +17 -31
- package/lib/guard-markdown.js +37 -104
- package/lib/guard-message-id.js +26 -45
- package/lib/guard-mime.js +39 -151
- package/lib/guard-oauth.js +54 -135
- package/lib/guard-pdf.js +45 -101
- package/lib/guard-pop3-command.js +21 -31
- package/lib/guard-posture-chain.js +24 -42
- package/lib/guard-regex.js +33 -107
- package/lib/guard-saga-config.js +24 -42
- package/lib/guard-shell.js +42 -172
- package/lib/guard-smtp-command.js +48 -54
- package/lib/guard-snapshot-envelope.js +24 -42
- package/lib/guard-sql.js +1491 -0
- package/lib/guard-stream-args.js +24 -43
- package/lib/guard-svg.js +47 -65
- package/lib/guard-template.js +35 -172
- package/lib/guard-tenant-id.js +26 -45
- package/lib/guard-time.js +32 -154
- package/lib/guard-trace-context.js +25 -44
- package/lib/guard-uuid.js +32 -153
- package/lib/guard-xml.js +38 -113
- package/lib/guard-yaml.js +51 -163
- package/lib/http-client.js +37 -9
- package/lib/inbox.js +120 -107
- package/lib/legal-hold.js +107 -50
- package/lib/log-stream-cloudwatch.js +47 -31
- package/lib/log-stream-otlp.js +32 -18
- package/lib/mail-crypto-smime.js +2 -6
- package/lib/mail-greylist.js +2 -6
- package/lib/mail-helo.js +2 -6
- package/lib/mail-journal.js +85 -64
- package/lib/mail-rbl.js +2 -6
- package/lib/mail-scan.js +2 -6
- package/lib/mail-server-jmap.js +117 -12
- package/lib/mail-spam-score.js +2 -6
- package/lib/mail-store.js +287 -154
- package/lib/middleware/body-parser.js +71 -25
- package/lib/middleware/csrf-protect.js +19 -8
- package/lib/middleware/fetch-metadata.js +17 -7
- package/lib/middleware/idempotency-key.js +54 -38
- package/lib/middleware/rate-limit.js +102 -32
- package/lib/middleware/security-headers.js +21 -5
- package/lib/migrations.js +108 -66
- package/lib/network-heartbeat.js +7 -0
- package/lib/nonce-store.js +31 -9
- package/lib/object-store/azure-blob-bucket-ops.js +9 -4
- package/lib/object-store/azure-blob.js +57 -3
- package/lib/object-store/sigv4.js +10 -0
- package/lib/observability.js +87 -0
- package/lib/otel-export.js +25 -1
- package/lib/outbox.js +136 -82
- package/lib/parsers/safe-xml.js +47 -7
- package/lib/pqc-agent.js +44 -0
- package/lib/pubsub-cluster.js +42 -20
- package/lib/queue-local.js +202 -139
- package/lib/queue-redis.js +9 -1
- package/lib/queue-sqs.js +6 -0
- package/lib/redact.js +68 -11
- package/lib/redis-client.js +160 -31
- package/lib/retention.js +82 -39
- package/lib/router.js +212 -5
- package/lib/safe-dns.js +29 -45
- package/lib/safe-ical.js +18 -33
- package/lib/safe-icap.js +27 -43
- package/lib/safe-sieve.js +21 -40
- package/lib/safe-sql.js +124 -3
- package/lib/safe-vcard.js +18 -33
- package/lib/scheduler.js +35 -12
- package/lib/seeders.js +122 -74
- package/lib/session-stores.js +42 -14
- package/lib/session.js +109 -72
- package/lib/sql.js +3885 -0
- package/lib/ssrf-guard.js +51 -4
- package/lib/static.js +177 -34
- package/lib/subject.js +55 -17
- package/lib/vault/index.js +3 -2
- package/lib/vault/passphrase-ops.js +3 -2
- package/lib/vault/rotate.js +104 -64
- package/lib/vendor-data.js +2 -0
- package/lib/websocket.js +35 -5
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/guard-domain.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
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
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
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 =
|
|
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
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
+
});
|
package/lib/guard-email.js
CHANGED
|
@@ -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
|
-
|
|
861
|
-
|
|
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
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
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
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
}
|
|
972
|
-
|
|
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
|
+
});
|
package/lib/guard-envelope.js
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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 =
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
+
});
|