From 57766ef2a8c34b6d11de506afd6ee323a04804c7 Mon Sep 17 00:00:00 2001 From: erhant Date: Thu, 2 Jul 2026 15:07:28 +0300 Subject: [PATCH] fix: copilot comments, add `isHex` helper --- src/lez_ffi_marshalling.cpp | 13 +++++++++++++ src/lez_ffi_marshalling.h | 5 +++++ src/lez_indexer_module_impl.cpp | 26 +++++++++++++++----------- src/lez_indexer_module_impl.h | 12 +++++++----- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/lez_ffi_marshalling.cpp b/src/lez_ffi_marshalling.cpp index 6f8f3b2..9d63143 100644 --- a/src/lez_ffi_marshalling.cpp +++ b/src/lez_ffi_marshalling.cpp @@ -152,6 +152,19 @@ namespace marshalling { return true; } + // True iff `s` is exactly `num_chars` hex digits (no prefix/whitespace). + bool isHex(const std::string& s, const size_t num_chars) { + if (s.size() != num_chars) { + return false; + } + for (const char c : s) { + if (hexNibble(c) < 0) { + return false; + } + } + return true; + } + // Base58-encode `length` raw bytes (plain Bitcoin alphabet). Big-integer // base 256 -> 58, leading zero bytes map to leading '1's. Mirrors // Base58.js::encode so account ids match the wallet UI and canonical LEZ. diff --git a/src/lez_ffi_marshalling.h b/src/lez_ffi_marshalling.h index 0eae49b..86b102b 100644 --- a/src/lez_ffi_marshalling.h +++ b/src/lez_ffi_marshalling.h @@ -40,6 +40,11 @@ namespace marshalling { // Returns false unless it decodes to exactly 32 bytes. bool hexToBytes32(const std::string& hex, FfiBytes32* out); + // True iff `s` is exactly `num_chars` hex digits (no 0x prefix, no whitespace). + // For validating fixed-width hex identifiers (e.g. a 64-char channel id) before + // trusting them as-is, such as when composing a filesystem path. + bool isHex(const std::string& s, size_t num_chars); + // Base58 (plain Bitcoin alphabet, no checksum) of `length` raw bytes. This is // the canonical string form of an LEZ *account id* — it matches lee::AccountId // Display, wallet_ffi's account_id_to_base58, and the wallet UI's Base58.js. diff --git a/src/lez_indexer_module_impl.cpp b/src/lez_indexer_module_impl.cpp index 53091e7..1314837 100644 --- a/src/lez_indexer_module_impl.cpp +++ b/src/lez_indexer_module_impl.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -18,8 +19,8 @@ namespace { } // Module diagnostics go to stderr; logos_host captures it and routes each line - // through its own logger, keying on a leading "Error:"/"Warning:" token to pick - // the level (plain lines are treated as info). Prefix accordingly. + // through its own logger, classifying by an "Error:"/"Warning:" token found + // anywhere in the line (plain lines are treated as info). Prefix accordingly. void info(const char* method, const std::string& msg) { std::fprintf(stderr, "lez_indexer_module: %s: %s\n", method, msg.c_str()); } @@ -117,24 +118,27 @@ int64_t LezIndexerModuleImpl::reset_storage(const std::string& config_path) { error("reset_storage", std::string("could not read channel_id from config: ") + e.what()); return -1; } - if (channel_id.empty()) { - error("reset_storage", "config has an empty channel_id"); + // A channel id is a 32-byte hex string. Validate before building a path so a + // malformed/edited config can't inject separators ("../", absolute paths) and + // make remove_all escape the storage dir. + if (!isHex(channel_id, 64)) { + error("reset_storage", "config channel_id is not a 64-char hex string; refusing to build a wipe path"); return -1; } const std::filesystem::path store = storage / ("rocksdb-" + channel_id); std::error_code ec; - if (!std::filesystem::exists(store, ec)) { - info("reset_storage", "no store at " + store.string() + "; nothing to wipe"); - return 0; - } - std::filesystem::remove_all(store, ec); + // remove_all returns the number of entries removed and only sets `ec` on a real + // error; a missing store removes 0 with no error. No exists() pre-check — that + // would mask an IO/permission error as a successful "nothing to wipe". + const std::uintmax_t removed = std::filesystem::remove_all(store, ec); if (ec) { error("reset_storage", "failed to remove " + store.string() + ": " + ec.message()); return -1; } - - info("reset_storage", "wiped rocksdb store " + store.string()); + info("reset_storage", + removed == 0 ? "no store at " + store.string() + "; nothing to wipe" + : "wiped rocksdb store " + store.string()); return 0; } diff --git a/src/lez_indexer_module_impl.h b/src/lez_indexer_module_impl.h index b496c23..b7e17d3 100644 --- a/src/lez_indexer_module_impl.h +++ b/src/lez_indexer_module_impl.h @@ -42,11 +42,13 @@ public: int64_t stop_indexer(); /// Stop the indexer (if running) and delete the RocksDB store for the config's - /// channel — `/rocksdb-`, the exact path the - /// FFI uses — so the next start_indexer re-indexes from scratch. The recovery - /// path when the store is stale against a different/reset chain. Pass the same - /// `config_path` given to start_indexer; does NOT restart. Returns 0 on success, - /// else non-zero. int64_t for the same codegen reason as start_indexer. + /// channel — `/rocksdb-`, where `` is resolved the + /// same way start_indexer resolves it (the host's instance persistence path, or + /// the process working directory when unset) — so the next start_indexer + /// re-indexes from scratch. The recovery path when the store is stale against a + /// different/reset chain. Pass the same `config_path` given to start_indexer; + /// does NOT restart. Returns 0 on success, else non-zero. int64_t for the same + /// codegen reason as start_indexer. int64_t reset_storage(const std::string& config_path); /// Account by id, accepting Base58 (canonical) or 32-byte hex. The returned