diff --git a/go.mod b/go.mod index fff3aa876..b55360a28 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/hashicorp/go-uuid v1.0.2 // indirect github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0 github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce // indirect - github.com/lacework/go-sdk v0.10.1 + github.com/lacework/go-sdk v0.10.2-0.20210724174255-10f6fdf9ad38 github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/oklog/run v1.1.0 // indirect github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 1ed99834e..b2ef2190a 100644 --- a/go.sum +++ b/go.sum @@ -488,6 +488,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/kyokomi/emoji/v2 v2.2.8/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE= github.com/lacework/go-sdk v0.10.1 h1:3RASm189SDo6jXRCP7oEtciWCHTVpL4XJnzCiHAzb44= github.com/lacework/go-sdk v0.10.1/go.mod h1:YRkaH/2GLcUqkvqXr55nqxcJfC+/j/h4EUUUwwVga7Y= +github.com/lacework/go-sdk v0.10.2-0.20210724174255-10f6fdf9ad38 h1:FsT/7JJ/pTKXXgKhYkMTRT8hLGWTVvM14kZpy/nGK0Q= +github.com/lacework/go-sdk v0.10.2-0.20210724174255-10f6fdf9ad38/go.mod h1:YRkaH/2GLcUqkvqXr55nqxcJfC+/j/h4EUUUwwVga7Y= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= diff --git a/vendor/github.com/lacework/go-sdk/api/alert_channels.go b/vendor/github.com/lacework/go-sdk/api/alert_channels.go new file mode 100644 index 000000000..0bc27ffa5 --- /dev/null +++ b/vendor/github.com/lacework/go-sdk/api/alert_channels.go @@ -0,0 +1,186 @@ +// +// Author:: Salim Afiune Maya () +// Copyright:: Copyright 2021, Lacework Inc. +// License:: Apache License, Version 2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package api + +import ( + "fmt" + + "github.com/pkg/errors" +) + +// AlertChannelsService is the service that interacts with +// the AlertChannels schema from the Lacework APIv2 Server +type AlertChannelsService struct { + client *Client +} + +// NewAlertChannel returns an instance of the AlertChannelRaw struct with the +// provided Alert Channel integration type, name and raw data as an interface{}. +// +// NOTE: This function must be used by any Alert Channel type. +// +// Basic usage: Initialize a new EmailUserAlertChannel struct, then +// use the new instance to do CRUD operations +// +// client, err := api.NewClient("account") +// if err != nil { +// return err +// } +// +// emailAlertChan := api.NewAlertChannel("foo", +// api.EmailUserAlertChannel, +// api.EmailUserData{ +// ChannelProps: api.EmailUserChannelProps{ +// Recipients: []string{"name@example.com"}, +// }, +// }, +// ) +// +// client.V2.AlertChannels.Creates(emailAlertChan) +// +func NewAlertChannel(name string, iType alertChannelType, data interface{}) AlertChannelRaw { + return AlertChannelRaw{ + v2CommonIntegrationData: v2CommonIntegrationData{ + Name: name, + Type: iType.String(), + Enabled: 1, + }, + Data: data, + } +} + +// AlertChannel is an interface that helps us implement a few functions +// that any Alert Channel might use, there are some cases, like during +// Update, where we need to get the ID of the Alert Channel and its type, +// this will allow users to pass any Alert Channel that implements these +// methods +type AlertChannel interface { + ID() string + AlertChannelType() alertChannelType +} + +type alertChannelType int + +const ( + // type that defines a non-existing Alert Channel integration + NoneAlertChannel alertChannelType = iota + EmailUserAlertChannel +) + +// AlertChannelTypes is the list of available Alert Channel integration types +var AlertChannelTypes = map[alertChannelType]string{ + NoneAlertChannel: "None", + EmailUserAlertChannel: "EmailUser", +} + +// String returns the string representation of a Alert Channel integration type +func (i alertChannelType) String() string { + return AlertChannelTypes[i] +} + +// FindAlertChannelType looks up inside the list of available alert channel types +// the matching type from the provided string, if none, returns NoneAlertChannel +func FindAlertChannelType(alertChannel string) (alertChannelType, bool) { + for cType, cStr := range AlertChannelTypes { + if cStr == alertChannel { + return cType, true + } + } + return NoneAlertChannel, false +} + +// List returns a list of Alert Channel integrations +func (svc *AlertChannelsService) List() (response AlertChannelsResponse, err error) { + err = svc.client.RequestDecoder("GET", apiV2AlertChannels, nil, &response) + return +} + +// Create creates a single Alert Channel integration +func (svc *AlertChannelsService) Create(integration AlertChannelRaw) ( + response AlertChannelResponse, + err error, +) { + err = svc.create(integration, &response) + return +} + +// Delete deletes a Alert Channel integration that matches the provided guid +func (svc *AlertChannelsService) Delete(guid string) error { + if guid == "" { + return errors.New("specify an intgGuid") + } + + return svc.client.RequestDecoder( + "DELETE", + fmt.Sprintf(apiV2AlertChannelFromGUID, guid), + nil, + nil, + ) +} + +// Get returns a raw response of the Alert Channel with the matching integration guid. +// +// To return a more specific Go struct of a Alert Channel integration, use the proper +// method such as GetEmailUser() where the function name is composed by: +// +// Get(guid) +// +// Where is the Alert Channel integration type. +func (svc *AlertChannelsService) Get(guid string) (response AlertChannelResponse, err error) { + err = svc.get(guid, &response) + return +} + +type AlertChannelRaw struct { + v2CommonIntegrationData + Data interface{} `json:"data,omitempty"` +} + +func (alert AlertChannelRaw) AlertChannelType() alertChannelType { + t, _ := FindAlertChannelType(alert.Type) + return t +} + +type AlertChannelResponse struct { + Data AlertChannelRaw `json:"data"` +} + +type AlertChannelsResponse struct { + Data []AlertChannelRaw `json:"data"` +} + +func (svc *AlertChannelsService) create(data interface{}, response interface{}) error { + return svc.client.RequestEncoderDecoder("POST", apiV2AlertChannels, data, response) +} + +func (svc *AlertChannelsService) get(guid string, response interface{}) error { + if guid == "" { + return errors.New("specify an intgGuid") + } + apiPath := fmt.Sprintf(apiV2AlertChannelFromGUID, guid) + return svc.client.RequestDecoder("GET", apiPath, nil, response) +} + +func (svc *AlertChannelsService) update(guid string, data interface{}, response interface{}) error { + if guid == "" { + return errors.New("specify an intgGuid") + } + apiPath := fmt.Sprintf(apiV2AlertChannelFromGUID, guid) + return svc.client.RequestEncoderDecoder("PATCH", apiPath, data, response) +} diff --git a/vendor/github.com/lacework/go-sdk/api/alert_channels_email_user.go b/vendor/github.com/lacework/go-sdk/api/alert_channels_email_user.go new file mode 100644 index 000000000..a68ac7c26 --- /dev/null +++ b/vendor/github.com/lacework/go-sdk/api/alert_channels_email_user.go @@ -0,0 +1,123 @@ +// +// Author:: Salim Afiune Maya () +// Copyright:: Copyright 2021, Lacework Inc. +// License:: Apache License, Version 2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package api + +import ( + "encoding/json" + "strings" +) + +// GetEmailUser gets a single EmailUser alert channel matching the +// provided integration guid +func (svc *AlertChannelsService) GetEmailUser(guid string) ( + response EmailUserAlertChannelResponse, + err error, +) { + + // by default, expect the correct response, if not, try the workaround + err = svc.get(guid, &response) + if err == nil { + return + } + + // Workaround from APIv2 + // Bug: https://lacework.atlassian.net/browse/RAIN-20070 + // + // This means that the response.Data.Data.ChannelProps.Recipients is a 'string' + // instead of '[]string'. We will try to deserialize and cast to correct response + var getResponse emailUserGetAlertChannelResponse + err = svc.get(guid, &getResponse) + if err != nil { + return + } + + // convert GET response to a consistent response + response, err = convertGetEmailUserAlertChannelResponse(getResponse) + return +} + +// UpdateEmailUser updates a single EmailUser integration on the Lacework Server +func (svc *AlertChannelsService) UpdateEmailUser(data AlertChannel) ( + response EmailUserAlertChannelResponse, + err error, +) { + err = svc.update(data.ID(), data, &response) + return +} + +type EmailUserAlertChannelResponse struct { + Data EmailUserIntegration `json:"data"` +} + +type EmailUserIntegration struct { + v2CommonIntegrationData + Data EmailUserData `json:"data"` +} + +type EmailUserData struct { + ChannelProps EmailUserChannelProps `json:"channelProps"` + NotificationTypes struct { + Properties interface{} `json:"properties,omitempty"` + } `json:"notificationTypes"` +} + +type EmailUserChannelProps struct { + Recipients []string `json:"recipients"` +} + +// Workaround from APIv2 +// Bug: https://lacework.atlassian.net/browse/RAIN-20070 +type emailUserGetData struct { + ChannelProps struct { + Recipients interface{} `json:"recipients"` + } `json:"channelProps"` + NotificationTypes struct { + Properties interface{} `json:"properties,omitempty"` + } `json:"notificationTypes"` +} +type emailUserGetIntegration struct { + v2CommonIntegrationData + Data emailUserGetData `json:"data"` +} +type emailUserGetAlertChannelResponse struct { + Data emailUserGetIntegration `json:"data"` +} + +func convertGetEmailUserAlertChannelResponse( + res emailUserGetAlertChannelResponse) (EmailUserAlertChannelResponse, error) { + + recipientsString, ok := res.Data.Data.ChannelProps.Recipients.(string) + if ok { + // deserialize string + res.Data.Data.ChannelProps.Recipients = strings.Split(recipientsString, ",") + } + + return castEmailUserAlertChannelResponse(res) +} + +func castEmailUserAlertChannelResponse( + res interface{}) (r EmailUserAlertChannelResponse, err error) { + var j []byte + j, err = json.Marshal(res) + if err != nil { + return + } + err = json.Unmarshal(j, &r) + return +} diff --git a/vendor/github.com/lacework/go-sdk/api/api.go b/vendor/github.com/lacework/go-sdk/api/api.go index 6db677933..eb25f980c 100644 --- a/vendor/github.com/lacework/go-sdk/api/api.go +++ b/vendor/github.com/lacework/go-sdk/api/api.go @@ -81,13 +81,15 @@ const ( // Alpha apiLQLDataSources = "v1/external/lql/dataSources" apiLQLDescribe = "v1/external/lql/describe" - apiLQLQuery = "v1/external/lql/query" // API v2 Endpoints // // These endpoints only exist in APIv2 and therefore we prefix them with 'v2/' apiV2UserProfile = "v2/UserProfile" + apiV2AlertChannels = "v2/AlertChannels" + apiV2AlertChannelFromGUID = "v2/AlertChannels/%s" + apiV2CloudAccounts = "v2/CloudAccounts" apiV2CloudAccountFromGUID = "v2/CloudAccounts/%s" @@ -97,6 +99,7 @@ const ( apiV2Policies = "v2/Policies" apiV2Queries = "v2/Queries" + apiV2QueriesExecute = "v2/Queries/execute" apiV2QueriesValidate = "v2/Queries/validate" ) diff --git a/vendor/github.com/lacework/go-sdk/api/lql.go b/vendor/github.com/lacework/go-sdk/api/lql.go index 7cd377cda..521d2d504 100644 --- a/vendor/github.com/lacework/go-sdk/api/lql.go +++ b/vendor/github.com/lacework/go-sdk/api/lql.go @@ -21,7 +21,6 @@ package api import ( "fmt" "net/url" - "time" "github.com/pkg/errors" ) @@ -56,15 +55,6 @@ type QueriesResponse struct { Message string `json:"message"` } -func validateQueryRange(start, end time.Time) (err error) { - // validate range - if start.After(end) { - err = errors.New("date range should have a start time before the end time") - return - } - return nil -} - // QueryService is a service that interacts with the Queries // endpoints from the Lacework Server type QueryService struct { @@ -120,23 +110,3 @@ func (svc *QueryService) Get(id string) ( ) return } - -func (svc *QueryService) Execute(queryText string, start time.Time, end time.Time) ( - response map[string]interface{}, - err error, -) { - if queryText == "" { - err = errors.New("query text must be provided") - return - } - if err = validateQueryRange(start, end); err != nil { - return - } - query := map[string]string{ - "query_text": queryText, - "start_time_range": start.UTC().Format(time.RFC3339), - "end_time_range": end.UTC().Format(time.RFC3339), - } - err = svc.client.RequestEncoderDecoder("POST", apiLQLQuery, query, &response) - return -} diff --git a/vendor/github.com/lacework/go-sdk/api/lql_execute.go b/vendor/github.com/lacework/go-sdk/api/lql_execute.go new file mode 100644 index 000000000..3ec6b8457 --- /dev/null +++ b/vendor/github.com/lacework/go-sdk/api/lql_execute.go @@ -0,0 +1,124 @@ +// +// Author:: Salim Afiune Maya () +// Copyright:: Copyright 2020, Lacework Inc. +// License:: Apache License, Version 2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package api + +import ( + "fmt" + "net/url" + "time" + + "github.com/lacework/go-sdk/lwtime" + "github.com/pkg/errors" +) + +type ExecuteQuery struct { + QueryText string `json:"queryText"` + EvaluatorID string `json:"evaluatorId"` +} + +type ExecuteQueryArgument struct { + Name string `json:"name"` + Value string `json:"value"` +} + +type ExecuteQueryRequest struct { + Query ExecuteQuery `json:"query"` + Arguments []ExecuteQueryArgument `json:"arguments"` +} + +type ExecuteQueryByIDRequest struct { + QueryID string `json:"queryId,omitempty"` + Arguments []ExecuteQueryArgument `json:"arguments"` +} + +func validateQueryArguments(args []ExecuteQueryArgument) (err error) { + var hasStart, hasEnd bool + var start, end time.Time + + for _, arg := range args { + if arg.Name == "StartTimeRange" { + hasStart = true + start, err = validateQueryTimeString(arg.Value) + } + if err != nil { + return errors.Wrap(err, "invalid StartTimeRange argument") + } + + if arg.Name == "EndTimeRange" { + hasEnd = true + end, err = validateQueryTimeString(arg.Value) + } + if err != nil { + return errors.Wrap(err, "invalid EndTimeRange argument") + } + } + + if hasStart && hasEnd { + return validateQueryRange(start, end) + } + return nil +} + +// StartTimeRange and EndTimeRange should be +func validateQueryTimeString(s string) (time.Time, error) { + return time.Parse(lwtime.RFC3339Milli, s) +} + +func validateQueryRange(start, end time.Time) (err error) { + // validate range + if start.After(end) { + err = errors.New("date range should have a start time before the end time") + return + } + return nil +} + +func (svc *QueryService) Execute(request ExecuteQueryRequest) ( + response map[string]interface{}, + err error, +) { + if err = validateQueryArguments(request.Arguments); err != nil { + return + } + err = svc.client.RequestEncoderDecoder("POST", apiV2QueriesExecute, request, &response) + return +} + +func (svc *QueryService) ExecuteByID(request ExecuteQueryByIDRequest) ( + response map[string]interface{}, + err error, +) { + if request.QueryID == "" { + err = errors.New("query ID must be provided") + return + } + queryID := request.QueryID + request.QueryID = "" // omit for POST + + if err = validateQueryArguments(request.Arguments); err != nil { + return + } + err = svc.client.RequestEncoderDecoder( + "POST", + fmt.Sprintf("%s/%s/execute", apiV2Queries, url.QueryEscape(queryID)), + request, + &response, + ) + return +} diff --git a/vendor/github.com/lacework/go-sdk/api/policy.go b/vendor/github.com/lacework/go-sdk/api/policy.go index 0f2fc8626..0a2e956a3 100644 --- a/vendor/github.com/lacework/go-sdk/api/policy.go +++ b/vendor/github.com/lacework/go-sdk/api/policy.go @@ -45,7 +45,7 @@ type NewPolicy struct { Remediation string `json:"remediation" yaml:"remediation"` Severity string `json:"severity" yaml:"severity"` Limit int `json:"limit,omitempty" yaml:"limit,omitempty"` - EvalFrequency string `json:"evalFrequency" yaml:"evalFrequency"` + EvalFrequency string `json:"evalFrequency,omitempty" yaml:"evalFrequency,omitempty"` AlertEnabled bool `json:"alertEnabled" yaml:"alertEnabled"` AlertProfile string `json:"alertProfile" yaml:"alertProfile"` PolicyUI map[string]string `json:"policyUi" yaml:"policyUi"` diff --git a/vendor/github.com/lacework/go-sdk/api/v2.go b/vendor/github.com/lacework/go-sdk/api/v2.go index 6d54b5ce9..e1dc3de4d 100644 --- a/vendor/github.com/lacework/go-sdk/api/v2.go +++ b/vendor/github.com/lacework/go-sdk/api/v2.go @@ -25,6 +25,7 @@ type V2Endpoints struct { // Every schema must have its own service UserProfile *UserProfileService + AlertChannels *AlertChannelsService CloudAccounts *CloudAccountsService AgentAccessTokens *AgentAccessTokensService Query *QueryService @@ -34,6 +35,7 @@ type V2Endpoints struct { func NewV2Endpoints(c *Client) *V2Endpoints { return &V2Endpoints{c, &UserProfileService{c}, + &AlertChannelsService{c}, &CloudAccountsService{c}, &AgentAccessTokensService{c}, &QueryService{c}, diff --git a/vendor/github.com/lacework/go-sdk/api/version.go b/vendor/github.com/lacework/go-sdk/api/version.go index 5e017b3ec..17908db5d 100644 --- a/vendor/github.com/lacework/go-sdk/api/version.go +++ b/vendor/github.com/lacework/go-sdk/api/version.go @@ -1,5 +1,5 @@ // Code generated by: scripts/version_updater.sh -// File generated at: 20210721095321 +// File generated at: 20210721150756 // // <<< DO NOT EDIT >>> // @@ -7,4 +7,4 @@ package api // Version is the semver coming from the VERSION file -const Version = "0.10.1" +const Version = "0.10.2-dev" diff --git a/vendor/github.com/lacework/go-sdk/lwtime/rfc3339.go b/vendor/github.com/lacework/go-sdk/lwtime/rfc3339.go new file mode 100644 index 000000000..a8744aa50 --- /dev/null +++ b/vendor/github.com/lacework/go-sdk/lwtime/rfc3339.go @@ -0,0 +1,5 @@ +package lwtime + +const ( + RFC3339Milli = "2006-01-02T15:04:05.000Z" +) diff --git a/vendor/modules.txt b/vendor/modules.txt index 97982477a..494e777b7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -190,7 +190,7 @@ github.com/klauspost/compress/fse github.com/klauspost/compress/huff0 github.com/klauspost/compress/zstd github.com/klauspost/compress/zstd/internal/xxhash -# github.com/lacework/go-sdk v0.10.1 +# github.com/lacework/go-sdk v0.10.2-0.20210724174255-10f6fdf9ad38 ## explicit github.com/lacework/go-sdk/api github.com/lacework/go-sdk/internal/array