@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/retention.js CHANGED
@@ -46,6 +46,7 @@ var C = require("./constants");
46
46
  var lazyRequire = require("./lazy-require");
47
47
  var validateOpts = require("./validate-opts");
48
48
  var safeSql = require("./safe-sql");
49
+ var sql = require("./sql");
49
50
  var { defineClass } = require("./framework-error");
50
51
 
51
52
  var audit = lazyRequire(function () { return require("./audit"); });
@@ -55,6 +56,25 @@ var legalHold = lazyRequire(function () { return require("./legal-hold"); });
55
56
  var RetentionError = defineClass("RetentionError", { alwaysPermanent: true });
56
57
  var _err = RetentionError.factory;
57
58
 
59
+ // Resolve the b.sql dialect for the operator-supplied handle. The framework's
60
+ // local b.db handle is always node:sqlite (db.js pins { dialect: "sqlite",
61
+ // quoteName: true }) and exposes no .dialect, so this defaults to "sqlite" —
62
+ // every sweep statement runs against that handle via .prepare(). An operator
63
+ // handle that DOES advertise a dialect (string or () -> string) has it
64
+ // threaded through so the emitted identifier quoting + idioms match the
65
+ // backend the handle dispatches to. quoteName stays on for every retention
66
+ // statement: the rule's table / ageField / softDeleteField identifiers are
67
+ // validated then quoted by construction (no clusterStorage prefix rewrite on
68
+ // this operator-app-schema path).
69
+ function _handleDialect(db) {
70
+ if (db && typeof db.dialect === "function") {
71
+ try { var d = db.dialect(); return typeof d === "string" ? d : "sqlite"; }
72
+ catch (_e) { return "sqlite"; }
73
+ }
74
+ if (db && typeof db.dialect === "string") return db.dialect;
75
+ return "sqlite";
76
+ }
77
+
58
78
  // Identifier-level SQLi defense: every operator-supplied table name,
59
79
  // column name, and cascade FK must pass safeSql.validateIdentifier
60
80
  // before reaching SQL string concatenation. Without this gate a
@@ -196,6 +216,11 @@ function create(opts) {
196
216
  throw _err("BAD_OPT", "create: opts.db is required (a b.db handle with .prepare(sql))");
197
217
  }
198
218
  var db = opts.db;
219
+ // b.sql opts for every retention statement built against this handle. The
220
+ // dialect tracks the handle (sqlite for the framework's local b.db); the
221
+ // validated operator identifiers are quoted by construction (quoteName)
222
+ // with no clusterStorage prefix rewrite on this path.
223
+ var SQL_OPTS = { dialect: _handleDialect(db), quoteName: true };
199
224
  var auditOn = opts.audit !== false && opts.audit != null;
200
225
  var auditInstance = (opts.audit && opts.audit !== true) ? opts.audit : null;
201
226
  var rules = {};
