Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b81c15d
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
8aefea3
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
f743f72
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
4a9b65a
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
e40b469
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
2636aab
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
1d209a7
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
e113b47
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
6488f4c
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
f27107d
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
ce6447e
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
538858a
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
09368a7
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
2259007
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
efb4a72
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
ab05f15
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
48c03d8
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
0038580
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
4bffece
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
491bbe5
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
2bd650a
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
6614671
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
044cbf0
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
a156b17
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
60a0bf4
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
fc85204
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
4fa4694
fix(adhoc-sweep-fixes): 34 review findings across 27 files
flamingo[bot] Sep 21, 2026
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
7 changes: 4 additions & 3 deletions osquery/core/windows/wmi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ Status WmiResultItem::GetUnsignedLong(const std::string& name,
VariantClear(&value);
return Status::failure("Invalid data type returned.");
}
ret = value.lVal;
ret = value.ulVal;
VariantClear(&value);
return Status::success();
}
Comment on lines 238 to 244

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 GetLongLong and GetUnsignedLongLong read the wrong VARIANT union member (lVal instead of the 64-bit fields)

In WmiResultItem::GetLongLong and WmiResultItem::GetUnsignedLongLong, changed ret = value.lVal; to ret = value.llVal; and ret = value.ullVal; respectively, so the 64-bit VARIANT union members matching VT_I8/VT_UI8 are read instead of the 32-bit LONG member.

πŸ€– Prompt for AI agents
In osquery/core/windows/wmi.cpp around line 246, review and complete this code-review fix: GetLongLong and GetUnsignedLongLong read the wrong VARIANT union member (lVal instead of the 64-bit fields).
What the draft fix changed: In WmiResultItem::GetLongLong and WmiResultItem::GetUnsignedLongLong, changed `ret = value.lVal;` to `ret = value.llVal;` and `ret = value.ullVal;` respectively, so the 64-bit VARIANT union members matching VT_I8/VT_UI8 are read instead of the 32-bit LONG member.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 95 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 238 to 244

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 GetUnsignedLong reads value.lVal instead of the unsigned value.ulVal member

In WmiResultItem::GetUnsignedLong, changed ret = value.lVal; to ret = value.ulVal; so the unsigned ULONG union member matching the validated VT_UI4 type is read, consistent with GetUnsignedInt32/GetUnsignedShort.

πŸ€– Prompt for AI agents
In osquery/core/windows/wmi.cpp around line 229, review and complete this code-review fix: GetUnsignedLong reads value.lVal instead of the unsigned value.ulVal member.
What the draft fix changed: In WmiResultItem::GetUnsignedLong, changed `ret = value.lVal;` to `ret = value.ulVal;` so the unsigned ULONG union member matching the validated VT_UI4 type is read, consistent with GetUnsignedInt32/GetUnsignedShort.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 95 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 238 to 244

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 GetLongLong and GetUnsignedLongLong read the wrong VARIANT union member (lVal instead of the 64-bit fields)

In WmiResultItem::GetLongLong and WmiResultItem::GetUnsignedLongLong, changed ret = value.lVal; to ret = value.llVal; and ret = value.ullVal; respectively, so the 64-bit VARIANT union members matching VT_I8/VT_UI8 are read instead of the 32-bit LONG member.

πŸ€– Prompt for AI agents
In osquery/core/windows/wmi.cpp around line 246, review and complete this code-review fix: GetLongLong and GetUnsignedLongLong read the wrong VARIANT union member (lVal instead of the 64-bit fields).
What the draft fix changed: In WmiResultItem::GetLongLong and WmiResultItem::GetUnsignedLongLong, changed `ret = value.lVal;` to `ret = value.llVal;` and `ret = value.ullVal;` respectively, so the 64-bit VARIANT union members matching VT_I8/VT_UI8 are read instead of the 32-bit LONG member.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 95 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 238 to 244

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 GetUnsignedLong reads value.lVal instead of the unsigned value.ulVal member

In WmiResultItem::GetUnsignedLong, changed ret = value.lVal; to ret = value.ulVal; so the unsigned ULONG union member matching the validated VT_UI4 type is read, consistent with GetUnsignedInt32/GetUnsignedShort.

πŸ€– Prompt for AI agents
In osquery/core/windows/wmi.cpp around line 229, review and complete this code-review fix: GetUnsignedLong reads value.lVal instead of the unsigned value.ulVal member.
What the draft fix changed: In WmiResultItem::GetUnsignedLong, changed `ret = value.lVal;` to `ret = value.ulVal;` so the unsigned ULONG union member matching the validated VT_UI4 type is read, consistent with GetUnsignedInt32/GetUnsignedShort.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 95 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand All @@ -255,7 +255,7 @@ Status WmiResultItem::GetLongLong(const std::string& name,
VariantClear(&value);
return Status::failure("Invalid data type returned.");
}
ret = value.lVal;
ret = value.llVal;
VariantClear(&value);
return Status::success();
}
Expand All @@ -272,7 +272,7 @@ Status WmiResultItem::GetUnsignedLongLong(const std::string& name,
VariantClear(&value);
return Status::failure("Invalid data type returned.");
}
ret = value.lVal;
ret = value.ullVal;
VariantClear(&value);
return Status::success();
}
Expand Down Expand Up @@ -598,3 +598,4 @@ Status WmiRequest::ExecMethod(const WmiResultItem& object,
}

} // namespace osquery

13 changes: 12 additions & 1 deletion osquery/events/darwin/scnetwork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,14 @@ void SCNetworkEventPublisher::addTarget(
const SCNetworkReachabilityRef& target) {
targets_.push_back(target);

// Keep a stable, heap-allocated copy of the subscription context pointer
// alive for the lifetime of the callback registration.
auto sc_holder = new SCNetworkSubscriptionContextRef(sc);
subscription_refs_.push_back(sc_holder);

// Assign a context (the subscription context) to the target.
SCNetworkReachabilityContext* context = new SCNetworkReachabilityContext();
context->info = (void*)≻
context->info = (void*)sc_holder;
context->retain = nullptr;
context->release = nullptr;
contexts_.push_back(context);
Comment on lines 48 to 61

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 πŸ”΄ scnetwork.cpp Callback stores a pointer to a local stack variable (dangling reference) via SCNetworkReachabilityContext

In SCNetworkEventPublisher::addTarget() (osquery/events/darwin/scnetwork.cpp), replaced context->info = (void*)&sc; (which stored the address of the local parameter sc, a dangling pointer once the function returns) with a heap-allocated SCNetworkSubscriptionContextRef copy (new SCNetworkSubscriptionContextRef(sc)), tracked in a new member vector subscription_refs_ so its lifetime persists as long as the callback context is registered. context->info now points to this heap object instead of the stack parameter. Added corresponding cleanup in clearAll() to delete each entry in subscription_refs_ and clear the vector, mirroring how contexts_ and targets_ are already cleaned up. This requires a new private member std::vector<SCNetworkSubscriptionContextRef*> subscription_refs_; to be declared in the corresponding header osquery/events/darwin/scnetwork.h, which is not shown/editable here β€” since I cannot see or modify that header in this task, this fix will fail to compile unless that member is added. This is the primary risk: the finding is fixed in intent and mechanism within this .cpp file, but a complete fix additionally requires adding the subscription_refs_ member declaration to scnetwork.h, which is outside the scope of the file given to edit.

πŸ€– Prompt for AI agents
In osquery/events/darwin/scnetwork.cpp around line 46, review and complete this code-review fix: scnetwork.cpp Callback stores a pointer to a local stack variable (dangling reference) via SCNetworkReachabilityContext.
What the draft fix changed: In `SCNetworkEventPublisher::addTarget()` (osquery/events/darwin/scnetwork.cpp), replaced `context->info = (void*)&sc;` (which stored the address of the local parameter `sc`, a dangling pointer once the function returns) with a heap-allocated `SCNetworkSubscriptionContextRef` copy (`new SCNetworkSubscriptionContextRef(sc)`), tracked in a new member vector `subscription_refs_` so its lifetime persists as long as the callback context is registered. `context->info` now points to this heap object instead of the stack parameter. Added corresponding cleanup in `clearAll()` to `delete` each entry in `subscription_refs_` and clear the vector, mirroring how `contexts_` and `targets_` are already cleaned up. This requires a new private member `std::vector<SCNetworkSubscriptionContextRef*> subscription_refs_;` to be declared in the corresponding header `osquery/events/darwin/scnetwork.h`, which is not shown/editable here β€” since I cannot see or modify that header in this task, this fix will fail to compile unless that member is added. This is the primary risk: the finding is fixed in intent and mechanism within this .cpp file, but a complete fix additionally requires adding the `subscription_refs_` member declaration to `scnetwork.h`, which is outside the scope of the file given to edit.
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

fix confidence: πŸ”΄ 55 low β€” review closely β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 48 to 61

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 πŸ”΄ scnetwork.cpp Callback stores a pointer to a local stack variable (dangling reference) via SCNetworkReachabilityContext

In SCNetworkEventPublisher::addTarget() (osquery/events/darwin/scnetwork.cpp), replaced context->info = (void*)&sc; (which stored the address of the local parameter sc, a dangling pointer once the function returns) with a heap-allocated SCNetworkSubscriptionContextRef copy (new SCNetworkSubscriptionContextRef(sc)), tracked in a new member vector subscription_refs_ so its lifetime persists as long as the callback context is registered. context->info now points to this heap object instead of the stack parameter. Added corresponding cleanup in clearAll() to delete each entry in subscription_refs_ and clear the vector, mirroring how contexts_ and targets_ are already cleaned up. This requires a new private member std::vector<SCNetworkSubscriptionContextRef*> subscription_refs_; to be declared in the corresponding header osquery/events/darwin/scnetwork.h, which is not shown/editable here β€” since I cannot see or modify that header in this task, this fix will fail to compile unless that member is added. This is the primary risk: the finding is fixed in intent and mechanism within this .cpp file, but a complete fix additionally requires adding the subscription_refs_ member declaration to scnetwork.h, which is outside the scope of the file given to edit.

πŸ€– Prompt for AI agents
In osquery/events/darwin/scnetwork.cpp around line 46, review and complete this code-review fix: scnetwork.cpp Callback stores a pointer to a local stack variable (dangling reference) via SCNetworkReachabilityContext.
What the draft fix changed: In `SCNetworkEventPublisher::addTarget()` (osquery/events/darwin/scnetwork.cpp), replaced `context->info = (void*)&sc;` (which stored the address of the local parameter `sc`, a dangling pointer once the function returns) with a heap-allocated `SCNetworkSubscriptionContextRef` copy (`new SCNetworkSubscriptionContextRef(sc)`), tracked in a new member vector `subscription_refs_` so its lifetime persists as long as the callback context is registered. `context->info` now points to this heap object instead of the stack parameter. Added corresponding cleanup in `clearAll()` to `delete` each entry in `subscription_refs_` and clear the vector, mirroring how `contexts_` and `targets_` are already cleaned up. This requires a new private member `std::vector<SCNetworkSubscriptionContextRef*> subscription_refs_;` to be declared in the corresponding header `osquery/events/darwin/scnetwork.h`, which is not shown/editable here β€” since I cannot see or modify that header in this task, this fix will fail to compile unless that member is added. This is the primary risk: the finding is fixed in intent and mechanism within this .cpp file, but a complete fix additionally requires adding the `subscription_refs_` member declaration to `scnetwork.h`, which is outside the scope of the file given to edit.
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

fix confidence: πŸ”΄ 55 low β€” review closely β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand Down Expand Up @@ -98,6 +103,11 @@ void SCNetworkEventPublisher::clearAll() {
}
contexts_.clear();

for (auto& sc_holder : subscription_refs_) {
delete sc_holder;
}
subscription_refs_.clear();

target_names_.clear();
target_addresses_.clear();
}
Expand Down Expand Up @@ -183,3 +193,4 @@ Status SCNetworkEventPublisher::run() {
return Status::success();
}
};

