@kinosec/cli 0.10.1 → 0.10.3
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 +132 -71
- package/dist/cli.js.map +1 -1
- package/package.json +13 -13
package/dist/cli.js
CHANGED
|
@@ -8048,12 +8048,8 @@ import { join as join2, dirname as dirname2 } from "path";
|
|
|
8048
8048
|
// src/lib/config.ts
|
|
8049
8049
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, openSync, writeSync, closeSync, unlinkSync, constants } from "fs";
|
|
8050
8050
|
import { join, dirname } from "path";
|
|
8051
|
-
import { homedir
|
|
8051
|
+
import { homedir } from "os";
|
|
8052
8052
|
function getConfigDir() {
|
|
8053
|
-
const p = platform();
|
|
8054
|
-
if (p === "win32") {
|
|
8055
|
-
return join(process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "kinosec");
|
|
8056
|
-
}
|
|
8057
8053
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
8058
8054
|
if (xdg) return join(xdg, "kinosec");
|
|
8059
8055
|
return join(homedir(), ".config", "kinosec");
|
|
@@ -8074,7 +8070,7 @@ function readJsonFile(path) {
|
|
|
8074
8070
|
function writeJsonFile(path, data, restrictPermissions = false) {
|
|
8075
8071
|
ensureDir(dirname(path));
|
|
8076
8072
|
const content = JSON.stringify(data, null, 2) + "\n";
|
|
8077
|
-
if (restrictPermissions
|
|
8073
|
+
if (restrictPermissions) {
|
|
8078
8074
|
const fd = openSync(path, constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC, 384);
|
|
8079
8075
|
writeSync(fd, content);
|
|
8080
8076
|
closeSync(fd);
|
|
@@ -8439,13 +8435,10 @@ ${import_picocolors3.default.dim("use of its services.")}
|
|
|
8439
8435
|
var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
8440
8436
|
|
|
8441
8437
|
// src/lib/validate-mode.ts
|
|
8438
|
+
var VALID_SCAN_MODES = ["full", "quick", "htb", "defcon"];
|
|
8442
8439
|
function validateScanMode(mode) {
|
|
8443
|
-
if (mode
|
|
8444
|
-
console.error(
|
|
8445
|
-
process.exit(2);
|
|
8446
|
-
}
|
|
8447
|
-
if (mode !== "full") {
|
|
8448
|
-
console.error(`Invalid mode "${mode}". Must be: full`);
|
|
8440
|
+
if (!VALID_SCAN_MODES.includes(mode)) {
|
|
8441
|
+
console.error(`Invalid mode "${mode}". Must be one of: ${VALID_SCAN_MODES.join(", ")}.`);
|
|
8449
8442
|
process.exit(2);
|
|
8450
8443
|
}
|
|
8451
8444
|
}
|
|
@@ -12019,10 +12012,9 @@ function extractPhase(entries) {
|
|
|
12019
12012
|
return null;
|
|
12020
12013
|
}
|
|
12021
12014
|
function printActivityLines(entries, tty3) {
|
|
12022
|
-
for (
|
|
12023
|
-
const
|
|
12024
|
-
const
|
|
12025
|
-
const text = tty3 ? isLastEntry ? import_picocolors5.default.green(entries[i]) : import_picocolors5.default.dim(import_picocolors5.default.green(entries[i])) : entries[i];
|
|
12015
|
+
for (const entry of entries) {
|
|
12016
|
+
const prompt = tty3 ? import_picocolors5.default.dim(import_picocolors5.default.green(">")) : ">";
|
|
12017
|
+
const text = tty3 ? import_picocolors5.default.dim(import_picocolors5.default.green(entry)) : entry;
|
|
12026
12018
|
console.log(`${prompt} ${text}`);
|
|
12027
12019
|
}
|
|
12028
12020
|
}
|
|
@@ -12049,7 +12041,7 @@ async function watchPentest(client, profileId, options) {
|
|
|
12049
12041
|
spinner.text = import_picocolors5.default.green(currentPhase ? `[${currentPhase}] ${buzz}...` : `${buzz}...`);
|
|
12050
12042
|
}
|
|
12051
12043
|
if (!useJson && tty3) {
|
|
12052
|
-
spinner = ora({ text: "Fetching pentest status...", color: "green", isEnabled: true }).start();
|
|
12044
|
+
spinner = ora({ text: "Fetching pentest status...", color: "green", isEnabled: true, discardStdin: false }).start();
|
|
12053
12045
|
cycler = createBuzzwordCycler(SCAN_EXECUTION_PHRASES, 2500, () => {
|
|
12054
12046
|
updateSpinnerText();
|
|
12055
12047
|
});
|
|
@@ -12064,71 +12056,134 @@ async function watchPentest(client, profileId, options) {
|
|
|
12064
12056
|
process.exit(0);
|
|
12065
12057
|
});
|
|
12066
12058
|
const showFullDetails = await resolveShowFullDetails(client);
|
|
12067
|
-
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
|
|
12082
|
-
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12059
|
+
let consecutiveErrors = 0;
|
|
12060
|
+
const MAX_CONSECUTIVE_ERRORS = 2;
|
|
12061
|
+
let deferredExitCode = null;
|
|
12062
|
+
try {
|
|
12063
|
+
while (true) {
|
|
12064
|
+
let scanDetail;
|
|
12065
|
+
try {
|
|
12066
|
+
scanDetail = await client.scans.get(profileId);
|
|
12067
|
+
consecutiveErrors = 0;
|
|
12068
|
+
} catch (error2) {
|
|
12069
|
+
if (error2 instanceof ApiError) {
|
|
12070
|
+
if (error2.status === 401 || error2.status === 403) {
|
|
12071
|
+
throw error2;
|
|
12072
|
+
}
|
|
12073
|
+
if (error2.status === 404) {
|
|
12074
|
+
if (!useJson) {
|
|
12075
|
+
const icon2 = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
|
|
12076
|
+
console.log(`
|
|
12077
|
+
${icon2} Pentest is no longer available.`);
|
|
12078
|
+
console.log(` Check status: kinosec pentest get ${profileId}`);
|
|
12079
|
+
}
|
|
12080
|
+
break;
|
|
12081
|
+
}
|
|
12082
|
+
}
|
|
12083
|
+
consecutiveErrors++;
|
|
12084
|
+
if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
12085
|
+
if (!useJson) {
|
|
12086
|
+
const icon2 = tty3 ? import_picocolors5.default.red(sym.fail) : sym.fail;
|
|
12087
|
+
console.log(`
|
|
12088
|
+
${icon2} Lost connection to pentest.`);
|
|
12089
|
+
console.log(` Check status: kinosec pentest get ${profileId}`);
|
|
12090
|
+
}
|
|
12091
|
+
break;
|
|
12092
|
+
}
|
|
12093
|
+
if (spinner) {
|
|
12094
|
+
spinner.text = import_picocolors5.default.yellow("Connection lost, retrying...");
|
|
12095
|
+
}
|
|
12096
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
12097
|
+
continue;
|
|
12098
|
+
}
|
|
12099
|
+
const totalMs = Date.now() - loopStart;
|
|
12100
|
+
if (useJson) {
|
|
12101
|
+
const elapsed = Math.round(totalMs / 1e3);
|
|
12102
|
+
process.stdout.write(
|
|
12103
|
+
JSON.stringify({ ...scanDetail, elapsed_seconds: elapsed }) + "\n"
|
|
12104
|
+
);
|
|
12105
|
+
} else {
|
|
12106
|
+
const normalized = normalizeActivityHistory(scanDetail.activity_history);
|
|
12107
|
+
const filtered = filterActivityForTier(normalized, showFullDetails);
|
|
12108
|
+
if (printedCount > filtered.length) printedCount = 0;
|
|
12109
|
+
const newEntries = filtered.slice(printedCount);
|
|
12110
|
+
if (newEntries.length > 0) {
|
|
12111
|
+
if (spinner) spinner.stop();
|
|
12112
|
+
printActivityLines(newEntries, tty3);
|
|
12113
|
+
printedCount += newEntries.length;
|
|
12114
|
+
if (spinner) spinner = ora({ color: "green", isEnabled: true, discardStdin: false }).start();
|
|
12115
|
+
}
|
|
12116
|
+
currentPhase = extractPhase(filtered);
|
|
12117
|
+
updateSpinnerText();
|
|
12118
|
+
}
|
|
12119
|
+
const status = scanDetail.scan_status;
|
|
12120
|
+
if (status === "Running...") {
|
|
12121
|
+
if (Date.now() - loopStart > timeoutMs) {
|
|
12122
|
+
const secs = Math.round(timeoutMs / 1e3);
|
|
12123
|
+
console.error(`
|
|
12124
|
+
Timeout: pentest still running after ${secs}s.`);
|
|
12125
|
+
console.error(`Check status: kinosec pentest get ${profileId}`);
|
|
12126
|
+
deferredExitCode = 3;
|
|
12127
|
+
break;
|
|
12128
|
+
}
|
|
12129
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
12130
|
+
continue;
|
|
12131
|
+
}
|
|
12092
12132
|
if (useJson) break;
|
|
12093
|
-
if (
|
|
12133
|
+
if (status === "Completed") {
|
|
12134
|
+
const totalDur = formatDuration(Date.now() - loopStart);
|
|
12135
|
+
const domain = tty3 ? import_picocolors5.default.bold(scanDetail.domain_or_ip) : scanDetail.domain_or_ip;
|
|
12136
|
+
const icon2 = tty3 ? import_picocolors5.default.green(sym.success) : sym.success;
|
|
12137
|
+
const dur = tty3 ? import_picocolors5.default.dim(totalDur) : totalDur;
|
|
12138
|
+
console.log(`
|
|
12139
|
+
${icon2} Pentest completed: ${domain} ${dur}`);
|
|
12140
|
+
if (showFindingsSummary) {
|
|
12141
|
+
await printFindingsSummary(client, profileId, tty3);
|
|
12142
|
+
}
|
|
12143
|
+
const cmd = `kinosec finding list ${profileId}`;
|
|
12144
|
+
const viewLabel = tty3 ? import_picocolors5.default.dim("View details:") : "View details:";
|
|
12145
|
+
const viewCmd = tty3 ? import_picocolors5.default.cyan(cmd) : cmd;
|
|
12146
|
+
console.log(`
|
|
12147
|
+
${viewLabel} ${viewCmd}`);
|
|
12148
|
+
console.log();
|
|
12149
|
+
break;
|
|
12150
|
+
}
|
|
12151
|
+
if (status === "Failed") {
|
|
12094
12152
|
const icon2 = tty3 ? import_picocolors5.default.red(sym.fail) : sym.fail;
|
|
12095
12153
|
const reason = scanDetail.failure_reason || "unknown reason";
|
|
12096
12154
|
console.log(`
|
|
12097
12155
|
${icon2} Pentest failed: ${reason}`);
|
|
12098
|
-
|
|
12156
|
+
deferredExitCode = 1;
|
|
12157
|
+
break;
|
|
12099
12158
|
}
|
|
12100
|
-
if (
|
|
12159
|
+
if (status === "Stopping...") {
|
|
12101
12160
|
const icon2 = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
|
|
12102
12161
|
console.log(`
|
|
12103
|
-
${icon2} Pentest
|
|
12162
|
+
${icon2} Pentest is being stopped.`);
|
|
12104
12163
|
break;
|
|
12105
12164
|
}
|
|
12106
|
-
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
${icon} Pentest completed: ${domain} ${dur}`);
|
|
12112
|
-
if (showFindingsSummary) {
|
|
12113
|
-
await printFindingsSummary(client, profileId, tty3);
|
|
12165
|
+
if (status === "Not Yet Started") {
|
|
12166
|
+
const icon2 = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
|
|
12167
|
+
console.log(`
|
|
12168
|
+
${icon2} Pentest was stopped.`);
|
|
12169
|
+
break;
|
|
12114
12170
|
}
|
|
12115
|
-
|
|
12116
|
-
|
|
12117
|
-
|
|
12171
|
+
if (status === "Scheduled") {
|
|
12172
|
+
const icon2 = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
|
|
12173
|
+
console.log(`
|
|
12174
|
+
${icon2} Pentest is scheduled, not currently running.`);
|
|
12175
|
+
break;
|
|
12176
|
+
}
|
|
12177
|
+
const icon = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
|
|
12118
12178
|
console.log(`
|
|
12119
|
-
|
|
12120
|
-
console.log();
|
|
12179
|
+
${icon} Unexpected status: ${status}`);
|
|
12121
12180
|
break;
|
|
12122
12181
|
}
|
|
12123
|
-
|
|
12124
|
-
|
|
12125
|
-
|
|
12126
|
-
|
|
12127
|
-
|
|
12128
|
-
console.error(`Check status: kinosec pentest get ${profileId}`);
|
|
12129
|
-
process.exit(3);
|
|
12130
|
-
}
|
|
12131
|
-
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
12182
|
+
} finally {
|
|
12183
|
+
cleanup();
|
|
12184
|
+
}
|
|
12185
|
+
if (deferredExitCode !== null) {
|
|
12186
|
+
process.exit(deferredExitCode);
|
|
12132
12187
|
}
|
|
12133
12188
|
}
|
|
12134
12189
|
async function printFindingsSummary(client, profileId, tty3) {
|
|
@@ -12508,7 +12563,7 @@ function parseTarget(input) {
|
|
|
12508
12563
|
|
|
12509
12564
|
// src/commands/pentest.ts
|
|
12510
12565
|
function registerPentestCommand(program3) {
|
|
12511
|
-
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", "full").option("-y, --yes", "Accept authorization terms without prompting").option("--no-watch", "Skip auto-watch after starting the pentest").option("--deployment <type>", "Deployment type: cloud or on-prem", "cloud").option("--include <hosts>", "Include only these subdomains (comma-separated)").option("--exclude <hosts>", "Exclude these subdomains (comma-separated)").option("--scope <action>", "Scope action: clear").addHelpText("after", `
|
|
12566
|
+
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: full, quick, htb, or defcon", "full").option("--flag <prefix>", "CTF flag prefix (defcon mode only, default HTB)").option("--context <text>", "CTF focus hint (defcon mode only)").option("-y, --yes", "Accept authorization terms without prompting").option("--no-watch", "Skip auto-watch after starting the pentest").option("--deployment <type>", "Deployment type: cloud or on-prem", "cloud").option("--include <hosts>", "Include only these subdomains (comma-separated)").option("--exclude <hosts>", "Exclude these subdomains (comma-separated)").option("--scope <action>", "Scope action: clear").addHelpText("after", `
|
|
12512
12567
|
Examples:
|
|
12513
12568
|
$ kinosec pentest -t delikado.org
|
|
12514
12569
|
$ kinosec pentest --profile abc123
|
|
@@ -12584,6 +12639,12 @@ Examples:
|
|
|
12584
12639
|
asset_id: asset.id,
|
|
12585
12640
|
scan_mode: options.mode
|
|
12586
12641
|
};
|
|
12642
|
+
if (options.mode === "defcon") {
|
|
12643
|
+
assetParams.flag_format = options.flag ?? "HTB";
|
|
12644
|
+
if (options.context) assetParams.ctf_context = options.context;
|
|
12645
|
+
} else if (options.flag || options.context) {
|
|
12646
|
+
console.error("Warning: --flag/--context apply only to --mode defcon; ignoring.");
|
|
12647
|
+
}
|
|
12587
12648
|
if (options.deployment !== "cloud") {
|
|
12588
12649
|
assetParams.deployment_type = options.deployment;
|
|
12589
12650
|
}
|
|
@@ -13732,7 +13793,7 @@ Examples:
|
|
|
13732
13793
|
|
|
13733
13794
|
// src/cli.ts
|
|
13734
13795
|
var program2 = new Command();
|
|
13735
|
-
program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.10.
|
|
13796
|
+
program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.10.3", "-v, --version").option("--token <pat>", "API token (overrides KINOSEC_TOKEN)").option("--output <format>", "Output format: text or json", "text").option("--json", "Shorthand for --output json").option("--no-color", "Disable colors");
|
|
13736
13797
|
registerAuthCommand(program2);
|
|
13737
13798
|
registerMeCommand(program2);
|
|
13738
13799
|
registerCreditsCommand(program2);
|
|
@@ -13741,7 +13802,7 @@ registerAssetCommand(program2);
|
|
|
13741
13802
|
registerFindingCommand(program2);
|
|
13742
13803
|
registerReportCommand(program2);
|
|
13743
13804
|
registerTokenCommand(program2);
|
|
13744
|
-
program2.parseAsync(process.argv).then(() => checkForUpdate("0.10.
|
|
13805
|
+
program2.parseAsync(process.argv).then(() => checkForUpdate("0.10.3")).catch(handleError);
|
|
13745
13806
|
/*! Bundled license information:
|
|
13746
13807
|
|
|
13747
13808
|
ky/distribution/index.js:
|