@blamejs/core 0.5.16 → 0.5.18

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/lib/csv.js CHANGED
@@ -1,229 +1,275 @@
1
1
  "use strict";
2
2
  /**
3
- * csv — RFC 4180 parser + serializer.
3
+ * csv — RFC 4180 parser + serializer with operator-friendly defaults.
4
4
  *
5
- * Public API:
5
+ * var rows = b.csv.parse(text);
6
+ * // header row by default — returns array of objects keyed by header
7
+ * // [ { name: "alice", age: "30" }, ... ]
6
8
  *
7
- * b.csv.parse(text, opts?) → { headers, rows } (or { rows } if no header)
8
- * b.csv.stringify(rows, opts?) → string
9
+ * var arrays = b.csv.parse(text, { header: false });
10
+ * // returns array of arrays
9
11
  *
10
- * Opts (parse):
11
- * delimiter: "," default
12
- * header: true default — first row becomes column names; rows
13
- * are objects keyed by header
14
- * maxBytes: 8 MiB default — refuses parse-bombs the same way
15
- * safeJson does
16
- * onBadRow: "throw" default | "skip" — what to do when a row has
17
- * a different column count than the header
12
+ * var out = b.csv.stringify(rows);
13
+ * // RFC 4180 + Excel-formula-injection prevention by default
18
14
  *
19
- * Opts (stringify):
20
- * delimiter: "," default
21
- * header: true default — emit a header row from object keys
22
- * columns: array of explicit column order (default: first row's keys)
23
- * eol: "\r\n" default — RFC 4180 mandates CRLF; some parsers
24
- * want LF. Operators choose.
15
+ * Defaults:
16
+ * parse:
17
+ * header: true first row is column names
18
+ * delimiter: "," single byte; "\t" / ";" supported
19
+ * quote: '"'
20
+ * trim: false cell whitespace preserved
21
+ * maxBytes: 16 MiB
22
+ * maxRows: 1,000,000
23
+ * maxFieldBytes: 1 MiB
24
+ * onBadRow: "throw" "skip" tolerates short/long rows
25
25
  *
26
- * Format support:
27
- * - Quoted fields with embedded commas, quotes ("" → "), newlines.
28
- * - Optional BOM at file start (consumed silently if present).
29
- * - Trailing newline tolerated (RFC 4180 says SHOULD; we tolerate).
26
+ * stringify:
27
+ * header: true (or array of explicit columns)
28
+ * delimiter: ","
29
+ * quote: '"'
30
+ * eol: "\r\n" RFC 4180; "\n" via opt
31
+ * alwaysQuote: false only quote when needed
32
+ * preventFormulaInjection: true prefix '=' / '+' / '-' / '@' / TAB / CR
33
+ * cells with "'" so Excel doesn't
34
+ * execute them when the CSV is opened
35
+ * formulaPrefixChars: ["=","+","-","@","\t","\r"]
30
36
  *
31
- * Throws CsvError (FrameworkError) on shape violations.
37
+ * SECURITY: stringify defends against CSV-injection (Excel/Sheets execute
38
+ * cells starting with "=" / "+" / "-" / "@" / TAB / CR as formulas).
39
+ * Operator-controlled cells stay safe; user-supplied cells get the leading
40
+ * single-quote so the formula engine sees a literal string.
41
+ *
42
+ * Throws CsvError (FrameworkError, permanent) on shape violations.
32
43
  */
33
44
  var C = require("./constants");
34
45
  var { defineClass } = require("./framework-error");
35
46
 
36
47
  var CsvError = defineClass("CsvError", { alwaysPermanent: true });
37
48
 