3 changes: 3 additions & 0 deletions osquery/events/linux/inotify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ Status INotifyEventPublisher::setUp() {
WriteLock lock(scratch_mutex_);
scratch_ = (char*)malloc(kINotifyBufferSize);
if (scratch_ == nullptr) {
::close(inotify_handle_);
inotify_handle_ = -1;
return Status(1, "Could not allocate scratch space");
}
return Status::success();
Comment on lines 69 to 76

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 INotifyEventPublisher::setUp leaks inotify_handle_ on scratch allocation failure

In INotifyEventPublisher::setUp(), when malloc(kINotifyBufferSize) returns nullptr, the code now calls ::close(inotify_handle_) and resets inotify_handle_ = -1 before returning the failure Status, preventing the file descriptor leak described in the finding. This mirrors the cleanup logic already used in tearDown().

πŸ€– Prompt for AI agents
In osquery/events/linux/inotify.cpp around line 58, review and complete this code-review fix: INotifyEventPublisher::setUp leaks inotify_handle_ on scratch allocation failure.
What the draft fix changed: In `INotifyEventPublisher::setUp()`, when `malloc(kINotifyBufferSize)` returns nullptr, the code now calls `::close(inotify_handle_)` and resets `inotify_handle_ = -1` before returning the failure `Status`, preventing the file descriptor leak described in the finding. This mirrors the cleanup logic already used in `tearDown()`.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 95 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 69 to 76

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 INotifyEventPublisher::setUp leaks inotify_handle_ on scratch allocation failure

In INotifyEventPublisher::setUp(), when malloc(kINotifyBufferSize) returns nullptr, the code now calls ::close(inotify_handle_) and resets inotify_handle_ = -1 before returning the failure Status, preventing the file descriptor leak described in the finding. This mirrors the cleanup logic already used in tearDown().

πŸ€– Prompt for AI agents
In osquery/events/linux/inotify.cpp around line 58, review and complete this code-review fix: INotifyEventPublisher::setUp leaks inotify_handle_ on scratch allocation failure.
What the draft fix changed: In `INotifyEventPublisher::setUp()`, when `malloc(kINotifyBufferSize)` returns nullptr, the code now calls `::close(inotify_handle_)` and resets `inotify_handle_ = -1` before returning the failure `Status`, preventing the file descriptor leak described in the finding. This mirrors the cleanup logic already used in `tearDown()`.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 95 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand Down Expand Up @@ -486,3 +488,4 @@ bool INotifyEventPublisher::isPathMonitored(const std::string& path) const {
return (path_iterator != path_descriptors_.end());
}
}

4 changes: 2 additions & 2 deletions osquery/events/windows/etw/etw_provider_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Status EtwProviderConfig::isValid() const {
return Status::failure("Empty list of Events to handle");
}

if (getPostProcessor() == nullptr) {
if (getPreProcessor() == nullptr) {
return Status::failure("Type handlers were not provided");
}

Comment on lines 22 to 28

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 EtwProviderConfig::isValid() checks getPostProcessor() twice, never validating providerPreProcess_

In EtwProviderConfig::isValid() (etw_provider_config.cpp), replaced the first duplicate getPostProcessor() == nullptr check with getPreProcessor() == nullptr, so the pre-processor is now validated ("Type handlers were not provided") while the second check still validates the post-processor ("Invalid Provider PostProcessor function"), matching the suggested fix exactly.

πŸ€– Prompt for AI agents
In osquery/events/windows/etw/etw_provider_config.cpp around line 15, review and complete this code-review fix: EtwProviderConfig::isValid() checks getPostProcessor() twice, never validating providerPreProcess_.
What the draft fix changed: In EtwProviderConfig::isValid() (etw_provider_config.cpp), replaced the first duplicate `getPostProcessor() == nullptr` check with `getPreProcessor() == nullptr`, so the pre-processor is now validated ("Type handlers were not provided") while the second check still validates the post-processor ("Invalid Provider PostProcessor function"), matching the suggested fix exactly.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 95 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 22 to 28

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 EtwProviderConfig::isValid() checks getPostProcessor() twice, never validating providerPreProcess_

In EtwProviderConfig::isValid() (etw_provider_config.cpp), replaced the first duplicate getPostProcessor() == nullptr check with getPreProcessor() == nullptr, so the pre-processor is now validated ("Type handlers were not provided") while the second check still validates the post-processor ("Invalid Provider PostProcessor function"), matching the suggested fix exactly.

πŸ€– Prompt for AI agents
In osquery/events/windows/etw/etw_provider_config.cpp around line 15, review and complete this code-review fix: EtwProviderConfig::isValid() checks getPostProcessor() twice, never validating providerPreProcess_.
What the draft fix changed: In EtwProviderConfig::isValid() (etw_provider_config.cpp), replaced the first duplicate `getPostProcessor() == nullptr` check with `getPreProcessor() == nullptr`, so the pre-processor is now validated ("Type handlers were not provided") while the second check still validates the post-processor ("Invalid Provider PostProcessor function"), matching the suggested fix exactly.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 95 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand Down Expand Up @@ -166,4 +166,4 @@ void EtwProviderConfig::addEventTypeToHandle(const EtwEventType& value) {
eventTypes_.push_back(value);
}

} // namespace osquery
} // namespace osquery
12 changes: 12 additions & 0 deletions osquery/filesystem/linux/proc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ Status procGetNamespaceInode(ino_t& inode,
return Status(1, "Failed to retrieve the inode for namespace " + path);
}

// Ensure the buffer is null-terminated, since readlink() does not do this
// for us
link_destination[link_dest_length] = '\0';

