RDK-61551: Revssh Hardening - Entertainement devices - #560
Lasya-Prakarsha-D-V wants to merge 4 commits into
Conversation
Reason for change: Signed-off-by: ldonth501 <LasyaPrakarsha_DonthiVenkata@comcast.com>
There was a problem hiding this comment.
🟡 Changes recommended
Required production-trigger test coverage is missing.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR hardens reverse SSH handling for production devices.
Changes:
- Validates SSH PID files.
- Rejects non-SHORTS triggers for
BUILD_TYPE=prod. - Improves SSH status and error handling.
File summaries
| File | Finding |
|---|---|
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp |
Critical (3 votes): Add tests for PROD non-SHORTS rejection and intended non-production/unset behavior, including expected NOK. |
Review details
Suppressed comments (1)
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3099
- This gate is fail-open for a production process that does not inherit
BUILD_TYPE=prod: theNULLcase falls through tostartTunnel.sh, so a non-SHORTS request can still establish a plain SSH tunnel. Since the requirement is to enforce SHORTS on PROD builds, derive the build identity from an immutable build-time/device source or reject non-SHORTS requests when the production marker is absent, rather than making this security control depend on the daemon environment.
const char *buildType = getenv("BUILD_TYPE");
if (buildType != NULL && strcmp(buildType, "prod") == 0) {
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| const char *buildType = getenv("BUILD_TYPE"); | ||
| if (buildType != NULL && strcmp(buildType, "prod") == 0) { | ||
| RDK_LOG(RDK_LOG_ERROR,LOG_TR69HOSTIF,"[%s] plain reverse SSH trigger rejected on prod-built device \n",__FUNCTION__); | ||
| return NOK; |
Code Coverage Summary |
b96aaea to
d3e883d
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Production enforcement can fail open, and environment cleanup and CI coverage issues remain.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
src/hostif/profiles/DeviceInfo/gtest/gtest_main.cpp:3230
BUILD_TYPEis process-global and is set before the fatalASSERT_NE; ifgetInstance(0)returns null, the assertion exits this test beforeunsetenv, leaving later tests running asprod. Acquire/assert the instance before mutating the environment, or use a scoped guard that restores the previous value on every exit path.
setenv("BUILD_TYPE", "prod", 1);
hostIf_DeviceInfo *pIface = hostIf_DeviceInfo::getInstance(0);
ASSERT_NE(pIface, nullptr);
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Review effort level: Lite
| const char *buildType = getenv("BUILD_TYPE"); | ||
| if (buildType != NULL && strcmp(buildType, "prod") == 0) { | ||
| RDK_LOG(RDK_LOG_ERROR,LOG_TR69HOSTIF,"[%s] plain reverse SSH trigger rejected on prod-built device \n",__FUNCTION__); | ||
| return NOK; |
| @pytest.mark.skipif(os.environ.get("BUILD_TYPE") != "prod", reason="requires a production build environment") | ||
| def test_ReverseSSH_Plain_Trigger_Rejected_On_Prod(): |
There was a problem hiding this comment.
🔵 Needs a closer look
Unresolved issues remain with production gating, CI coverage, and functional log validation.
Review details
Suppressed comments (4)
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3094
- This makes the production hardening depend on a mutable runtime environment variable rather than on the build itself. The repository only sets
BUILD_TYPEin the L2 harness (which defaults todev), so a production binary started without that variable—or with it overridden—will take the plaintextstartTunnel.shpath. Use an immutable build-time/product configuration for the production decision, or make SHORTS-only behavior the default and explicitly opt into plaintext for development/test builds.
if (buildType != NULL && strcmp(buildType, "prod") == 0) {
RDK_LOG(RDK_LOG_ERROR,LOG_TR69HOSTIF,"[%s] plain reverse SSH trigger rejected on prod-built device \n",__FUNCTION__);
return NOK;
}
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3094
- The public interface documentation currently describes this handler as gated only by
privacyMode(docs/api/thunder-plugin-interfaces.md:324-326), but this adds anotherNOKpath for a plainstarton production builds. Update the handler's API/BDD documentation to state that non-SHORTS triggers are rejected on PROD devices.
if (buildType != NULL && strcmp(buildType, "prod") == 0) {
RDK_LOG(RDK_LOG_ERROR,LOG_TR69HOSTIF,"[%s] plain reverse SSH trigger rejected on prod-built device \n",__FUNCTION__);
return NOK;
}
test/functional-tests/tests/tr69hostif_deviceip.py:189
- This new integration assertion is skipped unless the pytest process has
BUILD_TYPE=prod, but the repository's L2 workflow invokessh run_l2.shwithout that variable andrun_l2.shdefaults it todevat line 41. Consequently the production rejection path is never exercised by the existing L2 CI job; add a production-mode L2 invocation/job that starts the daemon with the same setting.
@pytest.mark.skipif(os.environ.get("BUILD_TYPE") != "prod", reason="requires a production build environment")
test/functional-tests/tests/tr69hostif_deviceip.py:196
- The log check scans the entire persistent log without clearing it first, so a matching line from an earlier run can satisfy this assertion even if the current
startrequest was rejected for a different reason (or the new guard is not reached). Clear the tr69hostif log immediately before issuing the request, or assert against a log position captured for this invocation.
assert "plain reverse SSH trigger rejected on prod-built device" in grep_tr69hostiflogs(
"plain reverse SSH trigger rejected on prod-built device"
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
Unresolved moderate findings affect production enforcement and test coverage.
Review details
Suppressed comments (5)
run_l2.sh:41
- The L2 workflow invokes this script without
BUILD_TYPE, so this defaults todevand the new production-only test intr69hostif_deviceip.pyis skipped. Since no other workflow or script setsBUILD_TYPE=prod, the PR adds no automated execution of the production rejection path; add a dedicated prod L2 invocation/job (while retaining the dev run) so this hardening is actually exercised.
export BUILD_TYPE=${BUILD_TYPE:-dev}
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3093
- This gate relies solely on the inherited, mutable
BUILD_TYPEenvironment variable. The only new assignment in this repository is inrun_l2.sh; the installedtr69hostifsystemd service instead loads/etc/device.properties, and this change does not ensure that production deployments setBUILD_TYPE=prod. If the variable is absent or overridden, the condition is false and the legacy plainstartTunnel.shpath remains available on a production image, defeating the hardening. Use a build/deployment-controlled value and fail closed when production status cannot be established.
}
stMsgData->paramtype = hostIf_StringType;
stMsgData->paramLen = strlen(stMsgData->paramValue);
src/hostif/profiles/DeviceInfo/gtest/gtest_main.cpp:3234
- This unconditionally removes
BUILD_TYPEfrom the gtest process after the case. When the test binary is launched with an existing value (for example,prod), later tests no longer see the caller's environment, so their results depend on this test's position; save and restore the prior value instead of always unsetting it.
addr.sin_port = htons(3000);
test/functional-tests/tests/tr69hostif_deviceip.py:192
- This log assertion scans the existing log without clearing it first, so a matching message from an earlier run can make the test pass even if this SET is accepted. Clear the log immediately before issuing the request so the assertion verifies this invocation; the repository's L2 test guidance calls for per-test log isolation.
rstdout = rbus_set_data(DATA_ELEMENT_NAME, "string", "start")
test/functional-tests/tests/tr69hostif_deviceip.py:192
- This case is unmarked, so it runs after
test_ReverseSSH_Set_Get_Handler(order 28), which has already issuedstart shortsand never stops it. On builds with__SINGLE_SESSION_ONLY__,isRsshactive()returns through the existing “session already active” path before reaching the new production check, so the expected rejection log is missing and this test does not exercise the hardening. Stop/clear the session or run this case while the tunnel is known to be inactive.
@pytest.mark.skipif(os.environ.get("BUILD_TYPE") != "prod", reason="requires a production build environment")
def test_ReverseSSH_Plain_Trigger_Rejected_On_Prod():
DATA_ELEMENT_NAME = "Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshTrigger"
rstdout = rbus_set_data(DATA_ELEMENT_NAME, "string", "start")
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Lite
Code Coverage Summary |
There was a problem hiding this comment.
🟡 Changes recommended
Production enforcement and associated test configuration need correction before approval.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (2)
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3233
- This makes the production policy fail open: when
BUILD_TYPEis unset (or has any value other than exactlyprod), a plainstartstill reachesstartTunnel.sh. The repository does not set this variable in the production build; the only in-repo assignment is the L2 harness defaulting it todev, so a production daemon launched by its normal service manager can bypass the SHORTS requirement. Make the product mode a build/deployment-controlled invariant and reject plain triggers when that invariant indicates production (including handling an unset/invalid value), rather than relying on this optional environment variable.
const char *buildType = getenv("BUILD_TYPE");
if (buildType != NULL && strcmp(buildType, "prod") == 0) {
test/functional-tests/tests/tr69hostif_deviceip.py:189
- The L2 workflow invokes
run_l2.shwithout settingBUILD_TYPE, and that script defaults it todevat line 41, so thisskipifcauses the new production rejection test to be skipped in CI. Add an explicit production L2 job/configuration (and retain a dev run separately) so the behavior described by the PR is actually exercised.
@pytest.mark.skipif(os.environ.get("BUILD_TYPE") != "prod", reason="requires a production build environment")
- Files reviewed: 4/4 changed files
- Comments generated: 1
- Review effort level: Lite
Code Coverage Summary |
There was a problem hiding this comment.
🔵 Needs a closer look
Unresolved production-policy and test-coverage issues must be addressed before approval.
Review details
Suppressed comments (6)
run_l2.sh:41
- This defaults the daemon and pytest process to
dev, so the new production-only functional test is skipped for the standardrun_l2.shinvocation. The documented L2 path therefore does not exercise the only end-to-end assertion for this hardening; add an explicit production L2/CI invocation (while retaining a separate dev run) so the rejection path is actually validated.
export BUILD_TYPE=${BUILD_TYPE:-dev}
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3233
- This guard treats an unset
BUILD_TYPEasprod. In the existing gtest sequence,set_xOpsReverseSshTrigger_blocksPlainStartOnProdunsets the variable beforeset_xOpsReverseSshTrigger_StartNoShorts, which expectsOK; that test now returnsNOKin the default environment. Since the requirement is production-only, treat only an explicitprodvalue as production (or explicitly set and restoredevin the non-production test).
if (buildType == NULL || strcmp(buildType, "prod") == 0) {
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3233
BUILD_TYPEis an externally supplied runtime environment variable rather than a property of the binary. A production daemon launched withBUILD_TYPE=dev(or through a launch path that sets it incorrectly) will take the non-SHORTS branch and bypass this hardening; in this repository the only setup is the test runner. Encode the production distinction as a build-time flag or trusted device configuration instead of allowing the process environment to select the security policy.
const char *buildType = getenv("BUILD_TYPE");
if (buildType == NULL || strcmp(buildType, "prod") == 0) {
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3235
docs/api/thunder-plugin-interfaces.md:326still says this handler's onlyNOKgate is privacy mode, but this adds a production plain-trigger gate. Update the public API notes to document the new behavior so consumers know why a SET can fail.
if (buildType == NULL || strcmp(buildType, "prod") == 0) {
RDK_LOG(RDK_LOG_ERROR,LOG_TR69HOSTIF,"[%s] plain reverse SSH trigger rejected on prod-built device \n",__FUNCTION__);
return NOK;
src/hostif/profiles/DeviceInfo/gtest/gtest_main.cpp:5100
unsetenvleaves the process in the state that the new production guard treats as production. The next existingset_xOpsReverseSshTrigger_StartNoShortstest (line 5118) therefore returnsNOKinstead of its expectedOKin the normal registration order. Restore the intended dev state or setBUILD_TYPE=devin that non-production test.
unsetenv("BUILD_TYPE");
test/functional-tests/tests/tr69hostif_deviceip.py:189
- The implementation treats an unset
BUILD_TYPEas production (buildType == NULLrejects plain start), but this marker treats the same state as non-production and skips the test. A production daemon launched without the variable will therefore bypass this regression test even though the new code is enforcing the rejection; align the marker's default with the handler's fail-closed behavior.
@pytest.mark.skipif(os.environ.get("BUILD_TYPE") != "prod", reason="requires a production build environment")
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Lite
Code Coverage Summary |
There was a problem hiding this comment.
🟡 Changes recommended
Production detection, header inclusion, and test environment cleanup issues remain unresolved.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (3)
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3236
- The security decision accepts every value other than the exact string
prod, so a production daemon launched withBUILD_TYPE=dev(or a typo) will still execute the unrestrictedstartTunnel.shpath. Because this is a mutable process environment variable rather than a trusted build property, it does not reliably enforce SHORTS on production images; derive the decision from immutable build-time configuration or another trusted product-build indicator.
const char *buildType = getenv("BUILD_TYPE");
if (buildType == NULL || strcmp(buildType, "prod") == 0) {
RDK_LOG(RDK_LOG_ERROR,LOG_TR69HOSTIF,"[%s] plain reverse SSH trigger rejected on prod-built device \n",__FUNCTION__);
return NOK;
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3233
getenvis newly used here, butDevice_DeviceInfo.cppdoes not include<cstdlib>; the added<cstdlib>include is only ingtest_main.cpp. This relies on transitive headers and can fail on toolchains that do not expose the declaration, so add the standard header to this implementation.
const char *buildType = getenv("BUILD_TYPE");
src/hostif/profiles/DeviceInfo/gtest/gtest_main.cpp:5096
BUILD_TYPEis process-global, but it is set before the fatalASSERT_NE. IfgetInstance(0)fails, the assertion returns from this test without reachingunsetenv, leaving later tests running with the production guard enabled. Acquire and assert the interface before changing the environment, or use a scoped environment restore.
setenv("BUILD_TYPE", "prod", 1);
hostIf_DeviceInfo *pIface = hostIf_DeviceInfo::getInstance(0);
ASSERT_NE(pIface, nullptr);
- Files reviewed: 4/4 changed files
- Comments generated: 1
- Review effort level: Lite
Code Coverage Summary |
There was a problem hiding this comment.
🟡 Changes recommended
Production gating and L2 coverage are not reliably enforced, and the test environment cleanup can leak across tests.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3100
- This guard is not actually tied to the production build. In this repository
BUILD_TYPEis only exported byrun_l2.sh, where it defaults todev; a production daemon launched by its normal service without that variable will take the fall-through path and still runstart, bypassing this hardening. Make the production/dev selection a build-time or otherwise guaranteed deployment setting, and validate the production launch path rather than relying on an optional environment variable.
const char *buildType = getenv("BUILD_TYPE");
if (buildType != NULL && strcmp(buildType, "prod") == 0) {
RDK_LOG(RDK_LOG_ERROR,LOG_TR69HOSTIF,"[%s] plain reverse SSH trigger rejected on prod-built device \n",__FUNCTION__);
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Review effort level: Lite
|
|
||
| echo "RDK_PROFILE=STB" > /etc/device.properties | ||
|
|
||
| export BUILD_TYPE=${BUILD_TYPE:-dev} |
02da4ce to
b1dd21b
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
Production enforcement, test isolation, cleanup, and L2 build-type handling issues remain unresolved.
Review details
Suppressed comments (6)
run_l2.sh:41
- The L2 harness defaults
BUILD_TYPEtodev, so running this procedure against a production image without an externally supplied variable starts the daemon in the non-production mode and causes the new pytest case to be skipped. That allows the stated PROD hardening test to pass without exercising it; the harness should obtain the build type from the image/build metadata or fail loudly when a production run is expected.
export BUILD_TYPE=${BUILD_TYPE:-dev}
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3101
- This enforcement depends on the daemon inheriting
BUILD_TYPE=prodat runtime; it is not tied to how the binary was built. The only added propagation is inrun_l2.sh, so a production image launched by its normal service without this variable (or with any other value) still reachesstartTunnel.shfor a plain trigger and bypasses the hardening. Use a compile-time production definition or an immutable device/build configuration that is guaranteed to be available to the daemon, and cover the missing-variable case in the test.
const char *buildType = getenv("BUILD_TYPE");
if (buildType != NULL && strcmp(buildType, "prod") == 0) {
RDK_LOG(RDK_LOG_ERROR,LOG_TR69HOSTIF,"[%s] plain reverse SSH trigger rejected on prod-built device \n",__FUNCTION__);
return NOK;
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:1434
- A missing
/var/tmp/rssh.pidis the normal representation of an inactive reverse-SSH session, andisRsshactive()is used by the status getter. Logging that expected state atRDK_LOG_ERRORwill emit an error on every inactive status check and can create operational noise; the previous implementation logged this path at debug level. Keep the normal missing-file path at debug (or reserve error level for unexpected open failures).
if (!pidstrm.is_open())
{
RDK_LOG(RDK_LOG_ERROR,LOG_TR69HOSTIF,"[%s] SSH Session inactive; failed to open pid file %s (errno=%d:%s) \n",__FUNCTION__, pidfile.c_str(), errno, strerror(errno));
RDK_LOG(RDK_LOG_TRACE1,LOG_TR69HOSTIF,"[%s] Exiting... \n",__FUNCTION__);
return false;
src/hostif/profiles/DeviceInfo/gtest/gtest_main.cpp:3234
- This test overwrites any
BUILD_TYPEsupplied by the test runner and unconditionally unsets it afterward, so the process environment is not restored; an earlier assertion failure atASSERT_NEalso exits before cleanup. That makes the suite order-dependent and can change the mode seen by later tests. Save the prior value, acquire/assert the fixture before changing the environment, and restore the prior value on exit.
setenv("BUILD_TYPE", "prod", 1);
hostIf_DeviceInfo *pIface = hostIf_DeviceInfo::getInstance(0);
ASSERT_NE(pIface, nullptr);
EXPECT_EQ(pIface->set_xOpsReverseSshTrigger(¶m), NOK);
unsetenv("BUILD_TYPE");
test/functional-tests/tests/tr69hostif_deviceip.py:197
- This test is order-dependent:
test_ReverseSSH_Set_Get_Handlerimmediately before it starts astart shortssession and never stops it. In builds with__SINGLE_SESSION_ONLY__, this request returns fromisRsshactive()before reaching the new PROD rejection branch, so the expected rejection log is never emitted (and the test either fails or validates the wrong reason). Stop and wait for the prior tunnel to become inactive, or isolate this test's daemon/session state before issuing the plain trigger.
def test_ReverseSSH_Plain_Trigger_Rejected_On_Prod():
DATA_ELEMENT_NAME = "Device.DeviceInfo.X_RDKCENTRAL-COM_xOpsDeviceMgmt.ReverseSSH.xOpsReverseSshTrigger"
rstdout = rbus_set_data(DATA_ELEMENT_NAME, "string", "start")
assert RBUS_SET_EXCEPTION_STRING in rstdout
assert "plain reverse SSH trigger rejected on prod-built device" in grep_tr69hostiflogs(
"plain reverse SSH trigger rejected on prod-built device"
)
test/functional-tests/tests/tr69hostif_deviceip.py:196
- The log assertion scans the entire persistent log, but this test never clears it before issuing the request. A matching line left by an earlier test run can satisfy the assertion even if the current daemon accepts the plain trigger or fails to log the rejection, so this security regression test can produce a false pass. Clear the tr69hostif log immediately before the RBUS SET.
rstdout = rbus_set_data(DATA_ELEMENT_NAME, "string", "start")
assert RBUS_SET_EXCEPTION_STRING in rstdout
assert "plain reverse SSH trigger rejected on prod-built device" in grep_tr69hostiflogs(
"plain reverse SSH trigger rejected on prod-built device"
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Lite
b1dd21b to
ac8ef1f
Compare
ac8ef1f to
070e44c
Compare
There was a problem hiding this comment.
🟡 Changes recommended
A compile-blocking issue and production enforcement gaps remain unresolved.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (2)
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3094
- This hardening is controlled only by the mutable runtime environment. If the production daemon is started without
BUILD_TYPE=prod(the checked-in service files only import/etc/device.properties, and the L2 script defaults this variable todev), a plainstartstill reachesstartTunnel.shand the production restriction is bypassed. Tie this to the production build configuration or otherwise make the deployed production service set and enforce an immutable production marker, failing closed when it is absent.
const char *buildType = getenv("BUILD_TYPE");
if (buildType != NULL && strcmp(buildType, "prod") == 0) {
RDK_LOG(RDK_LOG_ERROR,LOG_TR69HOSTIF,"[%s] plain reverse SSH trigger rejected on prod-built device \n",__FUNCTION__);
return NOK;
}
src/hostif/profiles/DeviceInfo/gtest/gtest_main.cpp:3234
- This test changes the process-wide
BUILD_TYPEand always unsets it instead of restoring its original value. Running the suite withBUILD_TYPEalready set toprodchanges the environment for subsequent tests, and theASSERT_NEearly return can leaveBUILD_TYPE=prodbehind; use an RAII guard or otherwise restore the prior value on every exit path.
setenv("BUILD_TYPE", "prod", 1);
hostIf_DeviceInfo *pIface = hostIf_DeviceInfo::getInstance(0);
ASSERT_NE(pIface, nullptr);
EXPECT_EQ(pIface->set_xOpsReverseSshTrigger(¶m), NOK);
unsetenv("BUILD_TYPE");
- Files reviewed: 4/4 changed files
- Comments generated: 1
- Review effort level: Lite
Signed-off-by: ldonth501 <LasyaPrakarsha_DonthiVenkata@comcast.com>
070e44c to
3559801
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
Production enforcement can be bypassed, and the test environment is not reliably restored.
Review details
Suppressed comments (2)
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3093
- This check is based only on a mutable process environment variable, not on the build itself. When
BUILD_TYPEis unset (or omitted by a production launch path),getenvreturns null and the code still executesstartTunnel.shforstart, so the SHORTS-only requirement is bypassed. Use an immutable build-time/product configuration for the production decision and make the production path fail closed.
const char *buildType = getenv("BUILD_TYPE");
if (buildType != NULL && strcmp(buildType, "prod") == 0) {
RDK_LOG(RDK_LOG_ERROR,LOG_TR69HOSTIF,"[%s] plain reverse SSH trigger rejected on prod-built device \n",__FUNCTION__);
return NOK;
src/hostif/profiles/DeviceInfo/gtest/gtest_main.cpp:3234
- This unconditionally removes
BUILD_TYPEafter the test, even when the process inherited a value, so subsequent tests observe a different environment. A fatal assertion before this line would also leave the test'sprodoverride installed. Preserve whether the variable was originally set and restore its original value after the assertion.
unsetenv("BUILD_TYPE");
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🟡 Changes recommended
Critical and moderate issues remain in production-mode enforcement and test environment handling.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (2)
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3093
BUILD_TYPEis an optional, mutable process environment variable, so this does not enforce a property of a production build. A production daemon started withoutBUILD_TYPE=prod—or launched by the updatedrun_l2.sh, which defaults it todev—will still execute the plain-tunnel path. That leaves non-SHORTS reverse SSH available on PROD; use a build/device configuration with a production-safe default rather than an opt-in environment check.
const char *buildType = getenv("BUILD_TYPE");
if (buildType != NULL && strcmp(buildType, "prod") == 0) {
RDK_LOG(RDK_LOG_ERROR,LOG_TR69HOSTIF,"[%s] plain reverse SSH trigger rejected on prod-built device \n",__FUNCTION__);
return NOK;
src/hostif/profiles/DeviceInfo/gtest/gtest_main.cpp:3230
- The test changes a process-global environment variable and then unconditionally unsets it at line 3234. If
BUILD_TYPEwas already set, or if theASSERT_NEreturns early, this leaves the test process in a different environment and can make later tests order-dependent. Save and restore the original value with an RAII guard, including assertion-failure paths.
setenv("BUILD_TYPE", "prod", 1);
hostIf_DeviceInfo *pIface = hostIf_DeviceInfo::getInstance(0);
ASSERT_NE(pIface, nullptr);
- Files reviewed: 4/4 changed files
- Comments generated: 1
- Review effort level: Lite
|
|
||
| echo "RDK_PROFILE=STB" > /etc/device.properties | ||
|
|
||
| export BUILD_TYPE=${BUILD_TYPE:-dev} |
There was a problem hiding this comment.
🔵 Needs a closer look
Production enforcement and test coverage remain dependent on mutable or missing BUILD_TYPE state, with environment cleanup issues in the unit test.
Review details
Suppressed comments (4)
run_l2.sh:41
- The L2 workflow invokes
run_l2.shwithout settingBUILD_TYPE, so this default makes the new production-only functional test skip (skipifchecks forprod) in CI. As a result, the PR's only functional CI run does not verify the rejection it introduces; add a dedicated production-mode invocation/job while retaining the development run as appropriate.
export BUILD_TYPE=${BUILD_TYPE:-dev}
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3236
- This makes the hardening dependent on a mutable runtime environment variable rather than on the production build itself. In this repository
BUILD_TYPEis only initialized byrun_l2.sh; the installed daemon/service has no corresponding setting, so a production device started normally with this variable unset will take the non-shorts path and accept plainstart. Encode the production/dev distinction in the build artifact or a trusted device configuration that is guaranteed to be present in production, and fail closed when it is unavailable.
const char *buildType = getenv("BUILD_TYPE");
if (buildType != NULL && strcmp(buildType, "prod") == 0) {
RDK_LOG(RDK_LOG_ERROR,LOG_TR69HOSTIF,"[%s] plain reverse SSH trigger rejected on prod-built device \n",__FUNCTION__);
return NOK;
src/hostif/profiles/DeviceInfo/gtest/gtest_main.cpp:5098
- This test unconditionally overwrites
BUILD_TYPEand then unsets it, so a suite launched with an existingBUILD_TYPEloses that state for subsequent tests. Also, theASSERT_NEcan return before the cleanup, leaving the process set toprod. Preserve and restore the original environment value with an RAII guard (or restore it on every exit path).
setenv("BUILD_TYPE", "prod", 1);
hostIf_DeviceInfo *pIface = hostIf_DeviceInfo::getInstance(0);
ASSERT_NE(pIface, nullptr);
EXPECT_EQ(pIface->set_xOpsReverseSshTrigger(¶m), NOK);
test/functional-tests/tests/tr69hostif_deviceip.py:190
- This skip condition reflects the pytest runner's environment, not the build identity of the already-running
tr69hostifdaemon. A production device launched by its service manager withoutBUILD_TYPE=prodwill skip this regression entirely, while a dev device tested from a shell with that variable will run it. Derive the mode from a target build marker or require an explicit, verified production-test setup so the hardening cannot silently go untested.
@pytest.mark.skipif(os.environ.get("BUILD_TYPE") != "prod", reason="requires a production build environment")
def test_ReverseSSH_Plain_Trigger_Rejected_On_Prod():
- Files reviewed: 4/4 changed files
- Comments generated: 0 new
- Review effort level: Lite
Code Coverage Summary |
There was a problem hiding this comment.
🟡 Changes recommended
Production coverage and build-mode enforcement are incomplete, and test environment handling can cause failures or contamination.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (5)
run_l2.sh:41
- The L2 workflow invokes
run_l2.shwithout settingBUILD_TYPE, and this new default isdev, so theskipifon the new production functional test is always true in CI. As a result, the stated production non-SHORTS regression is not exercised; add a dedicated prod-mode daemon/test invocation (while retaining dev coverage if required).
export BUILD_TYPE=${BUILD_TYPE:-dev}
src/hostif/profiles/DeviceInfo/Device_DeviceInfo.cpp:3234
- This gate is controlled only by the mutable runtime environment variable
BUILD_TYPE. The repository does not wire that variable into a production build (the L2 script actually defaults it todev), so a production daemon started withoutBUILD_TYPE=prodor with any other value still launches the non-SHORTSstartTunnel.shpath. Make the production/dev distinction a build-time or product-supplied configuration and ensure the production configuration cannot fall through to the plain-SSH path.
const char *buildType = getenv("BUILD_TYPE");
if (buildType != NULL && strcmp(buildType, "prod") == 0) {
src/hostif/profiles/DeviceInfo/gtest/gtest_main.cpp:5104
- Environment restoration occurs only after
EXPECT_EQ; if the assertion fails,BUILD_TYPEremains modified for every subsequent test in this process. Use an exception-/failure-safe scoped environment helper (or isolate the test process) so a failed test cannot contaminate the suite.
EXPECT_EQ(pIface->set_xOpsReverseSshTrigger(¶m), NOK);
if (savedBuildType) {
setenv("BUILD_TYPE", savedBuildTypeValue.c_str(), 1);
} else {
test/functional-tests/tests/tr69hostif_deviceip.py:190
- This test is skipped unless the caller exports
BUILD_TYPE=prod, whilerun_l2.shdefaults that variable todevand never runs a production-mode variant. Consequently the normal L2 run does not exercise the new rejection path, so a regression can pass unnoticed. Add an explicit production-mode test job/invocation and ensure the daemon and pytest process receive the same mode.
@pytest.mark.skipif(os.environ.get("BUILD_TYPE") != "prod", reason="requires a production build environment")
def test_ReverseSSH_Plain_Trigger_Rejected_On_Prod():
test/functional-tests/tests/tr69hostif_deviceip.py:192
grep_tr69hostiflogsscans the whole log file, but this test does not clear it before the SET. A stale matching line from an earlier invocation can therefore satisfy the assertion even when this request did not emit the rejection message.
rstdout = rbus_set_data(DATA_ELEMENT_NAME, "string", "start")
- Files reviewed: 4/4 changed files
- Comments generated: 1
- Review effort level: Lite
| EXPECT_EQ(pIface->set_xOpsReverseSshTrigger(¶m), OK); | ||
|
|
||
| if (savedBuildType) { | ||
| setenv("BUILD_TYPE", savedBuildTypeValue.c_str(), 1); | ||
| } else { | ||
| unsetenv("BUILD_TYPE"); |
Code Coverage Summary |
Reason for change: Enforcing SHORTS connection for PROD builds
Test procedure: NON-shorts ssh attempt for prod build should fail
Risks: Medium
Priority: P1