@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.
- package/CHANGELOG.md +4 -0
- package/README.md +2 -2
- package/index.js +4 -0
- package/lib/ai-content-detect.js +9 -10
- package/lib/api-key.js +107 -74
- package/lib/atomic-file.js +29 -1
- 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 +218 -100
- 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 +22 -0
- package/lib/consent.js +73 -24
- package/lib/constants.js +16 -11
- package/lib/crypto-field.js +387 -91
- 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/external-db-migrate.js +229 -139
- package/lib/external-db.js +25 -15
- package/lib/framework-error.js +11 -0
- package/lib/framework-files.js +73 -0
- package/lib/framework-schema.js +695 -394
- package/lib/gate-contract.js +596 -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 +14 -0
- 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-spam-score.js +2 -6
- package/lib/mail-store.js +287 -154
- 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 +31 -3
- package/lib/object-store/sigv4.js +10 -0
- package/lib/outbox.js +136 -82
- 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/retention.js +82 -39
- 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/static.js +45 -7
- 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 +16 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/guard-time.js
CHANGED
|
@@ -423,164 +423,42 @@ function sanitize(input, opts) {
|
|
|
423
423
|
throw _err("time.bad-input", "sanitize requires string input");
|
|
424
424
|
}
|
|
425
425
|
var issues = _detectIssues(input, opts);
|
|
426
|
-
|
|
427
|
-
if (issues[i].severity === "critical" || issues[i].severity === "high") {
|
|
428
|
-
throw _err(issues[i].ruleId || "time.refused",
|
|
429
|
-
"guardTime.sanitize: " + issues[i].snippet);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
426
|
+
gateContract.throwOnRefusalSeverity(issues, { errorClass: GuardTimeError, codePrefix: "time" });
|
|
432
427
|
// Normalize: lowercase the trailing `T` separator, uppercase the
|
|
433
428
|
// `Z` UTC marker.
|
|
434
429
|
return input.replace(/(\d) /, "$1T").replace(/z$/, "Z");
|
|
435
430
|
}
|
|
436
431
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
* @status stable
|
|
442
|
-
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
443
|
-
* @related b.guardTime.validate, b.guardTime.sanitize, b.guardAll.gate
|
|
444
|
-
*
|
|
445
|
-
* Build a guard gate whose async `check(ctx)` returns `{ ok, action, issues }`, consumable
|
|
446
|
-
* by `b.guardAll`, audit pipelines, scheduling primitives, and
|
|
447
|
-
* retention readers. The gate reads `ctx.identifier` (or
|
|
448
|
-
* `ctx.timestamp` / `ctx.time`), runs `validate`, and maps
|
|
449
|
-
* severity to action: zero issues `serve`; only low/medium
|
|
450
|
-
* `audit-only`; any high/critical `refuse`.
|
|
451
|
-
*
|
|
452
|
-
* @opts
|
|
453
|
-
* name: string, // gate label for audit / observability
|
|
454
|
-
* profile: "strict"|"balanced"|"permissive",
|
|
455
|
-
* compliancePosture: "hipaa"|"pci-dss"|"gdpr"|"soc2",
|
|
456
|
-
* ...: same shape as b.guardTime.validate opts,
|
|
457
|
-
*
|
|
458
|
-
* @example
|
|
459
|
-
* var g = b.guardTime.gate({ profile: "strict" });
|
|
460
|
-
* var rv = await g.check({ identifier: "2026-05-05T12:34:56Z" });
|
|
461
|
-
* rv.action; // → "serve"
|
|
462
|
-
*
|
|
463
|
-
* var bad = await g.check({ identifier: "2026-05-05 12:34:56" });
|
|
464
|
-
* bad.action; // → "refuse"
|
|
465
|
-
*/
|
|
466
|
-
function gate(opts) {
|
|
467
|
-
opts = _resolveOpts(opts);
|
|
468
|
-
return gateContract.buildGuardGate(
|
|
469
|
-
opts.name || "guardTime:" + (opts.profile || "default"),
|
|
470
|
-
opts,
|
|
471
|
-
async function (ctx) {
|
|
472
|
-
var identifier = ctx && (ctx.identifier || ctx.timestamp || ctx.time || "");
|
|
473
|
-
if (!identifier) return { ok: true, action: "serve" };
|
|
474
|
-
var rv = validate(identifier, opts);
|
|
475
|
-
if (rv.issues.length === 0) return { ok: true, action: "serve" };
|
|
476
|
-
var hasCritical = rv.issues.some(function (i) {
|
|
477
|
-
return i.severity === "critical";
|
|
478
|
-
});
|
|
479
|
-
var hasHigh = rv.issues.some(function (i) {
|
|
480
|
-
return i.severity === "high";
|
|
481
|
-
});
|
|
482
|
-
if (!hasCritical && !hasHigh) {
|
|
483
|
-
return { ok: true, action: "audit-only", issues: rv.issues };
|
|
484
|
-
}
|
|
485
|
-
return { ok: false, action: "refuse", issues: rv.issues };
|
|
486
|
-
});
|
|
487
|
-
}
|
|
432
|
+
// gate / buildProfile / compliancePosture / loadRulePack are assembled by
|
|
433
|
+
// gateContract.defineGuard below; their wiki sections render from the
|
|
434
|
+
// single-sourced @abiTemplate (defineGuard) blocks in gate-contract.js,
|
|
435
|
+
// instantiated per guard by the page generator.
|
|
488
436
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
* names (later entries shadow earlier ones); inline keys win last.
|
|
499
|
-
*
|
|
500
|
-
* @opts
|
|
501
|
-
* extends: string|string[], // base profile name(s) to compose
|
|
502
|
-
* ...: any guard-time key, // inline override of resolved keys
|
|
503
|
-
*
|
|
504
|
-
* @example
|
|
505
|
-
* var custom = b.guardTime.buildProfile({
|
|
506
|
-
* extends: "balanced",
|
|
507
|
-
* leapSecondPolicy: "audit",
|
|
508
|
-
* maxYear: 2200,
|
|
509
|
-
* });
|
|
510
|
-
* custom.naiveDatetimePolicy; // → "audit"
|
|
511
|
-
* custom.maxYear; // → 2200
|
|
512
|
-
*/
|
|
513
|
-
var buildProfile = gateContract.makeProfileBuilder(PROFILES);
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* @primitive b.guardTime.compliancePosture
|
|
517
|
-
* @signature b.guardTime.compliancePosture(name)
|
|
518
|
-
* @since 0.7.46
|
|
519
|
-
* @status stable
|
|
520
|
-
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
521
|
-
* @related b.guardTime.gate, b.guardTime.buildProfile
|
|
522
|
-
*
|
|
523
|
-
* Look up a compliance-posture overlay by name (`"hipaa"` /
|
|
524
|
-
* `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of
|
|
525
|
-
* the posture object — the caller may mutate freely. Throws
|
|
526
|
-
* `GuardTimeError("time.bad-posture")` on unknown name.
|
|
527
|
-
*
|
|
528
|
-
* @example
|
|
529
|
-
* var posture = b.guardTime.compliancePosture("hipaa");
|
|
530
|
-
* posture.naiveDatetimePolicy; // → "reject"
|
|
531
|
-
*/
|
|
532
|
-
function compliancePosture(name) {
|
|
533
|
-
return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES,
|
|
534
|
-
_err, "time");
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
var _timeRulePacks = gateContract.makeRulePackLoader(GuardTimeError, "time");
|
|
538
|
-
/**
|
|
539
|
-
* @primitive b.guardTime.loadRulePack
|
|
540
|
-
* @signature b.guardTime.loadRulePack(pack)
|
|
541
|
-
* @since 0.7.46
|
|
542
|
-
* @status stable
|
|
543
|
-
* @related b.guardTime.gate
|
|
544
|
-
*
|
|
545
|
-
* Register an operator-supplied rule pack with the guard-time
|
|
546
|
-
* registry. The pack is identified by `pack.id` (non-empty
|
|
547
|
-
* string) and stored for later inspection / dispatch by gates
|
|
548
|
-
* that opt in via `opts.rulePackId`. Throws
|
|
549
|
-
* `GuardTimeError("time.bad-opt")` when `pack` is missing or
|
|
550
|
-
* `pack.id` is not a non-empty string.
|
|
551
|
-
*
|
|
552
|
-
* @example
|
|
553
|
-
* var pack = b.guardTime.loadRulePack({
|
|
554
|
-
* id: "audit-window",
|
|
555
|
-
* minYear: 2020,
|
|
556
|
-
* maxYear: 2030,
|
|
557
|
-
* });
|
|
558
|
-
* pack.id; // → "audit-window"
|
|
559
|
-
*/
|
|
560
|
-
var loadRulePack = _timeRulePacks.load;
|
|
437
|
+
var INTEGRATION_FIXTURES = Object.freeze({
|
|
438
|
+
kind: "identifier",
|
|
439
|
+
benignBytes: Buffer.from("2026-05-05T12:34:56Z", "utf8"),
|
|
440
|
+
hostileBytes: Buffer.from("2026-05-05 12:34:56", "utf8"),
|
|
441
|
+
benignIdentifier: "2026-05-05T12:34:56Z",
|
|
442
|
+
// Hostile: naive datetime (space separator + no offset) — refused
|
|
443
|
+
// at strict (cross-region ambiguity class).
|
|
444
|
+
hostileIdentifier: "2026-05-05 12:34:56",
|
|
445
|
+
});
|
|
561
446
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
validate:
|
|
577
|
-
sanitize:
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
compliancePosture: compliancePosture,
|
|
581
|
-
loadRulePack: loadRulePack,
|
|
582
|
-
PROFILES: PROFILES,
|
|
583
|
-
DEFAULTS: DEFAULTS,
|
|
584
|
-
COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
|
|
585
|
-
GuardTimeError: GuardTimeError,
|
|
586
|
-
};
|
|
447
|
+
// Assembled from the gate-contract guard factory: error class, registry
|
|
448
|
+
// exports (NAME / KIND / INTEGRATION_FIXTURES), buildProfile /
|
|
449
|
+
// compliancePosture / loadRulePack wiring, plus the per-guard inspection
|
|
450
|
+
// surface (validate / sanitize). The gate is the factory default — the
|
|
451
|
+
// standard serve -> audit-only -> refuse chain — reading
|
|
452
|
+
// ctx.identifier / ctx.timestamp / ctx.time via ctxFields.
|
|
453
|
+
module.exports = gateContract.defineGuard({
|
|
454
|
+
name: "time",
|
|
455
|
+
kind: "identifier",
|
|
456
|
+
errorClass: GuardTimeError,
|
|
457
|
+
profiles: PROFILES,
|
|
458
|
+
defaults: DEFAULTS,
|
|
459
|
+
postures: COMPLIANCE_POSTURES,
|
|
460
|
+
integrationFixtures: INTEGRATION_FIXTURES,
|
|
461
|
+
validate: validate,
|
|
462
|
+
sanitize: sanitize,
|
|
463
|
+
ctxFields: ["identifier", "timestamp", "time"],
|
|
464
|
+
});
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
var { defineClass } = require("./framework-error");
|
|
30
|
+
var gateContract = require("./gate-contract");
|
|
30
31
|
|
|
31
32
|
var GuardTraceContextError = defineClass("GuardTraceContextError", { alwaysPermanent: true });
|
|
32
33
|
|
|
@@ -38,15 +39,18 @@ var PROFILES = Object.freeze({
|
|
|
38
39
|
permissive: { allowedVersions: ["*"], maxTracestateEntries: 64, maxTracestateBytes: 1024 },
|
|
39
40
|
});
|
|
40
41
|
|
|
41
|
-
var COMPLIANCE_POSTURES =
|
|
42
|
-
hipaa: "strict",
|
|
43
|
-
"pci-dss": "strict",
|
|
44
|
-
gdpr: "strict",
|
|
45
|
-
soc2: "strict",
|
|
46
|
-
});
|
|
42
|
+
var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
|
|
47
43
|
|
|
48
44
|
var TRACEPARENT_RE = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/; // allow:regex-no-length-cap — length-bound inline before test
|
|
49
45
|
|
|
46
|
+
var _resolveProfile = gateContract.makeProfileResolver({
|
|
47
|
+
profiles: PROFILES,
|
|
48
|
+
postures: COMPLIANCE_POSTURES,
|
|
49
|
+
defaults: DEFAULT_PROFILE,
|
|
50
|
+
errorClass: GuardTraceContextError,
|
|
51
|
+
codePrefix: "trace-context",
|
|
52
|
+
});
|
|
53
|
+
|
|
50
54
|
/**
|
|
51
55
|
* @primitive b.guardTraceContext.validate
|
|
52
56
|
* @signature b.guardTraceContext.validate(ctx, opts?)
|
|
@@ -132,41 +136,18 @@ function validate(ctx, opts) {
|
|
|
132
136
|
return ctx;
|
|
133
137
|
}
|
|
134
138
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function _resolveProfile(opts) {
|
|
153
|
-
if (opts.posture && COMPLIANCE_POSTURES[opts.posture]) {
|
|
154
|
-
return COMPLIANCE_POSTURES[opts.posture];
|
|
155
|
-
}
|
|
156
|
-
var p = opts.profile || DEFAULT_PROFILE;
|
|
157
|
-
if (!PROFILES[p]) {
|
|
158
|
-
throw new GuardTraceContextError("trace-context/bad-profile",
|
|
159
|
-
"guardTraceContext: unknown profile '" + p + "'");
|
|
160
|
-
}
|
|
161
|
-
return p;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
module.exports = {
|
|
165
|
-
validate: validate,
|
|
166
|
-
compliancePosture: compliancePosture,
|
|
167
|
-
PROFILES: PROFILES,
|
|
168
|
-
COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
|
|
169
|
-
GuardTraceContextError: GuardTraceContextError,
|
|
170
|
-
NAME: "traceContext",
|
|
171
|
-
KIND: "trace-context",
|
|
172
|
-
};
|
|
139
|
+
// compliancePosture is assembled by gateContract.defineParser below; its
|
|
140
|
+
// wiki section renders from the single-sourced @abiTemplate (defineParser)
|
|
141
|
+
// block in gate-contract.js, instantiated for this guard by the page
|
|
142
|
+
// generator.
|
|
143
|
+
module.exports = gateContract.defineParser({
|
|
144
|
+
name: "trace-context",
|
|
145
|
+
entry: validate,
|
|
146
|
+
errorClass: GuardTraceContextError,
|
|
147
|
+
profiles: PROFILES,
|
|
148
|
+
postures: COMPLIANCE_POSTURES,
|
|
149
|
+
extra: {
|
|
150
|
+
NAME: "traceContext",
|
|
151
|
+
KIND: "trace-context",
|
|
152
|
+
},
|
|
153
|
+
});
|
package/lib/guard-uuid.js
CHANGED
|
@@ -382,12 +382,7 @@ function sanitize(input, opts) {
|
|
|
382
382
|
throw _err("uuid.bad-input", "sanitize requires string input");
|
|
383
383
|
}
|
|
384
384
|
var issues = _detectIssues(input, opts);
|
|
385
|
-
|
|
386
|
-
if (issues[i].severity === "critical" || issues[i].severity === "high") {
|
|
387
|
-
throw _err(issues[i].ruleId || "uuid.refused",
|
|
388
|
-
"guardUuid.sanitize: " + issues[i].snippet);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
385
|
+
gateContract.throwOnRefusalSeverity(issues, { errorClass: GuardUuidError, codePrefix: "uuid" });
|
|
391
386
|
// Safe transforms: lowercase + strip braces / urn prefix → canonical
|
|
392
387
|
// hyphenated form.
|
|
393
388
|
var form = _classifyForm(input);
|
|
@@ -398,151 +393,35 @@ function sanitize(input, opts) {
|
|
|
398
393
|
hex.slice(20); // UUID hex slice positions
|
|
399
394
|
}
|
|
400
395
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
* `refuse`.
|
|
415
|
-
*
|
|
416
|
-
* @opts
|
|
417
|
-
* name: string, // gate label for audit / observability
|
|
418
|
-
* profile: "strict"|"balanced"|"permissive",
|
|
419
|
-
* compliancePosture: "hipaa"|"pci-dss"|"gdpr"|"soc2",
|
|
420
|
-
* ...: same shape as b.guardUuid.validate opts,
|
|
421
|
-
*
|
|
422
|
-
* @example
|
|
423
|
-
* var g = b.guardUuid.gate({ profile: "strict" });
|
|
424
|
-
* var rv = await g.check({ identifier: "550e8400-e29b-41d4-a716-446655440000" });
|
|
425
|
-
* rv.action; // → "serve"
|
|
426
|
-
*
|
|
427
|
-
* var bad = await g.check({ identifier: "{550e8400-e29b-41d4-a716-446655440000}" });
|
|
428
|
-
* bad.action; // → "refuse"
|
|
429
|
-
*/
|
|
430
|
-
function gate(opts) {
|
|
431
|
-
opts = _resolveOpts(opts);
|
|
432
|
-
return gateContract.buildGuardGate(
|
|
433
|
-
opts.name || "guardUuid:" + (opts.profile || "default"),
|
|
434
|
-
opts,
|
|
435
|
-
async function (ctx) {
|
|
436
|
-
var identifier = ctx && (ctx.identifier || ctx.uuid || "");
|
|
437
|
-
if (!identifier) return { ok: true, action: "serve" };
|
|
438
|
-
var rv = validate(identifier, opts);
|
|
439
|
-
if (rv.issues.length === 0) return { ok: true, action: "serve" };
|
|
440
|
-
var hasCritical = rv.issues.some(function (i) {
|
|
441
|
-
return i.severity === "critical";
|
|
442
|
-
});
|
|
443
|
-
var hasHigh = rv.issues.some(function (i) {
|
|
444
|
-
return i.severity === "high";
|
|
445
|
-
});
|
|
446
|
-
if (!hasCritical && !hasHigh) {
|
|
447
|
-
return { ok: true, action: "audit-only", issues: rv.issues };
|
|
448
|
-
}
|
|
449
|
-
return { ok: false, action: "refuse", issues: rv.issues };
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* @primitive b.guardUuid.buildProfile
|
|
455
|
-
* @signature b.guardUuid.buildProfile(opts)
|
|
456
|
-
* @since 0.7.44
|
|
457
|
-
* @status stable
|
|
458
|
-
* @related b.guardUuid.gate, b.guardUuid.compliancePosture
|
|
459
|
-
*
|
|
460
|
-
* Compose a derived profile from one or more named bases plus
|
|
461
|
-
* inline overrides. `opts.extends` is a profile name or array of
|
|
462
|
-
* names (later entries shadow earlier ones); inline keys win last.
|
|
463
|
-
*
|
|
464
|
-
* @opts
|
|
465
|
-
* extends: string|string[], // base profile name(s) to compose
|
|
466
|
-
* ...: any guard-uuid key, // inline override of resolved keys
|
|
467
|
-
*
|
|
468
|
-
* @example
|
|
469
|
-
* var custom = b.guardUuid.buildProfile({
|
|
470
|
-
* extends: "balanced",
|
|
471
|
-
* formatPolicy: "hyphenated-only",
|
|
472
|
-
* nilPolicy: "audit",
|
|
473
|
-
* });
|
|
474
|
-
* custom.formatPolicy; // → "hyphenated-only"
|
|
475
|
-
* custom.nilPolicy; // → "audit"
|
|
476
|
-
*/
|
|
477
|
-
var buildProfile = gateContract.makeProfileBuilder(PROFILES);
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* @primitive b.guardUuid.compliancePosture
|
|
481
|
-
* @signature b.guardUuid.compliancePosture(name)
|
|
482
|
-
* @since 0.7.44
|
|
483
|
-
* @status stable
|
|
484
|
-
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
485
|
-
* @related b.guardUuid.gate, b.guardUuid.buildProfile
|
|
486
|
-
*
|
|
487
|
-
* Look up a compliance-posture overlay by name (`"hipaa"` /
|
|
488
|
-
* `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of
|
|
489
|
-
* the posture object — the caller may mutate freely. Throws
|
|
490
|
-
* `GuardUuidError("uuid.bad-posture")` on unknown name.
|
|
491
|
-
*
|
|
492
|
-
* @example
|
|
493
|
-
* var posture = b.guardUuid.compliancePosture("hipaa");
|
|
494
|
-
* posture.nilPolicy; // → "reject"
|
|
495
|
-
*/
|
|
496
|
-
function compliancePosture(name) {
|
|
497
|
-
return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES,
|
|
498
|
-
_err, "uuid");
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
var _uuidRulePacks = gateContract.makeRulePackLoader(GuardUuidError, "uuid");
|
|
502
|
-
/**
|
|
503
|
-
* @primitive b.guardUuid.loadRulePack
|
|
504
|
-
* @signature b.guardUuid.loadRulePack(pack)
|
|
505
|
-
* @since 0.7.44
|
|
506
|
-
* @status stable
|
|
507
|
-
* @related b.guardUuid.gate
|
|
508
|
-
*
|
|
509
|
-
* Register an operator-supplied rule pack with the guard-uuid
|
|
510
|
-
* registry. The pack is identified by `pack.id` (non-empty
|
|
511
|
-
* string) and stored for later inspection / dispatch by gates
|
|
512
|
-
* that opt in via `opts.rulePackId`. Throws
|
|
513
|
-
* `GuardUuidError("uuid.bad-opt")` when `pack` is missing or
|
|
514
|
-
* `pack.id` is not a non-empty string.
|
|
515
|
-
*
|
|
516
|
-
* @example
|
|
517
|
-
* var pack = b.guardUuid.loadRulePack({
|
|
518
|
-
* id: "v7-only",
|
|
519
|
-
* allowedVersions: [7],
|
|
520
|
-
* });
|
|
521
|
-
* pack.id; // → "v7-only"
|
|
522
|
-
*/
|
|
523
|
-
var loadRulePack = _uuidRulePacks.load;
|
|
396
|
+
// gate / buildProfile / compliancePosture / loadRulePack are assembled by
|
|
397
|
+
// gateContract.defineGuard below; their wiki sections render from the
|
|
398
|
+
// single-sourced @abiTemplate (defineGuard) blocks in gate-contract.js,
|
|
399
|
+
// instantiated per guard by the page generator.
|
|
400
|
+
|
|
401
|
+
var INTEGRATION_FIXTURES = Object.freeze({
|
|
402
|
+
kind: "identifier",
|
|
403
|
+
benignBytes: Buffer.from("550e8400-e29b-41d4-a716-446655440000", "utf8"),
|
|
404
|
+
hostileBytes: Buffer.from("00000000-0000-0000-0000-000000000000", "utf8"),
|
|
405
|
+
benignIdentifier: "550e8400-e29b-41d4-a716-446655440000",
|
|
406
|
+
// Hostile: nil UUID — refused at strict (sentinel-leak class).
|
|
407
|
+
hostileIdentifier: "00000000-0000-0000-0000-000000000000",
|
|
408
|
+
});
|
|
524
409
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
loadRulePack: loadRulePack,
|
|
544
|
-
PROFILES: PROFILES,
|
|
545
|
-
DEFAULTS: DEFAULTS,
|
|
546
|
-
COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
|
|
547
|
-
GuardUuidError: GuardUuidError,
|
|
548
|
-
};
|
|
410
|
+
// Assembled from the gate-contract guard factory: error class, registry
|
|
411
|
+
// exports (NAME / KIND / INTEGRATION_FIXTURES), buildProfile /
|
|
412
|
+
// compliancePosture / loadRulePack wiring, plus the per-guard inspection
|
|
413
|
+
// surface (validate / sanitize). The gate is the factory default — the
|
|
414
|
+
// standard serve -> audit-only -> refuse chain — reading ctx.identifier ||
|
|
415
|
+
// ctx.uuid via ctxFields.
|
|
416
|
+
module.exports = gateContract.defineGuard({
|
|
417
|
+
name: "uuid",
|
|
418
|
+
kind: "identifier",
|
|
419
|
+
errorClass: GuardUuidError,
|
|
420
|
+
profiles: PROFILES,
|
|
421
|
+
defaults: DEFAULTS,
|
|
422
|
+
postures: COMPLIANCE_POSTURES,
|
|
423
|
+
integrationFixtures: INTEGRATION_FIXTURES,
|
|
424
|
+
validate: validate,
|
|
425
|
+
sanitize: sanitize,
|
|
426
|
+
ctxFields: ["identifier", "uuid"],
|
|
427
|
+
});
|
package/lib/guard-xml.js
CHANGED
|
@@ -478,12 +478,8 @@ function sanitize(input, opts) {
|
|
|
478
478
|
// shapes (DOCTYPE / ENTITY / external / parameter-entity) have no
|
|
479
479
|
// safe sanitization; throw.
|
|
480
480
|
var issues = _detectIssues(input, opts);
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
throw _err(issues[i].ruleId || "xml.refused",
|
|
484
|
-
"guardXml.sanitize: " + issues[i].snippet);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
481
|
+
gateContract.throwOnRefusalSeverity(issues,
|
|
482
|
+
{ errorClass: GuardXmlError, codePrefix: "xml", severities: ["critical"] });
|
|
487
483
|
// Strip character-class threats per policy via the shared helper.
|
|
488
484
|
return codepointClass.applyCharStripPolicies(input, opts);
|
|
489
485
|
}
|
|
@@ -556,111 +552,40 @@ function gate(opts) {
|
|
|
556
552
|
});
|
|
557
553
|
}
|
|
558
554
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
* ...: any guard-xml key, // inline override of resolved keys
|
|
576
|
-
*
|
|
577
|
-
* @example
|
|
578
|
-
* var custom = b.guardXml.buildProfile({
|
|
579
|
-
* extends: "balanced",
|
|
580
|
-
* cdataPolicy: "reject",
|
|
581
|
-
* maxElements: 4096,
|
|
582
|
-
* });
|
|
583
|
-
* custom.cdataPolicy; // → "reject"
|
|
584
|
-
* custom.maxElements; // → 4096
|
|
585
|
-
*/
|
|
586
|
-
var buildProfile = gateContract.makeProfileBuilder(PROFILES);
|
|
587
|
-
|
|
588
|
-
/**
|
|
589
|
-
* @primitive b.guardXml.compliancePosture
|
|
590
|
-
* @signature b.guardXml.compliancePosture(name)
|
|
591
|
-
* @since 0.7.15
|
|
592
|
-
* @status stable
|
|
593
|
-
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
594
|
-
* @related b.guardXml.gate, b.guardXml.buildProfile
|
|
595
|
-
*
|
|
596
|
-
* Look up a compliance-posture overlay by name (`"hipaa"` /
|
|
597
|
-
* `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of the
|
|
598
|
-
* posture object — the caller may mutate freely. Throws
|
|
599
|
-
* `GuardXmlError("xml.bad-posture")` on unknown name.
|
|
600
|
-
*
|
|
601
|
-
* @example
|
|
602
|
-
* var posture = b.guardXml.compliancePosture("hipaa");
|
|
603
|
-
* posture.doctypePolicy; // → "reject"
|
|
604
|
-
* posture.forensicSnippetBytes; // → 256
|
|
605
|
-
*/
|
|
606
|
-
function compliancePosture(name) {
|
|
607
|
-
return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES, _err, "xml");
|
|
608
|
-
}
|
|
555
|
+
// buildProfile / compliancePosture / loadRulePack are assembled by
|
|
556
|
+
// gateContract.defineGuard below; their wiki sections render from the
|
|
557
|
+
// single-sourced @abiTemplate (defineGuard) blocks in gate-contract.js,
|
|
558
|
+
// instantiated per guard by the page generator.
|
|
559
|
+
|
|
560
|
+
var INTEGRATION_FIXTURES = Object.freeze({
|
|
561
|
+
kind: "content",
|
|
562
|
+
contentType: "application/xml",
|
|
563
|
+
extension: ".xml",
|
|
564
|
+
benignBytes: Buffer.from('<?xml version="1.0"?><root><x>1</x></root>', "utf8"),
|
|
565
|
+
// Hostile: DOCTYPE with internal-subset entity declaration (XXE +
|
|
566
|
+
// billion-laughs vector — CVE-2026-24400 / CVE-2024-25062 class).
|
|
567
|
+
hostileBytes: Buffer.from(
|
|
568
|
+
'<?xml version="1.0"?>\n<!DOCTYPE root [<!ENTITY xx "yy">]>\n<root/>',
|
|
569
|
+
"utf8"),
|
|
570
|
+
});
|
|
609
571
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
* detect: function (text) { return text.indexOf("<soap:Envelope") === -1; },
|
|
631
|
-
* reason: "SOAP request missing soap:Envelope root" },
|
|
632
|
-
* ],
|
|
633
|
-
* });
|
|
634
|
-
* pack.id; // → "soap-envelope"
|
|
635
|
-
*/
|
|
636
|
-
var loadRulePack = _xmlRulePacks.load;
|
|
637
|
-
|
|
638
|
-
module.exports = {
|
|
639
|
-
// ---- guard-* family registry exports ----
|
|
640
|
-
NAME: "xml",
|
|
641
|
-
KIND: "content",
|
|
642
|
-
MIME_TYPES: Object.freeze(["application/xml", "text/xml"]),
|
|
643
|
-
EXTENSIONS: Object.freeze([".xml"]),
|
|
644
|
-
INTEGRATION_FIXTURES: Object.freeze({
|
|
645
|
-
kind: "content",
|
|
646
|
-
contentType: "application/xml",
|
|
647
|
-
extension: ".xml",
|
|
648
|
-
benignBytes: Buffer.from('<?xml version="1.0"?><root><x>1</x></root>', "utf8"),
|
|
649
|
-
// Hostile: DOCTYPE with internal-subset entity declaration (XXE +
|
|
650
|
-
// billion-laughs vector — CVE-2026-24400 / CVE-2024-25062 class).
|
|
651
|
-
hostileBytes: Buffer.from(
|
|
652
|
-
'<?xml version="1.0"?>\n<!DOCTYPE root [<!ENTITY xx "yy">]>\n<root/>',
|
|
653
|
-
"utf8"),
|
|
654
|
-
}),
|
|
655
|
-
// ---- primitive surface ----
|
|
656
|
-
validate: validate,
|
|
657
|
-
sanitize: sanitize,
|
|
658
|
-
gate: gate,
|
|
659
|
-
buildProfile: buildProfile,
|
|
660
|
-
compliancePosture: compliancePosture,
|
|
661
|
-
loadRulePack: loadRulePack,
|
|
662
|
-
PROFILES: PROFILES,
|
|
663
|
-
DEFAULTS: DEFAULTS,
|
|
664
|
-
COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
|
|
665
|
-
GuardXmlError: GuardXmlError,
|
|
666
|
-
};
|
|
572
|
+
// Assembled from the gate-contract guard factory: error class, registry
|
|
573
|
+
// exports (NAME / KIND / MIME_TYPES / EXTENSIONS / INTEGRATION_FIXTURES),
|
|
574
|
+
// buildProfile / compliancePosture / loadRulePack wiring, plus the
|
|
575
|
+
// per-guard inspection surface (validate / sanitize / bespoke gate)
|
|
576
|
+
// passed through verbatim. The bespoke `gate` carries XML's
|
|
577
|
+
// per-policy canSanitize matrix unchanged.
|
|
578
|
+
module.exports = gateContract.defineGuard({
|
|
579
|
+
name: "xml",
|
|
580
|
+
kind: "content",
|
|
581
|
+
errorClass: GuardXmlError,
|
|
582
|
+
profiles: PROFILES,
|
|
583
|
+
defaults: DEFAULTS,
|
|
584
|
+
postures: COMPLIANCE_POSTURES,
|
|
585
|
+
mimeTypes: ["application/xml", "text/xml"],
|
|
586
|
+
extensions: [".xml"],
|
|
587
|
+
integrationFixtures: INTEGRATION_FIXTURES,
|
|
588
|
+
validate: validate,
|
|
589
|
+
sanitize: sanitize,
|
|
590
|
+
gate: gate,
|
|
591
|
+
});
|