@kinosec/cli 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -7851,6 +7851,9 @@ var AssetsResource = class {
7851
7851
  async verify(id) {
7852
7852
  return this.api.post(`v1/assets/${id}/verify`, { json: {} }).json();
7853
7853
  }
7854
+ async lookup(domain) {
7855
+ return this.api.get("v1/assets/lookup", { searchParams: { domain } }).json();
7856
+ }
7854
7857
  };
7855
7858
  var ScansResource = class {
7856
7859
  constructor(api) {
@@ -8199,9 +8202,6 @@ Update: npm update -g @kinosec/cli`
8199
8202
  }
8200
8203
  }
8201
8204
 
8202
- // src/commands/auth.ts
8203
- import { createInterface } from "readline";
8204
-
8205
8205
  // src/lib/auth.ts
8206
8206
  var DEFAULT_API_URL = "https://api.kinosec.ai/v1";
8207
8207
  function resolveToken(options) {
@@ -8225,12 +8225,40 @@ function maskToken(token) {
8225
8225
  return token.slice(0, 16) + "\u2026" + token.slice(-4);
8226
8226
  }
8227
8227
  async function promptForToken() {
8228
- const rl = createInterface({ input: process.stdin, output: process.stderr });
8229
- return new Promise((resolve) => {
8230
- rl.question("Paste your API token: ", (answer) => {
8231
- rl.close();
8232
- resolve(answer.trim());
8233
- });
8228
+ process.stderr.write("API token: ");
8229
+ return new Promise((resolve, reject) => {
8230
+ const chars = [];
8231
+ process.stdin.setRawMode(true);
8232
+ process.stdin.resume();
8233
+ process.stdin.setEncoding("utf-8");
8234
+ const onData = (chunk) => {
8235
+ for (const ch of chunk) {
8236
+ if (ch === "\r" || ch === "\n") {
8237
+ process.stdin.setRawMode(false);
8238
+ process.stdin.pause();
8239
+ process.stdin.removeListener("data", onData);
8240
+ process.stderr.write("\n");
8241
+ resolve(chars.join(""));
8242
+ return;
8243
+ } else if (ch === "") {
8244
+ process.stdin.setRawMode(false);
8245
+ process.stdin.pause();
8246
+ process.stdin.removeListener("data", onData);
8247
+ process.stderr.write("\n");
8248
+ reject(new Error("Cancelled"));
8249
+ return;
8250
+ } else if (ch === "\x7F" || ch === "\b") {
8251
+ if (chars.length > 0) {
8252
+ chars.pop();
8253
+ process.stderr.write("\b \b");
8254
+ }
8255
+ } else if (ch >= " ") {
8256
+ chars.push(ch);
8257
+ process.stderr.write("*");
8258
+ }
8259
+ }
8260
+ };
8261
+ process.stdin.on("data", onData);
8234
8262
  });
8235
8263
  }
8236
8264
  function registerAuthCommand(program3) {
@@ -8248,7 +8276,7 @@ Examples:
8248
8276
  );
8249
8277
  }
8250
8278
  if (!token) {
8251
- if (isTTY()) {
8279
+ if (isTTY() && process.stdin.isTTY) {
8252
8280
  token = await promptForToken();
8253
8281
  } else {
8254
8282
  const chunks = [];
@@ -9817,21 +9845,19 @@ function ora(options) {
9817
9845
  return new Ora(options);
9818
9846
  }
9819
9847
 
9820
- // src/commands/scan.ts
9821
- var import_picocolors4 = __toESM(require_picocolors(), 1);
9822
-
9823
- // src/commands/schedule.ts
9824
- var import_picocolors3 = __toESM(require_picocolors(), 1);
9848
+ // src/commands/pentest.ts
9849
+ var import_picocolors5 = __toESM(require_picocolors(), 1);
9825
9850
 
9826
9851
  // src/lib/confirm.ts
9827
- import { createInterface as createInterface2 } from "readline";
9852
+ var import_picocolors3 = __toESM(require_picocolors(), 1);
9853
+ import { createInterface } from "readline";
9828
9854
  async function confirmDestructive(message, yes) {
9829
9855
  if (yes) return;
9830
9856
  if (!isTTY()) {
9831
9857
  console.error("Use --yes to confirm destructive operations in non-interactive mode.");
9832
9858
  process.exit(2);
9833
9859
  }
9834
- const rl = createInterface2({ input: process.stdin, output: process.stderr });
9860
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
9835
9861
  const answer = await new Promise((resolve) => {
9836
9862
  rl.question(`${message} (y/N) `, resolve);
9837
9863
  });
@@ -9841,8 +9867,45 @@ async function confirmDestructive(message, yes) {
9841
9867
  process.exit(0);
9842
9868
  }
9843
9869
  }
9870
+ async function confirmAuthorization(target, yes) {
9871
+ if (yes) return;
9872
+ if (!isTTY()) {
9873
+ console.error("Use --yes to accept authorization terms in non-interactive mode.");
9874
+ process.exit(2);
9875
+ }
9876
+ const warning = import_picocolors3.default.yellow("\u26A0");
9877
+ console.error(`
9878
+ ${warning} ${import_picocolors3.default.bold("Authorization Required")}
9879
+
9880
+ Penetration testing without proper authorization is illegal
9881
+ and may result in criminal prosecution.
9882
+
9883
+ ${import_picocolors3.default.bold("Target:")} ${target}
9884
+
9885
+ By proceeding, you confirm that:
9886
+ ${import_picocolors3.default.dim("\u2022")} You own or have explicit written authorization to test this target
9887
+ ${import_picocolors3.default.dim("\u2022")} You will comply with all applicable laws and regulations
9888
+ ${import_picocolors3.default.dim("\u2022")} You will not use the results for illegal or malicious purposes
9889
+ ${import_picocolors3.default.dim("\u2022")} You accept full legal responsibility for this pentest
9890
+
9891
+ ${import_picocolors3.default.dim("Unauthorized access to computer systems is a criminal offense under")}
9892
+ ${import_picocolors3.default.dim("laws such as the CFAA (US), Computer Misuse Act (UK), and similar")}
9893
+ ${import_picocolors3.default.dim("legislation worldwide. KinoSec bears no liability for unauthorized")}
9894
+ ${import_picocolors3.default.dim("use of its services.")}
9895
+ `);
9896
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
9897
+ const answer = await new Promise((resolve) => {
9898
+ rl.question("Agree and continue? (y/N) ", resolve);
9899
+ });
9900
+ rl.close();
9901
+ if (answer.toLowerCase() !== "y") {
9902
+ console.log("Cancelled.");
9903
+ process.exit(0);
9904
+ }
9905
+ }
9844
9906
 
9845
9907
  // src/commands/schedule.ts
9908
+ var import_picocolors4 = __toESM(require_picocolors(), 1);
9846
9909
  var VALID_PATTERNS = ["once", "daily", "weekly", "biweekly", "monthly"];
9847
9910
  var VALID_MODES = ["quick", "full"];
9848
9911
  var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
@@ -9868,12 +9931,12 @@ function formatPattern(s) {
9868
9931
  return s.recurrence_pattern;
9869
9932
  }
9870
9933
  }
9871
- function registerScheduleCommand(scanCmd) {
9872
- const schedule = scanCmd.command("schedule").description("Manage scheduled scans (continuous protection)");
9873
- schedule.command("list").description("List scheduled scans").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").option("--status <status>", "Filter: active, paused, or all", "all").addHelpText("after", `
9934
+ function registerScheduleCommand(pentestCmd) {
9935
+ const schedule = pentestCmd.command("schedule").description("Manage scheduled pentests");
9936
+ 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", `
9874
9937
  Examples:
9875
- $ kinosec scan schedule list
9876
- $ kinosec scan schedule list --status active --json`).action(async (options) => {
9938
+ $ kinosec pentest schedule list
9939
+ $ kinosec pentest schedule list --status active --json`).action(async (options) => {
9877
9940
  try {
9878
9941
  const opts = schedule.parent.parent.opts();
9879
9942
  const { token, apiUrl } = resolveToken(opts);
@@ -9888,7 +9951,7 @@ Examples:
9888
9951
  return;
9889
9952
  }
9890
9953
  if (result.items.length === 0) {
9891
- console.log("No scheduled scans found.");
9954
+ console.log("No scheduled pentests found.");
9892
9955
  return;
9893
9956
  }
9894
9957
  printTable(
@@ -9898,12 +9961,12 @@ Examples:
9898
9961
  truncate(s.domain_or_ip, 25),
9899
9962
  s.scan_mode,
9900
9963
  formatPattern(s),
9901
- s.is_active ? import_picocolors3.default.green("active") : import_picocolors3.default.yellow("paused"),
9964
+ s.is_active ? import_picocolors4.default.green("active") : import_picocolors4.default.yellow("paused"),
9902
9965
  s.next_scheduled_run ? new Date(s.next_scheduled_run).toLocaleString() : "\u2014"
9903
9966
  ])
9904
9967
  );
9905
9968
  if (result.next_cursor) {
9906
- console.log(import_picocolors3.default.dim(`
9969
+ console.log(import_picocolors4.default.dim(`
9907
9970
  More results available. Use --cursor ${result.next_cursor}`));
9908
9971
  }
9909
9972
  } catch (error) {
@@ -9912,8 +9975,8 @@ More results available. Use --cursor ${result.next_cursor}`));
9912
9975
  });
9913
9976
  schedule.command("get").description("Get schedule details").argument("<id>", "Schedule ID").addHelpText("after", `
9914
9977
  Examples:
9915
- $ kinosec scan schedule get abc123
9916
- $ kinosec scan schedule get abc123 --json`).action(async (id) => {
9978
+ $ kinosec pentest schedule get abc123
9979
+ $ kinosec pentest schedule get abc123 --json`).action(async (id) => {
9917
9980
  try {
9918
9981
  const opts = schedule.parent.parent.opts();
9919
9982
  const { token, apiUrl } = resolveToken(opts);
@@ -9942,11 +10005,11 @@ Examples:
9942
10005
  handleError(error);
9943
10006
  }
9944
10007
  });
9945
- schedule.command("create").description("Create a scheduled scan").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", `
10008
+ 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", `
9946
10009
  Examples:
9947
- $ kinosec scan schedule create --asset abc123 --pattern weekly --day-of-week 1 --time 09:00
9948
- $ kinosec scan schedule create --asset abc123 --pattern daily --mode quick --time 14:00
9949
- $ kinosec scan schedule create --asset abc123 --pattern monthly --day-of-month 15 --time 03:00 --timezone UTC`).action(async (options) => {
10010
+ $ kinosec pentest schedule create --asset abc123 --pattern weekly --day-of-week 1 --time 09:00
10011
+ $ kinosec pentest schedule create --asset abc123 --pattern daily --mode quick --time 14:00
10012
+ $ kinosec pentest schedule create --asset abc123 --pattern monthly --day-of-month 15 --time 03:00 --timezone UTC`).action(async (options) => {
9950
10013
  if (!VALID_PATTERNS.includes(options.pattern)) {
9951
10014
  console.error(`Invalid pattern "${options.pattern}". Must be: ${VALID_PATTERNS.join(", ")}`);
9952
10015
  process.exit(2);
@@ -9969,7 +10032,7 @@ Examples:
9969
10032
  const client = new KinoSecClient({ apiUrl, token });
9970
10033
  const params = {
9971
10034
  asset_id: options.domain_or_ip,
9972
- name: options.name ?? `${options.pattern} ${options.mode} scan`,
10035
+ name: options.name ?? `${options.pattern} ${options.mode} pentest`,
9973
10036
  scan_mode: options.mode,
9974
10037
  recurrence_pattern: options.pattern,
9975
10038
  time_of_day: options.time,
@@ -9987,15 +10050,15 @@ Examples:
9987
10050
  console.log(` Schedule: ${formatPattern(s)}`);
9988
10051
  console.log(` Next run: ${s.next_scheduled_run ?? "pending"}`);
9989
10052
  console.log(`
9990
- Manage: kinosec scan schedule get ${s.id}`);
10053
+ Manage: kinosec pentest schedule get ${s.id}`);
9991
10054
  } catch (error) {
9992
10055
  handleError(error);
9993
10056
  }
9994
10057
  });
9995
10058
  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", `
9996
10059
  Examples:
9997
- $ kinosec scan schedule update abc123 --pattern daily --time 14:00
9998
- $ kinosec scan schedule update abc123 --mode quick`).action(async (id, options) => {
10060
+ $ kinosec pentest schedule update abc123 --pattern daily --time 14:00
10061
+ $ kinosec pentest schedule update abc123 --mode quick`).action(async (id, options) => {
9999
10062
  const params = {};
10000
10063
  if (options.pattern) params.recurrence_pattern = options.pattern;
10001
10064
  if (options.name) params.name = options.name;
@@ -10026,8 +10089,8 @@ Examples:
10026
10089
  });
10027
10090
  schedule.command("delete").description("Delete a schedule").argument("<id>", "Schedule ID").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
10028
10091
  Examples:
10029
- $ kinosec scan schedule delete abc123
10030
- $ kinosec scan schedule delete abc123 --yes`).action(async (id, options) => {
10092
+ $ kinosec pentest schedule delete abc123
10093
+ $ kinosec pentest schedule delete abc123 --yes`).action(async (id, options) => {
10031
10094
  try {
10032
10095
  await confirmDestructive(`Delete schedule ${id}?`, Boolean(options.yes));
10033
10096
  const opts = schedule.parent.parent.opts();
@@ -10043,9 +10106,9 @@ Examples:
10043
10106
  handleError(error);
10044
10107
  }
10045
10108
  });
10046
- schedule.command("pause").description("Pause a scheduled scan").argument("<id>", "Schedule ID").addHelpText("after", `
10109
+ schedule.command("pause").description("Pause a scheduled pentest").argument("<id>", "Schedule ID").addHelpText("after", `
10047
10110
  Examples:
10048
- $ kinosec scan schedule pause abc123`).action(async (id) => {
10111
+ $ kinosec pentest schedule pause abc123`).action(async (id) => {
10049
10112
  try {
10050
10113
  const opts = schedule.parent.parent.opts();
10051
10114
  const { token, apiUrl } = resolveToken(opts);
@@ -10062,7 +10125,7 @@ Examples:
10062
10125
  });
10063
10126
  schedule.command("resume").description("Resume a paused schedule").argument("<id>", "Schedule ID").addHelpText("after", `
10064
10127
  Examples:
10065
- $ kinosec scan schedule resume abc123`).action(async (id) => {
10128
+ $ kinosec pentest schedule resume abc123`).action(async (id) => {
10066
10129
  try {
10067
10130
  const opts = schedule.parent.parent.opts();
10068
10131
  const { token, apiUrl } = resolveToken(opts);
@@ -10082,14 +10145,79 @@ Examples:
10082
10145
  });
10083
10146
  }
10084
10147
 
10085
- // src/commands/scan.ts
10086
- function registerScanCommand(program3) {
10087
- const scan = program3.command("scan").description("Manage security scans");
10088
- registerScheduleCommand(scan);
10089
- scan.command("list").description("List scans").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
10148
+ // src/commands/pentest.ts
10149
+ function stripProtocol(input) {
10150
+ const match = input.match(/^https?:\/\/(.+)/i);
10151
+ if (match) return { domain: match[1].replace(/\/+$/, ""), warned: true };
10152
+ return { domain: input, warned: false };
10153
+ }
10154
+ function isValidDomain(value) {
10155
+ return value.includes(".") && !/\s/.test(value);
10156
+ }
10157
+ function registerPentestCommand(program3) {
10158
+ 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", `
10090
10159
  Examples:
10091
- $ kinosec scan list
10092
- $ kinosec scan list --limit 5 --json`).action(async (options) => {
10160
+ $ kinosec pentest -t delikado.org
10161
+ $ kinosec pentest --target delikado.org --mode quick
10162
+ $ kinosec pentest --profile abc123`).action(async (options) => {
10163
+ if (!options.target && !options.profile) {
10164
+ console.error("Provide --target <domain> or --profile <id>.");
10165
+ process.exit(2);
10166
+ }
10167
+ try {
10168
+ const opts = program3.opts();
10169
+ const { token, apiUrl } = resolveToken(opts);
10170
+ const client = new KinoSecClient({ apiUrl, token });
10171
+ let targetDisplay;
10172
+ let params;
10173
+ if (options.profile) {
10174
+ const detail = await client.scans.get(options.profile);
10175
+ targetDisplay = detail.domain_or_ip;
10176
+ params = { scan_profile_id: options.profile };
10177
+ } else {
10178
+ const { domain, warned } = stripProtocol(options.target);
10179
+ if (warned) {
10180
+ console.error(`Warning: protocol stripped. Using "${domain}".`);
10181
+ }
10182
+ if (!isValidDomain(domain)) {
10183
+ console.error(`Invalid domain format: "${domain}"`);
10184
+ console.error("Provide a domain like delikado.org or juice.delikado.org.");
10185
+ process.exit(2);
10186
+ }
10187
+ let asset;
10188
+ try {
10189
+ asset = await client.assets.lookup(domain);
10190
+ } catch (err) {
10191
+ if (err instanceof ApiError && err.status === 404) {
10192
+ console.error(`No asset found for '${domain}'. Run kinosec asset list to see your assets.`);
10193
+ process.exit(1);
10194
+ }
10195
+ throw err;
10196
+ }
10197
+ targetDisplay = domain;
10198
+ params = { asset_id: asset.id, scan_mode: options.mode };
10199
+ }
10200
+ await confirmAuthorization(targetDisplay, Boolean(options.yes));
10201
+ const result = await client.scans.create(params);
10202
+ if (opts.json || opts.output === "json") {
10203
+ printJson(result);
10204
+ return;
10205
+ }
10206
+ printSuccess(`Pentest started: ${result.domain_or_ip}`);
10207
+ console.log(` Profile ID: ${result.scan_profile_id}`);
10208
+ console.log(` Mode: ${result.scan_mode}`);
10209
+ console.log(` Status: ${result.scan_status}`);
10210
+ console.log(`
10211
+ Watch progress: kinosec pentest watch ${result.scan_profile_id}`);
10212
+ } catch (error) {
10213
+ handleError(error);
10214
+ }
10215
+ });
10216
+ registerScheduleCommand(pentest);
10217
+ pentest.command("list").description("List pentests").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
10218
+ Examples:
10219
+ $ kinosec pentest list
10220
+ $ kinosec pentest list --limit 5 --json`).action(async (options) => {
10093
10221
  try {
10094
10222
  const opts = program3.opts();
10095
10223
  const { token, apiUrl } = resolveToken(opts);
@@ -10103,7 +10231,7 @@ Examples:
10103
10231
  return;
10104
10232
  }
10105
10233
  if (result.items.length === 0) {
10106
- console.log("No scans found.");
10234
+ console.log("No pentests found.");
10107
10235
  return;
10108
10236
  }
10109
10237
  printTable(
@@ -10117,17 +10245,17 @@ Examples:
10117
10245
  ])
10118
10246
  );
10119
10247
  if (result.next_cursor) {
10120
- console.log(import_picocolors4.default.dim(`
10248
+ console.log(import_picocolors5.default.dim(`
10121
10249
  More results available. Use --cursor ${result.next_cursor}`));
10122
10250
  }
10123
10251
  } catch (error) {
10124
10252
  handleError(error);
10125
10253
  }
10126
10254
  });
10127
- scan.command("get").description("Get scan details").argument("<id>", "Scan profile ID").addHelpText("after", `
10255
+ pentest.command("get").description("Get pentest details").argument("<profile-id>", "Pentest profile ID").showHelpAfterError().addHelpText("after", `
10128
10256
  Examples:
10129
- $ kinosec scan get abc123
10130
- $ kinosec scan get abc123 --json`).action(async (id) => {
10257
+ $ kinosec pentest get abc123
10258
+ $ kinosec pentest get abc123 --json`).action(async (id) => {
10131
10259
  try {
10132
10260
  const opts = program3.opts();
10133
10261
  const { token, apiUrl } = resolveToken(opts);
@@ -10154,45 +10282,9 @@ Examples:
10154
10282
  handleError(error);
10155
10283
  }
10156
10284
  });
10157
- scan.command("create").description("Start a new scan").option("--asset <id>", "Asset ID to scan").option("--profile <id>", "Scan profile ID (alternative to --asset)").option("--mode <mode>", "Scan mode: quick or full", "quick").addHelpText("after", `
10285
+ pentest.command("cancel").description("Cancel a running pentest").argument("<profile-id>", "Pentest profile ID").showHelpAfterError().addHelpText("after", `
10158
10286
  Examples:
10159
- $ kinosec scan create --asset abc123 --mode quick
10160
- $ kinosec scan create --profile def456`).action(async (options) => {
10161
- if (!options.asset && !options.profile) {
10162
- console.error("Provide --asset <id> or --profile <id>.");
10163
- process.exit(2);
10164
- }
10165
- const uuidRe = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10166
- const idValue = options.asset ?? options.profile;
10167
- if (!uuidRe.test(idValue)) {
10168
- console.error(`Invalid ID format: "${idValue}"`);
10169
- console.error("IDs are UUIDs (e.g. 550e8400-e29b-41d4-a716-446655440000).");
10170
- console.error("Run kinosec asset list to find your asset ID.");
10171
- process.exit(2);
10172
- }
10173
- try {
10174
- const opts = program3.opts();
10175
- const { token, apiUrl } = resolveToken(opts);
10176
- const client = new KinoSecClient({ apiUrl, token });
10177
- const params = options.profile ? { scan_profile_id: options.profile } : { asset_id: options.asset, scan_mode: options.mode };
10178
- const result = await client.scans.create(params);
10179
- if (opts.json || opts.output === "json") {
10180
- printJson(result);
10181
- return;
10182
- }
10183
- printSuccess(`Scan started: ${result.domain_or_ip}`);
10184
- console.log(` Profile ID: ${result.scan_profile_id}`);
10185
- console.log(` Mode: ${result.scan_mode}`);
10186
- console.log(` Status: ${result.scan_status}`);
10187
- console.log(`
10188
- Watch progress: kinosec scan watch ${result.scan_profile_id}`);
10189
- } catch (error) {
10190
- handleError(error);
10191
- }
10192
- });
10193
- scan.command("cancel").description("Cancel a running scan").argument("<id>", "Scan profile ID").addHelpText("after", `
10194
- Examples:
10195
- $ kinosec scan cancel abc123`).action(async (id) => {
10287
+ $ kinosec pentest cancel abc123`).action(async (id) => {
10196
10288
  try {
10197
10289
  const opts = program3.opts();
10198
10290
  const { token, apiUrl } = resolveToken(opts);
@@ -10202,16 +10294,16 @@ Examples:
10202
10294
  printJson(result);
10203
10295
  return;
10204
10296
  }
10205
- printSuccess(`Scan cancel requested: ${result.scan_status}`);
10297
+ printSuccess(`Pentest cancel requested: ${result.scan_status}`);
10206
10298
  } catch (error) {
10207
10299
  handleError(error);
10208
10300
  }
10209
10301
  });
10210
- scan.command("watch").description("Watch scan progress in real-time").argument("<id>", "Scan profile ID").option("--timeout <seconds>", "Max time to watch", "1800").option("--interval <seconds>", "Poll interval", "5").addHelpText("after", `
10302
+ pentest.command("watch").description("Watch pentest progress in real-time").argument("<profile-id>", "Pentest profile ID").showHelpAfterError().option("--timeout <seconds>", "Max time to watch", "1800").option("--interval <seconds>", "Poll interval", "5").addHelpText("after", `
10211
10303
  Examples:
10212
- $ kinosec scan watch abc123
10213
- $ kinosec scan watch abc123 --timeout 600
10214
- $ kinosec scan watch abc123 --json`).action(async (id, options) => {
10304
+ $ kinosec pentest watch abc123
10305
+ $ kinosec pentest watch abc123 --timeout 600
10306
+ $ kinosec pentest watch abc123 --json`).action(async (id, options) => {
10215
10307
  try {
10216
10308
  const opts = program3.opts();
10217
10309
  const { token, apiUrl } = resolveToken(opts);
@@ -10223,14 +10315,14 @@ Examples:
10223
10315
  let spinner = null;
10224
10316
  let lastActivity = "";
10225
10317
  if (!useJson && isTTY()) {
10226
- spinner = ora("Fetching scan status...").start();
10318
+ spinner = ora("Fetching pentest status...").start();
10227
10319
  }
10228
10320
  const cleanup = () => {
10229
10321
  if (spinner) spinner.stop();
10230
10322
  };
10231
10323
  process.once("SIGINT", () => {
10232
10324
  cleanup();
10233
- console.log("\nWatch cancelled. Scan is still running.");
10325
+ console.log("\nWatch cancelled. Pentest is still running.");
10234
10326
  process.exit(0);
10235
10327
  });
10236
10328
  while (true) {
@@ -10241,7 +10333,7 @@ Examples:
10241
10333
  process.stdout.write(JSON.stringify({ ...scanDetail, elapsed_seconds: elapsed }) + "\n");
10242
10334
  } else if (spinner) {
10243
10335
  if (activity && activity !== lastActivity && lastActivity) {
10244
- spinner.stopAndPersist({ symbol: import_picocolors4.default.green("\u2713"), text: lastActivity });
10336
+ spinner.stopAndPersist({ symbol: import_picocolors5.default.green("\u2713"), text: lastActivity });
10245
10337
  spinner = ora().start();
10246
10338
  }
10247
10339
  lastActivity = activity || lastActivity;
@@ -10255,24 +10347,24 @@ Examples:
10255
10347
  break;
10256
10348
  }
10257
10349
  if (scanDetail.scan_status.includes("Failed")) {
10258
- console.log(import_picocolors4.default.red(`
10259
- Scan failed: ${scanDetail.failure_reason || "unknown reason"}`));
10350
+ console.log(import_picocolors5.default.red(`
10351
+ Pentest failed: ${scanDetail.failure_reason || "unknown reason"}`));
10260
10352
  process.exit(1);
10261
10353
  }
10262
10354
  if (scanDetail.scan_status.includes("Stopped")) {
10263
- console.log(import_picocolors4.default.yellow("\nScan was cancelled."));
10355
+ console.log(import_picocolors5.default.yellow("\nPentest was cancelled."));
10264
10356
  break;
10265
10357
  }
10266
- console.log(import_picocolors4.default.green(`
10267
- Scan completed: ${scanDetail.domain_or_ip}`));
10268
- console.log(` View findings: kinosec finding list --scan ${id}`);
10358
+ console.log(import_picocolors5.default.green(`
10359
+ Pentest completed: ${scanDetail.domain_or_ip}`));
10360
+ console.log(` View findings: kinosec finding list ${id}`);
10269
10361
  break;
10270
10362
  }
10271
10363
  if (Date.now() - startTime > timeoutMs) {
10272
10364
  cleanup();
10273
10365
  console.error(`
10274
- Timeout: scan still running after ${options.timeout}s.`);
10275
- console.error(`Check status: kinosec scan get ${id}`);
10366
+ Timeout: pentest still running after ${options.timeout}s.`);
10367
+ console.error(`Check status: kinosec pentest get ${id}`);
10276
10368
  process.exit(3);
10277
10369
  }
10278
10370
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
@@ -10284,7 +10376,7 @@ Timeout: scan still running after ${options.timeout}s.`);
10284
10376
  }
10285
10377
 
10286
10378
  // src/commands/asset.ts
10287
- var import_picocolors5 = __toESM(require_picocolors(), 1);
10379
+ var import_picocolors6 = __toESM(require_picocolors(), 1);
10288
10380
  function registerAssetCommand(program3) {
10289
10381
  const asset = program3.command("asset").description("Manage assets");
10290
10382
  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", `
@@ -10323,7 +10415,7 @@ Examples:
10323
10415
  ])
10324
10416
  );
