mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
33858d7301
# Checklist for submitter If some of the following don't apply, delete the relevant line. - [ ] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [ ] Documented any API changes (docs/Using-Fleet/REST-API.md or docs/Contributing/API-for-contributors.md) - [ ] Documented any permissions changes (docs/Using Fleet/manage-access.md) - [ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for new osquery data ingestion features. - [ ] Added/updated tests - [ ] Manual QA for all new/changed functionality - For Orbit and Fleet Desktop changes: - [ ] Manual QA must be performed in the three main OSs, macOS, Windows and Linux. - [ ] Auto-update manual QA, from released version of component to new version (see [tools/tuf/test](../tools/tuf/test/README.md)). Signed-off-by: guoguangwu <guoguangwu@magic-shield.com>
231 lines
5.2 KiB
Go
231 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/fleetdm/fleet/v4/pkg/spec"
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
|
"github.com/ghodss/yaml"
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
// mappings based on https://github.com/osquery/osquery/blob/b87a4b5f1567415a72acd5ecd0e9e7ab75754959/tools/codegen/genwebsitejson.py#L38C18-L38C18
|
|
var platformMapping = map[string][]string{
|
|
"darwin": {"darwin"},
|
|
"linux": {"linux"},
|
|
"windows": {"windows"},
|
|
"chrome": {"chrome"},
|
|
"specs": {"darwin", "linux", "windows"},
|
|
"utility": {"darwin", "linux", "windows"},
|
|
"yara": {"darwin", "linux", "windows"},
|
|
"smart": {"darwin", "linux"},
|
|
"kernel": {"darwin"},
|
|
"linwin": {"linux", "windows"},
|
|
"macwin": {"darwin", "windows"},
|
|
"posix": {"darwin", "linux"},
|
|
"sleuthkit": {"darwin", "linux"},
|
|
"any": {""},
|
|
"all": {""},
|
|
"": {""},
|
|
}
|
|
|
|
func convertPlatforms(platformsIn string) (string, error) {
|
|
splitPlatformsIn := strings.Split(platformsIn, ",")
|
|
|
|
// validate and convert each substring
|
|
mapped := map[string]struct{}{} // use a set to dedupe
|
|
for _, substring := range splitPlatformsIn {
|
|
mappedSubstring, ok := platformMapping[substring]
|
|
// validate substring
|
|
if !ok {
|
|
return "", fmt.Errorf("unsupported platform: %s", substring)
|
|
}
|
|
for _, p := range mappedSubstring {
|
|
mapped[p] = struct{}{}
|
|
}
|
|
}
|
|
|
|
// convert set to slice
|
|
result := make([]string, 0, len(mapped))
|
|
|
|
for p := range mapped {
|
|
result = append(result, p)
|
|
}
|
|
|
|
// sort for deterministic output
|
|
sort.Strings(result)
|
|
|
|
resultString := strings.Join(result, ",")
|
|
|
|
return resultString, nil
|
|
}
|
|
|
|
func specGroupFromPack(name string, inputPack fleet.PermissivePackContent) (*spec.Group, error) {
|
|
specs := &spec.Group{
|
|
Queries: []*fleet.QuerySpec{},
|
|
}
|
|
|
|
// this ensures order is consistent in output
|
|
keys := make([]string, len(inputPack.Queries))
|
|
i := 0
|
|
for k := range inputPack.Queries {
|
|
keys[i] = k
|
|
i++
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
for _, name := range keys {
|
|
query := inputPack.Queries[name]
|
|
|
|
// get the interval as uint from a variety of possible types
|
|
interval := uint(0)
|
|
switch i := query.Interval.(type) {
|
|
case string:
|
|
u64, err := strconv.ParseUint(i, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("converting interval from string to uint: %w", err)
|
|
}
|
|
interval = uint(u64)
|
|
case uint:
|
|
interval = i
|
|
case float64:
|
|
interval = uint(i)
|
|
}
|
|
|
|
// handle nil query.Platform
|
|
var queryPlatforms string
|
|
if query.Platform != nil {
|
|
queryPlatforms = *query.Platform
|
|
}
|
|
convertedPlatforms, err := convertPlatforms(queryPlatforms)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// handle nil query.Version
|
|
var minOsqueryVersion string
|
|
if query.Version != nil {
|
|
minOsqueryVersion = *query.Version
|
|
}
|
|
|
|
spec := &fleet.QuerySpec{
|
|
Name: name,
|
|
Description: query.Description,
|
|
Query: query.Query,
|
|
Interval: interval,
|
|
Platform: convertedPlatforms,
|
|
MinOsqueryVersion: minOsqueryVersion,
|
|
}
|
|
|
|
specs.Queries = append(specs.Queries, spec)
|
|
}
|
|
|
|
return specs, nil
|
|
}
|
|
|
|
func convertCommand() *cli.Command {
|
|
var (
|
|
flFilename string
|
|
outputFilename string
|
|
)
|
|
return &cli.Command{
|
|
Name: "convert",
|
|
Usage: "Convert osquery packs into Fleet queries",
|
|
UsageText: `fleetctl convert [options]`,
|
|
Flags: []cli.Flag{
|
|
configFlag(),
|
|
contextFlag(),
|
|
&cli.StringFlag{
|
|
Name: "f",
|
|
EnvVars: []string{"FILENAME"},
|
|
Value: "",
|
|
Destination: &flFilename,
|
|
Usage: "A file to apply",
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "o",
|
|
EnvVars: []string{"OUTPUT_FILENAME"},
|
|
Value: "",
|
|
Destination: &outputFilename,
|
|
Usage: "The name of the file to output converted results",
|
|
},
|
|
},
|
|
Action: func(c *cli.Context) error {
|
|
if flFilename == "" {
|
|
return errors.New("-f must be specified")
|
|
}
|
|
|
|
b, err := os.ReadFile(flFilename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Remove any literal newlines (because they are not
|
|
// valid JSON but osquery accepts them) and replace
|
|
// with \n so that we get them in the YAML output where
|
|
// they are allowed.
|
|
re := regexp.MustCompile(`\s*\\\n`)
|
|
b = re.ReplaceAll(b, []byte(`\n`))
|
|
|
|
var specs *spec.Group
|
|
|
|
var pack fleet.PermissivePackContent
|
|
if err := json.Unmarshal(b, &pack); err != nil {
|
|
return err
|
|
}
|
|
|
|
base := filepath.Base(flFilename)
|
|
specs, err = specGroupFromPack(strings.TrimSuffix(base, filepath.Ext(base)), pack)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if specs == nil {
|
|
return errors.New("could not parse files")
|
|
}
|
|
|
|
var w io.Writer = os.Stdout
|
|
if outputFilename != "" {
|
|
file, err := os.Create(outputFilename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
w = file
|
|
}
|
|
|
|
for _, query := range specs.Queries {
|
|
specBytes, err := json.Marshal(query)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
meta := spec.Metadata{
|
|
Kind: fleet.QueryKind,
|
|
Version: fleet.ApiVersion,
|
|
Spec: specBytes,
|
|
}
|
|
|
|
out, err := yaml.Marshal(meta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Fprintln(w, "---")
|
|
fmt.Fprint(w, string(out))
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
}
|