diff --git a/internal/client/securityprofiles/securityprofiles.go b/internal/client/securityprofiles/securityprofiles.go index 71be6604b..4af72c852 100644 --- a/internal/client/securityprofiles/securityprofiles.go +++ b/internal/client/securityprofiles/securityprofiles.go @@ -29,6 +29,7 @@ import ( "strconv" "strings" "sync" + "time" ) type secprofiles struct { @@ -215,6 +216,36 @@ func Update(name string, content []byte) (respBody []byte, err error) { return respBody, err } +// GetProxyScore returns the security risk assessment score for a specific API proxy +// by filtering computeEnvironmentScores to the proxy's score path. +func GetProxyScore(profileName string, proxyName string, startTime string, endTime string) (respBody []byte, err error) { + if startTime == "" { + startTime = time.Now().AddDate(0, 0, -1).UTC().Format(time.RFC3339) + } + if endTime == "" { + endTime = time.Now().UTC().Format(time.RFC3339) + } + + scorePath := fmt.Sprintf("/org@%s/envs/%s/proxies/%s", + apiclient.GetApigeeOrg(), apiclient.GetApigeeEnv(), proxyName) + + score := computeenvscore{ + TimeRange: timeInterval{StartTime: startTime, EndTime: endTime}, + Filters: []scoreFilter{{ScorePath: scorePath}}, + } + + payload, err := json.Marshal(score) + if err != nil { + return nil, err + } + + u, _ := url.Parse(apiclient.GetApigeeBaseURL()) + u.Path = path.Join(u.Path, apiclient.GetApigeeOrg(), "securityProfiles", + profileName, "environments", apiclient.GetApigeeEnv()+":computeEnvironmentScores") + + return apiclient.HttpClient(u.String(), string(payload)) +} + // Compute func Compute(name string, startTime string, endTime string, filters []string, pageSize int, pageToken string, full bool, diff --git a/internal/cmd/securityprofiles/score.go b/internal/cmd/securityprofiles/score.go new file mode 100644 index 000000000..7bf4ecd3f --- /dev/null +++ b/internal/cmd/securityprofiles/score.go @@ -0,0 +1,81 @@ +// Copyright 2024 Google LLC +// +// 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 securityprofiles + +import ( + "fmt" + "internal/apiclient" + "internal/client/securityprofiles" + "time" + + "github.com/spf13/cobra" +) + +// ScoreCmd fetches the Advanced API Security risk assessment score for a proxy +var ScoreCmd = &cobra.Command{ + Use: "score", + Short: "Get Advanced API Security risk assessment score for an API proxy", + Long: "Get the Advanced API Security risk assessment score for a specific API proxy " + + "in an environment. Useful as a CI/CD guardrail to block deployments when the score is too low.", + Args: func(cmd *cobra.Command, args []string) (err error) { + if name == "" { + return fmt.Errorf("security profile name cannot be empty") + } + if proxyName == "" { + return fmt.Errorf("proxy name cannot be empty") + } + if scoreStartTime != "" { + if _, err = time.Parse(time.RFC3339, scoreStartTime); err != nil { + return fmt.Errorf("invalid format for start-time: %v", err) + } + } + if scoreEndTime != "" { + if _, err = time.Parse(time.RFC3339, scoreEndTime); err != nil { + return fmt.Errorf("invalid format for end-time: %v", err) + } + } + apiclient.SetApigeeEnv(environment) + apiclient.SetRegion(region) + return apiclient.SetApigeeOrg(org) + }, + RunE: func(cmd *cobra.Command, args []string) (err error) { + cmd.SilenceUsage = true + _, err = securityprofiles.GetProxyScore(name, proxyName, scoreStartTime, scoreEndTime) + return + }, +} + +var ( + proxyName string + scoreStartTime string + scoreEndTime string +) + +func init() { + ScoreCmd.Flags().StringVarP(&name, "name", "n", + "", "Name of the security profile") + ScoreCmd.Flags().StringVarP(&environment, "env", "e", + "", "Apigee environment name") + ScoreCmd.Flags().StringVarP(&proxyName, "proxy", "p", + "", "API proxy name to retrieve the risk assessment score for") + ScoreCmd.Flags().StringVarP(&scoreStartTime, "start-time", "", + "", "Inclusive start of the interval in RFC3339 format; default is 24 hours ago") + ScoreCmd.Flags().StringVarP(&scoreEndTime, "end-time", "", + "", "Exclusive end of the interval in RFC3339 format; default is current timestamp") + + _ = ScoreCmd.MarkFlagRequired("name") + _ = ScoreCmd.MarkFlagRequired("env") + _ = ScoreCmd.MarkFlagRequired("proxy") +} diff --git a/internal/cmd/securityprofiles/securityprofiles.go b/internal/cmd/securityprofiles/securityprofiles.go index c00bd7845..bbfd651c9 100644 --- a/internal/cmd/securityprofiles/securityprofiles.go +++ b/internal/cmd/securityprofiles/securityprofiles.go @@ -45,6 +45,7 @@ func init() { Cmd.AddCommand(ExpCmd) Cmd.AddCommand(ImpCmd) Cmd.AddCommand(ComputeCmd) + Cmd.AddCommand(ScoreCmd) _ = Cmd.MarkFlagRequired("org") }