Skip to content

Commit

Permalink
feat(api): add compliance service
Browse files Browse the repository at this point in the history
This new `ComplianceService` is a service that interacts with the
compliance endpoints from the Lacework Server, as well as the reports
endpoints.

The new api functions available for the Compliance Service are:
* `ListGcpProject`
* `GetAwsReport`
* `DownloadAwsReportPDF`
* `RunAwsReport`
* `ListAzureSubscription`
* `GetAzureReport`
* `DownloadAzureReportPDF`
* `RunAzureReport`
* `GetGcpReport`
* `DownloadGcpReportPDF`
* `RunGcpReport`

Signed-off-by: Salim Afiune Maya <[email protected]>
  • Loading branch information
afiune committed May 21, 2020
1 parent e1d3674 commit 862812c
Show file tree
Hide file tree
Showing 6 changed files with 526 additions and 0 deletions.
10 changes: 10 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ const (
apiVulnerabilitiesReportFromID = "external/vulnerabilities/container/imageId/%s"
apiVulnerabilitiesReportFromDigest = "external/vulnerabilities/container/imageDigest/%s"

apiComplianceAwsLatestReport = "external/compliance/aws/GetLatestComplianceReport?AWS_ACCOUNT_ID=%s"
apiComplianceGcpLatestReport = "external/compliance/gcp/GetLatestComplianceReport?GCP_ORG_ID=%s&GCP_PROJ_ID=%s"
apiComplianceGcpListProjects = "external/compliance/gcp/ListProjectsForOrganization?GCP_ORG_ID=%s"
apiComplianceAzureLatestReport = "external/compliance/azure/GetLatestComplianceReport?AZURE_TENANT_ID=%s&AZURE_SUBS_ID=%s"
apiComplianceAzureListSubscriptions = "external/compliance/azure/ListSubscriptionsForTenant?AZURE_TENANT_ID=%s"

apiRunReportGcp = "external/runReport/gcp/%s"
apiRunReportAws = "external/runReport/aws/%s"
apiRunReportAzure = "external/runReport/azure/%s"

apiEventsDetails = "external/events/GetEventDetails"
apiEventsDateRange = "external/events/GetEventsForDateRange"
)
Expand Down
2 changes: 2 additions & 0 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type Client struct {
log *zap.Logger

Events *EventsService
Compliance *ComplianceService
Integrations *IntegrationsService
Vulnerabilities *VulnerabilitiesService
}
Expand Down Expand Up @@ -86,6 +87,7 @@ func NewClient(account string, opts ...Option) (*Client, error) {
c: &http.Client{Timeout: defaultTimeout},
}
c.Events = &EventsService{c}
c.Compliance = &ComplianceService{c}
c.Integrations = &IntegrationsService{c}
c.Vulnerabilities = &VulnerabilitiesService{c}

Expand Down
99 changes: 99 additions & 0 deletions api/compliance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//
// Author:: Salim Afiune Maya (<[email protected]>)
// 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"

// ComplianceService is a service that interacts with the compliance
// endpoints from the Lacework Server
type ComplianceService struct {
client *Client
}

func (svc *ComplianceService) ListGcpProjects(orgID string) (
response compGcpProjectsResponse,
err error,
) {
apiPath := fmt.Sprintf(apiComplianceGcpListProjects, orgID)
err = svc.client.RequestDecoder("GET", apiPath, nil, &response)
return
}

type compGcpProjectsResponse struct {
Data []CompGcpProjects `json:"data"`
Ok bool `json:"ok"`
Message string `json:"message"`
}

type CompGcpProjects struct {
Organization string `json:"organization"`
Projects []string `json:"projects"`
}

