diff --git a/osquery/core/init.cpp b/osquery/core/init.cpp index cf23330851a..535bf0967b0 100644 --- a/osquery/core/init.cpp +++ b/osquery/core/init.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -92,7 +93,7 @@ enum { #endif // OpenFrame includes -#include "openframe/openframe_authorization_manager_provider.h" +#include "openframe/openframe_authorization_manager.h" #include "openframe/openframe_encryption_service.h" #include "openframe/openframe_token_extractor.h" #include "openframe/openframe_token_refresher.h" @@ -208,35 +209,32 @@ void initWorkDirectories() { } } -void initOpenFrame() { +Status initOpenFrame() { VLOG(1) << "OpenFrame mode enabled"; // Initialize OpenFrame components if secret is provided if (FLAGS_openframe_secret.empty()) { - LOG(ERROR) << "OpenFrame mode enabled but secret not set"; - return; + return Status::failure("OpenFrame mode enabled but secret not set"); } - try { - // Create openframe token services - auto encryption_service = std::make_shared(FLAGS_openframe_secret); - auto token_extractor = std::make_shared(encryption_service, FLAGS_openframe_token_path); - - auto initial_token = token_extractor->extractToken(); - if (!initial_token.empty()) { - auto& auth_manager = OpenframeAuthorizationManagerProvider::getInstance(); - auth_manager.updateToken(initial_token); - LOG(INFO) << "OpenFrame token extracted successfully"; - } else { - LOG(ERROR) << "Failed to get initial token from token file"; - } - - // Create and start token refresher - static auto token_refresher = std::make_shared(token_extractor); - token_refresher->start(); - } catch (const std::exception& e) { - LOG(ERROR) << "Failed to initialize OpenFrame components: " << e.what(); + // Create openframe token services + auto encryption_service = std::make_shared(FLAGS_openframe_secret); + auto token_extractor = std::make_shared(encryption_service, FLAGS_openframe_token_path); + + auto initial_token = token_extractor->extractToken(); + if (!initial_token.empty()) { + auto& auth_manager = OpenframeAuthorizationManager::getInstance(); + auth_manager.updateToken(initial_token); + LOG(INFO) << "OpenFrame token extracted successfully"; + } else { + return Status::failure("Failed to get initial token from token file"); } + + // Create and start token refresher + static auto token_refresher = std::make_shared(token_extractor); + token_refresher->start(); + + return Status::success(); } void signalHandler(int num) { @@ -258,7 +256,6 @@ void signalHandler(int num) { bool validateAlarmTimeout(const char* flagname, std::uint64_t value) { if (value < 10) { osquery::systemLog("Alarm timeout cannot be lower than 10 seconds"); - std::cerr << "Alarm timeout cannot be lower than 10 seconds" << std::endl; return false; } @@ -459,7 +456,11 @@ Initializer::Initializer(int& argc, // Initialize OpenFrame authorization manager and token refresher if mode is enabled if (FLAGS_openframe_mode) { - initOpenFrame(); + auto openframe_status = initOpenFrame(); + if (!openframe_status.ok()) { + LOG(ERROR) << "Failed to initialize OpenFrame components: " + << openframe_status.getMessage(); + } } else { VLOG(1) << "OpenFrame mode disabled"; } @@ -954,3 +955,4 @@ void Initializer::shutdownNow(int retcode) { _Exit(retcode); } } // namespace osquery + diff --git a/osquery/dispatcher/distributed_runner.cpp b/osquery/dispatcher/distributed_runner.cpp index 75773a20b7d..868cbbe644e 100644 --- a/osquery/dispatcher/distributed_runner.cpp +++ b/osquery/dispatcher/distributed_runner.cpp @@ -62,6 +62,15 @@ void DistributedRunner::start() { "Reading distributed queries", read_status, last_read_error); logOutcomeChange( "Writing distributed query results", write_status, last_write_error); + } else { + if (!read_status.ok()) { + LOG(ERROR) << "Error reading distributed queries: " + << read_status.getMessage(); + } + if (!write_status.ok()) { + LOG(ERROR) << "Error writing distributed query results: " + << write_status.getMessage(); + } } dist.cleanupExpiredRunningQueries(); @@ -89,3 +98,4 @@ Status startDistributed() { } } } // namespace osquery + diff --git a/osquery/events/linux/bpf/systemstatetracker.cpp b/osquery/events/linux/bpf/systemstatetracker.cpp index d01e8fbee5a..15eb71211a8 100644 --- a/osquery/events/linux/bpf/systemstatetracker.cpp +++ b/osquery/events/linux/bpf/systemstatetracker.cpp @@ -42,7 +42,9 @@ SystemStateTracker::Ref SystemStateTracker::create() { IProcessContextFactory::Ref process_context_factory; auto status = IProcessContextFactory::create(process_context_factory); if (!status) { - throw status; + LOG(ERROR) << "Failed to create the state tracker: " + << status.getMessage(); + return nullptr; } return create(std::move(process_context_factory)); @@ -319,6 +321,8 @@ Status SystemStateTracker::expireProcessContexts(Context& context, bool exists{false}; if (!fs.fileExists(exists, procfs_root.get(), process_id.c_str())) { return_error = true; + ++process_map_it; + continue; } if (!exists) { @@ -1357,3 +1361,4 @@ SystemStateTracker::Context SystemStateTracker::getContextCopy() const { } } // namespace osquery + diff --git a/osquery/events/windows/etw/etw_publisher.cpp b/osquery/events/windows/etw/etw_publisher.cpp index ee2040432a6..5ce8990315e 100644 --- a/osquery/events/windows/etw/etw_publisher.cpp +++ b/osquery/events/windows/etw/etw_publisher.cpp @@ -25,7 +25,7 @@ EtwController& EtwPublisherBase::EtwEngine() { } Status EtwPublisherBase::run() { - return Status::failure(0, + return Status::failure(1, "ETW provider is driven by event callbacks. " "A pooling thread is not required."); } @@ -120,3 +120,4 @@ void EtwPublisherBase::updateHardVolumeWithLogicalDrive(std::string& path) { } } // namespace osquery + diff --git a/osquery/remote/http_client.cpp b/osquery/remote/http_client.cpp index 68b4c960f70..853007f3bbf 100644 --- a/osquery/remote/http_client.cpp +++ b/osquery/remote/http_client.cpp @@ -100,7 +100,7 @@ void Client::readHandler(boost::system::error_code const& ec, size_t) { postResponseHandler(ec); } -void Client::createConnection() { +Status Client::createConnection() { std::string port = (client_options_.proxy_hostname_) ? kProxyDefaultPort : *client_options_.remote_port_; @@ -134,7 +134,8 @@ void Client::createConnection() { error += "proxy host "; } error += connect_host + ':' + port; - throw std::system_error(ec_, error); + error += ": " + ec_.message(); + return Status::failure(error); } if (client_options_.keep_alive_) { @@ -162,7 +163,7 @@ void Client::createConnection() { }); if (ec_) { - throw std::system_error(ec_); + return Status::failure(ec_.message()); } boost::beast::flat_buffer b; @@ -180,17 +181,19 @@ void Client::createConnection() { }); if (ec_) { - throw std::system_error(ec_); + return Status::failure(ec_.message()); } if (beast_http::to_status_class(rp.get().result()) != beast_http::status_class::successful) { - throw std::runtime_error(rp.get().reason().data()); + return Status::failure(rp.get().reason().data()); } } + + return Status::success(); } -void Client::encryptConnection() { +Status Client::encryptConnection() { boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23}; if (client_options_.always_verify_peer_) { @@ -241,14 +244,16 @@ void Client::encryptConnection() { }); if (ec_) { - throw std::system_error(ec_); + return Status::failure(ec_.message()); } + + return Status::success(); } template -void Client::sendRequest(STREAM_TYPE& stream, - Request& req, - beast_http_response_parser& resp) { +Status Client::sendRequest(STREAM_TYPE& stream, + Request& req, + beast_http_response_parser& resp) { req.target((req.remotePath()) ? *req.remotePath() : "/"); req.version(11); @@ -284,7 +289,7 @@ void Client::sendRequest(STREAM_TYPE& stream, }); if (ec_) { - throw std::system_error(ec_); + return Status::failure(ec_.message()); } boost::beast::flat_buffer b; @@ -300,7 +305,7 @@ void Client::sendRequest(STREAM_TYPE& stream, }); if (ec_) { - throw std::system_error(ec_); + return Status::failure(ec_.message()); } if (resp.get()["Connection"] == "close") { @@ -310,10 +315,12 @@ void Client::sendRequest(STREAM_TYPE& stream, if (!client_options_.keep_alive_) { closeSocket(); } + + return Status::success(); } -bool Client::initHTTPRequest(Request& req) { - bool create_connection = true; +Status Client::initHTTPRequest(Request& req, bool& create_connection) { + create_connection = true; if (req.remoteHost()) { std::string hostname = *req.remoteHost(); std::string port; @@ -349,7 +356,7 @@ bool Client::initHTTPRequest(Request& req) { } } else { if (!client_options_.remote_hostname_) { - throw std::runtime_error("Remote hostname missing"); + return Status::failure("Remote hostname missing"); } if (!client_options_.remote_port_) { @@ -361,10 +368,10 @@ bool Client::initHTTPRequest(Request& req) { } closeSocket(); } - return create_connection; + return Status::success(); } -Response Client::sendHTTPRequest(Request& req) { +Status Client::sendHTTPRequest(Request& req, Response& response) { if (client_options_.timeout_) { timer_.expires_from_now( boost::posix_time::seconds(client_options_.timeout_)); @@ -375,140 +382,168 @@ Response Client::sendHTTPRequest(Request& req) { do { bool create_connection = true; if (init_request) { - create_connection = initHTTPRequest(req); + auto status = initHTTPRequest(req, create_connection); + if (!status.ok()) { + return status; + } } - try { - beast_http_response_parser resp; - if (create_connection) { - createConnection(); - - if (client_options_.ssl_connection_) { - encryptConnection(); + beast_http_response_parser resp; + if (create_connection) { + auto status = createConnection(); + if (!status.ok()) { + closeSocket(); + if (init_request && ec_ != boost::asio::error::timed_out) { + init_request = false; + continue; } + ec_.clear(); + return status; } if (client_options_.ssl_connection_) { - sendRequest(*ssl_sock_, req, resp); - } else { - sendRequest(sock_, req, resp); + auto status2 = encryptConnection(); + if (!status2.ok()) { + closeSocket(); + if (init_request && ec_ != boost::asio::error::timed_out) { + init_request = false; + continue; + } + ec_.clear(); + return status2; + } } + } - switch (resp.get().result()) { - case beast_http::status::moved_permanently: - case beast_http::status::found: - case beast_http::status::see_other: - case beast_http::status::not_modified: - case beast_http::status::use_proxy: - case beast_http::status::temporary_redirect: - case beast_http::status::permanent_redirect: { - if (!client_options_.follow_redirects_) { - return Response(resp.release()); - } + Status send_status; + if (client_options_.ssl_connection_) { + send_status = sendRequest(*ssl_sock_, req, resp); + } else { + send_status = sendRequest(sock_, req, resp); + } - if (redirect_attempts++ >= 10) { - throw std::runtime_error("Exceeded max of 10 redirects"); - } + if (!send_status.ok()) { + closeSocket(); + if (init_request && ec_ != boost::asio::error::timed_out) { + init_request = false; + continue; + } + ec_.clear(); + return send_status; + } - std::string redir_url = Response(resp.release()).headers()["Location"]; - if (!redir_url.size()) { - throw std::runtime_error( - "Location header missing in redirect response"); - } + switch (resp.get().result()) { + case beast_http::status::moved_permanently: + case beast_http::status::found: + case beast_http::status::see_other: + case beast_http::status::not_modified: + case beast_http::status::use_proxy: + case beast_http::status::temporary_redirect: + case beast_http::status::permanent_redirect: { + if (!client_options_.follow_redirects_) { + response = Response(resp.release()); + return Status::success(); + } - VLOG(1) << "HTTP(S) request re-directed to: " << redir_url; - if (redir_url[0] == '/') { - // Relative URI. - if (req.remotePort()) { - redir_url.insert(0, *req.remotePort()); - redir_url.insert(0, ":"); - } - if (req.remoteHost()) { - redir_url.insert(0, *req.remoteHost()); - } - if (req.protocol()) { - redir_url.insert(0, "://"); - redir_url.insert(0, *req.protocol()); - } - } else { - // Absolute URI. - init_request = true; - } - req.uri(redir_url); - break; + if (redirect_attempts++ >= 10) { + return Status::failure("Exceeded max of 10 redirects"); } - default: - return Response(resp.release()); + + std::string redir_url = Response(resp.release()).headers()["Location"]; + if (!redir_url.size()) { + return Status::failure("Location header missing in redirect response"); } - } catch (std::exception const& /* e */) { - closeSocket(); - if (init_request && ec_ != boost::asio::error::timed_out) { - init_request = false; + + VLOG(1) << "HTTP(S) request re-directed to: " << redir_url; + if (redir_url[0] == '/') { + // Relative URI. + if (req.remotePort()) { + redir_url.insert(0, *req.remotePort()); + redir_url.insert(0, ":"); + } + if (req.remoteHost()) { + redir_url.insert(0, *req.remoteHost()); + } + if (req.protocol()) { + redir_url.insert(0, "://"); + redir_url.insert(0, *req.protocol()); + } } else { - ec_.clear(); - throw; + // Absolute URI. + init_request = true; } + req.uri(redir_url); + break; + } + default: + response = Response(resp.release()); + return Status::success(); } } while (true); } -Response Client::put(Request& req, - std::string const& body, - std::string const& content_type) { +Status Client::put(Request& req, + Response& response, + std::string const& body, + std::string const& content_type) { req.method(beast_http::verb::put); req.body() = body; if (!content_type.empty()) { req.set(beast_http::field::content_type, content_type); } - return sendHTTPRequest(req); + return sendHTTPRequest(req, response); } -Response Client::post(Request& req, - std::string const& body, - std::string const& content_type) { +Status Client::post(Request& req, + Response& response, + std::string const& body, + std::string const& content_type) { req.method(beast_http::verb::post); req.body() = body; if (!content_type.empty()) { req.set(beast_http::field::content_type, content_type); } - return sendHTTPRequest(req); + return sendHTTPRequest(req, response); } -Response Client::put(Request& req, - std::string&& body, - std::string const& content_type) { +Status Client::put(Request& req, + Response& response, + std::string&& body, + std::string const& content_type) { req.method(beast_http::verb::put); req.body() = std::move(body); if (!content_type.empty()) { req.set(beast_http::field::content_type, content_type); } - return sendHTTPRequest(req); + return sendHTTPRequest(req, response); } -Response Client::post(Request& req, - std::string&& body, - std::string const& content_type) { +Status Client::post(Request& req, + Response& response, + std::string&& body, + std::string const& content_type) { req.method(beast_http::verb::post); req.body() = std::move(body); if (!content_type.empty()) { req.set(beast_http::field::content_type, content_type); } - return sendHTTPRequest(req); + return sendHTTPRequest(req, response); } -Response Client::get(Request& req) { +Status Client::get(Request& req, Response& response) { req.method(beast_http::verb::get); - return sendHTTPRequest(req); + return sendHTTPRequest(req, response); } -Response Client::head(Request& req) { +Status Client::head(Request& req, Response& response) { req.method(beast_http::verb::head); - return sendHTTPRequest(req); + return sendHTTPRequest(req, response); } -Response Client::delete_(Request& req) { +Status Client::delete_(Request& req, Response& response) { req.method(beast_http::verb::delete_); - return sendHTTPRequest(req); + return sendHTTPRequest(req, response); } } // namespace http } // namespace osquery + diff --git a/osquery/tables/applications/windows/office_mru.cpp b/osquery/tables/applications/windows/office_mru.cpp index 6ea52b821e0..501ef6ea644 100644 --- a/osquery/tables/applications/windows/office_mru.cpp +++ b/osquery/tables/applications/windows/office_mru.cpp @@ -45,19 +45,36 @@ void parseOfficeData(QueryData& results, return; } - Row r; - // File path starts with * - r["path"] = file_path.substr(file_path.find("*") + 1); - // Extract the office application version from the registry path - auto version = office_version; - r["version"] = version.substr(office_version.find("Office\\"), 11).substr(7); + auto office_prefix_pos = office_version.find("Office\\"); + if (office_prefix_pos == std::string::npos || + office_version.size() < office_prefix_pos + 11) { + LOG(WARNING) << "Office registry path malformed: " << office_version; + return; + } + auto version = office_version.substr(office_prefix_pos, 11).substr(7); // Extract the office application name from the registry path - auto application = office_version; - auto office_app = application.substr(office_version.find(r["version"])); - r["application"] = office_app.substr(office_app.find("\\") + 1, - office_app.find(" MRU") - 10); + auto version_pos = office_version.find(version); + if (version_pos == std::string::npos) { + LOG(WARNING) << "Office registry path malformed: " << office_version; + return; + } + auto office_app = office_version.substr(version_pos); + + auto slash_pos = office_app.find("\\"); + auto mru_pos = office_app.find(" MRU"); + if (slash_pos == std::string::npos || mru_pos == std::string::npos || + mru_pos < 10) { + LOG(WARNING) << "Office registry path malformed: " << office_version; + return; + } + + Row r; + // File path starts with * + r["path"] = file_path.substr(file_path.find("*") + 1); + r["version"] = version; + r["application"] = office_app.substr(slash_pos + 1, mru_pos - 10); // Last opened time stored in Big endian Windows FILETIME Hex format, also // starts with T diff --git a/osquery/tables/system/posix/suid_bin.cpp b/osquery/tables/system/posix/suid_bin.cpp index 34c7b515eba..7c70e696c59 100644 --- a/osquery/tables/system/posix/suid_bin.cpp +++ b/osquery/tables/system/posix/suid_bin.cpp @@ -105,7 +105,10 @@ void genSuidBinsFromPath(const std::string& path, auto perms = dir_entry.status().permissions(); if ((perms & 04000) == 04000 || (perms & 02000) == 02000) { - genBin(dir_entry_path, perms, results); + auto status = genBin(dir_entry_path, perms, results); + if (!status.ok()) { + logger.log(google::GLOG_WARNING, status.getMessage()); + } } } } @@ -131,3 +134,4 @@ QueryData genSuidBin(QueryContext& context) { } } // namespace tables } // namespace osquery + diff --git a/osquery/tables/system/windows/services.cpp b/osquery/tables/system/windows/services.cpp index 97136804775..10df2492475 100644 --- a/osquery/tables/system/windows/services.cpp +++ b/osquery/tables/system/windows/services.cpp @@ -92,7 +92,7 @@ static inline Status getService(const SC_HANDLE& scmHandle, return Status(GetLastError(), "Failed to query service config"); } - try { + { (void)QueryServiceConfig2( svcHandle.get(), SERVICE_CONFIG_DESCRIPTION, nullptr, 0, &cbBufSize); err = GetLastError(); @@ -100,28 +100,28 @@ static inline Status getService(const SC_HANDLE& scmHandle, svc_descr_t lpsd(static_cast(malloc(cbBufSize)), freePtr); if (lpsd == nullptr) { - throw std::runtime_error("failed to malloc service description buffer"); - } - ret = QueryServiceConfig2(svcHandle.get(), - SERVICE_CONFIG_DESCRIPTION, - (LPBYTE)lpsd.get(), - cbBufSize, - &cbBufSize); - if (ret == 0) { - std::stringstream ss; - ss << "failed to query size of service description buffer, error: " - << GetLastError(); - throw std::runtime_error(ss.str()); - } - if (lpsd->lpDescription != nullptr) { - r["description"] = SQL_TEXT(wstringToString(lpsd->lpDescription)); + LOG(WARNING) << svc.lpServiceName + << ": failed to malloc service description buffer"; + } else { + ret = QueryServiceConfig2(svcHandle.get(), + SERVICE_CONFIG_DESCRIPTION, + (LPBYTE)lpsd.get(), + cbBufSize, + &cbBufSize); + if (ret == 0) { + LOG(WARNING) << svc.lpServiceName + << ": failed to query size of service description " + "buffer, error: " + << GetLastError(); + } else if (lpsd->lpDescription != nullptr) { + r["description"] = SQL_TEXT(wstringToString(lpsd->lpDescription)); + } } } else if (ERROR_MUI_FILE_NOT_FOUND != err) { // Bug in Windows 10 with CDPUserSvc_63718, just ignore description - throw std::runtime_error("failed to query service description"); + LOG(WARNING) << svc.lpServiceName + << ": failed to query service description"; } - } catch (const std::runtime_error& e) { - LOG(WARNING) << svc.lpServiceName << ": " << e.what(); } r["name"] = SQL_TEXT(wstringToString(svc.lpServiceName)); diff --git a/osquery/tables/yara/yara_events.cpp b/osquery/tables/yara/yara_events.cpp index ad6d524a600..d8b483ae79d 100644 --- a/osquery/tables/yara/yara_events.cpp +++ b/osquery/tables/yara/yara_events.cpp @@ -153,12 +153,8 @@ Status YARAEventSubscriber::Callback(const FileEventContextRef& ec, return Status(1, "ConfigParser unknown."); } - std::shared_ptr yaraParser; - try { - yaraParser = std::dynamic_pointer_cast(parser); - } catch (const std::bad_cast&) { - return Status(1, "Error casting yara config parser plugin"); - } + std::shared_ptr yaraParser = + std::dynamic_pointer_cast(parser); if (yaraParser == nullptr || yaraParser.get() == nullptr) { return Status(1, "Yara parser unknown."); } diff --git a/osquery/utils/aws/aws_util.cpp b/osquery/utils/aws/aws_util.cpp index eec730abe4f..a8bb16ddc33 100644 --- a/osquery/utils/aws/aws_util.cpp +++ b/osquery/utils/aws/aws_util.cpp @@ -165,10 +165,10 @@ bool validateIMDSV2RequestAttempts(const char* flagname, std::uint32_t value) { std::string error_message = "Only values higher than 0 are supported for " + std::string(flagname); osquery::systemLog(error_message); - std::cerr << error_message << std::endl; + LOG(WARNING) << error_message; return false; - } // namespace osquery + } return true; } @@ -763,3 +763,4 @@ void enableFIPSInClientConfig(const AWSServiceType service_type, config.region + ".amazonaws.com"; } } // namespace osquery + diff --git a/osquery/worker/ipc/posix/pipe_channel_factory.cpp b/osquery/worker/ipc/posix/pipe_channel_factory.cpp index f478a012ba0..1b4b4efc8a5 100644 --- a/osquery/worker/ipc/posix/pipe_channel_factory.cpp +++ b/osquery/worker/ipc/posix/pipe_channel_factory.cpp @@ -21,12 +21,11 @@ PipeChannelTicket::PipeChannelTicket(std::array read_pipe_fds, std::array write_pipe_fds) : read_pipe_fds_(read_pipe_fds), write_pipe_fds_(write_pipe_fds) {} -PipeChannelTicket PipeChannelFactory::createChannelTicket() { - PipeChannelTicket ticket; +Status PipeChannelFactory::createChannelTicket(PipeChannelTicket& ticket) { auto result = pipe(ticket.read_pipe_fds_.data()); if (result == -1) { - throw std::runtime_error( + return Status::failure( "Failed to create parent_write_child_read_pipe, error: " + std::to_string(errno)); } @@ -34,12 +33,12 @@ PipeChannelTicket PipeChannelFactory::createChannelTicket() { result = pipe(ticket.write_pipe_fds_.data()); if (result == -1) { - throw std::runtime_error( + return Status::failure( "Failed to create parent_read_child_write_pipe, error: " + std::to_string(errno)); } - return ticket; + return Status::success(); } PipeChannel& PipeChannelFactory::createChildChannel( diff --git a/plugins/database/sqlite.cpp b/plugins/database/sqlite.cpp index 9423b266451..19dd100efeb 100644 --- a/plugins/database/sqlite.cpp +++ b/plugins/database/sqlite.cpp @@ -116,13 +116,22 @@ Status SQLiteDatabasePlugin::get(const std::string& domain, const std::string& key, std::string& value) const { QueryData results; - char* err = nullptr; - std::string q = "select value from " + domain + " where key = '" + key + "';"; - sqlite3_exec(db_, q.c_str(), getData, &results, &err); - if (err != nullptr) { - sqlite3_free(err); + sqlite3_stmt* stmt = nullptr; + std::string q = "select value from " + domain + " where key = ?1;"; + sqlite3_prepare_v2(db_, q.c_str(), -1, &stmt, nullptr); + + sqlite3_bind_text(stmt, 1, key.c_str(), -1, SQLITE_STATIC); + + int rc = 0; + while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + Row r; + const unsigned char* val = sqlite3_column_text(stmt, 0); + r["value"] = (val != nullptr) ? reinterpret_cast(val) : ""; + results.push_back(std::move(r)); } + sqlite3_finalize(stmt); + // Only assign value if the query found a result. if (results.size() > 0) { value = std::move(results[0]["value"]); @@ -273,18 +282,26 @@ Status SQLiteDatabasePlugin::scan(const std::string& domain, const std::string& prefix, uint64_t max) const { QueryData _results; - char* err = nullptr; + sqlite3_stmt* stmt = nullptr; - std::string q = - "select key from " + domain + " where key LIKE '" + prefix + "%'"; + std::string q = "select key from " + domain + " where key LIKE ?1 || '%'"; if (max > 0) { q += " limit " + std::to_string(max); } - sqlite3_exec(db_, q.c_str(), getData, &_results, &err); - if (err != nullptr) { - sqlite3_free(err); + sqlite3_prepare_v2(db_, q.c_str(), -1, &stmt, nullptr); + + sqlite3_bind_text(stmt, 1, prefix.c_str(), -1, SQLITE_STATIC); + + int rc = 0; + while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + Row r; + const unsigned char* val = sqlite3_column_text(stmt, 0); + r["key"] = (val != nullptr) ? reinterpret_cast(val) : ""; + _results.push_back(std::move(r)); } + sqlite3_finalize(stmt); + // Only assign value if the query found a result. for (auto& r : _results) { results.push_back(std::move(r["key"]));