mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 00:45:19 +00:00
Add Kolide osquery tables
This commit is contained in:
parent
f8652a1b68
commit
ab7717009e
1
.github/workflows/build-orbit.yaml
vendored
1
.github/workflows/build-orbit.yaml
vendored
@ -14,6 +14,7 @@ on:
|
||||
|
||||
env:
|
||||
ORBIT_VERSION: 1.17.0
|
||||
CGO_ENABLED: 1
|
||||
|
||||
# This allows a subsequently queued workflow run to interrupt previous runs
|
||||
concurrency:
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -100,3 +100,6 @@ fleetd_tables_*
|
||||
tools/test_extensions/hello_world/macos
|
||||
tools/test_extensions/hello_world/windows
|
||||
tools/test_extensions/hello_world/linux
|
||||
|
||||
# Residual files when building fleet_tables extension.
|
||||
fleet_tables_*.ext
|
||||
|
@ -21,7 +21,7 @@ linters-settings:
|
||||
main:
|
||||
deny:
|
||||
- pkg: github.com/pkg/errors
|
||||
msg: "use ctxerr if a context.Context is available or stdlib errors.New / fmt.Errorf with the %w verb"
|
||||
desc: "use ctxerr if a context.Context is available or stdlib errors.New / fmt.Errorf with the %w verb"
|
||||
|
||||
errcheck:
|
||||
check-type-assertions: false
|
||||
|
8
Makefile
8
Makefile
@ -244,11 +244,11 @@ fleetd-tables-darwin:
|
||||
GOOS=darwin GOARCH=amd64 go build -o fleetd_tables_darwin.ext ./orbit/cmd/fleetd_tables
|
||||
fleetd-tables-darwin_arm:
|
||||
GOOS=darwin GOARCH=arm64 go build -o fleetd_tables_darwin_arm.ext ./orbit/cmd/fleetd_tables
|
||||
fleetd-tables-darwin-universal:
|
||||
$(MAKE) fleetd-tables-darwin fleetd-tables-darwin_arm
|
||||
fleetd-tables-darwin-universal: fleetd-tables-darwin fleetd-tables-darwin_arm
|
||||
lipo -create fleetd_tables_darwin.ext fleetd_tables_darwin_arm.ext -output fleetd_tables_darwin_universal.ext
|
||||
fleetd-tables-all:
|
||||
$(MAKE) fleetd-tables-windows fleetd-tables-linux fleetd-tables-darwin-universal
|
||||
fleetd-tables-all: fleetd-tables-windows fleetd-tables-linux fleetd-tables-darwin-universal
|
||||
fleetd-tables-clean:
|
||||
rm -f fleetd_tables_windows.exe fleetd_tables_linux.ext fleetd_tables_darwin.ext fleetd_tables_darwin_arm.ext fleetd_tables_darwin_universal.ext
|
||||
|
||||
.pre-binary-arch:
|
||||
ifndef GOOS
|
||||
|
11
changes/14464-add-kolide-tables
Normal file
11
changes/14464-add-kolide-tables
Normal file
@ -0,0 +1,11 @@
|
||||
- added tables to the fleetd extension:
|
||||
- app_icons
|
||||
- falconctl_options
|
||||
- falcon_kernel_check
|
||||
- cryptoinfo
|
||||
- cryptsetup_status
|
||||
- filevault_status
|
||||
- firefox_preferences
|
||||
- firmwarepasswd
|
||||
- ioreg
|
||||
- windows_updates
|
90
go.mod
90
go.mod
@ -3,7 +3,7 @@ module github.com/fleetdm/fleet/v4
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
cloud.google.com/go/pubsub v1.30.0
|
||||
cloud.google.com/go/pubsub v1.33.0
|
||||
github.com/AbGuthrie/goquery/v2 v2.0.1
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
@ -17,7 +17,8 @@ require (
|
||||
github.com/beevik/ntp v0.3.0
|
||||
github.com/briandowns/spinner v1.13.0
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||
github.com/cenkalti/backoff/v4 v4.2.0
|
||||
github.com/cenkalti/backoff/v4 v4.2.1
|
||||
github.com/clbanning/mxj v1.8.4
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/dgraph-io/badger/v2 v2.2007.2
|
||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e
|
||||
@ -32,6 +33,7 @@ require (
|
||||
github.com/getlantern/systray v1.2.2-0.20220329111105-6065fda28be8
|
||||
github.com/getsentry/sentry-go v0.18.0
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/go-ini/ini v1.67.0
|
||||
github.com/go-kit/kit v0.12.0
|
||||
github.com/go-kit/log v0.2.1
|
||||
github.com/go-ole/go-ole v1.2.6
|
||||
@ -56,8 +58,8 @@ require (
|
||||
github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5
|
||||
github.com/josephspurrier/goversioninfo v1.4.0
|
||||
github.com/kevinburke/go-bindata v3.24.0+incompatible
|
||||
github.com/kolide/kit v0.0.0-20191023141830-6312ecc11c23
|
||||
github.com/kolide/launcher v0.11.25-0.20220321235155-c3e9480037d2
|
||||
github.com/kolide/kit v0.0.0-20221107170827-fb85e3d59eab
|
||||
github.com/kolide/launcher v1.1.2
|
||||
github.com/macadmins/osquery-extension v0.0.15
|
||||
github.com/mattermost/xml-roundtrip-validator v0.0.0-20201213122252-bcd7e1b9601e
|
||||
github.com/mattn/go-sqlite3 v1.14.13
|
||||
@ -68,13 +70,14 @@ require (
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
github.com/mitchellh/gon v0.2.3
|
||||
github.com/mna/redisc v1.3.2
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
github.com/ngrok/sqlmw v0.0.0-20211220175533-9d16fdc47b31
|
||||
github.com/nukosuke/go-zendesk v0.13.1
|
||||
github.com/oklog/run v1.1.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/open-policy-agent/opa v0.44.0
|
||||
github.com/oschwald/geoip2-golang v1.8.0
|
||||
github.com/osquery/osquery-go v0.0.0-20230603132358-d2e851b3991b
|
||||
github.com/osquery/osquery-go v0.0.0-20231006172600-d6f325f636a9
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.13.0
|
||||
@ -83,13 +86,13 @@ require (
|
||||
github.com/russellhaering/goxmldsig v1.2.0
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9
|
||||
github.com/sethvargo/go-password v0.2.0
|
||||
github.com/shirou/gopsutil/v3 v3.22.8
|
||||
github.com/shirou/gopsutil/v3 v3.23.3
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||
github.com/spf13/cast v1.4.1
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/spf13/viper v1.10.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/theupdateframework/go-tuf v0.5.0
|
||||
github.com/theupdateframework/go-tuf v0.5.2
|
||||
github.com/throttled/throttled/v2 v2.8.0
|
||||
github.com/tj/assert v0.0.3
|
||||
github.com/ulikunitz/xz v0.5.10
|
||||
@ -100,32 +103,34 @@ require (
|
||||
go.elastic.co/apm/v2 v2.4.3
|
||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352
|
||||
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.40.0
|
||||
go.opentelemetry.io/otel v1.14.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0
|
||||
go.opentelemetry.io/otel/sdk v1.14.0
|
||||
golang.org/x/crypto v0.11.0
|
||||
go.opentelemetry.io/otel v1.19.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0
|
||||
go.opentelemetry.io/otel/sdk v1.19.0
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/exp v0.0.0-20230105202349-8879d0199aa3
|
||||
golang.org/x/net v0.12.0
|
||||
golang.org/x/oauth2 v0.6.0
|
||||
golang.org/x/image v0.10.0
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/oauth2 v0.12.0
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/sys v0.10.0
|
||||
golang.org/x/text v0.11.0
|
||||
google.golang.org/grpc v1.54.0
|
||||
golang.org/x/sys v0.13.0
|
||||
golang.org/x/text v0.13.0
|
||||
google.golang.org/grpc v1.58.3
|
||||
gopkg.in/guregu/null.v3 v3.5.0
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
howett.net/plist v1.0.0
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.110.0 // indirect
|
||||
cloud.google.com/go/compute v1.19.0 // indirect
|
||||
cloud.google.com/go v0.110.8 // indirect
|
||||
cloud.google.com/go/compute v1.23.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v0.13.0 // indirect
|
||||
cloud.google.com/go/kms v1.10.0 // indirect
|
||||
cloud.google.com/go/storage v1.28.1 // indirect
|
||||
cloud.google.com/go/iam v1.1.2 // indirect
|
||||
cloud.google.com/go/kms v1.15.2 // indirect
|
||||
cloud.google.com/go/storage v1.30.1 // indirect
|
||||
code.gitea.io/sdk/gitea v0.15.0 // indirect
|
||||
github.com/AlekSi/pointer v1.2.0 // indirect
|
||||
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
|
||||
@ -188,16 +193,16 @@ require (
|
||||
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/docker/distribution v2.8.0+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.2+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/elastic/go-sysinfo v1.7.1 // indirect
|
||||
github.com/elastic/go-windows v1.0.1 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/facebookincubator/flog v0.0.0-20190930132826-d2511d0ce33c // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect
|
||||
github.com/getlantern/errors v1.0.1 // indirect
|
||||
github.com/getlantern/golog v0.0.0-20211223150227-d4d95a44d873 // indirect
|
||||
@ -214,7 +219,7 @@ require (
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v1.0.0 // indirect
|
||||
github.com/golang/glog v1.1.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
@ -222,13 +227,14 @@ require (
|
||||
github.com/google/go-github/v39 v39.2.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/rpmpack v0.0.0-20210518075352-dc539ef4f2ea // indirect
|
||||
github.com/google/s2a-go v0.1.4 // indirect
|
||||
github.com/google/wire v0.5.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.7.1 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/goreleaser/chglog v0.1.2 // indirect
|
||||
github.com/goreleaser/fileglob v1.2.0 // indirect
|
||||
github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.0.0 // indirect
|
||||
@ -270,10 +276,10 @@ require (
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.5.0 // indirect
|
||||
github.com/sergi/go-diff v1.2.0 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/slack-go/slack v0.9.4 // indirect
|
||||
github.com/spf13/afero v1.6.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
@ -282,8 +288,8 @@ require (
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||
github.com/trivago/tgo v1.0.7 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/vartanbeno/go-reddit/v2 v2.0.0 // indirect
|
||||
@ -297,22 +303,24 @@ require (
|
||||
go.elastic.co/apm/module/apmhttp/v2 v2.3.0 // indirect
|
||||
go.elastic.co/fastjson v1.1.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.14.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.19.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
gocloud.dev v0.24.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/term v0.10.0 // indirect
|
||||
golang.org/x/term v0.13.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.11.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.114.0 // indirect
|
||||
google.golang.org/api v0.128.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/mail.v2 v2.3.1 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
@ -320,7 +328,7 @@ require (
|
||||
gotest.tools/v3 v3.0.3 // indirect
|
||||
)
|
||||
|
||||
replace github.com/kolide/kit => github.com/zwass/kit v0.0.0-20210625184505-ec5b5c5cce9c
|
||||
replace github.com/kolide/kit => github.com/fleetdm/kolide-kit v0.0.0-20230519160117-86cc9441f9c1
|
||||
|
||||
replace github.com/micromdm/nanomdm => github.com/fleetdm/nanomdm v0.3.1-0.20230710170238-fd0813187f24
|
||||
|
||||
|
275
go.sum
275
go.sum
@ -35,16 +35,16 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW
|
||||
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
|
||||
cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
|
||||
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
|
||||
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
|
||||
cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
|
||||
cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME=
|
||||
cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ=
|
||||
cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU=
|
||||
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
|
||||
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
@ -52,21 +52,19 @@ cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/firestore v1.5.0/go.mod h1:c4nNYR1qdq7eaZ+jSc5fonrQN2k3M7sWATcYTiakjEo=
|
||||
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
|
||||
cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
|
||||
cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
|
||||
cloud.google.com/go/iam v1.1.2 h1:gacbrBdWcoVmGLozRuStX45YKvJtzIjJdAolzUs1sm4=
|
||||
cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
|
||||
cloud.google.com/go/kms v0.1.0/go.mod h1:8Qp8PCAypHg4FdmlyW1QRAv09BGQ9Uzh7JnmIZxPk+c=
|
||||
cloud.google.com/go/kms v1.10.0 h1:Imrtp8792uqNP9bdfPrjtUkjjqOMBcAJ2bdFaAnLhnk=
|
||||
cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24=
|
||||
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
|
||||
cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
|
||||
cloud.google.com/go/kms v1.15.2 h1:lh6qra6oC4AyWe5fUUUBe/S27k12OHAleOOOw6KakdE=
|
||||
cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w=
|
||||
cloud.google.com/go/monitoring v0.1.0/go.mod h1:Hpm3XfzJv+UTiXzCG5Ffp0wijzHTC7Cv4eR7o3x/fEE=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/pubsub v1.16.0/go.mod h1:6A8EfoWZ/lUvCWStKGwAWauJZSiuV0Mkmu6WilK/TxQ=
|
||||
cloud.google.com/go/pubsub v1.30.0 h1:vCge8m7aUKBJYOgrZp7EsNDf6QMd2CAlXZqWTn3yq6s=
|
||||
cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4=
|
||||
cloud.google.com/go/pubsub v1.33.0 h1:6SPCPvWav64tj0sVX/+npCBKhUi/UjJehy9op/V3p2g=
|
||||
cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc=
|
||||
cloud.google.com/go/secretmanager v0.1.0/go.mod h1:3nGKHvnzDUVit7U0S9KAKJ4aOsO1xtwRG+7ey5LK1bM=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
@ -74,8 +72,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.16.1/go.mod h1:LaNorbty3ehnU3rEjXSNV/NRgQA0O8Y+uh6bPe5UOk4=
|
||||
cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcbgI=
|
||||
cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y=
|
||||
cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM=
|
||||
cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E=
|
||||
cloud.google.com/go/trace v0.1.0/go.mod h1:wxEwsoeRVPbeSkt7ZC9nWCgmoKQRAoySN7XHW2AmI7g=
|
||||
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
|
||||
code.gitea.io/sdk/gitea v0.15.0 h1:tsNhxDM/2N1Ohv1Xq5UWrht/esg0WmtRj4wsHVHriTg=
|
||||
@ -159,7 +157,6 @@ github.com/GoogleCloudPlatform/cloudsql-proxy v1.24.0/go.mod h1:3tx938GhY4FC+E1K
|
||||
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
@ -167,8 +164,6 @@ github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030I
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/Microsoft/go-winio v0.4.9/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
@ -188,12 +183,10 @@ github.com/ProtonMail/gopenpgp/v2 v2.2.2/go.mod h1:ajUlBGvxMH1UBZnaYO3d1FSVzjiC6
|
||||
github.com/PuerkitoBio/goquery v1.7.1/go.mod h1:XY0pP4kfraEmmV1O7Uf6XyjoslwsneBbgeDjLYuN8xY=
|
||||
github.com/RobotsAndPencils/buford v0.14.0 h1:+d18IMEisYlRZZYfe6uFlmQGbT07kWro25V35fGptZM=
|
||||
github.com/RobotsAndPencils/buford v0.14.0/go.mod h1:F5FvdB/nkMby8Pge6HFpPHgLOeUZne/iE5wKzvx64Y0=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f h1:HR5nRmUQgXrwqZOwZ2DAc/aCi3Bu3xENpspW935vxu0=
|
||||
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f/go.mod h1:f3HiCrHjHBdcm6E83vGaXh1KomZMA2P6aeo3hKx/wg0=
|
||||
github.com/WatchBeam/clock v0.0.0-20161028195133-dc1b57477882/go.mod h1:N5eJIl14rhNCrE5I3O10HIyhZ1HpjaRHT9WDg1eXxtI=
|
||||
github.com/WatchBeam/clock v0.0.0-20170901150240-b08e6b4da7ea h1:C9Xwp9fZf9BFJMsTqs8P+4PETXwJPUOuJZwBfVci+4A=
|
||||
github.com/WatchBeam/clock v0.0.0-20170901150240-b08e6b4da7ea/go.mod h1:N5eJIl14rhNCrE5I3O10HIyhZ1HpjaRHT9WDg1eXxtI=
|
||||
github.com/XSAM/otelsql v0.10.0 h1:y8o7q4NaZEV0dBiUC7TuNTHNKyDaX3Z4anntNu7dfYw=
|
||||
@ -202,7 +195,6 @@ github.com/aai/gocrypto v0.0.0-20160205191751-93df0c47f8b8/go.mod h1:nE/FnVUmtbP
|
||||
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
|
||||
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
|
||||
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
|
||||
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
||||
github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw=
|
||||
@ -229,8 +221,6 @@ github.com/antchfx/xmlquery v1.3.14/go.mod h1:yPRBXRdd2Xqz9c2Z61qvMKbK+u3NXXydp6
|
||||
github.com/antchfx/xpath v1.2.2 h1:fsKX4sHfxhsGpDMYjsvCmGC0EGdiT7XA0af/6PP6Oa0=
|
||||
github.com/antchfx/xpath v1.2.2/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/apache/thrift v0.13.1-0.20200603211036-eac4d0c79a5f/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
|
||||
github.com/apache/thrift v0.18.1 h1:lNhK/1nqjbwbiOPDBPFJVKxgDEGSepKuTh6OLiXW8kg=
|
||||
github.com/apache/thrift v0.18.1/go.mod h1:rdQn/dCcDKEWjjylUeueum4vQEjG2v8v2PqriUnbr+I=
|
||||
github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0=
|
||||
@ -301,8 +291,6 @@ github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/briandowns/spinner v1.13.0 h1:q/Y9LtpwtvL0CRzXrAMj0keVXqNhBYUFg6tBOUiY8ek=
|
||||
github.com/briandowns/spinner v1.13.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ=
|
||||
github.com/bugsnag/bugsnag-go v1.3.2/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||
github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/bytecodealliance/wasmtime-go v0.36.0 h1:B6thr7RMM9xQmouBtUqm1RpkJjuLS37m6nxX+iwsQSc=
|
||||
github.com/bytecodealliance/wasmtime-go v0.36.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI=
|
||||
github.com/c-bata/go-prompt v0.2.3 h1:jjCS+QhG/sULBhAaBdjb2PlMRVaKXQgn+4yzaauvs2s=
|
||||
@ -319,12 +307,11 @@ github.com/caarlos0/testfs v0.4.3 h1:q1zEM5hgsssqWanAfevJYYa0So60DdK6wlJeTc/yfUE
|
||||
github.com/caarlos0/testfs v0.4.3/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk=
|
||||
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e h1:hHg27A0RSSp2Om9lubZpiMgVbvn39bsUmW9U5h0twqc=
|
||||
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
|
||||
github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
|
||||
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
@ -338,9 +325,9 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
|
||||
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cfssl v0.0.0-20181102015659-ea4033a214e7/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
@ -399,21 +386,19 @@ github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:Y
|
||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
|
||||
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY=
|
||||
github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
|
||||
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go v1.5.1-1/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/doug-martin/goqu/v9 v9.18.0 h1:/6bcuEtAe6nsSMVK/M+fOiXUNfyFF3yYtE07DBPFMYY=
|
||||
github.com/doug-martin/goqu/v9 v9.18.0/go.mod h1:nf0Wc2/hV3gYK9LiyqIrzBEVGlI8qW3GuDCEobC4wBQ=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/e-dard/netbug v0.0.0-20151029172837-e64d308a0b20 h1:eDPsdileewX4H5a2Jph4gS8mFf749gzIrzpbnPy1oRs=
|
||||
github.com/e-dard/netbug v0.0.0-20151029172837-e64d308a0b20/go.mod h1:WXFUXJ0Y/SzNqXmhUU7VkE7a2Pag0zZnE2b6I87YWIs=
|
||||
github.com/elastic/go-licenser v0.4.0/go.mod h1:V56wHMpmdURfibNBggaSBfqgPxyT1Tldns1i87iTEvU=
|
||||
@ -451,6 +436,8 @@ github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBd
|
||||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fleetdm/goose v0.0.0-20221011170007-06aacf8ac547 h1:3Vlgx6mJYFlj3GPB3CgoQrR7URgE0GQGnKYNfoXxuUo=
|
||||
github.com/fleetdm/goose v0.0.0-20221011170007-06aacf8ac547/go.mod h1:d7Q+0eCENnKQUhkfAUVLfGnD4QcgJMF/uB9WRTN9TDI=
|
||||
github.com/fleetdm/kolide-kit v0.0.0-20230519160117-86cc9441f9c1 h1:9JGbRO6QKpHr5HO5t6g3/5EspV5eDWaLKbRH7xKqL/c=
|
||||
github.com/fleetdm/kolide-kit v0.0.0-20230519160117-86cc9441f9c1/go.mod h1:HHtqF91JHl66L+Ms8aswzqVb2eEU5O3DRNiFmUzOf60=
|
||||
github.com/fleetdm/nanodep v0.1.1-0.20221221202251-71b67ab1da24 h1:XhczaxKV3J4NjztroidSnYKyq5xtxF+amBYdBWeik58=
|
||||
github.com/fleetdm/nanodep v0.1.1-0.20221221202251-71b67ab1da24/go.mod h1:QzQrCUTmSr9HotzKZAcfmy+czbEGK8Mq26hA+0DN4ag=
|
||||
github.com/fleetdm/nanomdm v0.3.1-0.20230710170238-fd0813187f24 h1:oP0kOAFDzu646/TXDCcvOGdDgSHJgk+X0Pfr60kp7Jg=
|
||||
@ -466,8 +453,8 @@ github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897/go.mod h1:lgRN6+
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4=
|
||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
|
||||
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
|
||||
@ -495,7 +482,6 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-bindata/go-bindata v1.0.0/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
@ -514,9 +500,9 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-ini/ini v1.61.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.7.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4=
|
||||
@ -546,13 +532,11 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-stack/stack v1.7.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
||||
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
||||
@ -570,7 +554,6 @@ github.com/gocarina/gocsv v0.0.0-20220310154401-d4df709ca055 h1:UfcDMw41lSx3XM7U
|
||||
github.com/gocarina/gocsv v0.0.0-20220310154401-d4df709ca055/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
@ -580,8 +563,8 @@ github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQA
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
||||
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
|
||||
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@ -629,10 +612,8 @@ github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws
|
||||
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
|
||||
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/fscrypt v0.3.0/go.mod h1:1mPhM/LudtWD88Lzv4Fwb6aAzih0J3f/B/zmFa4sTiA=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@ -690,9 +671,9 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/rpmpack v0.0.0-20210518075352-dc539ef4f2ea h1:Fv9Ni1vIq9+Gv4Sm0Xq+NnPYcnsMbdNhJ4Cu4rkbPBM=
|
||||
github.com/google/rpmpack v0.0.0-20210518075352-dc539ef4f2ea/go.mod h1:+y9lKiqDhR4zkLl+V9h4q0rdyrYVsWWm6LLCQP33DIk=
|
||||
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
|
||||
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
||||
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@ -700,14 +681,14 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
|
||||
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.4 h1:uGy6JWR/uMIILU8wbf+OkstIrNiMjGpEIyhx8f6W7s4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
|
||||
github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A=
|
||||
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/goreleaser/chglog v0.1.2 h1:tdzAb/ILeMnphzI9zQ7Nkq+T8R9qyXli8GydD8plFRY=
|
||||
github.com/goreleaser/chglog v0.1.2/go.mod h1:tTZsFuSZK4epDXfjMkxzcGbrIOXprf0JFp47BjIr3B8=
|
||||
@ -731,17 +712,14 @@ github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY=
|
||||
github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI=
|
||||
github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda h1:5ikpG9mYCMFiZX0nkxoV6aU2IpCHPdws3gCNgdZeEV0=
|
||||
github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c=
|
||||
github.com/groob/plist v0.0.0-20190114192801-a99fbe489d03/go.mod h1:qg2Nek0ND/hIr+nY8H1oVqEW2cLzVVNaAQ0QexOyjyc=
|
||||
github.com/groob/plist v0.0.0-20220217120414-63fa881b19a5 h1:saaSiB25B1wgaxrshQhurfPKUGJ4It3OxNJUy0rdOjU=
|
||||
github.com/groob/plist v0.0.0-20220217120414-63fa881b19a5/go.mod h1:itkABA+w2cw7x5nYUS/pLRef6ludkZKOigbROmCTaFw=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 h1:gDLXvp5S9izjldquuoAhDzccbskOL6tDC5jMSyx3zxE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2/go.mod h1:7pdNwVWBBHGiCxa9lAszqCJMbfTISJ7oMftp8+UGV08=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
@ -825,15 +803,12 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
|
||||
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/jinzhu/gorm v1.9.1/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jmoiron/sqlx v0.0.0-20180406164412-2aeb6a910c2b/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU=
|
||||
github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5 h1:lrdPtrORjGv1HbbEvKWDUAy97mPpFm4B8hp77tcCUJY=
|
||||
github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4=
|
||||
@ -856,7 +831,6 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||
github.com/kevinburke/go-bindata v3.24.0+incompatible h1:qajFA3D0pH94OTLU4zcCCKCDgR+Zr2cZK/RPJHDdFoY=
|
||||
github.com/kevinburke/go-bindata v3.24.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
@ -871,12 +845,9 @@ github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e
|
||||
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
|
||||
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/knightsc/system_policy v1.1.1-0.20211029142728-5f4c0d5419cc/go.mod h1:5e34JEkxWsOeAd9jvcxkz01tAY/JAGFuabGnNBJ6TT4=
|
||||
github.com/kolide/launcher v0.11.25-0.20220321235155-c3e9480037d2 h1:PozvR7w1/Gd5X5xVn71il8vU68Pqwl3beQVF6k9fCm4=
|
||||
github.com/kolide/launcher v0.11.25-0.20220321235155-c3e9480037d2/go.mod h1:4dUso03feHUqlHfUJ+6dHXRuyPFFTB7+dcxgTNr1qUY=
|
||||
github.com/kolide/updater v0.0.0-20190315001611-15bbc19b5b80/go.mod h1:x3dEGYbZovhD1t8OwEgdyu/4ZCvrn9QvkbPtOZnul8k=
|
||||
github.com/kolide/launcher v1.1.2 h1:fJnGrofLMM+PaKKFrskCW0UeaIwYkMIo6gRSyi8Y+ls=
|
||||
github.com/kolide/launcher v1.1.2/go.mod h1:4h1FwciY5mWaNlG8ckHl3TU5yVN4+NkZtnrAvP65x1A=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
@ -887,7 +858,6 @@ github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NB
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.2/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
@ -909,7 +879,6 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
|
||||
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mat/besticon v3.9.0+incompatible/go.mod h1:mA1auQYHt6CW5e7L9HJLmqVQC8SzNk2gVwouO0AbiEU=
|
||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
@ -939,7 +908,6 @@ github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.13 h1:1tj15ngiFfcZzii7yd82foL+ks+ouQcj8j/TPq3fk1I=
|
||||
@ -957,7 +925,6 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/miekg/pkcs11 v0.0.0-20180208123018-5f6e0d0dad6f/go.mod h1:WCBAbTOdfhHhz7YXujeZMF7owC4tPb1naKFsgfUISjo=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
@ -966,7 +933,6 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
@ -976,7 +942,6 @@ github.com/mitchellh/gon v0.2.3/go.mod h1:Ua18ZhqjZHg8VyqZo8kNHAY331ntV6nNJ9mT3s
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
@ -986,7 +951,6 @@ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx
|
||||
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mixer/clock v0.0.0-20170901150240-b08e6b4da7ea/go.mod h1:U8TDygO2XZh1RtBCgX7oRbJ7gmSH4C6FROsBdQ6QyCc=
|
||||
github.com/mna/redisc v1.3.2 h1:sc9C+nj6qmrTFnsXb70xkjAHpXKtjjBuE6v2UcQV0ZE=
|
||||
github.com/mna/redisc v1.3.2/go.mod h1:CplIoaSTDi5h9icnj4FLbRgHoNKCHDNJDVRztWDGeSQ=
|
||||
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 h1:yH0SvLzcbZxcJXho2yh7CqdENGMQe73Cw3woZBpPli0=
|
||||
@ -1000,42 +964,34 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/ngrok/sqlmw v0.0.0-20211220175533-9d16fdc47b31 h1:FFHgfAIoAXCCL4xBoAugZVpekfGmZ/fBBueneUKBv7I=
|
||||
github.com/ngrok/sqlmw v0.0.0-20211220175533-9d16fdc47b31/go.mod h1:E26fwEtRNigBfFfHDWsklmo0T7Ixbg0XXgck+Hq4O9k=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nukosuke/go-zendesk v0.13.1 h1:EdYpn+FxROLguADEJK5reOHcpysM8wyWPOWO96SIc0A=
|
||||
github.com/nukosuke/go-zendesk v0.13.1/go.mod h1:86Cg7RhSvPfOqZOtQXteJEV9yIQVQsy2HVDk++Yf3jA=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/oklog/ulid v0.3.0/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/open-policy-agent/opa v0.44.0 h1:sEZthsrWBqIN+ShTMJ0Hcz6a3GkYsY4FaB2S/ou2hZk=
|
||||
github.com/open-policy-agent/opa v0.44.0/go.mod h1:YpJaFIk5pq89n/k72c1lVvfvR5uopdJft2tMg1CW/yU=
|
||||
github.com/opencensus-integrations/ocsql v0.1.1/go.mod h1:ozPYpNVBHZsX33jfoQPO5TlI5lqh0/3R36kirEqJKAM=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec=
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
|
||||
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
||||
github.com/osquery/osquery-go v0.0.0-20210622151333-99b4efa62ec5/go.mod h1:JKR5QhjsYdnIPY7hakgas5sxf8qlA/9wQnLqaMfWdcg=
|
||||
github.com/osquery/osquery-go v0.0.0-20220317165851-954ac78f381f/go.mod h1:0KzmMhe0PL19cdYq6nd1cT9/5bMMJBTssAfuEgM2i34=
|
||||
github.com/osquery/osquery-go v0.0.0-20230603132358-d2e851b3991b h1:kPna3NDVHKquM7hGLWcztO6eH+NTbTprHfGKrClGJqk=
|
||||
github.com/osquery/osquery-go v0.0.0-20230603132358-d2e851b3991b/go.mod h1:OSR0OKXZZ+mnt08q14OndgHjJJ9/1koA2dDO3jzYr/I=
|
||||
github.com/osquery/osquery-go v0.0.0-20231006172600-d6f325f636a9 h1:+7IDjPDpcEwVqphCBCi/VWMF6sSSrqzJ3lq09K9cnAU=
|
||||
github.com/osquery/osquery-go v0.0.0-20231006172600-d6f325f636a9/go.mod h1:mLJRc1Go8uP32LRALGvWj2lVJ+hDYyIfxDzVa+C5Yo8=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
@ -1043,11 +999,9 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/peterbourgon/ff/v3 v3.0.0/go.mod h1:UILIFjRH5a/ar8TjXYLTkIvSvekZqPm5Eb/qbGk6CT0=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
@ -1065,7 +1019,6 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
@ -1080,7 +1033,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
@ -1090,7 +1042,6 @@ github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+
|
||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
@ -1111,8 +1062,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
@ -1125,26 +1076,29 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
|
||||
github.com/scjalliance/comshim v0.0.0-20190308082608-cf06d2532c4e/go.mod h1:9Tc1SKnfACJb9N7cw2eyuI6xzy845G7uZONBsi5uPEA=
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sebdah/goldie v1.0.0 h1:9GNhIat69MSlz/ndaBg48vl9dF5fI+NBB6kfOxgfkMc=
|
||||
github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs=
|
||||
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.5.0 h1:oTiNu0QnulMQgN/hLK124wJD/r2f9ZhIUuKIeBsCBT8=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.5.0/go.mod h1:uoCqUC0Ap7jrBSEanxT+SdACYJTVplRXWLkGMuDjXqk=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI=
|
||||
github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE=
|
||||
github.com/shirou/gopsutil/v3 v3.22.8 h1:a4s3hXogo5mE2PfdfJIonDbstO/P+9JszdfhAHSzD9Y=
|
||||
github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI=
|
||||
github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE=
|
||||
github.com/shirou/gopsutil/v3 v3.23.3/go.mod h1:lSBNN6t3+D6W5e5nXTxc8KIMMVxAcS+6IJlffjRRlMU=
|
||||
github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
@ -1169,7 +1123,6 @@ github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY52
|
||||
github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
||||
@ -1186,7 +1139,6 @@ github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
@ -1198,8 +1150,6 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@ -1210,6 +1160,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
@ -1218,9 +1169,8 @@ github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BG
|
||||
github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
|
||||
github.com/theupdateframework/go-tuf v0.5.0 h1:aQ7i9CBw4q9QEZifCaW6G8qGQwoN23XGaZkOA+F50z4=
|
||||
github.com/theupdateframework/go-tuf v0.5.0/go.mod h1:vAqWV3zEs89byeFsAYoh/Q14vJTgJkHwnnRCWBBBINY=
|
||||
github.com/theupdateframework/notary v0.6.1/go.mod h1:MOfgIfmox8s7/7fduvB2xyPPMJCrjRLRizA8OFwpnKY=
|
||||
github.com/theupdateframework/go-tuf v0.5.2 h1:habfDzTmpbzBLIFGWa2ZpVhYvFBoK0C1onC3a4zuPRA=
|
||||
github.com/theupdateframework/go-tuf v0.5.2/go.mod h1:SyMV5kg5n4uEclsyxXJZI2UxPFJNDc4Y+r7wv+MlvTA=
|
||||
github.com/throttled/throttled/v2 v2.8.0 h1:B5VfdM8BE+ClI2Ji238SbNOTWfYcocvuAhgT27lvwrE=
|
||||
github.com/throttled/throttled/v2 v2.8.0/go.mod h1:q1QyZVQXxb2NUfJ+Hjucmlrsrz9s/jt2ilMwSMo7a2I=
|
||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
||||
@ -1230,10 +1180,10 @@ github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj
|
||||
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
|
||||
github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=
|
||||
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
|
||||
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
||||
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
||||
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
||||
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/trivago/tgo v1.0.7 h1:uaWH/XIy9aWYWpjm2CU3RpcqZXmX2ysQ9/Go+d9gyrM=
|
||||
github.com/trivago/tgo v1.0.7/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc=
|
||||
@ -1244,7 +1194,6 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY
|
||||
github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
|
||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli/v2 v2.23.5 h1:xbrU7tAYviSpqeR3X4nEFWUdB/uDZ6DE+HxmRU7Xtyw=
|
||||
github.com/urfave/cli/v2 v2.23.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
@ -1255,7 +1204,6 @@ github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7Fw
|
||||
github.com/vartanbeno/go-reddit/v2 v2.0.0 h1:fxYMqx5lhbmJ3yYRN1nnQC/gecRB3xpUS2BbG7GLpsk=
|
||||
github.com/vartanbeno/go-reddit/v2 v2.0.0/go.mod h1:758/S10hwZSLm43NPtwoNQdZFSg3sjB5745Mwjb0ANI=
|
||||
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad/go.mod h1:Hy8o65+MXnS6EwGElrSRjUzQDLXreJlzYLlWiHtt8hM=
|
||||
github.com/xanzy/go-gitlab v0.50.3 h1:M7ncgNhCN4jaFNyXxarJhCLa9Qi6fdmCxFFhMTQPZiY=
|
||||
github.com/xanzy/go-gitlab v0.50.3/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
@ -1287,8 +1235,6 @@ github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ
|
||||
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
github.com/zwass/kit v0.0.0-20210625184505-ec5b5c5cce9c h1:TWQ2UvXPkhPxI2KmApKBOCaV6yD2N4mlvqFQ/DlPtpQ=
|
||||
github.com/zwass/kit v0.0.0-20210625184505-ec5b5c5cce9c/go.mod h1:OYYulo9tUqRadRLwB0+LE914sa1ui2yL7OrcU3Q/1XY=
|
||||
go.elastic.co/apm/module/apmgorilla/v2 v2.3.0 h1:jHw8N252UTwKTk945+Am8AaawhHC6DWpFVeTXQO8Gko=
|
||||
go.elastic.co/apm/module/apmgorilla/v2 v2.3.0/go.mod h1:2LXDBbVhFf9rF65jZecvl78IZMuvSRldQ+9A/fjfIo0=
|
||||
go.elastic.co/apm/module/apmhttp/v2 v2.3.0 h1:yGZyp26uJXUCfRTwvMmDt1d1jJrHgTBBncZfpYAxR8s=
|
||||
@ -1301,7 +1247,6 @@ go.elastic.co/apm/v2 v2.4.3/go.mod h1:+CiBUdrrAGnGCL9TNx7tQz3BrfYV23L8Ljvotoc87s
|
||||
go.elastic.co/fastjson v1.1.0 h1:3MrGBWWVIxe/xvsbpghtkFoPciPhOCmjsR/HfwEeQR4=
|
||||
go.elastic.co/fastjson v1.1.0/go.mod h1:boNGISWMjQsUPy/t6yqt2/1Wx4YNPSe+mZjlyw9vKKI=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
|
||||
@ -1310,7 +1255,6 @@ go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvS
|
||||
go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@ -1322,23 +1266,23 @@ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.40.0 h1:KToMJH0+5VxWBGtfeluRmWR3wLtE7nP+80YrxNI5FGs=
|
||||
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.40.0/go.mod h1:RK3vgddjxVcF1q7IBVppzG6k2cW/NBnZHQ3X4g+EYBQ=
|
||||
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
|
||||
go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
|
||||
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 h1:/fXHZHGvro6MVqV34fJzDhi7sHGpX3Ej/Qjmfn003ho=
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0/go.mod h1:UFG7EBMRdXyFstOwH028U0sVf+AvukSGhF0g8+dmNG8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 h1:TKf2uAs2ueguzLaxOCBXNpHxfO/aC7PAdDsSH0IbeRQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0/go.mod h1:HrbCVv40OOLTABmOn1ZWty6CHXkU8DK/Urc43tHug70=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 h1:ap+y8RXX3Mu9apKVtOkM6WSFESLM8K3wNQyOU8sWHcc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0/go.mod h1:5w41DY6S9gZrbjuq6Y+753e96WfPha5IcsOSZTtullM=
|
||||
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
|
||||
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I=
|
||||
go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
|
||||
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
|
||||
go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
|
||||
go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
|
||||
go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
|
||||
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
|
||||
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
|
||||
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
|
||||
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
|
||||
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw=
|
||||
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
@ -1389,8 +1333,9 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@ -1405,6 +1350,8 @@ golang.org/x/exp v0.0.0-20230105202349-8879d0199aa3 h1:fJwx88sMf5RXwDwziL0/Mn9Wq
|
||||
golang.org/x/exp v0.0.0-20230105202349-8879d0199aa3/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M=
|
||||
golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@ -1431,6 +1378,7 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -1454,7 +1402,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@ -1497,8 +1444,9 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -1520,8 +1468,8 @@ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
|
||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||
golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
|
||||
golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -1534,13 +1482,13 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -1561,7 +1509,6 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -1573,7 +1520,6 @@ golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -1596,7 +1542,6 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201109165425-215b40eba54c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -1634,32 +1579,34 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211102192858-4dd72447c267/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -1669,10 +1616,13 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@ -1700,7 +1650,6 @@ golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191025023517-2077df36852e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@ -1747,6 +1696,7 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
|
||||
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -1790,14 +1740,13 @@ google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdr
|
||||
google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
|
||||
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
|
||||
google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
|
||||
google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
|
||||
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
|
||||
google.golang.org/api v0.128.0 h1:RjPESny5CnQRn9V6siglged+DZCgfu9l6mO9dkX9VOg=
|
||||
google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
@ -1872,8 +1821,12 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6
|
||||
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd h1:sLpv7bNL1AsX3fdnWh9WVh7ejIzXdOc1RRHGeAmeStU=
|
||||
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
|
||||
google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0=
|
||||
google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a h1:myvhA4is3vrit1a6NZCWBIwN0kNEnX21DJOJX/NvIfI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a h1:a2MQQVoTo96JC9PMGtGBymLp7+/RzpFc2yX/9WfFg1c=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@ -1901,8 +1854,9 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
|
||||
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||
google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
|
||||
google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
@ -1917,8 +1871,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
@ -1928,15 +1882,11 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/dancannon/gorethink.v3 v3.0.5/go.mod h1:GXsi1e3N2OcKhcP6nsYABTiUejbWMFO4GY5a4pEaeEc=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fatih/pool.v2 v2.0.0/go.mod h1:8xVGeu1/2jr2wm5V9SPuMht2H5AEmf5aFMGSQixtjTY=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gorethink/gorethink.v3 v3.0.5/go.mod h1:+3yIIHJUGMBK+wyPH+iN5TP+88ikFDfZdqTlK3Y9q8I=
|
||||
gopkg.in/guregu/null.v3 v3.5.0 h1:xTcasT8ETfMcUHn0zTvIYtQud/9Mx5dJqD554SZct0o=
|
||||
gopkg.in/guregu/null.v3 v3.5.0/go.mod h1:E4tX2Qe3h7QdL+uZ3a0vqvYwKQsRSQKM5V4YltdgH9Y=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.61.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
@ -1983,4 +1933,5 @@ nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4=
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78/go.mod h1:B7Wf0Ya4DHF9Yw+qfZuJijQYkWicqDa+79Ytmmq3Kjg=
|
||||
|
34
orbit/pkg/cryptoinfo/identify.go
Normal file
34
orbit/pkg/cryptoinfo/identify.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Package cryptoinfo is designed to examine keys and certificates on
|
||||
// disk, and return information about them. It is designed to work
|
||||
// with dataflatten, and may eventually it may replace pkg/keyidentifier
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package cryptoinfo
|
||||
|
||||
// identifierSignature is an internal type to denote the identification functions. It's
|
||||
// used to add a small amount of clarity to the array of possible identifiers.
|
||||
type identifierSignature func(data []byte, password string) (results []*KeyInfo, err error)
|
||||
|
||||
var defaultIdentifiers = []identifierSignature{
|
||||
tryP12,
|
||||
tryDer,
|
||||
tryPem,
|
||||
}
|
||||
|
||||
// Identify examines a []byte and attempts to descern what
|
||||
// cryptographic material is contained within.
|
||||
func Identify(data []byte, password string) ([]*KeyInfo, error) {
|
||||
|
||||
// Try the identifiers. Some future work might be to allow
|
||||
// callers to specify identifier order, or to try to discern
|
||||
// it from the file extension. But meanwhile, just try everything.
|
||||
for _, fn := range defaultIdentifiers {
|
||||
res, err := fn(data, password)
|
||||
if err == nil {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If we can't parse anything, return nothing. It's not a fatal error, and it's
|
||||
// somewhat obvious from context that nothing was parsed.
|
||||
return nil, nil
|
||||
}
|
111
orbit/pkg/cryptoinfo/identify_test.go
Normal file
111
orbit/pkg/cryptoinfo/identify_test.go
Normal file
@ -0,0 +1,111 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package cryptoinfo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIdentify(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var tests = []struct {
|
||||
in []string
|
||||
password string
|
||||
expectedCount int
|
||||
expectedError bool
|
||||
expectedSubjects []string
|
||||
}{
|
||||
|
||||
{
|
||||
in: []string{filepath.Join("testdata", "test_crt.pem")},
|
||||
expectedCount: 1,
|
||||
expectedSubjects: []string{"www.example.com"},
|
||||
},
|
||||
{
|
||||
in: []string{filepath.Join("testdata", "test_crt.pem"), filepath.Join("testdata", "test_crt.pem")},
|
||||
expectedCount: 2,
|
||||
expectedSubjects: []string{"www.example.com", "www.example.com"},
|
||||
},
|
||||
{
|
||||
in: []string{filepath.Join("testdata", "test_crt.der")},
|
||||
expectedCount: 1,
|
||||
expectedSubjects: []string{"www.example.com"},
|
||||
},
|
||||
{
|
||||
in: []string{filepath.Join("testdata", "empty")},
|
||||
expectedCount: 0,
|
||||
},
|
||||
{
|
||||
in: []string{filepath.Join("testdata", "sslcerts.pem")},
|
||||
expectedCount: 129,
|
||||
expectedSubjects: []string{
|
||||
"Autoridad de Certificacion Firmaprofesional CIF A62634068",
|
||||
"Chambers of Commerce Root - 2008",
|
||||
"Global Chambersign Root - 2008",
|
||||
"ACCVRAIZ1",
|
||||
"Actalis Authentication Root CA",
|
||||
},
|
||||
},
|
||||
{
|
||||
in: []string{filepath.Join("testdata", "test-unenc.p12")},
|
||||
expectedCount: 2,
|
||||
expectedSubjects: []string{"www.example.com"},
|
||||
},
|
||||
{
|
||||
in: []string{filepath.Join("testdata", "test-enc.p12")}, //password is test123
|
||||
password: "test123",
|
||||
expectedCount: 2,
|
||||
expectedSubjects: []string{"www.example.com"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
|
||||
t.Run(strings.Join(tt.in, ","), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
in := []byte{}
|
||||
for _, file := range tt.in {
|
||||
fileBytes, err := os.ReadFile(file)
|
||||
require.NoError(t, err, "reading input %s for setup", file)
|
||||
in = bytes.Join([][]byte{in, fileBytes}, nil)
|
||||
}
|
||||
|
||||
results, err := Identify(in, tt.password)
|
||||
if tt.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, results, tt.expectedCount)
|
||||
|
||||
// If we have expected subjects, do they match?
|
||||
count := 0
|
||||
for _, returnedCert := range results {
|
||||
// Some things aren't certs, just skep them for the expectedSubject test
|
||||
cert, ok := returnedCert.Data.(*certExtract)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
count++
|
||||
|
||||
// If we don't have any more expected subjects, just break
|
||||
if count > len(tt.expectedSubjects) {
|
||||
break
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.expectedSubjects[count-1], cert.Subject.CommonName)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
119
orbit/pkg/cryptoinfo/info.go
Normal file
119
orbit/pkg/cryptoinfo/info.go
Normal file
@ -0,0 +1,119 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package cryptoinfo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type KeyInfo struct {
|
||||
Type kiType
|
||||
Encoding kiEncoding
|
||||
Data interface{}
|
||||
DataName kiDataNames
|
||||
Error error
|
||||
Headers map[string]string
|
||||
}
|
||||
|
||||
// kiDataNames is an internal type. It's used to help provide uniformity in the returned data.
|
||||
type kiDataNames string
|
||||
|
||||
const (
|
||||
kiCaCertificate kiDataNames = "certificate"
|
||||
kiCertificate = "certificate"
|
||||
kiKey = "key"
|
||||
)
|
||||
|
||||
// kiType is an internal type to denote what an indentified blob is. It is ultimately presented as a string
|
||||
type kiType string
|
||||
|
||||
const (
|
||||
kiCACERTIFICATE kiType = "CA-CERTIFICATE" // Not totally sure what the correct string is here
|
||||
kiCERTIFICATE = "CERTIFICATE"
|
||||
kiKEY = "KEY"
|
||||
)
|
||||
|
||||
// kiType is an internal type to denote what encoding was used. It is ultimately presented as a string
|
||||
type kiEncoding string
|
||||
|
||||
const (
|
||||
kiPEM kiEncoding = "PEM"
|
||||
kiDER = "DER"
|
||||
kiP12 = "P12"
|
||||
)
|
||||
|
||||
func NewKey(encoding kiEncoding) *KeyInfo {
|
||||
return &KeyInfo{
|
||||
DataName: kiKey,
|
||||
Encoding: encoding,
|
||||
Type: kiKEY,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCertificate(encoding kiEncoding) *KeyInfo {
|
||||
return &KeyInfo{
|
||||
DataName: kiCertificate,
|
||||
Encoding: encoding,
|
||||
Type: kiCERTIFICATE,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCaCertificate(encoding kiEncoding) *KeyInfo {
|
||||
return &KeyInfo{
|
||||
DataName: kiCaCertificate,
|
||||
Encoding: encoding,
|
||||
Type: kiCACERTIFICATE,
|
||||
}
|
||||
}
|
||||
|
||||
func NewError(encoding kiEncoding, err error) *KeyInfo {
|
||||
return &KeyInfo{
|
||||
Encoding: encoding,
|
||||
Error: err,
|
||||
}
|
||||
}
|
||||
|
||||
func (ki *KeyInfo) SetHeaders(headers map[string]string) *KeyInfo {
|
||||
ki.Headers = headers
|
||||
return ki
|
||||
}
|
||||
|
||||
func (ki *KeyInfo) SetDataName(name kiDataNames) *KeyInfo {
|
||||
ki.DataName = name
|
||||
return ki
|
||||
}
|
||||
|
||||
func (ki *KeyInfo) SetData(data interface{}, err error) *KeyInfo {
|
||||
ki.Data = data
|
||||
ki.Error = err
|
||||
return ki
|
||||
}
|
||||
|
||||
// MarshalJSON is used by the go json marshaller. Using a custom one here
|
||||
// allows us a high degree of control over the resulting output. For example,
|
||||
// it allows us to use the same struct here to encapsulate both keys and
|
||||
// certificate, and still have somewhat differenciated output
|
||||
func (ki *KeyInfo) MarshalJSON() ([]byte, error) {
|
||||
// this feels somewhat inefficient WRT to allocations and shoving maps around. But it
|
||||
// also feels the simplest way to get consistent behavior without needing to push
|
||||
// the key/value pairs everywhere.
|
||||
ret := map[string]interface{}{
|
||||
"type": ki.Type,
|
||||
"encoding": ki.Encoding,
|
||||
}
|
||||
|
||||
if ki.Error != nil {
|
||||
ret["error"] = ki.Error.Error()
|
||||
} else {
|
||||
if ki.DataName != "" {
|
||||
ret[string(ki.DataName)] = ki.Data
|
||||
} else {
|
||||
ret["error"] = "No data name"
|
||||
}
|
||||
}
|
||||
|
||||
if len(ki.Headers) != 0 {
|
||||
ret["headers"] = ki.Headers
|
||||
}
|
||||
|
||||
return json.Marshal(ret)
|
||||
}
|
100
orbit/pkg/cryptoinfo/parse_certificate.go
Normal file
100
orbit/pkg/cryptoinfo/parse_certificate.go
Normal file
@ -0,0 +1,100 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package cryptoinfo
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type certExtract struct {
|
||||
CRLDistributionPoints []string
|
||||
DNSNames []string
|
||||
EmailAddresses []string
|
||||
ExcludedDNSDomains []string
|
||||
ExcludedEmailAddresses []string
|
||||
ExcludedIPRanges []*net.IPNet
|
||||
ExcludedURIDomains []string
|
||||
IPAddresses []net.IP
|
||||
Issuer pkix.Name
|
||||
IssuerParsed string
|
||||
IssuingCertificateURL []string
|
||||
KeyUsage int
|
||||
KeyUsageParsed []string
|
||||
NotBefore, NotAfter time.Time
|
||||
OCSPServer []string
|
||||
PermittedDNSDomains []string
|
||||
PermittedDNSDomainsCritical bool
|
||||
PermittedEmailAddresses []string
|
||||
PermittedIPRanges []*net.IPNet
|
||||
PermittedURIDomains []string
|
||||
PublicKeyAlgorithm string
|
||||
SerialNumber string
|
||||
SignatureAlgorithm string
|
||||
Subject pkix.Name
|
||||
SubjectParsed string
|
||||
URIs []*url.URL
|
||||
Version int
|
||||
}
|
||||
|
||||
// parseCertificate parses a certificate from a stream of bytes. We use this, instead of a bare x509.ParseCertificate, to handle some
|
||||
// string conversions, and bitfield enumerations.
|
||||
func parseCertificate(certBytes []byte) (interface{}, error) {
|
||||
c, err := x509.ParseCertificate(certBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing certificate: %w", err)
|
||||
}
|
||||
|
||||
return extractCert(c)
|
||||
}
|
||||
|
||||
func extractCert(c *x509.Certificate) (interface{}, error) {
|
||||
return &certExtract{
|
||||
CRLDistributionPoints: c.CRLDistributionPoints,
|
||||
DNSNames: c.DNSNames,
|
||||
EmailAddresses: c.EmailAddresses,
|
||||
IPAddresses: c.IPAddresses,
|
||||
Issuer: c.Issuer,
|
||||
IssuerParsed: c.Issuer.String(),
|
||||
IssuingCertificateURL: c.IssuingCertificateURL,
|
||||
KeyUsage: int(c.KeyUsage),
|
||||
KeyUsageParsed: keyUsageToStrings(c.KeyUsage),
|
||||
NotAfter: c.NotAfter,
|
||||
NotBefore: c.NotBefore,
|
||||
OCSPServer: c.OCSPServer,
|
||||
PublicKeyAlgorithm: c.PublicKeyAlgorithm.String(),
|
||||
SerialNumber: c.SerialNumber.String(),
|
||||
SignatureAlgorithm: c.SignatureAlgorithm.String(),
|
||||
Subject: c.Subject,
|
||||
SubjectParsed: c.Subject.String(),
|
||||
URIs: c.URIs,
|
||||
Version: c.Version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var keyUsageBits = map[x509.KeyUsage]string{
|
||||
x509.KeyUsageDigitalSignature: "Digital Signature",
|
||||
x509.KeyUsageContentCommitment: "Content Commitment",
|
||||
x509.KeyUsageKeyEncipherment: "Key Encipherment",
|
||||
x509.KeyUsageDataEncipherment: "Data Encipherment",
|
||||
x509.KeyUsageKeyAgreement: "Key Agreement",
|
||||
x509.KeyUsageCertSign: "Certificate Sign",
|
||||
x509.KeyUsageCRLSign: "CRL Sign",
|
||||
x509.KeyUsageEncipherOnly: "Encipher Only",
|
||||
x509.KeyUsageDecipherOnly: "Decipher Only",
|
||||
}
|
||||
|
||||
func keyUsageToStrings(k x509.KeyUsage) []string {
|
||||
var usage []string
|
||||
|
||||
for usageBit, usageMeaning := range keyUsageBits {
|
||||
if k&usageBit != 0 {
|
||||
usage = append(usage, usageMeaning)
|
||||
}
|
||||
}
|
||||
|
||||
return usage
|
||||
}
|
0
orbit/pkg/cryptoinfo/testdata/empty
vendored
Normal file
0
orbit/pkg/cryptoinfo/testdata/empty
vendored
Normal file
6306
orbit/pkg/cryptoinfo/testdata/sslcerts.pem
vendored
Normal file
6306
orbit/pkg/cryptoinfo/testdata/sslcerts.pem
vendored
Normal file
File diff suppressed because it is too large
Load Diff
BIN
orbit/pkg/cryptoinfo/testdata/test-enc.p12
vendored
Normal file
BIN
orbit/pkg/cryptoinfo/testdata/test-enc.p12
vendored
Normal file
Binary file not shown.
BIN
orbit/pkg/cryptoinfo/testdata/test-unenc.p12
vendored
Normal file
BIN
orbit/pkg/cryptoinfo/testdata/test-unenc.p12
vendored
Normal file
Binary file not shown.
15
orbit/pkg/cryptoinfo/testdata/test.key
vendored
Normal file
15
orbit/pkg/cryptoinfo/testdata/test.key
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXAIBAAKBgQC/ISgV6QxEurKeU+N4gtcyIBxw8ztUWZVZll6yh+BXcSrUGvz1
|
||||
JC5nas8Mbdk7QNkwka1rrH4MEJ7EnmN35ffmzO6j09p9RFy9Ez1AmMtF7/AYO66H
|
||||
rRH/BS+L+fq3iBlxjZEYjijWEHdfqIpactADbnqj8Y0UXXxjyY6qx9xUwwIDAQAB
|
||||
AoGAHs42SsnEK3O4BGLa//p+utqIGwBpKKBDvSvKWZYi55Ua5RLwgIZzYEHL22H9
|
||||
KFq8ZuKkA/3KVyF6pZAt0g5j1S0Bl80p9pcd836Ym4Y6N0SQ0mEeFWnpzTZ3n+2J
|
||||
kXaPBf1P386nmRpyxFvar8BtqbqSSRGbqsrbayWKxYDh2ukCQQDoDhP4GSEfzdTn
|
||||
MVgdDpBtUG5x6PHW+JDPb53FMbslzwIYBoFt0ouLFdtMI/jKfmD7m+jOInlp9t6i
|
||||
S+/nc3Z1AkEA0tn/r+UKZPaSXW+ibCLAvJGVbom8IOjDMo34sj2PDepBZ0tO9tw4
|
||||
+S51Ggq7coe5d3+p6NJAP8kjHx6X/F5nVwJAHOSj1+BJH4yhVafvMK7/jJzXI5e9
|
||||
hOauISXknwjyJGMB/7vPobz1Yvv1siVIdO4HZUykUAY619bFIbASzt6xgQJAfpWT
|
||||
9FSMPfrt+hxYJZVjopHAZaFZCWTUM1iact+UL6VwaIQEvx2NMsPaV60TxfmHth81
|
||||
sWnwWpr1c+xZEJDYdwJBAN0GqCFHpBAiDN6dmkSGS2R8Lk5CIuolWN5awuWSO02p
|
||||
wOAISlrer0ltgsI5+jCaSubG8fepXo6sDXiDi+NTlSs=
|
||||
-----END RSA PRIVATE KEY-----
|
BIN
orbit/pkg/cryptoinfo/testdata/test_crt.der
vendored
Normal file
BIN
orbit/pkg/cryptoinfo/testdata/test_crt.der
vendored
Normal file
Binary file not shown.
13
orbit/pkg/cryptoinfo/testdata/test_crt.pem
vendored
Normal file
13
orbit/pkg/cryptoinfo/testdata/test_crt.pem
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB3zCCAUgCCQD+JONnvOs4tTANBgkqhkiG9w0BAQsFADA0MQswCQYDVQQGEwJV
|
||||
UzELMAkGA1UECAwCTUExGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0yMjAy
|
||||
MDMxNTU1MzFaFw0yMjAzMDUxNTU1MzFaMDQxCzAJBgNVBAYTAlVTMQswCQYDVQQI
|
||||
DAJNQTEYMBYGA1UEAwwPd3d3LmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQC/ISgV6QxEurKeU+N4gtcyIBxw8ztUWZVZll6yh+BXcSrUGvz1
|
||||
JC5nas8Mbdk7QNkwka1rrH4MEJ7EnmN35ffmzO6j09p9RFy9Ez1AmMtF7/AYO66H
|
||||
rRH/BS+L+fq3iBlxjZEYjijWEHdfqIpactADbnqj8Y0UXXxjyY6qx9xUwwIDAQAB
|
||||
MA0GCSqGSIb3DQEBCwUAA4GBAI55YBLrEaaRwIxWhmbLZ1gB+MkliVa/OV8FOnFc
|
||||
Q/bnfP0L7gmN3kuDV9DD2QLFz/0ElRWftBlxnCo1/OqlGA+XEYFLmaq2icROW0N8
|
||||
4JUDVgYLaVI5QJnUQCgNOZXq/mPfFHQ9x50uXpvNtdTJkis0F1EJwdqGcB5hbYwH
|
||||
2+YR
|
||||
-----END CERTIFICATE-----
|
11
orbit/pkg/cryptoinfo/try_der.go
Normal file
11
orbit/pkg/cryptoinfo/try_der.go
Normal file
@ -0,0 +1,11 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package cryptoinfo
|
||||
|
||||
func tryDer(data []byte, _password string) ([]*KeyInfo, error) {
|
||||
cert, err := parseCertificate(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []*KeyInfo{NewCertificate(kiDER).SetData(cert, err)}, nil
|
||||
}
|
29
orbit/pkg/cryptoinfo/try_p12.go
Normal file
29
orbit/pkg/cryptoinfo/try_p12.go
Normal file
@ -0,0 +1,29 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package cryptoinfo
|
||||
|
||||
import (
|
||||
p12 "software.sslmate.com/src/go-pkcs12"
|
||||
)
|
||||
|
||||
func tryP12(data []byte, password string) ([]*KeyInfo, error) {
|
||||
privateKey, cert, caCerts, err := p12.DecodeChain(data, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results := []*KeyInfo{}
|
||||
|
||||
if privateKey != nil {
|
||||
results = append(results, NewKey(kiP12))
|
||||
}
|
||||
|
||||
if cert != nil {
|
||||
results = append(results, NewCertificate(kiP12).SetData(extractCert(cert)))
|
||||
}
|
||||
|
||||
for _, c := range caCerts {
|
||||
results = append(results, NewCaCertificate(kiP12).SetData(extractCert(c)))
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
40
orbit/pkg/cryptoinfo/try_pem.go
Normal file
40
orbit/pkg/cryptoinfo/try_pem.go
Normal file
@ -0,0 +1,40 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package cryptoinfo
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func tryPem(pemBytes []byte, _password string) ([]*KeyInfo, error) {
|
||||
expanded := []*KeyInfo{}
|
||||
|
||||
// Loop over the bytes, reading pem blocks
|
||||
var block *pem.Block
|
||||
for len(pemBytes) > 0 {
|
||||
block, pemBytes = pem.Decode(pemBytes)
|
||||
if block == nil {
|
||||
// When pem.Decode finds no pem, it returns a nil block, and the input as rest.
|
||||
// In that case, we stop parsing, as anything else would land in an infinite loop
|
||||
break
|
||||
}
|
||||
|
||||
expanded = append(expanded, expandPem(block))
|
||||
}
|
||||
|
||||
if len(expanded) == 0 {
|
||||
return nil, errors.New("No pem decoded")
|
||||
}
|
||||
|
||||
return expanded, nil
|
||||
}
|
||||
|
||||
func expandPem(block *pem.Block) *KeyInfo {
|
||||
switch block.Type {
|
||||
case "CERTIFICATE":
|
||||
return NewCertificate(kiPEM).SetHeaders(block.Headers).SetData(parseCertificate(block.Bytes))
|
||||
}
|
||||
|
||||
return NewError(kiPEM, fmt.Errorf("Unknown block type: %s", block.Type))
|
||||
}
|
531
orbit/pkg/dataflatten/flatten.go
Normal file
531
orbit/pkg/dataflatten/flatten.go
Normal file
@ -0,0 +1,531 @@
|
||||
// Package dataflatten contains tools to flatten complex data
|
||||
// structures.
|
||||
//
|
||||
// On macOS, many plists use an array of maps, these can be tricky to
|
||||
// filter. This package knows how to flatten that structure, as well
|
||||
// as rewriting it as a nested array, or filtering it. It is akin to
|
||||
// xpath, though simpler.
|
||||
//
|
||||
// This tool works primarily through string interfaces, so type
|
||||
// information may be lost.
|
||||
//
|
||||
// # Query Syntax
|
||||
//
|
||||
// The query syntax handles both filtering and basic rewriting. It is
|
||||
// not perfect. The idea behind it, is that we descend through an data
|
||||
// structure, specifying what matches at each level.
|
||||
//
|
||||
// Each level of query can do:
|
||||
// - specify a filter, this is a simple string match with wildcard support. (prefix and/or postfix, but not infix)
|
||||
// - If the data is an array, specify an index
|
||||
// - For array-of-maps, specify a key to rewrite as a nested map
|
||||
//
|
||||
// Each query term has 3 parts: [#]string[=>kvmatch]
|
||||
//
|
||||
// 1. An optional `#` This denotes a key to rewrite an array-of-maps with
|
||||
//
|
||||
// 2. A search term. If this is an integer, it is interpreted as an array index.
|
||||
//
|
||||
// 3. a key/value match string. For a map, this is to match the value of a key.
|
||||
//
|
||||
// Some examples:
|
||||
// * data/users Return everything under { data: { users: { ... } } }
|
||||
// * data/users/0 Return the first item in the users array
|
||||
// * data/users/name=>A* Return users whose name starts with "A"
|
||||
// * data/users/#id Return the users, and rewrite the users array to be a map with the id as the key
|
||||
//
|
||||
// See the test suite for extensive examples.
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflatten
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/groob/plist"
|
||||
|
||||
howett "howett.net/plist"
|
||||
)
|
||||
|
||||
// Flattener is an interface to flatten complex, nested, data
|
||||
// structures. It recurses through them, and returns a simplified
|
||||
// form. At the simplest level, this rewrites:
|
||||
//
|
||||
// { foo: { bar: { baz: 1 } } }
|
||||
//
|
||||
// To:
|
||||
//
|
||||
// [ { path: foo/bar/baz, value: 1 } ]
|
||||
//
|
||||
// It can optionally filtering and rewriting.
|
||||
type Flattener struct {
|
||||
debugLogging bool
|
||||
expandNestedPlist bool
|
||||
includeNestedRaw bool
|
||||
includeNils bool
|
||||
logger log.Logger
|
||||
query []string
|
||||
queryKeyDenoter string
|
||||
queryWildcard string
|
||||
rows []Row
|
||||
}
|
||||
|
||||
type FlattenOpts func(*Flattener)
|
||||
|
||||
// IncludeNulls indicates that Flatten should return null values,
|
||||
// instead of skipping over them.
|
||||
func IncludeNulls() FlattenOpts {
|
||||
return func(fl *Flattener) {
|
||||
fl.includeNils = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithNestedPlist indicates that nested plists should be expanded
|
||||
func WithNestedPlist() FlattenOpts {
|
||||
return func(fl *Flattener) {
|
||||
fl.expandNestedPlist = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger sets the logger to use
|
||||
func WithLogger(logger log.Logger) FlattenOpts {
|
||||
if logger == nil {
|
||||
return func(_ *Flattener) {}
|
||||
}
|
||||
|
||||
return func(fl *Flattener) {
|
||||
fl.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
// WithDebugLogging enables debug logging. With debug logs,
|
||||
// dataflatten is very verbose. This can overwhelm the other launcher
|
||||
// logs. As we're not generally debugging this library, the default is
|
||||
// to not enable debug logging.
|
||||
func WithDebugLogging() FlattenOpts {
|
||||
return func(fl *Flattener) {
|
||||
fl.debugLogging = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithQuery Specifies a query to flatten with. This is used both for
|
||||
// re-writing arrays into maps, and for filtering. See "Query
|
||||
// Specification" for docs.
|
||||
func WithQuery(q []string) FlattenOpts {
|
||||
if q == nil || len(q) == 0 || (len(q) == 1 && q[0] == "") {
|
||||
return func(_ *Flattener) {}
|
||||
}
|
||||
|
||||
return func(fl *Flattener) {
|
||||
fl.query = q
|
||||
}
|
||||
}
|
||||
|
||||
// Flatten is the entry point to the Flattener functionality.
|
||||
func Flatten(data interface{}, opts ...FlattenOpts) ([]Row, error) {
|
||||
fl := &Flattener{
|
||||
rows: []Row{},
|
||||
logger: log.NewNopLogger(),
|
||||
queryWildcard: `*`,
|
||||
queryKeyDenoter: `#`,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(fl)
|
||||
}
|
||||
|
||||
if !fl.debugLogging {
|
||||
fl.logger = level.NewFilter(fl.logger, level.AllowInfo())
|
||||
}
|
||||
|
||||
if err := fl.descend([]string{}, data, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fl.rows, nil
|
||||
}
|
||||
|
||||
// descend recurses through a given data structure flattening along the way.
|
||||
func (fl *Flattener) descend(path []string, data interface{}, depth int) error {
|
||||
queryTerm, isQueryMatched := fl.queryAtDepth(depth)
|
||||
|
||||
logger := log.With(fl.logger,
|
||||
"caller", "descend",
|
||||
"depth", depth,
|
||||
"rows-so-far", len(fl.rows),
|
||||
"query", queryTerm,
|
||||
"path", strings.Join(path, "/"),
|
||||
)
|
||||
|
||||
switch v := data.(type) {
|
||||
case []interface{}:
|
||||
for i, e := range v {
|
||||
pathKey := strconv.Itoa(i)
|
||||
level.Debug(logger).Log("msg", "checking an array", "indexStr", pathKey)
|
||||
|
||||
// If the queryTerm starts with
|
||||
// queryKeyDenoter, then we want to rewrite
|
||||
// the path based on it. Note that this does
|
||||
// no sanity checking. Multiple values will
|
||||
// re-write. If the value isn't there, you get
|
||||
// nothing. Etc.
|
||||
//
|
||||
// keyName == "name"
|
||||
// keyValue == "alex" (need to test this againsty queryTerm
|
||||
// pathKey == What we descend with
|
||||
if strings.HasPrefix(queryTerm, fl.queryKeyDenoter) {
|
||||
keyQuery := strings.SplitN(strings.TrimPrefix(queryTerm, fl.queryKeyDenoter), "=>", 2)
|
||||
keyName := keyQuery[0]
|
||||
|
||||
innerlogger := log.With(logger, "arraykeyname", keyName)
|
||||
level.Debug(logger).Log("msg", "attempting to coerce array into map")
|
||||
|
||||
e, ok := e.(map[string]interface{})
|
||||
if !ok {
|
||||
level.Debug(innerlogger).Log("msg", "can't coerce into map")
|
||||
continue
|
||||
}
|
||||
|
||||
// Is keyName in this array?
|
||||
val, ok := e[keyName]
|
||||
if !ok {
|
||||
level.Debug(innerlogger).Log("msg", "keyName not in map")
|
||||
continue
|
||||
}
|
||||
|
||||
pathKey, ok = val.(string)
|
||||
if !ok {
|
||||
level.Debug(innerlogger).Log("msg", "can't coerce pathKey val into string")
|
||||
continue
|
||||
}
|
||||
|
||||
// Looks good to descend. we're overwritten both e and pathKey. Exit this conditional.
|
||||
}
|
||||
|
||||
if !(isQueryMatched || fl.queryMatchArrayElement(e, i, queryTerm)) {
|
||||
level.Debug(logger).Log("msg", "query not matched")
|
||||
continue
|
||||
}
|
||||
|
||||
if err := fl.descend(append(path, pathKey), e, depth+1); err != nil {
|
||||
return fmt.Errorf("flattening array: %w", err)
|
||||
}
|
||||
}
|
||||
case map[string]interface{}:
|
||||
level.Debug(logger).Log("msg", "checking a map")
|
||||
for k, e := range v {
|
||||
// Check that the key name matches. If not, skip this entire
|
||||
// branch of the map
|
||||
if !(isQueryMatched || fl.queryMatchString(k, queryTerm)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := fl.descend(append(path, k), e, depth+1); err != nil {
|
||||
return fmt.Errorf("flattening map: %w", err)
|
||||
}
|
||||
}
|
||||
case []map[string]interface{}:
|
||||
level.Debug(logger).Log("msg", "checking an array of maps")
|
||||
for i, e := range v {
|
||||
if err := fl.descend(append(path, strconv.Itoa(i)), e, depth+1); err != nil {
|
||||
return fmt.Errorf("flattening array of maps: %w", err)
|
||||
}
|
||||
}
|
||||
case nil:
|
||||
// Because we want to filter nils out, we do _not_ examine isQueryMatched here
|
||||
if !(fl.queryMatchNil(queryTerm)) {
|
||||
level.Debug(logger).Log("msg", "query not matched")
|
||||
return nil
|
||||
}
|
||||
fl.rows = append(fl.rows, NewRow(path, ""))
|
||||
case string:
|
||||
return fl.descendMaybePlist(path, []byte(v), depth)
|
||||
case []byte:
|
||||
// Most string like data comes in this way
|
||||
return fl.descendMaybePlist(path, v, depth)
|
||||
default:
|
||||
if err := fl.handleStringLike(logger, path, v, depth); err != nil {
|
||||
return fmt.Errorf("flattening at path %v: %w", path, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleStringLike is called when we finally have an object we think
|
||||
// can be converted to a string. It uses the depth to compare against
|
||||
// the query, and returns a stringify'ed value
|
||||
func (fl *Flattener) handleStringLike(logger log.Logger, path []string, v interface{}, depth int) error {
|
||||
queryTerm, isQueryMatched := fl.queryAtDepth(depth)
|
||||
|
||||
stringValue, err := stringify(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !(isQueryMatched || fl.queryMatchString(stringValue, queryTerm)) {
|
||||
level.Debug(logger).Log("msg", "query not matched")
|
||||
return nil
|
||||
}
|
||||
|
||||
fl.rows = append(fl.rows, NewRow(path, stringValue))
|
||||
return nil
|
||||
}
|
||||
|
||||
// descendMaybePlist optionally tries to decode []byte data as an
|
||||
// embedded plist. In the case of failures, it falls back to treating
|
||||
// it like a plain string.
|
||||
func (fl *Flattener) descendMaybePlist(path []string, data []byte, depth int) error {
|
||||
logger := log.With(fl.logger,
|
||||
"caller", "descendMaybePlist",
|
||||
"depth", depth,
|
||||
"rows-so-far", len(fl.rows),
|
||||
"path", strings.Join(path, "/"),
|
||||
)
|
||||
|
||||
// Skip if we're not expanding nested plists
|
||||
if !fl.expandNestedPlist {
|
||||
return fl.handleStringLike(logger, path, data, depth)
|
||||
}
|
||||
|
||||
// Skip if this doesn't look like a plist.
|
||||
if !isPlist(data) {
|
||||
return fl.handleStringLike(logger, path, data, depth)
|
||||
}
|
||||
|
||||
// Looks like a plist. Try parsing it
|
||||
level.Debug(logger).Log("msg", "Parsing inner plist")
|
||||
|
||||
var innerData interface{}
|
||||
|
||||
if err := plist.Unmarshal(data, &innerData); err != nil {
|
||||
level.Info(logger).Log("msg", "plist parsing failed", "err", err)
|
||||
return fl.handleStringLike(logger, path, data, depth)
|
||||
}
|
||||
|
||||
// have a parsed plist. Descend and return from here.
|
||||
if fl.includeNestedRaw {
|
||||
if err := fl.handleStringLike(logger, append(path, "_raw"), data, depth); err != nil {
|
||||
level.Error(logger).Log("msg", "Failed to add _raw key", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := fl.descend(path, innerData, depth); err != nil {
|
||||
return fmt.Errorf("flattening plist data: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fl *Flattener) queryMatchNil(queryTerm string) bool {
|
||||
// TODO: If needed, we could use queryTerm for optional nil filtering
|
||||
return fl.includeNils
|
||||
}
|
||||
|
||||
// queryMatchArrayElement matches arrays. This one is magic.
|
||||
//
|
||||
// Syntax:
|
||||
//
|
||||
// #i -- Match index i. For example `#0`
|
||||
// k=>queryTerm -- If this is a map, it should have key k, that matches queryTerm
|
||||
//
|
||||
// We use `=>` as something that is reasonably intuitive, and not very
|
||||
// likely to occur on it's own. Unfortunately, `==` shows up in base64
|
||||
func (fl *Flattener) queryMatchArrayElement(data interface{}, arrIndex int, queryTerm string) bool {
|
||||
logger := log.With(fl.logger,
|
||||
"caller", "queryMatchArrayElement",
|
||||
"rows-so-far", len(fl.rows),
|
||||
"query", queryTerm,
|
||||
"arrIndex", arrIndex,
|
||||
)
|
||||
|
||||
// strip off the key re-write denotation before trying to match
|
||||
queryTerm = strings.TrimPrefix(queryTerm, fl.queryKeyDenoter)
|
||||
|
||||
if queryTerm == fl.queryWildcard {
|
||||
return true
|
||||
}
|
||||
|
||||
// If the queryTerm is an int, then we expect to match the index
|
||||
if queryIndex, err := strconv.Atoi(queryTerm); err == nil {
|
||||
level.Debug(logger).Log("msg", "using numeric index comparison")
|
||||
return queryIndex == arrIndex
|
||||
}
|
||||
|
||||
level.Debug(logger).Log("msg", "checking data type")
|
||||
|
||||
switch dataCasted := data.(type) {
|
||||
case []interface{}:
|
||||
// fails. We can't match an array that has arrays as elements. Use a wildcard
|
||||
return false
|
||||
case map[string]interface{}:
|
||||
kvQuery := strings.SplitN(queryTerm, "=>", 2)
|
||||
|
||||
// If this is one long, then we're testing for whether or not there's a key with this name,
|
||||
if len(kvQuery) == 1 {
|
||||
_, ok := dataCasted[kvQuery[0]]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Else see if the value matches
|
||||
for k, v := range dataCasted {
|
||||
// Since this needs to check against _every_
|
||||
// member, return true. Or fall through to the
|
||||
// false.
|
||||
if fl.queryMatchString(k, kvQuery[0]) && fl.queryMatchStringify(v, kvQuery[1]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
// non-iterable. stringify and be done
|
||||
return fl.queryMatchStringify(dataCasted, queryTerm)
|
||||
}
|
||||
}
|
||||
|
||||
func (fl *Flattener) queryMatchStringify(data interface{}, queryTerm string) bool {
|
||||
// strip off the key re-write denotation before trying to match
|
||||
queryTerm = strings.TrimPrefix(queryTerm, fl.queryKeyDenoter)
|
||||
|
||||
if queryTerm == fl.queryWildcard {
|
||||
return true
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
return fl.queryMatchNil(queryTerm)
|
||||
}
|
||||
|
||||
stringValue, err := stringify(data)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return fl.queryMatchString(stringValue, queryTerm)
|
||||
|
||||
}
|
||||
|
||||
func (fl *Flattener) queryMatchString(v, queryTerm string) bool {
|
||||
if queryTerm == fl.queryWildcard {
|
||||
return true
|
||||
}
|
||||
|
||||
// Some basic string manipulations to handle prefix and suffix operations
|
||||
switch {
|
||||
case strings.HasPrefix(queryTerm, fl.queryWildcard) && strings.HasSuffix(queryTerm, fl.queryWildcard):
|
||||
queryTerm = strings.TrimPrefix(queryTerm, fl.queryWildcard)
|
||||
queryTerm = strings.TrimSuffix(queryTerm, fl.queryWildcard)
|
||||
return strings.Contains(v, queryTerm)
|
||||
|
||||
case strings.HasPrefix(queryTerm, fl.queryWildcard):
|
||||
queryTerm = strings.TrimPrefix(queryTerm, fl.queryWildcard)
|
||||
return strings.HasSuffix(v, queryTerm)
|
||||
|
||||
case strings.HasSuffix(queryTerm, fl.queryWildcard):
|
||||
queryTerm = strings.TrimSuffix(queryTerm, fl.queryWildcard)
|
||||
return strings.HasPrefix(v, queryTerm)
|
||||
}
|
||||
|
||||
return v == queryTerm
|
||||
}
|
||||
|
||||
// queryAtDepth returns the query parameter for a given depth, and
|
||||
// boolean indicating we've run out of queries. If we've run out of
|
||||
// queries, than we can start checking, everything is a match.
|
||||
func (fl *Flattener) queryAtDepth(depth int) (string, bool) {
|
||||
// if we're nil, there's an implied wildcard
|
||||
//
|
||||
// This works because:
|
||||
// []string is len 0, and nil
|
||||
// []string{} is len 0, but not nil
|
||||
if fl.query == nil {
|
||||
return fl.queryWildcard, true
|
||||
}
|
||||
|
||||
// If there's no query for this depth, then there's an implied
|
||||
// wildcard. This allows the query to specify prefixes.
|
||||
if depth+1 > len(fl.query) {
|
||||
return fl.queryWildcard, true
|
||||
}
|
||||
|
||||
q := fl.query[depth]
|
||||
|
||||
return q, q == fl.queryWildcard
|
||||
}
|
||||
|
||||
// stringify takes an arbitrary piece of data, and attempst to coerce
|
||||
// it into a string.
|
||||
func stringify(data interface{}) (string, error) {
|
||||
switch v := data.(type) {
|
||||
case nil:
|
||||
return "", nil
|
||||
case string:
|
||||
return v, nil
|
||||
case []byte:
|
||||
s := string(v)
|
||||
if utf8.ValidString(s) {
|
||||
return s, nil
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(v), nil
|
||||
case uint8:
|
||||
return strconv.FormatUint(uint64(v), 10), nil
|
||||
case uint16:
|
||||
return strconv.FormatUint(uint64(v), 10), nil
|
||||
case uint32:
|
||||
return strconv.FormatUint(uint64(v), 10), nil
|
||||
case uint64:
|
||||
return strconv.FormatUint(v, 10), nil
|
||||
case float32:
|
||||
return strconv.FormatFloat(float64(v), 'f', -1, 32), nil
|
||||
case float64:
|
||||
return strconv.FormatFloat(v, 'f', -1, 64), nil
|
||||
case int:
|
||||
return strconv.Itoa(v), nil
|
||||
case int8:
|
||||
return strconv.FormatInt(int64(v), 10), nil
|
||||
case int16:
|
||||
return strconv.FormatInt(int64(v), 10), nil
|
||||
case int32:
|
||||
return strconv.FormatInt(int64(v), 10), nil
|
||||
case int64:
|
||||
return strconv.FormatInt(v, 10), nil
|
||||
case bool:
|
||||
return strconv.FormatBool(v), nil
|
||||
case time.Time:
|
||||
return strconv.FormatInt(v.Unix(), 10), nil
|
||||
case howett.UID:
|
||||
return strconv.FormatUint(uint64(v), 10), nil
|
||||
case fmt.Stringer:
|
||||
return v.String(), nil
|
||||
default:
|
||||
// spew.Dump(data)
|
||||
return "", fmt.Errorf("unknown type on %v", data)
|
||||
}
|
||||
}
|
||||
|
||||
// isPlist returns whether or not something looks like it might be a
|
||||
// plist. It uses Contains, instead of HasPrefix, as some encodings
|
||||
// have a leading character.
|
||||
func isPlist(data []byte) bool {
|
||||
var dataPrefix []byte
|
||||
if len(data) <= 30 {
|
||||
dataPrefix = data
|
||||
} else {
|
||||
dataPrefix = data[0:30]
|
||||
}
|
||||
|
||||
if bytes.Contains(dataPrefix, []byte("bplist0")) {
|
||||
return true
|
||||
}
|
||||
|
||||
if bytes.Contains(dataPrefix, []byte(`xml version="1.0"`)) && bytes.Contains(data, []byte(`<!DOCTYPE plist PUBLIC`)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
625
orbit/pkg/dataflatten/flatten_test.go
Normal file
625
orbit/pkg/dataflatten/flatten_test.go
Normal file
@ -0,0 +1,625 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflatten
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type flattenTestCase struct {
|
||||
in string
|
||||
out []Row
|
||||
options []FlattenOpts
|
||||
comment string
|
||||
err bool
|
||||
}
|
||||
|
||||
func TestFlatten_Complex2(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dataRaw, err := os.ReadFile(filepath.Join("testdata", "complex2.json"))
|
||||
require.NoError(t, err, "reading file")
|
||||
var dataIn interface{}
|
||||
require.NoError(t, json.Unmarshal(dataRaw, &dataIn), "unmarshalling json")
|
||||
|
||||
var tests = []flattenTestCase{
|
||||
{
|
||||
out: []Row{
|
||||
{Path: []string{"addons", "0", "bool1"}, Value: "true"},
|
||||
{Path: []string{"addons", "0", "nest2", "0", "string2"}, Value: "foo"},
|
||||
{Path: []string{"addons", "0", "nest3", "string6"}, Value: "null"},
|
||||
{Path: []string{"addons", "0", "nest3", "string7"}, Value: "A Very Long Sentence"},
|
||||
{Path: []string{"addons", "0", "nest3", "string8"}, Value: "short"},
|
||||
{Path: []string{"addons", "0", "string1"}, Value: "hello"},
|
||||
},
|
||||
},
|
||||
{
|
||||
out: []Row{
|
||||
{Path: []string{"addons", "0", "bool1"}, Value: "true"},
|
||||
{Path: []string{"addons", "0", "nest2", "0", "null3"}, Value: ""},
|
||||
{Path: []string{"addons", "0", "nest2", "0", "null4"}, Value: ""},
|
||||
{Path: []string{"addons", "0", "nest2", "0", "string2"}, Value: "foo"},
|
||||
{Path: []string{"addons", "0", "nest3", "string3"}, Value: ""},
|
||||
{Path: []string{"addons", "0", "nest3", "string4"}, Value: ""},
|
||||
{Path: []string{"addons", "0", "nest3", "string5"}, Value: ""},
|
||||
{Path: []string{"addons", "0", "nest3", "string6"}, Value: "null"},
|
||||
{Path: []string{"addons", "0", "nest3", "string7"}, Value: "A Very Long Sentence"},
|
||||
{Path: []string{"addons", "0", "nest3", "string8"}, Value: "short"},
|
||||
{Path: []string{"addons", "0", "null1"}, Value: ""},
|
||||
{Path: []string{"addons", "0", "null2"}, Value: ""},
|
||||
{Path: []string{"addons", "0", "string1"}, Value: "hello"},
|
||||
},
|
||||
options: []FlattenOpts{IncludeNulls()},
|
||||
comment: "includes null",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.comment, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual, err := Flatten(dataIn, tt.options...)
|
||||
testFlattenCase(t, tt, actual, err)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFlatten_NestingBug(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dataRaw, err := os.ReadFile(filepath.Join("testdata", "nested.json"))
|
||||
require.NoError(t, err, "reading file")
|
||||
var dataIn interface{}
|
||||
require.NoError(t, json.Unmarshal(dataRaw, &dataIn), "unmarshalling json")
|
||||
|
||||
var tests = []flattenTestCase{
|
||||
{
|
||||
out: []Row{
|
||||
{Path: []string{"addons", "0", "name"}, Value: "Nested Strings"},
|
||||
{Path: []string{"addons", "0", "nest1", "string3"}, Value: "string3"},
|
||||
{Path: []string{"addons", "0", "nest1", "string4"}, Value: "string4"},
|
||||
{Path: []string{"addons", "0", "nest1", "string5"}, Value: "string5"},
|
||||
{Path: []string{"addons", "0", "nest1", "string6"}, Value: "string6"},
|
||||
},
|
||||
},
|
||||
{
|
||||
out: []Row{
|
||||
{Path: []string{"addons", "0", "name"}, Value: "Nested Strings"},
|
||||
{Path: []string{"addons", "0", "nest1", "string1"}, Value: ""},
|
||||
{Path: []string{"addons", "0", "nest1", "string2"}, Value: ""},
|
||||
{Path: []string{"addons", "0", "nest1", "string3"}, Value: "string3"},
|
||||
{Path: []string{"addons", "0", "nest1", "string4"}, Value: "string4"},
|
||||
{Path: []string{"addons", "0", "nest1", "string5"}, Value: "string5"},
|
||||
{Path: []string{"addons", "0", "nest1", "string6"}, Value: "string6"},
|
||||
},
|
||||
options: []FlattenOpts{IncludeNulls()},
|
||||
comment: "includes null",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.comment, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual, err := Flatten(dataIn, tt.options...)
|
||||
testFlattenCase(t, tt, actual, err)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFlatten_Jsonl_Complex(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Do the unmarshaling here, so we don't keep doing it again and again
|
||||
dataRaw, err := os.ReadFile(filepath.Join("testdata", "animals.jsonl"))
|
||||
require.NoError(t, err, "reading file")
|
||||
|
||||
// We do a bunch of tests to select this user. So we'll pull
|
||||
// this out here and make the testcases more DRY
|
||||
testdataUser0 := []Row{
|
||||
{Path: []string{"2", "users", "0", "favorites", "0"}, Value: "ants"},
|
||||
{Path: []string{"2", "users", "0", "name"}, Value: "Alex Aardvark"},
|
||||
{Path: []string{"2", "users", "0", "uuid"}, Value: "abc123"},
|
||||
{Path: []string{"2", "users", "0", "id"}, Value: "1"},
|
||||
}
|
||||
|
||||
var tests = []flattenTestCase{
|
||||
{
|
||||
out: []Row{
|
||||
{Path: []string{"0", "metadata", "testing"}, Value: "true"},
|
||||
{Path: []string{"0", "metadata", "version"}, Value: "1.0.1"},
|
||||
{Path: []string{"1", "system"}, Value: "users demo"},
|
||||
{Path: []string{"2", "users", "0", "favorites", "0"}, Value: "ants"},
|
||||
{Path: []string{"2", "users", "0", "id"}, Value: "1"},
|
||||
{Path: []string{"2", "users", "0", "name"}, Value: "Alex Aardvark"},
|
||||
{Path: []string{"2", "users", "0", "uuid"}, Value: "abc123"},
|
||||
{Path: []string{"2", "users", "1", "favorites", "0"}, Value: "mice"},
|
||||
{Path: []string{"2", "users", "1", "favorites", "1"}, Value: "birds"},
|
||||
{Path: []string{"2", "users", "1", "id"}, Value: "2"},
|
||||
{Path: []string{"2", "users", "1", "name"}, Value: "Bailey Bobcat"},
|
||||
{Path: []string{"2", "users", "1", "uuid"}, Value: "def456"},
|
||||
{Path: []string{"2", "users", "2", "favorites", "0"}, Value: "seeds"},
|
||||
{Path: []string{"2", "users", "2", "id"}, Value: "3"},
|
||||
{Path: []string{"2", "users", "2", "name"}, Value: "Cam Chipmunk"},
|
||||
{Path: []string{"2", "users", "2", "uuid"}, Value: "ghi789"},
|
||||
{Path: []string{"3", "0"}, Value: "array-item-A"},
|
||||
{Path: []string{"3", "1"}, Value: "array-item-B"},
|
||||
{Path: []string{"3", "2"}, Value: "array-item-C"},
|
||||
},
|
||||
comment: "all together",
|
||||
},
|
||||
{
|
||||
comment: "query metadata",
|
||||
options: []FlattenOpts{WithQuery([]string{"*", "metadata"})},
|
||||
out: []Row{
|
||||
{Path: []string{"0", "metadata", "testing"}, Value: "true"},
|
||||
{Path: []string{"0", "metadata", "version"}, Value: "1.0.1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
comment: "array by #",
|
||||
options: []FlattenOpts{WithQuery([]string{"*", "users", "0"})},
|
||||
out: testdataUser0,
|
||||
},
|
||||
{
|
||||
comment: "array by id value",
|
||||
options: []FlattenOpts{WithQuery([]string{"*", "users", "id=>1"})},
|
||||
out: testdataUser0,
|
||||
},
|
||||
{
|
||||
comment: "array by uuid",
|
||||
options: []FlattenOpts{WithQuery([]string{"*", "users", "uuid=>abc123"})},
|
||||
out: testdataUser0,
|
||||
},
|
||||
{
|
||||
comment: "array by name with suffix wildcard",
|
||||
options: []FlattenOpts{WithQuery([]string{"*", "users", "name=>Al*"})},
|
||||
out: testdataUser0,
|
||||
},
|
||||
{
|
||||
comment: "array by name with prefix wildcard",
|
||||
options: []FlattenOpts{WithQuery([]string{"*", "users", "name=>*Aardvark"})},
|
||||
out: testdataUser0,
|
||||
},
|
||||
{
|
||||
comment: "array by name with suffix and prefix",
|
||||
options: []FlattenOpts{WithQuery([]string{"*", "users", "name=>*Aardv*"})},
|
||||
out: testdataUser0,
|
||||
},
|
||||
{
|
||||
comment: "who likes ants, array re-written",
|
||||
options: []FlattenOpts{WithQuery([]string{"*", "users", "#name", "favorites", "ants"})},
|
||||
out: []Row{
|
||||
{Path: []string{"2", "users", "Alex Aardvark", "favorites", "0"}, Value: "ants"},
|
||||
},
|
||||
},
|
||||
{
|
||||
comment: "rewritten and filtered",
|
||||
options: []FlattenOpts{WithQuery([]string{"*", "users", "#name=>Al*", "id"})},
|
||||
out: []Row{
|
||||
{Path: []string{"2", "users", "Alex Aardvark", "id"}, Value: "1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
comment: "bad key name",
|
||||
options: []FlattenOpts{WithQuery([]string{"*", "users", "#nokey"})},
|
||||
out: []Row{},
|
||||
},
|
||||
{
|
||||
comment: "rewrite array to map",
|
||||
options: []FlattenOpts{WithQuery([]string{"*", "users", "#name", "id"})},
|
||||
out: []Row{
|
||||
{Path: []string{"2", "users", "Alex Aardvark", "id"}, Value: "1"},
|
||||
{Path: []string{"2", "users", "Bailey Bobcat", "id"}, Value: "2"},
|
||||
{Path: []string{"2", "users", "Cam Chipmunk", "id"}, Value: "3"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.comment, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual, err := Jsonl(bytes.NewReader(dataRaw), tt.options...)
|
||||
testFlattenCase(t, tt, actual, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlatten_Complex(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Do the unmarshaling here, so we don't keep doing it again and again
|
||||
dataRaw, err := os.ReadFile(filepath.Join("testdata", "animals.json"))
|
||||
require.NoError(t, err, "reading file")
|
||||
var dataIn interface{}
|
||||
require.NoError(t, json.Unmarshal(dataRaw, &dataIn), "unmarshalling json")
|
||||
|
||||
// We do a bunch of tests to select this user. So we'll pull
|
||||
// this out here and make the testcases more DRY
|
||||
testdataUser0 := []Row{
|
||||
{Path: []string{"users", "0", "favorites", "0"}, Value: "ants"},
|
||||
{Path: []string{"users", "0", "id"}, Value: "1"},
|
||||
{Path: []string{"users", "0", "name"}, Value: "Alex Aardvark"},
|
||||
{Path: []string{"users", "0", "uuid"}, Value: "abc123"},
|
||||
}
|
||||
|
||||
var tests = []flattenTestCase{
|
||||
{
|
||||
out: []Row{
|
||||
{Path: []string{"metadata", "testing"}, Value: "true"},
|
||||
{Path: []string{"metadata", "version"}, Value: "1.0.1"},
|
||||
{Path: []string{"system"}, Value: "users demo"},
|
||||
{Path: []string{"users", "0", "favorites", "0"}, Value: "ants"},
|
||||
{Path: []string{"users", "0", "id"}, Value: "1"},
|
||||
{Path: []string{"users", "0", "name"}, Value: "Alex Aardvark"},
|
||||
{Path: []string{"users", "0", "uuid"}, Value: "abc123"},
|
||||
{Path: []string{"users", "1", "favorites", "0"}, Value: "mice"},
|
||||
{Path: []string{"users", "1", "favorites", "1"}, Value: "birds"},
|
||||
{Path: []string{"users", "1", "id"}, Value: "2"},
|
||||
{Path: []string{"users", "1", "name"}, Value: "Bailey Bobcat"},
|
||||
{Path: []string{"users", "1", "uuid"}, Value: "def456"},
|
||||
{Path: []string{"users", "2", "favorites", "0"}, Value: "seeds"},
|
||||
{Path: []string{"users", "2", "id"}, Value: "3"},
|
||||
{Path: []string{"users", "2", "name"}, Value: "Cam Chipmunk"},
|
||||
{Path: []string{"users", "2", "uuid"}, Value: "ghi789"},
|
||||
},
|
||||
comment: "all together",
|
||||
},
|
||||
{
|
||||
options: []FlattenOpts{WithQuery([]string{"metadata"})},
|
||||
out: []Row{
|
||||
{Path: []string{"metadata", "testing"}, Value: "true"},
|
||||
{Path: []string{"metadata", "version"}, Value: "1.0.1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
comment: "array by #",
|
||||
options: []FlattenOpts{WithQuery([]string{"users", "0"})},
|
||||
out: testdataUser0,
|
||||
},
|
||||
{
|
||||
comment: "array by id value",
|
||||
options: []FlattenOpts{WithQuery([]string{"users", "id=>1"})},
|
||||
out: testdataUser0,
|
||||
},
|
||||
{
|
||||
comment: "array by uuid",
|
||||
options: []FlattenOpts{WithQuery([]string{"users", "uuid=>abc123"})},
|
||||
out: testdataUser0,
|
||||
},
|
||||
{
|
||||
comment: "array by name with suffix wildcard",
|
||||
options: []FlattenOpts{WithQuery([]string{"users", "name=>Al*"})},
|
||||
out: testdataUser0,
|
||||
},
|
||||
{
|
||||
comment: "array by name with prefix wildcard",
|
||||
options: []FlattenOpts{WithQuery([]string{"users", "name=>*Aardvark"})},
|
||||
out: testdataUser0,
|
||||
},
|
||||
|
||||
{
|
||||
comment: "array by name with suffix and prefix",
|
||||
options: []FlattenOpts{WithQuery([]string{"users", "name=>*Aardv*"})},
|
||||
out: testdataUser0,
|
||||
},
|
||||
{
|
||||
comment: "who likes ants, array re-written",
|
||||
options: []FlattenOpts{WithQuery([]string{"users", "#name", "favorites", "ants"})},
|
||||
out: []Row{
|
||||
{Path: []string{"users", "Alex Aardvark", "favorites", "0"}, Value: "ants"},
|
||||
},
|
||||
},
|
||||
{
|
||||
comment: "rewritten and filtered",
|
||||
options: []FlattenOpts{WithQuery([]string{"users", "#name=>Al*", "id"})},
|
||||
out: []Row{
|
||||
{Path: []string{"users", "Alex Aardvark", "id"}, Value: "1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
comment: "bad key name",
|
||||
options: []FlattenOpts{WithQuery([]string{"users", "#nokey"})},
|
||||
out: []Row{},
|
||||
},
|
||||
{
|
||||
comment: "rewrite array to map",
|
||||
options: []FlattenOpts{WithQuery([]string{"users", "#name", "id"})},
|
||||
out: []Row{
|
||||
{Path: []string{"users", "Alex Aardvark", "id"}, Value: "1"},
|
||||
{Path: []string{"users", "Bailey Bobcat", "id"}, Value: "2"},
|
||||
{Path: []string{"users", "Cam Chipmunk", "id"}, Value: "3"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.comment, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual, err := Flatten(dataIn, tt.options...)
|
||||
testFlattenCase(t, tt, actual, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlatten_ArrayMaps(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var tests = []flattenTestCase{
|
||||
{
|
||||
in: `{"data": [{"v":1,"id":"a"},{"v":2,"id":"b"},{"v":3,"id":"c"}]}`,
|
||||
out: []Row{
|
||||
{Path: []string{"data", "0", "id"}, Value: "a"},
|
||||
{Path: []string{"data", "0", "v"}, Value: "1"},
|
||||
|
||||
{Path: []string{"data", "1", "id"}, Value: "b"},
|
||||
{Path: []string{"data", "1", "v"}, Value: "2"},
|
||||
|
||||
{Path: []string{"data", "2", "id"}, Value: "c"},
|
||||
{Path: []string{"data", "2", "v"}, Value: "3"},
|
||||
},
|
||||
comment: "nested array as array",
|
||||
},
|
||||
{
|
||||
in: `{"data": [{"v":1,"id":"a"},{"v":2,"id":"b"},{"v":3,"id":"c"}]}`,
|
||||
out: []Row{
|
||||
{Path: []string{"data", "a", "id"}, Value: "a"},
|
||||
{Path: []string{"data", "a", "v"}, Value: "1"},
|
||||
|
||||
{Path: []string{"data", "b", "id"}, Value: "b"},
|
||||
{Path: []string{"data", "b", "v"}, Value: "2"},
|
||||
|
||||
{Path: []string{"data", "c", "id"}, Value: "c"},
|
||||
{Path: []string{"data", "c", "v"}, Value: "3"},
|
||||
},
|
||||
options: []FlattenOpts{WithQuery([]string{"data", "#id"})},
|
||||
comment: "nested array as map",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.comment, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual, err := Json([]byte(tt.in), tt.options...)
|
||||
testFlattenCase(t, tt, actual, err)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFlatten(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var tests = []flattenTestCase{
|
||||
{
|
||||
in: "a",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
in: `["a", null]`,
|
||||
out: []Row{
|
||||
{Path: []string{"0"}, Value: "a"},
|
||||
},
|
||||
comment: "skip null",
|
||||
},
|
||||
|
||||
{
|
||||
in: `["a", "b", null]`,
|
||||
out: []Row{
|
||||
{Path: []string{"0"}, Value: "a"},
|
||||
{Path: []string{"1"}, Value: "b"},
|
||||
{Path: []string{"2"}, Value: ""},
|
||||
},
|
||||
options: []FlattenOpts{IncludeNulls()},
|
||||
comment: "includes null",
|
||||
},
|
||||
|
||||
{
|
||||
in: `["1"]`,
|
||||
out: []Row{
|
||||
{Path: []string{"0"}, Value: "1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: `["a", true, false, "1", 2, 3.3]`,
|
||||
out: []Row{
|
||||
{Path: []string{"0"}, Value: "a"},
|
||||
{Path: []string{"1"}, Value: "true"},
|
||||
{Path: []string{"2"}, Value: "false"},
|
||||
{Path: []string{"3"}, Value: "1"},
|
||||
{Path: []string{"4"}, Value: "2"},
|
||||
{Path: []string{"5"}, Value: "3.3"},
|
||||
},
|
||||
comment: "mixed types",
|
||||
},
|
||||
{
|
||||
in: `{"a": 1, "b": "2.2", "c": [1,2,3]}`,
|
||||
out: []Row{
|
||||
{Path: []string{"a"}, Value: "1"},
|
||||
{Path: []string{"b"}, Value: "2.2"},
|
||||
{Path: []string{"c", "0"}, Value: "1"},
|
||||
{Path: []string{"c", "1"}, Value: "2"},
|
||||
{Path: []string{"c", "2"}, Value: "3"},
|
||||
},
|
||||
comment: "nested types",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.comment, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual, err := Json([]byte(tt.in), tt.options...)
|
||||
testFlattenCase(t, tt, actual, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlattenJsonlErrors(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var tests = []flattenTestCase{
|
||||
{
|
||||
in: "a",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
// this test case was left over from attempting to parse json that
|
||||
// is contained within a file that is not stricly jsonl
|
||||
// it should error, maybe look at this again in the future?
|
||||
comment: "valid json inline text",
|
||||
in: `valid json is hidden["a"]in me`,
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
// this test case was left over from attempting to parse json that
|
||||
// is contained within a file that is not stricly jsonl
|
||||
// it should error, maybe look at this again in the future?
|
||||
comment: "valid json sandwich",
|
||||
in: `
|
||||
there is some json under me
|
||||
["a"]
|
||||
there is some json above me
|
||||
`,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.comment, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual, err := Jsonl(bytes.NewBuffer([]byte(tt.in)), tt.options...)
|
||||
testFlattenCase(t, tt, actual, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// add mutext due to data races when running locally, don't seem to appear in CI
|
||||
// maybe remove if slows down CI too much
|
||||
var testFlattenCaseMutex sync.Mutex
|
||||
|
||||
// testFlattenCase runs tests for a single test case. Normally this
|
||||
// would be in a for loop, instead it's abstracted here to make it
|
||||
// simpler to split up a giant array of test cases.
|
||||
func testFlattenCase(t *testing.T, tt flattenTestCase, actual []Row, actualErr error) {
|
||||
testFlattenCaseMutex.Lock()
|
||||
defer testFlattenCaseMutex.Unlock()
|
||||
|
||||
if tt.err {
|
||||
require.Error(t, actualErr, "test %s %s", tt.in, tt.comment)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, actualErr, "test %s %s", tt.in, tt.comment)
|
||||
|
||||
// Despite being an array. data is returned
|
||||
// unordered. This greatly complicates our testing. We
|
||||
// can either sort it, or use an unordered comparison
|
||||
// operator. The `require.ElementsMatch` produces much
|
||||
// harder to read diffs, so instead we'll sort things.
|
||||
sort.SliceStable(tt.out, func(i, j int) bool { return tt.out[i].StringPath("/") < tt.out[j].StringPath("/") })
|
||||
sort.SliceStable(actual, func(i, j int) bool { return actual[i].StringPath("/") < actual[j].StringPath("/") })
|
||||
require.EqualValues(t, tt.out, actual, "test %s %s", tt.in, tt.comment)
|
||||
}
|
||||
|
||||
func TestFlattenSliceOfMaps(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in interface{}
|
||||
opts []FlattenOpts
|
||||
out []Row
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "single",
|
||||
in: []map[string]interface{}{
|
||||
{
|
||||
"id": "a",
|
||||
"v": 1,
|
||||
},
|
||||
},
|
||||
opts: []FlattenOpts{},
|
||||
out: []Row{
|
||||
{Path: []string{"0", "id"}, Value: "a"},
|
||||
{Path: []string{"0", "v"}, Value: "1"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "multiple",
|
||||
in: []map[string]interface{}{
|
||||
{
|
||||
"id": "a",
|
||||
"v": 1,
|
||||
},
|
||||
{
|
||||
"id": "b",
|
||||
"v": 2,
|
||||
},
|
||||
{
|
||||
"id": "c",
|
||||
"v": 3,
|
||||
},
|
||||
},
|
||||
opts: []FlattenOpts{},
|
||||
out: []Row{
|
||||
{Path: []string{"0", "id"}, Value: "a"},
|
||||
{Path: []string{"0", "v"}, Value: "1"},
|
||||
{Path: []string{"1", "id"}, Value: "b"},
|
||||
{Path: []string{"1", "v"}, Value: "2"},
|
||||
{Path: []string{"2", "id"}, Value: "c"},
|
||||
{Path: []string{"2", "v"}, Value: "3"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "error",
|
||||
in: []map[string]interface{}{
|
||||
{
|
||||
"id": []string{"this should cause an error"},
|
||||
},
|
||||
},
|
||||
opts: []FlattenOpts{},
|
||||
out: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := Flatten(tt.in, tt.opts...)
|
||||
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, tt.out, got)
|
||||
})
|
||||
}
|
||||
}
|
63
orbit/pkg/dataflatten/ini.go
Normal file
63
orbit/pkg/dataflatten/ini.go
Normal file
@ -0,0 +1,63 @@
|
||||
// toml parsing won't work -- ini files don't quote the string and
|
||||
// tend to have random spaces. Bummer, since
|
||||
// https://github.com/pelletier/go-toml/pull/433 was right
|
||||
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflatten
|
||||
|
||||
import (
|
||||
"github.com/go-ini/ini"
|
||||
)
|
||||
|
||||
func IniFile(file string, opts ...FlattenOpts) ([]Row, error) {
|
||||
return flattenIni(file, opts...)
|
||||
}
|
||||
|
||||
func Ini(rawdata []byte, opts ...FlattenOpts) ([]Row, error) {
|
||||
return flattenIni(rawdata, opts...)
|
||||
}
|
||||
|
||||
// flattenIni uses go-ini to flatten ini data. The underlying library
|
||||
// accepts both files and []byte via the interface{} type. It also
|
||||
// makes heavy use of reflect, so this does some manual iteration to
|
||||
// extract things.
|
||||
func flattenIni(in interface{}, opts ...FlattenOpts) ([]Row, error) {
|
||||
|
||||
v := map[string]interface{}{}
|
||||
|
||||
iniFile, err := ini.Load(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, section := range iniFile.Sections() {
|
||||
// While we can use section.KeysHash() directly, instead we
|
||||
// iterate. This allows us to canonicalize the value to handle
|
||||
// booleans. Everything else we leave as string
|
||||
sectionMap := make(map[string]interface{})
|
||||
for _, key := range section.Keys() {
|
||||
//fmt.Println(section.Name(), key.Name(), key.Value())
|
||||
asBool, ok := iniToBool(key.Value())
|
||||
if ok {
|
||||
sectionMap[key.Name()] = asBool
|
||||
} else {
|
||||
sectionMap[key.Name()] = key.Value()
|
||||
}
|
||||
}
|
||||
v[section.Name()] = sectionMap
|
||||
}
|
||||
|
||||
return Flatten(v, opts...)
|
||||
}
|
||||
|
||||
// iniToBool attempts to convert an ini value to a boolean. It returns
|
||||
// the converted value, and ok. The list of strings comes from go-ini
|
||||
func iniToBool(val string) (bool, bool) {
|
||||
switch val {
|
||||
case "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "y", "ON", "on", "On":
|
||||
return true, true
|
||||
case "f", "F", "false", "FALSE", "False", "NO", "no", "No", "n", "OFF", "off", "Off":
|
||||
return false, true
|
||||
}
|
||||
return false, false
|
||||
}
|
109
orbit/pkg/dataflatten/ini_test.go
Normal file
109
orbit/pkg/dataflatten/ini_test.go
Normal file
@ -0,0 +1,109 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflatten
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
iniTestFilePath = path.Join("testdata", "secdata.ini")
|
||||
iniTestFileLen = 87
|
||||
)
|
||||
|
||||
func TestIniToBool(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var tests = []struct {
|
||||
in string
|
||||
expected bool
|
||||
isBool bool
|
||||
}{
|
||||
{
|
||||
in: "hello world",
|
||||
},
|
||||
{
|
||||
in: "Yes",
|
||||
expected: true,
|
||||
isBool: true,
|
||||
},
|
||||
{
|
||||
in: "No",
|
||||
expected: false,
|
||||
isBool: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.in, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
asBool, ok := iniToBool(tt.in)
|
||||
if tt.isBool {
|
||||
require.True(t, ok)
|
||||
require.Equal(t, tt.expected, asBool)
|
||||
} else {
|
||||
require.False(t, ok)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIniFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rows, err := IniFile(iniTestFilePath)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rows, iniTestFileLen)
|
||||
}
|
||||
|
||||
func TestIni(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fileBytes, err := os.ReadFile(iniTestFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
rows, err := Ini(fileBytes)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rows, iniTestFileLen)
|
||||
}
|
||||
|
||||
func TestIniSecedit(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rows, err := IniFile(path.Join("testdata", "secdata.ini"))
|
||||
require.NoError(t, err)
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
expected Row
|
||||
}{
|
||||
{
|
||||
name: "converted boolean",
|
||||
expected: Row{Path: []string{"Unicode", "Unicode"}, Value: "true"},
|
||||
},
|
||||
{
|
||||
name: "string value",
|
||||
expected: Row{Path: []string{"System Access", "NewAdministratorName"}, Value: "Administrator"},
|
||||
},
|
||||
{
|
||||
// We're not casting this to false
|
||||
name: "number value",
|
||||
expected: Row{Path: []string{"Event Audit", "AuditDSAccess"}, Value: "0"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Contains(t, rows, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
26
orbit/pkg/dataflatten/json.go
Normal file
26
orbit/pkg/dataflatten/json.go
Normal file
@ -0,0 +1,26 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflatten
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func JsonFile(file string, opts ...FlattenOpts) ([]Row, error) {
|
||||
rawdata, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Json(rawdata, opts...)
|
||||
}
|
||||
|
||||
func Json(rawdata []byte, opts ...FlattenOpts) ([]Row, error) {
|
||||
var data interface{}
|
||||
|
||||
if err := json.Unmarshal(rawdata, &data); err != nil {
|
||||
return nil, fmt.Errorf("unmarshalling json: %w", err)
|
||||
}
|
||||
|
||||
return Flatten(data, opts...)
|
||||
}
|
37
orbit/pkg/dataflatten/jsonl.go
Normal file
37
orbit/pkg/dataflatten/jsonl.go
Normal file
@ -0,0 +1,37 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflatten
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func JsonlFile(file string, opts ...FlattenOpts) ([]Row, error) {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return Jsonl(f, opts...)
|
||||
}
|
||||
|
||||
func Jsonl(r io.Reader, opts ...FlattenOpts) ([]Row, error) {
|
||||
decoder := json.NewDecoder(r)
|
||||
var objects []interface{}
|
||||
|
||||
for {
|
||||
var object interface{}
|
||||
err := decoder.Decode(&object)
|
||||
|
||||
switch {
|
||||
case err == nil:
|
||||
objects = append(objects, object)
|
||||
case err == io.EOF:
|
||||
return Flatten(objects, opts...)
|
||||
default:
|
||||
return nil, fmt.Errorf("unmarshalling jsonl: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
27
orbit/pkg/dataflatten/plist.go
Normal file
27
orbit/pkg/dataflatten/plist.go
Normal file
@ -0,0 +1,27 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflatten
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
func PlistFile(file string, opts ...FlattenOpts) ([]Row, error) {
|
||||
rawdata, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Plist(rawdata, opts...)
|
||||
}
|
||||
|
||||
func Plist(rawdata []byte, opts ...FlattenOpts) ([]Row, error) {
|
||||
var data interface{}
|
||||
|
||||
if _, err := plist.Unmarshal(rawdata, &data); err != nil {
|
||||
return nil, fmt.Errorf("unmarshalling plist: %w", err)
|
||||
}
|
||||
|
||||
return Flatten(data, opts...)
|
||||
}
|
84
orbit/pkg/dataflatten/plist_test.go
Normal file
84
orbit/pkg/dataflatten/plist_test.go
Normal file
@ -0,0 +1,84 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflatten
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestPlist is testing a very simple plist case. Most of the more complex testing is in the spec files.
|
||||
func TestPlist(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var tests = []flattenTestCase{
|
||||
{
|
||||
in: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0"><array><string>a</string><string>b</string></array></plist>`,
|
||||
out: []Row{
|
||||
{Path: []string{"0"}, Value: "a"},
|
||||
{Path: []string{"1"}, Value: "b"},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: `<?xml version="1.0" encoding="UTF-8"?>`,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.comment, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual, err := Plist([]byte(tt.in))
|
||||
testFlattenCase(t, tt, actual, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNestedPlists(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var tests = []flattenTestCase{
|
||||
{
|
||||
options: []FlattenOpts{WithNestedPlist()},
|
||||
comment: "expand nested",
|
||||
out: []Row{
|
||||
{Path: []string{"inbinary", "astring"}, Value: "hello"},
|
||||
{Path: []string{"inbinary", "arr", "0"}, Value: "one"},
|
||||
{Path: []string{"inbinary", "arr", "1"}, Value: "two"},
|
||||
{Path: []string{"inxml", "arr", "0"}, Value: "one"},
|
||||
{Path: []string{"inxml", "arr", "1"}, Value: "two"},
|
||||
{Path: []string{"inxml", "astring"}, Value: "hello"},
|
||||
},
|
||||
},
|
||||
{
|
||||
comment: "nested and queried",
|
||||
options: []FlattenOpts{WithNestedPlist(), WithQuery([]string{"*", "arr", "0"})},
|
||||
out: []Row{
|
||||
{Path: []string{"inbinary", "arr", "0"}, Value: "one"},
|
||||
{Path: []string{"inxml", "arr", "0"}, Value: "one"},
|
||||
},
|
||||
},
|
||||
{
|
||||
comment: "not expanded",
|
||||
out: []Row{
|
||||
{Path: []string{"inbinary"}, Value: "YnBsaXN0MDDSAQIDBlNhcnJXYXN0cmluZ6IEBVNvbmVTdHdvVWhlbGxvCA0RGRwgJAAAAAAAAAEBAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAq"},
|
||||
{Path: []string{"inxml"}, Value: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>arr</key>\n\t<array>\n\t\t<string>one</string>\n\t\t<string>two</string>\n\t</array>\n\t<key>astring</key>\n\t<string>hello</string>\n</dict>\n</plist>"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.comment, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, f := range []string{"nested.xml", "nested.plist"} {
|
||||
actual, err := PlistFile(filepath.Join("testdata", "nested", f), tt.options...)
|
||||
testFlattenCase(t, tt, actual, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
41
orbit/pkg/dataflatten/row.go
Normal file
41
orbit/pkg/dataflatten/row.go
Normal file
@ -0,0 +1,41 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflatten
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Row is the record type we return.
|
||||
type Row struct {
|
||||
Path []string
|
||||
Value string
|
||||
}
|
||||
|
||||
// NewRow does a copy of the path elements, and returns a row. We do
|
||||
// this copy to correct for some odd pointer passing bugs
|
||||
func NewRow(path []string, value string) Row {
|
||||
copiedPath := make([]string, len(path))
|
||||
copy(copiedPath, path)
|
||||
return Row{
|
||||
Path: copiedPath,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (r Row) StringPath(sep string) string {
|
||||
return strings.Join(r.Path, sep)
|
||||
}
|
||||
|
||||
func (r Row) ParentKey(sep string) (string, string) {
|
||||
switch len(r.Path) {
|
||||
case 0:
|
||||
return "", ""
|
||||
case 1:
|
||||
return "", r.Path[0]
|
||||
}
|
||||
|
||||
parent := strings.Join(r.Path[:len(r.Path)-1], sep)
|
||||
key := r.Path[len(r.Path)-1]
|
||||
|
||||
return parent, key
|
||||
}
|
47
orbit/pkg/dataflatten/row_test.go
Normal file
47
orbit/pkg/dataflatten/row_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflatten
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRowParentFunctions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var tests = []struct {
|
||||
in Row
|
||||
parent string
|
||||
key string
|
||||
}{
|
||||
{
|
||||
in: Row{},
|
||||
},
|
||||
|
||||
{
|
||||
in: Row{Path: []string{}},
|
||||
},
|
||||
{
|
||||
in: Row{Path: []string{"a"}},
|
||||
parent: "",
|
||||
key: "a",
|
||||
},
|
||||
{
|
||||
in: Row{Path: []string{"a", "b"}},
|
||||
parent: "a",
|
||||
key: "b",
|
||||
},
|
||||
{
|
||||
in: Row{Path: []string{"a", "b", "c"}},
|
||||
parent: "a/b",
|
||||
key: "c",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
parent, key := tt.in.ParentKey("/")
|
||||
require.Equal(t, tt.parent, parent)
|
||||
require.Equal(t, tt.key, key)
|
||||
}
|
||||
}
|
84
orbit/pkg/dataflatten/string_delimited.go
Normal file
84
orbit/pkg/dataflatten/string_delimited.go
Normal file
@ -0,0 +1,84 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflatten
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type dataFunc func(data []byte, opts ...FlattenOpts) ([]Row, error)
|
||||
|
||||
type recordSplittingStrategy int
|
||||
|
||||
const (
|
||||
None recordSplittingStrategy = iota
|
||||
DuplicateKeys
|
||||
)
|
||||
|
||||
func StringDelimitedFunc(kVDelimiter string, splittingStrategy recordSplittingStrategy) dataFunc {
|
||||
switch splittingStrategy {
|
||||
case None:
|
||||
return singleRecordFunc(kVDelimiter)
|
||||
case DuplicateKeys:
|
||||
return duplicateKeyFunc(kVDelimiter)
|
||||
default:
|
||||
panic("Unknown record splitting strategy")
|
||||
}
|
||||
}
|
||||
|
||||
// duplicateKeyFunc returns a function that conforms to the interface expected
|
||||
// by dataflattentable.Table's execDataFunc property. properties are grouped
|
||||
// into a single record based on 'duplicate key' strategy: If a key/value pair
|
||||
// is encountered, and the record being built already has a value for that key,
|
||||
// then that record is considered 'complete'. The record is stored in the
|
||||
// collection, and a new record is started. This strategy is only suitable if
|
||||
// properties for a single record are grouped together, and there is at least
|
||||
// one field that appears for every record before any sparse data.
|
||||
func duplicateKeyFunc(kVDelimiter string) dataFunc {
|
||||
return func(rawdata []byte, opts ...FlattenOpts) ([]Row, error) {
|
||||
results := []interface{}{}
|
||||
scanner := bufio.NewScanner(bytes.NewReader(rawdata))
|
||||
row := map[string]interface{}{}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.SplitN(line, kVDelimiter, 2)
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
if _, ok := row[key]; ok { // this key already exists, so we want to start a new record.
|
||||
results = append(results, row) // store the 'finished' record in the collection
|
||||
row = map[string]interface{}{} // reset the record
|
||||
}
|
||||
row[key] = value
|
||||
}
|
||||
results = append(results, row) // store the final record
|
||||
|
||||
return Flatten(results, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// singleRecordFunc returns an execData function that assumes 'rawdata'
|
||||
// only holds key-value pairs for a single record. Additionally, each k/v pair
|
||||
// must be on its own line. Useful for output that can be easily separated into
|
||||
// separate records before 'flattening'
|
||||
func singleRecordFunc(kVDelimiter string) dataFunc {
|
||||
return func(rawdata []byte, opts ...FlattenOpts) ([]Row, error) {
|
||||
results := []interface{}{}
|
||||
scanner := bufio.NewScanner(bytes.NewReader(rawdata))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.SplitN(line, kVDelimiter, 2)
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
results = append(results, map[string]interface{}{key: value})
|
||||
}
|
||||
|
||||
return Flatten(results, opts...)
|
||||
}
|
||||
}
|
34
orbit/pkg/dataflatten/testdata/animals.json
vendored
Normal file
34
orbit/pkg/dataflatten/testdata/animals.json
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"metadata": {
|
||||
"testing": true,
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"system": "users demo",
|
||||
"users": [
|
||||
{
|
||||
"favorites": [
|
||||
"ants"
|
||||
],
|
||||
"uuid": "abc123",
|
||||
"name": "Alex Aardvark",
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"favorites": [
|
||||
"mice",
|
||||
"birds"
|
||||
],
|
||||
"uuid": "def456",
|
||||
"name": "Bailey Bobcat",
|
||||
"id": 2
|
||||
},
|
||||
{
|
||||
"favorites": [
|
||||
"seeds"
|
||||
],
|
||||
"uuid": "ghi789",
|
||||
"name": "Cam Chipmunk",
|
||||
"id": 3
|
||||
}
|
||||
]
|
||||
}
|
43
orbit/pkg/dataflatten/testdata/animals.jsonl
vendored
Normal file
43
orbit/pkg/dataflatten/testdata/animals.jsonl
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"metadata": {
|
||||
"testing": true,
|
||||
"version": "1.0.1"
|
||||
}
|
||||
}
|
||||
{
|
||||
"system": "users demo"
|
||||
}
|
||||
{
|
||||
"users": [
|
||||
{
|
||||
"favorites": [
|
||||
"ants"
|
||||
],
|
||||
"uuid": "abc123",
|
||||
"name": "Alex Aardvark",
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"favorites": [
|
||||
"mice",
|
||||
"birds"
|
||||
],
|
||||
"uuid": "def456",
|
||||
"name": "Bailey Bobcat",
|
||||
"id": 2
|
||||
},
|
||||
{
|
||||
"favorites": [
|
||||
"seeds"
|
||||
],
|
||||
"uuid": "ghi789",
|
||||
"name": "Cam Chipmunk",
|
||||
"id": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
[
|
||||
"array-item-A",
|
||||
"array-item-B",
|
||||
"array-item-C"
|
||||
]
|
BIN
orbit/pkg/dataflatten/testdata/animals.plist
vendored
Normal file
BIN
orbit/pkg/dataflatten/testdata/animals.plist
vendored
Normal file
Binary file not shown.
55
orbit/pkg/dataflatten/testdata/animals.xml
vendored
Normal file
55
orbit/pkg/dataflatten/testdata/animals.xml
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>metadata</key>
|
||||
<dict>
|
||||
<key>testing</key>
|
||||
<true/>
|
||||
<key>version</key>
|
||||
<string>1.0.1</string>
|
||||
</dict>
|
||||
<key>system</key>
|
||||
<string>users demo</string>
|
||||
<key>users</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>favorites</key>
|
||||
<array>
|
||||
<string>ants</string>
|
||||
</array>
|
||||
<key>id</key>
|
||||
<integer>1</integer>
|
||||
<key>name</key>
|
||||
<string>Alex Aardvark</string>
|
||||
<key>uuid</key>
|
||||
<string>abc123</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>favorites</key>
|
||||
<array>
|
||||
<string>mice</string>
|
||||
<string>birds</string>
|
||||
</array>
|
||||
<key>id</key>
|
||||
<integer>2</integer>
|
||||
<key>name</key>
|
||||
<string>Bailey Bobcat</string>
|
||||
<key>uuid</key>
|
||||
<string>def456</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>favorites</key>
|
||||
<array>
|
||||
<string>seeds</string>
|
||||
</array>
|
||||
<key>id</key>
|
||||
<integer>3</integer>
|
||||
<key>name</key>
|
||||
<string>Cam Chipmunk</string>
|
||||
<key>uuid</key>
|
||||
<string>ghi789</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
28
orbit/pkg/dataflatten/testdata/complex2.json
vendored
Normal file
28
orbit/pkg/dataflatten/testdata/complex2.json
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"addons": [
|
||||
{
|
||||
"string1": "hello",
|
||||
"null1": null,
|
||||
"null2": null,
|
||||
"bool1": true,
|
||||
"nest1": {
|
||||
"anArray": []
|
||||
},
|
||||
"nest2": [
|
||||
{
|
||||
"null3": null,
|
||||
"null4": null,
|
||||
"string2": "foo"
|
||||
}
|
||||
],
|
||||
"nest3": {
|
||||
"string3": null,
|
||||
"string4": null,
|
||||
"string5": null,
|
||||
"string6": "null",
|
||||
"string7": "A Very Long Sentence",
|
||||
"string8": "short"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
15
orbit/pkg/dataflatten/testdata/nested.json
vendored
Normal file
15
orbit/pkg/dataflatten/testdata/nested.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"addons": [
|
||||
{
|
||||
"name": "Nested Strings",
|
||||
"nest1": {
|
||||
"string1": null,
|
||||
"string2": null,
|
||||
"string3": "string3",
|
||||
"string4": "string4",
|
||||
"string5": "string5",
|
||||
"string6": "string6"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
29
orbit/pkg/dataflatten/testdata/nested/Makefile
vendored
Normal file
29
orbit/pkg/dataflatten/testdata/nested/Makefile
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
TARGETS:= nested.xml nested.plist
|
||||
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
clean:
|
||||
-rm $(TARGETS)
|
||||
|
||||
|
||||
inner.xml: inner.json
|
||||
plutil -convert xml1 -o $@ $^
|
||||
|
||||
inner.plist.b64: inner.json
|
||||
plutil -convert binary1 -o - $^ | base64 -o $@
|
||||
|
||||
|
||||
inner.plist: inner.json
|
||||
plutil -convert binary1 -o $@ $^
|
||||
|
||||
|
||||
nested.xml: nested.template.xml inner.xml inner.plist.b64
|
||||
cat $< | ruby -pe ' \
|
||||
require "cgi"; \
|
||||
x = IO.readlines("inner.xml").join.chomp; \
|
||||
b = IO.readlines("inner.plist.b64").join.chomp; \
|
||||
STDIN.each_line { |l| l.sub!("INBINARY", b); l.sub!("INXML",CGI::escapeHTML(x)); puts l };' > $@
|
||||
|
||||
nested.plist: nested.xml
|
||||
plutil -convert binary1 -o $@ $<
|
1
orbit/pkg/dataflatten/testdata/nested/README.md
vendored
Normal file
1
orbit/pkg/dataflatten/testdata/nested/README.md
vendored
Normal file
@ -0,0 +1 @@
|
||||
Some simple shell/ruby/make hackery to get a nested plist
|
7
orbit/pkg/dataflatten/testdata/nested/inner.json
vendored
Normal file
7
orbit/pkg/dataflatten/testdata/nested/inner.json
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"arr": [
|
||||
"one",
|
||||
"two"
|
||||
],
|
||||
"astring": "hello"
|
||||
}
|
BIN
orbit/pkg/dataflatten/testdata/nested/nested.plist
vendored
Normal file
BIN
orbit/pkg/dataflatten/testdata/nested/nested.plist
vendored
Normal file
Binary file not shown.
11
orbit/pkg/dataflatten/testdata/nested/nested.template.xml
vendored
Normal file
11
orbit/pkg/dataflatten/testdata/nested/nested.template.xml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
plutil -convert xml1 -o - nested.template.json
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>inbinary</key>
|
||||
<data>INBINARY</data>
|
||||
<key>inxml</key>
|
||||
<string>INXML</string>
|
||||
</dict>
|
||||
</plist>
|
23
orbit/pkg/dataflatten/testdata/nested/nested.xml
vendored
Normal file
23
orbit/pkg/dataflatten/testdata/nested/nested.xml
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>inbinary</key>
|
||||
<data>YnBsaXN0MDDSAQIDBlNhcnJXYXN0cmluZ6IEBVNvbmVTdHdvVWhlbGxvCA0RGRwgJAAAAAAAAAEBAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAq</data>
|
||||
<key>inxml</key>
|
||||
<string><?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>arr</key>
|
||||
<array>
|
||||
<string>one</string>
|
||||
<string>two</string>
|
||||
</array>
|
||||
<key>astring</key>
|
||||
<string>hello</string>
|
||||
</dict>
|
||||
</plist></string>
|
||||
</dict>
|
||||
</plist>
|
||||
plutil -convert xml1 -o - nested.template.json
|
92
orbit/pkg/dataflatten/testdata/secdata.ini
vendored
Normal file
92
orbit/pkg/dataflatten/testdata/secdata.ini
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
[Unicode]
|
||||
Unicode=yes
|
||||
[System Access]
|
||||
MinimumPasswordAge = 0
|
||||
MaximumPasswordAge = 42
|
||||
MinimumPasswordLength = 0
|
||||
PasswordComplexity = 1
|
||||
PasswordHistorySize = 0
|
||||
LockoutBadCount = 1
|
||||
ResetLockoutCount = 30
|
||||
LockoutDuration = 30
|
||||
RequireLogonToChangePassword = 0
|
||||
ForceLogoffWhenHourExpire = 0
|
||||
NewAdministratorName = "Administrator"
|
||||
NewGuestName = "Guest"
|
||||
ClearTextPassword = 0
|
||||
LSAAnonymousNameLookup = 0
|
||||
EnableAdminAccount = 0
|
||||
EnableGuestAccount = 0
|
||||
[Event Audit]
|
||||
AuditSystemEvents = 0
|
||||
AuditLogonEvents = 0
|
||||
AuditObjectAccess = 0
|
||||
AuditPrivilegeUse = 0
|
||||
AuditPolicyChange = 0
|
||||
AuditAccountManage = 0
|
||||
AuditProcessTracking = 0
|
||||
AuditDSAccess = 0
|
||||
AuditAccountLogon = 0
|
||||
[Registry Values]
|
||||
MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SecurityLevel=4,0
|
||||
MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SetCommand=4,0
|
||||
MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\CachedLogonsCount=1,"10"
|
||||
MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\ForceUnlockLogon=4,0
|
||||
MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\PasswordExpiryWarning=4,5
|
||||
MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\ScRemoveOption=1,"0"
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorAdmin=4,5
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorUser=4,3
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayLastUserName=4,0
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableInstallerDetection=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableSecureUIAPaths=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableUIADesktopToggle=4,0
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableVirtualization=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\Parameters\SupportedEncryptionTypes=4,0
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\LegalNoticeCaption=1,""
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\LegalNoticeText=7,
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\PromptOnSecureDesktop=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ScForceOption=4,0
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ShutdownWithoutLogon=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\UndockWithoutLogon=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ValidateAdminCodeSignatures=4,0
|
||||
MACHINE\Software\Policies\Microsoft\Windows\Safer\CodeIdentifiers\AuthenticodeEnabled=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\AuditBaseObjects=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\CrashOnAuditFail=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\DisableDomainCreds=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\EveryoneIncludesAnonymous=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy\Enabled=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\ForceGuest=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\FullPrivilegeAuditing=3,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\LimitBlankPasswordUse=4,1
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\MSV1_0\NTLMMinClientSec=4,536870912
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\MSV1_0\NTLMMinServerSec=4,536870912
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\NoLMHash=4,1
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymous=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymousSAM=4,1
|
||||
MACHINE\System\CurrentControlSet\Control\Print\Providers\LanMan Print Services\Servers\AddPrinterDrivers=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedExactPaths\Machine=7,System\CurrentControlSet\Control\ProductOptions,System\CurrentControlSet\Control\Server Applications,Software\Microsoft\Windows NT\CurrentVersion
|
||||
MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedPaths\Machine=7,System\CurrentControlSet\Control\Print\Printers,System\CurrentControlSet\Services\Eventlog,Software\Microsoft\OLAP Server,Software\Microsoft\Windows NT\CurrentVersion\Print,Software\Microsoft\Windows NT\CurrentVersion\Windows,System\CurrentControlSet\Control\ContentIndex,System\CurrentControlSet\Control\Terminal Server,System\CurrentControlSet\Control\Terminal Server\UserConfig,System\CurrentControlSet\Control\Terminal Server\DefaultUserConfiguration,Software\Microsoft\Windows NT\CurrentVersion\Perflib,System\CurrentControlSet\Services\SysmonLog
|
||||
MACHINE\System\CurrentControlSet\Control\Session Manager\Kernel\ObCaseInsensitive=4,1
|
||||
MACHINE\System\CurrentControlSet\Control\Session Manager\Memory Management\ClearPageFileAtShutdown=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Session Manager\ProtectionMode=4,1
|
||||
MACHINE\System\CurrentControlSet\Control\Session Manager\SubSystems\optional=7,
|
||||
MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\AutoDisconnect=4,15
|
||||
MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\EnableForcedLogOff=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\EnableSecuritySignature=4,0
|
||||
MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\NullSessionPipes=7,
|
||||
MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\RequireSecuritySignature=4,0
|
||||
MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\RestrictNullSessAccess=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\EnablePlainTextPassword=4,0
|
||||
MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\EnableSecuritySignature=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\RequireSecuritySignature=4,0
|
||||
MACHINE\System\CurrentControlSet\Services\LDAP\LDAPClientIntegrity=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\DisablePasswordChange=4,0
|
||||
MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\MaximumPasswordAge=4,30
|
||||
MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\RequireSignOrSeal=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\RequireStrongKey=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\SealSecureChannel=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\SignSecureChannel=4,1
|
||||
[Version]
|
||||
signature="$CHICAGO$"
|
||||
Revision=1
|
33
orbit/pkg/dataflatten/xml.go
Normal file
33
orbit/pkg/dataflatten/xml.go
Normal file
@ -0,0 +1,33 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflatten
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/clbanning/mxj"
|
||||
)
|
||||
|
||||
func XmlFile(file string, opts ...FlattenOpts) ([]Row, error) {
|
||||
rdr, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mv, err := mxj.NewMapXmlReader(rdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Flatten(mv.Old(), opts...)
|
||||
}
|
||||
|
||||
func Xml(rawdata []byte, opts ...FlattenOpts) ([]Row, error) {
|
||||
mv, err := mxj.NewMapXml(rawdata)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("mxj parse: %w", err)
|
||||
}
|
||||
|
||||
return Flatten(mv.Old(), opts...)
|
||||
}
|
94
orbit/pkg/table/app-icons/app_icons_darwin.go
Normal file
94
orbit/pkg/table/app-icons/app_icons_darwin.go
Normal file
@ -0,0 +1,94 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package appicons
|
||||
|
||||
/*
|
||||
#cgo darwin CFLAGS: -DDARWIN -x objective-c
|
||||
#cgo darwin LDFLAGS: -framework Cocoa
|
||||
#import <Appkit/AppKit.h>
|
||||
void Icon(CFDataRef *iconDataRef, char* path) {
|
||||
NSString *appPath = [[NSString stringWithUTF8String:path] stringByStandardizingPath];
|
||||
NSImage *img = [[NSWorkspace sharedWorkspace] iconForFile:appPath];
|
||||
|
||||
//request 128x128 since we are going to resize the icon
|
||||
NSRect targetFrame = NSMakeRect(0, 0, 128, 128);
|
||||
CGImageRef cgref = [img CGImageForProposedRect:&targetFrame context:nil hints:nil];
|
||||
NSBitmapImageRep *brep = [[NSBitmapImageRep alloc] initWithCGImage:cgref];
|
||||
NSData *imageData = [brep TIFFRepresentation];
|
||||
*iconDataRef = (CFDataRef)imageData;
|
||||
}
|
||||
*/
|
||||
import (
|
||||
"C"
|
||||
)
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/crc64"
|
||||
"image"
|
||||
"image/png"
|
||||
"unsafe"
|
||||
|
||||
"github.com/nfnt/resize"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
|
||||
"golang.org/x/image/tiff"
|
||||
)
|
||||
|
||||
var crcTable = crc64.MakeTable(crc64.ECMA)
|
||||
|
||||
func AppIcons() *table.Plugin {
|
||||
columns := []table.ColumnDefinition{
|
||||
table.TextColumn("path"),
|
||||
table.TextColumn("icon"),
|
||||
table.TextColumn("hash"),
|
||||
}
|
||||
return table.NewPlugin("app_icons", columns, generateAppIcons)
|
||||
}
|
||||
|
||||
func generateAppIcons(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
|
||||
q, ok := queryContext.Constraints["path"]
|
||||
if !ok || len(q.Constraints) == 0 {
|
||||
return nil, errors.New("The app_icons table requires that you specify a constraint WHERE path =")
|
||||
}
|
||||
path := q.Constraints[0].Expression
|
||||
img, hash, err := getAppIcon(path, queryContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var results []map[string]string
|
||||
buf := new(bytes.Buffer)
|
||||
img = resize.Resize(128, 128, img, resize.Bilinear)
|
||||
if err := png.Encode(buf, img); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, map[string]string{
|
||||
"path": path,
|
||||
"icon": base64.StdEncoding.EncodeToString(buf.Bytes()),
|
||||
"hash": fmt.Sprintf("%x", hash),
|
||||
})
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func getAppIcon(appPath string, queryContext table.QueryContext) (image.Image, uint64, error) {
|
||||
var data C.CFDataRef
|
||||
C.Icon(&data, C.CString(appPath))
|
||||
defer C.CFRelease(C.CFTypeRef(data))
|
||||
|
||||
tiffBytes := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
|
||||
img, err := tiff.Decode(bytes.NewBuffer(tiffBytes))
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("decoding tiff bytes: %w", err)
|
||||
}
|
||||
checksum := crc64.Checksum(tiffBytes, crcTable)
|
||||
|
||||
return img, checksum, nil
|
||||
}
|
90
orbit/pkg/table/crowdstrike/falcon_kernel_check/table.go
Normal file
90
orbit/pkg/table/crowdstrike/falcon_kernel_check/table.go
Normal file
@ -0,0 +1,90 @@
|
||||
package falcon_kernel_check
|
||||
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/tablehelpers"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
)
|
||||
|
||||
const kernelCheckUtilPath = "/opt/CrowdStrike/falcon-kernel-check"
|
||||
|
||||
type Table struct {
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func TablePlugin(logger log.Logger) *table.Plugin {
|
||||
columns := []table.ColumnDefinition{
|
||||
table.TextColumn("kernel"),
|
||||
table.IntegerColumn("supported"),
|
||||
table.IntegerColumn("sensor_version"),
|
||||
}
|
||||
|
||||
tableName := "falcon_kernel_check"
|
||||
|
||||
t := &Table{
|
||||
logger: log.With(logger, "table", tableName),
|
||||
}
|
||||
|
||||
return table.NewPlugin(tableName, columns, t.generate)
|
||||
}
|
||||
|
||||
func (t *Table) generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
|
||||
output, err := tablehelpers.Exec(ctx, t.logger, 5, []string{kernelCheckUtilPath}, []string{}, false)
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log("msg", "exec failed", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
status, err := parseStatus(string(output))
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log("msg", "Error parsing exec status", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results := []map[string]string{status}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// Example falcon-kernel-check output:
|
||||
|
||||
// $ sudo /opt/CrowdStrike/falcon-kernel-check
|
||||
// Host OS 5.13.0-51-generic #58~20.04.1-Ubuntu SMP Tue Jun 14 11:29:12 UTC 2022 is supported by Sensor version 14006.
|
||||
|
||||
// # Upgrade happens
|
||||
// $ sudo /opt/CrowdStrike/falcon-kernel-check
|
||||
// Host OS Linux 5.15.0-46-generic #49~20.04.1-Ubuntu SMP Thu Aug 4 19:15:44 UTC 2022 is not supported by Sensor version 14006.
|
||||
//
|
||||
// This regexp gets matches for the kernel string, supported status, and sensor version number
|
||||
var kernelCheckRegexp = regexp.MustCompile(`^((?:Host OS (.*) (is supported|is not supported)))(?: by Sensor version (\d*))`)
|
||||
|
||||
func parseStatus(status string) (map[string]string, error) {
|
||||
matches := kernelCheckRegexp.FindAllStringSubmatch(status, -1)
|
||||
if len(matches) != 1 {
|
||||
return nil, fmt.Errorf("Failed to match output: %s", status)
|
||||
}
|
||||
if len(matches[0]) != 5 {
|
||||
return nil, fmt.Errorf("Got %d matches. Expected 5. Failed to match output: %s", len(matches[0]), status)
|
||||
}
|
||||
|
||||
// matches[0][2] = kernel version string
|
||||
// matches[0][3] = (is supported|is not supported)
|
||||
// matches[0][4] = sensor version number
|
||||
supported := "0"
|
||||
if matches[0][3] == "is supported" {
|
||||
supported = "1"
|
||||
}
|
||||
|
||||
data := make(map[string]string, 3)
|
||||
data["kernel"] = matches[0][2]
|
||||
data["supported"] = supported
|
||||
data["sensor_version"] = matches[0][4]
|
||||
|
||||
return data, nil
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package falcon_kernel_check
|
||||
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_ParseStatusErrors(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
status string
|
||||
expectedResult map[string]string
|
||||
}{
|
||||
{
|
||||
name: "no status",
|
||||
},
|
||||
{
|
||||
name: "bad output",
|
||||
status: "\n\n\n\n",
|
||||
},
|
||||
{
|
||||
name: "no supported string",
|
||||
status: "Host OS 5.13.0-51-generic #58~20.04.1-Ubuntu SMP Tue Jun 14 11:29:12 UTC 2022 might be supported. idk lol.",
|
||||
},
|
||||
{
|
||||
name: "no sensor version",
|
||||
status: "Host OS 5.13.0-51-generic #58~20.04.1-Ubuntu SMP Tue Jun 14 11:29:12 UTC 2022 is supported",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := parseStatus(tt.status)
|
||||
require.Error(t, err, "parseStatus")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ParseStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
status string
|
||||
expectedResult map[string]string
|
||||
}{
|
||||
{
|
||||
name: "is supported",
|
||||
status: "Host OS 5.13.0-51-generic #58~20.04.1-Ubuntu SMP Tue Jun 14 11:29:12 UTC 2022 is supported by Sensor version 14006.",
|
||||
expectedResult: map[string]string{"kernel": "5.13.0-51-generic #58~20.04.1-Ubuntu SMP Tue Jun 14 11:29:12 UTC 2022", "supported": "1", "sensor_version": "14006"},
|
||||
},
|
||||
{
|
||||
name: "is not supported",
|
||||
status: "Host OS Linux 5.15.0-46-generic #49~20.04.1-Ubuntu SMP Thu Aug 4 19:15:44 UTC 2022 is not supported by Sensor version 14006.",
|
||||
expectedResult: map[string]string{"kernel": "Linux 5.15.0-46-generic #49~20.04.1-Ubuntu SMP Thu Aug 4 19:15:44 UTC 2022", "supported": "0", "sensor_version": "14006"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data, err := parseStatus(tt.status)
|
||||
require.NoError(t, err, "parseStatus")
|
||||
|
||||
assert.Equal(t, tt.expectedResult, data)
|
||||
})
|
||||
}
|
||||
}
|
92
orbit/pkg/table/crowdstrike/falconctl/parser.go
Normal file
92
orbit/pkg/table/crowdstrike/falconctl/parser.go
Normal file
@ -0,0 +1,92 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package falconctl
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parseOptions parses the stdout returned from falconctl's displayed options. As far as we know, output is a single
|
||||
// line, comma separated. We parse multiple lines, but assume data does not space that. Eg: linebreaks and commas
|
||||
// treated as seperators.
|
||||
func parseOptions(reader io.Reader) (any, error) {
|
||||
results := make(map[string]interface{})
|
||||
errors := make([]error, 0)
|
||||
|
||||
// rfm-reason, oddly, produces two KV pairs on a single line. We need to track the last key we saw, and
|
||||
// append to that value.
|
||||
var lastKey string
|
||||
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
// sometimes lines end in , or ., remove them.
|
||||
line = strings.TrimRight(line, ",.")
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
pairs := strings.Split(line, ", ")
|
||||
for _, pair := range pairs {
|
||||
pair = strings.TrimSpace(pair)
|
||||
|
||||
// The format is quite inconsistent. The following sample shows 4 possible
|
||||
// outputs. We'll try to parse them all:
|
||||
//
|
||||
// cid="ac917ab****************************"
|
||||
// aid is not set
|
||||
// aph is not set
|
||||
// app is not set
|
||||
// rfm-state is not set
|
||||
// rfm-reason is not set
|
||||
// rfm-reason=None, code=0x0,
|
||||
// feature is not set
|
||||
// metadata-query=enable (unset default)
|
||||
// version = 6.38.13501.0
|
||||
// We see 4 different formats. We'll try to parse them all.
|
||||
|
||||
if strings.HasSuffix(pair, " is not set") {
|
||||
// What should this be set to? nil? "is not set"? TBD!
|
||||
results[pair[:len(pair)-len(" is not set")]] = "is not set"
|
||||
continue
|
||||
}
|
||||
|
||||
kv := strings.SplitN(pair, "=", 2)
|
||||
if len(kv) == 2 {
|
||||
// remove quotes and extra spaces
|
||||
kv[0] = strings.Trim(kv[0], `" `)
|
||||
kv[1] = strings.Trim(kv[1], `" `)
|
||||
|
||||
// Remove parenthetical note about an unset default
|
||||
if strings.HasSuffix(kv[1], " (unset default)") {
|
||||
kv[1] = kv[1][:len(kv[1])-len(" (unset default)")]
|
||||
}
|
||||
|
||||
if lastKey == "rfm-reason" && kv[0] == "code" {
|
||||
kv[0] = "rfm-reason-code"
|
||||
}
|
||||
|
||||
if kv[0] == "tags" {
|
||||
results[kv[0]] = strings.Split(kv[1], ",")
|
||||
continue
|
||||
}
|
||||
|
||||
results[kv[0]] = kv[1]
|
||||
lastKey = kv[0]
|
||||
continue
|
||||
}
|
||||
|
||||
// Unknown format. Note the error
|
||||
errors = append(errors, fmt.Errorf("unknown format: `%s` on line `%s`", pair, line))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return results, fmt.Errorf("errors parsing: %v", errors)
|
||||
}
|
||||
return results, nil
|
||||
}
|
166
orbit/pkg/table/crowdstrike/falconctl/parser_test.go
Normal file
166
orbit/pkg/table/crowdstrike/falconctl/parser_test.go
Normal file
@ -0,0 +1,166 @@
|
||||
// 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"},
|
||||
},
|
||||
|
||||
// Tags are quite tricky to parse\
|
||||
{
|
||||
name: "--tags",
|
||||
input: []byte(`tags=kolide-test-1,kolide-test-2,`),
|
||||
expected: map[string]any{"tags": []string{"kolide-test-1", "kolide-test-2"}},
|
||||
},
|
||||
{
|
||||
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": []string{"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": []string{"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,
|
||||
},
|
||||
}
|
||||
|
||||
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
|
||||
}
|
142
orbit/pkg/table/crowdstrike/falconctl/table.go
Normal file
142
orbit/pkg/table/crowdstrike/falconctl/table.go
Normal file
@ -0,0 +1,142 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package falconctl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/dataflatten"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/dataflattentable"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/tablehelpers"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
)
|
||||
|
||||
var (
|
||||
falconctlPaths = []string{"/opt/CrowdStrike/falconctl"}
|
||||
|
||||
// allowedOptions is the list of options this table is allowed to query. Notable exceptions
|
||||
// are `systags` (which is parsed seperatedly) and `provisioning-token` (which is a secret).
|
||||
allowedOptions = []string{
|
||||
"--aid",
|
||||
"--apd",
|
||||
"--aph",
|
||||
"--app",
|
||||
"--cid",
|
||||
"--feature",
|
||||
"--metadata-query",
|
||||
"--rfm-reason",
|
||||
"--rfm-state",
|
||||
"--tags",
|
||||
"--version",
|
||||
}
|
||||
|
||||
defaultOption = strings.Join(allowedOptions, " ")
|
||||
)
|
||||
|
||||
type execFunc func(context.Context, log.Logger, int, []string, []string, bool) ([]byte, error)
|
||||
|
||||
type falconctlOptionsTable struct {
|
||||
logger log.Logger
|
||||
tableName string
|
||||
execFunc execFunc
|
||||
}
|
||||
|
||||
func NewFalconctlOptionTable(logger log.Logger) *table.Plugin {
|
||||
columns := dataflattentable.Columns(
|
||||
table.TextColumn("options"),
|
||||
)
|
||||
|
||||
t := &falconctlOptionsTable{
|
||||
logger: log.With(logger, "table", "falconctl_options"),
|
||||
tableName: "falconctl_options",
|
||||
execFunc: tablehelpers.Exec,
|
||||
}
|
||||
|
||||
return table.NewPlugin(t.tableName, columns, t.generate)
|
||||
}
|
||||
|
||||
func (t *falconctlOptionsTable) generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
|
||||
var results []map[string]string
|
||||
|
||||
// Note that we don't use tablehelpers.AllowedValues here, because that would disallow us from
|
||||
// passing `where options = "--aid --aph"`, and allowing that, allows us a single exec.
|
||||
OUTER:
|
||||
for _, requested := range tablehelpers.GetConstraints(
|
||||
queryContext,
|
||||
"options",
|
||||
tablehelpers.WithDefaults(defaultOption),
|
||||
) {
|
||||
|
||||
options := strings.Split(requested, " ")
|
||||
|
||||
// Check that all requested options are allowed
|
||||
for _, option := range options {
|
||||
option = strings.Trim(option, " ")
|
||||
if !optionAllowed(option) {
|
||||
level.Info(t.logger).Log("msg", "requested option not allowed", "option", option)
|
||||
continue OUTER
|
||||
}
|
||||
}
|
||||
|
||||
rowData := map[string]string{"options": requested}
|
||||
|
||||
// As I understand it the falconctl command line uses `-g` to indicate it's fetching the options settings, and
|
||||
// then the list of options to fetch. Set the command line thusly.
|
||||
args := append([]string{"-g"}, options...)
|
||||
|
||||
output, err := t.execFunc(ctx, t.logger, 30, falconctlPaths, args, false)
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log("msg", "exec failed", "err", err)
|
||||
synthesizedData := map[string]string{
|
||||
"_error": fmt.Sprintf("falconctl parse failure: %s", err),
|
||||
}
|
||||
|
||||
flattened, err := dataflatten.Flatten(synthesizedData)
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log("msg", "failure flattening output", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, dataflattentable.ToMap(flattened, "", rowData)...)
|
||||
continue
|
||||
}
|
||||
|
||||
parsed, err := parseOptions(bytes.NewReader(output))
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log("msg", "parse failed", "err", err)
|
||||
parsed = map[string]string{
|
||||
"_error": fmt.Sprintf("falconctl parse failure: %s", err),
|
||||
}
|
||||
}
|
||||
|
||||
for _, dataQuery := range tablehelpers.GetConstraints(queryContext, "query", tablehelpers.WithDefaults("*")) {
|
||||
flattenOpts := []dataflatten.FlattenOpts{
|
||||
dataflatten.WithLogger(t.logger),
|
||||
dataflatten.WithQuery(strings.Split(dataQuery, "/")),
|
||||
}
|
||||
|
||||
flattened, err := dataflatten.Flatten(parsed, flattenOpts...)
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log("msg", "failure flattening output", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, dataflattentable.ToMap(flattened, dataQuery, rowData)...)
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func optionAllowed(opt string) bool {
|
||||
for _, b := range allowedOptions {
|
||||
if b == opt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
88
orbit/pkg/table/crowdstrike/falconctl/table_test.go
Normal file
88
orbit/pkg/table/crowdstrike/falconctl/table_test.go
Normal file
@ -0,0 +1,88 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package falconctl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/tablehelpers"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestOptionRestrictions tests that the table only allows the options we expect.
|
||||
func TestOptionRestrictions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
options []string
|
||||
expectedExecs int
|
||||
expectedDisallows int
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
expectedExecs: 1,
|
||||
expectedDisallows: 0,
|
||||
},
|
||||
{
|
||||
name: "allowed options as array",
|
||||
options: []string{"--aid", "--aph"},
|
||||
expectedExecs: 2,
|
||||
expectedDisallows: 0,
|
||||
},
|
||||
{
|
||||
name: "allowed options as string",
|
||||
options: []string{"--aid --aph"},
|
||||
expectedExecs: 1,
|
||||
expectedDisallows: 0,
|
||||
},
|
||||
{
|
||||
name: "disallowed option as array",
|
||||
options: []string{"--not-allowed", "--definitely-not-allowed", "--aid", "--aph"},
|
||||
expectedExecs: 2,
|
||||
expectedDisallows: 2,
|
||||
},
|
||||
{
|
||||
name: "disallowed option as string",
|
||||
options: []string{"--aid --aph --not-allowed"},
|
||||
expectedExecs: 0,
|
||||
expectedDisallows: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var logBytes bytes.Buffer
|
||||
|
||||
testTable := &falconctlOptionsTable{
|
||||
logger: log.NewLogfmtLogger(&logBytes),
|
||||
execFunc: noopExec,
|
||||
}
|
||||
|
||||
mockQC := tablehelpers.MockQueryContext(map[string][]string{
|
||||
"options": tt.options,
|
||||
})
|
||||
|
||||
_, err := testTable.generate(context.TODO(), mockQC)
|
||||
require.NoError(t, err)
|
||||
|
||||
// test the number of times exec was called
|
||||
require.Equal(t, tt.expectedExecs, strings.Count(logBytes.String(), "exec-in-test"))
|
||||
|
||||
// test the number of times we disallowed an option
|
||||
require.Equal(t, tt.expectedDisallows, strings.Count(logBytes.String(), "requested option not allowed"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func noopExec(_ context.Context, log log.Logger, _ int, _ []string, args []string, _ bool) ([]byte, error) {
|
||||
log.Log("exec", "exec-in-test", "args", strings.Join(args, " "))
|
||||
return []byte{}, nil
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
CID is not set. Use falconctl to set the CID
|
||||
ERROR: failed to process the option --cid
|
@ -0,0 +1 @@
|
||||
cid="ac917ab****************************", aid is not set, aph is not set, app is not set, rfm-state is not set, rfm-reason is not set, feature is not set, metadata-query=enable (unset default), version = 6.38.13501.0
|
108
orbit/pkg/table/cryptoinfotable/table.go
Normal file
108
orbit/pkg/table/cryptoinfotable/table.go
Normal file
@ -0,0 +1,108 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package cryptoinfotable
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/cryptoinfo"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/dataflatten"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/dataflattentable"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/tablehelpers"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
)
|
||||
|
||||
type Table struct {
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func TablePlugin(logger log.Logger) *table.Plugin {
|
||||
columns := dataflattentable.Columns(
|
||||
table.TextColumn("passphrase"),
|
||||
table.TextColumn("path"),
|
||||
)
|
||||
|
||||
t := &Table{
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
return table.NewPlugin("cryptoinfo", columns, t.generate)
|
||||
}
|
||||
|
||||
func (t *Table) generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
|
||||
var results []map[string]string
|
||||
|
||||
requestedPaths := tablehelpers.GetConstraints(queryContext, "path")
|
||||
if len(requestedPaths) == 0 {
|
||||
return results, errors.New("The cryptoinfo table requires that you specify an equals constraint for path")
|
||||
}
|
||||
|
||||
for _, requestedPath := range requestedPaths {
|
||||
|
||||
// We take globs in via the sql %, but glob needs *. So convert.
|
||||
filePaths, err := filepath.Glob(strings.ReplaceAll(requestedPath, `%`, `*`))
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log("msg", "bad file glob", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, filePath := range filePaths {
|
||||
for _, dataQuery := range tablehelpers.GetConstraints(queryContext, "query", tablehelpers.WithDefaults("*")) {
|
||||
for _, passphrase := range tablehelpers.GetConstraints(queryContext, "passphrase", tablehelpers.WithDefaults("")) {
|
||||
|
||||
flattenOpts := []dataflatten.FlattenOpts{
|
||||
dataflatten.WithLogger(t.logger),
|
||||
dataflatten.WithNestedPlist(),
|
||||
dataflatten.WithQuery(strings.Split(dataQuery, "/")),
|
||||
}
|
||||
|
||||
flatData, err := flattenCryptoInfo(filePath, passphrase, flattenOpts...)
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log(
|
||||
"msg", "failed to get data for path",
|
||||
"path", filePath,
|
||||
"err", err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
rowData := map[string]string{
|
||||
"path": filePath,
|
||||
"passphrase": passphrase,
|
||||
}
|
||||
results = append(results, dataflattentable.ToMap(flatData, dataQuery, rowData)...)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// flattenCryptoInfo is a small wrapper over pkg/cryptoinfo that passes it off to dataflatten for table generation
|
||||
func flattenCryptoInfo(filename, passphrase string, opts ...dataflatten.FlattenOpts) ([]dataflatten.Row, error) {
|
||||
filebytes, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading %s: %w", filename, err)
|
||||
}
|
||||
|
||||
result, err := cryptoinfo.Identify(filebytes, passphrase)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing with cryptoinfo: %w", err)
|
||||
}
|
||||
|
||||
// convert to json, so it's parsable
|
||||
jsonBytes, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("json: %w", err)
|
||||
}
|
||||
|
||||
return dataflatten.Json(jsonBytes, opts...)
|
||||
}
|
91
orbit/pkg/table/cryptsetup/parser.go
Normal file
91
orbit/pkg/table/cryptsetup/parser.go
Normal file
@ -0,0 +1,91 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package cryptsetup
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parseStatus parses the output from `cryptsetup status`. This is a
|
||||
// pretty simple key, value format, but does have a free form first
|
||||
// line. It's not clear if this is going to be stable, or change
|
||||
// across versions.
|
||||
func parseStatus(rawdata []byte) (map[string]interface{}, error) {
|
||||
var data map[string]interface{}
|
||||
|
||||
if len(rawdata) == 0 {
|
||||
return nil, errors.New("No data")
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewReader(rawdata))
|
||||
firstLine := true
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if firstLine {
|
||||
var err error
|
||||
data, err = parseFirstLine(line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
firstLine = false
|
||||
continue
|
||||
}
|
||||
|
||||
kv := strings.SplitN(line, ": ", 2)
|
||||
|
||||
// blank lines, or other unexpected input can just be skipped.
|
||||
if len(kv) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
data[strings.ReplaceAll(strings.TrimSpace(kv[0]), " ", "_")] = strings.TrimSpace(kv[1])
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// regexp for the first line of the status output.
|
||||
var firstLineRegexp = regexp.MustCompile(`^(?:Device (.*) (not found))|(?:(.*?) is ([a-z]+)(?:\.| and is (in use)))`)
|
||||
|
||||
// parseFirstLine parses the first line of the status output. This
|
||||
// appears to be a free form string indicating several pieces of
|
||||
// information. It is parsed with a single regexp. (See tests for
|
||||
// examples)
|
||||
func parseFirstLine(line string) (map[string]interface{}, error) {
|
||||
if line == "" {
|
||||
return nil, errors.New("Invalid first line")
|
||||
}
|
||||
|
||||
m := firstLineRegexp.FindAllStringSubmatch(line, -1)
|
||||
if len(m) != 1 {
|
||||
return nil, fmt.Errorf("Failed to match first line: %s", line)
|
||||
}
|
||||
if len(m[0]) != 6 {
|
||||
return nil, fmt.Errorf("Got %d matches. Expected 6. Failed to match first line: %s", len(m[0]), line)
|
||||
}
|
||||
|
||||
data := make(map[string]interface{}, 3)
|
||||
|
||||
// check for $1 and $2 for the error condition
|
||||
if m[0][1] != "" && m[0][2] != "" {
|
||||
data["short_name"] = m[0][1]
|
||||
data["status"] = strings.ReplaceAll(m[0][2], " ", "_")
|
||||
data["mounted"] = strconv.FormatBool(false)
|
||||
return data, nil
|
||||
}
|
||||
|
||||
if m[0][3] != "" && m[0][4] != "" {
|
||||
data["display_name"] = m[0][3]
|
||||
data["status"] = strings.ReplaceAll(m[0][4], " ", "_")
|
||||
data["mounted"] = strconv.FormatBool(m[0][5] != "")
|
||||
return data, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Unknown first line: %s", line)
|
||||
}
|
135
orbit/pkg/table/cryptsetup/parser_test.go
Normal file
135
orbit/pkg/table/cryptsetup/parser_test.go
Normal file
@ -0,0 +1,135 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package cryptsetup
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseStatusErrors(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
}{
|
||||
{
|
||||
input: "",
|
||||
},
|
||||
{
|
||||
input: "\n\n\n\n",
|
||||
},
|
||||
{
|
||||
input: "type: LUKS2",
|
||||
},
|
||||
{
|
||||
input: "Hello world",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run("", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data, err := parseStatus([]byte(tt.input))
|
||||
assert.Error(t, err, "parseStatus")
|
||||
assert.Nil(t, data, "data is nil")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
infile string
|
||||
len int
|
||||
status string
|
||||
mounted bool
|
||||
ctype string
|
||||
keysize string
|
||||
key_location string
|
||||
}{
|
||||
{
|
||||
infile: "status-active-luks1.txt",
|
||||
status: "active",
|
||||
mounted: true,
|
||||
ctype: "LUKS1",
|
||||
keysize: "512 bits",
|
||||
key_location: "dm-crypt",
|
||||
},
|
||||
{
|
||||
infile: "status-active-luks2.txt",
|
||||
status: "active",
|
||||
mounted: true,
|
||||
ctype: "LUKS2",
|
||||
keysize: "512 bits",
|
||||
key_location: "keyring",
|
||||
},
|
||||
{
|
||||
infile: "status-active-mounted.txt",
|
||||
status: "active",
|
||||
mounted: true,
|
||||
ctype: "PLAIN",
|
||||
keysize: "256 bits",
|
||||
key_location: "dm-crypt",
|
||||
},
|
||||
{
|
||||
infile: "status-active-umounted.txt",
|
||||
status: "active",
|
||||
ctype: "PLAIN",
|
||||
keysize: "256 bits",
|
||||
key_location: "dm-crypt",
|
||||
},
|
||||
{
|
||||
infile: "status-active.txt",
|
||||
status: "active",
|
||||
mounted: true,
|
||||
ctype: "PLAIN",
|
||||
keysize: "256 bits",
|
||||
key_location: "dm-crypt",
|
||||
},
|
||||
{
|
||||
infile: "status-error.txt",
|
||||
status: "not_found",
|
||||
},
|
||||
{
|
||||
infile: "status-inactive.txt",
|
||||
status: "inactive",
|
||||
},
|
||||
{
|
||||
infile: "status-unactive.txt",
|
||||
status: "inactive",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.infile, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input, err := os.ReadFile(filepath.Join("testdata", tt.infile))
|
||||
require.NoError(t, err, "read input file")
|
||||
|
||||
data, err := parseStatus(input)
|
||||
require.NoError(t, err, "parseStatus")
|
||||
|
||||
assert.Equal(t, tt.status, data["status"], "status")
|
||||
assert.Equal(t, strconv.FormatBool(tt.mounted), data["mounted"], "mounted")
|
||||
|
||||
// These values aren't populated in the map,
|
||||
// so only check them if the test case lists
|
||||
// them
|
||||
if tt.ctype != "" {
|
||||
assert.Equal(t, tt.ctype, data["type"], "type")
|
||||
assert.Equal(t, tt.keysize, data["keysize"], "keysize")
|
||||
assert.Equal(t, tt.key_location, data["key_location"], "key_location")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
90
orbit/pkg/table/cryptsetup/table.go
Normal file
90
orbit/pkg/table/cryptsetup/table.go
Normal file
@ -0,0 +1,90 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package cryptsetup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/dataflatten"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/dataflattentable"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/tablehelpers"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
)
|
||||
|
||||
var cryptsetupPaths = []string{
|
||||
"/usr/sbin/cryptsetup",
|
||||
"/sbin/cryptsetup",
|
||||
}
|
||||
|
||||
const allowedNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/_"
|
||||
|
||||
type Table struct {
|
||||
logger log.Logger
|
||||
name string
|
||||
}
|
||||
|
||||
func TablePlugin(logger log.Logger) *table.Plugin {
|
||||
columns := dataflattentable.Columns(
|
||||
table.TextColumn("name"),
|
||||
)
|
||||
|
||||
t := &Table{
|
||||
logger: logger,
|
||||
name: "cryptsetup_status",
|
||||
}
|
||||
|
||||
return table.NewPlugin(t.name, columns, t.generate)
|
||||
}
|
||||
|
||||
func (t *Table) generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
|
||||
var results []map[string]string
|
||||
|
||||
requestedNames := tablehelpers.GetConstraints(queryContext, "name",
|
||||
tablehelpers.WithAllowedCharacters(allowedNameCharacters),
|
||||
tablehelpers.WithLogger(t.logger),
|
||||
)
|
||||
|
||||
if len(requestedNames) == 0 {
|
||||
return results, fmt.Errorf("The %s table requires that you specify a constraint for name", t.name)
|
||||
}
|
||||
|
||||
for _, name := range requestedNames {
|
||||
output, err := tablehelpers.Exec(ctx, t.logger, 15, cryptsetupPaths, []string{"--readonly", "status", name}, false)
|
||||
if err != nil {
|
||||
level.Debug(t.logger).Log("msg", "Error execing for status", "name", name, "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
status, err := parseStatus(output)
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log("msg", "Error parsing status", "name", name, "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, dataQuery := range tablehelpers.GetConstraints(queryContext, "query", tablehelpers.WithDefaults("*")) {
|
||||
flatData, err := t.flattenOutput(dataQuery, status)
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log("msg", "flatten failed", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
rowData := map[string]string{"name": name}
|
||||
|
||||
results = append(results, dataflattentable.ToMap(flatData, dataQuery, rowData)...)
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (t *Table) flattenOutput(dataQuery string, status map[string]interface{}) ([]dataflatten.Row, error) {
|
||||
flattenOpts := []dataflatten.FlattenOpts{
|
||||
dataflatten.WithLogger(t.logger),
|
||||
dataflatten.WithQuery(strings.Split(dataQuery, "/")),
|
||||
}
|
||||
|
||||
return dataflatten.Flatten(status, flattenOpts...)
|
||||
}
|
11
orbit/pkg/table/cryptsetup/testdata/status-active-luks1.txt
vendored
Normal file
11
orbit/pkg/table/cryptsetup/testdata/status-active-luks1.txt
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/dev/mapper/dm-crypto-luks1 is active and is in use.
|
||||
type: LUKS1
|
||||
cipher: aes-xts-plain64
|
||||
keysize: 512 bits
|
||||
key location: dm-crypt
|
||||
device: /dev/sdc1
|
||||
sector size: 512
|
||||
offset: 4096 sectors
|
||||
size: 8382464 sectors
|
||||
mode: read/write
|
||||
|
10
orbit/pkg/table/cryptsetup/testdata/status-active-luks2.txt
vendored
Normal file
10
orbit/pkg/table/cryptsetup/testdata/status-active-luks2.txt
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/dev/mapper/dm-crypto-luks2 is active and is in use.
|
||||
type: LUKS2
|
||||
cipher: aes-xts-plain64
|
||||
keysize: 512 bits
|
||||
key location: keyring
|
||||
device: /dev/sdc2
|
||||
sector size: 512
|
||||
offset: 32768 sectors
|
||||
size: 8355840 sectors
|
||||
mode: read/write
|
10
orbit/pkg/table/cryptsetup/testdata/status-active-mounted.txt
vendored
Normal file
10
orbit/pkg/table/cryptsetup/testdata/status-active-mounted.txt
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/dev/mapper/dm-crypto-plain is active and is in use.
|
||||
type: PLAIN
|
||||
cipher: aes-cbc-essiv:sha256
|
||||
keysize: 256 bits
|
||||
key location: dm-crypt
|
||||
device: /dev/sdc3
|
||||
sector size: 512
|
||||
offset: 0 sectors
|
||||
size: 8388608 sectors
|
||||
mode: read/write
|
10
orbit/pkg/table/cryptsetup/testdata/status-active-umounted.txt
vendored
Normal file
10
orbit/pkg/table/cryptsetup/testdata/status-active-umounted.txt
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/dev/mapper/dm-crypto-plain is active.
|
||||
type: PLAIN
|
||||
cipher: aes-cbc-essiv:sha256
|
||||
keysize: 256 bits
|
||||
key location: dm-crypt
|
||||
device: /dev/sdc3
|
||||
sector size: 512
|
||||
offset: 0 sectors
|
||||
size: 8388608 sectors
|
||||
mode: read/write
|
10
orbit/pkg/table/cryptsetup/testdata/status-active.txt
vendored
Normal file
10
orbit/pkg/table/cryptsetup/testdata/status-active.txt
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/dev/mapper/dm-crypto-plain is active and is in use.
|
||||
type: PLAIN
|
||||
cipher: aes-cbc-essiv:sha256
|
||||
keysize: 256 bits
|
||||
key location: dm-crypt
|
||||
device: /dev/sdc3
|
||||
sector size: 512
|
||||
offset: 0 sectors
|
||||
size: 8388608 sectors
|
||||
mode: read/write
|
1
orbit/pkg/table/cryptsetup/testdata/status-error.txt
vendored
Normal file
1
orbit/pkg/table/cryptsetup/testdata/status-error.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
Device sdc3 not found
|
1
orbit/pkg/table/cryptsetup/testdata/status-inactive.txt
vendored
Normal file
1
orbit/pkg/table/cryptsetup/testdata/status-inactive.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
/dev/mapper/dm-crypto-plain is inactive.
|
1
orbit/pkg/table/cryptsetup/testdata/status-unactive.txt
vendored
Normal file
1
orbit/pkg/table/cryptsetup/testdata/status-unactive.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
/dev/mapper/dm-crypto-unknown is inactive.
|
139
orbit/pkg/table/dataflattentable/exec.go
Normal file
139
orbit/pkg/table/dataflattentable/exec.go
Normal file
@ -0,0 +1,139 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflattentable
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/dataflatten"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/tablehelpers"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
)
|
||||
|
||||
type ExecTableOpt func(*Table)
|
||||
|
||||
// WithKVSeparator sets the delimiter between key and value. It replaces the
|
||||
// default ":" in dataflattentable.Table
|
||||
func WithKVSeparator(separator string) ExecTableOpt {
|
||||
return func(t *Table) {
|
||||
t.keyValueSeparator = separator
|
||||
}
|
||||
}
|
||||
|
||||
func WithBinDirs(binDirs ...string) ExecTableOpt {
|
||||
return func(t *Table) {
|
||||
t.binDirs = binDirs
|
||||
}
|
||||
}
|
||||
|
||||
func TablePluginExec(logger log.Logger, tableName string, dataSourceType DataSourceType, execArgs []string, opts ...ExecTableOpt) *table.Plugin {
|
||||
columns := Columns()
|
||||
|
||||
t := &Table{
|
||||
logger: level.NewFilter(logger, level.AllowInfo()),
|
||||
tableName: tableName,
|
||||
execArgs: execArgs,
|
||||
keyValueSeparator: ":",
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(t)
|
||||
}
|
||||
|
||||
switch dataSourceType {
|
||||
case PlistType:
|
||||
t.flattenBytesFunc = dataflatten.Plist
|
||||
case JsonType:
|
||||
t.flattenBytesFunc = dataflatten.Json
|
||||
case KeyValueType:
|
||||
// TODO: allow callers of TablePluginExec to specify the record
|
||||
// splitting strategy
|
||||
t.flattenBytesFunc = dataflatten.StringDelimitedFunc(t.keyValueSeparator, dataflatten.DuplicateKeys)
|
||||
default:
|
||||
panic("Unknown data source type")
|
||||
}
|
||||
|
||||
return table.NewPlugin(t.tableName, columns, t.generateExec)
|
||||
}
|
||||
|
||||
func (t *Table) generateExec(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
|
||||
var results []map[string]string
|
||||
|
||||
execBytes, err := t.exec(ctx)
|
||||
if err != nil {
|
||||
// exec will error if there's no binary, so we never want to record that
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If the exec failed for some reason, it's probably better to return no results, and log the,
|
||||
// error. Returning an error here will cause a table failure, and thus break joins
|
||||
level.Info(t.logger).Log("msg", "failed to exec", "err", err)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for _, dataQuery := range tablehelpers.GetConstraints(queryContext, "query", tablehelpers.WithDefaults("*")) {
|
||||
flattenOpts := []dataflatten.FlattenOpts{
|
||||
dataflatten.WithLogger(t.logger),
|
||||
dataflatten.WithQuery(strings.Split(dataQuery, "/")),
|
||||
}
|
||||
|
||||
flattened, err := t.flattenBytesFunc(execBytes, flattenOpts...)
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log("msg", "failure flattening output", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, ToMap(flattened, dataQuery, nil)...)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (t *Table) exec(ctx context.Context) ([]byte, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 50*time.Second)
|
||||
defer cancel()
|
||||
|
||||
possibleBinaries := []string{}
|
||||
|
||||
if t.binDirs == nil || len(t.binDirs) == 0 {
|
||||
possibleBinaries = []string{t.execArgs[0]}
|
||||
} else {
|
||||
for _, possiblePath := range t.binDirs {
|
||||
possibleBinaries = append(possibleBinaries, filepath.Join(possiblePath, t.execArgs[0]))
|
||||
}
|
||||
}
|
||||
|
||||
for _, execPath := range possibleBinaries {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
cmd := exec.CommandContext(ctx, execPath, t.execArgs[1:]...)
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
level.Debug(t.logger).Log("msg", "calling %s", "args", cmd.String())
|
||||
|
||||
if err := cmd.Run(); os.IsNotExist(err) {
|
||||
// try the next binary
|
||||
continue
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("calling %s. Got: %s: %w", t.execArgs[0], string(stderr.Bytes()), err)
|
||||
}
|
||||
|
||||
// success!
|
||||
return stdout.Bytes(), nil
|
||||
}
|
||||
|
||||
// None of the possible execs were found
|
||||
return nil, fmt.Errorf("Unable to exec '%s'. No binary found is specified paths", t.execArgs[0])
|
||||
}
|
47
orbit/pkg/table/dataflattentable/helpers.go
Normal file
47
orbit/pkg/table/dataflattentable/helpers.go
Normal file
@ -0,0 +1,47 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflattentable
|
||||
|
||||
import (
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/dataflatten"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
)
|
||||
|
||||
// ToMap is a helper function to convert Flatten output directly for
|
||||
// consumption by osquery tables.
|
||||
func ToMap(rows []dataflatten.Row, query string, rowData map[string]string) []map[string]string {
|
||||
results := make([]map[string]string, len(rows))
|
||||
|
||||
for i, row := range rows {
|
||||
res := make(map[string]string, len(rowData)+5)
|
||||
for k, v := range rowData {
|
||||
res[k] = v
|
||||
}
|
||||
|
||||
p, k := row.ParentKey("/")
|
||||
|
||||
res["fullkey"] = row.StringPath("/")
|
||||
res["parent"] = p
|
||||
res["key"] = k
|
||||
res["value"] = row.Value
|
||||
res["query"] = query
|
||||
|
||||
results[i] = res
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// Columns returns the standard data flatten columns, plus whatever
|
||||
// ones have been provided as additional. This is syntantic sugar for
|
||||
// dataflatten based tables.
|
||||
func Columns(additional ...table.ColumnDefinition) []table.ColumnDefinition {
|
||||
columns := []table.ColumnDefinition{
|
||||
table.TextColumn("fullkey"),
|
||||
table.TextColumn("parent"),
|
||||
table.TextColumn("key"),
|
||||
table.TextColumn("value"),
|
||||
table.TextColumn("query"),
|
||||
}
|
||||
|
||||
return append(columns, additional...)
|
||||
}
|
81
orbit/pkg/table/dataflattentable/plist_test.go
Normal file
81
orbit/pkg/table/dataflattentable/plist_test.go
Normal file
@ -0,0 +1,81 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflattentable
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/dataflatten"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/tablehelpers"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestPlist runs some real-world tests against sample plist data.
|
||||
func TestPlist(t *testing.T) {
|
||||
t.Parallel()
|
||||
plistTable := Table{flattenFileFunc: dataflatten.PlistFile}
|
||||
|
||||
tests := []struct {
|
||||
paths []string
|
||||
queries []string
|
||||
expected []map[string]string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
paths: []string{filepath.Join("testdata", "NetworkInterfaces.plist")},
|
||||
queries: []string{"Interfaces/#BSD Name/SCNetworkInterfaceType/FireWire"},
|
||||
expected: []map[string]string{
|
||||
{
|
||||
"fullkey": "Interfaces/fw0/SCNetworkInterfaceType",
|
||||
"key": "SCNetworkInterfaceType",
|
||||
"parent": "Interfaces/fw0",
|
||||
"value": "FireWire",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
paths: []string{filepath.Join("testdata", "com.apple.launchservices.secure.plist")},
|
||||
queries: []string{
|
||||
"LSHandlers/LSHandlerURLScheme=>htt*/LSHandlerRole*",
|
||||
"LSHandlers/LSHandlerContentType=>*html/LSHandlerRole*",
|
||||
},
|
||||
expected: []map[string]string{
|
||||
{"fullkey": "LSHandlers/5/LSHandlerRoleAll", "key": "LSHandlerRoleAll", "parent": "LSHandlers/5", "value": "com.choosyosx.choosy"},
|
||||
{"fullkey": "LSHandlers/6/LSHandlerRoleAll", "key": "LSHandlerRoleAll", "parent": "LSHandlers/6", "value": "com.choosyosx.choosy"},
|
||||
{"fullkey": "LSHandlers/7/LSHandlerRoleAll", "key": "LSHandlerRoleAll", "parent": "LSHandlers/7", "value": "com.choosyosx.choosy"},
|
||||
{"fullkey": "LSHandlers/8/LSHandlerRoleAll", "key": "LSHandlerRoleAll", "parent": "LSHandlers/8", "value": "com.google.chrome"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
mockQC := tablehelpers.MockQueryContext(map[string][]string{
|
||||
"path": tt.paths,
|
||||
"query": tt.queries,
|
||||
})
|
||||
|
||||
rows, err := plistTable.generate(context.TODO(), mockQC)
|
||||
if tt.err {
|
||||
require.Error(t, err)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
// delete the path and query keys, so we don't need to enumerate them in the test case
|
||||
for _, row := range rows {
|
||||
delete(row, "path")
|
||||
delete(row, "query")
|
||||
}
|
||||
|
||||
// Despite being an array. data is returned unordered. Sort it.
|
||||
sort.SliceStable(tt.expected, func(i, j int) bool { return tt.expected[i]["fullkey"] < tt.expected[j]["fullkey"] })
|
||||
sort.SliceStable(rows, func(i, j int) bool { return rows[i]["fullkey"] < rows[j]["fullkey"] })
|
||||
|
||||
require.EqualValues(t, tt.expected, rows)
|
||||
}
|
||||
}
|
137
orbit/pkg/table/dataflattentable/tables.go
Normal file
137
orbit/pkg/table/dataflattentable/tables.go
Normal file
@ -0,0 +1,137 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflattentable
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/dataflatten"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/tablehelpers"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/osquery/osquery-go"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
)
|
||||
|
||||
type DataSourceType int
|
||||
|
||||
const (
|
||||
PlistType DataSourceType = iota + 1
|
||||
JsonType
|
||||
JsonlType
|
||||
ExecType
|
||||
XmlType
|
||||
IniType
|
||||
KeyValueType
|
||||
)
|
||||
|
||||
type Table struct {
|
||||
logger log.Logger
|
||||
tableName string
|
||||
|
||||
flattenFileFunc func(string, ...dataflatten.FlattenOpts) ([]dataflatten.Row, error)
|
||||
flattenBytesFunc func([]byte, ...dataflatten.FlattenOpts) ([]dataflatten.Row, error)
|
||||
|
||||
execArgs []string
|
||||
binDirs []string
|
||||
|
||||
keyValueSeparator string
|
||||
}
|
||||
|
||||
// AllTablePlugins is a helper to return all the expected flattening tables.
|
||||
func AllTablePlugins(logger log.Logger) []osquery.OsqueryPlugin {
|
||||
return []osquery.OsqueryPlugin{
|
||||
TablePlugin(logger, JsonType),
|
||||
TablePlugin(logger, XmlType),
|
||||
TablePlugin(logger, IniType),
|
||||
TablePlugin(logger, PlistType),
|
||||
TablePlugin(logger, JsonlType),
|
||||
}
|
||||
}
|
||||
|
||||
func TablePlugin(logger log.Logger, dataSourceType DataSourceType) osquery.OsqueryPlugin {
|
||||
columns := Columns(table.TextColumn("path"))
|
||||
|
||||
t := &Table{
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
switch dataSourceType {
|
||||
case PlistType:
|
||||
t.flattenFileFunc = dataflatten.PlistFile
|
||||
t.tableName = "parse_plist"
|
||||
case JsonType:
|
||||
t.flattenFileFunc = dataflatten.JsonFile
|
||||
t.tableName = "parse_json"
|
||||
case JsonlType:
|
||||
t.flattenFileFunc = dataflatten.JsonlFile
|
||||
t.tableName = "parse_jsonl"
|
||||
case XmlType:
|
||||
t.flattenFileFunc = dataflatten.XmlFile
|
||||
t.tableName = "parse_xml"
|
||||
case IniType:
|
||||
t.flattenFileFunc = dataflatten.IniFile
|
||||
t.tableName = "parse_ini"
|
||||
default:
|
||||
panic("Unknown data source type")
|
||||
}
|
||||
|
||||
return table.NewPlugin(t.tableName, columns, t.generate)
|
||||
}
|
||||
|
||||
func (t *Table) generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
|
||||
var results []map[string]string
|
||||
|
||||
requestedPaths := tablehelpers.GetConstraints(queryContext, "path")
|
||||
if len(requestedPaths) == 0 {
|
||||
return results, fmt.Errorf("The %s table requires that you specify a single constraint for path", t.tableName)
|
||||
}
|
||||
|
||||
for _, requestedPath := range requestedPaths {
|
||||
|
||||
// We take globs in via the sql %, but glob needs *. So convert.
|
||||
filePaths, err := filepath.Glob(strings.ReplaceAll(requestedPath, `%`, `*`))
|
||||
if err != nil {
|
||||
return results, fmt.Errorf("bad glob: %w", err)
|
||||
}
|
||||
|
||||
for _, filePath := range filePaths {
|
||||
for _, dataQuery := range tablehelpers.GetConstraints(queryContext, "query", tablehelpers.WithDefaults("*")) {
|
||||
subresults, err := t.generatePath(filePath, dataQuery)
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log(
|
||||
"msg", "failed to get data for path",
|
||||
"path", filePath,
|
||||
"err", err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, subresults...)
|
||||
}
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (t *Table) generatePath(filePath string, dataQuery string) ([]map[string]string, error) {
|
||||
flattenOpts := []dataflatten.FlattenOpts{
|
||||
dataflatten.WithLogger(t.logger),
|
||||
dataflatten.WithNestedPlist(),
|
||||
dataflatten.WithQuery(strings.Split(dataQuery, "/")),
|
||||
}
|
||||
|
||||
data, err := t.flattenFileFunc(filePath, flattenOpts...)
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log("msg", "failure parsing file", "file", filePath)
|
||||
return nil, fmt.Errorf("parsing data: %w", err)
|
||||
}
|
||||
|
||||
rowData := map[string]string{
|
||||
"path": filePath,
|
||||
}
|
||||
|
||||
return ToMap(data, dataQuery, rowData), nil
|
||||
}
|
160
orbit/pkg/table/dataflattentable/tables_test.go
Normal file
160
orbit/pkg/table/dataflattentable/tables_test.go
Normal file
@ -0,0 +1,160 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package dataflattentable
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/dataflatten"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/tablehelpers"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestDataFlattenTable_Animals tests the basic generation
|
||||
// functionality for both plist and json parsing using the mock
|
||||
// animals data.
|
||||
func TestDataFlattenTablePlist_Animals(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
|
||||
// Test plist parsing both the json and xml forms
|
||||
testTables := map[string]Table{
|
||||
"plist": {logger: logger, flattenFileFunc: dataflatten.PlistFile},
|
||||
"xml": {logger: logger, flattenFileFunc: dataflatten.PlistFile},
|
||||
"json": {logger: logger, flattenFileFunc: dataflatten.JsonFile},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
queries []string
|
||||
expected []map[string]string
|
||||
}{
|
||||
{
|
||||
queries: []string{
|
||||
"metadata",
|
||||
},
|
||||
expected: []map[string]string{
|
||||
{"fullkey": "metadata/testing", "key": "testing", "parent": "metadata", "value": "true"},
|
||||
{"fullkey": "metadata/version", "key": "version", "parent": "metadata", "value": "1.0.1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
queries: []string{
|
||||
"users/name=>*Aardvark/id",
|
||||
"users/name=>*Chipmunk/id",
|
||||
},
|
||||
expected: []map[string]string{
|
||||
{"fullkey": "users/0/id", "key": "id", "parent": "users/0", "value": "1"},
|
||||
{"fullkey": "users/2/id", "key": "id", "parent": "users/2", "value": "3"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
for dataType, tableFunc := range testTables {
|
||||
testFile := filepath.Join("testdata", "animals."+dataType)
|
||||
mockQC := tablehelpers.MockQueryContext(map[string][]string{
|
||||
"path": {testFile},
|
||||
"query": tt.queries,
|
||||
})
|
||||
|
||||
rows, err := tableFunc.generate(context.TODO(), mockQC)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
// delete the path and query keys, so we don't need to enumerate them in the test case
|
||||
for _, row := range rows {
|
||||
delete(row, "path")
|
||||
delete(row, "query")
|
||||
}
|
||||
|
||||
// Despite being an array. data is returned unordered. Sort it.
|
||||
sort.SliceStable(tt.expected, func(i, j int) bool { return tt.expected[i]["fullkey"] < tt.expected[j]["fullkey"] })
|
||||
sort.SliceStable(rows, func(i, j int) bool { return rows[i]["fullkey"] < rows[j]["fullkey"] })
|
||||
|
||||
require.EqualValues(t, tt.expected, rows, "table type %s test", dataType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDataFlattenTables(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
|
||||
tests := []struct {
|
||||
testTables map[string]Table
|
||||
testFile string
|
||||
queries []string
|
||||
expectedRows int
|
||||
expectNoData bool
|
||||
}{
|
||||
// xml
|
||||
{
|
||||
testTables: map[string]Table{"xml": {logger: logger, flattenFileFunc: dataflatten.XmlFile}},
|
||||
testFile: path.Join("testdata", "simple.xml"),
|
||||
expectedRows: 6,
|
||||
},
|
||||
{
|
||||
testTables: map[string]Table{"xml": {logger: logger, flattenFileFunc: dataflatten.XmlFile}},
|
||||
testFile: path.Join("testdata", "simple.xml"),
|
||||
queries: []string{"simple/Items"},
|
||||
expectedRows: 3,
|
||||
},
|
||||
{
|
||||
testTables: map[string]Table{"xml": {logger: logger, flattenFileFunc: dataflatten.XmlFile}},
|
||||
testFile: path.Join("testdata", "simple.xml"),
|
||||
queries: []string{"this/does/not/exist"},
|
||||
expectNoData: true,
|
||||
},
|
||||
|
||||
// ini
|
||||
{
|
||||
testTables: map[string]Table{"ini": {logger: logger, flattenFileFunc: dataflatten.IniFile}},
|
||||
testFile: path.Join("testdata", "secdata.ini"),
|
||||
expectedRows: 87,
|
||||
},
|
||||
{
|
||||
testTables: map[string]Table{"ini": {logger: logger, flattenFileFunc: dataflatten.IniFile}},
|
||||
testFile: path.Join("testdata", "secdata.ini"),
|
||||
queries: []string{"Registry Values"},
|
||||
expectedRows: 59,
|
||||
},
|
||||
{
|
||||
testTables: map[string]Table{"ini": {logger: logger, flattenFileFunc: dataflatten.IniFile}},
|
||||
testFile: path.Join("testdata", "secdata.ini"),
|
||||
queries: []string{"this/does/not/exist"},
|
||||
expectNoData: true,
|
||||
},
|
||||
}
|
||||
|
||||
for testN, tt := range tests {
|
||||
tt := tt
|
||||
for tableName, testTable := range tt.testTables {
|
||||
tableName, testTable := tableName, testTable
|
||||
|
||||
t.Run(fmt.Sprintf("%d/%s", testN, tableName), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mockQC := tablehelpers.MockQueryContext(map[string][]string{
|
||||
"path": {tt.testFile},
|
||||
"query": tt.queries,
|
||||
})
|
||||
|
||||
rows, err := testTable.generate(context.TODO(), mockQC)
|
||||
require.NoError(t, err)
|
||||
|
||||
if tt.expectNoData {
|
||||
require.Len(t, rows, 0)
|
||||
} else {
|
||||
require.Len(t, rows, tt.expectedRows)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
277
orbit/pkg/table/dataflattentable/testdata/NetworkInterfaces.plist
vendored
Normal file
277
orbit/pkg/table/dataflattentable/testdata/NetworkInterfaces.plist
vendored
Normal file
@ -0,0 +1,277 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Interfaces</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Active</key>
|
||||
<true/>
|
||||
<key>BSD Name</key>
|
||||
<string>en0</string>
|
||||
<key>IOBuiltin</key>
|
||||
<true/>
|
||||
<key>IOInterfaceNamePrefix</key>
|
||||
<string>en</string>
|
||||
<key>IOInterfaceType</key>
|
||||
<integer>6</integer>
|
||||
<key>IOInterfaceUnit</key>
|
||||
<integer>0</integer>
|
||||
<key>SCNetworkInterfaceInfo</key>
|
||||
<dict>
|
||||
<key>UserDefinedName</key>
|
||||
<string>Wi-Fi</string>
|
||||
</dict>
|
||||
<key>SCNetworkInterfaceType</key>
|
||||
<string>IEEE80211</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Active</key>
|
||||
<true/>
|
||||
<key>BSD Name</key>
|
||||
<string>en1</string>
|
||||
<key>IOBuiltin</key>
|
||||
<true/>
|
||||
<key>IOInterfaceNamePrefix</key>
|
||||
<string>en</string>
|
||||
<key>IOInterfaceType</key>
|
||||
<integer>6</integer>
|
||||
<key>IOInterfaceUnit</key>
|
||||
<integer>1</integer>
|
||||
<key>SCNetworkInterfaceInfo</key>
|
||||
<dict>
|
||||
<key>UserDefinedName</key>
|
||||
<string>Thunderbolt 1</string>
|
||||
</dict>
|
||||
<key>SCNetworkInterfaceType</key>
|
||||
<string>Ethernet</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Active</key>
|
||||
<true/>
|
||||
<key>BSD Name</key>
|
||||
<string>en2</string>
|
||||
<key>IOBuiltin</key>
|
||||
<true/>
|
||||
<key>IOInterfaceNamePrefix</key>
|
||||
<string>en</string>
|
||||
<key>IOInterfaceType</key>
|
||||
<integer>6</integer>
|
||||
<key>IOInterfaceUnit</key>
|
||||
<integer>2</integer>
|
||||
<key>SCNetworkInterfaceInfo</key>
|
||||
<dict>
|
||||
<key>UserDefinedName</key>
|
||||
<string>Thunderbolt 2</string>
|
||||
</dict>
|
||||
<key>SCNetworkInterfaceType</key>
|
||||
<string>Ethernet</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Active</key>
|
||||
<true/>
|
||||
<key>BSD Name</key>
|
||||
<string>en3</string>
|
||||
<key>IOBuiltin</key>
|
||||
<true/>
|
||||
<key>IOInterfaceNamePrefix</key>
|
||||
<string>en</string>
|
||||
<key>IOInterfaceType</key>
|
||||
<integer>6</integer>
|
||||
<key>IOInterfaceUnit</key>
|
||||
<integer>3</integer>
|
||||
<key>SCNetworkInterfaceInfo</key>
|
||||
<dict>
|
||||
<key>UserDefinedName</key>
|
||||
<string>Thunderbolt 3</string>
|
||||
</dict>
|
||||
<key>SCNetworkInterfaceType</key>
|
||||
<string>Ethernet</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Active</key>
|
||||
<true/>
|
||||
<key>BSD Name</key>
|
||||
<string>en4</string>
|
||||
<key>IOBuiltin</key>
|
||||
<true/>
|
||||
<key>IOInterfaceNamePrefix</key>
|
||||
<string>en</string>
|
||||
<key>IOInterfaceType</key>
|
||||
<integer>6</integer>
|
||||
<key>IOInterfaceUnit</key>
|
||||
<integer>4</integer>
|
||||
<key>SCNetworkInterfaceInfo</key>
|
||||
<dict>
|
||||
<key>UserDefinedName</key>
|
||||
<string>Thunderbolt 4</string>
|
||||
</dict>
|
||||
<key>SCNetworkInterfaceType</key>
|
||||
<string>Ethernet</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Active</key>
|
||||
<true/>
|
||||
<key>BSD Name</key>
|
||||
<string>en5</string>
|
||||
<key>IOBuiltin</key>
|
||||
<false/>
|
||||
<key>IOInterfaceNamePrefix</key>
|
||||
<string>en</string>
|
||||
<key>IOInterfaceType</key>
|
||||
<integer>6</integer>
|
||||
<key>IOInterfaceUnit</key>
|
||||
<integer>5</integer>
|
||||
<key>SCNetworkInterfaceInfo</key>
|
||||
<dict>
|
||||
<key>USB Product Name</key>
|
||||
<string>iBridge</string>
|
||||
<key>UserDefinedName</key>
|
||||
<string>iBridge</string>
|
||||
<key>idProduct</key>
|
||||
<integer>34304</integer>
|
||||
<key>idVendor</key>
|
||||
<integer>1452</integer>
|
||||
</dict>
|
||||
<key>SCNetworkInterfaceType</key>
|
||||
<string>Ethernet</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BSD Name</key>
|
||||
<string>en6</string>
|
||||
<key>IOBuiltin</key>
|
||||
<false/>
|
||||
<key>IOInterfaceNamePrefix</key>
|
||||
<string>en</string>
|
||||
<key>IOInterfaceType</key>
|
||||
<integer>6</integer>
|
||||
<key>IOInterfaceUnit</key>
|
||||
<integer>6</integer>
|
||||
<key>SCNetworkInterfaceInfo</key>
|
||||
<dict>
|
||||
<key>UserDefinedName</key>
|
||||
<string>Bluetooth PAN</string>
|
||||
</dict>
|
||||
<key>SCNetworkInterfaceType</key>
|
||||
<string>Ethernet</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BSD Name</key>
|
||||
<string>en7</string>
|
||||
<key>IOBuiltin</key>
|
||||
<false/>
|
||||
<key>IOInterfaceNamePrefix</key>
|
||||
<string>en</string>
|
||||
<key>IOInterfaceType</key>
|
||||
<integer>6</integer>
|
||||
<key>IOInterfaceUnit</key>
|
||||
<integer>7</integer>
|
||||
<key>SCNetworkInterfaceInfo</key>
|
||||
<dict>
|
||||
<key>USB Product Name</key>
|
||||
<string>Belkin USB_C LAN</string>
|
||||
<key>UserDefinedName</key>
|
||||
<string>Belkin USB-C LAN</string>
|
||||
<key>idProduct</key>
|
||||
<integer>33107</integer>
|
||||
<key>idVendor</key>
|
||||
<integer>3034</integer>
|
||||
</dict>
|
||||
<key>SCNetworkInterfaceType</key>
|
||||
<string>Ethernet</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Active</key>
|
||||
<true/>
|
||||
<key>BSD Name</key>
|
||||
<string>en8</string>
|
||||
<key>IOBuiltin</key>
|
||||
<false/>
|
||||
<key>IOInterfaceNamePrefix</key>
|
||||
<string>en</string>
|
||||
<key>IOInterfaceType</key>
|
||||
<integer>6</integer>
|
||||
<key>IOInterfaceUnit</key>
|
||||
<integer>8</integer>
|
||||
<key>SCNetworkInterfaceInfo</key>
|
||||
<dict>
|
||||
<key>USB Product Name</key>
|
||||
<string>USB 10_100_1000 LAN</string>
|
||||
<key>UserDefinedName</key>
|
||||
<string>USB 10/100/1000 LAN</string>
|
||||
<key>idProduct</key>
|
||||
<integer>33107</integer>
|
||||
<key>idVendor</key>
|
||||
<integer>3034</integer>
|
||||
</dict>
|
||||
<key>SCNetworkInterfaceType</key>
|
||||
<string>Ethernet</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BSD Name</key>
|
||||
<string>en9</string>
|
||||
<key>IOBuiltin</key>
|
||||
<false/>
|
||||
<key>IOInterfaceNamePrefix</key>
|
||||
<string>en</string>
|
||||
<key>IOInterfaceType</key>
|
||||
<integer>6</integer>
|
||||
<key>IOInterfaceUnit</key>
|
||||
<integer>9</integer>
|
||||
<key>SCNetworkInterfaceInfo</key>
|
||||
<dict>
|
||||
<key>USB Product Name</key>
|
||||
<string>iPhone</string>
|
||||
<key>UserDefinedName</key>
|
||||
<string>iPhone</string>
|
||||
<key>idProduct</key>
|
||||
<integer>4776</integer>
|
||||
<key>idVendor</key>
|
||||
<integer>1452</integer>
|
||||
</dict>
|
||||
<key>SCNetworkInterfaceType</key>
|
||||
<string>Ethernet</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BSD Name</key>
|
||||
<string>en10</string>
|
||||
<key>IOBuiltin</key>
|
||||
<false/>
|
||||
<key>IOInterfaceNamePrefix</key>
|
||||
<string>en</string>
|
||||
<key>IOInterfaceType</key>
|
||||
<integer>6</integer>
|
||||
<key>IOInterfaceUnit</key>
|
||||
<integer>10</integer>
|
||||
<key>SCNetworkInterfaceInfo</key>
|
||||
<dict>
|
||||
<key>UserDefinedName</key>
|
||||
<string>Display Ethernet</string>
|
||||
</dict>
|
||||
<key>SCNetworkInterfaceType</key>
|
||||
<string>Ethernet</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BSD Name</key>
|
||||
<string>fw0</string>
|
||||
<key>IOBuiltin</key>
|
||||
<false/>
|
||||
<key>IOInterfaceNamePrefix</key>
|
||||
<string>fw</string>
|
||||
<key>IOInterfaceType</key>
|
||||
<integer>144</integer>
|
||||
<key>IOInterfaceUnit</key>
|
||||
<integer>0</integer>
|
||||
<key>SCNetworkInterfaceInfo</key>
|
||||
<dict>
|
||||
<key>UserDefinedName</key>
|
||||
<string>Display FireWire</string>
|
||||
</dict>
|
||||
<key>SCNetworkInterfaceType</key>
|
||||
<string>FireWire</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>Model</key>
|
||||
<string>MacBookPro14,3</string>
|
||||
</dict>
|
||||
</plist>
|
34
orbit/pkg/table/dataflattentable/testdata/animals.json
vendored
Normal file
34
orbit/pkg/table/dataflattentable/testdata/animals.json
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"metadata": {
|
||||
"testing": true,
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"system": "users demo",
|
||||
"users": [
|
||||
{
|
||||
"favorites": [
|
||||
"ants"
|
||||
],
|
||||
"uuid": "abc123",
|
||||
"name": "Alex Aardvark",
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"favorites": [
|
||||
"mice",
|
||||
"birds"
|
||||
],
|
||||
"uuid": "def456",
|
||||
"name": "Bailey Bobcat",
|
||||
"id": 2
|
||||
},
|
||||
{
|
||||
"favorites": [
|
||||
"seeds"
|
||||
],
|
||||
"uuid": "ghi789",
|
||||
"name": "Cam Chipmunk",
|
||||
"id": 3
|
||||
}
|
||||
]
|
||||
}
|
BIN
orbit/pkg/table/dataflattentable/testdata/animals.plist
vendored
Normal file
BIN
orbit/pkg/table/dataflattentable/testdata/animals.plist
vendored
Normal file
Binary file not shown.
55
orbit/pkg/table/dataflattentable/testdata/animals.xml
vendored
Normal file
55
orbit/pkg/table/dataflattentable/testdata/animals.xml
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>metadata</key>
|
||||
<dict>
|
||||
<key>testing</key>
|
||||
<true/>
|
||||
<key>version</key>
|
||||
<string>1.0.1</string>
|
||||
</dict>
|
||||
<key>system</key>
|
||||
<string>users demo</string>
|
||||
<key>users</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>favorites</key>
|
||||
<array>
|
||||
<string>ants</string>
|
||||
</array>
|
||||
<key>id</key>
|
||||
<integer>1</integer>
|
||||
<key>name</key>
|
||||
<string>Alex Aardvark</string>
|
||||
<key>uuid</key>
|
||||
<string>abc123</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>favorites</key>
|
||||
<array>
|
||||
<string>mice</string>
|
||||
<string>birds</string>
|
||||
</array>
|
||||
<key>id</key>
|
||||
<integer>2</integer>
|
||||
<key>name</key>
|
||||
<string>Bailey Bobcat</string>
|
||||
<key>uuid</key>
|
||||
<string>def456</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>favorites</key>
|
||||
<array>
|
||||
<string>seeds</string>
|
||||
</array>
|
||||
<key>id</key>
|
||||
<integer>3</integer>
|
||||
<key>name</key>
|
||||
<string>Cam Chipmunk</string>
|
||||
<key>uuid</key>
|
||||
<string>ghi789</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
295
orbit/pkg/table/dataflattentable/testdata/com.apple.launchservices.secure.plist
vendored
Normal file
295
orbit/pkg/table/dataflattentable/testdata/com.apple.launchservices.secure.plist
vendored
Normal file
@ -0,0 +1,295 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>LSHandlers</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.apple.dt.xcode</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>xcbot</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerContentType</key>
|
||||
<string>public.svg-image</string>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>org.mozilla.firefox</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.logmein.mac.gotoopener</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>citrixonline488</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>keybase.electron</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>web+stellar</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.apple.facetime</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>facetime</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.choosyosx.choosy</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>http</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.choosyosx.choosy</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>https</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerContentType</key>
|
||||
<string>public.html</string>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.choosyosx.choosy</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerContentType</key>
|
||||
<string>public.xhtml</string>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.google.chrome</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>org.whispersystems.signal-desktop</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>sgnl</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.choosyosx.choosy</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>x-choosy</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerContentType</key>
|
||||
<string>com.apple.installer-package</string>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.apple.installer</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerContentType</key>
|
||||
<string>com.apple.installer-meta-package</string>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.apple.installer</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.bluejeans.nw.app</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>bjn</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.apple.dt.xcode</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>xcpref</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.apple.dt.xcode</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>xcdevice</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.apple.dt.xcode</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>xcdoc</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.apple.dt.xcode</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>apple-reference-documentation</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.logmein.mac.gotoopener</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>gotoopener</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.apple.dt.xcode</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>xcode</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.logmein.mac.gotoopener</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>citrixonline</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.google.chrome</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>webcal</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>keybase.electron</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>keybase</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.apple.dt.xcode</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>x-source-tag</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>com.logmein.mac.gotoopener</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>gotoopener488</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSHandlerPreferredVersions</key>
|
||||
<dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>-</string>
|
||||
</dict>
|
||||
<key>LSHandlerRoleAll</key>
|
||||
<string>us.zoom.xos</string>
|
||||
<key>LSHandlerURLScheme</key>
|
||||
<string>zoomphonecall</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
92
orbit/pkg/table/dataflattentable/testdata/secdata.ini
vendored
Normal file
92
orbit/pkg/table/dataflattentable/testdata/secdata.ini
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
[Unicode]
|
||||
Unicode=yes
|
||||
[System Access]
|
||||
MinimumPasswordAge = 0
|
||||
MaximumPasswordAge = 42
|
||||
MinimumPasswordLength = 0
|
||||
PasswordComplexity = 1
|
||||
PasswordHistorySize = 0
|
||||
LockoutBadCount = 1
|
||||
ResetLockoutCount = 30
|
||||
LockoutDuration = 30
|
||||
RequireLogonToChangePassword = 0
|
||||
ForceLogoffWhenHourExpire = 0
|
||||
NewAdministratorName = "Administrator"
|
||||
NewGuestName = "Guest"
|
||||
ClearTextPassword = 0
|
||||
LSAAnonymousNameLookup = 0
|
||||
EnableAdminAccount = 0
|
||||
EnableGuestAccount = 0
|
||||
[Event Audit]
|
||||
AuditSystemEvents = 0
|
||||
AuditLogonEvents = 0
|
||||
AuditObjectAccess = 0
|
||||
AuditPrivilegeUse = 0
|
||||
AuditPolicyChange = 0
|
||||
AuditAccountManage = 0
|
||||
AuditProcessTracking = 0
|
||||
AuditDSAccess = 0
|
||||
AuditAccountLogon = 0
|
||||
[Registry Values]
|
||||
MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SecurityLevel=4,0
|
||||
MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SetCommand=4,0
|
||||
MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\CachedLogonsCount=1,"10"
|
||||
MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\ForceUnlockLogon=4,0
|
||||
MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\PasswordExpiryWarning=4,5
|
||||
MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\ScRemoveOption=1,"0"
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorAdmin=4,5
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorUser=4,3
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayLastUserName=4,0
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableInstallerDetection=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableSecureUIAPaths=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableUIADesktopToggle=4,0
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableVirtualization=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\Parameters\SupportedEncryptionTypes=4,0
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\LegalNoticeCaption=1,""
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\LegalNoticeText=7,
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\PromptOnSecureDesktop=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ScForceOption=4,0
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ShutdownWithoutLogon=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\UndockWithoutLogon=4,1
|
||||
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ValidateAdminCodeSignatures=4,0
|
||||
MACHINE\Software\Policies\Microsoft\Windows\Safer\CodeIdentifiers\AuthenticodeEnabled=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\AuditBaseObjects=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\CrashOnAuditFail=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\DisableDomainCreds=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\EveryoneIncludesAnonymous=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy\Enabled=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\ForceGuest=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\FullPrivilegeAuditing=3,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\LimitBlankPasswordUse=4,1
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\MSV1_0\NTLMMinClientSec=4,536870912
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\MSV1_0\NTLMMinServerSec=4,536870912
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\NoLMHash=4,1
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymous=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymousSAM=4,1
|
||||
MACHINE\System\CurrentControlSet\Control\Print\Providers\LanMan Print Services\Servers\AddPrinterDrivers=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedExactPaths\Machine=7,System\CurrentControlSet\Control\ProductOptions,System\CurrentControlSet\Control\Server Applications,Software\Microsoft\Windows NT\CurrentVersion
|
||||
MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedPaths\Machine=7,System\CurrentControlSet\Control\Print\Printers,System\CurrentControlSet\Services\Eventlog,Software\Microsoft\OLAP Server,Software\Microsoft\Windows NT\CurrentVersion\Print,Software\Microsoft\Windows NT\CurrentVersion\Windows,System\CurrentControlSet\Control\ContentIndex,System\CurrentControlSet\Control\Terminal Server,System\CurrentControlSet\Control\Terminal Server\UserConfig,System\CurrentControlSet\Control\Terminal Server\DefaultUserConfiguration,Software\Microsoft\Windows NT\CurrentVersion\Perflib,System\CurrentControlSet\Services\SysmonLog
|
||||
MACHINE\System\CurrentControlSet\Control\Session Manager\Kernel\ObCaseInsensitive=4,1
|
||||
MACHINE\System\CurrentControlSet\Control\Session Manager\Memory Management\ClearPageFileAtShutdown=4,0
|
||||
MACHINE\System\CurrentControlSet\Control\Session Manager\ProtectionMode=4,1
|
||||
MACHINE\System\CurrentControlSet\Control\Session Manager\SubSystems\optional=7,
|
||||
MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\AutoDisconnect=4,15
|
||||
MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\EnableForcedLogOff=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\EnableSecuritySignature=4,0
|
||||
MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\NullSessionPipes=7,
|
||||
MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\RequireSecuritySignature=4,0
|
||||
MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\RestrictNullSessAccess=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\EnablePlainTextPassword=4,0
|
||||
MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\EnableSecuritySignature=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\RequireSecuritySignature=4,0
|
||||
MACHINE\System\CurrentControlSet\Services\LDAP\LDAPClientIntegrity=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\DisablePasswordChange=4,0
|
||||
MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\MaximumPasswordAge=4,30
|
||||
MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\RequireSignOrSeal=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\RequireStrongKey=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\SealSecureChannel=4,1
|
||||
MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\SignSecureChannel=4,1
|
||||
[Version]
|
||||
signature="$CHICAGO$"
|
||||
Revision=1
|
12
orbit/pkg/table/dataflattentable/testdata/simple.xml
vendored
Normal file
12
orbit/pkg/table/dataflattentable/testdata/simple.xml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<simple>
|
||||
<Items>
|
||||
<item1>One</item1>
|
||||
<item2>Two</item2>
|
||||
<item3>Three</item3>
|
||||
</Items>
|
||||
<Animals>
|
||||
<Cat/>
|
||||
<Dog/>
|
||||
<Mouse/>
|
||||
</Animals>
|
||||
</simple>
|
@ -8,12 +8,17 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/cryptoinfotable"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/firefox_preferences"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/sntp_request"
|
||||
|
||||
"github.com/macadmins/osquery-extension/tables/chromeuserprofiles"
|
||||
"github.com/macadmins/osquery-extension/tables/fileline"
|
||||
"github.com/macadmins/osquery-extension/tables/puppet"
|
||||
|
||||
"github.com/osquery/osquery-go"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
@ -32,7 +37,7 @@ type Runner struct {
|
||||
type Extension interface {
|
||||
// Name returns the name of the table.
|
||||
Name() string
|
||||
// Column returns the definition of the table columns.
|
||||
// Columns returns the definition of the table columns.
|
||||
Columns() []table.ColumnDefinition
|
||||
// GenerateFunc generates results for a query.
|
||||
GenerateFunc(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error)
|
||||
@ -41,6 +46,9 @@ type Extension interface {
|
||||
// Opt allows configuring a Runner.
|
||||
type Opt func(*Runner)
|
||||
|
||||
// Logger for osquery tables
|
||||
var osqueryLogger *Logger
|
||||
|
||||
// WithExtension registers the given Extension on the Runner.
|
||||
func WithExtension(t Extension) Opt {
|
||||
return func(r *Runner) {
|
||||
@ -61,6 +69,8 @@ func NewRunner(socket string, opts ...Opt) *Runner {
|
||||
func (r *Runner) Execute() error {
|
||||
log.Debug().Msg("start osquery extension")
|
||||
|
||||
osqueryLogger = NewOsqueryLogger()
|
||||
|
||||
if err := waitExtensionSocket(r.socket, 1*time.Minute); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -123,6 +133,9 @@ func OrbitDefaultTables() []osquery.OsqueryPlugin {
|
||||
|
||||
// Orbit extensions.
|
||||
table.NewPlugin("sntp_request", sntp_request.Columns(), sntp_request.GenerateFunc),
|
||||
|
||||
firefox_preferences.TablePlugin(osqueryLogger),
|
||||
cryptoinfotable.TablePlugin(osqueryLogger),
|
||||
}
|
||||
return plugins
|
||||
}
|
||||
|
@ -5,12 +5,16 @@ package table
|
||||
import (
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/authdb"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/csrutil_info"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/dataflattentable"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/diskutil/apfs"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/diskutil/corestorage"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/dscl"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/filevault_prk"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/filevault_status"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/find_cmd"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/firmware_eficheck_integrity_check"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/firmwarepasswd"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/ioreg"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/nvram_info"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/pmset"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/privaterelay"
|
||||
@ -18,18 +22,20 @@ import (
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/software_update"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/sudo_info"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/user_login_settings"
|
||||
|
||||
"github.com/macadmins/osquery-extension/tables/filevaultusers"
|
||||
"github.com/macadmins/osquery-extension/tables/macos_profiles"
|
||||
"github.com/macadmins/osquery-extension/tables/macosrsr"
|
||||
"github.com/macadmins/osquery-extension/tables/mdm"
|
||||
"github.com/macadmins/osquery-extension/tables/munki"
|
||||
"github.com/macadmins/osquery-extension/tables/unifiedlog"
|
||||
|
||||
"github.com/osquery/osquery-go"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
)
|
||||
|
||||
func PlatformTables() []osquery.OsqueryPlugin {
|
||||
return []osquery.OsqueryPlugin{
|
||||
plugins := []osquery.OsqueryPlugin{
|
||||
// Fleet tables
|
||||
table.NewPlugin("icloud_private_relay", privaterelay.Columns(), privaterelay.Generate),
|
||||
table.NewPlugin("user_login_settings", user_login_settings.Columns(), user_login_settings.Generate),
|
||||
@ -59,5 +65,19 @@ func PlatformTables() []osquery.OsqueryPlugin {
|
||||
// osquery version 5.5.0 and up ships a unified_log table in core
|
||||
// we are renaming the one from the macadmins extension to avoid collision
|
||||
table.NewPlugin("macadmins_unified_log", unifiedlog.UnifiedLogColumns(), unifiedlog.UnifiedLogGenerate),
|
||||
|
||||
filevault_status.TablePlugin(osqueryLogger), // table name is "filevault_status"
|
||||
ioreg.TablePlugin(osqueryLogger), // table name is "ioreg"
|
||||
|
||||
// firmwarepasswd table. Only returns valid data on a Mac with an Intel processor. Background: https://support.apple.com/en-us/HT204455
|
||||
firmwarepasswd.TablePlugin(osqueryLogger), // table name is "firmwarepasswd"
|
||||
|
||||
// Table for parsing Apple Property List files, which are typically stored in ~/Library/Preferences/
|
||||
dataflattentable.TablePlugin(osqueryLogger, dataflattentable.PlistType), // table name is "parse_plist"
|
||||
}
|
||||
|
||||
// append platform specific tables
|
||||
plugins = appendTables(plugins)
|
||||
|
||||
return plugins
|
||||
}
|
||||
|
10
orbit/pkg/table/extension_darwin_amd64.go
Normal file
10
orbit/pkg/table/extension_darwin_amd64.go
Normal file
@ -0,0 +1,10 @@
|
||||
//go:build darwin && amd64
|
||||
|
||||
package table
|
||||
|
||||
import "github.com/osquery/osquery-go"
|
||||
|
||||
// stub for amd64 platforms
|
||||
func appendTables(plugins []osquery.OsqueryPlugin) []osquery.OsqueryPlugin {
|
||||
return plugins
|
||||
}
|
18
orbit/pkg/table/extension_darwin_arm64.go
Normal file
18
orbit/pkg/table/extension_darwin_arm64.go
Normal file
@ -0,0 +1,18 @@
|
||||
//go:build darwin && arm64
|
||||
|
||||
package table
|
||||
|
||||
import (
|
||||
// ARM64 Kolide tables
|
||||
appicons "github.com/fleetdm/fleet/v4/orbit/pkg/table/app-icons"
|
||||
|
||||
"github.com/osquery/osquery-go"
|
||||
)
|
||||
|
||||
func appendTables(plugins []osquery.OsqueryPlugin) []osquery.OsqueryPlugin {
|
||||
plugins = append(plugins,
|
||||
// arm64 tables
|
||||
appicons.AppIcons(),
|
||||
)
|
||||
return plugins
|
||||
}
|
19
orbit/pkg/table/extension_linux.go
Normal file
19
orbit/pkg/table/extension_linux.go
Normal file
@ -0,0 +1,19 @@
|
||||
//go:build linux
|
||||
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/crowdstrike/falcon_kernel_check"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/crowdstrike/falconctl"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/cryptsetup"
|
||||
|
||||
"github.com/osquery/osquery-go"
|
||||
)
|
||||
|
||||
func PlatformTables() []osquery.OsqueryPlugin {
|
||||
return []osquery.OsqueryPlugin{
|
||||
cryptsetup.TablePlugin(osqueryLogger), // table name is "cryptsetup_status"
|
||||
falconctl.NewFalconctlOptionTable(osqueryLogger), // table name is "falconctl_option"
|
||||
falcon_kernel_check.TablePlugin(osqueryLogger), // table name is "falcon_kernel_check"
|
||||
}
|
||||
}
|
26
orbit/pkg/table/extension_logger.go
Normal file
26
orbit/pkg/table/extension_logger.go
Normal file
@ -0,0 +1,26 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Logger is a wrapper around zerolog, which we use for tables
|
||||
// using the go-kit logger
|
||||
type Logger struct {
|
||||
zerolog.Logger
|
||||
}
|
||||
|
||||
// Log logs a message, implementing log.Logger interface
|
||||
func (l *Logger) Log(keyValuePairs ...interface{}) error {
|
||||
log.Logger.Info().Msg(fmt.Sprint(keyValuePairs...))
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewOsqueryLogger returns the Logger struct.
|
||||
func NewOsqueryLogger() *Logger {
|
||||
// Return a Logger struct with our global logger, and use the existing global config for the log level.
|
||||
return &Logger{log.Logger}
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
//go:build !darwin && !windows
|
||||
//go:build !darwin && !windows && !linux
|
||||
|
||||
// Currently (2021/10/26) this file is not needed. However, keeping this around for potential
|
||||
// expansion to other OSs.
|
||||
|
||||
package table
|
||||
|
||||
|
@ -3,8 +3,10 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/cis_audit"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/mdm"
|
||||
cisaudit "github.com/fleetdm/fleet/v4/orbit/pkg/table/cis_audit"
|
||||
mdmbridge "github.com/fleetdm/fleet/v4/orbit/pkg/table/mdm"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/windowsupdatetable"
|
||||
|
||||
"github.com/osquery/osquery-go"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
)
|
||||
@ -14,5 +16,7 @@ func PlatformTables() []osquery.OsqueryPlugin {
|
||||
// Fleet tables
|
||||
table.NewPlugin("mdm_bridge", mdmbridge.Columns(), mdmbridge.Generate),
|
||||
table.NewPlugin("cis_audit", cisaudit.Columns(), cisaudit.Generate),
|
||||
|
||||
windowsupdatetable.TablePlugin(windowsupdatetable.UpdatesTable, osqueryLogger), // table name is "windows_updates"
|
||||
}
|
||||
}
|
||||
|
60
orbit/pkg/table/filevault_status/filevault_status_darwin.go
Normal file
60
orbit/pkg/table/filevault_status/filevault_status_darwin.go
Normal file
@ -0,0 +1,60 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
// based on https://github.com/fleetdm/launcher/blob/main/pkg/osquery/tables/filevault
|
||||
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package filevault_status
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/tablehelpers"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
)
|
||||
|
||||
const fdesetupPath = "/usr/bin/fdesetup"
|
||||
|
||||
type Table struct {
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func TablePlugin(logger log.Logger) *table.Plugin {
|
||||
columns := []table.ColumnDefinition{
|
||||
table.TextColumn("status"),
|
||||
}
|
||||
|
||||
t := &Table{
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
return table.NewPlugin("filevault_status", columns, t.generate)
|
||||
}
|
||||
|
||||
func (t *Table) generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
|
||||
output, err := tablehelpers.Exec(ctx, t.logger, 10, []string{fdesetupPath}, []string{"status"}, false)
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log("msg", "fdesetup failed", "err", err)
|
||||
|
||||
// Don't error out if the binary isn't found
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("calling fdesetup: %w", err)
|
||||
}
|
||||
|
||||
status := strings.TrimSuffix(string(output), "\n")
|
||||
|
||||
results := []map[string]string{
|
||||
{
|
||||
"status": status,
|
||||
},
|
||||
}
|
||||
return results, nil
|
||||
}
|
122
orbit/pkg/table/firefox_preferences/table.go
Normal file
122
orbit/pkg/table/firefox_preferences/table.go
Normal file
@ -0,0 +1,122 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package firefox_preferences
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/dataflatten"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/dataflattentable"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/tablehelpers"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
)
|
||||
|
||||
type Table struct {
|
||||
name string
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
const tableName = "firefox_preferences"
|
||||
|
||||
// For the first iteration of this table, we decided to do our own parsing with regex,
|
||||
// leaving the JSON strings as-is.
|
||||
//
|
||||
// input -> user_pref("app.normandy.foo", "{\"abc\":123}");
|
||||
// output -> [user_pref("app.normandy.foo", "{"abc":123}"); app.normandy.foo {"abc":123}]
|
||||
//
|
||||
// Note that we do not capture the surrounding quotes for either groups.
|
||||
//
|
||||
// In the future, we may want to use go-mozpref:
|
||||
// https://github.com/hansmi/go-mozpref
|
||||
var re = regexp.MustCompile(`^user_pref\("([^,]+)",\s*"?(.*?)"?\);$`)
|
||||
|
||||
func TablePlugin(logger log.Logger) *table.Plugin {
|
||||
columns := dataflattentable.Columns(
|
||||
table.TextColumn("path"),
|
||||
)
|
||||
|
||||
t := &Table{
|
||||
name: tableName,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
return table.NewPlugin(t.name, columns, t.generate)
|
||||
}
|
||||
|
||||
func (t *Table) generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
|
||||
var results []map[string]string
|
||||
|
||||
filePaths := tablehelpers.GetConstraints(queryContext, "path")
|
||||
|
||||
if len(filePaths) == 0 {
|
||||
level.Info(t.logger).Log(
|
||||
"msg", fmt.Sprintf("no path provided to %s", tableName),
|
||||
"table", tableName,
|
||||
)
|
||||
return results, nil
|
||||
}
|
||||
|
||||
for _, filePath := range filePaths {
|
||||
for _, dataQuery := range tablehelpers.GetConstraints(queryContext, "query", tablehelpers.WithDefaults("*")) {
|
||||
flattenOpts := []dataflatten.FlattenOpts{
|
||||
dataflatten.WithQuery(strings.Split(dataQuery, "/")),
|
||||
}
|
||||
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
level.Info(t.logger).Log(
|
||||
"msg", "failed to open file",
|
||||
"table", tableName,
|
||||
"path", filePath,
|
||||
"err", err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
rawKeyVals := make(map[string]interface{})
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
// Given the line format: user_pref("app.normandy.first_run", false);
|
||||
// the return value should be a three element array, where the second
|
||||
// and third elements are the key and value, respectively.
|
||||
match := re.FindStringSubmatch(line)
|
||||
|
||||
// If the match doesn't have a length of 3, the line is malformed in some way.
|
||||
// Skip it.
|
||||
if len(match) != 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
// The regex already stripped out the surrounding quotes, so now we're
|
||||
// left with escaped quotes that no longer make sense.
|
||||
// i.e. {\"249024122\":[1660860020218]}
|
||||
// Replace those with unescaped quotes.
|
||||
rawKeyVals[match[1]] = strings.ReplaceAll(match[2], "\\\"", "\"")
|
||||
}
|
||||
|
||||
flatData, err := dataflatten.Flatten(rawKeyVals, flattenOpts...)
|
||||
if err != nil {
|
||||
level.Debug(t.logger).Log(
|
||||
"msg", "failed to flatten data for path",
|
||||
"table", tableName,
|
||||
"path", filePath,
|
||||
"err", err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
rowData := map[string]string{"path": filePath}
|
||||
results = append(results, dataflattentable.ToMap(flatData, dataQuery, rowData)...)
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
80
orbit/pkg/table/firefox_preferences/table_test.go
Normal file
80
orbit/pkg/table/firefox_preferences/table_test.go
Normal file
@ -0,0 +1,80 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package firefox_preferences
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/tablehelpers"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_generate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
filePaths []string
|
||||
expectedResultsFilePath string
|
||||
query string
|
||||
}{
|
||||
{
|
||||
name: "no path",
|
||||
},
|
||||
{
|
||||
name: "single path",
|
||||
filePaths: []string{path.Join("testdata", "prefs.js")},
|
||||
expectedResultsFilePath: "testdata/output.single_path.json",
|
||||
},
|
||||
{
|
||||
name: "single path with query",
|
||||
filePaths: []string{path.Join("testdata", "prefs.js")},
|
||||
expectedResultsFilePath: "testdata/output.single_path_with_query.json",
|
||||
query: "app.normandy.first_run",
|
||||
},
|
||||
{
|
||||
name: "multiple paths",
|
||||
filePaths: []string{path.Join("testdata", "prefs.js"), path.Join("testdata", "prefs2.js")},
|
||||
expectedResultsFilePath: "testdata/output.multiple_paths.json",
|
||||
},
|
||||
{
|
||||
name: "file with bad data",
|
||||
filePaths: []string{path.Join("testdata", "prefs3.js")},
|
||||
expectedResultsFilePath: "testdata/output.file_with_bad_data.json",
|
||||
},
|
||||
}
|
||||
|
||||
table := Table{logger: log.NewNopLogger()}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
constraints := make(map[string][]string)
|
||||
constraints["path"] = tt.filePaths
|
||||
if tt.query != "" {
|
||||
constraints["query"] = append(constraints["query"], tt.query)
|
||||
}
|
||||
|
||||
got, _ := table.generate(context.Background(), tablehelpers.MockQueryContext(constraints))
|
||||
|
||||
var want []map[string]string
|
||||
|
||||
if tt.expectedResultsFilePath != "" {
|
||||
wantBytes, err := os.ReadFile(tt.expectedResultsFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = json.Unmarshal(wantBytes, &want)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.ElementsMatch(t, want, got)
|
||||
})
|
||||
}
|
||||
}
|
10
orbit/pkg/table/firefox_preferences/testdata/output.file_with_bad_data.json
vendored
Normal file
10
orbit/pkg/table/firefox_preferences/testdata/output.file_with_bad_data.json
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"fullkey": "browser.contextual-services.contextId",
|
||||
"parent": "",
|
||||
"key": "browser.contextual-services.contextId",
|
||||
"value": "{97961fa4-2a6a-470f-a916-45a3f51fc393}",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs3.js"
|
||||
}
|
||||
]
|
74
orbit/pkg/table/firefox_preferences/testdata/output.multiple_paths.json
vendored
Normal file
74
orbit/pkg/table/firefox_preferences/testdata/output.multiple_paths.json
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
[
|
||||
{
|
||||
"fullkey": "app.normandy.first_run",
|
||||
"parent": "",
|
||||
"key": "app.normandy.first_run",
|
||||
"value": "false",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "app.normandy.migrationsApplied",
|
||||
"parent": "",
|
||||
"key": "app.normandy.migrationsApplied",
|
||||
"value": "12",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "app.normandy.user_id",
|
||||
"parent": "",
|
||||
"key": "app.normandy.user_id",
|
||||
"value": "2324e712-7c70-4d9c-8e78-2a6eb9b2d967",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "app.update.background.previous.reasons",
|
||||
"parent": "",
|
||||
"key": "app.update.background.previous.reasons",
|
||||
"value": "[\"the maintenance service registry key is not present\"]",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "browser.contextual-services.contextId",
|
||||
"parent": "",
|
||||
"key": "browser.contextual-services.contextId",
|
||||
"value": "{97961fa4-2a6a-470f-a916-45a3f51fc393}",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "browser.newtabpage.activity-stream.discoverystream.rec.impressions",
|
||||
"parent": "",
|
||||
"key": "browser.newtabpage.activity-stream.discoverystream.rec.impressions",
|
||||
"value": "{\"138341\":1660860020223,\"138361\":1660860020223,\"257394347\":1660860020221}",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "browser.newtabpage.activity-stream.discoverystream.spoc.impressions",
|
||||
"parent": "",
|
||||
"key": "browser.newtabpage.activity-stream.discoverystream.spoc.impressions",
|
||||
"value": "{\"249024122\":[1660860020218]}",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "browser.newtabpage.pinned",
|
||||
"parent": "",
|
||||
"key": "browser.newtabpage.pinned",
|
||||
"value": "[{\"url\":\"https://amazon.com\",\"label\":\"@amazon\",\"searchTopSite\":true}]",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "app.normandy.first_run",
|
||||
"parent": "",
|
||||
"key": "app.normandy.first_run",
|
||||
"value": "true",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs2.js"
|
||||
}
|
||||
]
|
66
orbit/pkg/table/firefox_preferences/testdata/output.single_path.json
vendored
Normal file
66
orbit/pkg/table/firefox_preferences/testdata/output.single_path.json
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
[
|
||||
{
|
||||
"fullkey": "app.normandy.first_run",
|
||||
"parent": "",
|
||||
"key": "app.normandy.first_run",
|
||||
"value": "false",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "app.normandy.migrationsApplied",
|
||||
"parent": "",
|
||||
"key": "app.normandy.migrationsApplied",
|
||||
"value": "12",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "app.normandy.user_id",
|
||||
"parent": "",
|
||||
"key": "app.normandy.user_id",
|
||||
"value": "2324e712-7c70-4d9c-8e78-2a6eb9b2d967",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "app.update.background.previous.reasons",
|
||||
"parent": "",
|
||||
"key": "app.update.background.previous.reasons",
|
||||
"value": "[\"the maintenance service registry key is not present\"]",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "browser.contextual-services.contextId",
|
||||
"parent": "",
|
||||
"key": "browser.contextual-services.contextId",
|
||||
"value": "{97961fa4-2a6a-470f-a916-45a3f51fc393}",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "browser.newtabpage.activity-stream.discoverystream.rec.impressions",
|
||||
"parent": "",
|
||||
"key": "browser.newtabpage.activity-stream.discoverystream.rec.impressions",
|
||||
"value": "{\"138341\":1660860020223,\"138361\":1660860020223,\"257394347\":1660860020221}",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "browser.newtabpage.activity-stream.discoverystream.spoc.impressions",
|
||||
"parent": "",
|
||||
"key": "browser.newtabpage.activity-stream.discoverystream.spoc.impressions",
|
||||
"value": "{\"249024122\":[1660860020218]}",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
},
|
||||
{
|
||||
"fullkey": "browser.newtabpage.pinned",
|
||||
"parent": "",
|
||||
"key": "browser.newtabpage.pinned",
|
||||
"value": "[{\"url\":\"https://amazon.com\",\"label\":\"@amazon\",\"searchTopSite\":true}]",
|
||||
"query": "*",
|
||||
"path": "testdata/prefs.js"
|
||||
}
|
||||
]
|
10
orbit/pkg/table/firefox_preferences/testdata/output.single_path_with_query.json
vendored
Normal file
10
orbit/pkg/table/firefox_preferences/testdata/output.single_path_with_query.json
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"fullkey": "app.normandy.first_run",
|
||||
"parent": "",
|
||||
"key": "app.normandy.first_run",
|
||||
"value": "false",
|
||||
"query": "app.normandy.first_run",
|
||||
"path": "testdata/prefs.js"
|
||||
}
|
||||
]
|
19
orbit/pkg/table/firefox_preferences/testdata/prefs.js
vendored
Normal file
19
orbit/pkg/table/firefox_preferences/testdata/prefs.js
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// Mozilla User Preferences
|
||||
|
||||
// DO NOT EDIT THIS FILE.
|
||||
//
|
||||
// If you make changes to this file while the application is running,
|
||||
// the changes will be overwritten when the application exits.
|
||||
//
|
||||
// To change a preference value, you can either:
|
||||
// - modify it via the UI (e.g. via about:config in the browser); or
|
||||
// - set it within a user.js file in your profile.
|
||||
|
||||
user_pref("app.normandy.first_run", false);
|
||||
user_pref("app.normandy.migrationsApplied", 12);
|
||||
user_pref("app.normandy.user_id", "2324e712-7c70-4d9c-8e78-2a6eb9b2d967");
|
||||
user_pref("app.update.background.previous.reasons", "[\"the maintenance service registry key is not present\"]");
|
||||
user_pref("browser.contextual-services.contextId", "{97961fa4-2a6a-470f-a916-45a3f51fc393}");
|
||||
user_pref("browser.newtabpage.activity-stream.discoverystream.rec.impressions", "{\"138341\":1660860020223,\"138361\":1660860020223,\"257394347\":1660860020221}");
|
||||
user_pref("browser.newtabpage.activity-stream.discoverystream.spoc.impressions", "{\"249024122\":[1660860020218]}");
|
||||
user_pref("browser.newtabpage.pinned", "[{\"url\":\"https://amazon.com\",\"label\":\"@amazon\",\"searchTopSite\":true}]");
|
12
orbit/pkg/table/firefox_preferences/testdata/prefs2.js
vendored
Normal file
12
orbit/pkg/table/firefox_preferences/testdata/prefs2.js
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
// Mozilla User Preferences
|
||||
|
||||
// DO NOT EDIT THIS FILE.
|
||||
//
|
||||
// If you make changes to this file while the application is running,
|
||||
// the changes will be overwritten when the application exits.
|
||||
//
|
||||
// To change a preference value, you can either:
|
||||
// - modify it via the UI (e.g. via about:config in the browser); or
|
||||
// - set it within a user.js file in your profile.
|
||||
|
||||
user_pref("app.normandy.first_run", true);
|
14
orbit/pkg/table/firefox_preferences/testdata/prefs3.js
vendored
Normal file
14
orbit/pkg/table/firefox_preferences/testdata/prefs3.js
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
// Mozilla User Preferences
|
||||
|
||||
// DO NOT EDIT THIS FILE.
|
||||
//
|
||||
// If you make changes to this file while the application is running,
|
||||
// the changes will be overwritten when the application exits.
|
||||
//
|
||||
// To change a preference value, you can either:
|
||||
// - modify it via the UI (e.g. via about:config in the browser); or
|
||||
// - set it within a user.js file in your profile.
|
||||
|
||||
user_pref(false);
|
||||
user_pref("browser.contextual-services.contextId", "{97961fa4-2a6a-470f-a916-45a3f51fc393}");
|
||||
foobar("app.normandy.user_id", "2324e712-7c70-4d9c-8e78-2a6eb9b2d967");
|
166
orbit/pkg/table/firmwarepasswd/firmwarepasswd.go
Normal file
166
orbit/pkg/table/firmwarepasswd/firmwarepasswd.go
Normal file
@ -0,0 +1,166 @@
|
||||
// firmwarepasswd is a simple wrapper around the
|
||||
// `/usr/sbin/firmwarepasswd` tool. This should be considered beta at
|
||||
// best. It serves a bit as a pattern for future exec work.
|
||||
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package firmwarepasswd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
)
|
||||
|
||||
type Table struct {
|
||||
logger log.Logger
|
||||
parser *OutputParser
|
||||
}
|
||||
|
||||
func TablePlugin(logger log.Logger) *table.Plugin {
|
||||
columns := []table.ColumnDefinition{
|
||||
table.IntegerColumn("option_roms_allowed"),
|
||||
table.IntegerColumn("password_enabled"),
|
||||
table.TextColumn("mode"),
|
||||
}
|
||||
|
||||
t := New(logger)
|
||||
|
||||
return table.NewPlugin("firmwarepasswd", columns, t.generate)
|
||||
}
|
||||
|
||||
func New(logger log.Logger) *Table {
|
||||
parser := NewParser(logger,
|
||||
[]Matcher{
|
||||
{
|
||||
Match: func(in string) bool { return strings.HasPrefix(in, "Password Enabled: ") },
|
||||
KeyFunc: func(_ string) (string, error) { return "password_enabled", nil },
|
||||
ValFunc: func(in string) (string, error) { return passwordValue(in) },
|
||||
},
|
||||
{
|
||||
Match: func(in string) bool { return strings.HasPrefix(in, "Mode: ") },
|
||||
KeyFunc: func(_ string) (string, error) { return "mode", nil },
|
||||
ValFunc: func(in string) (string, error) { return modeValue(in) },
|
||||
},
|
||||
{
|
||||
Match: func(in string) bool { return strings.HasPrefix(in, "Option roms ") },
|
||||
KeyFunc: func(_ string) (string, error) { return "option_roms_allowed", nil },
|
||||
ValFunc: func(in string) (string, error) { return optionRomValue(in) },
|
||||
},
|
||||
})
|
||||
|
||||
return &Table{
|
||||
logger: level.NewFilter(logger, level.AllowInfo()),
|
||||
parser: parser,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Table) generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
|
||||
result := make(map[string]string)
|
||||
|
||||
for _, mode := range []string{"-check", "-mode"} {
|
||||
output := new(bytes.Buffer)
|
||||
if err := t.runFirmwarepasswd(ctx, mode, output); err != nil {
|
||||
level.Info(t.logger).Log(
|
||||
"msg", "Error running firmware password",
|
||||
"command", mode,
|
||||
"err", err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
// Merge resulting matches
|
||||
for _, row := range t.parser.Parse(output) {
|
||||
for k, v := range row {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return []map[string]string{result}, nil
|
||||
}
|
||||
|
||||
func (t *Table) runFirmwarepasswd(ctx context.Context, subcommand string, output *bytes.Buffer) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cmd := exec.CommandContext(ctx, "/usr/sbin/firmwarepasswd", subcommand)
|
||||
|
||||
dir, err := os.MkdirTemp("", "osq-firmwarepasswd")
|
||||
if err != nil {
|
||||
return fmt.Errorf("mktemp: %w", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
if err := os.Chmod(dir, 0o755); err != nil {
|
||||
return fmt.Errorf("chmod: %w", err)
|
||||
}
|
||||
|
||||
cmd.Dir = dir
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
cmd.Stderr = stderr
|
||||
|
||||
cmd.Stdout = output
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
level.Debug(t.logger).Log(
|
||||
"msg", "Error running firmwarepasswd",
|
||||
"stderr", strings.TrimSpace(stderr.String()),
|
||||
"stdout", strings.TrimSpace(output.String()),
|
||||
"err", err,
|
||||
)
|
||||
return fmt.Errorf("running firmwarepasswd: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func modeValue(in string) (string, error) {
|
||||
components := strings.SplitN(in, ":", 2)
|
||||
if len(components) < 2 {
|
||||
return "", fmt.Errorf("Can't tell mode from %s", in)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(strings.ToLower(components[1])), nil
|
||||
}
|
||||
|
||||
func passwordValue(in string) (string, error) {
|
||||
components := strings.SplitN(in, ":", 2)
|
||||
if len(components) < 2 {
|
||||
return "", fmt.Errorf("Can't tell value from %s", in)
|
||||
}
|
||||
|
||||
t, err := discernValBool(components[1])
|
||||
|
||||
if t {
|
||||
return "1", err
|
||||
}
|
||||
return "0", err
|
||||
}
|
||||
|
||||
func optionRomValue(in string) (string, error) {
|
||||
switch strings.TrimPrefix(in, "Option roms ") {
|
||||
case "not allowed":
|
||||
return "0", nil
|
||||
case "allowed":
|
||||
return "1", nil
|
||||
}
|
||||
return "", fmt.Errorf("Can't tell value from %s", in)
|
||||
}
|
||||
|
||||
func discernValBool(in string) (bool, error) {
|
||||
switch strings.TrimSpace(strings.ToLower(in)) {
|
||||
case "true", "t", "1", "y", "yes":
|
||||
return true, nil
|
||||
case "false", "f", "0", "n", "no":
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("Can't discern boolean from string <%s>", in)
|
||||
}
|
71
orbit/pkg/table/firmwarepasswd/firmwarepasswd_test.go
Normal file
71
orbit/pkg/table/firmwarepasswd/firmwarepasswd_test.go
Normal file
@ -0,0 +1,71 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package firmwarepasswd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParser(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
expected map[string]string
|
||||
}{
|
||||
{
|
||||
input: "check-no.txt",
|
||||
expected: map[string]string{"password_enabled": "0"},
|
||||
},
|
||||
{
|
||||
input: "check-garbage.txt",
|
||||
expected: map[string]string{"password_enabled": "0"},
|
||||
},
|
||||
{
|
||||
input: "check-yes.txt",
|
||||
expected: map[string]string{"password_enabled": "1"},
|
||||
},
|
||||
{
|
||||
input: "mode-command.txt",
|
||||
expected: map[string]string{
|
||||
"mode": "command",
|
||||
"option_roms_allowed": "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "mode-none.txt",
|
||||
expected: map[string]string{
|
||||
"mode": "none",
|
||||
"option_roms_allowed": "1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
parser := New(log.NewNopLogger()).parser
|
||||
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
inputBytes, err := os.ReadFile(filepath.Join("testdata", tt.input))
|
||||
require.NoError(t, err, "read file %s", tt.input)
|
||||
|
||||
inputBuffer := bytes.NewBuffer(inputBytes)
|
||||
|
||||
result := make(map[string]string)
|
||||
for _, row := range parser.Parse(inputBuffer) {
|
||||
for k, v := range row {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
require.EqualValues(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
83
orbit/pkg/table/firmwarepasswd/parser.go
Normal file
83
orbit/pkg/table/firmwarepasswd/parser.go
Normal file
@ -0,0 +1,83 @@
|
||||
// based on github.com/kolide/launcher/pkg/osquery/tables
|
||||
package firmwarepasswd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
)
|
||||
|
||||
type Matcher struct {
|
||||
Match func(string) bool
|
||||
KeyFunc func(string) (string, error)
|
||||
ValFunc func(string) (string, error)
|
||||
}
|
||||
|
||||
type OutputParser struct {
|
||||
matchers []Matcher
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func NewParser(logger log.Logger, matchers []Matcher) *OutputParser {
|
||||
p := &OutputParser{
|
||||
matchers: matchers,
|
||||
logger: logger,
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Parse looks at command output, line by line. It uses the defined Matchers to set any appropriate values
|
||||
func (p *OutputParser) Parse(input *bytes.Buffer) []map[string]string {
|
||||
var results []map[string]string
|
||||
|
||||
scanner := bufio.NewScanner(input)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
row := make(map[string]string)
|
||||
|
||||
// check each possible key match
|
||||
for _, m := range p.matchers {
|
||||
if m.Match(line) {
|
||||
key, err := m.KeyFunc(line)
|
||||
if err != nil {
|
||||
level.Debug(p.logger).Log(
|
||||
"msg", "key match failed",
|
||||
"line", line,
|
||||
"err", err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
val, err := m.ValFunc(line)
|
||||
if err != nil {
|
||||
level.Debug(p.logger).Log(
|
||||
"msg", "value match failed",
|
||||
"line", line,
|
||||
"err", err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
row[key] = val
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if len(row) == 0 {
|
||||
level.Debug(p.logger).Log("msg", "No matched keys", "line", line)
|
||||
continue
|
||||
}
|
||||
results = append(results, row)
|
||||
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
level.Debug(p.logger).Log("msg", "scanner error", "err", err)
|
||||
}
|
||||
return results
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user