icoa-cli 2.19.357 → 2.19.359

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 (75) hide show
  1. package/dist/commands/ai4ctf.js +1 -1
  2. package/dist/commands/ctf.js +1 -787
  3. package/dist/commands/ctf4ai-demo.js +1 -1
  4. package/dist/commands/ctf4vla.js +1 -1
  5. package/dist/commands/demo2.js +1 -1502
  6. package/dist/commands/doctor.d.ts +2 -0
  7. package/dist/commands/doctor.js +1 -0
  8. package/dist/commands/exam.js +1 -1
  9. package/dist/commands/files.js +1 -59
  10. package/dist/commands/lang.js +1 -202
  11. package/dist/commands/log.js +1 -171
  12. package/dist/commands/shell.js +1 -151
  13. package/dist/commands/sim.js +1 -389
  14. package/dist/index.js +1 -355
  15. package/dist/lib/access.js +1 -184
  16. package/dist/lib/aienv.js +1 -205
  17. package/dist/lib/arena-submit.js +1 -21
  18. package/dist/lib/banner.js +1 -31
  19. package/dist/lib/budget.js +1 -6
  20. package/dist/lib/challenge-dir.js +1 -16
  21. package/dist/lib/colors.js +1 -17
  22. package/dist/lib/comms.js +1 -212
  23. package/dist/lib/config.js +1 -93
  24. package/dist/lib/countdown.js +1 -43
  25. package/dist/lib/country-lang.js +1 -39
  26. package/dist/lib/ctfd-client.js +1 -417
  27. package/dist/lib/demo-exam.js +1 -478
  28. package/dist/lib/demo-flags.js +1 -27
  29. package/dist/lib/demo-stats.js +1 -62
  30. package/dist/lib/demo2-progress.js +1 -102
  31. package/dist/lib/docker-probe.js +1 -118
  32. package/dist/lib/editor-spawn.js +1 -53
  33. package/dist/lib/exam-client.js +1 -54
  34. package/dist/lib/exam-sandbox.js +1 -201
  35. package/dist/lib/exam-setup.js +1 -36
  36. package/dist/lib/exam-state.js +1 -273
  37. package/dist/lib/gemini.js +1 -247
  38. package/dist/lib/i18n.js +1 -302
  39. package/dist/lib/integrity-snapshot.js +1 -88
  40. package/dist/lib/interactive-spawn.js +1 -55
  41. package/dist/lib/ipynb-input.js +1 -65
  42. package/dist/lib/kernel-protocol.js +1 -88
  43. package/dist/lib/kernel.js +2 -146
  44. package/dist/lib/learn-curricula.js +1 -309
  45. package/dist/lib/learn-i18n.js +1 -184
  46. package/dist/lib/learn-input.js +1 -101
  47. package/dist/lib/learn-render.js +1 -863
  48. package/dist/lib/learn-state.js +1 -103
  49. package/dist/lib/log-sync.js +1 -155
  50. package/dist/lib/logger.js +1 -49
  51. package/dist/lib/main-rl.js +1 -7
  52. package/dist/lib/menu-nav.js +1 -105
  53. package/dist/lib/notebook-doc.js +1 -137
  54. package/dist/lib/open-file.js +1 -55
  55. package/dist/lib/paper-upgrade.js +1 -119
  56. package/dist/lib/platform.js +1 -99
  57. package/dist/lib/render-card.js +1 -112
  58. package/dist/lib/repl-asker.js +1 -67
  59. package/dist/lib/sample-runner.js +1 -227
  60. package/dist/lib/sandbox.js +1 -144
  61. package/dist/lib/shell-split.js +1 -69
  62. package/dist/lib/sim-cooldown.js +1 -75
  63. package/dist/lib/theme.js +1 -119
  64. package/dist/lib/token-format.js +1 -74
  65. package/dist/lib/tool-man.js +1 -418
  66. package/dist/lib/toolset-hash.js +1 -48
  67. package/dist/lib/translation.js +1 -80
  68. package/dist/lib/translations-fetcher.js +1 -95
  69. package/dist/lib/ui.js +1 -99
  70. package/dist/lib/update-check.js +1 -114
  71. package/dist/lib/version.js +1 -24
  72. package/dist/postinstall.js +1 -48
  73. package/dist/repl.js +1 -2391
  74. package/dist/types/index.js +1 -63
  75. package/package.json +1 -1
