@profullstack/threatcrush 0.1.16 → 0.2.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/dist/daemon.js ADDED
@@ -0,0 +1,4334 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __commonJS = (cb, mod) => function __require() {
10
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+
29
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/parser.js
30
+ var require_parser = __commonJS({
31
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/parser.js"(exports2, module2) {
32
+ "use strict";
33
+ var ParserEND = 1114112;
34
+ var ParserError = class _ParserError extends Error {
35
+ /* istanbul ignore next */
36
+ constructor(msg, filename, linenumber) {
37
+ super("[ParserError] " + msg, filename, linenumber);
38
+ this.name = "ParserError";
39
+ this.code = "ParserError";
40
+ if (Error.captureStackTrace) Error.captureStackTrace(this, _ParserError);
41
+ }
42
+ };
43
+ var State = class {
44
+ constructor(parser) {
45
+ this.parser = parser;
46
+ this.buf = "";
47
+ this.returned = null;
48
+ this.result = null;
49
+ this.resultTable = null;
50
+ this.resultArr = null;
51
+ }
52
+ };
53
+ var Parser = class {
54
+ constructor() {
55
+ this.pos = 0;
56
+ this.col = 0;
57
+ this.line = 0;
58
+ this.obj = {};
59
+ this.ctx = this.obj;
60
+ this.stack = [];
61
+ this._buf = "";
62
+ this.char = null;
63
+ this.ii = 0;
64
+ this.state = new State(this.parseStart);
65
+ }
66
+ parse(str) {
67
+ if (str.length === 0 || str.length == null) return;
68
+ this._buf = String(str);
69
+ this.ii = -1;
70
+ this.char = -1;
71
+ let getNext;
72
+ while (getNext === false || this.nextChar()) {
73
+ getNext = this.runOne();
74
+ }
75
+ this._buf = null;
76
+ }
77
+ nextChar() {
78
+ if (this.char === 10) {
79
+ ++this.line;
80
+ this.col = -1;
81
+ }
82
+ ++this.ii;
83
+ this.char = this._buf.codePointAt(this.ii);
84
+ ++this.pos;
85
+ ++this.col;
86
+ return this.haveBuffer();
87
+ }
88
+ haveBuffer() {
89
+ return this.ii < this._buf.length;
90
+ }
91
+ runOne() {
92
+ return this.state.parser.call(this, this.state.returned);
93
+ }
94
+ finish() {
95
+ this.char = ParserEND;
96
+ let last;
97
+ do {
98
+ last = this.state.parser;
99
+ this.runOne();
100
+ } while (this.state.parser !== last);
101
+ this.ctx = null;
102
+ this.state = null;
103
+ this._buf = null;
104
+ return this.obj;
105
+ }
106
+ next(fn) {
107
+ if (typeof fn !== "function") throw new ParserError("Tried to set state to non-existent state: " + JSON.stringify(fn));
108
+ this.state.parser = fn;
109
+ }
110
+ goto(fn) {
111
+ this.next(fn);
112
+ return this.runOne();
113
+ }
114
+ call(fn, returnWith) {
115
+ if (returnWith) this.next(returnWith);
116
+ this.stack.push(this.state);
117
+ this.state = new State(fn);
118
+ }
119
+ callNow(fn, returnWith) {
120
+ this.call(fn, returnWith);
121
+ return this.runOne();
122
+ }
123
+ return(value) {
124
+ if (this.stack.length === 0) throw this.error(new ParserError("Stack underflow"));
125
+ if (value === void 0) value = this.state.buf;
126
+ this.state = this.stack.pop();
127
+ this.state.returned = value;
128
+ }
129
+ returnNow(value) {
130
+ this.return(value);
131
+ return this.runOne();
132
+ }
133
+ consume() {
134
+ if (this.char === ParserEND) throw this.error(new ParserError("Unexpected end-of-buffer"));
135
+ this.state.buf += this._buf[this.ii];
136
+ }
137
+ error(err) {
138
+ err.line = this.line;
139
+ err.col = this.col;
140
+ err.pos = this.pos;
141
+ return err;
142
+ }
143
+ /* istanbul ignore next */
144
+ parseStart() {
145
+ throw new ParserError("Must declare a parseStart method");
146
+ }
147
+ };
148
+ Parser.END = ParserEND;
149
+ Parser.Error = ParserError;
150
+ module2.exports = Parser;
151
+ }
152
+ });
153
+
154
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/create-datetime.js
155
+ var require_create_datetime = __commonJS({
156
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/create-datetime.js"(exports2, module2) {
157
+ "use strict";
158
+ module2.exports = (value) => {
159
+ const date = new Date(value);
160
+ if (isNaN(date)) {
161
+ throw new TypeError("Invalid Datetime");
162
+ } else {
163
+ return date;
164
+ }
165
+ };
166
+ }
167
+ });
168
+
169
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/format-num.js
170
+ var require_format_num = __commonJS({
171
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/format-num.js"(exports2, module2) {
172
+ "use strict";
173
+ module2.exports = (d, num) => {
174
+ num = String(num);
175
+ while (num.length < d) num = "0" + num;
176
+ return num;
177
+ };
178
+ }
179
+ });
180
+
181
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/create-datetime-float.js
182
+ var require_create_datetime_float = __commonJS({
183
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/create-datetime-float.js"(exports2, module2) {
184
+ "use strict";
185
+ var f = require_format_num();
186
+ var FloatingDateTime = class extends Date {
187
+ constructor(value) {
188
+ super(value + "Z");
189
+ this.isFloating = true;
190
+ }
191
+ toISOString() {
192
+ const date = `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`;
193
+ const time = `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`;
194
+ return `${date}T${time}`;
195
+ }
196
+ };
197
+ module2.exports = (value) => {
198
+ const date = new FloatingDateTime(value);
199
+ if (isNaN(date)) {
200
+ throw new TypeError("Invalid Datetime");
201
+ } else {
202
+ return date;
203
+ }
204
+ };
205
+ }
206
+ });
207
+
208
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/create-date.js
209
+ var require_create_date = __commonJS({
210
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/create-date.js"(exports2, module2) {
211
+ "use strict";
212
+ var f = require_format_num();
213
+ var DateTime = global.Date;
214
+ var Date2 = class extends DateTime {
215
+ constructor(value) {
216
+ super(value);
217
+ this.isDate = true;
218
+ }
219
+ toISOString() {
220
+ return `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`;
221
+ }
222
+ };
223
+ module2.exports = (value) => {
224
+ const date = new Date2(value);
225
+ if (isNaN(date)) {
226
+ throw new TypeError("Invalid Datetime");
227
+ } else {
228
+ return date;
229
+ }
230
+ };
231
+ }
232
+ });
233
+
234
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/create-time.js
235
+ var require_create_time = __commonJS({
236
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/create-time.js"(exports2, module2) {
237
+ "use strict";
238
+ var f = require_format_num();
239
+ var Time = class extends Date {
240
+ constructor(value) {
241
+ super(`0000-01-01T${value}Z`);
242
+ this.isTime = true;
243
+ }
244
+ toISOString() {
245
+ return `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`;
246
+ }
247
+ };
248
+ module2.exports = (value) => {
249
+ const date = new Time(value);
250
+ if (isNaN(date)) {
251
+ throw new TypeError("Invalid Datetime");
252
+ } else {
253
+ return date;
254
+ }
255
+ };
256
+ }
257
+ });
258
+
259
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/toml-parser.js
260
+ var require_toml_parser = __commonJS({
261
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/lib/toml-parser.js"(exports, module) {
262
+ "use strict";
263
+ module.exports = makeParserClass(require_parser());
264
+ module.exports.makeParserClass = makeParserClass;
265
+ var TomlError = class _TomlError extends Error {
266
+ constructor(msg) {
267
+ super(msg);
268
+ this.name = "TomlError";
269
+ if (Error.captureStackTrace) Error.captureStackTrace(this, _TomlError);
270
+ this.fromTOML = true;
271
+ this.wrapped = null;
272
+ }
273
+ };
274
+ TomlError.wrap = (err) => {
275
+ const terr = new TomlError(err.message);
276
+ terr.code = err.code;
277
+ terr.wrapped = err;
278
+ return terr;
279
+ };
280
+ module.exports.TomlError = TomlError;
281
+ var createDateTime = require_create_datetime();
282
+ var createDateTimeFloat = require_create_datetime_float();
283
+ var createDate = require_create_date();
284
+ var createTime = require_create_time();
285
+ var CTRL_I = 9;
286
+ var CTRL_J = 10;
287
+ var CTRL_M = 13;
288
+ var CTRL_CHAR_BOUNDARY = 31;
289
+ var CHAR_SP = 32;
290
+ var CHAR_QUOT = 34;
291
+ var CHAR_NUM = 35;
292
+ var CHAR_APOS = 39;
293
+ var CHAR_PLUS = 43;
294
+ var CHAR_COMMA = 44;
295
+ var CHAR_HYPHEN = 45;
296
+ var CHAR_PERIOD = 46;
297
+ var CHAR_0 = 48;
298
+ var CHAR_1 = 49;
299
+ var CHAR_7 = 55;
300
+ var CHAR_9 = 57;
301
+ var CHAR_COLON = 58;
302
+ var CHAR_EQUALS = 61;
303
+ var CHAR_A = 65;
304
+ var CHAR_E = 69;
305
+ var CHAR_F = 70;
306
+ var CHAR_T = 84;
307
+ var CHAR_U = 85;
308
+ var CHAR_Z = 90;
309
+ var CHAR_LOWBAR = 95;
310
+ var CHAR_a = 97;
311
+ var CHAR_b = 98;
312
+ var CHAR_e = 101;
313
+ var CHAR_f = 102;
314
+ var CHAR_i = 105;
315
+ var CHAR_l = 108;
316
+ var CHAR_n = 110;
317
+ var CHAR_o = 111;
318
+ var CHAR_r = 114;
319
+ var CHAR_s = 115;
320
+ var CHAR_t = 116;
321
+ var CHAR_u = 117;
322
+ var CHAR_x = 120;
323
+ var CHAR_z = 122;
324
+ var CHAR_LCUB = 123;
325
+ var CHAR_RCUB = 125;
326
+ var CHAR_LSQB = 91;
327
+ var CHAR_BSOL = 92;
328
+ var CHAR_RSQB = 93;
329
+ var CHAR_DEL = 127;
330
+ var SURROGATE_FIRST = 55296;
331
+ var SURROGATE_LAST = 57343;
332
+ var escapes = {
333
+ [CHAR_b]: "\b",
334
+ [CHAR_t]: " ",
335
+ [CHAR_n]: "\n",
336
+ [CHAR_f]: "\f",
337
+ [CHAR_r]: "\r",
338
+ [CHAR_QUOT]: '"',
339
+ [CHAR_BSOL]: "\\"
340
+ };
341
+ function isDigit(cp) {
342
+ return cp >= CHAR_0 && cp <= CHAR_9;
343
+ }
344
+ function isHexit(cp) {
345
+ return cp >= CHAR_A && cp <= CHAR_F || cp >= CHAR_a && cp <= CHAR_f || cp >= CHAR_0 && cp <= CHAR_9;
346
+ }
347
+ function isBit(cp) {
348
+ return cp === CHAR_1 || cp === CHAR_0;
349
+ }
350
+ function isOctit(cp) {
351
+ return cp >= CHAR_0 && cp <= CHAR_7;
352
+ }
353
+ function isAlphaNumQuoteHyphen(cp) {
354
+ return cp >= CHAR_A && cp <= CHAR_Z || cp >= CHAR_a && cp <= CHAR_z || cp >= CHAR_0 && cp <= CHAR_9 || cp === CHAR_APOS || cp === CHAR_QUOT || cp === CHAR_LOWBAR || cp === CHAR_HYPHEN;
355
+ }
356
+ function isAlphaNumHyphen(cp) {
357
+ return cp >= CHAR_A && cp <= CHAR_Z || cp >= CHAR_a && cp <= CHAR_z || cp >= CHAR_0 && cp <= CHAR_9 || cp === CHAR_LOWBAR || cp === CHAR_HYPHEN;
358
+ }
359
+ var _type = /* @__PURE__ */ Symbol("type");
360
+ var _declared = /* @__PURE__ */ Symbol("declared");
361
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
362
+ var defineProperty = Object.defineProperty;
363
+ var descriptor = { configurable: true, enumerable: true, writable: true, value: void 0 };
364
+ function hasKey(obj, key) {
365
+ if (hasOwnProperty.call(obj, key)) return true;
366
+ if (key === "__proto__") defineProperty(obj, "__proto__", descriptor);
367
+ return false;
368
+ }
369
+ var INLINE_TABLE = /* @__PURE__ */ Symbol("inline-table");
370
+ function InlineTable() {
371
+ return Object.defineProperties({}, {
372
+ [_type]: { value: INLINE_TABLE }
373
+ });
374
+ }
375
+ function isInlineTable(obj) {
376
+ if (obj === null || typeof obj !== "object") return false;
377
+ return obj[_type] === INLINE_TABLE;
378
+ }
379
+ var TABLE = /* @__PURE__ */ Symbol("table");
380
+ function Table() {
381
+ return Object.defineProperties({}, {
382
+ [_type]: { value: TABLE },
383
+ [_declared]: { value: false, writable: true }
384
+ });
385
+ }
386
+ function isTable(obj) {
387
+ if (obj === null || typeof obj !== "object") return false;
388
+ return obj[_type] === TABLE;
389
+ }
390
+ var _contentType = /* @__PURE__ */ Symbol("content-type");
391
+ var INLINE_LIST = /* @__PURE__ */ Symbol("inline-list");
392
+ function InlineList(type) {
393
+ return Object.defineProperties([], {
394
+ [_type]: { value: INLINE_LIST },
395
+ [_contentType]: { value: type }
396
+ });
397
+ }
398
+ function isInlineList(obj) {
399
+ if (obj === null || typeof obj !== "object") return false;
400
+ return obj[_type] === INLINE_LIST;
401
+ }
402
+ var LIST = /* @__PURE__ */ Symbol("list");
403
+ function List() {
404
+ return Object.defineProperties([], {
405
+ [_type]: { value: LIST }
406
+ });
407
+ }
408
+ function isList(obj) {
409
+ if (obj === null || typeof obj !== "object") return false;
410
+ return obj[_type] === LIST;
411
+ }
412
+ var _custom;
413
+ try {
414
+ const utilInspect = eval("require('util').inspect");
415
+ _custom = utilInspect.custom;
416
+ } catch (_) {
417
+ }
418
+ var _inspect = _custom || "inspect";
419
+ var BoxedBigInt = class {
420
+ constructor(value) {
421
+ try {
422
+ this.value = global.BigInt.asIntN(64, value);
423
+ } catch (_) {
424
+ this.value = null;
425
+ }
426
+ Object.defineProperty(this, _type, { value: INTEGER });
427
+ }
428
+ isNaN() {
429
+ return this.value === null;
430
+ }
431
+ /* istanbul ignore next */
432
+ toString() {
433
+ return String(this.value);
434
+ }
435
+ /* istanbul ignore next */
436
+ [_inspect]() {
437
+ return `[BigInt: ${this.toString()}]}`;
438
+ }
439
+ valueOf() {
440
+ return this.value;
441
+ }
442
+ };
443
+ var INTEGER = /* @__PURE__ */ Symbol("integer");
444
+ function Integer(value) {
445
+ let num = Number(value);
446
+ if (Object.is(num, -0)) num = 0;
447
+ if (global.BigInt && !Number.isSafeInteger(num)) {
448
+ return new BoxedBigInt(value);
449
+ } else {
450
+ return Object.defineProperties(new Number(num), {
451
+ isNaN: { value: function() {
452
+ return isNaN(this);
453
+ } },
454
+ [_type]: { value: INTEGER },
455
+ [_inspect]: { value: () => `[Integer: ${value}]` }
456
+ });
457
+ }
458
+ }
459
+ function isInteger(obj) {
460
+ if (obj === null || typeof obj !== "object") return false;
461
+ return obj[_type] === INTEGER;
462
+ }
463
+ var FLOAT = /* @__PURE__ */ Symbol("float");
464
+ function Float(value) {
465
+ return Object.defineProperties(new Number(value), {
466
+ [_type]: { value: FLOAT },
467
+ [_inspect]: { value: () => `[Float: ${value}]` }
468
+ });
469
+ }
470
+ function isFloat(obj) {
471
+ if (obj === null || typeof obj !== "object") return false;
472
+ return obj[_type] === FLOAT;
473
+ }
474
+ function tomlType(value) {
475
+ const type = typeof value;
476
+ if (type === "object") {
477
+ if (value === null) return "null";
478
+ if (value instanceof Date) return "datetime";
479
+ if (_type in value) {
480
+ switch (value[_type]) {
481
+ case INLINE_TABLE:
482
+ return "inline-table";
483
+ case INLINE_LIST:
484
+ return "inline-list";
485
+ /* istanbul ignore next */
486
+ case TABLE:
487
+ return "table";
488
+ /* istanbul ignore next */
489
+ case LIST:
490
+ return "list";
491
+ case FLOAT:
492
+ return "float";
493
+ case INTEGER:
494
+ return "integer";
495
+ }
496
+ }
497
+ }
498
+ return type;
499
+ }
500
+ function makeParserClass(Parser) {
501
+ class TOMLParser extends Parser {
502
+ constructor() {
503
+ super();
504
+ this.ctx = this.obj = Table();
505
+ }
506
+ /* MATCH HELPER */
507
+ atEndOfWord() {
508
+ return this.char === CHAR_NUM || this.char === CTRL_I || this.char === CHAR_SP || this.atEndOfLine();
509
+ }
510
+ atEndOfLine() {
511
+ return this.char === Parser.END || this.char === CTRL_J || this.char === CTRL_M;
512
+ }
513
+ parseStart() {
514
+ if (this.char === Parser.END) {
515
+ return null;
516
+ } else if (this.char === CHAR_LSQB) {
517
+ return this.call(this.parseTableOrList);
518
+ } else if (this.char === CHAR_NUM) {
519
+ return this.call(this.parseComment);
520
+ } else if (this.char === CTRL_J || this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) {
521
+ return null;
522
+ } else if (isAlphaNumQuoteHyphen(this.char)) {
523
+ return this.callNow(this.parseAssignStatement);
524
+ } else {
525
+ throw this.error(new TomlError(`Unknown character "${this.char}"`));
526
+ }
527
+ }
528
+ // HELPER, this strips any whitespace and comments to the end of the line
529
+ // then RETURNS. Last state in a production.
530
+ parseWhitespaceToEOL() {
531
+ if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) {
532
+ return null;
533
+ } else if (this.char === CHAR_NUM) {
534
+ return this.goto(this.parseComment);
535
+ } else if (this.char === Parser.END || this.char === CTRL_J) {
536
+ return this.return();
537
+ } else {
538
+ throw this.error(new TomlError("Unexpected character, expected only whitespace or comments till end of line"));
539
+ }
540
+ }
541
+ /* ASSIGNMENT: key = value */
542
+ parseAssignStatement() {
543
+ return this.callNow(this.parseAssign, this.recordAssignStatement);
544
+ }
545
+ recordAssignStatement(kv) {
546
+ let target = this.ctx;
547
+ let finalKey = kv.key.pop();
548
+ for (let kw of kv.key) {
549
+ if (hasKey(target, kw) && (!isTable(target[kw]) || target[kw][_declared])) {
550
+ throw this.error(new TomlError("Can't redefine existing key"));
551
+ }
552
+ target = target[kw] = target[kw] || Table();
553
+ }
554
+ if (hasKey(target, finalKey)) {
555
+ throw this.error(new TomlError("Can't redefine existing key"));
556
+ }
557
+ if (isInteger(kv.value) || isFloat(kv.value)) {
558
+ target[finalKey] = kv.value.valueOf();
559
+ } else {
560
+ target[finalKey] = kv.value;
561
+ }
562
+ return this.goto(this.parseWhitespaceToEOL);
563
+ }
564
+ /* ASSSIGNMENT expression, key = value possibly inside an inline table */
565
+ parseAssign() {
566
+ return this.callNow(this.parseKeyword, this.recordAssignKeyword);
567
+ }
568
+ recordAssignKeyword(key) {
569
+ if (this.state.resultTable) {
570
+ this.state.resultTable.push(key);
571
+ } else {
572
+ this.state.resultTable = [key];
573
+ }
574
+ return this.goto(this.parseAssignKeywordPreDot);
575
+ }
576
+ parseAssignKeywordPreDot() {
577
+ if (this.char === CHAR_PERIOD) {
578
+ return this.next(this.parseAssignKeywordPostDot);
579
+ } else if (this.char !== CHAR_SP && this.char !== CTRL_I) {
580
+ return this.goto(this.parseAssignEqual);
581
+ }
582
+ }
583
+ parseAssignKeywordPostDot() {
584
+ if (this.char !== CHAR_SP && this.char !== CTRL_I) {
585
+ return this.callNow(this.parseKeyword, this.recordAssignKeyword);
586
+ }
587
+ }
588
+ parseAssignEqual() {
589
+ if (this.char === CHAR_EQUALS) {
590
+ return this.next(this.parseAssignPreValue);
591
+ } else {
592
+ throw this.error(new TomlError('Invalid character, expected "="'));
593
+ }
594
+ }
595
+ parseAssignPreValue() {
596
+ if (this.char === CHAR_SP || this.char === CTRL_I) {
597
+ return null;
598
+ } else {
599
+ return this.callNow(this.parseValue, this.recordAssignValue);
600
+ }
601
+ }
602
+ recordAssignValue(value) {
603
+ return this.returnNow({ key: this.state.resultTable, value });
604
+ }
605
+ /* COMMENTS: #...eol */
606
+ parseComment() {
607
+ do {
608
+ if (this.char === Parser.END || this.char === CTRL_J) {
609
+ return this.return();
610
+ }
611
+ } while (this.nextChar());
612
+ }
613
+ /* TABLES AND LISTS, [foo] and [[foo]] */
614
+ parseTableOrList() {
615
+ if (this.char === CHAR_LSQB) {
616
+ this.next(this.parseList);
617
+ } else {
618
+ return this.goto(this.parseTable);
619
+ }
620
+ }
621
+ /* TABLE [foo.bar.baz] */
622
+ parseTable() {
623
+ this.ctx = this.obj;
624
+ return this.goto(this.parseTableNext);
625
+ }
626
+ parseTableNext() {
627
+ if (this.char === CHAR_SP || this.char === CTRL_I) {
628
+ return null;
629
+ } else {
630
+ return this.callNow(this.parseKeyword, this.parseTableMore);
631
+ }
632
+ }
633
+ parseTableMore(keyword) {
634
+ if (this.char === CHAR_SP || this.char === CTRL_I) {
635
+ return null;
636
+ } else if (this.char === CHAR_RSQB) {
637
+ if (hasKey(this.ctx, keyword) && (!isTable(this.ctx[keyword]) || this.ctx[keyword][_declared])) {
638
+ throw this.error(new TomlError("Can't redefine existing key"));
639
+ } else {
640
+ this.ctx = this.ctx[keyword] = this.ctx[keyword] || Table();
641
+ this.ctx[_declared] = true;
642
+ }
643
+ return this.next(this.parseWhitespaceToEOL);
644
+ } else if (this.char === CHAR_PERIOD) {
645
+ if (!hasKey(this.ctx, keyword)) {
646
+ this.ctx = this.ctx[keyword] = Table();
647
+ } else if (isTable(this.ctx[keyword])) {
648
+ this.ctx = this.ctx[keyword];
649
+ } else if (isList(this.ctx[keyword])) {
650
+ this.ctx = this.ctx[keyword][this.ctx[keyword].length - 1];
651
+ } else {
652
+ throw this.error(new TomlError("Can't redefine existing key"));
653
+ }
654
+ return this.next(this.parseTableNext);
655
+ } else {
656
+ throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]"));
657
+ }
658
+ }
659
+ /* LIST [[a.b.c]] */
660
+ parseList() {
661
+ this.ctx = this.obj;
662
+ return this.goto(this.parseListNext);
663
+ }
664
+ parseListNext() {
665
+ if (this.char === CHAR_SP || this.char === CTRL_I) {
666
+ return null;
667
+ } else {
668
+ return this.callNow(this.parseKeyword, this.parseListMore);
669
+ }
670
+ }
671
+ parseListMore(keyword) {
672
+ if (this.char === CHAR_SP || this.char === CTRL_I) {
673
+ return null;
674
+ } else if (this.char === CHAR_RSQB) {
675
+ if (!hasKey(this.ctx, keyword)) {
676
+ this.ctx[keyword] = List();
677
+ }
678
+ if (isInlineList(this.ctx[keyword])) {
679
+ throw this.error(new TomlError("Can't extend an inline array"));
680
+ } else if (isList(this.ctx[keyword])) {
681
+ const next = Table();
682
+ this.ctx[keyword].push(next);
683
+ this.ctx = next;
684
+ } else {
685
+ throw this.error(new TomlError("Can't redefine an existing key"));
686
+ }
687
+ return this.next(this.parseListEnd);
688
+ } else if (this.char === CHAR_PERIOD) {
689
+ if (!hasKey(this.ctx, keyword)) {
690
+ this.ctx = this.ctx[keyword] = Table();
691
+ } else if (isInlineList(this.ctx[keyword])) {
692
+ throw this.error(new TomlError("Can't extend an inline array"));
693
+ } else if (isInlineTable(this.ctx[keyword])) {
694
+ throw this.error(new TomlError("Can't extend an inline table"));
695
+ } else if (isList(this.ctx[keyword])) {
696
+ this.ctx = this.ctx[keyword][this.ctx[keyword].length - 1];
697
+ } else if (isTable(this.ctx[keyword])) {
698
+ this.ctx = this.ctx[keyword];
699
+ } else {
700
+ throw this.error(new TomlError("Can't redefine an existing key"));
701
+ }
702
+ return this.next(this.parseListNext);
703
+ } else {
704
+ throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]"));
705
+ }
706
+ }
707
+ parseListEnd(keyword) {
708
+ if (this.char === CHAR_RSQB) {
709
+ return this.next(this.parseWhitespaceToEOL);
710
+ } else {
711
+ throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]"));
712
+ }
713
+ }
714
+ /* VALUE string, number, boolean, inline list, inline object */
715
+ parseValue() {
716
+ if (this.char === Parser.END) {
717
+ throw this.error(new TomlError("Key without value"));
718
+ } else if (this.char === CHAR_QUOT) {
719
+ return this.next(this.parseDoubleString);
720
+ }
721
+ if (this.char === CHAR_APOS) {
722
+ return this.next(this.parseSingleString);
723
+ } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) {
724
+ return this.goto(this.parseNumberSign);
725
+ } else if (this.char === CHAR_i) {
726
+ return this.next(this.parseInf);
727
+ } else if (this.char === CHAR_n) {
728
+ return this.next(this.parseNan);
729
+ } else if (isDigit(this.char)) {
730
+ return this.goto(this.parseNumberOrDateTime);
731
+ } else if (this.char === CHAR_t || this.char === CHAR_f) {
732
+ return this.goto(this.parseBoolean);
733
+ } else if (this.char === CHAR_LSQB) {
734
+ return this.call(this.parseInlineList, this.recordValue);
735
+ } else if (this.char === CHAR_LCUB) {
736
+ return this.call(this.parseInlineTable, this.recordValue);
737
+ } else {
738
+ throw this.error(new TomlError("Unexpected character, expecting string, number, datetime, boolean, inline array or inline table"));
739
+ }
740
+ }
741
+ recordValue(value) {
742
+ return this.returnNow(value);
743
+ }
744
+ parseInf() {
745
+ if (this.char === CHAR_n) {
746
+ return this.next(this.parseInf2);
747
+ } else {
748
+ throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"'));
749
+ }
750
+ }
751
+ parseInf2() {
752
+ if (this.char === CHAR_f) {
753
+ if (this.state.buf === "-") {
754
+ return this.return(-Infinity);
755
+ } else {
756
+ return this.return(Infinity);
757
+ }
758
+ } else {
759
+ throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"'));
760
+ }
761
+ }
762
+ parseNan() {
763
+ if (this.char === CHAR_a) {
764
+ return this.next(this.parseNan2);
765
+ } else {
766
+ throw this.error(new TomlError('Unexpected character, expected "nan"'));
767
+ }
768
+ }
769
+ parseNan2() {
770
+ if (this.char === CHAR_n) {
771
+ return this.return(NaN);
772
+ } else {
773
+ throw this.error(new TomlError('Unexpected character, expected "nan"'));
774
+ }
775
+ }
776
+ /* KEYS, barewords or basic, literal, or dotted */
777
+ parseKeyword() {
778
+ if (this.char === CHAR_QUOT) {
779
+ return this.next(this.parseBasicString);
780
+ } else if (this.char === CHAR_APOS) {
781
+ return this.next(this.parseLiteralString);
782
+ } else {
783
+ return this.goto(this.parseBareKey);
784
+ }
785
+ }
786
+ /* KEYS: barewords */
787
+ parseBareKey() {
788
+ do {
789
+ if (this.char === Parser.END) {
790
+ throw this.error(new TomlError("Key ended without value"));
791
+ } else if (isAlphaNumHyphen(this.char)) {
792
+ this.consume();
793
+ } else if (this.state.buf.length === 0) {
794
+ throw this.error(new TomlError("Empty bare keys are not allowed"));
795
+ } else {
796
+ return this.returnNow();
797
+ }
798
+ } while (this.nextChar());
799
+ }
800
+ /* STRINGS, single quoted (literal) */
801
+ parseSingleString() {
802
+ if (this.char === CHAR_APOS) {
803
+ return this.next(this.parseLiteralMultiStringMaybe);
804
+ } else {
805
+ return this.goto(this.parseLiteralString);
806
+ }
807
+ }
808
+ parseLiteralString() {
809
+ do {
810
+ if (this.char === CHAR_APOS) {
811
+ return this.return();
812
+ } else if (this.atEndOfLine()) {
813
+ throw this.error(new TomlError("Unterminated string"));
814
+ } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I) {
815
+ throw this.errorControlCharInString();
816
+ } else {
817
+ this.consume();
818
+ }
819
+ } while (this.nextChar());
820
+ }
821
+ parseLiteralMultiStringMaybe() {
822
+ if (this.char === CHAR_APOS) {
823
+ return this.next(this.parseLiteralMultiString);
824
+ } else {
825
+ return this.returnNow();
826
+ }
827
+ }
828
+ parseLiteralMultiString() {
829
+ if (this.char === CTRL_M) {
830
+ return null;
831
+ } else if (this.char === CTRL_J) {
832
+ return this.next(this.parseLiteralMultiStringContent);
833
+ } else {
834
+ return this.goto(this.parseLiteralMultiStringContent);
835
+ }
836
+ }
837
+ parseLiteralMultiStringContent() {
838
+ do {
839
+ if (this.char === CHAR_APOS) {
840
+ return this.next(this.parseLiteralMultiEnd);
841
+ } else if (this.char === Parser.END) {
842
+ throw this.error(new TomlError("Unterminated multi-line string"));
843
+ } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I && this.char !== CTRL_J && this.char !== CTRL_M) {
844
+ throw this.errorControlCharInString();
845
+ } else {
846
+ this.consume();
847
+ }
848
+ } while (this.nextChar());
849
+ }
850
+ parseLiteralMultiEnd() {
851
+ if (this.char === CHAR_APOS) {
852
+ return this.next(this.parseLiteralMultiEnd2);
853
+ } else {
854
+ this.state.buf += "'";
855
+ return this.goto(this.parseLiteralMultiStringContent);
856
+ }
857
+ }
858
+ parseLiteralMultiEnd2() {
859
+ if (this.char === CHAR_APOS) {
860
+ return this.return();
861
+ } else {
862
+ this.state.buf += "''";
863
+ return this.goto(this.parseLiteralMultiStringContent);
864
+ }
865
+ }
866
+ /* STRINGS double quoted */
867
+ parseDoubleString() {
868
+ if (this.char === CHAR_QUOT) {
869
+ return this.next(this.parseMultiStringMaybe);
870
+ } else {
871
+ return this.goto(this.parseBasicString);
872
+ }
873
+ }
874
+ parseBasicString() {
875
+ do {
876
+ if (this.char === CHAR_BSOL) {
877
+ return this.call(this.parseEscape, this.recordEscapeReplacement);
878
+ } else if (this.char === CHAR_QUOT) {
879
+ return this.return();
880
+ } else if (this.atEndOfLine()) {
881
+ throw this.error(new TomlError("Unterminated string"));
882
+ } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I) {
883
+ throw this.errorControlCharInString();
884
+ } else {
885
+ this.consume();
886
+ }
887
+ } while (this.nextChar());
888
+ }
889
+ recordEscapeReplacement(replacement) {
890
+ this.state.buf += replacement;
891
+ return this.goto(this.parseBasicString);
892
+ }
893
+ parseMultiStringMaybe() {
894
+ if (this.char === CHAR_QUOT) {
895
+ return this.next(this.parseMultiString);
896
+ } else {
897
+ return this.returnNow();
898
+ }
899
+ }
900
+ parseMultiString() {
901
+ if (this.char === CTRL_M) {
902
+ return null;
903
+ } else if (this.char === CTRL_J) {
904
+ return this.next(this.parseMultiStringContent);
905
+ } else {
906
+ return this.goto(this.parseMultiStringContent);
907
+ }
908
+ }
909
+ parseMultiStringContent() {
910
+ do {
911
+ if (this.char === CHAR_BSOL) {
912
+ return this.call(this.parseMultiEscape, this.recordMultiEscapeReplacement);
913
+ } else if (this.char === CHAR_QUOT) {
914
+ return this.next(this.parseMultiEnd);
915
+ } else if (this.char === Parser.END) {
916
+ throw this.error(new TomlError("Unterminated multi-line string"));
917
+ } else if (this.char === CHAR_DEL || this.char <= CTRL_CHAR_BOUNDARY && this.char !== CTRL_I && this.char !== CTRL_J && this.char !== CTRL_M) {
918
+ throw this.errorControlCharInString();
919
+ } else {
920
+ this.consume();
921
+ }
922
+ } while (this.nextChar());
923
+ }
924
+ errorControlCharInString() {
925
+ let displayCode = "\\u00";
926
+ if (this.char < 16) {
927
+ displayCode += "0";
928
+ }
929
+ displayCode += this.char.toString(16);
930
+ return this.error(new TomlError(`Control characters (codes < 0x1f and 0x7f) are not allowed in strings, use ${displayCode} instead`));
931
+ }
932
+ recordMultiEscapeReplacement(replacement) {
933
+ this.state.buf += replacement;
934
+ return this.goto(this.parseMultiStringContent);
935
+ }
936
+ parseMultiEnd() {
937
+ if (this.char === CHAR_QUOT) {
938
+ return this.next(this.parseMultiEnd2);
939
+ } else {
940
+ this.state.buf += '"';
941
+ return this.goto(this.parseMultiStringContent);
942
+ }
943
+ }
944
+ parseMultiEnd2() {
945
+ if (this.char === CHAR_QUOT) {
946
+ return this.return();
947
+ } else {
948
+ this.state.buf += '""';
949
+ return this.goto(this.parseMultiStringContent);
950
+ }
951
+ }
952
+ parseMultiEscape() {
953
+ if (this.char === CTRL_M || this.char === CTRL_J) {
954
+ return this.next(this.parseMultiTrim);
955
+ } else if (this.char === CHAR_SP || this.char === CTRL_I) {
956
+ return this.next(this.parsePreMultiTrim);
957
+ } else {
958
+ return this.goto(this.parseEscape);
959
+ }
960
+ }
961
+ parsePreMultiTrim() {
962
+ if (this.char === CHAR_SP || this.char === CTRL_I) {
963
+ return null;
964
+ } else if (this.char === CTRL_M || this.char === CTRL_J) {
965
+ return this.next(this.parseMultiTrim);
966
+ } else {
967
+ throw this.error(new TomlError("Can't escape whitespace"));
968
+ }
969
+ }
970
+ parseMultiTrim() {
971
+ if (this.char === CTRL_J || this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M) {
972
+ return null;
973
+ } else {
974
+ return this.returnNow();
975
+ }
976
+ }
977
+ parseEscape() {
978
+ if (this.char in escapes) {
979
+ return this.return(escapes[this.char]);
980
+ } else if (this.char === CHAR_u) {
981
+ return this.call(this.parseSmallUnicode, this.parseUnicodeReturn);
982
+ } else if (this.char === CHAR_U) {
983
+ return this.call(this.parseLargeUnicode, this.parseUnicodeReturn);
984
+ } else {
985
+ throw this.error(new TomlError("Unknown escape character: " + this.char));
986
+ }
987
+ }
988
+ parseUnicodeReturn(char) {
989
+ try {
990
+ const codePoint = parseInt(char, 16);
991
+ if (codePoint >= SURROGATE_FIRST && codePoint <= SURROGATE_LAST) {
992
+ throw this.error(new TomlError("Invalid unicode, character in range 0xD800 - 0xDFFF is reserved"));
993
+ }
994
+ return this.returnNow(String.fromCodePoint(codePoint));
995
+ } catch (err) {
996
+ throw this.error(TomlError.wrap(err));
997
+ }
998
+ }
999
+ parseSmallUnicode() {
1000
+ if (!isHexit(this.char)) {
1001
+ throw this.error(new TomlError("Invalid character in unicode sequence, expected hex"));
1002
+ } else {
1003
+ this.consume();
1004
+ if (this.state.buf.length >= 4) return this.return();
1005
+ }
1006
+ }
1007
+ parseLargeUnicode() {
1008
+ if (!isHexit(this.char)) {
1009
+ throw this.error(new TomlError("Invalid character in unicode sequence, expected hex"));
1010
+ } else {
1011
+ this.consume();
1012
+ if (this.state.buf.length >= 8) return this.return();
1013
+ }
1014
+ }
1015
+ /* NUMBERS */
1016
+ parseNumberSign() {
1017
+ this.consume();
1018
+ return this.next(this.parseMaybeSignedInfOrNan);
1019
+ }
1020
+ parseMaybeSignedInfOrNan() {
1021
+ if (this.char === CHAR_i) {
1022
+ return this.next(this.parseInf);
1023
+ } else if (this.char === CHAR_n) {
1024
+ return this.next(this.parseNan);
1025
+ } else {
1026
+ return this.callNow(this.parseNoUnder, this.parseNumberIntegerStart);
1027
+ }
1028
+ }
1029
+ parseNumberIntegerStart() {
1030
+ if (this.char === CHAR_0) {
1031
+ this.consume();
1032
+ return this.next(this.parseNumberIntegerExponentOrDecimal);
1033
+ } else {
1034
+ return this.goto(this.parseNumberInteger);
1035
+ }
1036
+ }
1037
+ parseNumberIntegerExponentOrDecimal() {
1038
+ if (this.char === CHAR_PERIOD) {
1039
+ this.consume();
1040
+ return this.call(this.parseNoUnder, this.parseNumberFloat);
1041
+ } else if (this.char === CHAR_E || this.char === CHAR_e) {
1042
+ this.consume();
1043
+ return this.next(this.parseNumberExponentSign);
1044
+ } else {
1045
+ return this.returnNow(Integer(this.state.buf));
1046
+ }
1047
+ }
1048
+ parseNumberInteger() {
1049
+ if (isDigit(this.char)) {
1050
+ this.consume();
1051
+ } else if (this.char === CHAR_LOWBAR) {
1052
+ return this.call(this.parseNoUnder);
1053
+ } else if (this.char === CHAR_E || this.char === CHAR_e) {
1054
+ this.consume();
1055
+ return this.next(this.parseNumberExponentSign);
1056
+ } else if (this.char === CHAR_PERIOD) {
1057
+ this.consume();
1058
+ return this.call(this.parseNoUnder, this.parseNumberFloat);
1059
+ } else {
1060
+ const result = Integer(this.state.buf);
1061
+ if (result.isNaN()) {
1062
+ throw this.error(new TomlError("Invalid number"));
1063
+ } else {
1064
+ return this.returnNow(result);
1065
+ }
1066
+ }
1067
+ }
1068
+ parseNoUnder() {
1069
+ if (this.char === CHAR_LOWBAR || this.char === CHAR_PERIOD || this.char === CHAR_E || this.char === CHAR_e) {
1070
+ throw this.error(new TomlError("Unexpected character, expected digit"));
1071
+ } else if (this.atEndOfWord()) {
1072
+ throw this.error(new TomlError("Incomplete number"));
1073
+ }
1074
+ return this.returnNow();
1075
+ }
1076
+ parseNoUnderHexOctBinLiteral() {
1077
+ if (this.char === CHAR_LOWBAR || this.char === CHAR_PERIOD) {
1078
+ throw this.error(new TomlError("Unexpected character, expected digit"));
1079
+ } else if (this.atEndOfWord()) {
1080
+ throw this.error(new TomlError("Incomplete number"));
1081
+ }
1082
+ return this.returnNow();
1083
+ }
1084
+ parseNumberFloat() {
1085
+ if (this.char === CHAR_LOWBAR) {
1086
+ return this.call(this.parseNoUnder, this.parseNumberFloat);
1087
+ } else if (isDigit(this.char)) {
1088
+ this.consume();
1089
+ } else if (this.char === CHAR_E || this.char === CHAR_e) {
1090
+ this.consume();
1091
+ return this.next(this.parseNumberExponentSign);
1092
+ } else {
1093
+ return this.returnNow(Float(this.state.buf));
1094
+ }
1095
+ }
1096
+ parseNumberExponentSign() {
1097
+ if (isDigit(this.char)) {
1098
+ return this.goto(this.parseNumberExponent);
1099
+ } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) {
1100
+ this.consume();
1101
+ this.call(this.parseNoUnder, this.parseNumberExponent);
1102
+ } else {
1103
+ throw this.error(new TomlError("Unexpected character, expected -, + or digit"));
1104
+ }
1105
+ }
1106
+ parseNumberExponent() {
1107
+ if (isDigit(this.char)) {
1108
+ this.consume();
1109
+ } else if (this.char === CHAR_LOWBAR) {
1110
+ return this.call(this.parseNoUnder);
1111
+ } else {
1112
+ return this.returnNow(Float(this.state.buf));
1113
+ }
1114
+ }
1115
+ /* NUMBERS or DATETIMES */
1116
+ parseNumberOrDateTime() {
1117
+ if (this.char === CHAR_0) {
1118
+ this.consume();
1119
+ return this.next(this.parseNumberBaseOrDateTime);
1120
+ } else {
1121
+ return this.goto(this.parseNumberOrDateTimeOnly);
1122
+ }
1123
+ }
1124
+ parseNumberOrDateTimeOnly() {
1125
+ if (this.char === CHAR_LOWBAR) {
1126
+ return this.call(this.parseNoUnder, this.parseNumberInteger);
1127
+ } else if (isDigit(this.char)) {
1128
+ this.consume();
1129
+ if (this.state.buf.length > 4) this.next(this.parseNumberInteger);
1130
+ } else if (this.char === CHAR_E || this.char === CHAR_e) {
1131
+ this.consume();
1132
+ return this.next(this.parseNumberExponentSign);
1133
+ } else if (this.char === CHAR_PERIOD) {
1134
+ this.consume();
1135
+ return this.call(this.parseNoUnder, this.parseNumberFloat);
1136
+ } else if (this.char === CHAR_HYPHEN) {
1137
+ return this.goto(this.parseDateTime);
1138
+ } else if (this.char === CHAR_COLON) {
1139
+ return this.goto(this.parseOnlyTimeHour);
1140
+ } else {
1141
+ return this.returnNow(Integer(this.state.buf));
1142
+ }
1143
+ }
1144
+ parseDateTimeOnly() {
1145
+ if (this.state.buf.length < 4) {
1146
+ if (isDigit(this.char)) {
1147
+ return this.consume();
1148
+ } else if (this.char === CHAR_COLON) {
1149
+ return this.goto(this.parseOnlyTimeHour);
1150
+ } else {
1151
+ throw this.error(new TomlError("Expected digit while parsing year part of a date"));
1152
+ }
1153
+ } else {
1154
+ if (this.char === CHAR_HYPHEN) {
1155
+ return this.goto(this.parseDateTime);
1156
+ } else {
1157
+ throw this.error(new TomlError("Expected hyphen (-) while parsing year part of date"));
1158
+ }
1159
+ }
1160
+ }
1161
+ parseNumberBaseOrDateTime() {
1162
+ if (this.char === CHAR_b) {
1163
+ this.consume();
1164
+ return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerBin);
1165
+ } else if (this.char === CHAR_o) {
1166
+ this.consume();
1167
+ return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerOct);
1168
+ } else if (this.char === CHAR_x) {
1169
+ this.consume();
1170
+ return this.call(this.parseNoUnderHexOctBinLiteral, this.parseIntegerHex);
1171
+ } else if (this.char === CHAR_PERIOD) {
1172
+ return this.goto(this.parseNumberInteger);
1173
+ } else if (isDigit(this.char)) {
1174
+ return this.goto(this.parseDateTimeOnly);
1175
+ } else {
1176
+ return this.returnNow(Integer(this.state.buf));
1177
+ }
1178
+ }
1179
+ parseIntegerHex() {
1180
+ if (isHexit(this.char)) {
1181
+ this.consume();
1182
+ } else if (this.char === CHAR_LOWBAR) {
1183
+ return this.call(this.parseNoUnderHexOctBinLiteral);
1184
+ } else {
1185
+ const result = Integer(this.state.buf);
1186
+ if (result.isNaN()) {
1187
+ throw this.error(new TomlError("Invalid number"));
1188
+ } else {
1189
+ return this.returnNow(result);
1190
+ }
1191
+ }
1192
+ }
1193
+ parseIntegerOct() {
1194
+ if (isOctit(this.char)) {
1195
+ this.consume();
1196
+ } else if (this.char === CHAR_LOWBAR) {
1197
+ return this.call(this.parseNoUnderHexOctBinLiteral);
1198
+ } else {
1199
+ const result = Integer(this.state.buf);
1200
+ if (result.isNaN()) {
1201
+ throw this.error(new TomlError("Invalid number"));
1202
+ } else {
1203
+ return this.returnNow(result);
1204
+ }
1205
+ }
1206
+ }
1207
+ parseIntegerBin() {
1208
+ if (isBit(this.char)) {
1209
+ this.consume();
1210
+ } else if (this.char === CHAR_LOWBAR) {
1211
+ return this.call(this.parseNoUnderHexOctBinLiteral);
1212
+ } else {
1213
+ const result = Integer(this.state.buf);
1214
+ if (result.isNaN()) {
1215
+ throw this.error(new TomlError("Invalid number"));
1216
+ } else {
1217
+ return this.returnNow(result);
1218
+ }
1219
+ }
1220
+ }
1221
+ /* DATETIME */
1222
+ parseDateTime() {
1223
+ if (this.state.buf.length < 4) {
1224
+ throw this.error(new TomlError("Years less than 1000 must be zero padded to four characters"));
1225
+ }
1226
+ this.state.result = this.state.buf;
1227
+ this.state.buf = "";
1228
+ return this.next(this.parseDateMonth);
1229
+ }
1230
+ parseDateMonth() {
1231
+ if (this.char === CHAR_HYPHEN) {
1232
+ if (this.state.buf.length < 2) {
1233
+ throw this.error(new TomlError("Months less than 10 must be zero padded to two characters"));
1234
+ }
1235
+ this.state.result += "-" + this.state.buf;
1236
+ this.state.buf = "";
1237
+ return this.next(this.parseDateDay);
1238
+ } else if (isDigit(this.char)) {
1239
+ this.consume();
1240
+ } else {
1241
+ throw this.error(new TomlError("Incomplete datetime"));
1242
+ }
1243
+ }
1244
+ parseDateDay() {
1245
+ if (this.char === CHAR_T || this.char === CHAR_SP) {
1246
+ if (this.state.buf.length < 2) {
1247
+ throw this.error(new TomlError("Days less than 10 must be zero padded to two characters"));
1248
+ }
1249
+ this.state.result += "-" + this.state.buf;
1250
+ this.state.buf = "";
1251
+ return this.next(this.parseStartTimeHour);
1252
+ } else if (this.atEndOfWord()) {
1253
+ return this.returnNow(createDate(this.state.result + "-" + this.state.buf));
1254
+ } else if (isDigit(this.char)) {
1255
+ this.consume();
1256
+ } else {
1257
+ throw this.error(new TomlError("Incomplete datetime"));
1258
+ }
1259
+ }
1260
+ parseStartTimeHour() {
1261
+ if (this.atEndOfWord()) {
1262
+ return this.returnNow(createDate(this.state.result));
1263
+ } else {
1264
+ return this.goto(this.parseTimeHour);
1265
+ }
1266
+ }
1267
+ parseTimeHour() {
1268
+ if (this.char === CHAR_COLON) {
1269
+ if (this.state.buf.length < 2) {
1270
+ throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters"));
1271
+ }
1272
+ this.state.result += "T" + this.state.buf;
1273
+ this.state.buf = "";
1274
+ return this.next(this.parseTimeMin);
1275
+ } else if (isDigit(this.char)) {
1276
+ this.consume();
1277
+ } else {
1278
+ throw this.error(new TomlError("Incomplete datetime"));
1279
+ }
1280
+ }
1281
+ parseTimeMin() {
1282
+ if (this.state.buf.length < 2 && isDigit(this.char)) {
1283
+ this.consume();
1284
+ } else if (this.state.buf.length === 2 && this.char === CHAR_COLON) {
1285
+ this.state.result += ":" + this.state.buf;
1286
+ this.state.buf = "";
1287
+ return this.next(this.parseTimeSec);
1288
+ } else {
1289
+ throw this.error(new TomlError("Incomplete datetime"));
1290
+ }
1291
+ }
1292
+ parseTimeSec() {
1293
+ if (isDigit(this.char)) {
1294
+ this.consume();
1295
+ if (this.state.buf.length === 2) {
1296
+ this.state.result += ":" + this.state.buf;
1297
+ this.state.buf = "";
1298
+ return this.next(this.parseTimeZoneOrFraction);
1299
+ }
1300
+ } else {
1301
+ throw this.error(new TomlError("Incomplete datetime"));
1302
+ }
1303
+ }
1304
+ parseOnlyTimeHour() {
1305
+ if (this.char === CHAR_COLON) {
1306
+ if (this.state.buf.length < 2) {
1307
+ throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters"));
1308
+ }
1309
+ this.state.result = this.state.buf;
1310
+ this.state.buf = "";
1311
+ return this.next(this.parseOnlyTimeMin);
1312
+ } else {
1313
+ throw this.error(new TomlError("Incomplete time"));
1314
+ }
1315
+ }
1316
+ parseOnlyTimeMin() {
1317
+ if (this.state.buf.length < 2 && isDigit(this.char)) {
1318
+ this.consume();
1319
+ } else if (this.state.buf.length === 2 && this.char === CHAR_COLON) {
1320
+ this.state.result += ":" + this.state.buf;
1321
+ this.state.buf = "";
1322
+ return this.next(this.parseOnlyTimeSec);
1323
+ } else {
1324
+ throw this.error(new TomlError("Incomplete time"));
1325
+ }
1326
+ }
1327
+ parseOnlyTimeSec() {
1328
+ if (isDigit(this.char)) {
1329
+ this.consume();
1330
+ if (this.state.buf.length === 2) {
1331
+ return this.next(this.parseOnlyTimeFractionMaybe);
1332
+ }
1333
+ } else {
1334
+ throw this.error(new TomlError("Incomplete time"));
1335
+ }
1336
+ }
1337
+ parseOnlyTimeFractionMaybe() {
1338
+ this.state.result += ":" + this.state.buf;
1339
+ if (this.char === CHAR_PERIOD) {
1340
+ this.state.buf = "";
1341
+ this.next(this.parseOnlyTimeFraction);
1342
+ } else {
1343
+ return this.return(createTime(this.state.result));
1344
+ }
1345
+ }
1346
+ parseOnlyTimeFraction() {
1347
+ if (isDigit(this.char)) {
1348
+ this.consume();
1349
+ } else if (this.atEndOfWord()) {
1350
+ if (this.state.buf.length === 0) throw this.error(new TomlError("Expected digit in milliseconds"));
1351
+ return this.returnNow(createTime(this.state.result + "." + this.state.buf));
1352
+ } else {
1353
+ throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z"));
1354
+ }
1355
+ }
1356
+ parseTimeZoneOrFraction() {
1357
+ if (this.char === CHAR_PERIOD) {
1358
+ this.consume();
1359
+ this.next(this.parseDateTimeFraction);
1360
+ } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) {
1361
+ this.consume();
1362
+ this.next(this.parseTimeZoneHour);
1363
+ } else if (this.char === CHAR_Z) {
1364
+ this.consume();
1365
+ return this.return(createDateTime(this.state.result + this.state.buf));
1366
+ } else if (this.atEndOfWord()) {
1367
+ return this.returnNow(createDateTimeFloat(this.state.result + this.state.buf));
1368
+ } else {
1369
+ throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z"));
1370
+ }
1371
+ }
1372
+ parseDateTimeFraction() {
1373
+ if (isDigit(this.char)) {
1374
+ this.consume();
1375
+ } else if (this.state.buf.length === 1) {
1376
+ throw this.error(new TomlError("Expected digit in milliseconds"));
1377
+ } else if (this.char === CHAR_HYPHEN || this.char === CHAR_PLUS) {
1378
+ this.consume();
1379
+ this.next(this.parseTimeZoneHour);
1380
+ } else if (this.char === CHAR_Z) {
1381
+ this.consume();
1382
+ return this.return(createDateTime(this.state.result + this.state.buf));
1383
+ } else if (this.atEndOfWord()) {
1384
+ return this.returnNow(createDateTimeFloat(this.state.result + this.state.buf));
1385
+ } else {
1386
+ throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z"));
1387
+ }
1388
+ }
1389
+ parseTimeZoneHour() {
1390
+ if (isDigit(this.char)) {
1391
+ this.consume();
1392
+ if (/\d\d$/.test(this.state.buf)) return this.next(this.parseTimeZoneSep);
1393
+ } else {
1394
+ throw this.error(new TomlError("Unexpected character in datetime, expected digit"));
1395
+ }
1396
+ }
1397
+ parseTimeZoneSep() {
1398
+ if (this.char === CHAR_COLON) {
1399
+ this.consume();
1400
+ this.next(this.parseTimeZoneMin);
1401
+ } else {
1402
+ throw this.error(new TomlError("Unexpected character in datetime, expected colon"));
1403
+ }
1404
+ }
1405
+ parseTimeZoneMin() {
1406
+ if (isDigit(this.char)) {
1407
+ this.consume();
1408
+ if (/\d\d$/.test(this.state.buf)) return this.return(createDateTime(this.state.result + this.state.buf));
1409
+ } else {
1410
+ throw this.error(new TomlError("Unexpected character in datetime, expected digit"));
1411
+ }
1412
+ }
1413
+ /* BOOLEAN */
1414
+ parseBoolean() {
1415
+ if (this.char === CHAR_t) {
1416
+ this.consume();
1417
+ return this.next(this.parseTrue_r);
1418
+ } else if (this.char === CHAR_f) {
1419
+ this.consume();
1420
+ return this.next(this.parseFalse_a);
1421
+ }
1422
+ }
1423
+ parseTrue_r() {
1424
+ if (this.char === CHAR_r) {
1425
+ this.consume();
1426
+ return this.next(this.parseTrue_u);
1427
+ } else {
1428
+ throw this.error(new TomlError("Invalid boolean, expected true or false"));
1429
+ }
1430
+ }
1431
+ parseTrue_u() {
1432
+ if (this.char === CHAR_u) {
1433
+ this.consume();
1434
+ return this.next(this.parseTrue_e);
1435
+ } else {
1436
+ throw this.error(new TomlError("Invalid boolean, expected true or false"));
1437
+ }
1438
+ }
1439
+ parseTrue_e() {
1440
+ if (this.char === CHAR_e) {
1441
+ return this.return(true);
1442
+ } else {
1443
+ throw this.error(new TomlError("Invalid boolean, expected true or false"));
1444
+ }
1445
+ }
1446
+ parseFalse_a() {
1447
+ if (this.char === CHAR_a) {
1448
+ this.consume();
1449
+ return this.next(this.parseFalse_l);
1450
+ } else {
1451
+ throw this.error(new TomlError("Invalid boolean, expected true or false"));
1452
+ }
1453
+ }
1454
+ parseFalse_l() {
1455
+ if (this.char === CHAR_l) {
1456
+ this.consume();
1457
+ return this.next(this.parseFalse_s);
1458
+ } else {
1459
+ throw this.error(new TomlError("Invalid boolean, expected true or false"));
1460
+ }
1461
+ }
1462
+ parseFalse_s() {
1463
+ if (this.char === CHAR_s) {
1464
+ this.consume();
1465
+ return this.next(this.parseFalse_e);
1466
+ } else {
1467
+ throw this.error(new TomlError("Invalid boolean, expected true or false"));
1468
+ }
1469
+ }
1470
+ parseFalse_e() {
1471
+ if (this.char === CHAR_e) {
1472
+ return this.return(false);
1473
+ } else {
1474
+ throw this.error(new TomlError("Invalid boolean, expected true or false"));
1475
+ }
1476
+ }
1477
+ /* INLINE LISTS */
1478
+ parseInlineList() {
1479
+ if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M || this.char === CTRL_J) {
1480
+ return null;
1481
+ } else if (this.char === Parser.END) {
1482
+ throw this.error(new TomlError("Unterminated inline array"));
1483
+ } else if (this.char === CHAR_NUM) {
1484
+ return this.call(this.parseComment);
1485
+ } else if (this.char === CHAR_RSQB) {
1486
+ return this.return(this.state.resultArr || InlineList());
1487
+ } else {
1488
+ return this.callNow(this.parseValue, this.recordInlineListValue);
1489
+ }
1490
+ }
1491
+ recordInlineListValue(value) {
1492
+ if (this.state.resultArr) {
1493
+ const listType = this.state.resultArr[_contentType];
1494
+ const valueType = tomlType(value);
1495
+ if (listType !== valueType) {
1496
+ throw this.error(new TomlError(`Inline lists must be a single type, not a mix of ${listType} and ${valueType}`));
1497
+ }
1498
+ } else {
1499
+ this.state.resultArr = InlineList(tomlType(value));
1500
+ }
1501
+ if (isFloat(value) || isInteger(value)) {
1502
+ this.state.resultArr.push(value.valueOf());
1503
+ } else {
1504
+ this.state.resultArr.push(value);
1505
+ }
1506
+ return this.goto(this.parseInlineListNext);
1507
+ }
1508
+ parseInlineListNext() {
1509
+ if (this.char === CHAR_SP || this.char === CTRL_I || this.char === CTRL_M || this.char === CTRL_J) {
1510
+ return null;
1511
+ } else if (this.char === CHAR_NUM) {
1512
+ return this.call(this.parseComment);
1513
+ } else if (this.char === CHAR_COMMA) {
1514
+ return this.next(this.parseInlineList);
1515
+ } else if (this.char === CHAR_RSQB) {
1516
+ return this.goto(this.parseInlineList);
1517
+ } else {
1518
+ throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])"));
1519
+ }
1520
+ }
1521
+ /* INLINE TABLE */
1522
+ parseInlineTable() {
1523
+ if (this.char === CHAR_SP || this.char === CTRL_I) {
1524
+ return null;
1525
+ } else if (this.char === Parser.END || this.char === CHAR_NUM || this.char === CTRL_J || this.char === CTRL_M) {
1526
+ throw this.error(new TomlError("Unterminated inline array"));
1527
+ } else if (this.char === CHAR_RCUB) {
1528
+ return this.return(this.state.resultTable || InlineTable());
1529
+ } else {
1530
+ if (!this.state.resultTable) this.state.resultTable = InlineTable();
1531
+ return this.callNow(this.parseAssign, this.recordInlineTableValue);
1532
+ }
1533
+ }
1534
+ recordInlineTableValue(kv) {
1535
+ let target = this.state.resultTable;
1536
+ let finalKey = kv.key.pop();
1537
+ for (let kw of kv.key) {
1538
+ if (hasKey(target, kw) && (!isTable(target[kw]) || target[kw][_declared])) {
1539
+ throw this.error(new TomlError("Can't redefine existing key"));
1540
+ }
1541
+ target = target[kw] = target[kw] || Table();
1542
+ }
1543
+ if (hasKey(target, finalKey)) {
1544
+ throw this.error(new TomlError("Can't redefine existing key"));
1545
+ }
1546
+ if (isInteger(kv.value) || isFloat(kv.value)) {
1547
+ target[finalKey] = kv.value.valueOf();
1548
+ } else {
1549
+ target[finalKey] = kv.value;
1550
+ }
1551
+ return this.goto(this.parseInlineTableNext);
1552
+ }
1553
+ parseInlineTableNext() {
1554
+ if (this.char === CHAR_SP || this.char === CTRL_I) {
1555
+ return null;
1556
+ } else if (this.char === Parser.END || this.char === CHAR_NUM || this.char === CTRL_J || this.char === CTRL_M) {
1557
+ throw this.error(new TomlError("Unterminated inline array"));
1558
+ } else if (this.char === CHAR_COMMA) {
1559
+ return this.next(this.parseInlineTable);
1560
+ } else if (this.char === CHAR_RCUB) {
1561
+ return this.goto(this.parseInlineTable);
1562
+ } else {
1563
+ throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])"));
1564
+ }
1565
+ }
1566
+ }
1567
+ return TOMLParser;
1568
+ }
1569
+ }
1570
+ });
1571
+
1572
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/parse-pretty-error.js
1573
+ var require_parse_pretty_error = __commonJS({
1574
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/parse-pretty-error.js"(exports2, module2) {
1575
+ "use strict";
1576
+ module2.exports = prettyError;
1577
+ function prettyError(err, buf) {
1578
+ if (err.pos == null || err.line == null) return err;
1579
+ let msg = err.message;
1580
+ msg += ` at row ${err.line + 1}, col ${err.col + 1}, pos ${err.pos}:
1581
+ `;
1582
+ if (buf && buf.split) {
1583
+ const lines = buf.split(/\n/);
1584
+ const lineNumWidth = String(Math.min(lines.length, err.line + 3)).length;
1585
+ let linePadding = " ";
1586
+ while (linePadding.length < lineNumWidth) linePadding += " ";
1587
+ for (let ii = Math.max(0, err.line - 1); ii < Math.min(lines.length, err.line + 2); ++ii) {
1588
+ let lineNum = String(ii + 1);
1589
+ if (lineNum.length < lineNumWidth) lineNum = " " + lineNum;
1590
+ if (err.line === ii) {
1591
+ msg += lineNum + "> " + lines[ii] + "\n";
1592
+ msg += linePadding + " ";
1593
+ for (let hh = 0; hh < err.col; ++hh) {
1594
+ msg += " ";
1595
+ }
1596
+ msg += "^\n";
1597
+ } else {
1598
+ msg += lineNum + ": " + lines[ii] + "\n";
1599
+ }
1600
+ }
1601
+ }
1602
+ err.message = msg + "\n";
1603
+ return err;
1604
+ }
1605
+ }
1606
+ });
1607
+
1608
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/parse-string.js
1609
+ var require_parse_string = __commonJS({
1610
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/parse-string.js"(exports2, module2) {
1611
+ "use strict";
1612
+ module2.exports = parseString;
1613
+ var TOMLParser = require_toml_parser();
1614
+ var prettyError = require_parse_pretty_error();
1615
+ function parseString(str) {
1616
+ if (global.Buffer && global.Buffer.isBuffer(str)) {
1617
+ str = str.toString("utf8");
1618
+ }
1619
+ const parser = new TOMLParser();
1620
+ try {
1621
+ parser.parse(str);
1622
+ return parser.finish();
1623
+ } catch (err) {
1624
+ throw prettyError(err, str);
1625
+ }
1626
+ }
1627
+ }
1628
+ });
1629
+
1630
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/parse-async.js
1631
+ var require_parse_async = __commonJS({
1632
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/parse-async.js"(exports2, module2) {
1633
+ "use strict";
1634
+ module2.exports = parseAsync;
1635
+ var TOMLParser = require_toml_parser();
1636
+ var prettyError = require_parse_pretty_error();
1637
+ function parseAsync(str, opts) {
1638
+ if (!opts) opts = {};
1639
+ const index = 0;
1640
+ const blocksize = opts.blocksize || 40960;
1641
+ const parser = new TOMLParser();
1642
+ return new Promise((resolve, reject) => {
1643
+ setImmediate(parseAsyncNext, index, blocksize, resolve, reject);
1644
+ });
1645
+ function parseAsyncNext(index2, blocksize2, resolve, reject) {
1646
+ if (index2 >= str.length) {
1647
+ try {
1648
+ return resolve(parser.finish());
1649
+ } catch (err) {
1650
+ return reject(prettyError(err, str));
1651
+ }
1652
+ }
1653
+ try {
1654
+ parser.parse(str.slice(index2, index2 + blocksize2));
1655
+ setImmediate(parseAsyncNext, index2 + blocksize2, blocksize2, resolve, reject);
1656
+ } catch (err) {
1657
+ reject(prettyError(err, str));
1658
+ }
1659
+ }
1660
+ }
1661
+ }
1662
+ });
1663
+
1664
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/parse-stream.js
1665
+ var require_parse_stream = __commonJS({
1666
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/parse-stream.js"(exports2, module2) {
1667
+ "use strict";
1668
+ module2.exports = parseStream;
1669
+ var stream = require("stream");
1670
+ var TOMLParser = require_toml_parser();
1671
+ function parseStream(stm) {
1672
+ if (stm) {
1673
+ return parseReadable(stm);
1674
+ } else {
1675
+ return parseTransform(stm);
1676
+ }
1677
+ }
1678
+ function parseReadable(stm) {
1679
+ const parser = new TOMLParser();
1680
+ stm.setEncoding("utf8");
1681
+ return new Promise((resolve, reject) => {
1682
+ let readable;
1683
+ let ended = false;
1684
+ let errored = false;
1685
+ function finish() {
1686
+ ended = true;
1687
+ if (readable) return;
1688
+ try {
1689
+ resolve(parser.finish());
1690
+ } catch (err) {
1691
+ reject(err);
1692
+ }
1693
+ }
1694
+ function error(err) {
1695
+ errored = true;
1696
+ reject(err);
1697
+ }
1698
+ stm.once("end", finish);
1699
+ stm.once("error", error);
1700
+ readNext();
1701
+ function readNext() {
1702
+ readable = true;
1703
+ let data;
1704
+ while ((data = stm.read()) !== null) {
1705
+ try {
1706
+ parser.parse(data);
1707
+ } catch (err) {
1708
+ return error(err);
1709
+ }
1710
+ }
1711
+ readable = false;
1712
+ if (ended) return finish();
1713
+ if (errored) return;
1714
+ stm.once("readable", readNext);
1715
+ }
1716
+ });
1717
+ }
1718
+ function parseTransform() {
1719
+ const parser = new TOMLParser();
1720
+ return new stream.Transform({
1721
+ objectMode: true,
1722
+ transform(chunk, encoding, cb) {
1723
+ try {
1724
+ parser.parse(chunk.toString(encoding));
1725
+ } catch (err) {
1726
+ this.emit("error", err);
1727
+ }
1728
+ cb();
1729
+ },
1730
+ flush(cb) {
1731
+ try {
1732
+ this.push(parser.finish());
1733
+ } catch (err) {
1734
+ this.emit("error", err);
1735
+ }
1736
+ cb();
1737
+ }
1738
+ });
1739
+ }
1740
+ }
1741
+ });
1742
+
1743
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/parse.js
1744
+ var require_parse = __commonJS({
1745
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/parse.js"(exports2, module2) {
1746
+ "use strict";
1747
+ module2.exports = require_parse_string();
1748
+ module2.exports.async = require_parse_async();
1749
+ module2.exports.stream = require_parse_stream();
1750
+ module2.exports.prettyError = require_parse_pretty_error();
1751
+ }
1752
+ });
1753
+
1754
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/stringify.js
1755
+ var require_stringify = __commonJS({
1756
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/stringify.js"(exports2, module2) {
1757
+ "use strict";
1758
+ module2.exports = stringify;
1759
+ module2.exports.value = stringifyInline;
1760
+ function stringify(obj) {
1761
+ if (obj === null) throw typeError("null");
1762
+ if (obj === void 0) throw typeError("undefined");
1763
+ if (typeof obj !== "object") throw typeError(typeof obj);
1764
+ if (typeof obj.toJSON === "function") obj = obj.toJSON();
1765
+ if (obj == null) return null;
1766
+ const type = tomlType2(obj);
1767
+ if (type !== "table") throw typeError(type);
1768
+ return stringifyObject("", "", obj);
1769
+ }
1770
+ function typeError(type) {
1771
+ return new Error("Can only stringify objects, not " + type);
1772
+ }
1773
+ function arrayOneTypeError() {
1774
+ return new Error("Array values can't have mixed types");
1775
+ }
1776
+ function getInlineKeys(obj) {
1777
+ return Object.keys(obj).filter((key) => isInline(obj[key]));
1778
+ }
1779
+ function getComplexKeys(obj) {
1780
+ return Object.keys(obj).filter((key) => !isInline(obj[key]));
1781
+ }
1782
+ function toJSON(obj) {
1783
+ let nobj = Array.isArray(obj) ? [] : Object.prototype.hasOwnProperty.call(obj, "__proto__") ? { ["__proto__"]: void 0 } : {};
1784
+ for (let prop of Object.keys(obj)) {
1785
+ if (obj[prop] && typeof obj[prop].toJSON === "function" && !("toISOString" in obj[prop])) {
1786
+ nobj[prop] = obj[prop].toJSON();
1787
+ } else {
1788
+ nobj[prop] = obj[prop];
1789
+ }
1790
+ }
1791
+ return nobj;
1792
+ }
1793
+ function stringifyObject(prefix, indent, obj) {
1794
+ obj = toJSON(obj);
1795
+ var inlineKeys;
1796
+ var complexKeys;
1797
+ inlineKeys = getInlineKeys(obj);
1798
+ complexKeys = getComplexKeys(obj);
1799
+ var result = [];
1800
+ var inlineIndent = indent || "";
1801
+ inlineKeys.forEach((key) => {
1802
+ var type = tomlType2(obj[key]);
1803
+ if (type !== "undefined" && type !== "null") {
1804
+ result.push(inlineIndent + stringifyKey(key) + " = " + stringifyAnyInline(obj[key], true));
1805
+ }
1806
+ });
1807
+ if (result.length > 0) result.push("");
1808
+ var complexIndent = prefix && inlineKeys.length > 0 ? indent + " " : "";
1809
+ complexKeys.forEach((key) => {
1810
+ result.push(stringifyComplex(prefix, complexIndent, key, obj[key]));
1811
+ });
1812
+ return result.join("\n");
1813
+ }
1814
+ function isInline(value) {
1815
+ switch (tomlType2(value)) {
1816
+ case "undefined":
1817
+ case "null":
1818
+ case "integer":
1819
+ case "nan":
1820
+ case "float":
1821
+ case "boolean":
1822
+ case "string":
1823
+ case "datetime":
1824
+ return true;
1825
+ case "array":
1826
+ return value.length === 0 || tomlType2(value[0]) !== "table";
1827
+ case "table":
1828
+ return Object.keys(value).length === 0;
1829
+ /* istanbul ignore next */
1830
+ default:
1831
+ return false;
1832
+ }
1833
+ }
1834
+ function tomlType2(value) {
1835
+ if (value === void 0) {
1836
+ return "undefined";
1837
+ } else if (value === null) {
1838
+ return "null";
1839
+ } else if (typeof value === "bigint" || Number.isInteger(value) && !Object.is(value, -0)) {
1840
+ return "integer";
1841
+ } else if (typeof value === "number") {
1842
+ return "float";
1843
+ } else if (typeof value === "boolean") {
1844
+ return "boolean";
1845
+ } else if (typeof value === "string") {
1846
+ return "string";
1847
+ } else if ("toISOString" in value) {
1848
+ return isNaN(value) ? "undefined" : "datetime";
1849
+ } else if (Array.isArray(value)) {
1850
+ return "array";
1851
+ } else {
1852
+ return "table";
1853
+ }
1854
+ }
1855
+ function stringifyKey(key) {
1856
+ var keyStr = String(key);
1857
+ if (/^[-A-Za-z0-9_]+$/.test(keyStr)) {
1858
+ return keyStr;
1859
+ } else {
1860
+ return stringifyBasicString(keyStr);
1861
+ }
1862
+ }
1863
+ function stringifyBasicString(str) {
1864
+ return '"' + escapeString(str).replace(/"/g, '\\"') + '"';
1865
+ }
1866
+ function stringifyLiteralString(str) {
1867
+ return "'" + str + "'";
1868
+ }
1869
+ function numpad(num, str) {
1870
+ while (str.length < num) str = "0" + str;
1871
+ return str;
1872
+ }
1873
+ function escapeString(str) {
1874
+ return str.replace(/\\/g, "\\\\").replace(/[\b]/g, "\\b").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\f/g, "\\f").replace(/\r/g, "\\r").replace(/([\u0000-\u001f\u007f])/, (c) => "\\u" + numpad(4, c.codePointAt(0).toString(16)));
1875
+ }
1876
+ function stringifyMultilineString(str) {
1877
+ let escaped = str.split(/\n/).map((str2) => {
1878
+ return escapeString(str2).replace(/"(?="")/g, '\\"');
1879
+ }).join("\n");
1880
+ if (escaped.slice(-1) === '"') escaped += "\\\n";
1881
+ return '"""\n' + escaped + '"""';
1882
+ }
1883
+ function stringifyAnyInline(value, multilineOk) {
1884
+ let type = tomlType2(value);
1885
+ if (type === "string") {
1886
+ if (multilineOk && /\n/.test(value)) {
1887
+ type = "string-multiline";
1888
+ } else if (!/[\b\t\n\f\r']/.test(value) && /"/.test(value)) {
1889
+ type = "string-literal";
1890
+ }
1891
+ }
1892
+ return stringifyInline(value, type);
1893
+ }
1894
+ function stringifyInline(value, type) {
1895
+ if (!type) type = tomlType2(value);
1896
+ switch (type) {
1897
+ case "string-multiline":
1898
+ return stringifyMultilineString(value);
1899
+ case "string":
1900
+ return stringifyBasicString(value);
1901
+ case "string-literal":
1902
+ return stringifyLiteralString(value);
1903
+ case "integer":
1904
+ return stringifyInteger(value);
1905
+ case "float":
1906
+ return stringifyFloat(value);
1907
+ case "boolean":
1908
+ return stringifyBoolean(value);
1909
+ case "datetime":
1910
+ return stringifyDatetime(value);
1911
+ case "array":
1912
+ return stringifyInlineArray(value.filter((_) => tomlType2(_) !== "null" && tomlType2(_) !== "undefined" && tomlType2(_) !== "nan"));
1913
+ case "table":
1914
+ return stringifyInlineTable(value);
1915
+ /* istanbul ignore next */
1916
+ default:
1917
+ throw typeError(type);
1918
+ }
1919
+ }
1920
+ function stringifyInteger(value) {
1921
+ return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, "_");
1922
+ }
1923
+ function stringifyFloat(value) {
1924
+ if (value === Infinity) {
1925
+ return "inf";
1926
+ } else if (value === -Infinity) {
1927
+ return "-inf";
1928
+ } else if (Object.is(value, NaN)) {
1929
+ return "nan";
1930
+ } else if (Object.is(value, -0)) {
1931
+ return "-0.0";
1932
+ }
1933
+ var chunks = String(value).split(".");
1934
+ var int = chunks[0];
1935
+ var dec = chunks[1] || 0;
1936
+ return stringifyInteger(int) + "." + dec;
1937
+ }
1938
+ function stringifyBoolean(value) {
1939
+ return String(value);
1940
+ }
1941
+ function stringifyDatetime(value) {
1942
+ return value.toISOString();
1943
+ }
1944
+ function isNumber(type) {
1945
+ return type === "float" || type === "integer";
1946
+ }
1947
+ function arrayType(values) {
1948
+ var contentType = tomlType2(values[0]);
1949
+ if (values.every((_) => tomlType2(_) === contentType)) return contentType;
1950
+ if (values.every((_) => isNumber(tomlType2(_)))) return "float";
1951
+ return "mixed";
1952
+ }
1953
+ function validateArray(values) {
1954
+ const type = arrayType(values);
1955
+ if (type === "mixed") {
1956
+ throw arrayOneTypeError();
1957
+ }
1958
+ return type;
1959
+ }
1960
+ function stringifyInlineArray(values) {
1961
+ values = toJSON(values);
1962
+ const type = validateArray(values);
1963
+ var result = "[";
1964
+ var stringified = values.map((_) => stringifyInline(_, type));
1965
+ if (stringified.join(", ").length > 60 || /\n/.test(stringified)) {
1966
+ result += "\n " + stringified.join(",\n ") + "\n";
1967
+ } else {
1968
+ result += " " + stringified.join(", ") + (stringified.length > 0 ? " " : "");
1969
+ }
1970
+ return result + "]";
1971
+ }
1972
+ function stringifyInlineTable(value) {
1973
+ value = toJSON(value);
1974
+ var result = [];
1975
+ Object.keys(value).forEach((key) => {
1976
+ result.push(stringifyKey(key) + " = " + stringifyAnyInline(value[key], false));
1977
+ });
1978
+ return "{ " + result.join(", ") + (result.length > 0 ? " " : "") + "}";
1979
+ }
1980
+ function stringifyComplex(prefix, indent, key, value) {
1981
+ var valueType = tomlType2(value);
1982
+ if (valueType === "array") {
1983
+ return stringifyArrayOfTables(prefix, indent, key, value);
1984
+ } else if (valueType === "table") {
1985
+ return stringifyComplexTable(prefix, indent, key, value);
1986
+ } else {
1987
+ throw typeError(valueType);
1988
+ }
1989
+ }
1990
+ function stringifyArrayOfTables(prefix, indent, key, values) {
1991
+ values = toJSON(values);
1992
+ validateArray(values);
1993
+ var firstValueType = tomlType2(values[0]);
1994
+ if (firstValueType !== "table") throw typeError(firstValueType);
1995
+ var fullKey = prefix + stringifyKey(key);
1996
+ var result = "";
1997
+ values.forEach((table) => {
1998
+ if (result.length > 0) result += "\n";
1999
+ result += indent + "[[" + fullKey + "]]\n";
2000
+ result += stringifyObject(fullKey + ".", indent, table);
2001
+ });
2002
+ return result;
2003
+ }
2004
+ function stringifyComplexTable(prefix, indent, key, value) {
2005
+ var fullKey = prefix + stringifyKey(key);
2006
+ var result = "";
2007
+ if (getInlineKeys(value).length > 0) {
2008
+ result += indent + "[" + fullKey + "]\n";
2009
+ }
2010
+ return result + stringifyObject(fullKey + ".", indent, value);
2011
+ }
2012
+ }
2013
+ });
2014
+
2015
+ // ../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/toml.js
2016
+ var require_toml = __commonJS({
2017
+ "../../node_modules/.pnpm/@iarna+toml@2.2.5/node_modules/@iarna/toml/toml.js"(exports2) {
2018
+ "use strict";
2019
+ exports2.parse = require_parse();
2020
+ exports2.stringify = require_stringify();
2021
+ }
2022
+ });
2023
+
2024
+ // src/daemon/index.ts
2025
+ var import_node_fs9 = require("fs");
2026
+ var import_node_path6 = require("path");
2027
+
2028
+ // src/daemon/paths.ts
2029
+ var import_node_fs = require("fs");
2030
+ var import_node_os = require("os");
2031
+ var import_node_path = require("path");
2032
+ function canWriteSystemPaths() {
2033
+ if (process.platform !== "linux") return false;
2034
+ if (process.getuid && process.getuid() === 0) return true;
2035
+ try {
2036
+ if (!(0, import_node_fs.existsSync)("/etc/threatcrush")) return false;
2037
+ (0, import_node_fs.mkdirSync)("/etc/threatcrush/.probe", { recursive: true });
2038
+ return true;
2039
+ } catch {
2040
+ return false;
2041
+ }
2042
+ }
2043
+ var systemMode = canWriteSystemPaths();
2044
+ var userBase = (0, import_node_path.join)((0, import_node_os.homedir)(), ".threatcrush");
2045
+ var PATHS = systemMode ? {
2046
+ mode: "system",
2047
+ configDir: "/etc/threatcrush",
2048
+ configFile: "/etc/threatcrush/threatcrushd.conf",
2049
+ confD: "/etc/threatcrush/threatcrushd.conf.d",
2050
+ moduleDir: "/etc/threatcrush/modules",
2051
+ logDir: "/var/log/threatcrush",
2052
+ logFile: "/var/log/threatcrush/threatcrushd.log",
2053
+ stateDir: "/var/lib/threatcrush",
2054
+ stateDb: "/var/lib/threatcrush/state.db",
2055
+ runDir: "/var/run/threatcrush",
2056
+ pidFile: "/var/run/threatcrush/threatcrushd.pid",
2057
+ socket: "/var/run/threatcrush/threatcrushd.sock"
2058
+ } : {
2059
+ mode: "user",
2060
+ configDir: userBase,
2061
+ configFile: (0, import_node_path.join)(userBase, "threatcrushd.conf"),
2062
+ confD: (0, import_node_path.join)(userBase, "threatcrushd.conf.d"),
2063
+ moduleDir: (0, import_node_path.join)(userBase, "modules"),
2064
+ logDir: (0, import_node_path.join)(userBase, "logs"),
2065
+ logFile: (0, import_node_path.join)(userBase, "logs", "threatcrushd.log"),
2066
+ stateDir: (0, import_node_path.join)(userBase, "state"),
2067
+ stateDb: (0, import_node_path.join)(userBase, "state", "state.db"),
2068
+ runDir: (0, import_node_path.join)(userBase, "run"),
2069
+ pidFile: (0, import_node_path.join)(userBase, "run", "threatcrushd.pid"),
2070
+ socket: (0, import_node_path.join)(userBase, "run", "threatcrushd.sock")
2071
+ };
2072
+ function ensureRuntimeDirs() {
2073
+ for (const dir of [PATHS.configDir, PATHS.confD, PATHS.moduleDir, PATHS.logDir, PATHS.stateDir, PATHS.runDir]) {
2074
+ try {
2075
+ (0, import_node_fs.mkdirSync)(dir, { recursive: true });
2076
+ } catch {
2077
+ }
2078
+ }
2079
+ }
2080
+
2081
+ // src/daemon/pidfile.ts
2082
+ var import_node_fs2 = require("fs");
2083
+ function writePidFile() {
2084
+ ensureRuntimeDirs();
2085
+ (0, import_node_fs2.writeFileSync)(PATHS.pidFile, String(process.pid), "utf-8");
2086
+ }
2087
+ function readPidFile() {
2088
+ if (!(0, import_node_fs2.existsSync)(PATHS.pidFile)) return null;
2089
+ const raw = (0, import_node_fs2.readFileSync)(PATHS.pidFile, "utf-8").trim();
2090
+ const pid = parseInt(raw, 10);
2091
+ return Number.isFinite(pid) ? pid : null;
2092
+ }
2093
+ function removePidFile() {
2094
+ try {
2095
+ if ((0, import_node_fs2.existsSync)(PATHS.pidFile)) (0, import_node_fs2.unlinkSync)(PATHS.pidFile);
2096
+ } catch {
2097
+ }
2098
+ }
2099
+ function isProcessAlive(pid) {
2100
+ try {
2101
+ process.kill(pid, 0);
2102
+ return true;
2103
+ } catch {
2104
+ return false;
2105
+ }
2106
+ }
2107
+ function findRunningDaemon() {
2108
+ const pid = readPidFile();
2109
+ if (pid && isProcessAlive(pid)) return pid;
2110
+ if (pid) removePidFile();
2111
+ return null;
2112
+ }
2113
+
2114
+ // src/daemon/ipc-server.ts
2115
+ var import_node_net = require("net");
2116
+ var import_node_fs3 = require("fs");
2117
+
2118
+ // src/daemon/event-bus.ts
2119
+ var import_node_events = require("events");
2120
+ var EventBus = class extends import_node_events.EventEmitter {
2121
+ publish(event) {
2122
+ this.emit("event", event);
2123
+ if (event.severity === "high" || event.severity === "critical") {
2124
+ this.emit("alert", event);
2125
+ }
2126
+ }
2127
+ announceModule(name, status, detail) {
2128
+ this.emit("module", { name, status, detail });
2129
+ }
2130
+ };
2131
+ var bus = new EventBus();
2132
+ bus.setMaxListeners(50);
2133
+
2134
+ // src/core/state.ts
2135
+ var import_better_sqlite3 = __toESM(require("better-sqlite3"));
2136
+ var db = null;
2137
+ var dbUnavailable = false;
2138
+ function initStateDB(dbPath = "/var/lib/threatcrush/state.db") {
2139
+ if (db) return db;
2140
+ if (dbUnavailable) {
2141
+ throw new Error("state db unavailable (previous init failed)");
2142
+ }
2143
+ try {
2144
+ try {
2145
+ db = new import_better_sqlite3.default(dbPath);
2146
+ } catch {
2147
+ db = new import_better_sqlite3.default(":memory:");
2148
+ }
2149
+ } catch (err) {
2150
+ dbUnavailable = true;
2151
+ throw err;
2152
+ }
2153
+ db.pragma("journal_mode = WAL");
2154
+ db.exec(`
2155
+ CREATE TABLE IF NOT EXISTS events (
2156
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
2157
+ timestamp TEXT NOT NULL,
2158
+ module TEXT NOT NULL,
2159
+ category TEXT NOT NULL,
2160
+ severity TEXT NOT NULL,
2161
+ message TEXT NOT NULL,
2162
+ source_ip TEXT,
2163
+ details TEXT
2164
+ );
2165
+
2166
+ CREATE TABLE IF NOT EXISTS module_state (
2167
+ module TEXT NOT NULL,
2168
+ key TEXT NOT NULL,
2169
+ value TEXT,
2170
+ PRIMARY KEY (module, key)
2171
+ );
2172
+
2173
+ CREATE TABLE IF NOT EXISTS stats (
2174
+ key TEXT PRIMARY KEY,
2175
+ value TEXT NOT NULL,
2176
+ updated_at TEXT NOT NULL
2177
+ );
2178
+
2179
+ CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
2180
+ CREATE INDEX IF NOT EXISTS idx_events_module ON events(module);
2181
+ CREATE INDEX IF NOT EXISTS idx_events_severity ON events(severity);
2182
+ CREATE INDEX IF NOT EXISTS idx_events_source_ip ON events(source_ip);
2183
+ `);
2184
+ return db;
2185
+ }
2186
+ function insertEvent(event) {
2187
+ const database = tryDb();
2188
+ if (!database) return -1;
2189
+ const stmt = database.prepare(`
2190
+ INSERT INTO events (timestamp, module, category, severity, message, source_ip, details)
2191
+ VALUES (?, ?, ?, ?, ?, ?, ?)
2192
+ `);
2193
+ const result = stmt.run(
2194
+ event.timestamp.toISOString(),
2195
+ event.module,
2196
+ event.category,
2197
+ event.severity,
2198
+ event.message,
2199
+ event.source_ip || null,
2200
+ event.details ? JSON.stringify(event.details) : null
2201
+ );
2202
+ return result.lastInsertRowid;
2203
+ }
2204
+ function tryDb() {
2205
+ if (db) return db;
2206
+ if (dbUnavailable) return null;
2207
+ try {
2208
+ return initStateDB();
2209
+ } catch {
2210
+ return null;
2211
+ }
2212
+ }
2213
+ function getRecentEvents(limit = 50) {
2214
+ const database = tryDb();
2215
+ if (!database) return [];
2216
+ const rows = database.prepare(`
2217
+ SELECT * FROM events ORDER BY timestamp DESC LIMIT ?
2218
+ `).all(limit);
2219
+ return rows.map(rowToEvent);
2220
+ }
2221
+ function getEventCount(since) {
2222
+ const database = tryDb();
2223
+ if (!database) return 0;
2224
+ if (since) {
2225
+ return database.prepare(`SELECT COUNT(*) as count FROM events WHERE timestamp >= ?`).get(since.toISOString()).count;
2226
+ }
2227
+ return database.prepare(`SELECT COUNT(*) as count FROM events`).get().count;
2228
+ }
2229
+ function getThreatCount(since) {
2230
+ const database = tryDb();
2231
+ if (!database) return 0;
2232
+ const severities = "('medium','high','critical')";
2233
+ if (since) {
2234
+ return database.prepare(
2235
+ `SELECT COUNT(*) as count FROM events WHERE severity IN ${severities} AND timestamp >= ?`
2236
+ ).get(since.toISOString()).count;
2237
+ }
2238
+ return database.prepare(
2239
+ `SELECT COUNT(*) as count FROM events WHERE severity IN ${severities}`
2240
+ ).get().count;
2241
+ }
2242
+ function getTopSources(limit = 10) {
2243
+ const database = tryDb();
2244
+ if (!database) return [];
2245
+ return database.prepare(`
2246
+ SELECT source_ip as ip, COUNT(*) as count FROM events
2247
+ WHERE source_ip IS NOT NULL
2248
+ GROUP BY source_ip ORDER BY count DESC LIMIT ?
2249
+ `).all(limit);
2250
+ }
2251
+ function getModuleState(module2, key) {
2252
+ const database = db || initStateDB();
2253
+ const row = database.prepare(`SELECT value FROM module_state WHERE module = ? AND key = ?`).get(module2, key);
2254
+ if (!row) return void 0;
2255
+ try {
2256
+ return JSON.parse(row.value);
2257
+ } catch {
2258
+ return row.value;
2259
+ }
2260
+ }
2261
+ function setModuleState(module2, key, value) {
2262
+ const database = db || initStateDB();
2263
+ database.prepare(`
2264
+ INSERT OR REPLACE INTO module_state (module, key, value) VALUES (?, ?, ?)
2265
+ `).run(module2, key, JSON.stringify(value));
2266
+ }
2267
+ function rowToEvent(row) {
2268
+ return {
2269
+ id: row.id,
2270
+ timestamp: new Date(row.timestamp),
2271
+ module: row.module,
2272
+ category: row.category,
2273
+ severity: row.severity,
2274
+ message: row.message,
2275
+ source_ip: row.source_ip,
2276
+ details: row.details ? JSON.parse(row.details) : void 0
2277
+ };
2278
+ }
2279
+ function closeDB() {
2280
+ if (db) {
2281
+ db.close();
2282
+ db = null;
2283
+ }
2284
+ }
2285
+
2286
+ // src/daemon/ipc-server.ts
2287
+ var IpcServer = class {
2288
+ constructor(version, moduleHost) {
2289
+ this.version = version;
2290
+ this.moduleHost = moduleHost;
2291
+ bus.on("event", (event) => {
2292
+ this.counters.events++;
2293
+ if (event.severity === "medium" || event.severity === "high" || event.severity === "critical") {
2294
+ this.counters.threats++;
2295
+ }
2296
+ this.broadcast({ push: "event", payload: event }, "event");
2297
+ });
2298
+ bus.on("alert", () => {
2299
+ this.counters.alerts++;
2300
+ });
2301
+ bus.on("module", (info) => {
2302
+ this.broadcast({ push: "module", payload: info }, "module");
2303
+ });
2304
+ }
2305
+ version;
2306
+ moduleHost;
2307
+ server = null;
2308
+ clients = /* @__PURE__ */ new Map();
2309
+ nextClientId = 1;
2310
+ startedAt = /* @__PURE__ */ new Date();
2311
+ counters = { events: 0, threats: 0, alerts: 0 };
2312
+ async start() {
2313
+ if ((0, import_node_fs3.existsSync)(PATHS.socket)) {
2314
+ try {
2315
+ (0, import_node_fs3.unlinkSync)(PATHS.socket);
2316
+ } catch {
2317
+ }
2318
+ }
2319
+ return new Promise((resolve, reject) => {
2320
+ this.server = (0, import_node_net.createServer)((sock) => this.handleClient(sock));
2321
+ this.server.on("error", reject);
2322
+ this.server.listen(PATHS.socket, () => {
2323
+ try {
2324
+ require("fs").chmodSync(PATHS.socket, 432);
2325
+ } catch {
2326
+ }
2327
+ resolve();
2328
+ });
2329
+ });
2330
+ }
2331
+ async stop() {
2332
+ for (const c of this.clients.values()) {
2333
+ try {
2334
+ c.socket.destroy();
2335
+ } catch {
2336
+ }
2337
+ }
2338
+ this.clients.clear();
2339
+ return new Promise((resolve) => {
2340
+ if (!this.server) {
2341
+ try {
2342
+ if ((0, import_node_fs3.existsSync)(PATHS.socket)) (0, import_node_fs3.unlinkSync)(PATHS.socket);
2343
+ } catch {
2344
+ }
2345
+ return resolve();
2346
+ }
2347
+ this.server.close(() => {
2348
+ try {
2349
+ if ((0, import_node_fs3.existsSync)(PATHS.socket)) (0, import_node_fs3.unlinkSync)(PATHS.socket);
2350
+ } catch {
2351
+ }
2352
+ resolve();
2353
+ });
2354
+ });
2355
+ }
2356
+ handleClient(socket) {
2357
+ const id = this.nextClientId++;
2358
+ const state = { id, socket, buffer: "", subscriptions: /* @__PURE__ */ new Set() };
2359
+ this.clients.set(id, state);
2360
+ socket.setEncoding("utf-8");
2361
+ socket.on("data", (chunk) => {
2362
+ state.buffer += chunk.toString();
2363
+ let idx;
2364
+ while ((idx = state.buffer.indexOf("\n")) >= 0) {
2365
+ const line = state.buffer.slice(0, idx);
2366
+ state.buffer = state.buffer.slice(idx + 1);
2367
+ if (!line.trim()) continue;
2368
+ this.handleLine(state, line).catch((err) => {
2369
+ this.send(state, { id: 0, ok: false, error: String(err?.message || err) });
2370
+ });
2371
+ }
2372
+ });
2373
+ socket.on("close", () => {
2374
+ this.clients.delete(id);
2375
+ });
2376
+ socket.on("error", () => {
2377
+ this.clients.delete(id);
2378
+ });
2379
+ }
2380
+ async handleLine(client, line) {
2381
+ let req;
2382
+ try {
2383
+ req = JSON.parse(line);
2384
+ } catch {
2385
+ return this.send(client, { id: 0, ok: false, error: "invalid json" });
2386
+ }
2387
+ switch (req.method) {
2388
+ case "ping":
2389
+ return this.send(client, { id: req.id, ok: true, result: "pong" });
2390
+ case "status": {
2391
+ const status = {
2392
+ pid: process.pid,
2393
+ startedAt: this.startedAt.toISOString(),
2394
+ uptimeSeconds: Math.floor((Date.now() - this.startedAt.getTime()) / 1e3),
2395
+ version: this.version,
2396
+ mode: PATHS.mode,
2397
+ paths: {
2398
+ config: PATHS.configFile,
2399
+ log: PATHS.logFile,
2400
+ state: PATHS.stateDb,
2401
+ socket: PATHS.socket
2402
+ },
2403
+ modules: this.moduleHost.summary(),
2404
+ counters: { ...this.counters }
2405
+ };
2406
+ return this.send(client, { id: req.id, ok: true, result: status });
2407
+ }
2408
+ case "recent_events": {
2409
+ const limit = req.params?.limit ?? 50;
2410
+ const events = getRecentEvents(limit);
2411
+ return this.send(client, { id: req.id, ok: true, result: events });
2412
+ }
2413
+ case "top_sources": {
2414
+ const limit = req.params?.limit ?? 10;
2415
+ return this.send(client, { id: req.id, ok: true, result: getTopSources(limit) });
2416
+ }
2417
+ case "counters": {
2418
+ return this.send(client, {
2419
+ id: req.id,
2420
+ ok: true,
2421
+ result: {
2422
+ total: getEventCount(),
2423
+ threats: getThreatCount(),
2424
+ last24h: getEventCount(new Date(Date.now() - 864e5)),
2425
+ threats24h: getThreatCount(new Date(Date.now() - 864e5))
2426
+ }
2427
+ });
2428
+ }
2429
+ case "module_list":
2430
+ return this.send(client, { id: req.id, ok: true, result: this.moduleHost.summary() });
2431
+ case "subscribe":
2432
+ for (const ch of req.params.channels) client.subscriptions.add(ch);
2433
+ return this.send(client, { id: req.id, ok: true, result: { subscribed: [...client.subscriptions] } });
2434
+ case "shutdown":
2435
+ this.send(client, { id: req.id, ok: true, result: "shutting down" });
2436
+ setTimeout(() => process.emit("SIGTERM"), 50);
2437
+ return;
2438
+ }
2439
+ }
2440
+ send(client, msg) {
2441
+ try {
2442
+ client.socket.write(JSON.stringify(msg) + "\n");
2443
+ } catch {
2444
+ }
2445
+ }
2446
+ broadcast(msg, channel) {
2447
+ for (const client of this.clients.values()) {
2448
+ if (!client.subscriptions.has(channel)) continue;
2449
+ this.send(client, msg);
2450
+ }
2451
+ }
2452
+ };
2453
+
2454
+ // src/daemon/module-host.ts
2455
+ var import_node_fs6 = require("fs");
2456
+ var import_node_path3 = require("path");
2457
+ var import_node_url = require("url");
2458
+ var import_toml2 = __toESM(require_toml());
2459
+
2460
+ // src/daemon/watchers/log-watcher.ts
2461
+ var import_node_fs4 = require("fs");
2462
+ var import_node_readline = require("readline");
2463
+
2464
+ // src/core/log-parser.ts
2465
+ var NGINX_REGEX = /^(\S+) \S+ \S+ \[([^\]]+)\] "(\S+) (\S+) \S+" (\d{3}) (\d+) "[^"]*" "([^"]*)"/;
2466
+ var AUTH_REGEX = /^(\w+\s+\d+\s+[\d:]+)\s+\S+\s+(\S+?)(?:\[\d+\])?:\s+(.*)/;
2467
+ var SYSLOG_REGEX = /^(\w+\s+\d+\s+[\d:]+)\s+\S+\s+(\S+?)(?:\[\d+\])?:\s+(.*)/;
2468
+ var IP_REGEX = /(?:from|FROM)\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/;
2469
+ var USER_REGEX = /(?:for|user)\s+(\S+?)(?:\s+from|\s*$)/;
2470
+ var ATTACK_PATTERNS = {
2471
+ sqli: [
2472
+ /(?:union\s+(?:all\s+)?select)/i,
2473
+ /(?:select\s+.*\s+from\s+)/i,
2474
+ /(?:insert\s+into\s+)/i,
2475
+ /(?:drop\s+(?:table|database))/i,
2476
+ /(?:or\s+1\s*=\s*1)/i,
2477
+ /(?:'\s*(?:or|and)\s+')/i,
2478
+ /(?:--\s*$|;\s*--)/,
2479
+ /(?:\/\*.*\*\/)/
2480
+ ],
2481
+ xss: [
2482
+ /<script[^>]*>/i,
2483
+ /javascript\s*:/i,
2484
+ /on(?:load|error|click|mouseover)\s*=/i,
2485
+ /eval\s*\(/i,
2486
+ /document\.(?:cookie|write|location)/i
2487
+ ],
2488
+ path_traversal: [
2489
+ /\.\.\//,
2490
+ /\.\.\\/,
2491
+ /etc\/(?:passwd|shadow|hosts)/,
2492
+ /proc\/self/,
2493
+ /windows\/system32/i
2494
+ ],
2495
+ rfi: [
2496
+ /(?:https?|ftp):\/\/.*\?/i,
2497
+ /php:\/\/(?:input|filter)/i,
2498
+ /data:\/\//i
2499
+ ]
2500
+ };
2501
+ function parseNginxLog(line) {
2502
+ const match = line.match(NGINX_REGEX);
2503
+ if (!match) return null;
2504
+ return {
2505
+ timestamp: parseNginxTimestamp(match[2]),
2506
+ raw: line,
2507
+ source: "nginx",
2508
+ fields: {
2509
+ ip: match[1],
2510
+ method: match[3],
2511
+ path: match[4],
2512
+ status: match[5],
2513
+ size: match[6],
2514
+ user_agent: match[7]
2515
+ }
2516
+ };
2517
+ }
2518
+ function parseAuthLog(line) {
2519
+ const match = line.match(AUTH_REGEX);
2520
+ if (!match) return null;
2521
+ const ipMatch = match[3].match(IP_REGEX);
2522
+ const userMatch = match[3].match(USER_REGEX);
2523
+ return {
2524
+ timestamp: parseSyslogTimestamp(match[1]),
2525
+ raw: line,
2526
+ source: "auth",
2527
+ fields: {
2528
+ process: match[2],
2529
+ message: match[3],
2530
+ ip: ipMatch?.[1],
2531
+ user: userMatch?.[1]
2532
+ }
2533
+ };
2534
+ }
2535
+ function parseSyslog(line) {
2536
+ const match = line.match(SYSLOG_REGEX);
2537
+ if (!match) return null;
2538
+ return {
2539
+ timestamp: parseSyslogTimestamp(match[1]),
2540
+ raw: line,
2541
+ source: "syslog",
2542
+ fields: {
2543
+ facility: "syslog",
2544
+ process: match[2],
2545
+ message: match[3]
2546
+ }
2547
+ };
2548
+ }
2549
+ function detectAttackPattern(path) {
2550
+ for (const [type, patterns] of Object.entries(ATTACK_PATTERNS)) {
2551
+ for (const pattern of patterns) {
2552
+ if (pattern.test(path)) {
2553
+ return type;
2554
+ }
2555
+ }
2556
+ }
2557
+ return null;
2558
+ }
2559
+ function autoDetectParser(line) {
2560
+ const nginx = parseNginxLog(line);
2561
+ if (nginx) return nginx;
2562
+ const auth = parseAuthLog(line);
2563
+ if (auth) return auth;
2564
+ return parseSyslog(line);
2565
+ }
2566
+ function parseNginxTimestamp(s) {
2567
+ try {
2568
+ const cleaned = s.replace(/(\d{2})\/(\w{3})\/(\d{4}):/, "$2 $1, $3 ");
2569
+ return new Date(cleaned);
2570
+ } catch {
2571
+ return /* @__PURE__ */ new Date();
2572
+ }
2573
+ }
2574
+ function parseSyslogTimestamp(s) {
2575
+ try {
2576
+ const withYear = `${s} ${(/* @__PURE__ */ new Date()).getFullYear()}`;
2577
+ return new Date(withYear);
2578
+ } catch {
2579
+ return /* @__PURE__ */ new Date();
2580
+ }
2581
+ }
2582
+
2583
+ // src/daemon/watchers/log-watcher.ts
2584
+ var DEFAULT_SOURCES = [
2585
+ { path: "/var/log/auth.log", module: "ssh-guard", category: "auth" },
2586
+ { path: "/var/log/secure", module: "ssh-guard", category: "auth" },
2587
+ { path: "/var/log/nginx/access.log", module: "log-watcher", category: "web" },
2588
+ { path: "/var/log/syslog", module: "log-watcher", category: "system" }
2589
+ ];
2590
+ var LogWatcher = class {
2591
+ constructor(bus2, sources = DEFAULT_SOURCES) {
2592
+ this.bus = bus2;
2593
+ this.sources = sources;
2594
+ }
2595
+ bus;
2596
+ sources;
2597
+ timers = /* @__PURE__ */ new Map();
2598
+ positions = /* @__PURE__ */ new Map();
2599
+ active = /* @__PURE__ */ new Set();
2600
+ start() {
2601
+ const started = [];
2602
+ for (const src of this.sources) {
2603
+ if (!(0, import_node_fs4.existsSync)(src.path)) continue;
2604
+ try {
2605
+ (0, import_node_fs4.accessSync)(src.path, import_node_fs4.constants.R_OK);
2606
+ } catch {
2607
+ continue;
2608
+ }
2609
+ this.tail(src);
2610
+ started.push(src.path);
2611
+ }
2612
+ return started;
2613
+ }
2614
+ stop() {
2615
+ for (const t of this.timers.values()) clearInterval(t);
2616
+ this.timers.clear();
2617
+ this.positions.clear();
2618
+ this.active.clear();
2619
+ }
2620
+ activeModules() {
2621
+ return [...this.active];
2622
+ }
2623
+ tail(src) {
2624
+ try {
2625
+ this.positions.set(src.path, (0, import_node_fs4.statSync)(src.path).size);
2626
+ } catch {
2627
+ this.positions.set(src.path, 0);
2628
+ }
2629
+ const timer = setInterval(() => this.poll(src), 1e3);
2630
+ this.timers.set(src.path, timer);
2631
+ this.active.add(src.module);
2632
+ }
2633
+ poll(src) {
2634
+ let stat;
2635
+ try {
2636
+ stat = (0, import_node_fs4.statSync)(src.path);
2637
+ } catch {
2638
+ return;
2639
+ }
2640
+ const prev = this.positions.get(src.path) ?? 0;
2641
+ if (stat.size < prev) {
2642
+ this.positions.set(src.path, 0);
2643
+ return;
2644
+ }
2645
+ if (stat.size === prev) return;
2646
+ const stream = (0, import_node_fs4.createReadStream)(src.path, { start: prev, encoding: "utf-8" });
2647
+ stream.on("error", () => this.positions.set(src.path, stat.size));
2648
+ const rl = (0, import_node_readline.createInterface)({ input: stream });
2649
+ rl.on("error", () => {
2650
+ });
2651
+ rl.on("line", (line) => {
2652
+ if (!line.trim()) return;
2653
+ this.process(line, src);
2654
+ });
2655
+ rl.on("close", () => this.positions.set(src.path, stat.size));
2656
+ }
2657
+ process(line, src) {
2658
+ const parsed = autoDetectParser(line);
2659
+ if (!parsed) return;
2660
+ let severity = "info";
2661
+ let message = line;
2662
+ let sourceIp;
2663
+ if (parsed.source === "auth") {
2664
+ const entry = parseAuthLog(line);
2665
+ if (!entry) return;
2666
+ sourceIp = entry.fields.ip;
2667
+ const msg = entry.fields.message;
2668
+ if (/failed password/i.test(msg)) {
2669
+ severity = "high";
2670
+ message = `Failed SSH login for ${entry.fields.user || "unknown"} from ${entry.fields.ip || "unknown"}`;
2671
+ } else if (/invalid user/i.test(msg)) {
2672
+ severity = "high";
2673
+ message = `Invalid SSH user: ${entry.fields.user || "unknown"} from ${entry.fields.ip || "unknown"}`;
2674
+ } else if (/accepted/i.test(msg)) {
2675
+ severity = "info";
2676
+ message = `SSH login accepted for ${entry.fields.user || "unknown"}`;
2677
+ } else {
2678
+ return;
2679
+ }
2680
+ } else if (parsed.source === "nginx") {
2681
+ const entry = parseNginxLog(line);
2682
+ if (!entry) return;
2683
+ sourceIp = entry.fields.ip;
2684
+ const status = parseInt(entry.fields.status, 10);
2685
+ const attack = detectAttackPattern(entry.fields.path);
2686
+ if (attack) {
2687
+ severity = "critical";
2688
+ message = `Attack [${attack.toUpperCase()}]: ${entry.fields.method} ${entry.fields.path}`;
2689
+ } else if (status >= 500) {
2690
+ severity = "medium";
2691
+ message = `Server error ${status}: ${entry.fields.method} ${entry.fields.path}`;
2692
+ } else if (status >= 400) {
2693
+ severity = "low";
2694
+ message = `Client error ${status}: ${entry.fields.method} ${entry.fields.path}`;
2695
+ } else {
2696
+ return;
2697
+ }
2698
+ } else {
2699
+ return;
2700
+ }
2701
+ const event = {
2702
+ timestamp: /* @__PURE__ */ new Date(),
2703
+ module: src.module,
2704
+ category: src.category,
2705
+ severity,
2706
+ message,
2707
+ source_ip: sourceIp
2708
+ };
2709
+ try {
2710
+ insertEvent(event);
2711
+ } catch {
2712
+ }
2713
+ this.bus.publish(event);
2714
+ }
2715
+ };
2716
+
2717
+ // src/daemon/watchers/journal-watcher.ts
2718
+ var import_node_child_process = require("child_process");
2719
+ var JournalWatcher = class _JournalWatcher {
2720
+ constructor(bus2) {
2721
+ this.bus = bus2;
2722
+ }
2723
+ bus;
2724
+ proc = null;
2725
+ buffer = "";
2726
+ moduleName = "user-journal";
2727
+ active = false;
2728
+ static isAvailable() {
2729
+ const probe = (0, import_node_child_process.spawnSync)("journalctl", ["--user", "-n", "0", "--no-pager"], {
2730
+ stdio: ["ignore", "ignore", "ignore"]
2731
+ });
2732
+ return probe.status === 0;
2733
+ }
2734
+ start() {
2735
+ if (!_JournalWatcher.isAvailable()) return false;
2736
+ const child = (0, import_node_child_process.spawn)(
2737
+ "journalctl",
2738
+ ["--user", "-o", "json", "-f", "--since", "now"],
2739
+ { stdio: ["ignore", "pipe", "pipe"] }
2740
+ );
2741
+ if (!child.stdout) return false;
2742
+ child.stdout.setEncoding("utf-8");
2743
+ child.stdout.on("data", (chunk) => this.onData(chunk));
2744
+ child.on("exit", () => {
2745
+ this.proc = null;
2746
+ this.active = false;
2747
+ });
2748
+ this.proc = child;
2749
+ this.active = true;
2750
+ return true;
2751
+ }
2752
+ stop() {
2753
+ if (this.proc) {
2754
+ try {
2755
+ this.proc.kill("SIGTERM");
2756
+ } catch {
2757
+ }
2758
+ this.proc = null;
2759
+ }
2760
+ this.active = false;
2761
+ }
2762
+ isActive() {
2763
+ return this.active;
2764
+ }
2765
+ moduleNameValue() {
2766
+ return this.moduleName;
2767
+ }
2768
+ onData(chunk) {
2769
+ this.buffer += chunk;
2770
+ let idx;
2771
+ while ((idx = this.buffer.indexOf("\n")) >= 0) {
2772
+ const line = this.buffer.slice(0, idx);
2773
+ this.buffer = this.buffer.slice(idx + 1);
2774
+ if (!line.trim()) continue;
2775
+ this.handleLine(line);
2776
+ }
2777
+ }
2778
+ handleLine(line) {
2779
+ let entry;
2780
+ try {
2781
+ entry = JSON.parse(line);
2782
+ } catch {
2783
+ return;
2784
+ }
2785
+ const message = entry.MESSAGE;
2786
+ if (!message) return;
2787
+ const priority = parseInt(entry.PRIORITY ?? "6", 10);
2788
+ const severity = priorityToSeverity(priority);
2789
+ const ident = entry.SYSLOG_IDENTIFIER || entry._COMM || "journal";
2790
+ const bumpedSeverity = bumpForIdent(ident, message, severity);
2791
+ const event = {
2792
+ timestamp: realtimeToDate(entry.__REALTIME_TIMESTAMP) || /* @__PURE__ */ new Date(),
2793
+ module: this.moduleName,
2794
+ category: "system",
2795
+ severity: bumpedSeverity,
2796
+ message: `[${ident}] ${message}`.slice(0, 500)
2797
+ };
2798
+ try {
2799
+ insertEvent(event);
2800
+ } catch {
2801
+ }
2802
+ this.bus.publish(event);
2803
+ }
2804
+ };
2805
+ function priorityToSeverity(priority) {
2806
+ if (priority <= 2) return "critical";
2807
+ if (priority === 3) return "high";
2808
+ if (priority === 4) return "medium";
2809
+ if (priority === 5) return "low";
2810
+ return "info";
2811
+ }
2812
+ function bumpForIdent(ident, message, base) {
2813
+ if (/sudo/i.test(ident) && /authentication failure|incorrect password|FAILED/i.test(message)) {
2814
+ return "high";
2815
+ }
2816
+ if (/sshd/i.test(ident) && /failed|invalid user|break-in/i.test(message)) {
2817
+ return "high";
2818
+ }
2819
+ return base;
2820
+ }
2821
+ function realtimeToDate(rt) {
2822
+ if (!rt) return null;
2823
+ const us = parseInt(rt, 10);
2824
+ if (!Number.isFinite(us)) return null;
2825
+ return new Date(Math.floor(us / 1e3));
2826
+ }
2827
+
2828
+ // src/core/config.ts
2829
+ var import_node_fs5 = require("fs");
2830
+ var import_node_path2 = require("path");
2831
+ var import_toml = __toESM(require_toml());
2832
+ var DEFAULT_CONFIG_PATH = "/etc/threatcrush/threatcrushd.conf";
2833
+ var DEFAULT_CONFDIR = "/etc/threatcrush/threatcrushd.conf.d";
2834
+ var DEFAULT_CONFIG = {
2835
+ daemon: {
2836
+ pid_file: "/var/run/threatcrush/threatcrushd.pid",
2837
+ log_level: "info",
2838
+ log_file: "/var/log/threatcrush/threatcrushd.log",
2839
+ state_db: "/var/lib/threatcrush/state.db"
2840
+ },
2841
+ api: {
2842
+ enabled: true,
2843
+ bind: "127.0.0.1:9393",
2844
+ tls: false
2845
+ },
2846
+ alerts: {},
2847
+ modules: {
2848
+ auto_update: true,
2849
+ update_interval: "24h",
2850
+ module_dir: "/etc/threatcrush/modules",
2851
+ config_dir: DEFAULT_CONFDIR
2852
+ }
2853
+ };
2854
+ function loadConfig(configPath) {
2855
+ const path = configPath || DEFAULT_CONFIG_PATH;
2856
+ if (!(0, import_node_fs5.existsSync)(path)) {
2857
+ return { ...DEFAULT_CONFIG };
2858
+ }
2859
+ try {
2860
+ const raw = (0, import_node_fs5.readFileSync)(path, "utf-8");
2861
+ const parsed = import_toml.default.parse(raw);
2862
+ return {
2863
+ daemon: { ...DEFAULT_CONFIG.daemon, ...parsed.daemon },
2864
+ api: { ...DEFAULT_CONFIG.api, ...parsed.api },
2865
+ alerts: parsed.alerts || {},
2866
+ modules: { ...DEFAULT_CONFIG.modules, ...parsed.modules },
2867
+ license: parsed.license
2868
+ };
2869
+ } catch {
2870
+ return { ...DEFAULT_CONFIG };
2871
+ }
2872
+ }
2873
+ function loadModuleConfigs(confDir) {
2874
+ const dir = confDir || DEFAULT_CONFDIR;
2875
+ const configs = /* @__PURE__ */ new Map();
2876
+ if (!(0, import_node_fs5.existsSync)(dir)) {
2877
+ return configs;
2878
+ }
2879
+ const files = (0, import_node_fs5.readdirSync)(dir).filter((f) => f.endsWith(".conf"));
2880
+ for (const file of files) {
2881
+ try {
2882
+ const raw = (0, import_node_fs5.readFileSync)((0, import_node_path2.join)(dir, file), "utf-8");
2883
+ const parsed = import_toml.default.parse(raw);
2884
+ for (const [name, config] of Object.entries(parsed)) {
2885
+ configs.set(name, config);
2886
+ }
2887
+ } catch {
2888
+ }
2889
+ }
2890
+ return configs;
2891
+ }
2892
+
2893
+ // src/daemon/module-host.ts
2894
+ var ModuleHost = class {
2895
+ constructor(bus2) {
2896
+ this.bus = bus2;
2897
+ bus2.on("event", (event) => {
2898
+ const mod = this.modules.get(event.module);
2899
+ if (mod) mod.events++;
2900
+ for (const hosted of this.modules.values()) {
2901
+ if (hosted.status !== "running" || !hosted.instance?.onEvent) continue;
2902
+ void hosted.instance.onEvent(event).catch((err) => {
2903
+ hosted.status = "error";
2904
+ hosted.detail = `onEvent failed: ${String(err.message || err)}`;
2905
+ this.bus.announceModule(hosted.name, "error", hosted.detail);
2906
+ });
2907
+ }
2908
+ });
2909
+ }
2910
+ bus;
2911
+ modules = /* @__PURE__ */ new Map();
2912
+ logWatcher = null;
2913
+ journalWatcher = null;
2914
+ async start() {
2915
+ this.registerBuiltins();
2916
+ await this.discoverAndStartInstalled();
2917
+ this.logWatcher = new LogWatcher(this.bus);
2918
+ const watched = this.logWatcher.start();
2919
+ for (const modName of this.logWatcher.activeModules()) {
2920
+ const mod = this.modules.get(modName);
2921
+ if (mod) {
2922
+ mod.status = "running";
2923
+ mod.detail = `watching ${watched.length} log source(s)`;
2924
+ this.bus.announceModule(modName, "running", mod.detail);
2925
+ }
2926
+ }
2927
+ this.journalWatcher = new JournalWatcher(this.bus);
2928
+ if (this.journalWatcher.start()) {
2929
+ const mod = this.modules.get("user-journal");
2930
+ if (mod) {
2931
+ mod.status = "running";
2932
+ mod.detail = "tailing journalctl --user";
2933
+ this.bus.announceModule("user-journal", "running", mod.detail);
2934
+ }
2935
+ }
2936
+ }
2937
+ async stop() {
2938
+ this.logWatcher?.stop();
2939
+ this.journalWatcher?.stop();
2940
+ for (const mod of this.modules.values()) {
2941
+ try {
2942
+ if (mod.instance && mod.status === "running") {
2943
+ await mod.instance.stop();
2944
+ }
2945
+ } catch (err) {
2946
+ mod.status = "error";
2947
+ mod.detail = `stop failed: ${String(err.message || err)}`;
2948
+ this.bus.announceModule(mod.name, "error", mod.detail);
2949
+ continue;
2950
+ }
2951
+ mod.status = "loaded";
2952
+ this.bus.announceModule(mod.name, "stopped");
2953
+ }
2954
+ }
2955
+ summary() {
2956
+ return [...this.modules.values()].map((m) => ({
2957
+ name: m.name,
2958
+ status: m.status,
2959
+ events: m.events,
2960
+ detail: m.detail
2961
+ }));
2962
+ }
2963
+ registerBuiltins() {
2964
+ const builtins = [
2965
+ { name: "log-watcher", version: "0.1.0", source: "builtin", status: "loaded", events: 0 },
2966
+ { name: "ssh-guard", version: "0.1.0", source: "builtin", status: "loaded", events: 0 },
2967
+ { name: "user-journal", version: "0.1.0", source: "builtin", status: "loaded", events: 0 }
2968
+ ];
2969
+ for (const m of builtins) this.modules.set(m.name, m);
2970
+ }
2971
+ async discoverAndStartInstalled() {
2972
+ if (!(0, import_node_fs6.existsSync)(PATHS.moduleDir)) return;
2973
+ const configs = loadModuleConfigs(PATHS.confD);
2974
+ const entries = (0, import_node_fs6.readdirSync)(PATHS.moduleDir, { withFileTypes: true });
2975
+ for (const entry of entries) {
2976
+ if (!entry.isDirectory()) continue;
2977
+ const manifestPath = (0, import_node_path3.join)(PATHS.moduleDir, entry.name, "mod.toml");
2978
+ if (!(0, import_node_fs6.existsSync)(manifestPath)) continue;
2979
+ try {
2980
+ const manifest = import_toml2.default.parse((0, import_node_fs6.readFileSync)(manifestPath, "utf-8"));
2981
+ const name = manifest.module?.name || entry.name;
2982
+ const defaults = manifest.module?.config?.defaults || {};
2983
+ const config = {
2984
+ enabled: true,
2985
+ ...defaults,
2986
+ ...configs.get(name) || {}
2987
+ };
2988
+ const hosted = {
2989
+ name,
2990
+ version: manifest.module?.version || "0.0.0",
2991
+ source: "installed",
2992
+ status: config.enabled === false ? "disabled" : "loaded",
2993
+ events: 0,
2994
+ path: (0, import_node_path3.join)(PATHS.moduleDir, entry.name),
2995
+ config
2996
+ };
2997
+ this.modules.set(name, hosted);
2998
+ if (config.enabled === false) continue;
2999
+ await this.startInstalled(hosted);
3000
+ } catch (err) {
3001
+ const name = entry.name;
3002
+ this.modules.set(name, {
3003
+ name,
3004
+ version: "0.0.0",
3005
+ source: "installed",
3006
+ status: "error",
3007
+ events: 0,
3008
+ detail: `manifest load failed: ${String(err.message || err)}`,
3009
+ path: (0, import_node_path3.join)(PATHS.moduleDir, entry.name)
3010
+ });
3011
+ }
3012
+ }
3013
+ }
3014
+ async startInstalled(hosted) {
3015
+ const entrypoint = this.installedEntrypoint(hosted.path);
3016
+ if (!entrypoint) {
3017
+ hosted.status = "loaded";
3018
+ hosted.detail = "no built entrypoint found; run npm install && npm run build in the module directory";
3019
+ return;
3020
+ }
3021
+ try {
3022
+ const imported = await import((0, import_node_url.pathToFileURL)(entrypoint).href);
3023
+ const exported = imported.default || imported.module || imported;
3024
+ const instance = typeof exported === "function" ? new exported() : exported;
3025
+ if (!this.isThreatCrushModule(instance)) {
3026
+ throw new Error("entrypoint does not export a ThreatCrush module");
3027
+ }
3028
+ hosted.instance = instance;
3029
+ await instance.init(this.contextFor(hosted));
3030
+ await instance.start();
3031
+ hosted.status = "running";
3032
+ hosted.detail = `started from ${entrypoint}`;
3033
+ this.bus.announceModule(hosted.name, "running", hosted.detail);
3034
+ } catch (err) {
3035
+ hosted.status = "error";
3036
+ hosted.detail = String(err.message || err);
3037
+ this.bus.announceModule(hosted.name, "error", hosted.detail);
3038
+ }
3039
+ }
3040
+ installedEntrypoint(modulePath) {
3041
+ const packageJson = (0, import_node_path3.join)(modulePath, "package.json");
3042
+ const candidates = [];
3043
+ if ((0, import_node_fs6.existsSync)(packageJson)) {
3044
+ try {
3045
+ const pkg = JSON.parse((0, import_node_fs6.readFileSync)(packageJson, "utf-8"));
3046
+ if (pkg.main) candidates.push((0, import_node_path3.join)(modulePath, pkg.main));
3047
+ } catch {
3048
+ }
3049
+ }
3050
+ candidates.push((0, import_node_path3.join)(modulePath, "dist", "index.js"), (0, import_node_path3.join)(modulePath, "index.js"));
3051
+ return candidates.find((candidate) => (0, import_node_fs6.existsSync)(candidate)) || null;
3052
+ }
3053
+ isThreatCrushModule(value) {
3054
+ return Boolean(
3055
+ value && typeof value === "object" && typeof value.init === "function" && typeof value.start === "function" && typeof value.stop === "function"
3056
+ );
3057
+ }
3058
+ contextFor(hosted) {
3059
+ return {
3060
+ config: hosted.config || { enabled: true },
3061
+ logger: this.loggerFor(hosted.name),
3062
+ emit: (event) => this.bus.publish(event),
3063
+ subscribe: (eventType, handler) => {
3064
+ this.bus.on("event", (event) => {
3065
+ if (event.category === eventType || event.module === eventType) handler(event);
3066
+ });
3067
+ },
3068
+ alert: (alert) => {
3069
+ this.bus.emit("alert", alert.event || {
3070
+ timestamp: /* @__PURE__ */ new Date(),
3071
+ module: hosted.name,
3072
+ category: "system",
3073
+ severity: alert.severity,
3074
+ message: alert.title,
3075
+ details: alert.body ? { body: alert.body } : void 0
3076
+ });
3077
+ },
3078
+ getState: (key) => getModuleState(hosted.name, key),
3079
+ setState: (key, value) => setModuleState(hosted.name, key, value)
3080
+ };
3081
+ }
3082
+ loggerFor(moduleName) {
3083
+ return {
3084
+ debug: (msg, ...args) => console.debug(`[${moduleName}] ${msg}`, ...args),
3085
+ info: (msg, ...args) => console.info(`[${moduleName}] ${msg}`, ...args),
3086
+ warn: (msg, ...args) => console.warn(`[${moduleName}] ${msg}`, ...args),
3087
+ error: (msg, ...args) => console.error(`[${moduleName}] ${msg}`, ...args)
3088
+ };
3089
+ }
3090
+ };
3091
+
3092
+ // src/daemon/alerts/smtp.ts
3093
+ var transporter = null;
3094
+ var nodemailer = null;
3095
+ var SEVERITY_RANK = {
3096
+ info: 0,
3097
+ low: 1,
3098
+ medium: 2,
3099
+ high: 3,
3100
+ critical: 4
3101
+ };
3102
+ async function ensureTransporter(config) {
3103
+ if (!config.host || !config.from) return null;
3104
+ if (transporter) return transporter;
3105
+ if (!nodemailer) {
3106
+ try {
3107
+ nodemailer = await import("nodemailer");
3108
+ } catch {
3109
+ return null;
3110
+ }
3111
+ }
3112
+ transporter = nodemailer.createTransport({
3113
+ host: config.host,
3114
+ port: config.port ?? 587,
3115
+ secure: config.secure ?? false,
3116
+ auth: config.user && config.pass ? { user: config.user, pass: config.pass } : void 0
3117
+ });
3118
+ return transporter;
3119
+ }
3120
+ function meetsSeverity(event, min) {
3121
+ if (!min) return true;
3122
+ return (SEVERITY_RANK[event.severity] ?? 0) >= (SEVERITY_RANK[min] ?? 0);
3123
+ }
3124
+ function renderBody(event) {
3125
+ const ts = event.timestamp.toISOString();
3126
+ const ip = event.source_ip ? `
3127
+ Source IP: ${event.source_ip}` : "";
3128
+ const text = `[${event.severity.toUpperCase()}] ${event.module}
3129
+
3130
+ ${event.message}${ip}
3131
+
3132
+ When: ${ts}
3133
+ Category: ${event.category}`;
3134
+ const html = `<div style="font-family:system-ui,sans-serif;line-height:1.5"><h2 style="margin:0 0 8px">\u26A0 ${event.severity.toUpperCase()} \u2014 ${event.module}</h2><p>${event.message}</p>` + (event.source_ip ? `<p><strong>Source IP:</strong> <code>${event.source_ip}</code></p>` : "") + `<p style="color:#888;margin-top:16px"><small>${ts} \xB7 ${event.category}</small></p></div>`;
3135
+ return { text, html };
3136
+ }
3137
+ function smtpChannel(config) {
3138
+ return async (event) => {
3139
+ if (!meetsSeverity(event, config.min_severity)) return;
3140
+ const t = await ensureTransporter(config);
3141
+ if (!t) return;
3142
+ const to = Array.isArray(config.to) ? config.to.join(", ") : config.to;
3143
+ if (!to) return;
3144
+ const { text, html } = renderBody(event);
3145
+ await t.sendMail({
3146
+ from: config.from,
3147
+ to,
3148
+ subject: `[ThreatCrush ${event.severity}] ${event.module} \u2014 ${event.message.slice(0, 60)}`,
3149
+ text,
3150
+ html
3151
+ });
3152
+ };
3153
+ }
3154
+
3155
+ // src/daemon/alerts/index.ts
3156
+ var AlertDispatcher = class {
3157
+ constructor(bus2, config) {
3158
+ this.bus = bus2;
3159
+ this.config = config;
3160
+ this.bindChannels();
3161
+ bus2.on("alert", (event) => {
3162
+ void this.dispatch(event);
3163
+ });
3164
+ }
3165
+ bus;
3166
+ config;
3167
+ channels = [];
3168
+ bindChannels() {
3169
+ const alerts = this.config.alerts || {};
3170
+ for (const [name, raw] of Object.entries(alerts)) {
3171
+ const cfg = raw;
3172
+ if (!cfg.enabled) continue;
3173
+ if (name === "webhook" && typeof cfg.url === "string") {
3174
+ this.channels.push(webhookChannel(cfg.url, cfg.secret));
3175
+ }
3176
+ if (name === "slack" && typeof cfg.webhook_url === "string") {
3177
+ this.channels.push(slackChannel(cfg.webhook_url));
3178
+ }
3179
+ if (name === "email" && typeof cfg.host === "string" && typeof cfg.from === "string") {
3180
+ this.channels.push(smtpChannel(cfg));
3181
+ }
3182
+ }
3183
+ }
3184
+ async dispatch(event) {
3185
+ await Promise.all(this.channels.map((ch) => ch(event).catch(() => {
3186
+ })));
3187
+ }
3188
+ };
3189
+ function webhookChannel(url, secret) {
3190
+ return async (event) => {
3191
+ const body = JSON.stringify({ event });
3192
+ const headers = { "Content-Type": "application/json" };
3193
+ if (secret) headers["X-Threatcrush-Signature"] = secret;
3194
+ await fetch(url, { method: "POST", headers, body });
3195
+ };
3196
+ }
3197
+ function slackChannel(webhookUrl) {
3198
+ return async (event) => {
3199
+ const emoji = event.severity === "critical" ? ":rotating_light:" : ":warning:";
3200
+ const text = `${emoji} *[${event.severity.toUpperCase()}]* \`${event.module}\` \u2014 ${event.message}${event.source_ip ? ` (from ${event.source_ip})` : ""}`;
3201
+ await fetch(webhookUrl, {
3202
+ method: "POST",
3203
+ headers: { "Content-Type": "application/json" },
3204
+ body: JSON.stringify({ text })
3205
+ });
3206
+ };
3207
+ }
3208
+
3209
+ // src/core/cli-config.ts
3210
+ var import_node_fs7 = require("fs");
3211
+ var import_node_path4 = require("path");
3212
+ var import_node_os2 = require("os");
3213
+ var CLI_CONFIG_DIR = (0, import_node_path4.join)((0, import_node_os2.homedir)(), ".threatcrush");
3214
+ var CLI_CONFIG_PATH = (0, import_node_path4.join)(CLI_CONFIG_DIR, "config.json");
3215
+ function readCliConfig() {
3216
+ try {
3217
+ return JSON.parse((0, import_node_fs7.readFileSync)(CLI_CONFIG_PATH, "utf-8"));
3218
+ } catch {
3219
+ return {};
3220
+ }
3221
+ }
3222
+ function isLoggedIn() {
3223
+ const cfg = readCliConfig();
3224
+ if (!cfg.token) return false;
3225
+ if (cfg.expires_at && cfg.expires_at * 1e3 < Date.now()) return false;
3226
+ return true;
3227
+ }
3228
+ function authHeaders() {
3229
+ const cfg = readCliConfig();
3230
+ const headers = { "Content-Type": "application/json" };
3231
+ if (cfg.token) headers["Authorization"] = `Bearer ${cfg.token}`;
3232
+ return headers;
3233
+ }
3234
+
3235
+ // src/commands/scan.ts
3236
+ var import_node_fs8 = require("fs");
3237
+ var import_node_path5 = require("path");
3238
+
3239
+ // ../../node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js
3240
+ var ANSI_BACKGROUND_OFFSET = 10;
3241
+ var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
3242
+ var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
3243
+ var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
3244
+ var styles = {
3245
+ modifier: {
3246
+ reset: [0, 0],
3247
+ // 21 isn't widely supported and 22 does the same thing
3248
+ bold: [1, 22],
3249
+ dim: [2, 22],
3250
+ italic: [3, 23],
3251
+ underline: [4, 24],
3252
+ overline: [53, 55],
3253
+ inverse: [7, 27],
3254
+ hidden: [8, 28],
3255
+ strikethrough: [9, 29]
3256
+ },
3257
+ color: {
3258
+ black: [30, 39],
3259
+ red: [31, 39],
3260
+ green: [32, 39],
3261
+ yellow: [33, 39],
3262
+ blue: [34, 39],
3263
+ magenta: [35, 39],
3264
+ cyan: [36, 39],
3265
+ white: [37, 39],
3266
+ // Bright color
3267
+ blackBright: [90, 39],
3268
+ gray: [90, 39],
3269
+ // Alias of `blackBright`
3270
+ grey: [90, 39],
3271
+ // Alias of `blackBright`
3272
+ redBright: [91, 39],
3273
+ greenBright: [92, 39],
3274
+ yellowBright: [93, 39],
3275
+ blueBright: [94, 39],
3276
+ magentaBright: [95, 39],
3277
+ cyanBright: [96, 39],
3278
+ whiteBright: [97, 39]
3279
+ },
3280
+ bgColor: {
3281
+ bgBlack: [40, 49],
3282
+ bgRed: [41, 49],
3283
+ bgGreen: [42, 49],
3284
+ bgYellow: [43, 49],
3285
+ bgBlue: [44, 49],
3286
+ bgMagenta: [45, 49],
3287
+ bgCyan: [46, 49],
3288
+ bgWhite: [47, 49],
3289
+ // Bright color
3290
+ bgBlackBright: [100, 49],
3291
+ bgGray: [100, 49],
3292
+ // Alias of `bgBlackBright`
3293
+ bgGrey: [100, 49],
3294
+ // Alias of `bgBlackBright`
3295
+ bgRedBright: [101, 49],
3296
+ bgGreenBright: [102, 49],
3297
+ bgYellowBright: [103, 49],
3298
+ bgBlueBright: [104, 49],
3299
+ bgMagentaBright: [105, 49],
3300
+ bgCyanBright: [106, 49],
3301
+ bgWhiteBright: [107, 49]
3302
+ }
3303
+ };
3304
+ var modifierNames = Object.keys(styles.modifier);
3305
+ var foregroundColorNames = Object.keys(styles.color);
3306
+ var backgroundColorNames = Object.keys(styles.bgColor);
3307
+ var colorNames = [...foregroundColorNames, ...backgroundColorNames];
3308
+ function assembleStyles() {
3309
+ const codes = /* @__PURE__ */ new Map();
3310
+ for (const [groupName, group] of Object.entries(styles)) {
3311
+ for (const [styleName, style] of Object.entries(group)) {
3312
+ styles[styleName] = {
3313
+ open: `\x1B[${style[0]}m`,
3314
+ close: `\x1B[${style[1]}m`
3315
+ };
3316
+ group[styleName] = styles[styleName];
3317
+ codes.set(style[0], style[1]);
3318
+ }
3319
+ Object.defineProperty(styles, groupName, {
3320
+ value: group,
3321
+ enumerable: false
3322
+ });
3323
+ }
3324
+ Object.defineProperty(styles, "codes", {
3325
+ value: codes,
3326
+ enumerable: false
3327
+ });
3328
+ styles.color.close = "\x1B[39m";
3329
+ styles.bgColor.close = "\x1B[49m";
3330
+ styles.color.ansi = wrapAnsi16();
3331
+ styles.color.ansi256 = wrapAnsi256();
3332
+ styles.color.ansi16m = wrapAnsi16m();
3333
+ styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
3334
+ styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
3335
+ styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
3336
+ Object.defineProperties(styles, {
3337
+ rgbToAnsi256: {
3338
+ value(red, green, blue) {
3339
+ if (red === green && green === blue) {
3340
+ if (red < 8) {
3341
+ return 16;
3342
+ }
3343
+ if (red > 248) {
3344
+ return 231;
3345
+ }
3346
+ return Math.round((red - 8) / 247 * 24) + 232;
3347
+ }
3348
+ return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
3349
+ },
3350
+ enumerable: false
3351
+ },
3352
+ hexToRgb: {
3353
+ value(hex) {
3354
+ const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
3355
+ if (!matches) {
3356
+ return [0, 0, 0];
3357
+ }
3358
+ let [colorString] = matches;
3359
+ if (colorString.length === 3) {
3360
+ colorString = [...colorString].map((character) => character + character).join("");
3361
+ }
3362
+ const integer = Number.parseInt(colorString, 16);
3363
+ return [
3364
+ /* eslint-disable no-bitwise */
3365
+ integer >> 16 & 255,
3366
+ integer >> 8 & 255,
3367
+ integer & 255
3368
+ /* eslint-enable no-bitwise */
3369
+ ];
3370
+ },
3371
+ enumerable: false
3372
+ },
3373
+ hexToAnsi256: {
3374
+ value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
3375
+ enumerable: false
3376
+ },
3377
+ ansi256ToAnsi: {
3378
+ value(code) {
3379
+ if (code < 8) {
3380
+ return 30 + code;
3381
+ }
3382
+ if (code < 16) {
3383
+ return 90 + (code - 8);
3384
+ }
3385
+ let red;
3386
+ let green;
3387
+ let blue;
3388
+ if (code >= 232) {
3389
+ red = ((code - 232) * 10 + 8) / 255;
3390
+ green = red;
3391
+ blue = red;
3392
+ } else {
3393
+ code -= 16;
3394
+ const remainder = code % 36;
3395
+ red = Math.floor(code / 36) / 5;
3396
+ green = Math.floor(remainder / 6) / 5;
3397
+ blue = remainder % 6 / 5;
3398
+ }
3399
+ const value = Math.max(red, green, blue) * 2;
3400
+ if (value === 0) {
3401
+ return 30;
3402
+ }
3403
+ let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
3404
+ if (value === 2) {
3405
+ result += 60;
3406
+ }
3407
+ return result;
3408
+ },
3409
+ enumerable: false
3410
+ },
3411
+ rgbToAnsi: {
3412
+ value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
3413
+ enumerable: false
3414
+ },
3415
+ hexToAnsi: {
3416
+ value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
3417
+ enumerable: false
3418
+ }
3419
+ });
3420
+ return styles;
3421
+ }
3422
+ var ansiStyles = assembleStyles();
3423
+ var ansi_styles_default = ansiStyles;
3424
+
3425
+ // ../../node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js
3426
+ var import_node_process = __toESM(require("process"), 1);
3427
+ var import_node_os3 = __toESM(require("os"), 1);
3428
+ var import_node_tty = __toESM(require("tty"), 1);
3429
+ function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : import_node_process.default.argv) {
3430
+ const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
3431
+ const position = argv.indexOf(prefix + flag);
3432
+ const terminatorPosition = argv.indexOf("--");
3433
+ return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
3434
+ }
3435
+ var { env } = import_node_process.default;
3436
+ var flagForceColor;
3437
+ if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
3438
+ flagForceColor = 0;
3439
+ } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
3440
+ flagForceColor = 1;
3441
+ }
3442
+ function envForceColor() {
3443
+ if ("FORCE_COLOR" in env) {
3444
+ if (env.FORCE_COLOR === "true") {
3445
+ return 1;
3446
+ }
3447
+ if (env.FORCE_COLOR === "false") {
3448
+ return 0;
3449
+ }
3450
+ return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
3451
+ }
3452
+ }
3453
+ function translateLevel(level) {
3454
+ if (level === 0) {
3455
+ return false;
3456
+ }
3457
+ return {
3458
+ level,
3459
+ hasBasic: true,
3460
+ has256: level >= 2,
3461
+ has16m: level >= 3
3462
+ };
3463
+ }
3464
+ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
3465
+ const noFlagForceColor = envForceColor();
3466
+ if (noFlagForceColor !== void 0) {
3467
+ flagForceColor = noFlagForceColor;
3468
+ }
3469
+ const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
3470
+ if (forceColor === 0) {
3471
+ return 0;
3472
+ }
3473
+ if (sniffFlags) {
3474
+ if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
3475
+ return 3;
3476
+ }
3477
+ if (hasFlag("color=256")) {
3478
+ return 2;
3479
+ }
3480
+ }
3481
+ if ("TF_BUILD" in env && "AGENT_NAME" in env) {
3482
+ return 1;
3483
+ }
3484
+ if (haveStream && !streamIsTTY && forceColor === void 0) {
3485
+ return 0;
3486
+ }
3487
+ const min = forceColor || 0;
3488
+ if (env.TERM === "dumb") {
3489
+ return min;
3490
+ }
3491
+ if (import_node_process.default.platform === "win32") {
3492
+ const osRelease = import_node_os3.default.release().split(".");
3493
+ if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
3494
+ return Number(osRelease[2]) >= 14931 ? 3 : 2;
3495
+ }
3496
+ return 1;
3497
+ }
3498
+ if ("CI" in env) {
3499
+ if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => key in env)) {
3500
+ return 3;
3501
+ }
3502
+ if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") {
3503
+ return 1;
3504
+ }
3505
+ return min;
3506
+ }
3507
+ if ("TEAMCITY_VERSION" in env) {
3508
+ return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
3509
+ }
3510
+ if (env.COLORTERM === "truecolor") {
3511
+ return 3;
3512
+ }
3513
+ if (env.TERM === "xterm-kitty") {
3514
+ return 3;
3515
+ }
3516
+ if (env.TERM === "xterm-ghostty") {
3517
+ return 3;
3518
+ }
3519
+ if (env.TERM === "wezterm") {
3520
+ return 3;
3521
+ }
3522
+ if ("TERM_PROGRAM" in env) {
3523
+ const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
3524
+ switch (env.TERM_PROGRAM) {
3525
+ case "iTerm.app": {
3526
+ return version >= 3 ? 3 : 2;
3527
+ }
3528
+ case "Apple_Terminal": {
3529
+ return 2;
3530
+ }
3531
+ }
3532
+ }
3533
+ if (/-256(color)?$/i.test(env.TERM)) {
3534
+ return 2;
3535
+ }
3536
+ if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
3537
+ return 1;
3538
+ }
3539
+ if ("COLORTERM" in env) {
3540
+ return 1;
3541
+ }
3542
+ return min;
3543
+ }
3544
+ function createSupportsColor(stream, options = {}) {
3545
+ const level = _supportsColor(stream, {
3546
+ streamIsTTY: stream && stream.isTTY,
3547
+ ...options
3548
+ });
3549
+ return translateLevel(level);
3550
+ }
3551
+ var supportsColor = {
3552
+ stdout: createSupportsColor({ isTTY: import_node_tty.default.isatty(1) }),
3553
+ stderr: createSupportsColor({ isTTY: import_node_tty.default.isatty(2) })
3554
+ };
3555
+ var supports_color_default = supportsColor;
3556
+
3557
+ // ../../node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/utilities.js
3558
+ function stringReplaceAll(string, substring, replacer) {
3559
+ let index = string.indexOf(substring);
3560
+ if (index === -1) {
3561
+ return string;
3562
+ }
3563
+ const substringLength = substring.length;
3564
+ let endIndex = 0;
3565
+ let returnValue = "";
3566
+ do {
3567
+ returnValue += string.slice(endIndex, index) + substring + replacer;
3568
+ endIndex = index + substringLength;
3569
+ index = string.indexOf(substring, endIndex);
3570
+ } while (index !== -1);
3571
+ returnValue += string.slice(endIndex);
3572
+ return returnValue;
3573
+ }
3574
+ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
3575
+ let endIndex = 0;
3576
+ let returnValue = "";
3577
+ do {
3578
+ const gotCR = string[index - 1] === "\r";
3579
+ returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix;
3580
+ endIndex = index + 1;
3581
+ index = string.indexOf("\n", endIndex);
3582
+ } while (index !== -1);
3583
+ returnValue += string.slice(endIndex);
3584
+ return returnValue;
3585
+ }
3586
+
3587
+ // ../../node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/index.js
3588
+ var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
3589
+ var GENERATOR = /* @__PURE__ */ Symbol("GENERATOR");
3590
+ var STYLER = /* @__PURE__ */ Symbol("STYLER");
3591
+ var IS_EMPTY = /* @__PURE__ */ Symbol("IS_EMPTY");
3592
+ var levelMapping = [
3593
+ "ansi",
3594
+ "ansi",
3595
+ "ansi256",
3596
+ "ansi16m"
3597
+ ];
3598
+ var styles2 = /* @__PURE__ */ Object.create(null);
3599
+ var applyOptions = (object, options = {}) => {
3600
+ if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
3601
+ throw new Error("The `level` option should be an integer from 0 to 3");
3602
+ }
3603
+ const colorLevel = stdoutColor ? stdoutColor.level : 0;
3604
+ object.level = options.level === void 0 ? colorLevel : options.level;
3605
+ };
3606
+ var chalkFactory = (options) => {
3607
+ const chalk2 = (...strings) => strings.join(" ");
3608
+ applyOptions(chalk2, options);
3609
+ Object.setPrototypeOf(chalk2, createChalk.prototype);
3610
+ return chalk2;
3611
+ };
3612
+ function createChalk(options) {
3613
+ return chalkFactory(options);
3614
+ }
3615
+ Object.setPrototypeOf(createChalk.prototype, Function.prototype);
3616
+ for (const [styleName, style] of Object.entries(ansi_styles_default)) {
3617
+ styles2[styleName] = {
3618
+ get() {
3619
+ const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
3620
+ Object.defineProperty(this, styleName, { value: builder });
3621
+ return builder;
3622
+ }
3623
+ };
3624
+ }
3625
+ styles2.visible = {
3626
+ get() {
3627
+ const builder = createBuilder(this, this[STYLER], true);
3628
+ Object.defineProperty(this, "visible", { value: builder });
3629
+ return builder;
3630
+ }
3631
+ };
3632
+ var getModelAnsi = (model, level, type, ...arguments_) => {
3633
+ if (model === "rgb") {
3634
+ if (level === "ansi16m") {
3635
+ return ansi_styles_default[type].ansi16m(...arguments_);
3636
+ }
3637
+ if (level === "ansi256") {
3638
+ return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
3639
+ }
3640
+ return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
3641
+ }
3642
+ if (model === "hex") {
3643
+ return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
3644
+ }
3645
+ return ansi_styles_default[type][model](...arguments_);
3646
+ };
3647
+ var usedModels = ["rgb", "hex", "ansi256"];
3648
+ for (const model of usedModels) {
3649
+ styles2[model] = {
3650
+ get() {
3651
+ const { level } = this;
3652
+ return function(...arguments_) {
3653
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
3654
+ return createBuilder(this, styler, this[IS_EMPTY]);
3655
+ };
3656
+ }
3657
+ };
3658
+ const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
3659
+ styles2[bgModel] = {
3660
+ get() {
3661
+ const { level } = this;
3662
+ return function(...arguments_) {
3663
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
3664
+ return createBuilder(this, styler, this[IS_EMPTY]);
3665
+ };
3666
+ }
3667
+ };
3668
+ }
3669
+ var proto = Object.defineProperties(() => {
3670
+ }, {
3671
+ ...styles2,
3672
+ level: {
3673
+ enumerable: true,
3674
+ get() {
3675
+ return this[GENERATOR].level;
3676
+ },
3677
+ set(level) {
3678
+ this[GENERATOR].level = level;
3679
+ }
3680
+ }
3681
+ });
3682
+ var createStyler = (open, close, parent) => {
3683
+ let openAll;
3684
+ let closeAll;
3685
+ if (parent === void 0) {
3686
+ openAll = open;
3687
+ closeAll = close;
3688
+ } else {
3689
+ openAll = parent.openAll + open;
3690
+ closeAll = close + parent.closeAll;
3691
+ }
3692
+ return {
3693
+ open,
3694
+ close,
3695
+ openAll,
3696
+ closeAll,
3697
+ parent
3698
+ };
3699
+ };
3700
+ var createBuilder = (self, _styler, _isEmpty) => {
3701
+ const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
3702
+ Object.setPrototypeOf(builder, proto);
3703
+ builder[GENERATOR] = self;
3704
+ builder[STYLER] = _styler;
3705
+ builder[IS_EMPTY] = _isEmpty;
3706
+ return builder;
3707
+ };
3708
+ var applyStyle = (self, string) => {
3709
+ if (self.level <= 0 || !string) {
3710
+ return self[IS_EMPTY] ? "" : string;
3711
+ }
3712
+ let styler = self[STYLER];
3713
+ if (styler === void 0) {
3714
+ return string;
3715
+ }
3716
+ const { openAll, closeAll } = styler;
3717
+ if (string.includes("\x1B")) {
3718
+ while (styler !== void 0) {
3719
+ string = stringReplaceAll(string, styler.close, styler.open);
3720
+ styler = styler.parent;
3721
+ }
3722
+ }
3723
+ const lfIndex = string.indexOf("\n");
3724
+ if (lfIndex !== -1) {
3725
+ string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
3726
+ }
3727
+ return openAll + string + closeAll;
3728
+ };
3729
+ Object.defineProperties(createChalk.prototype, styles2);
3730
+ var chalk = createChalk();
3731
+ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
3732
+ var source_default = chalk;
3733
+
3734
+ // src/core/logger.ts
3735
+ var SEVERITY_COLORS = {
3736
+ info: source_default.green,
3737
+ low: source_default.cyan,
3738
+ medium: source_default.yellow,
3739
+ high: source_default.red,
3740
+ critical: source_default.bgRed.white.bold
3741
+ };
3742
+ var LEVEL_COLORS = {
3743
+ debug: source_default.gray,
3744
+ info: source_default.green,
3745
+ warn: source_default.yellow,
3746
+ error: source_default.red
3747
+ };
3748
+
3749
+ // src/core/run-result.ts
3750
+ var import_node_os4 = __toESM(require("os"));
3751
+ function emptyCounts() {
3752
+ return { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
3753
+ }
3754
+ function summarize(findings) {
3755
+ const counts = emptyCounts();
3756
+ for (const f of findings) counts[f.severity] = (counts[f.severity] || 0) + 1;
3757
+ return counts;
3758
+ }
3759
+ function workerId() {
3760
+ return `${import_node_os4.default.hostname()}/${process.pid}`;
3761
+ }
3762
+
3763
+ // src/commands/scan.ts
3764
+ var SECRET_PATTERNS = [
3765
+ { name: "AWS Access Key", pattern: /(?:AKIA[0-9A-Z]{16})/g, severity: "critical" },
3766
+ { name: "AWS Secret Key", pattern: /(?:aws_secret_access_key|AWS_SECRET)\s*[=:]\s*['"]?([A-Za-z0-9/+=]{40})['"]?/gi, severity: "critical" },
3767
+ { name: "GitHub Token", pattern: /(?:ghp_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{82})/g, severity: "critical" },
3768
+ { name: "Generic API Key", pattern: /(?:api[_-]?key|apikey)\s*[=:]\s*['"]([A-Za-z0-9\-_]{20,})['"]?/gi, severity: "high" },
3769
+ { name: "Generic Secret", pattern: /(?:secret|password|passwd|pwd)\s*[=:]\s*['"]([^'"]{8,})['"]?/gi, severity: "high" },
3770
+ { name: "Private Key", pattern: /-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----/g, severity: "critical" },
3771
+ { name: "JWT Token", pattern: /eyJ[A-Za-z0-9-_]+\.eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_.+/=]*/g, severity: "high" },
3772
+ { name: "Slack Token", pattern: /xox[bpors]-[A-Za-z0-9-]{10,}/g, severity: "critical" },
3773
+ { name: "Stripe Key", pattern: /(?:sk_live_|pk_live_|sk_test_|pk_test_)[A-Za-z0-9]{20,}/g, severity: "critical" },
3774
+ { name: "Database URL", pattern: /(?:postgres|mysql|mongodb|redis):\/\/[^\s'"]+/gi, severity: "high" },
3775
+ { name: "Bearer Token", pattern: /Bearer\s+[A-Za-z0-9\-_\.]{20,}/g, severity: "medium" },
3776
+ { name: "Hex Token (32+)", pattern: /(?:token|key|secret|auth)\s*[=:]\s*['"]?([0-9a-f]{32,})['"]?/gi, severity: "medium" }
3777
+ ];
3778
+ var MISCONFIG_FILES = [
3779
+ { pattern: ".env", message: ".env file found \u2014 may contain secrets" },
3780
+ { pattern: ".env.local", message: ".env.local file found \u2014 may contain secrets" },
3781
+ { pattern: ".env.production", message: ".env.production file found \u2014 may contain secrets" },
3782
+ { pattern: "id_rsa", message: "Private SSH key found" },
3783
+ { pattern: "id_ed25519", message: "Private SSH key found" },
3784
+ { pattern: ".pem", message: "PEM certificate/key file found" },
3785
+ { pattern: ".p12", message: "PKCS#12 keystore found" },
3786
+ { pattern: ".keystore", message: "Keystore file found" }
3787
+ ];
3788
+ var SKIP_DIRS = /* @__PURE__ */ new Set([
3789
+ "node_modules",
3790
+ ".git",
3791
+ ".next",
3792
+ "dist",
3793
+ "build",
3794
+ "__pycache__",
3795
+ ".venv",
3796
+ "vendor",
3797
+ ".terraform",
3798
+ "coverage",
3799
+ ".cache"
3800
+ ]);
3801
+ var SCAN_EXTENSIONS = /* @__PURE__ */ new Set([
3802
+ ".ts",
3803
+ ".js",
3804
+ ".tsx",
3805
+ ".jsx",
3806
+ ".py",
3807
+ ".rb",
3808
+ ".go",
3809
+ ".java",
3810
+ ".php",
3811
+ ".rs",
3812
+ ".c",
3813
+ ".cpp",
3814
+ ".h",
3815
+ ".yml",
3816
+ ".yaml",
3817
+ ".json",
3818
+ ".toml",
3819
+ ".ini",
3820
+ ".cfg",
3821
+ ".conf",
3822
+ ".env",
3823
+ ".sh",
3824
+ ".bash",
3825
+ ".tf",
3826
+ ".hcl",
3827
+ ".xml",
3828
+ ".properties",
3829
+ ".gradle"
3830
+ ]);
3831
+ async function runScan(targetPath) {
3832
+ const findings = [];
3833
+ try {
3834
+ scanDirectory(targetPath, targetPath, findings, () => {
3835
+ });
3836
+ } catch (err) {
3837
+ return {
3838
+ type: "scan",
3839
+ target: targetPath,
3840
+ findings: [],
3841
+ severity_summary: { critical: 0, high: 0, medium: 0, low: 0, info: 0 },
3842
+ summary: `Scan failed: ${err.message}`,
3843
+ error: err.message
3844
+ };
3845
+ }
3846
+ const structured = findings.map((f) => ({
3847
+ type: f.type,
3848
+ severity: f.severity,
3849
+ message: f.message,
3850
+ location: `${f.file}:${f.line}`,
3851
+ details: { file: f.file, line: f.line, snippet: f.snippet }
3852
+ }));
3853
+ const summary = summarize(structured);
3854
+ return {
3855
+ type: "scan",
3856
+ target: targetPath,
3857
+ findings: structured,
3858
+ severity_summary: summary,
3859
+ summary: findings.length === 0 ? "No security issues found" : `${findings.length} issue(s): ${summary.critical}C ${summary.high}H ${summary.medium}M ${summary.low}L`
3860
+ };
3861
+ }
3862
+ function scanDirectory(basePath, currentPath, findings, onFile) {
3863
+ let entries;
3864
+ try {
3865
+ entries = (0, import_node_fs8.readdirSync)(currentPath, { withFileTypes: true });
3866
+ } catch {
3867
+ return;
3868
+ }
3869
+ for (const entry of entries) {
3870
+ const fullPath = (0, import_node_path5.join)(currentPath, entry.name);
3871
+ if (entry.isDirectory()) {
3872
+ if (SKIP_DIRS.has(entry.name)) continue;
3873
+ scanDirectory(basePath, fullPath, findings, onFile);
3874
+ continue;
3875
+ }
3876
+ if (!entry.isFile()) continue;
3877
+ for (const mc of MISCONFIG_FILES) {
3878
+ if (entry.name === mc.pattern || entry.name.endsWith(mc.pattern)) {
3879
+ findings.push({
3880
+ file: (0, import_node_path5.relative)(basePath, fullPath),
3881
+ line: 0,
3882
+ type: "Sensitive File",
3883
+ severity: "high",
3884
+ message: mc.message,
3885
+ snippet: ""
3886
+ });
3887
+ }
3888
+ }
3889
+ const ext = (0, import_node_path5.extname)(entry.name).toLowerCase();
3890
+ if (!SCAN_EXTENSIONS.has(ext) && !entry.name.startsWith(".env")) continue;
3891
+ try {
3892
+ const stat = (0, import_node_fs8.statSync)(fullPath);
3893
+ if (stat.size > 1024 * 1024) continue;
3894
+ } catch {
3895
+ continue;
3896
+ }
3897
+ onFile();
3898
+ let content;
3899
+ try {
3900
+ content = (0, import_node_fs8.readFileSync)(fullPath, "utf-8");
3901
+ } catch {
3902
+ continue;
3903
+ }
3904
+ const lines = content.split("\n");
3905
+ for (let i = 0; i < lines.length; i++) {
3906
+ const line = lines[i];
3907
+ if (line.trim().startsWith("//") && !line.includes("password") && !line.includes("secret")) continue;
3908
+ for (const pattern of SECRET_PATTERNS) {
3909
+ pattern.pattern.lastIndex = 0;
3910
+ if (pattern.pattern.test(line)) {
3911
+ findings.push({
3912
+ file: (0, import_node_path5.relative)(basePath, fullPath),
3913
+ line: i + 1,
3914
+ type: pattern.name,
3915
+ severity: pattern.severity,
3916
+ message: `Possible ${pattern.name} detected`,
3917
+ snippet: line.length > 120 ? line.slice(0, 120) + "..." : line
3918
+ });
3919
+ }
3920
+ }
3921
+ }
3922
+ }
3923
+ }
3924
+
3925
+ // src/commands/pentest.ts
3926
+ var PENTEST_CHECKS = [
3927
+ {
3928
+ name: "XSS Reflected",
3929
+ test: (html) => /<script>alert\(1\)<\/script>|<img\s+src=x\s+onerror=alert/i.test(html),
3930
+ severity: "critical",
3931
+ message: "Reflected XSS payload rendered in response"
3932
+ },
3933
+ {
3934
+ name: "Open Redirect",
3935
+ test: (url, body) => body.includes("location.href") || body.includes("window.location"),
3936
+ severity: "medium",
3937
+ message: "Potential open redirect detected"
3938
+ },
3939
+ {
3940
+ name: "Missing Security Headers",
3941
+ test: (_url, _body, headers) => {
3942
+ const required = ["x-content-type-options", "x-frame-options", "strict-transport-security"];
3943
+ const missing = required.filter((h) => !headers[h.toLowerCase()]);
3944
+ return missing.length > 2;
3945
+ },
3946
+ severity: "low",
3947
+ message: "Missing critical security headers (X-Content-Type-Options, X-Frame-Options, HSTS)"
3948
+ },
3949
+ {
3950
+ name: "Server Version Disclosure",
3951
+ test: (_url, _body, headers) => {
3952
+ return !!(headers["server"] && /apache|nginx|iis|tomcat/i.test(headers["server"]));
3953
+ },
3954
+ severity: "low",
3955
+ message: "Server version disclosed in response header"
3956
+ },
3957
+ {
3958
+ name: "Directory Listing",
3959
+ test: (html) => /Index of\s*\/|<title>Directory Listing/i.test(html),
3960
+ severity: "medium",
3961
+ message: "Directory listing enabled"
3962
+ },
3963
+ {
3964
+ name: "Error Page Information Disclosure",
3965
+ test: (html) => /stack trace|traceback|exception|at\s+\w+\.\w+\(/i.test(html),
3966
+ severity: "medium",
3967
+ message: "Error page reveals internal information"
3968
+ }
3969
+ ];
3970
+ async function runPentest(rawUrl) {
3971
+ const targetUrl = rawUrl.startsWith("http") ? rawUrl : `https://${rawUrl}`;
3972
+ const results = await collectPentestResults(targetUrl);
3973
+ const structured = results.map((r) => ({
3974
+ type: r.type,
3975
+ severity: r.severity === "low" ? "low" : r.severity,
3976
+ message: r.message,
3977
+ location: r.url,
3978
+ details: r.details ? { detail: r.details } : void 0
3979
+ }));
3980
+ const counts = summarize(structured);
3981
+ return {
3982
+ type: "pentest",
3983
+ target: targetUrl,
3984
+ findings: structured,
3985
+ severity_summary: counts,
3986
+ summary: structured.length === 0 ? "No vulnerabilities detected" : `${structured.length} issue(s): ${counts.critical}C ${counts.high}H ${counts.medium}M ${counts.low}L`
3987
+ };
3988
+ }
3989
+ async function collectPentestResults(targetUrl) {
3990
+ const results = [];
3991
+ try {
3992
+ const resp = await fetch(targetUrl, { redirect: "manual" });
3993
+ const headers = {};
3994
+ resp.headers.forEach((v, k) => {
3995
+ headers[k] = v;
3996
+ });
3997
+ const body = await resp.text();
3998
+ for (const check of PENTEST_CHECKS) {
3999
+ try {
4000
+ if (check.test(body, body, headers)) {
4001
+ results.push({ url: targetUrl, type: check.name, severity: check.severity, message: check.message });
4002
+ }
4003
+ } catch {
4004
+ }
4005
+ }
4006
+ } catch {
4007
+ return results;
4008
+ }
4009
+ const sqliPayloads = ["' OR 1=1--", "1' UNION SELECT NULL--", "' AND '1'='1"];
4010
+ for (const payload of sqliPayloads) {
4011
+ try {
4012
+ const testUrl = `${targetUrl}?id=${encodeURIComponent(payload)}`;
4013
+ const resp = await fetch(testUrl, { redirect: "manual", signal: AbortSignal.timeout(5e3) });
4014
+ const body = await resp.text();
4015
+ if (/sql\s+(syntax|error|exception)|mysql|postgres|ORA-\d+/i.test(body)) {
4016
+ results.push({ url: testUrl, type: "SQL Injection", severity: "critical", message: `SQL error with payload: ${payload}` });
4017
+ }
4018
+ } catch {
4019
+ }
4020
+ }
4021
+ const traversalPaths = ["../../etc/passwd", "..%2F..%2Fetc%2Fpasswd", "....//....//etc/passwd"];
4022
+ for (const path of traversalPaths) {
4023
+ try {
4024
+ const testUrl = `${targetUrl}/${path}`;
4025
+ const resp = await fetch(testUrl, { redirect: "manual", signal: AbortSignal.timeout(5e3) });
4026
+ const body = await resp.text();
4027
+ if (/root:.*:0:0:|daemon:.*:1:1:|nobody:.*:65534/i.test(body)) {
4028
+ results.push({ url: testUrl, type: "Path Traversal", severity: "critical", message: `Possible /etc/passwd disclosure: ${path}` });
4029
+ }
4030
+ } catch {
4031
+ }
4032
+ }
4033
+ const methods = ["OPTIONS", "TRACE", "DELETE", "PUT"];
4034
+ for (const method of methods) {
4035
+ try {
4036
+ const resp = await fetch(targetUrl, { method, signal: AbortSignal.timeout(5e3) });
4037
+ if (resp.status < 400 && method !== "OPTIONS") {
4038
+ results.push({ url: targetUrl, type: `Unsafe HTTP Method: ${method}`, severity: "medium", message: `${method} allowed (status ${resp.status})` });
4039
+ }
4040
+ if (method === "OPTIONS") {
4041
+ const allow = resp.headers.get("allow");
4042
+ if (allow && /DELETE|PUT|TRACE/i.test(allow)) {
4043
+ results.push({ url: targetUrl, type: "HTTP Methods", severity: "low", message: `Allowed methods: ${allow}` });
4044
+ }
4045
+ }
4046
+ } catch {
4047
+ }
4048
+ }
4049
+ return results;
4050
+ }
4051
+
4052
+ // src/daemon/workers/runs-worker.ts
4053
+ var API_URL = process.env.THREATCRUSH_API_URL || "https://threatcrush.com";
4054
+ var POLL_INTERVAL_MS = 3e4;
4055
+ var SCHEDULE_INTERVAL_MS = 12e4;
4056
+ var RunsWorker = class {
4057
+ constructor(bus2) {
4058
+ this.bus = bus2;
4059
+ }
4060
+ bus;
4061
+ pollTimer = null;
4062
+ scheduleTimer = null;
4063
+ running = false;
4064
+ orgIds = [];
4065
+ async start() {
4066
+ if (!isLoggedIn()) return;
4067
+ try {
4068
+ await this.refreshOrgs();
4069
+ } catch {
4070
+ }
4071
+ this.bus.announceModule("runs-worker", "running", `poll ${POLL_INTERVAL_MS / 1e3}s`);
4072
+ this.tick();
4073
+ this.scheduleTick();
4074
+ this.pollTimer = setInterval(() => this.tick(), POLL_INTERVAL_MS);
4075
+ this.scheduleTimer = setInterval(() => this.scheduleTick(), SCHEDULE_INTERVAL_MS);
4076
+ }
4077
+ stop() {
4078
+ if (this.pollTimer) clearInterval(this.pollTimer);
4079
+ if (this.scheduleTimer) clearInterval(this.scheduleTimer);
4080
+ this.pollTimer = null;
4081
+ this.scheduleTimer = null;
4082
+ this.bus.announceModule("runs-worker", "stopped");
4083
+ }
4084
+ async scheduleTick() {
4085
+ if (!isLoggedIn()) return;
4086
+ try {
4087
+ if (this.orgIds.length === 0) await this.refreshOrgs();
4088
+ for (const orgId of this.orgIds) {
4089
+ try {
4090
+ await fetch(`${API_URL}/api/orgs/${orgId}/schedules/tick`, {
4091
+ method: "POST",
4092
+ headers: authHeaders()
4093
+ });
4094
+ } catch {
4095
+ }
4096
+ }
4097
+ } catch {
4098
+ }
4099
+ }
4100
+ async tick() {
4101
+ if (this.running) return;
4102
+ if (!isLoggedIn()) return;
4103
+ this.running = true;
4104
+ try {
4105
+ if (this.orgIds.length === 0) await this.refreshOrgs();
4106
+ for (const orgId of this.orgIds) {
4107
+ const claimed = await this.claimOne(orgId);
4108
+ if (!claimed) continue;
4109
+ const result = await this.execute(claimed);
4110
+ await this.finalize(orgId, claimed, result);
4111
+ }
4112
+ } catch {
4113
+ } finally {
4114
+ this.running = false;
4115
+ }
4116
+ }
4117
+ async refreshOrgs() {
4118
+ const res = await fetch(`${API_URL}/api/orgs`, { headers: authHeaders() });
4119
+ if (!res.ok) return;
4120
+ const data = await res.json();
4121
+ this.orgIds = (data.organizations || []).map((o) => o.id);
4122
+ }
4123
+ async claimOne(orgId) {
4124
+ try {
4125
+ const res = await fetch(`${API_URL}/api/orgs/${orgId}/runs/pending`, {
4126
+ method: "POST",
4127
+ headers: authHeaders(),
4128
+ body: JSON.stringify({ worker_id: workerId() })
4129
+ });
4130
+ if (!res.ok) return null;
4131
+ const data = await res.json();
4132
+ return data.run ?? null;
4133
+ } catch {
4134
+ return null;
4135
+ }
4136
+ }
4137
+ async execute(run) {
4138
+ const target = run.property?.target;
4139
+ if (!target) {
4140
+ return {
4141
+ type: run.type,
4142
+ target: "",
4143
+ findings: [],
4144
+ severity_summary: { critical: 0, high: 0, medium: 0, low: 0, info: 0 },
4145
+ summary: "property target missing",
4146
+ error: "property target missing"
4147
+ };
4148
+ }
4149
+ this.bus.announceModule("runs-worker", "running", `${run.type} ${run.property?.name || target}`);
4150
+ try {
4151
+ if (run.type === "scan") return await runScan(target);
4152
+ return await runPentest(target);
4153
+ } catch (err) {
4154
+ return {
4155
+ type: run.type,
4156
+ target,
4157
+ findings: [],
4158
+ severity_summary: { critical: 0, high: 0, medium: 0, low: 0, info: 0 },
4159
+ summary: `failed: ${err.message}`,
4160
+ error: err.message
4161
+ };
4162
+ }
4163
+ }
4164
+ async finalize(orgId, claimed, result) {
4165
+ try {
4166
+ await fetch(
4167
+ `${API_URL}/api/orgs/${orgId}/properties/${claimed.property_id}/runs/${claimed.id}`,
4168
+ {
4169
+ method: "PATCH",
4170
+ headers: authHeaders(),
4171
+ body: JSON.stringify({
4172
+ status: result.error ? "failed" : "succeeded",
4173
+ findings_count: result.findings.length,
4174
+ severity_summary: result.severity_summary,
4175
+ summary: result.summary,
4176
+ findings: result.findings,
4177
+ error: result.error,
4178
+ source: "daemon",
4179
+ worker_id: workerId()
4180
+ })
4181
+ }
4182
+ );
4183
+ } catch {
4184
+ } finally {
4185
+ this.bus.announceModule("runs-worker", "idle");
4186
+ }
4187
+ }
4188
+ };
4189
+
4190
+ // src/core/telemetry.ts
4191
+ var ready = false;
4192
+ var sentry = null;
4193
+ async function loadSentry() {
4194
+ if (sentry) return;
4195
+ try {
4196
+ sentry = await import("@sentry/node");
4197
+ } catch {
4198
+ sentry = null;
4199
+ }
4200
+ }
4201
+ async function initTelemetry(context) {
4202
+ if (ready) return;
4203
+ const dsn = process.env.SENTRY_DSN;
4204
+ if (!dsn) return;
4205
+ await loadSentry();
4206
+ if (!sentry) return;
4207
+ sentry.init({
4208
+ dsn,
4209
+ environment: process.env.NODE_ENV || "production",
4210
+ release: process.env.SENTRY_RELEASE,
4211
+ serverName: context,
4212
+ tracesSampleRate: 0,
4213
+ beforeSend(event) {
4214
+ const req = event.request;
4215
+ if (req?.headers) {
4216
+ delete req.headers.authorization;
4217
+ delete req.headers.cookie;
4218
+ }
4219
+ return event;
4220
+ }
4221
+ });
4222
+ ready = true;
4223
+ }
4224
+ function captureException(err) {
4225
+ if (!ready || !sentry) return;
4226
+ sentry.captureException(err);
4227
+ }
4228
+ async function flushTelemetry(timeoutMs = 2e3) {
4229
+ if (!ready || !sentry) return;
4230
+ try {
4231
+ await sentry.flush(timeoutMs);
4232
+ } catch {
4233
+ }
4234
+ }
4235
+
4236
+ // src/daemon/index.ts
4237
+ function readVersion() {
4238
+ try {
4239
+ const pkg = JSON.parse((0, import_node_fs9.readFileSync)((0, import_node_path6.join)(__dirname, "..", "package.json"), "utf-8"));
4240
+ return pkg.version || "0.0.0";
4241
+ } catch {
4242
+ return "0.0.0";
4243
+ }
4244
+ }
4245
+ function logLine(line) {
4246
+ try {
4247
+ (0, import_node_fs9.appendFileSync)(PATHS.logFile, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
4248
+ `);
4249
+ } catch {
4250
+ }
4251
+ }
4252
+ async function runDaemon() {
4253
+ if (findRunningDaemon()) {
4254
+ console.error(`threatcrushd already running (pid file at ${PATHS.pidFile}).`);
4255
+ process.exit(1);
4256
+ }
4257
+ ensureRuntimeDirs();
4258
+ writePidFile();
4259
+ await initTelemetry("daemon");
4260
+ process.on("uncaughtException", (err) => {
4261
+ logLine(`[daemon] uncaughtException: ${err.message}`);
4262
+ captureException(err);
4263
+ });
4264
+ process.on("unhandledRejection", (reason) => {
4265
+ logLine(`[daemon] unhandledRejection: ${String(reason)}`);
4266
+ captureException(reason);
4267
+ });
4268
+ const version = readVersion();
4269
+ logLine(`[daemon] starting threatcrushd v${version} mode=${PATHS.mode}`);
4270
+ try {
4271
+ initStateDB(PATHS.stateDb);
4272
+ } catch (err) {
4273
+ logLine(`[daemon] state db unavailable: ${err.message}`);
4274
+ }
4275
+ const config = loadConfig((0, import_node_fs9.existsSync)(PATHS.configFile) ? PATHS.configFile : void 0);
4276
+ bus.on("event", (event) => {
4277
+ logLine(`[event] ${event.severity} ${event.module} ${event.message}`);
4278
+ });
4279
+ const moduleHost = new ModuleHost(bus);
4280
+ await moduleHost.start();
4281
+ new AlertDispatcher(bus, config);
4282
+ const runsWorker = new RunsWorker(bus);
4283
+ try {
4284
+ await runsWorker.start();
4285
+ } catch (err) {
4286
+ logLine(`[daemon] runs-worker failed to start: ${err.message}`);
4287
+ }
4288
+ const ipc = new IpcServer(version, moduleHost);
4289
+ await ipc.start();
4290
+ logLine(`[daemon] ipc listening on ${PATHS.socket}`);
4291
+ const shutdown = async (signal) => {
4292
+ logLine(`[daemon] received ${signal}, shutting down`);
4293
+ try {
4294
+ runsWorker.stop();
4295
+ } catch {
4296
+ }
4297
+ try {
4298
+ await moduleHost.stop();
4299
+ } catch {
4300
+ }
4301
+ try {
4302
+ await ipc.stop();
4303
+ } catch {
4304
+ }
4305
+ try {
4306
+ closeDB();
4307
+ } catch {
4308
+ }
4309
+ try {
4310
+ await flushTelemetry();
4311
+ } catch {
4312
+ }
4313
+ removePidFile();
4314
+ process.exit(0);
4315
+ };
4316
+ process.on("SIGINT", () => {
4317
+ void shutdown("SIGINT");
4318
+ });
4319
+ process.on("SIGTERM", () => {
4320
+ void shutdown("SIGTERM");
4321
+ });
4322
+ process.on("SIGHUP", () => {
4323
+ void shutdown("SIGHUP");
4324
+ });
4325
+ setInterval(() => {
4326
+ }, 1 << 30);
4327
+ }
4328
+
4329
+ // src/daemon-entry.ts
4330
+ runDaemon().catch((err) => {
4331
+ console.error("threatcrushd failed to start:", err);
4332
+ process.exit(1);
4333
+ });
4334
+ //# sourceMappingURL=daemon.js.map