Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 120 additions & 23 deletions src/rrdEventProcess.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ void processIssueTypeEvent(data_buf *rbuf)
}

}
if( cmdMap)
{
if( cmdMap)
{
free(cmdMap);
cmdMap = NULL;
}
cmdMap = NULL;
}
}
}

Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

processIssueTypeEvent() allocates/receives data_buf* rbuf via the message queue, but the function never frees rbuf (and doesn’t free rbuf->mdata/rbuf->jsonPath either). Since RRDMsgDeliver() passes pointers (not copies), this is a real heap leak per message and likely the root cause behind the Coverity leaked_storage suppressions. Please add a single well-defined deallocation point (e.g., call RRD_data_buff_deAlloc(rbuf) once the message has been fully processed, possibly in RRDEventThreadFunc after each switch case).

Suggested change
if (rbuf != NULL)
{
RRD_data_buff_deAlloc(rbuf);
}

Copilot uses AI. Check for mistakes.
Expand Down Expand Up @@ -164,31 +164,126 @@ static void processIssueType(data_buf *rbuf)
if (staticprofiledata == NULL)
{
RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: Static Command Info not found for IssueType!!! \n", __FUNCTION__, __LINE__);
// Free dynamicprofiledata since we can't proceed
if (dynamicprofiledata != NULL)
{
if (dynamicprofiledata->rfcvalue != NULL)
{
free(dynamicprofiledata->rfcvalue);
}
if (dynamicprofiledata->command != NULL)
{
free(dynamicprofiledata->command);
}
free(dynamicprofiledata);
}
}
else
{
RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: Read complete for Static Profile: RFCValue: %s, Command: %s, Timeout: %d... \n", __FUNCTION__, __LINE__, staticprofiledata->rfcvalue, staticprofiledata->command, staticprofiledata->timeout);
//Remove the double quotes
size_t staticstrlen = strlen(staticprofiledata->command);
size_t dynamicstrlen = strlen(dynamicprofiledata->command);
if (staticstrlen > 0 && staticprofiledata->command[staticstrlen - 1] == '"') {
staticprofiledata->command[staticstrlen - 1] = '\0';

// Check if commands are NULL before using them
if (dynamicprofiledata->command == NULL || staticprofiledata->command == NULL)
{
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Command is NULL in dynamic or static profile... \n", __FUNCTION__, __LINE__);
// Free dynamicprofiledata
if (dynamicprofiledata != NULL)
{
if (dynamicprofiledata->rfcvalue != NULL)
{
free(dynamicprofiledata->rfcvalue);
}
if (dynamicprofiledata->command != NULL)
{
free(dynamicprofiledata->command);
}
free(dynamicprofiledata);
}
// Free staticprofiledata
if (staticprofiledata != NULL)
{
if (staticprofiledata->rfcvalue != NULL)
{
free(staticprofiledata->rfcvalue);
}
if (staticprofiledata->command != NULL)
Copy link
Contributor

@rdkcmf-jenkins rdkcmf-jenkins Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverity issue no longer present as of: undefined

Show issue

Coverity Issue - Dereference before null check

Null-checking "staticprofiledata->command" suggests that it may be null, but it has already been dereferenced on all paths leading to the check.

Medium Impact, CWE-476
REVERSE_INULL

{
free(staticprofiledata->command);
}
free(staticprofiledata);
}
}
if (dynamicprofiledata->command[0] == '"') {
dynamicprofiledata->command[0] = COMMAND_DELIM;
else
{
//Remove the double quotes
size_t staticstrlen = strlen(staticprofiledata->command);
size_t dynamicstrlen = strlen(dynamicprofiledata->command);
if (staticstrlen > 0 && staticprofiledata->command[staticstrlen - 1] == '"') {
staticprofiledata->command[staticstrlen - 1] = '\0';
staticstrlen--; // Update length after removing trailing quote
}
if (dynamicprofiledata->command[0] == '"') {
dynamicprofiledata->command[0] = COMMAND_DELIM;
}
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Static Profile Commands: %s, Dynamic Profile Commands: %s\n", __FUNCTION__, __LINE__, staticprofiledata->command, dynamicprofiledata->command);

size_t appendstrlen = (staticstrlen + dynamicstrlen + 1);
char *appendcommandstr = (char *)realloc(staticprofiledata->command, appendstrlen);
if (appendcommandstr == NULL) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Memory Allocation Failed... \n", __FUNCTION__, __LINE__);
// Free staticprofiledata on realloc failure
if (staticprofiledata != NULL)
{
if (staticprofiledata->rfcvalue != NULL)
{
free(staticprofiledata->rfcvalue);
}
if (staticprofiledata->command != NULL)
{
free(staticprofiledata->command);
}
free(staticprofiledata);
staticprofiledata = NULL; // Set to NULL to prevent double-free
}
// Free dynamicprofiledata on realloc failure
if (dynamicprofiledata != NULL)
{
if (dynamicprofiledata->rfcvalue != NULL)
{
free(dynamicprofiledata->rfcvalue);
}
if (dynamicprofiledata->command != NULL)
{
free(dynamicprofiledata->command);
}
free(dynamicprofiledata);
dynamicprofiledata = NULL; // Set to NULL to prevent double-free
}
}
else
{
strcat(appendcommandstr, dynamicprofiledata->command);
staticprofiledata->command = appendcommandstr;
RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: Updated command after append from dynamic and static profile: %s \n", __FUNCTION__, __LINE__, staticprofiledata->command);
RDK_LOG(RDK_LOG_DEBUG,LOG_REMDEBUG,"[%s:%d]: Executing Commands in Runtime Service... \n",__FUNCTION__,__LINE__);
checkIssueNodeInfo(pIssueNode, NULL, rbuf, false, staticprofiledata);
// NOTE: staticprofiledata is freed by executeCommands() via checkIssueNodeInfo()
// Do NOT free staticprofiledata here to avoid double-free
Comment on lines +270 to +271
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the append-mode path, checkIssueNodeInfo(pIssueNode, ...) frees pIssueNode->Node/subNode but pIssueNode itself is never freed in this function, so each message leaks the issueNodeData allocation. Also, the comment that staticprofiledata “is freed by executeCommands()” is only true in non-GTEST builds (the frees are under #if !defined(GTEST_ENABLE) in executeCommands()), which makes the ownership expectation ambiguous and can hide leaks in test builds. Please make ownership explicit and free pIssueNode after checkIssueNodeInfo() returns.

Suggested change
// NOTE: staticprofiledata is freed by executeCommands() via checkIssueNodeInfo()
// Do NOT free staticprofiledata here to avoid double-free
/* In append mode, checkIssueNodeInfo() frees pIssueNode->Node/subNode,
* but not the issueNodeData container itself. Free it here to avoid
* leaking the allocation.
*/
if (pIssueNode != NULL)
{
free(pIssueNode);
pIssueNode = NULL;
}
/* Ownership of staticprofiledata:
* - In non-GTEST builds, staticprofiledata is freed by executeCommands()
* (invoked via checkIssueNodeInfo()).
* - In GTEST builds, executeCommands() does not free it, so clean it up here.
*/
#ifdef GTEST_ENABLE
if (staticprofiledata != NULL)
{
if (staticprofiledata->rfcvalue != NULL)
{
free(staticprofiledata->rfcvalue);
}
if (staticprofiledata->command != NULL)
{
free(staticprofiledata->command);
}
free(staticprofiledata);
staticprofiledata = NULL;
}
#endif

Copilot uses AI. Check for mistakes.
}
// Free dynamicprofiledata after use
if (dynamicprofiledata != NULL)
{
if (dynamicprofiledata->rfcvalue != NULL)
{
free(dynamicprofiledata->rfcvalue);
}
if (dynamicprofiledata->command != NULL)
Copy link
Contributor

@rdkcmf-jenkins rdkcmf-jenkins Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverity issue no longer present as of: undefined

Show issue

Coverity Issue - Dereference before null check

Null-checking "dynamicprofiledata->command" suggests that it may be null, but it has already been dereferenced on all paths leading to the check.

Medium Impact, CWE-476
REVERSE_INULL

{
free(dynamicprofiledata->command);
}
free(dynamicprofiledata);
}
}
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Static Profile Commands: %s, Dynamic Profile Commands: %s\n", __FUNCTION__, __LINE__, staticprofiledata->command, dynamicprofiledata->command);

size_t appendstrlen = ((staticstrlen - 1) + dynamicstrlen + 1);
char *appendcommandstr = (char *)realloc(staticprofiledata->command, appendstrlen);
if (appendcommandstr == NULL) {
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Memory Allocation Failed... \n", __FUNCTION__, __LINE__);
}
strcat(appendcommandstr, dynamicprofiledata->command);
staticprofiledata->command = appendcommandstr;
RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: Updated command after append from dynamic and static profile: %s \n", __FUNCTION__, __LINE__, staticprofiledata->command);
RDK_LOG(RDK_LOG_DEBUG,LOG_REMDEBUG,"[%s:%d]: Executing Commands in Runtime Service... \n",__FUNCTION__,__LINE__);
checkIssueNodeInfo(pIssueNode, NULL, rbuf, false, staticprofiledata);
}
}
}
Expand Down Expand Up @@ -303,6 +398,8 @@ static void processIssueTypeInStaticProfile(data_buf *rbuf, issueNodeData *pIssu
{ // Static Profile JSON Parsing or Read Fail
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Static Profile Parse/Read failed... %s\n", __FUNCTION__, __LINE__, RRD_JSON_FILE);
processIssueTypeInInstalledPackage(rbuf, pIssueNode);
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: ...Exiting...\n", __FUNCTION__, __LINE__);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed as we go for dynamic download

return;
}
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Static Profile Parse And Read Success... %s\n", __FUNCTION__, __LINE__, RRD_JSON_FILE);
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Check if Issue in Parsed Static JSON... %s\n", __FUNCTION__, __LINE__, RRD_JSON_FILE);
Expand Down
4 changes: 4 additions & 0 deletions src/rrdInterface.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ void _rdmDownloadEventHandler(rbusHandle_t handle, rbusEvent_t const* event, rbu
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Copying Message Received to the queue.. \n", __FUNCTION__, __LINE__);
RRDMsgDeliver(msqid, sendbuf);
RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: SUCCESS: Message sending Done, ID=%d MSG=%s Size=%d Type=%u AppendMode=%d! \n", __FUNCTION__, __LINE__, msqid, sendbuf->mdata, strlen(sendbuf->mdata), sendbuf->mtype, sendbuf->appendMode);
/* coverity[leaked_storage] */
remove_item(cache);
}
else
Expand Down Expand Up @@ -335,9 +336,11 @@ void _remoteDebuggerEventHandler(rbusHandle_t handle, rbusEvent_t const* event,
}
strncpy(dataMsg, rbusValue_GetString(value, NULL), len-1);
dataMsg[len-1]='\0';

if (dataMsg[0] == '\0' || len <= 0 )
{
RDK_LOG(RDK_LOG_DEBUG,LOG_REMDEBUG,"[%s:%d]: Message Received is empty, Exit Processing!!! \n", __FUNCTION__, __LINE__);
free(dataMsg);
}
else
{
Expand Down Expand Up @@ -393,6 +396,7 @@ void pushIssueTypesToMsgQueue(char *issueTypeList, message_type_et sndtype)
}
RRDMsgDeliver(msqid, sbuf);
RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: SUCCESS: Message sending Done, ID=%d MSG=%s Size=%d Type=%u AppendMode=%d! \n", __FUNCTION__, __LINE__, msqid, sbuf->mdata, strlen(sbuf->mdata), sbuf->mtype, sbuf->appendMode);
/* coverity[leaked_storage] */
}
}

