@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-markdown.js
CHANGED
|
@@ -589,12 +589,8 @@ function sanitize(input, opts) {
|
|
|
589
589
|
throw _err("markdown.bad-input", "sanitize requires string input");
|
|
590
590
|
}
|
|
591
591
|
var issues = _detectIssues(input, opts);
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
throw _err(issues[i].ruleId || "markdown.refused",
|
|
595
|
-
"guardMarkdown.sanitize: " + issues[i].snippet);
|
|
596
|
-
}
|
|
597
|
-
}
|
|
592
|
+
gateContract.throwOnRefusalSeverity(issues,
|
|
593
|
+
{ errorClass: GuardMarkdownError, codePrefix: "markdown", severities: ["critical"] });
|
|
598
594
|
return codepointClass.applyCharStripPolicies(input, opts);
|
|
599
595
|
}
|
|
600
596
|
|
|
@@ -667,102 +663,39 @@ function gate(opts) {
|
|
|
667
663
|
});
|
|
668
664
|
}
|
|
669
665
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
*
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
* var custom = b.guardMarkdown.buildProfile({
|
|
687
|
-
* extends: "balanced",
|
|
688
|
-
* dangerousTagPolicy: "strip",
|
|
689
|
-
* schemeAllowlist: ["http", "https"],
|
|
690
|
-
* });
|
|
691
|
-
* custom.dangerousTagPolicy; // → "strip"
|
|
692
|
-
* custom.schemeAllowlist.indexOf("mailto"); // → -1
|
|
693
|
-
*/
|
|
694
|
-
var buildProfile = gateContract.makeProfileBuilder(PROFILES);
|
|
695
|
-
|
|
696
|
-
/**
|
|
697
|
-
* @primitive b.guardMarkdown.compliancePosture
|
|
698
|
-
* @signature b.guardMarkdown.compliancePosture(name)
|
|
699
|
-
* @since 0.7.16
|
|
700
|
-
* @status stable
|
|
701
|
-
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
702
|
-
* @related b.guardMarkdown.gate, b.guardMarkdown.buildProfile
|
|
703
|
-
*
|
|
704
|
-
* Look up a compliance-posture overlay by name (`"hipaa"` /
|
|
705
|
-
* `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of
|
|
706
|
-
* the posture object — the caller may mutate freely. Throws
|
|
707
|
-
* `GuardMarkdownError("markdown.bad-posture")` on unknown name.
|
|
708
|
-
*
|
|
709
|
-
* @example
|
|
710
|
-
* var posture = b.guardMarkdown.compliancePosture("hipaa");
|
|
711
|
-
* posture.dangerousTagPolicy; // → "reject"
|
|
712
|
-
*/
|
|
713
|
-
function compliancePosture(name) {
|
|
714
|
-
return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES, _err, "markdown");
|
|
715
|
-
}
|
|
666
|
+
// buildProfile / compliancePosture / loadRulePack are assembled by
|
|
667
|
+
// gateContract.defineGuard below (makeProfileBuilder(PROFILES) /
|
|
668
|
+
// lookupCompliancePosture(_, COMPLIANCE_POSTURES) / makeRulePackLoader).
|
|
669
|
+
// Their wiki sections render from the single-sourced @abiTemplate blocks
|
|
670
|
+
// in gate-contract.js, instantiated per guard by the page generator.
|
|
671
|
+
|
|
672
|
+
var INTEGRATION_FIXTURES = Object.freeze({
|
|
673
|
+
kind: "content",
|
|
674
|
+
contentType: "text/markdown",
|
|
675
|
+
extension: ".md",
|
|
676
|
+
benignBytes: Buffer.from(
|
|
677
|
+
"# Title\n\nA [link](https://example.com) and *emphasis*.\n", "utf8"),
|
|
678
|
+
// Hostile: link with javascript: scheme — CVE-2025-9540 class.
|
|
679
|
+
hostileBytes: Buffer.from(
|
|
680
|
+
"# x\n\n[click](javascript:alert(1))\n", "utf8"),
|
|
681
|
+
});
|
|
716
682
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
* });
|
|
737
|
-
* pack.id; // → "wiki-internal"
|
|
738
|
-
*/
|
|
739
|
-
var loadRulePack = _markdownRulePacks.load;
|
|
740
|
-
|
|
741
|
-
module.exports = {
|
|
742
|
-
// ---- guard-* family registry exports ----
|
|
743
|
-
NAME: "markdown",
|
|
744
|
-
KIND: "content",
|
|
745
|
-
MIME_TYPES: Object.freeze(["text/markdown", "text/x-markdown", "text/x-gfm"]),
|
|
746
|
-
EXTENSIONS: Object.freeze([".md", ".markdown"]),
|
|
747
|
-
INTEGRATION_FIXTURES: Object.freeze({
|
|
748
|
-
kind: "content",
|
|
749
|
-
contentType: "text/markdown",
|
|
750
|
-
extension: ".md",
|
|
751
|
-
benignBytes: Buffer.from(
|
|
752
|
-
"# Title\n\nA [link](https://example.com) and *emphasis*.\n", "utf8"),
|
|
753
|
-
// Hostile: link with javascript: scheme — CVE-2025-9540 class.
|
|
754
|
-
hostileBytes: Buffer.from(
|
|
755
|
-
"# x\n\n[click](javascript:alert(1))\n", "utf8"),
|
|
756
|
-
}),
|
|
757
|
-
// ---- primitive surface ----
|
|
758
|
-
validate: validate,
|
|
759
|
-
sanitize: sanitize,
|
|
760
|
-
gate: gate,
|
|
761
|
-
buildProfile: buildProfile,
|
|
762
|
-
compliancePosture: compliancePosture,
|
|
763
|
-
loadRulePack: loadRulePack,
|
|
764
|
-
PROFILES: PROFILES,
|
|
765
|
-
DEFAULTS: DEFAULTS,
|
|
766
|
-
COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
|
|
767
|
-
GuardMarkdownError: GuardMarkdownError,
|
|
768
|
-
};
|
|
683
|
+
// Assembled from the gate-contract guard factory: error class, registry
|
|
684
|
+
// exports (NAME / KIND / MIME_TYPES / EXTENSIONS / INTEGRATION_FIXTURES),
|
|
685
|
+
// buildProfile / compliancePosture / loadRulePack wiring, plus the
|
|
686
|
+
// per-guard inspection surface (validate / sanitize). The bespoke `gate`
|
|
687
|
+
// carries markdown's sanitize-and-reemit chain unchanged.
|
|
688
|
+
module.exports = gateContract.defineGuard({
|
|
689
|
+
name: "markdown",
|
|
690
|
+
kind: "content",
|
|
691
|
+
errorClass: GuardMarkdownError,
|
|
692
|
+
profiles: PROFILES,
|
|
693
|
+
defaults: DEFAULTS,
|
|
694
|
+
postures: COMPLIANCE_POSTURES,
|
|
695
|
+
mimeTypes: ["text/markdown", "text/x-markdown", "text/x-gfm"],
|
|
696
|
+
extensions: [".md", ".markdown"],
|
|
697
|
+
integrationFixtures: INTEGRATION_FIXTURES,
|
|
698
|
+
validate: validate,
|
|
699
|
+
sanitize: sanitize,
|
|
700
|
+
gate: gate,
|
|
701
|
+
});
|
package/lib/guard-message-id.js
CHANGED
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
*/
|
|
51
51
|
|
|
52
52
|
var { defineClass } = require("./framework-error");
|
|
53
|
+
var gateContract = require("./gate-contract");
|
|
53
54
|
|
|
54
55
|
var GuardMessageIdError = defineClass("GuardMessageIdError", { alwaysPermanent: true });
|
|
55
56
|
|
|
@@ -61,12 +62,7 @@ var PROFILES = Object.freeze({
|
|
|
61
62
|
permissive: { requireBrackets: false, maxBytes: 4096 }, // permissive cap, not bytes-as-storage
|
|
62
63
|
});
|
|
63
64
|
|
|
64
|
-
var COMPLIANCE_POSTURES =
|
|
65
|
-
hipaa: "strict",
|
|
66
|
-
"pci-dss": "strict",
|
|
67
|
-
gdpr: "strict",
|
|
68
|
-
soc2: "strict",
|
|
69
|
-
});
|
|
65
|
+
var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
|
|
70
66
|
|
|
71
67
|
// Bidi codepoints refused — same set the framework's address-bidi
|
|
72
68
|
// defense uses (RFC 5322 §3.6.4 doesn't speak EAI codepoints, but RTL
|
|
@@ -225,43 +221,28 @@ function validateList(value, opts) {
|
|
|
225
221
|
return ids;
|
|
226
222
|
}
|
|
227
223
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
* b.guardMessageId.compliancePosture("unknown"); // → null
|
|
241
|
-
*/
|
|
242
|
-
function compliancePosture(posture) {
|
|
243
|
-
return COMPLIANCE_POSTURES[posture] || null;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function _resolveProfile(opts) {
|
|
247
|
-
if (opts.posture && COMPLIANCE_POSTURES[opts.posture]) {
|
|
248
|
-
return COMPLIANCE_POSTURES[opts.posture];
|
|
249
|
-
}
|
|
250
|
-
var p = opts.profile || DEFAULT_PROFILE;
|
|
251
|
-
if (!PROFILES[p]) {
|
|
252
|
-
throw new GuardMessageIdError("message-id/bad-profile",
|
|
253
|
-
"guardMessageId: unknown profile '" + p + "' (use strict / balanced / permissive)");
|
|
254
|
-
}
|
|
255
|
-
return p;
|
|
256
|
-
}
|
|
224
|
+
// compliancePosture is assembled by gateContract.defineParser below; its
|
|
225
|
+
// wiki section renders from the single-sourced @abiTemplate (defineParser)
|
|
226
|
+
// block in gate-contract.js, instantiated for this guard by the page
|
|
227
|
+
// generator.
|
|
228
|
+
|
|
229
|
+
var _resolveProfile = gateContract.makeProfileResolver({
|
|
230
|
+
profiles: PROFILES,
|
|
231
|
+
postures: COMPLIANCE_POSTURES,
|
|
232
|
+
defaults: DEFAULT_PROFILE,
|
|
233
|
+
errorClass: GuardMessageIdError,
|
|
234
|
+
codePrefix: "message-id",
|
|
235
|
+
});
|
|
257
236
|
|
|
258
|
-
module.exports = {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
237
|
+
module.exports = gateContract.defineParser({
|
|
238
|
+
name: "message-id",
|
|
239
|
+
entry: validate,
|
|
240
|
+
errorClass: GuardMessageIdError,
|
|
241
|
+
profiles: PROFILES,
|
|
242
|
+
postures: COMPLIANCE_POSTURES,
|
|
243
|
+
extra: {
|
|
244
|
+
validateList: validateList,
|
|
245
|
+
NAME: "messageId",
|
|
246
|
+
KIND: "identifier",
|
|
247
|
+
},
|
|
248
|
+
});
|
package/lib/guard-mime.js
CHANGED
|
@@ -443,12 +443,9 @@ function sanitize(input, opts) {
|
|
|
443
443
|
throw _err("mime.bad-input", "sanitize requires string input");
|
|
444
444
|
}
|
|
445
445
|
var issues = _detectIssues(input, opts);
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
"guardMime.sanitize: " + issues[i].snippet);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
446
|
+
gateContract.throwOnRefusalSeverity(issues, {
|
|
447
|
+
errorClass: GuardMimeError, codePrefix: "mime",
|
|
448
|
+
});
|
|
452
449
|
// Normalize: lowercase the type/subtype; preserve parameter case
|
|
453
450
|
// because some parameter values are case-significant (e.g. boundary
|
|
454
451
|
// tokens in multipart/form-data).
|
|
@@ -459,151 +456,42 @@ function sanitize(input, opts) {
|
|
|
459
456
|
}, canonical);
|
|
460
457
|
}
|
|
461
458
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
468
|
-
* @related b.guardMime.validate, b.guardMime.sanitize, b.guardAll.gate
|
|
469
|
-
*
|
|
470
|
-
* Build a guard gate whose async `check(ctx)` returns `{ ok, action, issues }`, consumable
|
|
471
|
-
* by `b.guardAll`, `b.staticServe`, `b.fileUpload`, and any other
|
|
472
|
-
* host that integrates the guard contract. The gate reads
|
|
473
|
-
* `ctx.identifier` (or `ctx.mime`), runs `validate`, and maps
|
|
474
|
-
* severity to action: zero issues `serve`; only low/medium
|
|
475
|
-
* `audit-only`; any high/critical `refuse`.
|
|
476
|
-
*
|
|
477
|
-
* @opts
|
|
478
|
-
* name: string, // gate label for audit / observability
|
|
479
|
-
* profile: "strict"|"balanced"|"permissive",
|
|
480
|
-
* compliancePosture: "hipaa"|"pci-dss"|"gdpr"|"soc2",
|
|
481
|
-
* ...: same shape as b.guardMime.validate opts,
|
|
482
|
-
*
|
|
483
|
-
* @example
|
|
484
|
-
* var g = b.guardMime.gate({ profile: "strict" });
|
|
485
|
-
* var rv = await g.check({ identifier: "application/json" });
|
|
486
|
-
* rv.action; // → "serve"
|
|
487
|
-
*
|
|
488
|
-
* var bad = await g.check({ identifier: "application/x-msdownload" });
|
|
489
|
-
* bad.action; // → "refuse"
|
|
490
|
-
*/
|
|
491
|
-
function gate(opts) {
|
|
492
|
-
opts = _resolveOpts(opts);
|
|
493
|
-
return gateContract.buildGuardGate(
|
|
494
|
-
opts.name || "guardMime:" + (opts.profile || "default"),
|
|
495
|
-
opts,
|
|
496
|
-
async function (ctx) {
|
|
497
|
-
var identifier = ctx && (ctx.identifier || ctx.mime || "");
|
|
498
|
-
if (!identifier) return { ok: true, action: "serve" };
|
|
499
|
-
var rv = validate(identifier, opts);
|
|
500
|
-
if (rv.issues.length === 0) return { ok: true, action: "serve" };
|
|
501
|
-
var hasCritical = rv.issues.some(function (i) {
|
|
502
|
-
return i.severity === "critical";
|
|
503
|
-
});
|
|
504
|
-
var hasHigh = rv.issues.some(function (i) {
|
|
505
|
-
return i.severity === "high";
|
|
506
|
-
});
|
|
507
|
-
if (!hasCritical && !hasHigh) {
|
|
508
|
-
return { ok: true, action: "audit-only", issues: rv.issues };
|
|
509
|
-
}
|
|
510
|
-
return { ok: false, action: "refuse", issues: rv.issues };
|
|
511
|
-
});
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
/**
|
|
515
|
-
* @primitive b.guardMime.buildProfile
|
|
516
|
-
* @signature b.guardMime.buildProfile(opts)
|
|
517
|
-
* @since 0.7.47
|
|
518
|
-
* @status stable
|
|
519
|
-
* @related b.guardMime.gate, b.guardMime.compliancePosture
|
|
520
|
-
*
|
|
521
|
-
* Compose a derived profile from one or more named bases plus
|
|
522
|
-
* inline overrides. `opts.extends` is a profile name or array of
|
|
523
|
-
* names (later entries shadow earlier ones); inline keys win last.
|
|
524
|
-
*
|
|
525
|
-
* @opts
|
|
526
|
-
* extends: string|string[], // base profile name(s) to compose
|
|
527
|
-
* ...: any guard-mime key, // inline override of resolved keys
|
|
528
|
-
*
|
|
529
|
-
* @example
|
|
530
|
-
* var custom = b.guardMime.buildProfile({
|
|
531
|
-
* extends: "balanced",
|
|
532
|
-
* vendorTreePolicy: "audit",
|
|
533
|
-
* });
|
|
534
|
-
* custom.bidiPolicy; // → "reject"
|
|
535
|
-
* custom.vendorTreePolicy; // → "audit"
|
|
536
|
-
*/
|
|
537
|
-
var buildProfile = gateContract.makeProfileBuilder(PROFILES);
|
|
459
|
+
// The request-boundary gate is the gate-contract factory default: it reads
|
|
460
|
+
// `ctx.identifier` (or `ctx.mime`), runs `validate`, and maps severity to
|
|
461
|
+
// action — `serve` (no issue) / `audit-only` (info / warn) / `refuse` (any
|
|
462
|
+
// high / critical). Its wiki section renders from the single-sourced
|
|
463
|
+
// `@abiTemplate gate` block in gate-contract.js.
|
|
538
464
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
545
|
-
* @related b.guardMime.gate, b.guardMime.buildProfile
|
|
546
|
-
*
|
|
547
|
-
* Look up a compliance-posture overlay by name (`"hipaa"` /
|
|
548
|
-
* `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of
|
|
549
|
-
* the posture object — the caller may mutate freely. Throws
|
|
550
|
-
* `GuardMimeError("mime.bad-posture")` on unknown name.
|
|
551
|
-
*
|
|
552
|
-
* @example
|
|
553
|
-
* var posture = b.guardMime.compliancePosture("pci-dss");
|
|
554
|
-
* posture.riskyTypesPolicy; // → "reject"
|
|
555
|
-
*/
|
|
556
|
-
function compliancePosture(name) {
|
|
557
|
-
return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES,
|
|
558
|
-
_err, "mime");
|
|
559
|
-
}
|
|
465
|
+
// buildProfile / compliancePosture / loadRulePack are assembled by
|
|
466
|
+
// gateContract.defineGuard below (makeProfileBuilder(PROFILES) /
|
|
467
|
+
// lookupCompliancePosture(_, COMPLIANCE_POSTURES) / makeRulePackLoader).
|
|
468
|
+
// Their wiki sections render from the single-sourced @abiTemplate blocks
|
|
469
|
+
// in gate-contract.js, instantiated per guard by the page generator.
|
|
560
470
|
|
|
561
|
-
var
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
* registry. The pack is identified by `pack.id` (non-empty
|
|
571
|
-
* string) and stored for later inspection / dispatch by gates
|
|
572
|
-
* that opt in via `opts.rulePackId`. Returns the pack object
|
|
573
|
-
* unchanged on success; throws `GuardMimeError("mime.bad-opt")`
|
|
574
|
-
* when `pack` is missing or `pack.id` is not a non-empty string.
|
|
575
|
-
*
|
|
576
|
-
* @example
|
|
577
|
-
* var pack = b.guardMime.loadRulePack({
|
|
578
|
-
* id: "operator-deny-flash",
|
|
579
|
-
* deny: ["application/x-shockwave-flash"],
|
|
580
|
-
* });
|
|
581
|
-
* pack.id; // → "operator-deny-flash"
|
|
582
|
-
*/
|
|
583
|
-
var loadRulePack = _mimeRulePacks.load;
|
|
471
|
+
var INTEGRATION_FIXTURES = Object.freeze({
|
|
472
|
+
kind: "identifier",
|
|
473
|
+
benignBytes: Buffer.from("application/json", "utf8"),
|
|
474
|
+
hostileBytes: Buffer.from("application/x-msdownload", "utf8"),
|
|
475
|
+
benignIdentifier: "application/json",
|
|
476
|
+
// Hostile: risky-type — refused at strict (executable script-host
|
|
477
|
+
// class).
|
|
478
|
+
hostileIdentifier: "application/x-msdownload",
|
|
479
|
+
});
|
|
584
480
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
buildProfile: buildProfile,
|
|
603
|
-
compliancePosture: compliancePosture,
|
|
604
|
-
loadRulePack: loadRulePack,
|
|
605
|
-
PROFILES: PROFILES,
|
|
606
|
-
DEFAULTS: DEFAULTS,
|
|
607
|
-
COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
|
|
608
|
-
GuardMimeError: GuardMimeError,
|
|
609
|
-
};
|
|
481
|
+
// Assembled from the gate-contract guard factory: error class, registry
|
|
482
|
+
// exports (NAME / KIND / INTEGRATION_FIXTURES), buildProfile /
|
|
483
|
+
// compliancePosture / loadRulePack wiring, plus the per-guard inspection
|
|
484
|
+
// surface (validate / sanitize). The gate is the factory default chain,
|
|
485
|
+
// dispatched to `ctx.identifier` / `ctx.mime` via ctxFields.
|
|
486
|
+
module.exports = gateContract.defineGuard({
|
|
487
|
+
name: "mime",
|
|
488
|
+
kind: "identifier",
|
|
489
|
+
errorClass: GuardMimeError,
|
|
490
|
+
profiles: PROFILES,
|
|
491
|
+
defaults: DEFAULTS,
|
|
492
|
+
postures: COMPLIANCE_POSTURES,
|
|
493
|
+
integrationFixtures: INTEGRATION_FIXTURES,
|
|
494
|
+
validate: validate,
|
|
495
|
+
sanitize: sanitize,
|
|
496
|
+
ctxFields: ["identifier", "mime"],
|
|
497
|
+
});
|
package/lib/guard-oauth.js
CHANGED
|
@@ -89,8 +89,6 @@ var { GuardOauthError } = require("./framework-error");
|
|
|
89
89
|
var observability = lazyRequire(function () { return require("./observability"); });
|
|
90
90
|
void observability;
|
|
91
91
|
|
|
92
|
-
var _err = GuardOauthError.factory;
|
|
93
|
-
|
|
94
92
|
var SCOPE_TOKEN_RE = /^[\x21\x23-\x5b\x5d-\x7e]+$/; // RFC 6749 §3.3 scope-token charset
|
|
95
93
|
var DEFAULT_RESPONSE_TYPES = Object.freeze(["code"]);
|
|
96
94
|
|
|
@@ -446,12 +444,7 @@ function sanitize(input, opts) {
|
|
|
446
444
|
// OAuth flows can't be repaired — sanitize either passes through
|
|
447
445
|
// valid input or throws.
|
|
448
446
|
var issues = _detectIssues(input, opts);
|
|
449
|
-
|
|
450
|
-
if (issues[i].severity === "critical" || issues[i].severity === "high") {
|
|
451
|
-
throw _err(issues[i].ruleId || "oauth.refused",
|
|
452
|
-
"guardOauth.sanitize: " + issues[i].snippet);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
447
|
+
gateContract.throwOnRefusalSeverity(issues, { errorClass: GuardOauthError, codePrefix: "oauth" });
|
|
455
448
|
return input;
|
|
456
449
|
}
|
|
457
450
|
|
|
@@ -519,132 +512,58 @@ function gate(opts) {
|
|
|
519
512
|
});
|
|
520
513
|
}
|
|
521
514
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
* @since 0.7.49
|
|
526
|
-
* @status stable
|
|
527
|
-
* @related b.guardOauth.gate, b.guardOauth.compliancePosture
|
|
528
|
-
*
|
|
529
|
-
* Compose a derived profile from one or more named bases plus
|
|
530
|
-
* inline overrides. `opts.extends` is a profile name (`"strict"` /
|
|
531
|
-
* `"balanced"` / `"permissive"`) or an array of names; later
|
|
532
|
-
* entries shadow earlier ones, and inline `opts` keys win last.
|
|
533
|
-
* Operators stage profile overlays here so the final shape is
|
|
534
|
-
* traceable to a baseline rather than a hand-typed dictionary.
|
|
535
|
-
*
|
|
536
|
-
* @opts
|
|
537
|
-
* extends: string|string[], // base profile name(s) to compose
|
|
538
|
-
* ...: any guardOauth key, // inline override of resolved keys
|
|
539
|
-
*
|
|
540
|
-
* @example
|
|
541
|
-
* var custom = b.guardOauth.buildProfile({
|
|
542
|
-
* extends: "balanced",
|
|
543
|
-
* pkcePolicy: "require-s256",
|
|
544
|
-
* allowedResponseTypes: ["code"],
|
|
545
|
-
* });
|
|
546
|
-
* custom.pkcePolicy; // → "require-s256"
|
|
547
|
-
* custom.allowedResponseTypes.length; // → 1
|
|
548
|
-
*/
|
|
549
|
-
var buildProfile = gateContract.makeProfileBuilder(PROFILES);
|
|
550
|
-
|
|
551
|
-
/**
|
|
552
|
-
* @primitive b.guardOauth.compliancePosture
|
|
553
|
-
* @signature b.guardOauth.compliancePosture(name)
|
|
554
|
-
* @since 0.7.49
|
|
555
|
-
* @status stable
|
|
556
|
-
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
557
|
-
* @related b.guardOauth.gate, b.guardOauth.buildProfile
|
|
558
|
-
*
|
|
559
|
-
* Look up a compliance-posture overlay by name (`"hipaa"` /
|
|
560
|
-
* `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of
|
|
561
|
-
* the posture object — the caller may mutate freely. Throws
|
|
562
|
-
* `GuardOauthError("oauth.bad-posture")` on unknown name.
|
|
563
|
-
* Postures extend the strict profile (or balanced for `gdpr`)
|
|
564
|
-
* with a `forensicSnippetBytes` cap appropriate to the regime.
|
|
565
|
-
*
|
|
566
|
-
* @example
|
|
567
|
-
* var posture = b.guardOauth.compliancePosture("pci-dss");
|
|
568
|
-
* posture.pkcePolicy; // → "require-s256"
|
|
569
|
-
* posture.forensicSnippetBytes; // → 256
|
|
570
|
-
*/
|
|
571
|
-
function compliancePosture(name) {
|
|
572
|
-
return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES,
|
|
573
|
-
_err, "oauth");
|
|
574
|
-
}
|
|
515
|
+
// buildProfile / compliancePosture / loadRulePack are assembled by
|
|
516
|
+
// gateContract.defineGuard below — their wiki sections render from the
|
|
517
|
+
// single-sourced @abiTemplate blocks in gate-contract.js.
|
|
575
518
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
519
|
+
// ---- adaptive integration-test fixtures (consumed by layer-5 host harness) ----
|
|
520
|
+
var INTEGRATION_FIXTURES = Object.freeze({
|
|
521
|
+
kind: "oauth-flow",
|
|
522
|
+
benignBytes: Buffer.from(JSON.stringify({
|
|
523
|
+
response_type: "code",
|
|
524
|
+
redirect_uri: "https://app.example.com/callback",
|
|
525
|
+
state: "csrf-rand-1",
|
|
526
|
+
scope: "openid profile",
|
|
527
|
+
code_challenge: "abc123def456ghi789jkl012mno345pqr678", // base64url-shaped fixture
|
|
528
|
+
code_challenge_method: "S256",
|
|
529
|
+
}), "utf8"),
|
|
530
|
+
hostileBytes: Buffer.from(JSON.stringify({
|
|
531
|
+
response_type: "code",
|
|
532
|
+
redirect_uri: "https://attacker.example/callback",
|
|
533
|
+
// state missing — CSRF class
|
|
534
|
+
scope: "openid",
|
|
535
|
+
}), "utf8"),
|
|
536
|
+
benignOauthFlow: {
|
|
537
|
+
response_type: "code",
|
|
538
|
+
redirect_uri: "https://app.example.com/callback",
|
|
539
|
+
state: "csrf-rand-1",
|
|
540
|
+
scope: "openid profile",
|
|
541
|
+
code_challenge: "abc123def456ghi789jkl012mno345pqr678", // base64url-shaped fixture
|
|
542
|
+
code_challenge_method: "S256",
|
|
543
|
+
},
|
|
544
|
+
hostileOauthFlow: {
|
|
545
|
+
response_type: "code",
|
|
546
|
+
redirect_uri: "https://attacker.example/callback",
|
|
547
|
+
// state missing → state-missing refuse
|
|
548
|
+
scope: "openid",
|
|
549
|
+
},
|
|
550
|
+
});
|
|
603
551
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
scope: "openid",
|
|
623
|
-
}), "utf8"),
|
|
624
|
-
benignOauthFlow: {
|
|
625
|
-
response_type: "code",
|
|
626
|
-
redirect_uri: "https://app.example.com/callback",
|
|
627
|
-
state: "csrf-rand-1",
|
|
628
|
-
scope: "openid profile",
|
|
629
|
-
code_challenge: "abc123def456ghi789jkl012mno345pqr678", // base64url-shaped fixture
|
|
630
|
-
code_challenge_method: "S256",
|
|
631
|
-
},
|
|
632
|
-
hostileOauthFlow: {
|
|
633
|
-
response_type: "code",
|
|
634
|
-
redirect_uri: "https://attacker.example/callback",
|
|
635
|
-
// state missing → state-missing refuse
|
|
636
|
-
scope: "openid",
|
|
637
|
-
},
|
|
638
|
-
}),
|
|
639
|
-
// ---- primitive surface ----
|
|
640
|
-
validate: validate,
|
|
641
|
-
sanitize: sanitize,
|
|
642
|
-
gate: gate,
|
|
643
|
-
buildProfile: buildProfile,
|
|
644
|
-
compliancePosture: compliancePosture,
|
|
645
|
-
loadRulePack: loadRulePack,
|
|
646
|
-
PROFILES: PROFILES,
|
|
647
|
-
DEFAULTS: DEFAULTS,
|
|
648
|
-
COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
|
|
649
|
-
GuardOauthError: GuardOauthError,
|
|
650
|
-
};
|
|
552
|
+
// Assembled from the gate-contract guard factory: error class, registry
|
|
553
|
+
// exports (NAME / KIND / INTEGRATION_FIXTURES), buildProfile /
|
|
554
|
+
// compliancePosture / loadRulePack wiring, plus the per-guard inspection
|
|
555
|
+
// surface (validate / sanitize / bespoke gate) passed through verbatim.
|
|
556
|
+
// The custom KIND ("oauth-flow") is accepted because the bespoke gate
|
|
557
|
+
// reads its own ctx fields (ctx.oauthFlow / ctx.flow).
|
|
558
|
+
module.exports = gateContract.defineGuard({
|
|
559
|
+
name: "oauth",
|
|
560
|
+
kind: "oauth-flow",
|
|
561
|
+
errorClass: GuardOauthError,
|
|
562
|
+
profiles: PROFILES,
|
|
563
|
+
defaults: DEFAULTS,
|
|
564
|
+
postures: COMPLIANCE_POSTURES,
|
|
565
|
+
integrationFixtures: INTEGRATION_FIXTURES,
|
|
566
|
+
validate: validate,
|
|
567
|
+
sanitize: sanitize,
|
|
568
|
+
gate: gate,
|
|
569
|
+
});
|