Skip to content

Commit

Permalink
feat(cli): disable/enable all <report-type> compliance command (#755)
Browse files Browse the repository at this point in the history
  • Loading branch information
dmurray-lacework committed Apr 8, 2022
1 parent 9a35ff6 commit e694304
Show file tree
Hide file tree
Showing 19 changed files with 2,703 additions and 10 deletions.
20 changes: 20 additions & 0 deletions api/_examples/recommendations-v1/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,24 @@ func main() {
log.Fatal(err)
}
fmt.Printf("Recommendations Patched: %v", response.Data)

// List all Recommendation IDs for a given report type
reportSchema, err := lacework.Recommendations.Aws.GetReport("AWS_CIS_S3")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s Report Schema \n ---------------------------- \n%v\n", "AWS_CIS_S3", reportSchema)

// Enable/Disable all recommendations of a given report type
enableAll := api.NewRecommendationV1State(reportSchema, true)

response, err = lacework.Recommendations.Aws.Patch(enableAll)
if err != nil {
log.Fatal(err)
}

fmt.Printf("\nEnabled Recommendations \n ---------------------------- \n")
for k, v := range enableAll {
fmt.Printf("%s:%s \n", k, v)
}
}
4 changes: 2 additions & 2 deletions api/compliance_gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (svc *ComplianceService) GetGcpReport(config ComplianceGcpReportConfig) (
err error,
) {
if config.OrganizationID == "" || config.ProjectID == "" {
err = errors.New("organization_id and project_id is required")
err = errors.New("organization_id and project_id are required")
return
}
apiPath := fmt.Sprintf(apiComplianceGcpLatestReport, config.OrganizationID, config.ProjectID)
Expand All @@ -55,7 +55,7 @@ func (svc *ComplianceService) GetGcpReport(config ComplianceGcpReportConfig) (

func (svc *ComplianceService) DownloadGcpReportPDF(filepath string, config ComplianceGcpReportConfig) error {
if config.OrganizationID == "" || config.ProjectID == "" {
return errors.New("organization_id and project_id is required")
return errors.New("organization_id and project_id are required")
}

apiPath := fmt.Sprintf(apiComplianceGcpLatestReport, config.OrganizationID, config.ProjectID)
Expand Down
63 changes: 61 additions & 2 deletions api/v1_recommendations.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@

package api

import "fmt"
import (
"fmt"

"github.com/lacework/go-sdk/internal/array"
)

// RecommendationsServiceV1 is a service that interacts with the V1 Recommendations
// endpoints from the Lacework Server
Expand All @@ -31,7 +35,8 @@ type RecommendationsServiceV1 struct {

type recommendationServiceV1 interface {
List() ([]RecommendationV1, error)
Patch(recommendations RecommendationStateV1) (response RecommendationResponseV1, err error)
Patch(recommendations RecommendationStateV1) (RecommendationResponseV1, error)
GetReport(reportType string) ([]RecommendationV1, error)
}

type RecommendationTypeV1 string
Expand Down Expand Up @@ -83,3 +88,57 @@ func (res *RecommendationResponseV1) RecommendationList() (recommendations []Rec
}
return
}

type ReportSchema struct {
Name string `json:"name"`
RecommendationIDs []string `json:"recommendationIDs"`
}

func NewRecommendationV1State(recommendations []RecommendationV1, state bool) RecommendationStateV1 {
request := make(map[string]string)
for _, rec := range recommendations {
if state {
request[rec.ID] = "enable"

} else {
request[rec.ID] = "disable"
}
}
return request
}

func NewRecommendationV1(recommendations []RecommendationV1) RecommendationStateV1 {
request := make(map[string]string)
for _, rec := range recommendations {
if rec.State {
request[rec.ID] = "enable"

} else {
request[rec.ID] = "disable"
}
}
return request
}

// ReportStatus This is an experimental feature. Returned RecommendationID's are not guaranteed to be correct.
func (res *RecommendationResponseV1) ReportStatus() map[string]bool {
var recommendations = make(map[string]bool)

for _, rec := range res.RecommendationList() {
recommendations[rec.ID] = rec.State
}

return recommendations
}

// filterRecommendations This is an experimental feature. Returned RecommendationID's are not guaranteed to be correct.
func filterRecommendations(allRecommendations []RecommendationV1, schema ReportSchema) []RecommendationV1 {
var recommendations []RecommendationV1

for _, rec := range allRecommendations {
if array.ContainsStr(schema.RecommendationIDs, rec.ID) {
recommendations = append(recommendations, rec)
}
}
return recommendations
}
38 changes: 37 additions & 1 deletion api/v1_recommendations_aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@

package api

import (
"encoding/json"
"errors"

"github.com/lacework/go-sdk/internal/databox"
)

// AwsRecommendationsV1 is a service that interacts with the V1 Recommendations
// endpoints from the Lacework Server
type AwsRecommendationsV1 struct {
Expand All @@ -28,6 +35,35 @@ func (svc *AwsRecommendationsV1) List() ([]RecommendationV1, error) {
return svc.client.Recommendations.list(AwsRecommendation)
}

func (svc *AwsRecommendationsV1) Patch(recommendations RecommendationStateV1) (response RecommendationResponseV1, err error) {
func (svc *AwsRecommendationsV1) Patch(recommendations RecommendationStateV1) (RecommendationResponseV1, error) {
return svc.client.Recommendations.patch(AwsRecommendation, recommendations)
}

// GetReport This is an experimental feature. Returned RecommendationID's are not guaranteed to be correct. Scoped to Lacework Account/Subaccount
func (svc *AwsRecommendationsV1) GetReport(reportType string) ([]RecommendationV1, error) {
report := struct {
Ids []string `json:"recommendation_ids"`
}{}

schemaBytes, ok := databox.Get("/reports/aws/cis.json")
if !ok {
return []RecommendationV1{}, errors.New(
"compliance report schema not found",
)
}

err := json.Unmarshal(schemaBytes, &report)
if err != nil {
return []RecommendationV1{}, err
}

schema := ReportSchema{reportType, report.Ids}

// fetch all aws recommendations
allRecommendations, err := svc.client.Recommendations.Aws.List()
if err != nil {
return []RecommendationV1{}, err
}
filteredRecommendations := filterRecommendations(allRecommendations, schema)
return filteredRecommendations, nil
}
60 changes: 60 additions & 0 deletions api/v1_recommendations_aws_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// Author:: Darren Murray (<[email protected]>)
// Copyright:: Copyright 2022, 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_test

import (
"fmt"
"net/http"
"testing"

"github.com/lacework/go-sdk/api"
"github.com/lacework/go-sdk/internal/lacework"
"github.com/stretchr/testify/assert"
)

func TestRecommendationsAwsGetReport(t *testing.T) {
var (
expectedLen = 160
fakeServer = lacework.MockServer()
)

fakeServer.MockToken("TOKEN")
fakeServer.MockAPI("external/recommendations/aws",
func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method, "GetReport() should be a GET method")
Recommendations := generateRecommendations()
fmt.Fprintf(w, Recommendations)
},
)
defer fakeServer.Close()

c, err := api.NewClient("test",
api.WithToken("TOKEN"),
api.WithURL(fakeServer.URL()),
)
assert.Nil(t, err)

response, err := c.Recommendations.Aws.GetReport("CIS_1_1")
assert.Nil(t, err)
assert.NotNil(t, response)
assert.Equal(t, expectedLen, len(response))
for _, rec := range response {
assert.NotEmpty(t, rec.ID)
}
}
55 changes: 54 additions & 1 deletion api/v1_recommendations_azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@

package api

import (
"encoding/json"
"errors"
"fmt"

"github.com/lacework/go-sdk/internal/databox"
)

// AzureRecommendationsV1 is a service that interacts with the V1 Recommendations
// endpoints from the Lacework Server
type AzureRecommendationsV1 struct {
Expand All @@ -28,6 +36,51 @@ func (svc *AzureRecommendationsV1) List() ([]RecommendationV1, error) {
return svc.client.Recommendations.list(AzureRecommendation)
}

func (svc *AzureRecommendationsV1) Patch(recommendations RecommendationStateV1) (response RecommendationResponseV1, err error) {
func (svc *AzureRecommendationsV1) Patch(recommendations RecommendationStateV1) (RecommendationResponseV1, error) {
return svc.client.Recommendations.patch(AzureRecommendation, recommendations)
}

// GetReport This is an experimental feature. Returned RecommendationID's are not guaranteed to be correct. Scoped to Lacework Account/Subaccount
func (svc *AzureRecommendationsV1) GetReport(reportType string) ([]RecommendationV1, error) {
var (
schemaBytes []byte
ok bool
)
report := struct {
Ids []string `json:"recommendation_ids"`
}{}

switch reportType {
case "CIS_1_0":
schemaBytes, ok = databox.Get("/reports/azure/cis.json")
if !ok {
return []RecommendationV1{}, errors.New(
"compliance report schema not found",
)
}
case "CIS_1_3_1":
schemaBytes, ok = databox.Get("/reports/azure/cis_131.json")
if !ok {
return []RecommendationV1{}, errors.New(
"compliance report schema not found",
)
}
default:
return nil, fmt.Errorf("unable to find recommendations for report type %s", reportType)
}

err := json.Unmarshal(schemaBytes, &report)
if err != nil {
return []RecommendationV1{}, err
}

schema := ReportSchema{reportType, report.Ids}

// fetch all azure recommendations
allRecommendations, err := svc.client.Recommendations.Azure.List()
if err != nil {
return []RecommendationV1{}, err
}
filteredRecommendations := filterRecommendations(allRecommendations, schema)
return filteredRecommendations, nil
}
Loading

0 comments on commit e694304

Please sign in to comment.