@blamejs/core 0.14.27 → 0.15.1

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 +6 -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 +158 -77
  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 +228 -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 +82 -29
  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 +517 -256
  26. package/lib/db-schema.js +209 -44
  27. package/lib/db.js +202 -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 +293 -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 +116 -72
  125. package/lib/sql.js +3885 -0
  126. package/lib/static.js +45 -7
  127. package/lib/subject.js +89 -49
  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
@@ -23,6 +23,7 @@
23
23
  */
24
24
 
25
25
  var { defineClass } = require("./framework-error");
26
+ var gateContract = require("./gate-contract");
26
27
 
27
28
  var GuardAgentRegistryError = defineClass("GuardAgentRegistryError", { alwaysPermanent: true });
28
29
 
@@ -34,12 +35,7 @@ var PROFILES = Object.freeze({
34
35
  permissive: { maxNameBytes: 512, maxKindBytes: 128 },
35
36
  });
36
37
 
37
- var COMPLIANCE_POSTURES = Object.freeze({
38
- hipaa: "strict",
39
- "pci-dss": "strict",
40
- gdpr: "strict",
41
- soc2: "strict",
42
- });
38
+ var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
43
39
 
44
40
  var RESERVED_PREFIXES = Object.freeze(["FRAMEWORK.", "ROOT.", "framework.", "root."]);
45
41
  var RESERVED_EXACT = Object.freeze({ "ROOT": true, "FRAMEWORK": true, "*": true });
@@ -89,22 +85,10 @@ function validate(op, opts) {
89
85
  return op;
90
86
  }
91
87
 
92
- /**
93
- * @primitive b.guardAgentRegistry.compliancePosture
94
- * @signature b.guardAgentRegistry.compliancePosture(posture)
95
- * @since 0.9.21
96
- * @status stable
97
- *
98
- * Return the effective profile for a given compliance posture name.
99
- * Returns `null` for unknown posture names so operator typos surface
100
- * here instead of silently falling through to the default profile.
101
- *
102
- * @example
103
- * b.guardAgentRegistry.compliancePosture("hipaa"); // → "strict"
104
- */
105
- function compliancePosture(posture) {
106
- return COMPLIANCE_POSTURES[posture] || null;
107
- }
88
+ // compliancePosture is assembled by gateContract.defineParser below; its
89
+ // wiki section renders from the single-sourced @abiTemplate (defineParser)
90
+ // block in gate-contract.js, instantiated for this guard by the page
91
+ // generator.
108
92
 
109
93
  function _checkName(name, profile) {
110
94
  if (typeof name !== "string" || name.length === 0) {
@@ -154,26 +138,24 @@ function _checkKind(kind, profile) {
154
138
  }
155
139
  }
156
140
 
157
- function _resolveProfile(opts) {
158
- if (opts.posture && COMPLIANCE_POSTURES[opts.posture]) {
159
- return COMPLIANCE_POSTURES[opts.posture];
160
- }
161
- var p = opts.profile || DEFAULT_PROFILE;
162
- if (!PROFILES[p]) {
163
- throw new GuardAgentRegistryError("agent-registry/bad-profile",
164
- "guardAgentRegistry: unknown profile '" + p + "'");
165
- }
166
- return p;
167
- }
141
+ var _resolveProfile = gateContract.makeProfileResolver({
142
+ profiles: PROFILES,
143
+ postures: COMPLIANCE_POSTURES,
144
+ defaults: DEFAULT_PROFILE,
145
+ errorClass: GuardAgentRegistryError,
146
+ codePrefix: "agent-registry",
147
+ });
168
148
 
169
- module.exports = {
170
- validate: validate,
171
- compliancePosture: compliancePosture,
172
- PROFILES: PROFILES,
173
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
174
- RESERVED_PREFIXES: RESERVED_PREFIXES,
175
- RESERVED_EXACT: RESERVED_EXACT,
176
- GuardAgentRegistryError: GuardAgentRegistryError,
177
- NAME: "agentRegistry",
178
- KIND: "agent-registry",
179
- };
149
+ module.exports = gateContract.defineParser({
150
+ name: "agent-registry",
151
+ entry: validate,
152
+ errorClass: GuardAgentRegistryError,
153
+ profiles: PROFILES,
154
+ postures: COMPLIANCE_POSTURES,
155
+ extra: {
156
+ RESERVED_PREFIXES: RESERVED_PREFIXES,
157
+ RESERVED_EXACT: RESERVED_EXACT,
158
+ NAME: "agentRegistry",
159
+ KIND: "agent-registry",
160
+ },
161
+ });
package/lib/guard-all.js CHANGED
@@ -84,6 +84,7 @@ var STANDALONE_GUARDS = [
84
84
  require("./guard-pdf"),
85
85
  require("./guard-auth"),
86
86
  require("./guard-smtp-command"),
87
+ require("./guard-sql"),
87
88
  ];
88
89
 
89
90
  // Framework-wide profile + posture vocabulary that every guard MUST
package/lib/guard-auth.js CHANGED
@@ -335,117 +335,47 @@ function gate(opts) {
335
335
  });
