diff --git a/pkg/osquery/table/platform_tables_darwin.go b/pkg/osquery/table/platform_tables_darwin.go index b3292bd105..45342ad464 100644 --- a/pkg/osquery/table/platform_tables_darwin.go +++ b/pkg/osquery/table/platform_tables_darwin.go @@ -12,6 +12,7 @@ import ( "github.com/kolide/launcher/pkg/osquery/tables/apple_silicon_security_policy" "github.com/kolide/launcher/pkg/osquery/tables/dataflattentable" "github.com/kolide/launcher/pkg/osquery/tables/execparsers/remotectl" + "github.com/kolide/launcher/pkg/osquery/tables/execparsers/softwareupdate" "github.com/kolide/launcher/pkg/osquery/tables/filevault" "github.com/kolide/launcher/pkg/osquery/tables/firmwarepasswd" "github.com/kolide/launcher/pkg/osquery/tables/ioreg" @@ -116,5 +117,6 @@ func platformTables(client *osquery.ExtensionManagerClient, logger log.Logger, c munki.ManagedInstalls(client, logger), munki.MunkiReport(client, logger), dataflattentable.NewExecAndParseTable(logger, "kolide_remotectl", remotectl.Parser, []string{`/usr/libexec/remotectl`, `dumpstate`}), + dataflattentable.NewExecAndParseTable(logger, "kolide_softwareupdate", softwareupdate.Parser, []string{`/usr/sbin/softwareupdate`, `--list`, `--no-scan`}), } } diff --git a/pkg/osquery/tables/execparsers/softwareupdate/parse.go b/pkg/osquery/tables/execparsers/softwareupdate/parse.go new file mode 100644 index 0000000000..44050f3626 --- /dev/null +++ b/pkg/osquery/tables/execparsers/softwareupdate/parse.go @@ -0,0 +1,62 @@ +//go:build darwin +// +build darwin + +package softwareupdate + +import ( + "bufio" + "fmt" + "io" + "strings" +) + +func (p *parser) parseSoftwareupdate(reader io.Reader) (any, error) { + results := make([]map[string]string, 0) + + p.scanner = bufio.NewScanner(reader) + for p.scanner.Scan() { + currentLine := strings.TrimSpace(p.scanner.Text()) + + // There are some header lines (e.g. `Software Update Tool`) that we can safely discard. + // We only care about pairs of lines, where the first line begins in the following way. + if !strings.HasPrefix(currentLine, "* Label:") { + continue + } + + // Software updates are listed in the following format: + // * Label: + // Title: <title>, Version: <version>, Size: <size>, Recommended: YES|?, Action: <action>, + label := strings.TrimSpace(strings.TrimPrefix(currentLine, "* Label:")) + labelAttributes, err := p.parseUpdate(label) + if err != nil { + return results, fmt.Errorf("could not parse software update data for label %s: %w", label, err) + } + + results = append(results, labelAttributes) + } + + return results, nil +} + +func (p *parser) parseUpdate(label string) (map[string]string, error) { + result := make(map[string]string) + result["Label"] = label + + // Get the next line + if !p.scanner.Scan() { + return result, fmt.Errorf("software update data missing for label %s", label) + } + updateDataStr := strings.TrimSuffix(strings.TrimSpace(p.scanner.Text()), ",") + + // Add each update attribute to the result + updateData := strings.Split(updateDataStr, ",") + for _, attr := range updateData { + keyValPair := strings.SplitN(attr, ":", 2) + if len(keyValPair) < 2 { + return result, fmt.Errorf("software update data has malformed attribute %s", attr) + } + result[strings.TrimSpace(keyValPair[0])] = strings.TrimSpace(keyValPair[1]) + } + + return result, nil +} diff --git a/pkg/osquery/tables/execparsers/softwareupdate/parse_test.go b/pkg/osquery/tables/execparsers/softwareupdate/parse_test.go new file mode 100644 index 0000000000..0e64df46c9 --- /dev/null +++ b/pkg/osquery/tables/execparsers/softwareupdate/parse_test.go @@ -0,0 +1,149 @@ +package softwareupdate + +import ( + "bytes" + _ "embed" + "testing" + + "github.com/stretchr/testify/require" +) + +//go:embed test-data/beta-update-available-noscan.txt +var beta_update_available_noscan []byte + +//go:embed test-data/beta-update-available-scan.txt +var beta_update_available_scan []byte + +//go:embed test-data/error-scan.txt +var error_scan []byte + +//go:embed test-data/multiple-updates-available-noscan.txt +var multiple_updates_available_noscan []byte + +//go:embed test-data/no-update-available-noscan.txt +var no_update_available_noscan []byte + +//go:embed test-data/no-update-available-scan.txt +var no_update_available_scan []byte + +//go:embed test-data/update-available-noscan.txt +var update_available_noscan []byte + +//go:embed test-data/update-available-scan.txt +var update_available_scan []byte + +func TestParse(t *testing.T) { + t.Parallel() + + var tests = []struct { + name string + input []byte + expected []map[string]string + }{ + { + name: "beta update available, --no-scan", + input: beta_update_available_noscan, + expected: []map[string]string{ + { + "Label": "macOS Ventura 13.3 Beta 3-22E5236f", + "Title": "macOS Ventura 13.3 Beta 3", + "Version": "13.3", + "Size": "3310848K", + "Recommended": "YES", + "Action": "restart", + }, + }, + }, + { + name: "beta update available", + input: beta_update_available_scan, + expected: []map[string]string{ + { + "Label": "macOS Ventura 13.3 Beta 3-22E5236f", + "Title": "macOS Ventura 13.3 Beta 3", + "Version": "13.3", + "Size": "3310848K", + "Recommended": "YES", + "Action": "restart", + }, + }, + }, + { + name: "error when scanning", + input: error_scan, + expected: make([]map[string]string, 0), + }, + { + name: "multiple updates available, --no-scan", + input: multiple_updates_available_noscan, + expected: []map[string]string{ + { + "Label": "Command Line Tools for Xcode-14.3", + "Title": "Command Line Tools for Xcode", + "Version": "14.3", + "Size": "711888KiB", + "Recommended": "YES", + }, + { + "Label": "macOS Ventura 13.4 Beta-22F5027f", + "Title": "macOS Ventura 13.4 Beta", + "Version": "13.4", + "Size": "11487824K", + "Recommended": "YES", + "Action": "restart", + }, + }, + }, + { + name: "no update available, --no-scan", + input: no_update_available_noscan, + expected: make([]map[string]string, 0), + }, + { + name: "no update available", + input: no_update_available_scan, + expected: make([]map[string]string, 0), + }, + { + name: "update available, --no-scan", + input: update_available_noscan, + expected: []map[string]string{ + { + "Label": "macOS Ventura 13.3.1-22E261", + "Title": "macOS Ventura 13.3.1", + "Version": "13.3.1", + "Size": "868648KiB", + "Recommended": "YES", + "Action": "restart", + }, + }, + }, + { + name: "update available", + input: update_available_scan, + expected: []map[string]string{ + { + "Label": "macOS Ventura 13.3.1-22E261", + "Title": "macOS Ventura 13.3.1", + "Version": "13.3.1", + "Size": "868648KiB", + "Recommended": "YES", + "Action": "restart", + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + }) + + p := New() + result, err := p.Parse(bytes.NewReader(tt.input)) + require.NoError(t, err, "unexpected error parsing input") + + require.ElementsMatch(t, tt.expected, result) + } +} diff --git a/pkg/osquery/tables/execparsers/softwareupdate/softwareupdate.go b/pkg/osquery/tables/execparsers/softwareupdate/softwareupdate.go new file mode 100644 index 0000000000..0e62460682 --- /dev/null +++ b/pkg/osquery/tables/execparsers/softwareupdate/softwareupdate.go @@ -0,0 +1,23 @@ +//go:build darwin +// +build darwin + +package softwareupdate + +import ( + "bufio" + "io" +) + +type parser struct { + scanner *bufio.Scanner +} + +var Parser = New() + +func New() *parser { + return &parser{} +} + +func (p *parser) Parse(reader io.Reader) (any, error) { + return p.parseSoftwareupdate(reader) +} diff --git a/pkg/osquery/tables/execparsers/softwareupdate/test-data/beta-update-available-noscan.txt b/pkg/osquery/tables/execparsers/softwareupdate/test-data/beta-update-available-noscan.txt new file mode 100644 index 0000000000..c089d1a1d6 --- /dev/null +++ b/pkg/osquery/tables/execparsers/softwareupdate/test-data/beta-update-available-noscan.txt @@ -0,0 +1,5 @@ +Software Update Tool + +Software Update found the following new or updated software: +* Label: macOS Ventura 13.3 Beta 3-22E5236f + Title: macOS Ventura 13.3 Beta 3, Version: 13.3, Size: 3310848K, Recommended: YES, Action: restart, diff --git a/pkg/osquery/tables/execparsers/softwareupdate/test-data/beta-update-available-scan.txt b/pkg/osquery/tables/execparsers/softwareupdate/test-data/beta-update-available-scan.txt new file mode 100644 index 0000000000..3482753194 --- /dev/null +++ b/pkg/osquery/tables/execparsers/softwareupdate/test-data/beta-update-available-scan.txt @@ -0,0 +1,7 @@ + +Software Update Tool + +Finding available software +Software Update found the following new or updated software: +* Label: macOS Ventura 13.3 Beta 3-22E5236f + Title: macOS Ventura 13.3 Beta 3, Version: 13.3, Size: 3310848K, Recommended: YES, Action: restart, diff --git a/pkg/osquery/tables/execparsers/softwareupdate/test-data/error-scan.txt b/pkg/osquery/tables/execparsers/softwareupdate/test-data/error-scan.txt new file mode 100644 index 0000000000..20713e2dba --- /dev/null +++ b/pkg/osquery/tables/execparsers/softwareupdate/test-data/error-scan.txt @@ -0,0 +1,3 @@ +Software Update Tool + +Scan finished with error: Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo={_kCFStreamErrorDomainKey=1, NSLocalizedRecoverySuggestion=Make sure you're connected to the Internet, and then try again., NSLocalizedDescription=The network connection was lost., SUErrorRelatedCode=SUErrorCodeScanCatalogNotFound, NSErrorFailingURLStringKey=https://swscan.apple.com/content/catalogs/others/index-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz, NSErrorFailingURLKey=https://swscan.apple.com/content/catalogs/others/index-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz, _kCFStreamErrorCodeKey=57} diff --git a/pkg/osquery/tables/execparsers/softwareupdate/test-data/multiple-updates-available-noscan.txt b/pkg/osquery/tables/execparsers/softwareupdate/test-data/multiple-updates-available-noscan.txt new file mode 100644 index 0000000000..4da9bfd889 --- /dev/null +++ b/pkg/osquery/tables/execparsers/softwareupdate/test-data/multiple-updates-available-noscan.txt @@ -0,0 +1,7 @@ +Software Update Tool + +Software Update found the following new or updated software: +* Label: Command Line Tools for Xcode-14.3 + Title: Command Line Tools for Xcode, Version: 14.3, Size: 711888KiB, Recommended: YES, +* Label: macOS Ventura 13.4 Beta-22F5027f + Title: macOS Ventura 13.4 Beta, Version: 13.4, Size: 11487824K, Recommended: YES, Action: restart, diff --git a/pkg/osquery/tables/execparsers/softwareupdate/test-data/no-update-available-noscan.txt b/pkg/osquery/tables/execparsers/softwareupdate/test-data/no-update-available-noscan.txt new file mode 100644 index 0000000000..608abdc1a5 --- /dev/null +++ b/pkg/osquery/tables/execparsers/softwareupdate/test-data/no-update-available-noscan.txt @@ -0,0 +1,3 @@ +Software Update Tool + +No new software available. diff --git a/pkg/osquery/tables/execparsers/softwareupdate/test-data/no-update-available-scan.txt b/pkg/osquery/tables/execparsers/softwareupdate/test-data/no-update-available-scan.txt new file mode 100644 index 0000000000..4cb14cac70 --- /dev/null +++ b/pkg/osquery/tables/execparsers/softwareupdate/test-data/no-update-available-scan.txt @@ -0,0 +1,4 @@ +Software Update Tool + +Finding available software +No new software available. diff --git a/pkg/osquery/tables/execparsers/softwareupdate/test-data/update-available-noscan.txt b/pkg/osquery/tables/execparsers/softwareupdate/test-data/update-available-noscan.txt new file mode 100644 index 0000000000..1c8a77821a --- /dev/null +++ b/pkg/osquery/tables/execparsers/softwareupdate/test-data/update-available-noscan.txt @@ -0,0 +1,5 @@ +Software Update Tool + +Software Update found the following new or updated software: +* Label: macOS Ventura 13.3.1-22E261 + Title: macOS Ventura 13.3.1, Version: 13.3.1, Size: 868648KiB, Recommended: YES, Action: restart, diff --git a/pkg/osquery/tables/execparsers/softwareupdate/test-data/update-available-scan.txt b/pkg/osquery/tables/execparsers/softwareupdate/test-data/update-available-scan.txt new file mode 100644 index 0000000000..d34d526a55 --- /dev/null +++ b/pkg/osquery/tables/execparsers/softwareupdate/test-data/update-available-scan.txt @@ -0,0 +1,6 @@ +Software Update Tool + +Finding available software +Software Update found the following new or updated software: +* Label: macOS Ventura 13.3.1-22E261 + Title: macOS Ventura 13.3.1, Version: 13.3.1, Size: 868648KiB, Recommended: YES, Action: restart,