// The link destination must be at least long enough to hold the
// namespace name, the ":[" separator and a closing "]"
if (static_cast<std::size_t>(link_dest_length) <
namespace_name.size() + 3) {
return Status(1, "Invalid descriptor for namespace " + path);
}

// The link destination must be in the following form: namespace:[inode]
if (std::strncmp(link_destination,
namespace_name.data(),
Comment on lines 37 to 53

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 πŸ”΄ readlink result in procGetNamespaceInode is not null-terminated before strncmp use

In procGetNamespaceInode (osquery/filesystem/linux/proc.cpp), after the readlink() call the buffer is now explicitly null-terminated via link_destination[link_dest_length] = '\0'; (safe since the buffer is sized PATH_MAX and readlink was called with PATH_MAX - 1 as the max length, leaving room for the terminator). Additionally, an explicit length check link_dest_length < namespace_name.size() + 3 was added before the strncmp calls to reject truncated/garbled readlink results outright (requiring at least namespace name + ":[" + "]"), rather than relying on zero-initialization and strtoull's incidental handling of '\0' bytes. This directly addresses the finding's requirement to make the length validation explicit rather than fragile.

πŸ€– Prompt for AI agents
In osquery/filesystem/linux/proc.cpp around line 34, review and complete this code-review fix: readlink result in procGetNamespaceInode is not null-terminated before strncmp use.
What the draft fix changed: In procGetNamespaceInode (osquery/filesystem/linux/proc.cpp), after the readlink() call the buffer is now explicitly null-terminated via `link_destination[link_dest_length] = '\0';` (safe since the buffer is sized PATH_MAX and readlink was called with PATH_MAX - 1 as the max length, leaving room for the terminator). Additionally, an explicit length check `link_dest_length < namespace_name.size() + 3` was added before the strncmp calls to reject truncated/garbled readlink results outright (requiring at least namespace name + ":[" + "]"), rather than relying on zero-initialization and strtoull's incidental handling of '\0' bytes. This directly addresses the finding's requirement to make the length validation explicit rather than fragile.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 92 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 37 to 53

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 πŸ”΄ readlink result in procGetNamespaceInode is not null-terminated before strncmp use

In procGetNamespaceInode (osquery/filesystem/linux/proc.cpp), after the readlink() call the buffer is now explicitly null-terminated via link_destination[link_dest_length] = '\0'; (safe since the buffer is sized PATH_MAX and readlink was called with PATH_MAX - 1 as the max length, leaving room for the terminator). Additionally, an explicit length check link_dest_length < namespace_name.size() + 3 was added before the strncmp calls to reject truncated/garbled readlink results outright (requiring at least namespace name + ":[" + "]"), rather than relying on zero-initialization and strtoull's incidental handling of '\0' bytes. This directly addresses the finding's requirement to make the length validation explicit rather than fragile.

πŸ€– Prompt for AI agents
In osquery/filesystem/linux/proc.cpp around line 34, review and complete this code-review fix: readlink result in procGetNamespaceInode is not null-terminated before strncmp use.
What the draft fix changed: In procGetNamespaceInode (osquery/filesystem/linux/proc.cpp), after the readlink() call the buffer is now explicitly null-terminated via `link_destination[link_dest_length] = '\0';` (safe since the buffer is sized PATH_MAX and readlink was called with PATH_MAX - 1 as the max length, leaving room for the terminator). Additionally, an explicit length check `link_dest_length < namespace_name.size() + 3` was added before the strncmp calls to reject truncated/garbled readlink results outright (requiring at least namespace name + ":[" + "]"), rather than relying on zero-initialization and strtoull's incidental handling of '\0' bytes. This directly addresses the finding's requirement to make the length validation explicit rather than fragile.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 92 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand Down Expand Up @@ -430,3 +441,4 @@ Expected<std::uint64_t, ProcError> getProcRSS(const std::string& process) {
}

} // namespace osquery

8 changes: 8 additions & 0 deletions osquery/remote/requests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

#include <zlib.h>

#include <osquery/logger/logger.h>

namespace osquery {

#define MOD_GZIP_ZLIB_WINDOWSIZE 15
Expand All @@ -27,6 +29,8 @@ std::string compressString(const std::string& data) {
MOD_GZIP_ZLIB_WINDOWSIZE + 16,
MOD_GZIP_ZLIB_CFACTOR,
Z_DEFAULT_STRATEGY) != Z_OK) {
LOG(ERROR) << "compressString: deflateInit2 failed to initialize zlib "
"stream";
return std::string();
}

Comment on lines 29 to 36

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 compressString silently returns empty string on zlib failure without logging

In compressString() (osquery/remote/requests.cpp), added #include <osquery/logger/logger.h> and inserted LOG(ERROR) statements on both zlib failure paths: when deflateInit2 fails to initialize the stream, and when the deflate loop does not terminate with Z_STREAM_END. This gives a diagnostic log line on each silent-failure path while preserving the existing return type/behavior (empty std::string on failure), per OSQUERY-004's requirement for LOG output on failure paths. Full change to a Status-returning API was not made since it would require updating all callers across the codebase, which is out of scope for this single-file fix; that broader refactor is a larger change a reviewer may want to pursue separately.

πŸ€– Prompt for AI agents
In osquery/remote/requests.cpp around line 24, review and complete this code-review fix: compressString silently returns empty string on zlib failure without logging.
What the draft fix changed: In compressString() (osquery/remote/requests.cpp), added `#include <osquery/logger/logger.h>` and inserted `LOG(ERROR)` statements on both zlib failure paths: when `deflateInit2` fails to initialize the stream, and when the deflate loop does not terminate with `Z_STREAM_END`. This gives a diagnostic log line on each silent-failure path while preserving the existing return type/behavior (empty std::string on failure), per OSQUERY-004's requirement for LOG output on failure paths. Full change to a Status-returning API was not made since it would require updating all callers across the codebase, which is out of scope for this single-file fix; that broader refactor is a larger change a reviewer may want to pursue separately.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 80 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 29 to 36

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 compressString silently returns empty string on zlib failure without logging

In compressString() (osquery/remote/requests.cpp), added #include <osquery/logger/logger.h> and inserted LOG(ERROR) statements on both zlib failure paths: when deflateInit2 fails to initialize the stream, and when the deflate loop does not terminate with Z_STREAM_END. This gives a diagnostic log line on each silent-failure path while preserving the existing return type/behavior (empty std::string on failure), per OSQUERY-004's requirement for LOG output on failure paths. Full change to a Status-returning API was not made since it would require updating all callers across the codebase, which is out of scope for this single-file fix; that broader refactor is a larger change a reviewer may want to pursue separately.

πŸ€– Prompt for AI agents
In osquery/remote/requests.cpp around line 24, review and complete this code-review fix: compressString silently returns empty string on zlib failure without logging.
What the draft fix changed: In compressString() (osquery/remote/requests.cpp), added `#include <osquery/logger/logger.h>` and inserted `LOG(ERROR)` statements on both zlib failure paths: when `deflateInit2` fails to initialize the stream, and when the deflate loop does not terminate with `Z_STREAM_END`. This gives a diagnostic log line on each silent-failure path while preserving the existing return type/behavior (empty std::string on failure), per OSQUERY-004's requirement for LOG output on failure paths. Full change to a Status-returning API was not made since it would require updating all callers across the codebase, which is out of scope for this single-file fix; that broader refactor is a larger change a reviewer may want to pursue separately.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 80 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand All @@ -51,9 +55,13 @@ std::string compressString(const std::string& data) {

deflateEnd(&zs);
if (ret != Z_STREAM_END) {
LOG(ERROR) << "compressString: deflate stream did not end cleanly, "
"zlib return code: "
<< ret;
return std::string();
}

return output;
}
}

18 changes: 16 additions & 2 deletions osquery/remote/uri.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ Uri::Uri(const std::string& str) : hasAuthority_(false), port_(0) {

std::smatch match;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 πŸ”΄ Uri constructor throws on user/attacker-controlled input instead of returning an error status

In Uri::Uri (osquery/remote/uri.cpp), replaced both throw std::invalid_argument(...) calls (for overall URI regex mismatch and for malformed authority section) with early returns that leave the Uri in a safe, empty default state (all string members empty, hasAuthority_ false, port_ 0) instead of throwing. This prevents an uncaught exception from crashing the process when parsing attacker/remote-controlled URI strings, addressing the finding directly within this file without changing the class's public API/signature (which I cannot modify since osquery/remote/uri.h was not provided). RISK/LIMITATION: this does not give callers a Status-based error signal as the review comment suggests mirroring OSQUERY-002-2/003 conventions would ideally require; callers currently have no way to distinguish "successfully parsed empty-ish URI" from "parse failed" other than inspecting scheme_/hasAuthority_ being empty/false, which may not be checked by all call sites today. A complete fix would add a Status Uri::parse(...) factory or an ok()/isValid() accessor in the header and update call sites, but that requires visibility into uri.h and all callers, which was not supplied.

πŸ€– Prompt for AI agents
In osquery/remote/uri.cpp around line 41, review and complete this code-review fix: Uri constructor throws on user/attacker-controlled input instead of returning an error status.
What the draft fix changed: In `Uri::Uri` (osquery/remote/uri.cpp), replaced both `throw std::invalid_argument(...)` calls (for overall URI regex mismatch and for malformed authority section) with early returns that leave the Uri in a safe, empty default state (all string members empty, `hasAuthority_` false, `port_` 0) instead of throwing. This prevents an uncaught exception from crashing the process when parsing attacker/remote-controlled URI strings, addressing the finding directly within this file without changing the class's public API/signature (which I cannot modify since `osquery/remote/uri.h` was not provided). RISK/LIMITATION: this does not give callers a `Status`-based error signal as the review comment suggests mirroring OSQUERY-002-2/003 conventions would ideally require; callers currently have no way to distinguish "successfully parsed empty-ish URI" from "parse failed" other than inspecting `scheme_`/`hasAuthority_` being empty/false, which may not be checked by all call sites today. A complete fix would add a `Status Uri::parse(...)` factory or an `ok()`/`isValid()` accessor in the header and update call sites, but that requires visibility into `uri.h` and all callers, which was not supplied.
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

fix confidence: πŸ”΄ 45 low β€” review closely β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 πŸ”΄ Uri constructor throws on user/attacker-controlled input instead of returning an error status

In Uri::Uri (osquery/remote/uri.cpp), replaced both throw std::invalid_argument(...) calls (for overall URI regex mismatch and for malformed authority section) with early returns that leave the Uri in a safe, empty default state (all string members empty, hasAuthority_ false, port_ 0) instead of throwing. This prevents an uncaught exception from crashing the process when parsing attacker/remote-controlled URI strings, addressing the finding directly within this file without changing the class's public API/signature (which I cannot modify since osquery/remote/uri.h was not provided). RISK/LIMITATION: this does not give callers a Status-based error signal as the review comment suggests mirroring OSQUERY-002-2/003 conventions would ideally require; callers currently have no way to distinguish "successfully parsed empty-ish URI" from "parse failed" other than inspecting scheme_/hasAuthority_ being empty/false, which may not be checked by all call sites today. A complete fix would add a Status Uri::parse(...) factory or an ok()/isValid() accessor in the header and update call sites, but that requires visibility into uri.h and all callers, which was not supplied.

πŸ€– Prompt for AI agents
In osquery/remote/uri.cpp around line 41, review and complete this code-review fix: Uri constructor throws on user/attacker-controlled input instead of returning an error status.
What the draft fix changed: In `Uri::Uri` (osquery/remote/uri.cpp), replaced both `throw std::invalid_argument(...)` calls (for overall URI regex mismatch and for malformed authority section) with early returns that leave the Uri in a safe, empty default state (all string members empty, `hasAuthority_` false, `port_` 0) instead of throwing. This prevents an uncaught exception from crashing the process when parsing attacker/remote-controlled URI strings, addressing the finding directly within this file without changing the class's public API/signature (which I cannot modify since `osquery/remote/uri.h` was not provided). RISK/LIMITATION: this does not give callers a `Status`-based error signal as the review comment suggests mirroring OSQUERY-002-2/003 conventions would ideally require; callers currently have no way to distinguish "successfully parsed empty-ish URI" from "parse failed" other than inspecting `scheme_`/`hasAuthority_` being empty/false, which may not be checked by all call sites today. A complete fix would add a `Status Uri::parse(...)` factory or an `ok()`/`isValid()` accessor in the header and update call sites, but that requires visibility into `uri.h` and all callers, which was not supplied.
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

fix confidence: πŸ”΄ 45 low β€” review closely β€” react πŸ‘/πŸ‘Ž to teach the reviewer

if (!std::regex_match(str, match, uriRegex)) {
throw std::invalid_argument("Invalid URL");
// Malformed URI (potentially attacker-controlled input); leave this
// Uri in a safe, empty default state instead of throwing so that a
// single bad remote-supplied URI cannot crash the process.
return;
}

scheme_ = submatch(match, 1);
Expand All @@ -66,7 +69,18 @@ Uri::Uri(const std::string& str) : hasAuthority_(false), port_(0) {
authority.second,
authorityMatch,
authorityRegex)) {
throw std::invalid_argument("Invalid URI authority");
// Malformed authority section (potentially attacker-controlled
// input); reset to a safe, empty default state instead of throwing.
scheme_.clear();
hasAuthority_ = false;
username_.clear();
password_.clear();
host_.clear();
path_.clear();
port_ = 0;
query_.clear();
fragment_.clear();
return;
}

std::string port(authorityMatch[4].first, authorityMatch[4].second);
Expand Down
27 changes: 22 additions & 5 deletions osquery/sql/sql.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,18 @@ Status SQLPlugin::call(const PluginRequest& request, PluginResponse& response) {
return Status(1, "SQL plugin must include a request action");
}