10325
10417
  if (result.next_cursor) {
10326
- console.log(import_picocolors5.default.dim(`
10418
+ console.log(import_picocolors6.default.dim(`
10327
10419
  More results available. Use --cursor ${result.next_cursor}`));
10328
10420
  }
10329
10421
  } catch (error) {
@@ -10434,31 +10526,31 @@ Examples:
10434
10526
  }
10435
10527
 
10436
10528
  // src/commands/finding.ts
10437
- var import_picocolors6 = __toESM(require_picocolors(), 1);
10529
+ var import_picocolors7 = __toESM(require_picocolors(), 1);
10438
10530
  function severityColor(level) {
10439
10531
  if (!isTTY()) return level;
10440
10532
  switch (level.toLowerCase()) {
10441
10533
  case "critical":
10442
- return import_picocolors6.default.red(import_picocolors6.default.bold(level));
10534
+ return import_picocolors7.default.red(import_picocolors7.default.bold(level));
10443
10535
  case "high":
10444
- return import_picocolors6.default.red(level);
10536
+ return import_picocolors7.default.red(level);
10445
10537
  case "medium":
10446
- return import_picocolors6.default.yellow(level);
10538
+ return import_picocolors7.default.yellow(level);
10447
10539
  case "low":
10448
- return import_picocolors6.default.blue(level);
10540
+ return import_picocolors7.default.blue(level);
10449
10541
  case "info":
10450
- return import_picocolors6.default.dim(level);
10542
+ return import_picocolors7.default.dim(level);
10451
10543
  default:
10452
10544
  return level;
10453
10545
  }
10454
10546
  }
10455
- var VALID_STATUSES = ["open", "fixed", "false_positive", "investigating", "wont_fix"];
10547
+ var VALID_STATUSES = ["open", "fixed", "false_positive", "accepted_risk", "resolved"];
10456
10548
  function registerFindingCommand(program3) {
10457
10549
  const finding = program3.command("finding").description("Manage findings");
10458
- finding.command("list").description("List findings").option("--scan <id>", "Filter by scan profile ID").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
10550
+ finding.command("list").description("List findings").argument("[profile-id]", "Pentest profile ID").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
10459
10551
  Examples:
10460
- $ kinosec finding list --scan abc123
10461
- $ kinosec finding list --limit 50 --json`).action(async (options) => {
10552
+ $ kinosec finding list abc123
10553
+ $ kinosec finding list --limit 50 --json`).action(async (profileId, options) => {
10462
10554
  try {
10463
10555
  const opts = program3.opts();
10464
10556
  const { token, apiUrl } = resolveToken(opts);
@@ -10466,7 +10558,7 @@ Examples:
10466
10558
  const result = await client.findings.list({
10467
10559
  limit: parseInt(options.limit, 10),
10468
10560
  cursor: options.cursor,
10469
- scan: options.scan
10561
+ scan: profileId
10470
10562
  });
10471
10563
  if (opts.json || opts.output === "json") {
10472
10564
  printJson(result);
@@ -10477,17 +10569,16 @@ Examples:
10477
10569
  return;
10478
10570
  }
10479
10571
  printTable(
10480
- ["ID", "Name", "Severity", "Status", "Phase"],
10572
+ ["ID", "Name", "Severity", "Status"],
10481
10573
  result.items.map((f) => [
10482
10574
  f.id,
10483
10575
  truncate(f.finding_name, 40),
10484
10576
  severityColor(f.impact_level),
10485
- f.status,
10486
- f.scan_phase
10577
+ f.status
10487
10578
  ])
10488
10579
  );
10489
10580
  if (result.next_cursor) {
10490
- console.log(import_picocolors6.default.dim(`
10581
+ console.log(import_picocolors7.default.dim(`
10491
10582
  More results available. Use --cursor ${result.next_cursor}`));
10492
10583
  }
10493
10584
  } catch (error) {
@@ -10515,7 +10606,6 @@ Examples:
10515
10606
  ["Severity", severityColor(detail.impact_level)],
10516
10607
  ["Status", detail.status],
10517
10608
  ["Verification", detail.verification_status],
10518
- ["Phase", detail.scan_phase],
10519
10609
  ["Description", truncate(detail.finding_description, 60)],
10520
10610
  ["Solution", truncate(detail.finding_solution, 60)],
10521
10611
  ["Created", detail.created_at]
@@ -10525,7 +10615,7 @@ Examples:
10525
10615
  handleError(error);
10526
10616
  }
10527
10617
  });
