@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-jsonpath.js
CHANGED
|
@@ -333,180 +333,47 @@ function sanitize(input, opts) {
|
|
|
333
333
|
throw _err("jsonpath.bad-input", "sanitize requires string input");
|
|
334
334
|
}
|
|
335
335
|
var issues = _detectIssues(input, opts);
|
|
336
|
-
|
|
337
|
-
if (issues[i].severity === "critical" || issues[i].severity === "high") {
|
|
338
|
-
throw _err(issues[i].ruleId || "jsonpath.refused",
|
|
339
|
-
"guardJsonpath.sanitize: " + issues[i].snippet);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
336
|
+
gateContract.throwOnRefusalSeverity(issues, { errorClass: GuardJsonpathError, codePrefix: "jsonpath" });
|
|
342
337
|
return input;
|
|
343
338
|
}
|
|
344
339
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
*
|
|
353
|
-
* Build a `b.gateContract` gate that screens `ctx.identifier` (or
|
|
354
|
-
* `ctx.jsonpath`) before the path reaches a JSONPath evaluator.
|
|
355
|
-
* Action chain: `serve` (no issues) → `audit-only` (warn-only) →
|
|
356
|
-
* `refuse` (any `critical` or `high`). No `sanitize` action —
|
|
357
|
-
* JSONPath strings cannot be repaired. Compose into query
|
|
358
|
-
* endpoints / search filters / data-export flows so operator-fed
|
|
359
|
-
* paths hit the guard before any evaluator dispatch.
|
|
360
|
-
*
|
|
361
|
-
* @opts
|
|
362
|
-
* profile: "strict"|"balanced"|"permissive",
|
|
363
|
-
* compliancePosture: "hipaa"|"pci-dss"|"gdpr"|"soc2",
|
|
364
|
-
* name: string, // override gate name in audit emissions
|
|
365
|
-
* filterExprPolicy: "reject"|"audit"|"allow",
|
|
366
|
-
* scriptExprPolicy: "reject"|"audit"|"allow",
|
|
367
|
-
* dynamicHintPolicy: "reject"|"audit"|"allow",
|
|
368
|
-
* bracketNestingPolicy: "reject"|"audit"|"allow",
|
|
369
|
-
* recursiveDescentPolicy: "reject"|"audit"|"allow",
|
|
370
|
-
* maxRecursiveDescents: number,
|
|
371
|
-
* maxPatternBytes: number,
|
|
372
|
-
*
|
|
373
|
-
* @example
|
|
374
|
-
* var gate = b.guardJsonpath.gate({ profile: "strict" });
|
|
375
|
-
*
|
|
376
|
-
* gate({ identifier: "$..[?(@.x)]" }).then(function (rv) {
|
|
377
|
-
* rv.ok; // → false
|
|
378
|
-
* rv.action; // → "refuse"
|
|
379
|
-
* });
|
|
380
|
-
*
|
|
381
|
-
* gate({ identifier: "$.users[*].name" }).then(function (rv) {
|
|
382
|
-
* rv.action; // → "serve"
|
|
383
|
-
* });
|
|
384
|
-
*/
|
|
385
|
-
function gate(opts) {
|
|
386
|
-
opts = _resolveOpts(opts);
|
|
387
|
-
return gateContract.buildGuardGate(
|
|
388
|
-
opts.name || "guardJsonpath:" + (opts.profile || "default"),
|
|
389
|
-
opts,
|
|
390
|
-
async function (ctx) {
|
|
391
|
-
var pattern = ctx && (ctx.identifier || ctx.jsonpath);
|
|
392
|
-
if (pattern === undefined || pattern === null) {
|
|
393
|
-
return { ok: true, action: "serve" };
|
|
394
|
-
}
|
|
395
|
-
var rv = validate(pattern, opts);
|
|
396
|
-
if (rv.issues.length === 0) return { ok: true, action: "serve" };
|
|
397
|
-
var hasCritical = rv.issues.some(function (i) {
|
|
398
|
-
return i.severity === "critical";
|
|
399
|
-
});
|
|
400
|
-
var hasHigh = rv.issues.some(function (i) {
|
|
401
|
-
return i.severity === "high";
|
|
402
|
-
});
|
|
403
|
-
if (!hasCritical && !hasHigh) {
|
|
404
|
-
return { ok: true, action: "audit-only", issues: rv.issues };
|
|
405
|
-
}
|
|
406
|
-
return { ok: false, action: "refuse", issues: rv.issues };
|
|
407
|
-
});
|
|
408
|
-
}
|
|
340
|
+
// The gate is the standard serve -> audit-only -> refuse chain; it is
|
|
341
|
+
// assembled by gateContract.defineGuard's default gate below. JSONPath
|
|
342
|
+
// strings can't be repaired, so there's no sanitize action — the default
|
|
343
|
+
// chain (no sanitize) matches exactly. The default gate reads the path
|
|
344
|
+
// from ctx.identifier || ctx.jsonpath via spec.ctxFields; its
|
|
345
|
+
// "guardJsonpath:<profile>" gate name and serve/audit-only/refuse
|
|
346
|
+
// decisions are identical to the hand-written gate this replaced.
|
|
409
347
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
* @related b.guardJsonpath.gate, b.guardJsonpath.compliancePosture
|
|
416
|
-
*
|
|
417
|
-
* Compose a derived guardJsonpath profile from one or more named
|
|
418
|
-
* bases plus inline overrides. `opts.extends` is a profile name
|
|
419
|
-
* (`"strict"` / `"balanced"` / `"permissive"`) or an array of
|
|
420
|
-
* names; later entries shadow earlier ones. Inline `opts` keys win
|
|
421
|
-
* last. Used to keep operator-defined profiles traceable to a
|
|
422
|
-
* baseline rather than re-typing every key.
|
|
423
|
-
*
|
|
424
|
-
* @opts
|
|
425
|
-
* extends: string|string[], // base profile name(s) to compose
|
|
426
|
-
* ...: any guardJsonpath key, // inline override of resolved keys
|
|
427
|
-
*
|
|
428
|
-
* @example
|
|
429
|
-
* var custom = b.guardJsonpath.buildProfile({
|
|
430
|
-
* extends: "balanced",
|
|
431
|
-
* maxRecursiveDescents: 1,
|
|
432
|
-
* recursiveDescentPolicy: "reject",
|
|
433
|
-
* });
|
|
434
|
-
* custom.maxRecursiveDescents; // → 1
|
|
435
|
-
* custom.filterExprPolicy; // → "reject"
|
|
436
|
-
*/
|
|
437
|
-
var buildProfile = gateContract.makeProfileBuilder(PROFILES);
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
* @primitive b.guardJsonpath.compliancePosture
|
|
441
|
-
* @signature b.guardJsonpath.compliancePosture(name)
|
|
442
|
-
* @since 0.7.13
|
|
443
|
-
* @status stable
|
|
444
|
-
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
445
|
-
* @related b.guardJsonpath.gate, b.guardJsonpath.buildProfile
|
|
446
|
-
*
|
|
447
|
-
* Look up a compliance-posture overlay by name (`"hipaa"` /
|
|
448
|
-
* `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of
|
|
449
|
-
* the posture object — the caller may mutate freely. Throws
|
|
450
|
-
* `GuardJsonpathError("jsonpath.bad-posture")` on unknown name.
|
|
451
|
-
*
|
|
452
|
-
* @example
|
|
453
|
-
* var posture = b.guardJsonpath.compliancePosture("hipaa");
|
|
454
|
-
* posture.filterExprPolicy; // → "reject"
|
|
455
|
-
* posture.forensicSnippetBytes; // → 256
|
|
456
|
-
*/
|
|
457
|
-
function compliancePosture(name) {
|
|
458
|
-
return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES,
|
|
459
|
-
_err, "jsonpath");
|
|
460
|
-
}
|
|
348
|
+
// buildProfile / compliancePosture / loadRulePack are assembled by
|
|
349
|
+
// gateContract.defineGuard below (makeProfileBuilder(PROFILES) /
|
|
350
|
+
// lookupCompliancePosture(_, COMPLIANCE_POSTURES) / makeRulePackLoader).
|
|
351
|
+
// Their wiki sections render from the single-sourced @abiTemplate blocks
|
|
352
|
+
// in gate-contract.js, instantiated per guard by the page generator.
|
|
461
353
|
|
|
462
|
-
var
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
*
|
|
470
|
-
* Register an operator-supplied rule pack with the guardJsonpath
|
|
471
|
-
* registry. The pack is identified by `pack.id` (non-empty string)
|
|
472
|
-
* and stored for later inspection / dispatch by gates that opt in
|
|
473
|
-
* via `opts.rulePackId`. Returns the pack object unchanged on
|
|
474
|
-
* success; throws `GuardJsonpathError("jsonpath.bad-opt")` when
|
|
475
|
-
* `pack` is missing or `pack.id` is not a non-empty string.
|
|
476
|
-
*
|
|
477
|
-
* @example
|
|
478
|
-
* var pack = b.guardJsonpath.loadRulePack({
|
|
479
|
-
* id: "no-wildcards",
|
|
480
|
-
* rules: [
|
|
481
|
-
* { id: "wildcard", severity: "high",
|
|
482
|
-
* detect: function (path) { return path.indexOf("[*]") !== -1; },
|
|
483
|
-
* reason: "wildcard index forbidden in this context" },
|
|
484
|
-
* ],
|
|
485
|
-
* });
|
|
486
|
-
* pack.id; // → "no-wildcards"
|
|
487
|
-
*/
|
|
488
|
-
var loadRulePack = _jpRulePacks.load;
|
|
354
|
+
var INTEGRATION_FIXTURES = Object.freeze({
|
|
355
|
+
kind: "identifier",
|
|
356
|
+
benignBytes: Buffer.from("$.users[*].name", "utf8"),
|
|
357
|
+
hostileBytes: Buffer.from("$..[?(@.x)]", "utf8"),
|
|
358
|
+
benignIdentifier: "$.users[*].name",
|
|
359
|
+
hostileIdentifier: "$..[?(@.x)]",
|
|
360
|
+
});
|
|
489
361
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
PROFILES: PROFILES,
|
|
509
|
-
DEFAULTS: DEFAULTS,
|
|
510
|
-
COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
|
|
511
|
-
GuardJsonpathError: GuardJsonpathError,
|
|
512
|
-
};
|
|
362
|
+
// Assembled from the gate-contract guard factory: error class, registry
|
|
363
|
+
// exports (NAME / KIND / INTEGRATION_FIXTURES), buildProfile /
|
|
364
|
+
// compliancePosture / loadRulePack wiring, plus the per-guard inspection
|
|
365
|
+
// surface (validate / sanitize). The gate is the factory default
|
|
366
|
+
// serve/audit-only/refuse chain, reading the path from
|
|
367
|
+
// `ctx.identifier` || `ctx.jsonpath` via `ctxFields`.
|
|
368
|
+
module.exports = gateContract.defineGuard({
|
|
369
|
+
name: "jsonpath",
|
|
370
|
+
kind: "identifier",
|
|
371
|
+
errorClass: GuardJsonpathError,
|
|
372
|
+
profiles: PROFILES,
|
|
373
|
+
defaults: DEFAULTS,
|
|
374
|
+
postures: COMPLIANCE_POSTURES,
|
|
375
|
+
integrationFixtures: INTEGRATION_FIXTURES,
|
|
376
|
+
validate: validate,
|
|
377
|
+
sanitize: sanitize,
|
|
378
|
+
ctxFields: ["identifier", "jsonpath"],
|
|
379
|
+
});
|
package/lib/guard-jwt.js
CHANGED
|
@@ -545,152 +545,17 @@ function sanitize(input, opts) {
|
|
|
545
545
|
// JWT shape can't be repaired — sanitize either passes through
|
|
546
546
|
// valid input or throws.
|
|
547
547
|
var issues = _detectIssues(input, opts);
|
|
548
|
-
|
|
549
|
-
if (issues[i].severity === "critical" || issues[i].severity === "high") {
|
|
550
|
-
throw _err(issues[i].ruleId || "jwt.refused",
|
|
551
|
-
"guardJwt.sanitize: " + issues[i].snippet);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
548
|
+
gateContract.throwOnRefusalSeverity(issues, { errorClass: GuardJwtError, codePrefix: "jwt" });
|
|
554
549
|
return input;
|
|
555
550
|
}
|
|
556
551
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
*
|
|
565
|
-
* Build a `gateContract.buildGuardGate`-shaped gate that pulls
|
|
566
|
-
* `ctx.identifier` (or `ctx.token` / `ctx.jwt`) and dispatches to
|
|
567
|
-
* `validate`. Returns `{ ok: true, action: "serve" }` when the
|
|
568
|
-
* issue list is empty, `{ ok: true, action: "audit-only", issues }`
|
|
569
|
-
* when only low-severity issues fire, and `{ ok: false, action:
|
|
570
|
-
* "refuse", issues }` on any `critical` / `high` issue. Compose
|
|
571
|
-
* into auth pipelines via `b.middleware.bearerAuth` so every
|
|
572
|
-
* bearer token is shape-checked before signature verification.
|
|
573
|
-
*
|
|
574
|
-
* @opts
|
|
575
|
-
* profile: "strict"|"balanced"|"permissive",
|
|
576
|
-
* compliancePosture: "hipaa"|"pci-dss"|"gdpr"|"soc2",
|
|
577
|
-
* name: string, // gate label for audit trails
|
|
578
|
-
* ...: every guardJwt.validate opt is honored,
|
|
579
|
-
*
|
|
580
|
-
* @example
|
|
581
|
-
* var jwtGate = b.guardJwt.gate({ profile: "strict" });
|
|
582
|
-
* var rv = await jwtGate.check({
|
|
583
|
-
* identifier:
|
|
584
|
-
* "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0." +
|
|
585
|
-
* "eyJzdWIiOiJhdHRhY2tlciJ9.",
|
|
586
|
-
* });
|
|
587
|
-
* rv.action; // → "refuse"
|
|
588
|
-
* rv.issues[0].ruleId; // → "jwt.alg-none"
|
|
589
|
-
*/
|
|
590
|
-
function gate(opts) {
|
|
591
|
-
opts = _resolveOpts(opts);
|
|
592
|
-
return gateContract.buildGuardGate(
|
|
593
|
-
opts.name || "guardJwt:" + (opts.profile || "default"),
|
|
594
|
-
opts,
|
|
595
|
-
async function (ctx) {
|
|
596
|
-
var identifier = ctx && (ctx.identifier || ctx.token || ctx.jwt || "");
|
|
597
|
-
if (!identifier) return { ok: true, action: "serve" };
|
|
598
|
-
var rv = validate(identifier, opts);
|
|
599
|
-
if (rv.issues.length === 0) return { ok: true, action: "serve" };
|
|
600
|
-
var hasCritical = rv.issues.some(function (i) {
|
|
601
|
-
return i.severity === "critical";
|
|
602
|
-
});
|
|
603
|
-
var hasHigh = rv.issues.some(function (i) {
|
|
604
|
-
return i.severity === "high";
|
|
605
|
-
});
|
|
606
|
-
if (!hasCritical && !hasHigh) {
|
|
607
|
-
return { ok: true, action: "audit-only", issues: rv.issues };
|
|
608
|
-
}
|
|
609
|
-
return { ok: false, action: "refuse", issues: rv.issues };
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* @primitive b.guardJwt.buildProfile
|
|
615
|
-
* @signature b.guardJwt.buildProfile(opts)
|
|
616
|
-
* @since 0.7.49
|
|
617
|
-
* @status stable
|
|
618
|
-
* @related b.guardJwt.gate, b.guardJwt.compliancePosture
|
|
619
|
-
*
|
|
620
|
-
* Compose a derived profile from one or more named bases plus
|
|
621
|
-
* inline overrides. `opts.extends` is a profile name (`"strict"` /
|
|
622
|
-
* `"balanced"` / `"permissive"`) or an array of names; later
|
|
623
|
-
* entries shadow earlier ones, and inline `opts` keys win last.
|
|
624
|
-
* Operators stage profile overlays here so the final shape is
|
|
625
|
-
* traceable to a baseline rather than a hand-typed dictionary.
|
|
626
|
-
*
|
|
627
|
-
* @opts
|
|
628
|
-
* extends: string|string[], // base profile name(s) to compose
|
|
629
|
-
* ...: any guardJwt key, // inline override of resolved keys
|
|
630
|
-
*
|
|
631
|
-
* @example
|
|
632
|
-
* var custom = b.guardJwt.buildProfile({
|
|
633
|
-
* extends: "balanced",
|
|
634
|
-
* algAllowlistPolicy: "reject",
|
|
635
|
-
* allowedAlgs: ["ES256", "EdDSA"],
|
|
636
|
-
* });
|
|
637
|
-
* custom.algAllowlistPolicy; // → "reject"
|
|
638
|
-
* custom.allowedAlgs.indexOf("ES256"); // → 0
|
|
639
|
-
*/
|
|
640
|
-
var buildProfile = gateContract.makeProfileBuilder(PROFILES);
|
|
641
|
-
|
|
642
|
-
/**
|
|
643
|
-
* @primitive b.guardJwt.compliancePosture
|
|
644
|
-
* @signature b.guardJwt.compliancePosture(name)
|
|
645
|
-
* @since 0.7.49
|
|
646
|
-
* @status stable
|
|
647
|
-
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
648
|
-
* @related b.guardJwt.gate, b.guardJwt.buildProfile
|
|
649
|
-
*
|
|
650
|
-
* Look up a compliance-posture overlay by name (`"hipaa"` /
|
|
651
|
-
* `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of
|
|
652
|
-
* the posture object — the caller may mutate freely. Throws
|
|
653
|
-
* `GuardJwtError("jwt.bad-posture")` on unknown name. Postures
|
|
654
|
-
* extend the strict profile (or balanced for `gdpr`) with a
|
|
655
|
-
* `forensicSnippetBytes` cap appropriate to the regime.
|
|
656
|
-
*
|
|
657
|
-
* @example
|
|
658
|
-
* var posture = b.guardJwt.compliancePosture("hipaa");
|
|
659
|
-
* posture.algNonePolicy; // → "reject"
|
|
660
|
-
* posture.forensicSnippetBytes; // → 256
|
|
661
|
-
*/
|
|
662
|
-
function compliancePosture(name) {
|
|
663
|
-
return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES,
|
|
664
|
-
_err, "jwt");
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
var _jwtRulePacks = gateContract.makeRulePackLoader(GuardJwtError, "jwt");
|
|
668
|
-
/**
|
|
669
|
-
* @primitive b.guardJwt.loadRulePack
|
|
670
|
-
* @signature b.guardJwt.loadRulePack(pack)
|
|
671
|
-
* @since 0.7.49
|
|
672
|
-
* @status stable
|
|
673
|
-
* @related b.guardJwt.gate
|
|
674
|
-
*
|
|
675
|
-
* Register an operator-supplied rule pack with the guard-jwt
|
|
676
|
-
* registry. The pack is identified by `pack.id` (non-empty
|
|
677
|
-
* string) and stored for later inspection / dispatch by gates
|
|
678
|
-
* that opt in via `opts.rulePackId`. Returns the pack object
|
|
679
|
-
* unchanged on success; throws `GuardJwtError("jwt.bad-opt")`
|
|
680
|
-
* when `pack` is missing or `pack.id` is not a non-empty string.
|
|
681
|
-
*
|
|
682
|
-
* @example
|
|
683
|
-
* var pack = b.guardJwt.loadRulePack({
|
|
684
|
-
* id: "tenant-issuer-pin",
|
|
685
|
-
* rules: [
|
|
686
|
-
* { id: "iss-pin", severity: "high",
|
|
687
|
-
* detect: function (claims) { return claims.iss !== "https://idp.example/"; },
|
|
688
|
-
* reason: "tenant pins iss to a single IdP" },
|
|
689
|
-
* ],
|
|
690
|
-
* });
|
|
691
|
-
* pack.id; // → "tenant-issuer-pin"
|
|
692
|
-
*/
|
|
693
|
-
var loadRulePack = _jwtRulePacks.load;
|
|
552
|
+
// gate is the standard serve -> audit-only -> refuse chain over
|
|
553
|
+
// ctx.identifier || ctx.token || ctx.jwt (the KIND "identifier" ctx
|
|
554
|
+
// fields); gateContract.defineGuard supplies it as the default gate.
|
|
555
|
+
// buildProfile / compliancePosture / loadRulePack are assembled by
|
|
556
|
+
// gateContract.defineGuard below; their wiki sections render from the
|
|
557
|
+
// single-sourced @abiTemplate blocks in gate-contract.js, instantiated
|
|
558
|
+
// per guard by the page generator.
|
|
694
559
|
|
|
695
560
|
/**
|
|
696
561
|
* @primitive b.guardJwt.kidSafe
|
|
@@ -735,39 +600,44 @@ function kidSafe(kid) {
|
|
|
735
600
|
return kid;
|
|
736
601
|
}
|
|
737
602
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
603
|
+
// ---- guard-* family registry exports ----
|
|
604
|
+
var INTEGRATION_FIXTURES = Object.freeze({
|
|
605
|
+
kind: "identifier",
|
|
606
|
+
// Benign: minimal v4 token with alg=ES256, valid JSON header / payload.
|
|
607
|
+
benignBytes: Buffer.from(
|
|
608
|
+
"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9." +
|
|
609
|
+
"eyJpc3MiOiJleGFtcGxlIiwiZXhwIjo5OTk5OTk5OTk5LCJpYXQiOjE3MDAwMDAwMDB9." +
|
|
610
|
+
"sig", "utf8"),
|
|
611
|
+
benignIdentifier:
|
|
612
|
+
"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9." +
|
|
613
|
+
"eyJpc3MiOiJleGFtcGxlIiwiZXhwIjo5OTk5OTk5OTk5LCJpYXQiOjE3MDAwMDAwMDB9." +
|
|
614
|
+
"sig",
|
|
615
|
+
// Hostile: alg=none — universal refuse class.
|
|
616
|
+
hostileBytes: Buffer.from(
|
|
617
|
+
"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0." +
|
|
618
|
+
"eyJzdWIiOiJhdHRhY2tlciIsImV4cCI6OTk5OTk5OTk5OX0.", "utf8"),
|
|
619
|
+
hostileIdentifier:
|
|
620
|
+
"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0." +
|
|
621
|
+
"eyJzdWIiOiJhdHRhY2tlciIsImV4cCI6OTk5OTk5OTk5OX0.",
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
// Assembled from the gate-contract guard factory. KIND "identifier"; the
|
|
625
|
+
// gate is the standard serve -> audit-only -> refuse chain over
|
|
626
|
+
// ctx.identifier || ctx.token || ctx.jwt, so the guard takes the factory
|
|
627
|
+
// default gate (no bespoke `gate` passed) and the factory supplies the
|
|
628
|
+
// error class, registry exports, buildProfile / compliancePosture /
|
|
629
|
+
// loadRulePack wiring, and the kidSafe extra.
|
|
630
|
+
module.exports = gateContract.defineGuard({
|
|
631
|
+
name: "jwt",
|
|
632
|
+
kind: "identifier",
|
|
633
|
+
errorClass: GuardJwtError,
|
|
634
|
+
profiles: PROFILES,
|
|
635
|
+
defaults: DEFAULTS,
|
|
636
|
+
postures: COMPLIANCE_POSTURES,
|
|
637
|
+
integrationFixtures: INTEGRATION_FIXTURES,
|
|
638
|
+
validate: validate,
|
|
639
|
+
sanitize: sanitize,
|
|
640
|
+
extra: {
|
|
641
|
+
kidSafe: kidSafe,
|
|
642
|
+
},
|
|
643
|
+
});
|
package/lib/guard-list-id.js
CHANGED
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
|
|
74
74
|
var C = require("./constants");
|
|
75
75
|
var { defineClass } = require("./framework-error");
|
|
76
|
+
var gateContract = require("./gate-contract");
|
|
76
77
|
|
|
77
78
|
var GuardListIdError = defineClass("GuardListIdError", { alwaysPermanent: true });
|
|
78
79
|
|
|
@@ -102,11 +103,15 @@ var PROFILES = Object.freeze({
|
|
|
102
103
|
},
|
|
103
104
|
});
|
|
104
105
|
|
|
105
|
-
var COMPLIANCE_POSTURES =
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
|
|
107
|
+
|
|
108
|
+
var _resolveProfile = gateContract.makeProfileResolver({
|
|
109
|
+
profiles: PROFILES,
|
|
110
|
+
postures: COMPLIANCE_POSTURES,
|
|
111
|
+
defaults: DEFAULT_PROFILE,
|
|
112
|
+
errorClass: GuardListIdError,
|
|
113
|
+
codePrefix: "guard-list-id",
|
|
114
|
+
byObject: true,
|
|
110
115
|
});
|
|
111
116
|
|
|
112
117
|
// RFC 5322 §3.2.3 dot-atom-text shape — alphanumeric + select
|
|
@@ -261,21 +266,10 @@ function validate(headerValue, opts) {
|
|
|
261
266
|
};
|
|
262
267
|
}
|
|
263
268
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
* @status stable
|
|
269
|
-
*
|
|
270
|
-
* Return the effective profile name for a compliance posture, or
|
|
271
|
-
* `null` for unknown posture names.
|
|
272
|
-
*
|
|
273
|
-
* @example
|
|
274
|
-
* b.guardListId.compliancePosture("hipaa"); // → "strict"
|
|
275
|
-
*/
|
|
276
|
-
function compliancePosture(posture) {
|
|
277
|
-
return COMPLIANCE_POSTURES[posture] || null;
|
|
278
|
-
}
|
|
269
|
+
// compliancePosture is assembled by gateContract.defineParser below; its
|
|
270
|
+
// wiki section renders from the single-sourced @abiTemplate (defineParser)
|
|
271
|
+
// block in gate-contract.js, instantiated for this guard by the page
|
|
272
|
+
// generator.
|
|
279
273
|
|
|
280
274
|
function _hasControlChar(s) {
|
|
281
275
|
for (var i = 0; i < s.length; i += 1) {
|
|
@@ -296,24 +290,14 @@ function _refuse(reason) {
|
|
|
296
290
|
};
|
|
297
291
|
}
|
|
298
292
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
module.exports = {
|
|
312
|
-
validate: validate,
|
|
313
|
-
compliancePosture: compliancePosture,
|
|
314
|
-
PROFILES: PROFILES,
|
|
315
|
-
COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
|
|
316
|
-
GuardListIdError: GuardListIdError,
|
|
317
|
-
NAME: "listId",
|
|
318
|
-
KIND: "list-id",
|
|
319
|
-
};
|
|
293
|
+
module.exports = gateContract.defineParser({
|
|
294
|
+
name: "listId",
|
|
295
|
+
entry: validate,
|
|
296
|
+
errorClass: GuardListIdError,
|
|
297
|
+
profiles: PROFILES,
|
|
298
|
+
postures: COMPLIANCE_POSTURES,
|
|
299
|
+
extra: {
|
|
300
|
+
NAME: "listId",
|
|
301
|
+
KIND: "list-id",
|
|
302
|
+
},
|
|
303
|
+
});
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
var C = require("./constants");
|
|
80
80
|
var { defineClass } = require("./framework-error");
|
|
81
81
|
var safeUrl = require("./safe-url");
|
|
82
|
+
var gateContract = require("./gate-contract");
|
|
82
83
|
|
|
83
84
|
var GuardListUnsubscribeError = defineClass("GuardListUnsubscribeError", { alwaysPermanent: true });
|
|
84
85
|
|
|
@@ -111,11 +112,15 @@ var PROFILES = Object.freeze({
|
|
|
111
112
|
},
|
|
112
113
|
});
|
|
113
114
|
|
|
114
|
-
var COMPLIANCE_POSTURES =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
|
|
116
|
+
|
|
117
|
+
var _resolveProfile = gateContract.makeProfileResolver({
|
|
118
|
+
profiles: PROFILES,
|
|
119
|
+
postures: COMPLIANCE_POSTURES,
|
|
120
|
+
defaults: DEFAULT_PROFILE,
|
|
121
|
+
errorClass: GuardListUnsubscribeError,
|
|
122
|
+
codePrefix: "guard-list-unsubscribe",
|
|
123
|
+
byObject: true,
|
|
119
124
|
});
|
|
120
125
|
|
|
121
126
|
// RFC 8058 §2: Post header value MUST be exactly
|
|
@@ -328,21 +333,10 @@ function validate(headers, opts) {
|
|
|
328
333
|
{ uris: classified, hasHttpsUri: hasHttpsUri, hasMailtoUri: hasMailtoUri, postHeaderOk: postHeaderOk });
|
|
329
334
|
}
|
|
330
335
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
* @status stable
|
|
336
|
-
*
|
|
337
|
-
* Return the effective profile name for a compliance posture, or
|
|
338
|
-
* `null` for unknown posture names.
|
|
339
|
-
*
|
|
340
|
-
* @example
|
|
341
|
-
* b.guardListUnsubscribe.compliancePosture("hipaa"); // → "strict"
|
|
342
|
-
*/
|
|
343
|
-
function compliancePosture(posture) {
|
|
344
|
-
return COMPLIANCE_POSTURES[posture] || null;
|
|
345
|
-
}
|
|
336
|
+
// compliancePosture is assembled by gateContract.defineParser below; its
|
|
337
|
+
// wiki section renders from the single-sourced @abiTemplate (defineParser)
|
|
338
|
+
// block in gate-contract.js, instantiated for this guard by the page
|
|
339
|
+
// generator.
|
|
346
340
|
|
|
347
341
|
function _extractUris(raw, maxUris) {
|
|
348
342
|
// RFC 2369 §3.1 — comma-separated `<URI>` items. Walk angle-
|
|
@@ -386,26 +380,16 @@ function _verdict(action, reason, extra) {
|
|
|
386
380
|
};
|
|
387
381
|
}
|
|
388
382
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
validate: validate,
|
|
403
|
-
compliancePosture: compliancePosture,
|
|
404
|
-
PROFILES: PROFILES,
|
|
405
|
-
COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
|
|
406
|
-
ONE_CLICK_POST_VALUE: ONE_CLICK_POST_VALUE,
|
|
407
|
-
DANGEROUS_SCHEMES: DANGEROUS_SCHEMES,
|
|
408
|
-
GuardListUnsubscribeError: GuardListUnsubscribeError,
|
|
409
|
-
NAME: "listUnsubscribe",
|
|
410
|
-
KIND: "list-unsubscribe",
|
|
411
|
-
};
|
|
383
|
+
module.exports = gateContract.defineParser({
|
|
384
|
+
name: "listUnsubscribe",
|
|
385
|
+
entry: validate,
|
|
386
|
+
errorClass: GuardListUnsubscribeError,
|
|
387
|
+
profiles: PROFILES,
|
|
388
|
+
postures: COMPLIANCE_POSTURES,
|
|
389
|
+
extra: {
|
|
390
|
+
ONE_CLICK_POST_VALUE: ONE_CLICK_POST_VALUE,
|
|
391
|
+
DANGEROUS_SCHEMES: DANGEROUS_SCHEMES,
|
|
392
|
+
NAME: "listUnsubscribe",
|
|
393
|
+
KIND: "list-unsubscribe",
|
|
394
|
+
},
|
|
395
|
+
});
|