icoa-cli 1.9.0 → 2.0.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.
@@ -61,9 +61,9 @@ const SYSTEM_TOOLS = [
61
61
  { name: 'as', check: CMD('as'), category: 'Compilers & Build' },
62
62
  { name: 'ld', check: CMD('ld'), category: 'Compilers & Build' },
63
63
  { name: 'pkg-config', check: CMD('pkg-config'), brew: 'pkg-config', apt: 'pkg-config', category: 'Compilers & Build' },
64
- // Python (3)
65
- { name: 'python3', check: W ? 'python --version' : 'python3 --version', brew: 'python@3.12', apt: 'python3', choco: 'python312', category: 'Python Runtime' },
66
- { name: 'pip3', check: W ? 'pip --version' : 'pip3 --version', category: 'Python Runtime' },
64
+ // Python (3) — prefer brew 3.12 on macOS
65
+ { name: 'python3', check: process.platform === 'darwin' ? '/opt/homebrew/opt/python@3.12/bin/python3.12 --version' : (W ? 'python --version' : 'python3 --version'), brew: 'python@3.12', apt: 'python3', choco: 'python312', category: 'Python Runtime' },
66
+ { name: 'pip3', check: process.platform === 'darwin' ? '/opt/homebrew/opt/python@3.12/bin/pip3.12 --version' : (W ? 'pip --version' : 'pip3 --version'), category: 'Python Runtime' },
67
67
  { name: 'python3-venv', check: W ? 'python -c "import venv"' : 'python3 -c "import venv"', apt: 'python3-venv', category: 'Python Runtime' },
68
68
  // Networking (12)
69
69
  { name: 'curl', check: CMD('curl'), brew: 'curl', apt: 'curl', choco: 'curl', category: 'Networking' },
@@ -160,8 +160,8 @@ function isInstalled(check) {
160
160
  function getVersion(name) {
161
161
  // Version commands for each tool
162
162
  const versionCmds = {
163
- 'python3': 'python3 --version',
164
- 'pip3': 'pip3 --version',
163
+ 'python3': process.platform === 'darwin' ? '/opt/homebrew/opt/python@3.12/bin/python3.12 --version' : 'python3 --version',
164
+ 'pip3': process.platform === 'darwin' ? '/opt/homebrew/opt/python@3.12/bin/pip3.12 --version' : 'pip3 --version',
165
165
  'gcc': 'gcc --version',
166
166
  'g++': 'g++ --version',
167
167
  'make': 'make --version',
@@ -237,6 +237,13 @@ function getNodeInfo() {
237
237
  return `Node.js ${process.version}`;
238
238
  }
239
239
  function getPythonMajorMinor() {
240
+ // On macOS, prefer brew Python 3.12
241
+ if (process.platform === 'darwin') {
242
+ try {
243
+ return execSync('/opt/homebrew/opt/python@3.12/bin/python3.12 -c "import sys; print(f\'{sys.version_info.major}.{sys.version_info.minor}\')"', { encoding: 'utf-8' }).trim();
244
+ }
245
+ catch { /* fall through */ }
246
+ }
240
247
  try {
241
248
  return execSync('python3 -c "import sys; print(f\'{sys.version_info.major}.{sys.version_info.minor}\')"', { encoding: 'utf-8' }).trim();
242
249
  }
@@ -244,6 +251,20 @@ function getPythonMajorMinor() {
244
251
  return '';
245
252
  }
246
253
  }
254
+ function getPythonFullVersion() {
255
+ if (process.platform === 'darwin') {
256
+ try {
257
+ return execSync('/opt/homebrew/opt/python@3.12/bin/python3.12 --version', { encoding: 'utf-8' }).trim().replace('Python ', '');
258
+ }
259
+ catch { /* fall through */ }
260
+ }
261
+ try {
262
+ return execSync('python3 --version', { encoding: 'utf-8' }).trim().replace('Python ', '');
263
+ }
264
+ catch {
265
+ return '';
266
+ }
267
+ }
247
268
  export function registerEnvCommand(program) {
248
269
  const envCmd = program.command('env').description('Manage competition environment');
249
270
  envCmd.command('status').alias('check').description('Check all 109 tools').action(() => showStatus());
@@ -261,19 +282,14 @@ function showStatus() {
261
282
  console.log(chalk.gray(' Package: ') + chalk.white(pm));
262
283
  console.log(chalk.gray(' Target: ') + chalk.white(`Python ${PYTHON_TARGET}.x`));
263
284
  console.log(chalk.gray(' ─────────────────────────────────────────────'));
264
- // Python version check
285
+ // Python version check — use brew 3.12 on macOS
265
286
  const pyVer = getPythonMajorMinor();
266
- if (pyVer) {
267
- const fullVer = getVersion('python3');
268
- if (pyVer === '3.12') {
269
- console.log(chalk.green(` ✓ Python ${fullVer}`) + chalk.gray(' (recommended)'));
270
- }
271
- else if (pyVer === '3.11' || pyVer === '3.13') {
272
- console.log(chalk.yellow(` ~ Python ${fullVer}`) + chalk.gray(' (3.12.x recommended for full compatibility)'));
273
- }
274
- else {
275
- console.log(chalk.red(` ✗ Python ${fullVer}`) + chalk.gray(' (3.12.x required — some tools may not work)'));
276
- }
287
+ const fullVer = getPythonFullVersion();
288
+ if (pyVer === '3.12') {
289
+ console.log(chalk.green(` ✓ Python ${fullVer}`) + chalk.gray(' (recommended)'));
290
+ }
291
+ else if (pyVer) {
292
+ console.log(chalk.yellow(` ~ Python ${fullVer}`) + chalk.gray(' (3.12.x recommended)'));
277
293
  }
278
294
  else {
279
295
  console.log(chalk.red(' ✗ Python 3 not found'));
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ ${LINE}
36
36
  ${chalk.white('Sydney, Australia')} ${chalk.gray('Jun 27 - Jul 2, 2026')}
37
37
  ${chalk.cyan.underline('https://icoa2026.au')}
38
38
 
39
- ${chalk.gray('CLI-Native Competition Terminal v1.9.0')}
39
+ ${chalk.gray('CLI-Native Competition Terminal v2.0.0')}
40
40
 
41
41
  ${LINE}
42
42
  `;
package/dist/repl.js CHANGED
@@ -7,8 +7,27 @@ import { resetTerminalTheme } from './lib/theme.js';
7
7
  import { ensureSandbox, runInSandbox, isDockerAvailable } from './lib/sandbox.js';
8
8
  import { logCommand } from './lib/logger.js';
9
9
  import { startLogSync, stopLogSync } from './lib/log-sync.js';
10
+ import { existsSync, mkdirSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+ import { homedir } from 'node:os';
13
+ // Competition workspace — all system commands restricted here
14
+ const WORKSPACE = join(homedir(), 'icoa-workspace');
15
+ function ensureWorkspace() {
16
+ if (!existsSync(WORKSPACE))
17
+ mkdirSync(WORKSPACE, { recursive: true });
18
+ return WORKSPACE;
19
+ }
20
+ // Blocked commands — security risk
21
+ const BLOCKED_COMMANDS = new Set([
22
+ 'sudo', 'su', 'doas', 'pkexec', // privilege escalation
23
+ 'brew', 'apt', 'apt-get', 'yum', 'choco', // package managers (use env setup)
24
+ 'npm', 'npx', 'pip', 'pip3', // package install (use env setup)
25
+ 'shutdown', 'reboot', 'halt', // system control
26
+ 'mkfs', 'fdisk', 'dd', // disk operations
27
+ 'iptables', 'ufw', // firewall
28
+ ]);
10
29
  const INTERCEPT = '__REPL_NO_EXIT__';
11
- const VERSION = '1.9.0';
30
+ const VERSION = '2.0.0';
12
31
  export async function startRepl(program, resumeMode) {
13
32
  const config = getConfig();
14
33
  const connected = isConnected();
@@ -83,7 +102,9 @@ export async function startRepl(program, resumeMode) {
83
102
  console.log();
84
103
  }
85
104
  else if (activated) {
105
+ ensureWorkspace();
86
106
  console.log(chalk.green(' Welcome, competitor! Ready to hack.'));
107
+ console.log(chalk.gray(` Workspace: ${WORKSPACE}`));
87
108
  console.log();
88
109
  console.log(chalk.gray(' Quick Start'));
89
110
  console.log(chalk.gray(' ─────────────'));
@@ -193,20 +214,24 @@ export async function startRepl(program, resumeMode) {
193
214
  'log', 'lang', 'setup', 'env', 'model', 'ctf',
194
215
  ];
195
216
  if (!knownCommands.includes(cmd)) {
196
- // Force Python 3.12 — rewrite python3/python/pip3 to 3.12 binaries
217
+ // Block dangerous commands
218
+ if (BLOCKED_COMMANDS.has(cmd)) {
219
+ console.log(chalk.red(` Blocked: ${cmd} is not allowed during competition.`));
220
+ console.log();
221
+ rl.prompt();
222
+ return;
223
+ }
224
+ // Force Python 3.12 — rewrite python3/python to 3.12 binaries
197
225
  let resolvedInput = input;
198
226
  if (process.platform === 'darwin') {
199
227
  const py12 = '/opt/homebrew/opt/python@3.12/bin/python3.12';
200
- const pip12 = '/opt/homebrew/opt/python@3.12/bin/pip3.12';
201
228
  resolvedInput = resolvedInput
202
229
  .replace(/^python3?\s/, `${py12} `)
203
- .replace(/^pip3?\s/, `${pip12} `);
204
- if (resolvedInput === 'python3' || resolvedInput === 'python')
205
- resolvedInput = py12;
206
- if (resolvedInput === 'pip3' || resolvedInput === 'pip')
207
- resolvedInput = pip12;
230
+ .replace(/^(python3|python)$/, py12);
208
231
  }
209
- // Route to Docker sandbox if available, otherwise system shell
232
+ // Ensure workspace directory
233
+ const cwd = ensureWorkspace();
234
+ // Route to Docker sandbox if available, otherwise system shell (in workspace)
210
235
  processing = true;
211
236
  try {
212
237
  if (isDockerAvailable()) {
@@ -215,11 +240,11 @@ export async function startRepl(program, resumeMode) {
215
240
  await runInSandbox(resolvedInput, rl);
216
241
  }
217
242
  else {
218
- await runSystemCommand(resolvedInput, rl);
243
+ await runSystemCommand(resolvedInput, rl, cwd);
219
244
  }
220
245
  }
221
246
  else {
222
- await runSystemCommand(resolvedInput, rl);
247
+ await runSystemCommand(resolvedInput, rl, cwd);
223
248
  }
224
249
  }
225
250
  catch {
@@ -264,13 +289,13 @@ export async function startRepl(program, resumeMode) {
264
289
  realExit(0);
265
290
  });
266
291
  }
267
- function runSystemCommand(input, rl) {
292
+ function runSystemCommand(input, rl, cwd) {
268
293
  return new Promise((resolve) => {
269
- // Pause readline so the child process gets full terminal control
270
294
  rl.pause();
271
295
  const child = spawn(input, {
272
296
  shell: true,
273
297
  stdio: 'inherit',
298
+ cwd: cwd || process.cwd(),
274
299
  });
275
300
  child.on('close', () => {
276
301
  rl.resume();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "1.9.0",
3
+ "version": "2.0.0",
4
4
  "description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
5
5
  "type": "module",
6
6
  "bin": {