336
336
  }
337
337
 
338
- /**
339
- * @primitive b.guardAuth.buildProfile
340
- * @signature b.guardAuth.buildProfile(opts)
341
- * @since 0.7.41
342
- * @status stable
343
- * @related b.guardAuth.gate, b.guardAuth.compliancePosture
344
- *
345
- * Compose a derived profile from one or more named bases plus inline
346
- * overrides. `opts.extends` is a profile name (`"strict"` /
347
- * `"balanced"` / `"permissive"`) or an array of names; later entries
348
- * shadow earlier ones. Inline `opts` keys win last. Used to keep
349
- * operator-defined profiles traceable to a baseline rather than
350
- * re-typing every key.
351
- *
352
- * @opts
353
- * extends: string|string[], // base profile name(s) to compose
354
- *
355
- * @example
356
- * var custom = b.guardAuth.buildProfile({
357
- * extends: "balanced",
358
- * requireAtLeastOne: true,
359
- * });
360
- * custom.requireAtLeastOne; // → true
361
- * custom.bidiPolicy; // → "reject"
362
- */
363
- var buildProfile = gateContract.makeProfileBuilder(PROFILES);
338
+ // buildProfile / compliancePosture / loadRulePack are assembled by
339
+ // gateContract.defineGuard below — their wiki sections render from the
340
+ // single-sourced @abiTemplate blocks in gate-contract.js.
364
341
 
365
- /**
366
- * @primitive b.guardAuth.compliancePosture
367
- * @signature b.guardAuth.compliancePosture(name)
368
- * @since 0.7.41
369
- * @status stable
370
- * @compliance hipaa, pci-dss, gdpr, soc2
371
- * @related b.guardAuth.gate, b.guardAuth.buildProfile
372
- *
373
- * Look up a compliance-posture overlay by name (`"hipaa"` /
374
- * `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of the
375
- * posture object — the caller may mutate freely. Throws
376
- * `GuardAuthError("auth.bad-posture")` on unknown name.
377
- *
378
- * @example
379
- * var posture = b.guardAuth.compliancePosture("hipaa");
380
- * posture.forensicSnippetBytes; // → 512
381
- * posture.bidiPolicy; // "reject"
382
- */
383
- function compliancePosture(name) {
384
- return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES,
385
- _err, "auth");
386
- }
387
-
388
- var _authRulePacks = gateContract.makeRulePackLoader(GuardAuthError, "auth");
389
- /**
390
- * @primitive b.guardAuth.loadRulePack
391
- * @signature b.guardAuth.loadRulePack(pack)
392
- * @since 0.7.41
393
- * @status stable
394
- * @related b.guardAuth.gate
395
- *
396
- * Register an operator-supplied rule pack with the guard-auth
397
- * registry. The pack is identified by `pack.id` (non-empty string)
398
- * and stored for later inspection / dispatch by gates that opt in
399
- * via `opts.rulePackId`. Returns the pack object unchanged on
400
- * success; throws `GuardAuthError("auth.bad-opt")` when `pack` is
401
- * missing or `pack.id` is not a non-empty string.
402
- *
403
- * @example
404
- * var pack = b.guardAuth.loadRulePack({
405
- * id: "tenant-bearer-prefix",
406
- * rules: [
407
- * { id: "tenant-prefix", severity: "high",
408
- * detect: function (b2) { return b2.jwtToken && b2.jwtToken.indexOf("tenant_") !== 0; },
409
- * reason: "JWT does not carry the required tenant_ prefix" },
410
- * ],
411
- * });
412
- * pack.id; // → "tenant-bearer-prefix"
413
- */
414
- var loadRulePack = _authRulePacks.load;
342
+ // ---- adaptive integration-test fixtures (consumed by layer-5 host harness) ----
343
+ var INTEGRATION_FIXTURES = Object.freeze({
344
+ kind: "auth-bundle",
345
+ benignBytes: Buffer.from(JSON.stringify({
346
+ jwtToken: "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9." +
347
+ "eyJpc3MiOiJleGFtcGxlIiwiZXhwIjo5OTk5OTk5OTk5LCJpYXQiOjE3MDAwMDAwMDB9.sig",
348
+ cookieHeader: "sid=abc123; theme=dark",
349
+ }), "utf8"),
350
+ hostileBytes: Buffer.from(JSON.stringify({
351
+ jwtToken: "eyJhbGciOiJub25lIn0.eyJzdWIiOiJ4In0.",
352
+ }), "utf8"),
353
+ benignAuthBundle: {
354
+ jwtToken: "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9." +
355
+ "eyJpc3MiOiJleGFtcGxlIiwiZXhwIjo5OTk5OTk5OTk5LCJpYXQiOjE3MDAwMDAwMDB9.sig",
356
+ cookieHeader: "sid=abc123; theme=dark",
357
+ },
358
+ // Hostile: alg=none JWT — universal refuse routed through guardJwt.
359
+ hostileAuthBundle: {
360
+ jwtToken: "eyJhbGciOiJub25lIn0.eyJzdWIiOiJ4In0.",
361
+ },
362
+ });
415
363
 
416
- module.exports = {
417
- // ---- guard-* family registry exports ----
418
- NAME: "auth",
419
- KIND: "auth-bundle",
420
- INTEGRATION_FIXTURES: Object.freeze({
421
- kind: "auth-bundle",
422
- benignBytes: Buffer.from(JSON.stringify({
423
- jwtToken: "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9." +
424
- "eyJpc3MiOiJleGFtcGxlIiwiZXhwIjo5OTk5OTk5OTk5LCJpYXQiOjE3MDAwMDAwMDB9.sig",
425
- cookieHeader: "sid=abc123; theme=dark",
426
- }), "utf8"),
427
- hostileBytes: Buffer.from(JSON.stringify({
428
- jwtToken: "eyJhbGciOiJub25lIn0.eyJzdWIiOiJ4In0.",
429
- }), "utf8"),
430
- benignAuthBundle: {
431
- jwtToken: "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9." +
432
- "eyJpc3MiOiJleGFtcGxlIiwiZXhwIjo5OTk5OTk5OTk5LCJpYXQiOjE3MDAwMDAwMDB9.sig",
433
- cookieHeader: "sid=abc123; theme=dark",
434
- },
435
- // Hostile: alg=none JWT — universal refuse routed through guardJwt.
436
- hostileAuthBundle: {
437
- jwtToken: "eyJhbGciOiJub25lIn0.eyJzdWIiOiJ4In0.",
438
- },
439
- }),
440
- // ---- primitive surface ----
441
- validate: validate,
442
- sanitize: sanitize,
443
- gate: gate,
444
- buildProfile: buildProfile,
445
- compliancePosture: compliancePosture,
446
- loadRulePack: loadRulePack,
447
- PROFILES: PROFILES,
448
- DEFAULTS: DEFAULTS,
449
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
450
- GuardAuthError: GuardAuthError,
451
- };
364
+ // Assembled from the gate-contract guard factory: error class, registry
365
+ // exports (NAME / KIND / INTEGRATION_FIXTURES), buildProfile /
366
+ // compliancePosture / loadRulePack wiring, plus the per-guard inspection
367
+ // surface (validate / sanitize / bespoke gate) passed through verbatim.
368
+ // The custom KIND ("auth-bundle") is accepted because the bespoke gate
369
+ // reads its own ctx fields (ctx.authBundle / ctx.auth).
370
+ module.exports = gateContract.defineGuard({
371
+ name: "auth",
372
+ kind: "auth-bundle",
373
+ errorClass: GuardAuthError,
374
+ profiles: PROFILES,
375
+ defaults: DEFAULTS,
376
+ postures: COMPLIANCE_POSTURES,
377
+ integrationFixtures: INTEGRATION_FIXTURES,
378
+ validate: validate,
379
+ sanitize: sanitize,
380
+ gate: gate,
381
+ });
package/lib/guard-cidr.js CHANGED
@@ -511,12 +511,7 @@ function sanitize(input, opts) {
511
511
  throw _err("cidr.bad-input", "sanitize requires string input");
512
512
  }
513
513
  var issues = _detectIssues(input, opts);
514
- for (var i = 0; i < issues.length; i += 1) {
515
- if (issues[i].severity === "critical" || issues[i].severity === "high") {
516
- throw _err(issues[i].ruleId || "cidr.refused",
517
- "guardCidr.sanitize: " + issues[i].snippet);
518
- }
519
- }
514
+ gateContract.throwOnRefusalSeverity(issues, { errorClass: GuardCidrError, codePrefix: "cidr" });
520
515
  // Normalize: lowercase IPv6 groups + canonical mask form.
521
516
  var slashAt = input.indexOf("/");
522
517
  var addr = slashAt === -1 ? input : input.slice(0, slashAt);
@@ -525,152 +520,36 @@ function sanitize(input, opts) {
525
520
  return mask === null ? addr.toLowerCase() : addr.toLowerCase() + "/" + mask;
526
521
  }
527
522
 