type ComplianceSummary struct {
AssessedResourceCount int `json:"assessed_resource_count"`
NumCompliant int `json:"num_compliant"`
NumNotCompliant int `json:"num_not_compliant"`
NumRecommendations int `json:"num_recommendations"`
NumSeverity1NonCompliance int `json:"num_severity_1_non_compliance"`
NumSeverity2NonCompliance int `json:"num_severity_2_non_compliance"`
NumSeverity3NonCompliance int `json:"num_severity_3_non_compliance"`
NumSeverity4NonCompliance int `json:"num_severity_4_non_compliance"`
NumSeverity5NonCompliance int `json:"num_severity_5_non_compliance"`
NumSuppressed int `json:"num_suppressed"`
SuppressedResourceCount int `json:"suppressed_resource_count"`
ViolatedResourceCount int `json:"violated_resource_count"`
}

type ComplianceRecommendation struct {
RecID string `json:"rec_id"`
AssessedResourceCount int `json:"assessed_resource_count"`
ResourceCount int `json:"resource_count"`
Category string `json:"category"`
InfoLink string `json:"info_link"`
Service string `json:"service"`
Severity int `json:"severity"`
Status string `json:"status"`
Suppressions []string `json:"suppressions"`
Title string `json:"title"`
Violations []ComplianceViolation `json:"violations"`
}

func (r *ComplianceRecommendation) SeverityString() string {
switch r.Severity {
case 1:
return "Critical"
case 2:
return "High"
case 3:
return "Medium"
case 4:
return "Low"
case 5:
return "Info"
default:
return "Unknown"
}
}

type ComplianceViolation struct {
Region string `json:"region"`
Resource string `json:"resource"`
Reasons []string `json:"reasons"`
}
117 changes: 117 additions & 0 deletions api/compliance_aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//
// Author:: Salim Afiune Maya (<[email protected]>)
// 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"
"io"
"os"
"time"

"github.com/pkg/errors"
)

type ComplianceAwsReportConfig struct {
AccountID string
Type string
}

func (svc *ComplianceService) GetAwsReport(config ComplianceAwsReportConfig) (
response complianceAwsReportResponse,
err error,
) {
if config.AccountID == "" {
err = errors.New("account_id is required")
return
}
apiPath := fmt.Sprintf(apiComplianceAwsLatestReport, config.AccountID)

if config.Type != "" {
apiPath = fmt.Sprintf("%s&REPORT_TYPE=%s", apiPath, config.Type)
}

// add JSON format, if not, the default is PDF
apiPath = fmt.Sprintf("%s&FILE_FORMAT=json", apiPath)
err = svc.client.RequestDecoder("GET", apiPath, nil, &response)
return
}

func (svc *ComplianceService) DownloadAwsReportPDF(filepath string, config ComplianceAwsReportConfig) error {
if config.AccountID == "" {
return errors.New("account_id is required")
}

apiPath := fmt.Sprintf(apiComplianceAwsLatestReport, config.AccountID)

if config.Type != "" {
apiPath = fmt.Sprintf("%s&REPORT_TYPE=%s", apiPath, config.Type)
}

request, err := svc.client.NewRequest("GET", apiPath, nil)
if err != nil {
return err
}

response, err := svc.client.Do(request)
if err != nil {
return err
}
defer response.Body.Close()

err = checkErrorInResponse(response)
if err != nil {
return err
}

// Create the file
out, err := os.Create(filepath)
if err != nil {
return err
}
defer out.Close()

// Write the body to file
_, err = io.Copy(out, response.Body)
return err
}

func (svc *ComplianceService) RunAwsReport(accountID string) (
response map[string]interface{}, // @afiune not consistent with the other cloud providers
err error,
) {
apiPath := fmt.Sprintf(apiRunReportAws, accountID)
err = svc.client.RequestDecoder("POST", apiPath, nil, &response)
return
}

type complianceAwsReportResponse struct {
Data []ComplianceAwsReport `json:"data"`
Ok bool `json:"ok"`
Message string `json:"message"`
}

type ComplianceAwsReport struct {
ReportTitle string `json:"reportTitle"`
ReportType string `json:"reportType"`
ReportTime time.Time `json:"reportTime"`
AccountID string `json:"accountId"`
AccountAlias string `json:"accountAlias"`
Summary []ComplianceSummary `json:"summary"`
Recommendations []ComplianceRecommendation `json:"recommendations"`
}
Loading

0 comments on commit 862812c

Please sign in to comment.