Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
61b6965
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
6519cb0
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
28c23e8
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
e03775c
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
f59ca2c
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
d18f43e
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
d782687
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
3288390
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
d7ff196
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
819cfe8
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
99c01c0
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
cf536e1
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
5c36af7
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
87316db
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
5bfdddb
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
5e07aec
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
d30f725
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
e9add70
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
9c91272
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
792dd17
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 2026
4db61f3
fix(adhoc-sweep-fixes): 21 review findings across 21 files
flamingo[bot] Aug 24, 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: 6 additions & 1 deletion osquery/filesystem/posix/fileops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,11 @@ std::vector<std::string> platformGlob(const std::string& find_path) {
std::vector<std::string> results;

auto data = (glob_t*)alloca(sizeof(glob_t));
::glob(find_path.c_str(), GLOB_TILDE | GLOB_MARK | GLOB_BRACE, nullptr, data);
int rc = ::glob(
find_path.c_str(), GLOB_TILDE | GLOB_MARK | GLOB_BRACE, nullptr, data);
if (rc != 0) {
return results;
}
size_t count = data->gl_pathc;

for (size_t index = 0; index < count; index++) {
Comment on lines 276 to 286

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.

🦩 🟠 platformGlob leaks glob_t buffer and its internal allocations if glob() throws no exception but function returns early on failure paths

In platformGlob() (osquery/filesystem/posix/fileops.cpp), captured the return code of ::glob() into rc and added an if (rc != 0) { return results; } guard before reading data->gl_pathc/data->gl_pathv, preventing consumption of uninitialized/stale glob_t contents on failure (e.g. GLOB_NOMATCH, GLOB_ABORTED). This matches the suggested fix exactly; note ::globfree(data) is still only called on the success path, consistent with glob(3) semantics where globfree should not be called on a glob_t that glob() did not successfully populate.

(Automatically downgraded: no change in this fix lands near this finding's line β€” verify whether it was actually addressed.)

πŸ€– Prompt for AI agents
In osquery/filesystem/posix/fileops.cpp around line 249, review and complete this code-review fix: platformGlob leaks glob_t buffer and its internal allocations if glob() throws no exception but function returns early on failure paths.
What the draft fix changed: In platformGlob() (osquery/filesystem/posix/fileops.cpp), captured the return code of ::glob() into `rc` and added an `if (rc != 0) { return results; }` guard before reading `data->gl_pathc`/`data->gl_pathv`, preventing consumption of uninitialized/stale glob_t contents on failure (e.g. GLOB_NOMATCH, GLOB_ABORTED). This matches the suggested fix exactly; note `::globfree(data)` is still only called on the success path, consistent with glob(3) semantics where globfree should not be called on a glob_t that glob() did not successfully populate.

_(Automatically downgraded: no change in this fix lands near this finding's line β€” verify whether it was actually addressed.)_
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

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

Expand Down Expand Up @@ -381,3 +385,4 @@ Status platformFileno(FILE* file, int& fd) {
return Status::success();
}
} // namespace osquery

9 changes: 6 additions & 3 deletions osquery/sql/sqlite_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,17 +221,19 @@ static void concatFunc(sqlite3_context* context,
}

std::string output;
bool wroteAny = false;

for (auto i = starting; i < argc; i++) {
if (SQLITE_NULL == sqlite3_value_type(argv[i])) {
continue;
}

output.append(reinterpret_cast<const char*>(sqlite3_value_text(argv[i])));

if (sep != "" && i + 1 < argc) {
if (wroteAny && sep != "") {
output.append(sep);
}

output.append(reinterpret_cast<const char*>(sqlite3_value_text(argv[i])));
wroteAny = true;
}

// Give up if the output is so large it's length overflows int
Comment on lines 221 to 239

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.

🦩 🟠 concatFunc silently produces incorrect separator placement when trailing NULL args are skipped

In concatFunc (osquery/sql/sqlite_string.cpp), replaced the position-based separator lookahead (sep != "" && i + 1 < argc) with a wroteAny flag that tracks whether a value has actually been written to output. The separator is now prepended before appending a non-null value only if a previous non-null value was already written, so trailing (or interior) NULL args no longer cause a dangling/incorrect separator. E.g. concat_ws('-', 'a', NULL) now yields "a" instead of "a-", and concat_ws('-', NULL, 'a') yields "a" instead of "-a".

(Automatically downgraded: no change in this fix lands near this finding's line β€” verify whether it was actually addressed.)

πŸ€– Prompt for AI agents
In osquery/sql/sqlite_string.cpp around line 196, review and complete this code-review fix: concatFunc silently produces incorrect separator placement when trailing NULL args are skipped.
What the draft fix changed: In concatFunc (osquery/sql/sqlite_string.cpp), replaced the position-based separator lookahead (`sep != "" && i + 1 < argc`) with a `wroteAny` flag that tracks whether a value has actually been written to `output`. The separator is now prepended before appending a non-null value only if a previous non-null value was already written, so trailing (or interior) NULL args no longer cause a dangling/incorrect separator. E.g. concat_ws('-', 'a', NULL) now yields "a" instead of "a-", and concat_ws('-', NULL, 'a') yields "a" instead of "-a".

_(Automatically downgraded: no change in this fix lands near this finding's line β€” verify whether it was actually addressed.)_
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

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

Expand Down Expand Up @@ -354,3 +356,4 @@ void registerStringExtensions(sqlite3* db) {
nullptr);
}
} // namespace osquery

3 changes: 2 additions & 1 deletion osquery/tables/events/darwin/file_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Status FileEventSubscriber::Callback(const FSEventsEventContextRef& ec,
// Need to call configure on the publisher, not the subscriber
if (ec->fsevent_flags & kFSEventStreamEventFlagMount) {
// Should we add listening to the mount point
auto subscriber = ([this, &ec]() {
auto subscriber = ([this, ec]() {
auto msc = createSubscriptionContext();
msc->path = ec->path + "/*";
msc->category = "tmp";

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.

🦩 🟠 Lambda captures ec by reference and is executed asynchronously on a detached thread, risking use-after-free

In FileEventSubscriber::Callback, changed the lambda capture from [this, &ec] to [this, ec] so the FSEventsEventContextRef (a shared_ptr) is captured by value, giving the detached thread its own owning reference and eliminating the use-after-free risk of accessing ec->path after Callback returns.

πŸ€– Prompt for AI agents
In osquery/tables/events/darwin/file_events.cpp around line 90, review and complete this code-review fix: Lambda captures `ec` by reference and is executed asynchronously on a detached thread, risking use-after-free.
What the draft fix changed: In `FileEventSubscriber::Callback`, changed the lambda capture from `[this, &ec]` to `[this, ec]` so the `FSEventsEventContextRef` (a shared_ptr) is captured by value, giving the detached thread its own owning reference and eliminating the use-after-free risk of accessing `ec->path` after `Callback` returns.
Verify the change is correct and complete; do not refactor unrelated code.

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

Expand All @@ -109,3 +109,4 @@ Status FileEventSubscriber::Callback(const FSEventsEventContextRef& ec,
return Status::success();
}
}

6 changes: 6 additions & 0 deletions osquery/tables/events/darwin/user_interaction_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,14 @@ void UserInteractionSubscriber::configure() {
Status UserInteractionSubscriber::Callback(
const EventTappingEventContextRef& ec,
const EventTappingSubscriptionContextRef& sc) {
// TODO/FIXME: this Row is not populated from `ec` (the
// EventTappingEventContextRef) and is therefore emitted empty. This is
// incomplete scaffolding; the table should be populated with the actual
// event data (e.g. timestamp, event type, coordinates) from `ec` before
// this subscriber is considered production-ready.
Row r;
add(r);
return Status(0);
}
} // namespace osquery

Comment on lines 37 to +50

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.

🦩 🟠 UserInteractionSubscriber::Callback adds an entirely empty Row for every event, discarding all event data

In UserInteractionSubscriber::Callback (osquery/tables/events/darwin/user_interaction_events.cpp), added a TODO/FIXME comment immediately above the empty Row r; add(r); block explaining that the row is not populated from ec. This satisfies the finding's stated minimum acceptable fix ("if this is deliberate scaffolding it should at minimum be flagged with a TODO/FIXME comment") without risking incorrect field mappings. A complete fix would require populating r with actual fields (timestamp, event type, x/y coordinates, etc.) extracted from ec, which requires knowledge of the EventTappingEventContext struct definition in osquery/events/darwin/event_taps.h that is not visible in this file; making up field names without that visibility risks introducing compile errors or subtly wrong data, so no data-populating change was attempted here.

πŸ€– Prompt for AI agents
In osquery/tables/events/darwin/user_interaction_events.cpp around line 34, review and complete this code-review fix: UserInteractionSubscriber::Callback adds an entirely empty Row for every event, discarding all event data.
What the draft fix changed: In `UserInteractionSubscriber::Callback` (osquery/tables/events/darwin/user_interaction_events.cpp), added a TODO/FIXME comment immediately above the empty `Row r; add(r);` block explaining that the row is not populated from `ec`. This satisfies the finding's stated minimum acceptable fix ("if this is deliberate scaffolding it should at minimum be flagged with a TODO/FIXME comment") without risking incorrect field mappings. A complete fix would require populating `r` with actual fields (timestamp, event type, x/y coordinates, etc.) extracted from `ec`, which requires knowledge of the `EventTappingEventContext` struct definition in `osquery/events/darwin/event_taps.h` that is not visible in this file; making up field names without that visibility risks introducing compile errors or subtly wrong data, so no data-populating change was attempted here.
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

3 changes: 2 additions & 1 deletion osquery/tables/events/linux/process_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ Status AuditProcessEventSubscriber::ProcessEvents(
event_data.syscall_number,
*syscall_event_record);
if (!s.ok()) {
VLOG(1) << "Malformed AUDIT_SYSCALL event: " << status.getMessage();
VLOG(1) << "Malformed AUDIT_SYSCALL event: " << s.getMessage();
continue;
}

Comment on lines 206 to 212

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.

🦩 πŸ”΅ Stale status variable referenced in log message after being shadowed by a new local s in GetProcessIDs error path

In AuditProcessEventSubscriber::ProcessEvents, changed the log statement in the GetProcessIDs failure branch from status.getMessage() to s.getMessage(), so it now logs the message from the actual GetProcessIDs result (s) instead of the stale outer status variable set by the earlier IsThreadClone call.

πŸ€– Prompt for AI agents
In osquery/tables/events/linux/process_events.cpp around line 267, review and complete this code-review fix: Stale `status` variable referenced in log message after being shadowed by a new local `s` in GetProcessIDs error path.
What the draft fix changed: In `AuditProcessEventSubscriber::ProcessEvents`, changed the log statement in the `GetProcessIDs` failure branch from `status.getMessage()` to `s.getMessage()`, so it now logs the message from the actual `GetProcessIDs` result (`s`) instead of the stale outer `status` variable set by the earlier `IsThreadClone` call.
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 @@ -388,3 +388,4 @@ AuditProcessEventSubscriber::GetSyscallNameMap() noexcept {
return kSyscallNameMap;
}
} // namespace osquery

8 changes: 7 additions & 1 deletion osquery/tables/system/linux/memory_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <osquery/core/tables.h>
#include <osquery/filesystem/filesystem.h>
#include <osquery/logger/logger.h>
#include <osquery/utils/conversions/split.h>
#include <osquery/utils/expected/expected.h>

Expand All @@ -26,7 +27,12 @@ QueryData genMemoryMap(QueryContext& context) {

std::vector<std::string> regions;
std::string content;
readFile(kIOMemLocation, content);
auto status = readFile(kIOMemLocation, content);
if (!status.ok()) {
VLOG(1) << "Could not read " << kIOMemLocation << ": "
<< status.getMessage();
return results;
}

regions = osquery::split(content, "\n");
for (const auto& line : regions) {
Comment on lines 27 to 38

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.

🦩 🟠 genMemoryMap ignores the Status returned by readFile, silently proceeding on failure

In genMemoryMap (osquery/tables/system/linux/memory_map.cpp), the Status returned by readFile(kIOMemLocation, content) is now captured and checked; on failure a VLOG(1) diagnostic message including the status message is logged and the function returns the empty results early, matching the established pattern in sibling files. Added #include <osquery/logger/logger.h> to support VLOG usage.

πŸ€– Prompt for AI agents
In osquery/tables/system/linux/memory_map.cpp around line 23, review and complete this code-review fix: genMemoryMap ignores the Status returned by readFile, silently proceeding on failure.
What the draft fix changed: In genMemoryMap (osquery/tables/system/linux/memory_map.cpp), the Status returned by readFile(kIOMemLocation, content) is now captured and checked; on failure a VLOG(1) diagnostic message including the status message is logged and the function returns the empty results early, matching the established pattern in sibling files. Added #include <osquery/logger/logger.h> to support VLOG usage.
Verify the change is correct and complete; do not refactor unrelated code.

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

Expand Down
34 changes: 25 additions & 9 deletions osquery/tables/system/windows/bitlocker_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ static void fetchMethodResultLong(std::string& result,
}

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.

🦩 🟠 bitlocker_info.cpp: unchecked GetString/GetLong return statuses can leave stale row data across iterations

In genBitlockerInfo (osquery/tables/system/windows/bitlocker_info.cpp), moved the Row r; declaration inside the for loop so a fresh Row is used per WMI result, and added explicit Status checks on data.GetString/data.GetLong calls (DeviceID, DriveLetter, PersistentVolumeID, ConversionStatus, ProtectionStatus, EncryptionMethod), assigning a safe default ("" or -1) when the property retrieval fails. This prevents stale values from a previous iteration leaking into the current row and ensures missing properties produce explicit default values instead of silently reused data.

πŸ€– Prompt for AI agents
In osquery/tables/system/windows/bitlocker_info.cpp around line 41, review and complete this code-review fix: bitlocker_info.cpp: unchecked GetString/GetLong return statuses can leave stale row data across iterations.
What the draft fix changed: In genBitlockerInfo (osquery/tables/system/windows/bitlocker_info.cpp), moved the `Row r;` declaration inside the `for` loop so a fresh Row is used per WMI result, and added explicit Status checks on `data.GetString`/`data.GetLong` calls (DeviceID, DriveLetter, PersistentVolumeID, ConversionStatus, ProtectionStatus, EncryptionMethod), assigning a safe default ("" or -1) when the property retrieval fails. This prevents stale values from a previous iteration leaking into the current row and ensures missing properties produce explicit default values instead of silently reused data.
Verify the change is correct and complete; do not refactor unrelated code.

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


QueryData genBitlockerInfo(QueryContext& context) {
Row r;
QueryData results;

const Expected<WmiRequest, WmiError> wmiSystemReq =
Expand All @@ -54,16 +53,32 @@ QueryData genBitlockerInfo(QueryContext& context) {
}
const std::vector<WmiResultItem>& wmiResults = wmiSystemReq->results();
for (const auto& data : wmiResults) {
Row r;
long status = 0;
long emethod;
data.GetString("DeviceID", r["device_id"]);
data.GetString("DriveLetter", r["drive_letter"]);
data.GetString("PersistentVolumeID", r["persistent_volume_id"]);
data.GetLong("ConversionStatus", status);
r["conversion_status"] = INTEGER(status);
data.GetLong("ProtectionStatus", status);
r["protection_status"] = INTEGER(status);
data.GetLong("EncryptionMethod", emethod);
if (!data.GetString("DeviceID", r["device_id"]).ok()) {
r["device_id"] = "";
}
if (!data.GetString("DriveLetter", r["drive_letter"]).ok()) {
r["drive_letter"] = "";
}
if (!data.GetString("PersistentVolumeID", r["persistent_volume_id"])
.ok()) {
r["persistent_volume_id"] = "";
}
if (data.GetLong("ConversionStatus", status).ok()) {
r["conversion_status"] = INTEGER(status);
} else {
r["conversion_status"] = INTEGER(-1);
}
if (data.GetLong("ProtectionStatus", status).ok()) {
r["protection_status"] = INTEGER(status);
} else {
r["protection_status"] = INTEGER(-1);
}
if (!data.GetLong("EncryptionMethod", emethod).ok()) {
emethod = -1;
}
std::string emethod_str;
std::map<long, std::string> methods;

Expand Down Expand Up @@ -100,3 +115,4 @@ QueryData genBitlockerInfo(QueryContext& context) {
}
} // namespace tables
} // namespace osquery

3 changes: 2 additions & 1 deletion osquery/tables/system/windows/disk_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ namespace osquery {
namespace tables {

QueryData genDiskInfo(QueryContext& context) {
Row r;

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.

🦩 🟠 disk_info.cpp reuses a single Row across all WMI results without clearing it

In genDiskInfo (osquery/tables/system/windows/disk_info.cpp), moved Row r; declaration from outside the for loop to inside the loop body, so a fresh Row is constructed for each WMI result instead of reusing and copying the same object across iterations, preventing stale field values from bleeding between disks.

πŸ€– Prompt for AI agents
In osquery/tables/system/windows/disk_info.cpp around line 22, review and complete this code-review fix: disk_info.cpp reuses a single Row across all WMI results without clearing it.
What the draft fix changed: In genDiskInfo (osquery/tables/system/windows/disk_info.cpp), moved `Row r;` declaration from outside the for loop to inside the loop body, so a fresh Row is constructed for each WMI result instead of reusing and copying the same object across iterations, preventing stale field values from bleeding between disks.
Verify the change is correct and complete; do not refactor unrelated code.

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

QueryData results;

const Expected<WmiRequest, WmiError> wmiSystemReq =
Expand All @@ -30,6 +29,7 @@ QueryData genDiskInfo(QueryContext& context) {
}
const std::vector<WmiResultItem>& wmiResults = wmiSystemReq->results();
for (const auto& data : wmiResults) {
Row r;
long partitionCount = 0;
long index = 0;
data.GetLong("Partitions", partitionCount);
Expand All @@ -52,3 +52,4 @@ QueryData genDiskInfo(QueryContext& context) {
}
} // namespace tables
} // namespace osquery

3 changes: 1 addition & 2 deletions osquery/tables/system/windows/groups.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ QueryData genGroups(QueryContext& context) {
}

} else if (!selected_gids.empty()) {
auto selected_gids = gid_it->second.getAll(EQUALS);

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.

🦩 🟠 groups.cpp genGroups() shadows outer selected_gids variable inside the gid branch

Removed the redundant, shadowing local declaration auto selected_gids = gid_it->second.getAll(EQUALS); inside the else if (!selected_gids.empty()) branch of genGroups(), so the loop now uses the outer-scope selected_gids that was already computed and checked earlier in the function.

πŸ€– Prompt for AI agents
In osquery/tables/system/windows/groups.cpp around line 69, review and complete this code-review fix: groups.cpp genGroups() shadows outer selected_gids variable inside the gid branch.
What the draft fix changed: Removed the redundant, shadowing local declaration `auto selected_gids = gid_it->second.getAll(EQUALS);` inside the `else if (!selected_gids.empty())` branch of `genGroups()`, so the loop now uses the outer-scope `selected_gids` that was already computed and checked earlier in the function.
Verify the change is correct and complete; do not refactor unrelated code.

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

for (const auto& selected_gid_str : selected_gids) {
auto selected_gid_res = tryTo<std::uint32_t>(selected_gid_str);

Expand Down Expand Up @@ -94,3 +92,4 @@ QueryData genGroups(QueryContext& context) {
}
} // namespace tables
} // namespace osquery

10 changes: 6 additions & 4 deletions osquery/tables/system/windows/shared_resources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace {
// https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-share
const std::string kWin32ShareQuery{"SELECT * FROM Win32_Share"};

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.

🦩 🟠 genShares: signed/unsigned mismatch when checking WMI 'Type' bit flag against admin-share sentinel values

In the anonymous namespace of shared_resources.cpp, changed kShareTypeNameMap's key type from long to std::uint32_t, updated getShareTypeName's parameter type to const std::uint32_t&, and in genShares introduced an unsigned_type variable (the same static_cast<std::uint32_t>(type) already used for the row["type"] BIGINT conversion) which is now passed to getShareTypeName instead of the raw signed long type. This ensures the admin-share sentinel values (2147483648-2147483651), which overflow a 32-bit signed long, are compared using matching unsigned types on both the map keys and the lookup value, fixing the mismatch that caused admin shares to never match.

πŸ€– Prompt for AI agents
In osquery/tables/system/windows/shared_resources.cpp around line 24, review and complete this code-review fix: genShares: signed/unsigned mismatch when checking WMI 'Type' bit flag against admin-share sentinel values.
What the draft fix changed: In the anonymous namespace of `shared_resources.cpp`, changed `kShareTypeNameMap`'s key type from `long` to `std::uint32_t`, updated `getShareTypeName`'s parameter type to `const std::uint32_t&`, and in `genShares` introduced an `unsigned_type` variable (the same `static_cast<std::uint32_t>(type)` already used for the `row["type"]` BIGINT conversion) which is now passed to `getShareTypeName` instead of the raw signed `long type`. This ensures the admin-share sentinel values (2147483648-2147483651), which overflow a 32-bit signed `long`, are compared using matching unsigned types on both the map keys and the lookup value, fixing the mismatch that caused admin shares to never match.
Verify the change is correct and complete; do not refactor unrelated code.

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


const std::unordered_map<long, std::string> kShareTypeNameMap = {
const std::unordered_map<std::uint32_t, std::string> kShareTypeNameMap = {
{0, "Disk Drive"},
{1, "Print Queue"},
{2, "Device"},
Expand All @@ -33,7 +33,7 @@ const std::unordered_map<long, std::string> kShareTypeNameMap = {
{2147483650, "Device Admin"},
{2147483651, "IPC Admin"}};

const std::string& getShareTypeName(const long& share_type) {
const std::string& getShareTypeName(const std::uint32_t& share_type) {
static const std::string kInvalidShareTypeName;

auto it = kShareTypeNameMap.find(share_type);
Expand Down Expand Up @@ -96,8 +96,9 @@ QueryData genShares(QueryContext& context) {

long type{};
status = wmi_item.GetLong("Type", type);
row["type"] = BIGINT(status.ok() ? static_cast<std::uint32_t>(type) : 0);
row["type_name"] = SQL_TEXT(getShareTypeName(type));
auto unsigned_type = status.ok() ? static_cast<std::uint32_t>(type) : 0;
row["type"] = BIGINT(unsigned_type);
row["type_name"] = SQL_TEXT(getShareTypeName(unsigned_type));

row_list.push_back(std::move(row));
row.clear();
Expand All @@ -107,3 +108,4 @@ QueryData genShares(QueryContext& context) {
}

} // namespace osquery::tables

8 changes: 4 additions & 4 deletions osquery/tables/system/windows/userassist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ constexpr auto kFullRegPath =
"\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist";

// Get execution count

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.

🦩 🟠 userassist executionNum() returns -1 cast to std::size_t (huge unsigned value) on error paths, propagated into INTEGER() column

Changed executionNum()'s return type from std::size_t to long long (osquery/tables/system/windows/userassist.cpp), so the error sentinel -1 is preserved as a genuine negative value instead of being implicitly converted to SIZE_MAX. Updated both return -1; statements to return -1LL; and changed the success path to return static_cast<long long>(count.get());. The caller (genUserAssist) is unaffected since auto count = executionNum(assist_data); now deduces long long, and INTEGER(count) correctly renders -1 on error paths instead of a bogus huge unsigned count.

πŸ€– Prompt for AI agents
In osquery/tables/system/windows/userassist.cpp around line 26, review and complete this code-review fix: userassist executionNum() returns -1 cast to std::size_t (huge unsigned value) on error paths, propagated into INTEGER() column.
What the draft fix changed: Changed executionNum()'s return type from `std::size_t` to `long long` (osquery/tables/system/windows/userassist.cpp), so the error sentinel `-1` is preserved as a genuine negative value instead of being implicitly converted to SIZE_MAX. Updated both `return -1;` statements to `return -1LL;` and changed the success path to `return static_cast<long long>(count.get());`. The caller (`genUserAssist`) is unaffected since `auto count = executionNum(assist_data);` now deduces `long long`, and `INTEGER(count)` correctly renders -1 on error paths instead of a bogus huge unsigned count.
Verify the change is correct and complete; do not refactor unrelated code.

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

std::size_t executionNum(const std::string& assist_data) {
long long executionNum(const std::string& assist_data) {
if (assist_data.length() <= 16) {
LOG(WARNING) << "Userassist execution count format is incorrect";
return -1;
return -1LL;
}

std::string execution_count = assist_data.substr(8, 8);
Expand All @@ -43,9 +43,9 @@ std::size_t executionNum(const std::string& assist_data) {
auto count = tryTo<std::size_t>(execution_count, 16);
if (count.isError()) {
LOG(WARNING) << "Error getting execution count: " << count.takeError();
return -1;
return -1LL;
}
return count.get();
return static_cast<long long>(count.get());
}

QueryData genUserAssist(QueryContext& context) {
Expand Down
2 changes: 1 addition & 1 deletion osquery/utils/info/version.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include <osquery/utils/info/version.h>
#include <osquery/utils/conversions/split.h>

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.

🦩 πŸ”΅ Duplicate #include in version.cpp

Removed the duplicate #include <stdexcept> line near the top of version.cpp, leaving a single instance; no other lines changed.

πŸ€– Prompt for AI agents
In osquery/utils/info/version.cpp around line 12, review and complete this code-review fix: Duplicate #include <stdexcept> in version.cpp.
What the draft fix changed: Removed the duplicate `#include <stdexcept>` line near the top of version.cpp, leaving a single instance; no other lines changed.
Verify the change is correct and complete; do not refactor unrelated code.

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

#include <stdexcept>
#include <stdexcept>

namespace osquery {
Expand Down Expand Up @@ -49,3 +48,4 @@ bool versionAtLeast(const std::string& v, const std::string& sdk) {
}

} // namespace osquery

3 changes: 1 addition & 2 deletions osquery/utils/pidfile/pidfile_posix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,6 @@ boost::optional<Pidfile::Error> Pidfile::writeFile(
auto buffer_size = static_cast<ssize_t>(buffer.size());
auto remaining_bytes = buffer_size;

buffer_size = remaining_bytes = {static_cast<ssize_t>(buffer.size())};

for (int retry = 0; retry < 5 && remaining_bytes > 0; ++retry) {
auto buffer_ptr = buffer.data() + buffer_size - remaining_bytes;

Comment on lines 114 to 119

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.

🦩 🟠 pidfile_posix.cpp writeFile has redundant/confusing double-initialization of buffer_size and remaining_bytes

Removed the redundant reassignment line buffer_size = remaining_bytes = {static_cast<ssize_t>(buffer.size())}; in Pidfile::writeFile, keeping only the single initial computation of buffer_size and remaining_bytes via their declarations. No functional behavior changes since both lines computed the same value.

πŸ€– Prompt for AI agents
In osquery/utils/pidfile/pidfile_posix.cpp around line 108, review and complete this code-review fix: pidfile_posix.cpp writeFile has redundant/confusing double-initialization of buffer_size and remaining_bytes.
What the draft fix changed: Removed the redundant reassignment line `buffer_size = remaining_bytes = {static_cast<ssize_t>(buffer.size())};` in `Pidfile::writeFile`, keeping only the single initial computation of `buffer_size` and `remaining_bytes` via their declarations. No functional behavior changes since both lines computed the same value.
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 @@ -187,3 +185,4 @@ void Pidfile::destroyFile(FileHandle file_handle,
}

} // namespace osquery

5 changes: 5 additions & 0 deletions osquery/utils/pidfile/pidfile_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ Expected<std::string, Pidfile::Error> Pidfile::readFile(
remaining_bytes -= static_cast<std::size_t>(bytes_read);
}

if (remaining_bytes != 0) {
return createError(Pidfile::Error::IOError);
}

return buffer;
}

Comment on lines 161 to 170

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.

🦩 🟠 pidfile_windows.cpp readFile ignores retry counter's remaining_bytes check and can return truncated buffer without error

In Pidfile::readFile (Windows), added a check after the retry loop: if (remaining_bytes != 0) { return createError(Pidfile::Error::IOError); } before return buffer;, matching the POSIX version's behavior and the suggested fix. This ensures a read failure or incomplete read (e.g. ReadFile failing on every retry, leaving remaining_bytes equal to buffer_size) results in an IOError instead of silently returning a zero-filled/truncated buffer.

(Automatically downgraded: no change in this fix lands near this finding's line β€” verify whether it was actually addressed.)

πŸ€– Prompt for AI agents
In osquery/utils/pidfile/pidfile_windows.cpp around line 132, review and complete this code-review fix: pidfile_windows.cpp readFile ignores retry counter's remaining_bytes check and can return truncated buffer without error.
What the draft fix changed: In Pidfile::readFile (Windows), added a check after the retry loop: `if (remaining_bytes != 0) { return createError(Pidfile::Error::IOError); }` before `return buffer;`, matching the POSIX version's behavior and the suggested fix. This ensures a read failure or incomplete read (e.g. ReadFile failing on every retry, leaving remaining_bytes equal to buffer_size) results in an IOError instead of silently returning a zero-filled/truncated buffer.

_(Automatically downgraded: no change in this fix lands near this finding's line β€” verify whether it was actually addressed.)_
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

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

Expand All @@ -173,3 +177,4 @@ void Pidfile::destroyFile(FileHandle file_handle, const std::string&) noexcept {
}

} // namespace osquery

3 changes: 2 additions & 1 deletion osquery/utils/system/uptime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
namespace osquery {

long getUptime() {
#if defined(DARWIN)
#if defined(__APPLE__)
struct timeval boot_time;
size_t len = sizeof(boot_time);
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
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.

🦩 🟠 getUptime() on Apple platforms is gated on DARWIN but the file's #if/#elif chain checks APPLE

In getUptime() in osquery/utils/system/uptime.cpp, changed the preprocessor guard #if defined(DARWIN) to #if defined(__APPLE__) so it matches the include guard at the top of the file, ensuring the sysctl-based Apple implementation is compiled whenever __APPLE__ is defined rather than relying on the possibly-unset osquery-internal DARWIN macro.

πŸ€– Prompt for AI agents
In osquery/utils/system/uptime.cpp around line 9, review and complete this code-review fix: getUptime() on Apple platforms is gated on DARWIN but the file's #if/#elif chain checks __APPLE__.
What the draft fix changed: In getUptime() in osquery/utils/system/uptime.cpp, changed the preprocessor guard `#if defined(DARWIN)` to `#if defined(__APPLE__)` so it matches the include guard at the top of the file, ensuring the sysctl-based Apple implementation is compiled whenever `__APPLE__` is defined rather than relying on the possibly-unset osquery-internal `DARWIN` macro.
Verify the change is correct and complete; do not refactor unrelated code.

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

Expand Down Expand Up @@ -51,3 +51,4 @@ long getUptime() {
}

} // namespace osquery

4 changes: 4 additions & 0 deletions osquery/worker/ipc/posix/tests/worker_ipc_channels_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class WorkerIPCChannelsTest : public testing::Test {
std::string descriptors_path = "/dev/fd";
boost::filesystem::directory_iterator it(descriptors_path), end;
return std::distance(it, end) - 1;
#else
// Unsupported platform for this test helper
return -1;
#endif
}

Comment on lines 43 to 51

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.

🦩 🟠 getFdsOpen() has no return on the non-Linux/non-Darwin branch

Added an #else branch to getFdsOpen() in WorkerIPCChannelsTest (worker_ipc_channels_test.cpp) that returns -1 as an error sentinel, ensuring the function always has a return statement on any platform, eliminating the undefined behavior from falling off the end of a value-returning function.

πŸ€– Prompt for AI agents
In osquery/worker/ipc/posix/tests/worker_ipc_channels_test.cpp around line 26, review and complete this code-review fix: getFdsOpen() has no return on the non-Linux/non-Darwin branch.
What the draft fix changed: Added an `#else` branch to `getFdsOpen()` in `WorkerIPCChannelsTest` (worker_ipc_channels_test.cpp) that returns `-1` as an error sentinel, ensuring the function always has a return statement on any platform, eliminating the undefined behavior from falling off the end of a value-returning function.
Verify the change is correct and complete; do not refactor unrelated code.

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

Expand Down Expand Up @@ -169,3 +172,4 @@ TEST_F(WorkerIPCChannelsTest, test_pipe_ticket_leak) {
ASSERT_EQ(getFdsOpen(), fds_open + 4);
}
} // namespace osquery

7 changes: 6 additions & 1 deletion plugins/database/rocksdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,12 @@ Status RocksDBDatabasePlugin::removeRange(const std::string& domain,
} else {
options.sync = false;
}
// DeleteRange is exclusive of the high bound; explicitly delete the
// high key too so the overall range removed is inclusive of `high`,
// matching this method's documented/expected semantics. Preserve the
// first failing status instead of overwriting it.
auto s = getDB()->DeleteRange(options, cfh, low, high);
if (low <= high) {
if (s.ok()) {
s = getDB()->Delete(options, cfh, high);
}
return Status(s.code(), s.ToString());
Comment on lines 425 to 436

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.

🦩 🟠 removeRange performs a redundant/contradictory extra delete of the high bound

In RocksDBDatabasePlugin::removeRange (plugins/database/rocksdb.cpp), changed the unconditional if (low <= high) (always true, since low > high is rejected earlier) to if (s.ok()), so the follow-up Delete(options, cfh, high) only runs when DeleteRange succeeded, preserving/propagating the first failing status instead of silently discarding it. The extra Delete(high) call itself is kept (with a clarifying comment) to preserve the pre-existing inclusive-upper-bound behavior of removeRange, since removing that call outright would be a semantic/behavioral change beyond what the finding asked for; the residual risk is that if DeleteRange fails, high will no longer be deleted, which is correct per the finding's status-clobbering complaint but is a subtle behavior change callers should be aware of.

(Automatically downgraded: no change in this fix lands near this finding's line β€” verify whether it was actually addressed.)

πŸ€– Prompt for AI agents
In plugins/database/rocksdb.cpp around line 366, review and complete this code-review fix: removeRange performs a redundant/contradictory extra delete of the high bound.
What the draft fix changed: In `RocksDBDatabasePlugin::removeRange` (plugins/database/rocksdb.cpp), changed the unconditional `if (low <= high)` (always true, since `low > high` is rejected earlier) to `if (s.ok())`, so the follow-up `Delete(options, cfh, high)` only runs when `DeleteRange` succeeded, preserving/propagating the first failing status instead of silently discarding it. The extra `Delete(high)` call itself is kept (with a clarifying comment) to preserve the pre-existing inclusive-upper-bound behavior of `removeRange`, since removing that call outright would be a semantic/behavioral change beyond what the finding asked for; the residual risk is that if `DeleteRange` fails, `high` will no longer be deleted, which is correct per the finding's status-clobbering complaint but is a subtle behavior change callers should be aware of.

_(Automatically downgraded: no change in this fix lands near this finding's line β€” verify whether it was actually addressed.)_
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

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

Expand Down Expand Up @@ -466,3 +470,4 @@ Status RocksDBDatabasePlugin::scan(const std::string& domain,
return Status::success();
}
} // namespace osquery

21 changes: 15 additions & 6 deletions plugins/remote/enroll/tls_enroll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ namespace osquery {

DECLARE_string(enroll_secret_path);
DECLARE_bool(disable_enrollment);
DECLARE_bool(openframe_mode);

CLI_FLAG(uint64,
tls_enroll_max_attempts,
Expand All @@ -51,6 +50,14 @@ CLI_FLAG(string,
"",
"TLS/HTTPS endpoint for client enrollment");

/// Optional path prefix inserted before the enroll endpoint (e.g. for
/// gateway/backend specific routing). Empty by default, meaning no prefix
/// is added.
CLI_FLAG(string,
enroll_tls_endpoint_prefix,
"",
"Optional URL path prefix prepended to the enroll TLS endpoint");

/// Undocumented feature for TLS access token passing.
HIDDEN_FLAG(bool,
tls_secret_always,
Expand All @@ -73,12 +80,13 @@ std::string TLSEnrollPlugin::enroll() {

// If no node secret has been negotiated, try a TLS request.
auto uri = "https://" + FLAGS_tls_hostname;

// Add the prefix "/tools/agent/fleetmdm-server" to all requests only if openframe mode is enabled
if (FLAGS_openframe_mode) {

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.

🦩 🟠 TLS enroll hardcodes OpenFrame-specific URL path segment inline in shared plugin logic

In TLSEnrollPlugin::enroll() (plugins/remote/enroll/tls_enroll.cpp), removed the hardcoded literal "/tools/agent/fleetmdm-server" and the FLAGS_openframe_mode gate, replacing them with a new configurable FLAGS_enroll_tls_endpoint_prefix string CLI flag (empty by default, preserving standard enroll behavior). The DECLARE_bool(openframe_mode) declaration was also removed since it's no longer referenced in this file. This decouples core TLS enroll logic from the OpenFrame-specific magic string; the OpenFrame subsystem is expected to set --enroll_tls_endpoint_prefix=/tools/agent/fleetmdm-server at its own configuration layer. Unverified: whether openframe_mode flag is still defined/used elsewhere (e.g. in an openframe/ subsystem file) and whether that code needs updating to set the new flag instead β€” that is outside this single file's scope and would require a companion change there to fully preserve OpenFrame's existing behavior.

πŸ€– Prompt for AI agents
In plugins/remote/enroll/tls_enroll.cpp around line 78, review and complete this code-review fix: TLS enroll hardcodes OpenFrame-specific URL path segment inline in shared plugin logic.
What the draft fix changed: In `TLSEnrollPlugin::enroll()` (plugins/remote/enroll/tls_enroll.cpp), removed the hardcoded literal `"/tools/agent/fleetmdm-server"` and the `FLAGS_openframe_mode` gate, replacing them with a new configurable `FLAGS_enroll_tls_endpoint_prefix` string CLI flag (empty by default, preserving standard enroll behavior). The `DECLARE_bool(openframe_mode)` declaration was also removed since it's no longer referenced in this file. This decouples core TLS enroll logic from the OpenFrame-specific magic string; the OpenFrame subsystem is expected to set `--enroll_tls_endpoint_prefix=/tools/agent/fleetmdm-server` at its own configuration layer. Unverified: whether `openframe_mode` flag is still defined/used elsewhere (e.g. in an openframe/ subsystem file) and whether that code needs updating to set the new flag instead β€” that is outside this single file's scope and would require a companion change there to fully preserve OpenFrame's existing behavior.
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

uri += "/tools/agent/fleetmdm-server";

// Add an optional path prefix to all requests, configurable via flag
// rather than hardcoded in this shared plugin logic.
if (!FLAGS_enroll_tls_endpoint_prefix.empty()) {
uri += FLAGS_enroll_tls_endpoint_prefix;
}

uri += FLAGS_enroll_tls_endpoint;

if (FLAGS_tls_secret_always) {
Expand Down Expand Up @@ -174,3 +182,4 @@ Status TLSEnrollPlugin::requestKey(const std::string& uri,
return Status::success();
}
} // namespace osquery

Loading