Skip to content

Commit

Permalink
Add os_version predicates
Browse files Browse the repository at this point in the history
  • Loading branch information
grahamgilbert committed Apr 27, 2024
1 parent 44c8c59 commit 38bb7be
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 51 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ For production deployment, you should refer to the [osquery documentation](https
| `puppet_logs` | Logs from the last [Puppet](https://puppetlabs.com) run | Linux / macOS / Windows | |
| `puppet_state` | State of every resource [Puppet](https://puppetlabs.com) is managing | Linux / macOS / Windows | |
| `puppet_facts` | [Puppet](https://puppetlabs.com) facts | Linux / macOS / Windows | |
| `sofa_security_release_info` | The information on the security release the device is running from [Sofa](https://sofa.macadmins.io) | macOS | Use the `url` constraint to specify a data source other than `https://sofa.macadmins.io/v1/macos_data_feed.json` |
| `sofa_unpatched_cves` | The CVEs that are unpatched on the device from [Sofa](https://sofa.macadmins.io) | macOS | Use the `url` constraint to specify a data source other than `https://sofa.macadmins.io/v1/macos_data_feed.json` |
| `sofa_security_release_info` | The information on the security release the device is running from [Sofa](https://sofa.macadmins.io) | macOS | Use the `url` constraint to specify a data source other than `https://sofa.macadmins.io/v1/macos_data_feed.json` . By default this table will return vulnerability data for the running operating system. For historical data, use the `os_version` predicate (e.g `select * from sofa_security_release_info where os_version="14.4.0";`) |
| `sofa_unpatched_cves` | The CVEs that are unpatched on the device from [Sofa](https://sofa.macadmins.io) | macOS | Use the `url` constraint to specify a data source other than `https://sofa.macadmins.io/v1/macos_data_feed.json`. By default this table will return all unpatched vulnerability data. For historical data, use the `os_version` predicate (e.g `select * from sofa_unpatched_cves where os_version="14.4.0";`) |
| `unified_log` | Results from macOS' Unified Log | macOS | Use the constraints `predicate` and `last` to limit the number of results you pull, or this will not be very performant at all. Use `level` with a value of `info` to include info level messages. Use `level` with a value of `debug` to include info and debug level messages. (`select * from unified_log where last="1h" and level="debug" and predicate='processImagePath contains "mdmclient"';`) |

## Development
Expand Down
3 changes: 2 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import (
"github.com/macadmins/osquery-extension/tables/networkquality"
"github.com/macadmins/osquery-extension/tables/pendingappleupdates"
"github.com/macadmins/osquery-extension/tables/puppet"
"github.com/macadmins/osquery-extension/tables/sofa"
"github.com/macadmins/osquery-extension/tables/unifiedlog"

"github.com/macadmins/osquery-extension/tables/sofa"
osquery "github.com/osquery/osquery-go"
"github.com/osquery/osquery-go/plugin/table"
)
Expand Down
37 changes: 25 additions & 12 deletions tables/sofa/sofa_cves.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,38 @@ func SofaUnpatchedCVEsColumns() []table.ColumnDefinition {
func SofaUnpatchedCVEsGenerate(ctx context.Context, queryContext table.QueryContext, socketPath string) ([]map[string]string, error) {
url := SofaV1URL
if constraintList, present := queryContext.Constraints["url"]; present {
// 'path' is in the where clause
// 'url' is in the where clause
for _, constraint := range constraintList.Constraints {
// =
if constraint.Operator == table.OperatorEquals {
url = constraint.Expression
}
}
}
osVersion := ""
if constraintList, present := queryContext.Constraints["os_version"]; present {
// 'os_version' is in the where clause
for _, constraint := range constraintList.Constraints {
// =
if constraint.Operator == table.OperatorEquals {
osVersion = constraint.Expression
}
}
}

if osVersion == "" {
// get the current device os version from osquery
osqueryClient, err := osquery.NewClient(socketPath, 10*time.Second)
if err != nil {
return nil, err
}
defer osqueryClient.Close()

osVersion, err = getCurrentOSVersion(osqueryClient)
if err != nil {
return nil, err
}
}

client := NewSofaClient(WithURL(url))
root, err := client.downloadSofaJSON()
Expand All @@ -44,17 +68,6 @@ func SofaUnpatchedCVEsGenerate(ctx context.Context, queryContext table.QueryCont
}

var results []map[string]string
// get the current device os version from osquery
osqueryClient, err := osquery.NewClient(socketPath, 10*time.Second)
if err != nil {
return nil, err
}
defer osqueryClient.Close()

osVersion, err := getCurrentOSVersion(osqueryClient)
if err != nil {
return nil, err
}

// get all unpatched cves (for any os version that is higher than the current os version)
unpatchedCVEs, err := getUnpatchedCVEs(root, osVersion)
Expand Down
81 changes: 56 additions & 25 deletions tables/sofa/sofa_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strconv"
"time"

"github.com/hashicorp/go-version"
osquery "github.com/osquery/osquery-go"
"github.com/osquery/osquery-go/plugin/table"
)
Expand Down Expand Up @@ -156,21 +157,45 @@ func SofaSecurityReleaseInfoColumns() []table.ColumnDefinition {
table.TextColumn("security_info"),
table.IntegerColumn("unique_cves_count"),
table.IntegerColumn("days_since_previous_release"),
table.TextColumn("os_version"),
}
}

func SofaSecurityReleaseInfoGenerate(ctx context.Context, queryContext table.QueryContext, socketPath string) ([]map[string]string, error) {

url := SofaV1URL
if constraintList, present := queryContext.Constraints["url"]; present {
// 'path' is in the where clause
// 'url' is in the where clause
for _, constraint := range constraintList.Constraints {
// =
if constraint.Operator == table.OperatorEquals {
url = constraint.Expression
}
}
}
osVersion := ""
if constraintList, present := queryContext.Constraints["os_version"]; present {
// 'os_version' is in the where clause
for _, constraint := range constraintList.Constraints {
// =
if constraint.Operator == table.OperatorEquals {
osVersion = constraint.Expression
}
}
}

if osVersion == "" {
// get the current device os version from osquery
osqueryClient, err := osquery.NewClient(socketPath, 10*time.Second)
if err != nil {
return nil, err
}
defer osqueryClient.Close()

osVersion, err = getCurrentOSVersion(osqueryClient)
if err != nil {
return nil, err
}
}

client := NewSofaClient(WithURL(url))
root, err := client.downloadSofaJSON()
Expand All @@ -179,44 +204,50 @@ func SofaSecurityReleaseInfoGenerate(ctx context.Context, queryContext table.Que
}

var results []map[string]string
// get the current device os version from osquery
osqueryClient, err := osquery.NewClient(socketPath, 10*time.Second)
if err != nil {
return nil, err
}
defer osqueryClient.Close()

osVersion, err := getCurrentOSVersion(osqueryClient)
// get the security release info for the current os version
securityReleases, err := getSecurityReleaseInfoForOSVersion(root, osVersion)
if err != nil {
return nil, err
}

// get the security release info for the current os version
securityRelease, err := getSecurityReleaseInfoForCurrentOSVersion(root, osVersion)
if err != nil {
return nil, err
for _, securityRelease := range securityReleases {
results = append(results, map[string]string{
"update_name": securityRelease.UpdateName,
"product_version": securityRelease.ProductVersion,
"release_date": securityRelease.ReleaseDate,
"security_info": securityRelease.SecurityInfo,
"unique_cves_count": strconv.Itoa(securityRelease.UniqueCVEsCount),
"days_since_previous_release": strconv.Itoa(securityRelease.DaysSincePreviousRelease),
"os_version": osVersion,
})
}

results = append(results, map[string]string{
"update_name": securityRelease.UpdateName,
"product_version": securityRelease.ProductVersion,
"release_date": securityRelease.ReleaseDate,
"security_info": securityRelease.SecurityInfo,
"unique_cves_count": strconv.Itoa(securityRelease.UniqueCVEsCount),
"days_since_previous_release": strconv.Itoa(securityRelease.DaysSincePreviousRelease),
})
return results, nil
}

func getSecurityReleaseInfoForCurrentOSVersion(root Root, osVersion string) (SecurityRelease, error) {
func getSecurityReleaseInfoForOSVersion(root Root, osVersion string) ([]SecurityRelease, error) {
out := []SecurityRelease{}
parsedOSVersion, err := version.NewVersion(osVersion)
if err != nil {
return out, err
}
for _, os := range root.OSVersions {
for _, securityRelease := range os.SecurityReleases {
if securityRelease.ProductVersion == osVersion {
return securityRelease, nil
parsedProductVersion, err := version.NewVersion(securityRelease.ProductVersion)
if err != nil {
return out, err
}
if parsedProductVersion.GreaterThanOrEqual(parsedOSVersion) {
out = append(out, securityRelease)
}
}
}
return SecurityRelease{}, errors.New("no security release info found for current os version")
if len(out) == 0 {
return []SecurityRelease{}, errors.New("no security release info found for os version")
}

return out, nil
}

func getCurrentOSVersion(client OsqueryClient) (string, error) {
Expand Down
26 changes: 15 additions & 11 deletions tables/sofa/sofa_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ func TestWithEndpoint(t *testing.T) {
assert.Equal(t, "http://example.com", client.endpoint)
}

func TestGetSecurityReleaseInfoForCurrentOSVersion(t *testing.T) {
func TestGetSecurityReleaseInfoForOSVersion(t *testing.T) {
tests := []struct {
name string
root Root
osVersion string
wantVersion string
wantErr bool
name string
root Root
osVersion string
wantVersions []string
wantErr bool
}{
{
name: "security release info found",
Expand All @@ -70,9 +70,9 @@ func TestGetSecurityReleaseInfoForCurrentOSVersion(t *testing.T) {
},
},
},
osVersion: "10.0",
wantVersion: "10.0",
wantErr: false,
osVersion: "9.0",
wantVersions: []string{"10.0"},
wantErr: false,
},
{
name: "security release info not found",
Expand All @@ -94,12 +94,16 @@ func TestGetSecurityReleaseInfoForCurrentOSVersion(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getSecurityReleaseInfoForCurrentOSVersion(tt.root, tt.osVersion)
got, err := getSecurityReleaseInfoForOSVersion(tt.root, tt.osVersion)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.wantVersion, got.ProductVersion)
gotVersions := make([]string, len(got))
for i, release := range got {
gotVersions[i] = release.ProductVersion
}
assert.Equal(t, tt.wantVersions, gotVersions)
}
})
}
Expand Down

0 comments on commit 38bb7be

Please sign in to comment.