@@ -1,418 +1 @@
1
- /**
2
- * `man <tool>` — quick CTF usage cheatsheet for the frozen sandbox toolset.
3
- *
4
- * This is the *usage* counterpart to `env` (which only reports install status).
5
- * Scope = the curated ~40 tools players actually use to SOLVE challenges, not
6
- * all 110 (a `man ls` stub is noise). Every tool here is confirmed present in
7
- * `src/commands/env.ts` SYSTEM_TOOLS / PYTHON_LIBS and the sandbox image.
8
- *
9
- * Bundled offline (tiny text, universal to every CTF player) — plane-1 OK,
10
- * same class as i18n strings; NOT a dataset (Thin-Client three-plane rule).
11
- *
12
- * Black-bg colour rule: chalk only; cyan = tool/command, gray = note,
13
- * bold.yellow = the "important" lifeline.
14
- */
15
- import chalk from 'chalk';
16
- // Ordered roughly by how often a CTF player reaches for them.
17
- export const TOOL_DOCS = [
18
- // ── Recon / first look ───────────────────────────────────────────
19
- {
20
- name: 'file',
21
- cat: 'Forensics',
22
- summary: 'identify a file type (always step 1)',
23
- ex: [['file mystery', 'what is this really? (extension lies)']],
24
- },
25
- {
26
- name: 'strings',
27
- cat: 'Forensics',
28
- summary: 'pull printable text out of any binary',
29
- ex: [
30
- ['strings -n 8 bin', 'runs ≥8 chars (cuts noise)'],
31
- ['strings bin | grep -i flag', 'hunt the flag directly'],
32
- ['strings -e l bin', '16-bit little-endian (wide) strings'],
33
- ],
34
- },
35
- {
36
- name: 'xxd',
37
- cat: 'Data',
38
- summary: 'hex dump / reverse hex back to bytes',
39
- ex: [
40
- ['xxd file | head', 'inspect magic bytes / header'],
41
- ['xxd -r hex.txt out.bin', 'rebuild a file from a hex dump'],
42
- ['xxd -s 0x100 -l 64 file', 'dump 64 bytes from offset 0x100'],
43
- ],
44
- },
45
- {
46
- name: 'hexdump',
47
- aliases: ['od'],
48
- cat: 'Data',
49
- summary: 'byte/octal dump (od is the BSD cousin)',
50
- ex: [
51
- ['hexdump -C file | head', 'canonical hex+ASCII view'],
52
- ['od -An -tx1 file', 'raw hex bytes, no offsets'],
53
- ],
54
- },
55
- // ── Stego / carving ──────────────────────────────────────────────
56
- {
57
- name: 'binwalk',
58
- cat: 'Forensics',
59
- summary: 'find + extract files embedded in files',
60
- ex: [
61
- ['binwalk file', 'scan for embedded signatures'],
62
- ['binwalk -e file', 'auto-extract everything it finds'],
63
- ['binwalk --dd=".*" file', 'carve every match (greedy)'],
64
- ],
65
- },
66
- {
67
- name: 'foremost',
68
- cat: 'Forensics',
69
- summary: 'carve files by header (recover deleted/hidden)',
70
- ex: [['foremost -i disk.img -o out/', 'recover into out/ by type']],
71
- },
72
- {
73
- name: 'steghide',
74
- cat: 'Forensics',
75
- summary: 'hide/extract data in JPG/BMP/WAV/AU (passphrase)',
76
- ex: [
77
- ['steghide info cat.jpg', 'is anything embedded?'],
78
- ['steghide extract -sf cat.jpg', 'pull it out (asks passphrase; blank often works)'],
79
- ],
80
- },
81
- {
82
- name: 'exiftool',
83
- cat: 'Forensics',
84
- summary: 'read/write image & doc metadata (flags hide here)',
85
- ex: [
86
- ['exiftool pic.png', 'dump all metadata'],
87
- ['exiftool -Comment pic.png', 'one specific tag'],
88
- ],
89
- },
90
- {
91
- name: 'pngcheck',
92
- cat: 'Forensics',
93
- summary: 'validate PNG chunks (find tampering / appended data)',
94
- ex: [['pngcheck -v flag.png', 'verbose chunk-by-chunk check']],
95
- },
96
- {
97
- name: 'pdftotext',
98
- cat: 'Data',
99
- summary: 'extract text (and hidden layers) from a PDF',
100
- ex: [['pdftotext doc.pdf -', 'print extracted text to stdout']],
101
- },
102
- // ── Disk / memory forensics ──────────────────────────────────────
103
- {
104
- name: 'sleuthkit',
105
- aliases: ['mmls', 'fls', 'icat', 'tsk'],
106
- cat: 'Forensics',
107
- summary: 'disk-image forensics (partition → files → recover)',
108
- ex: [
109
- ['mmls disk.img', 'list partitions + offsets'],
110
- ['fls -o 2048 -r disk.img', 'list files (use the partition offset)'],
111
- ['icat -o 2048 disk.img 12 > out', 'dump file by inode number'],
112
- ],
113
- },
114
- {
115
- name: 'volatility3',
116
- aliases: ['vol', 'volatility'],
117
- cat: 'Forensics',
118
- summary: 'memory-dump analysis',
119
- ex: [
120
- ['vol -f mem.raw windows.pslist', 'list processes'],
121
- ['vol -f mem.raw windows.cmdline', 'recover command lines'],
122
- ],
123
- },
124
- // ── Crypto / passwords ───────────────────────────────────────────
125
- {
126
- name: 'openssl',
127
- cat: 'Crypto',
128
- summary: 'swiss-army crypto (decode, hash, enc/dec, certs)',
129
- ex: [
130
- ['openssl enc -d -aes-256-cbc -in f -k pass', 'decrypt AES'],
131
- ['openssl x509 -in cert.pem -text -noout', 'read a certificate'],
132
- ['echo -n txt | openssl dgst -sha256', 'hash it'],
133
- ],
134
- },
135
- {
136
- name: 'john',
137
- aliases: ['john-the-ripper'],
138
- cat: 'Crypto',
139
- summary: 'crack password hashes (CPU)',
140
- ex: [
141
- ['john --wordlist=rockyou.txt hashes', 'dictionary attack'],
142
- ['zip2john x.zip > h ; john h', 'crack a zip password'],
143
- ['john --show hashes', 'print cracked results'],
144
- ],
145
- },
146
- {
147
- name: 'hashcat',
148
- cat: 'Crypto',
149
- summary: 'fast hash cracker (modes by -m)',
150
- ex: [['hashcat -m 0 -a 0 hash rockyou.txt', 'MD5 dictionary (-m 0 = MD5)']],
151
- },
152
- {
153
- name: 'gpg',
154
- cat: 'Crypto',
155
- summary: 'PGP decrypt / symmetric decrypt',
156
- ex: [['gpg -d secret.gpg', 'decrypt (prompts for passphrase/key)']],
157
- },
158
- {
159
- name: 'base64',
160
- aliases: ['base32'],
161
- cat: 'Data',
162
- summary: 'decode/encode base64 (base32 alike)',
163
- ex: [
164
- ['echo -n SGk= | base64 -d', 'decode'],
165
- ['base64 -d f.txt', 'decode a file blob'],
166
- ],
167
- },
168
- // ── Network ──────────────────────────────────────────────────────
169
- {
170
- name: 'nmap',
171
- cat: 'Networking',
172
- summary: 'port/service scan a target host',
173
- ex: [
174
- ['nmap -sV -p- host', 'all ports + version detection'],
175
- ['nmap -sC -p 80,443 host', 'default scripts on web ports'],
176
- ],
177
- },
178
- {
179
- name: 'nc',
180
- aliases: ['netcat', 'ncat'],
181
- cat: 'Networking',
182
- summary: 'raw TCP/UDP connect (talk to a service)',
183
- ex: [
184
- ['nc host 1337', 'connect to a challenge service'],
185
- ['nc -lvnp 4444', 'listen (catch a callback)'],
186
- ],
187
- },
188
- {
189
- name: 'socat',
190
- cat: 'Networking',
191
- summary: 'flexible socket relay (TLS, pty, bridging)',
192
- ex: [['socat - OPENSSL:host:443', 'connect over TLS interactively']],
193
- },
194
- {
195
- name: 'curl',
196
- cat: 'Web',
197
- summary: 'HTTP client — the web-CTF workhorse',
198
- ex: [
199
- ['curl -i url', 'show response headers'],
200
- ['curl -b "session=..." url', 'send a cookie'],
201
- ['curl -X POST -d "a=1" url', 'POST form data'],
202
- ],
203
- },
204
- {
205
- name: 'wget',
206
- cat: 'Web',
207
- summary: 'download files / mirror a site',
208
- ex: [['wget -r url', 'recursive download']],
209
- },
210
- {
211
- name: 'tshark',
212
- aliases: ['wireshark-cli'],
213
- cat: 'Networking',
214
- summary: 'CLI Wireshark — read/filter pcaps',
215
- ex: [
216
- ['tshark -r cap.pcap -Y http', 'show only HTTP packets'],
217
- ['tshark -r cap.pcap -T fields -e data', 'dump a field across packets'],
218
- ],
219
- },
220
- {
221
- name: 'tcpdump',
222
- cat: 'Networking',
223
- summary: 'capture/read packets (pcap)',
224
- ex: [['tcpdump -r cap.pcap -A', 'print packets as ASCII']],
225
- },
226
- {
227
- name: 'dig',
228
- aliases: ['whois'],
229
- cat: 'Networking',
230
- summary: 'DNS lookups (whois = registration data)',
231
- ex: [
232
- ['dig TXT domain', 'read TXT records (flags hide here)'],
233
- ['whois domain', 'ownership info'],
234
- ],
235
- },
236
- {
237
- name: 'sqlmap',
238
- cat: 'Web',
239
- summary: 'automated SQL-injection exploitation',
240
- ex: [
241
- ['sqlmap -u "url?id=1" --dbs', 'enumerate databases'],
242
- ['sqlmap -u url --dump -T users', 'dump a table'],
243
- ],
244
- },
245
- // ── Reverse engineering / binary ─────────────────────────────────
246
- {
247
- name: 'radare2',
248
- aliases: ['r2'],
249
- cat: 'Reverse Engineering',
250
- summary: 'interactive RE framework (disasm/debug)',
251
- ex: [
252
- ['r2 -A bin', 'open + auto-analyze'],
253
- ['(in r2) afl ; pdf @ main', 'list funcs; disasm main'],
254
- ],
255
- },
256
- {
257
- name: 'rabin2',
258
- cat: 'Reverse Engineering',
259
- summary: 'binary info (radare2 family)',
260
- ex: [
261
- ['rabin2 -z bin', 'list strings + offsets'],
262
- ['rabin2 -I bin', 'arch/bits/protections'],
263
- ],
264
- },
265
- {
266
- name: 'objdump',
267
- cat: 'Reverse Engineering',
268
- summary: 'disassemble / inspect object files',
269
- ex: [['objdump -d -M intel bin', 'Intel-syntax disassembly']],
270
- },
271
- {
272
- name: 'readelf',
273
- cat: 'Reverse Engineering',
274
- summary: 'ELF header / sections / symbols',
275
- ex: [
276
- ['readelf -a bin', 'everything'],
277
- ['readelf -d bin', 'dynamic section / RPATH'],
278
- ],
279
- },
280
- {
281
- name: 'gdb',
282
- aliases: ['pwndbg'],
283
- cat: 'Debuggers',
284
- summary: 'debugger (pwndbg plugin preloaded for pwn)',
285
- ex: [
286
- ['gdb ./bin', 'start; then: break main / run / info registers'],
287
- ['(pwndbg) checksec', 'show binary mitigations'],
288
- ['(pwndbg) cyclic 200', 'gen pattern to find offset'],
289
- ],
290
- },
291
- {
292
- name: 'ltrace',
293
- aliases: ['strace'],
294
- cat: 'Debuggers',
295
- summary: 'trace library (ltrace) / syscalls (strace)',
296
- ex: [
297
- ['ltrace ./bin', 'watch libc calls (e.g. strcmp the flag)'],
298
- ['strace ./bin', 'watch syscalls'],
299
- ],
300
- },
301
- {
302
- name: 'upx',
303
- cat: 'Reverse Engineering',
304
- summary: 'un/pack executables',
305
- ex: [['upx -d packed', 'unpack before analyzing']],
306
- },
307
- // ── Pwn ──────────────────────────────────────────────────────────
308
- {
309
- name: 'pwntools',
310
- aliases: ['pwn'],
311
- cat: 'CTF Core',
312
- summary: 'Python exploit framework (+ pwn CLI)',
313
- ex: [
314
- ['pwn checksec ./bin', 'show mitigations'],
315
- ['pwn cyclic 100 / pwn cyclic -l 0x6161', 'pattern + find offset'],
316
- ['python3: from pwn import *; p=process("./bin")', 'scripting entrypoint'],
317
- ],
318
- },
319
- {
320
- name: 'ROPgadget',
321
- aliases: ['ropper'],
322
- cat: 'Binary & RE',
323
- summary: 'find ROP gadgets in a binary',
324
- ex: [
325
- ['ROPgadget --binary bin | grep "pop rdi"', 'find a gadget'],
326
- ['ropper -f bin --search "pop rdi"', 'ropper equivalent'],
327
- ],
328
- },
329
- // ── Data wrangling ───────────────────────────────────────────────
330
- {
331
- name: 'jq',
332
- cat: 'Data',
333
- summary: 'slice/query JSON',
334
- ex: [
335
- ['curl url | jq .', 'pretty-print'],
336
- ['jq -r ".data[].flag" f.json', 'extract a field'],
337
- ],
338
- },
339
- {
340
- name: 'sqlite3',
341
- cat: 'Data',
342
- summary: 'open/query a SQLite DB file',
343
- ex: [
344
- ['sqlite3 app.db ".tables"', 'list tables'],
345
- ['sqlite3 app.db "select * from users"', 'query'],
346
- ],
347
- },
348
- ];
349
- // alias / name → doc
350
- const INDEX = new Map();
351
- for (const d of TOOL_DOCS) {
352
- INDEX.set(d.name.toLowerCase(), d);
353
- for (const a of d.aliases ?? [])
354
- INDEX.set(a.toLowerCase(), d);
355
- }
356
- function printOne(d) {
357
- const alias = d.aliases?.length ? chalk.gray(' (' + d.aliases.join(', ') + ')') : '';
358
- console.log();
359
- console.log(' ' + chalk.bold.cyan(d.name) + alias + chalk.gray(' · ' + d.cat));
360
- console.log(' ' + chalk.white(d.summary));
361
- console.log();
362
- for (const [cmd, note] of d.ex) {
363
- console.log(' ' + chalk.cyan(cmd));
364
- console.log(' ' + chalk.gray(note));
365
- }
366
- console.log();
367
- console.log(chalk.gray(' More: ') + chalk.cyan('man tools') + chalk.gray(' · not a real judge — verify your own output.'));
368
- console.log();
369
- }
370
- function printIndex() {
371
- const byCat = new Map();
372
- for (const d of TOOL_DOCS) {
373
- const k = d.cat;
374
- if (!byCat.has(k))
375
- byCat.set(k, []);
376
- byCat.get(k).push(d);
377
- }
378
- console.log();
379
- console.log(chalk.bold.yellow(' ★ Tool cheatsheet') +
380
- chalk.gray(' — type ') +
381
- chalk.cyan('man <tool>') +
382
- chalk.gray(' for usage, e.g. ') +
383
- chalk.cyan('man steghide'));
384
- console.log();
385
- for (const [cat, list] of byCat) {
386
- console.log(' ' + chalk.bold.white(cat));
387
- console.log(' ' + chalk.cyan(list.map((d) => d.name).join(' ')));
388
- }
389
- console.log();
390
- console.log(chalk.gray(' These are the frozen sandbox tools — no pip/apt install. ') +
391
- chalk.cyan('env') +
392
- chalk.gray(' shows what is installed.'));
393
- console.log();
394
- }
395
- /** Render `man <query>`: empty/"tools"/"list" → index; a tool → its sheet; else nearest hints. */
396
- export function renderToolMan(query) {
397
- const q = (query || '').trim().toLowerCase();
398
- if (!q || q === 'tools' || q === 'list' || q === 'all') {
399
- printIndex();
400
- return;
401
- }
402
- const hit = INDEX.get(q);
403
- if (hit) {
404
- printOne(hit);
405
- return;
406
- }
407
- // fuzzy: prefix then substring on names+aliases
408
- const keys = [...INDEX.keys()];
409
- const near = keys.filter((k) => k.startsWith(q)).concat(keys.filter((k) => k.includes(q) && !k.startsWith(q)));
410
- const uniq = [...new Set(near.map((k) => INDEX.get(k).name))].slice(0, 5);
411
- console.log();
412
- console.log(chalk.yellow(` No cheatsheet for "${query}".`));
413
- if (uniq.length) {
414
- console.log(chalk.gray(' Did you mean: ') + chalk.cyan(uniq.join(' ')) + '?');
415
- }
416
- console.log(chalk.gray(' Type ') + chalk.cyan('man tools') + chalk.gray(' for the full list.'));
417
- console.log();
418
- }
1
+ import chalk from"chalk";export const TOOL_DOCS=[{name:"file",cat:"Forensics",summary:"identify a file type (always step 1)",ex:[["file mystery","what is this really? (extension lies)"]]},{name:"strings",cat:"Forensics",summary:"pull printable text out of any binary",ex:[["strings -n 8 bin","runs ≥8 chars (cuts noise)"],["strings bin | grep -i flag","hunt the flag directly"],["strings -e l bin","16-bit little-endian (wide) strings"]]},{name:"xxd",cat:"Data",summary:"hex dump / reverse hex back to bytes",ex:[["xxd file | head","inspect magic bytes / header"],["xxd -r hex.txt out.bin","rebuild a file from a hex dump"],["xxd -s 0x100 -l 64 file","dump 64 bytes from offset 0x100"]]},{name:"hexdump",aliases:["od"],cat:"Data",summary:"byte/octal dump (od is the BSD cousin)",ex:[["hexdump -C file | head","canonical hex+ASCII view"],["od -An -tx1 file","raw hex bytes, no offsets"]]},{name:"binwalk",cat:"Forensics",summary:"find + extract files embedded in files",ex:[["binwalk file","scan for embedded signatures"],["binwalk -e file","auto-extract everything it finds"],['binwalk --dd=".*" file',"carve every match (greedy)"]]},{name:"foremost",cat:"Forensics",summary:"carve files by header (recover deleted/hidden)",ex:[["foremost -i disk.img -o out/","recover into out/ by type"]]},{name:"steghide",cat:"Forensics",summary:"hide/extract data in JPG/BMP/WAV/AU (passphrase)",ex:[["steghide info cat.jpg","is anything embedded?"],["steghide extract -sf cat.jpg","pull it out (asks passphrase; blank often works)"]]},{name:"exiftool",cat:"Forensics",summary:"read/write image & doc metadata (flags hide here)",ex:[["exiftool pic.png","dump all metadata"],["exiftool -Comment pic.png","one specific tag"]]},{name:"pngcheck",cat:"Forensics",summary:"validate PNG chunks (find tampering / appended data)",ex:[["pngcheck -v flag.png","verbose chunk-by-chunk check"]]},{name:"pdftotext",cat:"Data",summary:"extract text (and hidden layers) from a PDF",ex:[["pdftotext doc.pdf -","print extracted text to stdout"]]},{name:"sleuthkit",aliases:["mmls","fls","icat","tsk"],cat:"Forensics",summary:"disk-image forensics (partition → files → recover)",ex:[["mmls disk.img","list partitions + offsets"],["fls -o 2048 -r disk.img","list files (use the partition offset)"],["icat -o 2048 disk.img 12 > out","dump file by inode number"]]},{name:"volatility3",aliases:["vol","volatility"],cat:"Forensics",summary:"memory-dump analysis",ex:[["vol -f mem.raw windows.pslist","list processes"],["vol -f mem.raw windows.cmdline","recover command lines"]]},{name:"openssl",cat:"Crypto",summary:"swiss-army crypto (decode, hash, enc/dec, certs)",ex:[["openssl enc -d -aes-256-cbc -in f -k pass","decrypt AES"],["openssl x509 -in cert.pem -text -noout","read a certificate"],["echo -n txt | openssl dgst -sha256","hash it"]]},{name:"john",aliases:["john-the-ripper"],cat:"Crypto",summary:"crack password hashes (CPU)",ex:[["john --wordlist=rockyou.txt hashes","dictionary attack"],["zip2john x.zip > h ; john h","crack a zip password"],["john --show hashes","print cracked results"]]},{name:"hashcat",cat:"Crypto",summary:"fast hash cracker (modes by -m)",ex:[["hashcat -m 0 -a 0 hash rockyou.txt","MD5 dictionary (-m 0 = MD5)"]]},{name:"gpg",cat:"Crypto",summary:"PGP decrypt / symmetric decrypt",ex:[["gpg -d secret.gpg","decrypt (prompts for passphrase/key)"]]},{name:"base64",aliases:["base32"],cat:"Data",summary:"decode/encode base64 (base32 alike)",ex:[["echo -n SGk= | base64 -d","decode"],["base64 -d f.txt","decode a file blob"]]},{name:"nmap",cat:"Networking",summary:"port/service scan a target host",ex:[["nmap -sV -p- host","all ports + version detection"],["nmap -sC -p 80,443 host","default scripts on web ports"]]},{name:"nc",aliases:["netcat","ncat"],cat:"Networking",summary:"raw TCP/UDP connect (talk to a service)",ex:[["nc host 1337","connect to a challenge service"],["nc -lvnp 4444","listen (catch a callback)"]]},{name:"socat",cat:"Networking",summary:"flexible socket relay (TLS, pty, bridging)",ex:[["socat - OPENSSL:host:443","connect over TLS interactively"]]},{name:"curl",cat:"Web",summary:"HTTP client — the web-CTF workhorse",ex:[["curl -i url","show response headers"],['curl -b "session=..." url',"send a cookie"],['curl -X POST -d "a=1" url',"POST form data"]]},{name:"wget",cat:"Web",summary:"download files / mirror a site",ex:[["wget -r url","recursive download"]]},{name:"tshark",aliases:["wireshark-cli"],cat:"Networking",summary:"CLI Wireshark — read/filter pcaps",ex:[["tshark -r cap.pcap -Y http","show only HTTP packets"],["tshark -r cap.pcap -T fields -e data","dump a field across packets"]]},{name:"tcpdump",cat:"Networking",summary:"capture/read packets (pcap)",ex:[["tcpdump -r cap.pcap -A","print packets as ASCII"]]},{name:"dig",aliases:["whois"],cat:"Networking",summary:"DNS lookups (whois = registration data)",ex:[["dig TXT domain","read TXT records (flags hide here)"],["whois domain","ownership info"]]},{name:"sqlmap",cat:"Web",summary:"automated SQL-injection exploitation",ex:[['sqlmap -u "url?id=1" --dbs',"enumerate databases"],["sqlmap -u url --dump -T users","dump a table"]]},{name:"radare2",aliases:["r2"],cat:"Reverse Engineering",summary:"interactive RE framework (disasm/debug)",ex:[["r2 -A bin","open + auto-analyze"],["(in r2) afl ; pdf @ main","list funcs; disasm main"]]},{name:"rabin2",cat:"Reverse Engineering",summary:"binary info (radare2 family)",ex:[["rabin2 -z bin","list strings + offsets"],["rabin2 -I bin","arch/bits/protections"]]},{name:"objdump",cat:"Reverse Engineering",summary:"disassemble / inspect object files",ex:[["objdump -d -M intel bin","Intel-syntax disassembly"]]},{name:"readelf",cat:"Reverse Engineering",summary:"ELF header / sections / symbols",ex:[["readelf -a bin","everything"],["readelf -d bin","dynamic section / RPATH"]]},{name:"gdb",aliases:["pwndbg"],cat:"Debuggers",summary:"debugger (pwndbg plugin preloaded for pwn)",ex:[["gdb ./bin","start; then: break main / run / info registers"],["(pwndbg) checksec","show binary mitigations"],["(pwndbg) cyclic 200","gen pattern to find offset"]]},{name:"ltrace",aliases:["strace"],cat:"Debuggers",summary:"trace library (ltrace) / syscalls (strace)",ex:[["ltrace ./bin","watch libc calls (e.g. strcmp the flag)"],["strace ./bin","watch syscalls"]]},{name:"upx",cat:"Reverse Engineering",summary:"un/pack executables",ex:[["upx -d packed","unpack before analyzing"]]},{name:"pwntools",aliases:["pwn"],cat:"CTF Core",summary:"Python exploit framework (+ pwn CLI)",ex:[["pwn checksec ./bin","show mitigations"],["pwn cyclic 100 / pwn cyclic -l 0x6161","pattern + find offset"],['python3: from pwn import *; p=process("./bin")',"scripting entrypoint"]]},{name:"ROPgadget",aliases:["ropper"],cat:"Binary & RE",summary:"find ROP gadgets in a binary",ex:[['ROPgadget --binary bin | grep "pop rdi"',"find a gadget"],['ropper -f bin --search "pop rdi"',"ropper equivalent"]]},{name:"jq",cat:"Data",summary:"slice/query JSON",ex:[["curl url | jq .","pretty-print"],['jq -r ".data[].flag" f.json',"extract a field"]]},{name:"sqlite3",cat:"Data",summary:"open/query a SQLite DB file",ex:[['sqlite3 app.db ".tables"',"list tables"],['sqlite3 app.db "select * from users"',"query"]]}];const e=new Map;for(const a of TOOL_DOCS){e.set(a.name.toLowerCase(),a);for(const s of a.aliases??[])e.set(s.toLowerCase(),a)}export function renderToolMan(a){const s=(a||"").trim().toLowerCase();if(!s||"tools"===s||"list"===s||"all"===s)return void function(){const e=new Map;for(const a of TOOL_DOCS){const s=a.cat;e.has(s)||e.set(s,[]),e.get(s).push(a)}console.log(),console.log(chalk.bold.yellow(" ★ Tool cheatsheet")+chalk.gray(" — type ")+chalk.cyan("man <tool>")+chalk.gray(" for usage, e.g. ")+chalk.cyan("man steghide")),console.log();for(const[a,s]of e)console.log(" "+chalk.bold.white(a)),console.log(" "+chalk.cyan(s.map(e=>e.name).join(" ")));console.log(),console.log(chalk.gray(" These are the frozen sandbox tools — no pip/apt install. ")+chalk.cyan("env")+chalk.gray(" shows what is installed.")),console.log()}();const t=e.get(s);if(t)return void function(e){const a=e.aliases?.length?chalk.gray(" ("+e.aliases.join(", ")+")"):"";console.log(),console.log(" "+chalk.bold.cyan(e.name)+a+chalk.gray(" · "+e.cat)),console.log(" "+chalk.white(e.summary)),console.log();for(const[a,s]of e.ex)console.log(" "+chalk.cyan(a)),console.log(" "+chalk.gray(s));console.log(),console.log(chalk.gray(" More: ")+chalk.cyan("man tools")+chalk.gray(" · not a real judge — verify your own output.")),console.log()}(t);const n=[...e.keys()],o=n.filter(e=>e.startsWith(s)).concat(n.filter(e=>e.includes(s)&&!e.startsWith(s))),r=[...new Set(o.map(a=>e.get(a).name))].slice(0,5);console.log(),console.log(chalk.yellow(` No cheatsheet for "${a}".`)),r.length&&console.log(chalk.gray(" Did you mean: ")+chalk.cyan(r.join(" "))+"?"),console.log(chalk.gray(" Type ")+chalk.cyan("man tools")+chalk.gray(" for the full list.")),console.log()}
@@ -1,48 +1 @@
1
- import { createHash } from 'node:crypto';
2
- import { readFileSync, writeFileSync } from 'node:fs';
3
- import { join } from 'node:path';
4
- import { platform } from 'node:os';
5
- import { getIcoaDir } from './config.js';
6
- // T3 — environment fingerprint (FORENSIC, not a gate).
7
- //
8
- // Hashes the {name=version} of the sanctioned toolset detected on this host.
9
- // Reported at exam start and stored server-side so a reviewer can compare
10
- // environments across students (and spot a session whose env fingerprint
11
- // changed). Honest limits: this proves which sanctioned tools/versions are
12
- // PRESENT — it does NOT prove the ABSENCE of forbidden tools, and the expected
13
- // value differs per platform (mac/ubuntu/WSL ship different versions). So it is
14
- // a comparison fingerprint, never a pass/fail check. See icoa2026event/07.
15
- const CACHE = () => join(getIcoaDir(), 'toolset-hash.json');
16
- /** Compute the hash over (name=version) pairs, persist it, return the hash.
17
- * Called once at the end of `env setup` so the exam-start path only READS. */
18
- export function computeAndCacheToolsetHash(entries) {
19
- const norm = entries
20
- .map((e) => `${e.name}=${(e.version || '').trim()}`)
21
- .sort()
22
- .join('\n');
23
- const hash = createHash('sha256').update(norm).digest('hex').slice(0, 16);
24
- const rec = {
25
- hash,
26
- platform: platform(),
27
- count: entries.length,
28
- computedAt: new Date().toISOString(),
29
- };
30
- try {
31
- writeFileSync(CACHE(), JSON.stringify(rec));
32
- }
33
- catch {
34
- /* best-effort — a missing cache just means no env fingerprint is reported */
35
- }
36
- return hash;
37
- }
38
- /** Read the cached env-fingerprint hash. Returns undefined if `env setup` has
39
- * not run (so the exam start path never blocks or spawns 30 version probes). */
40
- export function getCachedToolsetHash() {
41
- try {
42
- const rec = JSON.parse(readFileSync(CACHE(), 'utf-8'));
43
- return typeof rec.hash === 'string' && rec.hash ? rec.hash : undefined;
44
- }
45
- catch {
46
- return undefined;
47
- }
48
- }
1
+ import{createHash as t}from"node:crypto";import{readFileSync as o,writeFileSync as r}from"node:fs";import{join as e}from"node:path";import{platform as n}from"node:os";import{getIcoaDir as s}from"./config.js";const h=()=>e(s(),"toolset-hash.json");export function computeAndCacheToolsetHash(o){const e=o.map(t=>`${t.name}=${(t.version||"").trim()}`).sort().join("\n"),s=t("sha256").update(e).digest("hex").slice(0,16),a={hash:s,platform:n(),count:o.length,computedAt:(new Date).toISOString()};try{r(h(),JSON.stringify(a))}catch{}return s}export function getCachedToolsetHash(){try{const t=JSON.parse(o(h(),"utf-8"));return"string"==typeof t.hash&&t.hash?t.hash:void 0}catch{return}}
@@ -1,80 +1 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
- import { join, dirname } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import { getIcoaDir } from './config.js';
5
- import { translateText } from './gemini.js';
6
- import { ensureLangCache } from './translations-fetcher.js';
7
- const __dirname = dirname(fileURLToPath(import.meta.url));
8
- // Bundled translations shipped with the package (translations/<lang>/<id>.json)
9
- function getBundledPath(lang, challengeId) {
10
- return join(__dirname, '..', '..', 'translations', lang, `${challengeId}.json`);
11
- }
12
- // User-local cache (~/.icoa/translations/)
13
- function getCachePath(lang, challengeId) {
14
- const dir = join(getIcoaDir(), 'translations', lang);
15
- mkdirSync(dir, { recursive: true });
16
- return join(dir, `${challengeId}.json`);
17
- }
18
- export async function getTranslation(text, challengeId, targetLang) {
19
- if (targetLang === 'en')
20
- return text;
21
- // 1. Check bundled static translations — only present in dev (npm-link) trees;
22
- // v2.19.197+ no longer ships translations/ in the npm package.
23
- const bundledPath = getBundledPath(targetLang, challengeId);
24
- if (existsSync(bundledPath)) {
25
- try {
26
- const bundled = JSON.parse(readFileSync(bundledPath, 'utf-8'));
27
- if (bundled.translation)
28
- return bundled.translation;
29
- }
30
- catch {
31
- /* corrupt, fall through */
32
- }
33
- }
34
- // 2. Safety net: if this language hasn't been fetched yet (e.g. lang was set
35
- // by country auto-detect rather than the `lang` command), pull the pack
36
- // once. No-op when already cached.
37
- await ensureLangCache(targetLang, { silent: false });
38
- // 3. Check user-local cache (populated by step 2 or earlier `lang` calls)
39
- const cachePath = getCachePath(targetLang, challengeId);
40
- if (existsSync(cachePath)) {
41
- try {
42
- const cached = JSON.parse(readFileSync(cachePath, 'utf-8'));
43
- if (cached.translation)
44
- return cached.translation;
45
- }
46
- catch {
47
- /* corrupt, fall through */
48
- }
49
- }
50
- // 4. Translate via Gemini API (last resort — covers untranslated tail content)
51
- const translation = await translateText(text, getLangName(targetLang));
52
- writeFileSync(cachePath, JSON.stringify({ original: text, translation, timestamp: new Date().toISOString() }));
53
- return translation;
54
- }
55
- function getLangName(code) {
56
- const names = {
57
- en: 'English',
58
- zh: 'Chinese (Simplified)',
59
- ja: 'Japanese',
60
- ko: 'Korean',
61
- es: 'Spanish',
62
- ar: 'Arabic',
63
- fr: 'French',
64
- pt: 'Portuguese (Brazilian)',
65
- ru: 'Russian',
66
- hi: 'Hindi',
67
- de: 'German',
68
- id: 'Indonesian',
69
- th: 'Thai',
70
- vi: 'Vietnamese',
71
- tr: 'Turkish',
72
- uk: 'Ukrainian',
73
- ht: 'Haitian Creole',
74
- bn: 'Bengali',
75
- kk: 'Kazakh',
76
- uz: 'Uzbek',
77
- lo: 'Lao',
78
- };
79
- return names[code] || 'English';
80
- }
1
+ import{existsSync as n,mkdirSync as t,readFileSync as i,writeFileSync as r}from"node:fs";import{join as a,dirname as o}from"node:path";import{fileURLToPath as e}from"node:url";import{getIcoaDir as s}from"./config.js";import{translateText as f}from"./gemini.js";import{ensureLangCache as u}from"./translations-fetcher.js";const c=o(e(import.meta.url));export async function getTranslation(o,e,l){if("en"===l)return o;const m=function(n,t){return a(c,"..","..","translations",n,`${t}.json`)}(l,e);if(n(m))try{const n=JSON.parse(i(m,"utf-8"));if(n.translation)return n.translation}catch{}await u(l,{silent:!1});const h=function(n,i){const r=a(s(),"translations",n);return t(r,{recursive:!0}),a(r,`${i}.json`)}(l,e);if(n(h))try{const n=JSON.parse(i(h,"utf-8"));if(n.translation)return n.translation}catch{}const p=await f(o,(g=l,{en:"English",zh:"Chinese (Simplified)",ja:"Japanese",ko:"Korean",es:"Spanish",ar:"Arabic",fr:"French",pt:"Portuguese (Brazilian)",ru:"Russian",hi:"Hindi",de:"German",id:"Indonesian",th:"Thai",vi:"Vietnamese",tr:"Turkish",uk:"Ukrainian",ht:"Haitian Creole",bn:"Bengali",kk:"Kazakh",uz:"Uzbek",lo:"Lao"}[g]||"English"));var g;return r(h,JSON.stringify({original:o,translation:p,timestamp:(new Date).toISOString()})),p}