@kinosec/cli 0.10.1 → 0.10.2

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 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, platform } from "os";
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 && platform() !== "win32") {
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);
@@ -12019,10 +12015,9 @@ function extractPhase(entries) {
12019
12015
  return null;
12020
12016
  }
12021
12017
  function printActivityLines(entries, tty3) {
12022
- for (let i = 0; i < entries.length; i++) {
12023
- const isLastEntry = i === entries.length - 1;
12024
- const prompt = tty3 ? import_picocolors5.default.green(">") : ">";
12025
- const text = tty3 ? isLastEntry ? import_picocolors5.default.green(entries[i]) : import_picocolors5.default.dim(import_picocolors5.default.green(entries[i])) : entries[i];
12018
+ for (const entry of entries) {
12019
+ const prompt = tty3 ? import_picocolors5.default.dim(import_picocolors5.default.green(">")) : ">";
12020
+ const text = tty3 ? import_picocolors5.default.dim(import_picocolors5.default.green(entry)) : entry;
12026
12021
  console.log(`${prompt} ${text}`);
12027
12022
  }
12028
12023
  }
@@ -12049,7 +12044,7 @@ async function watchPentest(client, profileId, options) {
12049
12044
  spinner.text = import_picocolors5.default.green(currentPhase ? `[${currentPhase}] ${buzz}...` : `${buzz}...`);
12050
12045
  }
12051
12046
  if (!useJson && tty3) {
12052
- spinner = ora({ text: "Fetching pentest status...", color: "green", isEnabled: true }).start();
12047
+ spinner = ora({ text: "Fetching pentest status...", color: "green", isEnabled: true, discardStdin: false }).start();
12053
12048
  cycler = createBuzzwordCycler(SCAN_EXECUTION_PHRASES, 2500, () => {
12054
12049
  updateSpinnerText();
12055
12050
  });
@@ -12064,71 +12059,134 @@ async function watchPentest(client, profileId, options) {
12064
12059
  process.exit(0);
12065
12060
  });
12066
12061
  const showFullDetails = await resolveShowFullDetails(client);
12067
- while (true) {
12068
- const scanDetail = await client.scans.get(profileId);
12069
- const totalMs = Date.now() - loopStart;
12070
- if (useJson) {
12071
- const elapsed = Math.round(totalMs / 1e3);
12072
- process.stdout.write(
12073
- JSON.stringify({ ...scanDetail, elapsed_seconds: elapsed }) + "\n"
12074
- );
12075
- } else {
12076
- const normalized = normalizeActivityHistory(scanDetail.activity_history);
12077
- const filtered = filterActivityForTier(normalized, showFullDetails);
12078
- if (printedCount > filtered.length) printedCount = 0;
12079
- const newEntries = filtered.slice(printedCount);
12080
- if (newEntries.length > 0) {
12081
- if (spinner) spinner.stop();
12082
- printActivityLines(newEntries, tty3);
12083
- printedCount += newEntries.length;
12084
- if (spinner) spinner = ora({ color: "green", isEnabled: true }).start();
12085
- }
12086
- currentPhase = extractPhase(filtered);
12087
- updateSpinnerText();
12088
- }
12089
- const terminalStatuses = ["Completed", "Failed", "Stopped"];
12090
- if (terminalStatuses.some((s) => scanDetail.scan_status.includes(s))) {
12091
- cleanup();
12062
+ let consecutiveErrors = 0;
12063
+ const MAX_CONSECUTIVE_ERRORS = 2;
12064
+ let deferredExitCode = null;
12065
+ try {
12066
+ while (true) {
12067
+ let scanDetail;
12068
+ try {
12069
+ scanDetail = await client.scans.get(profileId);
12070
+ consecutiveErrors = 0;
12071
+ } catch (error2) {
12072
+ if (error2 instanceof ApiError) {
12073
+ if (error2.status === 401 || error2.status === 403) {
12074
+ throw error2;
12075
+ }
12076
+ if (error2.status === 404) {
12077
+ if (!useJson) {
12078
+ const icon2 = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
12079
+ console.log(`
12080
+ ${icon2} Pentest is no longer available.`);
12081
+ console.log(` Check status: kinosec pentest get ${profileId}`);
12082
+ }
12083
+ break;
12084
+ }
12085
+ }
12086
+ consecutiveErrors++;
12087
+ if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
12088
+ if (!useJson) {
12089
+ const icon2 = tty3 ? import_picocolors5.default.red(sym.fail) : sym.fail;
12090
+ console.log(`
12091
+ ${icon2} Lost connection to pentest.`);
12092
+ console.log(` Check status: kinosec pentest get ${profileId}`);
12093
+ }
12094
+ break;
12095
+ }
12096
+ if (spinner) {
12097
+ spinner.text = import_picocolors5.default.yellow("Connection lost, retrying...");
12098
+ }
12099
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
12100
+ continue;
12101
+ }
12102
+ const totalMs = Date.now() - loopStart;
12103
+ if (useJson) {
12104
+ const elapsed = Math.round(totalMs / 1e3);
12105
+ process.stdout.write(
12106
+ JSON.stringify({ ...scanDetail, elapsed_seconds: elapsed }) + "\n"
12107
+ );
12108
+ } else {
12109
+ const normalized = normalizeActivityHistory(scanDetail.activity_history);
12110
+ const filtered = filterActivityForTier(normalized, showFullDetails);
12111
+ if (printedCount > filtered.length) printedCount = 0;
12112
+ const newEntries = filtered.slice(printedCount);
12113
+ if (newEntries.length > 0) {
12114
+ if (spinner) spinner.stop();
12115
+ printActivityLines(newEntries, tty3);
12116
+ printedCount += newEntries.length;
12117
+ if (spinner) spinner = ora({ color: "green", isEnabled: true, discardStdin: false }).start();
12118
+ }
12119
+ currentPhase = extractPhase(filtered);
12120
+ updateSpinnerText();
12121
+ }
12122
+ const status = scanDetail.scan_status;
12123
+ if (status === "Running...") {
12124
+ if (Date.now() - loopStart > timeoutMs) {
12125
+ const secs = Math.round(timeoutMs / 1e3);
12126
+ console.error(`
12127
+ Timeout: pentest still running after ${secs}s.`);
12128
+ console.error(`Check status: kinosec pentest get ${profileId}`);
12129
+ deferredExitCode = 3;
12130
+ break;
12131
+ }
12132
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
12133
+ continue;
12134
+ }
12092
12135
  if (useJson) break;
12093
- if (scanDetail.scan_status.includes("Failed")) {
12136
+ if (status === "Completed") {
12137
+ const totalDur = formatDuration(Date.now() - loopStart);
12138
+ const domain = tty3 ? import_picocolors5.default.bold(scanDetail.domain_or_ip) : scanDetail.domain_or_ip;
12139
+ const icon2 = tty3 ? import_picocolors5.default.green(sym.success) : sym.success;
12140
+ const dur = tty3 ? import_picocolors5.default.dim(totalDur) : totalDur;
12141
+ console.log(`
12142
+ ${icon2} Pentest completed: ${domain} ${dur}`);
12143
+ if (showFindingsSummary) {
12144
+ await printFindingsSummary(client, profileId, tty3);
12145
+ }
12146
+ const cmd = `kinosec finding list ${profileId}`;
12147
+ const viewLabel = tty3 ? import_picocolors5.default.dim("View details:") : "View details:";
12148
+ const viewCmd = tty3 ? import_picocolors5.default.cyan(cmd) : cmd;
12149
+ console.log(`
12150
+ ${viewLabel} ${viewCmd}`);
12151
+ console.log();
12152
+ break;
12153
+ }
12154
+ if (status === "Failed") {
12094
12155
  const icon2 = tty3 ? import_picocolors5.default.red(sym.fail) : sym.fail;
12095
12156
  const reason = scanDetail.failure_reason || "unknown reason";
12096
12157
  console.log(`
12097
12158
  ${icon2} Pentest failed: ${reason}`);
12098
- process.exit(1);
12159
+ deferredExitCode = 1;
12160
+ break;
12099
12161
  }
12100
- if (scanDetail.scan_status.includes("Stopped")) {
12162
+ if (status === "Stopping...") {
12101
12163
  const icon2 = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
12102
12164
  console.log(`
12103
- ${icon2} Pentest was cancelled.`);
12165
+ ${icon2} Pentest is being stopped.`);
12104
12166
  break;
12105
12167
  }
12106
- const totalDur = formatDuration(Date.now() - loopStart);
12107
- const domain = tty3 ? import_picocolors5.default.bold(scanDetail.domain_or_ip) : scanDetail.domain_or_ip;
12108
- const icon = tty3 ? import_picocolors5.default.green(sym.success) : sym.success;
12109
- const dur = tty3 ? import_picocolors5.default.dim(totalDur) : totalDur;
12110
- console.log(`
12111
- ${icon} Pentest completed: ${domain} ${dur}`);
12112
- if (showFindingsSummary) {
12113
- await printFindingsSummary(client, profileId, tty3);
12168
+ if (status === "Not Yet Started") {
12169
+ const icon2 = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
12170
+ console.log(`
12171
+ ${icon2} Pentest was stopped.`);
12172
+ break;
12114
12173
  }
12115
- const cmd = `kinosec finding list ${profileId}`;
12116
- const viewLabel = tty3 ? import_picocolors5.default.dim("View details:") : "View details:";
12117
- const viewCmd = tty3 ? import_picocolors5.default.cyan(cmd) : cmd;
12174
+ if (status === "Scheduled") {
12175
+ const icon2 = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
12176
+ console.log(`
12177
+ ${icon2} Pentest is scheduled, not currently running.`);
12178
+ break;
12179
+ }
12180
+ const icon = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
12118
12181
  console.log(`
12119
- ${viewLabel} ${viewCmd}`);
12120
- console.log();
12182
+ ${icon} Unexpected status: ${status}`);
12121
12183
  break;
12122
12184
  }
12123
- if (Date.now() - loopStart > timeoutMs) {
12124
- cleanup();
12125
- const secs = Math.round(timeoutMs / 1e3);
12126
- console.error(`
12127
- Timeout: pentest still running after ${secs}s.`);
12128
- console.error(`Check status: kinosec pentest get ${profileId}`);
12129
- process.exit(3);
12130
- }
12131
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
12185
+ } finally {
12186
+ cleanup();
12187
+ }
12188
+ if (deferredExitCode !== null) {
12189
+ process.exit(deferredExitCode);
12132
12190
  }
12133
12191
  }
12134
12192
  async function printFindingsSummary(client, profileId, tty3) {
@@ -13732,7 +13790,7 @@ Examples:
13732
13790
 
13733
13791
  // src/cli.ts
13734
13792
  var program2 = new Command();
13735
- program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.10.1", "-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");
13793
+ program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.10.2", "-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
13794
  registerAuthCommand(program2);
13737
13795
  registerMeCommand(program2);
13738
13796
  registerCreditsCommand(program2);
@@ -13741,7 +13799,7 @@ registerAssetCommand(program2);
13741
13799
  registerFindingCommand(program2);
13742
13800
  registerReportCommand(program2);
13743
13801
  registerTokenCommand(program2);
13744
- program2.parseAsync(process.argv).then(() => checkForUpdate("0.10.1")).catch(handleError);
13802
+ program2.parseAsync(process.argv).then(() => checkForUpdate("0.10.2")).catch(handleError);
13745
13803
  /*! Bundled license information:
13746
13804
 
13747
13805
  ky/distribution/index.js: