icoa-cli 2.19.75 → 2.19.76

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.
Files changed (2) hide show
  1. package/dist/repl.js +59 -0
  2. package/package.json +1 -1
package/dist/repl.js CHANGED
@@ -17,6 +17,55 @@ import { startLogSync, stopLogSync } from './lib/log-sync.js';
17
17
  import { existsSync, mkdirSync } from 'node:fs';
18
18
  import { join } from 'node:path';
19
19
  import { homedir } from 'node:os';
20
+ // Pre-import commonly-used CTF modules on interactive Python startup.
21
+ // Failures are silent — selectors import only what's available so a partial
22
+ // exam setup doesn't crash the shell. Creates ~/.icoa/python-startup.py once.
23
+ const PYTHON_STARTUP_CONTENT = `# ICOA exam interactive startup — auto-loaded by PYTHONSTARTUP
24
+ import base64, struct, hashlib, re, json, os, sys, binascii
25
+ try: import requests
26
+ except ImportError: pass
27
+ try: from Crypto.Cipher import AES
28
+ except ImportError: pass
29
+ try: from Crypto.Util.Padding import pad, unpad
30
+ except ImportError: pass
31
+ try: from pwn import xor, p32, u32, p64, u64
32
+ except ImportError: pass
33
+ try: import bs4
34
+ except ImportError: pass
35
+ try: import numpy as np
36
+ except ImportError: pass
37
+ `;
38
+ function ensurePythonStartup() {
39
+ const icoaDir = join(homedir(), '.icoa');
40
+ if (!existsSync(icoaDir))
41
+ mkdirSync(icoaDir, { recursive: true });
42
+ const path = join(icoaDir, 'python-startup.py');
43
+ if (!existsSync(path)) {
44
+ const { writeFileSync } = require('node:fs');
45
+ writeFileSync(path, PYTHON_STARTUP_CONTENT);
46
+ }
47
+ return path;
48
+ }
49
+ function printPythonBanner() {
50
+ console.log();
51
+ console.log(chalk.cyan(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
52
+ console.log(chalk.bold.white(' Python ready — ICOA exam toolkit pre-loaded'));
53
+ console.log(chalk.cyan(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
54
+ console.log();
55
+ console.log(chalk.white(' Already imported: ') + chalk.gray('base64, struct, hashlib, re, json, binascii'));
56
+ console.log(chalk.white(' Also available: ') + chalk.gray('requests, bs4, numpy, AES, pad/unpad, xor, p32/u32/p64/u64'));
57
+ console.log();
58
+ console.log(chalk.yellow(' Quick examples:'));
59
+ console.log(chalk.gray(' base64.b64decode("aGVsbG8=") ') + chalk.gray('# decode base64'));
60
+ console.log(chalk.gray(' bytes.fromhex("48656c6c6f") ') + chalk.gray('# hex → bytes'));
61
+ console.log(chalk.gray(' "ICOA{x}".encode() ') + chalk.gray('# str → bytes'));
62
+ console.log(chalk.gray(' [chr(c) for c in [73,67,79,65]] ') + chalk.gray('# ASCII codes'));
63
+ console.log(chalk.gray(' xor(bytes.fromhex("0a2b"), b"IC") ') + chalk.gray('# pwntools XOR'));
64
+ console.log();
65
+ console.log(chalk.gray(' Exit: ') + chalk.white('exit()') + chalk.gray(' or Ctrl-D'));
66
+ console.log(chalk.cyan(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
67
+ console.log();
68
+ }
20
69
  // Compute the REPL prompt based on current state.
21
70
  // Real exam wins over demo (matches getExamState() priority). Chat modes have
22
71
  // their own prompts and are handled separately.
@@ -792,6 +841,16 @@ export async function startRepl(program, resumeMode) {
792
841
  }
793
842
  // Ensure workspace directory
794
843
  const cwd = ensureWorkspace();
844
+ // Interactive Python (no script args, no -c): print welcome banner and
845
+ // pre-import common CTF modules via a startup file. Makes entry painless
846
+ // for contestants — they land in a shell with base64/struct/hashlib/pwn
847
+ // already imported and see a 4-line cheat sheet of common one-liners.
848
+ const isInteractivePython = /^(\S*python3?(\.\d+)?)\s*$/.test(resolvedInput);
849
+ if (isInteractivePython) {
850
+ const startupPath = ensurePythonStartup();
851
+ resolvedInput = `PYTHONSTARTUP="${startupPath}" ${resolvedInput}`;
852
+ printPythonBanner();
853
+ }
795
854
  // Route to Docker sandbox if available, otherwise system shell (in workspace)
796
855
  processing = true;
797
856
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.19.75",
3
+ "version": "2.19.76",
4
4
  "description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
5
5
  "type": "module",
6
6
  "bin": {