@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.
- package/CHANGELOG.md +6 -0
- package/README.md +2 -2
- package/index.js +4 -0
- package/lib/ai-content-detect.js +9 -10
- package/lib/api-key.js +158 -77
- 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 +228 -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 +82 -29
- 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 +517 -256
- package/lib/db-schema.js +209 -44
- package/lib/db.js +202 -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 +293 -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 +116 -72
- package/lib/sql.js +3885 -0
- package/lib/static.js +45 -7
- package/lib/subject.js +89 -49
- 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/legal-hold.js
CHANGED
|
@@ -63,8 +63,19 @@ var bCrypto = require("./crypto");
|
|
|
63
63
|
var lazyRequire = require("./lazy-require");
|
|
64
64
|
var safeJson = require("./safe-json");
|
|
65
65
|
var validateOpts = require("./validate-opts");
|
|
66
|
+
var sql = require("./sql");
|
|
66
67
|
var { defineClass } = require("./framework-error");
|
|
67
68
|
|
|
69
|
+
// Local-SQLite framework table names. These run against the b.db() handle
|
|
70
|
+
// directly (single-node legal-hold registry + the audit_log read), so the
|
|
71
|
+
// b.sql builders carry { quoteName: true } to emit the quoted local name
|
|
72
|
+
// (no clusterStorage prefix rewrite on this path) and bind every value as a
|
|
73
|
+
// placeholder. The names are passed as literals here for the same reason
|
|
74
|
+
// db.js declares them as literals — they ARE the canonical local table
|
|
75
|
+
// identifiers.
|
|
76
|
+
var HOLD_TABLE = "_blamejs_legal_hold"; // allow:hand-rolled-sql — canonical local table-name; passed to b.sql with quoteName
|
|
77
|
+
var AUDIT_TABLE = "audit_log";
|
|
78
|
+
|
|
68
79
|
var audit = lazyRequire(function () { return require("./audit"); });
|
|
69
80
|
|
|
70
81
|
var LegalHoldError = defineClass("LegalHoldError", { alwaysPermanent: true });
|
|
@@ -99,6 +110,24 @@ function _hashSubject(subjectId) {
|
|
|
99
110
|
return bCrypto.sha3Hash("bj-legal-hold:" + subjectId);
|
|
100
111
|
}
|
|
101
112
|
|
|
113
|
+
// Resolve the b.sql dialect for the operator-supplied handle. The framework's
|
|
114
|
+
// local b.db handle is always node:sqlite (db.js pins { dialect: "sqlite",
|
|
115
|
+
// quoteName: true }) and exposes no .dialect, so this defaults to "sqlite" —
|
|
116
|
+
// the registry + the audit_log read both run against that local handle via
|
|
117
|
+
// .prepare(). An operator handle that DOES advertise a dialect (string or
|
|
118
|
+
// () -> string) has it threaded through so the emitted identifier quoting +
|
|
119
|
+
// idioms match the backend the handle dispatches to. quoteName stays on for
|
|
120
|
+
// every legal-hold statement: these are canonical local table names quoted by
|
|
121
|
+
// construction (no clusterStorage prefix rewrite on this path).
|
|
122
|
+
function _handleDialect(db) {
|
|
123
|
+
if (db && typeof db.dialect === "function") {
|
|
124
|
+
try { var d = db.dialect(); return typeof d === "string" ? d : "sqlite"; }
|
|
125
|
+
catch (_e) { return "sqlite"; }
|
|
126
|
+
}
|
|
127
|
+
if (db && typeof db.dialect === "string") return db.dialect;
|
|
128
|
+
return "sqlite";
|
|
129
|
+
}
|
|
130
|
+
|
|
102
131
|
function create(opts) {
|
|
103
132
|
opts = opts || {};
|
|
104
133
|
validateOpts(opts, ["db", "audit", "signWith"], "legalHold");
|
|
@@ -106,6 +135,11 @@ function create(opts) {
|
|
|
106
135
|
throw _err("BAD_OPT", "create: opts.db is required (a b.db handle)");
|
|
107
136
|
}
|
|
108
137
|
var db = opts.db;
|
|
138
|
+
// b.sql opts for every legal-hold statement built against this handle. The
|
|
139
|
+
// dialect tracks the handle (sqlite for the framework's local b.db); the
|
|
140
|
+
// local table names are quoted by construction (quoteName) with no
|
|
141
|
+
// clusterStorage prefix rewrite on this path.
|
|
142
|
+
var SQL_OPTS = { dialect: _handleDialect(db), quoteName: true };
|
|
109
143
|
var auditOn = opts.audit !== false && opts.audit != null;
|
|
110
144
|
var auditInstance = (opts.audit && opts.audit !== true) ? opts.audit : null;
|
|
111
145
|
// signWith is reserved for future per-event detached signatures;
|
|
@@ -135,19 +169,21 @@ function create(opts) {
|
|
|
135
169
|
// table via FRAMEWORK_SCHEMA at boot; this guard is for the
|
|
136
170
|
// external-db / cluster path where schema migrations are operator-
|
|
137
171
|
// driven. Either way the IF NOT EXISTS shape is safe to re-run.
|
|
172
|
+
// DDL built through b.sql.createTable so every identifier is quoted by
|
|
173
|
+
// construction (quoteName) and the type tokens come from the framework
|
|
174
|
+
// type map.
|
|
138
175
|
var fn = db.runSql || db.execRaw;
|
|
139
176
|
if (typeof fn === "function") {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
);
|
|
177
|
+
var ddl = sql.createTable(HOLD_TABLE, [
|
|
178
|
+
{ name: "subjectIdHash", type: "text", primaryKey: true },
|
|
179
|
+
{ name: "placedAt", type: "int", notNull: true },
|
|
180
|
+
{ name: "placedBy", type: "text" },
|
|
181
|
+
{ name: "reason", type: "text", notNull: true },
|
|
182
|
+
{ name: "custodian", type: "text" },
|
|
183
|
+
{ name: "citation", type: "text" },
|
|
184
|
+
{ name: "retainUntil", type: "int" },
|
|
185
|
+
], SQL_OPTS);
|
|
186
|
+
fn(ddl.sql);
|
|
151
187
|
}
|
|
152
188
|
}
|
|
153
189
|
|
|
@@ -171,9 +207,12 @@ function create(opts) {
|
|
|
171
207
|
}
|
|
172
208
|
_ensureSchema();
|
|
173
209
|
var hash = _hashSubject(sid);
|
|
174
|
-
var
|
|
175
|
-
|
|
176
|
-
|
|
210
|
+
var placeSelBuilt = sql.select(HOLD_TABLE, SQL_OPTS)
|
|
211
|
+
.columns(["placedAt"])
|
|
212
|
+
.where("subjectIdHash", hash)
|
|
213
|
+
.toSql();
|
|
214
|
+
var placeSelStmt = db.prepare(placeSelBuilt.sql);
|
|
215
|
+
var existing = placeSelStmt.get.apply(placeSelStmt, placeSelBuilt.params);
|
|
177
216
|
if (existing) {
|
|
178
217
|
_emit("legalhold.place_rejected",
|
|
179
218
|
{ subjectId: sid, reason: "already-held",
|
|
@@ -182,18 +221,19 @@ function create(opts) {
|
|
|
182
221
|
return { error: "already-held", placedAt: existing.placedAt };
|
|
183
222
|
}
|
|
184
223
|
var nowMs = Date.now();
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
args.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
);
|
|
224
|
+
var placeInsBuilt = sql.insert(HOLD_TABLE, SQL_OPTS)
|
|
225
|
+
.values({
|
|
226
|
+
subjectIdHash: hash,
|
|
227
|
+
placedAt: nowMs,
|
|
228
|
+
placedBy: args.placedBy || null,
|
|
229
|
+
reason: args.reason,
|
|
230
|
+
custodian: args.custodian || null,
|
|
231
|
+
citation: args.citation || null,
|
|
232
|
+
retainUntil: args.retainUntil || null,
|
|
233
|
+
})
|
|
234
|
+
.toSql();
|
|
235
|
+
var placeInsStmt = db.prepare(placeInsBuilt.sql);
|
|
236
|
+
placeInsStmt.run.apply(placeInsStmt, placeInsBuilt.params);
|
|
197
237
|
_emit("legalhold.placed",
|
|
198
238
|
{ subjectId: sid, reason: args.reason,
|
|
199
239
|
custodian: args.custodian || null,
|
|
@@ -216,18 +256,23 @@ function create(opts) {
|
|
|
216
256
|
}
|
|
217
257
|
_ensureSchema();
|
|
218
258
|
var hash = _hashSubject(sid);
|
|
219
|
-
var
|
|
220
|
-
|
|
221
|
-
|
|
259
|
+
var relSelBuilt = sql.select(HOLD_TABLE, SQL_OPTS)
|
|
260
|
+
.columns(["placedAt", "reason"])
|
|
261
|
+
.where("subjectIdHash", hash)
|
|
262
|
+
.toSql();
|
|
263
|
+
var relSelStmt = db.prepare(relSelBuilt.sql);
|
|
264
|
+
var existing = relSelStmt.get.apply(relSelStmt, relSelBuilt.params);
|
|
222
265
|
if (!existing) {
|
|
223
266
|
_emit("legalhold.release_rejected",
|
|
224
267
|
{ subjectId: sid, reason: "not-held" },
|
|
225
268
|
"denied");
|
|
226
269
|
return { error: "not-held" };
|
|
227
270
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
271
|
+
var relDelBuilt = sql.delete(HOLD_TABLE, SQL_OPTS)
|
|
272
|
+
.where("subjectIdHash", hash)
|
|
273
|
+
.toSql();
|
|
274
|
+
var relDelStmt = db.prepare(relDelBuilt.sql);
|
|
275
|
+
relDelStmt.run.apply(relDelStmt, relDelBuilt.params);
|
|
231
276
|
_emit("legalhold.released",
|
|
232
277
|
{ subjectId: sid, reason: args.reason,
|
|
233
278
|
approver: args.approver,
|
|
@@ -241,9 +286,12 @@ function create(opts) {
|
|
|
241
286
|
var sid = _subjectIdString(subjectId);
|
|
242
287
|
_ensureSchema();
|
|
243
288
|
var hash = _hashSubject(sid);
|
|
244
|
-
var
|
|
245
|
-
|
|
246
|
-
|
|
289
|
+
var heldBuilt = sql.select(HOLD_TABLE, SQL_OPTS)
|
|
290
|
+
.columns(["retainUntil"])
|
|
291
|
+
.where("subjectIdHash", hash)
|
|
292
|
+
.toSql();
|
|
293
|
+
var heldStmt = db.prepare(heldBuilt.sql);
|
|
294
|
+
var row = heldStmt.get.apply(heldStmt, heldBuilt.params);
|
|
247
295
|
if (!row) return false;
|
|
248
296
|
// retainUntil expiry — when the operator pinned a sunset and it
|
|
249
297
|
// has passed, the hold has lapsed and isHeld returns false. The
|
|
@@ -257,10 +305,12 @@ function create(opts) {
|
|
|
257
305
|
var sid = _subjectIdString(subjectId);
|
|
258
306
|
_ensureSchema();
|
|
259
307
|
var hash = _hashSubject(sid);
|
|
260
|
-
var
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
308
|
+
var getBuilt = sql.select(HOLD_TABLE, SQL_OPTS)
|
|
309
|
+
.columns(["subjectIdHash", "placedAt", "placedBy", "reason", "custodian", "citation", "retainUntil"])
|
|
310
|
+
.where("subjectIdHash", hash)
|
|
311
|
+
.toSql();
|
|
312
|
+
var getStmt = db.prepare(getBuilt.sql);
|
|
313
|
+
var row = getStmt.get.apply(getStmt, getBuilt.params);
|
|
264
314
|
if (!row) return null;
|
|
265
315
|
return {
|
|
266
316
|
subjectId: sid,
|
|
@@ -276,10 +326,12 @@ function create(opts) {
|
|
|
276
326
|
|
|
277
327
|
function list() {
|
|
278
328
|
_ensureSchema();
|
|
279
|
-
var
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
329
|
+
var listBuilt = sql.select(HOLD_TABLE, SQL_OPTS)
|
|
330
|
+
.columns(["subjectIdHash", "placedAt", "placedBy", "reason", "custodian", "citation", "retainUntil"])
|
|
331
|
+
.orderBy("placedAt", "asc")
|
|
332
|
+
.toSql();
|
|
333
|
+
var listStmt = db.prepare(listBuilt.sql);
|
|
334
|
+
var rows = listStmt.all.apply(listStmt, listBuilt.params);
|
|
283
335
|
var nowMs = Date.now();
|
|
284
336
|
return rows.map(function (r) {
|
|
285
337
|
return {
|
|
@@ -302,15 +354,20 @@ function create(opts) {
|
|
|
302
354
|
var sid = _subjectIdString(subjectId);
|
|
303
355
|
var rows = [];
|
|
304
356
|
try {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
357
|
+
// `action LIKE 'legalhold.%'` is a prefix match — b.sql's whereLike
|
|
358
|
+
// in prefix mode emits `"action" LIKE ? ESCAPE '~'` with the live
|
|
359
|
+
// trailing wildcard while escaping the term's own metacharacters
|
|
360
|
+
// (none here), so the wildcard stays builder-controlled.
|
|
361
|
+
var histBuilt = sql.select(AUDIT_TABLE, SQL_OPTS)
|
|
362
|
+
.columns(["recordedAt", "action", "metadata", "outcome"])
|
|
363
|
+
.whereLike("action", "legalhold.", "prefix")
|
|
364
|
+
.where("resourceKind", "legal-hold")
|
|
365
|
+
.orderBy("recordedAt", "asc")
|
|
366
|
+
.toSql();
|
|
367
|
+
var auditQuery = db.prepare(histBuilt.sql);
|
|
311
368
|
// resourceId is sealed, so match on resourceKind + post-filter
|
|
312
369
|
// by parsed metadata.
|
|
313
|
-
var raw = auditQuery.all(
|
|
370
|
+
var raw = auditQuery.all.apply(auditQuery, histBuilt.params);
|
|
314
371
|
for (var i = 0; i < raw.length; i++) {
|
|
315
372
|
var meta = null;
|
|
316
373
|
try { meta = safeJson.parse(raw[i].metadata || "{}"); } catch (_e) { meta = null; }
|
|
@@ -205,6 +205,10 @@ function create(config) {
|
|
|
205
205
|
var inFlight = false;
|
|
206
206
|
var closed = false;
|
|
207
207
|
var sequenceToken = null;
|
|
208
|
+
// Captures the in-flight drain (the IIFE inside _flush) so close() can await
|
|
209
|
+
// the actual run before flipping `closed` — otherwise the _flush while-loop's
|
|
210
|
+
// `&& !closed` bails and buffered records are stranded at shutdown.
|
|
211
|
+
var inFlightPromise = null;
|
|
208
212
|
var flushScheduler = safeAsync.makeScheduledFlush(cfg.maxBatchAgeMs, function () { return _flush(); });
|
|
209
213
|
|
|
210
214
|
function _takeBatch() {
|
|
@@ -236,40 +240,44 @@ function create(config) {
|
|
|
236
240
|
}
|
|
237
241
|
|
|
238
242
|
async function _flush() {
|
|
239
|
-
if (inFlight) return;
|
|
243
|
+
if (inFlight) return inFlightPromise;
|
|
240
244
|
if (buffer.length === 0) return;
|
|
241
245
|
inFlight = true;
|
|
242
|
-
|
|
243
|
-
try {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
246
|
+
inFlightPromise = (async function () {
|
|
247
|
+
try {
|
|
248
|
+
try { await _ensureAutoCreated(); }
|
|
249
|
+
catch (acErr) {
|
|
250
|
+
// autoCreate failure is permanent — every subsequent batch
|
|
251
|
+
// would hit the same error. Drop the queue with the
|
|
252
|
+
// operator-supplied onDrop callback so they see exactly which
|
|
253
|
+
// events were lost AND why, then bail.
|
|
254
|
+
var allBuffered = buffer.splice(0, buffer.length);
|
|
255
|
+
dropCount += allBuffered.length;
|
|
256
|
+
_emitDrop("autocreate-failed", allBuffered, acErr);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
while (buffer.length > 0 && !closed) {
|
|
260
|
+
var batch = _takeBatch();
|
|
261
|
+
if (batch.length === 0) break;
|
|
262
|
+
try {
|
|
263
|
+
await retryHelper.withRetry(function () {
|
|
264
|
+
return _send(batch);
|
|
265
|
+
}, Object.assign({
|
|
266
|
+
isPermanent: _isPermanentAwsError,
|
|
267
|
+
}, cfg.retry || {}));
|
|
268
|
+
} catch (e) {
|
|
269
|
+
dropCount += batch.length;
|
|
270
|
+
_emitDrop("retry-exhausted", batch, e);
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
267
273
|
}
|
|
274
|
+
} finally {
|
|
275
|
+
inFlight = false;
|
|
276
|
+
inFlightPromise = null;
|
|
277
|
+
if (buffer.length > 0) flushScheduler.schedule();
|
|
268
278
|
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (buffer.length > 0) flushScheduler.schedule();
|
|
272
|
-
}
|
|
279
|
+
})();
|
|
280
|
+
return inFlightPromise;
|
|
273
281
|
}
|
|
274
282
|
|
|
275
283
|
async function _send(batch) {
|
|
@@ -333,9 +341,17 @@ function create(config) {
|
|
|
333
341
|
}
|
|
334
342
|
|
|
335
343
|
async function close() {
|
|
336
|
-
closed
|
|
344
|
+
// Drain BEFORE flipping closed=true — _flush()'s `while (... && !closed)`
|
|
345
|
+
// loop bails on !closed, so flipping the flag first strands the records the
|
|
346
|
+
// operator queued just before shutdown (the b.logStream drain contract).
|
|
347
|
+
// Stop the timer, await any in-flight drain, drain the remainder, THEN
|
|
348
|
+
// refuse new enqueues. Mirrors the webhook sink.
|
|
337
349
|
flushScheduler.cancel();
|
|
350
|
+
if (inFlightPromise) {
|
|
351
|
+
try { await inFlightPromise; } catch (_e) { /* surfaced via onDrop */ }
|
|
352
|
+
}
|
|
338
353
|
await _flush();
|
|
354
|
+
closed = true;
|
|
339
355
|
}
|
|
340
356
|
|
|
341
357
|
function stats() {
|
package/lib/log-stream-otlp.js
CHANGED
|
@@ -210,30 +210,38 @@ function create(config) {
|
|
|
210
210
|
var dropCount = 0;
|
|
211
211
|
var inFlight = false;
|
|
212
212
|
var closed = false;
|
|
213
|
+
// Captures the in-flight drain so close() awaits the real run before flipping
|
|
214
|
+
// `closed` (the _flush while-loop bails on !closed → flipping first strands
|
|
215
|
+
// buffered records at shutdown).
|
|
216
|
+
var inFlightPromise = null;
|
|
213
217
|
var flushScheduler = safeAsync.makeScheduledFlush(cfg.maxBatchAgeMs, function () { return _flush(); });
|
|
214
218
|
|
|
215
219
|
async function _flush() {
|
|
216
|
-
if (inFlight) return;
|
|
220
|
+
if (inFlight) return inFlightPromise;
|
|
217
221
|
if (buffer.length === 0) return;
|
|
218
222
|
inFlight = true;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
223
|
+
inFlightPromise = (async function () {
|
|
224
|
+
try {
|
|
225
|
+
while (buffer.length > 0 && !closed) {
|
|
226
|
+
var batch = buffer.splice(0, cfg.batchSize);
|
|
227
|
+
var body = _serializeBatch(batch, cfg, scopeVersion);
|
|
228
|
+
try {
|
|
229
|
+
await retryHelper.withRetry(function () {
|
|
230
|
+
return _post(resolvedUrl, body, headers, cfg.timeoutMs, cfg.allowedProtocols, cfg.allowInternal);
|
|
231
|
+
}, cfg.retry);
|
|
232
|
+
} catch (e) {
|
|
233
|
+
dropCount += batch.length;
|
|
234
|
+
_emitDrop("retry-exhausted", batch, e);
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
231
237
|
}
|
|
238
|
+
} finally {
|
|
239
|
+
inFlight = false;
|
|
240
|
+
inFlightPromise = null;
|
|
241
|
+
if (buffer.length > 0) flushScheduler.schedule();
|
|
232
242
|
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (buffer.length > 0) flushScheduler.schedule();
|
|
236
|
-
}
|
|
243
|
+
})();
|
|
244
|
+
return inFlightPromise;
|
|
237
245
|
}
|
|
238
246
|
|
|
239
247
|
function emit(record) {
|
|
@@ -253,9 +261,15 @@ function create(config) {
|
|
|
253
261
|
}
|
|
254
262
|
|
|
255
263
|
async function close() {
|
|
256
|
-
closed
|
|
264
|
+
// Drain BEFORE flipping closed=true (see _flush's `&& !closed` guard) so
|
|
265
|
+
// records queued just before shutdown reach the wire. Mirrors the webhook
|
|
266
|
+
// + cloudwatch sinks.
|
|
257
267
|
flushScheduler.cancel();
|
|
268
|
+
if (inFlightPromise) {
|
|
269
|
+
try { await inFlightPromise; } catch (_e) { /* surfaced via onDrop */ }
|
|
270
|
+
}
|
|
258
271
|
await _flush();
|
|
272
|
+
closed = true;
|
|
259
273
|
}
|
|
260
274
|
|
|
261
275
|
function stats() {
|
package/lib/mail-crypto-smime.js
CHANGED
|
@@ -102,6 +102,7 @@ var cms = require("./cms-codec");
|
|
|
102
102
|
var asn1 = require("./asn1-der");
|
|
103
103
|
var pqcSoftware = require("./pqc-software");
|
|
104
104
|
var bCrypto = require("./crypto");
|
|
105
|
+
var gateContract = require("./gate-contract");
|
|
105
106
|
var { defineClass } = require("./framework-error");
|
|
106
107
|
|
|
107
108
|
var MailCryptoError = defineClass("MailCryptoError", { alwaysPermanent: true });
|
|
@@ -121,12 +122,7 @@ var REFUSED_HASHES = ["md5", "sha1"];
|
|
|
121
122
|
// encryption is added, it composes the same set with the @intro EFAIL
|
|
122
123
|
// defenses applied.
|
|
123
124
|
var PROFILES = ["strict", "balanced", "permissive"];
|
|
124
|
-
var COMPLIANCE_POSTURES =
|
|
125
|
-
hipaa: "strict",
|
|
126
|
-
"pci-dss": "strict",
|
|
127
|
-
gdpr: "strict",
|
|
128
|
-
soc2: "strict",
|
|
129
|
-
};
|
|
125
|
+
var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
|
|
130
126
|
|
|
131
127
|
// ---- Public surface (v0.10.16 lights up — composes b.cms) ----
|
|
132
128
|
|
package/lib/mail-greylist.js
CHANGED
|
@@ -102,6 +102,7 @@ var { defineClass } = require("./framework-error");
|
|
|
102
102
|
var bCrypto = require("./crypto");
|
|
103
103
|
var lazyRequire = require("./lazy-require");
|
|
104
104
|
var ipUtils = require("./ip-utils");
|
|
105
|
+
var gateContract = require("./gate-contract");
|
|
105
106
|
|
|
106
107
|
var audit = lazyRequire(function () { return require("./audit"); });
|
|
107
108
|
|
|
@@ -126,12 +127,7 @@ var PROFILES = Object.freeze({
|
|
|
126
127
|
permissive: { minDelayMs: C.TIME.seconds(30), whitelistTtlMs: C.TIME.days(30), ipv4Prefix: 32, ipv6Prefix: 128 }, // RFC 6647 §4.4 prefixes
|
|
127
128
|
});
|
|
128
129
|
|
|
129
|
-
var COMPLIANCE_POSTURES =
|
|
130
|
-
hipaa: "strict",
|
|
131
|
-
"pci-dss": "strict",
|
|
132
|
-
gdpr: "strict",
|
|
133
|
-
soc2: "strict",
|
|
134
|
-
});
|
|
130
|
+
var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
|
|
135
131
|
|
|
136
132
|
var IPV4_RE = /^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/; // allow:regex-no-length-cap — anchored + per-octet repeat-cap
|
|
137
133
|
var IPV6_RE = /^[0-9a-fA-F:]+$/; // allow:regex-no-length-cap — length-checked separately
|
package/lib/mail-helo.js
CHANGED
|
@@ -104,6 +104,7 @@
|
|
|
104
104
|
var { defineClass } = require("./framework-error");
|
|
105
105
|
var lazyRequire = require("./lazy-require");
|
|
106
106
|
var ipUtils = require("./ip-utils");
|
|
107
|
+
var gateContract = require("./gate-contract");
|
|
107
108
|
|
|
108
109
|
var audit = lazyRequire(function () { return require("./audit"); });
|
|
109
110
|
|
|
@@ -145,12 +146,7 @@ var PROFILES = Object.freeze({
|
|
|
145
146
|
},
|
|
146
147
|
});
|
|
147
148
|
|
|
148
|
-
var COMPLIANCE_POSTURES =
|
|
149
|
-
hipaa: "strict",
|
|
150
|
-
"pci-dss": "strict",
|
|
151
|
-
gdpr: "strict",
|
|
152
|
-
soc2: "strict",
|
|
153
|
-
});
|
|
149
|
+
var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
|
|
154
150
|
|
|
155
151
|
// Operator-extensible default list of generic-rDNS patterns the
|
|
156
152
|
// framework ships. Each is a RegExp — case-insensitive — designed
|