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.
- package/dist/repl.js +59 -0
- 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 {
|