Skip to content

Commit

Permalink
Add homebrew upgradeable kolide table
Browse files Browse the repository at this point in the history
  • Loading branch information
Micah-Kolide committed Mar 1, 2024
1 parent 7b1b2f1 commit b10e044
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,)

Check failure on line 56 in ee/tables/homebrew/upgradeable.go

View workflow job for this annotation

GitHub Actions / lint (macos-latest)

File is not `gofmt`-ed (gofmt)

Check failure on line 56 in ee/tables/homebrew/upgradeable.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-latest)

File is not `gofmt`-ed (gofmt)
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 @@ -17,6 +17,7 @@ import (
pacman_group "github.com/kolide/launcher/ee/tables/execparsers/pacman/group"
pacman_info "github.com/kolide/launcher/ee/tables/execparsers/pacman/info"
pacman_upgradeable "github.com/kolide/launcher/ee/tables/execparsers/pacman/upgradeable"
"github.com/kolide/launcher/ee/tables/homebrew"

Check failure on line 20 in pkg/osquery/table/platform_tables_linux.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-latest)

File is not `gofmt`-ed (gofmt)
"github.com/kolide/launcher/ee/tables/execparsers/repcli"
"github.com/kolide/launcher/ee/tables/execparsers/rpm"
"github.com/kolide/launcher/ee/tables/execparsers/simple_array"
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 b10e044

Please sign in to comment.