10528
- finding.command("update").description("Update finding status").argument("<id>", "Finding ID").requiredOption("--status <status>", "New status: open, fixed, false_positive, investigating, wont_fix").addHelpText("after", `
10618
+ finding.command("update").description("Update finding status").argument("<id>", "Finding ID").requiredOption("--status <status>", "New status: open, fixed, false_positive, accepted_risk, resolved").addHelpText("after", `
10529
10619
  Examples:
10530
10620
  $ kinosec finding update abc123 --status false_positive
10531
10621
  $ kinosec finding update abc123 --status fixed`).action(async (id, options) => {
@@ -10572,7 +10662,7 @@ Examples:
10572
10662
  }
10573
10663
 
10574
10664
  // src/commands/report.ts
10575
- var import_picocolors7 = __toESM(require_picocolors(), 1);
10665
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
10576
10666
  function renderMarkdown(data) {
10577
10667
  const { scan, findings } = data;
10578
10668
  const lines = [];
@@ -10651,7 +10741,7 @@ Examples:
10651
10741
  ])
10652
10742
  );
10653
10743
  if (result.next_cursor) {
10654
- console.log(import_picocolors7.default.dim(`
10744
+ console.log(import_picocolors8.default.dim(`
10655
10745
  More results available. Use --cursor ${result.next_cursor}`));
10656
10746
  }
