@kinosec/cli 0.3.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1376 -835
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -7750,6 +7750,27 @@ function parseApiError(body, status) {
|
|
|
7750
7750
|
requestId: typeof b.request_id === "string" ? b.request_id : "unknown"
|
|
7751
7751
|
});
|
|
7752
7752
|
}
|
|
7753
|
+
var CredentialsResource = class {
|
|
7754
|
+
constructor(api) {
|
|
7755
|
+
this.api = api;
|
|
7756
|
+
}
|
|
7757
|
+
api;
|
|
7758
|
+
async set(assetId, params) {
|
|
7759
|
+
return this.api.put(`v1/assets/${assetId}/credentials`, { json: params }).json();
|
|
7760
|
+
}
|
|
7761
|
+
async get(assetId) {
|
|
7762
|
+
return this.api.get(`v1/assets/${assetId}/credentials`).json();
|
|
7763
|
+
}
|
|
7764
|
+
async remove(assetId) {
|
|
7765
|
+
return this.api.delete(`v1/assets/${assetId}/credentials`).json();
|
|
7766
|
+
}
|
|
7767
|
+
async setTarget(assetId, params) {
|
|
7768
|
+
return this.api.put(`v1/assets/${assetId}/credentials/target`, { json: params }).json();
|
|
7769
|
+
}
|
|
7770
|
+
async removeTarget(assetId) {
|
|
7771
|
+
return this.api.delete(`v1/assets/${assetId}/credentials/target`).json();
|
|
7772
|
+
}
|
|
7773
|
+
};
|
|
7753
7774
|
var IDEMPOTENT_EXACT_PATHS = ["/v1/scans", "/v1/tokens"];
|
|
7754
7775
|
var IDEMPOTENT_SUFFIXES = ["/cancel", "/retest"];
|
|
7755
7776
|
function needsIdempotencyKey(method, path) {
|
|
@@ -7831,8 +7852,10 @@ var KinoSecClient = class {
|
|
|
7831
7852
|
var AssetsResource = class {
|
|
7832
7853
|
constructor(api) {
|
|
7833
7854
|
this.api = api;
|
|
7855
|
+
this.credentials = new CredentialsResource(api);
|
|
7834
7856
|
}
|
|
7835
7857
|
api;
|
|
7858
|
+
credentials;
|
|
7836
7859
|
async list(params = {}) {
|
|
7837
7860
|
return this.api.get("v1/assets", { searchParams: buildSearchParams(params) }).json();
|
|
7838
7861
|
}
|
|
@@ -9260,238 +9283,552 @@ Examples:
|
|
|
9260
9283
|
});
|
|
9261
9284
|
}
|
|
9262
9285
|
|
|
9263
|
-
//
|
|
9264
|
-
|
|
9286
|
+
// src/commands/pentest.ts
|
|
9287
|
+
var import_picocolors6 = __toESM(require_picocolors(), 1);
|
|
9265
9288
|
|
|
9266
|
-
//
|
|
9267
|
-
var
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
// 21 isn't widely supported and 22 does the same thing
|
|
9275
|
-
bold: [1, 22],
|
|
9276
|
-
dim: [2, 22],
|
|
9277
|
-
italic: [3, 23],
|
|
9278
|
-
underline: [4, 24],
|
|
9279
|
-
overline: [53, 55],
|
|
9280
|
-
inverse: [7, 27],
|
|
9281
|
-
hidden: [8, 28],
|
|
9282
|
-
strikethrough: [9, 29]
|
|
9283
|
-
},
|
|
9284
|
-
color: {
|
|
9285
|
-
black: [30, 39],
|
|
9286
|
-
red: [31, 39],
|
|
9287
|
-
green: [32, 39],
|
|
9288
|
-
yellow: [33, 39],
|
|
9289
|
-
blue: [34, 39],
|
|
9290
|
-
magenta: [35, 39],
|
|
9291
|
-
cyan: [36, 39],
|
|
9292
|
-
white: [37, 39],
|
|
9293
|
-
// Bright color
|
|
9294
|
-
blackBright: [90, 39],
|
|
9295
|
-
gray: [90, 39],
|
|
9296
|
-
// Alias of `blackBright`
|
|
9297
|
-
grey: [90, 39],
|
|
9298
|
-
// Alias of `blackBright`
|
|
9299
|
-
redBright: [91, 39],
|
|
9300
|
-
greenBright: [92, 39],
|
|
9301
|
-
yellowBright: [93, 39],
|
|
9302
|
-
blueBright: [94, 39],
|
|
9303
|
-
magentaBright: [95, 39],
|
|
9304
|
-
cyanBright: [96, 39],
|
|
9305
|
-
whiteBright: [97, 39]
|
|
9306
|
-
},
|
|
9307
|
-
bgColor: {
|
|
9308
|
-
bgBlack: [40, 49],
|
|
9309
|
-
bgRed: [41, 49],
|
|
9310
|
-
bgGreen: [42, 49],
|
|
9311
|
-
bgYellow: [43, 49],
|
|
9312
|
-
bgBlue: [44, 49],
|
|
9313
|
-
bgMagenta: [45, 49],
|
|
9314
|
-
bgCyan: [46, 49],
|
|
9315
|
-
bgWhite: [47, 49],
|
|
9316
|
-
// Bright color
|
|
9317
|
-
bgBlackBright: [100, 49],
|
|
9318
|
-
bgGray: [100, 49],
|
|
9319
|
-
// Alias of `bgBlackBright`
|
|
9320
|
-
bgGrey: [100, 49],
|
|
9321
|
-
// Alias of `bgBlackBright`
|
|
9322
|
-
bgRedBright: [101, 49],
|
|
9323
|
-
bgGreenBright: [102, 49],
|
|
9324
|
-
bgYellowBright: [103, 49],
|
|
9325
|
-
bgBlueBright: [104, 49],
|
|
9326
|
-
bgMagentaBright: [105, 49],
|
|
9327
|
-
bgCyanBright: [106, 49],
|
|
9328
|
-
bgWhiteBright: [107, 49]
|
|
9329
|
-
}
|
|
9330
|
-
};
|
|
9331
|
-
var modifierNames2 = Object.keys(styles2.modifier);
|
|
9332
|
-
var foregroundColorNames2 = Object.keys(styles2.color);
|
|
9333
|
-
var backgroundColorNames2 = Object.keys(styles2.bgColor);
|
|
9334
|
-
var colorNames2 = [...foregroundColorNames2, ...backgroundColorNames2];
|
|
9335
|
-
function assembleStyles2() {
|
|
9336
|
-
const codes = /* @__PURE__ */ new Map();
|
|
9337
|
-
for (const [groupName, group] of Object.entries(styles2)) {
|
|
9338
|
-
for (const [styleName, style] of Object.entries(group)) {
|
|
9339
|
-
styles2[styleName] = {
|
|
9340
|
-
open: `\x1B[${style[0]}m`,
|
|
9341
|
-
close: `\x1B[${style[1]}m`
|
|
9342
|
-
};
|
|
9343
|
-
group[styleName] = styles2[styleName];
|
|
9344
|
-
codes.set(style[0], style[1]);
|
|
9345
|
-
}
|
|
9346
|
-
Object.defineProperty(styles2, groupName, {
|
|
9347
|
-
value: group,
|
|
9348
|
-
enumerable: false
|
|
9349
|
-
});
|
|
9289
|
+
// src/lib/confirm.ts
|
|
9290
|
+
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
9291
|
+
import { createInterface } from "readline";
|
|
9292
|
+
async function confirmDestructive(message, yes) {
|
|
9293
|
+
if (yes) return;
|
|
9294
|
+
if (!isTTY()) {
|
|
9295
|
+
console.error("Use --yes to confirm destructive operations in non-interactive mode.");
|
|
9296
|
+
process.exit(2);
|
|
9350
9297
|
}
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
});
|
|
9355
|
-
styles2.color.close = "\x1B[39m";
|
|
9356
|
-
styles2.bgColor.close = "\x1B[49m";
|
|
9357
|
-
styles2.color.ansi = wrapAnsi162();
|
|
9358
|
-
styles2.color.ansi256 = wrapAnsi2562();
|
|
9359
|
-
styles2.color.ansi16m = wrapAnsi16m2();
|
|
9360
|
-
styles2.bgColor.ansi = wrapAnsi162(ANSI_BACKGROUND_OFFSET2);
|
|
9361
|
-
styles2.bgColor.ansi256 = wrapAnsi2562(ANSI_BACKGROUND_OFFSET2);
|
|
9362
|
-
styles2.bgColor.ansi16m = wrapAnsi16m2(ANSI_BACKGROUND_OFFSET2);
|
|
9363
|
-
Object.defineProperties(styles2, {
|
|
9364
|
-
rgbToAnsi256: {
|
|
9365
|
-
value(red, green, blue) {
|
|
9366
|
-
if (red === green && green === blue) {
|
|
9367
|
-
if (red < 8) {
|
|
9368
|
-
return 16;
|
|
9369
|
-
}
|
|
9370
|
-
if (red > 248) {
|
|
9371
|
-
return 231;
|
|
9372
|
-
}
|
|
9373
|
-
return Math.round((red - 8) / 247 * 24) + 232;
|
|
9374
|
-
}
|
|
9375
|
-
return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
|
|
9376
|
-
},
|
|
9377
|
-
enumerable: false
|
|
9378
|
-
},
|
|
9379
|
-
hexToRgb: {
|
|
9380
|
-
value(hex) {
|
|
9381
|
-
const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
|
|
9382
|
-
if (!matches) {
|
|
9383
|
-
return [0, 0, 0];
|
|
9384
|
-
}
|
|
9385
|
-
let [colorString] = matches;
|
|
9386
|
-
if (colorString.length === 3) {
|
|
9387
|
-
colorString = [...colorString].map((character) => character + character).join("");
|
|
9388
|
-
}
|
|
9389
|
-
const integer = Number.parseInt(colorString, 16);
|
|
9390
|
-
return [
|
|
9391
|
-
/* eslint-disable no-bitwise */
|
|
9392
|
-
integer >> 16 & 255,
|
|
9393
|
-
integer >> 8 & 255,
|
|
9394
|
-
integer & 255
|
|
9395
|
-
/* eslint-enable no-bitwise */
|
|
9396
|
-
];
|
|
9397
|
-
},
|
|
9398
|
-
enumerable: false
|
|
9399
|
-
},
|
|
9400
|
-
hexToAnsi256: {
|
|
9401
|
-
value: (hex) => styles2.rgbToAnsi256(...styles2.hexToRgb(hex)),
|
|
9402
|
-
enumerable: false
|
|
9403
|
-
},
|
|
9404
|
-
ansi256ToAnsi: {
|
|
9405
|
-
value(code) {
|
|
9406
|
-
if (code < 8) {
|
|
9407
|
-
return 30 + code;
|
|
9408
|
-
}
|
|
9409
|
-
if (code < 16) {
|
|
9410
|
-
return 90 + (code - 8);
|
|
9411
|
-
}
|
|
9412
|
-
let red;
|
|
9413
|
-
let green;
|
|
9414
|
-
let blue;
|
|
9415
|
-
if (code >= 232) {
|
|
9416
|
-
red = ((code - 232) * 10 + 8) / 255;
|
|
9417
|
-
green = red;
|
|
9418
|
-
blue = red;
|
|
9419
|
-
} else {
|
|
9420
|
-
code -= 16;
|
|
9421
|
-
const remainder = code % 36;
|
|
9422
|
-
red = Math.floor(code / 36) / 5;
|
|
9423
|
-
green = Math.floor(remainder / 6) / 5;
|
|
9424
|
-
blue = remainder % 6 / 5;
|
|
9425
|
-
}
|
|
9426
|
-
const value = Math.max(red, green, blue) * 2;
|
|
9427
|
-
if (value === 0) {
|
|
9428
|
-
return 30;
|
|
9429
|
-
}
|
|
9430
|
-
let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
|
|
9431
|
-
if (value === 2) {
|
|
9432
|
-
result += 60;
|
|
9433
|
-
}
|
|
9434
|
-
return result;
|
|
9435
|
-
},
|
|
9436
|
-
enumerable: false
|
|
9437
|
-
},
|
|
9438
|
-
rgbToAnsi: {
|
|
9439
|
-
value: (red, green, blue) => styles2.ansi256ToAnsi(styles2.rgbToAnsi256(red, green, blue)),
|
|
9440
|
-
enumerable: false
|
|
9441
|
-
},
|
|
9442
|
-
hexToAnsi: {
|
|
9443
|
-
value: (hex) => styles2.ansi256ToAnsi(styles2.hexToAnsi256(hex)),
|
|
9444
|
-
enumerable: false
|
|
9445
|
-
}
|
|
9298
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
9299
|
+
const answer = await new Promise((resolve) => {
|
|
9300
|
+
rl.question(`${message} (y/N) `, resolve);
|
|
9446
9301
|
});
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
// ../../node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js
|
|
9453
|
-
import process2 from "process";
|
|
9454
|
-
import os from "os";
|
|
9455
|
-
import tty from "tty";
|
|
9456
|
-
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
|
|
9457
|
-
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
9458
|
-
const position = argv.indexOf(prefix + flag);
|
|
9459
|
-
const terminatorPosition = argv.indexOf("--");
|
|
9460
|
-
return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
|
|
9461
|
-
}
|
|
9462
|
-
var { env } = process2;
|
|
9463
|
-
var flagForceColor;
|
|
9464
|
-
if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
|
|
9465
|
-
flagForceColor = 0;
|
|
9466
|
-
} else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
|
|
9467
|
-
flagForceColor = 1;
|
|
9468
|
-
}
|
|
9469
|
-
function envForceColor() {
|
|
9470
|
-
if ("FORCE_COLOR" in env) {
|
|
9471
|
-
if (env.FORCE_COLOR === "true") {
|
|
9472
|
-
return 1;
|
|
9473
|
-
}
|
|
9474
|
-
if (env.FORCE_COLOR === "false") {
|
|
9475
|
-
return 0;
|
|
9476
|
-
}
|
|
9477
|
-
return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
|
|
9302
|
+
rl.close();
|
|
9303
|
+
if (answer.toLowerCase() !== "y") {
|
|
9304
|
+
console.log("Cancelled.");
|
|
9305
|
+
process.exit(0);
|
|
9478
9306
|
}
|
|
9479
9307
|
}
|
|
9480
|
-
function
|
|
9481
|
-
if (
|
|
9482
|
-
|
|
9308
|
+
async function confirmAuthorization(target, yes) {
|
|
9309
|
+
if (yes) return;
|
|
9310
|
+
if (!isTTY()) {
|
|
9311
|
+
console.error("Use --yes to accept authorization terms in non-interactive mode.");
|
|
9312
|
+
process.exit(2);
|
|
9483
9313
|
}
|
|
9484
|
-
|
|
9485
|
-
|
|
9486
|
-
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
|
|
9490
|
-
|
|
9491
|
-
|
|
9492
|
-
|
|
9493
|
-
|
|
9494
|
-
|
|
9314
|
+
const warning = import_picocolors3.default.yellow("\u26A0");
|
|
9315
|
+
console.error(`
|
|
9316
|
+
${warning} ${import_picocolors3.default.bold("Authorization Required")}
|
|
9317
|
+
|
|
9318
|
+
Penetration testing without proper authorization is illegal
|
|
9319
|
+
and may result in criminal prosecution.
|
|
9320
|
+
|
|
9321
|
+
${import_picocolors3.default.bold("Target:")} ${target}
|
|
9322
|
+
|
|
9323
|
+
By proceeding, you confirm that:
|
|
9324
|
+
${import_picocolors3.default.dim("\u2022")} You own or have explicit written authorization to test this target
|
|
9325
|
+
${import_picocolors3.default.dim("\u2022")} You will comply with all applicable laws and regulations
|
|
9326
|
+
${import_picocolors3.default.dim("\u2022")} You will not use the results for illegal or malicious purposes
|
|
9327
|
+
${import_picocolors3.default.dim("\u2022")} You accept full legal responsibility for this pentest
|
|
9328
|
+
|
|
9329
|
+
${import_picocolors3.default.dim("Unauthorized access to computer systems is a criminal offense under")}
|
|
9330
|
+
${import_picocolors3.default.dim("laws such as the CFAA (US), Computer Misuse Act (UK), and similar")}
|
|
9331
|
+
${import_picocolors3.default.dim("legislation worldwide. KinoSec bears no liability for unauthorized")}
|
|
9332
|
+
${import_picocolors3.default.dim("use of its services.")}
|
|
9333
|
+
`);
|
|
9334
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
9335
|
+
const answer = await new Promise((resolve) => {
|
|
9336
|
+
rl.question("Agree and continue? (y/N) ", resolve);
|
|
9337
|
+
});
|
|
9338
|
+
rl.close();
|
|
9339
|
+
if (answer.toLowerCase() !== "y") {
|
|
9340
|
+
console.log("Cancelled.");
|
|
9341
|
+
process.exit(0);
|
|
9342
|
+
}
|
|
9343
|
+
}
|
|
9344
|
+
|
|
9345
|
+
// src/commands/schedule.ts
|
|
9346
|
+
var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
9347
|
+
var VALID_PATTERNS = ["once", "daily", "weekly", "biweekly", "monthly"];
|
|
9348
|
+
var VALID_MODES = ["quick", "full"];
|
|
9349
|
+
var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
9350
|
+
function extractTime(scheduledAt) {
|
|
9351
|
+
if (!scheduledAt) return "\u2014";
|
|
9352
|
+
const d = new Date(scheduledAt);
|
|
9353
|
+
return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: false });
|
|
9354
|
+
}
|
|
9355
|
+
function formatPattern(s) {
|
|
9356
|
+
const time = extractTime(s.scheduled_at);
|
|
9357
|
+
switch (s.recurrence_pattern) {
|
|
9358
|
+
case "once":
|
|
9359
|
+
return `Once at ${time}`;
|
|
9360
|
+
case "daily":
|
|
9361
|
+
return `Daily at ${time}`;
|
|
9362
|
+
case "weekly":
|
|
9363
|
+
return `Weekly ${s.day_of_week !== null ? DAY_NAMES[s.day_of_week] : ""} at ${time}`;
|
|
9364
|
+
case "biweekly":
|
|
9365
|
+
return `Every 2 weeks ${s.day_of_week !== null ? DAY_NAMES[s.day_of_week] : ""} at ${time}`;
|
|
9366
|
+
case "monthly":
|
|
9367
|
+
return `Monthly day ${s.day_of_month ?? "?"} at ${time}`;
|
|
9368
|
+
default:
|
|
9369
|
+
return s.recurrence_pattern;
|
|
9370
|
+
}
|
|
9371
|
+
}
|
|
9372
|
+
function registerScheduleCommand(pentestCmd) {
|
|
9373
|
+
const schedule = pentestCmd.command("schedule").description("Manage scheduled pentests");
|
|
9374
|
+
schedule.command("list").description("List scheduled pentests").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").option("--status <status>", "Filter: active, paused, or all", "all").addHelpText("after", `
|
|
9375
|
+
Examples:
|
|
9376
|
+
$ kinosec pentest schedule list
|
|
9377
|
+
$ kinosec pentest schedule list --status active --json`).action(async (options) => {
|
|
9378
|
+
try {
|
|
9379
|
+
const opts = schedule.parent.parent.opts();
|
|
9380
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
9381
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
9382
|
+
const result = await client.schedules.list({
|
|
9383
|
+
limit: parseInt(options.limit, 10),
|
|
9384
|
+
cursor: options.cursor,
|
|
9385
|
+
status: options.status
|
|
9386
|
+
});
|
|
9387
|
+
if (opts.json || opts.output === "json") {
|
|
9388
|
+
printJson(result);
|
|
9389
|
+
return;
|
|
9390
|
+
}
|
|
9391
|
+
if (result.items.length === 0) {
|
|
9392
|
+
console.log("No scheduled pentests found.");
|
|
9393
|
+
return;
|
|
9394
|
+
}
|
|
9395
|
+
printTable(
|
|
9396
|
+
["ID", "Target", "Mode", "Schedule", "Status", "Next Run"],
|
|
9397
|
+
result.items.map((s) => [
|
|
9398
|
+
s.id,
|
|
9399
|
+
truncate(s.domain_or_ip, 25),
|
|
9400
|
+
s.scan_mode,
|
|
9401
|
+
formatPattern(s),
|
|
9402
|
+
s.is_active ? import_picocolors4.default.green("active") : import_picocolors4.default.yellow("paused"),
|
|
9403
|
+
s.next_scheduled_run ? new Date(s.next_scheduled_run).toLocaleString() : "\u2014"
|
|
9404
|
+
])
|
|
9405
|
+
);
|
|
9406
|
+
if (result.next_cursor) {
|
|
9407
|
+
console.log(import_picocolors4.default.dim(`
|
|
9408
|
+
More results available. Use --cursor ${result.next_cursor}`));
|
|
9409
|
+
}
|
|
9410
|
+
} catch (error) {
|
|
9411
|
+
handleError(error);
|
|
9412
|
+
}
|
|
9413
|
+
});
|
|
9414
|
+
schedule.command("get").description("Get schedule details").argument("<id>", "Schedule ID").addHelpText("after", `
|
|
9415
|
+
Examples:
|
|
9416
|
+
$ kinosec pentest schedule get abc123
|
|
9417
|
+
$ kinosec pentest schedule get abc123 --json`).action(async (id) => {
|
|
9418
|
+
try {
|
|
9419
|
+
const opts = schedule.parent.parent.opts();
|
|
9420
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
9421
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
9422
|
+
const s = await client.schedules.get(id);
|
|
9423
|
+
if (opts.json || opts.output === "json") {
|
|
9424
|
+
printJson(s);
|
|
9425
|
+
return;
|
|
9426
|
+
}
|
|
9427
|
+
printTable(
|
|
9428
|
+
["Field", "Value"],
|
|
9429
|
+
[
|
|
9430
|
+
["ID", s.id],
|
|
9431
|
+
["Target", s.domain_or_ip],
|
|
9432
|
+
["Name", s.name],
|
|
9433
|
+
["Mode", s.scan_mode],
|
|
9434
|
+
["Schedule", formatPattern(s)],
|
|
9435
|
+
["Timezone", s.timezone],
|
|
9436
|
+
["Status", s.is_active ? "Active" : "Paused"],
|
|
9437
|
+
["Next Run", s.next_scheduled_run ?? "\u2014"],
|
|
9438
|
+
["Last Run", s.last_scheduled_run ?? "Never"],
|
|
9439
|
+
["Created", s.created_at]
|
|
9440
|
+
]
|
|
9441
|
+
);
|
|
9442
|
+
} catch (error) {
|
|
9443
|
+
handleError(error);
|
|
9444
|
+
}
|
|
9445
|
+
});
|
|
9446
|
+
schedule.command("create").description("Create a scheduled pentest").requiredOption("--asset <id>", "Asset ID to scan").requiredOption("--pattern <pattern>", "Recurrence: once, daily, weekly, biweekly, monthly").option("--name <name>", "Schedule name").option("--mode <mode>", "Scan mode: quick or full", "full").option("--time <HH:MM>", "Time of day (24h format)", "09:00").option("--day-of-week <0-6>", "Day of week (0=Sun..6=Sat) for weekly/biweekly").option("--day-of-month <1-31>", "Day of month for monthly").option("--timezone <tz>", "Timezone (e.g. America/New_York)", Intl.DateTimeFormat().resolvedOptions().timeZone).addHelpText("after", `
|
|
9447
|
+
Examples:
|
|
9448
|
+
$ kinosec pentest schedule create --asset abc123 --pattern weekly --day-of-week 1 --time 09:00
|
|
9449
|
+
$ kinosec pentest schedule create --asset abc123 --pattern daily --mode quick --time 14:00
|
|
9450
|
+
$ kinosec pentest schedule create --asset abc123 --pattern monthly --day-of-month 15 --time 03:00 --timezone UTC`).action(async (options) => {
|
|
9451
|
+
if (!VALID_PATTERNS.includes(options.pattern)) {
|
|
9452
|
+
console.error(`Invalid pattern "${options.pattern}". Must be: ${VALID_PATTERNS.join(", ")}`);
|
|
9453
|
+
process.exit(2);
|
|
9454
|
+
}
|
|
9455
|
+
if (!VALID_MODES.includes(options.mode)) {
|
|
9456
|
+
console.error(`Invalid mode "${options.mode}". Must be: ${VALID_MODES.join(", ")}`);
|
|
9457
|
+
process.exit(2);
|
|
9458
|
+
}
|
|
9459
|
+
if ((options.pattern === "weekly" || options.pattern === "biweekly") && options.dayOfWeek === void 0) {
|
|
9460
|
+
console.error(`--day-of-week is required for ${options.pattern} schedules.`);
|
|
9461
|
+
process.exit(2);
|
|
9462
|
+
}
|
|
9463
|
+
if (options.pattern === "monthly" && options.dayOfMonth === void 0) {
|
|
9464
|
+
console.error("--day-of-month is required for monthly schedules.");
|
|
9465
|
+
process.exit(2);
|
|
9466
|
+
}
|
|
9467
|
+
try {
|
|
9468
|
+
const opts = schedule.parent.parent.opts();
|
|
9469
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
9470
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
9471
|
+
const params = {
|
|
9472
|
+
asset_id: options.domain_or_ip,
|
|
9473
|
+
name: options.name ?? `${options.pattern} ${options.mode} pentest`,
|
|
9474
|
+
scan_mode: options.mode,
|
|
9475
|
+
recurrence_pattern: options.pattern,
|
|
9476
|
+
time_of_day: options.time,
|
|
9477
|
+
timezone: options.timezone
|
|
9478
|
+
};
|
|
9479
|
+
if (options.dayOfWeek !== void 0) params.day_of_week = parseInt(options.dayOfWeek, 10);
|
|
9480
|
+
if (options.dayOfMonth !== void 0) params.day_of_month = parseInt(options.dayOfMonth, 10);
|
|
9481
|
+
const s = await client.schedules.create(params);
|
|
9482
|
+
if (opts.json || opts.output === "json") {
|
|
9483
|
+
printJson(s);
|
|
9484
|
+
return;
|
|
9485
|
+
}
|
|
9486
|
+
printSuccess(`Schedule created: ${s.domain_or_ip}`);
|
|
9487
|
+
console.log(` ID: ${s.id}`);
|
|
9488
|
+
console.log(` Schedule: ${formatPattern(s)}`);
|
|
9489
|
+
console.log(` Next run: ${s.next_scheduled_run ?? "pending"}`);
|
|
9490
|
+
console.log(`
|
|
9491
|
+
Manage: kinosec pentest schedule get ${s.id}`);
|
|
9492
|
+
} catch (error) {
|
|
9493
|
+
handleError(error);
|
|
9494
|
+
}
|
|
9495
|
+
});
|
|
9496
|
+
schedule.command("update").description("Update a schedule").argument("<id>", "Schedule ID").option("--pattern <pattern>", "Recurrence: once, daily, weekly, biweekly, monthly").option("--name <name>", "Schedule name").option("--mode <mode>", "Scan mode: quick or full").option("--time <HH:MM>", "Time of day (24h format)").option("--day-of-week <0-6>", "Day of week (0=Sun..6=Sat)").option("--day-of-month <1-31>", "Day of month").option("--timezone <tz>", "Timezone").addHelpText("after", `
|
|
9497
|
+
Examples:
|
|
9498
|
+
$ kinosec pentest schedule update abc123 --pattern daily --time 14:00
|
|
9499
|
+
$ kinosec pentest schedule update abc123 --mode quick`).action(async (id, options) => {
|
|
9500
|
+
const params = {};
|
|
9501
|
+
if (options.pattern) params.recurrence_pattern = options.pattern;
|
|
9502
|
+
if (options.name) params.name = options.name;
|
|
9503
|
+
if (options.mode) params.scan_mode = options.mode;
|
|
9504
|
+
if (options.time) params.time_of_day = options.time;
|
|
9505
|
+
if (options.dayOfWeek !== void 0) params.day_of_week = parseInt(options.dayOfWeek, 10);
|
|
9506
|
+
if (options.dayOfMonth !== void 0) params.day_of_month = parseInt(options.dayOfMonth, 10);
|
|
9507
|
+
if (options.timezone) params.timezone = options.timezone;
|
|
9508
|
+
if (Object.keys(params).length === 0) {
|
|
9509
|
+
console.error("Provide at least one field to update.");
|
|
9510
|
+
process.exit(2);
|
|
9511
|
+
}
|
|
9512
|
+
try {
|
|
9513
|
+
const opts = schedule.parent.parent.opts();
|
|
9514
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
9515
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
9516
|
+
const s = await client.schedules.update(id, params);
|
|
9517
|
+
if (opts.json || opts.output === "json") {
|
|
9518
|
+
printJson(s);
|
|
9519
|
+
return;
|
|
9520
|
+
}
|
|
9521
|
+
printSuccess(`Schedule updated: ${s.domain_or_ip}`);
|
|
9522
|
+
console.log(` Schedule: ${formatPattern(s)}`);
|
|
9523
|
+
console.log(` Next run: ${s.next_scheduled_run ?? "pending"}`);
|
|
9524
|
+
} catch (error) {
|
|
9525
|
+
handleError(error);
|
|
9526
|
+
}
|
|
9527
|
+
});
|
|
9528
|
+
schedule.command("delete").description("Delete a schedule").argument("<id>", "Schedule ID").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
|
|
9529
|
+
Examples:
|
|
9530
|
+
$ kinosec pentest schedule delete abc123
|
|
9531
|
+
$ kinosec pentest schedule delete abc123 --yes`).action(async (id, options) => {
|
|
9532
|
+
try {
|
|
9533
|
+
await confirmDestructive(`Delete schedule ${id}?`, Boolean(options.yes));
|
|
9534
|
+
const opts = schedule.parent.parent.opts();
|
|
9535
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
9536
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
9537
|
+
await client.schedules.delete(id);
|
|
9538
|
+
if (opts.json || opts.output === "json") {
|
|
9539
|
+
printJson({ deleted: true, id });
|
|
9540
|
+
return;
|
|
9541
|
+
}
|
|
9542
|
+
printSuccess(`Schedule ${id} deleted.`);
|
|
9543
|
+
} catch (error) {
|
|
9544
|
+
handleError(error);
|
|
9545
|
+
}
|
|
9546
|
+
});
|
|
9547
|
+
schedule.command("pause").description("Pause a scheduled pentest").argument("<id>", "Schedule ID").addHelpText("after", `
|
|
9548
|
+
Examples:
|
|
9549
|
+
$ kinosec pentest schedule pause abc123`).action(async (id) => {
|
|
9550
|
+
try {
|
|
9551
|
+
const opts = schedule.parent.parent.opts();
|
|
9552
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
9553
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
9554
|
+
const result = await client.schedules.pause(id);
|
|
9555
|
+
if (opts.json || opts.output === "json") {
|
|
9556
|
+
printJson(result);
|
|
9557
|
+
return;
|
|
9558
|
+
}
|
|
9559
|
+
printSuccess("Schedule paused.");
|
|
9560
|
+
} catch (error) {
|
|
9561
|
+
handleError(error);
|
|
9562
|
+
}
|
|
9563
|
+
});
|
|
9564
|
+
schedule.command("resume").description("Resume a paused schedule").argument("<id>", "Schedule ID").addHelpText("after", `
|
|
9565
|
+
Examples:
|
|
9566
|
+
$ kinosec pentest schedule resume abc123`).action(async (id) => {
|
|
9567
|
+
try {
|
|
9568
|
+
const opts = schedule.parent.parent.opts();
|
|
9569
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
9570
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
9571
|
+
const result = await client.schedules.resume(id);
|
|
9572
|
+
if (opts.json || opts.output === "json") {
|
|
9573
|
+
printJson(result);
|
|
9574
|
+
return;
|
|
9575
|
+
}
|
|
9576
|
+
printSuccess("Schedule resumed.");
|
|
9577
|
+
if (result.next_scheduled_run) {
|
|
9578
|
+
console.log(` Next run: ${new Date(result.next_scheduled_run).toLocaleString()}`);
|
|
9579
|
+
}
|
|
9580
|
+
} catch (error) {
|
|
9581
|
+
handleError(error);
|
|
9582
|
+
}
|
|
9583
|
+
});
|
|
9584
|
+
}
|
|
9585
|
+
|
|
9586
|
+
// src/lib/watch.ts
|
|
9587
|
+
var import_picocolors5 = __toESM(require_picocolors(), 1);
|
|
9588
|
+
|
|
9589
|
+
// ../../node_modules/.pnpm/is-unicode-supported@2.1.0/node_modules/is-unicode-supported/index.js
|
|
9590
|
+
import process2 from "process";
|
|
9591
|
+
function isUnicodeSupported() {
|
|
9592
|
+
const { env: env2 } = process2;
|
|
9593
|
+
const { TERM, TERM_PROGRAM } = env2;
|
|
9594
|
+
if (process2.platform !== "win32") {
|
|
9595
|
+
return TERM !== "linux";
|
|
9596
|
+
}
|
|
9597
|
+
return Boolean(env2.WT_SESSION) || Boolean(env2.TERMINUS_SUBLIME) || env2.ConEmuTask === "{cmd::Cmder}" || TERM_PROGRAM === "Terminus-Sublime" || TERM_PROGRAM === "vscode" || TERM === "xterm-256color" || TERM === "alacritty" || TERM === "rxvt-unicode" || TERM === "rxvt-unicode-256color" || env2.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
9598
|
+
}
|
|
9599
|
+
|
|
9600
|
+
// ../../node_modules/.pnpm/ora@8.2.0/node_modules/ora/index.js
|
|
9601
|
+
import process9 from "process";
|
|
9602
|
+
|
|
9603
|
+
// ../../node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
9604
|
+
var ANSI_BACKGROUND_OFFSET2 = 10;
|
|
9605
|
+
var wrapAnsi162 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
|
|
9606
|
+
var wrapAnsi2562 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
|
|
9607
|
+
var wrapAnsi16m2 = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
|
|
9608
|
+
var styles2 = {
|
|
9609
|
+
modifier: {
|
|
9610
|
+
reset: [0, 0],
|
|
9611
|
+
// 21 isn't widely supported and 22 does the same thing
|
|
9612
|
+
bold: [1, 22],
|
|
9613
|
+
dim: [2, 22],
|
|
9614
|
+
italic: [3, 23],
|
|
9615
|
+
underline: [4, 24],
|
|
9616
|
+
overline: [53, 55],
|
|
9617
|
+
inverse: [7, 27],
|
|
9618
|
+
hidden: [8, 28],
|
|
9619
|
+
strikethrough: [9, 29]
|
|
9620
|
+
},
|
|
9621
|
+
color: {
|
|
9622
|
+
black: [30, 39],
|
|
9623
|
+
red: [31, 39],
|
|
9624
|
+
green: [32, 39],
|
|
9625
|
+
yellow: [33, 39],
|
|
9626
|
+
blue: [34, 39],
|
|
9627
|
+
magenta: [35, 39],
|
|
9628
|
+
cyan: [36, 39],
|
|
9629
|
+
white: [37, 39],
|
|
9630
|
+
// Bright color
|
|
9631
|
+
blackBright: [90, 39],
|
|
9632
|
+
gray: [90, 39],
|
|
9633
|
+
// Alias of `blackBright`
|
|
9634
|
+
grey: [90, 39],
|
|
9635
|
+
// Alias of `blackBright`
|
|
9636
|
+
redBright: [91, 39],
|
|
9637
|
+
greenBright: [92, 39],
|
|
9638
|
+
yellowBright: [93, 39],
|
|
9639
|
+
blueBright: [94, 39],
|
|
9640
|
+
magentaBright: [95, 39],
|
|
9641
|
+
cyanBright: [96, 39],
|
|
9642
|
+
whiteBright: [97, 39]
|
|
9643
|
+
},
|
|
9644
|
+
bgColor: {
|
|
9645
|
+
bgBlack: [40, 49],
|
|
9646
|
+
bgRed: [41, 49],
|
|
9647
|
+
bgGreen: [42, 49],
|
|
9648
|
+
bgYellow: [43, 49],
|
|
9649
|
+
bgBlue: [44, 49],
|
|
9650
|
+
bgMagenta: [45, 49],
|
|
9651
|
+
bgCyan: [46, 49],
|
|
9652
|
+
bgWhite: [47, 49],
|
|
9653
|
+
// Bright color
|
|
9654
|
+
bgBlackBright: [100, 49],
|
|
9655
|
+
bgGray: [100, 49],
|
|
9656
|
+
// Alias of `bgBlackBright`
|
|
9657
|
+
bgGrey: [100, 49],
|
|
9658
|
+
// Alias of `bgBlackBright`
|
|
9659
|
+
bgRedBright: [101, 49],
|
|
9660
|
+
bgGreenBright: [102, 49],
|
|
9661
|
+
bgYellowBright: [103, 49],
|
|
9662
|
+
bgBlueBright: [104, 49],
|
|
9663
|
+
bgMagentaBright: [105, 49],
|
|
9664
|
+
bgCyanBright: [106, 49],
|
|
9665
|
+
bgWhiteBright: [107, 49]
|
|
9666
|
+
}
|
|
9667
|
+
};
|
|
9668
|
+
var modifierNames2 = Object.keys(styles2.modifier);
|
|
9669
|
+
var foregroundColorNames2 = Object.keys(styles2.color);
|
|
9670
|
+
var backgroundColorNames2 = Object.keys(styles2.bgColor);
|
|
9671
|
+
var colorNames2 = [...foregroundColorNames2, ...backgroundColorNames2];
|
|
9672
|
+
function assembleStyles2() {
|
|
9673
|
+
const codes = /* @__PURE__ */ new Map();
|
|
9674
|
+
for (const [groupName, group] of Object.entries(styles2)) {
|
|
9675
|
+
for (const [styleName, style] of Object.entries(group)) {
|
|
9676
|
+
styles2[styleName] = {
|
|
9677
|
+
open: `\x1B[${style[0]}m`,
|
|
9678
|
+
close: `\x1B[${style[1]}m`
|
|
9679
|
+
};
|
|
9680
|
+
group[styleName] = styles2[styleName];
|
|
9681
|
+
codes.set(style[0], style[1]);
|
|
9682
|
+
}
|
|
9683
|
+
Object.defineProperty(styles2, groupName, {
|
|
9684
|
+
value: group,
|
|
9685
|
+
enumerable: false
|
|
9686
|
+
});
|
|
9687
|
+
}
|
|
9688
|
+
Object.defineProperty(styles2, "codes", {
|
|
9689
|
+
value: codes,
|
|
9690
|
+
enumerable: false
|
|
9691
|
+
});
|
|
9692
|
+
styles2.color.close = "\x1B[39m";
|
|
9693
|
+
styles2.bgColor.close = "\x1B[49m";
|
|
9694
|
+
styles2.color.ansi = wrapAnsi162();
|
|
9695
|
+
styles2.color.ansi256 = wrapAnsi2562();
|
|
9696
|
+
styles2.color.ansi16m = wrapAnsi16m2();
|
|
9697
|
+
styles2.bgColor.ansi = wrapAnsi162(ANSI_BACKGROUND_OFFSET2);
|
|
9698
|
+
styles2.bgColor.ansi256 = wrapAnsi2562(ANSI_BACKGROUND_OFFSET2);
|
|
9699
|
+
styles2.bgColor.ansi16m = wrapAnsi16m2(ANSI_BACKGROUND_OFFSET2);
|
|
9700
|
+
Object.defineProperties(styles2, {
|
|
9701
|
+
rgbToAnsi256: {
|
|
9702
|
+
value(red, green, blue) {
|
|
9703
|
+
if (red === green && green === blue) {
|
|
9704
|
+
if (red < 8) {
|
|
9705
|
+
return 16;
|
|
9706
|
+
}
|
|
9707
|
+
if (red > 248) {
|
|
9708
|
+
return 231;
|
|
9709
|
+
}
|
|
9710
|
+
return Math.round((red - 8) / 247 * 24) + 232;
|
|
9711
|
+
}
|
|
9712
|
+
return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
|
|
9713
|
+
},
|
|
9714
|
+
enumerable: false
|
|
9715
|
+
},
|
|
9716
|
+
hexToRgb: {
|
|
9717
|
+
value(hex) {
|
|
9718
|
+
const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
|
|
9719
|
+
if (!matches) {
|
|
9720
|
+
return [0, 0, 0];
|
|
9721
|
+
}
|
|
9722
|
+
let [colorString] = matches;
|
|
9723
|
+
if (colorString.length === 3) {
|
|
9724
|
+
colorString = [...colorString].map((character) => character + character).join("");
|
|
9725
|
+
}
|
|
9726
|
+
const integer = Number.parseInt(colorString, 16);
|
|
9727
|
+
return [
|
|
9728
|
+
/* eslint-disable no-bitwise */
|
|
9729
|
+
integer >> 16 & 255,
|
|
9730
|
+
integer >> 8 & 255,
|
|
9731
|
+
integer & 255
|
|
9732
|
+
/* eslint-enable no-bitwise */
|
|
9733
|
+
];
|
|
9734
|
+
},
|
|
9735
|
+
enumerable: false
|
|
9736
|
+
},
|
|
9737
|
+
hexToAnsi256: {
|
|
9738
|
+
value: (hex) => styles2.rgbToAnsi256(...styles2.hexToRgb(hex)),
|
|
9739
|
+
enumerable: false
|
|
9740
|
+
},
|
|
9741
|
+
ansi256ToAnsi: {
|
|
9742
|
+
value(code) {
|
|
9743
|
+
if (code < 8) {
|
|
9744
|
+
return 30 + code;
|
|
9745
|
+
}
|
|
9746
|
+
if (code < 16) {
|
|
9747
|
+
return 90 + (code - 8);
|
|
9748
|
+
}
|
|
9749
|
+
let red;
|
|
9750
|
+
let green;
|
|
9751
|
+
let blue;
|
|
9752
|
+
if (code >= 232) {
|
|
9753
|
+
red = ((code - 232) * 10 + 8) / 255;
|
|
9754
|
+
green = red;
|
|
9755
|
+
blue = red;
|
|
9756
|
+
} else {
|
|
9757
|
+
code -= 16;
|
|
9758
|
+
const remainder = code % 36;
|
|
9759
|
+
red = Math.floor(code / 36) / 5;
|
|
9760
|
+
green = Math.floor(remainder / 6) / 5;
|
|
9761
|
+
blue = remainder % 6 / 5;
|
|
9762
|
+
}
|
|
9763
|
+
const value = Math.max(red, green, blue) * 2;
|
|
9764
|
+
if (value === 0) {
|
|
9765
|
+
return 30;
|
|
9766
|
+
}
|
|
9767
|
+
let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
|
|
9768
|
+
if (value === 2) {
|
|
9769
|
+
result += 60;
|
|
9770
|
+
}
|
|
9771
|
+
return result;
|
|
9772
|
+
},
|
|
9773
|
+
enumerable: false
|
|
9774
|
+
},
|
|
9775
|
+
rgbToAnsi: {
|
|
9776
|
+
value: (red, green, blue) => styles2.ansi256ToAnsi(styles2.rgbToAnsi256(red, green, blue)),
|
|
9777
|
+
enumerable: false
|
|
9778
|
+
},
|
|
9779
|
+
hexToAnsi: {
|
|
9780
|
+
value: (hex) => styles2.ansi256ToAnsi(styles2.hexToAnsi256(hex)),
|
|
9781
|
+
enumerable: false
|
|
9782
|
+
}
|
|
9783
|
+
});
|
|
9784
|
+
return styles2;
|
|
9785
|
+
}
|
|
9786
|
+
var ansiStyles2 = assembleStyles2();
|
|
9787
|
+
var ansi_styles_default2 = ansiStyles2;
|
|
9788
|
+
|
|
9789
|
+
// ../../node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js
|
|
9790
|
+
import process3 from "process";
|
|
9791
|
+
import os from "os";
|
|
9792
|
+
import tty from "tty";
|
|
9793
|
+
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process3.argv) {
|
|
9794
|
+
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
9795
|
+
const position = argv.indexOf(prefix + flag);
|
|
9796
|
+
const terminatorPosition = argv.indexOf("--");
|
|
9797
|
+
return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
|
|
9798
|
+
}
|
|
9799
|
+
var { env } = process3;
|
|
9800
|
+
var flagForceColor;
|
|
9801
|
+
if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
|
|
9802
|
+
flagForceColor = 0;
|
|
9803
|
+
} else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
|
|
9804
|
+
flagForceColor = 1;
|
|
9805
|
+
}
|
|
9806
|
+
function envForceColor() {
|
|
9807
|
+
if ("FORCE_COLOR" in env) {
|
|
9808
|
+
if (env.FORCE_COLOR === "true") {
|
|
9809
|
+
return 1;
|
|
9810
|
+
}
|
|
9811
|
+
if (env.FORCE_COLOR === "false") {
|
|
9812
|
+
return 0;
|
|
9813
|
+
}
|
|
9814
|
+
return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
|
|
9815
|
+
}
|
|
9816
|
+
}
|
|
9817
|
+
function translateLevel(level) {
|
|
9818
|
+
if (level === 0) {
|
|
9819
|
+
return false;
|
|
9820
|
+
}
|
|
9821
|
+
return {
|
|
9822
|
+
level,
|
|
9823
|
+
hasBasic: true,
|
|
9824
|
+
has256: level >= 2,
|
|
9825
|
+
has16m: level >= 3
|
|
9826
|
+
};
|
|
9827
|
+
}
|
|
9828
|
+
function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
9829
|
+
const noFlagForceColor = envForceColor();
|
|
9830
|
+
if (noFlagForceColor !== void 0) {
|
|
9831
|
+
flagForceColor = noFlagForceColor;
|
|
9495
9832
|
}
|
|
9496
9833
|
const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
|
|
9497
9834
|
if (forceColor === 0) {
|
|
@@ -9515,7 +9852,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
|
9515
9852
|
if (env.TERM === "dumb") {
|
|
9516
9853
|
return min;
|
|
9517
9854
|
}
|
|
9518
|
-
if (
|
|
9855
|
+
if (process3.platform === "win32") {
|
|
9519
9856
|
const osRelease = os.release().split(".");
|
|
9520
9857
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
9521
9858
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
@@ -9759,10 +10096,10 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
|
9759
10096
|
var source_default = chalk;
|
|
9760
10097
|
|
|
9761
10098
|
// ../../node_modules/.pnpm/cli-cursor@5.0.0/node_modules/cli-cursor/index.js
|
|
9762
|
-
import
|
|
10099
|
+
import process6 from "process";
|
|
9763
10100
|
|
|
9764
10101
|
// ../../node_modules/.pnpm/restore-cursor@5.1.0/node_modules/restore-cursor/index.js
|
|
9765
|
-
import
|
|
10102
|
+
import process5 from "process";
|
|
9766
10103
|
|
|
9767
10104
|
// ../../node_modules/.pnpm/mimic-function@5.0.1/node_modules/mimic-function/index.js
|
|
9768
10105
|
var copyProperty = (to, from, property, ignoreNonConfigurable) => {
|
|
@@ -9951,7 +10288,7 @@ var SignalExit = class extends SignalExitBase {
|
|
|
9951
10288
|
// "SIGHUP" throws an `ENOSYS` error on Windows,
|
|
9952
10289
|
// so use a supported signal instead
|
|
9953
10290
|
/* c8 ignore start */
|
|
9954
|
-
#hupSig =
|
|
10291
|
+
#hupSig = process4.platform === "win32" ? "SIGINT" : "SIGHUP";
|
|
9955
10292
|
/* c8 ignore stop */
|
|
9956
10293
|
#emitter = new Emitter();
|
|
9957
10294
|
#process;
|
|
@@ -10062,7 +10399,7 @@ var SignalExit = class extends SignalExitBase {
|
|
|
10062
10399
|
}
|
|
10063
10400
|
}
|
|
10064
10401
|
};
|
|
10065
|
-
var
|
|
10402
|
+
var process4 = globalThis.process;
|
|
10066
10403
|
var {
|
|
10067
10404
|
/**
|
|
10068
10405
|
* Called when the process is exiting, whether via signal, explicit
|
|
@@ -10090,10 +10427,10 @@ var {
|
|
|
10090
10427
|
* @internal
|
|
10091
10428
|
*/
|
|
10092
10429
|
unload
|
|
10093
|
-
} = signalExitWrap(processOk(
|
|
10430
|
+
} = signalExitWrap(processOk(process4) ? new SignalExit(process4) : new SignalExitFallback());
|
|
10094
10431
|
|
|
10095
10432
|
// ../../node_modules/.pnpm/restore-cursor@5.1.0/node_modules/restore-cursor/index.js
|
|
10096
|
-
var terminal =
|
|
10433
|
+
var terminal = process5.stderr.isTTY ? process5.stderr : process5.stdout.isTTY ? process5.stdout : void 0;
|
|
10097
10434
|
var restoreCursor = terminal ? onetime_default(() => {
|
|
10098
10435
|
onExit(() => {
|
|
10099
10436
|
terminal.write("\x1B[?25h");
|
|
@@ -10105,14 +10442,14 @@ var restore_cursor_default = restoreCursor;
|
|
|
10105
10442
|
// ../../node_modules/.pnpm/cli-cursor@5.0.0/node_modules/cli-cursor/index.js
|
|
10106
10443
|
var isHidden = false;
|
|
10107
10444
|
var cliCursor = {};
|
|
10108
|
-
cliCursor.show = (writableStream =
|
|
10445
|
+
cliCursor.show = (writableStream = process6.stderr) => {
|
|
10109
10446
|
if (!writableStream.isTTY) {
|
|
10110
10447
|
return;
|
|
10111
10448
|
}
|
|
10112
10449
|
isHidden = false;
|
|
10113
10450
|
writableStream.write("\x1B[?25h");
|
|
10114
10451
|
};
|
|
10115
|
-
cliCursor.hide = (writableStream =
|
|
10452
|
+
cliCursor.hide = (writableStream = process6.stderr) => {
|
|
10116
10453
|
if (!writableStream.isTTY) {
|
|
10117
10454
|
return;
|
|
10118
10455
|
}
|
|
@@ -10136,12 +10473,12 @@ var cli_cursor_default = cliCursor;
|
|
|
10136
10473
|
var import_cli_spinners = __toESM(require_cli_spinners(), 1);
|
|
10137
10474
|
|
|
10138
10475
|
// ../../node_modules/.pnpm/is-unicode-supported@1.3.0/node_modules/is-unicode-supported/index.js
|
|
10139
|
-
import
|
|
10140
|
-
function
|
|
10141
|
-
if (
|
|
10142
|
-
return
|
|
10476
|
+
import process7 from "process";
|
|
10477
|
+
function isUnicodeSupported2() {
|
|
10478
|
+
if (process7.platform !== "win32") {
|
|
10479
|
+
return process7.env.TERM !== "linux";
|
|
10143
10480
|
}
|
|
10144
|
-
return Boolean(
|
|
10481
|
+
return Boolean(process7.env.CI) || Boolean(process7.env.WT_SESSION) || Boolean(process7.env.TERMINUS_SUBLIME) || process7.env.ConEmuTask === "{cmd::Cmder}" || process7.env.TERM_PROGRAM === "Terminus-Sublime" || process7.env.TERM_PROGRAM === "vscode" || process7.env.TERM === "xterm-256color" || process7.env.TERM === "alacritty" || process7.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
10145
10482
|
}
|
|
10146
10483
|
|
|
10147
10484
|
// ../../node_modules/.pnpm/log-symbols@6.0.0/node_modules/log-symbols/index.js
|
|
@@ -10157,7 +10494,7 @@ var fallback = {
|
|
|
10157
10494
|
warning: source_default.yellow("\u203C"),
|
|
10158
10495
|
error: source_default.red("\xD7")
|
|
10159
10496
|
};
|
|
10160
|
-
var logSymbols =
|
|
10497
|
+
var logSymbols = isUnicodeSupported2() ? main : fallback;
|
|
10161
10498
|
var log_symbols_default = logSymbols;
|
|
10162
10499
|
|
|
10163
10500
|
// ../../node_modules/.pnpm/emoji-regex@10.6.0/node_modules/emoji-regex/index.mjs
|
|
@@ -10220,17 +10557,6 @@ function isInteractive({ stream = process.stdout } = {}) {
|
|
|
10220
10557
|
);
|
|
10221
10558
|
}
|
|
10222
10559
|
|
|
10223
|
-
// ../../node_modules/.pnpm/is-unicode-supported@2.1.0/node_modules/is-unicode-supported/index.js
|
|
10224
|
-
import process7 from "process";
|
|
10225
|
-
function isUnicodeSupported2() {
|
|
10226
|
-
const { env: env2 } = process7;
|
|
10227
|
-
const { TERM, TERM_PROGRAM } = env2;
|
|
10228
|
-
if (process7.platform !== "win32") {
|
|
10229
|
-
return TERM !== "linux";
|
|
10230
|
-
}
|
|
10231
|
-
return Boolean(env2.WT_SESSION) || Boolean(env2.TERMINUS_SUBLIME) || env2.ConEmuTask === "{cmd::Cmder}" || TERM_PROGRAM === "Terminus-Sublime" || TERM_PROGRAM === "vscode" || TERM === "xterm-256color" || TERM === "alacritty" || TERM === "rxvt-unicode" || TERM === "rxvt-unicode-256color" || env2.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
10232
|
-
}
|
|
10233
|
-
|
|
10234
10560
|
// ../../node_modules/.pnpm/stdin-discarder@0.2.2/node_modules/stdin-discarder/index.js
|
|
10235
10561
|
import process8 from "process";
|
|
10236
10562
|
var ASCII_ETX_CODE = 3;
|
|
@@ -10324,548 +10650,404 @@ var Ora = class {
|
|
|
10324
10650
|
this._isEnabled = this.#isEnabled;
|
|
10325
10651
|
Object.defineProperty(this, "_linesToClear", {
|
|
10326
10652
|
get() {
|
|
10327
|
-
return this.#linesToClear;
|
|
10328
|
-
},
|
|
10329
|
-
set(newValue) {
|
|
10330
|
-
this.#linesToClear = newValue;
|
|
10331
|
-
}
|
|
10332
|
-
});
|
|
10333
|
-
Object.defineProperty(this, "_frameIndex", {
|
|
10334
|
-
get() {
|
|
10335
|
-
return this.#frameIndex;
|
|
10336
|
-
}
|
|
10337
|
-
});
|
|
10338
|
-
Object.defineProperty(this, "_lineCount", {
|
|
10339
|
-
get() {
|
|
10340
|
-
return this.#lineCount;
|
|
10341
|
-
}
|
|
10342
|
-
});
|
|
10343
|
-
}
|
|
10344
|
-
}
|
|
10345
|
-
get indent() {
|
|
10346
|
-
return this.#indent;
|
|
10347
|
-
}
|
|
10348
|
-
set indent(indent = 0) {
|
|
10349
|
-
if (!(indent >= 0 && Number.isInteger(indent))) {
|
|
10350
|
-
throw new Error("The `indent` option must be an integer from 0 and up");
|
|
10351
|
-
}
|
|
10352
|
-
this.#indent = indent;
|
|
10353
|
-
this.#updateLineCount();
|
|
10354
|
-
}
|
|
10355
|
-
get interval() {
|
|
10356
|
-
return this.#initialInterval ?? this.#spinner.interval ?? 100;
|
|
10357
|
-
}
|
|
10358
|
-
get spinner() {
|
|
10359
|
-
return this.#spinner;
|
|
10360
|
-
}
|
|
10361
|
-
set spinner(spinner) {
|
|
10362
|
-
this.#frameIndex = -1;
|
|
10363
|
-
this.#initialInterval = void 0;
|
|
10364
|
-
if (typeof spinner === "object") {
|
|
10365
|
-
if (spinner.frames === void 0) {
|
|
10366
|
-
throw new Error("The given spinner must have a `frames` property");
|
|
10367
|
-
}
|
|
10368
|
-
this.#spinner = spinner;
|
|
10369
|
-
} else if (!isUnicodeSupported2()) {
|
|
10370
|
-
this.#spinner = import_cli_spinners.default.line;
|
|
10371
|
-
} else if (spinner === void 0) {
|
|
10372
|
-
this.#spinner = import_cli_spinners.default.dots;
|
|
10373
|
-
} else if (spinner !== "default" && import_cli_spinners.default[spinner]) {
|
|
10374
|
-
this.#spinner = import_cli_spinners.default[spinner];
|
|
10375
|
-
} else {
|
|
10376
|
-
throw new Error(`There is no built-in spinner named '${spinner}'. See https://github.com/sindresorhus/cli-spinners/blob/main/spinners.json for a full list.`);
|
|
10377
|
-
}
|
|
10378
|
-
}
|
|
10379
|
-
get text() {
|
|
10380
|
-
return this.#text;
|
|
10381
|
-
}
|
|
10382
|
-
set text(value = "") {
|
|
10383
|
-
this.#text = value;
|
|
10384
|
-
this.#updateLineCount();
|
|
10385
|
-
}
|
|
10386
|
-
get prefixText() {
|
|
10387
|
-
return this.#prefixText;
|
|
10388
|
-
}
|
|
10389
|
-
set prefixText(value = "") {
|
|
10390
|
-
this.#prefixText = value;
|
|
10391
|
-
this.#updateLineCount();
|
|
10392
|
-
}
|
|
10393
|
-
get suffixText() {
|
|
10394
|
-
return this.#suffixText;
|
|
10395
|
-
}
|
|
10396
|
-
set suffixText(value = "") {
|
|
10397
|
-
this.#suffixText = value;
|
|
10398
|
-
this.#updateLineCount();
|
|
10399
|
-
}
|
|
10400
|
-
get isSpinning() {
|
|
10401
|
-
return this.#id !== void 0;
|
|
10402
|
-
}
|
|
10403
|
-
#getFullPrefixText(prefixText = this.#prefixText, postfix = " ") {
|
|
10404
|
-
if (typeof prefixText === "string" && prefixText !== "") {
|
|
10405
|
-
return prefixText + postfix;
|
|
10406
|
-
}
|
|
10407
|
-
if (typeof prefixText === "function") {
|
|
10408
|
-
return prefixText() + postfix;
|
|
10409
|
-
}
|
|
10410
|
-
return "";
|
|
10411
|
-
}
|
|
10412
|
-
#getFullSuffixText(suffixText = this.#suffixText, prefix = " ") {
|
|
10413
|
-
if (typeof suffixText === "string" && suffixText !== "") {
|
|
10414
|
-
return prefix + suffixText;
|
|
10415
|
-
}
|
|
10416
|
-
if (typeof suffixText === "function") {
|
|
10417
|
-
return prefix + suffixText();
|
|
10418
|
-
}
|
|
10419
|
-
return "";
|
|
10420
|
-
}
|
|
10421
|
-
#updateLineCount() {
|
|
10422
|
-
const columns = this.#stream.columns ?? 80;
|
|
10423
|
-
const fullPrefixText = this.#getFullPrefixText(this.#prefixText, "-");
|
|
10424
|
-
const fullSuffixText = this.#getFullSuffixText(this.#suffixText, "-");
|
|
10425
|
-
const fullText = " ".repeat(this.#indent) + fullPrefixText + "--" + this.#text + "--" + fullSuffixText;
|
|
10426
|
-
this.#lineCount = 0;
|
|
10427
|
-
for (const line of stripAnsi(fullText).split("\n")) {
|
|
10428
|
-
this.#lineCount += Math.max(1, Math.ceil(stringWidth2(line, { countAnsiEscapeCodes: true }) / columns));
|
|
10429
|
-
}
|
|
10430
|
-
}
|
|
10431
|
-
get isEnabled() {
|
|
10432
|
-
return this.#isEnabled && !this.#isSilent;
|
|
10433
|
-
}
|
|
10434
|
-
set isEnabled(value) {
|
|
10435
|
-
if (typeof value !== "boolean") {
|
|
10436
|
-
throw new TypeError("The `isEnabled` option must be a boolean");
|
|
10437
|
-
}
|
|
10438
|
-
this.#isEnabled = value;
|
|
10439
|
-
}
|
|
10440
|
-
get isSilent() {
|
|
10441
|
-
return this.#isSilent;
|
|
10442
|
-
}
|
|
10443
|
-
set isSilent(value) {
|
|
10444
|
-
if (typeof value !== "boolean") {
|
|
10445
|
-
throw new TypeError("The `isSilent` option must be a boolean");
|
|
10446
|
-
}
|
|
10447
|
-
this.#isSilent = value;
|
|
10448
|
-
}
|
|
10449
|
-
frame() {
|
|
10450
|
-
const now = Date.now();
|
|
10451
|
-
if (this.#frameIndex === -1 || now - this.#lastSpinnerFrameTime >= this.interval) {
|
|
10452
|
-
this.#frameIndex = ++this.#frameIndex % this.#spinner.frames.length;
|
|
10453
|
-
this.#lastSpinnerFrameTime = now;
|
|
10454
|
-
}
|
|
10455
|
-
const { frames } = this.#spinner;
|
|
10456
|
-
let frame = frames[this.#frameIndex];
|
|
10457
|
-
if (this.color) {
|
|
10458
|
-
frame = source_default[this.color](frame);
|
|
10459
|
-
}
|
|
10460
|
-
const fullPrefixText = typeof this.#prefixText === "string" && this.#prefixText !== "" ? this.#prefixText + " " : "";
|
|
10461
|
-
const fullText = typeof this.text === "string" ? " " + this.text : "";
|
|
10462
|
-
const fullSuffixText = typeof this.#suffixText === "string" && this.#suffixText !== "" ? " " + this.#suffixText : "";
|
|
10463
|
-
return fullPrefixText + frame + fullText + fullSuffixText;
|
|
10464
|
-
}
|
|
10465
|
-
clear() {
|
|
10466
|
-
if (!this.#isEnabled || !this.#stream.isTTY) {
|
|
10467
|
-
return this;
|
|
10468
|
-
}
|
|
10469
|
-
this.#stream.cursorTo(0);
|
|
10470
|
-
for (let index = 0; index < this.#linesToClear; index++) {
|
|
10471
|
-
if (index > 0) {
|
|
10472
|
-
this.#stream.moveCursor(0, -1);
|
|
10473
|
-
}
|
|
10474
|
-
this.#stream.clearLine(1);
|
|
10475
|
-
}
|
|
10476
|
-
if (this.#indent || this.lastIndent !== this.#indent) {
|
|
10477
|
-
this.#stream.cursorTo(this.#indent);
|
|
10478
|
-
}
|
|
10479
|
-
this.lastIndent = this.#indent;
|
|
10480
|
-
this.#linesToClear = 0;
|
|
10481
|
-
return this;
|
|
10482
|
-
}
|
|
10483
|
-
render() {
|
|
10484
|
-
if (this.#isSilent) {
|
|
10485
|
-
return this;
|
|
10486
|
-
}
|
|
10487
|
-
this.clear();
|
|
10488
|
-
this.#stream.write(this.frame());
|
|
10489
|
-
this.#linesToClear = this.#lineCount;
|
|
10490
|
-
return this;
|
|
10491
|
-
}
|
|
10492
|
-
start(text) {
|
|
10493
|
-
if (text) {
|
|
10494
|
-
this.text = text;
|
|
10495
|
-
}
|
|
10496
|
-
if (this.#isSilent) {
|
|
10497
|
-
return this;
|
|
10498
|
-
}
|
|
10499
|
-
if (!this.#isEnabled) {
|
|
10500
|
-
if (this.text) {
|
|
10501
|
-
this.#stream.write(`- ${this.text}
|
|
10502
|
-
`);
|
|
10503
|
-
}
|
|
10504
|
-
return this;
|
|
10505
|
-
}
|
|
10506
|
-
if (this.isSpinning) {
|
|
10507
|
-
return this;
|
|
10508
|
-
}
|
|
10509
|
-
if (this.#options.hideCursor) {
|
|
10510
|
-
cli_cursor_default.hide(this.#stream);
|
|
10511
|
-
}
|
|
10512
|
-
if (this.#options.discardStdin && process9.stdin.isTTY) {
|
|
10513
|
-
this.#isDiscardingStdin = true;
|
|
10514
|
-
stdin_discarder_default.start();
|
|
10653
|
+
return this.#linesToClear;
|
|
10654
|
+
},
|
|
10655
|
+
set(newValue) {
|
|
10656
|
+
this.#linesToClear = newValue;
|
|
10657
|
+
}
|
|
10658
|
+
});
|
|
10659
|
+
Object.defineProperty(this, "_frameIndex", {
|
|
10660
|
+
get() {
|
|
10661
|
+
return this.#frameIndex;
|
|
10662
|
+
}
|
|
10663
|
+
});
|
|
10664
|
+
Object.defineProperty(this, "_lineCount", {
|
|
10665
|
+
get() {
|
|
10666
|
+
return this.#lineCount;
|
|
10667
|
+
}
|
|
10668
|
+
});
|
|
10515
10669
|
}
|
|
10516
|
-
this.render();
|
|
10517
|
-
this.#id = setInterval(this.render.bind(this), this.interval);
|
|
10518
|
-
return this;
|
|
10519
10670
|
}
|
|
10520
|
-
|
|
10521
|
-
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
this.#frameIndex = 0;
|
|
10527
|
-
this.clear();
|
|
10528
|
-
if (this.#options.hideCursor) {
|
|
10529
|
-
cli_cursor_default.show(this.#stream);
|
|
10671
|
+
get indent() {
|
|
10672
|
+
return this.#indent;
|
|
10673
|
+
}
|
|
10674
|
+
set indent(indent = 0) {
|
|
10675
|
+
if (!(indent >= 0 && Number.isInteger(indent))) {
|
|
10676
|
+
throw new Error("The `indent` option must be an integer from 0 and up");
|
|
10530
10677
|
}
|
|
10531
|
-
|
|
10532
|
-
|
|
10533
|
-
|
|
10678
|
+
this.#indent = indent;
|
|
10679
|
+
this.#updateLineCount();
|
|
10680
|
+
}
|
|
10681
|
+
get interval() {
|
|
10682
|
+
return this.#initialInterval ?? this.#spinner.interval ?? 100;
|
|
10683
|
+
}
|
|
10684
|
+
get spinner() {
|
|
10685
|
+
return this.#spinner;
|
|
10686
|
+
}
|
|
10687
|
+
set spinner(spinner) {
|
|
10688
|
+
this.#frameIndex = -1;
|
|
10689
|
+
this.#initialInterval = void 0;
|
|
10690
|
+
if (typeof spinner === "object") {
|
|
10691
|
+
if (spinner.frames === void 0) {
|
|
10692
|
+
throw new Error("The given spinner must have a `frames` property");
|
|
10693
|
+
}
|
|
10694
|
+
this.#spinner = spinner;
|
|
10695
|
+
} else if (!isUnicodeSupported()) {
|
|
10696
|
+
this.#spinner = import_cli_spinners.default.line;
|
|
10697
|
+
} else if (spinner === void 0) {
|
|
10698
|
+
this.#spinner = import_cli_spinners.default.dots;
|
|
10699
|
+
} else if (spinner !== "default" && import_cli_spinners.default[spinner]) {
|
|
10700
|
+
this.#spinner = import_cli_spinners.default[spinner];
|
|
10701
|
+
} else {
|
|
10702
|
+
throw new Error(`There is no built-in spinner named '${spinner}'. See https://github.com/sindresorhus/cli-spinners/blob/main/spinners.json for a full list.`);
|
|
10534
10703
|
}
|
|
10535
|
-
return this;
|
|
10536
10704
|
}
|
|
10537
|
-
|
|
10538
|
-
return this
|
|
10705
|
+
get text() {
|
|
10706
|
+
return this.#text;
|
|
10539
10707
|
}
|
|
10540
|
-
|
|
10541
|
-
|
|
10708
|
+
set text(value = "") {
|
|
10709
|
+
this.#text = value;
|
|
10710
|
+
this.#updateLineCount();
|
|
10542
10711
|
}
|
|
10543
|
-
|
|
10544
|
-
return this
|
|
10712
|
+
get prefixText() {
|
|
10713
|
+
return this.#prefixText;
|
|
10545
10714
|
}
|
|
10546
|
-
|
|
10547
|
-
|
|
10715
|
+
set prefixText(value = "") {
|
|
10716
|
+
this.#prefixText = value;
|
|
10717
|
+
this.#updateLineCount();
|
|
10548
10718
|
}
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
return this;
|
|
10552
|
-
}
|
|
10553
|
-
const prefixText = options.prefixText ?? this.#prefixText;
|
|
10554
|
-
const fullPrefixText = this.#getFullPrefixText(prefixText, " ");
|
|
10555
|
-
const symbolText = options.symbol ?? " ";
|
|
10556
|
-
const text = options.text ?? this.text;
|
|
10557
|
-
const separatorText = symbolText ? " " : "";
|
|
10558
|
-
const fullText = typeof text === "string" ? separatorText + text : "";
|
|
10559
|
-
const suffixText = options.suffixText ?? this.#suffixText;
|
|
10560
|
-
const fullSuffixText = this.#getFullSuffixText(suffixText, " ");
|
|
10561
|
-
const textToWrite = fullPrefixText + symbolText + fullText + fullSuffixText + "\n";
|
|
10562
|
-
this.stop();
|
|
10563
|
-
this.#stream.write(textToWrite);
|
|
10564
|
-
return this;
|
|
10719
|
+
get suffixText() {
|
|
10720
|
+
return this.#suffixText;
|
|
10565
10721
|
}
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
}
|
|
10570
|
-
|
|
10571
|
-
// src/commands/pentest.ts
|
|
10572
|
-
var import_picocolors5 = __toESM(require_picocolors(), 1);
|
|
10573
|
-
|
|
10574
|
-
// src/lib/confirm.ts
|
|
10575
|
-
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
10576
|
-
import { createInterface } from "readline";
|
|
10577
|
-
async function confirmDestructive(message, yes) {
|
|
10578
|
-
if (yes) return;
|
|
10579
|
-
if (!isTTY()) {
|
|
10580
|
-
console.error("Use --yes to confirm destructive operations in non-interactive mode.");
|
|
10581
|
-
process.exit(2);
|
|
10722
|
+
set suffixText(value = "") {
|
|
10723
|
+
this.#suffixText = value;
|
|
10724
|
+
this.#updateLineCount();
|
|
10582
10725
|
}
|
|
10583
|
-
|
|
10584
|
-
|
|
10585
|
-
rl.question(`${message} (y/N) `, resolve);
|
|
10586
|
-
});
|
|
10587
|
-
rl.close();
|
|
10588
|
-
if (answer.toLowerCase() !== "y") {
|
|
10589
|
-
console.log("Cancelled.");
|
|
10590
|
-
process.exit(0);
|
|
10726
|
+
get isSpinning() {
|
|
10727
|
+
return this.#id !== void 0;
|
|
10591
10728
|
}
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
|
|
10729
|
+
#getFullPrefixText(prefixText = this.#prefixText, postfix = " ") {
|
|
10730
|
+
if (typeof prefixText === "string" && prefixText !== "") {
|
|
10731
|
+
return prefixText + postfix;
|
|
10732
|
+
}
|
|
10733
|
+
if (typeof prefixText === "function") {
|
|
10734
|
+
return prefixText() + postfix;
|
|
10735
|
+
}
|
|
10736
|
+
return "";
|
|
10598
10737
|
}
|
|
10599
|
-
|
|
10600
|
-
|
|
10601
|
-
|
|
10602
|
-
|
|
10603
|
-
|
|
10604
|
-
|
|
10605
|
-
|
|
10606
|
-
|
|
10607
|
-
|
|
10608
|
-
By proceeding, you confirm that:
|
|
10609
|
-
${import_picocolors3.default.dim("\u2022")} You own or have explicit written authorization to test this target
|
|
10610
|
-
${import_picocolors3.default.dim("\u2022")} You will comply with all applicable laws and regulations
|
|
10611
|
-
${import_picocolors3.default.dim("\u2022")} You will not use the results for illegal or malicious purposes
|
|
10612
|
-
${import_picocolors3.default.dim("\u2022")} You accept full legal responsibility for this pentest
|
|
10613
|
-
|
|
10614
|
-
${import_picocolors3.default.dim("Unauthorized access to computer systems is a criminal offense under")}
|
|
10615
|
-
${import_picocolors3.default.dim("laws such as the CFAA (US), Computer Misuse Act (UK), and similar")}
|
|
10616
|
-
${import_picocolors3.default.dim("legislation worldwide. KinoSec bears no liability for unauthorized")}
|
|
10617
|
-
${import_picocolors3.default.dim("use of its services.")}
|
|
10618
|
-
`);
|
|
10619
|
-
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
10620
|
-
const answer = await new Promise((resolve) => {
|
|
10621
|
-
rl.question("Agree and continue? (y/N) ", resolve);
|
|
10622
|
-
});
|
|
10623
|
-
rl.close();
|
|
10624
|
-
if (answer.toLowerCase() !== "y") {
|
|
10625
|
-
console.log("Cancelled.");
|
|
10626
|
-
process.exit(0);
|
|
10738
|
+
#getFullSuffixText(suffixText = this.#suffixText, prefix = " ") {
|
|
10739
|
+
if (typeof suffixText === "string" && suffixText !== "") {
|
|
10740
|
+
return prefix + suffixText;
|
|
10741
|
+
}
|
|
10742
|
+
if (typeof suffixText === "function") {
|
|
10743
|
+
return prefix + suffixText();
|
|
10744
|
+
}
|
|
10745
|
+
return "";
|
|
10627
10746
|
}
|
|
10628
|
-
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
const d = new Date(scheduledAt);
|
|
10638
|
-
return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: false });
|
|
10639
|
-
}
|
|
10640
|
-
function formatPattern(s) {
|
|
10641
|
-
const time = extractTime(s.scheduled_at);
|
|
10642
|
-
switch (s.recurrence_pattern) {
|
|
10643
|
-
case "once":
|
|
10644
|
-
return `Once at ${time}`;
|
|
10645
|
-
case "daily":
|
|
10646
|
-
return `Daily at ${time}`;
|
|
10647
|
-
case "weekly":
|
|
10648
|
-
return `Weekly ${s.day_of_week !== null ? DAY_NAMES[s.day_of_week] : ""} at ${time}`;
|
|
10649
|
-
case "biweekly":
|
|
10650
|
-
return `Every 2 weeks ${s.day_of_week !== null ? DAY_NAMES[s.day_of_week] : ""} at ${time}`;
|
|
10651
|
-
case "monthly":
|
|
10652
|
-
return `Monthly day ${s.day_of_month ?? "?"} at ${time}`;
|
|
10653
|
-
default:
|
|
10654
|
-
return s.recurrence_pattern;
|
|
10747
|
+
#updateLineCount() {
|
|
10748
|
+
const columns = this.#stream.columns ?? 80;
|
|
10749
|
+
const fullPrefixText = this.#getFullPrefixText(this.#prefixText, "-");
|
|
10750
|
+
const fullSuffixText = this.#getFullSuffixText(this.#suffixText, "-");
|
|
10751
|
+
const fullText = " ".repeat(this.#indent) + fullPrefixText + "--" + this.#text + "--" + fullSuffixText;
|
|
10752
|
+
this.#lineCount = 0;
|
|
10753
|
+
for (const line of stripAnsi(fullText).split("\n")) {
|
|
10754
|
+
this.#lineCount += Math.max(1, Math.ceil(stringWidth2(line, { countAnsiEscapeCodes: true }) / columns));
|
|
10755
|
+
}
|
|
10655
10756
|
}
|
|
10656
|
-
|
|
10657
|
-
|
|
10658
|
-
|
|
10659
|
-
|
|
10660
|
-
|
|
10661
|
-
|
|
10662
|
-
$ kinosec pentest schedule list --status active --json`).action(async (options) => {
|
|
10663
|
-
try {
|
|
10664
|
-
const opts = schedule.parent.parent.opts();
|
|
10665
|
-
const { token, apiUrl } = resolveToken(opts);
|
|
10666
|
-
const client = new KinoSecClient({ apiUrl, token });
|
|
10667
|
-
const result = await client.schedules.list({
|
|
10668
|
-
limit: parseInt(options.limit, 10),
|
|
10669
|
-
cursor: options.cursor,
|
|
10670
|
-
status: options.status
|
|
10671
|
-
});
|
|
10672
|
-
if (opts.json || opts.output === "json") {
|
|
10673
|
-
printJson(result);
|
|
10674
|
-
return;
|
|
10675
|
-
}
|
|
10676
|
-
if (result.items.length === 0) {
|
|
10677
|
-
console.log("No scheduled pentests found.");
|
|
10678
|
-
return;
|
|
10679
|
-
}
|
|
10680
|
-
printTable(
|
|
10681
|
-
["ID", "Target", "Mode", "Schedule", "Status", "Next Run"],
|
|
10682
|
-
result.items.map((s) => [
|
|
10683
|
-
s.id,
|
|
10684
|
-
truncate(s.domain_or_ip, 25),
|
|
10685
|
-
s.scan_mode,
|
|
10686
|
-
formatPattern(s),
|
|
10687
|
-
s.is_active ? import_picocolors4.default.green("active") : import_picocolors4.default.yellow("paused"),
|
|
10688
|
-
s.next_scheduled_run ? new Date(s.next_scheduled_run).toLocaleString() : "\u2014"
|
|
10689
|
-
])
|
|
10690
|
-
);
|
|
10691
|
-
if (result.next_cursor) {
|
|
10692
|
-
console.log(import_picocolors4.default.dim(`
|
|
10693
|
-
More results available. Use --cursor ${result.next_cursor}`));
|
|
10694
|
-
}
|
|
10695
|
-
} catch (error) {
|
|
10696
|
-
handleError(error);
|
|
10757
|
+
get isEnabled() {
|
|
10758
|
+
return this.#isEnabled && !this.#isSilent;
|
|
10759
|
+
}
|
|
10760
|
+
set isEnabled(value) {
|
|
10761
|
+
if (typeof value !== "boolean") {
|
|
10762
|
+
throw new TypeError("The `isEnabled` option must be a boolean");
|
|
10697
10763
|
}
|
|
10698
|
-
|
|
10699
|
-
|
|
10700
|
-
|
|
10701
|
-
|
|
10702
|
-
|
|
10703
|
-
|
|
10704
|
-
|
|
10705
|
-
|
|
10706
|
-
|
|
10707
|
-
|
|
10708
|
-
|
|
10709
|
-
|
|
10710
|
-
|
|
10764
|
+
this.#isEnabled = value;
|
|
10765
|
+
}
|
|
10766
|
+
get isSilent() {
|
|
10767
|
+
return this.#isSilent;
|
|
10768
|
+
}
|
|
10769
|
+
set isSilent(value) {
|
|
10770
|
+
if (typeof value !== "boolean") {
|
|
10771
|
+
throw new TypeError("The `isSilent` option must be a boolean");
|
|
10772
|
+
}
|
|
10773
|
+
this.#isSilent = value;
|
|
10774
|
+
}
|
|
10775
|
+
frame() {
|
|
10776
|
+
const now = Date.now();
|
|
10777
|
+
if (this.#frameIndex === -1 || now - this.#lastSpinnerFrameTime >= this.interval) {
|
|
10778
|
+
this.#frameIndex = ++this.#frameIndex % this.#spinner.frames.length;
|
|
10779
|
+
this.#lastSpinnerFrameTime = now;
|
|
10780
|
+
}
|
|
10781
|
+
const { frames } = this.#spinner;
|
|
10782
|
+
let frame = frames[this.#frameIndex];
|
|
10783
|
+
if (this.color) {
|
|
10784
|
+
frame = source_default[this.color](frame);
|
|
10785
|
+
}
|
|
10786
|
+
const fullPrefixText = typeof this.#prefixText === "string" && this.#prefixText !== "" ? this.#prefixText + " " : "";
|
|
10787
|
+
const fullText = typeof this.text === "string" ? " " + this.text : "";
|
|
10788
|
+
const fullSuffixText = typeof this.#suffixText === "string" && this.#suffixText !== "" ? " " + this.#suffixText : "";
|
|
10789
|
+
return fullPrefixText + frame + fullText + fullSuffixText;
|
|
10790
|
+
}
|
|
10791
|
+
clear() {
|
|
10792
|
+
if (!this.#isEnabled || !this.#stream.isTTY) {
|
|
10793
|
+
return this;
|
|
10794
|
+
}
|
|
10795
|
+
this.#stream.cursorTo(0);
|
|
10796
|
+
for (let index = 0; index < this.#linesToClear; index++) {
|
|
10797
|
+
if (index > 0) {
|
|
10798
|
+
this.#stream.moveCursor(0, -1);
|
|
10711
10799
|
}
|
|
10712
|
-
|
|
10713
|
-
["Field", "Value"],
|
|
10714
|
-
[
|
|
10715
|
-
["ID", s.id],
|
|
10716
|
-
["Target", s.domain_or_ip],
|
|
10717
|
-
["Name", s.name],
|
|
10718
|
-
["Mode", s.scan_mode],
|
|
10719
|
-
["Schedule", formatPattern(s)],
|
|
10720
|
-
["Timezone", s.timezone],
|
|
10721
|
-
["Status", s.is_active ? "Active" : "Paused"],
|
|
10722
|
-
["Next Run", s.next_scheduled_run ?? "\u2014"],
|
|
10723
|
-
["Last Run", s.last_scheduled_run ?? "Never"],
|
|
10724
|
-
["Created", s.created_at]
|
|
10725
|
-
]
|
|
10726
|
-
);
|
|
10727
|
-
} catch (error) {
|
|
10728
|
-
handleError(error);
|
|
10800
|
+
this.#stream.clearLine(1);
|
|
10729
10801
|
}
|
|
10730
|
-
|
|
10731
|
-
|
|
10732
|
-
Examples:
|
|
10733
|
-
$ kinosec pentest schedule create --asset abc123 --pattern weekly --day-of-week 1 --time 09:00
|
|
10734
|
-
$ kinosec pentest schedule create --asset abc123 --pattern daily --mode quick --time 14:00
|
|
10735
|
-
$ kinosec pentest schedule create --asset abc123 --pattern monthly --day-of-month 15 --time 03:00 --timezone UTC`).action(async (options) => {
|
|
10736
|
-
if (!VALID_PATTERNS.includes(options.pattern)) {
|
|
10737
|
-
console.error(`Invalid pattern "${options.pattern}". Must be: ${VALID_PATTERNS.join(", ")}`);
|
|
10738
|
-
process.exit(2);
|
|
10802
|
+
if (this.#indent || this.lastIndent !== this.#indent) {
|
|
10803
|
+
this.#stream.cursorTo(this.#indent);
|
|
10739
10804
|
}
|
|
10740
|
-
|
|
10741
|
-
|
|
10742
|
-
|
|
10805
|
+
this.lastIndent = this.#indent;
|
|
10806
|
+
this.#linesToClear = 0;
|
|
10807
|
+
return this;
|
|
10808
|
+
}
|
|
10809
|
+
render() {
|
|
10810
|
+
if (this.#isSilent) {
|
|
10811
|
+
return this;
|
|
10743
10812
|
}
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
|
|
10813
|
+
this.clear();
|
|
10814
|
+
this.#stream.write(this.frame());
|
|
10815
|
+
this.#linesToClear = this.#lineCount;
|
|
10816
|
+
return this;
|
|
10817
|
+
}
|
|
10818
|
+
start(text) {
|
|
10819
|
+
if (text) {
|
|
10820
|
+
this.text = text;
|
|
10747
10821
|
}
|
|
10748
|
-
if (
|
|
10749
|
-
|
|
10750
|
-
process.exit(2);
|
|
10822
|
+
if (this.#isSilent) {
|
|
10823
|
+
return this;
|
|
10751
10824
|
}
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
|
|
10755
|
-
|
|
10756
|
-
const params = {
|
|
10757
|
-
asset_id: options.domain_or_ip,
|
|
10758
|
-
name: options.name ?? `${options.pattern} ${options.mode} pentest`,
|
|
10759
|
-
scan_mode: options.mode,
|
|
10760
|
-
recurrence_pattern: options.pattern,
|
|
10761
|
-
time_of_day: options.time,
|
|
10762
|
-
timezone: options.timezone
|
|
10763
|
-
};
|
|
10764
|
-
if (options.dayOfWeek !== void 0) params.day_of_week = parseInt(options.dayOfWeek, 10);
|
|
10765
|
-
if (options.dayOfMonth !== void 0) params.day_of_month = parseInt(options.dayOfMonth, 10);
|
|
10766
|
-
const s = await client.schedules.create(params);
|
|
10767
|
-
if (opts.json || opts.output === "json") {
|
|
10768
|
-
printJson(s);
|
|
10769
|
-
return;
|
|
10825
|
+
if (!this.#isEnabled) {
|
|
10826
|
+
if (this.text) {
|
|
10827
|
+
this.#stream.write(`- ${this.text}
|
|
10828
|
+
`);
|
|
10770
10829
|
}
|
|
10771
|
-
|
|
10772
|
-
console.log(` ID: ${s.id}`);
|
|
10773
|
-
console.log(` Schedule: ${formatPattern(s)}`);
|
|
10774
|
-
console.log(` Next run: ${s.next_scheduled_run ?? "pending"}`);
|
|
10775
|
-
console.log(`
|
|
10776
|
-
Manage: kinosec pentest schedule get ${s.id}`);
|
|
10777
|
-
} catch (error) {
|
|
10778
|
-
handleError(error);
|
|
10830
|
+
return this;
|
|
10779
10831
|
}
|
|
10780
|
-
|
|
10781
|
-
|
|
10782
|
-
Examples:
|
|
10783
|
-
$ kinosec pentest schedule update abc123 --pattern daily --time 14:00
|
|
10784
|
-
$ kinosec pentest schedule update abc123 --mode quick`).action(async (id, options) => {
|
|
10785
|
-
const params = {};
|
|
10786
|
-
if (options.pattern) params.recurrence_pattern = options.pattern;
|
|
10787
|
-
if (options.name) params.name = options.name;
|
|
10788
|
-
if (options.mode) params.scan_mode = options.mode;
|
|
10789
|
-
if (options.time) params.time_of_day = options.time;
|
|
10790
|
-
if (options.dayOfWeek !== void 0) params.day_of_week = parseInt(options.dayOfWeek, 10);
|
|
10791
|
-
if (options.dayOfMonth !== void 0) params.day_of_month = parseInt(options.dayOfMonth, 10);
|
|
10792
|
-
if (options.timezone) params.timezone = options.timezone;
|
|
10793
|
-
if (Object.keys(params).length === 0) {
|
|
10794
|
-
console.error("Provide at least one field to update.");
|
|
10795
|
-
process.exit(2);
|
|
10832
|
+
if (this.isSpinning) {
|
|
10833
|
+
return this;
|
|
10796
10834
|
}
|
|
10797
|
-
|
|
10798
|
-
|
|
10799
|
-
const { token, apiUrl } = resolveToken(opts);
|
|
10800
|
-
const client = new KinoSecClient({ apiUrl, token });
|
|
10801
|
-
const s = await client.schedules.update(id, params);
|
|
10802
|
-
if (opts.json || opts.output === "json") {
|
|
10803
|
-
printJson(s);
|
|
10804
|
-
return;
|
|
10805
|
-
}
|
|
10806
|
-
printSuccess(`Schedule updated: ${s.domain_or_ip}`);
|
|
10807
|
-
console.log(` Schedule: ${formatPattern(s)}`);
|
|
10808
|
-
console.log(` Next run: ${s.next_scheduled_run ?? "pending"}`);
|
|
10809
|
-
} catch (error) {
|
|
10810
|
-
handleError(error);
|
|
10835
|
+
if (this.#options.hideCursor) {
|
|
10836
|
+
cli_cursor_default.hide(this.#stream);
|
|
10811
10837
|
}
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
|
|
10815
|
-
$ kinosec pentest schedule delete abc123
|
|
10816
|
-
$ kinosec pentest schedule delete abc123 --yes`).action(async (id, options) => {
|
|
10817
|
-
try {
|
|
10818
|
-
await confirmDestructive(`Delete schedule ${id}?`, Boolean(options.yes));
|
|
10819
|
-
const opts = schedule.parent.parent.opts();
|
|
10820
|
-
const { token, apiUrl } = resolveToken(opts);
|
|
10821
|
-
const client = new KinoSecClient({ apiUrl, token });
|
|
10822
|
-
await client.schedules.delete(id);
|
|
10823
|
-
if (opts.json || opts.output === "json") {
|
|
10824
|
-
printJson({ deleted: true, id });
|
|
10825
|
-
return;
|
|
10826
|
-
}
|
|
10827
|
-
printSuccess(`Schedule ${id} deleted.`);
|
|
10828
|
-
} catch (error) {
|
|
10829
|
-
handleError(error);
|
|
10838
|
+
if (this.#options.discardStdin && process9.stdin.isTTY) {
|
|
10839
|
+
this.#isDiscardingStdin = true;
|
|
10840
|
+
stdin_discarder_default.start();
|
|
10830
10841
|
}
|
|
10831
|
-
|
|
10832
|
-
|
|
10833
|
-
|
|
10834
|
-
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
|
|
10839
|
-
|
|
10840
|
-
|
|
10841
|
-
|
|
10842
|
-
|
|
10843
|
-
|
|
10844
|
-
|
|
10845
|
-
} catch (error) {
|
|
10846
|
-
handleError(error);
|
|
10842
|
+
this.render();
|
|
10843
|
+
this.#id = setInterval(this.render.bind(this), this.interval);
|
|
10844
|
+
return this;
|
|
10845
|
+
}
|
|
10846
|
+
stop() {
|
|
10847
|
+
if (!this.#isEnabled) {
|
|
10848
|
+
return this;
|
|
10849
|
+
}
|
|
10850
|
+
clearInterval(this.#id);
|
|
10851
|
+
this.#id = void 0;
|
|
10852
|
+
this.#frameIndex = 0;
|
|
10853
|
+
this.clear();
|
|
10854
|
+
if (this.#options.hideCursor) {
|
|
10855
|
+
cli_cursor_default.show(this.#stream);
|
|
10847
10856
|
}
|
|
10857
|
+
if (this.#options.discardStdin && process9.stdin.isTTY && this.#isDiscardingStdin) {
|
|
10858
|
+
stdin_discarder_default.stop();
|
|
10859
|
+
this.#isDiscardingStdin = false;
|
|
10860
|
+
}
|
|
10861
|
+
return this;
|
|
10862
|
+
}
|
|
10863
|
+
succeed(text) {
|
|
10864
|
+
return this.stopAndPersist({ symbol: log_symbols_default.success, text });
|
|
10865
|
+
}
|
|
10866
|
+
fail(text) {
|
|
10867
|
+
return this.stopAndPersist({ symbol: log_symbols_default.error, text });
|
|
10868
|
+
}
|
|
10869
|
+
warn(text) {
|
|
10870
|
+
return this.stopAndPersist({ symbol: log_symbols_default.warning, text });
|
|
10871
|
+
}
|
|
10872
|
+
info(text) {
|
|
10873
|
+
return this.stopAndPersist({ symbol: log_symbols_default.info, text });
|
|
10874
|
+
}
|
|
10875
|
+
stopAndPersist(options = {}) {
|
|
10876
|
+
if (this.#isSilent) {
|
|
10877
|
+
return this;
|
|
10878
|
+
}
|
|
10879
|
+
const prefixText = options.prefixText ?? this.#prefixText;
|
|
10880
|
+
const fullPrefixText = this.#getFullPrefixText(prefixText, " ");
|
|
10881
|
+
const symbolText = options.symbol ?? " ";
|
|
10882
|
+
const text = options.text ?? this.text;
|
|
10883
|
+
const separatorText = symbolText ? " " : "";
|
|
10884
|
+
const fullText = typeof text === "string" ? separatorText + text : "";
|
|
10885
|
+
const suffixText = options.suffixText ?? this.#suffixText;
|
|
10886
|
+
const fullSuffixText = this.#getFullSuffixText(suffixText, " ");
|
|
10887
|
+
const textToWrite = fullPrefixText + symbolText + fullText + fullSuffixText + "\n";
|
|
10888
|
+
this.stop();
|
|
10889
|
+
this.#stream.write(textToWrite);
|
|
10890
|
+
return this;
|
|
10891
|
+
}
|
|
10892
|
+
};
|
|
10893
|
+
function ora(options) {
|
|
10894
|
+
return new Ora(options);
|
|
10895
|
+
}
|
|
10896
|
+
|
|
10897
|
+
// src/lib/watch.ts
|
|
10898
|
+
var UNICODE = isUnicodeSupported();
|
|
10899
|
+
var sym = {
|
|
10900
|
+
success: UNICODE ? "\u2713" : "v",
|
|
10901
|
+
fail: UNICODE ? "\u2717" : "x",
|
|
10902
|
+
warn: UNICODE ? "\u26A0" : "!",
|
|
10903
|
+
rule: UNICODE ? "\u2500" : "-"
|
|
10904
|
+
};
|
|
10905
|
+
var LABEL_WIDTH = 40;
|
|
10906
|
+
var TIMING_WIDTH = 8;
|
|
10907
|
+
function formatDuration(ms) {
|
|
10908
|
+
if (ms < 1e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
10909
|
+
if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
|
|
10910
|
+
const totalSec = Math.round(ms / 1e3);
|
|
10911
|
+
const m = Math.floor(totalSec / 60);
|
|
10912
|
+
const r = totalSec % 60;
|
|
10913
|
+
return `${m}m ${r.toString().padStart(2, "0")}s`;
|
|
10914
|
+
}
|
|
10915
|
+
function completedStepLine(label, phaseMs, totalMs) {
|
|
10916
|
+
const lbl = label.padEnd(LABEL_WIDTH);
|
|
10917
|
+
const dur = formatDuration(phaseMs).padStart(TIMING_WIDTH);
|
|
10918
|
+
const tty2 = isTTY();
|
|
10919
|
+
const total = tty2 ? import_picocolors5.default.dim(`(total ${formatDuration(totalMs)})`) : `(total ${formatDuration(totalMs)})`;
|
|
10920
|
+
const icon = tty2 ? import_picocolors5.default.green(sym.success) : sym.success;
|
|
10921
|
+
return `${icon} ${lbl}${dur} ${total}`;
|
|
10922
|
+
}
|
|
10923
|
+
function renderFindingsSummary(counts) {
|
|
10924
|
+
const levels = [
|
|
10925
|
+
{ key: "critical", label: "critical", color: import_picocolors5.default.red },
|
|
10926
|
+
{ key: "high", label: "high", color: import_picocolors5.default.yellow },
|
|
10927
|
+
{ key: "medium", label: "medium", color: (s) => import_picocolors5.default.dim(import_picocolors5.default.yellow(s)) },
|
|
10928
|
+
{ key: "low", label: "low", color: import_picocolors5.default.green },
|
|
10929
|
+
{ key: "info", label: "info", color: import_picocolors5.default.cyan }
|
|
10930
|
+
];
|
|
10931
|
+
const tty2 = isTTY();
|
|
10932
|
+
const parts = levels.map(({ key, label, color }) => {
|
|
10933
|
+
const count = findCount(counts, key);
|
|
10934
|
+
const text = `${count} ${label}`;
|
|
10935
|
+
if (!tty2) return text;
|
|
10936
|
+
return count === 0 ? import_picocolors5.default.dim(text) : color(text);
|
|
10848
10937
|
});
|
|
10849
|
-
|
|
10850
|
-
|
|
10851
|
-
|
|
10852
|
-
|
|
10853
|
-
|
|
10854
|
-
|
|
10855
|
-
|
|
10856
|
-
|
|
10857
|
-
|
|
10858
|
-
|
|
10859
|
-
|
|
10938
|
+
return parts.join(" ");
|
|
10939
|
+
}
|
|
10940
|
+
function findCount(counts, level) {
|
|
10941
|
+
const key = Object.keys(counts).find((k) => k.toLowerCase() === level);
|
|
10942
|
+
return key ? counts[key] : 0;
|
|
10943
|
+
}
|
|
10944
|
+
async function watchPentest(client, profileId, options) {
|
|
10945
|
+
const { timeoutMs, intervalMs, useJson, showFindingsSummary } = options;
|
|
10946
|
+
const tty2 = isTTY();
|
|
10947
|
+
const loopStart = Date.now();
|
|
10948
|
+
let spinner = null;
|
|
10949
|
+
let lastActivity = "";
|
|
10950
|
+
let phaseStart = Date.now();
|
|
10951
|
+
if (!useJson && tty2) {
|
|
10952
|
+
spinner = ora({ text: "Fetching pentest status...", isEnabled: true }).start();
|
|
10953
|
+
}
|
|
10954
|
+
const cleanup = () => {
|
|
10955
|
+
if (spinner) spinner.stop();
|
|
10956
|
+
};
|
|
10957
|
+
process.once("SIGINT", () => {
|
|
10958
|
+
cleanup();
|
|
10959
|
+
console.log("\nWatch cancelled. Pentest is still running.");
|
|
10960
|
+
process.exit(0);
|
|
10961
|
+
});
|
|
10962
|
+
while (true) {
|
|
10963
|
+
const scanDetail = await client.scans.get(profileId);
|
|
10964
|
+
const activity = scanDetail.current_activity || "";
|
|
10965
|
+
const totalMs = Date.now() - loopStart;
|
|
10966
|
+
const currentPhaseMs = Date.now() - phaseStart;
|
|
10967
|
+
if (useJson) {
|
|
10968
|
+
const elapsed = Math.round(totalMs / 1e3);
|
|
10969
|
+
process.stdout.write(
|
|
10970
|
+
JSON.stringify({ ...scanDetail, elapsed_seconds: elapsed }) + "\n"
|
|
10971
|
+
);
|
|
10972
|
+
} else if (spinner) {
|
|
10973
|
+
if (activity && activity !== lastActivity && lastActivity) {
|
|
10974
|
+
const phaseDuration = Date.now() - phaseStart;
|
|
10975
|
+
const line = completedStepLine(lastActivity, phaseDuration, totalMs);
|
|
10976
|
+
spinner.stopAndPersist({ symbol: "", text: line });
|
|
10977
|
+
spinner = ora({ isEnabled: true }).start();
|
|
10978
|
+
phaseStart = Date.now();
|
|
10979
|
+
}
|
|
10980
|
+
lastActivity = activity || lastActivity;
|
|
10981
|
+
const dur = formatDuration(currentPhaseMs);
|
|
10982
|
+
spinner.text = `${activity || lastActivity} ${import_picocolors5.default.dim(dur)}`;
|
|
10983
|
+
} else if (!useJson) {
|
|
10984
|
+
if (activity && activity !== lastActivity && lastActivity) {
|
|
10985
|
+
const phaseDuration = Date.now() - phaseStart;
|
|
10986
|
+
console.log(`${sym.success} ${lastActivity} ${formatDuration(phaseDuration)}`);
|
|
10987
|
+
phaseStart = Date.now();
|
|
10988
|
+
}
|
|
10989
|
+
if (activity && activity !== lastActivity) {
|
|
10990
|
+
console.log(`\u2022 ${activity}`);
|
|
10991
|
+
}
|
|
10992
|
+
lastActivity = activity || lastActivity;
|
|
10993
|
+
}
|
|
10994
|
+
const terminalStatuses = ["Completed", "Failed", "Stopped"];
|
|
10995
|
+
if (terminalStatuses.some((s) => scanDetail.scan_status.includes(s))) {
|
|
10996
|
+
cleanup();
|
|
10997
|
+
if (useJson) break;
|
|
10998
|
+
if (scanDetail.scan_status.includes("Failed")) {
|
|
10999
|
+
const icon2 = tty2 ? import_picocolors5.default.red(sym.fail) : sym.fail;
|
|
11000
|
+
const reason = scanDetail.failure_reason || "unknown reason";
|
|
11001
|
+
console.log(`
|
|
11002
|
+
${icon2} Pentest failed: ${reason}`);
|
|
11003
|
+
process.exit(1);
|
|
11004
|
+
}
|
|
11005
|
+
if (scanDetail.scan_status.includes("Stopped")) {
|
|
11006
|
+
const icon2 = tty2 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
|
|
11007
|
+
console.log(`
|
|
11008
|
+
${icon2} Pentest was cancelled.`);
|
|
11009
|
+
break;
|
|
10860
11010
|
}
|
|
10861
|
-
|
|
10862
|
-
|
|
10863
|
-
|
|
11011
|
+
const totalDur = formatDuration(Date.now() - loopStart);
|
|
11012
|
+
const domain = tty2 ? import_picocolors5.default.bold(scanDetail.domain_or_ip) : scanDetail.domain_or_ip;
|
|
11013
|
+
const icon = tty2 ? import_picocolors5.default.green(sym.success) : sym.success;
|
|
11014
|
+
const dur = tty2 ? import_picocolors5.default.dim(totalDur) : totalDur;
|
|
11015
|
+
console.log(`
|
|
11016
|
+
${icon} Pentest completed: ${domain} ${dur}`);
|
|
11017
|
+
if (showFindingsSummary) {
|
|
11018
|
+
await printFindingsSummary(client, profileId, tty2);
|
|
10864
11019
|
}
|
|
10865
|
-
|
|
10866
|
-
|
|
11020
|
+
const cmd = `kinosec finding list ${profileId}`;
|
|
11021
|
+
const viewLabel = tty2 ? import_picocolors5.default.dim("View details:") : "View details:";
|
|
11022
|
+
const viewCmd = tty2 ? import_picocolors5.default.cyan(cmd) : cmd;
|
|
11023
|
+
console.log(`
|
|
11024
|
+
${viewLabel} ${viewCmd}`);
|
|
11025
|
+
console.log();
|
|
11026
|
+
break;
|
|
10867
11027
|
}
|
|
10868
|
-
|
|
11028
|
+
if (Date.now() - loopStart > timeoutMs) {
|
|
11029
|
+
cleanup();
|
|
11030
|
+
const secs = Math.round(timeoutMs / 1e3);
|
|
11031
|
+
console.error(`
|
|
11032
|
+
Timeout: pentest still running after ${secs}s.`);
|
|
11033
|
+
console.error(`Check status: kinosec pentest get ${profileId}`);
|
|
11034
|
+
process.exit(3);
|
|
11035
|
+
}
|
|
11036
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
11037
|
+
}
|
|
11038
|
+
}
|
|
11039
|
+
async function printFindingsSummary(client, profileId, tty2) {
|
|
11040
|
+
try {
|
|
11041
|
+
const reports = await client.reports.list({ limit: 20 });
|
|
11042
|
+
const report = reports.items.find((r) => r.scan_profile_id === profileId);
|
|
11043
|
+
if (report?.severity_counts) {
|
|
11044
|
+
const label = tty2 ? import_picocolors5.default.dim("Findings:") : "Findings:";
|
|
11045
|
+
const summary = renderFindingsSummary(report.severity_counts);
|
|
11046
|
+
console.log(`
|
|
11047
|
+
${label} ${summary}`);
|
|
11048
|
+
}
|
|
11049
|
+
} catch {
|
|
11050
|
+
}
|
|
10869
11051
|
}
|
|
10870
11052
|
|
|
10871
11053
|
// src/commands/pentest.ts
|
|
@@ -10878,7 +11060,7 @@ function isValidDomain(value) {
|
|
|
10878
11060
|
return value.includes(".") && !/\s/.test(value);
|
|
10879
11061
|
}
|
|
10880
11062
|
function registerPentestCommand(program3) {
|
|
10881
|
-
const pentest = program3.command("pentest").description("Run and manage pentests").option("-t, --target <domain>", "Target domain to pentest").option("--profile <id>", "Pentest profile ID (alternative to --target)").option("--mode <mode>", "Pentest mode: quick or full", "full").option("-y, --yes", "Accept authorization terms without prompting").addHelpText("after", `
|
|
11063
|
+
const pentest = program3.command("pentest").description("Run and manage pentests").option("-t, --target <domain>", "Target domain to pentest").option("--profile <id>", "Pentest profile ID (alternative to --target)").option("--mode <mode>", "Pentest mode: quick or full", "full").option("-y, --yes", "Accept authorization terms without prompting").option("--no-watch", "Skip auto-watch after starting the pentest").addHelpText("after", `
|
|
10882
11064
|
Examples:
|
|
10883
11065
|
$ kinosec pentest -t delikado.org
|
|
10884
11066
|
$ kinosec pentest --target delikado.org --mode quick
|
|
@@ -10912,10 +11094,15 @@ Examples:
|
|
|
10912
11094
|
asset = await client.assets.lookup(domain);
|
|
10913
11095
|
} catch (err) {
|
|
10914
11096
|
if (err instanceof ApiError && err.status === 404) {
|
|
10915
|
-
|
|
10916
|
-
|
|
11097
|
+
asset = await client.assets.create({
|
|
11098
|
+
asset_type: "domain",
|
|
11099
|
+
asset_value: domain,
|
|
11100
|
+
verification_method: "dns"
|
|
11101
|
+
});
|
|
11102
|
+
console.error(`Asset created for ${domain}.`);
|
|
11103
|
+
} else {
|
|
11104
|
+
throw err;
|
|
10917
11105
|
}
|
|
10918
|
-
throw err;
|
|
10919
11106
|
}
|
|
10920
11107
|
targetDisplay = domain;
|
|
10921
11108
|
params = { asset_id: asset.id, scan_mode: options.mode };
|
|
@@ -10929,9 +11116,22 @@ Examples:
|
|
|
10929
11116
|
printSuccess(`Pentest started: ${result.domain_or_ip}`);
|
|
10930
11117
|
console.log(` Profile ID: ${result.scan_profile_id}`);
|
|
10931
11118
|
console.log(` Mode: ${result.scan_mode}`);
|
|
10932
|
-
|
|
10933
|
-
|
|
11119
|
+
if (!options.watch || !isTTY()) {
|
|
11120
|
+
console.log(` Status: ${result.scan_status}`);
|
|
11121
|
+
console.log(`
|
|
10934
11122
|
Watch progress: kinosec pentest watch ${result.scan_profile_id}`);
|
|
11123
|
+
return;
|
|
11124
|
+
}
|
|
11125
|
+
const divider = isTTY() ? import_picocolors6.default.dim("\u2500".repeat(37)) : "-".repeat(37);
|
|
11126
|
+
console.log(`
|
|
11127
|
+
${divider}
|
|
11128
|
+
`);
|
|
11129
|
+
await watchPentest(client, result.scan_profile_id, {
|
|
11130
|
+
timeoutMs: 1800 * 1e3,
|
|
11131
|
+
intervalMs: 5 * 1e3,
|
|
11132
|
+
useJson: false,
|
|
11133
|
+
showFindingsSummary: true
|
|
11134
|
+
});
|
|
10935
11135
|
} catch (error) {
|
|
10936
11136
|
handleError(error);
|
|
10937
11137
|
}
|
|
@@ -10968,7 +11168,7 @@ Examples:
|
|
|
10968
11168
|
])
|
|
10969
11169
|
);
|
|
10970
11170
|
if (result.next_cursor) {
|
|
10971
|
-
console.log(
|
|
11171
|
+
console.log(import_picocolors6.default.dim(`
|
|
10972
11172
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
10973
11173
|
}
|
|
10974
11174
|
} catch (error) {
|
|
@@ -11031,67 +11231,408 @@ Examples:
|
|
|
11031
11231
|
const opts = program3.opts();
|
|
11032
11232
|
const { token, apiUrl } = resolveToken(opts);
|
|
11033
11233
|
const client = new KinoSecClient({ apiUrl, token });
|
|
11034
|
-
|
|
11035
|
-
|
|
11036
|
-
|
|
11037
|
-
|
|
11038
|
-
|
|
11039
|
-
let lastActivity = "";
|
|
11040
|
-
if (!useJson && isTTY()) {
|
|
11041
|
-
spinner = ora("Fetching pentest status...").start();
|
|
11042
|
-
}
|
|
11043
|
-
const cleanup = () => {
|
|
11044
|
-
if (spinner) spinner.stop();
|
|
11045
|
-
};
|
|
11046
|
-
process.once("SIGINT", () => {
|
|
11047
|
-
cleanup();
|
|
11048
|
-
console.log("\nWatch cancelled. Pentest is still running.");
|
|
11049
|
-
process.exit(0);
|
|
11234
|
+
await watchPentest(client, id, {
|
|
11235
|
+
timeoutMs: parseInt(options.timeout, 10) * 1e3,
|
|
11236
|
+
intervalMs: parseInt(options.interval, 10) * 1e3,
|
|
11237
|
+
useJson: Boolean(opts.json || opts.output === "json"),
|
|
11238
|
+
showFindingsSummary: true
|
|
11050
11239
|
});
|
|
11051
|
-
|
|
11052
|
-
|
|
11053
|
-
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
|
|
11061
|
-
|
|
11062
|
-
|
|
11063
|
-
|
|
11064
|
-
|
|
11065
|
-
|
|
11066
|
-
|
|
11067
|
-
|
|
11068
|
-
|
|
11069
|
-
|
|
11070
|
-
|
|
11240
|
+
} catch (error) {
|
|
11241
|
+
handleError(error);
|
|
11242
|
+
}
|
|
11243
|
+
});
|
|
11244
|
+
}
|
|
11245
|
+
|
|
11246
|
+
// src/commands/asset.ts
|
|
11247
|
+
var import_picocolors8 = __toESM(require_picocolors(), 1);
|
|
11248
|
+
|
|
11249
|
+
// src/commands/asset-auth.ts
|
|
11250
|
+
var import_picocolors7 = __toESM(require_picocolors(), 1);
|
|
11251
|
+
|
|
11252
|
+
// src/lib/resolve-asset.ts
|
|
11253
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-/i;
|
|
11254
|
+
async function resolveAsset(client, identifier) {
|
|
11255
|
+
try {
|
|
11256
|
+
if (UUID_RE.test(identifier)) {
|
|
11257
|
+
return await client.assets.get(identifier);
|
|
11258
|
+
}
|
|
11259
|
+
return await client.assets.lookup(identifier);
|
|
11260
|
+
} catch (error) {
|
|
11261
|
+
if (error instanceof ApiError && error.status === 404) {
|
|
11262
|
+
throw new ApiError({
|
|
11263
|
+
status: 404,
|
|
11264
|
+
code: error.code,
|
|
11265
|
+
detail: `Asset "${identifier}" not found. Create it with:
|
|
11266
|
+
kinosec asset create --type domain --value ${identifier} --verify-method dns
|
|
11267
|
+
or use pentest which auto-creates the asset:
|
|
11268
|
+
kinosec pentest -t ${identifier}`,
|
|
11269
|
+
requestId: error.requestId
|
|
11270
|
+
});
|
|
11271
|
+
}
|
|
11272
|
+
throw error;
|
|
11273
|
+
}
|
|
11274
|
+
}
|
|
11275
|
+
|
|
11276
|
+
// src/lib/prompt.ts
|
|
11277
|
+
import { createInterface as createInterface2 } from "readline";
|
|
11278
|
+
async function promptMasked(label) {
|
|
11279
|
+
if (!isTTY() || !process.stdin.isTTY) {
|
|
11280
|
+
console.error(`Cannot prompt for ${label} in non-interactive mode. Use flags instead.`);
|
|
11281
|
+
process.exit(2);
|
|
11282
|
+
}
|
|
11283
|
+
process.stderr.write(`${label}: `);
|
|
11284
|
+
return new Promise((resolve, reject) => {
|
|
11285
|
+
const chars = [];
|
|
11286
|
+
process.stdin.setRawMode(true);
|
|
11287
|
+
process.stdin.resume();
|
|
11288
|
+
process.stdin.setEncoding("utf-8");
|
|
11289
|
+
const onData = (chunk) => {
|
|
11290
|
+
for (const ch of chunk) {
|
|
11291
|
+
if (ch === "\r" || ch === "\n") {
|
|
11292
|
+
process.stdin.setRawMode(false);
|
|
11293
|
+
process.stdin.pause();
|
|
11294
|
+
process.stdin.removeListener("data", onData);
|
|
11295
|
+
process.stderr.write("\n");
|
|
11296
|
+
resolve(chars.join(""));
|
|
11297
|
+
return;
|
|
11298
|
+
} else if (ch === "") {
|
|
11299
|
+
process.stdin.setRawMode(false);
|
|
11300
|
+
process.stdin.pause();
|
|
11301
|
+
process.stdin.removeListener("data", onData);
|
|
11302
|
+
process.stderr.write("\n");
|
|
11303
|
+
reject(new Error("Cancelled"));
|
|
11304
|
+
return;
|
|
11305
|
+
} else if (ch === "\x7F" || ch === "\b") {
|
|
11306
|
+
if (chars.length > 0) {
|
|
11307
|
+
chars.pop();
|
|
11308
|
+
process.stderr.write("\b \b");
|
|
11071
11309
|
}
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
|
|
11310
|
+
} else if (ch >= " ") {
|
|
11311
|
+
chars.push(ch);
|
|
11312
|
+
process.stderr.write("*");
|
|
11313
|
+
}
|
|
11314
|
+
}
|
|
11315
|
+
};
|
|
11316
|
+
process.stdin.on("data", onData);
|
|
11317
|
+
});
|
|
11318
|
+
}
|
|
11319
|
+
async function promptText(label) {
|
|
11320
|
+
if (!isTTY() || !process.stdin.isTTY) {
|
|
11321
|
+
console.error(`Cannot prompt for ${label} in non-interactive mode. Use flags instead.`);
|
|
11322
|
+
process.exit(2);
|
|
11323
|
+
}
|
|
11324
|
+
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
11325
|
+
const answer = await new Promise((resolve) => {
|
|
11326
|
+
rl.question(`${label}: `, resolve);
|
|
11327
|
+
});
|
|
11328
|
+
rl.close();
|
|
11329
|
+
return answer;
|
|
11330
|
+
}
|
|
11331
|
+
async function promptSelect(message, options) {
|
|
11332
|
+
if (!isTTY() || !process.stdin.isTTY) {
|
|
11333
|
+
console.error("Cannot show interactive menu in non-interactive mode. Use flags instead.");
|
|
11334
|
+
process.exit(2);
|
|
11335
|
+
}
|
|
11336
|
+
console.error(`
|
|
11337
|
+
${message}`);
|
|
11338
|
+
for (let i = 0; i < options.length; i++) {
|
|
11339
|
+
console.error(` ${i + 1}. ${options[i].label} \u2014 ${options[i].description}`);
|
|
11340
|
+
}
|
|
11341
|
+
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
11342
|
+
const answer = await new Promise((resolve) => {
|
|
11343
|
+
rl.question("> ", resolve);
|
|
11344
|
+
});
|
|
11345
|
+
rl.close();
|
|
11346
|
+
const index = parseInt(answer, 10) - 1;
|
|
11347
|
+
if (isNaN(index) || index < 0 || index >= options.length) {
|
|
11348
|
+
console.error("Invalid selection.");
|
|
11349
|
+
process.exit(2);
|
|
11350
|
+
}
|
|
11351
|
+
return options[index].value;
|
|
11352
|
+
}
|
|
11353
|
+
|
|
11354
|
+
// src/commands/asset-auth.ts
|
|
11355
|
+
var VALID_AUTH_TYPES = ["bearer", "basic", "apikey", "session", "cookie"];
|
|
11356
|
+
var AUTH_TYPE_OPTIONS = [
|
|
11357
|
+
{ value: "bearer", label: "Bearer Token", description: "Authorization header with token" },
|
|
11358
|
+
{ value: "apikey", label: "API Key", description: "Custom header (e.g. X-API-Key)" },
|
|
11359
|
+
{ value: "basic", label: "Basic Auth", description: "Username + password" },
|
|
11360
|
+
{ value: "session", label: "Session Login", description: "Replays login form POST" },
|
|
11361
|
+
{ value: "cookie", label: "Cookie / Session", description: "Inject browser cookies" }
|
|
11362
|
+
];
|
|
11363
|
+
async function collectCredentialsInteractive(authType) {
|
|
11364
|
+
const params = { auth_type: authType, credentials: {} };
|
|
11365
|
+
switch (authType) {
|
|
11366
|
+
case "bearer": {
|
|
11367
|
+
params.credentials = { token: await promptMasked("Bearer token") };
|
|
11368
|
+
break;
|
|
11369
|
+
}
|
|
11370
|
+
case "basic": {
|
|
11371
|
+
const username = await promptText("Username");
|
|
11372
|
+
const password = await promptMasked("Password");
|
|
11373
|
+
params.credentials = { username, password };
|
|
11374
|
+
break;
|
|
11375
|
+
}
|
|
11376
|
+
case "apikey": {
|
|
11377
|
+
const key = await promptMasked("API key");
|
|
11378
|
+
const headerName = await promptText("Header name (e.g. X-API-Key)");
|
|
11379
|
+
params.credentials = { key };
|
|
11380
|
+
params.auth_header_name = headerName;
|
|
11381
|
+
break;
|
|
11382
|
+
}
|
|
11383
|
+
case "session": {
|
|
11384
|
+
const username = await promptText("Username or email");
|
|
11385
|
+
const password = await promptMasked("Password");
|
|
11386
|
+
const endpoint = await promptText("Login endpoint URL");
|
|
11387
|
+
params.credentials = { email: username, password };
|
|
11388
|
+
params.auth_endpoint_url = endpoint;
|
|
11389
|
+
break;
|
|
11390
|
+
}
|
|
11391
|
+
case "cookie": {
|
|
11392
|
+
params.credentials = { cookie: await promptMasked("Cookie string") };
|
|
11393
|
+
break;
|
|
11394
|
+
}
|
|
11395
|
+
}
|
|
11396
|
+
return params;
|
|
11397
|
+
}
|
|
11398
|
+
function collectCredentialsFromFlags(options) {
|
|
11399
|
+
const authType = options.type;
|
|
11400
|
+
const params = { auth_type: authType, credentials: {} };
|
|
11401
|
+
switch (authType) {
|
|
11402
|
+
case "bearer": {
|
|
11403
|
+
if (!options.bearerToken) {
|
|
11404
|
+
console.error("--bearer-token is required for bearer auth type.");
|
|
11405
|
+
process.exit(2);
|
|
11406
|
+
}
|
|
11407
|
+
params.credentials = { token: options.bearerToken };
|
|
11408
|
+
break;
|
|
11409
|
+
}
|
|
11410
|
+
case "basic": {
|
|
11411
|
+
if (!options.username || !options.password) {
|
|
11412
|
+
console.error("--username and --password are required for basic auth type.");
|
|
11413
|
+
process.exit(2);
|
|
11414
|
+
}
|
|
11415
|
+
params.credentials = { username: options.username, password: options.password };
|
|
11416
|
+
break;
|
|
11417
|
+
}
|
|
11418
|
+
case "apikey": {
|
|
11419
|
+
if (!options.key || !options.headerName) {
|
|
11420
|
+
console.error("--key and --header-name are required for apikey auth type.");
|
|
11421
|
+
process.exit(2);
|
|
11422
|
+
}
|
|
11423
|
+
params.credentials = { key: options.key };
|
|
11424
|
+
params.auth_header_name = options.headerName;
|
|
11425
|
+
break;
|
|
11426
|
+
}
|
|
11427
|
+
case "session": {
|
|
11428
|
+
if (!options.username || !options.password || !options.authEndpoint) {
|
|
11429
|
+
console.error("--username, --password, and --auth-endpoint are required for session auth type.");
|
|
11430
|
+
process.exit(2);
|
|
11431
|
+
}
|
|
11432
|
+
params.credentials = { email: options.username, password: options.password };
|
|
11433
|
+
params.auth_endpoint_url = options.authEndpoint;
|
|
11434
|
+
break;
|
|
11435
|
+
}
|
|
11436
|
+
case "cookie": {
|
|
11437
|
+
if (!options.cookie) {
|
|
11438
|
+
console.error("--cookie is required for cookie auth type.");
|
|
11439
|
+
process.exit(2);
|
|
11440
|
+
}
|
|
11441
|
+
params.credentials = { cookie: options.cookie };
|
|
11442
|
+
break;
|
|
11443
|
+
}
|
|
11444
|
+
}
|
|
11445
|
+
if (options.context) {
|
|
11446
|
+
params.context_text = options.context;
|
|
11447
|
+
}
|
|
11448
|
+
return params;
|
|
11449
|
+
}
|
|
11450
|
+
function registerAssetAuthCommand(assetCmd) {
|
|
11451
|
+
const auth = assetCmd.command("auth").description("Manage asset authentication credentials");
|
|
11452
|
+
auth.command("set").description("Configure authentication credentials for an asset").argument("<asset>", "Asset domain name or UUID").option("--type <type>", "Auth type: bearer, basic, apikey, session, cookie").option("--bearer-token <value>", "Bearer token (bearer type)").option("--username <value>", "Username or email (basic, session types)").option("--password <value>", "Password (basic, session types)").option("--key <value>", "API key value (apikey type)").option("--header-name <name>", "Custom header name (apikey type, e.g. X-API-Key)").option("--cookie <value>", "Cookie string (cookie type)").option("--auth-endpoint <url>", "Login endpoint URL (session type)").option("--context <text>", "Optional notes about the auth configuration").addHelpText("after", `
|
|
11453
|
+
Examples:
|
|
11454
|
+
$ kinosec asset auth set example.com # interactive
|
|
11455
|
+
$ kinosec asset auth set example.com --type bearer --bearer-token "$TOKEN"
|
|
11456
|
+
$ kinosec asset auth set example.com --type basic --username admin --password "$PASS"
|
|
11457
|
+
$ kinosec asset auth set example.com --type apikey --key "$KEY" --header-name "X-API-Key"
|
|
11458
|
+
$ kinosec asset auth set example.com --type session --username admin@example.com --password "$PASS" --auth-endpoint "https://example.com/api/login"
|
|
11459
|
+
$ kinosec asset auth set example.com --type cookie --cookie "session_id=abc123"
|
|
11460
|
+
|
|
11461
|
+
Security: Pass credentials via environment variables to avoid shell history exposure.`).action(async (assetIdentifier, options) => {
|
|
11462
|
+
try {
|
|
11463
|
+
const opts = auth.parent.parent.opts();
|
|
11464
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
11465
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
11466
|
+
const asset = await resolveAsset(client, assetIdentifier);
|
|
11467
|
+
let params;
|
|
11468
|
+
if (options.type) {
|
|
11469
|
+
if (!VALID_AUTH_TYPES.includes(options.type)) {
|
|
11470
|
+
console.error(`Invalid auth type "${options.type}". Must be: ${VALID_AUTH_TYPES.join(", ")}`);
|
|
11471
|
+
process.exit(2);
|
|
11472
|
+
}
|
|
11473
|
+
params = collectCredentialsFromFlags(options);
|
|
11474
|
+
} else {
|
|
11475
|
+
const authType = await promptSelect("Authentication type:", AUTH_TYPE_OPTIONS);
|
|
11476
|
+
params = await collectCredentialsInteractive(authType);
|
|
11477
|
+
if (options.context) {
|
|
11478
|
+
params.context_text = options.context;
|
|
11479
|
+
}
|
|
11480
|
+
}
|
|
11481
|
+
const result = await client.assets.credentials.set(asset.id, params);
|
|
11482
|
+
if (opts.json || opts.output === "json") {
|
|
11483
|
+
printJson({ ...result, asset: asset.asset_value });
|
|
11484
|
+
return;
|
|
11485
|
+
}
|
|
11486
|
+
printSuccess(`Credentials saved for ${asset.asset_value} (${result.auth_type})`);
|
|
11487
|
+
} catch (error) {
|
|
11488
|
+
handleError(error);
|
|
11489
|
+
}
|
|
11490
|
+
});
|
|
11491
|
+
auth.command("show").description("Show authentication configuration for an asset").argument("<asset>", "Asset domain name or UUID").addHelpText("after", `
|
|
11492
|
+
Examples:
|
|
11493
|
+
$ kinosec asset auth show example.com
|
|
11494
|
+
$ kinosec asset auth show example.com --json`).action(async (assetIdentifier) => {
|
|
11495
|
+
try {
|
|
11496
|
+
const opts = auth.parent.parent.opts();
|
|
11497
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
11498
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
11499
|
+
const asset = await resolveAsset(client, assetIdentifier);
|
|
11500
|
+
const creds = await client.assets.credentials.get(asset.id);
|
|
11501
|
+
if (opts.json || opts.output === "json") {
|
|
11502
|
+
printJson({ asset: asset.asset_value, ...creds });
|
|
11503
|
+
return;
|
|
11504
|
+
}
|
|
11505
|
+
if (!creds.has_credentials) {
|
|
11506
|
+
console.log(`Asset: ${asset.asset_value}`);
|
|
11507
|
+
console.log(`Auth Type: ${import_picocolors7.default.dim("(none)")}`);
|
|
11508
|
+
console.log(`Has Credentials: no`);
|
|
11509
|
+
return;
|
|
11510
|
+
}
|
|
11511
|
+
console.log(`Asset: ${asset.asset_value}`);
|
|
11512
|
+
console.log(`Auth Type: ${creds.auth_type}`);
|
|
11513
|
+
console.log(`Has Credentials: yes`);
|
|
11514
|
+
if (creds.auth_header_name) {
|
|
11515
|
+
console.log(`Header Name: ${creds.auth_header_name}`);
|
|
11516
|
+
}
|
|
11517
|
+
if (creds.auth_endpoint_url) {
|
|
11518
|
+
console.log(`Auth Endpoint: ${creds.auth_endpoint_url}`);
|
|
11519
|
+
}
|
|
11520
|
+
if (creds.context_text) {
|
|
11521
|
+
console.log(`Notes: ${creds.context_text}`);
|
|
11522
|
+
}
|
|
11523
|
+
if (creds.target_account) {
|
|
11524
|
+
const ta = creds.target_account;
|
|
11525
|
+
const hasAny = ta.has_username || ta.has_cookie || ta.has_bearer_token || ta.has_apikey;
|
|
11526
|
+
if (hasAny) {
|
|
11527
|
+
const detail = ta.has_username ? "username" : ta.has_bearer_token ? "bearer token" : ta.has_cookie ? "cookie" : "API key";
|
|
11528
|
+
console.log(`Target Account: ${import_picocolors7.default.green("configured")} (${detail})`);
|
|
11529
|
+
} else {
|
|
11530
|
+
console.log(`Target Account: ${import_picocolors7.default.dim("not configured")}`);
|
|
11531
|
+
}
|
|
11532
|
+
}
|
|
11533
|
+
} catch (error) {
|
|
11534
|
+
handleError(error);
|
|
11535
|
+
}
|
|
11536
|
+
});
|
|
11537
|
+
auth.command("remove").description("Remove all credentials for an asset").argument("<asset>", "Asset domain name or UUID").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
|
|
11538
|
+
Examples:
|
|
11539
|
+
$ kinosec asset auth remove example.com
|
|
11540
|
+
$ kinosec asset auth remove example.com --yes`).action(async (assetIdentifier, options) => {
|
|
11541
|
+
try {
|
|
11542
|
+
const opts = auth.parent.parent.opts();
|
|
11543
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
11544
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
11545
|
+
const asset = await resolveAsset(client, assetIdentifier);
|
|
11546
|
+
await confirmDestructive(`Remove all credentials for ${asset.asset_value}?`, Boolean(options.yes));
|
|
11547
|
+
await client.assets.credentials.remove(asset.id);
|
|
11548
|
+
if (opts.json || opts.output === "json") {
|
|
11549
|
+
printJson({ deleted: true, asset: asset.asset_value });
|
|
11550
|
+
return;
|
|
11551
|
+
}
|
|
11552
|
+
printSuccess(`Credentials removed for ${asset.asset_value}`);
|
|
11553
|
+
} catch (error) {
|
|
11554
|
+
handleError(error);
|
|
11555
|
+
}
|
|
11556
|
+
});
|
|
11557
|
+
auth.command("set-target").description("Configure IDOR target account credentials").argument("<asset>", "Asset domain name or UUID").option("--target-username <value>", "Target account username or email").option("--target-password <value>", "Target account password").option("--target-totp-secret <value>", "Target account TOTP secret").option("--target-token <value>", "Target bearer token").option("--target-cookie <value>", "Target cookie string").option("--target-key <value>", "Target API key").addHelpText("after", `
|
|
11558
|
+
Examples:
|
|
11559
|
+
$ kinosec asset auth set-target example.com # interactive
|
|
11560
|
+
$ kinosec asset auth set-target example.com --target-username victim@example.com --target-password "$PASS"
|
|
11561
|
+
$ kinosec asset auth set-target example.com --target-token "$VICTIM_TOKEN"
|
|
11562
|
+
$ kinosec asset auth set-target example.com --target-cookie "session_id=victim_abc"`).action(async (assetIdentifier, options) => {
|
|
11563
|
+
try {
|
|
11564
|
+
const opts = auth.parent.parent.opts();
|
|
11565
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
11566
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
11567
|
+
const asset = await resolveAsset(client, assetIdentifier);
|
|
11568
|
+
const creds = await client.assets.credentials.get(asset.id);
|
|
11569
|
+
if (!creds.has_credentials || !creds.auth_type) {
|
|
11570
|
+
console.error(`No primary credentials configured for ${asset.asset_value}. Run 'kinosec asset auth set' first.`);
|
|
11571
|
+
process.exit(2);
|
|
11572
|
+
}
|
|
11573
|
+
const isTokenBased = creds.auth_type === "bearer" || creds.auth_type === "apikey" || creds.auth_type === "cookie";
|
|
11574
|
+
const hasAnyFlag = options.targetUsername || options.targetPassword || options.targetTotpSecret || options.targetToken || options.targetCookie || options.targetKey;
|
|
11575
|
+
let targetParams;
|
|
11576
|
+
if (hasAnyFlag) {
|
|
11577
|
+
targetParams = {
|
|
11578
|
+
username: options.targetUsername,
|
|
11579
|
+
password: options.targetPassword,
|
|
11580
|
+
totp_secret: options.targetTotpSecret,
|
|
11581
|
+
bearer_token: options.targetToken,
|
|
11582
|
+
cookie: options.targetCookie,
|
|
11583
|
+
apikey: options.targetKey
|
|
11584
|
+
};
|
|
11585
|
+
} else {
|
|
11586
|
+
console.error(`
|
|
11587
|
+
Primary auth type: ${creds.auth_type}
|
|
11588
|
+
`);
|
|
11589
|
+
if (isTokenBased) {
|
|
11590
|
+
if (creds.auth_type === "bearer") {
|
|
11591
|
+
const targetToken = await promptMasked("Target bearer token");
|
|
11592
|
+
targetParams = { bearer_token: targetToken };
|
|
11593
|
+
} else if (creds.auth_type === "cookie") {
|
|
11594
|
+
const targetCookie = await promptMasked("Target cookie string");
|
|
11595
|
+
targetParams = { cookie: targetCookie };
|
|
11596
|
+
} else {
|
|
11597
|
+
const targetKey = await promptMasked("Target API key");
|
|
11598
|
+
targetParams = { apikey: targetKey };
|
|
11076
11599
|
}
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11600
|
+
} else {
|
|
11601
|
+
const username = await promptText("Target account username");
|
|
11602
|
+
const password = await promptMasked("Target account password");
|
|
11603
|
+
targetParams = { username, password };
|
|
11604
|
+
const totp = await promptText("Target TOTP secret (optional, Enter to skip)");
|
|
11605
|
+
if (totp) {
|
|
11606
|
+
targetParams.totp_secret = totp;
|
|
11080
11607
|
}
|
|
11081
|
-
console.log(import_picocolors5.default.green(`
|
|
11082
|
-
Pentest completed: ${scanDetail.domain_or_ip}`));
|
|
11083
|
-
console.log(` View findings: kinosec finding list ${id}`);
|
|
11084
|
-
break;
|
|
11085
|
-
}
|
|
11086
|
-
if (Date.now() - startTime > timeoutMs) {
|
|
11087
|
-
cleanup();
|
|
11088
|
-
console.error(`
|
|
11089
|
-
Timeout: pentest still running after ${options.timeout}s.`);
|
|
11090
|
-
console.error(`Check status: kinosec pentest get ${id}`);
|
|
11091
|
-
process.exit(3);
|
|
11092
11608
|
}
|
|
11093
|
-
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
11094
11609
|
}
|
|
11610
|
+
await client.assets.credentials.setTarget(asset.id, targetParams);
|
|
11611
|
+
if (opts.json || opts.output === "json") {
|
|
11612
|
+
printJson({ saved: true, asset: asset.asset_value });
|
|
11613
|
+
return;
|
|
11614
|
+
}
|
|
11615
|
+
printSuccess(`Target account saved for ${asset.asset_value} (IDOR testing)`);
|
|
11616
|
+
} catch (error) {
|
|
11617
|
+
handleError(error);
|
|
11618
|
+
}
|
|
11619
|
+
});
|
|
11620
|
+
auth.command("remove-target").description("Remove target account credentials (keeps primary credentials)").argument("<asset>", "Asset domain name or UUID").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
|
|
11621
|
+
Examples:
|
|
11622
|
+
$ kinosec asset auth remove-target example.com
|
|
11623
|
+
$ kinosec asset auth remove-target example.com --yes`).action(async (assetIdentifier, options) => {
|
|
11624
|
+
try {
|
|
11625
|
+
const opts = auth.parent.parent.opts();
|
|
11626
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
11627
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
11628
|
+
const asset = await resolveAsset(client, assetIdentifier);
|
|
11629
|
+
await confirmDestructive(`Remove target account for ${asset.asset_value}?`, Boolean(options.yes));
|
|
11630
|
+
await client.assets.credentials.removeTarget(asset.id);
|
|
11631
|
+
if (opts.json || opts.output === "json") {
|
|
11632
|
+
printJson({ deleted: true, asset: asset.asset_value });
|
|
11633
|
+
return;
|
|
11634
|
+
}
|
|
11635
|
+
printSuccess(`Target account removed for ${asset.asset_value}`);
|
|
11095
11636
|
} catch (error) {
|
|
11096
11637
|
handleError(error);
|
|
11097
11638
|
}
|
|
@@ -11099,7 +11640,6 @@ Timeout: pentest still running after ${options.timeout}s.`);
|
|
|
11099
11640
|
}
|
|
11100
11641
|
|
|
11101
11642
|
// src/commands/asset.ts
|
|
11102
|
-
var import_picocolors6 = __toESM(require_picocolors(), 1);
|
|
11103
11643
|
function registerAssetCommand(program3) {
|
|
11104
11644
|
const asset = program3.command("asset").description("Manage assets");
|
|
11105
11645
|
asset.command("list").description("List assets").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").option("--search <term>", "Filter by asset value (substring match)").option("--status <status>", "Filter by status (verified, unverified)").option("--type <type>", "Filter by type (domain, subdomain, ip)").addHelpText("after", `
|
|
@@ -11138,7 +11678,7 @@ Examples:
|
|
|
11138
11678
|
])
|
|
11139
11679
|
);
|
|
11140
11680
|
if (result.next_cursor) {
|
|
11141
|
-
console.log(
|
|
11681
|
+
console.log(import_picocolors8.default.dim(`
|
|
11142
11682
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
11143
11683
|
}
|
|
11144
11684
|
} catch (error) {
|
|
@@ -11246,10 +11786,11 @@ Examples:
|
|
|
11246
11786
|
handleError(error);
|
|
11247
11787
|
}
|
|
11248
11788
|
});
|
|
11789
|
+
registerAssetAuthCommand(asset);
|
|
11249
11790
|
}
|
|
11250
11791
|
|
|
11251
11792
|
// src/commands/finding.ts
|
|
11252
|
-
var
|
|
11793
|
+
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
11253
11794
|
function formatPoc(poc) {
|
|
11254
11795
|
if (poc == null) return null;
|
|
11255
11796
|
if (typeof poc === "string") return poc.trim() || null;
|
|
@@ -11290,7 +11831,7 @@ Examples:
|
|
|
11290
11831
|
])
|
|
11291
11832
|
);
|
|
11292
11833
|
if (result.next_cursor) {
|
|
11293
|
-
console.log(
|
|
11834
|
+
console.log(import_picocolors9.default.dim(`
|
|
11294
11835
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
11295
11836
|
}
|
|
11296
11837
|
} catch (error) {
|
|
@@ -11378,7 +11919,7 @@ Examples:
|
|
|
11378
11919
|
}
|
|
11379
11920
|
|
|
11380
11921
|
// src/commands/report.ts
|
|
11381
|
-
var
|
|
11922
|
+
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
11382
11923
|
function renderMarkdown(data) {
|
|
11383
11924
|
const { scan, findings } = data;
|
|
11384
11925
|
const lines = [];
|
|
@@ -11455,7 +11996,7 @@ Examples:
|
|
|
11455
11996
|
])
|
|
11456
11997
|
);
|
|
11457
11998
|
if (result.next_cursor) {
|
|
11458
|
-
console.log(
|
|
11999
|
+
console.log(import_picocolors10.default.dim(`
|
|
11459
12000
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
11460
12001
|
}
|
|
11461
12002
|
} catch (error) {
|
|
@@ -11526,7 +12067,7 @@ Examples:
|
|
|
11526
12067
|
}
|
|
11527
12068
|
|
|
11528
12069
|
// src/commands/token.ts
|
|
11529
|
-
var
|
|
12070
|
+
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
11530
12071
|
function registerTokenCommand(program3) {
|
|
11531
12072
|
const token = program3.command("token").description("Manage API tokens");
|
|
11532
12073
|
token.command("list").description("List API tokens").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
|
|
@@ -11561,7 +12102,7 @@ Examples:
|
|
|
11561
12102
|
])
|
|
11562
12103
|
);
|
|
11563
12104
|
if (result.next_cursor) {
|
|
11564
|
-
console.log(
|
|
12105
|
+
console.log(import_picocolors11.default.dim(`
|
|
11565
12106
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
11566
12107
|
}
|
|
11567
12108
|
} catch (error) {
|
|
@@ -11588,7 +12129,7 @@ Examples:
|
|
|
11588
12129
|
}
|
|
11589
12130
|
printSuccess(`Token created: ${result.name}`);
|
|
11590
12131
|
console.log("");
|
|
11591
|
-
console.log(
|
|
12132
|
+
console.log(import_picocolors11.default.bold(" Token: ") + result.token);
|
|
11592
12133
|
console.log("");
|
|
11593
12134
|
printWarning("Save this token \u2014 it will not be shown again.");
|
|
11594
12135
|
console.log(` ID: ${result.id}`);
|
|
@@ -11621,7 +12162,7 @@ Examples:
|
|
|
11621
12162
|
|
|
11622
12163
|
// src/cli.ts
|
|
11623
12164
|
var program2 = new Command();
|
|
11624
|
-
program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.
|
|
12165
|
+
program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.5.0", "-v, --version").option("--token <pat>", "API token (overrides KINOSEC_TOKEN and config)").option("--api-url <url>", "API base URL (overrides config)").option("--output <format>", "Output format: text or json", "text").option("--json", "Shorthand for --output json").option("--no-color", "Disable colors");
|
|
11625
12166
|
registerAuthCommand(program2);
|
|
11626
12167
|
registerConfigCommand(program2);
|
|
11627
12168
|
registerMeCommand(program2);
|
|
@@ -11630,7 +12171,7 @@ registerAssetCommand(program2);
|
|
|
11630
12171
|
registerFindingCommand(program2);
|
|
11631
12172
|
registerReportCommand(program2);
|
|
11632
12173
|
registerTokenCommand(program2);
|
|
11633
|
-
program2.parseAsync(process.argv).then(() => checkForUpdate("0.
|
|
12174
|
+
program2.parseAsync(process.argv).then(() => checkForUpdate("0.5.0")).catch(handleError);
|
|
11634
12175
|
/*! Bundled license information:
|
|
11635
12176
|
|
|
11636
12177
|
ky/distribution/index.js:
|