Skip to content

Commit

Permalink
Add kolide_brew_upgradeable table (#1634)
Browse files Browse the repository at this point in the history
  • Loading branch information
Micah-Kolide authored Mar 1, 2024
1 parent 0c783a3 commit 136c75a
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 0 deletions.
4 changes: 4 additions & 0 deletions ee/allowedcmd/cmd_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ func Bputil(ctx context.Context, arg ...string) (*exec.Cmd, error) {
return validatedCommand(ctx, "/usr/bin/bputil", arg...)
}

func Brew(ctx context.Context, arg ...string) (*exec.Cmd, error) {
return validatedCommand(ctx, "/opt/homebrew/bin/brew", arg...)
}

func Diskutil(ctx context.Context, arg ...string) (*exec.Cmd, error) {
return validatedCommand(ctx, "/usr/sbin/diskutil", arg...)
}
Expand Down
4 changes: 4 additions & 0 deletions ee/allowedcmd/cmd_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ func Apt(ctx context.Context, arg ...string) (*exec.Cmd, error) {
return validatedCommand(ctx, "/usr/bin/apt", arg...)
}

func Brew(ctx context.Context, arg ...string) (*exec.Cmd, error) {
return validatedCommand(ctx, "/home/linuxbrew/.linuxbrew/bin/brew", arg...)
}

func Cryptsetup(ctx context.Context, arg ...string) (*exec.Cmd, error) {
for _, p := range []string{"/usr/sbin/cryptsetup", "/sbin/cryptsetup"} {
validatedCmd, err := validatedCommand(ctx, p, arg...)
Expand Down
146 changes: 146 additions & 0 deletions ee/tables/homebrew/upgradeable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
//go:build !windows
// +build !windows

package brew_upgradeable

import (
"context"
"fmt"
"io"
"log/slog"
"os/exec"
"os/user"
"strconv"
"strings"
"syscall"

"github.com/kolide/launcher/ee/allowedcmd"
"github.com/kolide/launcher/ee/dataflatten"
"github.com/kolide/launcher/ee/tables/dataflattentable"
"github.com/kolide/launcher/ee/tables/tablehelpers"
"github.com/osquery/osquery-go/plugin/table"
)

const allowedCharacters = "0123456789"

type Table struct {
slogger *slog.Logger
execCC allowedcmd.AllowedCommand
}

func TablePlugin(slogger *slog.Logger) *table.Plugin {
columns := dataflattentable.Columns(
table.TextColumn("uid"),
)

t := &Table{
slogger: slogger.With("table", "kolide_brew_upgradeable"),
execCC: allowedcmd.Brew,
}

return table.NewPlugin("kolide_brew_upgradeable", columns, t.generate)
}

func (t *Table) generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
var results []map[string]string

uids := tablehelpers.GetConstraints(queryContext, "uid", tablehelpers.WithAllowedCharacters(allowedCharacters))
if len(uids) < 1 {
return results, fmt.Errorf("kolide_brew_upgradeable requires at least one user id to be specified")
}

for _, uid := range uids {
for _, dataQuery := range tablehelpers.GetConstraints(queryContext, "query", tablehelpers.WithDefaults("*")) {
output, err := t.getBrewOutdated(ctx, uid)
if err != nil {
t.slogger.Log(ctx, slog.LevelInfo, "failure querying user brew installed packages", "err", err, "target_uid", uid)
continue
}

flattenOpts := []dataflatten.FlattenOpts{
dataflatten.WithSlogger(t.slogger),
dataflatten.WithQuery(strings.Split(dataQuery, "/")),
}

flattened, err := dataflatten.Json(output, flattenOpts...)
if err != nil {
t.slogger.Log(ctx, slog.LevelInfo, "failure flattening output", "err", err)
continue
}

rowData := map[string]string{
"uid": uid,
}

results = append(results, dataflattentable.ToMap(flattened, dataQuery, rowData)...)
}
}

return results, nil
}

func (t *Table) getBrewOutdated(ctx context.Context, uid string) ([]byte, error) {
cmd, err := t.execCC(ctx, "outdated", "--json")
if err != nil {
return nil, fmt.Errorf("creating brew outdated command: %w", err)
}

stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("assigning StdoutPipe for brew outdated command: %w", err)
}

if err := runAsUser(ctx, uid, cmd); err != nil {
return nil, fmt.Errorf("runAsUser brew outdated command as user %s: %w", uid, err)
}

data, err := io.ReadAll(stdout)
if err != nil {
return nil, fmt.Errorf("ReadAll brew outdated stdout: %w", err)
}

if err := cmd.Wait(); err != nil {
return nil, fmt.Errorf("deallocation of brew outdated command as user %s: %w", uid, err)
}

return data, nil
}

func runAsUser(ctx context.Context, uid string, cmd *exec.Cmd) error {
currentUser, err := user.Current()
if err != nil {
return fmt.Errorf("getting current user: %w", err)
}

runningUser, err := user.LookupId(uid)
if err != nil {
return fmt.Errorf("looking up user with uid %s: %w", uid, err)
}

if currentUser.Uid != "0" {
if currentUser.Uid != runningUser.Uid {
return fmt.Errorf("current user %s is not root and can't start process for other user %s", currentUser.Uid, uid)
}

return cmd.Start()
}

runningUserUid, err := strconv.ParseUint(runningUser.Uid, 10, 32)
if err != nil {
return fmt.Errorf("converting uid %s to int: %w", runningUser.Uid, err)
}

runningUserGid, err := strconv.ParseUint(runningUser.Gid, 10, 32)
if err != nil {
return fmt.Errorf("converting gid %s to int: %w", runningUser.Gid, err)
}

cmd.SysProcAttr = &syscall.SysProcAttr{
Credential: &syscall.Credential{
Uid: uint32(runningUserUid),
Gid: uint32(runningUserGid),
},
}

return cmd.Start()
}
2 changes: 2 additions & 0 deletions pkg/osquery/table/platform_tables_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/kolide/launcher/ee/tables/execparsers/softwareupdate"
"github.com/kolide/launcher/ee/tables/filevault"
"github.com/kolide/launcher/ee/tables/firmwarepasswd"
"github.com/kolide/launcher/ee/tables/homebrew"
"github.com/kolide/launcher/ee/tables/ioreg"
"github.com/kolide/launcher/ee/tables/macos_software_update"
"github.com/kolide/launcher/ee/tables/mdmclient"
Expand Down Expand Up @@ -81,6 +82,7 @@ func platformSpecificTables(slogger *slog.Logger, currentOsquerydBinaryPath stri
keychainAclsTable,
keychainItemsTable,
appicons.AppIcons(),
brew_upgradeable.TablePlugin(slogger),
ChromeLoginKeychainInfo(slogger),
firmwarepasswd.TablePlugin(slogger),
GDriveSyncConfig(slogger),
Expand Down
2 changes: 2 additions & 0 deletions pkg/osquery/table/platform_tables_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/kolide/launcher/ee/tables/execparsers/simple_array"
"github.com/kolide/launcher/ee/tables/fscrypt_info"
"github.com/kolide/launcher/ee/tables/gsettings"
"github.com/kolide/launcher/ee/tables/homebrew"
nix_env_upgradeable "github.com/kolide/launcher/ee/tables/nix_env/upgradeable"
"github.com/kolide/launcher/ee/tables/secureboot"
"github.com/kolide/launcher/ee/tables/xfconf"
Expand All @@ -32,6 +33,7 @@ import (

func platformSpecificTables(slogger *slog.Logger, currentOsquerydBinaryPath string) []osquery.OsqueryPlugin {
return []osquery.OsqueryPlugin{
brew_upgradeable.TablePlugin(slogger),
cryptsetup.TablePlugin(slogger),
gsettings.Settings(slogger),
gsettings.Metadata(slogger),
Expand Down

0 comments on commit 136c75a

Please sign in to comment.