ctf-agent 1.2.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.
- package/AGENTS.md +131 -0
- package/Dockerfile +192 -0
- package/README.md +522 -0
- package/agents/ctf-analyzer.md +58 -0
- package/agents/ctf-controller.md +151 -0
- package/agents/ctf-speedrun.md +74 -0
- package/bin/ctf-agent.js +119 -0
- package/docker-compose.yml +13 -0
- package/mcp_config.json +19 -0
- package/package.json +44 -0
- package/references/ctf-triage-ladder.md +68 -0
- package/references/exploit-databases.md +575 -0
- package/references/llm-safety-and-policy-compliance.md +173 -0
- package/references/multi-agent-orchestration-and-policy-routing.md +278 -0
- package/references/security-events-and-intelligence.md +566 -0
- package/references/version-matrix.md +59 -0
- package/rules/ctf-execution-rules.md +44 -0
- package/rules/ctf-safety-framing-rules.md +140 -0
- package/rules/flag-validation-rules.md +25 -0
- package/scripts/__init__.py +1 -0
- package/scripts/ctf-tools.lock +205 -0
- package/scripts/ctf_init.py +1026 -0
- package/scripts/ctf_update.py +583 -0
- package/scripts/ctfd_client.py +81 -0
- package/scripts/cve_lookup.py +453 -0
- package/scripts/extract_flags.py +155 -0
- package/scripts/install_as_agent.py +278 -0
- package/scripts/install_ctf_tools.sh +603 -0
- package/scripts/parallel_triage.py +395 -0
- package/scripts/prompt_policy_sanitizer.py +283 -0
- package/scripts/scope_guard.py +444 -0
- package/scripts/skill_validator.py +504 -0
- package/scripts/workspace_cleaner.py +141 -0
- package/skills/ctf-ai-ml/SKILL.md +120 -0
- package/skills/ctf-ai-ml/adversarial-ml.md +623 -0
- package/skills/ctf-ai-ml/llm-attacks.md +487 -0
- package/skills/ctf-ai-ml/model-attacks.md +422 -0
- package/skills/ctf-crypto/SKILL.md +320 -0
- package/skills/ctf-crypto/advanced-math.md +798 -0
- package/skills/ctf-crypto/classic-ciphers.md +651 -0
- package/skills/ctf-crypto/ecc-attacks.md +347 -0
- package/skills/ctf-crypto/exotic-crypto-2.md +380 -0
- package/skills/ctf-crypto/exotic-crypto.md +528 -0
- package/skills/ctf-crypto/historical.md +113 -0
- package/skills/ctf-crypto/lattice-and-lwe.md +524 -0
- package/skills/ctf-crypto/modern-ciphers-2.md +563 -0
- package/skills/ctf-crypto/modern-ciphers-3.md +453 -0
- package/skills/ctf-crypto/modern-ciphers.md +649 -0
- package/skills/ctf-crypto/prng-attacks.md +257 -0
- package/skills/ctf-crypto/prng.md +664 -0
- package/skills/ctf-crypto/rsa-attacks-2.md +792 -0
- package/skills/ctf-crypto/rsa-attacks.md +487 -0
- package/skills/ctf-crypto/stream-ciphers.md +390 -0
- package/skills/ctf-crypto/zkp-and-advanced.md +456 -0
- package/skills/ctf-forensics/3d-printing.md +121 -0
- package/skills/ctf-forensics/SKILL.md +379 -0
- package/skills/ctf-forensics/disk-advanced.md +497 -0
- package/skills/ctf-forensics/disk-and-memory.md +491 -0
- package/skills/ctf-forensics/disk-recovery.md +699 -0
- package/skills/ctf-forensics/linux-forensics.md +511 -0
- package/skills/ctf-forensics/network-advanced.md +583 -0
- package/skills/ctf-forensics/network.md +645 -0
- package/skills/ctf-forensics/peripheral-capture.md +287 -0
- package/skills/ctf-forensics/signals-and-hardware.md +713 -0
- package/skills/ctf-forensics/steganography.md +694 -0
- package/skills/ctf-forensics/stego-advanced-2.md +475 -0
- package/skills/ctf-forensics/stego-advanced.md +481 -0
- package/skills/ctf-forensics/stego-image.md +691 -0
- package/skills/ctf-forensics/windows.md +625 -0
- package/skills/ctf-malware/SKILL.md +181 -0
- package/skills/ctf-malware/c2-and-protocols.md +274 -0
- package/skills/ctf-malware/pe-and-dotnet.md +108 -0
- package/skills/ctf-malware/scripts-and-obfuscation.md +449 -0
- package/skills/ctf-misc/SKILL.md +498 -0
- package/skills/ctf-misc/bashjails.md +323 -0
- package/skills/ctf-misc/ctfd-navigation.md +465 -0
- package/skills/ctf-misc/dns.md +255 -0
- package/skills/ctf-misc/encodings-advanced.md +504 -0
- package/skills/ctf-misc/encodings.md +431 -0
- package/skills/ctf-misc/games-and-vms-2.md +254 -0
- package/skills/ctf-misc/games-and-vms-3.md +690 -0
- package/skills/ctf-misc/games-and-vms-4.md +229 -0
- package/skills/ctf-misc/games-and-vms.md +529 -0
- package/skills/ctf-misc/linux-privesc.md +333 -0
- package/skills/ctf-misc/pyjails.md +671 -0
- package/skills/ctf-misc/rf-sdr.md +91 -0
- package/skills/ctf-osint/SKILL.md +198 -0
- package/skills/ctf-osint/geolocation-and-media.md +464 -0
- package/skills/ctf-osint/social-media.md +312 -0
- package/skills/ctf-osint/web-and-dns.md +341 -0
- package/skills/ctf-pwn/SKILL.md +214 -0
- package/skills/ctf-pwn/advanced-exploits-2.md +579 -0
- package/skills/ctf-pwn/advanced-exploits-3.md +598 -0
- package/skills/ctf-pwn/advanced-exploits-4.md +590 -0
- package/skills/ctf-pwn/advanced-exploits-5.md +119 -0
- package/skills/ctf-pwn/advanced-exploits.md +773 -0
- package/skills/ctf-pwn/advanced.md +326 -0
- package/skills/ctf-pwn/field-notes.md +245 -0
- package/skills/ctf-pwn/format-string.md +694 -0
- package/skills/ctf-pwn/heap-fsop.md +285 -0
- package/skills/ctf-pwn/heap-techniques-2.md +333 -0
- package/skills/ctf-pwn/heap-techniques.md +513 -0
- package/skills/ctf-pwn/kernel-bypass.md +421 -0
- package/skills/ctf-pwn/kernel-techniques.md +366 -0
- package/skills/ctf-pwn/kernel.md +636 -0
- package/skills/ctf-pwn/overflow-basics.md +611 -0
- package/skills/ctf-pwn/rop-advanced.md +725 -0
- package/skills/ctf-pwn/rop-and-shellcode.md +659 -0
- package/skills/ctf-pwn/sandbox-escape.md +313 -0
- package/skills/ctf-reverse/SKILL.md +163 -0
- package/skills/ctf-reverse/anti-analysis-ctf.md +204 -0
- package/skills/ctf-reverse/anti-analysis.md +693 -0
- package/skills/ctf-reverse/field-notes.md +376 -0
- package/skills/ctf-reverse/languages-compiled.md +666 -0
- package/skills/ctf-reverse/languages-platforms.md +592 -0
- package/skills/ctf-reverse/languages.md +553 -0
- package/skills/ctf-reverse/patterns-ctf-2.md +397 -0
- package/skills/ctf-reverse/patterns-ctf-3.md +797 -0
- package/skills/ctf-reverse/patterns-ctf.md +670 -0
- package/skills/ctf-reverse/patterns-runtime.md +274 -0
- package/skills/ctf-reverse/patterns.md +572 -0
- package/skills/ctf-reverse/platforms-hardware.md +387 -0
- package/skills/ctf-reverse/platforms.md +664 -0
- package/skills/ctf-reverse/tools-advanced-2.md +421 -0
- package/skills/ctf-reverse/tools-advanced.md +407 -0
- package/skills/ctf-reverse/tools-dynamic.md +679 -0
- package/skills/ctf-reverse/tools-emulation.md +319 -0
- package/skills/ctf-reverse/tools.md +573 -0
- package/skills/ctf-web/SKILL.md +153 -0
- package/skills/ctf-web/auth-and-access-2.md +82 -0
- package/skills/ctf-web/auth-and-access.md +783 -0
- package/skills/ctf-web/auth-infra.md +321 -0
- package/skills/ctf-web/auth-jwt.md +186 -0
- package/skills/ctf-web/client-side-advanced.md +739 -0
- package/skills/ctf-web/client-side.md +529 -0
- package/skills/ctf-web/cves.md +373 -0
- package/skills/ctf-web/field-notes.md +482 -0
- package/skills/ctf-web/node-and-prototype.md +200 -0
- package/skills/ctf-web/server-side-2.md +337 -0
- package/skills/ctf-web/server-side-advanced-2.md +559 -0
- package/skills/ctf-web/server-side-advanced-3.md +125 -0
- package/skills/ctf-web/server-side-advanced-4.md +480 -0
- package/skills/ctf-web/server-side-advanced.md +378 -0
- package/skills/ctf-web/server-side-deser.md +443 -0
- package/skills/ctf-web/server-side-exec-2.md +799 -0
- package/skills/ctf-web/server-side-exec.md +457 -0
- package/skills/ctf-web/server-side.md +629 -0
- package/skills/ctf-web/sql-injection.md +790 -0
- package/skills/ctf-web/web3.md +374 -0
- package/skills/ctf-writeup/SKILL.md +90 -0
- package/skills/solve-challenge/SKILL.md +269 -0
- package/skills.json +16 -0
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# shellcheck disable=SC2024 # redirects target user-owned log file, not sudo
|
|
3
|
+
# ===========================================================================
|
|
4
|
+
# CTF Security Workstation Installer & Profile Manager (v2.0)
|
|
5
|
+
# Modular 3-Tier Architecture: Core, Category Profiles & Heavy Research
|
|
6
|
+
# ===========================================================================
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# bash scripts/install_ctf_tools.sh [OPTIONS] [PROFILE]
|
|
10
|
+
#
|
|
11
|
+
# Available Profiles:
|
|
12
|
+
# core - Tier 1: Essential system utilities, compilers, network tools, and base libraries
|
|
13
|
+
# pwn - Tier 2: Binary exploitation, ROP generation, seccomp bypass, and heap tooling
|
|
14
|
+
# rev - Tier 2: Reverse engineering, disassemblers, decompilers, and symbolics
|
|
15
|
+
# crypto - Tier 2: Cryptanalysis, lattice reduction, number theory, and solvers
|
|
16
|
+
# forensics - Tier 2: Digital forensics, memory analysis, PCAP carvers, and file parsing
|
|
17
|
+
# stego - Tier 2: Steganography, bit-plane inspection, and audio/image recovery
|
|
18
|
+
# web - Tier 2: Modern web exploitation, fuzzers, JWT, HTTP/2/3, and crawler pipelines
|
|
19
|
+
# mobile - Tier 2: Android & iOS reverse engineering, APK patching, and dynamic instrumentation
|
|
20
|
+
# cloud - Tier 2: Container security, Kubernetes inspection, and cloud metadata testing
|
|
21
|
+
# ad - Tier 2: Active Directory, Kerberos, SMB, and Windows penetration testing
|
|
22
|
+
# kernel - Tier 2: Linux kernel exploitation, eBPF inspection, debugging, and tracing
|
|
23
|
+
# hardware - Tier 2: IoT firmware, serial debugging, JTAG, and multi-architecture cross tools
|
|
24
|
+
# web3 - Tier 2: Smart contract auditing, EVM interaction, and bytecode analysis
|
|
25
|
+
# wasm - Tier 2: WebAssembly binary analysis, disassembling, and execution
|
|
26
|
+
# ai - Tier 2: Machine learning security, adversarial models, and safetensors analysis
|
|
27
|
+
# all - Install all category profiles sequentially
|
|
28
|
+
#
|
|
29
|
+
# Legacy Modes (Backwards Compatibility):
|
|
30
|
+
# python, apt, brew, gems, go, manual
|
|
31
|
+
#
|
|
32
|
+
# Options:
|
|
33
|
+
# --list-profiles Show available profiles, descriptions, and package breakdowns
|
|
34
|
+
# --verify [PROF] Verify tool availability and exact Python package versions
|
|
35
|
+
# --dry-run Preview packages and commands without executing installations
|
|
36
|
+
# --force Reinstall packages even if already present
|
|
37
|
+
# --update Upgrade packages to match locked definitions
|
|
38
|
+
#
|
|
39
|
+
# Examples:
|
|
40
|
+
# bash scripts/install_ctf_tools.sh core
|
|
41
|
+
# bash scripts/install_ctf_tools.sh web
|
|
42
|
+
# bash scripts/install_ctf_tools.sh --verify pwn
|
|
43
|
+
# bash scripts/install_ctf_tools.sh --list-profiles
|
|
44
|
+
# bash scripts/install_ctf_tools.sh all
|
|
45
|
+
|
|
46
|
+
set -euo pipefail
|
|
47
|
+
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
# Globals & Configuration
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
53
|
+
LOCKFILE="${SCRIPT_DIR}/ctf-tools.lock"
|
|
54
|
+
LOG_DIR="${HOME}/.ctf-tools"
|
|
55
|
+
CTF_VENV="${HOME}/.ctf-tools/venv"
|
|
56
|
+
|
|
57
|
+
DRY_RUN=false
|
|
58
|
+
FORCE=false
|
|
59
|
+
UPDATE=false
|
|
60
|
+
MODE=""
|
|
61
|
+
TARGET_PROFILE=""
|
|
62
|
+
LOG_FILE=""
|
|
63
|
+
|
|
64
|
+
FAILED=()
|
|
65
|
+
SUCCEEDED=()
|
|
66
|
+
SKIPPED=()
|
|
67
|
+
|
|
68
|
+
ALL_PROFILES=(
|
|
69
|
+
"core" "pwn" "rev" "crypto" "forensics" "stego"
|
|
70
|
+
"web" "mobile" "cloud" "ad" "kernel" "hardware"
|
|
71
|
+
"web3" "wasm" "ai"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
# Logging & Output Helpers
|
|
76
|
+
# ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
setup_logging() {
|
|
79
|
+
mkdir -p "$LOG_DIR"
|
|
80
|
+
LOG_FILE="${LOG_DIR}/install-$(date +%Y-%m-%d_%H%M%S).log"
|
|
81
|
+
log_info "Session log: $LOG_FILE"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
log_info() { echo "==> $*" | tee -a "${LOG_FILE:-/dev/null}"; }
|
|
85
|
+
log_warn() { echo "WARNING: $*" | tee -a "${LOG_FILE:-/dev/null}" >&2; }
|
|
86
|
+
log_error() { echo "ERROR: $*" | tee -a "${LOG_FILE:-/dev/null}" >&2; }
|
|
87
|
+
log_detail() { echo " $*" >> "${LOG_FILE:-/dev/null}"; }
|
|
88
|
+
|
|
89
|
+
require_cmd() {
|
|
90
|
+
local cmd="$1"
|
|
91
|
+
if ! command -v "$cmd" >/dev/null 2>&1; then
|
|
92
|
+
log_error "'$cmd' is required but not found in PATH"
|
|
93
|
+
return 1
|
|
94
|
+
fi
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
# Version & Installation Verification Checks
|
|
99
|
+
# ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
# Check if exact Python package version matches requested version via importlib.metadata
|
|
102
|
+
py_pkg_version_matches() {
|
|
103
|
+
local pkg_name="$1"
|
|
104
|
+
local req_ver="$2"
|
|
105
|
+
|
|
106
|
+
python3 -c "
|
|
107
|
+
import sys
|
|
108
|
+
try:
|
|
109
|
+
import importlib.metadata as m
|
|
110
|
+
v = m.version('$pkg_name')
|
|
111
|
+
req = '$req_ver'.strip()
|
|
112
|
+
if not req or v == req:
|
|
113
|
+
sys.exit(0)
|
|
114
|
+
sys.exit(1)
|
|
115
|
+
except Exception:
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
" 2>/dev/null
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# Check if an apt package is installed
|
|
121
|
+
apt_pkg_installed() {
|
|
122
|
+
dpkg -s "$1" >/dev/null 2>&1
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# Check if a Homebrew formula is installed
|
|
126
|
+
brew_pkg_installed() {
|
|
127
|
+
brew list --formula "$1" >/dev/null 2>&1
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
# Check if a Ruby gem is installed
|
|
131
|
+
gem_installed() {
|
|
132
|
+
local gem_name="$1"
|
|
133
|
+
local gem_ver="${2:-}"
|
|
134
|
+
if [ -n "$gem_ver" ]; then
|
|
135
|
+
gem list -i "^${gem_name}$" -v "$gem_ver" >/dev/null 2>&1
|
|
136
|
+
else
|
|
137
|
+
gem list -i "^${gem_name}$" >/dev/null 2>&1
|
|
138
|
+
fi
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
# ---------------------------------------------------------------------------
|
|
142
|
+
# Lockfile Query Engine
|
|
143
|
+
# ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
query_lockfile() {
|
|
146
|
+
local profile="$1"
|
|
147
|
+
local query_type="$2" # apt, pip, gem, go, verify_bins, manual, description
|
|
148
|
+
|
|
149
|
+
if [ ! -f "$LOCKFILE" ]; then
|
|
150
|
+
log_warn "Lockfile not found at $LOCKFILE, operating with internal fallbacks."
|
|
151
|
+
return 1
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
python3 -c "
|
|
155
|
+
import json, sys
|
|
156
|
+
try:
|
|
157
|
+
with open('$LOCKFILE', 'r') as f:
|
|
158
|
+
data = json.load(f)
|
|
159
|
+
prof = data.get('profiles', {}).get('$profile', {})
|
|
160
|
+
qtype = '$query_type'
|
|
161
|
+
|
|
162
|
+
if qtype == 'description':
|
|
163
|
+
print(prof.get('description', 'No description available.'))
|
|
164
|
+
elif qtype == 'apt':
|
|
165
|
+
for pkg in prof.get('apt', []):
|
|
166
|
+
print(pkg)
|
|
167
|
+
elif qtype == 'pip':
|
|
168
|
+
pips = prof.get('pip', {})
|
|
169
|
+
for name, meta in pips.items():
|
|
170
|
+
ver = meta.get('version', '')
|
|
171
|
+
imp = meta.get('import', name)
|
|
172
|
+
if ver:
|
|
173
|
+
print(f'{name}=={ver}:{imp}')
|
|
174
|
+
else:
|
|
175
|
+
print(f'{name}:{imp}')
|
|
176
|
+
elif qtype == 'gem':
|
|
177
|
+
for g in prof.get('gem', []):
|
|
178
|
+
print(f\"{g.get('name')}:{g.get('version', '')}\")
|
|
179
|
+
elif qtype == 'go':
|
|
180
|
+
for g in prof.get('go', []):
|
|
181
|
+
print(f\"{g.get('package')}@{g.get('version')}:{g.get('bin')}\")
|
|
182
|
+
elif qtype == 'verify_bins':
|
|
183
|
+
for b in prof.get('verify_bins', []):
|
|
184
|
+
print(b)
|
|
185
|
+
elif qtype == 'manual':
|
|
186
|
+
for m in prof.get('manual', []):
|
|
187
|
+
print(m)
|
|
188
|
+
except Exception as e:
|
|
189
|
+
sys.exit(1)
|
|
190
|
+
" 2>/dev/null
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
# ---------------------------------------------------------------------------
|
|
194
|
+
# Dedicated Virtualenv Setup (PEP 668 Compliant)
|
|
195
|
+
# ---------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
ensure_virtualenv() {
|
|
198
|
+
require_cmd python3 || return 1
|
|
199
|
+
|
|
200
|
+
if [ -n "${VIRTUAL_ENV:-}" ]; then
|
|
201
|
+
return 0
|
|
202
|
+
fi
|
|
203
|
+
|
|
204
|
+
if [ ! -d "$CTF_VENV" ]; then
|
|
205
|
+
if [ "$DRY_RUN" = true ]; then
|
|
206
|
+
log_info "Would create dedicated virtualenv at $CTF_VENV"
|
|
207
|
+
return 0
|
|
208
|
+
fi
|
|
209
|
+
log_info "Creating dedicated CTF virtualenv at $CTF_VENV"
|
|
210
|
+
python3 -m venv "$CTF_VENV" >>"${LOG_FILE:-/dev/null}" 2>&1 || {
|
|
211
|
+
log_warn "venv creation failed, defaulting to active Python runtime"
|
|
212
|
+
return 0
|
|
213
|
+
}
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
if [ -f "$CTF_VENV/bin/activate" ]; then
|
|
217
|
+
# shellcheck disable=SC1091
|
|
218
|
+
source "$CTF_VENV/bin/activate"
|
|
219
|
+
log_info "Active virtualenv: $CTF_VENV"
|
|
220
|
+
fi
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
# ---------------------------------------------------------------------------
|
|
224
|
+
# Installation Primitives
|
|
225
|
+
# ---------------------------------------------------------------------------
|
|
226
|
+
|
|
227
|
+
install_apt_packages() {
|
|
228
|
+
local packages=("$@")
|
|
229
|
+
if [ ${#packages[@]} -eq 0 ]; then return 0; fi
|
|
230
|
+
if ! command -v apt-get >/dev/null 2>&1; then
|
|
231
|
+
log_warn "apt-get not available; skipping APT packages"
|
|
232
|
+
return 0
|
|
233
|
+
fi
|
|
234
|
+
|
|
235
|
+
local to_install=()
|
|
236
|
+
for pkg in "${packages[@]}"; do
|
|
237
|
+
if [ "$FORCE" = false ] && apt_pkg_installed "$pkg"; then
|
|
238
|
+
SKIPPED+=("apt:$pkg")
|
|
239
|
+
continue
|
|
240
|
+
fi
|
|
241
|
+
to_install+=("$pkg")
|
|
242
|
+
done
|
|
243
|
+
|
|
244
|
+
if [ ${#to_install[@]} -eq 0 ]; then
|
|
245
|
+
log_info "APT: All ${#packages[@]} packages already installed"
|
|
246
|
+
return 0
|
|
247
|
+
fi
|
|
248
|
+
|
|
249
|
+
log_info "APT: Installing ${#to_install[@]} package(s): ${to_install[*]}"
|
|
250
|
+
if [ "$DRY_RUN" = true ]; then return 0; fi
|
|
251
|
+
|
|
252
|
+
sudo DEBIAN_FRONTEND=noninteractive apt-get update -q >>"${LOG_FILE:-/dev/null}" 2>&1 || true
|
|
253
|
+
for pkg in "${to_install[@]}"; do
|
|
254
|
+
if sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -q "$pkg" >>"${LOG_FILE:-/dev/null}" 2>&1; then
|
|
255
|
+
SUCCEEDED+=("apt:$pkg")
|
|
256
|
+
else
|
|
257
|
+
log_warn "APT installation failed: $pkg"
|
|
258
|
+
FAILED+=("apt:$pkg")
|
|
259
|
+
fi
|
|
260
|
+
done
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
install_pip_packages() {
|
|
264
|
+
local entries=("$@")
|
|
265
|
+
if [ ${#entries[@]} -eq 0 ]; then return 0; fi
|
|
266
|
+
ensure_virtualenv
|
|
267
|
+
|
|
268
|
+
local to_install=()
|
|
269
|
+
local to_install_names=()
|
|
270
|
+
|
|
271
|
+
for entry in "${entries[@]}"; do
|
|
272
|
+
local spec="${entry%%:*}"
|
|
273
|
+
local name="${spec%%==*}"
|
|
274
|
+
local ver="${spec#*==}"
|
|
275
|
+
if [ "$ver" = "$spec" ]; then ver=""; fi
|
|
276
|
+
|
|
277
|
+
if [ "$FORCE" = false ] && [ "$UPDATE" = false ] && py_pkg_version_matches "$name" "$ver"; then
|
|
278
|
+
SKIPPED+=("pip:$name")
|
|
279
|
+
continue
|
|
280
|
+
fi
|
|
281
|
+
|
|
282
|
+
to_install+=("$spec")
|
|
283
|
+
to_install_names+=("$name")
|
|
284
|
+
done
|
|
285
|
+
|
|
286
|
+
if [ ${#to_install[@]} -eq 0 ]; then
|
|
287
|
+
log_info "Pip: All ${#entries[@]} package(s) up-to-date and matching locked versions"
|
|
288
|
+
return 0
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
log_info "Pip: Installing/Updating ${#to_install[@]} package(s): ${to_install_names[*]}"
|
|
292
|
+
if [ "$DRY_RUN" = true ]; then return 0; fi
|
|
293
|
+
|
|
294
|
+
python3 -m pip install --upgrade pip setuptools wheel >>"${LOG_FILE:-/dev/null}" 2>&1 || true
|
|
295
|
+
|
|
296
|
+
for spec in "${to_install[@]}"; do
|
|
297
|
+
local name="${spec%%==*}"
|
|
298
|
+
if python3 -m pip install "$spec" >>"${LOG_FILE:-/dev/null}" 2>&1; then
|
|
299
|
+
SUCCEEDED+=("pip:$name")
|
|
300
|
+
else
|
|
301
|
+
log_warn "Pip installation failed: $spec"
|
|
302
|
+
FAILED+=("pip:$name")
|
|
303
|
+
fi
|
|
304
|
+
done
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
install_gem_packages() {
|
|
308
|
+
local entries=("$@")
|
|
309
|
+
if [ ${#entries[@]} -eq 0 ]; then return 0; fi
|
|
310
|
+
if ! command -v gem >/dev/null 2>&1; then
|
|
311
|
+
log_warn "gem command not found; skipping Ruby gems"
|
|
312
|
+
for e in "${entries[@]}"; do SKIPPED+=("gem:${e%%:*}"); done
|
|
313
|
+
return 0
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
for entry in "${entries[@]}"; do
|
|
317
|
+
local name="${entry%%:*}"
|
|
318
|
+
local ver="${entry##*:}"
|
|
319
|
+
|
|
320
|
+
if [ "$FORCE" = false ] && gem_installed "$name" "$ver"; then
|
|
321
|
+
SKIPPED+=("gem:$name")
|
|
322
|
+
continue
|
|
323
|
+
fi
|
|
324
|
+
|
|
325
|
+
log_info "Gem: Installing $name ($ver)"
|
|
326
|
+
if [ "$DRY_RUN" = true ]; then continue; fi
|
|
327
|
+
|
|
328
|
+
local cmd=()
|
|
329
|
+
if [ "$(id -u)" -ne 0 ] && command -v sudo >/dev/null 2>&1; then
|
|
330
|
+
cmd+=(sudo)
|
|
331
|
+
fi
|
|
332
|
+
cmd+=(gem install "$name" --no-document)
|
|
333
|
+
if [ -n "$ver" ]; then cmd+=(-v "$ver"); fi
|
|
334
|
+
|
|
335
|
+
if "${cmd[@]}" >>"${LOG_FILE:-/dev/null}" 2>&1; then
|
|
336
|
+
SUCCEEDED+=("gem:$name")
|
|
337
|
+
else
|
|
338
|
+
log_warn "Gem installation failed: $name"
|
|
339
|
+
FAILED+=("gem:$name")
|
|
340
|
+
fi
|
|
341
|
+
done
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
install_go_packages() {
|
|
345
|
+
local entries=("$@")
|
|
346
|
+
if [ ${#entries[@]} -eq 0 ]; then return 0; fi
|
|
347
|
+
if ! command -v go >/dev/null 2>&1; then
|
|
348
|
+
log_warn "go command not found; skipping Go binaries"
|
|
349
|
+
for e in "${entries[@]}"; do SKIPPED+=("go:${e##*:}"); done
|
|
350
|
+
return 0
|
|
351
|
+
fi
|
|
352
|
+
|
|
353
|
+
for entry in "${entries[@]}"; do
|
|
354
|
+
local spec="${entry%%:*}"
|
|
355
|
+
local bin="${entry##*:}"
|
|
356
|
+
|
|
357
|
+
if [ "$FORCE" = false ] && command -v "$bin" >/dev/null 2>&1; then
|
|
358
|
+
SKIPPED+=("go:$bin")
|
|
359
|
+
continue
|
|
360
|
+
fi
|
|
361
|
+
|
|
362
|
+
log_info "Go: Installing $bin from $spec"
|
|
363
|
+
if [ "$DRY_RUN" = true ]; then continue; fi
|
|
364
|
+
|
|
365
|
+
if go install "$spec" >>"${LOG_FILE:-/dev/null}" 2>&1; then
|
|
366
|
+
local gopath_bin
|
|
367
|
+
gopath_bin="$(go env GOPATH 2>/dev/null || echo "$HOME/go")/bin"
|
|
368
|
+
if [ -f "$gopath_bin/$bin" ]; then
|
|
369
|
+
if [ "$(id -u)" -eq 0 ]; then
|
|
370
|
+
cp -f "$gopath_bin/$bin" /usr/local/bin/ 2>/dev/null || true
|
|
371
|
+
elif command -v sudo >/dev/null 2>&1; then
|
|
372
|
+
sudo cp -f "$gopath_bin/$bin" /usr/local/bin/ 2>/dev/null || true
|
|
373
|
+
fi
|
|
374
|
+
fi
|
|
375
|
+
SUCCEEDED+=("go:$bin")
|
|
376
|
+
else
|
|
377
|
+
log_warn "Go installation failed: $bin"
|
|
378
|
+
FAILED+=("go:$bin")
|
|
379
|
+
fi
|
|
380
|
+
done
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
# ---------------------------------------------------------------------------
|
|
384
|
+
# Profile Dispatcher
|
|
385
|
+
# ---------------------------------------------------------------------------
|
|
386
|
+
|
|
387
|
+
install_profile() {
|
|
388
|
+
local profile="$1"
|
|
389
|
+
log_info "Processing Profile: [$profile]"
|
|
390
|
+
|
|
391
|
+
local desc
|
|
392
|
+
desc="$(query_lockfile "$profile" "description" || echo "")"
|
|
393
|
+
if [ -n "$desc" ]; then
|
|
394
|
+
log_info "Description: $desc"
|
|
395
|
+
fi
|
|
396
|
+
|
|
397
|
+
# 1. APT packages
|
|
398
|
+
mapfile -t apt_pkgs < <(query_lockfile "$profile" "apt" || true)
|
|
399
|
+
if [ ${#apt_pkgs[@]} -gt 0 ]; then
|
|
400
|
+
install_apt_packages "${apt_pkgs[@]}"
|
|
401
|
+
fi
|
|
402
|
+
|
|
403
|
+
# 2. Pip packages
|
|
404
|
+
mapfile -t pip_pkgs < <(query_lockfile "$profile" "pip" || true)
|
|
405
|
+
if [ ${#pip_pkgs[@]} -gt 0 ]; then
|
|
406
|
+
install_pip_packages "${pip_pkgs[@]}"
|
|
407
|
+
fi
|
|
408
|
+
|
|
409
|
+
# 3. Ruby Gems
|
|
410
|
+
mapfile -t gem_pkgs < <(query_lockfile "$profile" "gem" || true)
|
|
411
|
+
if [ ${#gem_pkgs[@]} -gt 0 ]; then
|
|
412
|
+
install_gem_packages "${gem_pkgs[@]}"
|
|
413
|
+
fi
|
|
414
|
+
|
|
415
|
+
# 4. Go binaries
|
|
416
|
+
mapfile -t go_pkgs < <(query_lockfile "$profile" "go" || true)
|
|
417
|
+
if [ ${#go_pkgs[@]} -gt 0 ]; then
|
|
418
|
+
install_go_packages "${go_pkgs[@]}"
|
|
419
|
+
fi
|
|
420
|
+
|
|
421
|
+
# 5. Manual Tools Advisories
|
|
422
|
+
mapfile -t manual_notes < <(query_lockfile "$profile" "manual" || true)
|
|
423
|
+
if [ ${#manual_notes[@]} -gt 0 ]; then
|
|
424
|
+
echo ""
|
|
425
|
+
echo " [i] Note for profile [$profile] - Additional tools available:"
|
|
426
|
+
for m in "${manual_notes[@]}"; do
|
|
427
|
+
echo " * $m"
|
|
428
|
+
done
|
|
429
|
+
echo ""
|
|
430
|
+
fi
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
# ---------------------------------------------------------------------------
|
|
434
|
+
# Verification Engine
|
|
435
|
+
# ---------------------------------------------------------------------------
|
|
436
|
+
|
|
437
|
+
verify_profile() {
|
|
438
|
+
local profile="$1"
|
|
439
|
+
echo "======================================================================"
|
|
440
|
+
echo "[*] VERIFYING PROFILE: [$profile]"
|
|
441
|
+
echo "======================================================================"
|
|
442
|
+
|
|
443
|
+
ensure_virtualenv 2>/dev/null || true
|
|
444
|
+
|
|
445
|
+
local found_bins=()
|
|
446
|
+
local missing_bins=()
|
|
447
|
+
local found_py=()
|
|
448
|
+
local missing_py=()
|
|
449
|
+
|
|
450
|
+
# 1. Check binaries
|
|
451
|
+
mapfile -t bins < <(query_lockfile "$profile" "verify_bins" || true)
|
|
452
|
+
for b in "${bins[@]}"; do
|
|
453
|
+
[ -z "$b" ] && continue
|
|
454
|
+
if command -v "$b" >/dev/null 2>&1; then
|
|
455
|
+
found_bins+=("$b")
|
|
456
|
+
else
|
|
457
|
+
missing_bins+=("$b")
|
|
458
|
+
fi
|
|
459
|
+
done
|
|
460
|
+
|
|
461
|
+
# 2. Check Pip Packages with exact version validation
|
|
462
|
+
mapfile -t pips < <(query_lockfile "$profile" "pip" || true)
|
|
463
|
+
for p in "${pips[@]}"; do
|
|
464
|
+
[ -z "$p" ] && continue
|
|
465
|
+
local spec="${p%%:*}"
|
|
466
|
+
local name="${spec%%==*}"
|
|
467
|
+
local ver="${spec#*==}"
|
|
468
|
+
if [ "$ver" = "$spec" ]; then ver=""; fi
|
|
469
|
+
|
|
470
|
+
if py_pkg_version_matches "$name" "$ver"; then
|
|
471
|
+
found_py+=("$name ($ver)")
|
|
472
|
+
else
|
|
473
|
+
missing_py+=("$name ($ver)")
|
|
474
|
+
fi
|
|
475
|
+
done
|
|
476
|
+
|
|
477
|
+
echo " - CLI Binaries Verified: ${#found_bins[@]}/${#bins[@]}"
|
|
478
|
+
if [ ${#missing_bins[@]} -gt 0 ]; then
|
|
479
|
+
echo " Missing Binaries: ${missing_bins[*]}"
|
|
480
|
+
fi
|
|
481
|
+
|
|
482
|
+
echo " - Python Packages Verified: ${#found_py[@]}/${#pips[@]}"
|
|
483
|
+
if [ ${#missing_py[@]} -gt 0 ]; then
|
|
484
|
+
echo " Missing/Version Mismatch: ${missing_py[*]}"
|
|
485
|
+
fi
|
|
486
|
+
echo ""
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
verify_all() {
|
|
490
|
+
local target="${1:-all}"
|
|
491
|
+
if [ "$target" = "all" ]; then
|
|
492
|
+
for p in "${ALL_PROFILES[@]}"; do
|
|
493
|
+
verify_profile "$p"
|
|
494
|
+
done
|
|
495
|
+
else
|
|
496
|
+
verify_profile "$target"
|
|
497
|
+
fi
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
# ---------------------------------------------------------------------------
|
|
501
|
+
# List Profiles
|
|
502
|
+
# ---------------------------------------------------------------------------
|
|
503
|
+
|
|
504
|
+
list_profiles() {
|
|
505
|
+
echo "======================================================================"
|
|
506
|
+
echo " CTF Security Workstation - Available Profiles (v2.0)"
|
|
507
|
+
echo "======================================================================"
|
|
508
|
+
for p in "${ALL_PROFILES[@]}"; do
|
|
509
|
+
local desc
|
|
510
|
+
desc="$(query_lockfile "$p" "description" || echo "")"
|
|
511
|
+
printf " %-12s : %s\n" "$p" "$desc"
|
|
512
|
+
done
|
|
513
|
+
printf " %-12s : %s\n" "all" "Install all 15 category profiles sequentially"
|
|
514
|
+
echo "======================================================================"
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
# ---------------------------------------------------------------------------
|
|
518
|
+
# Execution Summary
|
|
519
|
+
# ---------------------------------------------------------------------------
|
|
520
|
+
|
|
521
|
+
print_summary() {
|
|
522
|
+
echo ""
|
|
523
|
+
echo "========================================"
|
|
524
|
+
echo " Installation Summary"
|
|
525
|
+
echo "========================================"
|
|
526
|
+
echo " Installed: ${#SUCCEEDED[@]}"
|
|
527
|
+
echo " Skipped: ${#SKIPPED[@]} (already up to date)"
|
|
528
|
+
echo " Failed: ${#FAILED[@]}"
|
|
529
|
+
if [ ${#FAILED[@]} -gt 0 ]; then
|
|
530
|
+
echo ""
|
|
531
|
+
echo " Failed Packages:"
|
|
532
|
+
for f in "${FAILED[@]}"; do
|
|
533
|
+
echo " - $f"
|
|
534
|
+
done
|
|
535
|
+
fi
|
|
536
|
+
echo "========================================"
|
|
537
|
+
if [ -n "${LOG_FILE:-}" ]; then
|
|
538
|
+
echo " Session Log: $LOG_FILE"
|
|
539
|
+
echo "========================================"
|
|
540
|
+
fi
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
# ---------------------------------------------------------------------------
|
|
544
|
+
# CLI Argument Parser & Entry Point
|
|
545
|
+
# ---------------------------------------------------------------------------
|
|
546
|
+
|
|
547
|
+
main() {
|
|
548
|
+
local positional=()
|
|
549
|
+
|
|
550
|
+
while [ $# -gt 0 ]; do
|
|
551
|
+
case "$1" in
|
|
552
|
+
--dry-run) DRY_RUN=true; shift ;;
|
|
553
|
+
--force) FORCE=true; shift ;;
|
|
554
|
+
--update) UPDATE=true; shift ;;
|
|
555
|
+
--list-profiles) list_profiles; exit 0 ;;
|
|
556
|
+
--verify) MODE="verify"; shift ;;
|
|
557
|
+
-*) echo "Unknown option: $1" >&2; exit 2 ;;
|
|
558
|
+
*) positional+=("$1"); shift ;;
|
|
559
|
+
esac
|
|
560
|
+
done
|
|
561
|
+
|
|
562
|
+
TARGET_PROFILE="${positional[0]:-}"
|
|
563
|
+
|
|
564
|
+
# Verification Mode
|
|
565
|
+
if [ "$MODE" = "verify" ]; then
|
|
566
|
+
verify_all "${TARGET_PROFILE:-all}"
|
|
567
|
+
exit 0
|
|
568
|
+
fi
|
|
569
|
+
|
|
570
|
+
# Default to 'all' if no positional profile was supplied
|
|
571
|
+
TARGET_PROFILE="${TARGET_PROFILE:-all}"
|
|
572
|
+
|
|
573
|
+
# Backwards compatibility with legacy mode strings
|
|
574
|
+
case "$TARGET_PROFILE" in
|
|
575
|
+
python) TARGET_PROFILE="core" ;;
|
|
576
|
+
apt) TARGET_PROFILE="core" ;;
|
|
577
|
+
brew) TARGET_PROFILE="core" ;;
|
|
578
|
+
gems) TARGET_PROFILE="pwn" ;;
|
|
579
|
+
go) TARGET_PROFILE="web" ;;
|
|
580
|
+
manual) TARGET_PROFILE="rev" ;;
|
|
581
|
+
esac
|
|
582
|
+
|
|
583
|
+
if [ "$DRY_RUN" = false ]; then
|
|
584
|
+
setup_logging
|
|
585
|
+
fi
|
|
586
|
+
|
|
587
|
+
if [ "$TARGET_PROFILE" = "all" ]; then
|
|
588
|
+
log_info "Initiating Full Workstation Installation Across All Profiles"
|
|
589
|
+
for p in "${ALL_PROFILES[@]}"; do
|
|
590
|
+
install_profile "$p"
|
|
591
|
+
done
|
|
592
|
+
else
|
|
593
|
+
install_profile "$TARGET_PROFILE"
|
|
594
|
+
fi
|
|
595
|
+
|
|
596
|
+
print_summary
|
|
597
|
+
|
|
598
|
+
if [ ${#FAILED[@]} -gt 0 ]; then
|
|
599
|
+
exit 1
|
|
600
|
+
fi
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
main "$@"
|