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 open-format validation + project-audit handlers out
of main.cpp:
--validate --validate-wom
--validate-wob --validate-woc
--validate-whm --validate-all
--validate-project --validate-project-open-only
--audit-project --bench-audit-project
--bench-validate-project
Also moves the four shared validate*Errors helpers (validateWom/
Wob/Woc/WhmErrors, ~365 lines) into the same module's anonymous
namespace — they were file-scope helpers in main.cpp used only
by these handlers, so co-locating eliminates the cross-TU
coupling.
main.cpp drops 19,446 → 18,396 lines (-1,050). Two build errors
caught during extraction (wrong include path for the WHM loader
header; missing #include for ContentPacker / std::set / std::map);
all fixed before commit.