@kinosec/cli 0.6.2 → 0.7.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 +130 -40
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -10894,16 +10894,89 @@ function ora(options) {
|
|
|
10894
10894
|
return new Ora(options);
|
|
10895
10895
|
}
|
|
10896
10896
|
|
|
10897
|
+
// src/lib/activity-filter.ts
|
|
10898
|
+
function normalizeActivityHistory(raw) {
|
|
10899
|
+
if (!raw) return [];
|
|
10900
|
+
return raw.map((entry) => {
|
|
10901
|
+
if (typeof entry === "string") return entry;
|
|
10902
|
+
if (entry && typeof entry === "object" && "message" in entry) {
|
|
10903
|
+
return String(entry.message);
|
|
10904
|
+
}
|
|
10905
|
+
return JSON.stringify(entry);
|
|
10906
|
+
});
|
|
10907
|
+
}
|
|
10908
|
+
function filterActivityForTier(entries, showFullDetails) {
|
|
10909
|
+
if (showFullDetails) return entries;
|
|
10910
|
+
return entries.filter((line) => !/^\s{4,}/.test(line)).map((line) => {
|
|
10911
|
+
if (/\] Running:/.test(line)) {
|
|
10912
|
+
return line.replace(
|
|
10913
|
+
/Running:.*/,
|
|
10914
|
+
"Running: [upgrade to Security Pro for full details]"
|
|
10915
|
+
);
|
|
10916
|
+
}
|
|
10917
|
+
return line;
|
|
10918
|
+
});
|
|
10919
|
+
}
|
|
10920
|
+
|
|
10921
|
+
// src/lib/buzzwords.ts
|
|
10922
|
+
var SCAN_EXECUTION_PHRASES = [
|
|
10923
|
+
"DEEP SCAN",
|
|
10924
|
+
"PROBING",
|
|
10925
|
+
"SWEEPING",
|
|
10926
|
+
"SIGNAL LOCK",
|
|
10927
|
+
"ENUMERATING",
|
|
10928
|
+
"TRAVERSING",
|
|
10929
|
+
"MAPPING",
|
|
10930
|
+
"RECON",
|
|
10931
|
+
"INTERCEPTING",
|
|
10932
|
+
"CALIBRATING",
|
|
10933
|
+
"FINGERPRINTING",
|
|
10934
|
+
"NET SWEEP",
|
|
10935
|
+
"FULL SPECTRUM",
|
|
10936
|
+
"RESOLVING",
|
|
10937
|
+
"HANDSHAKE",
|
|
10938
|
+
"DISPATCHING",
|
|
10939
|
+
"CORRELATING",
|
|
10940
|
+
"DARK TRACE",
|
|
10941
|
+
"ECHO RETURN",
|
|
10942
|
+
"MULTIPLEXING",
|
|
10943
|
+
"NODE ACTIVE",
|
|
10944
|
+
"FLUX STATE",
|
|
10945
|
+
"PARSING",
|
|
10946
|
+
"BEACON ACTIVE"
|
|
10947
|
+
];
|
|
10948
|
+
function shuffle(arr) {
|
|
10949
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
10950
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
10951
|
+
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
10952
|
+
}
|
|
10953
|
+
}
|
|
10954
|
+
function createBuzzwordCycler(phrases, intervalMs, onTick) {
|
|
10955
|
+
const pool = [...phrases];
|
|
10956
|
+
shuffle(pool);
|
|
10957
|
+
let index = 0;
|
|
10958
|
+
function advance() {
|
|
10959
|
+
index++;
|
|
10960
|
+
if (index >= pool.length) {
|
|
10961
|
+
shuffle(pool);
|
|
10962
|
+
index = 0;
|
|
10963
|
+
}
|
|
10964
|
+
onTick?.(pool[index]);
|
|
10965
|
+
}
|
|
10966
|
+
const timer = setInterval(advance, intervalMs);
|
|
10967
|
+
return {
|
|
10968
|
+
current: () => pool[index],
|
|
10969
|
+
destroy: () => clearInterval(timer)
|
|
10970
|
+
};
|
|
10971
|
+
}
|
|
10972
|
+
|
|
10897
10973
|
// src/lib/watch.ts
|
|
10898
10974
|
var UNICODE = isUnicodeSupported();
|
|
10899
10975
|
var sym = {
|
|
10900
10976
|
success: UNICODE ? "\u2713" : "v",
|
|
10901
10977
|
fail: UNICODE ? "\u2717" : "x",
|
|
10902
|
-
warn: UNICODE ? "\u26A0" : "!"
|
|
10903
|
-
rule: UNICODE ? "\u2500" : "-"
|
|
10978
|
+
warn: UNICODE ? "\u26A0" : "!"
|
|
10904
10979
|
};
|
|
10905
|
-
var LABEL_WIDTH = 40;
|
|
10906
|
-
var TIMING_WIDTH = 8;
|
|
10907
10980
|
function formatDuration(ms) {
|
|
10908
10981
|
if (ms < 1e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
10909
10982
|
if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
|
|
@@ -10912,14 +10985,6 @@ function formatDuration(ms) {
|
|
|
10912
10985
|
const r = totalSec % 60;
|
|
10913
10986
|
return `${m}m ${r.toString().padStart(2, "0")}s`;
|
|
10914
10987
|
}
|
|
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
10988
|
function renderFindingsSummary(counts) {
|
|
10924
10989
|
const levels = [
|
|
10925
10990
|
{ key: "critical", label: "critical", color: import_picocolors5.default.red },
|
|
@@ -10941,55 +11006,80 @@ function findCount(counts, level) {
|
|
|
10941
11006
|
const key = Object.keys(counts).find((k) => k.toLowerCase() === level);
|
|
10942
11007
|
return key ? counts[key] : 0;
|
|
10943
11008
|
}
|
|
11009
|
+
function extractPhase(entries) {
|
|
11010
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
11011
|
+
const m = entries[i].match(/^\[([^\]]+)\]/);
|
|
11012
|
+
if (m) return m[1].toUpperCase();
|
|
11013
|
+
}
|
|
11014
|
+
return null;
|
|
11015
|
+
}
|
|
11016
|
+
function printActivityLines(entries, tty2) {
|
|
11017
|
+
for (let i = 0; i < entries.length; i++) {
|
|
11018
|
+
const isLastEntry = i === entries.length - 1;
|
|
11019
|
+
const prompt = tty2 ? import_picocolors5.default.green(">") : ">";
|
|
11020
|
+
const text = tty2 ? isLastEntry ? import_picocolors5.default.green(entries[i]) : import_picocolors5.default.dim(import_picocolors5.default.green(entries[i])) : entries[i];
|
|
11021
|
+
console.log(`${prompt} ${text}`);
|
|
11022
|
+
}
|
|
11023
|
+
}
|
|
11024
|
+
async function resolveShowFullDetails(client) {
|
|
11025
|
+
try {
|
|
11026
|
+
const me = await client.me.get();
|
|
11027
|
+
const slug = me.plan.slug;
|
|
11028
|
+
return slug === "security_pro" || slug === "enterprise";
|
|
11029
|
+
} catch {
|
|
11030
|
+
return false;
|
|
11031
|
+
}
|
|
11032
|
+
}
|
|
10944
11033
|
async function watchPentest(client, profileId, options) {
|
|
10945
11034
|
const { timeoutMs, intervalMs, useJson, showFindingsSummary } = options;
|
|
10946
11035
|
const tty2 = isTTY();
|
|
10947
11036
|
const loopStart = Date.now();
|
|
10948
11037
|
let spinner = null;
|
|
10949
|
-
let
|
|
10950
|
-
let
|
|
11038
|
+
let printedCount = 0;
|
|
11039
|
+
let currentPhase = null;
|
|
11040
|
+
let cycler = null;
|
|
11041
|
+
function updateSpinnerText() {
|
|
11042
|
+
if (!spinner || !cycler) return;
|
|
11043
|
+
const buzz = cycler.current();
|
|
11044
|
+
spinner.text = import_picocolors5.default.green(currentPhase ? `[${currentPhase}] ${buzz}...` : `${buzz}...`);
|
|
11045
|
+
}
|
|
10951
11046
|
if (!useJson && tty2) {
|
|
10952
|
-
spinner = ora({ text: "Fetching pentest status...", isEnabled: true }).start();
|
|
11047
|
+
spinner = ora({ text: "Fetching pentest status...", color: "green", isEnabled: true }).start();
|
|
11048
|
+
cycler = createBuzzwordCycler(SCAN_EXECUTION_PHRASES, 2500, () => {
|
|
11049
|
+
updateSpinnerText();
|
|
11050
|
+
});
|
|
10953
11051
|
}
|
|
10954
11052
|
const cleanup = () => {
|
|
10955
11053
|
if (spinner) spinner.stop();
|
|
11054
|
+
if (cycler) cycler.destroy();
|
|
10956
11055
|
};
|
|
10957
11056
|
process.once("SIGINT", () => {
|
|
10958
11057
|
cleanup();
|
|
10959
11058
|
console.log("\nWatch cancelled. Pentest is still running.");
|
|
10960
11059
|
process.exit(0);
|
|
10961
11060
|
});
|
|
11061
|
+
const showFullDetails = await resolveShowFullDetails(client);
|
|
10962
11062
|
while (true) {
|
|
10963
11063
|
const scanDetail = await client.scans.get(profileId);
|
|
10964
|
-
const activity = scanDetail.current_activity || "";
|
|
10965
11064
|
const totalMs = Date.now() - loopStart;
|
|
10966
|
-
const currentPhaseMs = Date.now() - phaseStart;
|
|
10967
11065
|
if (useJson) {
|
|
10968
11066
|
const elapsed = Math.round(totalMs / 1e3);
|
|
10969
11067
|
process.stdout.write(
|
|
10970
11068
|
JSON.stringify({ ...scanDetail, elapsed_seconds: elapsed }) + "\n"
|
|
10971
11069
|
);
|
|
10972
|
-
} else
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
10982
|
-
|
|
10983
|
-
|
|
10984
|
-
|
|
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;
|
|
11070
|
+
} else {
|
|
11071
|
+
const normalized = normalizeActivityHistory(scanDetail.activity_history);
|
|
11072
|
+
const filtered = filterActivityForTier(normalized, showFullDetails);
|
|
11073
|
+
if (printedCount > filtered.length) printedCount = 0;
|
|
11074
|
+
const newEntries = filtered.slice(printedCount);
|
|
11075
|
+
if (newEntries.length > 0) {
|
|
11076
|
+
if (spinner) spinner.stop();
|
|
11077
|
+
printActivityLines(newEntries, tty2);
|
|
11078
|
+
printedCount += newEntries.length;
|
|
11079
|
+
if (spinner) spinner = ora({ color: "green", isEnabled: true }).start();
|
|
11080
|
+
}
|
|
11081
|
+
currentPhase = extractPhase(filtered);
|
|
11082
|
+
updateSpinnerText();
|
|
10993
11083
|
}
|
|
10994
11084
|
const terminalStatuses = ["Completed", "Failed", "Stopped"];
|
|
10995
11085
|
if (terminalStatuses.some((s) => scanDetail.scan_status.includes(s))) {
|
|
@@ -12162,7 +12252,7 @@ Examples:
|
|
|
12162
12252
|
|
|
12163
12253
|
// src/cli.ts
|
|
12164
12254
|
var program2 = new Command();
|
|
12165
|
-
program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.
|
|
12255
|
+
program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.7.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");
|
|
12166
12256
|
registerAuthCommand(program2);
|
|
12167
12257
|
registerConfigCommand(program2);
|
|
12168
12258
|
registerMeCommand(program2);
|
|
@@ -12171,7 +12261,7 @@ registerAssetCommand(program2);
|
|
|
12171
12261
|
registerFindingCommand(program2);
|
|
12172
12262
|
registerReportCommand(program2);
|
|
12173
12263
|
registerTokenCommand(program2);
|
|
12174
|
-
program2.parseAsync(process.argv).then(() => checkForUpdate("0.
|
|
12264
|
+
program2.parseAsync(process.argv).then(() => checkForUpdate("0.7.0")).catch(handleError);
|
|
12175
12265
|
/*! Bundled license information:
|
|
12176
12266
|
|
|
12177
12267
|
ky/distribution/index.js:
|