528
- /**
529
- * @primitive b.guardCidr.gate
530
- * @signature b.guardCidr.gate(opts?)
531
- * @since 0.7.41
532
- * @status stable
533
- * @compliance hipaa, pci-dss, gdpr, soc2
534
- * @related b.guardCidr.validate, b.guardCidr.sanitize
535
- *
536
- * Build a `b.gateContract` gate that consumes `ctx.identifier` (or
537
- * `ctx.cidr`) and dispatches `serve` (no input or clean) →
538
- * `audit-only` (warn-only issues) `refuse` (any critical or high
539
- * issue). No `sanitize` action — CIDR sanitization is caller-driven
540
- * via `b.guardCidr.sanitize`; an allowlist gate that silently rewrote
541
- * the operator's network range would be its own bug class.
542
- *
543
- * @opts
544
- * profile: "strict"|"balanced"|"permissive",
545
- * compliancePosture: "hipaa"|"pci-dss"|"gdpr"|"soc2",
546
- * name: string, // gate identity for audit / observability
547
- * family: "either"|"ipv4-only"|"ipv6-only",
548
- *
549
- * @example
550
- * var cidrGate = b.guardCidr.gate({ profile: "strict", family: "ipv4-only" });
551
- * var verdict = await cidrGate.check({ identifier: "10.0.0.0/8" });
552
- * verdict.action; // → "refuse"
553
- */
554
- function gate(opts) {
555
- opts = _resolveOpts(opts);
556
- return gateContract.buildGuardGate(
557
- opts.name || "guardCidr:" + (opts.profile || "default"),
558
- opts,
559
- async function (ctx) {
560
- var identifier = ctx && (ctx.identifier || ctx.cidr || "");
561
- if (!identifier) return { ok: true, action: "serve" };
562
- var rv = validate(identifier, opts);
563
- if (rv.issues.length === 0) return { ok: true, action: "serve" };
564
- var hasCritical = rv.issues.some(function (i) {
565
- return i.severity === "critical";
566
- });
567
- var hasHigh = rv.issues.some(function (i) {
568
- return i.severity === "high";
569
- });
570
- if (!hasCritical && !hasHigh) {
571
- return { ok: true, action: "audit-only", issues: rv.issues };
572
- }
573
- return { ok: false, action: "refuse", issues: rv.issues };
574
- });
575
- }
576
-
577
- /**
578
- * @primitive b.guardCidr.buildProfile
579
- * @signature b.guardCidr.buildProfile(opts)
580
- * @since 0.7.41
581
- * @status stable
582
- * @related b.guardCidr.gate, b.guardCidr.compliancePosture
583
- *
584
- * Compose a derived profile from one or more named bases plus inline
585
- * overrides. `opts.extends` is a profile name (`"strict"` /
586
- * `"balanced"` / `"permissive"`) or an array of names; later entries
587
- * shadow earlier ones. Inline `opts` keys win last.
588
- *
589
- * @opts
590
- * extends: string|string[], // base profile name(s) to compose
591
- *
592
- * @example
593
- * var custom = b.guardCidr.buildProfile({
594
- * extends: "balanced",
595
- * reservedRangesPolicy: "reject",
596
- * });
597
- * custom.reservedRangesPolicy; // → "reject"
598
- * custom.bidiPolicy; // → "reject"
599
- */
600
- var buildProfile = gateContract.makeProfileBuilder(PROFILES);
601
-
602
- /**
603
- * @primitive b.guardCidr.compliancePosture
604
- * @signature b.guardCidr.compliancePosture(name)
605
- * @since 0.7.41
606
- * @status stable
607
- * @compliance hipaa, pci-dss, gdpr, soc2
608
- * @related b.guardCidr.gate, b.guardCidr.buildProfile
609
- *
610
- * Look up a compliance-posture overlay by name (`"hipaa"` /
611
- * `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of the
612
- * posture object — the caller may mutate freely. Throws
613
- * `GuardCidrError("cidr.bad-posture")` on unknown name.
614
- *
615
- * @example
616
- * var posture = b.guardCidr.compliancePosture("hipaa");
617
- * posture.reservedRangesPolicy; // → "reject"
618
- * posture.forensicSnippetBytes; // → 128
619
- */
620
- function compliancePosture(name) {
621
- return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES,
622
- _err, "cidr");
623
- }
523
+ // gate / buildProfile / compliancePosture / loadRulePack are assembled by
524
+ // gateContract.defineGuard below; their wiki sections render from the
525
+ // single-sourced @abiTemplate (defineGuard) blocks in gate-contract.js,
526
+ // instantiated per guard by the page generator.
527
+
528
+ var INTEGRATION_FIXTURES = Object.freeze({
529
+ kind: "identifier",
530
+ benignBytes: Buffer.from("8.8.8.0/24", "utf8"),
531
+ hostileBytes: Buffer.from("10.0.0.0/8", "utf8"),
532
+ benignIdentifier: "8.8.8.0/24",
533
+ // Hostile: RFC 1918 private range refused at strict.
534
+ hostileIdentifier: "10.0.0.0/8",
535
+ });
624
536
 
625
- var _cidrRulePacks = gateContract.makeRulePackLoader(GuardCidrError, "cidr");
626
- /**
627
- * @primitive b.guardCidr.loadRulePack
628
- * @signature b.guardCidr.loadRulePack(pack)
629
- * @since 0.7.41
630
- * @status stable
631
- * @related b.guardCidr.gate
632
- *
633
- * Register an operator-supplied rule pack with the guard-cidr
634
- * registry. The pack is identified by `pack.id` (non-empty string)
635
- * and stored for later inspection / dispatch by gates that opt in
636
- * via `opts.rulePackId`. Returns the pack object unchanged on
637
- * success; throws `GuardCidrError("cidr.bad-opt")` when `pack` is
638
- * missing or `pack.id` is not a non-empty string.
639
- *
640
- * @example
641
- * var pack = b.guardCidr.loadRulePack({
642
- * id: "tenant-private-only",
643
- * rules: [
644
- * { id: "external-allowlisted", severity: "high",
645
- * detect: function (cidr) { return cidr.indexOf("10.") !== 0; },
646
- * reason: "tenant policy: only 10.0.0.0/8 ranges permitted" },
647
- * ],
648
- * });
649
- * pack.id; // → "tenant-private-only"
650
- */
651
- var loadRulePack = _cidrRulePacks.load;
652
-
653
- module.exports = {
654
- // ---- guard-* family registry exports ----
655
- NAME: "cidr",
656
- KIND: "identifier",
657
- INTEGRATION_FIXTURES: Object.freeze({
658
- kind: "identifier",
659
- benignBytes: Buffer.from("8.8.8.0/24", "utf8"),
660
- hostileBytes: Buffer.from("10.0.0.0/8", "utf8"),
661
- benignIdentifier: "8.8.8.0/24",
662
- // Hostile: RFC 1918 private range — refused at strict.
663
- hostileIdentifier: "10.0.0.0/8",
664
- }),
665
- // ---- primitive surface ----
666
- validate: validate,
667
- sanitize: sanitize,
668
- gate: gate,
669
- buildProfile: buildProfile,
670
- compliancePosture: compliancePosture,
671
- loadRulePack: loadRulePack,
672
- PROFILES: PROFILES,
673
- DEFAULTS: DEFAULTS,
674
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
675
- GuardCidrError: GuardCidrError,
676
- };
537
+ // Assembled from the gate-contract guard factory: error class, registry
538
+ // exports (NAME / KIND / INTEGRATION_FIXTURES), buildProfile /
539
+ // compliancePosture / loadRulePack wiring, plus the per-guard inspection
540
+ // surface (validate / sanitize). The gate is the factory default — the
541
+ // standard serve -> audit-only -> refuse chain — reading ctx.identifier ||
542
+ // ctx.cidr via ctxFields. No sanitize action: an allowlist gate never
543
+ // rewrites the operator's stored network range.
544
+ module.exports = gateContract.defineGuard({
545
+ name: "cidr",
546
+ kind: "identifier",
547
+ errorClass: GuardCidrError,
548
+ profiles: PROFILES,
549
+ defaults: DEFAULTS,
550
+ postures: COMPLIANCE_POSTURES,
551
+ integrationFixtures: INTEGRATION_FIXTURES,
552
+ validate: validate,
553
+ sanitize: sanitize,
554
+ ctxFields: ["identifier", "cidr"],
555
+ });
package/lib/guard-csv.js CHANGED
@@ -1060,117 +1060,50 @@ function gate(opts) {
1060
1060
  });
1061
1061
  }
1062
1062
 
