fix(security): compile the UI sweep only into dev builds
The screenshot sweep and the full UI sweep are dev tools. The full sweep injects demo data into the live wallet state and the real data and config dirs, and the tab sweep screenshots real balances and addresses. Both were compiled into every build: the Settings buttons were reachable in full-node releases, and the DRAGONX_FULL_SWEEP env hook worked in every build, Lite releases included. A default-OFF option alone would fail open: dev and release builds share build/linux (and build/mac), so a release configured without --clean would inherit a cached ON from the last dev configure. - CMake: option(DRAGONX_ENABLE_UI_SWEEP ... OFF), passed to ObsidianDragon and ObsidianDragonTests as a compile definition. - build.sh passes it explicitly: ON in the dev build, OFF in every release configure (Linux, Windows, macOS osxcross and native). - Packaging guard: while the cache says ON, dragonx_release depends on dragonx_release_sweep_guard, which fails, so a hand-configured ON tree cannot package a release. - startSweepImpl is a no-op unless the option is ON (an undefined macro counts as 0, so it fails closed). The enabled branch logs the sentinel "[Sweep] DRAGONX_UI_SWEEP_COMPILED_IN", so a release can be checked with `strings <bin> | grep -c DRAGONX_UI_SWEEP_COMPILED_IN` (must be 0). - main.cpp: the DRAGONX_FULL_SWEEP hook is compiled out (autoSweep is false). It must be: with the sweep a no-op, the hook would quit the app once its warm-up frames ran out. - Settings > Node > Debug options: both sweep buttons, open-dir, the seed-demo-chat button and the current-theme checkbox are compiled only into dev builds. The daemon debug categories are unchanged. No i18n change: the existing strings stay in the tables. Verified with an isolated release (DRAGONX_BUILD_ROOT/DRAGONX_RELEASE_ROOT in a scratch dir) whose tree was first configured by hand with ON: build.sh --linux-release without --clean leaves the cache OFF, and the sentinel count is 0 in the built binary, the zip and the AppImage. With the cache set back to ON by hand, --target dragonx_release fails at the guard before compiling anything. Dev trees configured ON keep the sweep (sentinel present; a one-surface DRAGONX_FULL_SWEEP run against the fake daemon captured and exited cleanly). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,10 @@ option(DRAGONX_BUILD_LITE "Build ObsidianDragonLite variant without full-node fe
|
||||
option(DRAGONX_ENABLE_LITE_BACKEND "Enable real lite wallet backend integration" OFF)
|
||||
option(DRAGONX_ENABLE_CHAT "Enable the HushChat protocol/UI integration" ON)
|
||||
option(DRAGONX_BUILD_FAKE_DAEMON "Build the fake slow dragonxd test stub (tools/fake_dragonxd); never shipped" OFF)
|
||||
# The screenshot / full UI sweep injects demo data and touches the wallet's data and config dirs, so it is a
|
||||
# dev tool only. build.sh passes it EXPLICITLY (ON for dev, OFF for every release configure) because build/linux
|
||||
# and build/mac are shared between dev and release builds; dragonx_release refuses to package while it is ON.
|
||||
option(DRAGONX_ENABLE_UI_SWEEP "Compile the UI sweep dev tool (never for shipped builds)" OFF)
|
||||
# Embedded fallback copy of res/lang/*.json (xxd -i -> lang_*.h). Needed only by the Windows single-file exe (no res/ beside it);
|
||||
# Linux zip/AppImage and the macOS .app always ship res/lang, which loadLanguage() reads first.
|
||||
if(WIN32)
|
||||
@@ -938,6 +942,7 @@ target_compile_definitions(ObsidianDragon PRIVATE
|
||||
DRAGONX_ENABLE_LITE_BACKEND=$<BOOL:${DRAGONX_LITE_BACKEND_READY}>
|
||||
DRAGONX_ENABLE_CHAT=$<BOOL:${DRAGONX_ENABLE_CHAT}>
|
||||
DRAGONX_EMBED_LANG=$<BOOL:${DRAGONX_EMBED_LANG}>
|
||||
DRAGONX_ENABLE_UI_SWEEP=$<BOOL:${DRAGONX_ENABLE_UI_SWEEP}>
|
||||
)
|
||||
if(WIN32)
|
||||
target_compile_definitions(ObsidianDragon PRIVATE DRAGONX_USE_DX11)
|
||||
@@ -1170,6 +1175,13 @@ foreach(_dragonx_release_dep dragonx-wallet-rebuild copy_langs copy_themes)
|
||||
add_dependencies(dragonx_release ${_dragonx_release_dep})
|
||||
endif()
|
||||
endforeach()
|
||||
# Backstop for the UI sweep dev tool: a tree whose cache says ON (e.g. hand-configured) cannot package a release.
|
||||
if(DRAGONX_ENABLE_UI_SWEEP)
|
||||
add_custom_target(dragonx_release_sweep_guard
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "DRAGONX_ENABLE_UI_SWEEP=ON: refusing to package a release"
|
||||
COMMAND ${CMAKE_COMMAND} -E false)
|
||||
add_dependencies(dragonx_release dragonx_release_sweep_guard)
|
||||
endif()
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# fake_dragonxd — a deliberately slow, scriptable dragonxd stand-in that reproduces the daemon-stop
|
||||
@@ -1298,6 +1310,7 @@ if(BUILD_TESTING)
|
||||
DRAGONX_ENABLE_EMBEDDED_DAEMON=$<BOOL:${DRAGONX_ENABLE_EMBEDDED_DAEMON}>
|
||||
DRAGONX_ENABLE_LITE_BACKEND=$<BOOL:${DRAGONX_LITE_BACKEND_READY}>
|
||||
DRAGONX_EMBED_LANG=$<BOOL:${DRAGONX_EMBED_LANG}>
|
||||
DRAGONX_ENABLE_UI_SWEEP=$<BOOL:${DRAGONX_ENABLE_UI_SWEEP}>
|
||||
DRAGONX_TEST_FIXTURE_DIR="${CMAKE_SOURCE_DIR}/tests/fixtures"
|
||||
)
|
||||
|
||||
|
||||
@@ -349,6 +349,7 @@ build_dev() {
|
||||
-DCMAKE_BUILD_TYPE="$BUILD_TYPE" \
|
||||
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
|
||||
-DDRAGONX_USE_SYSTEM_SDL3=ON \
|
||||
-DDRAGONX_ENABLE_UI_SWEEP=ON \
|
||||
"${CMAKE_LITE_ARGS[@]}"
|
||||
|
||||
info "Building with $JOBS jobs ..."
|
||||
@@ -385,10 +386,12 @@ build_release_linux() {
|
||||
exit 1
|
||||
fi
|
||||
info "Configuring ..."
|
||||
# UI sweep OFF explicitly: build/linux is shared with dev builds, which cache it ON.
|
||||
cmake "$SCRIPT_DIR" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
|
||||
-DDRAGONX_USE_SYSTEM_SDL3=ON \
|
||||
-DDRAGONX_ENABLE_UI_SWEEP=OFF \
|
||||
"${BDB_ARGS[@]}" \
|
||||
"${CMAKE_LITE_ARGS[@]}"
|
||||
|
||||
@@ -864,6 +867,7 @@ HDR
|
||||
-DCMAKE_TOOLCHAIN_FILE="$bd/mingw-toolchain.cmake" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
||||
-DDRAGONX_ENABLE_UI_SWEEP=OFF \
|
||||
"${BDB_ARGS[@]}" \
|
||||
"${FT_CMAKE_ARG[@]}" \
|
||||
"${CMAKE_LITE_ARGS[@]}"
|
||||
@@ -1129,6 +1133,7 @@ TOOLCHAIN
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
|
||||
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
||||
-DDRAGONX_ENABLE_UI_SWEEP=OFF \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
||||
${COMPILER_RT:+-DOSXCROSS_COMPILER_RT="$COMPILER_RT"} \
|
||||
"${BDB_ARGS[@]}" \
|
||||
@@ -1163,6 +1168,7 @@ TOOLCHAIN
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
|
||||
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
||||
-DDRAGONX_ENABLE_UI_SWEEP=OFF \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="$MAC_ARCHS" \
|
||||
"${BDB_ARGS[@]}" \
|
||||
|
||||
@@ -718,7 +718,15 @@ void App::buildSweepCatalog()
|
||||
// ── State machine ───────────────────────────────────────────────────────────────────────────
|
||||
void App::startSweepImpl(bool full)
|
||||
{
|
||||
#if !DRAGONX_ENABLE_UI_SWEEP
|
||||
// Dev tool only (CMake DRAGONX_ENABLE_UI_SWEEP, OFF in every release); an undefined macro counts as 0.
|
||||
(void)full;
|
||||
DEBUG_LOGF("[Sweep] disabled in this build\n");
|
||||
return;
|
||||
#else
|
||||
if (screenshot_sweep_active_) return;
|
||||
// Sentinel: a release binary must not contain this literal (strings <bin> | grep -c ... == 0).
|
||||
DEBUG_LOGF("[Sweep] DRAGONX_UI_SWEEP_COMPILED_IN\n");
|
||||
|
||||
sweep_skins_.clear();
|
||||
for (const auto& sk : ui::schema::SkinManager::instance().available())
|
||||
@@ -761,6 +769,7 @@ void App::startSweepImpl(bool full)
|
||||
ui::Notifications::instance().info(full ? "Full UI sweep running…" : "Screenshot sweep running…");
|
||||
DEBUG_LOGF("[Sweep] %s -> %s (%d skins x %d surfaces)\n", full ? "FULL" : "tabs",
|
||||
sweep_dir_.c_str(), (int)sweep_skins_.size(), (int)sweep_targets_.size());
|
||||
#endif
|
||||
}
|
||||
|
||||
void App::startScreenshotSweep() { startSweepImpl(false); }
|
||||
|
||||
@@ -1611,7 +1611,13 @@ int main(int argc, char* argv[])
|
||||
|
||||
// Headless verification hook (DRAGONX_FULL_SWEEP=1, handled after present), hoisted so the pacing
|
||||
// decision can keep its warm-up frames coming.
|
||||
#if DRAGONX_ENABLE_UI_SWEEP
|
||||
const bool autoSweep = std::getenv("DRAGONX_FULL_SWEEP") != nullptr;
|
||||
#else
|
||||
// The env hook is dead in shipped builds. It must be: with startSweepImpl a no-op, an enabled
|
||||
// autoSweep would quit the app as soon as the warm-up frames ran out.
|
||||
const bool autoSweep = false;
|
||||
#endif
|
||||
int autoSweepFrames = 0;
|
||||
bool autoSweepStarted = false;
|
||||
|
||||
|
||||
@@ -2895,6 +2895,9 @@ void RenderSettingsPage(App* app) {
|
||||
|
||||
material::GlassCardScope card(dl, availWidth, pad, bottomPad, glassSpec);
|
||||
|
||||
#if DRAGONX_ENABLE_UI_SWEEP
|
||||
// Dev builds only (both sweeps, open-dir, demo chat and the theme checkbox): the tab sweep
|
||||
// screenshots real balances and addresses, and the demo chat fabricates a chat identity.
|
||||
// Screenshot sweep — capture every theme x every tab (above the daemon debug categories).
|
||||
ImGui::TextWrapped("%s", TR("screenshot_sweep_desc"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
@@ -2928,6 +2931,7 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
ImGui::Separator();
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
#endif
|
||||
|
||||
ImGui::TextColored(ImVec4(1,1,1,0.5f), "%s", TR("settings_debug_select"));
|
||||
ImGui::TextColored(ImVec4(1,1,1,0.35f), "%s", TR("settings_debug_restart_note"));
|
||||
|
||||
Reference in New Issue
Block a user