38
- var DEFAULT_MAX_BYTES = C.BYTES.mib(8);
39
- var DEFAULT_DELIMITER = ",";
40
- var DEFAULT_EOL = "\r\n";
49
+ var DEFAULTS_PARSE = {
50
+ header: true,
51
+ delimiter: ",",
52
+ quote: "\"",
53
+ trim: false,
54
+ maxBytes: C.BYTES.mib(16),
55
+ maxRows: 1000000,
56
+ maxFieldBytes: C.BYTES.mib(1),
57
+ onBadRow: "throw",
58
+ };
59
+
60
+ var DEFAULTS_STRINGIFY = {
61
+ header: true,
62
+ delimiter: ",",
63
+ quote: "\"",
64
+ eol: "\r\n",
65
+ alwaysQuote: false,
66
+ preventFormulaInjection: true,
67
+ formulaPrefixChars: ["=", "+", "-", "@", "\t", "\r"],
68
+ columns: null,
69
+ };
70
+
71
+ function _validateDelim(name, value) {
72
+ if (typeof value !== "string" || value.length !== 1) {
73
+ throw new CsvError("csv/bad-delimiter",
74
+ name + " must be a single character, got " + JSON.stringify(value));
75
+ }
76
+ if (value === "\r" || value === "\n") {
77
+ throw new CsvError("csv/bad-delimiter",
78
+ name + " cannot be CR or LF");
79
+ }
80
+ }
41
81
 
42
82
  // ---- parse ----
43
83
 
