icoa-cli 2.19.81 → 2.19.83
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/commands/env.js +38 -20
- package/dist/commands/exam.js +1 -1
- package/dist/repl.js +29 -15
- package/package.json +1 -1
package/dist/commands/env.js
CHANGED
|
@@ -44,7 +44,7 @@ const PYTHON_LIBS = [
|
|
|
44
44
|
{ name: 'pyserial', check: 'python3 -c "import serial"', install: 'pyserial==3.5', category: 'Security Tools' },
|
|
45
45
|
];
|
|
46
46
|
// ══════════════════════════════════════════════════════════
|
|
47
|
-
//
|
|
47
|
+
// 83 System Tools — brew (Mac) / apt (Ubuntu) / choco (Win)
|
|
48
48
|
// ══════════════════════════════════════════════════════════
|
|
49
49
|
const W = process.platform === 'win32';
|
|
50
50
|
const CMD = (unix, win) => W ? (win || `where ${unix}`) : `which ${unix}`;
|
|
@@ -109,6 +109,8 @@ const SYSTEM_TOOLS = [
|
|
|
109
109
|
{ name: 'xxd', check: CMD('xxd'), brew: 'vim', apt: 'xxd', category: 'Forensics' },
|
|
110
110
|
{ name: 'pdftotext', check: CMD('pdftotext'), brew: 'poppler', apt: 'poppler-utils', category: 'Forensics' },
|
|
111
111
|
{ name: 'pngcheck', check: CMD('pngcheck'), brew: 'pngcheck', apt: 'pngcheck', category: 'Forensics' },
|
|
112
|
+
// sleuthkit — disk-image forensics (mmls/fls/icat/blkcat + 20 more)
|
|
113
|
+
{ name: 'sleuthkit', check: CMD('mmls'), brew: 'sleuthkit', apt: 'sleuthkit', choco: 'sleuthkit', category: 'Forensics' },
|
|
112
114
|
// Crypto & Password (4)
|
|
113
115
|
{ name: 'john', check: CMD('john'), brew: 'john', apt: 'john', choco: 'john', category: 'Crypto & Password' },
|
|
114
116
|
{ name: 'hashcat', check: CMD('hashcat'), brew: 'hashcat', apt: 'hashcat', choco: 'hashcat', category: 'Crypto & Password' },
|
|
@@ -270,12 +272,12 @@ function getPythonFullVersion() {
|
|
|
270
272
|
}
|
|
271
273
|
export function registerEnvCommand(program) {
|
|
272
274
|
const envCmd = program.command('env').description('Manage competition environment');
|
|
273
|
-
envCmd.command('status').alias('check').description('Check all
|
|
275
|
+
envCmd.command('status').alias('check').description('Check all 110 tools').action(() => showStatus());
|
|
274
276
|
envCmd.command('setup').description('Install all Python libraries + system tools').action(async () => { await installAll(); });
|
|
275
277
|
envCmd.command('python').description('Show Python 3.12 install guide for your platform').action(() => showPythonInstallGuide());
|
|
276
278
|
envCmd.action(() => showStatus());
|
|
277
279
|
}
|
|
278
|
-
// Lightweight Python 3.12 install guide for Selection mode (no
|
|
280
|
+
// Lightweight Python 3.12 install guide for Selection mode (no 110-tool check).
|
|
279
281
|
// Detects distro on Linux so contestants get the right command the first time.
|
|
280
282
|
function showPythonInstallGuide() {
|
|
281
283
|
const os = platform();
|
|
@@ -285,21 +287,37 @@ function showPythonInstallGuide() {
|
|
|
285
287
|
console.log();
|
|
286
288
|
let currentPy = '';
|
|
287
289
|
let pyStatus = 'missing';
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
290
|
+
// Prefer the versioned binary if available. On macOS with Homebrew, `python3`
|
|
291
|
+
// often points to the latest (e.g. 3.13) while python@3.12 is also installed
|
|
292
|
+
// at /opt/homebrew/opt/python@3.12/bin/python3.12. Don't force the user to
|
|
293
|
+
// reinstall just because the default changed.
|
|
294
|
+
const pyProbes = [
|
|
295
|
+
'python3.12 --version',
|
|
296
|
+
'/opt/homebrew/opt/python@3.12/bin/python3.12 --version',
|
|
297
|
+
'/usr/local/opt/python@3.12/bin/python3.12 --version',
|
|
298
|
+
'python3 --version',
|
|
299
|
+
];
|
|
300
|
+
for (const cmd of pyProbes) {
|
|
301
|
+
try {
|
|
302
|
+
const out = execSync(cmd, { encoding: 'utf-8', timeout: 3000 }).trim();
|
|
303
|
+
const ver = out.replace('Python ', '');
|
|
304
|
+
const parts = ver.split('.').map(Number);
|
|
305
|
+
// Only accept as "ok" if this probe returned 3.12. Otherwise keep trying.
|
|
306
|
+
if (parts[0] === 3 && parts[1] === 12) {
|
|
307
|
+
currentPy = ver;
|
|
308
|
+
pyStatus = 'ok';
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
// Remember the last-seen version for fallback reporting
|
|
312
|
+
currentPy = ver;
|
|
313
|
+
if (parts[0] === 3 && parts[1] >= 10 && parts[1] < 12)
|
|
314
|
+
pyStatus = 'old';
|
|
315
|
+
else if (parts[0] === 3 && parts[1] > 12)
|
|
316
|
+
pyStatus = 'new';
|
|
317
|
+
else
|
|
318
|
+
pyStatus = 'missing';
|
|
319
|
+
}
|
|
320
|
+
catch { /* try next probe */ }
|
|
303
321
|
}
|
|
304
322
|
if (pyStatus === 'ok') {
|
|
305
323
|
console.log(chalk.green(` ✓ Python ${currentPy} — you're good!`));
|
|
@@ -389,7 +407,7 @@ function showStatus() {
|
|
|
389
407
|
console.log(chalk.gray(' You need these for most challenges'));
|
|
390
408
|
console.log(chalk.yellow(' Recommended') + chalk.gray(' pycryptodome, beautifulsoup4, scapy, sympy'));
|
|
391
409
|
console.log(chalk.gray(' Covers Web, Crypto, and Forensics'));
|
|
392
|
-
console.log(chalk.gray(' Full (
|
|
410
|
+
console.log(chalk.gray(' Full (110) All tools for every category'));
|
|
393
411
|
console.log();
|
|
394
412
|
console.log(chalk.gray(' Missing tools? Run ') + chalk.bold.cyan('env setup') + chalk.gray(' to install everything.'));
|
|
395
413
|
console.log(chalk.gray(' ─────────────────────────────────────────────'));
|
|
@@ -486,7 +504,7 @@ function showStatus() {
|
|
|
486
504
|
const total = installed + missing;
|
|
487
505
|
console.log();
|
|
488
506
|
console.log(chalk.gray(' ─────────────────────────────────────────────'));
|
|
489
|
-
console.log(` ${chalk.green(`✓ ${installed}/${total}`)} ${missing > 0 ? chalk.red(`✗ ${missing} missing`) : chalk.green('All
|
|
507
|
+
console.log(` ${chalk.green(`✓ ${installed}/${total}`)} ${missing > 0 ? chalk.red(`✗ ${missing} missing`) : chalk.green('All 110 ready!')}`);
|
|
490
508
|
if (missing > 0) {
|
|
491
509
|
console.log(chalk.gray(' Install everything: ') + chalk.white('env setup'));
|
|
492
510
|
}
|
package/dist/commands/exam.js
CHANGED
|
@@ -101,7 +101,7 @@ async function playDemoIntro() {
|
|
|
101
101
|
await waitOrSkip(1500);
|
|
102
102
|
if (skipped)
|
|
103
103
|
return;
|
|
104
|
-
console.log(chalk.gray(' ·
|
|
104
|
+
console.log(chalk.gray(' · 110 CTF tools pre-configured'));
|
|
105
105
|
await waitOrSkip(400);
|
|
106
106
|
if (skipped)
|
|
107
107
|
return;
|
package/dist/repl.js
CHANGED
|
@@ -103,21 +103,35 @@ const VERSION = '2.5.1';
|
|
|
103
103
|
// Quick Python version check (used in Selection menu startup warning).
|
|
104
104
|
// Returns {ok, version, status}. Silent/defensive — any error means 'missing'.
|
|
105
105
|
function checkPython() {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
106
|
+
// Probe python3.12 first — macOS Homebrew installs python@3.12 alongside a
|
|
107
|
+
// newer default python3, and we don't want to flag a 3.12-ready machine just
|
|
108
|
+
// because the default alias moved to 3.13.
|
|
109
|
+
const probes = [
|
|
110
|
+
'python3.12 --version',
|
|
111
|
+
'/opt/homebrew/opt/python@3.12/bin/python3.12 --version',
|
|
112
|
+
'/usr/local/opt/python@3.12/bin/python3.12 --version',
|
|
113
|
+
'python3 --version',
|
|
114
|
+
];
|
|
115
|
+
let lastVersion = '';
|
|
116
|
+
let lastStatus = 'missing';
|
|
117
|
+
for (const cmd of probes) {
|
|
118
|
+
try {
|
|
119
|
+
const out = execSyncFn(cmd, { encoding: 'utf-8', timeout: 2000 }).trim();
|
|
120
|
+
const version = out.replace('Python ', '');
|
|
121
|
+
const [maj, min] = version.split('.').map(Number);
|
|
122
|
+
if (maj === 3 && min === 12)
|
|
123
|
+
return { ok: true, version, status: 'ok' };
|
|
124
|
+
lastVersion = version;
|
|
125
|
+
if (maj === 3 && min >= 10 && min < 12)
|
|
126
|
+
lastStatus = 'old';
|
|
127
|
+
else if (maj === 3 && min > 12)
|
|
128
|
+
lastStatus = 'new';
|
|
129
|
+
else
|
|
130
|
+
lastStatus = 'missing';
|
|
131
|
+
}
|
|
132
|
+
catch { /* try next probe */ }
|
|
120
133
|
}
|
|
134
|
+
return { ok: lastStatus !== 'missing', version: lastVersion, status: lastStatus };
|
|
121
135
|
}
|
|
122
136
|
function printSelectionMenu() {
|
|
123
137
|
const stats = getDemoStats();
|
|
@@ -231,7 +245,7 @@ export async function startRepl(program, resumeMode) {
|
|
|
231
245
|
console.log(chalk.bold.white(' What Makes ICOA Different'));
|
|
232
246
|
console.log(chalk.gray(' · AI-native AI teammate, AI adversary, AI translation'));
|
|
233
247
|
console.log(chalk.gray(' · CLI OS Complete competition environment in terminal'));
|
|
234
|
-
console.log(chalk.gray(' ·
|
|
248
|
+
console.log(chalk.gray(' · 110 tools pwntools, z3, gdb, nmap, sleuthkit... pre-configured'));
|
|
235
249
|
console.log(chalk.gray(' · Global scale 15,000+ concurrent exams · 15 languages'));
|
|
236
250
|
console.log();
|
|
237
251
|
console.log(chalk.bold.white(' Competition Format'));
|