@@ -235,16 +260,24 @@ function create(opts) {
235
260
 
236
261
  function _hardDelete(table, rowId, dryRun) {
237
262
  if (dryRun) return { wouldDelete: 1 };
238
- var del = db.prepare("DELETE FROM \"" + table + "\" WHERE _id = ?");
239
- del.run(rowId);
263
+ // Operator app table quoteName so the validated identifier emits as a
264
+ // quoted local name; the row id binds as a placeholder.
265
+ var built = sql.delete(table, SQL_OPTS)
266
+ .where("_id", rowId)
267
+ .toSql();
268
+ var del = db.prepare(built.sql);
269
+ del.run.apply(del, built.params);
240
270
  return { deleted: 1 };
241
271
  }
242
272
 
243
273
  function _softDelete(table, rowId, softField, dryRun) {
244
274
  if (dryRun) return { wouldSoftDelete: 1 };
245
- var upd = db.prepare(
246
- "UPDATE \"" + table + "\" SET \"" + softField + "\" = ? WHERE _id = ?");
247
- upd.run(Date.now(), rowId);
275
+ var built = sql.update(table, SQL_OPTS)
276
+ .set(softField, Date.now())
277
+ .where("_id", rowId)
278
+ .toSql();
279
+ var upd = db.prepare(built.sql);
280
+ upd.run.apply(upd, built.params);
248
281
  return { softDeleted: 1 };
249
282
  }
250
283
 
@@ -261,19 +294,17 @@ function create(opts) {
261
294
  return _hardDelete(table, row._id, dryRun);
262
295
  }
263
296
  if (dryRun) return { wouldErase: 1, sealedFieldCount: sealedFields.length };
264
- var setClauses = [];
265
- var values = [];
266
- for (var si = 0; si < sealedFields.length; si++) {
267
- setClauses.push('"' + sealedFields[si] + '" = ?');
268
- values.push(null);
269
- }
270
- for (var hi = 0; hi < hashFields.length; hi++) {
271
- setClauses.push('"' + hashFields[hi] + '" = ?');
272
- values.push(null);
273
- }
274
- values.push(row._id);
275
- var upd2 = db.prepare("UPDATE \"" + table + "\" SET " + setClauses.join(", ") + " WHERE _id = ?");
276
- upd2.run.apply(upd2, values);
297
+ // NULL every sealed column + its derived-hash sibling. b.sql binds each
298
+ // null as a placeholder (the set map preserves the column ordering).
299
+ var eraseSet = {};
300
+ for (var si = 0; si < sealedFields.length; si++) eraseSet[sealedFields[si]] = null;
301
+ for (var hi = 0; hi < hashFields.length; hi++) eraseSet[hashFields[hi]] = null;
302
+ var eraseBuilt = sql.update(table, SQL_OPTS)
303
+ .set(eraseSet)
304
+ .where("_id", row._id)
305
+ .toSql();
306
+ var upd2 = db.prepare(eraseBuilt.sql);
307
+ upd2.run.apply(upd2, eraseBuilt.params);
277
308
  // Per-row-key tables (declarePerRowKey): NULLing the sealed columns
278
309
  // is not enough — WAL / replica residuals keep the old K_row cells.
279
310
  // Destroy the row's wrapped secret so K_row is unrecoverable and the
@@ -294,15 +325,20 @@ function create(opts) {
294
325
  for (var i = 0; i < rule.cascade.length; i++) {
295
326
  var c = rule.cascade[i];
296
327
  if (dryRun) {
297
- var sel = db.prepare(
298
- "SELECT COUNT(*) AS n FROM \"" + c.table + "\" WHERE \"" + c.foreignKey + "\" = ?");
299
- var n = sel.get(rowId);
328
+ var selBuilt = sql.select(c.table, SQL_OPTS)
329
+ .count("*", "n")
330
+ .where(c.foreignKey, rowId)
331
+ .toSql();
332
+ var sel = db.prepare(selBuilt.sql);
333
+ var n = sel.get.apply(sel, selBuilt.params);
300
334
  cascadeSummary.push({ table: c.table, foreignKey: c.foreignKey,
301
335
  wouldDelete: (n && typeof n.n === "number") ? n.n : 0 });
302
336
  } else {
303
- var del = db.prepare(
304
- "DELETE FROM \"" + c.table + "\" WHERE \"" + c.foreignKey + "\" = ?");
305
- var result = del.run(rowId);
337
+ var delBuilt = sql.delete(c.table, SQL_OPTS)
338
+ .where(c.foreignKey, rowId)
339
+ .toSql();
340
+ var del = db.prepare(delBuilt.sql);
341
+ var result = del.run.apply(del, delBuilt.params);
306
342
  cascadeSummary.push({ table: c.table, foreignKey: c.foreignKey,
307
343
  deleted: result.changes || 0 });
308
344
  }
@@ -392,24 +428,31 @@ function create(opts) {
392
428
  while (moreRows) {
393
429
  var rows;
394
430
  // The candidate WHERE-clause: age + not-already-erased + not-on-legal-hold +
395
- // (when soft-delete is configured) not-already-soft-deleted.
396
- var whereParts = ['"' + rule.ageField + '" <= ?'];
397
- var whereArgs = [cutoff];
398
- if (rule.softDeleteField) {
399
- whereParts.push('("' + rule.softDeleteField + '" IS NULL)');
431
+ // (when soft-delete is configured) not-already-soft-deleted. Built
432
+ // through b.sql so the operator-supplied table / ageField / softDeleteField
433
+ // identifiers are quoted by construction and every value binds as a
434
+ // placeholder (the '' empty-string compare included — no embedded literal).
435
+ function _candidateBase() {
436
+ var qb = sql.select(rule.table, SQL_OPTS)
437
+ .where(rule.ageField, "<=", cutoff);
438
+ if (rule.softDeleteField) qb.whereNull(rule.softDeleteField);
439
+ return qb;
400
440
  }
401
- var sql = "SELECT * FROM \"" + rule.table + "\" " +
402
- "WHERE " + whereParts.join(" AND ") + " " +
403
- "AND (__erasedAt IS NULL OR __erasedAt = '') " +
404
- "LIMIT ?";
405
441
  var selStmt;
406
- try { selStmt = db.prepare(sql); rows = selStmt.all.apply(selStmt, whereArgs.concat([rule.batchSize])); }
407
- catch (_eA) {
442
+ try {
443
+ var built = _candidateBase()
444
+ .whereGroup(function (g) {
445
+ g.whereNull("__erasedAt").orWhereOp("__erasedAt", "=", "");
446
+ })
447
+ .limit(rule.batchSize)
448
+ .toSql();
449
+ selStmt = db.prepare(built.sql);
450
+ rows = selStmt.all.apply(selStmt, built.params);
451
+ } catch (_eA) {
408
452
  // Fallback: tables without __erasedAt
409
- var sqlPlain = "SELECT * FROM \"" + rule.table + "\" " +
410
- "WHERE " + whereParts.join(" AND ") + " LIMIT ?";
411
- var selPlain = db.prepare(sqlPlain);
412
- rows = selPlain.all.apply(selPlain, whereArgs.concat([rule.batchSize]));
453
+ var plainBuilt = _candidateBase().limit(rule.batchSize).toSql();
454
+ var selPlain = db.prepare(plainBuilt.sql);
455
+ rows = selPlain.all.apply(selPlain, plainBuilt.params);
413
456
  }
414
457
  if (!rows || rows.length === 0) { moreRows = false; break; }
415
458
  summary.scanned += rows.length;
package/lib/safe-dns.js CHANGED
@@ -57,6 +57,7 @@
57
57
 
58
58
  var C = require("./constants");
59
59
  var { defineClass } = require("./framework-error");
60
+ var gateContract = require("./gate-contract");
60
61
 
61
62
  var SafeDnsError = defineClass("SafeDnsError", { alwaysPermanent: true });
62
63
 
@@ -153,11 +154,15 @@ var PROFILES = Object.freeze({
153
154
  },
154
155
  });
155
156
 
156
- var COMPLIANCE_POSTURES = Object.freeze({
157
- hipaa: "strict",
158
- "pci-dss": "strict",
159
- gdpr: "strict",
160
- soc2: "strict",
157
+ var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
158
+
159
+ var _resolveProfile = gateContract.makeProfileResolver({
160
+ profiles: PROFILES,
161
+ postures: COMPLIANCE_POSTURES,
162
+ defaults: DEFAULT_PROFILE,
163
+ errorClass: SafeDnsError,
164
+ codePrefix: "safe-dns",
165
+ byObject: true,
161
166
  });
162
167
 
163
168
  /**
@@ -349,22 +354,6 @@ function checkCnameChainDepth(depth, opts) {
349
354
  }
350
355
  }
351
356
 
352
- /**
353
- * @primitive b.safeDns.compliancePosture
354
- * @signature b.safeDns.compliancePosture(posture)
355
- * @since 0.9.31
356
- * @status stable
357
- *
358
- * Return the effective profile name for a compliance posture, or
359
- * `null` for unknown posture names (operator typo surfaces here).
360
- *
361
- * @example
362
- * b.safeDns.compliancePosture("hipaa"); // → "strict"
363
- */
364
- function compliancePosture(posture) {
365
- return COMPLIANCE_POSTURES[posture] || null;
366
- }
367
-
368
357
  function _readName(state, pointerDepth) {
369
358
  if (pointerDepth > state.caps.maxPointerDepth) {
370
359
  throw new SafeDnsError("safe-dns/oversize-pointer-depth",
@@ -639,27 +628,22 @@ function _decodeOpt(rr, caps) {
639
628
  };
640
629
  }
641
630
 
642
- function _resolveProfile(opts) {
643
- if (opts.posture && COMPLIANCE_POSTURES[opts.posture]) {
644
- return PROFILES[COMPLIANCE_POSTURES[opts.posture]];
645
- }
646
- var p = opts.profile || DEFAULT_PROFILE;
647
- if (!PROFILES[p]) {
648
- throw new SafeDnsError("safe-dns/bad-profile",
649
- "safeDns: unknown profile '" + p + "' (valid: strict / balanced / permissive)");
650
- }
651
- return PROFILES[p];
652
- }
653
-
654
- module.exports = {
655
- parseResponse: parseResponse,
656
- boundEdns0: boundEdns0,
657
- checkCnameChainDepth: checkCnameChainDepth,
658
- compliancePosture: compliancePosture,
659
- PROFILES: PROFILES,
660
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
661
- RTYPE_NAMES: RTYPE_NAMES,
662
- SafeDnsError: SafeDnsError,
663
- NAME: "dns",
664
- KIND: "dns-response",
665
- };
631
+ // compliancePosture is assembled by gateContract.defineParser below; its
632
+ // wiki section renders from the single-sourced @abiTemplate (defineParser)
633
+ // block in gate-contract.js, instantiated for this guard by the page
634
+ // generator.
635
+ module.exports = gateContract.defineParser({
636
+ name: "dns",
637
+ entry: parseResponse,
638
+ entryName: "parseResponse",
639
+ errorClass: SafeDnsError,
640
+ profiles: PROFILES,
641
+ postures: COMPLIANCE_POSTURES,
642
+ extra: {
643
+ boundEdns0: boundEdns0,
644
+ checkCnameChainDepth: checkCnameChainDepth,
645
+ RTYPE_NAMES: RTYPE_NAMES,
646
+ NAME: "dns",
647
+ KIND: "dns-response",
648
+ },
649
+ });
package/lib/safe-ical.js CHANGED
@@ -81,6 +81,7 @@
81
81
 
82
82
  var C = require("./constants");
83
83
  var { defineClass } = require("./framework-error");
84
+ var gateContract = require("./gate-contract");
84
85
 
85
86
  var SafeIcalError = defineClass("SafeIcalError", { alwaysPermanent: true });
86
87
 
@@ -116,12 +117,7 @@ var PROFILES = Object.freeze({
116
117
  }),
117
118
  });
118
119
 
119
- var COMPLIANCE_POSTURES = Object.freeze({
120
- hipaa: "strict",
121
- "pci-dss": "strict",
122
- gdpr: "strict",
123
- soc2: "strict",
124
- });
120
+ var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
125
121
 
126
122
  // Property-name allowlist per RFC 5545 §8.7 (Property Registry) +
127
123
  // RFC 5546 §4.3 (iTIP additions) + RFC 7986 §5 (new calendar
@@ -273,24 +269,6 @@ function parse(text, opts) {
273
269
  : { vcalendar: vcalendars[0], vcalendars: vcalendars };
274
270
  }
275
271
 
276
- /**
277
- * @primitive b.safeIcal.compliancePosture
278
- * @signature b.safeIcal.compliancePosture(name)
279
- * @since 0.9.81
280
- * @status stable
281
- * @related b.safeIcal.parse
282
- *
283
- * Map a compliance-posture name to its profile. Returns the profile
284
- * string for a known posture, `null` for unknown names.
285
- *
286
- * @example
287
- * b.safeIcal.compliancePosture("hipaa"); // → "strict"
288
- * b.safeIcal.compliancePosture("loose"); // → null
289
- */
290
- function compliancePosture(name) {
291
- return COMPLIANCE_POSTURES[name] || null;
292
- }
293
-
294
272
  // ---- Profile / opt resolution ----
295
273
 
296
274
  function _resolveCaps(opts) {
@@ -623,12 +601,19 @@ function _preview(s) {
623
601
  return s.length > 64 ? s.slice(0, 64) + "..." : s; // log-preview length cap
624
602
  }
625
603
 
626
- module.exports = {
627
- parse: parse,
628
- compliancePosture: compliancePosture,
629
- PROFILES: PROFILES,
630
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
631
- KNOWN_PROPERTIES: KNOWN_PROPERTIES,
632
- KNOWN_COMPONENTS: KNOWN_COMPONENTS,
633
- SafeIcalError: SafeIcalError,
634
- };
604
+ // compliancePosture is assembled by gateContract.defineParser below; its
605
+ // wiki section renders from the single-sourced @abiTemplate (defineParser)
606
+ // block in gate-contract.js, instantiated for this guard by the page
607
+ // generator.
608
+ module.exports = gateContract.defineParser({
609
+ name: "ical",
610
+ entry: parse,
611
+ entryName: "parse",
612
+ errorClass: SafeIcalError,
613
+ profiles: PROFILES,
614
+ postures: COMPLIANCE_POSTURES,
615
+ extra: {
616
+ KNOWN_PROPERTIES: KNOWN_PROPERTIES,
617
+ KNOWN_COMPONENTS: KNOWN_COMPONENTS,
618
+ },
619
+ });
package/lib/safe-icap.js CHANGED
@@ -77,6 +77,7 @@
77
77
 
78
78
  var C = require("./constants");
79
79
  var { defineClass } = require("./framework-error");
80
+ var gateContract = require("./gate-contract");
80
81
 
81
82
  var SafeIcapError = defineClass("SafeIcapError", { alwaysPermanent: true });
82
83
 
@@ -131,11 +132,15 @@ var PROFILES = Object.freeze({
131
132
  },
132
133
  });
133
134
 
134
- var COMPLIANCE_POSTURES = Object.freeze({
135
- hipaa: "strict",
136
- "pci-dss": "strict",
137
- gdpr: "strict",
138
- soc2: "strict",
135
+ var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
136
+
137
+ var _resolveProfile = gateContract.makeProfileResolver({
138
+ profiles: PROFILES,
139
+ postures: COMPLIANCE_POSTURES,
140
+ defaults: DEFAULT_PROFILE,
141
+ errorClass: SafeIcapError,
142
+ codePrefix: "safe-icap",
143
+ byObject: true,
139
144
  });
140
145
 
141
146
  /**
@@ -257,22 +262,6 @@ function parse(buf, opts) {
257
262
  };
258
263
  }
259
264
 
260
- /**
261
- * @primitive b.safeIcap.compliancePosture
262
- * @signature b.safeIcap.compliancePosture(posture)
263
- * @since 0.9.81
264
- * @status stable
265
- *
266
- * Return the effective profile name for a compliance posture, or
267
- * `null` for unknown posture names.
268
- *
269
- * @example
270
- * b.safeIcap.compliancePosture("hipaa"); // → "strict"
271
- */
272
- function compliancePosture(posture) {
273
- return COMPLIANCE_POSTURES[posture] || null;
274
- }
275
-
276
265
  // ---- internals ----
277
266
 
278
267
  function _findHeaderEnd(buf, maxHeaderBytes) {
@@ -479,25 +468,20 @@ function _detectThreat(statusCode, headers) {
479
468
  return { found: found, name: name };
480
469
  }
481
470
 
482
- function _resolveProfile(opts) {
483
- if (opts.posture && COMPLIANCE_POSTURES[opts.posture]) {
484
- return PROFILES[COMPLIANCE_POSTURES[opts.posture]];
485
- }
486
- var p = opts.profile || DEFAULT_PROFILE;
487
- if (!PROFILES[p]) {
488
- throw new SafeIcapError("safe-icap/bad-profile",
489
- "safeIcap: unknown profile '" + p + "' (valid: strict / balanced / permissive)");
490
- }
491
- return PROFILES[p];
492
- }
493
-
494
- module.exports = {
495
- parse: parse,
496
- compliancePosture: compliancePosture,
497
- PROFILES: PROFILES,
498
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
499
- ALLOWED_STATUS: ALLOWED_STATUS,
500
- SafeIcapError: SafeIcapError,
501
- NAME: "icap",
502
- KIND: "icap-response",
503
- };
471
+ // compliancePosture is assembled by gateContract.defineParser below; its
472
+ // wiki section renders from the single-sourced @abiTemplate (defineParser)
473
+ // block in gate-contract.js, instantiated for this guard by the page
474
+ // generator.
475
+ module.exports = gateContract.defineParser({
476
+ name: "icap",
477
+ entry: parse,
478
+ entryName: "parse",
479
+ errorClass: SafeIcapError,
480
+ profiles: PROFILES,
481
+ postures: COMPLIANCE_POSTURES,
482
+ extra: {
483
+ ALLOWED_STATUS: ALLOWED_STATUS,
484
+ NAME: "icap",
485
+ KIND: "icap-response",
486
+ },
487
+ });
package/lib/safe-sieve.js CHANGED
@@ -49,6 +49,7 @@
49
49
  */
50
50
 
51
51
  var { defineClass } = require("./framework-error");
52
+ var gateContract = require("./gate-contract");
52
53
 
53
54
  var SafeSieveError = defineClass("SafeSieveError", { alwaysPermanent: true });
54
55
 
@@ -82,12 +83,7 @@ var PROFILES = Object.freeze({
82
83
  }),
83
84
  });
84
85
 
85
- var COMPLIANCE_POSTURES = Object.freeze({
86
- hipaa: "strict",
87
- "pci-dss": "strict",
88
- gdpr: "strict",
89
- soc2: "strict",
90
- });
86
+ var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
91
87
 
92
88
  // RFC 5228 §1.2 capability identifiers. Each entry lists whether the
93
89
  // framework's v0.9.55 interpreter implements the capability. Unknown
@@ -648,37 +644,22 @@ function validate(script, opts) {
648
644
  }
649
645
  }
650
646
 
651
- /**
652
- * @primitive b.safeSieve.compliancePosture
653
- * @signature b.safeSieve.compliancePosture(name)
654
- * @since 0.9.55
655
- * @status stable
656
- * @related b.safeSieve.parse, b.safeSieve.validate
657
- *
658
- * Look up the recommended profile name for a compliance posture
659
- * (`hipaa` / `pci-dss` / `gdpr` / `soc2`). Returns `"strict"` for any
660
- * known posture, `null` for unknown names. Operator-facing primitives
661
- * that thread `compliancePosture` opt through to safeSieve compose
662
- * this for the explicit-cast pattern when they need the name string
663
- * (rather than relying on `_resolveOpts` to do the lookup).
664
- *
665
- * @example
666
- * b.safeSieve.compliancePosture("hipaa"); // → "strict"
667
- * b.safeSieve.compliancePosture("loose"); // → null
668
- */
669
- function compliancePosture(name) {
670
- return COMPLIANCE_POSTURES[name] || null;
671
- }
672
-
673
- module.exports = {
674
- parse: parse,
675
- validate: validate,
676
- compliancePosture: compliancePosture,
677
- KNOWN_CAPABILITIES: KNOWN_CAPABILITIES,
678
- PROFILES: PROFILES,
679
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
680
- SafeSieveError: SafeSieveError,
681
- // Internal exports for the interpreter at lib/mail-sieve.js.
682
- _tokenize: _tokenize,
683
- _resolveCaps: _resolveCaps,
684
- };
647
+ // compliancePosture is assembled by gateContract.defineParser below; its
648
+ // wiki section renders from the single-sourced @abiTemplate (defineParser)
649
+ // block in gate-contract.js, instantiated for this guard by the page
650
+ // generator.
651
+ module.exports = gateContract.defineParser({
652
+ name: "sieve",
653
+ entry: parse,
654
+ entryName: "parse",
655
+ errorClass: SafeSieveError,
656
+ profiles: PROFILES,
657
+ postures: COMPLIANCE_POSTURES,
658
+ extra: {
659
+ validate: validate,
660
+ KNOWN_CAPABILITIES: KNOWN_CAPABILITIES,
661
+ // Internal exports for the interpreter at lib/mail-sieve.js.
662
+ _tokenize: _tokenize,
663
+ _resolveCaps: _resolveCaps,
664
+ },
665
+ });