Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ repos:
name: shellcheck
entry: shellcheck -x
language: system
files: '^(bin/dotsec|bin/dotsec-build|lib/.*\.sh|tests/integration-smoke\.sh|tests/stubs/.*|\.github/scripts/.*\.sh)$'
files: '^(bin/dotsec|bin/dotsec-build|lib/.*\.sh|tests/integration-smoke\.sh|tests/stubs/.*|\.github/scripts/.*\.sh|exegol/my-resources/bin/[^/]+|exegol/my-resources/deploy\.sh)$'
- id: bats
name: bats
entry: bats tests/
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ test: ## Run bats tests
@bats tests/

lint: ## shellcheck all bash (uses .shellcheckrc)
@shellcheck -x bin/dotsec bin/dotsec-build lib/*.sh tests/integration-smoke.sh && echo "[+] shellcheck clean"
@shellcheck -x bin/dotsec bin/dotsec-build lib/*.sh tests/integration-smoke.sh exegol/my-resources/bin/* exegol/my-resources/deploy.sh && echo "[+] shellcheck clean"

smoke: ## Docker integration smoke (requires docker + make build)
@bash tests/integration-smoke.sh
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,22 +130,28 @@ The project ships a `my-resources` bundle deployed (merged) to `~/.exegol/my-res
via `make exegol-setup` (also run by `make install`).

The bundle includes:
- **bin/** scripts: `recon-subs`, `recon-alive`, `recon-crawl`, `recon-loot`, `recon-sourcemaps`, `recon-full`, `dl`
- **recon** scripts: `recon-subs`, `recon-alive`, `recon-fingerprint`, `recon-portscan`, `recon-screenshot`, `recon-crawl`, `recon-urls`, `recon-loot`, `recon-extract`, `recon-sourcemaps`, `recon-full`, `dl`
- **scan** scripts: `scan-nuclei` (vuln scan), `scan-takeover` (dangling CNAME; subzy → nuclei fallback)
- **audit** scripts: `audit-code` (trufflehog + gitleaks + semgrep + osv-scanner over the `code/` zone)
- Shell aliases and preloaded history
- `load_user_setup.sh`: idempotent installer for the tools the scripts need that the base image lacks (xnLinkFinder, waymore, sourcemapper, osv-scanner, …)

```bash
make exegol-setup # deploy/merge bundle to ~/.exegol/my-resources/
```

Scripts run **inside** the Exegol container, driven by engagement env vars (`$DOMAIN`, `$WORKSPACE`).
Example in a loaded engagement window:
Typical flow in a loaded engagement window:

```bash
recon-full
recon-full # discovery → portscan → screenshots → crawl → loot → JS extract
scan-nuclei # vulnerability scan of the alive hosts (routed through the proxy)
scan-takeover # subdomain takeover check
audit-code # white-box audit of recovered source / sourcemaps
```

On first container start, Exegol auto-runs `/opt/my-resources/setup/load_user_setup.sh`.
To trigger it manually:
To trigger it manually (also installs missing tooling):

```bash
dotsec exegol setup
Expand Down
45 changes: 45 additions & 0 deletions exegol/my-resources/bin/audit-code
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -euo pipefail
WS="${WORKSPACE:-$PWD}"
CODE="${1:-$WS/code}"
OUTD="$WS/scans/code"
mkdir -p "$OUTD"

# White-box audit of recovered source / sourcemaps / beautified JS / loot.
[[ -d "$CODE" ]] || { echo "[!] no dir: $CODE"; exit 1; }
[[ -n "$(find "$CODE" -type f -print -quit 2>/dev/null)" ]] \
|| { echo "[!] $CODE is empty — run recon-sourcemaps / recon-loot first"; exit 1; }
echo "[*] auditing $CODE"

# 1. Verified secret scan (trufflehog tests creds against their live APIs).
if command -v trufflehog >/dev/null 2>&1; then
trufflehog filesystem "$CODE" --results=verified,unknown --json \
> "$OUTD/secrets_trufflehog.json" 2>/dev/null || true
echo "[+] trufflehog → $OUTD/secrets_trufflehog.json"
fi

# 2. Regex secret scan (gitleaks). Pick the subcommand by capability: `dir`
# on v8.19+, `detect` on older builds. A non-zero exit just means leaks found.
if command -v gitleaks >/dev/null 2>&1; then
if gitleaks dir --help >/dev/null 2>&1; then
gitleaks dir "$CODE" -r "$OUTD/secrets_gitleaks.json" >/dev/null 2>&1 || true
else
gitleaks detect --no-git --source "$CODE" -r "$OUTD/secrets_gitleaks.json" >/dev/null 2>&1 || true
fi
echo "[+] gitleaks → $OUTD/secrets_gitleaks.json"
fi

# 3. SAST (semgrep, registry "auto" ruleset).
if command -v semgrep >/dev/null 2>&1; then
semgrep scan --config auto --json -o "$OUTD/sast_semgrep.json" "$CODE" >/dev/null 2>&1 || true
echo "[+] semgrep → $OUTD/sast_semgrep.json"
fi

# 4. SCA / vulnerable dependencies (osv-scanner v2 over any lockfile/manifest).
# Exit 1 = vulns found (or no lockfiles present), so swallow it.
if command -v osv-scanner >/dev/null 2>&1; then
osv-scanner scan source --recursive "$CODE" --format json --output "$OUTD/sca_osv.json" >/dev/null 2>&1 || true
echo "[+] osv-scanner → $OUTD/sca_osv.json"
fi

echo "[+] audit done → $OUTD"
5 changes: 4 additions & 1 deletion exegol/my-resources/bin/recon-alive
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ OUT="${WORKSPACE:-$PWD}/recon"
mkdir -p "$OUT"

[[ -s "$OUT/subdomains.txt" ]] || { echo "[!] run recon-subs first"; exit 1; }
# httpx binary: Exegol ships it as `httpx`, Kali as `httpx-toolkit`.
HTTPX="$(command -v httpx-toolkit || command -v httpx || true)"
[[ -n "$HTTPX" ]] || { echo "[!] projectdiscovery httpx not found"; exit 1; }
echo "[*] httpx alive check"
httpx-toolkit -l "$OUT/subdomains.txt" -mc 200 -ports 443,80,8080,8000,8888 -threads 200 2>/dev/null \
"$HTTPX" -l "$OUT/subdomains.txt" -mc 200 -ports 443,80,8080,8000,8888 -threads 200 2>/dev/null \
| sort -u > "$OUT/subdomains_alive.txt"
echo "[+] $(wc -l < "$OUT/subdomains_alive.txt") alive → $OUT/subdomains_alive.txt"
5 changes: 4 additions & 1 deletion exegol/my-resources/bin/recon-fingerprint
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ OUT="${WORKSPACE:-$PWD}/recon"; mkdir -p "$OUT"

# Technology fingerprint of the alive hosts.
[[ -s "$OUT/subdomains_alive.txt" ]] || { echo "[!] run recon-alive first"; exit 1; }
# httpx binary: Exegol ships it as `httpx`, Kali as `httpx-toolkit`.
HTTPX="$(command -v httpx-toolkit || command -v httpx || true)"
[[ -n "$HTTPX" ]] || { echo "[!] projectdiscovery httpx not found"; exit 1; }
echo "[*] tech fingerprint"
httpx-toolkit -l "$OUT/subdomains_alive.txt" -td -silent 2>/dev/null > "$OUT/tech.txt" || true
"$HTTPX" -l "$OUT/subdomains_alive.txt" -td -silent 2>/dev/null > "$OUT/tech.txt" || true
if command -v whatweb >/dev/null 2>&1; then whatweb -i "$OUT/subdomains_alive.txt" --no-errors >> "$OUT/tech.txt" 2>/dev/null || true; fi
echo "[+] tech → $OUT/tech.txt"
15 changes: 10 additions & 5 deletions exegol/my-resources/bin/recon-full
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ OUT="${WORKSPACE:-$PWD}/recon"
mkdir -p "$OUT"

echo "[*] full recon pipeline for $DOMAIN"
# Core discovery steps: if these fail the rest has no input, so let them abort.
recon-subs
recon-alive
# Enrichment + scanning steps: best-effort, a single failure must not kill the run.
recon-fingerprint || echo "[i] fingerprint step skipped"
recon-crawl
recon-urls || echo "[i] urls step skipped"
recon-loot || echo "[i] loot step skipped"
recon-extract || echo "[i] extract step skipped"
recon-sourcemaps || echo "[i] sourcemaps step skipped"
recon-portscan || echo "[i] portscan step skipped"
recon-screenshot || echo "[i] screenshot step skipped"
recon-crawl || echo "[i] crawl step skipped"
recon-urls || echo "[i] urls step skipped"
recon-loot || echo "[i] loot step skipped"
recon-extract || echo "[i] extract step skipped"
recon-sourcemaps || echo "[i] sourcemaps step skipped"
echo "[+] done → $OUT"
echo "[i] next: scan-nuclei · scan-takeover · audit-code"
27 changes: 27 additions & 0 deletions exegol/my-resources/bin/recon-portscan
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
: "${DOMAIN:?load an engagement first (DOMAIN unset) — use: dl <target>}"
OUT="${WORKSPACE:-$PWD}/recon"
PORTS="${WORKSPACE:-$PWD}/scans/ports"
mkdir -p "$PORTS"

# Port scan the alive hosts (fallback: the raw subdomain list).
command -v naabu >/dev/null 2>&1 || { echo "[!] naabu not found"; exit 1; }
src="$OUT/subdomains_alive.txt"
[[ -s "$src" ]] || src="$OUT/subdomains.txt"
[[ -s "$src" ]] || { echo "[!] run recon-subs / recon-alive first"; exit 1; }

# naabu wants bare hosts: strip scheme, path and any :port suffix.
sed -E 's#^[a-z]+://##; s#/.*$##; s#:[0-9]+$##' "$src" | sort -u > "$PORTS/hosts.txt"
echo "[*] naabu port scan ($(wc -l < "$PORTS/hosts.txt") hosts)"

# PORTSCAN_NMAP=1 → naabu pipes discovered ports straight into nmap -sV.
if [[ "${PORTSCAN_NMAP:-0}" == "1" ]] && command -v nmap >/dev/null 2>&1; then
naabu -list "$PORTS/hosts.txt" -top-ports "${NAABU_TOP_PORTS:-1000}" -silent \
-nmap-cli "nmap -sV -Pn -oN $PORTS/nmap.txt" 2>/dev/null | sort -u > "$PORTS/ports.txt" || true
echo "[+] nmap services → $PORTS/nmap.txt"
else
naabu -list "$PORTS/hosts.txt" -top-ports "${NAABU_TOP_PORTS:-1000}" -silent 2>/dev/null \
| sort -u > "$PORTS/ports.txt" || true
fi
echo "[+] $(wc -l < "$PORTS/ports.txt" 2>/dev/null || echo 0) host:port → $PORTS/ports.txt"
18 changes: 18 additions & 0 deletions exegol/my-resources/bin/recon-screenshot
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -euo pipefail
: "${DOMAIN:?load an engagement first (DOMAIN unset) — use: dl <target>}"
OUT="${WORKSPACE:-$PWD}/recon"
SHOTS="$OUT/screenshots"
mkdir -p "$SHOTS"

# Screenshot every alive host (gowitness v3: `scan file`).
command -v gowitness >/dev/null 2>&1 || { echo "[!] gowitness not found"; exit 1; }
[[ -s "$OUT/subdomains_alive.txt" ]] || { echo "[!] run recon-alive first"; exit 1; }

echo "[*] gowitness screenshots"
gowitness scan file -f "$OUT/subdomains_alive.txt" -s "$SHOTS" \
-t "${GOWITNESS_THREADS:-20}" -T "${GOWITNESS_TIMEOUT:-30}" \
--write-csv --write-csv-file "$SHOTS/index.csv" >/dev/null 2>&1 || true

n="$(find "$SHOTS" -maxdepth 1 -type f \( -name '*.jpeg' -o -name '*.png' \) 2>/dev/null | wc -l)"
echo "[+] $n screenshots → $SHOTS (index: $SHOTS/index.csv)"
24 changes: 24 additions & 0 deletions exegol/my-resources/bin/scan-nuclei
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -euo pipefail
: "${DOMAIN:?load an engagement first (DOMAIN unset) — use: dl <target>}"
OUT="${WORKSPACE:-$PWD}/recon"
VULN="${WORKSPACE:-$PWD}/scans/vuln"
mkdir -p "$VULN"

# Vulnerability scan the alive hosts with nuclei.
command -v nuclei >/dev/null 2>&1 || { echo "[!] nuclei not found"; exit 1; }
src="$OUT/subdomains_alive.txt"
[[ -s "$src" ]] || { echo "[!] run recon-alive first"; exit 1; }

# Conditional flags via an array (keeps UA header / proxy correctly quoted).
args=(-l "$src"
-severity "${NUCLEI_SEVERITY:-critical,high,medium}"
-rate-limit "${NUCLEI_RATE:-150}"
-stats -o "$VULN/nuclei.txt")
[[ -n "${UA:-}" ]] && args+=(-header "User-Agent: ${UA}")
# Route through the engagement proxy so findings are captured in mitmproxy.
[[ -n "${HTTP_PROXY:-}" ]] && args+=(-proxy "$HTTP_PROXY")

echo "[*] nuclei scan ($(wc -l < "$src") targets, severity ${NUCLEI_SEVERITY:-critical,high,medium})"
nuclei "${args[@]}" 2>/dev/null || true
echo "[+] $(wc -l < "$VULN/nuclei.txt" 2>/dev/null || echo 0) findings → $VULN/nuclei.txt"
23 changes: 23 additions & 0 deletions exegol/my-resources/bin/scan-takeover
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
: "${DOMAIN:?load an engagement first (DOMAIN unset) — use: dl <target>}"
OUT="${WORKSPACE:-$PWD}/recon"
VULN="${WORKSPACE:-$PWD}/scans/vuln"
mkdir -p "$VULN"

# Subdomain takeover check over the full subdomain list (dangling CNAMEs).
src="$OUT/subdomains.txt"
[[ -s "$src" ]] || { echo "[!] run recon-subs first"; exit 1; }

if command -v subzy >/dev/null 2>&1; then
echo "[*] subzy takeover check ($(wc -l < "$src") subdomains)"
subzy run --targets "$src" --concurrency 100 --hide_fails --verify_ssl 2>/dev/null \
| tee "$VULN/takeover.txt" >/dev/null || true
elif command -v nuclei >/dev/null 2>&1; then
echo "[*] nuclei takeover templates ($(wc -l < "$src") subdomains)"
nuclei -l "$src" -tags takeover -severity critical,high,medium \
-o "$VULN/takeover.txt" 2>/dev/null || true
else
echo "[!] neither subzy nor nuclei found"; exit 1
fi
echo "[+] takeover results → $VULN/takeover.txt"
8 changes: 5 additions & 3 deletions exegol/my-resources/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ merge_block() {
}

mkdir -p "${DEST}/bin"
if compgen -G "${SRC}/bin/recon-*" >/dev/null; then
cp "${SRC}/bin/"recon-* "${DEST}/bin/"
fi
for prefix in recon- scan- audit-; do
if compgen -G "${SRC}/bin/${prefix}*" >/dev/null; then
cp "${SRC}/bin/${prefix}"* "${DEST}/bin/"
fi
done
[[ -f "${SRC}/bin/dl" ]] && cp "${SRC}/bin/dl" "${DEST}/bin/"
chmod +x "${DEST}/bin/"* 2>/dev/null || true

Expand Down
9 changes: 8 additions & 1 deletion exegol/my-resources/fragments/aliases.dotsec
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# dotsec recon aliases (env-driven: needs $DOMAIN / an engagement loaded)
alias dl='source /opt/my-resources/bin/dl'
alias subs='subfinder -d "$DOMAIN" -all -recursive'
alias alive='httpx-toolkit -mc 200 -ports 443,80,8080,8000,8888 -threads 200'
alias alive='httpx -mc 200 -ports 443,80,8080,8000,8888 -threads 200'
alias crawl='katana -d 5 -jc -kf -ps -pss waybackarchive,commoncrawl,alienvault'

# dotsec phase shortcuts (each wraps a /opt/my-resources/bin script)
alias portscan='recon-portscan'
alias shots='recon-screenshot'
alias scan='scan-nuclei'
alias takeover='scan-takeover'
alias audit='audit-code'
9 changes: 7 additions & 2 deletions exegol/my-resources/fragments/history.dotsec
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
: 0:0;subfinder -d $DOMAIN -all -recursive > subdomains.txt
: 0:0;cat subdomains.txt | httpx-toolkit -mc 200 -l subdomains.txt -ports 443,80,8080,8000,8888 -threads 200 > subdomains_alive.txt
: 0:0;cat subdomains.txt | httpx -mc 200 -l subdomains.txt -ports 443,80,8080,8000,8888 -threads 200 > subdomains_alive.txt
: 0:0;katana -u subdomains_alive.txt -d 5 -ps -pss waybackarchive,commoncrawl,alienvault -kf -jc -ef woff,css,png,svg,jpg,woff2,jpeg,gif -o allurls.txt
: 0:0;cat allurls.txt | grep -E "\.js($|\?)" | sort -u > js.txt
: 0:0;cat subdomains_alive.txt | gau > params.txt
: 0:0;cat js.txt | while read url; do python3 ~/tools/secretfinder/SecretFinder.py -i $url -o cli; done >> secrets.txt
: 0:0;xnLinkFinder -i js.txt -sf "$DOMAIN" -o endpoints.txt
: 0:0;trufflehog filesystem code/ --results=verified,unknown
: 0:0;dirsearch -u https://$DOMAIN -e conf,config,bak,old,sql,json,js,zip,tar.gz
: 0:0;feroxbuster -u https://$DOMAIN -w /usr/share/seclists/Discovery/Web-Content/big.txt -C 404,403,429,401,405,302,400,301 -t 50
: 0:0;nuclei -l subdomains_alive.txt -t ~/nuclei-templates/http/cves -severity critical,high,medium -c 100
: 0:0;ffuf -w /usr/share/seclists/Discovery/Web-Content/big.txt -u https://$DOMAIN/FUZZ -mc 200,403 -fs 0
: 0:0;sqlmap -u "https://$DOMAIN/?id=1" --level=5 --risk=3 -p id --dbs
: 0:0;subzy run --targets subdomains.txt --concurrency 100 --hide_fails --verify_ssl
: 0:0;naabu -list subdomains_alive.txt -top-ports 1000 -silent
: 0:0;gowitness scan file -f subdomains_alive.txt --screenshot-path recon/screenshots --write-none
: 0:0;osv-scanner scan --recursive code/
: 0:0;semgrep --config auto code/
76 changes: 62 additions & 14 deletions exegol/my-resources/fragments/load_user_setup.dotsec.sh
Original file line number Diff line number Diff line change
@@ -1,15 +1,63 @@
# shellcheck shell=bash
# dotsec recon tooling — runs on first container start. Idempotent.
if ! command -v uv >/dev/null 2>&1; then
curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh
fi
if ! command -v pnpm >/dev/null 2>&1; then
npm install -g pnpm 2>/dev/null || true
fi
if ! command -v unwebpack-sourcemap >/dev/null 2>&1; then
npm install -g unwebpack-sourcemap 2>/dev/null || true
fi
# url/endpoint harvesting helpers (ProjectDiscovery + LinkFinder are already in Exegol)
if ! command -v waybackurls >/dev/null 2>&1; then
go install github.com/tomnomnom/waybackurls@latest 2>/dev/null || true
fi
# dotsec offensive tooling installer — runs on first container start and on
# `dotsec exegol setup`. Idempotent and best-effort: a single failed install
# never aborts the container build. Tools already shipped by the Exegol image
# (subfinder/httpx/katana/naabu/nuclei/gowitness/ffuf/gau/semgrep/trufflehog/
# gitleaks) are NOT reinstalled — only what the dotsec scripts need on top.
#
# go install writes to an asdf-managed GOPATH that is NOT on PATH, so binaries
# are forced into /usr/local/bin (on PATH, writable). go tools are best-effort:
# many now require go >= 1.23 while the base image may ship 1.22 — if the build
# fails the script falls back (scan-takeover→nuclei, sourcemaps→unwebpack, …).
_dotsec_gobin="/usr/local/bin"

_dotsec_have() { command -v "$1" >/dev/null 2>&1; }

_dotsec_pipx() { # _dotsec_pipx <bin> <pypi-spec>
_dotsec_have "$1" && return 0
pipx install "$2" >/dev/null 2>&1 || true
}

_dotsec_go() { # _dotsec_go <bin> <module@version> (best-effort)
_dotsec_have "$1" && return 0
GOBIN="$_dotsec_gobin" go install "$2" >/dev/null 2>&1 || true
}

_dotsec_release() { # _dotsec_release <bin> <url-amd64> <url-arm64>
_dotsec_have "$1" && return 0
local url
case "$(uname -m)" in
x86_64 | amd64) url="$2" ;;
aarch64 | arm64) url="$3" ;;
*) return 0 ;;
esac
if curl -fsSL "$url" -o "${_dotsec_gobin}/$1" 2>/dev/null; then
chmod +x "${_dotsec_gobin}/$1" 2>/dev/null || true
fi
}

echo "[dotsec] provisioning recon/scan/audit tooling (idempotent)…"

# ── base runtimes ───────────────────────────────────────
_dotsec_have uv || curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh
_dotsec_have pnpm || npm install -g pnpm >/dev/null 2>&1 || true

# ── recon: JS endpoint extraction + sourcemap reconstruction ──
_dotsec_pipx xnLinkFinder xnLinkFinder
_dotsec_have unwebpack-sourcemap || npm install -g unwebpack-sourcemap >/dev/null 2>&1 || true
_dotsec_go sourcemapper github.com/denandz/sourcemapper@latest

# ── recon: passive URL aggregation (recon-urls) ─────────
_dotsec_go waybackurls github.com/tomnomnom/waybackurls@latest
_dotsec_pipx waymore waymore
_dotsec_go urlfinder github.com/projectdiscovery/urlfinder/cmd/urlfinder@latest

# ── scan: subdomain takeover (bonus — scan-takeover falls back to nuclei) ──
_dotsec_go subzy github.com/PentestPad/subzy@latest

# ── code audit: SCA (audit-code; semgrep/trufflehog/gitleaks ship with Exegol) ──
_dotsec_release osv-scanner \
https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64 \
https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_arm64

echo "[dotsec] tooling ready (go-built extras skipped if go < 1.23 — covered by fallbacks)."
2 changes: 1 addition & 1 deletion lib/engagement.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ cmd_new() {

# 1. Workspace structure (code/ = white-box / source-map / JS audit zone)
printf '%b\n' " ${DIM}[1/8]${RESET} ${DIM}Creating workspace...${RESET}"
mkdir -p "${ws}"/{recon/{passive,active},scans/{ports,web,vuln},code/{recovered,sources,sourcemaps,js,beautified},exploits/{pocs,payloads},loot/{credentials,data},logs,report/assets,replays/{recon,scan,exploit,post,report,monitor},keys}
mkdir -p "${ws}"/{recon/{passive,active,screenshots},scans/{ports,web,vuln,code},code/{recovered,sources,sourcemaps,js,beautified},exploits/{pocs,payloads},loot/{credentials,data},logs,report/assets,replays/{recon,scan,exploit,post,report,monitor},keys}
# Default ACLs so files the root Exegol/proxy containers create in the
# workspace stay editable from the host (your user keeps rwx by inheritance).
if command -v setfacl >/dev/null 2>&1; then
Expand Down
Loading