Skip to content

Add ai_tools fleetd table#49243

Open
dantecatalfamo wants to merge 6 commits into
mainfrom
47619-ai-tools-table
Open

Add ai_tools fleetd table#49243
dantecatalfamo wants to merge 6 commits into
mainfrom
47619-ai-tools-table

Conversation

@dantecatalfamo

@dantecatalfamo dantecatalfamo commented Jul 13, 2026

Copy link
Copy Markdown
Member

Related issue: Resolves #47619

Adds a new ai_tools table to the fleetd tables extension so hosts running plain osquery (e.g. via ufa) get it too, not only hosts running the standalone extension from #47641.

What this does

Vendors the karmine05/agentic-detector osquery extension (v0.3.0, commit 7c942d0) into orbit/pkg/table/ai_tools/ by copying its source (internal/* collectors + the tables package, renamed to ai_tools), and registers the unified ai_tools table cross-platform in OrbitDefaultTables().

The table exposes a single schema with a type discriminator (mcp_server, ide_plugins, agents, apps, sockets, agent_instruction, browser_extension) plus risk_flags, sha256, and a JSON detail column. WHERE type = '…' pushes down so only the requested collectors run.

Dependencies (gopsutil/v4, howett.net/plist, gopkg.in/yaml.v3, osquery-go, golang.org/x/sys) already exist in go.mod at Fleet's pinned versions; only yaml.v3 moved from indirect to direct. The upstream module is not added as a dependency — the source is vendored.

Commits

  1. Add vendored ai_tools fleetd table — the as-is import + wiring, adjusted only to satisfy Fleet's linters (behavior-preserving).
  2. Harden ai_tools collectors against root-privilege abuse — this table runs in the root/SYSTEM daemon over every user's home dir; a security audit of v0.3.0 found five ways a low-priv local user could abuse that. Fixed: FIFO/special-file DoS, symlink hash/content disclosure, path traversal via attacker-controlled config/manifest/plist fields, unexpected outbound DNS egress from untrusted MCP hostnames, and missing panic recovery.
  3. Add changes file.

Note for reviewers: the upstream repo has no LICENSE file; please confirm the licensing arrangement before merge. A LOW/informational audit item (homes.All trusting the home directory name for uid/username attribution) is left as a documented follow-up.

Checklist for submitter

  • Changes file added for user-visible changes in orbit/changes/.
  • Input data is properly validated (attacker-controlled config/manifest/plist fields are contained/rejected; files opened non-following and non-blocking; no SQL/JS/shell interpolation in this code).
  • Timeouts are implemented and retries are limited to avoid infinite loops (bounded directory walk; non-blocking file opens; no unbounded reads or network resolution).

Testing

  • Added/updated automated tests (vendored suite retained; added regression tests for symlink/FIFO refusal and addon-id path traversal).
  • QA'd all new/changed functionality manually

fleetd/orbit/Fleet Desktop

  • If the change applies to only one platform, confirmed that runtime.GOOS is used as needed to isolate changes
  • Verified that fleetd runs on macOS, Linux and Windows (package builds for darwin arm64/amd64, linux, windows, and the freebsd fallback stub; both lint gates clean on all three primary OSes)

Summary by CodeRabbit

  • New Features
    • Added a unified ai_tools inventory table covering AI apps, IDE plugins, browser extensions, agent tooling, MCP servers, instruction files, and related network/socket activity.
    • Provides consolidated per-artifact details (versions, install paths, ownership, running/PID, endpoints when available), plus hashes and risk flags across macOS, Linux, and Windows.
  • Bug Fixes
    • Improved robustness against malformed/untrusted host data by safely handling failures and more conservatively flagging uncertain permissions, sideloading, and detection.
  • Tests
    • Added broader unit coverage and an optional live-host smoke test to validate table output.
  • Chores
    • Updated YAML dependency declaration.

Vendor the karmine05/agentic-detector osquery extension (v0.3.0, commit
7c942d0) into orbit/pkg/table/ai_tools/ and register the unified `ai_tools`
table cross-platform in OrbitDefaultTables(), so plain-osquery deployments
get the table via the fleetd tables extension.

The extension exposes a single `ai_tools` table with a `type` discriminator
covering mcp_server, ide_plugins, agents, apps, sockets, agent_instruction,
and browser_extension, plus risk_flags/sha256/detail columns.

Imported as-is: internal/* collectors and the tables package copied verbatim
with the import prefix rewritten and the package renamed to ai_tools. Added an
apps stub for the non-darwin/linux/windows fallback build and exported
Columns()/Generate() wrappers for registration. Adjusted the copied source to
satisfy Fleet's linters (set types, modernize idioms, defensive nil guards);
all changes are behavior-preserving.

Deps (gopsutil/v4, howett.net/plist, gopkg.in/yaml.v3, osquery-go,
golang.org/x/sys) already exist in go.mod at Fleet's pinned versions; only
yaml.v3 changed from indirect to direct.
The ai_tools table runs in-process in the root/SYSTEM orbit daemon and
walks, reads, and hashes files across every user's home directory. A prior
audit of the vendored v0.3.0 source found five ways an unprivileged local
user could abuse that. Fix each while preserving behavior for legitimate
inputs:

- FIFO / special-file DoS: fsutil now opens files via OpenRegular, which
  Lstat-checks for a regular file and opens O_NONBLOCK, so a named pipe or
  device planted at a scanned path can no longer block the daemon. SHA256,
  Exists, and the new ReadFileBounded all go through it, and every raw
  os.ReadFile / os.Open in the collectors now routes through the safe
  helpers.
- Symlink hash/content disclosure: the same Lstat + IsRegular check refuses
  symlinks, so a low-priv user can no longer point a scanned path at a
  root-only file to leak its hash or content signals. Agent binaries under
  trusted system bin dirs (Homebrew et al.) are still resolved and hashed.
- Path traversal via attacker-controlled fields: the Chromium Preferences
  "path", Gecko addon id, and macOS CFBundleExecutable are now contained to
  the owning home / rejected when they contain separators or "..".
- Outbound DNS egress: netsock no longer resolves hostnames taken from
  untrusted MCP config files; egress is attributed by owning process, which
  is DNS-free.
- No panic recovery: the exported Generate wrapper now recovers from any
  collector panic and returns it as a query error, so one malformed file
  cannot crash the daemon's custom-table surface.

Adds regression tests for symlink/FIFO refusal and addon-id traversal.

Not addressed (informational): homes.All trusts the home directory name for
the uid/username attribution columns; enumerating from the OS user database
would be a follow-up.
@dantecatalfamo dantecatalfamo requested a review from a team as a code owner July 13, 2026 20:47
Copilot AI review requested due to automatic review settings July 13, 2026 20:47
@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 56.92521% with 933 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.91%. Comparing base (2eb1cba) to head (c316715).
⚠️ Report is 54 commits behind head on main.

Files with missing lines Patch % Lines
orbit/pkg/table/ai_tools/tables.go 17.46% 239 Missing and 2 partials ⚠️
orbit/pkg/table/ai_tools/internal/ide/others.go 33.33% 105 Missing and 1 partial ⚠️
orbit/pkg/table/ai_tools/internal/ide/jetbrains.go 14.95% 91 Missing ⚠️
orbit/pkg/table/ai_tools/internal/mcp/mcp.go 72.12% 71 Missing and 9 partials ⚠️
...bit/pkg/table/ai_tools/internal/apps/apps_linux.go 0.00% 54 Missing ⚠️
orbit/pkg/table/ai_tools/internal/agents/agents.go 66.88% 36 Missing and 14 partials ⚠️
orbit/pkg/table/ai_tools/internal/homes/homes.go 7.54% 49 Missing ⚠️
...g/table/ai_tools/internal/browserext/browserext.go 60.82% 35 Missing and 3 partials ⚠️
orbit/pkg/table/ai_tools/internal/apps/apps.go 55.84% 30 Missing and 4 partials ⚠️
...pkg/table/ai_tools/internal/browserext/chromium.go 77.24% 17 Missing and 16 partials ⚠️
... and 14 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #49243      +/-   ##
==========================================
- Coverage   67.95%   67.91%   -0.04%     
==========================================
  Files        3766     3797      +31     
  Lines      238601   242088    +3487     
  Branches    12465    12465              
==========================================
+ Hits       162132   164418    +2286     
- Misses      61816    62708     +892     
- Partials    14653    14962     +309     
Flag Coverage Δ
backend 69.44% <56.92%> (-0.07%) ⬇️
backend-activity 85.64% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds the new ai_tools osquery table to Orbit’s default (fleetd tables) extension so it’s available to hosts running plain osquery with the fleetd tables extension (e.g. via ufa). The implementation vendors the upstream “agentic-detector” collectors into orbit/pkg/table/ai_tools/, wires the table into OrbitDefaultTables(), and includes a suite of unit tests plus an opt-in live-host smoke test.

Changes:

  • Register ai_tools in OrbitDefaultTables() and expose exported Columns()/Generate() wrappers for extension registration.
  • Add a unified ai_tools table implementation with collector pushdown by discriminator and type-specific detail JSON.
  • Add/retain unit tests across collectors and promote gopkg.in/yaml.v3 to a direct dependency.

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
orbit/pkg/table/extension.go Registers the ai_tools plugin in Orbit’s default table set.
orbit/pkg/table/ai_tools/tables.go Defines unified schema, constraint pushdown, and row mapping for ai_tools.
orbit/pkg/table/ai_tools/ai_tools.go Exports Columns()/Generate() wrappers and adds panic recovery around generation.
orbit/pkg/table/ai_tools/smoke_test.go Adds opt-in live-host smoke test for the unified table + pushdown.
orbit/pkg/table/ai_tools/browserext_row_test.go Unit tests for browser extension row mapping and type registration.
orbit/pkg/table/ai_tools/internal/proc/proc.go Process/connection snapshot used by multiple collectors to avoid repeated enumeration.
orbit/pkg/table/ai_tools/internal/paths/paths.go Resolves per-OS app-data roots from a home directory.
orbit/pkg/table/ai_tools/internal/netsock/netsock.go Classifies listening/egress sockets into AI/MCP-related categories.
orbit/pkg/table/ai_tools/internal/netsock/netsock_test.go Tests socket classification behavior (including loopback IPC exclusion).
orbit/pkg/table/ai_tools/internal/mcp/mcp.go Discovers MCP servers from client config files across platforms and scopes.
orbit/pkg/table/ai_tools/internal/mcp/correlate.go Correlates declared MCP servers with running processes; adds process-only discoveries.
orbit/pkg/table/ai_tools/internal/mcp/risk.go Computes MCP posture/risk flags (fetch-and-run, secrets, cleartext, perms, capabilities).
orbit/pkg/table/ai_tools/internal/mcp/mcp_test.go Tests MCP config scanning and process correlation.
orbit/pkg/table/ai_tools/internal/mcp/risk_test.go Tests MCP risk flag derivation and helper behaviors (pinning, secret env detection).
orbit/pkg/table/ai_tools/internal/instructions/instructions.go Discovers agent instruction files and flags injection/hidden-unicode/world-writable risks.
orbit/pkg/table/ai_tools/internal/instructions/instructions_test.go Tests instruction discovery, injection marker detection, hidden unicode, and perms flags.
orbit/pkg/table/ai_tools/internal/ide/ide.go Entry point for IDE/plugin discovery across editor families.
orbit/pkg/table/ai_tools/internal/ide/vscode.go VS Code family extension scanning and AI classification (AI-only emission).
orbit/pkg/table/ai_tools/internal/ide/jetbrains.go JetBrains plugin scanning via plugin.xml discovery in dirs/jars.
orbit/pkg/table/ai_tools/internal/ide/others.go Scanners for Zed/Sublime/Vim/Emacs and minimal manifest parsing helpers.
orbit/pkg/table/ai_tools/internal/ide/ide_test.go Tests VS Code scanning, obsolete filtering, ELPA parsing, and JetBrains editor label derivation.
orbit/pkg/table/ai_tools/internal/homes/homes.go Enumerates real user home directories across platforms.
orbit/pkg/table/ai_tools/internal/fsutil/fsutil.go Safe filesystem helpers (bounded reads/hashes, perms, bounded directory walks).
orbit/pkg/table/ai_tools/internal/fsutil/fsutil_unix_test.go Tests symlink refusal and FIFO non-blocking behavior on non-Windows.
orbit/pkg/table/ai_tools/internal/fsutil/fsutil_test.go Tests hashing, perms, bounded walking, and Exists behavior.
orbit/pkg/table/ai_tools/internal/classify/kb.json Embedded knowledge base for AI identifiers/regex/ports/capabilities.
orbit/pkg/table/ai_tools/internal/classify/classify.go Loads embedded KB and exposes classification helpers (plugins/cmdline/ports/capabilities/exts).
orbit/pkg/table/ai_tools/internal/classify/classify_test.go Tests classification behaviors for VS Code plugins, cmdlines, ports, capabilities, browser extensions.
orbit/pkg/table/ai_tools/internal/browserext/browserext.go Scans Chromium/Gecko browser profiles for AI extensions across users.
orbit/pkg/table/ai_tools/internal/browserext/chromium.go Chromium extension inventory (disk + prefs union), i18n name resolution, path containment.
orbit/pkg/table/ai_tools/internal/browserext/gecko.go Gecko extension inventory from extensions.json + XPI manifest fallback; traversal rejection.
orbit/pkg/table/ai_tools/internal/browserext/risk.go Browser extension risk flags (broad hosts, sideloading/unverified).
orbit/pkg/table/ai_tools/internal/browserext/browserext_test.go Tests Chromium/Gecko extension collection, risk flags, traversal rejection, profiles discovery, Scan integration.
orbit/pkg/table/ai_tools/internal/apps/apps.go Cross-platform AI desktop app detection orchestration + liveness + hashing.
orbit/pkg/table/ai_tools/internal/apps/apps_darwin.go macOS app bundle scanning + Info.plist parsing with executable path hardening.
orbit/pkg/table/ai_tools/internal/apps/apps_linux.go Linux .desktop scanning and service-binary fallback detection.
orbit/pkg/table/ai_tools/internal/apps/apps_windows.go Windows uninstall-registry scanning for known AI apps.
orbit/pkg/table/ai_tools/internal/apps/apps_stub.go Stub for unsupported platforms to keep builds working.
orbit/pkg/table/ai_tools/internal/apps/apps_test.go Tests known-app matching logic.
orbit/pkg/table/ai_tools/internal/agents/agents.go Detects known AI agent CLIs from disk (no exec), versions from manifests, runtime posture from process snapshot.
orbit/pkg/table/ai_tools/internal/agents/posture.go Enriches autonomy posture/risk flags (Claude settings + runtime flags).
orbit/pkg/table/ai_tools/internal/agents/agents_test.go Tests agent detection and running-process matching.
orbit/pkg/table/ai_tools/internal/agents/posture_test.go Tests permission mode parsing and posture risk flagging.
orbit/changes/47619-ai-tools-table Adds Orbit change log entry for the new ai_tools table.
go.mod Promotes gopkg.in/yaml.v3 to a direct dependency.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +46
if !fi.Mode().IsRegular() {
return nil, os.ErrInvalid
}
return os.OpenFile(path, os.O_RDONLY|syscall.O_NONBLOCK, 0) // #nosec G304 -- path discovered by a curated collector; opened non-following, non-blocking, regular-only
}
Comment thread orbit/pkg/table/ai_tools/tables.go Outdated
Comment on lines +1 to +5
// Package tables exposes a single, unified osquery table — ai_tools —
// covering every AI-tool type (MCP servers, IDE plugins, AI agent
// CLIs, AI desktop apps, live AI/MCP sockets, agent instruction files, and browser extensions)
// through one schema with a `type` discriminator, security `risk_flags` and
// `sha256` columns, and a JSON `detail` column for type-specific fields.
Comment on lines +88 to +92
func generate(ctx context.Context, qc table.QueryContext) ([]map[string]string, error) {
types := requestedTypes(qc)
has := func(t string) bool { _, ok := types[t]; return ok }

hs := homes.All()
Comment on lines +46 to +50
// columns is the unified schema. Common fields are first-class; everything
// type-specific lives in `detail` (compact JSON, empty fields omitted).
var columns = []string{
"type", // mcp_server | ide_plugins | agents | apps | sockets | agent_instruction | browser_extension
"name", // server/plugin/agent/app/process/instruction-file name
Comment on lines +37 to +49
// Collect classifies the snapshot's connections. remoteMCPHosts maps a hostname
// parsed from a remote MCP config to a label; those hosts are resolved to IPs so
// established connections can be attributed even without per-connection reverse
// DNS.
func Collect(ctx context.Context, snap *proc.Snapshot, remoteMCPHosts map[string]string) []Socket {
if snap == nil {
return nil
}
hosts := buildHostSet(remoteMCPHosts)
var out []Socket

for _, c := range snap.Conns {
p := snap.Procs[c.PID]
Comment thread orbit/pkg/table/ai_tools/internal/mcp/mcp.go Outdated
Comment thread orbit/pkg/table/ai_tools/smoke_test.go Outdated
// unconstrained (all types), then with a type='ide_plugins' constraint to prove
// pushdown returns only that type. Opt-in (AED_SMOKE=1) — reads live state.
//
// AED_SMOKE=1 go test -run TestSmokeLiveHost -v ./tables/
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2d94bde1-6049-4447-9737-d9c073ff3980

📥 Commits

Reviewing files that changed from the base of the PR and between 5bb82c1 and c316715.

📒 Files selected for processing (14)
  • orbit/pkg/table/ai_tools/internal/apps/apps.go
  • orbit/pkg/table/ai_tools/internal/apps/apps_test.go
  • orbit/pkg/table/ai_tools/internal/browserext/browserext.go
  • orbit/pkg/table/ai_tools/internal/browserext/browserext_test.go
  • orbit/pkg/table/ai_tools/internal/browserext/gecko.go
  • orbit/pkg/table/ai_tools/internal/fsutil/fsutil.go
  • orbit/pkg/table/ai_tools/internal/fsutil/openflags_unix.go
  • orbit/pkg/table/ai_tools/internal/fsutil/openflags_windows.go
  • orbit/pkg/table/ai_tools/internal/ide/jetbrains.go
  • orbit/pkg/table/ai_tools/internal/instructions/instructions.go
  • orbit/pkg/table/ai_tools/internal/mcp/mcp.go
  • orbit/pkg/table/ai_tools/internal/netsock/netsock.go
  • orbit/pkg/table/ai_tools/smoke_test.go
  • orbit/pkg/table/ai_tools/tables.go
🚧 Files skipped from review as they are similar to previous changes (10)
  • orbit/pkg/table/ai_tools/smoke_test.go
  • orbit/pkg/table/ai_tools/internal/browserext/gecko.go
  • orbit/pkg/table/ai_tools/internal/fsutil/fsutil.go
  • orbit/pkg/table/ai_tools/internal/browserext/browserext.go
  • orbit/pkg/table/ai_tools/internal/mcp/mcp.go
  • orbit/pkg/table/ai_tools/internal/ide/jetbrains.go
  • orbit/pkg/table/ai_tools/tables.go
  • orbit/pkg/table/ai_tools/internal/instructions/instructions.go
  • orbit/pkg/table/ai_tools/internal/browserext/browserext_test.go
  • orbit/pkg/table/ai_tools/internal/netsock/netsock.go

Walkthrough

Adds a unified ai_tools osquery table that inventories AI agents, desktop applications, IDE and browser extensions, MCP servers, network sockets, and instruction files. New cross-platform collectors discover artifacts across user homes, capture process state, compute hashes, classify AI tooling, and attach risk metadata. The table supports type-based collector filtering and JSON details, is registered in the default Orbit extension tables, and includes supporting tests plus a direct YAML dependency declaration.

Possibly related PRs

  • fleetdm/fleet#47871: Adds Fleet schema and YAML definitions corresponding to the Go ai_tools table implementation.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding the ai_tools fleetd table.
Description check ✅ Passed The description follows the template well and includes the related issue, summary, commits, and relevant checklist items.
Linked Issues check ✅ Passed The changes implement the requested cross-platform ai_tools table, collectors, schema, pushdown, risk flags, and tests described in #47619.
Out of Scope Changes check ✅ Passed The added files and go.mod tweak support the ai_tools table and its hardening, with no obvious unrelated code changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 47619-ai-tools-table

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (5)
orbit/pkg/table/ai_tools/internal/instructions/instructions.go (1)

189-191: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Single Read call may silently truncate the scanned content.

f.Read(buf) isn't guaranteed to fill buf up to maxReadBytes or EOF in one call; a short read (more plausible on networked home directories) would cause injectionMarkers/hidden-unicode detection to silently miss content near/after the truncation point.

♻️ Proposed fix
-	buf := make([]byte, maxReadBytes)
-	n, _ := f.Read(buf)
-	content := string(buf[:n])
+	buf := make([]byte, maxReadBytes)
+	n, _ := io.ReadFull(f, buf) // expected io.ErrUnexpectedEOF for files < maxReadBytes; n is still valid
+	content := string(buf[:n])

(requires adding "io" to imports)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@orbit/pkg/table/ai_tools/internal/instructions/instructions.go` around lines
189 - 191, Update the file-reading logic in the surrounding instruction-loading
function to use io.ReadAllLimit (or an equivalent io-based loop) instead of
relying on the single f.Read call. Preserve the maxReadBytes limit, ensure short
reads continue until EOF or the limit is reached, and retain the existing string
conversion and downstream detection behavior.
orbit/pkg/table/ai_tools/internal/proc/proc.go (1)

45-76: 🩺 Stability & Availability | 🔵 Trivial

Consider a hard deadline around process/connection enumeration.

ctx.Err() is only checked between iterations (Line 50); a single stuck syscall inside gopsutil (e.g. UsernameWithContext hitting a slow NSS backend, or ExeWithContext on a hung network mount) won't be interrupted by context cancellation. Since this runs in-process in the root/SYSTEM orbit daemon and the table already added panic recovery for similar "one bad artifact shouldn't crash everything" reasons, consider giving Take (or its caller in tables.go) an explicit context.WithTimeout budget so a single hung syscall degrades gracefully instead of stalling the whole query.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@orbit/pkg/table/ai_tools/internal/proc/proc.go` around lines 45 - 76, Update
Take to enforce an explicit context timeout covering both process and connection
enumeration, using a bounded budget appropriate for the orbit daemon. Derive the
timeout context at the start of Take, pass it to all gopsutil enumeration and
per-process calls, and ensure cancellation causes enumeration to stop and the
snapshot to return rather than waiting indefinitely on a single syscall.
orbit/pkg/table/ai_tools/internal/classify/classify.go (1)

41-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Panic on invalid regex patterns for consistency with JSON parsing.

The init() function panics on json.Unmarshal failure (line 39) but silently skips regex compilation errors. Both kbBytes and data.NameRegex are embedded, in-repo data — an invalid regex is equally a build-time bug. Silently skipping would cause missing classification patterns to go unnoticed.

♻️ Proposed fix
 	for _, p := range data.NameRegex {
-		if re, err := regexp.Compile(p); err == nil {
-			nameRes = append(nameRes, re)
-		}
+		re, err := regexp.Compile(p)
+		if err != nil {
+			panic("classify: invalid regex in kb.json " + strconv.Quote(p) + ": " + err.Error())
+		}
+		nameRes = append(nameRes, re)
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@orbit/pkg/table/ai_tools/internal/classify/classify.go` around lines 41 - 45,
Update the regex compilation loop in init to panic when regexp.Compile fails,
matching the existing json.Unmarshal failure behavior for embedded data. Do not
silently skip invalid patterns; preserve appending successfully compiled
expressions to nameRes.
orbit/pkg/table/ai_tools/internal/browserext/chromium.go (1)

93-112: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Lexical version-dir comparison can pick a stale extension version.

e.Name() > best compares version directory names as plain strings. For multi-digit version components (e.g. "9.0.0.0" vs "10.0.0.0"), lexical ordering picks "9.0.0.0" as "greater," so during an update window where both directories briefly coexist, the collector could report metadata/hash for the older, not the latest, version.

♻️ Proposed fix: compare version segments numerically
-	best := ""
-	for _, e := range entries {
-		if e.IsDir() && e.Name() > best {
-			best = e.Name()
-		}
-	}
+	best := ""
+	for _, e := range entries {
+		if e.IsDir() && (best == "" || compareVersions(e.Name(), best) > 0) {
+			best = e.Name()
+		}
+	}

Where compareVersions splits on . and _ and compares each numeric segment, falling back to string comparison on parse failure.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@orbit/pkg/table/ai_tools/internal/browserext/chromium.go` around lines 93 -
112, Update latestVersionManifest to select the greatest version directory using
compareVersions rather than the lexical e.Name() > best check. Preserve the
existing directory filtering, empty-result handling, manifest existence
validation, and return values.
orbit/pkg/table/ai_tools/internal/mcp/risk.go (1)

14-20: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

fetchRunners["dlx"] likely never matches; pnpx is missing.

pnpm dlx <pkg> is invoked as base command pnpm with dlx as the first argument, not as a standalone dlx executable, so the "dlx" key in this map is effectively dead for its intended purpose. Meanwhile pnpx (the pnpm-dlx alias, a real standalone command) isn't in the map at all, so MCP servers launched via pnpm dlx ... or pnpx ... won't be flagged remote_fetch_exec/unpinned_dependency.

🔒 Proposed fix
 var fetchRunners = map[string]struct{}{
 	"npx": {}, "npx.cmd": {},
 	"bunx": {}, "bunx.cmd": {},
 	"pnpx": {}, "pnpx.cmd": {},
 	"uvx": {}, "uvx.cmd": {},
-	"dlx": {},
 }

And in fetchSpec, special-case pnpm/pnpm.cmd with args[0] == "dlx" as an additional fetch-runner match.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@orbit/pkg/table/ai_tools/internal/mcp/risk.go` around lines 14 - 20, Update
fetchRunners to include the standalone pnpx and pnpx.cmd commands, and remove
the ineffective dlx entry. In fetchSpec, additionally classify pnpm and pnpm.cmd
as fetch runners when args[0] is dlx, while preserving existing runner matching
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@orbit/pkg/table/ai_tools/internal/apps/apps.go`:
- Around line 42-58: Update the process matching used by markRunning so short
processNames tokens such as those in the Jan and Dia entries cannot match as
arbitrary substrings; require a process-name boundary or otherwise reuse the
padded, collision-resistant matching semantics already represented by match.
Preserve valid detection of the intended applications while preventing unrelated
process names from marking rows as running.

In `@orbit/pkg/table/ai_tools/internal/browserext/gecko.go`:
- Around line 133-173: The geckoProfiles path construction currently permits
relative Path values containing .. to escape root. Update the flush closure in
geckoProfiles to normalize each resolved profile path and retain it only when it
remains contained within the expected Gecko root, while continuing to allow
valid absolute profile paths and relative paths inside root; exclude escaped
profiles before collectGeckoProfile scans them.

In `@orbit/pkg/table/ai_tools/internal/fsutil/fsutil.go`:
- Around line 30-46: The OpenRegular function remains vulnerable to symlink
replacement between Lstat and OpenFile. Add O_NOFOLLOW to the open flags, using
the project’s platform-specific handling where required, while preserving the
existing read-only, nonblocking, regular-file checks.

In `@orbit/pkg/table/ai_tools/internal/ide/jetbrains.go`:
- Around line 135-156: The readPluginXMLFromJar function must validate the jar
path with fsutil.OpenRegular before opening it as a ZIP. Reuse the same
regular-file guard and error handling used by ReadFileBounded, then pass the
validated file handle or path into the archive-opening flow so symlinks and
special files cannot be followed or block the scan.

In `@orbit/pkg/table/ai_tools/internal/instructions/instructions.go`:
- Line 38: Update the RiskFlags field comment to replace world_readable with
world_writable, matching the flag emitted by the implementation and verified by
TestWorldWritableFlag. Keep the other documented flags unchanged.
- Around line 125-133: Update build so the Size lookup uses lstat semantics
rather than os.Stat, preventing symlinks from exposing target metadata while
preserving the existing behavior for regular files. Keep the SHA256 and other
Instruction fields unchanged, and use the existing filesystem utility or
equivalent non-following metadata call.

In `@orbit/pkg/table/ai_tools/tables.go`:
- Around line 210-238: Update mcpRow so the enabled field uses a descriptive
label for the meaningful disabled state instead of passing itoa(s.Enabled)
directly to compactJSON, which drops "0"; follow the existing
fromWebstoreStr/signedStateStr handling for analogous zero-valued states while
preserving the enabled representation for other states.

---

Nitpick comments:
In `@orbit/pkg/table/ai_tools/internal/browserext/chromium.go`:
- Around line 93-112: Update latestVersionManifest to select the greatest
version directory using compareVersions rather than the lexical e.Name() > best
check. Preserve the existing directory filtering, empty-result handling,
manifest existence validation, and return values.

In `@orbit/pkg/table/ai_tools/internal/classify/classify.go`:
- Around line 41-45: Update the regex compilation loop in init to panic when
regexp.Compile fails, matching the existing json.Unmarshal failure behavior for
embedded data. Do not silently skip invalid patterns; preserve appending
successfully compiled expressions to nameRes.

In `@orbit/pkg/table/ai_tools/internal/instructions/instructions.go`:
- Around line 189-191: Update the file-reading logic in the surrounding
instruction-loading function to use io.ReadAllLimit (or an equivalent io-based
loop) instead of relying on the single f.Read call. Preserve the maxReadBytes
limit, ensure short reads continue until EOF or the limit is reached, and retain
the existing string conversion and downstream detection behavior.

In `@orbit/pkg/table/ai_tools/internal/mcp/risk.go`:
- Around line 14-20: Update fetchRunners to include the standalone pnpx and
pnpx.cmd commands, and remove the ineffective dlx entry. In fetchSpec,
additionally classify pnpm and pnpm.cmd as fetch runners when args[0] is dlx,
while preserving existing runner matching behavior.

In `@orbit/pkg/table/ai_tools/internal/proc/proc.go`:
- Around line 45-76: Update Take to enforce an explicit context timeout covering
both process and connection enumeration, using a bounded budget appropriate for
the orbit daemon. Derive the timeout context at the start of Take, pass it to
all gopsutil enumeration and per-process calls, and ensure cancellation causes
enumeration to stop and the snapshot to return rather than waiting indefinitely
on a single syscall.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0f94b3a5-5e86-4ff3-8aeb-b014aa753b38

📥 Commits

Reviewing files that changed from the base of the PR and between 7ba5d4c and f7877b7.

📒 Files selected for processing (45)
  • go.mod
  • orbit/changes/47619-ai-tools-table
  • orbit/pkg/table/ai_tools/ai_tools.go
  • orbit/pkg/table/ai_tools/browserext_row_test.go
  • orbit/pkg/table/ai_tools/internal/agents/agents.go
  • orbit/pkg/table/ai_tools/internal/agents/agents_test.go
  • orbit/pkg/table/ai_tools/internal/agents/posture.go
  • orbit/pkg/table/ai_tools/internal/agents/posture_test.go
  • orbit/pkg/table/ai_tools/internal/apps/apps.go
  • orbit/pkg/table/ai_tools/internal/apps/apps_darwin.go
  • orbit/pkg/table/ai_tools/internal/apps/apps_linux.go
  • orbit/pkg/table/ai_tools/internal/apps/apps_stub.go
  • orbit/pkg/table/ai_tools/internal/apps/apps_test.go
  • orbit/pkg/table/ai_tools/internal/apps/apps_windows.go
  • orbit/pkg/table/ai_tools/internal/browserext/browserext.go
  • orbit/pkg/table/ai_tools/internal/browserext/browserext_test.go
  • orbit/pkg/table/ai_tools/internal/browserext/chromium.go
  • orbit/pkg/table/ai_tools/internal/browserext/gecko.go
  • orbit/pkg/table/ai_tools/internal/browserext/risk.go
  • orbit/pkg/table/ai_tools/internal/classify/classify.go
  • orbit/pkg/table/ai_tools/internal/classify/classify_test.go
  • orbit/pkg/table/ai_tools/internal/classify/kb.json
  • orbit/pkg/table/ai_tools/internal/fsutil/fsutil.go
  • orbit/pkg/table/ai_tools/internal/fsutil/fsutil_test.go
  • orbit/pkg/table/ai_tools/internal/fsutil/fsutil_unix_test.go
  • orbit/pkg/table/ai_tools/internal/homes/homes.go
  • orbit/pkg/table/ai_tools/internal/ide/ide.go
  • orbit/pkg/table/ai_tools/internal/ide/ide_test.go
  • orbit/pkg/table/ai_tools/internal/ide/jetbrains.go
  • orbit/pkg/table/ai_tools/internal/ide/others.go
  • orbit/pkg/table/ai_tools/internal/ide/vscode.go
  • orbit/pkg/table/ai_tools/internal/instructions/instructions.go
  • orbit/pkg/table/ai_tools/internal/instructions/instructions_test.go
  • orbit/pkg/table/ai_tools/internal/mcp/correlate.go
  • orbit/pkg/table/ai_tools/internal/mcp/mcp.go
  • orbit/pkg/table/ai_tools/internal/mcp/mcp_test.go
  • orbit/pkg/table/ai_tools/internal/mcp/risk.go
  • orbit/pkg/table/ai_tools/internal/mcp/risk_test.go
  • orbit/pkg/table/ai_tools/internal/netsock/netsock.go
  • orbit/pkg/table/ai_tools/internal/netsock/netsock_test.go
  • orbit/pkg/table/ai_tools/internal/paths/paths.go
  • orbit/pkg/table/ai_tools/internal/proc/proc.go
  • orbit/pkg/table/ai_tools/smoke_test.go
  • orbit/pkg/table/ai_tools/tables.go
  • orbit/pkg/table/extension.go

Comment thread orbit/pkg/table/ai_tools/internal/apps/apps.go
Comment thread orbit/pkg/table/ai_tools/internal/browserext/gecko.go Outdated
Comment thread orbit/pkg/table/ai_tools/internal/fsutil/fsutil.go
Comment thread orbit/pkg/table/ai_tools/internal/ide/jetbrains.go
Comment thread orbit/pkg/table/ai_tools/internal/instructions/instructions.go Outdated
Comment thread orbit/pkg/table/ai_tools/internal/instructions/instructions.go
Comment thread orbit/pkg/table/ai_tools/tables.go
Follow-up to the collector hardening, from an independent adversarial review:

- fsutil.OpenRegular: close the stat->open TOCTOU race. The pre-open Lstat
  alone did not stop a symlink swapped in after the check (the open would
  still follow it). Add a post-open fstat that re-checks IsRegular and, via
  os.SameFile, confirms the opened file is the same inode Lstat saw; refuse
  otherwise. Document the ReadFileBounded truncation contract.

- netsock: remove the now-dead remote-MCP host plumbing. With DNS resolution
  gone, the host set was keyed by hostname but looked up by IP, so it could
  never match. Drop buildHostSet, the hostInfo type, Collect's ctx and
  remoteMCPHosts params, and the dead attribution branch; fix the stale doc
  comment. tables.go no longer builds a remote-host map and only scans MCP
  configs for the mcp_server type.

- Add regression tests: Chromium out-of-home Preferences path is recovered
  but not hashed, and a macOS CFBundleExecutable with path traversal yields
  no execPath.

- Note the symlink-not-followed detection limitation in the changes file.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
orbit/pkg/table/ai_tools/tables.go (1)

143-146: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Honor query cancellation before scanning applications.

Unlike the other collectors, this branch invokes apps.Scan without checking ctx.Err(). A canceled query can therefore still start the potentially expensive all-home application scan.

Suggested fix
 	if has("apps") {
+		if err := ctx.Err(); err != nil {
+			return nil, err
+		}
 		for _, a := range apps.Scan(hs, snap) {

If the scan itself is long-running, propagate the context into apps.Scan as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@orbit/pkg/table/ai_tools/tables.go` around lines 143 - 146, In the
applications collector branch guarded by has("apps"), check ctx.Err() before
starting apps.Scan and return the cancellation error instead of scanning when
the query is already canceled. If supported by the API, also pass the context
into apps.Scan so cancellation can stop an in-progress scan.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@orbit/pkg/table/ai_tools/internal/netsock/netsock.go`:
- Around line 61-65: Update the connection-state condition in the socket
classification switch to classify only connections whose status is explicitly
ESTABLISHED, or whose status is explicitly empty/unknown; remove the
RemotePort/RemoteIP fallback. Preserve the existing classifyEstablished(&sock)
behavior for accepted connections and continue ignoring transient states such as
TIME_WAIT and CLOSE_WAIT.

---

Outside diff comments:
In `@orbit/pkg/table/ai_tools/tables.go`:
- Around line 143-146: In the applications collector branch guarded by
has("apps"), check ctx.Err() before starting apps.Scan and return the
cancellation error instead of scanning when the query is already canceled. If
supported by the API, also pass the context into apps.Scan so cancellation can
stop an in-progress scan.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e0463137-dc06-4914-a7b2-0df566acd389

📥 Commits

Reviewing files that changed from the base of the PR and between f7877b7 and d352f63.

📒 Files selected for processing (7)
  • orbit/changes/47619-ai-tools-table
  • orbit/pkg/table/ai_tools/internal/apps/apps_darwin_test.go
  • orbit/pkg/table/ai_tools/internal/browserext/browserext_test.go
  • orbit/pkg/table/ai_tools/internal/fsutil/fsutil.go
  • orbit/pkg/table/ai_tools/internal/netsock/netsock.go
  • orbit/pkg/table/ai_tools/internal/netsock/netsock_test.go
  • orbit/pkg/table/ai_tools/tables.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • orbit/changes/47619-ai-tools-table
  • orbit/pkg/table/ai_tools/internal/netsock/netsock_test.go
  • orbit/pkg/table/ai_tools/internal/browserext/browserext_test.go
  • orbit/pkg/table/ai_tools/internal/fsutil/fsutil.go

Comment thread orbit/pkg/table/ai_tools/internal/netsock/netsock.go Outdated
@juan-fdz-hawa juan-fdz-hawa self-assigned this Jul 14, 2026
- homes.All now derives each home's uid/username from the directory's actual
  owner (stat) instead of its name, so a low-privilege user who creates a
  directory named after another account (e.g. /Users/root) can no longer forge
  the uid/username attribution columns. Falls back to name-based lookup where
  the OS does not expose a numeric owner uid (Windows). Adds a regression test.

- Added a README recording the vendoring provenance (upstream repo, tag,
  commit, the modifications made) and flagging that the upstream repository had
  no LICENSE at the vendored commit, which must be resolved before shipping.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@orbit/pkg/table/ai_tools/internal/homes/owner_unix_test.go`:
- Around line 33-38: Update the username assertion in the owner test around
owner so it compares username directly with cur.Username, replacing the
special-case root check. Preserve the existing UID assertion and ensure the test
validates the exact resolved UID-to-username attribution.

In `@orbit/pkg/table/ai_tools/internal/homes/owner_unix.go`:
- Line 1: Narrow the build constraint on owner_unix.go from !windows to explicit
GOOS tags for each supported Unix platform where syscall.Stat_t is available, or
provide a separate fallback implementation for unsupported targets. Keep the
existing Unix implementation unchanged for supported platforms.

In `@orbit/pkg/table/ai_tools/internal/homes/owner_windows.go`:
- Around line 7-10: Update the Windows statOwnerUID fallback path so owner()
does not use filepath.Base(dir) for attribution when ownership lookup fails.
Return and propagate an explicit unknown or unowned result on Windows,
preserving name-based attribution only for trusted directories if that existing
mechanism is available.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dfab3353-548b-4e27-b722-a4ecb637b0e4

📥 Commits

Reviewing files that changed from the base of the PR and between d352f63 and 5bb82c1.

⛔ Files ignored due to path filters (1)
  • orbit/pkg/table/ai_tools/README.md is excluded by !**/*.md
📒 Files selected for processing (4)
  • orbit/pkg/table/ai_tools/internal/homes/homes.go
  • orbit/pkg/table/ai_tools/internal/homes/owner_unix.go
  • orbit/pkg/table/ai_tools/internal/homes/owner_unix_test.go
  • orbit/pkg/table/ai_tools/internal/homes/owner_windows.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • orbit/pkg/table/ai_tools/internal/homes/homes.go

Comment on lines +33 to +38
uid, username := owner(dir, fi)
if uid != cur.Uid {
t.Errorf("uid = %q, want the real owner %q (must be read from stat, not the name)", uid, cur.Uid)
}
if username == "root" {
t.Errorf("username = %q, forged from the directory name; want the real owner %q", username, cur.Username)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the resolved username exactly.

Checking only username != "root" would let an incorrect mapping such as "nobody" pass. Assert username == cur.Username to verify the complete UID-to-username attribution.

Proposed test fix
-	if username == "root" {
-		t.Errorf("username = %q, forged from the directory name; want the real owner %q", username, cur.Username)
+	if username != cur.Username {
+		t.Errorf("username = %q, want the real owner %q", username, cur.Username)
	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
uid, username := owner(dir, fi)
if uid != cur.Uid {
t.Errorf("uid = %q, want the real owner %q (must be read from stat, not the name)", uid, cur.Uid)
}
if username == "root" {
t.Errorf("username = %q, forged from the directory name; want the real owner %q", username, cur.Username)
uid, username := owner(dir, fi)
if uid != cur.Uid {
t.Errorf("uid = %q, want the real owner %q (must be read from stat, not the name)", uid, cur.Uid)
}
if username != cur.Username {
t.Errorf("username = %q, want the real owner %q", username, cur.Username)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@orbit/pkg/table/ai_tools/internal/homes/owner_unix_test.go` around lines 33 -
38, Update the username assertion in the owner test around owner so it compares
username directly with cur.Username, replacing the special-case root check.
Preserve the existing UID assertion and ensure the test validates the exact
resolved UID-to-username attribution.

@@ -0,0 +1,20 @@
//go:build !windows

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

for target in "linux amd64" "darwin amd64" "windows amd64" "plan9 amd64"; do
  read -r goos goarch <<< "$target"
  GOOS="$goos" GOARCH="$goarch" go test -c \
    -o "/tmp/homes-${goos}-${goarch}.test" \
    ./orbit/pkg/table/ai_tools/internal/homes
done

Repository: fleetdm/fleet

Length of output: 472


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== files ==\n'
git ls-files orbit/pkg/table/ai_tools/internal/homes

printf '\n== owner_unix.go ==\n'
cat -n orbit/pkg/table/ai_tools/internal/homes/owner_unix.go

printf '\n== other homes files with build tags ==\n'
for f in $(git ls-files orbit/pkg/table/ai_tools/internal/homes); do
  echo "--- $f"
  sed -n '1,20p' "$f" | cat -n
done

Repository: fleetdm/fleet

Length of output: 3571


Narrow the build tag to supported Unix targets.
!windows includes every non-Windows GOOS, but syscall.Stat_t isn’t available on all of them, so this package can fail to build on unsupported platforms. Use explicit GOOS tags for the platforms you support, or add a fallback implementation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@orbit/pkg/table/ai_tools/internal/homes/owner_unix.go` at line 1, Narrow the
build constraint on owner_unix.go from !windows to explicit GOOS tags for each
supported Unix platform where syscall.Stat_t is available, or provide a separate
fallback implementation for unsupported targets. Keep the existing Unix
implementation unchanged for supported platforms.

Comment on lines +7 to +10
// statOwnerUID has no portable implementation on Windows (ownership is
// expressed via security descriptors / SIDs, not a numeric uid), so callers
// fall back to name-based attribution there.
func statOwnerUID(_ os.FileInfo) (string, bool) { return "", false }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the target file and all references to statOwnerUID
git ls-files 'orbit/pkg/table/ai_tools/internal/homes/*' 'orbit/pkg/table/ai_tools/**' | sed -n '1,200p'
printf '\n--- references ---\n'
rg -n "statOwnerUID|owner attribution|name-based|security descriptor|SID|owner" orbit/pkg/table/ai_tools -S

printf '\n--- target file ---\n'
cat -n orbit/pkg/table/ai_tools/internal/homes/owner_windows.go

Repository: fleetdm/fleet

Length of output: 6119


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- homes.go ---'
sed -n '60,100p' orbit/pkg/table/ai_tools/internal/homes/homes.go

printf '\n%s\n' '--- README.md excerpt ---'
sed -n '20,45p' orbit/pkg/table/ai_tools/README.md

printf '\n%s\n' '--- owner_unix_test.go excerpt ---'
sed -n '1,60p' orbit/pkg/table/ai_tools/internal/homes/owner_unix_test.go

Repository: fleetdm/fleet

Length of output: 3723


Don't fall back to directory-name attribution on Windows. owner() uses filepath.Base(dir) when statOwnerUID fails, so a folder named after another account can still be attributed to that account. Return an explicit unknown/unowned state instead, or limit this path to trusted directories.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@orbit/pkg/table/ai_tools/internal/homes/owner_windows.go` around lines 7 -
10, Update the Windows statOwnerUID fallback path so owner() does not use
filepath.Base(dir) for attribution when ownership lookup fails. Return and
propagate an explicit unknown or unowned result on Windows, preserving
name-based attribution only for trusted directories if that existing mechanism
is available.

From the automated PR reviewers (Copilot, CodeRabbit); valid items only:

Security:
- fsutil.OpenRegular: add O_NOFOLLOW (unix, build-tagged; no-op on Windows)
  so the open itself refuses a final-component symlink, on top of the existing
  Lstat pre-check and post-open SameFile identity re-check.
- jetbrains/gecko: open plugin .jar / .xpi archives through fsutil.OpenRegular
  + zip.NewReader instead of zip.OpenReader, so a symlinked/special file in a
  user-writable plugin dir can't be followed or block the root scan.
- gecko: contain profiles.ini profile paths to the user's home (relative ".."
  and absolute escapes are refused).
- instructions: read file Size via Lstat (not os.Stat), so a symlinked probe
  path can't leak a root-only file's size.

Correctness:
- apps.markRunning: match process names on word boundaries; short tokens like
  "dia"/"jan" no longer collide with substrings (e.g. "mediaanalysisd").
- tables: render MCP `enabled` as a label so an explicitly-disabled server (0)
  survives compactJSON instead of looking unset.
- netsock: only classify ESTABLISHED (or status-less) connections; named
  transient states (TIME_WAIT/CLOSE_WAIT) are no longer treated as established.

Robustness / cleanup:
- instructions.scanContent: bounded io.ReadAll instead of a single Read that
  could short-read and truncate the scanned window.
- tables.generate: short-circuit when the type constraint selects nothing.
- tables.generate: honor ctx cancellation before the apps scan.
- Fix package doc comment (ai_tools), the world_readable->world_writable risk
  comment, a stale smoke-test command path, and dead `_ = proj`.

Adds regression tests for word-boundary process matching and profiles.ini
containment.

Deliberately not changed: the discriminator column stays `type` (confirmed in
issue #47619; docs should reconcile to it, not the code); classify keeps
skipping invalid embedded regexes rather than panicking in a root daemon's
init; proc enumeration keeps its per-iteration cancellation check.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New fleetd table: ai_tools

3 participants