From 3cf1c1c64d3473e19923cbe49e87bda29ce6932b Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy B <57708013+pavankumar464@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:39:27 +0530 Subject: [PATCH 1/5] RDKB-66116 : use freedesktop dbus-1.14 and gate native-build/CodeQL by source paths (#31) Reason for change: Address PRs native builds failing in the javascript-templates Test Procedure: PRs native builds should pass for javascript-templates Risks: Low Priority: P2 --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/native-build.yml | 192 +++++++++++++++++++++++- cov_docker_script/component_config.json | 4 +- 2 files changed, 192 insertions(+), 4 deletions(-) diff --git a/.github/workflows/native-build.yml b/.github/workflows/native-build.yml index 5be45d3..e7008d2 100644 --- a/.github/workflows/native-build.yml +++ b/.github/workflows/native-build.yml @@ -6,16 +6,49 @@ on: pull_request: branches: [ main, 'sprint/**', 'release/**', topic/RDK*, develop ] +permissions: + actions: read + contents: read + pull-requests: read + jobs: + build-jst-on-push: + name: Build javascript-templates component on push + if: github.event_name == 'push' + runs-on: ubuntu-latest + container: + image: ghcr.io/rdkcentral/docker-rdk-ci:latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: native build + run: | + # Trust the workspace + git config --global --add safe.directory '*' + # Pull the latest changes for the native build system + git submodule update --init --recursive --remote + # Build and install dependencies + chmod +x build_tools_workflows/cov_docker_script/setup_dependencies.sh + ./build_tools_workflows/cov_docker_script/setup_dependencies.sh ./cov_docker_script/component_config.json + # Build component + chmod +x build_tools_workflows/cov_docker_script/build_native.sh + ./build_tools_workflows/cov_docker_script/build_native.sh ./cov_docker_script/component_config.json "$(pwd)" + env: + GITHUB_TOKEN: ${{ secrets.RDKCM_RDKE }} + build-jst-on-pr: - name: Build javascript-templates component in github rdkcentral + name: Build javascript-templates component on PR + needs: detect-source-changes + if: github.event_name == 'pull_request' && needs.detect-source-changes.outputs.has_component == 'true' runs-on: ubuntu-latest container: image: ghcr.io/rdkcentral/docker-rdk-ci:latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: native build run: | @@ -31,3 +64,158 @@ jobs: ./build_tools_workflows/cov_docker_script/build_native.sh ./cov_docker_script/component_config.json "$(pwd)" env: GITHUB_TOKEN: ${{ secrets.RDKCM_RDKE }} + + detect-source-changes: + name: Detect source path changes for CodeQL + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + outputs: + has_component: ${{ steps.filter.outputs.component }} + has_cpp: ${{ steps.filter.outputs.cpp }} + has_python: ${{ steps.filter.outputs.python }} + has_js: ${{ steps.filter.outputs.javascript }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Detect changed source paths + id: filter + uses: dorny/paths-filter@v4 + with: + predicate-quantifier: some-with-excludes + filters: | + component: + - 'source/**/*' + cpp: + - 'source/**/*.c' + - 'source/**/*.h' + - 'source/**/*.cpp' + - 'tools/**/*.c' + - 'tools/**/*.h' + - 'tools/**/*.cpp' + - 'tests/**/*.c' + - 'tests/**/*.h' + - 'tests/**/*.cpp' + python: + - 'build_tools_workflows/**/*.py' + - 'cov_docker_script/**/*.py' + - 'tools/**/*.py' + - 'tests/**/*.py' + javascript: + - 'source/**/*.js' + - 'source/**/*.ts' + - 'jsts/**/*.js' + - 'jsts/**/*.ts' + - 'tests/**/*.js' + - 'tests/**/*.ts' + - '!jsts/jst_prefix.js' + - '!jsts/jst_suffix.js' + - '!tests/parser/jst_prefix.js' + - '!tests/parser/jst_suffix.js' + - '!tests/parser/**/*.jst.parsed' + + codeql-c-cpp: + name: CodeQL (C/C++) + needs: detect-source-changes + if: github.event_name == 'pull_request' && needs.detect-source-changes.outputs.has_cpp == 'true' + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + container: + image: ghcr.io/rdkcentral/docker-rdk-ci:latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Initialize CodeQL (C/C++) + uses: github/codeql-action/init@v4 + with: + languages: c-cpp + build-mode: manual + + - name: Build component for CodeQL + run: | + git config --global --add safe.directory '*' + git submodule update --init --recursive + chmod +x build_tools_workflows/cov_docker_script/setup_dependencies.sh + ./build_tools_workflows/cov_docker_script/setup_dependencies.sh ./cov_docker_script/component_config.json + chmod +x build_tools_workflows/cov_docker_script/build_native.sh + ./build_tools_workflows/cov_docker_script/build_native.sh ./cov_docker_script/component_config.json "$(pwd)" + env: + GITHUB_TOKEN: ${{ secrets.RDKCM_RDKE }} + + - name: Analyze C/C++ + uses: github/codeql-action/analyze@v4 + with: + category: '/language:c-cpp' + + codeql-python: + name: CodeQL (Python) + needs: detect-source-changes + if: github.event_name == 'pull_request' && needs.detect-source-changes.outputs.has_python == 'true' + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Initialize CodeQL (Python) + uses: github/codeql-action/init@v4 + with: + languages: python + build-mode: none + config: | + paths: + - build_tools_workflows + - cov_docker_script + - tools + - tests + + - name: Analyze Python + uses: github/codeql-action/analyze@v4 + with: + category: '/language:python' + + codeql-javascript: + name: CodeQL (JavaScript) + needs: detect-source-changes + if: github.event_name == 'pull_request' && needs.detect-source-changes.outputs.has_js == 'true' + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Initialize CodeQL (JavaScript) + uses: github/codeql-action/init@v4 + with: + languages: javascript-typescript + build-mode: none + config: | + paths: + - source + - jsts + - tests + paths-ignore: + - jsts/jst_prefix.js + - jsts/jst_suffix.js + - tests/parser/jst_prefix.js + - tests/parser/jst_suffix.js + - tests/parser/**/*.jst.parsed + - name: Analyze JavaScript + uses: github/codeql-action/analyze@v4 + with: + category: '/language:javascript-typescript' diff --git a/cov_docker_script/component_config.json b/cov_docker_script/component_config.json index c4d738f..e3e6448 100644 --- a/cov_docker_script/component_config.json +++ b/cov_docker_script/component_config.json @@ -75,8 +75,8 @@ }, { "name": "dbus", - "repo": "https://github.com/deepin-community/dbus.git", - "branch" : "master", + "repo": "https://gitlab.freedesktop.org/dbus/dbus.git", + "branch": "dbus-1.14", "build": { "type": "cmake", "build_dir": "build", From 6b11cca2860472c1c93799e98264ec6103852d9b Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy B <57708013+pavankumar464@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:56:54 +0530 Subject: [PATCH 2/5] RDKB-65597 : [Risk-High] JST (Generic) Security Fuzzing Report (#35) RDKB-65597 : [Risk-High] Fixing JST (Generic) Security Fuzzing Report Reason for change: Root Cause: ftell() causing calloc() to request an enormous allocation and trigger an OOM abort. Recommendation - Check `ftell()` return value for `-1` before using it as allocation size: Root Cause: `strtok()` on const/env Memory Recommendation - Replace destructive strtok() parsing with read-only boundary detection using strchr(), copy the session ID into a local writable buffer, and use that buffer for validation and file lookup. Root Cause: Session identifier validation can be bypassed, potentially allowing session hijacking. Recommendation - Check Session IDs length and prefix, it should contain only alphanumeric suffix characters, avoid in-place cookie modification during parsing, and are accepted only if the corresponding session file exists. Test Procedure: WebGUI should work as expected Risks: Medium Priority: P1 --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- source/jst_internal.c | 28 ++++++++++++++++++++++++++-- source/jst_session.c | 28 +++++++++++++++++++--------- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/source/jst_internal.c b/source/jst_internal.c index e7312f0..4d55f94 100644 --- a/source/jst_internal.c +++ b/source/jst_internal.c @@ -164,6 +164,7 @@ int read_file(const char *filename, char** bufout, size_t* lenout) { FILE* pf; size_t size; + long tell_result; size_t rc; char* buf; @@ -179,8 +180,31 @@ int read_file(const char *filename, char** bufout, size_t* lenout) return 0; } - fseek(pf, 0, SEEK_END); - size = ftell(pf); + if (fseek(pf, 0, SEEK_END) != 0) + { + CosaPhpExtLog("read_file fseek failed:%s error:%s\n", filename, strerror(errno)); + fclose(pf); + fprintf(stderr, "Error: fseek failed %s\n", filename); + return 0; + } + + errno = 0; + tell_result = ftell(pf); + if (tell_result < 0) + { + CosaPhpExtLog("read_file ftell failed:%s error:%s\n", filename, strerror(errno)); + fclose(pf); + fprintf(stderr, "Error: ftell failed %s\n", filename); + return 0; + } + if ((unsigned long long)tell_result > (unsigned long long)(SIZE_MAX - 1)) + { + CosaPhpExtLog("read_file size overflow:%s size:%ld\n", filename, tell_result); + fclose(pf); + fprintf(stderr, "Error: file too large %s\n", filename); + return 0; + } + size = (size_t)tell_result; rewind(pf); buf = (char*)calloc(size+1, 1); diff --git a/source/jst_session.c b/source/jst_session.c index 69f542f..5d0d334 100644 --- a/source/jst_session.c +++ b/source/jst_session.c @@ -75,6 +75,9 @@ static duk_ret_t session_start(duk_context *ctx) { CosaPhpExtLog("%s: entered\n", __PRETTY_FUNCTION__); const char* cookie; + const char* sesid_end; + size_t sesid_len; + char parsed_sesid[SESSION_ID_LENGTH + 1]; /* if session already created then do nothing */ if(session_identifier) { @@ -112,14 +115,20 @@ static duk_ret_t session_start(duk_context *ctx) if(sesid) { sesid += 7; - int len = strlen(sesid); - if(len >= SESSION_ID_LENGTH) + sesid_end = strchr(sesid, ';'); + sesid_len = sesid_end ? (size_t)(sesid_end - sesid) : strlen(sesid); + if(sesid_len == SESSION_ID_LENGTH) { int idx = SESSION_PREFIX_LEN; int isvalid = 1; + if(strncmp(sesid, SESSION_PREFIX, SESSION_PREFIX_LEN) != 0) + { + CosaPhpExtLog("Invalid SessionID prefix\n"); + isvalid = 0; + } /* Validate session ID*/ - while ( idx < SESSION_ID_LENGTH) { - if (!isalnum(sesid[idx])) { + while (isvalid && idx < SESSION_ID_LENGTH) { + if (!isalnum((unsigned char)sesid[idx])) { CosaPhpExtLog("Invalid SessionID\n"); isvalid = 0; break; @@ -128,19 +137,20 @@ static duk_ret_t session_start(duk_context *ctx) } if(isvalid) { - sesid = strtok(sesid, ";"); - const char filename[SESSION_FILE_MAX_PATH]; - snprintf(filename, SESSION_FILE_MAX_PATH, "%s/%s", SESSION_TMP_DIR, sesid); + memcpy(parsed_sesid, sesid, SESSION_ID_LENGTH); + parsed_sesid[SESSION_ID_LENGTH] = '\0'; + char filename[SESSION_FILE_MAX_PATH]; + snprintf(filename, SESSION_FILE_MAX_PATH, "%s/%s", SESSION_TMP_DIR, parsed_sesid); CosaPhpExtLog("%s: Checking for Session file %s\n", __PRETTY_FUNCTION__, filename); if (access(filename, F_OK) == 0) { CosaPhpExtLog("%s: Session file %s exists\n", __PRETTY_FUNCTION__, filename); - strncpy(session_identifier, sesid, SESSION_ID_LENGTH); + strncpy(session_identifier, parsed_sesid, SESSION_ID_LENGTH); } else { CosaPhpExtLog("%s: Failed to read Session file %s\n", __PRETTY_FUNCTION__, filename); } } - } else { + } else { CosaPhpExtLog("Invalid SessionID Entropy\n"); } } From 4d69e6e4bf48f5768bdb7dd9939fd9d65debfd9d Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy B <57708013+pavankumar464@users.noreply.github.com> Date: Thu, 20 Aug 2026 06:03:49 +0530 Subject: [PATCH 3/5] RDKB-66532 RDKB-66534 : Validate session ID format before handling (#36) Reason for change: Fix session ID format before handling Test Procedure: Test for session identifier Risks: High Priority: P1 --- source/jst_session.c | 142 +++++++++++++++++++++++++++++------------- tests/parser_test.cpp | 116 ++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+), 42 deletions(-) diff --git a/source/jst_session.c b/source/jst_session.c index 5d0d334..1935b6d 100644 --- a/source/jst_session.c +++ b/source/jst_session.c @@ -71,6 +71,54 @@ static char* session_identifier = NULL; +static int is_valid_session_identifier(const char* session_id) +{ + size_t idx; + + if(!session_id) + return 0; + + if(strlen(session_id) != SESSION_ID_LENGTH) + { + CosaPhpExtLog("Invalid SessionID length\n"); + return 0; + } + + if(strncmp(session_id, SESSION_PREFIX, SESSION_PREFIX_LEN) != 0) + { + CosaPhpExtLog("Invalid SessionID prefix\n"); + return 0; + } + + for(idx = SESSION_PREFIX_LEN; idx < SESSION_ID_LENGTH; ++idx) + { + if(!isalnum((unsigned char)session_id[idx])) + { + CosaPhpExtLog("Invalid SessionID token\n"); + return 0; + } + } + + return 1; +} + +static int get_session_file_path(const char* session_id, char* path, size_t path_len) +{ + int written; + + if(!is_valid_session_identifier(session_id)) + return 0; + + written = snprintf(path, path_len, "%s/%s", SESSION_TMP_DIR, session_id); + if(written < 0 || (size_t)written >= path_len) + { + CosaPhpExtLog("Failed to build session path\n"); + return 0; + } + + return 1; +} + static duk_ret_t session_start(duk_context *ctx) { CosaPhpExtLog("%s: entered\n", __PRETTY_FUNCTION__); @@ -82,7 +130,12 @@ static duk_ret_t session_start(duk_context *ctx) if(session_identifier) { char path[SESSION_FILE_MAX_PATH]; - snprintf(path, SESSION_FILE_MAX_PATH, "%s/%s", SESSION_TMP_DIR, session_identifier); + if(!get_session_file_path(session_identifier, path, sizeof(path))) + { + free(session_identifier); + session_identifier = NULL; + RETURN_FALSE; + } if(utime(path, NULL) != 0) { CosaPhpExtLog("failed to update last accesstime on file %s: %s", path, strerror(errno)); @@ -117,48 +170,42 @@ static duk_ret_t session_start(duk_context *ctx) sesid += 7; sesid_end = strchr(sesid, ';'); sesid_len = sesid_end ? (size_t)(sesid_end - sesid) : strlen(sesid); - if(sesid_len == SESSION_ID_LENGTH) + if(sesid_len == SESSION_ID_LENGTH) + { + memcpy(parsed_sesid, sesid, SESSION_ID_LENGTH); + parsed_sesid[SESSION_ID_LENGTH] = '\0'; + + if(is_valid_session_identifier(parsed_sesid)) + { + char filename[SESSION_FILE_MAX_PATH]; + if(get_session_file_path(parsed_sesid, filename, sizeof(filename))) + { + CosaPhpExtLog("%s: Checking for Session file %s\n", __PRETTY_FUNCTION__, filename); + if(access(filename, F_OK) == 0) + { + CosaPhpExtLog("%s: Session file %s exists\n", __PRETTY_FUNCTION__, filename); + memcpy(session_identifier, parsed_sesid, SESSION_ID_LENGTH); + session_identifier[SESSION_ID_LENGTH] = '\0'; + } + else + { + CosaPhpExtLog("%s: Failed to read Session file %s\n", __PRETTY_FUNCTION__, filename); + } + } + } + } + else { - int idx = SESSION_PREFIX_LEN; - int isvalid = 1; - if(strncmp(sesid, SESSION_PREFIX, SESSION_PREFIX_LEN) != 0) - { - CosaPhpExtLog("Invalid SessionID prefix\n"); - isvalid = 0; - } - /* Validate session ID*/ - while (isvalid && idx < SESSION_ID_LENGTH) { - if (!isalnum((unsigned char)sesid[idx])) { - CosaPhpExtLog("Invalid SessionID\n"); - isvalid = 0; - break; - } - idx++; - } - if(isvalid) - { - memcpy(parsed_sesid, sesid, SESSION_ID_LENGTH); - parsed_sesid[SESSION_ID_LENGTH] = '\0'; - char filename[SESSION_FILE_MAX_PATH]; - snprintf(filename, SESSION_FILE_MAX_PATH, "%s/%s", SESSION_TMP_DIR, parsed_sesid); - CosaPhpExtLog("%s: Checking for Session file %s\n", __PRETTY_FUNCTION__, filename); - if (access(filename, F_OK) == 0) - { - CosaPhpExtLog("%s: Session file %s exists\n", __PRETTY_FUNCTION__, filename); - strncpy(session_identifier, parsed_sesid, SESSION_ID_LENGTH); - } else { - CosaPhpExtLog("%s: Failed to read Session file %s\n", __PRETTY_FUNCTION__, filename); - } - } - } else { - CosaPhpExtLog("Invalid SessionID Entropy\n"); + CosaPhpExtLog("Invalid SessionID Entropy\n"); } } } if(!session_identifier[0]) { - CosaPhpExtLog("Invalid Session\n"); - RETURN_FALSE; + CosaPhpExtLog("Invalid Session\n"); + free(session_identifier); + session_identifier = NULL; + RETURN_FALSE; } RETURN_TRUE; @@ -196,8 +243,8 @@ static duk_ret_t session_create(duk_context *ctx) if(session_identifier) { char filename[SESSION_FILE_MAX_PATH]; - snprintf(filename, SESSION_FILE_MAX_PATH, "%s/%s", SESSION_TMP_DIR, session_identifier); - unlink(filename); + if(get_session_file_path(session_identifier, filename, sizeof(filename))) + unlink(filename); free(session_identifier); session_identifier = NULL; } @@ -244,7 +291,12 @@ static duk_ret_t session_get_data(duk_context *ctx) idx = duk_push_object(ctx); - snprintf(filename, SESSION_FILE_MAX_PATH, "%s/%s", SESSION_TMP_DIR, session_identifier); + if(!get_session_file_path(session_identifier, filename, sizeof(filename))) + { + duk_pop(ctx); + duk_push_object(ctx); + return 1; + } CosaPhpExtLog( "session_get_data filename=%s\n", filename ); @@ -365,7 +417,8 @@ static duk_ret_t session_set_data(duk_context *ctx) RETURN_FALSE; } - snprintf(filename, SESSION_FILE_MAX_PATH, "%s/%s", SESSION_TMP_DIR, session_identifier); + if(!get_session_file_path(session_identifier, filename, sizeof(filename))) + RETURN_FALSE; CosaPhpExtLog( "session_set_data filename=%s\n", filename ); @@ -449,7 +502,12 @@ static duk_ret_t session_destroy(duk_context *ctx) if(session_identifier) { /*remove the session file*/ - snprintf(filename, SESSION_FILE_MAX_PATH, "%s/%s", SESSION_TMP_DIR, session_identifier); + if(!get_session_file_path(session_identifier, filename, sizeof(filename))) + { + free(session_identifier); + session_identifier = NULL; + RETURN_FALSE; + } CosaPhpExtLog( "session_destroy removing %s\n", filename ); diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index 897e536..d7eb028 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -134,6 +134,11 @@ class StdinRedirectGuard bool active_; }; +static std::string makeValidSessionId(char fill) +{ + return std::string("jst_sess") + std::string(32, fill); +} + static string getFieldValue(const string& input, const string& key) { string pattern = key + "="; @@ -341,6 +346,117 @@ TEST(general, session_create_destroy_cycle_and_id_format) duk_destroy_heap(ctx); } +TEST(general, session_start_accepts_existing_valid_cookie_id) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + const std::string session_id = makeValidSessionId('A'); + const std::string cookie = "DUKSID=" + session_id; + const std::string session_file = "/tmp/" + session_id; + + FILE* file = fopen(session_file.c_str(), "w"); + ASSERT_NE(file, nullptr); + fclose(file); + + cookie_guard.set(cookie.c_str()); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "start"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_TRUE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "getId"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + ASSERT_TRUE(duk_is_string(ctx, -1)); + EXPECT_STREQ(duk_get_string(ctx, -1), session_id.c_str()); + duk_pop_2(ctx); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "destroy"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_TRUE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); +} + +TEST(general, session_start_rejects_invalid_cookie_ids) +{ + const std::vector cookies = { + "DUKSID=jst_sessshort", + "DUKSID=jst_sessAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/", + "DUKSID=jst_sesAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "DUKSID=jst_sessAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!", + "DUKSID=jst_sessAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAextra"}; + + for (const std::string& cookie : cookies) + { + EnvVarGuard cookie_guard("HTTP_COOKIE"); + cookie_guard.set(cookie.c_str()); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "start"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)) << "cookie was unexpectedly accepted: " << cookie; + duk_pop_2(ctx); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "getStatus"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); + } +} + +TEST(general, session_start_rejects_missing_session_file) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + const std::string session_id = makeValidSessionId('B'); + const std::string cookie = "DUKSID=" + session_id; + const std::string session_file = "/tmp/" + session_id; + + unlink(session_file.c_str()); + cookie_guard.set(cookie.c_str()); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "start"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "getStatus"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); +} + int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); From 471bdb13d4d5e6d4a42fd39db973f7727a63d4b5 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy B <57708013+pavankumar464@users.noreply.github.com> Date: Thu, 20 Aug 2026 06:43:10 +0530 Subject: [PATCH 4/5] RDKB-65595 : [Risk-Critical] JST (Generic) Security Fuzzing Report (#34) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed Heap Buffer Overflow in `do_openssl_verify_with_cert` Recommendation - Check `strlen(filepath) >= 7` before calling `memcmp`, or use `strncmp` which handles short strings safely Fixed Command Injection via `popen()` Recommendation - Never pass untrusted input to `popen()` — use `execve()` with argument arrays or sanitize input --------- Co-authored-by: anoopchelakkode <65686868+anoopchelakkode@users.noreply.github.com> --- source/jst_functions.c | 167 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 161 insertions(+), 6 deletions(-) diff --git a/source/jst_functions.c b/source/jst_functions.c index a09e7ad..7f504c3 100644 --- a/source/jst_functions.c +++ b/source/jst_functions.c @@ -18,8 +18,12 @@ */ #include #include +#include +#include #include #include +#include +#include #include #include "jst_internal.h" #include "jst.h" @@ -42,6 +46,108 @@ typedef struct { size_t memsize; // allocated memory size (if applicable) } MemData; +typedef struct { + char** argv; + char* command_copy; +} ExecArgv; + +static int is_exec_command_safe(const char* command) +{ + const char* p = NULL; + + if (!command || !*command) + return 0; + + for (p = command; *p; ++p) + { + if (isalnum((unsigned char)*p) || *p == '/' || *p == '.' || *p == '_' || + *p == '-' || *p == ':' || *p == '=' || *p == '+' || *p == '@' || + isspace((unsigned char)*p)) + { + continue; + } + + return 0; + } + + return 1; +} + +static void free_exec_argv(ExecArgv* exec_argv) +{ + if (exec_argv) + { + free(exec_argv->command_copy); + free(exec_argv->argv); + exec_argv->command_copy = NULL; + exec_argv->argv = NULL; + } +} + +static int build_exec_argv(const char* command, ExecArgv* exec_argv) +{ + char* command_copy = NULL; + char* command_scan_copy = NULL; + char* scan_ctx = NULL; + char* token = NULL; + int argc = 0; + int i = 0; + char** argv = NULL; + + if (!exec_argv) + return 0; + + exec_argv->argv = NULL; + exec_argv->command_copy = NULL; + + command_copy = strdup(command); + if (!command_copy) + return 0; + + command_scan_copy = strdup(command); + if (!command_scan_copy) + { + free(command_copy); + return 0; + } + + token = strtok_r(command_scan_copy, " \t\r\n", &scan_ctx); + while (token) + { + argc++; + token = strtok_r(NULL, " \t\r\n", &scan_ctx); + } + + free(command_scan_copy); + command_scan_copy = NULL; + + if (argc == 0) + { + free(command_copy); + return 0; + } + + argv = calloc((size_t)argc + 1, sizeof(char*)); + if (!argv) + { + free(command_copy); + return 0; + } + + scan_ctx = NULL; + token = strtok_r(command_copy, " \t\r\n", &scan_ctx); + while (token && i < argc) + { + argv[i++] = token; + token = strtok_r(NULL, " \t\r\n", &scan_ctx); + } + argv[i] = NULL; + + exec_argv->argv = argv; + exec_argv->command_copy = command_copy; + return 1; +} + static duk_ret_t do_getenv(duk_context *ctx) { @@ -148,28 +254,75 @@ static duk_ret_t do_gettext(duk_context *ctx) static duk_ret_t do_exec(duk_context *ctx) { char* command; + ExecArgv exec_argv = {0}; + int pipefd[2] = {-1, -1}; + pid_t child_pid; + int child_status; char *line = NULL; size_t len = 0; ssize_t nread; duk_idx_t idx; int index = 0; + FILE* output_pipe = NULL; idx = duk_push_array(ctx); if (!parse_parameter(__FUNCTION__, ctx, "s", &command)) return 1; + if (!is_exec_command_safe(command)) + { + CosaPhpExtLog("exec rejected unsafe command input\n"); + return 1; + } + + if (!build_exec_argv(command, &exec_argv)) + { + CosaPhpExtLog("exec failed to parse command arguments\n"); + return 1; + } + CosaPhpExtLog("exec command=%s\n", command); - FILE* pipe = popen(command, "r"); - if (!pipe) + if (pipe(pipefd) != 0) + { + CosaPhpExtLog("exec failed to create pipe error=%s\n", strerror(errno)); + free_exec_argv(&exec_argv); + return 1; + } + + child_pid = fork(); + if (child_pid < 0) + { + CosaPhpExtLog("exec failed to fork error=%s\n", strerror(errno)); + close(pipefd[0]); + close(pipefd[1]); + free_exec_argv(&exec_argv); + return 1; + } + + if (child_pid == 0) + { + close(pipefd[0]); + dup2(pipefd[1], STDOUT_FILENO); + dup2(pipefd[1], STDERR_FILENO); + close(pipefd[1]); + execvp(exec_argv.argv[0], exec_argv.argv); + _exit(127); + } + + close(pipefd[1]); + output_pipe = fdopen(pipefd[0], "r"); + if (!output_pipe) { CosaPhpExtLog("exec failed to open pipe\n"); - duk_pop(ctx); + close(pipefd[0]); + waitpid(child_pid, &child_status, 0); + free_exec_argv(&exec_argv); return 1; } - while((nread = getline(&line, &len, pipe)) != -1) + while((nread = getline(&line, &len, output_pipe)) != -1) { CosaPhpExtLog("exec line: %s\n", line); duk_push_string(ctx, line); @@ -177,7 +330,9 @@ static duk_ret_t do_exec(duk_context *ctx) } free(line); - pclose(pipe); + fclose(output_pipe); + waitpid(child_pid, &child_status, 0); + free_exec_argv(&exec_argv); return 1; } @@ -615,7 +770,7 @@ static duk_ret_t do_openssl_verify_with_cert(duk_context *ctx) /* === NOW PROCEED WITH SIGNATURE VERIFICATION === */ //open certificate file - if(memcmp(filepath, "file://", sizeof("file://")-1) != 0) + if(strncmp(filepath, "file://", sizeof("file://")-1) != 0) { CosaPhpExtLog("openssl_verify_with_cert: file %s doesn't begin with 'file://'\n", filepath); free(sig_bytes); From 664c13e2f971516443bca4f3c0aa9c3c01daddf6 Mon Sep 17 00:00:00 2001 From: bunnam988 Date: Fri, 21 Aug 2026 07:03:12 +0000 Subject: [PATCH 5/5] Add changelog for release 2.9.0 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a023c6..f04b97c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,28 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [2.9.0](https://github.com/rdkcentral/javascript-templates/compare/2.8.0...2.9.0) + +- RDKB-65595 : [Risk-Critical] JST (Generic) Security Fuzzing Report [`#34`](https://github.com/rdkcentral/javascript-templates/pull/34) +- RDKB-66532 RDKB-66534 : Validate session ID format before handling [`#36`](https://github.com/rdkcentral/javascript-templates/pull/36) +- RDKB-65597 : [Risk-High] JST (Generic) Security Fuzzing Report [`#35`](https://github.com/rdkcentral/javascript-templates/pull/35) +- RDKB-66116 : use freedesktop dbus-1.14 and gate native-build/CodeQL by source paths [`#31`](https://github.com/rdkcentral/javascript-templates/pull/31) + +#### [2.8.0](https://github.com/rdkcentral/javascript-templates/compare/2.3.0...2.8.0) + +> 22 July 2026 + +- Merge tag '2.3.0' into develop [`f0478c8`](https://github.com/rdkcentral/javascript-templates/commit/f0478c8b644c4feb6f322abc556f527d562ebd58) + #### [2.3.0](https://github.com/rdkcentral/javascript-templates/compare/2.2.0...2.3.0) +> 22 July 2026 + - RDKB-66032 : Add PR Format Check workflow [`#29`](https://github.com/rdkcentral/javascript-templates/pull/29) - RDKB-64641 sets post_data = NULL to keep getPost() unset for file-only multipart bodies [`#27`](https://github.com/rdkcentral/javascript-templates/pull/27) - RDKB-64256 fix OOB access in log_syntax_error for malformed include parsing [`#26`](https://github.com/rdkcentral/javascript-templates/pull/26) - RDKB-65677 eliminate session_create leaks and strengthen regression coverage [`#24`](https://github.com/rdkcentral/javascript-templates/pull/24) +- Add changelog for release 2.3.0 [`6d3c2ca`](https://github.com/rdkcentral/javascript-templates/commit/6d3c2ca36a06cc878c9b125b4053dd66d4be3f25) - Merge tag '2.2.0' into develop [`d73972d`](https://github.com/rdkcentral/javascript-templates/commit/d73972dcf3281b29682f4561a32904d0eb9611dc) #### [2.2.0](https://github.com/rdkcentral/javascript-templates/compare/2.1.0...2.2.0)