44
- function parse(text, opts) {
45
- opts = opts || {};
46
- if (typeof text !== "string" && !Buffer.isBuffer(text)) {
84
+ function parse(input, opts) {
85
+ opts = Object.assign({}, DEFAULTS_PARSE, opts || {});
86
+
87
+ var s;
88
+ if (typeof input === "string") s = input;
89
+ else if (Buffer.isBuffer(input)) s = input.toString("utf8");
90
+ else if (input instanceof Uint8Array) s = Buffer.from(input).toString("utf8");
91
+ else {
47
92
  throw new CsvError("csv/bad-input",
48
- "parse: input must be a string or Buffer, got " + typeof text);
93
+ "parse: input must be string, Buffer, or Uint8Array, got " + typeof input);
49
94
  }
50
- var s = Buffer.isBuffer(text) ? text.toString("utf8") : text;
51
- var maxBytes = opts.maxBytes != null ? opts.maxBytes : DEFAULT_MAX_BYTES;
52
- if (Buffer.byteLength(s, "utf8") > maxBytes) {
95
+ if (s.charCodeAt(0) === 0xFEFF) s = s.slice(1);
96
+ if (Buffer.byteLength(s, "utf8") > opts.maxBytes) {
53
97
  throw new CsvError("csv/too-large",
54
- "parse: input exceeds maxBytes (" + maxBytes + ")");
55
- }
56
- var delimiter = opts.delimiter || DEFAULT_DELIMITER;
57
- if (typeof delimiter !== "string" || delimiter.length !== 1) {
58
- throw new CsvError("csv/bad-delimiter",
59
- "parse: delimiter must be a single character, got " + JSON.stringify(delimiter));
98
+ "parse: input exceeds maxBytes (" + opts.maxBytes + ")");
60
99
  }
61
- if (delimiter === "\"" || delimiter === "\r" || delimiter === "\n") {
100
+
101
+ _validateDelim("delimiter", opts.delimiter);
102
+ _validateDelim("quote", opts.quote);
103
+ if (opts.delimiter === opts.quote) {
62
104
  throw new CsvError("csv/bad-delimiter",
63
- "parse: delimiter cannot be quote / CR / LF");
105
+ "delimiter and quote must differ");
64
106
  }
65
- var hasHeader = opts.header !== false;
66
- var onBadRow = opts.onBadRow || "throw";
67
- if (onBadRow !== "throw" && onBadRow !== "skip") {
107
+ if (opts.onBadRow !== "throw" && opts.onBadRow !== "skip") {
68
108
  throw new CsvError("csv/bad-opt",
69
- "parse: onBadRow must be 'throw' or 'skip'");
109
+ "onBadRow must be 'throw' or 'skip'");
70
110
  }
71
111
 
72
- // Strip BOM if present.
73
- if (s.charCodeAt(0) === 0xfeff) s = s.slice(1);
74
-
112
+ var len = s.length;
113
+ var pos = 0;
75
114
  var rows = [];
76
- var field = "";
77
115
  var row = [];
78
- var inQuotes = false;
79
- var i = 0;
80
- var len = s.length;
81
- while (i < len) {
82
- var c = s.charAt(i);
83
- if (inQuotes) {
84
- if (c === "\"") {
85
- if (i + 1 < len && s.charAt(i + 1) === "\"") {
86
- field += "\""; // escaped quote
87
- i += 2;
116
+ var field = "";
117
+ var inQuote = false;
118
+
119
+ function pushField() {
120
+ if (Buffer.byteLength(field, "utf8") > opts.maxFieldBytes) {
121
+ throw new CsvError("csv/field-too-large",
122
+ "field exceeds maxFieldBytes at row " + (rows.length + 1));
123
+ }
124
+ row.push(opts.trim ? field.trim() : field);
125
+ field = "";
126
+ }
127
+ function pushRow() {
128
+ pushField();
129
+ rows.push(row);
130
+ if (rows.length > opts.maxRows) {
131
+ throw new CsvError("csv/too-many-rows",
132
+ "row count exceeds maxRows (" + opts.maxRows + ")");
133
+ }
134
+ row = [];
135
+ }
136
+
137
+ while (pos < len) {
138
+ var ch = s.charAt(pos);
139
+ if (inQuote) {
140
+ if (ch === opts.quote) {
141
+ if (pos + 1 < len && s.charAt(pos + 1) === opts.quote) {
142
+ field += opts.quote;
143
+ pos += 2;
88
144
  continue;
89
145
  }
90
- inQuotes = false;
91
- i++;
146
+ inQuote = false;
147
+ pos += 1;
92
148
  continue;
93
149
  }
94
- field += c;
95
- i++;
96
- continue;
97
- }
98
- if (c === "\"") {
99
- // Quote at the start of a field (or anywhere — treat as start of
100
- // quoted run). RFC 4180 says quoted fields begin with a quote;
101
- // fields that contain a quote not at the start are technically
102
- // malformed but we tolerate by appending literally.
103
- if (field.length === 0) {
104
- inQuotes = true;
105
- i++;
106
- continue;
150
+ field += ch;
151
+ pos += 1;
152
+ } else {
153
+ if (ch === opts.delimiter) {
154
+ pushField();
155
+ pos += 1;
156
+ } else if (ch === "\r") {
157
+ pushRow();
158
+ pos += 1;
159
+ if (pos < len && s.charAt(pos) === "\n") pos += 1;
160
+ } else if (ch === "\n") {
161
+ pushRow();
162
+ pos += 1;
163
+ } else if (ch === opts.quote && field === "") {
164
+ inQuote = true;
165
+ pos += 1;
166
+ } else {
167
+ field += ch;
168
+ pos += 1;
107
169
  }
108
- field += "\"";
109
- i++;
110
- continue;
111
- }
112
- if (c === delimiter) {
113
- row.push(field);
114
- field = "";
115
- i++;
116
- continue;
117
- }
118
- if (c === "\r") {
119
- // CRLF or bare CR — treat both as row terminator.
120
- row.push(field);
121
- rows.push(row);
122
- field = "";
123
- row = [];
124
- if (i + 1 < len && s.charAt(i + 1) === "\n") i += 2;
125
- else i++;
126
- continue;
127
- }
128
- if (c === "\n") {
129
- row.push(field);
130
- rows.push(row);
131
- field = "";
132
- row = [];
133
- i++;
134
- continue;
135
170
  }
136
- field += c;
137
- i++;
138
171
  }
139
- // Flush any trailing field/row (no trailing newline case)
140
- if (field.length > 0 || row.length > 0) {
141
- row.push(field);
142
- rows.push(row);
172
+ if (inQuote) {
173
+ throw new CsvError("csv/unterminated-quote",
174
+ "unterminated quoted field");
143
175
  }
176
+ if (field.length > 0 || row.length > 0) pushRow();
144
177
 
145
- if (!hasHeader) {
146
- return { rows: rows };
147
- }
148
- if (rows.length === 0) {
149
- return { headers: [], rows: [] };
150
- }
151
- var headers = rows[0];
178
+ if (!opts.header) return rows;
179
+ if (rows.length === 0) return [];
180
+
181
+ var header = rows[0];
152
182
  var out = [];
153
183
  for (var r = 1; r < rows.length; r++) {
154
- var current = rows[r];
155
- if (current.length === 1 && current[0] === "") continue; // skip blank lines
156
- if (current.length !== headers.length) {
157
- if (onBadRow === "skip") continue;
184
+ var rec = rows[r];
185
+ if (rec.length !== header.length) {
186
+ if (opts.onBadRow === "skip") continue;
158
187
  throw new CsvError("csv/row-length-mismatch",
159
- "parse: row " + r + " has " + current.length + " columns, expected " + headers.length);
188
+ "row " + r + " has " + rec.length + " fields, header has " + header.length);
160
189
  }
161
190
  var obj = {};
162
- for (var k = 0; k < headers.length; k++) obj[headers[k]] = current[k];
191
+ for (var i = 0; i < header.length; i++) obj[header[i]] = rec[i];
163
192
  out.push(obj);
164
193
  }
165
- return { headers: headers, rows: out };
194
+ return out;
166
195
  }
167
196
 
168
197
  // ---- stringify ----
169
198
 
170
199
  function stringify(rows, opts) {
171
- opts = opts || {};
200
+ opts = Object.assign({}, DEFAULTS_STRINGIFY, opts || {});
172
201
  if (!Array.isArray(rows)) {
173
202
  throw new CsvError("csv/bad-input",
174
- "stringify: rows must be an array");
203
+ "stringify: rows must be an array, got " + typeof rows);
204
+ }
205
+ _validateDelim("delimiter", opts.delimiter);
206
+ _validateDelim("quote", opts.quote);
207
+ if (opts.delimiter === opts.quote) {
208
+ throw new CsvError("csv/bad-delimiter",
209
+ "delimiter and quote must differ");
210
+ }
211
+ if (opts.eol !== "\r\n" && opts.eol !== "\n") {
212
+ throw new CsvError("csv/bad-opt",
213
+ "eol must be '\\r\\n' or '\\n'");
175
214
  }
176
- var delimiter = opts.delimiter || DEFAULT_DELIMITER;
177
- var eol = opts.eol || DEFAULT_EOL;
178
- var hasHeader = opts.header !== false;
179
215
  if (rows.length === 0) return "";
180
216
 
181
- var columns;
182
- if (Array.isArray(opts.columns)) {
183
- columns = opts.columns.slice();
184
- } else if (Array.isArray(rows[0])) {
185
- // Array-of-arrays input — no headers to derive; emit as-is.
186
- columns = null;
187
- } else if (rows[0] && typeof rows[0] === "object") {
188
- columns = Object.keys(rows[0]);
217
+ var first = rows[0];
218
+ var asObjects;
219
+ var header;
220
+ if (Array.isArray(first)) {
221
+ asObjects = false;
222
+ header = Array.isArray(opts.columns) ? opts.columns : null;
223
+ } else if (first !== null && typeof first === "object") {
224
+ asObjects = true;
225
+ header = Array.isArray(opts.columns) ? opts.columns : Object.keys(first);
189
226
  } else {
190
227
  throw new CsvError("csv/bad-input",
191
- "stringify: rows[0] must be an object or array");
228
+ "stringify: rows must be arrays or plain objects");
192
229
  }
193
230
 
194
- var lines = [];
195
- if (hasHeader && columns) lines.push(columns.map(_quoteCell).join(delimiter));
231
+ var prefixSet = Object.create(null);
232
+ for (var pi = 0; pi < opts.formulaPrefixChars.length; pi++) {
233
+ prefixSet[opts.formulaPrefixChars[pi]] = true;
234
+ }
196
235
 
197
- for (var r = 0; r < rows.length; r++) {
198
- var row = rows[r];
199
- var cells;
200
- if (Array.isArray(row)) {
201
- cells = row.map(_quoteCell);
202
- } else if (row && typeof row === "object") {
203
- cells = (columns || Object.keys(row)).map(function (col) {
204
- return _quoteCell(row[col]);
205
- });
206
- } else {
207
- throw new CsvError("csv/bad-input",
208
- "stringify: rows[" + r + "] must be an object or array");
236
+ function escapeCell(value) {
237
+ var str = value == null ? "" : String(value);
238
+ if (opts.preventFormulaInjection && str.length > 0 && prefixSet[str.charAt(0)]) {
239
+ str = "'" + str;
240
+ }
241
+ var needsQuote = opts.alwaysQuote ||
242
+ str.indexOf(opts.delimiter) !== -1 ||
243
+ str.indexOf(opts.quote) !== -1 ||
244
+ str.indexOf("\n") !== -1 ||
245
+ str.indexOf("\r") !== -1;
246
+ if (needsQuote) {
247
+ str = opts.quote + str.split(opts.quote).join(opts.quote + opts.quote) + opts.quote;
209
248
  }
210
- lines.push(cells.join(delimiter));
249
+ return str;
211
250
  }
212
- return lines.join(eol) + eol;
213
- }
214
251
 
215
- function _quoteCell(value) {
216
- if (value === null || value === undefined) return "";
217
- var s = typeof value === "string" ? value : String(value);
218
- if (s.indexOf("\"") !== -1 || s.indexOf(",") !== -1 ||
219
- s.indexOf("\r") !== -1 || s.indexOf("\n") !== -1) {
220
- return "\"" + s.replace(/"/g, "\"\"") + "\"";
252
+ var out = [];
253
+ if (opts.header && header) {
254
+ out.push(header.map(escapeCell).join(opts.delimiter));
255
+ }
256
+ for (var i = 0; i < rows.length; i++) {
257
+ var rec = rows[i];
258
+ var cells;
259
+ if (asObjects) {
260
+ cells = header.map(function (k) { return escapeCell(rec[k]); });
261
+ } else {
262
+ cells = rec.map(escapeCell);
263
+ }
264
+ out.push(cells.join(opts.delimiter));
221
265
  }
222
- return s;
266
+ return out.join(opts.eol);
223
267
  }
224
268
 
225
269
  module.exports = {
226
- parse: parse,
227
- stringify: stringify,
228
- CsvError: CsvError,
270
+ parse: parse,
271
+ stringify: stringify,
272
+ CsvError: CsvError,
273
+ DEFAULTS_PARSE: DEFAULTS_PARSE,
274
+ DEFAULTS_STRINGIFY: DEFAULTS_STRINGIFY,
229
275
  };
package/lib/db.js CHANGED
@@ -55,10 +55,17 @@ var cryptoField = require("./crypto-field");
55
55
  var { Query } = require("./db-query");
56
56
  var dbSchema = require("./db-schema");
57
57
  var { boot } = require("./log");
58
+ var lazyRequire = require("./lazy-require");
59
+ var safeAsync = require("./safe-async");
58
60
  var safeEnv = require("./parsers/safe-env");
59
61
  var safeJson = require("./safe-json");
60
62
  var vault = require("./vault");
61
63
 
64
+ // Lazy: cluster-storage's _localDb pulls db back in, so eager require
65
+ // would deadlock the load order. cluster-storage is only used on the
66
+ // purge-audit-chain external-db path, which always runs after init.
67
+ var clusterStorage = lazyRequire(function () { return require("./cluster-storage"); });
68
+
62
69
  var AUDIT_TIP_SCHEMA = {
63
70
  type: "object",
64
71
  required: ["atMonotonicCounter"],
@@ -797,12 +804,11 @@ async function init(opts) {
797
804
 
798
805
  // Start periodic encrypt timer (encrypted mode only)
799
806
  if (atRest === "encrypted") {
800
- encTimer = setInterval(function () {
807
+ encTimer = safeAsync.repeating(function () {
801
808
  try { encryptToDisk(); } catch (e) {
802
809
  log.error("periodic encrypt failed: " + e.message);
803
810
  }
804
- }, C.TIME.minutes(5));
805
- encTimer.unref();
811
+ }, C.TIME.minutes(5), { name: "db-periodic-encrypt" });
806
812
 
807
813
  // Final encrypt on process exit. We don't try to unlink the plaintext
808
814
  // here — the SQLite handle may still be open, and the OS reclaims tmpfs
@@ -914,7 +920,7 @@ function hashFor(table, field, value) {
914
920
  function close() {
915
921
  if (!initialized) return;
916
922
  if (encTimer) {
917
- clearInterval(encTimer);
923
+ encTimer.stop();
918
924
  encTimer = null;
919
925
  }
920
926
  // Best-effort final checkpoint before shutdown so the audit.tip sidecar
@@ -1070,7 +1076,7 @@ async function _runNtpBootCheck(opts) {
1070
1076
 
1071
1077
  // Test helpers — not part of public contract
1072
1078
  function _resetForTest() {
1073
- if (encTimer) { clearInterval(encTimer); encTimer = null; }
1079
+ if (encTimer) { encTimer.stop(); encTimer = null; }
1074
1080
  try { if (database) database.close(); } catch (_e) {}
1075
1081
  database = null;
1076
1082
  dbPath = null;
@@ -1119,10 +1125,9 @@ module.exports = {
1119
1125
  if (!Number.isFinite(lastPurgedCounter) || lastPurgedCounter < 0) {
1120
1126
  throw new Error("purgeAuditChain: lastPurgedCounter must be a non-negative number");
1121
1127
  }
1122
- var c = require("./cluster");
1123
- if (c.isClusterMode()) {
1128
+ if (cluster.isClusterMode()) {
1124
1129
  // External-db has no append-only triggers; ordinary DELETE works.
1125
- var cs = require("./cluster-storage");
1130
+ var cs = clusterStorage();
1126
1131
  var d = await cs.execute(
1127
1132
  "DELETE FROM audit_log WHERE monotonicCounter <= ?", [lastPurgedCounter]
1128
1133
  );
package/lib/deprecate.js CHANGED
@@ -50,6 +50,7 @@
50
50
  * call counter is still incremented so dep.list() shows usage volume.
51
51
  */
52
52
 
53
+ var safeEnv = require("./parsers/safe-env");
53
54
  var { FrameworkError } = require("./framework-error");
54
55
 
55
56
  class DeprecateError extends FrameworkError {
@@ -65,12 +66,12 @@ class DeprecateError extends FrameworkError {
65
66
  var _seen = new Map();
66
67
 
67
68
  function _modeFromEnv() {
68
- var env = process.env.BLAMEJS_DEPRECATIONS;
69
+ var env = safeEnv.readVar("BLAMEJS_DEPRECATIONS");
69
70
  if (typeof env === "string" && env.length > 0) {
70
71
  var v = env.toLowerCase();
71
72
  if (v === "warn" || v === "silent" || v === "error") return v;
72
73
  }
73
- if (process.env.NODE_ENV === "production") return "silent";
74
+ if (safeEnv.readVar("NODE_ENV") === "production") return "silent";
74
75
  return "warn";
75
76
  }
76
77
 
package/lib/error-page.js CHANGED
@@ -40,6 +40,7 @@
40
40
 
41
41
  var lazyRequire = require("./lazy-require");
42
42
  var requestHelpers = require("./request-helpers");
43
+ var safeEnv = require("./parsers/safe-env");
43
44
  var template = require("./template");
44
45
  var audit = lazyRequire(function () { return require("./audit"); });
45
46
 
@@ -273,7 +274,7 @@ function create(opts) {
273
274
  if (modeOpt === "dev" || modeOpt === "prod") {
274
275
  mode = modeOpt;
275
276
  } else {
276
- var nodeEnv = process.env.NODE_ENV;
277
+ var nodeEnv = safeEnv.readVar("NODE_ENV");
277
278
  mode = (nodeEnv === "production") ? "prod" : "dev";
278
279
  }
279
280
 
@@ -53,6 +53,7 @@
53
53
  var retryHelper = require("./retry");
54
54
  var C = require("./constants");
55
55
  var lazyRequire = require("./lazy-require");
56
+ var safeAsync = require("./safe-async");
56
57
  var { ExternalDbError } = require("./framework-error");
57
58
 
58
59
  var audit = lazyRequire(function () { return require("./audit"); });
@@ -78,8 +79,8 @@ class Pool {
78
79
  this.idle = []; // [{ client, lastUsedAt }]
79
80
  this.active = 0; // count of in-use clients
80
81
  this.waiters = []; // queued acquisitions when at max
81
- this._reaper = setInterval(this._reapIdle.bind(this), C.TIME.seconds(10));
82
- this._reaper.unref();
82
+ this._reaper = safeAsync.repeating(this._reapIdle.bind(this),
83
+ C.TIME.seconds(10), { name: "external-db-reaper" });
83
84
  }
84
85
 
85
86
  async acquire() {
@@ -141,7 +142,7 @@ class Pool {
141
142
  }
142
143
 
143
144
  async drain() {
144
- if (this._reaper) { clearInterval(this._reaper); this._reaper = null; }
145
+ if (this._reaper) { this._reaper.stop(); this._reaper = null; }
145
146
  var idleClients = this.idle.map(function (e) { return e.client; });
146
147
  this.idle = [];
147
148
  var self = this;
package/lib/forms.js CHANGED
@@ -35,7 +35,7 @@
35
35
  * forms.escapeAttribute(value) → string (double-quoted-attr context)
36
36
  * forms.escapeHtml = template.escapeHtml (re-export for convenience)
37
37
  */
38
- var nodeCrypto = require("crypto");
38
+ var crypto = require("./crypto");
39
39
  var safeSchema = require("./safe-schema");
40
40
  var safeUrl = require("./safe-url");
41
41
  var template = require("./template");
@@ -47,16 +47,13 @@ var template = require("./template");
47
47
  var CSRF_TOKEN_BYTES = 32;
48
48
 
49
49
  function generateCsrfToken() {
50
- return nodeCrypto.randomBytes(CSRF_TOKEN_BYTES).toString("hex");
50
+ return crypto.generateToken(CSRF_TOKEN_BYTES);
51
51
  }
52
52
 
53
53
  function verifyCsrfToken(submitted, expected) {
54
54
  if (typeof submitted !== "string" || typeof expected !== "string") return false;
55
55
  if (submitted.length === 0 || submitted.length !== expected.length) return false;
56
- var a = Buffer.from(submitted, "utf8");
57
- var b = Buffer.from(expected, "utf8");
58
- if (a.length !== b.length) return false;
59
- return nodeCrypto.timingSafeEqual(a, b);
56
+ return crypto.timingSafeEqual(submitted, expected);
60
57
  }
61
58
 
62
59
  // ============================================================
@@ -43,6 +43,7 @@
43
43
  * }
44
44
  */
45
45
 
46
+ var C = require("./constants");
46
47
  var safeUrl = require("./safe-url");
47
48
  var { defineClass } = require("./framework-error");
48
49
 
@@ -186,7 +187,7 @@ function create(opts) {
186
187
  if (attrs["max-age"] !== undefined) {
187
188
  var maxAge = parseInt(attrs["max-age"], 10);
188
189
  if (!isNaN(maxAge)) {
189
- expiresAt = maxAge <= 0 ? 0 : (now + maxAge * 1000);
190
+ expiresAt = maxAge <= 0 ? 0 : (now + C.TIME.seconds(maxAge));
190
191
  }
191
192
  } else if (attrs.expires) {
192
193
  expiresAt = _parseHttpDate(attrs.expires);
@@ -64,10 +64,10 @@
64
64
  var http = require("http");
65
65
  var https = require("https");
66
66
  var http2 = require("http2");
67
- var nodeCrypto = require("node:crypto");
68
67
  var nodeStream = require("node:stream");
69
68
  var { URL } = require("url");
70
69
  var C = require("./constants");
70
+ var crypto = require("./crypto");
71
71
  var pqcAgent = require("./pqc-agent");
72
72
  var safeAsync = require("./safe-async");
73
73
  var safeBuffer = require("./safe-buffer");
@@ -356,7 +356,7 @@ function _attachJarCookie(headers, jar, url) {
356
356
  // parser accepts so round-trip from one blamejs app's outbound to
357
357
  // another's inbound is exact.
358
358
  function _buildMultipartBody(spec) {
359
- var boundary = "----blamejs-mp-" + nodeCrypto.randomBytes(16).toString("hex");
359
+ var boundary = "----blamejs-mp-" + crypto.generateToken(16);
360
360
  var CRLF = "\r\n";
361
361
  var parts = [];
362
362
 
package/lib/log.js CHANGED
@@ -60,6 +60,7 @@
60
60
  */
61
61
 
62
62
  var { AsyncLocalStorage } = require("node:async_hooks");
63
+ var nodeCrypto = require("node:crypto");
63
64
  var redact = require("./redact");
64
65
  var validateOpts = require("./validate-opts");
65
66
  var { FrameworkError } = require("./framework-error");
@@ -315,7 +316,7 @@ function create(opts) {
315
316
  ? mwOpts.generate
316
317
  : function () {
317
318
  // 16 random hex chars — short, sufficient correlation entropy
318
- return require("crypto").randomBytes(8).toString("hex");
319
+ return nodeCrypto.randomBytes(8).toString("hex");
319
320
  };
320
321
  return function logRequestIdMiddleware(req, res, next) {
321
322
  var inbound = req.headers && req.headers[headerName];