1063
- /**
1064
- * @primitive b.guardCsv.buildProfile
1065
- * @signature b.guardCsv.buildProfile(opts)
1066
- * @since 0.7.5
1067
- * @status stable
1068
- * @related b.guardCsv.gate, b.guardCsv.compliancePosture
1069
- *
1070
- * Compose a derived profile from one or more named bases plus
1071
- * inline overrides. `opts.extends` is a profile name (`"strict"`
1072
- * / `"balanced"` / `"permissive"` / `"email-attachment"`) or an
1073
- * array of names; later entries shadow earlier ones. Inline
1074
- * `opts` keys win last. Used to keep operator-defined profiles
1075
- * traceable to a baseline rather than re-typing every key.
1076
- *
1077
- * @opts
1078
- * extends: string|string[], // base profile name(s) to compose
1079
- * ...: any guard-csv key, // inline override of resolved keys
1080
- *
1081
- * @example
1082
- * var custom = b.guardCsv.buildProfile({
1083
- * extends: "strict",
1084
- * trailingWhitespacePolicy: "preserve",
1085
- * bomPrefix: true,
1086
- * });
1087
- * custom.formulaInjectionPolicy; // → "prefix-tab"
1088
- * custom.bomPrefix; // → true
1089
- */
1090
- var buildProfile = gateContract.makeProfileBuilder(PROFILES);
1091
-
1092
- /**
1093
- * @primitive b.guardCsv.compliancePosture
1094
- * @signature b.guardCsv.compliancePosture(name)
1095
- * @since 0.7.5
1096
- * @status stable
1097
- * @compliance hipaa, pci-dss, gdpr, soc2
1098
- * @related b.guardCsv.gate, b.guardCsv.buildProfile
1099
- *
1100
- * Look up a compliance-posture overlay by name (`"hipaa"` /
1101
- * `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of
1102
- * the posture object — the caller may mutate freely. Throws
1103
- * `GuardCsvError("csv.bad-posture")` on unknown name.
1104
- *
1105
- * @example
1106
- * var posture = b.guardCsv.compliancePosture("hipaa");
1107
- * posture.piiPolicy; // → "redact"
1108
- * posture.bidiCharPolicy; // → "reject"
1109
- */
1110
- function compliancePosture(name) {
1111
- return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES, _err, "csv");
1112
- }
1063
+ // buildProfile / compliancePosture / loadRulePack are assembled by
1064
+ // gateContract.defineGuard below (makeProfileBuilder(PROFILES) /
1065
+ // lookupCompliancePosture(_, COMPLIANCE_POSTURES) / makeRulePackLoader).
1066
+ // Their wiki sections render from the single-sourced @abiTemplate blocks
1067
+ // in gate-contract.js, instantiated per guard by the page generator.
1068
+
1069
+ // ---- adaptive integration-test fixtures (consumed by layer-5 host harness) ----
1070
+ var INTEGRATION_FIXTURES = Object.freeze({
1071
+ kind: "content",
1072
+ contentType: "text/csv",
1073
+ extension: ".csv",
1074
+ benignBytes: Buffer.from("name,age\r\nalice,30\r\n", "utf8"),
1075
+ // Hostile: cell starts with formula trigger `=cmd|x` strict
1076
+ // profile prepends TAB so spreadsheets disarm at evaluation time;
1077
+ // gate's check returns refuse for any critical/high issue.
1078
+ hostileBytes: Buffer.from("name,formula\r\nalice,=cmd|x\r\n", "utf8"),
1079
+ });
1113
1080
 