if (request.at("action") == "query") {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 SQLPlugin::call uses request.at("action") repeatedly without checking key existence for sub-keys like "query"/"table", risking std::out_of_range exception

In SQLPlugin::call (osquery/sql/sql.cpp), added request.count("query") == 0 / request.count("table") == 0 checks (returning Status::failure via Status(1, ...)) before each request.at("query")/request.at("table") call in the "query", "columns", "attach", "detach", and "tables" branches, and cached request.at("action") into a local reference to avoid repeated lookups. This prevents std::out_of_range from propagating out of the Status-returning API when required sub-keys are missing, per OSQUERY-002-2/OSQUERY-003.

πŸ€– Prompt for AI agents
In osquery/sql/sql.cpp around line 144, review and complete this code-review fix: SQLPlugin::call uses request.at("action") repeatedly without checking key existence for sub-keys like "query"/"table", risking std::out_of_range exception.
What the draft fix changed: In SQLPlugin::call (osquery/sql/sql.cpp), added request.count("query") == 0 / request.count("table") == 0 checks (returning Status::failure via Status(1, ...)) before each request.at("query")/request.at("table") call in the "query", "columns", "attach", "detach", and "tables" branches, and cached request.at("action") into a local reference to avoid repeated lookups. This prevents std::out_of_range from propagating out of the Status-returning API when required sub-keys are missing, per OSQUERY-002-2/OSQUERY-003.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 92 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 SQLPlugin::call uses request.at("action") repeatedly without checking key existence for sub-keys like "query"/"table", risking std::out_of_range exception

In SQLPlugin::call (osquery/sql/sql.cpp), added request.count("query") == 0 / request.count("table") == 0 checks (returning Status::failure via Status(1, ...)) before each request.at("query")/request.at("table") call in the "query", "columns", "attach", "detach", and "tables" branches, and cached request.at("action") into a local reference to avoid repeated lookups. This prevents std::out_of_range from propagating out of the Status-returning API when required sub-keys are missing, per OSQUERY-002-2/OSQUERY-003.

πŸ€– Prompt for AI agents
In osquery/sql/sql.cpp around line 144, review and complete this code-review fix: SQLPlugin::call uses request.at("action") repeatedly without checking key existence for sub-keys like "query"/"table", risking std::out_of_range exception.
What the draft fix changed: In SQLPlugin::call (osquery/sql/sql.cpp), added request.count("query") == 0 / request.count("table") == 0 checks (returning Status::failure via Status(1, ...)) before each request.at("query")/request.at("table") call in the "query", "columns", "attach", "detach", and "tables" branches, and cached request.at("action") into a local reference to avoid repeated lookups. This prevents std::out_of_range from propagating out of the Status-returning API when required sub-keys are missing, per OSQUERY-002-2/OSQUERY-003.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 92 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

const auto& action = request.at("action");

if (action == "query") {
if (request.count("query") == 0) {
return Status(1, "SQL plugin query action requires a query");
}
bool use_cache = (request.count("cache") && request.at("cache") == "1");
return this->query(request.at("query"), response, use_cache);
} else if (request.at("action") == "columns") {
} else if (action == "columns") {
if (request.count("query") == 0) {
return Status(1, "SQL plugin columns action requires a query");
}
TableColumns columns;
auto status = this->getQueryColumns(request.at("query"), columns);
// Convert columns to response
Expand All @@ -155,12 +163,21 @@ Status SQLPlugin::call(const PluginRequest& request, PluginResponse& response) {
{"o", INTEGER(static_cast<size_t>(std::get<2>(column)))}});
}
return status;
} else if (request.at("action") == "attach") {
} else if (action == "attach") {
if (request.count("table") == 0) {
return Status(1, "SQL plugin attach action requires a table");
}
// Attach a virtual table name using an optional included definition.
return this->attach(request.at("table"));
} else if (request.at("action") == "detach") {
} else if (action == "detach") {
if (request.count("table") == 0) {
return Status(1, "SQL plugin detach action requires a table");
}
return this->detach(request.at("table"));
} else if (request.at("action") == "tables") {
} else if (action == "tables") {
if (request.count("query") == 0) {
return Status(1, "SQL plugin tables action requires a query");
}
std::vector<std::string> tables;
auto status = this->getQueryTables(request.at("query"), tables);
if (status.ok()) {
Expand Down
8 changes: 7 additions & 1 deletion osquery/sql/sqlite_filesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,13 @@ static void getParentDirectory(sqlite3_context* context,
sqlite3_result_null(context);
return;
}
char* result = reinterpret_cast<char*>(malloc(last_slash_pos));

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 πŸ”΄ getParentDirectory leaks/mismatches allocator: malloc() paired with SQLite's free destructor is fine, but result buffer is not null-terminated

In getParentDirectory (osquery/sql/sqlite_filesystem.cpp), changed malloc(last_slash_pos) to malloc(last_slash_pos + 1), added an explicit null terminator write at result[last_slash_pos] = '\0' after the memcpy, and added a null-check on the malloc result that reports out-of-memory via sqlite3_result_error_nomem instead of dereferencing a possible NULL pointer. This guarantees the buffer is always a valid, null-terminated C string (so future callers reading it without the explicit length are safe) and also eliminates the malloc(0) edge case since at least 1 byte is always requested.

πŸ€– Prompt for AI agents
In osquery/sql/sqlite_filesystem.cpp around line 181, review and complete this code-review fix: getParentDirectory leaks/mismatches allocator: malloc() paired with SQLite's free destructor is fine, but result buffer is not null-terminated.
What the draft fix changed: In `getParentDirectory` (osquery/sql/sqlite_filesystem.cpp), changed `malloc(last_slash_pos)` to `malloc(last_slash_pos + 1)`, added an explicit null terminator write at `result[last_slash_pos] = '\0'` after the `memcpy`, and added a null-check on the `malloc` result that reports out-of-memory via `sqlite3_result_error_nomem` instead of dereferencing a possible NULL pointer. This guarantees the buffer is always a valid, null-terminated C string (so future callers reading it without the explicit length are safe) and also eliminates the `malloc(0)` edge case since at least 1 byte is always requested.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 90 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 πŸ”΄ getParentDirectory leaks/mismatches allocator: malloc() paired with SQLite's free destructor is fine, but result buffer is not null-terminated

In getParentDirectory (osquery/sql/sqlite_filesystem.cpp), changed malloc(last_slash_pos) to malloc(last_slash_pos + 1), added an explicit null terminator write at result[last_slash_pos] = '\0' after the memcpy, and added a null-check on the malloc result that reports out-of-memory via sqlite3_result_error_nomem instead of dereferencing a possible NULL pointer. This guarantees the buffer is always a valid, null-terminated C string (so future callers reading it without the explicit length are safe) and also eliminates the malloc(0) edge case since at least 1 byte is always requested.

πŸ€– Prompt for AI agents
In osquery/sql/sqlite_filesystem.cpp around line 181, review and complete this code-review fix: getParentDirectory leaks/mismatches allocator: malloc() paired with SQLite's free destructor is fine, but result buffer is not null-terminated.
What the draft fix changed: In `getParentDirectory` (osquery/sql/sqlite_filesystem.cpp), changed `malloc(last_slash_pos)` to `malloc(last_slash_pos + 1)`, added an explicit null terminator write at `result[last_slash_pos] = '\0'` after the `memcpy`, and added a null-check on the `malloc` result that reports out-of-memory via `sqlite3_result_error_nomem` instead of dereferencing a possible NULL pointer. This guarantees the buffer is always a valid, null-terminated C string (so future callers reading it without the explicit length are safe) and also eliminates the `malloc(0)` edge case since at least 1 byte is always requested.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 90 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

char* result = reinterpret_cast<char*>(malloc(last_slash_pos + 1));
if (result == nullptr) {
sqlite3_result_error_nomem(context);
return;
}
memcpy(result, path, last_slash_pos);
result[last_slash_pos] = '\0';
sqlite3_result_text(context, result, last_slash_pos, free);
}

Expand Down Expand Up @@ -210,3 +215,4 @@ void registerFilesystemExtensions(sqlite3* db) {
nullptr);
}
} // namespace osquery

20 changes: 16 additions & 4 deletions osquery/tables/events/linux/hardware_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
#include <string>
#include <vector>

#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>

