diff --git a/run_l2.sh b/run_l2.sh index b29d2f956..7caeb2608 100644 --- a/run_l2.sh +++ b/run_l2.sh @@ -87,6 +87,8 @@ pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_c_ cp remote_debugger.json /etc/rrd/ pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_profile_data.json test/functional-tests/tests/test_rrd_profile_data.py +pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_dynamic_profile_rdm_node_length_exceeded.json test/functional-tests/tests/test_rrd_dynamic_profile_rdm_node_length_exceeded.py + diff --git a/src/rrdCommon.h b/src/rrdCommon.h index 8f5544b77..191ee4745 100644 --- a/src/rrdCommon.h +++ b/src/rrdCommon.h @@ -58,6 +58,7 @@ extern "C" #define RRD_MEDIA_APPS "/media/apps/" #define RDM_PKG_PREFIX "RDK-RRD-" #define RDM_PKG_SUFFIX ":1.0" +#define RRD_DYNAMIC_PROFILE_MAX_LENGTH 34 #ifndef RRD_PROFILE_LIST #define RRD_DEVICE_PROFILE "" diff --git a/src/rrdDynamic.c b/src/rrdDynamic.c index 2deee32ab..2ca6038d8 100644 --- a/src/rrdDynamic.c +++ b/src/rrdDynamic.c @@ -127,7 +127,20 @@ int RRDGetProfileStringLength(issueNodeData *pissueStructNode, bool isDeepSleepA unsigned int prefixlen = strlen(RDM_PKG_PREFIX); unsigned int suffixlen = strlen(RDM_PKG_SUFFIX); unsigned int nodelen = 0; + size_t boundedNodeLen = 0; + if ((pissueStructNode == NULL) || (pissueStructNode->Node == NULL)) + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Invalid issue node data for profile length calculation\n", __FUNCTION__, __LINE__); + return -1; + } + + boundedNodeLen = strnlen(pissueStructNode->Node, RRD_DYNAMIC_PROFILE_MAX_LENGTH + 1); + if (boundedNodeLen > RRD_DYNAMIC_PROFILE_MAX_LENGTH) + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Issue node length must be less than %d\n", __FUNCTION__, __LINE__, RRD_DYNAMIC_PROFILE_MAX_LENGTH); + return -1; + } /* Calculate Length for Device Type for Deep Sleep Awake Event*/ if (isDeepSleepAwakeEvent) { @@ -143,7 +156,7 @@ int RRDGetProfileStringLength(issueNodeData *pissueStructNode, bool isDeepSleepA } else { - nodelen = strlen(pissueStructNode->Node); + nodelen = boundedNodeLen; length = prefixlen + nodelen + suffixlen; } return length + 1; @@ -270,9 +283,15 @@ void RRDRdmManagerDownloadRequest(issueNodeData *pissueStructNode, char *dynJSON { RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Memory Allocation Failed for Request RDM Manager Download.\n", __FUNCTION__, __LINE__); } - free(pissueStructNode->Node); - free(pissueStructNode->subNode); } + else + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Invalid profile length, skipping download request\n", __FUNCTION__, __LINE__); + } + free(pissueStructNode->Node); + free(pissueStructNode->subNode); + pissueStructNode->Node = NULL; + pissueStructNode->subNode = NULL; } else { diff --git a/src/unittest/rrdUnitTestRunner.cpp b/src/unittest/rrdUnitTestRunner.cpp index 059e5ce41..957b4b2eb 100644 --- a/src/unittest/rrdUnitTestRunner.cpp +++ b/src/unittest/rrdUnitTestRunner.cpp @@ -951,6 +951,50 @@ TEST(RRDGetProfileStringLengthTest, HandlesIsDeepSleepAwakeEventTrueRRD_DEFAULT_ free(issue.Node); free(issue.subNode); } + +TEST(RRDGetProfileStringLengthTest, HandlesNullStructNode) +{ + int length = RRDGetProfileStringLength(NULL, false); + ASSERT_EQ(length, -1); +} + +TEST(RRDGetProfileStringLengthTest, HandlesNullNodeField) +{ + issueNodeData issue; + issue.Node = NULL; + issue.subNode = strdup("SubNode"); + int length = RRDGetProfileStringLength(&issue, false); + ASSERT_EQ(length, -1); + free(issue.subNode); +} + +TEST(RRDGetProfileStringLengthTest, HandlesNodeLengthExceedsMax) +{ + issueNodeData issue; + issue.Node = (char*)malloc(RRD_DYNAMIC_PROFILE_MAX_LENGTH + 10); + memset(issue.Node, 'A', RRD_DYNAMIC_PROFILE_MAX_LENGTH + 9); + issue.Node[RRD_DYNAMIC_PROFILE_MAX_LENGTH + 9] = '\0'; + issue.subNode = strdup("SubNode"); + int length = RRDGetProfileStringLength(&issue, false); + ASSERT_EQ(length, -1); + free(issue.Node); + free(issue.subNode); +} + +TEST(RRDGetProfileStringLengthTest, HandlesDeepSleepAwakeEventEmptyProfile) +{ + issueNodeData issue; + issue.Node = strdup("MainNode"); + issue.subNode = strdup("SubNode"); + devPropData.deviceType = RRD_DEFAULT_PLTFMS; + // Simulate empty profile name + int (*orig_strlen)(const char*) = strlen; + int length = RRDGetProfileStringLength(&issue, true); + ASSERT_GE(length, 0); // Should not crash + free(issue.Node); + free(issue.subNode); +} + #endif /* --------------- Test RRDCheckIssueInDynamicProfile() from rrdDeepSleep --------------- */ class RRDCheckIssueInDynamicProfileTest : public ::testing::Test @@ -1150,6 +1194,44 @@ TEST_F(RRDRdmManagerDownloadRequestTest, DeepSleepAwakeEventIsFalse_SetParamRetu free(buff.mdata); } +TEST_F(RRDRdmManagerDownloadRequestTest, HandlesMSGLengthNegative) +{ + issueNodeData issuestructNode; + issuestructNode.Node = strdup("MainNode"); + issuestructNode.subNode = strdup("SubNode"); + data_buf buff; + buff.mdata = strdup("ValidIssueTypeData"); + buff.jsonPath = strdup("UTJson/validJson.json"); + buff.inDynamic = false; + // Patch RRDGetProfileStringLength to return -1 + // Simulate by passing NULL + RRDRdmManagerDownloadRequest(NULL, buff.jsonPath, &buff, false); + free(issuestructNode.Node); + free(issuestructNode.subNode); + free(buff.jsonPath); + free(buff.mdata); +} + +TEST_F(RRDRdmManagerDownloadRequestTest, HandlesAppendModeTrue) +{ + issueNodeData issuestructNode; + issuestructNode.Node = strdup("MainNode"); + issuestructNode.subNode = strdup("SubNode"); + data_buf buff; + buff.mdata = strdup("ValidIssueTypeData"); + buff.jsonPath = strdup("UTJson/validJson.json"); + buff.inDynamic = false; + buff.appendMode = true; + EXPECT_CALL(mock_rbus_api, rbusValue_Init(_)).WillOnce(Return(RBUS_ERROR_SUCCESS)); + EXPECT_CALL(mock_rbus_api, rbusValue_SetString(_, _)).WillOnce(Return(RBUS_ERROR_SUCCESS)); + EXPECT_CALL(mock_rbus_api, rbus_set(_, _, _, _)).WillOnce(Return(RBUS_ERROR_SUCCESS)); + RRDRdmManagerDownloadRequest(&issuestructNode, buff.jsonPath, &buff, false); + free(issuestructNode.Node); + free(issuestructNode.subNode); + free(buff.jsonPath); + free(buff.mdata); +} + /* --------------- Test RRDProcessDeepSleepAwakeEvents() from rrdDeepSleep --------------- */ class RRDProcessDeepSleepAwakeEventsTest : public ::testing::Test { diff --git a/test/functional-tests/features/rrd_dynamic_profile_node_length_exceeded.feature b/test/functional-tests/features/rrd_dynamic_profile_node_length_exceeded.feature new file mode 100644 index 000000000..c2818afa2 --- /dev/null +++ b/test/functional-tests/features/rrd_dynamic_profile_node_length_exceeded.feature @@ -0,0 +1,48 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +Feature: Remote Debugger Rejects RDM Download Request When IssueType Node Exceeds Maximum Length + + Scenario: Verify remote debugger process is running + Given the remote debugger process is not running + When I start the remote debugger process + Then the remote debugger process should be running + + Scenario: Send WebPA event with an IssueType string longer than 34 characters and verify logs + Given the remote debugger is running + When I trigger the event "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType" with a value longer than 34 characters + Then the logs should contain "SUCCESS: Message sending Done" + Then the logs should be seen with "SUCCESS: Message Reception Done" + And the issuetype request should match between Send and Receive + + Scenario: Verify the oversized IssueType is not found in static profile + Given the remote debugger received the message from RBUS command + When the remotedebugger static json profile is present + Then remotedebugger should read the Json file + And remotedebugger logs should contain the Json File Parse Success + And remotedebugger should log as the Issue requested is not found in the profile + + Scenario: Verify the RDM download request is rejected due to IssueType node length exceeding 34 characters + Given the remote debugger issuetype is missing in static profile + When the remotedebugger reads the json file from the dynamic path + Then remotedebugger json read and parse should fail + And remotedebugger should attempt to request RDM to download the package + But the RDM download request should be skipped because the dynamic profile node length exceeds 34 characters + And the logs should contain "Issue node length must be less than 34" + And the logs should contain "Invalid profile length, skipping download request" diff --git a/test/functional-tests/tests/test_rrd_dynamic_profile_rdm_node_length_exceeded.py b/test/functional-tests/tests/test_rrd_dynamic_profile_rdm_node_length_exceeded.py new file mode 100644 index 000000000..4c72fbd77 --- /dev/null +++ b/test/functional-tests/tests/test_rrd_dynamic_profile_rdm_node_length_exceeded.py @@ -0,0 +1,104 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +import json +import subprocess +from helper_functions import * + +def test_check_remote_debugger_config_file(): + config_file_path = JSON_FILE + assert check_file_exists(config_file_path), f"Configuration file '{config_file_path}' does not exist." + +def test_check_rrd_directory_exists(): + dir_path = OUTPUT_DIR + assert check_directory_exists(dir_path), f"Directory '{dir_path}' does not exist." + +def test_check_and_start_remotedebugger(): + kill_rrd() + remove_logfile() + print("Starting remotedebugger process") + command_to_start = "nohup /usr/local/bin/remotedebugger > /dev/null 2>&1 &" + run_shell_silent(command_to_start) + command_to_get_pid = "pidof remotedebugger" + pid = run_shell_command(command_to_get_pid) + assert pid != "", "remotedebugger process did not start" + +def reset_issuetype_rfc(): + command = 'rbuscli set Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType string ""' + result = subprocess.run(command, shell=True, capture_output=True, text=True) + assert result.returncode == 0 + +def test_remote_debugger_trigger_event(): + STRING_TEST = "hfkerfjrjfjfjfjfjfjfjfjfjfjfjfjfjfjf" + reset_issuetype_rfc() + sleep(10) + command = [ + 'rbuscli', 'set', + 'Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType', + 'string', STRING_TEST + ] + result = subprocess.run(command, capture_output=True, text=True) + assert result.returncode == 0 + + sleep(15) + + QUERY_MSG = "Received event for RRD_SET_ISSUE_EVENT" + assert QUERY_MSG in grep_rrdlogs(QUERY_MSG) + + MSG_SEND = "SUCCESS: Message sending Done" + sleep(2) + assert MSG_SEND in grep_rrdlogs(MSG_SEND) + + MSG_RECEIVE = "SUCCESS: Message Reception Done" + sleep(2) + assert MSG_RECEIVE in grep_rrdlogs(MSG_RECEIVE) + +def test_check_issue_in_static_profile(): + READ_JSON = "Start Reading JSON File... /etc/rrd/remote_debugger.json" + assert READ_JSON in grep_rrdlogs(READ_JSON) + + PARSE_JSON = "Static Profile Parse And Read Success" + assert PARSE_JSON in grep_rrdlogs(PARSE_JSON) + + MISSING_MSG = "Issue Data Not found in Static JSON File" + assert MISSING_MSG in grep_rrdlogs(MISSING_MSG) + +def test_check_issue_in_dynamic_profile(): + DYNAMIC_READ = "Checking Dynamic Profile..." + assert DYNAMIC_READ in grep_rrdlogs(DYNAMIC_READ) + + DYNAMIC_JSONFILE = "Reading json config file /media/apps/RDK-RRD-hfkerfjrjfjfjfjfjfjfjfjfjfjfjfjfjfjf/etc/rrd/remote_debugger.json" + assert DYNAMIC_JSONFILE in grep_rrdlogs(DYNAMIC_JSONFILE) + + PARSE_FAILED = "Dynamic Profile Parse/Read failed" + assert PARSE_FAILED in grep_rrdlogs(PARSE_FAILED) + + RDM_MSG = "Request RDM Manager Download for a new Issue Type" + assert RDM_MSG in grep_rrdlogs(RDM_MSG) + + INVALID_LENGTH = "Issue node length must be less than 34" + assert INVALID_LENGTH in grep_rrdlogs(INVALID_LENGTH) + + SKIP_DOWNLOAD = "Invalid profile length, skipping download request" + assert SKIP_DOWNLOAD in grep_rrdlogs(SKIP_DOWNLOAD) + + + remove_logfile() + remove_outdir_contents(OUTPUT_DIR) + kill_rrd()