1114
- var _csvRulePacks = gateContract.makeRulePackLoader(GuardCsvError, "csv");
1115
- /**
1116
- * @primitive b.guardCsv.loadRulePack
1117
- * @signature b.guardCsv.loadRulePack(pack)
1118
- * @since 0.7.5
1119
- * @status stable
1120
- * @related b.guardCsv.gate
1121
- *
1122
- * Register an operator-supplied rule pack with the guard-csv
1123
- * registry. The pack is identified by `pack.id` (non-empty
1124
- * string) and stored for later inspection / dispatch by gates
1125
- * that opt in via `opts.rulePackId`. Returns the pack object
1126
- * unchanged on success; throws `GuardCsvError("csv.bad-opt")`
1127
- * when `pack` is missing or `pack.id` is not a non-empty string.
1128
- *
1129
- * @example
1130
- * var pack = b.guardCsv.loadRulePack({
1131
- * id: "pii-extra",
1132
- * rules: [
1133
- * { id: "ssn-cell", severity: "critical",
1134
- * detect: function (cell) { return /^\d{3}-\d{2}-\d{4}$/.test(cell); },
1135
- * reason: "US SSN-shaped value in CSV cell" },
1136
- * ],
1137
- * });
1138
- * pack.id; // → "pii-extra"
1139
- */
1140
- var loadRulePack = _csvRulePacks.load;
1141
-
1142
- module.exports = {
1143
- // ---- guard-* family registry exports (consumed by b.guardAll) ----
1144
- NAME: "csv",
1145
- KIND: "content", // content-bytes guard (consumes ctx.bytes)
1146
- MIME_TYPES: Object.freeze(["text/csv"]),
1147
- EXTENSIONS: Object.freeze([".csv"]),
1148
- // ---- adaptive integration-test fixtures (consumed by layer-5 host harness) ----
1149
- INTEGRATION_FIXTURES: Object.freeze({
1150
- kind: "content",
1151
- contentType: "text/csv",
1152
- extension: ".csv",
1153
- benignBytes: Buffer.from("name,age\r\nalice,30\r\n", "utf8"),
1154
- // Hostile: cell starts with formula trigger `=cmd|x` — strict
1155
- // profile prepends TAB so spreadsheets disarm at evaluation time;
1156
- // gate's check returns refuse for any critical/high issue.
1157
- hostileBytes: Buffer.from("name,formula\r\nalice,=cmd|x\r\n", "utf8"),
1158
- }),
1159
- // ---- primitive surface ----
1160
- serialize: serialize,
1161
- validate: validate,
1162
- sanitize: sanitize,
1163
- escapeCell: escapeCell,
1164
- detect: detect,
1165
- schema: schema,
1166
- gate: gate,
1167
- buildProfile: buildProfile,
1168
- compliancePosture: compliancePosture,
1169
- loadRulePack: loadRulePack,
1170
- PROFILES: PROFILES,
1171
- DEFAULTS: DEFAULTS,
1172
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
1173
- FORMULA_PREFIXES: FORMULA_PREFIXES,
1174
- DANGEROUS_FUNCTIONS: DANGEROUS_FUNCTIONS,
1175
- GuardCsvError: GuardCsvError,
1176
- };
1081
+ // Assembled from the gate-contract guard factory: error class, registry
1082
+ // exports (NAME / KIND / MIME_TYPES / EXTENSIONS / INTEGRATION_FIXTURES),
1083
+ // buildProfile / compliancePosture / loadRulePack wiring, plus the
1084
+ // per-guard inspection surface (validate / sanitize / gate) and CSV
1085
+ // extras (serialize / escapeCell / detect / schema / FORMULA_PREFIXES /
1086
+ // DANGEROUS_FUNCTIONS) passed through verbatim. The bespoke `gate` carries
1087
+ // CSV's sanitize-reparse-reserialize chain unchanged.
1088
+ module.exports = gateContract.defineGuard({
1089
+ name: "csv",
1090
+ kind: "content",
1091
+ errorClass: GuardCsvError,
1092
+ profiles: PROFILES,
1093
+ defaults: DEFAULTS,
1094
+ postures: COMPLIANCE_POSTURES,
1095
+ mimeTypes: ["text/csv"],
1096
+ extensions: [".csv"],
1097
+ integrationFixtures: INTEGRATION_FIXTURES,
1098
+ validate: validate,
1099
+ sanitize: sanitize,
1100
+ gate: gate,
1101
+ extra: {
1102
+ serialize: serialize,
1103
+ escapeCell: escapeCell,
1104
+ detect: detect,
1105
+ schema: schema,
1106
+ FORMULA_PREFIXES: FORMULA_PREFIXES,
1107
+ DANGEROUS_FUNCTIONS: DANGEROUS_FUNCTIONS,
1108
+ },
1109
+ });