fleet/orbit/pkg/table/crowdstrike/falconctl/parser_test.go
Ian Littman a5299d4eb6
Make various fixes to falconctl parsing to remove errors on selecting from the falconctl_options table (#35479)
Fixes #32239.

This changes tags to return a comma-delimited list on multiple tags, the
single tag when there's only one, and "is not set" (similar to other
values) when no tags are set.

Confirmed that this allows us to run `SELECT * FROM falconctl_options`
without issue on various configurations of Crowdstrike Falcon on Linux.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually

## fleetd/orbit/Fleet Desktop

- [x] Verified compatibility with the latest released version of Fleet
(see [Must
rule](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/workflows/fleetd-development-and-release-strategy.md))
- [x] If the change applies to only one platform, confirmed that
`runtime.GOOS` is used as needed to isolate changes
2025-11-11 18:11:04 -06:00

186 lines
5.1 KiB
Go

// based on github.com/kolide/launcher/pkg/osquery/tables
package falconctl
import (
"bytes"
"os"
"path"
"testing"
"github.com/stretchr/testify/require"
)
func TestParseOptions(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input []byte
expected any
expectedErr bool
}{
{
name: "empty",
expected: map[string]any{},
},
{
name: "--cid",
input: []byte(`cid="REDACTED"`),
expected: map[string]any{"cid": "REDACTED"},
},
{
name: "--aid",
input: []byte(`aid="REDACTED"`),
expected: map[string]any{"aid": "REDACTED"},
},
{
name: "--apd",
input: []byte(`apd is not set,`),
expected: map[string]any{"apd": "is not set"},
},
{
name: "--aph",
input: []byte(`aph is not set,`),
expected: map[string]any{"aph": "is not set"},
},
{
name: "--app",
input: []byte(`app is not set,`),
expected: map[string]any{"app": "is not set"},
},
{
name: "--rfm-state",
input: []byte(`rfm-state=false,`),
expected: map[string]any{"rfm-state": "false"},
},
{
name: "--rfm-reason",
input: []byte(`rfm-reason=None, code=0x0,`),
expected: map[string]any{"rfm-reason": "None", "rfm-reason-code": "0x0"},
},
{
name: "--trace",
input: []byte(`trace is not set,`),
expected: map[string]any{"trace": "is not set"},
},
{
name: "--feature",
input: []byte(`feature= (hex bitmask: 0),`),
expected: map[string]any{"feature": "(hex bitmask: 0)"},
},
{
name: "--metadata-query",
input: []byte(`metadata-query=enable (unset default),`),
expected: map[string]any{"metadata-query": "enable"},
},
{
name: "--version",
input: []byte(`version = 6.45.14203.0,`),
expected: map[string]any{"version": "6.45.14203.0"},
},
{
name: "--billing",
input: []byte(`billing is not set,`),
expected: map[string]any{"billing": "is not set"},
},
{
name: "--tags",
input: []byte(`tags=kolide-test-1,kolide-test-2,`),
expected: map[string]any{"tags": "kolide-test-1,kolide-test-2"},
},
{
name: "--tags with no tags",
input: []byte(`Sensor grouping tags are not set`),
expected: map[string]any{"tags": "is not set"},
},
{
name: "--rfm-state --rfm-reason --aph --tags",
input: []byte("aph is not set, rfm-state=false, rfm-reason=None, code=0x0, tags=kolide-test-1,kolide-test-2."),
expected: map[string]any{
"aph": "is not set",
"rfm-reason": "None",
"rfm-reason-code": "0x0",
"rfm-state": "false",
"tags": "kolide-test-1,kolide-test-2",
},
},
{
name: "-rfm-state --rfm-reason --aph --tags --version",
input: []byte("aph is not set, rfm-state=false, rfm-reason=None, code=0x0, version = 6.45.14203.0\ntags=kolide-test-1,kolide-test-2, "),
expected: map[string]any{
"aph": "is not set",
"rfm-reason": "None",
"rfm-reason-code": "0x0",
"rfm-state": "false",
"version": "6.45.14203.0",
"tags": "kolide-test-1,kolide-test-2",
},
},
// something with a bunch of things
{
name: "normal",
input: readTestFile(t, path.Join("test-data", "options.txt")),
expected: map[string]any{
"aid": "is not set",
"aph": "is not set",
"app": "is not set",
"cid": "ac917ab****************************",
"feature": "is not set",
"metadata-query": "enable",
"rfm-reason": "is not set",
"rfm-state": "is not set",
"version": "6.38.13501.0",
},
},
{
name: "--rfm-state --rfm-reason --aph",
input: []byte("aph is not set, rfm-state=false, rfm-reason=None, code=0x0.\n"),
expected: map[string]any{"aph": "is not set", "rfm-reason": "None", "rfm-reason-code": "0x0", "rfm-state": "false"},
},
{
name: "cid not set",
input: readTestFile(t, path.Join("test-data", "cid-error.txt")),
expectedErr: true,
},
{
name: "all options",
input: []byte("cid=\"deadbeefdeadbeefdeadbeefdeadbeef\", aid is not set, apd is not set, aph is not set, app is not set, feature is not set, metadata-query=enable (unset default), version = 7.30.18306.0\ntags=foo,bar, rfm-state is not set, rfm-reason is not set,"),
expected: map[string]any{
"cid": "deadbeefdeadbeefdeadbeefdeadbeef",
"aid": "is not set",
"apd": "is not set",
"aph": "is not set",
"app": "is not set",
"feature": "is not set",
"metadata-query": "enable",
"version": "7.30.18306.0",
"tags": "foo,bar",
"rfm-state": "is not set",
"rfm-reason": "is not set",
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
actual, err := parseOptions(bytes.NewReader(tt.input))
if tt.expectedErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tt.expected, actual)
})
}
}
func readTestFile(t *testing.T, filepath string) []byte {
b, err := os.ReadFile(filepath)
require.NoError(t, err)
return b
}