@blamejs/core 0.5.16 → 0.5.17
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 +1 -0
- package/lib/backup/index.js +2 -2
- package/lib/cache.js +5 -9
- package/lib/cli.js +4 -3
- package/lib/cluster.js +4 -4
- package/lib/csv.js +208 -162
- package/lib/db.js +5 -5
- package/lib/external-db.js +4 -3
- package/lib/forms.js +3 -6
- package/lib/http-client.js +2 -2
- package/lib/mail.js +2 -2
- package/lib/middleware/body-parser.js +2 -1
- package/lib/middleware/compression.js +3 -2
- package/lib/middleware/cors.js +1 -1
- package/lib/middleware/rate-limit.js +4 -4
- package/lib/mtls-engine-default.js +3 -2
- package/lib/nonce-store.js +4 -4
- package/lib/otel-export.js +5 -10
- package/lib/parsers/index.js +3 -7
- package/lib/queue.js +6 -7
- package/lib/request-helpers.js +35 -3
- package/lib/restore.js +3 -3
- package/lib/safe-async.js +122 -0
- package/lib/tracing.js +3 -3
- package/lib/webhook.js +3 -3
- package/lib/websocket.js +6 -4
- package/package.json +1 -1
- package/lib/parsers/safe-csv.js +0 -224
package/lib/parsers/safe-csv.js
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Security-focused CSV parser + writer.
|
|
4
|
-
*
|
|
5
|
-
* RFC 4180 compliant parsing + the operator-friendly defaults that the
|
|
6
|
-
* RFC doesn't address:
|
|
7
|
-
*
|
|
8
|
-
* - Size + row-count + field-length limits (DoS prevention)
|
|
9
|
-
* - BOM stripping
|
|
10
|
-
* - Configurable delimiter (',' default; '\t' for TSV; ';' for European)
|
|
11
|
-
* - Configurable quote char
|
|
12
|
-
* - CRLF / LF / CR line endings all accepted
|
|
13
|
-
*
|
|
14
|
-
* SECURITY: writer prevents CSV/Excel formula injection. Excel and other
|
|
15
|
-
* spreadsheet apps execute cells starting with '=', '+', '-', '@', or
|
|
16
|
-
* tab/CR characters. By default the writer prefixes such cells with a
|
|
17
|
-
* single quote so the formula doesn't execute when the file is opened.
|
|
18
|
-
* Toggle with { preventFormulaInjection: false } for true RFC 4180 output.
|
|
19
|
-
*
|
|
20
|
-
* Public API:
|
|
21
|
-
* csv.parse(input, opts?) → array of arrays | array of objects
|
|
22
|
-
* csv.stringify(rows, opts?) → string (RFC 4180 + injection-safe)
|
|
23
|
-
* csv.SafeCsvError → error class
|
|
24
|
-
*
|
|
25
|
-
* Defaults (parse):
|
|
26
|
-
* maxBytes: 16 MiB
|
|
27
|
-
* maxRows: 1,000,000
|
|
28
|
-
* maxFieldBytes: 1 MiB
|
|
29
|
-
* delimiter: ','
|
|
30
|
-
* quote: '"'
|
|
31
|
-
* header: true (first row is column names; rows returned as objects)
|
|
32
|
-
* trim: false (don't trim cell whitespace by default)
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
var C = require("../constants");
|
|
36
|
-
var safeBuffer = require("../safe-buffer");
|
|
37
|
-
var { FrameworkError } = require("../framework-error");
|
|
38
|
-
|
|
39
|
-
class SafeCsvError extends FrameworkError {
|
|
40
|
-
constructor(message, code, position) {
|
|
41
|
-
super(message);
|
|
42
|
-
this.name = "SafeCsvError";
|
|
43
|
-
this.code = code || "csv/invalid";
|
|
44
|
-
this.position = position || null;
|
|
45
|
-
this.isSafeCsvError = true;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
var DEFAULTS_PARSE = {
|
|
50
|
-
maxBytes: C.BYTES.mib(16),
|
|
51
|
-
maxRows: 1000000,
|
|
52
|
-
maxFieldBytes: C.BYTES.mib(1),
|
|
53
|
-
delimiter: ",",
|
|
54
|
-
quote: '"',
|
|
55
|
-
header: true,
|
|
56
|
-
trim: false,
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
var DEFAULTS_STRINGIFY = {
|
|
60
|
-
delimiter: ",",
|
|
61
|
-
quote: '"',
|
|
62
|
-
preventFormulaInjection: true,
|
|
63
|
-
formulaPrefixChars: ["=", "+", "-", "@", "\t", "\r"],
|
|
64
|
-
always_quote: false, // quote every field; default only quotes when needed
|
|
65
|
-
newline: "\r\n", // RFC 4180
|
|
66
|
-
header: null, // explicit array; null → derive from first object's keys
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// ---- parse ----
|
|
70
|
-
|
|
71
|
-
function parse(input, opts) {
|
|
72
|
-
opts = Object.assign({}, DEFAULTS_PARSE, opts || {});
|
|
73
|
-
|
|
74
|
-
input = safeBuffer.normalizeText(input, {
|
|
75
|
-
maxBytes: opts.maxBytes,
|
|
76
|
-
errorClass: SafeCsvError,
|
|
77
|
-
typeCode: "csv/wrong-input-type",
|
|
78
|
-
sizeCode: "csv/too-large",
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
var len = input.length;
|
|
82
|
-
var pos = 0;
|
|
83
|
-
var rows = [];
|
|
84
|
-
var row = [];
|
|
85
|
-
var field = "";
|
|
86
|
-
var inQuote = false;
|
|
87
|
-
|
|
88
|
-
function pushField() {
|
|
89
|
-
if (Buffer.byteLength(field, "utf8") > opts.maxFieldBytes) {
|
|
90
|
-
throw new SafeCsvError("field exceeds maxFieldBytes at row " + (rows.length + 1), "csv/field-too-large");
|
|
91
|
-
}
|
|
92
|
-
row.push(opts.trim ? field.trim() : field);
|
|
93
|
-
field = "";
|
|
94
|
-
}
|
|
95
|
-
function pushRow() {
|
|
96
|
-
pushField();
|
|
97
|
-
rows.push(row);
|
|
98
|
-
if (rows.length > opts.maxRows) {
|
|
99
|
-
throw new SafeCsvError("row count exceeds maxRows", "csv/too-many-rows");
|
|
100
|
-
}
|
|
101
|
-
row = [];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
while (pos < len) {
|
|
105
|
-
var ch = input.charAt(pos);
|
|
106
|
-
if (inQuote) {
|
|
107
|
-
if (ch === opts.quote) {
|
|
108
|
-
// Possible escaped quote (double-quote inside quoted field)
|
|
109
|
-
if (pos + 1 < len && input.charAt(pos + 1) === opts.quote) {
|
|
110
|
-
field += opts.quote;
|
|
111
|
-
pos += 2;
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
// End of quoted field
|
|
115
|
-
inQuote = false;
|
|
116
|
-
pos += 1;
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
field += ch;
|
|
120
|
-
pos += 1;
|
|
121
|
-
} else {
|
|
122
|
-
if (ch === opts.delimiter) {
|
|
123
|
-
pushField();
|
|
124
|
-
pos += 1;
|
|
125
|
-
} else if (ch === "\r") {
|
|
126
|
-
// CR or CRLF — both end the row
|
|
127
|
-
pushRow();
|
|
128
|
-
pos += 1;
|
|
129
|
-
if (pos < len && input.charAt(pos) === "\n") pos += 1;
|
|
130
|
-
} else if (ch === "\n") {
|
|
131
|
-
pushRow();
|
|
132
|
-
pos += 1;
|
|
133
|
-
} else if (ch === opts.quote && field === "") {
|
|
134
|
-
inQuote = true;
|
|
135
|
-
pos += 1;
|
|
136
|
-
} else {
|
|
137
|
-
field += ch;
|
|
138
|
-
pos += 1;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (inQuote) throw new SafeCsvError("unterminated quoted field", "csv/unterminated-quote");
|
|
143
|
-
// Final row (no trailing newline)
|
|
144
|
-
if (field.length > 0 || row.length > 0) {
|
|
145
|
-
pushRow();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (opts.header) {
|
|
149
|
-
if (rows.length === 0) return [];
|
|
150
|
-
var header = rows[0];
|
|
151
|
-
return rows.slice(1).map(function (r) {
|
|
152
|
-
var obj = {};
|
|
153
|
-
for (var i = 0; i < header.length; i++) obj[header[i]] = r[i] !== undefined ? r[i] : null;
|
|
154
|
-
return obj;
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
return rows;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// ---- stringify ----
|
|
161
|
-
|
|
162
|
-
function stringify(rows, opts) {
|
|
163
|
-
opts = Object.assign({}, DEFAULTS_STRINGIFY, opts || {});
|
|
164
|
-
if (!Array.isArray(rows)) {
|
|
165
|
-
throw new SafeCsvError("stringify expects an array of rows", "csv/wrong-input-type");
|
|
166
|
-
}
|
|
167
|
-
if (rows.length === 0) return "";
|
|
168
|
-
|
|
169
|
-
// Determine header + row shape
|
|
170
|
-
var header;
|
|
171
|
-
var isObjectRows = false;
|
|
172
|
-
if (Array.isArray(rows[0])) {
|
|
173
|
-
isObjectRows = false;
|
|
174
|
-
header = opts.header || null;
|
|
175
|
-
} else if (typeof rows[0] === "object" && rows[0] !== null) {
|
|
176
|
-
isObjectRows = true;
|
|
177
|
-
header = opts.header || Object.keys(rows[0]);
|
|
178
|
-
} else {
|
|
179
|
-
throw new SafeCsvError("rows must be arrays or objects", "csv/wrong-input-type");
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function escapeCell(value) {
|
|
183
|
-
var s = value == null ? "" : String(value);
|
|
184
|
-
if (opts.preventFormulaInjection && s.length > 0) {
|
|
185
|
-
var first = s.charAt(0);
|
|
186
|
-
if (opts.formulaPrefixChars.indexOf(first) !== -1) {
|
|
187
|
-
s = "'" + s; // Excel-safe prefix
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
var needsQuote = opts.always_quote ||
|
|
191
|
-
s.indexOf(opts.delimiter) !== -1 ||
|
|
192
|
-
s.indexOf(opts.quote) !== -1 ||
|
|
193
|
-
s.indexOf("\n") !== -1 ||
|
|
194
|
-
s.indexOf("\r") !== -1;
|
|
195
|
-
if (needsQuote) {
|
|
196
|
-
s = opts.quote + s.split(opts.quote).join(opts.quote + opts.quote) + opts.quote;
|
|
197
|
-
}
|
|
198
|
-
return s;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
var out = [];
|
|
202
|
-
if (header) {
|
|
203
|
-
out.push(header.map(escapeCell).join(opts.delimiter));
|
|
204
|
-
}
|
|
205
|
-
for (var i = 0; i < rows.length; i++) {
|
|
206
|
-
var r = rows[i];
|
|
207
|
-
var cells;
|
|
208
|
-
if (isObjectRows) {
|
|
209
|
-
cells = header.map(function (k) { return escapeCell(r[k]); });
|
|
210
|
-
} else {
|
|
211
|
-
cells = r.map(escapeCell);
|
|
212
|
-
}
|
|
213
|
-
out.push(cells.join(opts.delimiter));
|
|
214
|
-
}
|
|
215
|
-
return out.join(opts.newline);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
module.exports = {
|
|
219
|
-
parse: parse,
|
|
220
|
-
stringify: stringify,
|
|
221
|
-
SafeCsvError: SafeCsvError,
|
|
222
|
-
DEFAULTS_PARSE: DEFAULTS_PARSE,
|
|
223
|
-
DEFAULTS_STRINGIFY: DEFAULTS_STRINGIFY,
|
|
224
|
-
};
|