CodeQL flagged 21 cpp/command-line-injection alerts in tools/editor/.
All matched the same pattern: build a shell command string from
argv[0] + a user-supplied path, then std::system() it. Even though
the threat model (user invokes their own CLI on their own machine)
makes the alert mostly academic, the std::system path is also
fragile — paths with spaces, quotes, or shell metacharacters
silently break.
Add tools/editor/cli_subprocess.{hpp,cpp} exposing a single
runChild(argv0, args, quiet=false) that uses posix_spawn on POSIX
and CreateProcess on Windows. No shell, argv passed verbatim,
optional stdout/stderr redirect to /dev/null (NUL on Windows).
Refactor 14 call sites across cli_convert.cpp, cli_data_tree.cpp,
cli_format_validate.cpp, cli_items.cpp, cli_random.cpp,
cli_repair.cpp, cli_spawn_audit.cpp.
Also fix two cpp/integer-multiplication-cast-to-long alerts:
- cli_gen_texture.cpp:3049 — seeds.reserve grid-size product
- cli_convert_single.cpp:224 — vector size for DBC record block
Both now widen one operand to size_t before multiplying.
Moves the four bulk format-conversion handlers out of main.cpp:
--convert-m2-batch (M2 → WOM)
--convert-wmo-batch (WMO → WOB)
--convert-blp-batch (BLP → PNG)
--convert-dbc-batch (DBC → JSON)
These all share the same pattern: walk srcDir recursively for
files of the input extension and fan out to the single-file
--convert-* counterpart via subprocess (preserving the existing
per-file logic as the source of truth, no duplication).
Single-file converters (--convert-m2, --convert-wmo, etc.) and
the --migrate-* meta-commands still live in main.cpp; they're
in dedicated argv-rescan loops that need a different extraction
approach.
main.cpp drops 18,396 → 18,198 lines (-198). Behavior verified
by re-running --convert-blp-batch with a missing directory and
confirming the same error message.