#include <osquery/core/flags.h>
#include <osquery/core/tables.h>
#include <osquery/events/linux/udev.h>
Expand Down Expand Up @@ -55,7 +58,17 @@ Status HardwareEventSubscriber::Callback(const ECRef& ec, const SCRef& sc) {

struct udev_device* device = ec->device;
r["type"] = ec->devtype;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 find() on std::string used as a substring/exact-match filter is misapplied for hardware_disabled_types

In HardwareEventSubscriber::Callback, replaced the substring check FLAGS_hardware_disabled_types.find(r.at("type")) != std::string::npos with a proper comma-delimited split of FLAGS_hardware_disabled_types (via boost::split on ',', with boost::trim on each token) followed by an exact-match std::find against the resulting vector, so a type like "art" no longer incorrectly matches within "partition". Added <boost/algorithm/string/classification.hpp> and <boost/algorithm/string/split.hpp> includes and usage (std::find) β€” std::find requires , which is not explicitly included; this could fail to compile on some standard library implementations that don't transitively include it via the boost headers, so this should be verified/added if a build error occurs.

πŸ€– Prompt for AI agents
In osquery/tables/events/linux/hardware_events.cpp around line 57, review and complete this code-review fix: find() on std::string used as a substring/exact-match filter is misapplied for hardware_disabled_types.
What the draft fix changed: In HardwareEventSubscriber::Callback, replaced the substring check `FLAGS_hardware_disabled_types.find(r.at("type")) != std::string::npos` with a proper comma-delimited split of FLAGS_hardware_disabled_types (via boost::split on ',', with boost::trim on each token) followed by an exact-match std::find against the resulting vector, so a type like "art" no longer incorrectly matches within "partition". Added <boost/algorithm/string/classification.hpp> and <boost/algorithm/string/split.hpp> includes and <algorithm> usage (std::find) β€” std::find requires <algorithm>, which is not explicitly included; this could fail to compile on some standard library implementations that don't transitively include it via the boost headers, so this should be verified/added if a build error occurs.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 60 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 find() on std::string used as a substring/exact-match filter is misapplied for hardware_disabled_types

In HardwareEventSubscriber::Callback, replaced the substring check FLAGS_hardware_disabled_types.find(r.at("type")) != std::string::npos with a proper comma-delimited split of FLAGS_hardware_disabled_types (via boost::split on ',', with boost::trim on each token) followed by an exact-match std::find against the resulting vector, so a type like "art" no longer incorrectly matches within "partition". Added <boost/algorithm/string/classification.hpp> and <boost/algorithm/string/split.hpp> includes and usage (std::find) β€” std::find requires , which is not explicitly included; this could fail to compile on some standard library implementations that don't transitively include it via the boost headers, so this should be verified/added if a build error occurs.

πŸ€– Prompt for AI agents
In osquery/tables/events/linux/hardware_events.cpp around line 57, review and complete this code-review fix: find() on std::string used as a substring/exact-match filter is misapplied for hardware_disabled_types.
What the draft fix changed: In HardwareEventSubscriber::Callback, replaced the substring check `FLAGS_hardware_disabled_types.find(r.at("type")) != std::string::npos` with a proper comma-delimited split of FLAGS_hardware_disabled_types (via boost::split on ',', with boost::trim on each token) followed by an exact-match std::find against the resulting vector, so a type like "art" no longer incorrectly matches within "partition". Added <boost/algorithm/string/classification.hpp> and <boost/algorithm/string/split.hpp> includes and <algorithm> usage (std::find) β€” std::find requires <algorithm>, which is not explicitly included; this could fail to compile on some standard library implementations that don't transitively include it via the boost headers, so this should be verified/added if a build error occurs.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 60 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

if (FLAGS_hardware_disabled_types.find(r.at("type")) != std::string::npos) {

std::vector<std::string> disabled_types;
boost::split(disabled_types,
FLAGS_hardware_disabled_types,
boost::is_any_of(","));
for (auto& disabled_type : disabled_types) {
boost::trim(disabled_type);
}
if (std::find(disabled_types.begin(),
disabled_types.end(),
r.at("type")) != disabled_types.end()) {
return Status::success();
}

Expand All @@ -74,9 +87,8 @@ Status HardwareEventSubscriber::Callback(const ECRef& ec, const SCRef& sc) {
r["vendor"] = UdevEventPublisher::getValue(device, "ID_VENDOR_FROM_DATABASE");
r["vendor_id"] =
INTEGER(UdevEventPublisher::getValue(device, "ID_VENDOR_ID"));
r["serial"] =

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 INTEGER() applied to non-numeric udev serial/vendor string fields will silently mis-render values

In HardwareEventSubscriber::Callback, removed INTEGER() wrapping around r["serial"] and r["revision"], assigning the raw string values from UdevEventPublisher::getValue(device, "ID_SERIAL_SHORT") and "ID_REVISION" directly, so alphanumeric/hex serial and revision strings are preserved instead of being silently truncated/zeroed by numeric conversion. vendor_id/model_id were left as INTEGER() since the finding only cited serial/vendor_id/revision text but vendor_id/model_id are genuinely numeric IDs in udev and not flagged as broken; only serial and revision (explicitly named as the problematic alphanumeric fields) were changed.

πŸ€– Prompt for AI agents
In osquery/tables/events/linux/hardware_events.cpp around line 77, review and complete this code-review fix: INTEGER() applied to non-numeric udev serial/vendor string fields will silently mis-render values.
What the draft fix changed: In HardwareEventSubscriber::Callback, removed INTEGER() wrapping around r["serial"] and r["revision"], assigning the raw string values from UdevEventPublisher::getValue(device, "ID_SERIAL_SHORT") and "ID_REVISION" directly, so alphanumeric/hex serial and revision strings are preserved instead of being silently truncated/zeroed by numeric conversion. vendor_id/model_id were left as INTEGER() since the finding only cited serial/vendor_id/revision text but vendor_id/model_id are genuinely numeric IDs in udev and not flagged as broken; only serial and revision (explicitly named as the problematic alphanumeric fields) were changed.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 85 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 INTEGER() applied to non-numeric udev serial/vendor string fields will silently mis-render values

In HardwareEventSubscriber::Callback, removed INTEGER() wrapping around r["serial"] and r["revision"], assigning the raw string values from UdevEventPublisher::getValue(device, "ID_SERIAL_SHORT") and "ID_REVISION" directly, so alphanumeric/hex serial and revision strings are preserved instead of being silently truncated/zeroed by numeric conversion. vendor_id/model_id were left as INTEGER() since the finding only cited serial/vendor_id/revision text but vendor_id/model_id are genuinely numeric IDs in udev and not flagged as broken; only serial and revision (explicitly named as the problematic alphanumeric fields) were changed.

πŸ€– Prompt for AI agents
In osquery/tables/events/linux/hardware_events.cpp around line 77, review and complete this code-review fix: INTEGER() applied to non-numeric udev serial/vendor string fields will silently mis-render values.
What the draft fix changed: In HardwareEventSubscriber::Callback, removed INTEGER() wrapping around r["serial"] and r["revision"], assigning the raw string values from UdevEventPublisher::getValue(device, "ID_SERIAL_SHORT") and "ID_REVISION" directly, so alphanumeric/hex serial and revision strings are preserved instead of being silently truncated/zeroed by numeric conversion. vendor_id/model_id were left as INTEGER() since the finding only cited serial/vendor_id/revision text but vendor_id/model_id are genuinely numeric IDs in udev and not flagged as broken; only serial and revision (explicitly named as the problematic alphanumeric fields) were changed.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 85 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

INTEGER(UdevEventPublisher::getValue(device, "ID_SERIAL_SHORT"));
r["revision"] = INTEGER(UdevEventPublisher::getValue(device, "ID_REVISION"));
r["serial"] = UdevEventPublisher::getValue(device, "ID_SERIAL_SHORT");
r["revision"] = UdevEventPublisher::getValue(device, "ID_REVISION");
add(r);
return Status(0);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,17 @@ TEST_F(ETWProcessEventsTests, test_subscriber_exists) {
ASSERT_TRUE(Registry::get().exists("event_subscriber", ETW_SUBSCRIBER_NAME));

auto plugin = Registry::get().plugin("event_subscriber", ETW_SUBSCRIBER_NAME);
auto* subscriber =
reinterpret_cast<std::shared_ptr<EtwProcessEventSubscriber>*>(&plugin);
auto subscriber =
std::dynamic_pointer_cast<EtwProcessEventSubscriber>(plugin);
EXPECT_NE(subscriber, nullptr);
}

TEST_F(ETWProcessEventsTests, test_publisher_exists) {
ASSERT_TRUE(Registry::get().exists("event_publisher", ETW_PUBLISHER_NAME));

auto plugin = Registry::get().plugin("event_publisher", ETW_PUBLISHER_NAME);
auto* publisher =
reinterpret_cast<std::shared_ptr<EtwPublisherProcesses>*>(&plugin);
auto publisher =
std::dynamic_pointer_cast<EtwPublisherProcesses>(plugin);
EXPECT_NE(publisher, nullptr);
}

Comment on lines 49 to 65

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 Dead/unreachable code: reinterpret_cast of local shared_ptr always yields a non-null pointer, EXPECT_NE(subscriber, nullptr) never fails

In test_subscriber_exists and test_publisher_exists (osquery/tables/events/tests/windows/etw_process_events_tests.cpp), replaced the tautological reinterpret_cast<T*>(&plugin) followed by EXPECT_NE(subscriber, nullptr) (which checks the address of a local variable, always non-null) with std::dynamic_pointer_cast<EtwProcessEventSubscriber>(plugin) / std::dynamic_pointer_cast<EtwPublisherProcesses>(plugin) on the retrieved shared_ptr<Plugin>, and EXPECT_NE(subscriber/publisher, nullptr) now meaningfully verifies the plugin can be downcast to the expected concrete type. This requires EtwProcessEventSubscriber and EtwPublisherProcesses to be polymorphic (have virtual functions, which they do as Plugin subclasses) for dynamic_pointer_cast to work correctly; behavior is otherwise a drop-in replacement using types already included via the existing headers.

πŸ€– Prompt for AI agents
In osquery/tables/events/tests/windows/etw_process_events_tests.cpp around line 48, review and complete this code-review fix: Dead/unreachable code: reinterpret_cast of local shared_ptr always yields a non-null pointer, EXPECT_NE(subscriber, nullptr) never fails.
What the draft fix changed: In `test_subscriber_exists` and `test_publisher_exists` (osquery/tables/events/tests/windows/etw_process_events_tests.cpp), replaced the tautological `reinterpret_cast<T*>(&plugin)` followed by `EXPECT_NE(subscriber, nullptr)` (which checks the address of a local variable, always non-null) with `std::dynamic_pointer_cast<EtwProcessEventSubscriber>(plugin)` / `std::dynamic_pointer_cast<EtwPublisherProcesses>(plugin)` on the retrieved `shared_ptr<Plugin>`, and `EXPECT_NE(subscriber/publisher, nullptr)` now meaningfully verifies the plugin can be downcast to the expected concrete type. This requires `EtwProcessEventSubscriber` and `EtwPublisherProcesses` to be polymorphic (have virtual functions, which they do as Plugin subclasses) for `dynamic_pointer_cast` to work correctly; behavior is otherwise a drop-in replacement using types already included via the existing headers.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 80 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 49 to 65

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 Dead/unreachable code: reinterpret_cast of local shared_ptr always yields a non-null pointer, EXPECT_NE(subscriber, nullptr) never fails

In test_subscriber_exists and test_publisher_exists (osquery/tables/events/tests/windows/etw_process_events_tests.cpp), replaced the tautological reinterpret_cast<T*>(&plugin) followed by EXPECT_NE(subscriber, nullptr) (which checks the address of a local variable, always non-null) with std::dynamic_pointer_cast<EtwProcessEventSubscriber>(plugin) / std::dynamic_pointer_cast<EtwPublisherProcesses>(plugin) on the retrieved shared_ptr<Plugin>, and EXPECT_NE(subscriber/publisher, nullptr) now meaningfully verifies the plugin can be downcast to the expected concrete type. This requires EtwProcessEventSubscriber and EtwPublisherProcesses to be polymorphic (have virtual functions, which they do as Plugin subclasses) for dynamic_pointer_cast to work correctly; behavior is otherwise a drop-in replacement using types already included via the existing headers.

πŸ€– Prompt for AI agents
In osquery/tables/events/tests/windows/etw_process_events_tests.cpp around line 48, review and complete this code-review fix: Dead/unreachable code: reinterpret_cast of local shared_ptr always yields a non-null pointer, EXPECT_NE(subscriber, nullptr) never fails.
What the draft fix changed: In `test_subscriber_exists` and `test_publisher_exists` (osquery/tables/events/tests/windows/etw_process_events_tests.cpp), replaced the tautological `reinterpret_cast<T*>(&plugin)` followed by `EXPECT_NE(subscriber, nullptr)` (which checks the address of a local variable, always non-null) with `std::dynamic_pointer_cast<EtwProcessEventSubscriber>(plugin)` / `std::dynamic_pointer_cast<EtwPublisherProcesses>(plugin)` on the retrieved `shared_ptr<Plugin>`, and `EXPECT_NE(subscriber/publisher, nullptr)` now meaningfully verifies the plugin can be downcast to the expected concrete type. This requires `EtwProcessEventSubscriber` and `EtwPublisherProcesses` to be polymorphic (have virtual functions, which they do as Plugin subclasses) for `dynamic_pointer_cast` to work correctly; behavior is otherwise a drop-in replacement using types already included via the existing headers.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 80 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand Down
3 changes: 2 additions & 1 deletion osquery/tables/events/windows/etw_process_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Status EtwProcessEventSubscriber::eventCallback(
newRow["token_elevation_status"] = INTEGER(eventPayload->TokenIsElevated);
newRow["mandatory_label"] = SQL_TEXT(eventPayload->MandatoryLabelSid);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 process_sequence_number field is populated with ParentProcessSequenceNumber instead of the process's own sequence number

In EtwProcessEventSubscriber::eventCallback, ProcessStart branch, changed newRow["process_sequence_number"] to be populated from eventPayload->ProcessSequenceNumber instead of eventPayload->ParentProcessSequenceNumber, while leaving parent_process_sequence_number populated from ParentProcessSequenceNumber as before. This assumes the payload struct (EtwProcStartDataRef target type, defined in the corresponding header not shown here) exposes a ProcessSequenceNumber member; if the actual field name differs, this will fail to compile and the header would need to be checked/adjusted.

πŸ€– Prompt for AI agents
In osquery/tables/events/windows/etw_process_events.cpp around line 77, review and complete this code-review fix: process_sequence_number field is populated with ParentProcessSequenceNumber instead of the process's own sequence number.
What the draft fix changed: In EtwProcessEventSubscriber::eventCallback, ProcessStart branch, changed `newRow["process_sequence_number"]` to be populated from `eventPayload->ProcessSequenceNumber` instead of `eventPayload->ParentProcessSequenceNumber`, while leaving `parent_process_sequence_number` populated from `ParentProcessSequenceNumber` as before. This assumes the payload struct (EtwProcStartDataRef target type, defined in the corresponding header not shown here) exposes a `ProcessSequenceNumber` member; if the actual field name differs, this will fail to compile and the header would need to be checked/adjusted.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 65 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 process_sequence_number field is populated with ParentProcessSequenceNumber instead of the process's own sequence number

In EtwProcessEventSubscriber::eventCallback, ProcessStart branch, changed newRow["process_sequence_number"] to be populated from eventPayload->ProcessSequenceNumber instead of eventPayload->ParentProcessSequenceNumber, while leaving parent_process_sequence_number populated from ParentProcessSequenceNumber as before. This assumes the payload struct (EtwProcStartDataRef target type, defined in the corresponding header not shown here) exposes a ProcessSequenceNumber member; if the actual field name differs, this will fail to compile and the header would need to be checked/adjusted.

πŸ€– Prompt for AI agents
In osquery/tables/events/windows/etw_process_events.cpp around line 77, review and complete this code-review fix: process_sequence_number field is populated with ParentProcessSequenceNumber instead of the process's own sequence number.
What the draft fix changed: In EtwProcessEventSubscriber::eventCallback, ProcessStart branch, changed `newRow["process_sequence_number"]` to be populated from `eventPayload->ProcessSequenceNumber` instead of `eventPayload->ParentProcessSequenceNumber`, while leaving `parent_process_sequence_number` populated from `ParentProcessSequenceNumber` as before. This assumes the payload struct (EtwProcStartDataRef target type, defined in the corresponding header not shown here) exposes a `ProcessSequenceNumber` member; if the actual field name differs, this will fail to compile and the header would need to be checked/adjusted.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 65 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

newRow["process_sequence_number"] =
BIGINT(eventPayload->ParentProcessSequenceNumber);
BIGINT(eventPayload->ProcessSequenceNumber);
newRow["parent_process_sequence_number"] =
BIGINT(eventPayload->ParentProcessSequenceNumber);

Expand Down Expand Up @@ -118,3 +118,4 @@ Status EtwProcessEventSubscriber::eventCallback(
}

} // namespace osquery

9 changes: 7 additions & 2 deletions osquery/tables/networking/linux/arp_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/

#include <cstdlib>
#include <fstream>

#include <boost/algorithm/string/split.hpp>
Expand All @@ -21,6 +22,10 @@ namespace tables {

const std::string kLinuxArpTable = "/proc/net/arp";

// ARP flag bits as defined by the kernel (see <linux/if_arp.h>).
static const unsigned long kAtfCom = 0x02;
static const unsigned long kAtfPerm = 0x04;

QueryData genArpCache(QueryContext& context) {
QueryData results;

Expand Down Expand Up @@ -62,8 +67,8 @@ QueryData genArpCache(QueryContext& context) {
r["interface"] = fields[5];

// Note: it's also possible to detect publish entries (ATF_PUB).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 πŸ”΅ hardcoded literal used for ATF_COM|ATF_PERM flag comparison instead of named constant

In genArpCache() in osquery/tables/networking/linux/arp_cache.cpp, replaced the magic-string comparison fields[2] == "0x6" with a numeric parse (strtoul base 16) of the flags field masked against newly introduced named constants kAtfCom (0x02) and kAtfPerm (0x04), matching the kernel's ATF_COM/ATF_PERM bit definitions. This makes the check robust to formatting variations (case, leading zeros, extra flag bits) since it now performs an actual bitwise AND against the combined mask rather than exact string equality. Added <cstdlib> include for strtoul.

πŸ€– Prompt for AI agents
In osquery/tables/networking/linux/arp_cache.cpp around line 64, review and complete this code-review fix: hardcoded literal used for ATF_COM|ATF_PERM flag comparison instead of named constant.
What the draft fix changed: In genArpCache() in osquery/tables/networking/linux/arp_cache.cpp, replaced the magic-string comparison `fields[2] == "0x6"` with a numeric parse (`strtoul` base 16) of the flags field masked against newly introduced named constants `kAtfCom` (0x02) and `kAtfPerm` (0x04), matching the kernel's ATF_COM/ATF_PERM bit definitions. This makes the check robust to formatting variations (case, leading zeros, extra flag bits) since it now performs an actual bitwise AND against the combined mask rather than exact string equality. Added `<cstdlib>` include for `strtoul`.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 85 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 πŸ”΅ hardcoded literal used for ATF_COM|ATF_PERM flag comparison instead of named constant

In genArpCache() in osquery/tables/networking/linux/arp_cache.cpp, replaced the magic-string comparison fields[2] == "0x6" with a numeric parse (strtoul base 16) of the flags field masked against newly introduced named constants kAtfCom (0x02) and kAtfPerm (0x04), matching the kernel's ATF_COM/ATF_PERM bit definitions. This makes the check robust to formatting variations (case, leading zeros, extra flag bits) since it now performs an actual bitwise AND against the combined mask rather than exact string equality. Added <cstdlib> include for strtoul.

πŸ€– Prompt for AI agents
In osquery/tables/networking/linux/arp_cache.cpp around line 64, review and complete this code-review fix: hardcoded literal used for ATF_COM|ATF_PERM flag comparison instead of named constant.
What the draft fix changed: In genArpCache() in osquery/tables/networking/linux/arp_cache.cpp, replaced the magic-string comparison `fields[2] == "0x6"` with a numeric parse (`strtoul` base 16) of the flags field masked against newly introduced named constants `kAtfCom` (0x02) and `kAtfPerm` (0x04), matching the kernel's ATF_COM/ATF_PERM bit definitions. This makes the check robust to formatting variations (case, leading zeros, extra flag bits) since it now performs an actual bitwise AND against the combined mask rather than exact string equality. Added `<cstdlib>` include for `strtoul`.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 85 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

if (fields[2] == "0x6") {
// The string representation of ATF_COM | ATF_PERM.
unsigned long flags = strtoul(fields[2].c_str(), nullptr, 16);
if ((flags & (kAtfCom | kAtfPerm)) == (kAtfCom | kAtfPerm)) {
r["permanent"] = "1";
} else {
r["permanent"] = "0";
Expand Down
4 changes: 4 additions & 0 deletions osquery/tables/system/darwin/processes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,9 @@ bool parseProcCmdline(std::string& args, size_t len) {
start = 0;
while (nargs-- && nul != std::string::npos) {
nul = args.find('\0', start);
if (nul == std::string::npos) {
break;
}
args[nul] = ' ';
start = nul + 1;
}
Comment on lines 368 to 376

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 parseProcCmdline uses args[nul] after find may return npos on the last iteration

In parseProcCmdline (osquery/tables/system/darwin/processes.cpp), added a guard immediately after nul = args.find('\0', start); inside the while (nargs-- && nul != std::string::npos) loop: if nul == std::string::npos, the loop now breaks before executing args[nul] = ' ';, preventing the out-of-bounds/UB access via operator[] with pos == npos. The subsequent if (nargs != -1) return false; and args.erase(nul) logic is unchanged, so malformed input without a trailing NUL will correctly fail validation (nargs won't reach -1) rather than corrupt memory.

πŸ€– Prompt for AI agents
In osquery/tables/system/darwin/processes.cpp around line 366, review and complete this code-review fix: parseProcCmdline uses args[nul] after find may return npos on the last iteration.
What the draft fix changed: In `parseProcCmdline` (osquery/tables/system/darwin/processes.cpp), added a guard immediately after `nul = args.find('\0', start);` inside the `while (nargs-- && nul != std::string::npos)` loop: if `nul == std::string::npos`, the loop now `break`s before executing `args[nul] = ' ';`, preventing the out-of-bounds/UB access via `operator[]` with `pos == npos`. The subsequent `if (nargs != -1) return false;` and `args.erase(nul)` logic is unchanged, so malformed input without a trailing NUL will correctly fail validation (nargs won't reach -1) rather than corrupt memory.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 92 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 368 to 376

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 parseProcCmdline uses args[nul] after find may return npos on the last iteration

In parseProcCmdline (osquery/tables/system/darwin/processes.cpp), added a guard immediately after nul = args.find('\0', start); inside the while (nargs-- && nul != std::string::npos) loop: if nul == std::string::npos, the loop now breaks before executing args[nul] = ' ';, preventing the out-of-bounds/UB access via operator[] with pos == npos. The subsequent if (nargs != -1) return false; and args.erase(nul) logic is unchanged, so malformed input without a trailing NUL will correctly fail validation (nargs won't reach -1) rather than corrupt memory.

πŸ€– Prompt for AI agents
In osquery/tables/system/darwin/processes.cpp around line 366, review and complete this code-review fix: parseProcCmdline uses args[nul] after find may return npos on the last iteration.
What the draft fix changed: In `parseProcCmdline` (osquery/tables/system/darwin/processes.cpp), added a guard immediately after `nul = args.find('\0', start);` inside the `while (nargs-- && nul != std::string::npos)` loop: if `nul == std::string::npos`, the loop now `break`s before executing `args[nul] = ' ';`, preventing the out-of-bounds/UB access via `operator[]` with `pos == npos`. The subsequent `if (nargs != -1) return false;` and `args.erase(nul)` logic is unchanged, so malformed input without a trailing NUL will correctly fail validation (nargs won't reach -1) rather than corrupt memory.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 92 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand Down Expand Up @@ -808,3 +811,4 @@ QueryData genProcessMemoryMap(QueryContext& context) {
}
} // namespace tables
} // namespace osquery

2 changes: 1 addition & 1 deletion osquery/tables/system/darwin/sharing_preferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ int getPrinterSharingStatus() {
int ret = cupsAdminGetServerSettings(cups, &num_settings, &settings);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 getPrinterSharingStatus leaks cups_option_t settings array when cupsAdminGetServerSettings fails

In getPrinterSharingStatus (sharing_preferences.cpp), moved the cupsFreeOptions(num_settings, settings) call out of the success-only branch of the if (ret != 0) block so it is now called unconditionally after the if/else, ensuring settings allocated by cupsAdminGetServerSettings are freed regardless of success or failure. cupsFreeOptions safely handles num_settings == 0 / settings == nullptr on the failure path, so no double-free or null-deref risk is introduced.

πŸ€– Prompt for AI agents
In osquery/tables/system/darwin/sharing_preferences.cpp around line 120, review and complete this code-review fix: getPrinterSharingStatus leaks cups_option_t settings array when cupsAdminGetServerSettings fails.
What the draft fix changed: In getPrinterSharingStatus (sharing_preferences.cpp), moved the cupsFreeOptions(num_settings, settings) call out of the success-only branch of the `if (ret != 0)` block so it is now called unconditionally after the if/else, ensuring settings allocated by cupsAdminGetServerSettings are freed regardless of success or failure. cupsFreeOptions safely handles num_settings == 0 / settings == nullptr on the failure path, so no double-free or null-deref risk is introduced.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 85 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 getPrinterSharingStatus leaks cups_option_t settings array when cupsAdminGetServerSettings fails

In getPrinterSharingStatus (sharing_preferences.cpp), moved the cupsFreeOptions(num_settings, settings) call out of the success-only branch of the if (ret != 0) block so it is now called unconditionally after the if/else, ensuring settings allocated by cupsAdminGetServerSettings are freed regardless of success or failure. cupsFreeOptions safely handles num_settings == 0 / settings == nullptr on the failure path, so no double-free or null-deref risk is introduced.

πŸ€– Prompt for AI agents
In osquery/tables/system/darwin/sharing_preferences.cpp around line 120, review and complete this code-review fix: getPrinterSharingStatus leaks cups_option_t settings array when cupsAdminGetServerSettings fails.
What the draft fix changed: In getPrinterSharingStatus (sharing_preferences.cpp), moved the cupsFreeOptions(num_settings, settings) call out of the success-only branch of the `if (ret != 0)` block so it is now called unconditionally after the if/else, ensuring settings allocated by cupsAdminGetServerSettings are freed regardless of success or failure. cupsFreeOptions safely handles num_settings == 0 / settings == nullptr on the failure path, so no double-free or null-deref risk is introduced.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 85 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

if (ret != 0) {
value = cupsGetOption("_share_printers", num_settings, settings);
cupsFreeOptions(num_settings, settings);
} else {
VLOG(1) << "Unable to get CUPS server settings: " << cupsLastErrorString();
}
cupsFreeOptions(num_settings, settings);
httpClose(cups);

if (value != nullptr) {
Expand Down
7 changes: 5 additions & 2 deletions osquery/tables/system/darwin/usb_devices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ void genUSBDevice(const io_service_t& device, QueryData& results) {
Row r;

// Get the device details

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 genUSBDevice does not check the return value of IORegistryEntryCreateCFProperties

In genUSBDevice (osquery/tables/system/darwin/usb_devices.cpp), initialized details to nullptr, captured the kern_return_t from IORegistryEntryCreateCFProperties, and added an early return when the call fails or details is nullptr, matching the suggested fix and the pattern used in genPCIDevice. This prevents using an uninitialized dictionary and calling CFRelease on garbage memory.

πŸ€– Prompt for AI agents
In osquery/tables/system/darwin/usb_devices.cpp around line 30, review and complete this code-review fix: genUSBDevice does not check the return value of IORegistryEntryCreateCFProperties.
What the draft fix changed: In genUSBDevice (osquery/tables/system/darwin/usb_devices.cpp), initialized `details` to nullptr, captured the kern_return_t from IORegistryEntryCreateCFProperties, and added an early return when the call fails or details is nullptr, matching the suggested fix and the pattern used in genPCIDevice. This prevents using an uninitialized dictionary and calling CFRelease on garbage memory.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 92 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🦩 🟠 genUSBDevice does not check the return value of IORegistryEntryCreateCFProperties

In genUSBDevice (osquery/tables/system/darwin/usb_devices.cpp), initialized details to nullptr, captured the kern_return_t from IORegistryEntryCreateCFProperties, and added an early return when the call fails or details is nullptr, matching the suggested fix and the pattern used in genPCIDevice. This prevents using an uninitialized dictionary and calling CFRelease on garbage memory.

πŸ€– Prompt for AI agents
In osquery/tables/system/darwin/usb_devices.cpp around line 30, review and complete this code-review fix: genUSBDevice does not check the return value of IORegistryEntryCreateCFProperties.
What the draft fix changed: In genUSBDevice (osquery/tables/system/darwin/usb_devices.cpp), initialized `details` to nullptr, captured the kern_return_t from IORegistryEntryCreateCFProperties, and added an early return when the call fails or details is nullptr, matching the suggested fix and the pattern used in genPCIDevice. This prevents using an uninitialized dictionary and calling CFRelease on garbage memory.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 92 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

CFMutableDictionaryRef details;
IORegistryEntryCreateCFProperties(
CFMutableDictionaryRef details = nullptr;
auto ret = IORegistryEntryCreateCFProperties(
device, &details, kCFAllocatorDefault, kNilOptions);
if (ret != KERN_SUCCESS || details == nullptr) {
return;
}

r["usb_address"] = getIOKitProperty(details, "USB Address");
r["usb_port"] = getIOKitProperty(details, "PortNum");
Expand Down
Loading
Loading