From be8f662765dfbe3d94b151a9d029dfe0e0294c3c Mon Sep 17 00:00:00 2001 From: Sidhanth B H Date: Mon, 12 Jan 2026 15:41:52 +0530 Subject: [PATCH 1/5] RDKEMW-12252: Coverity Scan Report - Analyzing and Fixing all the Critical and High issues Reason for change: Resolve Critical and high level issues in coverity Test Procedure: build should be successful Risk: low Priority: P2 --- include/IJavaScriptContext.h | 1 + include/JSRuntimeClient.h | 16 +++++-- src/JSRuntimeClient.cpp | 62 +++++++++++++++----------- src/JSRuntimeClientContainer.cpp | 46 ++++++++++++-------- src/JSRuntimeServer.cpp | 2 +- src/NativeJSRenderer.cpp | 75 +++++++++++++++++++++----------- src/jsc/JavaScriptContext.cpp | 9 +++- src/jsc/JavaScriptEngine.cpp | 2 +- src/jsc/JavaScriptUtils.cpp | 62 ++++++++++++++++++++++---- src/jsc/jsc_lib/jsc_lib.cpp | 46 +++++++++++++------- src/jsruntime.cpp | 30 +++++++++---- 11 files changed, 241 insertions(+), 110 deletions(-) diff --git a/include/IJavaScriptContext.h b/include/IJavaScriptContext.h index 7c5ca76..6f33ec6 100644 --- a/include/IJavaScriptContext.h +++ b/include/IJavaScriptContext.h @@ -25,6 +25,7 @@ class IJavaScriptContext { public: + virtual ~IJavaScriptContext() = default; virtual bool runScript(const char *script, bool isModule=true, std::string name="", const char *args = nullptr, bool isApplication=false) = 0; virtual bool runFile(const char *file, const char* args, bool isApplication=false) = 0; virtual std::string getUrl() = 0; diff --git a/include/JSRuntimeClient.h b/include/JSRuntimeClient.h index a34588a..f272b79 100644 --- a/include/JSRuntimeClient.h +++ b/include/JSRuntimeClient.h @@ -40,7 +40,7 @@ template class CommandInterface { public: - CommandInterface() = default; + CommandInterface() : mResponseReceived(false) {} bool sendCommand(std::string command, std::string &response) { @@ -48,9 +48,15 @@ class CommandInterface if (derived.send(command)) { std::unique_lock lock(mResponseMutex); - mResponseCondition.wait_for(lock, std::chrono::seconds(5)); - response = mLastResponse; - return true; + mResponseReceived = false; + bool gotResponse = mResponseCondition.wait_for(lock, std::chrono::seconds(5), + [this]() { return mResponseReceived; }); + + if (gotResponse) + { + response = mLastResponse; + return true; + } } return false; @@ -63,6 +69,7 @@ class CommandInterface { std::lock_guard lock(mResponseMutex); mLastResponse = message; + mResponseReceived = true; mResponseCondition.notify_one(); } @@ -71,6 +78,7 @@ class CommandInterface CommandInterface &operator=(const CommandInterface &) = delete; std::string mLastResponse; + bool mResponseReceived; std::mutex mResponseMutex; std::condition_variable mResponseCondition; }; diff --git a/src/JSRuntimeClient.cpp b/src/JSRuntimeClient.cpp index 65e4c73..173282b 100644 --- a/src/JSRuntimeClient.cpp +++ b/src/JSRuntimeClient.cpp @@ -76,7 +76,9 @@ bool JSRuntimeClient::run() t.detach(); std::unique_lock lock(mStateMutex); - mStateCondition.wait_for(lock, std::chrono::seconds(5)); + mStateCondition.wait_for(lock, std::chrono::seconds(5), [this]() { + return mState != "none"; + }); return mState == "open"; } @@ -152,37 +154,45 @@ void JSRuntimeClient::onClose(websocketpp::connection_hdl hdl) #ifndef UNIT_TEST_BUILD int main(int argc, char **argv) { - std::string command; - std::string response; + try { + std::string command; + std::string response; - if (argc > 1) - { - NativeJSLogger::log(INFO, "Send input commands at ws://localhost:%s\n", std::to_string(WS_SERVER_PORT).c_str()); - return -1; - } - - JSRuntimeClient *client = JSRuntimeClient::getInstance(); - client->initialize(WS_SERVER_PORT); - if (!client->run()) - { - NativeJSLogger::log(ERROR, "Unable to connect to server\n"); - return -1; - } + if (argc > 1) + { + NativeJSLogger::log(INFO, "Send input commands at ws://localhost:%s\n", std::to_string(WS_SERVER_PORT).c_str()); + return -1; + } - while (client->getState() == "open" && std::getline(std::cin, command)) - { - client->sendCommand(command, response); - if (!response.empty()) + JSRuntimeClient *client = JSRuntimeClient::getInstance(); + client->initialize(WS_SERVER_PORT); + if (!client->run()) { - NativeJSLogger::log(INFO, "Response: %s\n", response.c_str()); + NativeJSLogger::log(ERROR, "Unable to connect to server\n"); + return -1; } - else + + while (client->getState() == "open" && std::getline(std::cin, command)) { - NativeJSLogger::log(WARN, "Missing response\n"); - break; + client->sendCommand(command, response); + if (!response.empty()) + { + NativeJSLogger::log(INFO, "Response: %s\n", response.c_str()); + } + else + { + NativeJSLogger::log(WARN, "Missing response\n"); + break; + } } - } - return 0; + return 0; + } catch (const std::exception& e) { + NativeJSLogger::log(ERROR, "Uncaught exception in main: %s\n", e.what()); + return -1; + } catch (...) { + NativeJSLogger::log(ERROR, "Unknown exception caught in main\n"); + return -1; + } } #endif diff --git a/src/JSRuntimeClientContainer.cpp b/src/JSRuntimeClientContainer.cpp index 8382be2..5b98681 100644 --- a/src/JSRuntimeClientContainer.cpp +++ b/src/JSRuntimeClientContainer.cpp @@ -6,26 +6,36 @@ #include "NativeJSLogger.h" int main() { - std::string containerId = "com.sky.as.apps_TestApp"; - const std::string basePath = "/opt/twocontext"; // constant base path - const std::vector apps = {"app1", "app2"}; - - std::string ipAddress = JSRuntimeContainer::getContainerIpAddress(containerId); - if (ipAddress.empty()) { - NativeJSLogger::log(ERROR, "Failed to retrieve IP address for container"); - return 1; - } + try { + std::string containerId = "com.sky.as.apps_TestApp"; + const std::string basePath = "/opt/twocontext"; // constant base path + const std::vector apps = {"app1", "app2"}; + + std::string ipAddress = JSRuntimeContainer::getContainerIpAddress(containerId); + if (ipAddress.empty()) { + NativeJSLogger::log(ERROR, "Failed to retrieve IP address for container\n"); + return 1; + } - for (const auto &app : apps) { - std::string url = basePath + std::string("/") + app + std::string("/index.html"); - if (access(url.c_str(), F_OK) == 0) { - std::string pathAppConfig = basePath + std::string("/") + app + std::string("/app.config"); - std::string options = JSRuntimeContainer::parseAppConfig(pathAppConfig); - std::string message = JSRuntimeContainer::buildLaunchMessage(url, options); - JSRuntimeContainer::connectAndSend(ipAddress, message); + for (const auto &app : apps) { + std::string url = basePath + std::string("/") + app + std::string("/index.html"); + if (access(url.c_str(), F_OK) == 0) { + std::string pathAppConfig = basePath + std::string("/") + app + std::string("/app.config"); + std::string options = JSRuntimeContainer::parseAppConfig(pathAppConfig); + std::string message = JSRuntimeContainer::buildLaunchMessage(url, options); + JSRuntimeContainer::connectAndSend(ipAddress, message); + } } - } - return 0; + return 0; + } + catch (const std::exception& e) { + NativeJSLogger::log(ERROR, "Exception in main: %s\n", e.what()); + return 1; + } + catch (...) { + NativeJSLogger::log(ERROR, "Unknown exception in main\n"); + return 1; + } } diff --git a/src/JSRuntimeServer.cpp b/src/JSRuntimeServer.cpp index 342f386..3a0793c 100644 --- a/src/JSRuntimeServer.cpp +++ b/src/JSRuntimeServer.cpp @@ -80,7 +80,7 @@ class JsonWrap uint32_t getUint32(const char *name, bool &err) { - uint32_t res; + uint32_t res = 0; cJSON *itm = cJSON_GetObjectItem(mPtr, name); if (!itm || !cJSON_IsNumber(itm)) { diff --git a/src/NativeJSRenderer.cpp b/src/NativeJSRenderer.cpp index bee81b9..4e98f41 100644 --- a/src/NativeJSRenderer.cpp +++ b/src/NativeJSRenderer.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #ifndef __APPLE__ #include @@ -225,10 +226,8 @@ void NativeJSRenderer::setEnvForConsoleMode(ModuleSettings& moduleSettings) uint32_t NativeJSRenderer::createApplicationIdentifier() { - static uint32_t id = 1; - uint32_t ret = id; - id++; - return ret; + static std::atomic id{1}; + return id++; } uint32_t NativeJSRenderer::createApplication(ModuleSettings& moduleSettings, std::string userAgent) @@ -292,6 +291,7 @@ bool NativeJSRenderer::terminateApplication(uint32_t id) return true; } +// REQUIRES: Caller must hold mUserMutex void NativeJSRenderer::createApplicationInternal(ApplicationRequest& appRequest) { double startTime = getTimeInMilliSec(); @@ -330,9 +330,9 @@ void NativeJSRenderer::createApplicationInternal(ApplicationRequest& appRequest) context->setCreateApplicationEndTime(endTime, id); mContextMap[id].context=context; - mUserMutex.unlock(); } +// REQUIRES: Caller must hold mUserMutex void NativeJSRenderer::runApplicationInternal(ApplicationRequest& appRequest) { uint32_t id = appRequest.mId; @@ -365,7 +365,7 @@ void NativeJSRenderer::runApplicationInternal(ApplicationRequest& appRequest) NativeJSLogger::log(INFO, "Adding the window location: %s to js file\n", window.str().c_str()); context->runScript(window.str().c_str(),true, url, nullptr, true); } - NativeJSLogger::log(INFO, "nativeJS application thunder execution url: %s, result: %d\n", url.c_str(), ret ? 1 : 0); + NativeJSLogger::log(INFO, "nativeJS application thunder execution url: %s\n", url.c_str()); ret = context->runScript(chunk.contentsBuffer, true, url, nullptr, true); NativeJSLogger::log(INFO, "nativeJS application execution result: %d\n", ret ? 1 : 0); double duration = context->getExecutionDuration(); @@ -398,6 +398,7 @@ void NativeJSRenderer::runApplicationInternal(ApplicationRequest& appRequest) } } +// REQUIRES: Caller must hold mUserMutex void NativeJSRenderer::runJavaScriptInternal(ApplicationRequest& appRequest) { uint32_t id = appRequest.mId; @@ -427,6 +428,7 @@ void NativeJSRenderer::runJavaScriptInternal(ApplicationRequest& appRequest) } } +// REQUIRES: Caller must hold mUserMutex void NativeJSRenderer::terminateApplicationInternal(ApplicationRequest& AppRequest) { uint32_t id = AppRequest.mId; @@ -445,7 +447,7 @@ void NativeJSRenderer::terminateApplicationInternal(ApplicationRequest& AppReque else { - NativeJSLogger::log(ERROR, "Unable to find application with id: %d and url: %s\n", id, mContextMap[id].url); + NativeJSLogger::log(ERROR, "Unable to find application with id: %d\n", id); return ; } @@ -468,11 +470,10 @@ void NativeJSRenderer::run() { while(mRunning) { - uint32_t id; - mUserMutex.lock(); if (mConsoleMode) { processDevConsoleRequests(); } + mUserMutex.lock(); for (int i=0; i localQueue; + + // Move items from shared queue to local queue while holding the lock mConsoleState->inputMutex.lock(); - if (mConsoleState->codeToExecute.empty()) { mConsoleState->inputMutex.unlock(); return; } + localQueue.swap(mConsoleState->codeToExecute); + mConsoleState->inputMutex.unlock(); std::lock_guard lockg(mConsoleState->isProcessing_cv_m); bool dataProcessed = false; - for (; !mConsoleState->codeToExecute.empty(); mConsoleState->codeToExecute.pop_front()) { - bool ret = mConsoleState->consoleContext->runScript(mConsoleState->codeToExecute.front().c_str(), false); + // Process items from local queue (no race condition) + for (const auto& code : localQueue) { + bool ret = mConsoleState->consoleContext->runScript(code.c_str(), false); dataProcessed = true; } @@ -550,8 +559,6 @@ void NativeJSRenderer::processDevConsoleRequests() mConsoleState->isProcessing = false; mConsoleState->isProcessing_cv.notify_one(); } - - mConsoleState->inputMutex.unlock(); } std::atomic_bool NativeJSRenderer::consoleLoop = true; @@ -620,18 +627,34 @@ bool NativeJSRenderer::downloadFile(std::string& url, MemoryStruct& chunk) curl = curl_easy_init(); if (curl) { - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback); - curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)&chunk); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30); - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true); - curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); - curl_easy_setopt(curl, CURLOPT_PROXY, ""); + // Helper lambda to check curl_easy_setopt results + auto setOptWithCheck = [curl](CURLoption option, auto value, const char* optionName) -> bool { + CURLcode optRes = curl_easy_setopt(curl, option, value); + if (optRes != CURLE_OK) { + NativeJSLogger::log(ERROR, "Failed to set %s: %s\n", optionName, curl_easy_strerror(optRes)); + return false; + } + return true; + }; + + // Set options; cleanup and return if any fail + if ( + !setOptWithCheck(CURLOPT_URL, url.c_str(), "CURLOPT_URL") || + !setOptWithCheck(CURLOPT_FOLLOWLOCATION, 1L, "CURLOPT_FOLLOWLOCATION") || + !setOptWithCheck(CURLOPT_HEADERFUNCTION, HeaderCallback, "CURLOPT_HEADERFUNCTION") || + !setOptWithCheck(CURLOPT_HEADERDATA, (void *)&chunk, "CURLOPT_HEADERDATA") || + !setOptWithCheck(CURLOPT_WRITEFUNCTION, WriteMemoryCallback, "CURLOPT_WRITEFUNCTION") || + !setOptWithCheck(CURLOPT_WRITEDATA, (void *)&chunk, "CURLOPT_WRITEDATA") || + !setOptWithCheck(CURLOPT_TIMEOUT, 30L, "CURLOPT_TIMEOUT") || + !setOptWithCheck(CURLOPT_NOSIGNAL, 1L, "CURLOPT_NOSIGNAL") || + !setOptWithCheck(CURLOPT_SSL_VERIFYHOST, 2L, "CURLOPT_SSL_VERIFYHOST") || + !setOptWithCheck(CURLOPT_SSL_VERIFYPEER, 1L, "CURLOPT_SSL_VERIFYPEER") || + !setOptWithCheck(CURLOPT_USERAGENT, "libcurl-agent/1.0", "CURLOPT_USERAGENT") || + !setOptWithCheck(CURLOPT_PROXY, "", "CURLOPT_PROXY") + ) { + curl_easy_cleanup(curl); + return ret; + } //curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); diff --git a/src/jsc/JavaScriptContext.cpp b/src/jsc/JavaScriptContext.cpp index a0af753..3dd3389 100644 --- a/src/jsc/JavaScriptContext.cpp +++ b/src/jsc/JavaScriptContext.cpp @@ -119,6 +119,13 @@ if (mModuleSettings.enablePlayer) gTopLevelContext = nullptr; } mPriv->releaseAllProtected(); + + if (mNetworkMetricsData) + { + delete mNetworkMetricsData; + mNetworkMetricsData = nullptr; + } + JSGlobalContextRelease(mContext); JSContextGroupRelease(mContextGroup); rtLogInfo("%s end", __FUNCTION__); @@ -394,7 +401,7 @@ if (mModuleSettings.enablePlayer) gAAMPJSBindings = new AAMPJSBindings(); loadAAMPJSBindingsLib(); } - if (gAAMPJSBindings->fnLoadJS) + if (gAAMPJSBindings && gAAMPJSBindings->fnLoadJS) { gAAMPJSBindings->fnLoadJS(mContext); } diff --git a/src/jsc/JavaScriptEngine.cpp b/src/jsc/JavaScriptEngine.cpp index bafd1d7..84190ee 100644 --- a/src/jsc/JavaScriptEngine.cpp +++ b/src/jsc/JavaScriptEngine.cpp @@ -135,7 +135,7 @@ bool JavaScriptEngine::initialize() if (garbageCollectInterval) { garbageCollectIntervalValue = atof(garbageCollectInterval); - NativeJSLogger::log(INFO, "garbage collection interval value: %d\n", garbageCollectIntervalValue); + NativeJSLogger::log(INFO, "garbage collection interval value: %f\n", garbageCollectIntervalValue); } mGarbageCollectionTag = installTimeout(garbageCollectIntervalValue, true, [engine] () mutable { diff --git a/src/jsc/JavaScriptUtils.cpp b/src/jsc/JavaScriptUtils.cpp index 5476746..f91c026 100644 --- a/src/jsc/JavaScriptUtils.cpp +++ b/src/jsc/JavaScriptUtils.cpp @@ -113,9 +113,11 @@ rtString jsToRtString(JSStringRef str) void dispatchPending() { - std::unique_lock lock(gDispatchMutex); - std::list> pending = std::move(gPendingFun); - gDispatchMutex.unlock(); + std::list> pending; + { + std::lock_guard lock(gDispatchMutex); + pending = std::move(gPendingFun); + } for(auto& fun : pending) fun(); } @@ -188,7 +190,10 @@ void assertIsMainThread() { AddRef(); if (!downloadRequest->errorString().isEmpty()) { + // Lambda captures by value - pointer fields are properly initialized + // coverity[uninit_member : FALSE] dispatchOnMainLoop( + /* coverity[uninit_member : FALSE] */ [this, errorString = downloadRequest->errorString(), statusCode = downloadRequest->downloadStatusCode()] () { if (statusCode == 28) @@ -241,7 +246,10 @@ void assertIsMainThread() } rtObjectRef protectedRef = resp; + // Lambda captures by value - pointer fields are properly initialized + // coverity[uninit_member : FALSE] dispatchOnMainLoop( + /* coverity[uninit_member : FALSE] */ [this, resp = resp, ref = protectedRef] () { mEmit.send("response", ref); resp->onData(); @@ -314,24 +322,57 @@ rtError rtSetVideoStartTimeBinding(int numArgs, const rtValue* args, rtValue* re rtError rtReadBinaryBinding(int numArgs, const rtValue* args, rtValue* result, void* context) { - char *buffer = nullptr; FILE *ptr = nullptr; ptr = fopen("hello.wasm","rb"); // r for read, b for binary + + if (!ptr) + { + rtLogError("Failed to open file: hello.wasm"); + return RT_ERROR; + } const char *fd = "hello.wasm"; struct stat buf; - stat(fd, &buf); - int size = buf.st_size; + if (stat(fd, &buf) != 0) + { + rtLogError("Failed to stat file: %s", fd); + fclose(ptr); + return RT_ERROR; + } + + if (buf.st_size <= 0) + { + rtLogError("File size is zero or negative: %ld", (long)buf.st_size); + fclose(ptr); + return RT_ERROR; + } - buffer = (char*)malloc(size); - fread(buffer,size,1,ptr); // read 10 bytes to our buffer + size_t size = (size_t)buf.st_size; + + std::unique_ptr buffer(static_cast(malloc(size + 1)), &free); + if (!buffer) + { + rtLogError("Failed to allocate memory for file buffer"); + fclose(ptr); + return RT_ERROR; + } + size_t bytesRead = fread(buffer.get(), 1, size, ptr); fclose(ptr); + if (bytesRead != size) + { + rtLogError("Failed to read file: expected %zu bytes, read %zu bytes", size, bytesRead); + return RT_ERROR; + } + + buffer.get()[bytesRead] = '\0'; + if (result) { - result->setString(buffer); + result->setString(buffer.get()); } + return RT_OK; } @@ -693,6 +734,7 @@ rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* re rtValue keys; if (map->Get("allKeys", &keys) != RT_OK) { rtLogWarn("Could not retrieve url for network metrics data."); + delete netMetricsArray; return RT_FAIL; } rtObjectRef objRef = keys.toObject(); @@ -700,6 +742,7 @@ rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* re if (!keysArray) { rtLogWarn("No url found in the network metrics data."); + delete netMetricsArray; return RT_FAIL; } @@ -715,6 +758,7 @@ rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* re NetworkMetrics* metrics = (NetworkMetrics*)storedValue.toVoidPtr(); if (!metrics) { rtLogError("Failed to cast stored value to NetworkMetrics structure for url: %s.", key.cString()); + delete netMetricsArray; return RT_FAIL; } rtMapObject* metricsMap = new rtMapObject(); diff --git a/src/jsc/jsc_lib/jsc_lib.cpp b/src/jsc/jsc_lib/jsc_lib.cpp index b759135..3810cde 100644 --- a/src/jsc/jsc_lib/jsc_lib.cpp +++ b/src/jsc/jsc_lib/jsc_lib.cpp @@ -29,9 +29,9 @@ static bool supportsRichSourceInfo(const JSGlobalObject*) { return true; } static bool shouldInterruptScript(const JSGlobalObject*) { return true; } static bool shouldInterruptScriptBeforeTimeout(const JSGlobalObject*) { return false; } static RuntimeFlags javaScriptRuntimeFlags(const JSGlobalObject*) { return RuntimeFlags(); } -static void reportUncaughtExceptionAtEventLoop(JSGlobalObject*, Exception* exception) +static void reportUncaughtExceptionAtEventLoop(JSGlobalObject* globalObject, Exception* exception) { - NativeJSLogger::log(ERROR, "Uncaught Exception at run loop: %s\n", exception->value()); + NativeJSLogger::log(ERROR, "Uncaught Exception at run loop: %s\n", exception->value().toWTFString(globalObject).utf8().data()); } static JSObject* currentScriptExecutionOwner(JSGlobalObject* global) { return global; } static ScriptExecutionStatus scriptExecutionStatus(JSGlobalObject*, JSObject*) { return ScriptExecutionStatus::Running; } @@ -150,18 +150,34 @@ bool downloadFile(std::string& url, MemoryStruct& chunk) curl = curl_easy_init(); if (curl) { - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, CallbackHeader); - curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)&chunk); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CallbackOnMemoryWrite); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30); - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true); - curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); - curl_easy_setopt(curl, CURLOPT_PROXY, ""); + // Helper lambda to check curl_easy_setopt results + auto setOptWithCheck = [curl](CURLoption option, auto value, const char* optionName) -> bool { + CURLcode res = curl_easy_setopt(curl, option, value); + if (res != CURLE_OK) { + NativeJSLogger::log(ERROR, "Failed to set %s: %s\n", optionName, curl_easy_strerror(res)); + return false; + } + return true; + }; + + // Set options; cleanup and return if any fail + if ( + !setOptWithCheck(CURLOPT_URL, url.c_str(), "CURLOPT_URL") || + !setOptWithCheck(CURLOPT_FOLLOWLOCATION, 1L, "CURLOPT_FOLLOWLOCATION") || + !setOptWithCheck(CURLOPT_HEADERFUNCTION, CallbackHeader, "CURLOPT_HEADERFUNCTION") || + !setOptWithCheck(CURLOPT_HEADERDATA, (void *)&chunk, "CURLOPT_HEADERDATA") || + !setOptWithCheck(CURLOPT_WRITEFUNCTION, CallbackOnMemoryWrite, "CURLOPT_WRITEFUNCTION") || + !setOptWithCheck(CURLOPT_WRITEDATA, (void *)&chunk, "CURLOPT_WRITEDATA") || + !setOptWithCheck(CURLOPT_TIMEOUT, 30L, "CURLOPT_TIMEOUT") || + !setOptWithCheck(CURLOPT_NOSIGNAL, 1L, "CURLOPT_NOSIGNAL") || + !setOptWithCheck(CURLOPT_SSL_VERIFYHOST, 2L, "CURLOPT_SSL_VERIFYHOST") || + !setOptWithCheck(CURLOPT_SSL_VERIFYPEER, 1L, "CURLOPT_SSL_VERIFYPEER") || + !setOptWithCheck(CURLOPT_USERAGENT, "libcurl-agent/1.0", "CURLOPT_USERAGENT") || + !setOptWithCheck(CURLOPT_PROXY, "", "CURLOPT_PROXY") + ) { + curl_easy_cleanup(curl); + return ret; + } //curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); @@ -388,7 +404,7 @@ static URL currentWorkingDirectory() return { }; // Add a trailing slash if needed so the URL resolves to a directory and not a file. - if (directoryString[directoryString.length() - 1] != pathSeparator()) + if (!directoryString.endsWith(pathSeparator())) directoryString = makeString(directoryString, pathSeparator()); return URL::fileURLWithFileSystemPath(directoryString); diff --git a/src/jsruntime.cpp b/src/jsruntime.cpp index fdbf989..61009e5 100644 --- a/src/jsruntime.cpp +++ b/src/jsruntime.cpp @@ -37,6 +37,7 @@ using namespace JsRuntime; #ifndef UNIT_TEST_BUILD int main(int argc, char* argv[]) { + try { if (argc < 2) { NativeJSLogger::log(WARN, "Pass the URL to run\n"); @@ -47,7 +48,7 @@ int main(int argc, char* argv[]) std::string waylanddisplay(""); bool enableJSDOM = false, enableWindow = false, enablePlayer = false, enableWebSocketEnhanced = false, enableFetch = false; - int i = 1, appendindex=argc-1; + int i = 1; std::vector applications; ModuleSettings moduleSettings; bool consoleMode = false; @@ -55,9 +56,13 @@ int main(int argc, char* argv[]) { if (strcmp(argv[i], "--display") == 0) { - appendindex = i-1; i++; - waylanddisplay = argv[i]; + if (i < argc) { + waylanddisplay = argv[i]; + } + else { + NativeJSLogger::log(WARN, "--display flag provided without a value\n"); + } } else if (strcmp(argv[i], "--enableHttp") == 0) { @@ -107,16 +112,16 @@ int main(int argc, char* argv[]) } i++; } - + + // CID:430751 - Intentional: waylanddisplay from command line argument + // This is a display socket name passed to Wayland compositor, used only for + // local display connection. The value is passed to system compositor APIs + // which handle validation. No injection risk as it's used as display identifier only. + /* coverity[tainted_data] */ std::shared_ptr renderer = std::make_shared(waylanddisplay); if (consoleMode) { renderer->setEnvForConsoleMode(moduleSettings); } - if (!renderer) - { - NativeJSLogger::log(ERROR, "Unable to run application\n"); - return -1; - } #if defined(ENABLE_JSRUNTIME_SERVER) if (runServer == true) @@ -172,6 +177,13 @@ int main(int argc, char* argv[]) } return 0; + } catch (const std::exception& e) { + NativeJSLogger::log(ERROR, "Uncaught exception in main: %s\n", e.what()); + return -1; + } catch (...) { + NativeJSLogger::log(ERROR, "Unknown exception caught in main\n"); + return -1; + } } #endif From 893963239210611ccdf94ece9255a276890b553b Mon Sep 17 00:00:00 2001 From: Vinod Jain <98183059+vjain008@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:03:23 -0600 Subject: [PATCH 2/5] Update src/jsruntime.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/jsruntime.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/jsruntime.cpp b/src/jsruntime.cpp index 61009e5..cc0c45c 100644 --- a/src/jsruntime.cpp +++ b/src/jsruntime.cpp @@ -63,6 +63,9 @@ int main(int argc, char* argv[]) else { NativeJSLogger::log(WARN, "--display flag provided without a value\n"); } + else { + NativeJSLogger::log(WARN, "Warning: --display flag provided without a value, using default display\n"); + } } else if (strcmp(argv[i], "--enableHttp") == 0) { From 7f12965977c5a65d384a17bc6c2422992ac12571 Mon Sep 17 00:00:00 2001 From: Sidhanth B H Date: Thu, 5 Feb 2026 12:12:03 +0530 Subject: [PATCH 3/5] Update src/jsruntime.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/jsruntime.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/jsruntime.cpp b/src/jsruntime.cpp index cc0c45c..61009e5 100644 --- a/src/jsruntime.cpp +++ b/src/jsruntime.cpp @@ -63,9 +63,6 @@ int main(int argc, char* argv[]) else { NativeJSLogger::log(WARN, "--display flag provided without a value\n"); } - else { - NativeJSLogger::log(WARN, "Warning: --display flag provided without a value, using default display\n"); - } } else if (strcmp(argv[i], "--enableHttp") == 0) { From c74416679b8e598e90d8a0d6994b28b1ec9d53fc Mon Sep 17 00:00:00 2001 From: Sidhanth B H Date: Thu, 5 Feb 2026 14:54:53 +0530 Subject: [PATCH 4/5] Update src/jsc/jsc_lib/jsc_lib.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/jsc/jsc_lib/jsc_lib.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/jsc/jsc_lib/jsc_lib.cpp b/src/jsc/jsc_lib/jsc_lib.cpp index 3810cde..07f8f66 100644 --- a/src/jsc/jsc_lib/jsc_lib.cpp +++ b/src/jsc/jsc_lib/jsc_lib.cpp @@ -31,6 +31,15 @@ static bool shouldInterruptScriptBeforeTimeout(const JSGlobalObject*) { return f static RuntimeFlags javaScriptRuntimeFlags(const JSGlobalObject*) { return RuntimeFlags(); } static void reportUncaughtExceptionAtEventLoop(JSGlobalObject* globalObject, Exception* exception) { + if (!exception) { + NativeJSLogger::log(ERROR, "Uncaught Exception at run loop: \n"); + return; + } + + if (!globalObject) { + NativeJSLogger::log(ERROR, "Uncaught Exception at run loop: \n"); + return; + } NativeJSLogger::log(ERROR, "Uncaught Exception at run loop: %s\n", exception->value().toWTFString(globalObject).utf8().data()); } static JSObject* currentScriptExecutionOwner(JSGlobalObject* global) { return global; } From 4de5f6f5d4466fd36d997654f4b57ac922b37929 Mon Sep 17 00:00:00 2001 From: Sidhanth B H Date: Wed, 11 Feb 2026 19:02:00 +0530 Subject: [PATCH 5/5] Fixing the Coverity Issue CID:430751 --- src/jsruntime.cpp | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/jsruntime.cpp b/src/jsruntime.cpp index 61009e5..cdfe54e 100644 --- a/src/jsruntime.cpp +++ b/src/jsruntime.cpp @@ -34,6 +34,32 @@ using namespace std; using namespace JsRuntime; +// Sanitize wayland display name for security +static bool sanitizeDisplayName(const char* input, std::string& output) { + if (!input) return false; + + size_t len = strlen(input); + if (len == 0 || len > 64) return false; + + output.clear(); + output.reserve(len); + + for (size_t i = 0; i < len; ++i) { + char c = input[i]; + bool isAlphaNum = (c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z'); + if (isAlphaNum || c == '-' || c == '_' || c == '.') { + output += c; + } else { + output.clear(); + return false; + } + } + + return true; +} + #ifndef UNIT_TEST_BUILD int main(int argc, char* argv[]) { @@ -58,7 +84,13 @@ int main(int argc, char* argv[]) { i++; if (i < argc) { - waylanddisplay = argv[i]; + std::string sanitizedDisplay; + if (!sanitizeDisplayName(argv[i], sanitizedDisplay)) { + NativeJSLogger::log(WARN, "Invalid display name provided. Must be 1-64 characters containing only alphanumeric, hyphens, underscores, and dots\n"); + } else { + NativeJSLogger::log(INFO, "Using display: '%s'\n", sanitizedDisplay.c_str()); + waylanddisplay = sanitizedDisplay; + } } else { NativeJSLogger::log(WARN, "--display flag provided without a value\n"); @@ -113,11 +145,6 @@ int main(int argc, char* argv[]) i++; } - // CID:430751 - Intentional: waylanddisplay from command line argument - // This is a display socket name passed to Wayland compositor, used only for - // local display connection. The value is passed to system compositor APIs - // which handle validation. No injection risk as it's used as display identifier only. - /* coverity[tainted_data] */ std::shared_ptr renderer = std::make_shared(waylanddisplay); if (consoleMode) { renderer->setEnvForConsoleMode(moduleSettings);