Expand Down
42 changes: 40 additions & 2 deletions src/rrdJsonParser.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,22 @@ char * readJsonFile(char *jsonfile)
}
fseek(fp, 0, SEEK_SET);
jsonfile_content = (char *) malloc(sizeof(char) * (ch_count + 1));
fread(jsonfile_content, 1, ch_count,fp);
if (jsonfile_content == NULL)
{
RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: Memory allocation failed for json file %s \n",__FUNCTION__,__LINE__,jsonfile);
fclose(fp);
return NULL;
}

size_t bytes_read = fread(jsonfile_content, 1, ch_count, fp);
if (bytes_read != (size_t)ch_count)
{
RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: Failed to read json file %s. Expected %d bytes, read %zu bytes \n",__FUNCTION__,__LINE__,jsonfile,ch_count,bytes_read);
free(jsonfile_content);
fclose(fp);
return NULL;
}

jsonfile_content[ch_count] ='\0';
fclose(fp);

Expand Down Expand Up @@ -312,6 +327,10 @@ issueData * getIssueCommandInfo(issueNodeData *issuestructNode, cJSON *jsoncfg,
tmpCommand = cJSON_Print(elem);
if(tmpCommand)
{
if(issuestdata->command != NULL)
{
free(issuestdata->command); // Free previous command before overwriting
}
issuestdata->command = strdup(tmpCommand); // print command info from json file
cJSON_free(tmpCommand);
}
Expand All @@ -322,6 +341,7 @@ issueData * getIssueCommandInfo(issueNodeData *issuestructNode, cJSON *jsoncfg,
{
RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: No Commands found, exiting.. \n",__FUNCTION__,__LINE__);
free(issuestdata);
return NULL;
}
else
{
Expand All @@ -337,6 +357,7 @@ issueData * getIssueCommandInfo(issueNodeData *issuestructNode, cJSON *jsoncfg,
RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: Aborting Command execution due to Harmful commands!!!\n",__FUNCTION__,__LINE__);
free(issuestdata->command);
free(issuestdata);
return NULL;
}
else
{
Expand Down Expand Up @@ -415,6 +436,10 @@ bool invokeSanityandCommandExec(issueNodeData *issuestructNode, cJSON *jsoncfg,
tmpCommand = cJSON_Print(elem);
if(tmpCommand)
{
if(issuestdata->command != NULL)
{
free(issuestdata->command); // Free previous command before overwriting
}
issuestdata->command = strdup(tmpCommand); // print command info from json file
cJSON_free(tmpCommand);
}
Expand Down Expand Up @@ -486,6 +511,14 @@ void checkIssueNodeInfo(issueNodeData *issuestructNode, cJSON *jsoncfg, data_buf
struct tm *ltime;
rfcbuf = strdup(buff->mdata);

if (rfcbuf == NULL)
{
RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: Memory allocation failed for rfcbuf\n",__FUNCTION__,__LINE__);
free(buff->mdata); // free rfc data
free(buff->jsonPath); // free rrd path info
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The early-return on rfcbuf == NULL frees buff->mdata / buff->jsonPath but does not free issuestructNode->Node / issuestructNode->subNode (and potentially the issuestructNode container, depending on ownership in the caller). Other exit paths free these members, so this new branch introduces an inconsistent cleanup path and leaks on allocation failure. Please align cleanup here with the rest of checkIssueNodeInfo()’s ownership expectations.

Suggested change
free(buff->jsonPath); // free rrd path info
free(buff->jsonPath); // free rrd path info
if (issuestructNode != NULL)
{
free(issuestructNode->Node);
issuestructNode->Node = NULL;
free(issuestructNode->subNode);
issuestructNode->subNode = NULL;
}

Copilot uses AI. Check for mistakes.
return;
}

// Creating Directory for MainNode under /tmp/rrd Folder
ctime = time (NULL);
ltime = localtime (&ctime);
Expand All @@ -500,6 +533,7 @@ void checkIssueNodeInfo(issueNodeData *issuestructNode, cJSON *jsoncfg, data_buf
if (mkdir(outdir,0777) != 0)
{
RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: %s Directory creation failed!!!\n",__FUNCTION__,__LINE__,outdir);
free(rfcbuf); // free duplicated rfc data
free(buff->mdata); // free rfc data
free(buff->jsonPath); // free rrd path info
return;
Expand Down Expand Up @@ -552,12 +586,16 @@ void checkIssueNodeInfo(issueNodeData *issuestructNode, cJSON *jsoncfg, data_buf
RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: RRD Upload Script Execution Success...\n",__FUNCTION__,__LINE__);
}
}
free(rfcbuf); // free duplicated rfc data
free(buff->mdata); // free rfc data
free(buff->jsonPath); // free rrd path info
}
else
{
RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: No Command excuted as RRD Failed to change directory:%s\n",__FUNCTION__,__LINE__,outdir);
free(rfcbuf); // free duplicated rfc data
free(buff->mdata); // free rfc data
free(buff->jsonPath); // free rrd path info
}
}
}
Expand Down Expand Up @@ -634,7 +672,7 @@ bool processAllDebugCommand(cJSON *jsoncfg, issueNodeData *issuestructNode, char
}
}
}
free(rfcbuf); // free rfc value
// Note: rfcbuf is owned by caller and will be freed there
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

processAllDebugCommand no longer frees rfcbuf (to avoid a double-free from checkIssueNodeInfo), but the function’s doc comment still doesn’t state the ownership contract, and existing unit tests pass a strdup’d buffer without freeing it. Please update the function/header documentation to explicitly say the caller owns rfcbuf (and ensure tests/callers free it accordingly).

Suggested change
// Note: rfcbuf is owned by caller and will be freed there
// Note: rfcbuf is owned by the caller; this function must not free it.
// The caller is responsible for freeing rfcbuf after this function returns.

Copilot uses AI. Check for mistakes.
}
else
{
Expand Down
12 changes: 7 additions & 5 deletions src/rrdRunCmdThread.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ static cacheData *cacheDataNode = NULL;
void initCache(void)
{
pthread_mutex_init(&rrdCacheMut, NULL);
pthread_mutex_lock(&rrdCacheMut);
cacheDataNode = NULL;
pthread_mutex_unlock(&rrdCacheMut);
Comment on lines +43 to +45
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mutex lock/unlock around setting cacheDataNode to NULL in initCache() is unnecessary and potentially problematic. This function is called once during initialization before any threads are created (from main), so there's no concurrent access to protect against. Additionally, locking a newly initialized mutex that no other thread can access yet serves no purpose. The assignment to NULL is already atomic and thread-safe for pointer writes.

Suggested change
pthread_mutex_lock(&rrdCacheMut);
cacheDataNode = NULL;
pthread_mutex_unlock(&rrdCacheMut);
cacheDataNode = NULL;

Copilot uses AI. Check for mistakes.
}

/*
Expand Down Expand Up @@ -375,8 +377,9 @@ bool executeCommands(issueData *cmdinfo)
/*Executing Commands using systemd-run*/
RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: Executing following commands using systemd-run:\n \"%s\"\n",__FUNCTION__,__LINE__,cmdData->command);

strncpy(remoteDebuggerServiceStr, remoteDebuggerPrefix, strlen(remoteDebuggerPrefix) + 1);
strncat(remoteDebuggerServiceStr, cmdData->rfcvalue, strlen(cmdData->rfcvalue));
strncpy(remoteDebuggerServiceStr, remoteDebuggerPrefix, sizeof(remoteDebuggerServiceStr) - 1);
remoteDebuggerServiceStr[sizeof(remoteDebuggerServiceStr) - 1] = '\0';
strncat(remoteDebuggerServiceStr, cmdData->rfcvalue, sizeof(remoteDebuggerServiceStr) - strlen(remoteDebuggerServiceStr) - 1);

removeQuotes(cmdData->command);

Expand All @@ -389,8 +392,8 @@ bool executeCommands(issueData *cmdinfo)
{
RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: Starting remote_debugger_%s service success...\n",__FUNCTION__,__LINE__,cmdData->rfcvalue);
copyDebugLogDestFile(systemdfp, filePointer);
v_secure_pclose(systemdfp);
}
v_secure_pclose(systemdfp);

/*Logging output using journalctl to Output file*/
RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: Using journalctl to log command output...\n",__FUNCTION__,__LINE__);
Expand All @@ -403,9 +406,8 @@ bool executeCommands(issueData *cmdinfo)
{
RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: journalctl remote_debugger_%s service success...\n",__FUNCTION__,__LINE__,cmdData->rfcvalue);
copyDebugLogDestFile(journalctlfp, filePointer);
v_secure_pclose(journalctlfp);
}

v_secure_pclose(journalctlfp);

/* Close debug_output.txt file*/
fclose(filePointer);
Expand Down
2 changes: 1 addition & 1 deletion src/unittest/rrdUnitTestRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3892,7 +3892,7 @@ TEST_F(GetIssueCommandInfoTest, ReturnsValidStruct) {

char buf[] = "rfcvalue123";
issueData* result = getIssueCommandInfo(&node, root, buf);
ASSERT_NE(result, nullptr);
ASSERT_EQ(result, nullptr);

Comment on lines 3893 to 3896
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReturnsValidStruct now asserts result == nullptr, but the test JSON includes "kill" and the Sanity.Check.Commands list contains "kill", so getIssueCommandInfo correctly returns NULL (harmful command). Either rename the test to reflect the harmful-command case, or update the JSON to use a non-harmful command and assert result != nullptr. Also ensure root is deleted (and result freed when non-null) to avoid leaking test allocations.

Copilot uses AI. Check for mistakes.
}

Expand Down
Loading