@blamejs/core 0.14.27 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +2 -2
  3. package/index.js +4 -0
  4. package/lib/ai-content-detect.js +9 -10
  5. package/lib/api-key.js +107 -74
  6. package/lib/atomic-file.js +29 -1
  7. package/lib/audit-chain.js +47 -11
  8. package/lib/audit-sign.js +77 -2
  9. package/lib/audit-tools.js +79 -51
  10. package/lib/audit.js +218 -100
  11. package/lib/backup/index.js +13 -10
  12. package/lib/break-glass.js +202 -144
  13. package/lib/cache.js +174 -105
  14. package/lib/chain-writer.js +38 -16
  15. package/lib/cli.js +19 -14
  16. package/lib/cluster-provider-db.js +130 -104
  17. package/lib/cluster-storage.js +119 -22
  18. package/lib/cluster.js +119 -71
  19. package/lib/compliance.js +22 -0
  20. package/lib/consent.js +73 -24
  21. package/lib/constants.js +16 -11
  22. package/lib/crypto-field.js +387 -91
  23. package/lib/db-declare-row-policy.js +35 -22
  24. package/lib/db-file-lifecycle.js +3 -2
  25. package/lib/db-query.js +497 -255
  26. package/lib/db-schema.js +209 -44
  27. package/lib/db.js +176 -95
  28. package/lib/external-db-migrate.js +229 -139
  29. package/lib/external-db.js +25 -15
  30. package/lib/framework-error.js +11 -0
  31. package/lib/framework-files.js +73 -0
  32. package/lib/framework-schema.js +695 -394
  33. package/lib/gate-contract.js +596 -1
  34. package/lib/guard-agent-registry.js +26 -44
  35. package/lib/guard-all.js +1 -0
  36. package/lib/guard-auth.js +42 -112
  37. package/lib/guard-cidr.js +33 -154
  38. package/lib/guard-csv.js +46 -113
  39. package/lib/guard-domain.js +34 -157
  40. package/lib/guard-dsn.js +27 -43
  41. package/lib/guard-email.js +47 -69
  42. package/lib/guard-envelope.js +19 -32
  43. package/lib/guard-event-bus-payload.js +24 -42
  44. package/lib/guard-event-bus-topic.js +25 -43
  45. package/lib/guard-filename.js +42 -106
  46. package/lib/guard-graphql.js +42 -123
  47. package/lib/guard-html.js +53 -108
  48. package/lib/guard-idempotency-key.js +24 -42
  49. package/lib/guard-image.js +46 -103
  50. package/lib/guard-imap-command.js +18 -32
  51. package/lib/guard-jmap.js +16 -30
  52. package/lib/guard-json.js +38 -108
  53. package/lib/guard-jsonpath.js +38 -171
  54. package/lib/guard-jwt.js +49 -179
  55. package/lib/guard-list-id.js +25 -41
  56. package/lib/guard-list-unsubscribe.js +27 -43
  57. package/lib/guard-mail-compose.js +24 -42
  58. package/lib/guard-mail-move.js +26 -44
  59. package/lib/guard-mail-query.js +28 -46
  60. package/lib/guard-mail-reply.js +24 -42
  61. package/lib/guard-mail-sieve.js +24 -42
  62. package/lib/guard-managesieve-command.js +17 -31
  63. package/lib/guard-markdown.js +37 -104
  64. package/lib/guard-message-id.js +26 -45
  65. package/lib/guard-mime.js +39 -151
  66. package/lib/guard-oauth.js +54 -135
  67. package/lib/guard-pdf.js +45 -101
  68. package/lib/guard-pop3-command.js +21 -31
  69. package/lib/guard-posture-chain.js +24 -42
  70. package/lib/guard-regex.js +33 -107
  71. package/lib/guard-saga-config.js +24 -42
  72. package/lib/guard-shell.js +42 -172
  73. package/lib/guard-smtp-command.js +48 -54
  74. package/lib/guard-snapshot-envelope.js +24 -42
  75. package/lib/guard-sql.js +1491 -0
  76. package/lib/guard-stream-args.js +24 -43
  77. package/lib/guard-svg.js +47 -65
  78. package/lib/guard-template.js +35 -172
  79. package/lib/guard-tenant-id.js +26 -45
  80. package/lib/guard-time.js +32 -154
  81. package/lib/guard-trace-context.js +25 -44
  82. package/lib/guard-uuid.js +32 -153
  83. package/lib/guard-xml.js +38 -113
  84. package/lib/guard-yaml.js +51 -163
  85. package/lib/http-client.js +14 -0
  86. package/lib/inbox.js +120 -107
  87. package/lib/legal-hold.js +107 -50
  88. package/lib/log-stream-cloudwatch.js +47 -31
  89. package/lib/log-stream-otlp.js +32 -18
  90. package/lib/mail-crypto-smime.js +2 -6
  91. package/lib/mail-greylist.js +2 -6
  92. package/lib/mail-helo.js +2 -6
  93. package/lib/mail-journal.js +85 -64
  94. package/lib/mail-rbl.js +2 -6
  95. package/lib/mail-scan.js +2 -6
  96. package/lib/mail-spam-score.js +2 -6
  97. package/lib/mail-store.js +287 -154
  98. package/lib/middleware/fetch-metadata.js +17 -7
  99. package/lib/middleware/idempotency-key.js +54 -38
  100. package/lib/middleware/rate-limit.js +102 -32
  101. package/lib/middleware/security-headers.js +21 -5
  102. package/lib/migrations.js +108 -66
  103. package/lib/network-heartbeat.js +7 -0
  104. package/lib/nonce-store.js +31 -9
  105. package/lib/object-store/azure-blob-bucket-ops.js +9 -4
  106. package/lib/object-store/azure-blob.js +31 -3
  107. package/lib/object-store/sigv4.js +10 -0
  108. package/lib/outbox.js +136 -82
  109. package/lib/pqc-agent.js +44 -0
  110. package/lib/pubsub-cluster.js +42 -20
  111. package/lib/queue-local.js +202 -139
  112. package/lib/queue-redis.js +9 -1
  113. package/lib/queue-sqs.js +6 -0
  114. package/lib/retention.js +82 -39
  115. package/lib/safe-dns.js +29 -45
  116. package/lib/safe-ical.js +18 -33
  117. package/lib/safe-icap.js +27 -43
  118. package/lib/safe-sieve.js +21 -40
  119. package/lib/safe-sql.js +124 -3
  120. package/lib/safe-vcard.js +18 -33
  121. package/lib/scheduler.js +35 -12
  122. package/lib/seeders.js +122 -74
  123. package/lib/session-stores.js +42 -14
  124. package/lib/session.js +109 -72
  125. package/lib/sql.js +3885 -0
  126. package/lib/static.js +45 -7
  127. package/lib/subject.js +55 -17
  128. package/lib/vault/index.js +3 -2
  129. package/lib/vault/passphrase-ops.js +3 -2
  130. package/lib/vault/rotate.js +104 -64
  131. package/lib/vendor-data.js +2 -0
  132. package/lib/websocket.js +16 -0
  133. package/package.json +1 -1
  134. package/sbom.cdx.json +6 -6
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
- for (var i = 0; i < issues.length; i += 1) {
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
- * @primitive b.guardTime.gate
439
- * @signature b.guardTime.gate(opts?)
440
- * @since 0.7.46
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
- * @primitive b.guardTime.buildProfile
491
- * @signature b.guardTime.buildProfile(opts)
492
- * @since 0.7.46
493
- * @status stable
494
- * @related b.guardTime.gate, b.guardTime.compliancePosture
495
- *
496
- * Compose a derived profile from one or more named bases plus
497
- * inline overrides. `opts.extends` is a profile name or array of
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
- module.exports = {
563
- // ---- guard-* family registry exports ----
564
- NAME: "time",
565
- KIND: "identifier",
566
- INTEGRATION_FIXTURES: Object.freeze({
567
- kind: "identifier",
568
- benignBytes: Buffer.from("2026-05-05T12:34:56Z", "utf8"),
569
- hostileBytes: Buffer.from("2026-05-05 12:34:56", "utf8"),
570
- benignIdentifier: "2026-05-05T12:34:56Z",
571
- // Hostile: naive datetime (space separator + no offset) — refused
572
- // at strict (cross-region ambiguity class).
573
- hostileIdentifier: "2026-05-05 12:34:56",
574
- }),
575
- // ---- primitive surface ----
576
- validate: validate,
577
- sanitize: sanitize,
578
- gate: gate,
579
- buildProfile: buildProfile,
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 = Object.freeze({
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
- * @primitive b.guardTraceContext.compliancePosture
137
- * @signature b.guardTraceContext.compliancePosture(posture)
138
- * @since 0.9.29
139
- * @status stable
140
- *
141
- * Return the effective profile for a given compliance posture name.
142
- * Returns `null` for unknown posture names so operator typos surface
143
- * here instead of silently falling through to the default profile.
144
- *
145
- * @example
146
- * b.guardTraceContext.compliancePosture("hipaa"); // returns "strict"
147
- */
148
- function compliancePosture(posture) {
149
- return COMPLIANCE_POSTURES[posture] || null;
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
- for (var i = 0; i < issues.length; i += 1) {
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
- * @primitive b.guardUuid.gate
403
- * @signature b.guardUuid.gate(opts?)
404
- * @since 0.7.44
405
- * @status stable
406
- * @compliance hipaa, pci-dss, gdpr, soc2
407
- * @related b.guardUuid.validate, b.guardUuid.sanitize, b.guardAll.gate
408
- *
409
- * Build a guard gate whose async `check(ctx)` returns `{ ok, action, issues }`, consumable
410
- * by `b.guardAll`, ID validators, and any host that handles
411
- * UUID-shaped tokens. The gate reads `ctx.identifier` (or
412
- * `ctx.uuid`), runs `validate`, and maps severity to action: zero
413
- * issues `serve`; only low/medium `audit-only`; any high/critical
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
- module.exports = {
526
- // ---- guard-* family registry exports ----
527
- NAME: "uuid",
528
- KIND: "identifier",
529
- INTEGRATION_FIXTURES: Object.freeze({
530
- kind: "identifier",
531
- benignBytes: Buffer.from("550e8400-e29b-41d4-a716-446655440000", "utf8"),
532
- hostileBytes: Buffer.from("00000000-0000-0000-0000-000000000000", "utf8"),
533
- benignIdentifier: "550e8400-e29b-41d4-a716-446655440000",
534
- // Hostile: nil UUID — refused at strict (sentinel-leak class).
535
- hostileIdentifier: "00000000-0000-0000-0000-000000000000",
536
- }),
537
- // ---- primitive surface ----
538
- validate: validate,
539
- sanitize: sanitize,
540
- gate: gate,
541
- buildProfile: buildProfile,
542
- compliancePosture: compliancePosture,
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
- for (var i = 0; i < issues.length; i += 1) {
482
- if (issues[i].severity === "critical") {
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
- * @primitive b.guardXml.buildProfile
561
- * @signature b.guardXml.buildProfile(opts)
562
- * @since 0.7.15
563
- * @status stable
564
- * @related b.guardXml.gate, b.guardXml.compliancePosture
565
- *
566
- * Compose a derived profile from one or more named bases plus
567
- * inline overrides. `opts.extends` is a profile name (`"strict"` /
568
- * `"balanced"` / `"permissive"`) or an array of names; later entries
569
- * shadow earlier ones. Inline `opts` keys win last. Used to keep
570
- * operator-defined profiles traceable to a baseline rather than re-
571
- * typing every key.
572
- *
573
- * @opts
574
- * extends: string|string[], // base profile name(s) to compose
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
- var _xmlRulePacks = gateContract.makeRulePackLoader(GuardXmlError, "xml");
611
- /**
612
- * @primitive b.guardXml.loadRulePack
613
- * @signature b.guardXml.loadRulePack(pack)
614
- * @since 0.7.15
615
- * @status stable
616
- * @related b.guardXml.gate
617
- *
618
- * Register an operator-supplied rule pack with the guard-xml
619
- * registry. The pack is identified by `pack.id` (non-empty string)
620
- * and stored for later inspection / dispatch by gates that opt in
621
- * via `opts.rulePackId`. Returns the pack object unchanged on
622
- * success; throws `GuardXmlError("xml.bad-opt")` when `pack` is
623
- * missing or `pack.id` is not a non-empty string.
624
- *
625
- * @example
626
- * var pack = b.guardXml.loadRulePack({
627
- * id: "soap-envelope",
628
- * rules: [
629
- * { id: "must-have-envelope", severity: "high",
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
+ });