10657
10747
  } catch (error) {
@@ -10693,10 +10783,10 @@ Examples:
10693
10783
  handleError(error);
10694
10784
  }
10695
10785
  });
10696
- report.command("export").description("Export a scan report").requiredOption("--scan <id>", "Scan report ID").option("--format <format>", "Export format: json or markdown", "json").addHelpText("after", `
10786
+ report.command("export").description("Export a report").argument("<id>", "Report ID").option("--format <format>", "Export format: json or markdown", "json").addHelpText("after", `
10697
10787
  Examples:
10698
- $ kinosec report export --scan abc123 --format json > report.json
10699
- $ kinosec report export --scan abc123 --format markdown > report.md`).action(async (options) => {
10788
+ $ kinosec report export abc123 --format json > report.json
10789
+ $ kinosec report export abc123 --format markdown > report.md`).action(async (id, options) => {
10700
10790
  const validFormats = ["json", "markdown"];
10701
10791
  if (!validFormats.includes(options.format)) {
10702
10792
  console.error(`Invalid format "${options.format}". Valid: ${validFormats.join(", ")}`);
@@ -10707,7 +10797,7 @@ Examples:
10707
10797
  const { token, apiUrl } = resolveToken(opts);
10708
10798
  const client = new KinoSecClient({ apiUrl, token });
10709
10799
  const result = await client.reports.export({
10710
- scan_report_id: options.scan,
10800
+ scan_report_id: id,
10711
10801
  format: "json"
10712
10802
  });
10713
10803
  if (options.format === "markdown" && !(opts.json || opts.output === "json")) {
@@ -10722,7 +10812,7 @@ Examples:
10722
10812
  }
10723
10813
 
10724
10814
  // src/commands/token.ts
10725
- var import_picocolors8 = __toESM(require_picocolors(), 1);
10815
+ var import_picocolors9 = __toESM(require_picocolors(), 1);
10726
10816
  function registerTokenCommand(program3) {
10727
10817
  const token = program3.command("token").description("Manage API tokens");
10728
10818
  token.command("list").description("List API tokens").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
@@ -10757,7 +10847,7 @@ Examples:
10757
10847
  ])
10758
10848
  );
10759
10849
  if (result.next_cursor) {
10760
- console.log(import_picocolors8.default.dim(`
10850
+ console.log(import_picocolors9.default.dim(`
10761
10851
  More results available. Use --cursor ${result.next_cursor}`));
10762
10852
  }
10763
10853
  } catch (error) {
@@ -10784,7 +10874,7 @@ Examples:
10784
10874
  }
10785
10875
  printSuccess(`Token created: ${result.name}`);
10786
10876
  console.log("");
10787
- console.log(import_picocolors8.default.bold(" Token: ") + result.token);
10877
+ console.log(import_picocolors9.default.bold(" Token: ") + result.token);
10788
10878
  console.log("");
10789
10879
  printWarning("Save this token \u2014 it will not be shown again.");
10790
10880
  console.log(` ID: ${result.id}`);
@@ -10817,16 +10907,16 @@ Examples:
10817
10907
 
10818
10908
  // src/cli.ts
10819
10909
  var program2 = new Command();
10820
- program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.2.1", "-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");
10910
+ program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.3.1", "-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");
10821
10911
  registerAuthCommand(program2);
10822
10912
  registerConfigCommand(program2);
10823
10913
  registerMeCommand(program2);
10824
- registerScanCommand(program2);
10914
+ registerPentestCommand(program2);
10825
10915
  registerAssetCommand(program2);
10826
10916
  registerFindingCommand(program2);
10827
10917
  registerReportCommand(program2);
10828
10918
  registerTokenCommand(program2);
10829
- program2.parseAsync(process.argv).then(() => checkForUpdate("0.2.1")).catch(handleError);
10919
+ program2.parseAsync(process.argv).then(() => checkForUpdate("0.3.1")).catch(handleError);
10830
10920
  /*! Bundled license information:
10831
10921
 
10832
10922
  ky/distribution/index.js: